blob: 145a5c0c1eb1ef3fa22338dc41e68af76b0ad33a [file] [log] [blame]
Thieu Lee41a72d2012-02-06 20:46:51 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
mukesh agrawalb54601c2011-06-07 17:39:22 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Chris Masone092df3e2011-08-22 09:41:39 -07005#include "shill/wifi_endpoint.h"
6
Paul Stewart72b2fdc2012-06-02 08:58:51 -07007#include <algorithm>
8
Chris Masone092df3e2011-08-22 09:41:39 -07009#include <base/logging.h>
Eric Shienbrood3e20a232012-02-16 11:35:56 -050010#include <base/stl_util.h>
mukesh agrawalb54601c2011-06-07 17:39:22 -070011#include <base/stringprintf.h>
12#include <base/string_number_conversions.h>
mukesh agrawal6e277772011-09-29 15:04:23 -070013#include <base/string_util.h>
Chris Masone092df3e2011-08-22 09:41:39 -070014#include <chromeos/dbus/service_constants.h>
mukesh agrawalb54601c2011-06-07 17:39:22 -070015
Thieu Le1df7f4e2012-02-10 15:21:45 -080016#include "shill/ieee80211.h"
mukesh agrawalb20776f2012-02-10 16:00:36 -080017#include "shill/proxy_factory.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070018#include "shill/scope_logger.h"
mukesh agrawalb20776f2012-02-10 16:00:36 -080019#include "shill/supplicant_bss_proxy_interface.h"
mukesh agrawal16bc1b82012-02-09 18:38:26 -080020#include "shill/wifi.h"
mukesh agrawalb20776f2012-02-10 16:00:36 -080021#include "shill/wifi_endpoint.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070022#include "shill/wpa_supplicant.h"
23
24using std::map;
25using std::set;
mukesh agrawalb54601c2011-06-07 17:39:22 -070026using std::string;
mukesh agrawal6e277772011-09-29 15:04:23 -070027using std::vector;
mukesh agrawalb54601c2011-06-07 17:39:22 -070028
29namespace shill {
30
mukesh agrawalb20776f2012-02-10 16:00:36 -080031WiFiEndpoint::WiFiEndpoint(ProxyFactory *proxy_factory,
32 const WiFiRefPtr &device,
33 const string &rpc_id,
34 const map<string, ::DBus::Variant> &properties)
35 : frequency_(0),
36 proxy_factory_(proxy_factory),
37 device_(device),
38 rpc_id_(rpc_id) {
mukesh agrawalb54601c2011-06-07 17:39:22 -070039 // XXX will segfault on missing properties
40 ssid_ =
mukesh agrawal6e277772011-09-29 15:04:23 -070041 properties.find(wpa_supplicant::kBSSPropertySSID)->second.
mukesh agrawalb54601c2011-06-07 17:39:22 -070042 operator std::vector<uint8_t>();
43 bssid_ =
mukesh agrawal6e277772011-09-29 15:04:23 -070044 properties.find(wpa_supplicant::kBSSPropertyBSSID)->second.
mukesh agrawalb54601c2011-06-07 17:39:22 -070045 operator std::vector<uint8_t>();
46 signal_strength_ =
mukesh agrawal15908392011-11-16 18:29:25 +000047 properties.find(wpa_supplicant::kBSSPropertySignal)->second.
48 reader().get_int16();
Thieu Lee41a72d2012-02-06 20:46:51 +000049 map<string, ::DBus::Variant>::const_iterator it =
50 properties.find(wpa_supplicant::kBSSPropertyFrequency);
51 if (it != properties.end())
52 frequency_ = it->second.reader().get_uint16();
Paul Stewart72b2fdc2012-06-02 08:58:51 -070053
54 Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
55 if (!ParseIEs(properties, &phy_mode, &vendor_information_)) {
56 phy_mode = DeterminePhyModeFromFrequency(properties, frequency_);
57 }
58 physical_mode_ = phy_mode;
59
Chris Masone092df3e2011-08-22 09:41:39 -070060 network_mode_ = ParseMode(
mukesh agrawal6e277772011-09-29 15:04:23 -070061 properties.find(wpa_supplicant::kBSSPropertyMode)->second);
62 security_mode_ = ParseSecurity(properties);
mukesh agrawalb54601c2011-06-07 17:39:22 -070063
Chris Masone092df3e2011-08-22 09:41:39 -070064 if (network_mode_.empty()) {
mukesh agrawalb54601c2011-06-07 17:39:22 -070065 // XXX log error?
66 }
67
68 ssid_string_ = string(ssid_.begin(), ssid_.end());
mukesh agrawal16bc1b82012-02-09 18:38:26 -080069 WiFi::SanitizeSSID(&ssid_string_);
mukesh agrawalb54601c2011-06-07 17:39:22 -070070 ssid_hex_ = base::HexEncode(&(*ssid_.begin()), ssid_.size());
71 bssid_string_ = StringPrintf("%02x:%02x:%02x:%02x:%02x:%02x",
72 bssid_[0], bssid_[1], bssid_[2],
73 bssid_[3], bssid_[4], bssid_[5]);
74 bssid_hex_ = base::HexEncode(&(*bssid_.begin()), bssid_.size());
75}
76
77WiFiEndpoint::~WiFiEndpoint() {}
78
mukesh agrawalb20776f2012-02-10 16:00:36 -080079void WiFiEndpoint::Start() {
80 supplicant_bss_proxy_.reset(
81 proxy_factory_->CreateSupplicantBSSProxy(
82 this, rpc_id_, wpa_supplicant::kDBusAddr));
83}
84
85void WiFiEndpoint::PropertiesChanged(
86 const map<string, ::DBus::Variant> &properties) {
Darin Petkov3abc3be2012-06-27 10:48:23 +020087 SLOG(WiFi, 2) << __func__;
mukesh agrawalb20776f2012-02-10 16:00:36 -080088 map<string, ::DBus::Variant>::const_iterator properties_it =
89 properties.find(wpa_supplicant::kBSSPropertySignal);
90 if (properties_it != properties.end()) {
91 signal_strength_ = properties_it->second.reader().get_int16();
Ben Chanfad4a0b2012-04-18 15:49:59 -070092 SLOG(WiFi, 2) << "WiFiEndpoint " << bssid_string_ << " signal is now "
93 << signal_strength_;
mukesh agrawalb20776f2012-02-10 16:00:36 -080094 device_->NotifyEndpointChanged(*this);
95 }
96}
97
Paul Stewart72b2fdc2012-06-02 08:58:51 -070098
99map<string, string> WiFiEndpoint::GetVendorInformation() const {
100 map<string, string> vendor_information;
101 if (!vendor_information_.wps_manufacturer.empty()) {
102 vendor_information[kVendorWPSManufacturerProperty] =
103 vendor_information_.wps_manufacturer;
104 }
105 if (!vendor_information_.wps_model_name.empty()) {
106 vendor_information[kVendorWPSModelNameProperty] =
107 vendor_information_.wps_model_name;
108 }
109 if (!vendor_information_.wps_model_number.empty()) {
110 vendor_information[kVendorWPSModelNumberProperty] =
111 vendor_information_.wps_model_number;
112 }
113 if (!vendor_information_.wps_device_name.empty()) {
114 vendor_information[kVendorWPSDeviceNameProperty] =
115 vendor_information_.wps_device_name;
116 }
117 if (!vendor_information_.oui_list.empty()) {
118 vector<string> oui_list;
119 set<uint32_t>::const_iterator it;
120 for (it = vendor_information_.oui_list.begin();
121 it != vendor_information_.oui_list.end();
122 ++it) {
123 oui_list.push_back(
124 StringPrintf("%02x-%02x-%02x",
125 *it >> 16, (*it >> 8) & 0xff, *it & 0xff));
126 }
127 vendor_information[kVendorOUIListProperty] =
128 JoinString(oui_list, ' ');
129 }
130 return vendor_information;
131}
132
Chris Masone092df3e2011-08-22 09:41:39 -0700133// static
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700134uint32_t WiFiEndpoint::ModeStringToUint(const string &mode_string) {
Chris Masone092df3e2011-08-22 09:41:39 -0700135 if (mode_string == flimflam::kModeManaged)
mukesh agrawal6e277772011-09-29 15:04:23 -0700136 return wpa_supplicant::kNetworkModeInfrastructureInt;
Chris Masone092df3e2011-08-22 09:41:39 -0700137 else if (mode_string == flimflam::kModeAdhoc)
mukesh agrawal6e277772011-09-29 15:04:23 -0700138 return wpa_supplicant::kNetworkModeAdHocInt;
Chris Masone092df3e2011-08-22 09:41:39 -0700139 else
140 NOTIMPLEMENTED() << "Shill dos not support " << mode_string
141 << " mode at this time.";
142 return 0;
143}
144
mukesh agrawal6e277772011-09-29 15:04:23 -0700145const vector<uint8_t> &WiFiEndpoint::ssid() const {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700146 return ssid_;
147}
148
149const string &WiFiEndpoint::ssid_string() const {
150 return ssid_string_;
151}
152
153const string &WiFiEndpoint::ssid_hex() const {
154 return ssid_hex_;
155}
156
157const string &WiFiEndpoint::bssid_string() const {
158 return bssid_string_;
159}
160
161const string &WiFiEndpoint::bssid_hex() const {
162 return bssid_hex_;
163}
164
165int16_t WiFiEndpoint::signal_strength() const {
166 return signal_strength_;
167}
168
Thieu Lee41a72d2012-02-06 20:46:51 +0000169uint16 WiFiEndpoint::frequency() const {
170 return frequency_;
171}
172
Thieu Le1df7f4e2012-02-10 15:21:45 -0800173uint16 WiFiEndpoint::physical_mode() const {
174 return physical_mode_;
175}
176
Chris Masone092df3e2011-08-22 09:41:39 -0700177const string &WiFiEndpoint::network_mode() const {
mukesh agrawal6e277772011-09-29 15:04:23 -0700178 return network_mode_;
179}
180
181const string &WiFiEndpoint::security_mode() const {
182 return security_mode_;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700183}
184
Chris Masone092df3e2011-08-22 09:41:39 -0700185// static
mukesh agrawalb20776f2012-02-10 16:00:36 -0800186WiFiEndpoint *WiFiEndpoint::MakeOpenEndpoint(ProxyFactory *proxy_factory,
187 const WiFiRefPtr &wifi,
188 const string &ssid,
mukesh agrawale1d90e92012-02-15 17:36:08 -0800189 const string &bssid,
190 uint16 frequency,
191 int16 signal_dbm) {
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000192 map <string, ::DBus::Variant> args;
193 ::DBus::MessageIter writer;
194
195 writer = args[wpa_supplicant::kBSSPropertySSID].writer();
196 writer << vector<uint8_t>(ssid.begin(), ssid.end());
197
198 string bssid_nosep;
199 RemoveChars(bssid, ":", &bssid_nosep);
200 vector<uint8_t> bssid_bytes;
201 base::HexStringToBytes(bssid_nosep, &bssid_bytes);
202 writer = args[wpa_supplicant::kBSSPropertyBSSID].writer();
203 writer << bssid_bytes;
204
mukesh agrawale1d90e92012-02-15 17:36:08 -0800205 args[wpa_supplicant::kBSSPropertySignal].writer().append_int16(signal_dbm);
206 args[wpa_supplicant::kBSSPropertyFrequency].writer().append_uint16(frequency);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000207 args[wpa_supplicant::kBSSPropertyMode].writer().append_string(
208 wpa_supplicant::kNetworkModeInfrastructure);
209 // We indicate this is an open BSS by leaving out all security properties.
210
mukesh agrawalb20776f2012-02-10 16:00:36 -0800211 return new WiFiEndpoint(
212 proxy_factory, wifi, bssid, args); // |bssid| fakes an RPC ID
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000213}
214
215// static
mukesh agrawal6e277772011-09-29 15:04:23 -0700216const char *WiFiEndpoint::ParseMode(const string &mode_string) {
217 if (mode_string == wpa_supplicant::kNetworkModeInfrastructure) {
Chris Masone092df3e2011-08-22 09:41:39 -0700218 return flimflam::kModeManaged;
mukesh agrawal6e277772011-09-29 15:04:23 -0700219 } else if (mode_string == wpa_supplicant::kNetworkModeAdHoc) {
Chris Masone092df3e2011-08-22 09:41:39 -0700220 return flimflam::kModeAdhoc;
mukesh agrawal6e277772011-09-29 15:04:23 -0700221 } else if (mode_string == wpa_supplicant::kNetworkModeAccessPoint) {
Chris Masone092df3e2011-08-22 09:41:39 -0700222 NOTREACHED() << "Shill does not support AP mode at this time.";
223 return NULL;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700224 } else {
Chris Masone092df3e2011-08-22 09:41:39 -0700225 NOTREACHED() << "Unknown WiFi endpoint mode!";
226 return NULL;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700227 }
228}
229
mukesh agrawal6e277772011-09-29 15:04:23 -0700230// static
231const char *WiFiEndpoint::ParseSecurity(
232 const map<string, ::DBus::Variant> &properties) {
233 set<KeyManagement> rsn_key_management_methods;
234 if (ContainsKey(properties, wpa_supplicant::kPropertyRSN)) {
235 // TODO(quiche): check type before casting
236 const map<string, ::DBus::Variant> rsn_properties(
237 properties.find(wpa_supplicant::kPropertyRSN)->second.
238 operator map<string, ::DBus::Variant>());
239 ParseKeyManagementMethods(rsn_properties, &rsn_key_management_methods);
240 }
241
242 set<KeyManagement> wpa_key_management_methods;
243 if (ContainsKey(properties, wpa_supplicant::kPropertyWPA)) {
mukesh agrawal165e6142011-11-22 02:22:56 +0000244 // TODO(quiche): check type before casting
mukesh agrawal6e277772011-09-29 15:04:23 -0700245 const map<string, ::DBus::Variant> rsn_properties(
246 properties.find(wpa_supplicant::kPropertyWPA)->second.
247 operator map<string, ::DBus::Variant>());
248 ParseKeyManagementMethods(rsn_properties, &wpa_key_management_methods);
249 }
250
251 bool wep_privacy = false;
252 if (ContainsKey(properties, wpa_supplicant::kPropertyPrivacy)) {
253 wep_privacy = properties.find(wpa_supplicant::kPropertyPrivacy)->second.
254 reader().get_bool();
255 }
256
257 if (ContainsKey(rsn_key_management_methods, kKeyManagement802_1x) ||
258 ContainsKey(wpa_key_management_methods, kKeyManagement802_1x)) {
259 return flimflam::kSecurity8021x;
260 } else if (ContainsKey(rsn_key_management_methods, kKeyManagementPSK)) {
261 return flimflam::kSecurityRsn;
262 } else if (ContainsKey(wpa_key_management_methods, kKeyManagementPSK)) {
263 return flimflam::kSecurityWpa;
264 } else if (wep_privacy) {
265 return flimflam::kSecurityWep;
266 } else {
267 return flimflam::kSecurityNone;
268 }
269}
270
271// static
272void WiFiEndpoint::ParseKeyManagementMethods(
273 const map<string, ::DBus::Variant> &security_method_properties,
274 set<KeyManagement> *key_management_methods) {
275 if (!ContainsKey(security_method_properties,
276 wpa_supplicant::kSecurityMethodPropertyKeyManagement)) {
277 return;
278 }
279
280 // TODO(quiche): check type before cast
281 const vector<string> key_management_vec =
282 security_method_properties.
283 find(wpa_supplicant::kSecurityMethodPropertyKeyManagement)->second.
284 operator vector<string>();
285 for (vector<string>::const_iterator it = key_management_vec.begin();
286 it != key_management_vec.end();
287 ++it) {
288 if (EndsWith(*it, wpa_supplicant::kKeyManagementMethodSuffixEAP, true)) {
289 key_management_methods->insert(kKeyManagement802_1x);
290 } else if (
291 EndsWith(*it, wpa_supplicant::kKeyManagementMethodSuffixPSK, true)) {
292 key_management_methods->insert(kKeyManagementPSK);
293 }
294 }
295}
296
Thieu Le1df7f4e2012-02-10 15:21:45 -0800297// static
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700298Metrics::WiFiNetworkPhyMode WiFiEndpoint::DeterminePhyModeFromFrequency(
Thieu Le1df7f4e2012-02-10 15:21:45 -0800299 const map<string, ::DBus::Variant> &properties, uint16 frequency) {
300 uint32_t max_rate = 0;
301 map<string, ::DBus::Variant>::const_iterator it =
302 properties.find(wpa_supplicant::kBSSPropertyRates);
303 if (it != properties.end()) {
304 vector<uint32_t> rates = it->second.operator vector<uint32_t>();
305 if (rates.size() > 0)
306 max_rate = rates[0]; // Rates are sorted in descending order
307 }
308
309 Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
Thieu Le1df7f4e2012-02-10 15:21:45 -0800310 if (frequency < 3000) {
311 // 2.4GHz legacy, check for tx rate for 11b-only
312 // (note 22M is valid)
313 if (max_rate < 24000000)
314 phy_mode = Metrics::kWiFiNetworkPhyMode11b;
315 else
316 phy_mode = Metrics::kWiFiNetworkPhyMode11g;
317 } else {
318 phy_mode = Metrics::kWiFiNetworkPhyMode11a;
319 }
320
321 return phy_mode;
322}
323
324// static
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700325bool WiFiEndpoint::ParseIEs(
326 const map<string, ::DBus::Variant> &properties,
327 Metrics::WiFiNetworkPhyMode *phy_mode,
328 VendorInformation *vendor_information) {
329
330 map<string, ::DBus::Variant>::const_iterator ies_property =
331 properties.find(wpa_supplicant::kBSSPropertyIEs);
332 if (ies_property == properties.end()) {
333 SLOG(WiFi, 2) << __func__ << ": No IE property in BSS.";
334 return false;
Thieu Le1df7f4e2012-02-10 15:21:45 -0800335 }
336
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700337 vector<uint8_t> ies = ies_property->second.operator vector<uint8_t>();
338
339
340 // Format of an information element:
341 // 1 1 1 - 252
342 // +------+--------+----------------+
343 // | Type | Length | Data |
344 // +------+--------+----------------+
345 *phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
346 bool found_ht = false;
347 bool found_erp = false;
348 int ie_len = 0;
349 vector<uint8_t>::iterator it;
350 for (it = ies.begin();
351 std::distance(it, ies.end()) > 1; // Ensure Length field is within PDU.
352 it += ie_len) {
353 ie_len = 2 + *(it + 1);
354 if (std::distance(it, ies.end()) < ie_len) {
355 LOG(ERROR) << __func__ << ": IE extends past containing PDU.";
356 break;
357 }
358 switch (*it) {
359 case IEEE_80211::kElemIdErp:
360 if (!found_ht) {
361 *phy_mode = Metrics::kWiFiNetworkPhyMode11g;
362 }
363 found_erp = true;
364 break;
365 case IEEE_80211::kElemIdHTCap:
366 case IEEE_80211::kElemIdHTInfo:
367 *phy_mode = Metrics::kWiFiNetworkPhyMode11n;
368 found_ht = true;
369 break;
370 case IEEE_80211::kElemIdVendor:
371 ParseVendorIE(it + 2, it + ie_len, vendor_information);
372 break;
373 }
374 }
375 return found_ht || found_erp;
376}
377
378// static
379void WiFiEndpoint::ParseVendorIE(vector<uint8_t>::const_iterator ie,
380 vector<uint8_t>::const_iterator end,
381 VendorInformation *vendor_information) {
382 // Format of an vendor-specific information element (with type
383 // and length field for the IE removed by the caller):
384 // 3 1 1 - 248
385 // +------------+----------+----------------+
386 // | OUI | OUI Type | Data |
387 // +------------+----------+----------------+
388
389 if (std::distance(ie, end) < 4) {
390 LOG(ERROR) << __func__ << ": no room in IE for OUI and type field.";
391 return;
392 }
393 uint32_t oui = (*ie << 16) | (*(ie + 1) << 8) | *(ie + 2);
394 uint8_t oui_type = *(ie + 3);
395 ie += 4;
396
397 if (oui == IEEE_80211::kOUIVendorMicrosoft &&
398 oui_type == IEEE_80211::kOUIMicrosoftWPS) {
399 // Format of a WPS data element:
400 // 2 2
401 // +------+--------+----------------+
402 // | Type | Length | Data |
403 // +------+--------+----------------+
404 while (std::distance(ie, end) >= 4) {
405 int element_type = (*ie << 8) | *(ie + 1);
406 int element_length = (*(ie + 2) << 8) | *(ie + 3);
407 ie += 4;
408 if (std::distance(ie, end) < element_length) {
409 LOG(ERROR) << __func__ << ": WPS element extends past containing PDU.";
410 break;
411 }
412 string s(ie, ie + element_length);
413 if (IsStringASCII(s)) {
414 switch (element_type) {
415 case IEEE_80211::kWPSElementManufacturer:
416 vendor_information->wps_manufacturer = s;
417 break;
418 case IEEE_80211::kWPSElementModelName:
419 vendor_information->wps_model_name = s;
420 break;
421 case IEEE_80211::kWPSElementModelNumber:
422 vendor_information->wps_model_number = s;
423 break;
424 case IEEE_80211::kWPSElementDeviceName:
425 vendor_information->wps_device_name = s;
426 break;
427 }
428 }
429 ie += element_length;
430 }
431 } else if (oui != IEEE_80211::kOUIVendorEpigram &&
432 oui != IEEE_80211::kOUIVendorMicrosoft) {
433 vendor_information->oui_list.insert(oui);
434 }
Thieu Le1df7f4e2012-02-10 15:21:45 -0800435}
436
mukesh agrawalb54601c2011-06-07 17:39:22 -0700437} // namespace shill