blob: e22bb9f68208cb8fc2bbf263dd3c3a0d72b8ed4d [file] [log] [blame]
Paul Stewartb50f0b92011-05-16 16:31:42 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Chris Masone2b105542011-06-22 10:58:09 -07005#include "shill/wifi.h"
6
Paul Stewartb50f0b92011-05-16 16:31:42 -07007#include <time.h>
8#include <stdio.h>
mukesh agrawalc7426a42011-06-03 13:04:28 -07009#include <string.h>
mukesh agrawalf2f68a52011-09-01 12:15:48 -070010#include <netinet/ether.h>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -070011#include <linux/if.h> // Needs definitions from netinet/ether.h
Paul Stewartb50f0b92011-05-16 16:31:42 -070012
mukesh agrawalab87ea42011-05-18 11:44:49 -070013#include <map>
Paul Stewartb50f0b92011-05-16 16:31:42 -070014#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070015#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016
17#include <base/logging.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070018#include <base/string_number_conversions.h>
19#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070020#include <chromeos/dbus/service_constants.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070021
22#include "shill/control_interface.h"
23#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070024#include "shill/error.h"
25#include "shill/key_value_store.h"
26#include "shill/ieee80211.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070027#include "shill/manager.h"
28#include "shill/profile.h"
Darin Petkovd1967262011-07-18 14:55:18 -070029#include "shill/proxy_factory.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070030#include "shill/shill_event.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070031#include "shill/supplicant_interface_proxy_interface.h"
32#include "shill/supplicant_process_proxy_interface.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070033#include "shill/wifi_endpoint.h"
34#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070035#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070036
mukesh agrawal7a4e4002011-09-06 11:26:05 -070037using std::map;
mukesh agrawalab87ea42011-05-18 11:44:49 -070038using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070039using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070040
Paul Stewartb50f0b92011-05-16 16:31:42 -070041namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070042
43// statics
44//
45// Note that WiFi generates some manager-level errors, because it implements
46// the Manager.GetWiFiService flimflam API. The API is implemented here,
47// rather than in manager, to keep WiFi-specific logic in the right place.
48const char WiFi::kManagerErrorPassphraseRequired[] = "must specify passphrase";
49const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
50const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
51const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
52const char WiFi::kManagerErrorTypeRequired[] = "must specify service type";
53const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
54 "security mode is unsupported";
55const char WiFi::kManagerErrorUnsupportedServiceType[] =
56 "service type is unsupported";
57const char WiFi::kManagerErrorUnsupportedServiceMode[] =
58 "service mode is unsupported";
mukesh agrawalb54601c2011-06-07 17:39:22 -070059
mukesh agrawalab87ea42011-05-18 11:44:49 -070060// NB: we assume supplicant is already running. [quiche.20110518]
Paul Stewartb50f0b92011-05-16 16:31:42 -070061WiFi::WiFi(ControlInterface *control_interface,
62 EventDispatcher *dispatcher,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070063 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070064 const string& link,
Chris Masone626719f2011-08-18 16:58:48 -070065 const std::string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070066 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070067 : Device(control_interface,
68 dispatcher,
69 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070070 link,
Chris Masone626719f2011-08-18 16:58:48 -070071 address,
mukesh agrawalab87ea42011-05-18 11:44:49 -070072 interface_index),
Darin Petkovab565bb2011-10-06 02:55:51 -070073 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawalb54601c2011-06-07 17:39:22 -070074 task_factory_(this),
Chris Masone853b81b2011-06-24 14:11:41 -070075 bgscan_short_interval_(0),
76 bgscan_signal_threshold_(0),
77 scan_pending_(false),
mukesh agrawalf2f68a52011-09-01 12:15:48 -070078 scan_interval_(0),
79 link_up_(false) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070080 PropertyStore *store = this->mutable_store();
Paul Stewartac4ac002011-08-26 12:04:26 -070081 store->RegisterString(flimflam::kBgscanMethodProperty, &bgscan_method_);
82 store->RegisterUint16(flimflam::kBgscanShortIntervalProperty,
Chris Masone853b81b2011-06-24 14:11:41 -070083 &bgscan_short_interval_);
Paul Stewartac4ac002011-08-26 12:04:26 -070084 store->RegisterInt32(flimflam::kBgscanSignalThresholdProperty,
Chris Masone853b81b2011-06-24 14:11:41 -070085 &bgscan_signal_threshold_);
86
Chris Masoneb925cc82011-06-22 15:39:57 -070087 // TODO(quiche): Decide if scan_pending_ is close enough to
88 // "currently scanning" that we don't care, or if we want to track
89 // scan pending/currently scanning/no scan scheduled as a tri-state
90 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -070091 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
92 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
93 VLOG(2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -070094}
95
mukesh agrawalaf571952011-07-14 14:31:12 -070096WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -070097
mukesh agrawalab87ea42011-05-18 11:44:49 -070098void WiFi::Start() {
mukesh agrawalab87ea42011-05-18 11:44:49 -070099 ::DBus::Path interface_path;
100
mukesh agrawalaf571952011-07-14 14:31:12 -0700101 supplicant_process_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700102 proxy_factory_->CreateSupplicantProcessProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700103 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700104 try {
105 std::map<string, DBus::Variant> create_interface_args;
106 create_interface_args["Ifname"].writer().
Paul Stewartac4ac002011-08-26 12:04:26 -0700107 append_string(link_name().c_str());
mukesh agrawalc7426a42011-06-03 13:04:28 -0700108 create_interface_args["Driver"].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700109 append_string(wpa_supplicant::kDriverNL80211);
mukesh agrawalc7426a42011-06-03 13:04:28 -0700110 // TODO(quiche) create_interface_args["ConfigFile"].writer().append_string
111 // (file with pkcs config info)
112 interface_path =
113 supplicant_process_proxy_->CreateInterface(create_interface_args);
114 } catch (const DBus::Error e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700115 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
mukesh agrawalc7426a42011-06-03 13:04:28 -0700116 interface_path =
Paul Stewartac4ac002011-08-26 12:04:26 -0700117 supplicant_process_proxy_->GetInterface(link_name());
mukesh agrawalb54601c2011-06-07 17:39:22 -0700118 // XXX crash here, if device missing?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700119 } else {
120 // XXX
121 }
122 }
123
mukesh agrawalab87ea42011-05-18 11:44:49 -0700124 supplicant_interface_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700125 proxy_factory_->CreateSupplicantInterfaceProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700126 this, interface_path, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700127
mukesh agrawalab87ea42011-05-18 11:44:49 -0700128 // TODO(quiche) set ApScan=1 and BSSExpireAge=190, like flimflam does?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700129
130 // clear out any networks that might previously have been configured
131 // for this interface.
132 supplicant_interface_proxy_->RemoveAllNetworks();
133
134 // flush interface's BSS cache, so that we get BSSAdded signals for
135 // all BSSes (not just new ones since the last scan).
136 supplicant_interface_proxy_->FlushBSS(0);
137
Darin Petkovc0865312011-09-16 15:31:20 -0700138 Scan(NULL);
mukesh agrawalab87ea42011-05-18 11:44:49 -0700139 Device::Start();
140}
141
mukesh agrawalab87ea42011-05-18 11:44:49 -0700142void WiFi::Stop() {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700143 VLOG(2) << "WiFi " << link_name() << " stopping.";
mukesh agrawal31950242011-07-14 11:53:38 -0700144 // TODO(quiche): remove interface from supplicant
145 supplicant_interface_proxy_.reset(); // breaks a reference cycle
146 supplicant_process_proxy_.reset();
147 endpoint_by_bssid_.clear();
148 service_by_private_id_.clear(); // breaks reference cycles
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700149
150 for (std::vector<ServiceRefPtr>::const_iterator it = services()->begin();
151 it != services()->end();
152 ++it) {
153 manager()->DeregisterService(*it);
154 }
155 services()->clear(); // breaks reference cycles
156
mukesh agrawalab87ea42011-05-18 11:44:49 -0700157 Device::Stop();
mukesh agrawal31950242011-07-14 11:53:38 -0700158 // XXX anything else to do?
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700159
160 VLOG(3) << "WiFi " << link_name() << " task_factory_ "
161 << (task_factory_.empty() ? "is empty." : "is not empty.");
162 VLOG(3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
163 << (supplicant_process_proxy_.get() ? "is set." : "is not set.");
164 VLOG(3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
165 << (supplicant_interface_proxy_.get() ? "is set." : "is not set.");
166 VLOG(3) << "WiFi " << link_name() << " has " << endpoint_by_bssid_.size()
167 << " EndpointMap entries.";
168 VLOG(3) << "WiFi " << link_name() << " has " << service_by_private_id_.size()
169 << " ServiceMap entries.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700170}
171
mukesh agrawal1830fa12011-09-26 14:31:40 -0700172void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700173 LOG(INFO) << __func__;
174
175 // needs to send a D-Bus message, but may be called from D-Bus
176 // signal handler context (via Manager::RequestScan). so defer work
177 // to event loop.
178 dispatcher()->PostTask(
179 task_factory_.NewRunnableMethod(&WiFi::ScanTask));
180}
181
Paul Stewartfdd16072011-09-16 12:41:35 -0700182bool WiFi::TechnologyIs(const Technology::Identifier type) const {
183 return type == Technology::kWifi;
Paul Stewartb50f0b92011-05-16 16:31:42 -0700184}
185
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700186void WiFi::LinkEvent(unsigned int flags, unsigned int change) {
187 // TODO(quiche): figure out how to relate these events to supplicant
188 // events. e.g., may be we can ignore LinkEvent, in favor of events
189 // from SupplicantInterfaceProxy?
190 Device::LinkEvent(flags, change);
191 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
192 LOG(INFO) << link_name() << " is up; should start L3!";
193 link_up_ = true;
194 if (AcquireDHCPConfig()) {
195 SetServiceState(Service::kStateConfiguring);
196 } else {
197 LOG(ERROR) << "Unable to acquire DHCP config.";
198 }
199 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700200 LOG(INFO) << link_name() << " is down";
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700201 link_up_ = false;
202 // TODO(quiche): attempt to reconnect to current SSID, another SSID,
203 // or initiate a scan.
204 }
205}
206
mukesh agrawalb54601c2011-06-07 17:39:22 -0700207void WiFi::BSSAdded(
mukesh agrawal1830fa12011-09-26 14:31:40 -0700208 const ::DBus::Path &/*BSS*/,
mukesh agrawalb54601c2011-06-07 17:39:22 -0700209 const std::map<string, ::DBus::Variant> &properties) {
210 // TODO(quiche): write test to verify correct behavior in the case
211 // where we get multiple BSSAdded events for a single endpoint.
212 // (old Endpoint's refcount should fall to zero, and old Endpoint
213 // should be destroyed)
214 //
215 // note: we assume that BSSIDs are unique across endpoints. this
216 // means that if an AP reuses the same BSSID for multiple SSIDs, we
217 // lose.
218 WiFiEndpointRefPtr endpoint(new WiFiEndpoint(properties));
219 endpoint_by_bssid_[endpoint->bssid_hex()] = endpoint;
220}
221
222void WiFi::ScanDone() {
223 LOG(INFO) << __func__;
224
225 // defer handling of scan result processing, because that processing
226 // may require the the registration of new D-Bus objects. and such
227 // registration can't be done in the context of a D-Bus signal
228 // handler.
Paul Stewartac4ac002011-08-26 12:04:26 -0700229 dispatcher()->PostTask(
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700230 task_factory_.NewRunnableMethod(&WiFi::ScanDoneTask));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700231}
232
mukesh agrawal6e277772011-09-29 15:04:23 -0700233void WiFi::ConnectTo(WiFiService *service,
234 const map<string, DBus::Variant> &service_params) {
mukesh agrawal445e72c2011-06-22 11:13:50 -0700235 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700236
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700237 // TODO(quiche): handle cases where already connected
mukesh agrawal32399322011-09-01 10:53:43 -0700238
mukesh agrawal6e277772011-09-29 15:04:23 -0700239 // TODO(quiche): set scan_ssid=1 in service_params, like flimflam does?
240 try {
241 network_path =
242 supplicant_interface_proxy_->AddNetwork(service_params);
243 } catch (const DBus::Error e) { // NOLINT
244 LOG(ERROR) << "exception while adding network: " << e.what();
245 return;
246 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700247
mukesh agrawal445e72c2011-06-22 11:13:50 -0700248 supplicant_interface_proxy_->SelectNetwork(network_path);
249 // XXX add to favorite networks list?
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700250
251 // SelectService here (instead of in LinkEvent, like Ethernet), so
252 // that, if we fail to bring up L2, we can attribute failure correctly.
253 //
254 // TODO(quiche): when we add code for dealing with connection failures,
255 // reconsider if this is the right place to change the selected service.
256 // see discussion in crosbug.com/20191.
257 SelectService(service);
mukesh agrawalb54601c2011-06-07 17:39:22 -0700258}
259
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700260void WiFi::ScanDoneTask() {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700261 LOG(INFO) << __func__;
262
263 scan_pending_ = false;
264
265 // TODO(quiche): group endpoints into services, instead of creating
266 // a service for every endpoint.
267 for (EndpointMap::iterator i(endpoint_by_bssid_.begin());
268 i != endpoint_by_bssid_.end(); ++i) {
269 const WiFiEndpoint &endpoint(*(i->second));
270 string service_id_private;
271
272 service_id_private =
273 endpoint.ssid_hex() + "_" + endpoint.bssid_hex();
274 if (service_by_private_id_.find(service_id_private) ==
275 service_by_private_id_.end()) {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700276 LOG(INFO) << "found new endpoint. "
277 << "ssid: " << endpoint.ssid_string() << ", "
278 << "bssid: " << endpoint.bssid_string() << ", "
mukesh agrawal6e277772011-09-29 15:04:23 -0700279 << "signal: " << endpoint.signal_strength() << ", "
280 << "security: " << endpoint.security_mode();
mukesh agrawalb54601c2011-06-07 17:39:22 -0700281
mukesh agrawal31950242011-07-14 11:53:38 -0700282 WiFiServiceRefPtr service(
Paul Stewartac4ac002011-08-26 12:04:26 -0700283 new WiFiService(control_interface(),
284 dispatcher(),
285 manager(),
Chris Masone7aa5f902011-07-11 11:13:35 -0700286 this,
Chris Masone7aa5f902011-07-11 11:13:35 -0700287 endpoint.ssid(),
288 endpoint.network_mode(),
mukesh agrawal6e277772011-09-29 15:04:23 -0700289 endpoint.security_mode()));
Paul Stewartac4ac002011-08-26 12:04:26 -0700290 services()->push_back(service);
mukesh agrawalb54601c2011-06-07 17:39:22 -0700291 service_by_private_id_[service_id_private] = service;
mukesh agrawal32399322011-09-01 10:53:43 -0700292 manager()->RegisterService(service);
mukesh agrawal51a7e932011-07-27 16:18:26 -0700293
294 LOG(INFO) << "new service " << service->GetRpcIdentifier();
mukesh agrawalb54601c2011-06-07 17:39:22 -0700295 }
296 }
297
mukesh agrawalb54601c2011-06-07 17:39:22 -0700298 // TODO(quiche): unregister removed services from manager
299}
300
mukesh agrawal32399322011-09-01 10:53:43 -0700301void WiFi::ScanTask() {
302 VLOG(2) << "WiFi " << link_name() << " scan requested.";
303 std::map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -0700304 scan_args[wpa_supplicant::kPropertyScanType].writer().
305 append_string(wpa_supplicant::kScanTypeActive);
mukesh agrawal32399322011-09-01 10:53:43 -0700306 // TODO(quiche) indicate scanning in UI
307 supplicant_interface_proxy_->Scan(scan_args);
308 scan_pending_ = true;
309}
310
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700311// used by manager, via static WiFi::GetService method
312WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
313 if (!args.ContainsString(flimflam::kTypeProperty)) {
314 error->Populate(Error::kInvalidArguments, kManagerErrorTypeRequired);
315 return NULL;
316 }
317
318 if (args.GetString(flimflam::kTypeProperty) != flimflam::kTypeWifi) {
319 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceType);
320 return NULL;
321 }
322
323 if (args.ContainsString(flimflam::kModeProperty) &&
324 args.GetString(flimflam::kModeProperty) !=
325 flimflam::kModeManaged) {
326 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceMode);
327 return NULL;
328 }
329
330 if (!args.ContainsString(flimflam::kSSIDProperty)) {
331 error->Populate(Error::kInvalidArguments, kManagerErrorSSIDRequired);
332 return NULL;
333 }
334
335 string ssid = args.GetString(flimflam::kSSIDProperty);
336 if (ssid.length() < 1) {
337 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooShort);
338 return NULL;
339 }
340
341 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
342 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooLong);
343 return NULL;
344 }
345
346 string security_method;
347 if (args.ContainsString(flimflam::kSecurityProperty)) {
348 security_method = args.GetString(flimflam::kSecurityProperty);
349 } else {
350 security_method = flimflam::kSecurityNone;
351 }
352
353 if (security_method != flimflam::kSecurityNone &&
354 security_method != flimflam::kSecurityWep &&
355 security_method != flimflam::kSecurityPsk &&
356 security_method != flimflam::kSecurityWpa &&
357 security_method != flimflam::kSecurityRsn &&
358 security_method != flimflam::kSecurity8021x) {
359 error->Populate(Error::kNotSupported,
360 kManagerErrorUnsupportedSecurityMode);
361 return NULL;
362 }
363
364 if ((security_method == flimflam::kSecurityWep ||
365 security_method == flimflam::kSecurityPsk ||
366 security_method == flimflam::kSecurityWpa ||
367 security_method == flimflam::kSecurityRsn) &&
368 !args.ContainsString(flimflam::kPassphraseProperty)) {
369 error->Populate(Error::kInvalidArguments,
370 kManagerErrorPassphraseRequired);
371 return NULL;
372 }
373
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700374 WiFiService *service = NULL;
375
376 // TODO(quiche): search for existing service
377
378 if (service == NULL) {
mukesh agrawal1a056262011-10-05 14:36:54 -0700379 service = new WiFiService(control_interface(),
380 dispatcher(),
381 manager(),
382 this,
383 vector<uint8_t>(ssid.begin(), ssid.end()),
384 flimflam::kModeManaged,
385 security_method);
386 services()->push_back(service);
387 // TODO(quiche): add to service_by_private_id_?
388 // TODO(quiche): register service with manager
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700389 }
390
mukesh agrawal1a056262011-10-05 14:36:54 -0700391 if (security_method == flimflam::kSecurityWep ||
392 security_method == flimflam::kSecurityPsk ||
393 security_method == flimflam::kSecurityWpa ||
394 security_method == flimflam::kSecurityRsn) {
395 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
396 error);
397 if (error->IsFailure()) {
398 return NULL;
399 }
400 }
401
402 // TODO(quiche): apply any other configuration parameters
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700403
404 return service;
405}
406
Paul Stewartb50f0b92011-05-16 16:31:42 -0700407} // namespace shill