1 //
2 // Copyright (C) 2012 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 "shill/wifi/wifi_endpoint.h"
18
19 #include <algorithm>
20
21 #include <base/stl_util.h>
22 #include <base/strings/stringprintf.h>
23 #include <base/strings/string_number_conversions.h>
24 #include <base/strings/string_util.h>
25 #if defined(__ANDROID__)
26 #include <dbus/service_constants.h>
27 #else
28 #include <chromeos/dbus/service_constants.h>
29 #endif // __ANDROID__
30
31 #include "shill/control_interface.h"
32 #include "shill/logging.h"
33 #include "shill/metrics.h"
34 #include "shill/net/ieee80211.h"
35 #include "shill/supplicant/supplicant_bss_proxy_interface.h"
36 #include "shill/supplicant/wpa_supplicant.h"
37 #include "shill/tethering.h"
38 #include "shill/wifi/wifi.h"
39
40 using base::StringPrintf;
41 using std::map;
42 using std::set;
43 using std::string;
44 using std::vector;
45
46 namespace shill {
47
48 namespace Logging {
49 static auto kModuleLogScope = ScopeLogger::kWiFi;
ObjectID(WiFiEndpoint * w)50 static string ObjectID(WiFiEndpoint* w) { return "(wifi_endpoint)"; }
51 }
52
WiFiEndpoint(ControlInterface * control_interface,const WiFiRefPtr & device,const string & rpc_id,const KeyValueStore & properties)53 WiFiEndpoint::WiFiEndpoint(ControlInterface* control_interface,
54 const WiFiRefPtr& device,
55 const string& rpc_id,
56 const KeyValueStore& properties)
57 : frequency_(0),
58 physical_mode_(Metrics::kWiFiNetworkPhyModeUndef),
59 ieee80211w_required_(false),
60 control_interface_(control_interface),
61 device_(device),
62 rpc_id_(rpc_id) {
63 ssid_ = properties.GetUint8s(WPASupplicant::kBSSPropertySSID);
64 bssid_ = properties.GetUint8s(WPASupplicant::kBSSPropertyBSSID);
65 signal_strength_ = properties.GetInt16(WPASupplicant::kBSSPropertySignal);
66 if (properties.ContainsUint16(WPASupplicant::kBSSPropertyFrequency)) {
67 frequency_ = properties.GetUint16(WPASupplicant::kBSSPropertyFrequency);
68 }
69
70 Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
71 if (!ParseIEs(properties, &phy_mode, &vendor_information_,
72 &ieee80211w_required_, &country_code_)) {
73 phy_mode = DeterminePhyModeFromFrequency(properties, frequency_);
74 }
75 physical_mode_ = phy_mode;
76
77 network_mode_ = ParseMode(
78 properties.GetString(WPASupplicant::kBSSPropertyMode));
79 set_security_mode(ParseSecurity(properties, &security_flags_));
80 has_rsn_property_ =
81 properties.ContainsKeyValueStore(WPASupplicant::kPropertyRSN);
82 has_wpa_property_ =
83 properties.ContainsKeyValueStore(WPASupplicant::kPropertyWPA);
84
85 if (network_mode_.empty()) {
86 // XXX log error?
87 }
88
89 ssid_string_ = string(ssid_.begin(), ssid_.end());
90 WiFi::SanitizeSSID(&ssid_string_);
91 ssid_hex_ = base::HexEncode(&(*ssid_.begin()), ssid_.size());
92 bssid_string_ = Device::MakeStringFromHardwareAddress(bssid_);
93 bssid_hex_ = base::HexEncode(&(*bssid_.begin()), bssid_.size());
94
95 CheckForTetheringSignature();
96 }
97
~WiFiEndpoint()98 WiFiEndpoint::~WiFiEndpoint() {}
99
Start()100 void WiFiEndpoint::Start() {
101 supplicant_bss_proxy_.reset(
102 control_interface_->CreateSupplicantBSSProxy(this, rpc_id_));
103 }
104
PropertiesChanged(const KeyValueStore & properties)105 void WiFiEndpoint::PropertiesChanged(const KeyValueStore& properties) {
106 SLOG(this, 2) << __func__;
107 bool should_notify = false;
108 if (properties.ContainsInt16(WPASupplicant::kBSSPropertySignal)) {
109 signal_strength_ = properties.GetInt16(WPASupplicant::kBSSPropertySignal);
110 should_notify = true;
111 }
112
113 if (properties.ContainsString(WPASupplicant::kBSSPropertyMode)) {
114 string new_mode =
115 ParseMode(properties.GetString(WPASupplicant::kBSSPropertyMode));
116 if (new_mode != network_mode_) {
117 network_mode_ = new_mode;
118 SLOG(this, 2) << "WiFiEndpoint " << bssid_string_ << " mode is now "
119 << network_mode_;
120 should_notify = true;
121 }
122 }
123
124 const char* new_security_mode = ParseSecurity(properties, &security_flags_);
125 if (new_security_mode != security_mode()) {
126 set_security_mode(new_security_mode);
127 SLOG(this, 2) << "WiFiEndpoint " << bssid_string_ << " security is now "
128 << security_mode();
129 should_notify = true;
130 }
131
132 if (should_notify) {
133 device_->NotifyEndpointChanged(this);
134 }
135 }
136
UpdateSignalStrength(int16_t strength)137 void WiFiEndpoint::UpdateSignalStrength(int16_t strength) {
138 if (signal_strength_ == strength) {
139 return;
140 }
141
142 SLOG(this, 2) << __func__ << ": signal strength "
143 << signal_strength_ << " -> " << strength;
144 signal_strength_ = strength;
145 device_->NotifyEndpointChanged(this);
146 }
147
GetVendorInformation() const148 map<string, string> WiFiEndpoint::GetVendorInformation() const {
149 map<string, string> vendor_information;
150 if (!vendor_information_.wps_manufacturer.empty()) {
151 vendor_information[kVendorWPSManufacturerProperty] =
152 vendor_information_.wps_manufacturer;
153 }
154 if (!vendor_information_.wps_model_name.empty()) {
155 vendor_information[kVendorWPSModelNameProperty] =
156 vendor_information_.wps_model_name;
157 }
158 if (!vendor_information_.wps_model_number.empty()) {
159 vendor_information[kVendorWPSModelNumberProperty] =
160 vendor_information_.wps_model_number;
161 }
162 if (!vendor_information_.wps_device_name.empty()) {
163 vendor_information[kVendorWPSDeviceNameProperty] =
164 vendor_information_.wps_device_name;
165 }
166 if (!vendor_information_.oui_set.empty()) {
167 vector<string> oui_vector;
168 for (auto oui : vendor_information_.oui_set) {
169 oui_vector.push_back(
170 StringPrintf("%02x-%02x-%02x",
171 oui >> 16, (oui >> 8) & 0xff, oui & 0xff));
172 }
173 vendor_information[kVendorOUIListProperty] =
174 base::JoinString(oui_vector, " ");
175 }
176 return vendor_information;
177 }
178
179 // static
ModeStringToUint(const string & mode_string)180 uint32_t WiFiEndpoint::ModeStringToUint(const string& mode_string) {
181 if (mode_string == kModeManaged)
182 return WPASupplicant::kNetworkModeInfrastructureInt;
183 else if (mode_string == kModeAdhoc)
184 return WPASupplicant::kNetworkModeAdHocInt;
185 else
186 NOTIMPLEMENTED() << "Shill dos not support " << mode_string
187 << " mode at this time.";
188 return 0;
189 }
190
ssid() const191 const vector<uint8_t>& WiFiEndpoint::ssid() const {
192 return ssid_;
193 }
194
ssid_string() const195 const string& WiFiEndpoint::ssid_string() const {
196 return ssid_string_;
197 }
198
ssid_hex() const199 const string& WiFiEndpoint::ssid_hex() const {
200 return ssid_hex_;
201 }
202
bssid_string() const203 const string& WiFiEndpoint::bssid_string() const {
204 return bssid_string_;
205 }
206
bssid_hex() const207 const string& WiFiEndpoint::bssid_hex() const {
208 return bssid_hex_;
209 }
210
country_code() const211 const string& WiFiEndpoint::country_code() const {
212 return country_code_;
213 }
214
device() const215 const WiFiRefPtr& WiFiEndpoint::device() const {
216 return device_;
217 }
218
signal_strength() const219 int16_t WiFiEndpoint::signal_strength() const {
220 return signal_strength_;
221 }
222
frequency() const223 uint16_t WiFiEndpoint::frequency() const {
224 return frequency_;
225 }
226
physical_mode() const227 uint16_t WiFiEndpoint::physical_mode() const {
228 return physical_mode_;
229 }
230
network_mode() const231 const string& WiFiEndpoint::network_mode() const {
232 return network_mode_;
233 }
234
security_mode() const235 const string& WiFiEndpoint::security_mode() const {
236 return security_mode_;
237 }
238
ieee80211w_required() const239 bool WiFiEndpoint::ieee80211w_required() const {
240 return ieee80211w_required_;
241 }
242
has_rsn_property() const243 bool WiFiEndpoint::has_rsn_property() const {
244 return has_rsn_property_;
245 }
246
has_wpa_property() const247 bool WiFiEndpoint::has_wpa_property() const {
248 return has_wpa_property_;
249 }
250
has_tethering_signature() const251 bool WiFiEndpoint::has_tethering_signature() const {
252 return has_tethering_signature_;
253 }
254
255 // static
MakeOpenEndpoint(ControlInterface * control_interface,const WiFiRefPtr & wifi,const string & ssid,const string & bssid,const string & network_mode,uint16_t frequency,int16_t signal_dbm)256 WiFiEndpoint* WiFiEndpoint::MakeOpenEndpoint(
257 ControlInterface* control_interface,
258 const WiFiRefPtr& wifi,
259 const string& ssid,
260 const string& bssid,
261 const string& network_mode,
262 uint16_t frequency,
263 int16_t signal_dbm) {
264 return MakeEndpoint(control_interface, wifi, ssid, bssid, network_mode,
265 frequency, signal_dbm, false, false);
266 }
267
268
269 // static
MakeEndpoint(ControlInterface * control_interface,const WiFiRefPtr & wifi,const string & ssid,const string & bssid,const string & network_mode,uint16_t frequency,int16_t signal_dbm,bool has_wpa_property,bool has_rsn_property)270 WiFiEndpoint* WiFiEndpoint::MakeEndpoint(ControlInterface* control_interface,
271 const WiFiRefPtr& wifi,
272 const string& ssid,
273 const string& bssid,
274 const string& network_mode,
275 uint16_t frequency,
276 int16_t signal_dbm,
277 bool has_wpa_property,
278 bool has_rsn_property) {
279 KeyValueStore args;
280
281 args.SetUint8s(WPASupplicant::kBSSPropertySSID,
282 vector<uint8_t>(ssid.begin(), ssid.end()));
283
284 vector<uint8_t> bssid_bytes =
285 Device::MakeHardwareAddressFromString(bssid);
286 args.SetUint8s(WPASupplicant::kBSSPropertyBSSID, bssid_bytes);
287
288 args.SetInt16(WPASupplicant::kBSSPropertySignal, signal_dbm);
289 args.SetUint16(WPASupplicant::kBSSPropertyFrequency, frequency);
290 args.SetString(WPASupplicant::kBSSPropertyMode, network_mode);
291
292 if (has_wpa_property) {
293 KeyValueStore empty_args;
294 args.SetKeyValueStore(WPASupplicant::kPropertyWPA, empty_args);
295 }
296 if (has_rsn_property) {
297 KeyValueStore empty_args;
298 args.SetKeyValueStore(WPASupplicant::kPropertyRSN, empty_args);
299 }
300
301 return new WiFiEndpoint(
302 control_interface, wifi, bssid, args); // |bssid| fakes an RPC ID
303 }
304
305 // static
ParseMode(const string & mode_string)306 const char* WiFiEndpoint::ParseMode(const string& mode_string) {
307 if (mode_string == WPASupplicant::kNetworkModeInfrastructure) {
308 return kModeManaged;
309 } else if (mode_string == WPASupplicant::kNetworkModeAdHoc) {
310 return kModeAdhoc;
311 } else if (mode_string == WPASupplicant::kNetworkModeAccessPoint) {
312 NOTREACHED() << "Shill does not support AP mode at this time.";
313 return nullptr;
314 } else {
315 NOTREACHED() << "Unknown WiFi endpoint mode!";
316 return nullptr;
317 }
318 }
319
320 // static
ParseSecurity(const KeyValueStore & properties,SecurityFlags * flags)321 const char* WiFiEndpoint::ParseSecurity(
322 const KeyValueStore& properties, SecurityFlags* flags) {
323 if (properties.ContainsKeyValueStore(WPASupplicant::kPropertyRSN)) {
324 KeyValueStore rsn_properties =
325 properties.GetKeyValueStore(WPASupplicant::kPropertyRSN);
326 set<KeyManagement> key_management;
327 ParseKeyManagementMethods(rsn_properties, &key_management);
328 flags->rsn_8021x = ContainsKey(key_management, kKeyManagement802_1x);
329 flags->rsn_psk = ContainsKey(key_management, kKeyManagementPSK);
330 }
331
332 if (properties.ContainsKeyValueStore(WPASupplicant::kPropertyWPA)) {
333 KeyValueStore rsn_properties =
334 properties.GetKeyValueStore(WPASupplicant::kPropertyWPA);
335 set<KeyManagement> key_management;
336 ParseKeyManagementMethods(rsn_properties, &key_management);
337 flags->wpa_8021x = ContainsKey(key_management, kKeyManagement802_1x);
338 flags->wpa_psk = ContainsKey(key_management, kKeyManagementPSK);
339 }
340
341 if (properties.ContainsBool(WPASupplicant::kPropertyPrivacy)) {
342 flags->privacy = properties.GetBool(WPASupplicant::kPropertyPrivacy);
343 }
344
345 if (flags->rsn_8021x || flags->wpa_8021x) {
346 return kSecurity8021x;
347 } else if (flags->rsn_psk) {
348 return kSecurityRsn;
349 } else if (flags->wpa_psk) {
350 return kSecurityWpa;
351 } else if (flags->privacy) {
352 return kSecurityWep;
353 } else {
354 return kSecurityNone;
355 }
356 }
357
358 // static
ParseKeyManagementMethods(const KeyValueStore & security_method_properties,set<KeyManagement> * key_management_methods)359 void WiFiEndpoint::ParseKeyManagementMethods(
360 const KeyValueStore& security_method_properties,
361 set<KeyManagement>* key_management_methods) {
362 if (!security_method_properties.ContainsStrings(
363 WPASupplicant::kSecurityMethodPropertyKeyManagement)) {
364 return;
365 }
366
367 const vector<string> key_management_vec =
368 security_method_properties.GetStrings(
369 WPASupplicant::kSecurityMethodPropertyKeyManagement);
370
371 for (const auto& method : key_management_vec) {
372 if (base::EndsWith(method, WPASupplicant::kKeyManagementMethodSuffixEAP,
373 base::CompareCase::SENSITIVE)) {
374 key_management_methods->insert(kKeyManagement802_1x);
375 } else if (base::EndsWith(method,
376 WPASupplicant::kKeyManagementMethodSuffixPSK,
377 base::CompareCase::SENSITIVE)) {
378 key_management_methods->insert(kKeyManagementPSK);
379 }
380 }
381 }
382
383 // static
DeterminePhyModeFromFrequency(const KeyValueStore & properties,uint16_t frequency)384 Metrics::WiFiNetworkPhyMode WiFiEndpoint::DeterminePhyModeFromFrequency(
385 const KeyValueStore& properties, uint16_t frequency) {
386 uint32_t max_rate = 0;
387 if (properties.ContainsUint32s(WPASupplicant::kBSSPropertyRates)) {
388 vector<uint32_t> rates =
389 properties.GetUint32s(WPASupplicant::kBSSPropertyRates);
390 if (rates.size() > 0) {
391 max_rate = rates[0]; // Rates are sorted in descending order
392 }
393 }
394
395 Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
396 if (frequency < 3000) {
397 // 2.4GHz legacy, check for tx rate for 11b-only
398 // (note 22M is valid)
399 if (max_rate < 24000000)
400 phy_mode = Metrics::kWiFiNetworkPhyMode11b;
401 else
402 phy_mode = Metrics::kWiFiNetworkPhyMode11g;
403 } else {
404 phy_mode = Metrics::kWiFiNetworkPhyMode11a;
405 }
406
407 return phy_mode;
408 }
409
410 // static
ParseIEs(const KeyValueStore & properties,Metrics::WiFiNetworkPhyMode * phy_mode,VendorInformation * vendor_information,bool * ieee80211w_required,string * country_code)411 bool WiFiEndpoint::ParseIEs(
412 const KeyValueStore& properties,
413 Metrics::WiFiNetworkPhyMode* phy_mode,
414 VendorInformation* vendor_information,
415 bool* ieee80211w_required, string* country_code) {
416
417 if (!properties.ContainsUint8s(WPASupplicant::kBSSPropertyIEs)) {
418 SLOG(nullptr, 2) << __func__ << ": No IE property in BSS.";
419 return false;
420 }
421 vector<uint8_t> ies = properties.GetUint8s(WPASupplicant::kBSSPropertyIEs);
422
423 // Format of an information element:
424 // 1 1 1 - 252
425 // +------+--------+----------------+
426 // | Type | Length | Data |
427 // +------+--------+----------------+
428 *phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
429 bool found_ht = false;
430 bool found_vht = false;
431 bool found_erp = false;
432 int ie_len = 0;
433 vector<uint8_t>::iterator it;
434 for (it = ies.begin();
435 std::distance(it, ies.end()) > 1; // Ensure Length field is within PDU.
436 it += ie_len) {
437 ie_len = 2 + *(it + 1);
438 if (std::distance(it, ies.end()) < ie_len) {
439 LOG(ERROR) << __func__ << ": IE extends past containing PDU.";
440 break;
441 }
442 switch (*it) {
443 case IEEE_80211::kElemIdCountry:
444 // Retrieve 2-character country code from the beginning of the element.
445 if (ie_len >= 4) {
446 *country_code = string(it + 2, it + 4);
447 }
448 case IEEE_80211::kElemIdErp:
449 found_erp = true;
450 break;
451 case IEEE_80211::kElemIdHTCap:
452 case IEEE_80211::kElemIdHTInfo:
453 found_ht = true;
454 break;
455 case IEEE_80211::kElemIdVHTCap:
456 case IEEE_80211::kElemIdVHTOperation:
457 found_vht = true;
458 break;
459 case IEEE_80211::kElemIdRSN:
460 ParseWPACapabilities(it + 2, it + ie_len, ieee80211w_required);
461 break;
462 case IEEE_80211::kElemIdVendor:
463 ParseVendorIE(it + 2, it + ie_len, vendor_information,
464 ieee80211w_required);
465 break;
466 }
467 }
468 if (found_vht) {
469 *phy_mode = Metrics::kWiFiNetworkPhyMode11ac;
470 } else if (found_ht) {
471 *phy_mode = Metrics::kWiFiNetworkPhyMode11n;
472 } else if (found_erp) {
473 *phy_mode = Metrics::kWiFiNetworkPhyMode11g;
474 } else {
475 return false;
476 }
477 return true;
478 }
479
480 // static
ParseWPACapabilities(vector<uint8_t>::const_iterator ie,vector<uint8_t>::const_iterator end,bool * ieee80211w_required)481 void WiFiEndpoint::ParseWPACapabilities(
482 vector<uint8_t>::const_iterator ie,
483 vector<uint8_t>::const_iterator end,
484 bool* ieee80211w_required) {
485 // Format of an RSN Information Element:
486 // 2 4
487 // +------+--------------------+
488 // | Type | Group Cipher Suite |
489 // +------+--------------------+
490 // 2 4 * pairwise count
491 // +-----------------------+---------------------+
492 // | Pairwise Cipher Count | Pairwise Ciphers... |
493 // +-----------------------+---------------------+
494 // 2 4 * authkey count
495 // +-----------------------+---------------------+
496 // | AuthKey Suite Count | AuthKey Suites... |
497 // +-----------------------+---------------------+
498 // 2
499 // +------------------+
500 // | RSN Capabilities |
501 // +------------------+
502 // 2 16 * pmkid count
503 // +------------------+-------------------+
504 // | PMKID Count | PMKIDs... |
505 // +------------------+-------------------+
506 // 4
507 // +-------------------------------+
508 // | Group Management Cipher Suite |
509 // +-------------------------------+
510 if (std::distance(ie, end) < IEEE_80211::kRSNIECipherCountOffset) {
511 return;
512 }
513 ie += IEEE_80211::kRSNIECipherCountOffset;
514
515 // Advance past the pairwise and authkey ciphers. Each is a little-endian
516 // cipher count followed by n * cipher_selector.
517 for (int i = 0; i < IEEE_80211::kRSNIENumCiphers; ++i) {
518 // Retrieve a little-endian cipher count.
519 if (std::distance(ie, end) < IEEE_80211::kRSNIECipherCountLen) {
520 return;
521 }
522 uint16_t cipher_count = *ie | (*(ie + 1) << 8);
523
524 // Skip over the cipher selectors.
525 int skip_length = IEEE_80211::kRSNIECipherCountLen +
526 cipher_count * IEEE_80211::kRSNIESelectorLen;
527 if (std::distance(ie, end) < skip_length) {
528 return;
529 }
530 ie += skip_length;
531 }
532
533 if (std::distance(ie, end) < IEEE_80211::kRSNIECapabilitiesLen) {
534 return;
535 }
536
537 // Retrieve a little-endian capabilities bitfield.
538 uint16_t capabilities = *ie | (*(ie + 1) << 8);
539
540 if (capabilities & IEEE_80211::kRSNCapabilityFrameProtectionRequired &&
541 ieee80211w_required) {
542 // Never set this value to false, since there may be multiple RSN
543 // information elements.
544 *ieee80211w_required = true;
545 }
546 }
547
548
549 // static
ParseVendorIE(vector<uint8_t>::const_iterator ie,vector<uint8_t>::const_iterator end,VendorInformation * vendor_information,bool * ieee80211w_required)550 void WiFiEndpoint::ParseVendorIE(vector<uint8_t>::const_iterator ie,
551 vector<uint8_t>::const_iterator end,
552 VendorInformation* vendor_information,
553 bool* ieee80211w_required) {
554 // Format of an vendor-specific information element (with type
555 // and length field for the IE removed by the caller):
556 // 3 1 1 - 248
557 // +------------+----------+----------------+
558 // | OUI | OUI Type | Data |
559 // +------------+----------+----------------+
560
561 if (std::distance(ie, end) < 4) {
562 LOG(ERROR) << __func__ << ": no room in IE for OUI and type field.";
563 return;
564 }
565 uint32_t oui = (*ie << 16) | (*(ie + 1) << 8) | *(ie + 2);
566 uint8_t oui_type = *(ie + 3);
567 ie += 4;
568
569 if (oui == IEEE_80211::kOUIVendorMicrosoft &&
570 oui_type == IEEE_80211::kOUIMicrosoftWPS) {
571 // Format of a WPS data element:
572 // 2 2
573 // +------+--------+----------------+
574 // | Type | Length | Data |
575 // +------+--------+----------------+
576 while (std::distance(ie, end) >= 4) {
577 int element_type = (*ie << 8) | *(ie + 1);
578 int element_length = (*(ie + 2) << 8) | *(ie + 3);
579 ie += 4;
580 if (std::distance(ie, end) < element_length) {
581 LOG(ERROR) << __func__ << ": WPS element extends past containing PDU.";
582 break;
583 }
584 string s(ie, ie + element_length);
585 if (base::IsStringASCII(s)) {
586 switch (element_type) {
587 case IEEE_80211::kWPSElementManufacturer:
588 vendor_information->wps_manufacturer = s;
589 break;
590 case IEEE_80211::kWPSElementModelName:
591 vendor_information->wps_model_name = s;
592 break;
593 case IEEE_80211::kWPSElementModelNumber:
594 vendor_information->wps_model_number = s;
595 break;
596 case IEEE_80211::kWPSElementDeviceName:
597 vendor_information->wps_device_name = s;
598 break;
599 }
600 }
601 ie += element_length;
602 }
603 } else if (oui == IEEE_80211::kOUIVendorMicrosoft &&
604 oui_type == IEEE_80211::kOUIMicrosoftWPA) {
605 ParseWPACapabilities(ie, end, ieee80211w_required);
606 } else if (oui != IEEE_80211::kOUIVendorEpigram &&
607 oui != IEEE_80211::kOUIVendorMicrosoft) {
608 vendor_information->oui_set.insert(oui);
609 }
610 }
611
CheckForTetheringSignature()612 void WiFiEndpoint::CheckForTetheringSignature() {
613 has_tethering_signature_ =
614 Tethering::IsAndroidBSSID(bssid_) ||
615 (Tethering::IsLocallyAdministeredBSSID(bssid_) &&
616 Tethering::HasIosOui(vendor_information_.oui_set));
617 }
618
619 } // namespace shill
620