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.

289 lines
10 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:
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_