289 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			289 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2016 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #ifndef WIFICOND_NET_NL80211_ATTRIBUTE_H_
 | |
| #define WIFICOND_NET_NL80211_ATTRIBUTE_H_
 | |
| 
 | |
| #include <memory>
 | |
| #include <string>
 | |
| #include <type_traits>
 | |
| #include <vector>
 | |
| 
 | |
| #include <linux/netlink.h>
 | |
| 
 | |
| #include <android-base/logging.h>
 | |
| #include <android-base/macros.h>
 | |
| 
 | |
| namespace android {
 | |
| namespace wificond {
 | |
| 
 | |
| class BaseNL80211Attr {
 | |
|  public:
 | |
|   BaseNL80211Attr(int id, const std::vector<uint8_t>& raw_buffer);
 | |
|   virtual ~BaseNL80211Attr() = default;
 | |
| 
 | |
|   const std::vector<uint8_t>& GetConstData() const;
 | |
|   int GetAttributeId() const;
 | |
|   // This is used when we initialize a NL80211 attribute from an existing
 | |
|   // buffer.
 | |
|   virtual bool IsValid() const;
 | |
|   // A util helper function to find a specific sub attribute from a buffer.
 | |
|   // This buffer is supposed to be from a nested attribute or a nl80211 packet.
 | |
|   // |*start| and |*end| are the start and end pointers of buffer where
 | |
|   // |id| atrribute locates.
 | |
|   static bool GetAttributeImpl(const uint8_t* buf,
 | |
|                               size_t len,
 | |
|                               int attr_id,
 | |
|                               uint8_t** attr_start,
 | |
|                               uint8_t** attr_end);
 | |
|   // Merge the payload of |attr| to current attribute.
 | |
|   // This is only used for merging attribute from the response of split dump.
 | |
|   // Returns true on success.
 | |
|   bool Merge(const BaseNL80211Attr& attr);
 | |
| 
 | |
|  protected:
 | |
|   BaseNL80211Attr() = default;
 | |
|   void InitHeaderAndResize(int attribute_id, int payload_length);
 | |
| 
 | |
|   std::vector<uint8_t> data_;
 | |
| };
 | |
| 
 | |
| template <typename T>
 | |
| class NL80211Attr : public BaseNL80211Attr {
 | |
|  public:
 | |
|   NL80211Attr(int id, T value) {
 | |
|     static_assert(
 | |
|         std::is_integral<T>::value,
 | |
|         "Failed to create NL80211Attr class with non-integral type");
 | |
|     InitHeaderAndResize(id, sizeof(T));
 | |
|     T* storage = reinterpret_cast<T*>(data_.data() + NLA_HDRLEN);
 | |
|     *storage = value;
 | |
|   }
 | |
|   // Caller is responsible for ensuring that |data| is:
 | |
|   //   1) Is at least NLA_HDRLEN long.
 | |
|   //   2) That *data when interpreted as a nlattr is internally consistent.
 | |
|   // (e.g. data.size() == NLA_ALIGN(nlattr.nla_len)
 | |
|   // and nla_len == NLA_HDRLEN + payload size
 | |
|   explicit NL80211Attr(const std::vector<uint8_t>& data) {
 | |
|     data_ = data;
 | |
|   }
 | |
| 
 | |
|   ~NL80211Attr() override = default;
 | |
| 
 | |
|   bool IsValid() const override {
 | |
|     if (!BaseNL80211Attr::IsValid()) {
 | |
|       return false;
 | |
|     }
 | |
|     // If BaseNL80211Attr::IsValid() == true, at least we have enough valid
 | |
|     // buffer for header.
 | |
|     const nlattr* header = reinterpret_cast<const nlattr*>(data_.data());
 | |
|     // Buffer size = header size +  payload size + padding size
 | |
|     // nla_len  =  header size + payload size
 | |
|     if (NLA_ALIGN(sizeof(T)) + NLA_HDRLEN != data_.size() ||
 | |
|         sizeof(T) + NLA_HDRLEN != header->nla_len ) {
 | |
|       return false;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   T GetValue() const {
 | |
|     return *reinterpret_cast<const T*>(data_.data() + NLA_HDRLEN);
 | |
|   }
 | |
| };  // class NL80211Attr for POD-types
 | |
| 
 | |
| template <>
 | |
| class NL80211Attr<std::vector<uint8_t>> : public BaseNL80211Attr {
 | |
|  public:
 | |
|   NL80211Attr(int id, const std::vector<uint8_t>& raw_buffer);
 | |
|   explicit NL80211Attr(const std::vector<uint8_t>& data);
 | |
|   ~NL80211Attr() override = default;
 | |
|   std::vector<uint8_t> GetValue() const;
 | |
| }; // class NL80211Attr for raw data
 | |
| 
 | |
| template <size_t N>
 | |
| class NL80211Attr<std::array<uint8_t, N>> : public BaseNL80211Attr {
 | |
|  public:
 | |
|   NL80211Attr(int id, const std::array<uint8_t, N>& raw_buffer)
 | |
|       : BaseNL80211Attr(
 | |
|           id, std::vector<uint8_t>(raw_buffer.begin(), raw_buffer.end())) {}
 | |
|   explicit NL80211Attr(const std::vector<uint8_t>& data) {
 | |
|     data_ = data;
 | |
|   }
 | |
|   ~NL80211Attr() override = default;
 | |
|   std::array<uint8_t, N> GetValue() const {
 | |
|     std::array<uint8_t, N> arr;
 | |
|     std::copy_n(data_.data() + NLA_HDRLEN, N, arr.begin());
 | |
|     return arr;
 | |
|   }
 | |
| }; // class NL80211Attr for fixed size array
 | |
| 
 | |
| template <>
 | |
| class NL80211Attr<std::string> : public BaseNL80211Attr {
 | |
|  public:
 | |
|   NL80211Attr(int id, const std::string& str);
 | |
|   // We parse string attribute buffer in the same way kernel does.
 | |
|   // All trailing zeros are trimmed when retrieving a std::string from
 | |
|   // byte array.
 | |
|   explicit NL80211Attr(const std::vector<uint8_t>& data);
 | |
|   ~NL80211Attr() override = default;
 | |
|   std::string GetValue() const;
 | |
| };  // class NL80211Attr for string
 | |
| 
 | |
| // Force the compiler not to instantiate these templates because
 | |
| // they will be instantiated in nl80211_attribute.cpp file. This helps
 | |
| // reduce compile time as well as object file size.
 | |
| extern template class NL80211Attr<uint8_t>;
 | |
| extern template class NL80211Attr<uint16_t>;
 | |
| extern template class NL80211Attr<uint32_t>;
 | |
| extern template class NL80211Attr<uint64_t>;
 | |
| extern template class NL80211Attr<std::vector<uint8_t>>;
 | |
| extern template class NL80211Attr<std::string>;
 | |
| 
 | |
| class NL80211NestedAttr : public BaseNL80211Attr {
 | |
|  public:
 | |
|   explicit NL80211NestedAttr(int id);
 | |
|   explicit NL80211NestedAttr(const std::vector<uint8_t>& data);
 | |
|   ~NL80211NestedAttr() override = default;
 | |
| 
 | |
|   void AddAttribute(const BaseNL80211Attr& attribute);
 | |
|   // For NLA_FLAG attribute
 | |
|   void AddFlagAttribute(int attribute_id);
 | |
|   bool HasAttribute(int id) const;
 | |
| 
 | |
|   // Access an attribute nested within |this|.
 | |
|   // The result is returned by writing the attribute object to |*attribute|.
 | |
|   // Deeper nested attributes are not included. This means if A is nested within
 | |
|   // |this|, and B is nested within A, this function can't be used to access B.
 | |
|   // The reason is that we may have multiple attributes having the same
 | |
|   // attribute id, nested within different level of |this|.
 | |
|   bool GetAttribute(int id, NL80211NestedAttr* attribute) const;
 | |
| 
 | |
|   template <typename T>
 | |
|   bool GetAttributeValue(int id, T* value) const {
 | |
|     std::vector<uint8_t> empty_vec;
 | |
|     // All data in |attribute| created here will be overwritten by
 | |
|     // GetAttribute(). So we use an empty vector to initialize it,
 | |
|     // regardless of the fact that an empty buffer is not qualified
 | |
|     // for creating a valid attribute.
 | |
|     NL80211Attr<T> attribute(empty_vec);
 | |
|     if (!GetAttribute(id, &attribute)) {
 | |
|       return false;
 | |
|     }
 | |
|     *value = attribute.GetValue();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Some of the nested attribute contains a list of same type sub-attributes.
 | |
|   // This function retrieves a vector of attribute values from a nested
 | |
|   // attribute.
 | |
|   //
 | |
|   // This is for both correctness and performance reasons: Refer to
 | |
|   // GetListOfAttributes().
 | |
|   //
 | |
|   // Returns true on success.
 | |
|   template <typename T>
 | |
|   bool GetListOfAttributeValues(std::vector<T>* value) const {
 | |
|     const uint8_t* ptr = data_.data() + NLA_HDRLEN;
 | |
|     const uint8_t* end_ptr = data_.data() + data_.size();
 | |
|     std::vector<T> attr_list;
 | |
|     while (ptr + NLA_HDRLEN <= end_ptr) {
 | |
|       const nlattr* header = reinterpret_cast<const nlattr*>(ptr);
 | |
|       if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) {
 | |
|         LOG(ERROR) << "Failed to get list of attributes: invalid nla_len.";
 | |
|         return false;
 | |
|       }
 | |
|       NL80211Attr<T> attribute(std::vector<uint8_t>(
 | |
|           ptr,
 | |
|           ptr + NLA_ALIGN(header->nla_len)));
 | |
|       if (!attribute.IsValid()) {
 | |
|         return false;
 | |
|       }
 | |
|       attr_list.emplace_back(attribute.GetValue());
 | |
|       ptr += NLA_ALIGN(header->nla_len);
 | |
|     }
 | |
|     *value = std::move(attr_list);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Some of the nested attribute contains a list of same type sub-attributes.
 | |
|   // This function retrieves a vector of attributes from a nested
 | |
|   // attribute.
 | |
|   //
 | |
|   // This is for both correctness and performance reasons:
 | |
|   // Correctness reason:
 | |
|   // These sub-attributes have attribute id from '0 to n' or '1 to n'.
 | |
|   // There is no document defining what the start index should be.
 | |
|   //
 | |
|   // Performance reson:
 | |
|   // Calling GetAttribute() from '0 to n' results a n^2 time complexity.
 | |
|   // This function get a list of attributes in one pass.
 | |
|   //
 | |
|   // Returns true on success.
 | |
|   template <typename T>
 | |
|   bool GetListOfAttributes(std::vector<NL80211Attr<T>>* value) const {
 | |
|     const uint8_t* ptr = data_.data() + NLA_HDRLEN;
 | |
|     const uint8_t* end_ptr = data_.data() + data_.size();
 | |
|     std::vector<NL80211Attr<T>> attr_list;
 | |
|     while (ptr + NLA_HDRLEN <= end_ptr) {
 | |
|       const nlattr* header = reinterpret_cast<const nlattr*>(ptr);
 | |
|       if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) {
 | |
|         LOG(ERROR) << "Failed to get list of attributes: invalid nla_len.";
 | |
|         return false;
 | |
|       }
 | |
|       NL80211Attr<T> attribute(std::vector<uint8_t>(
 | |
|           ptr,
 | |
|           ptr + NLA_ALIGN(header->nla_len)));
 | |
|       if (!attribute.IsValid()) {
 | |
|         return false;
 | |
|       }
 | |
|       attr_list.emplace_back(attribute);
 | |
|       ptr += NLA_ALIGN(header->nla_len);
 | |
|     }
 | |
|     *value = std::move(attr_list);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // This is similar to |GetListOfAttributeValues|, but for the cases where all
 | |
|   // the sub-attributes are nested attributes.
 | |
|   bool GetListOfNestedAttributes(std::vector<NL80211NestedAttr>* value) const;
 | |
| 
 | |
|   template <typename T>
 | |
|   bool GetAttribute(int id, NL80211Attr<T>* attribute) const {
 | |
|     uint8_t* start = nullptr;
 | |
|     uint8_t* end = nullptr;
 | |
|     if (!BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN,
 | |
|                                            data_.size() - NLA_HDRLEN,
 | |
|                                            id, &start, &end) ||
 | |
|         start == nullptr ||
 | |
|         end == nullptr) {
 | |
|       return false;
 | |
|     }
 | |
|     *attribute = NL80211Attr<T>(std::vector<uint8_t>(start, end));
 | |
|     if (!attribute->IsValid()) {
 | |
|       return false;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   void DebugLog() const;
 | |
| 
 | |
| };
 | |
| 
 | |
| }  // namespace wificond
 | |
| }  // namespace android
 | |
| 
 | |
| #endif  // WIFICOND_NET_NL80211_ATTRIBUTE_H_
 |