// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/wifi/wifi_endpoint.h" #include #include #include #include #include #if defined(__ANDROID__) #include #else #include #endif // __ANDROID__ #include "shill/control_interface.h" #include "shill/logging.h" #include "shill/metrics.h" #include "shill/net/ieee80211.h" #include "shill/supplicant/supplicant_bss_proxy_interface.h" #include "shill/supplicant/wpa_supplicant.h" #include "shill/tethering.h" #include "shill/wifi/wifi.h" using base::StringPrintf; using std::map; using std::set; using std::string; using std::vector; namespace shill { namespace Logging { static auto kModuleLogScope = ScopeLogger::kWiFi; static string ObjectID(WiFiEndpoint* w) { return "(wifi_endpoint)"; } } WiFiEndpoint::WiFiEndpoint(ControlInterface* control_interface, const WiFiRefPtr& device, const string& rpc_id, const KeyValueStore& properties) : frequency_(0), physical_mode_(Metrics::kWiFiNetworkPhyModeUndef), ieee80211w_required_(false), control_interface_(control_interface), device_(device), rpc_id_(rpc_id) { ssid_ = properties.GetUint8s(WPASupplicant::kBSSPropertySSID); bssid_ = properties.GetUint8s(WPASupplicant::kBSSPropertyBSSID); signal_strength_ = properties.GetInt16(WPASupplicant::kBSSPropertySignal); if (properties.ContainsUint16(WPASupplicant::kBSSPropertyFrequency)) { frequency_ = properties.GetUint16(WPASupplicant::kBSSPropertyFrequency); } Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef; if (!ParseIEs(properties, &phy_mode, &vendor_information_, &ieee80211w_required_, &country_code_)) { phy_mode = DeterminePhyModeFromFrequency(properties, frequency_); } physical_mode_ = phy_mode; network_mode_ = ParseMode( properties.GetString(WPASupplicant::kBSSPropertyMode)); set_security_mode(ParseSecurity(properties, &security_flags_)); has_rsn_property_ = properties.ContainsKeyValueStore(WPASupplicant::kPropertyRSN); has_wpa_property_ = properties.ContainsKeyValueStore(WPASupplicant::kPropertyWPA); if (network_mode_.empty()) { // XXX log error? } ssid_string_ = string(ssid_.begin(), ssid_.end()); WiFi::SanitizeSSID(&ssid_string_); ssid_hex_ = base::HexEncode(&(*ssid_.begin()), ssid_.size()); bssid_string_ = Device::MakeStringFromHardwareAddress(bssid_); bssid_hex_ = base::HexEncode(&(*bssid_.begin()), bssid_.size()); CheckForTetheringSignature(); } WiFiEndpoint::~WiFiEndpoint() {} void WiFiEndpoint::Start() { supplicant_bss_proxy_.reset( control_interface_->CreateSupplicantBSSProxy(this, rpc_id_)); } void WiFiEndpoint::PropertiesChanged(const KeyValueStore& properties) { SLOG(this, 2) << __func__; bool should_notify = false; if (properties.ContainsInt16(WPASupplicant::kBSSPropertySignal)) { signal_strength_ = properties.GetInt16(WPASupplicant::kBSSPropertySignal); should_notify = true; } if (properties.ContainsString(WPASupplicant::kBSSPropertyMode)) { string new_mode = ParseMode(properties.GetString(WPASupplicant::kBSSPropertyMode)); if (new_mode != network_mode_) { network_mode_ = new_mode; SLOG(this, 2) << "WiFiEndpoint " << bssid_string_ << " mode is now " << network_mode_; should_notify = true; } } const char* new_security_mode = ParseSecurity(properties, &security_flags_); if (new_security_mode != security_mode()) { set_security_mode(new_security_mode); SLOG(this, 2) << "WiFiEndpoint " << bssid_string_ << " security is now " << security_mode(); should_notify = true; } if (should_notify) { device_->NotifyEndpointChanged(this); } } void WiFiEndpoint::UpdateSignalStrength(int16_t strength) { if (signal_strength_ == strength) { return; } SLOG(this, 2) << __func__ << ": signal strength " << signal_strength_ << " -> " << strength; signal_strength_ = strength; device_->NotifyEndpointChanged(this); } map WiFiEndpoint::GetVendorInformation() const { map vendor_information; if (!vendor_information_.wps_manufacturer.empty()) { vendor_information[kVendorWPSManufacturerProperty] = vendor_information_.wps_manufacturer; } if (!vendor_information_.wps_model_name.empty()) { vendor_information[kVendorWPSModelNameProperty] = vendor_information_.wps_model_name; } if (!vendor_information_.wps_model_number.empty()) { vendor_information[kVendorWPSModelNumberProperty] = vendor_information_.wps_model_number; } if (!vendor_information_.wps_device_name.empty()) { vendor_information[kVendorWPSDeviceNameProperty] = vendor_information_.wps_device_name; } if (!vendor_information_.oui_set.empty()) { vector oui_vector; for (auto oui : vendor_information_.oui_set) { oui_vector.push_back( StringPrintf("%02x-%02x-%02x", oui >> 16, (oui >> 8) & 0xff, oui & 0xff)); } vendor_information[kVendorOUIListProperty] = base::JoinString(oui_vector, " "); } return vendor_information; } // static uint32_t WiFiEndpoint::ModeStringToUint(const string& mode_string) { if (mode_string == kModeManaged) return WPASupplicant::kNetworkModeInfrastructureInt; else if (mode_string == kModeAdhoc) return WPASupplicant::kNetworkModeAdHocInt; else NOTIMPLEMENTED() << "Shill dos not support " << mode_string << " mode at this time."; return 0; } const vector& WiFiEndpoint::ssid() const { return ssid_; } const string& WiFiEndpoint::ssid_string() const { return ssid_string_; } const string& WiFiEndpoint::ssid_hex() const { return ssid_hex_; } const string& WiFiEndpoint::bssid_string() const { return bssid_string_; } const string& WiFiEndpoint::bssid_hex() const { return bssid_hex_; } const string& WiFiEndpoint::country_code() const { return country_code_; } const WiFiRefPtr& WiFiEndpoint::device() const { return device_; } int16_t WiFiEndpoint::signal_strength() const { return signal_strength_; } uint16_t WiFiEndpoint::frequency() const { return frequency_; } uint16_t WiFiEndpoint::physical_mode() const { return physical_mode_; } const string& WiFiEndpoint::network_mode() const { return network_mode_; } const string& WiFiEndpoint::security_mode() const { return security_mode_; } bool WiFiEndpoint::ieee80211w_required() const { return ieee80211w_required_; } bool WiFiEndpoint::has_rsn_property() const { return has_rsn_property_; } bool WiFiEndpoint::has_wpa_property() const { return has_wpa_property_; } bool WiFiEndpoint::has_tethering_signature() const { return has_tethering_signature_; } // static WiFiEndpoint* WiFiEndpoint::MakeOpenEndpoint( ControlInterface* control_interface, const WiFiRefPtr& wifi, const string& ssid, const string& bssid, const string& network_mode, uint16_t frequency, int16_t signal_dbm) { return MakeEndpoint(control_interface, wifi, ssid, bssid, network_mode, frequency, signal_dbm, false, false); } // static WiFiEndpoint* WiFiEndpoint::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) { KeyValueStore args; args.SetUint8s(WPASupplicant::kBSSPropertySSID, vector(ssid.begin(), ssid.end())); vector bssid_bytes = Device::MakeHardwareAddressFromString(bssid); args.SetUint8s(WPASupplicant::kBSSPropertyBSSID, bssid_bytes); args.SetInt16(WPASupplicant::kBSSPropertySignal, signal_dbm); args.SetUint16(WPASupplicant::kBSSPropertyFrequency, frequency); args.SetString(WPASupplicant::kBSSPropertyMode, network_mode); if (has_wpa_property) { KeyValueStore empty_args; args.SetKeyValueStore(WPASupplicant::kPropertyWPA, empty_args); } if (has_rsn_property) { KeyValueStore empty_args; args.SetKeyValueStore(WPASupplicant::kPropertyRSN, empty_args); } return new WiFiEndpoint( control_interface, wifi, bssid, args); // |bssid| fakes an RPC ID } // static const char* WiFiEndpoint::ParseMode(const string& mode_string) { if (mode_string == WPASupplicant::kNetworkModeInfrastructure) { return kModeManaged; } else if (mode_string == WPASupplicant::kNetworkModeAdHoc) { return kModeAdhoc; } else if (mode_string == WPASupplicant::kNetworkModeAccessPoint) { NOTREACHED() << "Shill does not support AP mode at this time."; return nullptr; } else { NOTREACHED() << "Unknown WiFi endpoint mode!"; return nullptr; } } // static const char* WiFiEndpoint::ParseSecurity( const KeyValueStore& properties, SecurityFlags* flags) { if (properties.ContainsKeyValueStore(WPASupplicant::kPropertyRSN)) { KeyValueStore rsn_properties = properties.GetKeyValueStore(WPASupplicant::kPropertyRSN); set key_management; ParseKeyManagementMethods(rsn_properties, &key_management); flags->rsn_8021x = ContainsKey(key_management, kKeyManagement802_1x); flags->rsn_psk = ContainsKey(key_management, kKeyManagementPSK); } if (properties.ContainsKeyValueStore(WPASupplicant::kPropertyWPA)) { KeyValueStore rsn_properties = properties.GetKeyValueStore(WPASupplicant::kPropertyWPA); set key_management; ParseKeyManagementMethods(rsn_properties, &key_management); flags->wpa_8021x = ContainsKey(key_management, kKeyManagement802_1x); flags->wpa_psk = ContainsKey(key_management, kKeyManagementPSK); } if (properties.ContainsBool(WPASupplicant::kPropertyPrivacy)) { flags->privacy = properties.GetBool(WPASupplicant::kPropertyPrivacy); } if (flags->rsn_8021x || flags->wpa_8021x) { return kSecurity8021x; } else if (flags->rsn_psk) { return kSecurityRsn; } else if (flags->wpa_psk) { return kSecurityWpa; } else if (flags->privacy) { return kSecurityWep; } else { return kSecurityNone; } } // static void WiFiEndpoint::ParseKeyManagementMethods( const KeyValueStore& security_method_properties, set* key_management_methods) { if (!security_method_properties.ContainsStrings( WPASupplicant::kSecurityMethodPropertyKeyManagement)) { return; } const vector key_management_vec = security_method_properties.GetStrings( WPASupplicant::kSecurityMethodPropertyKeyManagement); for (const auto& method : key_management_vec) { if (base::EndsWith(method, WPASupplicant::kKeyManagementMethodSuffixEAP, base::CompareCase::SENSITIVE)) { key_management_methods->insert(kKeyManagement802_1x); } else if (base::EndsWith(method, WPASupplicant::kKeyManagementMethodSuffixPSK, base::CompareCase::SENSITIVE)) { key_management_methods->insert(kKeyManagementPSK); } } } // static Metrics::WiFiNetworkPhyMode WiFiEndpoint::DeterminePhyModeFromFrequency( const KeyValueStore& properties, uint16_t frequency) { uint32_t max_rate = 0; if (properties.ContainsUint32s(WPASupplicant::kBSSPropertyRates)) { vector rates = properties.GetUint32s(WPASupplicant::kBSSPropertyRates); if (rates.size() > 0) { max_rate = rates[0]; // Rates are sorted in descending order } } Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef; if (frequency < 3000) { // 2.4GHz legacy, check for tx rate for 11b-only // (note 22M is valid) if (max_rate < 24000000) phy_mode = Metrics::kWiFiNetworkPhyMode11b; else phy_mode = Metrics::kWiFiNetworkPhyMode11g; } else { phy_mode = Metrics::kWiFiNetworkPhyMode11a; } return phy_mode; } // static bool WiFiEndpoint::ParseIEs( const KeyValueStore& properties, Metrics::WiFiNetworkPhyMode* phy_mode, VendorInformation* vendor_information, bool* ieee80211w_required, string* country_code) { if (!properties.ContainsUint8s(WPASupplicant::kBSSPropertyIEs)) { SLOG(nullptr, 2) << __func__ << ": No IE property in BSS."; return false; } vector ies = properties.GetUint8s(WPASupplicant::kBSSPropertyIEs); // Format of an information element: // 1 1 1 - 252 // +------+--------+----------------+ // | Type | Length | Data | // +------+--------+----------------+ *phy_mode = Metrics::kWiFiNetworkPhyModeUndef; bool found_ht = false; bool found_vht = false; bool found_erp = false; int ie_len = 0; vector::iterator it; for (it = ies.begin(); std::distance(it, ies.end()) > 1; // Ensure Length field is within PDU. it += ie_len) { ie_len = 2 + *(it + 1); if (std::distance(it, ies.end()) < ie_len) { LOG(ERROR) << __func__ << ": IE extends past containing PDU."; break; } switch (*it) { case IEEE_80211::kElemIdCountry: // Retrieve 2-character country code from the beginning of the element. if (ie_len >= 4) { *country_code = string(it + 2, it + 4); } case IEEE_80211::kElemIdErp: found_erp = true; break; case IEEE_80211::kElemIdHTCap: case IEEE_80211::kElemIdHTInfo: found_ht = true; break; case IEEE_80211::kElemIdVHTCap: case IEEE_80211::kElemIdVHTOperation: found_vht = true; break; case IEEE_80211::kElemIdRSN: ParseWPACapabilities(it + 2, it + ie_len, ieee80211w_required); break; case IEEE_80211::kElemIdVendor: ParseVendorIE(it + 2, it + ie_len, vendor_information, ieee80211w_required); break; } } if (found_vht) { *phy_mode = Metrics::kWiFiNetworkPhyMode11ac; } else if (found_ht) { *phy_mode = Metrics::kWiFiNetworkPhyMode11n; } else if (found_erp) { *phy_mode = Metrics::kWiFiNetworkPhyMode11g; } else { return false; } return true; } // static void WiFiEndpoint::ParseWPACapabilities( vector::const_iterator ie, vector::const_iterator end, bool* ieee80211w_required) { // Format of an RSN Information Element: // 2 4 // +------+--------------------+ // | Type | Group Cipher Suite | // +------+--------------------+ // 2 4 * pairwise count // +-----------------------+---------------------+ // | Pairwise Cipher Count | Pairwise Ciphers... | // +-----------------------+---------------------+ // 2 4 * authkey count // +-----------------------+---------------------+ // | AuthKey Suite Count | AuthKey Suites... | // +-----------------------+---------------------+ // 2 // +------------------+ // | RSN Capabilities | // +------------------+ // 2 16 * pmkid count // +------------------+-------------------+ // | PMKID Count | PMKIDs... | // +------------------+-------------------+ // 4 // +-------------------------------+ // | Group Management Cipher Suite | // +-------------------------------+ if (std::distance(ie, end) < IEEE_80211::kRSNIECipherCountOffset) { return; } ie += IEEE_80211::kRSNIECipherCountOffset; // Advance past the pairwise and authkey ciphers. Each is a little-endian // cipher count followed by n * cipher_selector. for (int i = 0; i < IEEE_80211::kRSNIENumCiphers; ++i) { // Retrieve a little-endian cipher count. if (std::distance(ie, end) < IEEE_80211::kRSNIECipherCountLen) { return; } uint16_t cipher_count = *ie | (*(ie + 1) << 8); // Skip over the cipher selectors. int skip_length = IEEE_80211::kRSNIECipherCountLen + cipher_count * IEEE_80211::kRSNIESelectorLen; if (std::distance(ie, end) < skip_length) { return; } ie += skip_length; } if (std::distance(ie, end) < IEEE_80211::kRSNIECapabilitiesLen) { return; } // Retrieve a little-endian capabilities bitfield. uint16_t capabilities = *ie | (*(ie + 1) << 8); if (capabilities & IEEE_80211::kRSNCapabilityFrameProtectionRequired && ieee80211w_required) { // Never set this value to false, since there may be multiple RSN // information elements. *ieee80211w_required = true; } } // static void WiFiEndpoint::ParseVendorIE(vector::const_iterator ie, vector::const_iterator end, VendorInformation* vendor_information, bool* ieee80211w_required) { // Format of an vendor-specific information element (with type // and length field for the IE removed by the caller): // 3 1 1 - 248 // +------------+----------+----------------+ // | OUI | OUI Type | Data | // +------------+----------+----------------+ if (std::distance(ie, end) < 4) { LOG(ERROR) << __func__ << ": no room in IE for OUI and type field."; return; } uint32_t oui = (*ie << 16) | (*(ie + 1) << 8) | *(ie + 2); uint8_t oui_type = *(ie + 3); ie += 4; if (oui == IEEE_80211::kOUIVendorMicrosoft && oui_type == IEEE_80211::kOUIMicrosoftWPS) { // Format of a WPS data element: // 2 2 // +------+--------+----------------+ // | Type | Length | Data | // +------+--------+----------------+ while (std::distance(ie, end) >= 4) { int element_type = (*ie << 8) | *(ie + 1); int element_length = (*(ie + 2) << 8) | *(ie + 3); ie += 4; if (std::distance(ie, end) < element_length) { LOG(ERROR) << __func__ << ": WPS element extends past containing PDU."; break; } string s(ie, ie + element_length); if (base::IsStringASCII(s)) { switch (element_type) { case IEEE_80211::kWPSElementManufacturer: vendor_information->wps_manufacturer = s; break; case IEEE_80211::kWPSElementModelName: vendor_information->wps_model_name = s; break; case IEEE_80211::kWPSElementModelNumber: vendor_information->wps_model_number = s; break; case IEEE_80211::kWPSElementDeviceName: vendor_information->wps_device_name = s; break; } } ie += element_length; } } else if (oui == IEEE_80211::kOUIVendorMicrosoft && oui_type == IEEE_80211::kOUIMicrosoftWPA) { ParseWPACapabilities(ie, end, ieee80211w_required); } else if (oui != IEEE_80211::kOUIVendorEpigram && oui != IEEE_80211::kOUIVendorMicrosoft) { vendor_information->oui_set.insert(oui); } } void WiFiEndpoint::CheckForTetheringSignature() { has_tethering_signature_ = Tethering::IsAndroidBSSID(bssid_) || (Tethering::IsLocallyAdministeredBSSID(bssid_) && Tethering::HasIosOui(vendor_information_.oui_set)); } } // namespace shill