1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef WIFICOND_NET_NL80211_ATTRIBUTE_H_ 18 #define WIFICOND_NET_NL80211_ATTRIBUTE_H_ 19 20 #include <memory> 21 #include <string> 22 #include <type_traits> 23 #include <vector> 24 25 #include <linux/netlink.h> 26 27 #include <android-base/logging.h> 28 #include <android-base/macros.h> 29 30 namespace android { 31 namespace wificond { 32 33 class BaseNL80211Attr { 34 public: 35 BaseNL80211Attr(int id, const std::vector<uint8_t>& raw_buffer); 36 virtual ~BaseNL80211Attr() = default; 37 38 const std::vector<uint8_t>& GetConstData() const; 39 int GetAttributeId() const; 40 // This is used when we initialize a NL80211 attribute from an existing 41 // buffer. 42 virtual bool IsValid() const; 43 // A util helper function to find a specific sub attribute from a buffer. 44 // This buffer is supposed to be from a nested attribute or a nl80211 packet. 45 // |*start| and |*end| are the start and end pointers of buffer where 46 // |id| atrribute locates. 47 static bool GetAttributeImpl(const uint8_t* buf, 48 size_t len, 49 int attr_id, 50 uint8_t** attr_start, 51 uint8_t** attr_end); 52 // Merge the payload of |attr| to current attribute. 53 // This is only used for merging attribute from the response of split dump. 54 // Returns true on success. 55 bool Merge(const BaseNL80211Attr& attr); 56 57 protected: 58 BaseNL80211Attr() = default; 59 void InitHeaderAndResize(int attribute_id, int payload_length); 60 61 std::vector<uint8_t> data_; 62 }; 63 64 template <typename T> 65 class NL80211Attr : public BaseNL80211Attr { 66 public: NL80211Attr(int id,T value)67 NL80211Attr(int id, T value) { 68 static_assert( 69 std::is_integral<T>::value, 70 "Failed to create NL80211Attr class with non-integral type"); 71 InitHeaderAndResize(id, sizeof(T)); 72 T* storage = reinterpret_cast<T*>(data_.data() + NLA_HDRLEN); 73 *storage = value; 74 } 75 // Caller is responsible for ensuring that |data| is: 76 // 1) Is at least NLA_HDRLEN long. 77 // 2) That *data when interpreted as a nlattr is internally consistent. 78 // (e.g. data.size() == NLA_ALIGN(nlattr.nla_len) 79 // and nla_len == NLA_HDRLEN + payload size NL80211Attr(const std::vector<uint8_t> & data)80 explicit NL80211Attr(const std::vector<uint8_t>& data) { 81 data_ = data; 82 } 83 84 ~NL80211Attr() override = default; 85 IsValid()86 bool IsValid() const override { 87 if (!BaseNL80211Attr::IsValid()) { 88 return false; 89 } 90 // If BaseNL80211Attr::IsValid() == true, at least we have enough valid 91 // buffer for header. 92 const nlattr* header = reinterpret_cast<const nlattr*>(data_.data()); 93 // Buffer size = header size + payload size + padding size 94 // nla_len = header size + payload size 95 if (NLA_ALIGN(sizeof(T)) + NLA_HDRLEN != data_.size() || 96 sizeof(T) + NLA_HDRLEN != header->nla_len ) { 97 return false; 98 } 99 return true; 100 } 101 GetValue()102 T GetValue() const { 103 return *reinterpret_cast<const T*>(data_.data() + NLA_HDRLEN); 104 } 105 }; // class NL80211Attr for POD-types 106 107 template <> 108 class NL80211Attr<std::vector<uint8_t>> : public BaseNL80211Attr { 109 public: 110 NL80211Attr(int id, const std::vector<uint8_t>& raw_buffer); 111 explicit NL80211Attr(const std::vector<uint8_t>& data); 112 ~NL80211Attr() override = default; 113 std::vector<uint8_t> GetValue() const; 114 }; // class NL80211Attr for raw data 115 116 template <size_t N> 117 class NL80211Attr<std::array<uint8_t, N>> : public BaseNL80211Attr { 118 public: NL80211Attr(int id,const std::array<uint8_t,N> & raw_buffer)119 NL80211Attr(int id, const std::array<uint8_t, N>& raw_buffer) 120 : BaseNL80211Attr( 121 id, std::vector<uint8_t>(raw_buffer.begin(), raw_buffer.end())) {} NL80211Attr(const std::vector<uint8_t> & data)122 explicit NL80211Attr(const std::vector<uint8_t>& data) { 123 data_ = data; 124 } 125 ~NL80211Attr() override = default; GetValue()126 std::array<uint8_t, N> GetValue() const { 127 std::array<uint8_t, N> arr; 128 std::copy_n(data_.data() + NLA_HDRLEN, N, arr.begin()); 129 return arr; 130 } 131 }; // class NL80211Attr for fixed size array 132 133 template <> 134 class NL80211Attr<std::string> : public BaseNL80211Attr { 135 public: 136 NL80211Attr(int id, const std::string& str); 137 // We parse string attribute buffer in the same way kernel does. 138 // All trailing zeros are trimmed when retrieving a std::string from 139 // byte array. 140 explicit NL80211Attr(const std::vector<uint8_t>& data); 141 ~NL80211Attr() override = default; 142 std::string GetValue() const; 143 }; // class NL80211Attr for string 144 145 // Force the compiler not to instantiate these templates because 146 // they will be instantiated in nl80211_attribute.cpp file. This helps 147 // reduce compile time as well as object file size. 148 extern template class NL80211Attr<uint8_t>; 149 extern template class NL80211Attr<uint16_t>; 150 extern template class NL80211Attr<uint32_t>; 151 extern template class NL80211Attr<uint64_t>; 152 extern template class NL80211Attr<std::vector<uint8_t>>; 153 extern template class NL80211Attr<std::string>; 154 155 class NL80211NestedAttr : public BaseNL80211Attr { 156 public: 157 explicit NL80211NestedAttr(int id); 158 explicit NL80211NestedAttr(const std::vector<uint8_t>& data); 159 ~NL80211NestedAttr() override = default; 160 161 void AddAttribute(const BaseNL80211Attr& attribute); 162 // For NLA_FLAG attribute 163 void AddFlagAttribute(int attribute_id); 164 bool HasAttribute(int id) const; 165 166 // Access an attribute nested within |this|. 167 // The result is returned by writing the attribute object to |*attribute|. 168 // Deeper nested attributes are not included. This means if A is nested within 169 // |this|, and B is nested within A, this function can't be used to access B. 170 // The reason is that we may have multiple attributes having the same 171 // attribute id, nested within different level of |this|. 172 bool GetAttribute(int id, NL80211NestedAttr* attribute) const; 173 174 template <typename T> GetAttributeValue(int id,T * value)175 bool GetAttributeValue(int id, T* value) const { 176 std::vector<uint8_t> empty_vec; 177 // All data in |attribute| created here will be overwritten by 178 // GetAttribute(). So we use an empty vector to initialize it, 179 // regardless of the fact that an empty buffer is not qualified 180 // for creating a valid attribute. 181 NL80211Attr<T> attribute(empty_vec); 182 if (!GetAttribute(id, &attribute)) { 183 return false; 184 } 185 *value = attribute.GetValue(); 186 return true; 187 } 188 189 // Some of the nested attribute contains a list of same type sub-attributes. 190 // This function retrieves a vector of attribute values from a nested 191 // attribute. 192 // 193 // This is for both correctness and performance reasons: Refer to 194 // GetListOfAttributes(). 195 // 196 // Returns true on success. 197 template <typename T> GetListOfAttributeValues(std::vector<T> * value)198 bool GetListOfAttributeValues(std::vector<T>* value) const { 199 const uint8_t* ptr = data_.data() + NLA_HDRLEN; 200 const uint8_t* end_ptr = data_.data() + data_.size(); 201 std::vector<T> attr_list; 202 while (ptr + NLA_HDRLEN <= end_ptr) { 203 const nlattr* header = reinterpret_cast<const nlattr*>(ptr); 204 if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { 205 LOG(ERROR) << "Failed to get list of attributes: invalid nla_len."; 206 return false; 207 } 208 NL80211Attr<T> attribute(std::vector<uint8_t>( 209 ptr, 210 ptr + NLA_ALIGN(header->nla_len))); 211 if (!attribute.IsValid()) { 212 return false; 213 } 214 attr_list.emplace_back(attribute.GetValue()); 215 ptr += NLA_ALIGN(header->nla_len); 216 } 217 *value = std::move(attr_list); 218 return true; 219 } 220 221 // Some of the nested attribute contains a list of same type sub-attributes. 222 // This function retrieves a vector of attributes from a nested 223 // attribute. 224 // 225 // This is for both correctness and performance reasons: 226 // Correctness reason: 227 // These sub-attributes have attribute id from '0 to n' or '1 to n'. 228 // There is no document defining what the start index should be. 229 // 230 // Performance reson: 231 // Calling GetAttribute() from '0 to n' results a n^2 time complexity. 232 // This function get a list of attributes in one pass. 233 // 234 // Returns true on success. 235 template <typename T> GetListOfAttributes(std::vector<NL80211Attr<T>> * value)236 bool GetListOfAttributes(std::vector<NL80211Attr<T>>* value) const { 237 const uint8_t* ptr = data_.data() + NLA_HDRLEN; 238 const uint8_t* end_ptr = data_.data() + data_.size(); 239 std::vector<NL80211Attr<T>> attr_list; 240 while (ptr + NLA_HDRLEN <= end_ptr) { 241 const nlattr* header = reinterpret_cast<const nlattr*>(ptr); 242 if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { 243 LOG(ERROR) << "Failed to get list of attributes: invalid nla_len."; 244 return false; 245 } 246 NL80211Attr<T> attribute(std::vector<uint8_t>( 247 ptr, 248 ptr + NLA_ALIGN(header->nla_len))); 249 if (!attribute.IsValid()) { 250 return false; 251 } 252 attr_list.emplace_back(attribute); 253 ptr += NLA_ALIGN(header->nla_len); 254 } 255 *value = std::move(attr_list); 256 return true; 257 } 258 259 // This is similar to |GetListOfAttributeValues|, but for the cases where all 260 // the sub-attributes are nested attributes. 261 bool GetListOfNestedAttributes(std::vector<NL80211NestedAttr>* value) const; 262 263 template <typename T> GetAttribute(int id,NL80211Attr<T> * attribute)264 bool GetAttribute(int id, NL80211Attr<T>* attribute) const { 265 uint8_t* start = nullptr; 266 uint8_t* end = nullptr; 267 if (!BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN, 268 data_.size() - NLA_HDRLEN, 269 id, &start, &end) || 270 start == nullptr || 271 end == nullptr) { 272 return false; 273 } 274 *attribute = NL80211Attr<T>(std::vector<uint8_t>(start, end)); 275 if (!attribute->IsValid()) { 276 return false; 277 } 278 return true; 279 } 280 281 void DebugLog() const; 282 283 }; 284 285 } // namespace wificond 286 } // namespace android 287 288 #endif // WIFICOND_NET_NL80211_ATTRIBUTE_H_ 289