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
30namespace android {
31namespace wificond {
32
33class 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
59template <typename T>
60class NL80211Attr : public BaseNL80211Attr {
61 public:
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
75  explicit NL80211Attr(const std::vector<uint8_t>& data) {
76    data_ = data;
77  }
78
79  ~NL80211Attr() override = default;
80
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
97  T GetValue() const {
98    return *reinterpret_cast<const T*>(data_.data() + NLA_HDRLEN);
99  }
100};  // class NL80211Attr for POD-types
101
102template <>
103class 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
111template <>
112class 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.
126extern template class NL80211Attr<uint8_t>;
127extern template class NL80211Attr<uint16_t>;
128extern template class NL80211Attr<uint32_t>;
129extern template class NL80211Attr<uint64_t>;
130extern template class NL80211Attr<std::vector<uint8_t>>;
131extern template class NL80211Attr<std::string>;
132
133class 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>
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>
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>
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