/* * 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 #include #include #include #include #include #include namespace android { namespace wificond { class BaseNL80211Attr { public: BaseNL80211Attr(int id, const std::vector& raw_buffer); virtual ~BaseNL80211Attr() = default; const std::vector& 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 data_; }; template class NL80211Attr : public BaseNL80211Attr { public: NL80211Attr(int id, T value) { static_assert( std::is_integral::value, "Failed to create NL80211Attr class with non-integral type"); InitHeaderAndResize(id, sizeof(T)); T* storage = reinterpret_cast(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& 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(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(data_.data() + NLA_HDRLEN); } }; // class NL80211Attr for POD-types template <> class NL80211Attr> : public BaseNL80211Attr { public: NL80211Attr(int id, const std::vector& raw_buffer); explicit NL80211Attr(const std::vector& data); ~NL80211Attr() override = default; std::vector GetValue() const; }; // class NL80211Attr for raw data template class NL80211Attr> : public BaseNL80211Attr { public: NL80211Attr(int id, const std::array& raw_buffer) : BaseNL80211Attr( id, std::vector(raw_buffer.begin(), raw_buffer.end())) {} explicit NL80211Attr(const std::vector& data) { data_ = data; } ~NL80211Attr() override = default; std::array GetValue() const { std::array arr; std::copy_n(data_.data() + NLA_HDRLEN, N, arr.begin()); return arr; } }; // class NL80211Attr for fixed size array template <> class NL80211Attr : 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& 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; extern template class NL80211Attr; extern template class NL80211Attr; extern template class NL80211Attr; extern template class NL80211Attr>; extern template class NL80211Attr; class NL80211NestedAttr : public BaseNL80211Attr { public: explicit NL80211NestedAttr(int id); explicit NL80211NestedAttr(const std::vector& 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 bool GetAttributeValue(int id, T* value) const { std::vector 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 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 bool GetListOfAttributeValues(std::vector* value) const { const uint8_t* ptr = data_.data() + NLA_HDRLEN; const uint8_t* end_ptr = data_.data() + data_.size(); std::vector attr_list; while (ptr + NLA_HDRLEN <= end_ptr) { const nlattr* header = reinterpret_cast(ptr); if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { LOG(ERROR) << "Failed to get list of attributes: invalid nla_len."; return false; } NL80211Attr attribute(std::vector( 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 bool GetListOfAttributes(std::vector>* value) const { const uint8_t* ptr = data_.data() + NLA_HDRLEN; const uint8_t* end_ptr = data_.data() + data_.size(); std::vector> attr_list; while (ptr + NLA_HDRLEN <= end_ptr) { const nlattr* header = reinterpret_cast(ptr); if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { LOG(ERROR) << "Failed to get list of attributes: invalid nla_len."; return false; } NL80211Attr attribute(std::vector( 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* value) const; template bool GetAttribute(int id, NL80211Attr* 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(std::vector(start, end)); if (!attribute->IsValid()) { return false; } return true; } void DebugLog() const; }; } // namespace wificond } // namespace android #endif // WIFICOND_NET_NL80211_ATTRIBUTE_H_