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 virtual ~BaseNL80211Attr() = default; 36 37 const std::vector<uint8_t>& GetConstData() const; 38 int GetAttributeId() const; 39 // This is used when we initialize a NL80211 attribute from an existing 40 // buffer. 41 virtual bool IsValid() const; 42 // A util helper function to find a specific sub attribute from a buffer. 43 // This buffer is supposed to be from a nested attribute or a nl80211 packet. 44 // |*start| and |*end| are the start and end pointers of buffer where 45 // |id| atrribute locates. 46 static bool GetAttributeImpl(const uint8_t* buf, 47 size_t len, 48 int attr_id, 49 uint8_t** attr_start, 50 uint8_t** attr_end); 51 52 protected: 53 BaseNL80211Attr() = default; 54 void InitHeaderAndResize(int attribute_id, int payload_length); 55 56 std::vector<uint8_t> data_; 57 }; 58 59 template <typename T> 60 class NL80211Attr : public BaseNL80211Attr { 61 public: NL80211Attr(int id,T value)62 NL80211Attr(int id, T value) { 63 static_assert( 64 std::is_integral<T>::value, 65 "Failed to create NL80211Attr class with non-integral type"); 66 InitHeaderAndResize(id, sizeof(T)); 67 T* storage = reinterpret_cast<T*>(data_.data() + NLA_HDRLEN); 68 *storage = value; 69 } 70 // Caller is responsible for ensuring that |data| is: 71 // 1) Is at least NLA_HDRLEN long. 72 // 2) That *data when interpreted as a nlattr is internally consistent. 73 // (e.g. data.size() == NLA_ALIGN(nlattr.nla_len) 74 // and nla_len == NLA_HDRLEN + payload size NL80211Attr(const std::vector<uint8_t> & data)75 explicit NL80211Attr(const std::vector<uint8_t>& data) { 76 data_ = data; 77 } 78 79 ~NL80211Attr() override = default; 80 IsValid()81 bool IsValid() const override { 82 if (!BaseNL80211Attr::IsValid()) { 83 return false; 84 } 85 // If BaseNL80211Attr::IsValid() == true, at least we have enough valid 86 // buffer for header. 87 const nlattr* header = reinterpret_cast<const nlattr*>(data_.data()); 88 // Buffer size = header size + payload size + padding size 89 // nla_len = header size + payload size 90 if (NLA_ALIGN(sizeof(T)) + NLA_HDRLEN != data_.size() || 91 sizeof(T) + NLA_HDRLEN != header->nla_len ) { 92 return false; 93 } 94 return true; 95 } 96 GetValue()97 T GetValue() const { 98 return *reinterpret_cast<const T*>(data_.data() + NLA_HDRLEN); 99 } 100 }; // class NL80211Attr for POD-types 101 102 template <> 103 class NL80211Attr<std::vector<uint8_t>> : public BaseNL80211Attr { 104 public: 105 NL80211Attr(int id, const std::vector<uint8_t>& raw_buffer); 106 explicit NL80211Attr(const std::vector<uint8_t>& data); 107 ~NL80211Attr() override = default; 108 std::vector<uint8_t> GetValue() const; 109 }; // class NL80211Attr for raw data 110 111 template <> 112 class NL80211Attr<std::string> : public BaseNL80211Attr { 113 public: 114 NL80211Attr(int id, const std::string& str); 115 // We parse string attribute buffer in the same way kernel does. 116 // All trailing zeros are trimmed when retrieving a std::string from 117 // byte array. 118 explicit NL80211Attr(const std::vector<uint8_t>& data); 119 ~NL80211Attr() override = default; 120 std::string GetValue() const; 121 }; // class NL80211Attr for string 122 123 // Force the compiler not to instantiate these templates because 124 // they will be instantiated in nl80211_attribute.cpp file. This helps 125 // reduce compile time as well as object file size. 126 extern template class NL80211Attr<uint8_t>; 127 extern template class NL80211Attr<uint16_t>; 128 extern template class NL80211Attr<uint32_t>; 129 extern template class NL80211Attr<uint64_t>; 130 extern template class NL80211Attr<std::vector<uint8_t>>; 131 extern template class NL80211Attr<std::string>; 132 133 class NL80211NestedAttr : public BaseNL80211Attr { 134 public: 135 explicit NL80211NestedAttr(int id); 136 explicit NL80211NestedAttr(const std::vector<uint8_t>& data); 137 ~NL80211NestedAttr() override = default; 138 139 void AddAttribute(const BaseNL80211Attr& attribute); 140 // For NLA_FLAG attribute 141 void AddFlagAttribute(int attribute_id); 142 bool HasAttribute(int id) const; 143 144 // Access an attribute nested within |this|. 145 // The result is returned by writing the attribute object to |*attribute|. 146 // Deeper nested attributes are not included. This means if A is nested within 147 // |this|, and B is nested within A, this function can't be used to access B. 148 // The reason is that we may have multiple attributes having the same 149 // attribute id, nested within different level of |this|. 150 bool GetAttribute(int id, NL80211NestedAttr* attribute) const; 151 152 template <typename T> GetAttributeValue(int id,T * value)153 bool GetAttributeValue(int id, T* value) const { 154 std::vector<uint8_t> empty_vec; 155 // All data in |attribute| created here will be overwritten by 156 // GetAttribute(). So we use an empty vector to initialize it, 157 // regardless of the fact that an empty buffer is not qualified 158 // for creating a valid attribute. 159 NL80211Attr<T> attribute(empty_vec); 160 if (!GetAttribute(id, &attribute)) { 161 return false; 162 } 163 *value = attribute.GetValue(); 164 return true; 165 } 166 167 // Some of the nested attribute contains a list of same type sub-attributes. 168 // This function retrieves a vector of attribute value from a nested 169 // attribute. 170 // This is for both correctness and performance reasons: 171 // 172 // Correctness reason: 173 // These sub-attributes have attribute id from '0 to n' or '1 to n'. 174 // There is no document defining what the start index should be. 175 // This function ignore all these fake attribute ids. 176 // 177 // Performance reson: 178 // Calling GetAttributeValue() from '0 to n' results a n^2 time complexity. 179 // This function get a list of attribute values in one pass. 180 // 181 // Returns true on success. 182 template <typename T> GetListOfAttributeValues(std::vector<T> * value)183 bool GetListOfAttributeValues(std::vector<T>* value) const { 184 const uint8_t* ptr = data_.data() + NLA_HDRLEN; 185 const uint8_t* end_ptr = data_.data() + data_.size(); 186 std::vector<T> attr_list; 187 while (ptr + NLA_HDRLEN <= end_ptr) { 188 const nlattr* header = reinterpret_cast<const nlattr*>(ptr); 189 if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { 190 LOG(ERROR) << "Failed to get list of attributes: invalid nla_len."; 191 return false; 192 } 193 NL80211Attr<T> attribute(std::vector<uint8_t>( 194 ptr, 195 ptr + NLA_ALIGN(header->nla_len))); 196 if (!attribute.IsValid()) { 197 return false; 198 } 199 attr_list.emplace_back(attribute.GetValue()); 200 ptr += NLA_ALIGN(header->nla_len); 201 } 202 *value = std::move(attr_list); 203 return true; 204 } 205 206 // This is similar to |GetListOfAttributeValues|, but for the cases where all 207 // the sub-attributes are nested attributes. 208 bool GetListOfNestedAttributes(std::vector<NL80211NestedAttr>* value) const; 209 210 template <typename T> GetAttribute(int id,NL80211Attr<T> * attribute)211 bool GetAttribute(int id, NL80211Attr<T>* attribute) const { 212 uint8_t* start = nullptr; 213 uint8_t* end = nullptr; 214 if (!BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN, 215 data_.size() - NLA_HDRLEN, 216 id, &start, &end) || 217 start == nullptr || 218 end == nullptr) { 219 return false; 220 } 221 *attribute = NL80211Attr<T>(std::vector<uint8_t>(start, end)); 222 if (!attribute->IsValid()) { 223 return false; 224 } 225 return true; 226 } 227 228 void DebugLog() const; 229 230 }; 231 232 } // namespace wificond 233 } // namespace android 234 235 #endif // WIFICOND_NET_NL80211_ATTRIBUTE_H_ 236