blob: 61115a22f5f08bffce6860a5ce812bd20607b4de [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
Eric Shienbrood3e20a232012-02-16 11:35:56 -05009#include <base/stl_util.h>
mukesh agrawalb54601c2011-06-07 17:39:22 -070010#include <base/stringprintf.h>
11#include <base/string_number_conversions.h>
mukesh agrawal6e277772011-09-29 15:04:23 -070012#include <base/string_util.h>
Chris Masone092df3e2011-08-22 09:41:39 -070013#include <chromeos/dbus/service_constants.h>
mukesh agrawalb54601c2011-06-07 17:39:22 -070014
Thieu Le1df7f4e2012-02-10 15:21:45 -080015#include "shill/ieee80211.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070016#include "shill/logging.h"
mukesh agrawalb20776f2012-02-10 16:00:36 -080017#include "shill/proxy_factory.h"
18#include "shill/supplicant_bss_proxy_interface.h"
mukesh agrawal16bc1b82012-02-09 18:38:26 -080019#include "shill/wifi.h"
mukesh agrawalb20776f2012-02-10 16:00:36 -080020#include "shill/wifi_endpoint.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070021#include "shill/wpa_supplicant.h"
22
23using std::map;
24using std::set;
mukesh agrawalb54601c2011-06-07 17:39:22 -070025using std::string;
mukesh agrawal6e277772011-09-29 15:04:23 -070026using std::vector;
mukesh agrawalb54601c2011-06-07 17:39:22 -070027
28namespace shill {
29
mukesh agrawalb20776f2012-02-10 16:00:36 -080030WiFiEndpoint::WiFiEndpoint(ProxyFactory *proxy_factory,
31 const WiFiRefPtr &device,
32 const string &rpc_id,
33 const map<string, ::DBus::Variant> &properties)
34 : frequency_(0),
35 proxy_factory_(proxy_factory),
36 device_(device),
37 rpc_id_(rpc_id) {
mukesh agrawalb54601c2011-06-07 17:39:22 -070038 // XXX will segfault on missing properties
39 ssid_ =
mukesh agrawal6e277772011-09-29 15:04:23 -070040 properties.find(wpa_supplicant::kBSSPropertySSID)->second.
mukesh agrawalb54601c2011-06-07 17:39:22 -070041 operator std::vector<uint8_t>();
42 bssid_ =
mukesh agrawal6e277772011-09-29 15:04:23 -070043 properties.find(wpa_supplicant::kBSSPropertyBSSID)->second.
mukesh agrawalb54601c2011-06-07 17:39:22 -070044 operator std::vector<uint8_t>();
45 signal_strength_ =
mukesh agrawal15908392011-11-16 18:29:25 +000046 properties.find(wpa_supplicant::kBSSPropertySignal)->second.
47 reader().get_int16();
Thieu Lee41a72d2012-02-06 20:46:51 +000048 map<string, ::DBus::Variant>::const_iterator it =
49 properties.find(wpa_supplicant::kBSSPropertyFrequency);
50 if (it != properties.end())
51 frequency_ = it->second.reader().get_uint16();
Paul Stewart72b2fdc2012-06-02 08:58:51 -070052
53 Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
54 if (!ParseIEs(properties, &phy_mode, &vendor_information_)) {
55 phy_mode = DeterminePhyModeFromFrequency(properties, frequency_);
56 }
57 physical_mode_ = phy_mode;
58
Chris Masone092df3e2011-08-22 09:41:39 -070059 network_mode_ = ParseMode(
mukesh agrawal6e277772011-09-29 15:04:23 -070060 properties.find(wpa_supplicant::kBSSPropertyMode)->second);
61 security_mode_ = ParseSecurity(properties);
mukesh agrawalb54601c2011-06-07 17:39:22 -070062
Chris Masone092df3e2011-08-22 09:41:39 -070063 if (network_mode_.empty()) {
mukesh agrawalb54601c2011-06-07 17:39:22 -070064 // XXX log error?
65 }
66
67 ssid_string_ = string(ssid_.begin(), ssid_.end());
mukesh agrawal16bc1b82012-02-09 18:38:26 -080068 WiFi::SanitizeSSID(&ssid_string_);
mukesh agrawalb54601c2011-06-07 17:39:22 -070069 ssid_hex_ = base::HexEncode(&(*ssid_.begin()), ssid_.size());
70 bssid_string_ = StringPrintf("%02x:%02x:%02x:%02x:%02x:%02x",
71 bssid_[0], bssid_[1], bssid_[2],
72 bssid_[3], bssid_[4], bssid_[5]);
73 bssid_hex_ = base::HexEncode(&(*bssid_.begin()), bssid_.size());
74}
75
76WiFiEndpoint::~WiFiEndpoint() {}
77
mukesh agrawalb20776f2012-02-10 16:00:36 -080078void WiFiEndpoint::Start() {
79 supplicant_bss_proxy_.reset(
80 proxy_factory_->CreateSupplicantBSSProxy(
81 this, rpc_id_, wpa_supplicant::kDBusAddr));
82}
83
84void WiFiEndpoint::PropertiesChanged(
85 const map<string, ::DBus::Variant> &properties) {
Darin Petkov3abc3be2012-06-27 10:48:23 +020086 SLOG(WiFi, 2) << __func__;
mukesh agrawalb20776f2012-02-10 16:00:36 -080087 map<string, ::DBus::Variant>::const_iterator properties_it =
88 properties.find(wpa_supplicant::kBSSPropertySignal);
89 if (properties_it != properties.end()) {
90 signal_strength_ = properties_it->second.reader().get_int16();
Ben Chanfad4a0b2012-04-18 15:49:59 -070091 SLOG(WiFi, 2) << "WiFiEndpoint " << bssid_string_ << " signal is now "
92 << signal_strength_;
mukesh agrawalb20776f2012-02-10 16:00:36 -080093 device_->NotifyEndpointChanged(*this);
94 }
95}
96
Paul Stewart72b2fdc2012-06-02 08:58:51 -070097
98map<string, string> WiFiEndpoint::GetVendorInformation() const {
99 map<string, string> vendor_information;
100 if (!vendor_information_.wps_manufacturer.empty()) {
101 vendor_information[kVendorWPSManufacturerProperty] =
102 vendor_information_.wps_manufacturer;
103 }
104 if (!vendor_information_.wps_model_name.empty()) {
105 vendor_information[kVendorWPSModelNameProperty] =
106 vendor_information_.wps_model_name;
107 }
108 if (!vendor_information_.wps_model_number.empty()) {
109 vendor_information[kVendorWPSModelNumberProperty] =
110 vendor_information_.wps_model_number;
111 }
112 if (!vendor_information_.wps_device_name.empty()) {
113 vendor_information[kVendorWPSDeviceNameProperty] =
114 vendor_information_.wps_device_name;
115 }
116 if (!vendor_information_.oui_list.empty()) {
117 vector<string> oui_list;
118 set<uint32_t>::const_iterator it;
119 for (it = vendor_information_.oui_list.begin();
120 it != vendor_information_.oui_list.end();
121 ++it) {
122 oui_list.push_back(
123 StringPrintf("%02x-%02x-%02x",
124 *it >> 16, (*it >> 8) & 0xff, *it & 0xff));
125 }
126 vendor_information[kVendorOUIListProperty] =
127 JoinString(oui_list, ' ');
128 }
129 return vendor_information;
130}
131
Chris Masone092df3e2011-08-22 09:41:39 -0700132// static
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700133uint32_t WiFiEndpoint::ModeStringToUint(const string &mode_string) {
Chris Masone092df3e2011-08-22 09:41:39 -0700134 if (mode_string == flimflam::kModeManaged)
mukesh agrawal6e277772011-09-29 15:04:23 -0700135 return wpa_supplicant::kNetworkModeInfrastructureInt;
Chris Masone092df3e2011-08-22 09:41:39 -0700136 else if (mode_string == flimflam::kModeAdhoc)
mukesh agrawal6e277772011-09-29 15:04:23 -0700137 return wpa_supplicant::kNetworkModeAdHocInt;
Chris Masone092df3e2011-08-22 09:41:39 -0700138 else
139 NOTIMPLEMENTED() << "Shill dos not support " << mode_string
140 << " mode at this time.";
141 return 0;
142}
143
mukesh agrawal6e277772011-09-29 15:04:23 -0700144const vector<uint8_t> &WiFiEndpoint::ssid() const {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700145 return ssid_;
146}
147
148const string &WiFiEndpoint::ssid_string() const {
149 return ssid_string_;
150}
151
152const string &WiFiEndpoint::ssid_hex() const {
153 return ssid_hex_;
154}
155
156const string &WiFiEndpoint::bssid_string() const {
157 return bssid_string_;
158}
159
160const string &WiFiEndpoint::bssid_hex() const {
161 return bssid_hex_;
162}
163
164int16_t WiFiEndpoint::signal_strength() const {
165 return signal_strength_;
166}
167
Thieu Lee41a72d2012-02-06 20:46:51 +0000168uint16 WiFiEndpoint::frequency() const {
169 return frequency_;
170}
171
Thieu Le1df7f4e2012-02-10 15:21:45 -0800172uint16 WiFiEndpoint::physical_mode() const {
173 return physical_mode_;
174}
175
Chris Masone092df3e2011-08-22 09:41:39 -0700176const string &WiFiEndpoint::network_mode() const {
mukesh agrawal6e277772011-09-29 15:04:23 -0700177 return network_mode_;
178}
179
180const string &WiFiEndpoint::security_mode() const {
181 return security_mode_;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700182}
183
Chris Masone092df3e2011-08-22 09:41:39 -0700184// static
mukesh agrawalb20776f2012-02-10 16:00:36 -0800185WiFiEndpoint *WiFiEndpoint::MakeOpenEndpoint(ProxyFactory *proxy_factory,
186 const WiFiRefPtr &wifi,
187 const string &ssid,
mukesh agrawale1d90e92012-02-15 17:36:08 -0800188 const string &bssid,
189 uint16 frequency,
190 int16 signal_dbm) {
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000191 map <string, ::DBus::Variant> args;
192 ::DBus::MessageIter writer;
193
194 writer = args[wpa_supplicant::kBSSPropertySSID].writer();
195 writer << vector<uint8_t>(ssid.begin(), ssid.end());
196
197 string bssid_nosep;
198 RemoveChars(bssid, ":", &bssid_nosep);
199 vector<uint8_t> bssid_bytes;
200 base::HexStringToBytes(bssid_nosep, &bssid_bytes);
201 writer = args[wpa_supplicant::kBSSPropertyBSSID].writer();
202 writer << bssid_bytes;
203
mukesh agrawale1d90e92012-02-15 17:36:08 -0800204 args[wpa_supplicant::kBSSPropertySignal].writer().append_int16(signal_dbm);
205 args[wpa_supplicant::kBSSPropertyFrequency].writer().append_uint16(frequency);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000206 args[wpa_supplicant::kBSSPropertyMode].writer().append_string(
207 wpa_supplicant::kNetworkModeInfrastructure);
208 // We indicate this is an open BSS by leaving out all security properties.
209
mukesh agrawalb20776f2012-02-10 16:00:36 -0800210 return new WiFiEndpoint(
211 proxy_factory, wifi, bssid, args); // |bssid| fakes an RPC ID
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000212}
213
214// static
mukesh agrawal6e277772011-09-29 15:04:23 -0700215const char *WiFiEndpoint::ParseMode(const string &mode_string) {
216 if (mode_string == wpa_supplicant::kNetworkModeInfrastructure) {
Chris Masone092df3e2011-08-22 09:41:39 -0700217 return flimflam::kModeManaged;
mukesh agrawal6e277772011-09-29 15:04:23 -0700218 } else if (mode_string == wpa_supplicant::kNetworkModeAdHoc) {
Chris Masone092df3e2011-08-22 09:41:39 -0700219 return flimflam::kModeAdhoc;
mukesh agrawal6e277772011-09-29 15:04:23 -0700220 } else if (mode_string == wpa_supplicant::kNetworkModeAccessPoint) {
Chris Masone092df3e2011-08-22 09:41:39 -0700221 NOTREACHED() << "Shill does not support AP mode at this time.";
222 return NULL;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700223 } else {
Chris Masone092df3e2011-08-22 09:41:39 -0700224 NOTREACHED() << "Unknown WiFi endpoint mode!";
225 return NULL;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700226 }
227}
228
mukesh agrawal6e277772011-09-29 15:04:23 -0700229// static
230const char *WiFiEndpoint::ParseSecurity(
231 const map<string, ::DBus::Variant> &properties) {
232 set<KeyManagement> rsn_key_management_methods;
233 if (ContainsKey(properties, wpa_supplicant::kPropertyRSN)) {
234 // TODO(quiche): check type before casting
235 const map<string, ::DBus::Variant> rsn_properties(
236 properties.find(wpa_supplicant::kPropertyRSN)->second.
237 operator map<string, ::DBus::Variant>());
238 ParseKeyManagementMethods(rsn_properties, &rsn_key_management_methods);
239 }
240
241 set<KeyManagement> wpa_key_management_methods;
242 if (ContainsKey(properties, wpa_supplicant::kPropertyWPA)) {
mukesh agrawal165e6142011-11-22 02:22:56 +0000243 // TODO(quiche): check type before casting
mukesh agrawal6e277772011-09-29 15:04:23 -0700244 const map<string, ::DBus::Variant> rsn_properties(
245 properties.find(wpa_supplicant::kPropertyWPA)->second.
246 operator map<string, ::DBus::Variant>());
247 ParseKeyManagementMethods(rsn_properties, &wpa_key_management_methods);
248 }
249
250 bool wep_privacy = false;
251 if (ContainsKey(properties, wpa_supplicant::kPropertyPrivacy)) {
252 wep_privacy = properties.find(wpa_supplicant::kPropertyPrivacy)->second.
253 reader().get_bool();
254 }
255
256 if (ContainsKey(rsn_key_management_methods, kKeyManagement802_1x) ||
257 ContainsKey(wpa_key_management_methods, kKeyManagement802_1x)) {
258 return flimflam::kSecurity8021x;
259 } else if (ContainsKey(rsn_key_management_methods, kKeyManagementPSK)) {
260 return flimflam::kSecurityRsn;
261 } else if (ContainsKey(wpa_key_management_methods, kKeyManagementPSK)) {
262 return flimflam::kSecurityWpa;
263 } else if (wep_privacy) {
264 return flimflam::kSecurityWep;
265 } else {
266 return flimflam::kSecurityNone;
267 }
268}
269
270// static
271void WiFiEndpoint::ParseKeyManagementMethods(
272 const map<string, ::DBus::Variant> &security_method_properties,
273 set<KeyManagement> *key_management_methods) {
274 if (!ContainsKey(security_method_properties,
275 wpa_supplicant::kSecurityMethodPropertyKeyManagement)) {
276 return;
277 }
278
279 // TODO(quiche): check type before cast
280 const vector<string> key_management_vec =
281 security_method_properties.
282 find(wpa_supplicant::kSecurityMethodPropertyKeyManagement)->second.
283 operator vector<string>();
284 for (vector<string>::const_iterator it = key_management_vec.begin();
285 it != key_management_vec.end();
286 ++it) {
287 if (EndsWith(*it, wpa_supplicant::kKeyManagementMethodSuffixEAP, true)) {
288 key_management_methods->insert(kKeyManagement802_1x);
289 } else if (
290 EndsWith(*it, wpa_supplicant::kKeyManagementMethodSuffixPSK, true)) {
291 key_management_methods->insert(kKeyManagementPSK);
292 }
293 }
294}
295
Thieu Le1df7f4e2012-02-10 15:21:45 -0800296// static
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700297Metrics::WiFiNetworkPhyMode WiFiEndpoint::DeterminePhyModeFromFrequency(
Thieu Le1df7f4e2012-02-10 15:21:45 -0800298 const map<string, ::DBus::Variant> &properties, uint16 frequency) {
299 uint32_t max_rate = 0;
300 map<string, ::DBus::Variant>::const_iterator it =
301 properties.find(wpa_supplicant::kBSSPropertyRates);
302 if (it != properties.end()) {
303 vector<uint32_t> rates = it->second.operator vector<uint32_t>();
304 if (rates.size() > 0)
305 max_rate = rates[0]; // Rates are sorted in descending order
306 }
307
308 Metrics::WiFiNetworkPhyMode phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
Thieu Le1df7f4e2012-02-10 15:21:45 -0800309 if (frequency < 3000) {
310 // 2.4GHz legacy, check for tx rate for 11b-only
311 // (note 22M is valid)
312 if (max_rate < 24000000)
313 phy_mode = Metrics::kWiFiNetworkPhyMode11b;
314 else
315 phy_mode = Metrics::kWiFiNetworkPhyMode11g;
316 } else {
317 phy_mode = Metrics::kWiFiNetworkPhyMode11a;
318 }
319
320 return phy_mode;
321}
322
323// static
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700324bool WiFiEndpoint::ParseIEs(
325 const map<string, ::DBus::Variant> &properties,
326 Metrics::WiFiNetworkPhyMode *phy_mode,
327 VendorInformation *vendor_information) {
328
329 map<string, ::DBus::Variant>::const_iterator ies_property =
330 properties.find(wpa_supplicant::kBSSPropertyIEs);
331 if (ies_property == properties.end()) {
332 SLOG(WiFi, 2) << __func__ << ": No IE property in BSS.";
333 return false;
Thieu Le1df7f4e2012-02-10 15:21:45 -0800334 }
335
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700336 vector<uint8_t> ies = ies_property->second.operator vector<uint8_t>();
337
338
339 // Format of an information element:
340 // 1 1 1 - 252
341 // +------+--------+----------------+
342 // | Type | Length | Data |
343 // +------+--------+----------------+
344 *phy_mode = Metrics::kWiFiNetworkPhyModeUndef;
345 bool found_ht = false;
346 bool found_erp = false;
347 int ie_len = 0;
348 vector<uint8_t>::iterator it;
349 for (it = ies.begin();
350 std::distance(it, ies.end()) > 1; // Ensure Length field is within PDU.
351 it += ie_len) {
352 ie_len = 2 + *(it + 1);
353 if (std::distance(it, ies.end()) < ie_len) {
354 LOG(ERROR) << __func__ << ": IE extends past containing PDU.";
355 break;
356 }
357 switch (*it) {
358 case IEEE_80211::kElemIdErp:
359 if (!found_ht) {
360 *phy_mode = Metrics::kWiFiNetworkPhyMode11g;
361 }
362 found_erp = true;
363 break;
364 case IEEE_80211::kElemIdHTCap:
365 case IEEE_80211::kElemIdHTInfo:
366 *phy_mode = Metrics::kWiFiNetworkPhyMode11n;
367 found_ht = true;
368 break;
369 case IEEE_80211::kElemIdVendor:
370 ParseVendorIE(it + 2, it + ie_len, vendor_information);
371 break;
372 }
373 }
374 return found_ht || found_erp;
375}
376
377// static
378void WiFiEndpoint::ParseVendorIE(vector<uint8_t>::const_iterator ie,
379 vector<uint8_t>::const_iterator end,
380 VendorInformation *vendor_information) {
381 // Format of an vendor-specific information element (with type
382 // and length field for the IE removed by the caller):
383 // 3 1 1 - 248
384 // +------------+----------+----------------+
385 // | OUI | OUI Type | Data |
386 // +------------+----------+----------------+
387
388 if (std::distance(ie, end) < 4) {
389 LOG(ERROR) << __func__ << ": no room in IE for OUI and type field.";
390 return;
391 }
392 uint32_t oui = (*ie << 16) | (*(ie + 1) << 8) | *(ie + 2);
393 uint8_t oui_type = *(ie + 3);
394 ie += 4;
395
396 if (oui == IEEE_80211::kOUIVendorMicrosoft &&
397 oui_type == IEEE_80211::kOUIMicrosoftWPS) {
398 // Format of a WPS data element:
399 // 2 2
400 // +------+--------+----------------+
401 // | Type | Length | Data |
402 // +------+--------+----------------+
403 while (std::distance(ie, end) >= 4) {
404 int element_type = (*ie << 8) | *(ie + 1);
405 int element_length = (*(ie + 2) << 8) | *(ie + 3);
406 ie += 4;
407 if (std::distance(ie, end) < element_length) {
408 LOG(ERROR) << __func__ << ": WPS element extends past containing PDU.";
409 break;
410 }
411 string s(ie, ie + element_length);
412 if (IsStringASCII(s)) {
413 switch (element_type) {
414 case IEEE_80211::kWPSElementManufacturer:
415 vendor_information->wps_manufacturer = s;
416 break;
417 case IEEE_80211::kWPSElementModelName:
418 vendor_information->wps_model_name = s;
419 break;
420 case IEEE_80211::kWPSElementModelNumber:
421 vendor_information->wps_model_number = s;
422 break;
423 case IEEE_80211::kWPSElementDeviceName:
424 vendor_information->wps_device_name = s;
425 break;
426 }
427 }
428 ie += element_length;
429 }
430 } else if (oui != IEEE_80211::kOUIVendorEpigram &&
431 oui != IEEE_80211::kOUIVendorMicrosoft) {
432 vendor_information->oui_list.insert(oui);
433 }
Thieu Le1df7f4e2012-02-10 15:21:45 -0800434}
435
mukesh agrawalb54601c2011-06-07 17:39:22 -0700436} // namespace shill