Home | History | Annotate | Download | only in wifi
      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;
     50 static string ObjectID(WiFiEndpoint* w) { return "(wifi_endpoint)"; }
     51 }
     52 
     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 
     98 WiFiEndpoint::~WiFiEndpoint() {}
     99 
    100 void WiFiEndpoint::Start() {
    101   supplicant_bss_proxy_.reset(
    102       control_interface_->CreateSupplicantBSSProxy(this, rpc_id_));
    103 }
    104 
    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 
    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 
    148 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
    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 
    191 const vector<uint8_t>& WiFiEndpoint::ssid() const {
    192   return ssid_;
    193 }
    194 
    195 const string& WiFiEndpoint::ssid_string() const {
    196   return ssid_string_;
    197 }
    198 
    199 const string& WiFiEndpoint::ssid_hex() const {
    200   return ssid_hex_;
    201 }
    202 
    203 const string& WiFiEndpoint::bssid_string() const {
    204   return bssid_string_;
    205 }
    206 
    207 const string& WiFiEndpoint::bssid_hex() const {
    208   return bssid_hex_;
    209 }
    210 
    211 const string& WiFiEndpoint::country_code() const {
    212   return country_code_;
    213 }
    214 
    215 const WiFiRefPtr& WiFiEndpoint::device() const {
    216   return device_;
    217 }
    218 
    219 int16_t WiFiEndpoint::signal_strength() const {
    220   return signal_strength_;
    221 }
    222 
    223 uint16_t WiFiEndpoint::frequency() const {
    224   return frequency_;
    225 }
    226 
    227 uint16_t WiFiEndpoint::physical_mode() const {
    228   return physical_mode_;
    229 }
    230 
    231 const string& WiFiEndpoint::network_mode() const {
    232   return network_mode_;
    233 }
    234 
    235 const string& WiFiEndpoint::security_mode() const {
    236   return security_mode_;
    237 }
    238 
    239 bool WiFiEndpoint::ieee80211w_required() const {
    240   return ieee80211w_required_;
    241 }
    242 
    243 bool WiFiEndpoint::has_rsn_property() const {
    244   return has_rsn_property_;
    245 }
    246 
    247 bool WiFiEndpoint::has_wpa_property() const {
    248   return has_wpa_property_;
    249 }
    250 
    251 bool WiFiEndpoint::has_tethering_signature() const {
    252   return has_tethering_signature_;
    253 }
    254 
    255 // static
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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