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 #include "wificond/net/nl80211_attribute.h"
18 
19 using std::string;
20 using std::vector;
21 
22 namespace android {
23 namespace wificond {
24 
25 // Explicit instantiation
26 template class NL80211Attr<uint8_t>;
27 template class NL80211Attr<uint16_t>;
28 template class NL80211Attr<uint32_t>;
29 template class NL80211Attr<uint64_t>;
30 template class NL80211Attr<vector<uint8_t>>;
31 template class NL80211Attr<string>;
32 
33 // For BaseNL80211Attr
34 
BaseNL80211Attr(int id,const vector<uint8_t> & raw_buffer)35 BaseNL80211Attr::BaseNL80211Attr(int id,
36     const vector<uint8_t>& raw_buffer) {
37   size_t size = raw_buffer.size();
38   InitHeaderAndResize(id, size);
39   memcpy(data_.data() + NLA_HDRLEN, raw_buffer.data(), raw_buffer.size());
40 }
41 
InitHeaderAndResize(int attribute_id,int payload_length)42 void BaseNL80211Attr::InitHeaderAndResize(int attribute_id,
43                                           int payload_length) {
44   data_.resize(NLA_HDRLEN + NLA_ALIGN(payload_length), 0);
45   nlattr* header = reinterpret_cast<nlattr*>(data_.data());
46   header->nla_type = attribute_id;
47   header->nla_len = NLA_HDRLEN + payload_length;
48 }
49 
GetAttributeId() const50 int BaseNL80211Attr::GetAttributeId() const {
51   const nlattr* header = reinterpret_cast<const nlattr*>(data_.data());
52   return header->nla_type;
53 }
54 
IsValid() const55 bool BaseNL80211Attr::IsValid() const {
56   if (data_.size() < NLA_HDRLEN) {
57     return false;
58   }
59   const nlattr* header = reinterpret_cast<const nlattr*>(data_.data());
60   return NLA_ALIGN(header->nla_len) == data_.size();
61 }
62 
GetConstData() const63 const vector<uint8_t>& BaseNL80211Attr::GetConstData() const {
64   return data_;
65 }
66 
GetAttributeImpl(const uint8_t * buf,size_t len,int attr_id,uint8_t ** attr_start,uint8_t ** attr_end)67 bool BaseNL80211Attr::GetAttributeImpl(const uint8_t* buf,
68                                        size_t len,
69                                        int attr_id,
70                                        uint8_t** attr_start,
71                                        uint8_t** attr_end) {
72   // Skip the top level attribute header.
73   const uint8_t* ptr = buf;
74   const uint8_t* end_ptr = buf + len;
75   while (ptr + NLA_HDRLEN <= end_ptr) {
76     const nlattr* header = reinterpret_cast<const nlattr*>(ptr);
77     if (header->nla_type == attr_id) {
78       if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) {
79         LOG(ERROR) << "Failed to get attribute: broken nl80211 atrribute.";
80         return false;
81       }
82       if (attr_start != nullptr && attr_end != nullptr) {
83         *attr_start = const_cast<uint8_t*>(ptr);
84         *attr_end = const_cast<uint8_t*>(ptr + NLA_ALIGN(header->nla_len));
85       }
86       return true;
87     }
88     ptr += NLA_ALIGN(header->nla_len);
89   }
90   return false;
91 }
92 
93 
Merge(const BaseNL80211Attr & other_attr)94 bool BaseNL80211Attr::Merge(const BaseNL80211Attr& other_attr) {
95   if (!other_attr.IsValid()) {
96     LOG(ERROR) << "Can not merge invalid attribute";
97     return false;
98   }
99   if (GetAttributeId() != other_attr.GetAttributeId()) {
100     LOG(ERROR) << "Can not merge attributes with different ids";
101     return false;
102   }
103 
104   auto our_header = reinterpret_cast<nlattr*>(data_.data());
105   int our_len_without_padding = our_header->nla_len;
106   auto other_header =
107       reinterpret_cast<const nlattr*>(other_attr.GetConstData().data());
108   int other_len_without_padding = other_header->nla_len;
109   // Update the length to include the content of |other_attr|.
110   int total_len_without_padding =
111       our_len_without_padding + other_len_without_padding - NLA_HDRLEN;
112   our_header->nla_len = total_len_without_padding;
113 
114   // Remove padding 0s.
115   data_.resize(our_len_without_padding);
116   // Insert content of |other_attr|.
117   data_.insert(
118       data_.end(),
119       reinterpret_cast<const uint8_t*>(other_header) + NLA_HDRLEN,
120       reinterpret_cast<const uint8_t*>(other_header) +
121           other_len_without_padding);
122   // Add padding 0s.
123   data_.resize(NLA_ALIGN(total_len_without_padding), 0);
124   return true;
125 }
126 
127 // For NL80211Attr<std::vector<uint8_t>>
NL80211Attr(int id,const vector<uint8_t> & raw_buffer)128 NL80211Attr<vector<uint8_t>>::NL80211Attr(int id,
129     const vector<uint8_t>& raw_buffer) : BaseNL80211Attr(id, raw_buffer) {
130 }
131 
NL80211Attr(const vector<uint8_t> & data)132 NL80211Attr<vector<uint8_t>>::NL80211Attr(
133     const vector<uint8_t>& data) {
134   data_ = data;
135 }
136 
GetValue() const137 vector<uint8_t> NL80211Attr<vector<uint8_t>>::GetValue() const {
138   const nlattr* header = reinterpret_cast<const nlattr*>(data_.data());
139   return vector<uint8_t>(
140       data_.data() + NLA_HDRLEN,
141       data_.data() + header->nla_len);
142 }
143 
144 // For NL80211Attr<std::string>
NL80211Attr(int id,const string & str)145 NL80211Attr<string>::NL80211Attr(int id, const string& str) {
146   size_t size = str.size();
147   // This string is storaged as a null-terminated string.
148   // Buffer is initialized with 0s so we only need to make a space for
149   // the null terminator.
150   InitHeaderAndResize(id, size + 1);
151   char* storage = reinterpret_cast<char*>(data_.data() + NLA_HDRLEN);
152   str.copy(storage, size);
153 }
154 
NL80211Attr(const vector<uint8_t> & data)155 NL80211Attr<string>::NL80211Attr(const vector<uint8_t>& data) {
156   data_ = data;
157 }
158 
GetValue() const159 string NL80211Attr<string>::GetValue() const {
160   const nlattr* header = reinterpret_cast<const nlattr*>(data_.data());
161   size_t str_length = header->nla_len - NLA_HDRLEN;
162   // Remove trailing zeros.
163   while (str_length > 0 &&
164          *(data_.data() + NLA_HDRLEN + str_length - 1) == 0) {
165     str_length--;
166   }
167   return string(reinterpret_cast<const char*>(data_.data() + NLA_HDRLEN),
168                 str_length);
169 }
170 
171 // For NL80211NestedAttr
NL80211NestedAttr(int id)172 NL80211NestedAttr::NL80211NestedAttr(int id) {
173   InitHeaderAndResize(id, 0);
174 }
175 
NL80211NestedAttr(const vector<uint8_t> & data)176 NL80211NestedAttr::NL80211NestedAttr(const vector<uint8_t>& data) {
177   data_ = data;
178 }
179 
AddAttribute(const BaseNL80211Attr & attribute)180 void NL80211NestedAttr::AddAttribute(const BaseNL80211Attr& attribute) {
181   const vector<uint8_t>& append_data = attribute.GetConstData();
182   // Append the data of |attribute| to |this|.
183   data_.insert(data_.end(), append_data.begin(), append_data.end());
184   nlattr* header = reinterpret_cast<nlattr*>(data_.data());
185   // We don't need to worry about padding for nested attribute.
186   // Because as long as all sub attributes have padding, the payload is aligned.
187   header->nla_len += append_data.size();
188 }
189 
AddFlagAttribute(int attribute_id)190 void NL80211NestedAttr::AddFlagAttribute(int attribute_id) {
191   // We only need to append a header for flag attribute.
192   // Make space for the new attribute.
193   data_.resize(data_.size() + NLA_HDRLEN, 0);
194   nlattr* flag_header =
195       reinterpret_cast<nlattr*>(data_.data() + data_.size() - NLA_HDRLEN);
196   flag_header->nla_type = attribute_id;
197   flag_header->nla_len = NLA_HDRLEN;
198   nlattr* nl_header = reinterpret_cast<nlattr*>(data_.data());
199   nl_header->nla_len += NLA_HDRLEN;
200 }
201 
HasAttribute(int id) const202 bool NL80211NestedAttr::HasAttribute(int id) const {
203   return BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN,
204                                            data_.size() - NLA_HDRLEN,
205                                            id, nullptr, nullptr);
206 }
207 
GetAttribute(int id,NL80211NestedAttr * attribute) const208 bool NL80211NestedAttr::GetAttribute(int id,
209     NL80211NestedAttr* attribute) const {
210   uint8_t* start = nullptr;
211   uint8_t* end = nullptr;
212   if (!BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN,
213                                          data_.size() - NLA_HDRLEN,
214                                          id, &start, &end) ||
215       start == nullptr ||
216       end == nullptr) {
217     return false;
218   }
219   *attribute = NL80211NestedAttr(vector<uint8_t>(start, end));
220   if (!attribute->IsValid()) {
221     return false;
222   }
223   return true;
224 }
225 
GetListOfNestedAttributes(vector<NL80211NestedAttr> * value) const226 bool NL80211NestedAttr::GetListOfNestedAttributes(
227     vector<NL80211NestedAttr>* value) const {
228   const uint8_t* ptr = data_.data() + NLA_HDRLEN;
229   const uint8_t* end_ptr = data_.data() + data_.size();
230   vector<NL80211NestedAttr> nested_attr_list;
231   while (ptr + NLA_HDRLEN <= end_ptr) {
232     const nlattr* header = reinterpret_cast<const nlattr*>(ptr);
233     if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) {
234       LOG(ERROR) << "Failed to get list of nested attributes: invalid nla_len.";
235       return false;
236     }
237     nested_attr_list.emplace_back(
238         NL80211NestedAttr(vector<uint8_t>(ptr,
239                                           ptr + NLA_ALIGN(header->nla_len))));
240     if (!nested_attr_list.back().IsValid()) {
241       return false;
242     }
243     ptr += NLA_ALIGN(header->nla_len);
244   }
245   *value = std::move(nested_attr_list);
246   return true;
247 }
248 
249 
DebugLog() const250 void NL80211NestedAttr::DebugLog() const {
251   const uint8_t* ptr = data_.data() + NLA_HDRLEN;
252   const uint8_t* end_ptr = data_.data() + data_.size();
253   while (ptr + NLA_HDRLEN <= end_ptr) {
254     const nlattr* header = reinterpret_cast<const nlattr*>(ptr);
255     if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) {
256       LOG(ERROR) << "broken nl80211 atrribute.";
257       return;
258     }
259     LOG(INFO) << "Have attribute with nla_type=" << header->nla_type
260               << " and nla_len=" << header->nla_len;
261     if (header->nla_len == 0) {
262       LOG(ERROR) << "0 is a bad nla_len";
263       return;
264     }
265     ptr += NLA_ALIGN(header->nla_len);
266   }
267 }
268 
269 }  // namespace wificond
270 }  // namespace android
271