You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
8.1 KiB
235 lines
8.1 KiB
/* |
|
* 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: |
|
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); |
|
|
|
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 <> |
|
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 value 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. |
|
// This function ignore all these fake attribute ids. |
|
// |
|
// Performance reson: |
|
// Calling GetAttributeValue() from '0 to n' results a n^2 time complexity. |
|
// This function get a list of attribute values in one pass. |
|
// |
|
// 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; |
|
} |
|
|
|
// 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_
|
|
|