blob: 7226bd5256a2688c5fdcfc09942c073ff6f40d57 [file] [log] [blame]
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartb50f0b92011-05-16 16:31:42 -07002// 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 agrawal8a3188d2011-12-01 20:56:44 +000013#include <algorithm>
mukesh agrawalab87ea42011-05-18 11:44:49 -070014#include <map>
Paul Stewartced6a0b2011-11-08 15:32:04 -080015#include <set>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070017#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070018
19#include <base/logging.h>
mukesh agrawal15908392011-11-16 18:29:25 +000020#include <base/stringprintf.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070021#include <base/string_number_conversions.h>
22#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070023#include <chromeos/dbus/service_constants.h>
mukesh agrawal16bc1b82012-02-09 18:38:26 -080024#include <glib.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070025
26#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080027#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070028#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070029#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070030#include "shill/event_dispatcher.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070031#include "shill/key_value_store.h"
32#include "shill/ieee80211.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070033#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000034#include "shill/metrics.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070035#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080036#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070037#include "shill/proxy_factory.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080038#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070039#include "shill/supplicant_interface_proxy_interface.h"
40#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080041#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070042#include "shill/wifi_endpoint.h"
43#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070044#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070045
mukesh agrawal15908392011-11-16 18:29:25 +000046using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070047using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080048using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070049using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070050using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070051
Paul Stewartb50f0b92011-05-16 16:31:42 -070052namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070053
54// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080055const char *WiFi::kDefaultBgscanMethod =
56 wpa_supplicant::kNetworkBgscanMethodSimple;
57const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
58const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
59const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070060// Note that WiFi generates some manager-level errors, because it implements
61// the Manager.GetWiFiService flimflam API. The API is implemented here,
62// rather than in manager, to keep WiFi-specific logic in the right place.
63const char WiFi::kManagerErrorPassphraseRequired[] = "must specify passphrase";
64const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
65const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
66const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
67const char WiFi::kManagerErrorTypeRequired[] = "must specify service type";
68const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
69 "security mode is unsupported";
70const char WiFi::kManagerErrorUnsupportedServiceType[] =
71 "service type is unsupported";
72const char WiFi::kManagerErrorUnsupportedServiceMode[] =
73 "service mode is unsupported";
mukesh agrawal7ec71312011-11-10 02:08:26 +000074const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalb54601c2011-06-07 17:39:22 -070075
Paul Stewartb50f0b92011-05-16 16:31:42 -070076WiFi::WiFi(ControlInterface *control_interface,
77 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080078 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070079 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070080 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080081 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070082 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070083 : Device(control_interface,
84 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080085 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070086 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070087 link,
Chris Masone626719f2011-08-18 16:58:48 -070088 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080089 interface_index,
90 Technology::kWifi),
Darin Petkovab565bb2011-10-06 02:55:51 -070091 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawalb54601c2011-06-07 17:39:22 -070092 task_factory_(this),
mukesh agrawal15908392011-11-16 18:29:25 +000093 supplicant_state_(kInterfaceStateUnknown),
94 supplicant_bss_("(unknown)"),
Paul Stewart66c86002012-01-30 18:00:52 -080095 clear_cached_credentials_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -080096 bgscan_method_(kDefaultBgscanMethod),
97 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
98 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -070099 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800100 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700101 PropertyStore *store = this->mutable_store();
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800102 HelpRegisterDerivedString(store,
103 flimflam::kBgscanMethodProperty,
104 &WiFi::GetBgscanMethod,
105 &WiFi::SetBgscanMethod);
106 HelpRegisterDerivedUint16(store,
107 flimflam::kBgscanShortIntervalProperty,
108 &WiFi::GetBgscanShortInterval,
109 &WiFi::SetBgscanShortInterval);
110 HelpRegisterDerivedInt32(store,
111 flimflam::kBgscanSignalThresholdProperty,
112 &WiFi::GetBgscanSignalThreshold,
113 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700114
Chris Masoneb925cc82011-06-22 15:39:57 -0700115 // TODO(quiche): Decide if scan_pending_ is close enough to
116 // "currently scanning" that we don't care, or if we want to track
117 // scan pending/currently scanning/no scan scheduled as a tri-state
118 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700119 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800120 HelpRegisterDerivedUint16(store,
121 flimflam::kScanIntervalProperty,
122 &WiFi::GetScanInterval,
123 &WiFi::SetScanInterval);
Paul Stewartac4ac002011-08-26 12:04:26 -0700124 VLOG(2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700125}
126
mukesh agrawalaf571952011-07-14 14:31:12 -0700127WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700128
mukesh agrawalab87ea42011-05-18 11:44:49 -0700129void WiFi::Start() {
mukesh agrawalab87ea42011-05-18 11:44:49 -0700130 ::DBus::Path interface_path;
131
mukesh agrawalaf571952011-07-14 14:31:12 -0700132 supplicant_process_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700133 proxy_factory_->CreateSupplicantProcessProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700134 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700135 try {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800136 map<string, DBus::Variant> create_interface_args;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700137 create_interface_args["Ifname"].writer().
Paul Stewartac4ac002011-08-26 12:04:26 -0700138 append_string(link_name().c_str());
mukesh agrawalc7426a42011-06-03 13:04:28 -0700139 create_interface_args["Driver"].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700140 append_string(wpa_supplicant::kDriverNL80211);
mukesh agrawalc7426a42011-06-03 13:04:28 -0700141 // TODO(quiche) create_interface_args["ConfigFile"].writer().append_string
142 // (file with pkcs config info)
143 interface_path =
144 supplicant_process_proxy_->CreateInterface(create_interface_args);
145 } catch (const DBus::Error e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700146 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
mukesh agrawalc7426a42011-06-03 13:04:28 -0700147 interface_path =
Paul Stewartac4ac002011-08-26 12:04:26 -0700148 supplicant_process_proxy_->GetInterface(link_name());
mukesh agrawal7ec71312011-11-10 02:08:26 +0000149 // TODO(quiche): Is it okay to crash here, if device is missing?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700150 } else {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000151 // TODO(quiche): Log error.
mukesh agrawalc7426a42011-06-03 13:04:28 -0700152 }
153 }
154
mukesh agrawalab87ea42011-05-18 11:44:49 -0700155 supplicant_interface_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700156 proxy_factory_->CreateSupplicantInterfaceProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700157 this, interface_path, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700158
mukesh agrawal7ec71312011-11-10 02:08:26 +0000159 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700160
mukesh agrawal7ec71312011-11-10 02:08:26 +0000161 // Clear out any networks that might previously have been configured
mukesh agrawalc7426a42011-06-03 13:04:28 -0700162 // for this interface.
163 supplicant_interface_proxy_->RemoveAllNetworks();
164
mukesh agrawal7ec71312011-11-10 02:08:26 +0000165 // Flush interface's BSS cache, so that we get BSSAdded signals for
mukesh agrawalc7426a42011-06-03 13:04:28 -0700166 // all BSSes (not just new ones since the last scan).
167 supplicant_interface_proxy_->FlushBSS(0);
168
mukesh agrawal23e9f282012-02-17 15:36:36 -0800169 try {
170 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
171 // with RADIUS servers that respond strangely to such requests.
172 // crosbug.com/25630
173 supplicant_interface_proxy_->SetFastReauth(false);
174 } catch (const DBus::Error e) { // NOLINT
175 LOG(INFO) << "Failed to disable fast_reauth. "
176 << "May be running an older version of wpa_supplicant.";
177 }
Paul Stewart2987dcf2012-01-30 15:47:42 -0800178
Darin Petkovc0865312011-09-16 15:31:20 -0700179 Scan(NULL);
mukesh agrawalab87ea42011-05-18 11:44:49 -0700180 Device::Start();
181}
182
mukesh agrawalab87ea42011-05-18 11:44:49 -0700183void WiFi::Stop() {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700184 VLOG(2) << "WiFi " << link_name() << " stopping.";
mukesh agrawal7ec71312011-11-10 02:08:26 +0000185 // TODO(quiche): Remove interface from supplicant.
mukesh agrawal31950242011-07-14 11:53:38 -0700186 supplicant_interface_proxy_.reset(); // breaks a reference cycle
187 supplicant_process_proxy_.reset();
mukesh agrawal15908392011-11-16 18:29:25 +0000188 endpoint_by_rpcid_.clear();
mukesh agrawal15908392011-11-16 18:29:25 +0000189 rpcid_by_service_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700190
Paul Stewartced6a0b2011-11-08 15:32:04 -0800191 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
192 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700193 ++it) {
mukesh agrawalb20776f2012-02-10 16:00:36 -0800194 VLOG(3) << "WiFi " << link_name() << " deregistering service "
195 << (*it)->friendly_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700196 manager()->DeregisterService(*it);
197 }
Paul Stewartced6a0b2011-11-08 15:32:04 -0800198 services_.clear(); // breaks reference cycles
mukesh agrawalb20776f2012-02-10 16:00:36 -0800199 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000200 pending_service_ = NULL; // breaks a reference cycle
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700201
mukesh agrawalab87ea42011-05-18 11:44:49 -0700202 Device::Stop();
mukesh agrawal7ec71312011-11-10 02:08:26 +0000203 // TODO(quiche): Anything else to do?
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700204
205 VLOG(3) << "WiFi " << link_name() << " task_factory_ "
206 << (task_factory_.empty() ? "is empty." : "is not empty.");
207 VLOG(3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
208 << (supplicant_process_proxy_.get() ? "is set." : "is not set.");
209 VLOG(3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
210 << (supplicant_interface_proxy_.get() ? "is set." : "is not set.");
mukesh agrawal7ec71312011-11-10 02:08:26 +0000211 VLOG(3) << "WiFi " << link_name() << " pending_service_ "
212 << (pending_service_.get() ? "is set." : "is not set.");
mukesh agrawal165e6142011-11-22 02:22:56 +0000213 VLOG(3) << "WiFi " << link_name() << " has " << endpoint_by_rpcid_.size()
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700214 << " EndpointMap entries.";
mukesh agrawal165e6142011-11-22 02:22:56 +0000215 VLOG(3) << "WiFi " << link_name() << " has " << services_.size()
216 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700217}
218
Paul Stewarta41e38d2011-11-11 07:47:29 -0800219bool WiFi::Load(StoreInterface *storage) {
220 LoadHiddenServices(storage);
221 return Device::Load(storage);
222}
223
mukesh agrawal1830fa12011-09-26 14:31:40 -0700224void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700225 LOG(INFO) << __func__;
226
mukesh agrawal7ec71312011-11-10 02:08:26 +0000227 // Needs to send a D-Bus message, but may be called from D-Bus
228 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700229 // to event loop.
230 dispatcher()->PostTask(
231 task_factory_.NewRunnableMethod(&WiFi::ScanTask));
232}
233
Paul Stewartfdd16072011-09-16 12:41:35 -0700234bool WiFi::TechnologyIs(const Technology::Identifier type) const {
235 return type == Technology::kWifi;
Paul Stewartb50f0b92011-05-16 16:31:42 -0700236}
237
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000238bool WiFi::IsConnectingTo(const WiFiService &service) const {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800239 return pending_service_ == &service ||
240 (current_service_ == &service &&
241 service.state() != Service::kStateConnected);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000242}
243
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000244void WiFi::BSSAdded(const ::DBus::Path &path,
245 const map<string, ::DBus::Variant> &properties) {
246 // Called from a D-Bus signal handler, and nay need to send a D-Bus
247 // message. So defer work to event loop.
248 dispatcher()->PostTask(
249 task_factory_.NewRunnableMethod(&WiFi::BSSAddedTask, path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000250}
251
252void WiFi::BSSRemoved(const ::DBus::Path &path) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000253 // Called from a D-Bus signal handler, and nay need to send a D-Bus
254 // message. So defer work to event loop.
255 dispatcher()->PostTask(
256 task_factory_.NewRunnableMethod(&WiFi::BSSRemovedTask, path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700257}
258
mukesh agrawal7ec71312011-11-10 02:08:26 +0000259void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
mukesh agrawal15908392011-11-16 18:29:25 +0000260 LOG(INFO) << "In " << __func__ << "(): called";
261 // Called from D-Bus signal handler, but may need to send a D-Bus
262 // message. So defer work to event loop.
263 dispatcher()->PostTask(
264 task_factory_.NewRunnableMethod(&WiFi::PropertiesChangedTask,
265 properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000266}
267
mukesh agrawalb54601c2011-06-07 17:39:22 -0700268void WiFi::ScanDone() {
269 LOG(INFO) << __func__;
270
mukesh agrawal7ec71312011-11-10 02:08:26 +0000271 // Defer handling of scan result processing, because that processing
272 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700273 // registration can't be done in the context of a D-Bus signal
274 // handler.
Paul Stewartac4ac002011-08-26 12:04:26 -0700275 dispatcher()->PostTask(
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700276 task_factory_.NewRunnableMethod(&WiFi::ScanDoneTask));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700277}
278
mukesh agrawal6e277772011-09-29 15:04:23 -0700279void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000280 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800281 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700282 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700283
mukesh agrawal7ec71312011-11-10 02:08:26 +0000284 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000285 if (pending_service_ && pending_service_ == service) {
286 // TODO(quiche): Return an error to the caller. crosbug.com/23832
287 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
288 << service->friendly_name()
289 << ", which is already pending.";
290 return;
291 }
292
293 if (pending_service_ && pending_service_ != service) {
294 DisconnectFrom(pending_service_);
295 }
mukesh agrawal32399322011-09-01 10:53:43 -0700296
mukesh agrawal6e277772011-09-29 15:04:23 -0700297 try {
mukesh agrawal15908392011-11-16 18:29:25 +0000298 // TODO(quiche): Set a timeout here. In the normal case, we expect
299 // that, if wpa_supplicant fails to connect, it will eventually send
300 // a signal that its CurrentBSS has changed. But there may be cases
301 // where the signal is not sent. (crosbug.com/23206)
mukesh agrawal64896322011-12-01 01:13:10 +0000302 const uint32_t scan_ssid = 1; // "True": Use directed probe.
303 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
304 append_uint32(scan_ssid);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800305 service_params[wpa_supplicant::kNetworkPropertyBgscan].writer().
306 append_string(CreateBgscanConfigString().c_str());
mukesh agrawal6e277772011-09-29 15:04:23 -0700307 network_path =
308 supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000309 rpcid_by_service_[service] = network_path;
mukesh agrawal6e277772011-09-29 15:04:23 -0700310 } catch (const DBus::Error e) { // NOLINT
311 LOG(ERROR) << "exception while adding network: " << e.what();
312 return;
313 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700314
mukesh agrawal445e72c2011-06-22 11:13:50 -0700315 supplicant_interface_proxy_->SelectNetwork(network_path);
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700316
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000317 pending_service_ = service;
318 CHECK(current_service_.get() != pending_service_.get());
319
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700320 // SelectService here (instead of in LinkEvent, like Ethernet), so
321 // that, if we fail to bring up L2, we can attribute failure correctly.
322 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000323 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700324 // reconsider if this is the right place to change the selected service.
325 // see discussion in crosbug.com/20191.
326 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000327}
328
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000329void WiFi::DisconnectFrom(WiFiService *service) {
330 if (service != current_service_ && service != pending_service_) {
331 // TODO(quiche): Once we have asynchronous reply support, we should
332 // generate a D-Bus error here. (crosbug.com/23832)
333 LOG(WARNING) << "In " << __func__ << "(): "
334 << " ignoring request to disconnect from service "
335 << service->friendly_name()
336 << " which is neither current nor pending";
337 return;
338 }
339
340 if (pending_service_ && service != pending_service_) {
341 // TODO(quiche): Once we have asynchronous reply support, we should
342 // generate a D-Bus error here. (crosbug.com/23832)
343 LOG(WARNING) << "In " << __func__ << "(): "
344 << " ignoring request to disconnect from service "
345 << service->friendly_name()
346 << " which is not the pending service.";
347 return;
348 }
349
350 if (!pending_service_ && service != current_service_) {
351 // TODO(quiche): Once we have asynchronous reply support, we should
352 // generate a D-Bus error here. (crosbug.com/23832)
353 LOG(WARNING) << "In " << __func__ << "(): "
354 << " ignoring request to disconnect from service "
355 << service->friendly_name()
356 << " which is not the current service.";
357 return;
358 }
359
360 pending_service_ = NULL;
361 try {
362 supplicant_interface_proxy_->Disconnect();
363 // We'll call RemoveNetwork and reset |current_service_| after
364 // supplicant notifies us that the CurrentBSS has changed.
365 } catch (const DBus::Error e) { // NOLINT
366 // Can't depend on getting a notification of CurrentBSS change.
367 // So effect changes immediately.
368 ReverseServiceMap::const_iterator rpcid_it =
369 rpcid_by_service_.find(service);
370 DCHECK(rpcid_it != rpcid_by_service_.end());
371 if (rpcid_it == rpcid_by_service_.end()) {
372 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
373 << service->friendly_name() << ": "
374 << "could not find supplicant network to disable.";
375 } else {
376 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
377 }
378 current_service_ = NULL;
379 }
380
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800381 CHECK(current_service_ == NULL ||
382 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000383}
384
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000385bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800386 return !current_service_ && !pending_service_;
387}
388
Paul Stewart66c86002012-01-30 18:00:52 -0800389void WiFi::ClearCachedCredentials() {
390 LOG(INFO) << __func__;
391
392 // Needs to send a D-Bus message, but may be called from D-Bus
393 // caller context (via Manager::PopProfile). So defer work
394 // to event loop.
395 if (!clear_cached_credentials_pending_) {
396 clear_cached_credentials_pending_ = true;
397 dispatcher()->PostTask(
398 task_factory_.NewRunnableMethod(&WiFi::ClearCachedCredentialsTask));
399 }
400}
401
mukesh agrawalb20776f2012-02-10 16:00:36 -0800402void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
403 WiFiService *service = FindServiceForEndpoint(endpoint);
404 DCHECK(service);
405 if (service) {
406 service->NotifyEndpointUpdated(endpoint);
407 return;
408 }
409}
410
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800411string WiFi::CreateBgscanConfigString() {
412 return StringPrintf("%s:%d:%d:%d",
413 bgscan_method_.c_str(),
414 bgscan_short_interval_seconds_,
415 bgscan_signal_threshold_dbm_,
416 scan_interval_seconds_);
417}
418
419void WiFi::SetBgscanMethod(const string &method, Error *error) {
420 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
421 method != wpa_supplicant::kNetworkBgscanMethodLearn) {
422 const string error_message =
423 StringPrintf("Unrecognized bgscan method %s", method.c_str());
424 LOG(WARNING) << error_message;
425 error->Populate(Error::kInvalidArguments, error_message);
426 return;
427 }
428
429 bgscan_method_ = method;
430 // We do not update kNetworkPropertyBgscan for |pending_service_| or
431 // |current_service_|, because supplicant does not allow for
432 // reconfiguration without disconnect and reconnect.
433}
434
435void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
436 bgscan_short_interval_seconds_ = seconds;
437 // We do not update kNetworkPropertyBgscan for |pending_service_| or
438 // |current_service_|, because supplicant does not allow for
439 // reconfiguration without disconnect and reconnect.
440}
441
442void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
443 bgscan_signal_threshold_dbm_ = dbm;
444 // We do not update kNetworkPropertyBgscan for |pending_service_| or
445 // |current_service_|, because supplicant does not allow for
446 // reconfiguration without disconnect and reconnect.
447}
448
449void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
450 scan_interval_seconds_ = seconds;
451 // We do not update |pending_service_| or |current_service_|, because
452 // supplicant does not allow for reconfiguration without disconnect
453 // and reconnect.
454
455 // TODO(quiche): Update scan timer. crosbug.com/24337
456}
457
mukesh agrawal165e6142011-11-22 02:22:56 +0000458// To avoid creating duplicate services, call FindServiceForEndpoint
459// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000460WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
461 bool hidden_ssid) {
462 WiFiServiceRefPtr service =
463 new WiFiService(control_interface(),
464 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800465 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000466 manager(),
467 this,
468 endpoint.ssid(),
469 endpoint.network_mode(),
470 endpoint.security_mode(),
471 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000472 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000473 return service;
474}
475
476void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
477 VLOG(3) << "WiFi " << link_name() << " CurrentBSS "
478 << supplicant_bss_ << " -> " << new_bss;
479 supplicant_bss_ = new_bss;
480 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
481 HandleDisconnect();
482 } else {
483 HandleRoam(new_bss);
484 }
485
486 SelectService(current_service_);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000487 // Invariant check: a Service can either be current, or pending, but
488 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000489 CHECK(current_service_.get() != pending_service_.get() ||
490 current_service_.get() == NULL);
491
492 // TODO(quiche): Update BSSID property on the Service
493 // (crosbug.com/22377).
494}
495
496void WiFi::HandleDisconnect() {
497 // Identify the affected service. We expect to get a disconnect
498 // event when we fall off a Service that we were connected
499 // to. However, we also allow for the case where we get a disconnect
500 // event while attempting to connect from a disconnected state.
501 WiFiService *affected_service =
502 current_service_.get() ? current_service_.get() : pending_service_.get();
503
504 current_service_ = NULL;
505 if (!affected_service) {
506 VLOG(2) << "WiFi " << link_name()
507 << " disconnected while not connected or connecting";
508 return;
509 }
510
511 ReverseServiceMap::const_iterator rpcid_it =
512 rpcid_by_service_.find(affected_service);
513 if (rpcid_it == rpcid_by_service_.end()) {
514 VLOG(2) << "WiFi " << link_name() << " disconnected from "
515 << " (or failed to connect to) "
516 << affected_service->friendly_name() << ", "
517 << "but could not find supplicant network to disable.";
518 return;
519 }
520
521 VLOG(2) << "WiFi " << link_name() << " disconnected from "
522 << " (or failed to connect to) "
523 << affected_service->friendly_name();
524 // TODO(quiche): Reconsider giving up immediately. Maybe give
525 // wpa_supplicant some time to retry, first.
526 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000527 // TODO(quiche): If we initated the disconnect, we should probably
528 // go to the idle state instead. crosbug.com/24700
529 affected_service->SetFailure(Service::kFailureUnknown);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800530 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000531 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000532
533 if (affected_service == pending_service_.get()) {
534 // The attempt to connect to |pending_service_| failed. Clear
535 // |pending_service_|, to indicate we're no longer in the middle
536 // of a connect request.
537 pending_service_ = NULL;
538 } else if (pending_service_.get()) {
539 // We've attributed the disconnection to what was the
540 // |current_service_|, rather than the |pending_service_|.
541 //
542 // If we're wrong about that (i.e. supplicant reported this
543 // CurrentBSS change after attempting to connect to
544 // |pending_service_|), we're depending on supplicant to retry
545 // connecting to |pending_service_|, and delivering another
546 // CurrentBSS change signal in the future.
547 //
548 // Log this fact, to help us debug (in case our assumptions are
549 // wrong).
550 VLOG(2) << "WiFi " << link_name() << " pending connection to "
551 << pending_service_->friendly_name()
552 << " after disconnect";
553 }
554}
555
556// We use the term "Roam" loosely. In particular, we include the case
557// where we "Roam" to a BSS from the disconnected state.
558void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
559 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
560 if (endpoint_it == endpoint_by_rpcid_.end()) {
561 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
562 << new_bss;
563 return;
564 }
565
566 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000567 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000568 if (!service.get()) {
569 LOG(WARNING) << "WiFi " << link_name()
570 << " could not find Service for Endpoint "
571 << endpoint.bssid_string()
572 << " (service will be unchanged)";
573 return;
574 }
575
576 VLOG(2) << "WiFi " << link_name()
577 << " roamed to Endpoint " << endpoint.bssid_string()
578 << " (SSID " << endpoint.ssid_string() << ")";
579
mukesh agrawale1d90e92012-02-15 17:36:08 -0800580 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000581
mukesh agrawal15908392011-11-16 18:29:25 +0000582 if (pending_service_.get() &&
583 service.get() != pending_service_.get()) {
584 // The Service we've roamed on to is not the one we asked for.
585 // We assume that this is transient, and that wpa_supplicant
586 // is trying / will try to connect to |pending_service_|.
587 //
588 // If it succeeds, we'll end up back here, but with |service|
589 // pointing at the same service as |pending_service_|.
590 //
591 // If it fails, we'll process things in HandleDisconnect.
592 //
593 // So we leave |pending_service_| untouched.
594 VLOG(2) << "WiFi " << link_name()
595 << " new current Endpoint "
596 << endpoint.bssid_string()
597 << " is not part of pending service "
598 << pending_service_->friendly_name();
599
600 // Sanity check: if we didn't roam onto |pending_service_|, we
601 // should still be on |current_service_|.
602 if (service.get() != current_service_.get()) {
603 LOG(WARNING) << "WiFi " << link_name()
604 << " new current Endpoint "
605 << endpoint.bssid_string()
606 << " is neither part of pending service "
607 << pending_service_->friendly_name()
608 << " nor part of current service "
609 << (current_service_.get() ?
610 current_service_->friendly_name() :
611 "(NULL)");
612 // Although we didn't expect to get here, we should keep
613 // |current_service_| in sync with what supplicant has done.
614 current_service_ = service;
615 }
616 return;
617 }
618
619 if (pending_service_.get()) {
620 // We assume service.get() == pending_service_.get() here, because
621 // of the return in the previous if clause.
622 //
623 // Boring case: we've connected to the service we asked
624 // for. Simply update |current_service_| and |pending_service_|.
625 current_service_ = service;
626 pending_service_ = NULL;
627 return;
628 }
629
630 // |pending_service_| was NULL, so we weren't attempting to connect
631 // to a new Service. Sanity check that we're still on
632 // |current_service_|.
633 if (service.get() != current_service_.get()) {
634 LOG(WARNING)
635 << "WiFi " << link_name()
636 << " new current Endpoint "
637 << endpoint.bssid_string()
638 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000639 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000640 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000641 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000642 // We didn't expect to be here, but let's cope as well as we
643 // can. Update |current_service_| to keep it in sync with
644 // supplicant.
645 current_service_ = service;
646 return;
647 }
648
649 // At this point, we know that |pending_service_| was NULL, and that
650 // we're still on |current_service_|. This is the most boring case
651 // of all, because there's no state to update here.
652 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700653}
654
Paul Stewarta41e38d2011-11-11 07:47:29 -0800655WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
656 const string &mode,
657 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800658 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
659 it != services_.end();
660 ++it) {
661 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
662 (*it)->IsSecurityMatch(security)) {
663 return *it;
664 }
665 }
666 return NULL;
667}
668
mukesh agrawal165e6142011-11-22 02:22:56 +0000669WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
670 return FindService(endpoint.ssid(),
671 endpoint.network_mode(),
672 endpoint.security_mode());
673}
674
Paul Stewartced6a0b2011-11-08 15:32:04 -0800675ByteArrays WiFi::GetHiddenSSIDList() {
676 // Create a unique set of hidden SSIDs.
677 set<ByteArray> hidden_ssids_set;
678 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
679 it != services_.end();
680 ++it) {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800681 if ((*it)->hidden_ssid() && (*it)->favorite()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800682 hidden_ssids_set.insert((*it)->ssid());
683 }
684 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800685 VLOG(2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800686 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
687 if (!hidden_ssids.empty()) {
688 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
689 // not fit in.
690 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
691 hidden_ssids.erase(
692 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
693 hidden_ssids.end());
694 }
695 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
696 // SSIDs to wpa_supplicant, we need to explicitly specify the default
697 // behavior of doing a broadcast probe.
698 hidden_ssids.push_back(ByteArray());
699 }
700 return hidden_ssids;
701}
702
Paul Stewarta41e38d2011-11-11 07:47:29 -0800703bool WiFi::LoadHiddenServices(StoreInterface *storage) {
704 bool created_hidden_service = false;
705 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
706 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
707 bool is_hidden = false;
708 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
709 VLOG(2) << "Storage group " << *it << " returned by GetGroupsWithKey "
710 << "failed for GetBool(" << flimflam::kWifiHiddenSsid
711 << ") -- possible non-bool key";
712 continue;
713 }
714 if (!is_hidden) {
715 continue;
716 }
717 string ssid_hex;
718 vector<uint8_t> ssid_bytes;
719 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
720 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
721 VLOG(2) << "Hidden network is missing/invalid \""
722 << flimflam::kSSIDProperty << "\" property";
723 continue;
724 }
725 string device_address;
726 string network_mode;
727 string security;
728 // It is gross that we have to do this, but the only place we can
729 // get information about the service is from its storage name.
730 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
731 &network_mode, &security) ||
732 device_address != address()) {
733 VLOG(2) << "Hidden network has unparsable storage identifier \""
734 << *it << "\"";
735 continue;
736 }
737 if (FindService(ssid_bytes, network_mode, security).get()) {
738 // If service already exists, we have nothing to do, since the
739 // service has already loaded its configuration from storage.
740 // This is guaranteed to happen in both cases where Load() is
741 // called on a device (via a ConfigureDevice() call on the
742 // profile):
743 // - In RegisterDevice() the Device hasn't been started yet,
744 // so it has no services registered, except for those
745 // created by previous iterations of LoadHiddenService().
746 // The latter can happen if another profile in the Manager's
747 // stack defines the same ssid/mode/security. Even this
748 // case is okay, since even if the profiles differ
749 // materially on configuration and credentials, the "right"
750 // one will be configured in the course of the
751 // RegisterService() call below.
752 // - In PushProfile(), all registered services have been
753 // introduced to the profile via ConfigureService() prior
754 // to calling ConfigureDevice() on the profile.
755 continue;
756 }
757 WiFiServiceRefPtr service(new WiFiService(control_interface(),
758 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800759 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800760 manager(),
761 this,
762 ssid_bytes,
763 network_mode,
764 security,
765 true));
766 services_.push_back(service);
767
768 // By registering the service, the rest of the configuration
769 // will be loaded from the profile into the service via ConfigureService().
770 manager()->RegisterService(service);
771
772 created_hidden_service = true;
773 }
774
775 // If we are idle and we created a service as a result of opening the
776 // profile, we should initiate a scan for our new hidden SSID.
777 if (running() && created_hidden_service &&
778 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
779 Scan(NULL);
780 }
781
782 return created_hidden_service;
783}
784
Paul Stewart66c86002012-01-30 18:00:52 -0800785void WiFi::ClearCachedCredentialsTask() {
786 supplicant_interface_proxy_->ClearCachedCredentials();
787 clear_cached_credentials_pending_ = false;
788}
789
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000790void WiFi::BSSAddedTask(
791 const ::DBus::Path &path,
792 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000793 // Note: we assume that BSSIDs are unique across endpoints. This
794 // means that if an AP reuses the same BSSID for multiple SSIDs, we
795 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800796 WiFiEndpointRefPtr endpoint(
797 new WiFiEndpoint(proxy_factory_, this, path, properties));
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000798 LOG(INFO) << "Found endpoint. "
mukesh agrawale9adda12012-02-09 18:33:48 -0800799 << "RPC path: " << path << ", "
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000800 << "ssid: " << endpoint->ssid_string() << ", "
801 << "bssid: " << endpoint->bssid_string() << ", "
802 << "signal: " << endpoint->signal_strength() << ", "
Thieu Lee41a72d2012-02-06 20:46:51 +0000803 << "security: " << endpoint->security_mode() << ", "
804 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000805
806 if (endpoint->ssid_string().empty()) {
807 // Don't bother trying to find or create a Service for an Endpoint
808 // without an SSID. We wouldn't be able to connect to it anyway.
809 return;
810 }
811
mukesh agrawalb3857612012-01-18 16:23:29 -0800812 if (endpoint->ssid()[0] == 0) {
813 // Assume that an SSID starting with NULL is bogus/misconfigured,
814 // and filter it out.
815 return;
816 }
817
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000818 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
819 if (service) {
820 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
821 << " to service " << service->friendly_name() << ".";
822 service->AddEndpoint(endpoint);
823
824 if (manager()->HasService(service)) {
825 manager()->UpdateService(service);
826 } else {
827 DCHECK_EQ(1, service->NumEndpoints()); // Expect registered by now if >1.
828 manager()->RegisterService(service);
829 }
mukesh agrawale9adda12012-02-09 18:33:48 -0800830 } else {
831 const bool hidden_ssid = false;
832 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
833 LOG(INFO) << "New service " << service->GetRpcIdentifier()
834 << " (" << service->friendly_name() << ")";
835 service->AddEndpoint(endpoint);
836 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000837 }
838
mukesh agrawale9adda12012-02-09 18:33:48 -0800839 // Do this last, to maintain the invariant that any Endpoint we
840 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800841 //
842 // TODO(quiche): Write test to verify correct behavior in the case
843 // where we get multiple BSSAdded events for a single endpoint.
844 // (Old Endpoint's refcount should fall to zero, and old Endpoint
845 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800846 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800847 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000848}
849
850void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
851 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
852 if (i == endpoint_by_rpcid_.end()) {
853 LOG(WARNING) << "WiFi " << link_name()
854 << " could not find BSS " << path
855 << " to remove.";
856 return;
857 }
858
859 WiFiEndpointRefPtr endpoint = i->second;
860 CHECK(endpoint);
861 endpoint_by_rpcid_.erase(i);
862
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000863 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -0800864 CHECK(service) << "Can't find Service for Endpoint "
865 << path << " "
866 << "(with BSSID " << endpoint->bssid_string() << ").";
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000867 VLOG(2) << "Removing Endpoint " << endpoint->bssid_string()
868 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000869 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000870
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000871 bool disconnect_service = !service->HasEndpoints() &&
872 (service->IsConnecting() || service->IsConnected());
873 bool forget_service =
874 // Forget Services without Endpoints, except that we always keep
875 // hidden services around. (We need them around to populate the
876 // hidden SSIDs list.)
877 !service->HasEndpoints() && !service->hidden_ssid();
878 bool deregister_service =
879 // Only deregister a Service if we're forgetting it. Otherwise,
880 // Manager can't keep our configuration up-to-date (as Profiles
881 // change).
882 forget_service;
883
884 if (disconnect_service) {
885 DisconnectFrom(service);
886 }
887
888 if (deregister_service) {
889 manager()->DeregisterService(service);
890 } else {
891 manager()->UpdateService(service);
892 }
893
894 if (forget_service) {
895 vector<WiFiServiceRefPtr>::iterator it;
896 it = std::find(services_.begin(), services_.end(), service);
897 if (it != services_.end()) {
898 services_.erase(it);
899 }
900 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000901}
902
mukesh agrawal15908392011-11-16 18:29:25 +0000903void WiFi::PropertiesChangedTask(
904 const map<string, ::DBus::Variant> &properties) {
905 // TODO(quiche): Handle changes in other properties (e.g. signal
906 // strength).
907
908 // Note that order matters here. In particular, we want to process
909 // changes in the current BSS before changes in state. This is so
910 // that we update the state of the correct Endpoint/Service.
911
912 map<string, ::DBus::Variant>::const_iterator properties_it =
913 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
914 if (properties_it != properties.end()) {
915 CurrentBSSChanged(properties_it->second.reader().get_path());
916 }
917
918 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
919 if (properties_it != properties.end()) {
920 StateChanged(properties_it->second.reader().get_string());
921 }
922}
923
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700924void WiFi::ScanDoneTask() {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700925 LOG(INFO) << __func__;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700926 scan_pending_ = false;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700927}
928
mukesh agrawal32399322011-09-01 10:53:43 -0700929void WiFi::ScanTask() {
930 VLOG(2) << "WiFi " << link_name() << " scan requested.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800931 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -0700932 scan_args[wpa_supplicant::kPropertyScanType].writer().
933 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -0800934
935 ByteArrays hidden_ssids = GetHiddenSSIDList();
936 if (!hidden_ssids.empty()) {
937 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
938 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
939 }
940
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800941 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -0800942 // FIXME(gauravsh): A scan can fail if, for example, wpa_supplicant
943 // was restarted. This is done by a few of the 802.1x tests.
944 // crosbug.com/25657
945 try {
946 supplicant_interface_proxy_->Scan(scan_args);
947 scan_pending_ = true;
948 } catch (const DBus::Error e) { // NOLINT
949 LOG(WARNING) << "Scan failed. Attempting to re-connect to the supplicant.";
950 Stop();
951 Start();
952 }
mukesh agrawal32399322011-09-01 10:53:43 -0700953}
954
mukesh agrawal15908392011-11-16 18:29:25 +0000955void WiFi::StateChanged(const string &new_state) {
956 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +0000957 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +0000958 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
959 << old_state << " -> " << new_state;
960
961 WiFiService *affected_service;
962 // Identify the service to which the state change applies. If
963 // |pending_service_| is non-NULL, then the state change applies to
964 // |pending_service_|. Otherwise, it applies to |current_service_|.
965 //
966 // This policy is driven by the fact that the |pending_service_|
967 // doesn't become the |current_service_| until wpa_supplicant
968 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -0800969 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +0000970 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
971 affected_service =
972 pending_service_.get() ? pending_service_.get() : current_service_.get();
973 if (!affected_service) {
974 VLOG(2) << "WiFi " << link_name() << " " << __func__
975 << " with no service";
976 return;
977 }
978
mukesh agrawalc01f3982012-01-24 13:48:39 -0800979 if (new_state == wpa_supplicant::kInterfaceStateCompleted &&
980 !affected_service->IsConnected()) {
981 if (AcquireIPConfig()) {
982 LOG(INFO) << link_name() << " is up; should start L3!";
983 affected_service->SetState(Service::kStateConfiguring);
984 } else {
985 LOG(ERROR) << "Unable to acquire DHCP config.";
986 }
mukesh agrawal15908392011-11-16 18:29:25 +0000987 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
988 affected_service->SetState(Service::kStateAssociating);
989 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
990 new_state == wpa_supplicant::kInterfaceStateAssociating ||
991 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
992 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
993 // Ignore transitions into these states from Completed, to avoid
994 // bothering the user when roaming, or re-keying.
995 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
996 affected_service->SetState(Service::kStateAssociating);
997 // TOOD(quiche): On backwards transitions, we should probably set
998 // a timeout for getting back into the completed state. At present,
999 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001000 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001001 // (crosbug.com/23207)
1002 } else {
1003 // Other transitions do not affect Service state.
1004 //
1005 // Note in particular that we ignore a State change into
1006 // kInterfaceStateDisconnected, in favor of observing the corresponding
1007 // change in CurrentBSS.
1008 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001009}
1010
1011// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001012WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
1013 if (!args.ContainsString(flimflam::kTypeProperty)) {
1014 error->Populate(Error::kInvalidArguments, kManagerErrorTypeRequired);
1015 return NULL;
1016 }
1017
1018 if (args.GetString(flimflam::kTypeProperty) != flimflam::kTypeWifi) {
1019 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceType);
1020 return NULL;
1021 }
1022
1023 if (args.ContainsString(flimflam::kModeProperty) &&
1024 args.GetString(flimflam::kModeProperty) !=
1025 flimflam::kModeManaged) {
1026 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceMode);
1027 return NULL;
1028 }
1029
1030 if (!args.ContainsString(flimflam::kSSIDProperty)) {
1031 error->Populate(Error::kInvalidArguments, kManagerErrorSSIDRequired);
1032 return NULL;
1033 }
1034
1035 string ssid = args.GetString(flimflam::kSSIDProperty);
1036 if (ssid.length() < 1) {
1037 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooShort);
1038 return NULL;
1039 }
1040
1041 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
1042 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooLong);
1043 return NULL;
1044 }
1045
1046 string security_method;
1047 if (args.ContainsString(flimflam::kSecurityProperty)) {
1048 security_method = args.GetString(flimflam::kSecurityProperty);
1049 } else {
1050 security_method = flimflam::kSecurityNone;
1051 }
1052
1053 if (security_method != flimflam::kSecurityNone &&
1054 security_method != flimflam::kSecurityWep &&
1055 security_method != flimflam::kSecurityPsk &&
1056 security_method != flimflam::kSecurityWpa &&
1057 security_method != flimflam::kSecurityRsn &&
1058 security_method != flimflam::kSecurity8021x) {
1059 error->Populate(Error::kNotSupported,
1060 kManagerErrorUnsupportedSecurityMode);
1061 return NULL;
1062 }
1063
1064 if ((security_method == flimflam::kSecurityWep ||
1065 security_method == flimflam::kSecurityPsk ||
1066 security_method == flimflam::kSecurityWpa ||
1067 security_method == flimflam::kSecurityRsn) &&
1068 !args.ContainsString(flimflam::kPassphraseProperty)) {
1069 error->Populate(Error::kInvalidArguments,
1070 kManagerErrorPassphraseRequired);
1071 return NULL;
1072 }
1073
Paul Stewart6ab23a92011-11-09 17:17:47 -08001074 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001075 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1076 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001077 } else {
1078 // If the service is not found, and the caller hasn't specified otherwise,
1079 // we assume this is is a hidden network.
1080 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001081 }
1082
Paul Stewart6ab23a92011-11-09 17:17:47 -08001083 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1084 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1085 security_method));
1086 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001087 service = new WiFiService(control_interface(),
1088 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001089 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001090 manager(),
1091 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001092 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001093 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001094 security_method,
1095 hidden_ssid);
1096 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001097 // NB: We do not register the newly created Service with the Manager.
1098 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001099 }
1100
mukesh agrawal1a056262011-10-05 14:36:54 -07001101 if (security_method == flimflam::kSecurityWep ||
1102 security_method == flimflam::kSecurityPsk ||
1103 security_method == flimflam::kSecurityWpa ||
1104 security_method == flimflam::kSecurityRsn) {
1105 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
1106 error);
1107 if (error->IsFailure()) {
1108 return NULL;
1109 }
1110 }
1111
mukesh agrawal7ec71312011-11-10 02:08:26 +00001112 // TODO(quiche): Apply any other configuration parameters.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001113
1114 return service;
1115}
1116
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001117// static
1118bool WiFi::SanitizeSSID(string *ssid) {
1119 CHECK(ssid);
1120
1121 size_t ssid_len = ssid->length();
1122 size_t i;
1123 bool changed = false;
1124
1125 for (i=0; i < ssid_len; ++i) {
1126 if (!g_ascii_isprint((*ssid)[i])) {
1127 (*ssid)[i] = '?';
1128 changed = true;
1129 }
1130 }
1131
1132 return changed;
1133}
1134
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001135void WiFi::HelpRegisterDerivedInt32(
1136 PropertyStore *store,
1137 const string &name,
1138 int32(WiFi::*get)(Error *error),
1139 void(WiFi::*set)(const int32 &value, Error *error)) {
1140 store->RegisterDerivedInt32(
1141 name,
1142 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1143}
1144
1145void WiFi::HelpRegisterDerivedString(
1146 PropertyStore *store,
1147 const string &name,
1148 string(WiFi::*get)(Error *error),
1149 void(WiFi::*set)(const string &value, Error *error)) {
1150 store->RegisterDerivedString(
1151 name,
1152 StringAccessor(new CustomAccessor<WiFi, string>(this, get, set)));
1153}
1154
1155void WiFi::HelpRegisterDerivedUint16(
1156 PropertyStore *store,
1157 const string &name,
1158 uint16(WiFi::*get)(Error *error),
1159 void(WiFi::*set)(const uint16 &value, Error *error)) {
1160 store->RegisterDerivedUint16(
1161 name,
1162 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1163}
1164
Paul Stewartb50f0b92011-05-16 16:31:42 -07001165} // namespace shill