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