blob: 3966a1f7fca73466f03e4d5547729794902e7553 [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 <stdio.h>
mukesh agrawalc7426a42011-06-03 13:04:28 -07008#include <string.h>
mukesh agrawalf2f68a52011-09-01 12:15:48 -07009#include <netinet/ether.h>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -070010#include <linux/if.h> // Needs definitions from netinet/ether.h
Paul Stewartb50f0b92011-05-16 16:31:42 -070011
mukesh agrawal8a3188d2011-12-01 20:56:44 +000012#include <algorithm>
mukesh agrawalab87ea42011-05-18 11:44:49 -070013#include <map>
Paul Stewartced6a0b2011-11-08 15:32:04 -080014#include <set>
Paul Stewartb50f0b92011-05-16 16:31:42 -070015#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070016#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070017
Eric Shienbrood3e20a232012-02-16 11:35:56 -050018#include <base/bind.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070019#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"
Gary Morainac1bdb42012-02-16 17:42:29 -080035#include "shill/power_manager.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070036#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080037#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070038#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050039#include "shill/rtnl_handler.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070040#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080041#include "shill/shill_time.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080042#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070043#include "shill/supplicant_interface_proxy_interface.h"
44#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080045#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070046#include "shill/wifi_endpoint.h"
47#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070048#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070049
Eric Shienbrood3e20a232012-02-16 11:35:56 -050050using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000051using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070052using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080053using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070054using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070055using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070056
Paul Stewartb50f0b92011-05-16 16:31:42 -070057namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070058
59// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080060const char *WiFi::kDefaultBgscanMethod =
61 wpa_supplicant::kNetworkBgscanMethodSimple;
62const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
63const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
64const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070065// Note that WiFi generates some manager-level errors, because it implements
66// the Manager.GetWiFiService flimflam API. The API is implemented here,
67// rather than in manager, to keep WiFi-specific logic in the right place.
68const char WiFi::kManagerErrorPassphraseRequired[] = "must specify passphrase";
69const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
70const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
71const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070072const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
73 "security mode is unsupported";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070074const char WiFi::kManagerErrorUnsupportedServiceMode[] =
75 "service mode is unsupported";
mukesh agrawal5c05b292012-03-07 10:12:52 -080076// Age (in seconds) beyond which a BSS cache entry will not be preserved,
77// across a suspend/resume.
78const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000079const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070080const time_t WiFi::kRescanIntervalSeconds = 1;
mukesh agrawalb54601c2011-06-07 17:39:22 -070081
Paul Stewartb50f0b92011-05-16 16:31:42 -070082WiFi::WiFi(ControlInterface *control_interface,
83 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080084 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070085 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070086 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080087 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070088 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070089 : Device(control_interface,
90 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080091 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070092 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070093 link,
Chris Masone626719f2011-08-18 16:58:48 -070094 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080095 interface_index,
96 Technology::kWifi),
Eric Shienbrood9a245532012-03-07 14:20:39 -050097 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -070098 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -080099 time_(Time::GetInstance()),
mukesh agrawal15908392011-11-16 18:29:25 +0000100 supplicant_state_(kInterfaceStateUnknown),
101 supplicant_bss_("(unknown)"),
Paul Stewart66c86002012-01-30 18:00:52 -0800102 clear_cached_credentials_pending_(false),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800103 need_bss_flush_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800104 bgscan_method_(kDefaultBgscanMethod),
105 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
106 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700107 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800108 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700109 PropertyStore *store = this->mutable_store();
mukesh agrawal5c05b292012-03-07 10:12:52 -0800110 resumed_at_.tv_sec = 0;
111 resumed_at_.tv_usec = 0;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800112 HelpRegisterDerivedString(store,
113 flimflam::kBgscanMethodProperty,
114 &WiFi::GetBgscanMethod,
115 &WiFi::SetBgscanMethod);
116 HelpRegisterDerivedUint16(store,
117 flimflam::kBgscanShortIntervalProperty,
118 &WiFi::GetBgscanShortInterval,
119 &WiFi::SetBgscanShortInterval);
120 HelpRegisterDerivedInt32(store,
121 flimflam::kBgscanSignalThresholdProperty,
122 &WiFi::GetBgscanSignalThreshold,
123 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700124
Chris Masoneb925cc82011-06-22 15:39:57 -0700125 // TODO(quiche): Decide if scan_pending_ is close enough to
126 // "currently scanning" that we don't care, or if we want to track
127 // scan pending/currently scanning/no scan scheduled as a tri-state
128 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700129 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800130 HelpRegisterDerivedUint16(store,
131 flimflam::kScanIntervalProperty,
132 &WiFi::GetScanInterval,
133 &WiFi::SetScanInterval);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700134 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700135}
136
mukesh agrawalaf571952011-07-14 14:31:12 -0700137WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700138
Eric Shienbrood9a245532012-03-07 14:20:39 -0500139void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
mukesh agrawalab87ea42011-05-18 11:44:49 -0700140 ::DBus::Path interface_path;
141
mukesh agrawalaf571952011-07-14 14:31:12 -0700142 supplicant_process_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700143 proxy_factory_->CreateSupplicantProcessProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700144 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700145 try {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800146 map<string, DBus::Variant> create_interface_args;
Paul Stewart20550982012-04-16 12:16:11 -0700147 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
Paul Stewartac4ac002011-08-26 12:04:26 -0700148 append_string(link_name().c_str());
Paul Stewart20550982012-04-16 12:16:11 -0700149 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700150 append_string(wpa_supplicant::kDriverNL80211);
Paul Stewart20550982012-04-16 12:16:11 -0700151 create_interface_args[
152 wpa_supplicant::kInterfacePropertyConfigFile].writer().
153 append_string(SCRIPTDIR "/wpa_supplicant.conf");
mukesh agrawalc7426a42011-06-03 13:04:28 -0700154 interface_path =
155 supplicant_process_proxy_->CreateInterface(create_interface_args);
156 } catch (const DBus::Error e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700157 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
mukesh agrawalc7426a42011-06-03 13:04:28 -0700158 interface_path =
Paul Stewartac4ac002011-08-26 12:04:26 -0700159 supplicant_process_proxy_->GetInterface(link_name());
mukesh agrawal7ec71312011-11-10 02:08:26 +0000160 // TODO(quiche): Is it okay to crash here, if device is missing?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700161 } else {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000162 // TODO(quiche): Log error.
mukesh agrawalc7426a42011-06-03 13:04:28 -0700163 }
164 }
165
mukesh agrawalab87ea42011-05-18 11:44:49 -0700166 supplicant_interface_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700167 proxy_factory_->CreateSupplicantInterfaceProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700168 this, interface_path, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700169
Eric Shienbrood9a245532012-03-07 14:20:39 -0500170 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
171 IFF_UP);
mukesh agrawal7ec71312011-11-10 02:08:26 +0000172 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700173
mukesh agrawal7ec71312011-11-10 02:08:26 +0000174 // Clear out any networks that might previously have been configured
mukesh agrawalc7426a42011-06-03 13:04:28 -0700175 // for this interface.
176 supplicant_interface_proxy_->RemoveAllNetworks();
177
mukesh agrawal7ec71312011-11-10 02:08:26 +0000178 // Flush interface's BSS cache, so that we get BSSAdded signals for
mukesh agrawalc7426a42011-06-03 13:04:28 -0700179 // all BSSes (not just new ones since the last scan).
180 supplicant_interface_proxy_->FlushBSS(0);
181
mukesh agrawal23e9f282012-02-17 15:36:36 -0800182 try {
183 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
184 // with RADIUS servers that respond strangely to such requests.
185 // crosbug.com/25630
186 supplicant_interface_proxy_->SetFastReauth(false);
187 } catch (const DBus::Error e) { // NOLINT
mukesh agrawalf2028172012-03-13 14:20:22 -0700188 LOG(INFO) << "Failed to disable fast_reauth."
189 << "May be running an older version of wpa_supplicant.";
190 }
191
192 try {
193 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
194 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
195 } catch (const DBus::Error e) { // NOLINT
196 LOG(INFO) << "Failed to set scan_interval. "
mukesh agrawal23e9f282012-02-17 15:36:36 -0800197 << "May be running an older version of wpa_supplicant.";
198 }
Paul Stewart2987dcf2012-01-30 15:47:42 -0800199
Gary Morainac1bdb42012-02-16 17:42:29 -0800200 // Register for power state changes. HandlePowerStateChange() will be called
201 // when the power state changes.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500202 manager()->power_manager()->AddStateChangeCallback(
203 UniqueName(),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500204 Bind(&WiFi::HandlePowerStateChange, weak_ptr_factory_.GetWeakPtr()));
Gary Morainac1bdb42012-02-16 17:42:29 -0800205
Darin Petkovc0865312011-09-16 15:31:20 -0700206 Scan(NULL);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500207 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
208 if (error)
209 error->Reset(); // indicate immediate completion
mukesh agrawalab87ea42011-05-18 11:44:49 -0700210}
211
Eric Shienbrood9a245532012-03-07 14:20:39 -0500212void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700213 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
mukesh agrawal7ec71312011-11-10 02:08:26 +0000214 // TODO(quiche): Remove interface from supplicant.
mukesh agrawal31950242011-07-14 11:53:38 -0700215 supplicant_interface_proxy_.reset(); // breaks a reference cycle
216 supplicant_process_proxy_.reset();
mukesh agrawal15908392011-11-16 18:29:25 +0000217 endpoint_by_rpcid_.clear();
mukesh agrawal15908392011-11-16 18:29:25 +0000218 rpcid_by_service_.clear();
Gary Morain91001122012-02-29 16:22:26 -0800219 manager()->power_manager()->RemoveStateChangeCallback(UniqueName());
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700220
Paul Stewartced6a0b2011-11-08 15:32:04 -0800221 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
222 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700223 ++it) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700224 SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
225 << (*it)->friendly_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700226 manager()->DeregisterService(*it);
227 }
Paul Stewartced6a0b2011-11-08 15:32:04 -0800228 services_.clear(); // breaks reference cycles
mukesh agrawalb20776f2012-02-10 16:00:36 -0800229 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000230 pending_service_ = NULL; // breaks a reference cycle
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700231
Eric Shienbrood9a245532012-03-07 14:20:39 -0500232 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
233 if (error)
234 error->Reset(); // indicate immediate completion
mukesh agrawal7ec71312011-11-10 02:08:26 +0000235 // TODO(quiche): Anything else to do?
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700236
Ben Chanfad4a0b2012-04-18 15:49:59 -0700237 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
238 << (supplicant_process_proxy_.get() ?
239 "is set." : "is not set.");
240 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
241 << (supplicant_interface_proxy_.get() ?
242 "is set." : "is not set.");
243 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
244 << (pending_service_.get() ? "is set." : "is not set.");
245 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
246 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
247 SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
248 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700249}
250
Paul Stewarta41e38d2011-11-11 07:47:29 -0800251bool WiFi::Load(StoreInterface *storage) {
252 LoadHiddenServices(storage);
253 return Device::Load(storage);
254}
255
mukesh agrawal1830fa12011-09-26 14:31:40 -0700256void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700257 LOG(INFO) << __func__;
258
mukesh agrawal7ec71312011-11-10 02:08:26 +0000259 // Needs to send a D-Bus message, but may be called from D-Bus
260 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700261 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500262 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700263}
264
Paul Stewartfdd16072011-09-16 12:41:35 -0700265bool WiFi::TechnologyIs(const Technology::Identifier type) const {
266 return type == Technology::kWifi;
Paul Stewartb50f0b92011-05-16 16:31:42 -0700267}
268
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000269bool WiFi::IsConnectingTo(const WiFiService &service) const {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800270 return pending_service_ == &service ||
271 (current_service_ == &service &&
272 service.state() != Service::kStateConnected);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000273}
274
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000275void WiFi::BSSAdded(const ::DBus::Path &path,
276 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500277 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000278 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500279 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
280 weak_ptr_factory_.GetWeakPtr(),
281 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000282}
283
284void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500285 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000286 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500287 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
288 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700289}
290
mukesh agrawal7ec71312011-11-10 02:08:26 +0000291void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
mukesh agrawal15908392011-11-16 18:29:25 +0000292 LOG(INFO) << "In " << __func__ << "(): called";
293 // Called from D-Bus signal handler, but may need to send a D-Bus
294 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500295 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
296 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000297}
298
mukesh agrawalb54601c2011-06-07 17:39:22 -0700299void WiFi::ScanDone() {
300 LOG(INFO) << __func__;
301
mukesh agrawal7ec71312011-11-10 02:08:26 +0000302 // Defer handling of scan result processing, because that processing
303 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700304 // registration can't be done in the context of a D-Bus signal
305 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500306 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
307 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700308}
309
mukesh agrawal6e277772011-09-29 15:04:23 -0700310void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000311 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800312 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700313 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700314
mukesh agrawal7ec71312011-11-10 02:08:26 +0000315 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000316 if (pending_service_ && pending_service_ == service) {
317 // TODO(quiche): Return an error to the caller. crosbug.com/23832
318 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
319 << service->friendly_name()
320 << ", which is already pending.";
321 return;
322 }
323
324 if (pending_service_ && pending_service_ != service) {
325 DisconnectFrom(pending_service_);
326 }
mukesh agrawal32399322011-09-01 10:53:43 -0700327
mukesh agrawal6e277772011-09-29 15:04:23 -0700328 try {
mukesh agrawal15908392011-11-16 18:29:25 +0000329 // TODO(quiche): Set a timeout here. In the normal case, we expect
330 // that, if wpa_supplicant fails to connect, it will eventually send
331 // a signal that its CurrentBSS has changed. But there may be cases
332 // where the signal is not sent. (crosbug.com/23206)
mukesh agrawal64896322011-12-01 01:13:10 +0000333 const uint32_t scan_ssid = 1; // "True": Use directed probe.
334 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
335 append_uint32(scan_ssid);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800336 service_params[wpa_supplicant::kNetworkPropertyBgscan].writer().
337 append_string(CreateBgscanConfigString().c_str());
mukesh agrawal6e277772011-09-29 15:04:23 -0700338 network_path =
339 supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000340 rpcid_by_service_[service] = network_path;
mukesh agrawal6e277772011-09-29 15:04:23 -0700341 } catch (const DBus::Error e) { // NOLINT
342 LOG(ERROR) << "exception while adding network: " << e.what();
343 return;
344 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700345
mukesh agrawal445e72c2011-06-22 11:13:50 -0700346 supplicant_interface_proxy_->SelectNetwork(network_path);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000347 pending_service_ = service;
348 CHECK(current_service_.get() != pending_service_.get());
349
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700350 // SelectService here (instead of in LinkEvent, like Ethernet), so
351 // that, if we fail to bring up L2, we can attribute failure correctly.
352 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000353 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700354 // reconsider if this is the right place to change the selected service.
355 // see discussion in crosbug.com/20191.
356 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000357}
358
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000359void WiFi::DisconnectFrom(WiFiService *service) {
360 if (service != current_service_ && service != pending_service_) {
361 // TODO(quiche): Once we have asynchronous reply support, we should
362 // generate a D-Bus error here. (crosbug.com/23832)
363 LOG(WARNING) << "In " << __func__ << "(): "
364 << " ignoring request to disconnect from service "
365 << service->friendly_name()
366 << " which is neither current nor pending";
367 return;
368 }
369
370 if (pending_service_ && service != pending_service_) {
371 // TODO(quiche): Once we have asynchronous reply support, we should
372 // generate a D-Bus error here. (crosbug.com/23832)
373 LOG(WARNING) << "In " << __func__ << "(): "
374 << " ignoring request to disconnect from service "
375 << service->friendly_name()
376 << " which is not the pending service.";
377 return;
378 }
379
380 if (!pending_service_ && service != current_service_) {
381 // TODO(quiche): Once we have asynchronous reply support, we should
382 // generate a D-Bus error here. (crosbug.com/23832)
383 LOG(WARNING) << "In " << __func__ << "(): "
384 << " ignoring request to disconnect from service "
385 << service->friendly_name()
386 << " which is not the current service.";
387 return;
388 }
389
390 pending_service_ = NULL;
391 try {
392 supplicant_interface_proxy_->Disconnect();
393 // We'll call RemoveNetwork and reset |current_service_| after
394 // supplicant notifies us that the CurrentBSS has changed.
395 } catch (const DBus::Error e) { // NOLINT
396 // Can't depend on getting a notification of CurrentBSS change.
397 // So effect changes immediately.
398 ReverseServiceMap::const_iterator rpcid_it =
399 rpcid_by_service_.find(service);
400 DCHECK(rpcid_it != rpcid_by_service_.end());
401 if (rpcid_it == rpcid_by_service_.end()) {
402 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
403 << service->friendly_name() << ": "
404 << "could not find supplicant network to disable.";
405 } else {
406 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
407 }
408 current_service_ = NULL;
409 }
410
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800411 CHECK(current_service_ == NULL ||
412 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000413}
414
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000415bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800416 return !current_service_ && !pending_service_;
417}
418
Paul Stewart66c86002012-01-30 18:00:52 -0800419void WiFi::ClearCachedCredentials() {
420 LOG(INFO) << __func__;
421
422 // Needs to send a D-Bus message, but may be called from D-Bus
423 // caller context (via Manager::PopProfile). So defer work
424 // to event loop.
425 if (!clear_cached_credentials_pending_) {
426 clear_cached_credentials_pending_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500427 dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask,
428 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart66c86002012-01-30 18:00:52 -0800429 }
430}
431
mukesh agrawalb20776f2012-02-10 16:00:36 -0800432void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
433 WiFiService *service = FindServiceForEndpoint(endpoint);
434 DCHECK(service);
435 if (service) {
436 service->NotifyEndpointUpdated(endpoint);
437 return;
438 }
439}
440
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800441string WiFi::CreateBgscanConfigString() {
442 return StringPrintf("%s:%d:%d:%d",
443 bgscan_method_.c_str(),
444 bgscan_short_interval_seconds_,
445 bgscan_signal_threshold_dbm_,
446 scan_interval_seconds_);
447}
448
449void WiFi::SetBgscanMethod(const string &method, Error *error) {
450 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
451 method != wpa_supplicant::kNetworkBgscanMethodLearn) {
452 const string error_message =
453 StringPrintf("Unrecognized bgscan method %s", method.c_str());
454 LOG(WARNING) << error_message;
455 error->Populate(Error::kInvalidArguments, error_message);
456 return;
457 }
458
459 bgscan_method_ = method;
460 // We do not update kNetworkPropertyBgscan for |pending_service_| or
461 // |current_service_|, because supplicant does not allow for
462 // reconfiguration without disconnect and reconnect.
463}
464
465void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
466 bgscan_short_interval_seconds_ = seconds;
467 // We do not update kNetworkPropertyBgscan for |pending_service_| or
468 // |current_service_|, because supplicant does not allow for
469 // reconfiguration without disconnect and reconnect.
470}
471
472void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
473 bgscan_signal_threshold_dbm_ = dbm;
474 // We do not update kNetworkPropertyBgscan for |pending_service_| or
475 // |current_service_|, because supplicant does not allow for
476 // reconfiguration without disconnect and reconnect.
477}
478
479void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
480 scan_interval_seconds_ = seconds;
481 // We do not update |pending_service_| or |current_service_|, because
482 // supplicant does not allow for reconfiguration without disconnect
483 // and reconnect.
484
485 // TODO(quiche): Update scan timer. crosbug.com/24337
486}
487
mukesh agrawal165e6142011-11-22 02:22:56 +0000488// To avoid creating duplicate services, call FindServiceForEndpoint
489// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000490WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
491 bool hidden_ssid) {
492 WiFiServiceRefPtr service =
493 new WiFiService(control_interface(),
494 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800495 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000496 manager(),
497 this,
498 endpoint.ssid(),
499 endpoint.network_mode(),
500 endpoint.security_mode(),
501 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000502 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000503 return service;
504}
505
506void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700507 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
508 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000509 supplicant_bss_ = new_bss;
510 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
511 HandleDisconnect();
512 } else {
513 HandleRoam(new_bss);
514 }
515
516 SelectService(current_service_);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000517 // Invariant check: a Service can either be current, or pending, but
518 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000519 CHECK(current_service_.get() != pending_service_.get() ||
520 current_service_.get() == NULL);
521
522 // TODO(quiche): Update BSSID property on the Service
523 // (crosbug.com/22377).
524}
525
526void WiFi::HandleDisconnect() {
527 // Identify the affected service. We expect to get a disconnect
528 // event when we fall off a Service that we were connected
529 // to. However, we also allow for the case where we get a disconnect
530 // event while attempting to connect from a disconnected state.
531 WiFiService *affected_service =
532 current_service_.get() ? current_service_.get() : pending_service_.get();
533
534 current_service_ = NULL;
535 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700536 SLOG(WiFi, 2) << "WiFi " << link_name()
537 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000538 return;
539 }
540
541 ReverseServiceMap::const_iterator rpcid_it =
542 rpcid_by_service_.find(affected_service);
543 if (rpcid_it == rpcid_by_service_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700544 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
545 << " (or failed to connect to) "
546 << affected_service->friendly_name() << ", "
547 << "but could not find supplicant network to disable.";
mukesh agrawal15908392011-11-16 18:29:25 +0000548 return;
549 }
550
Ben Chanfad4a0b2012-04-18 15:49:59 -0700551 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
552 << " (or failed to connect to) "
553 << affected_service->friendly_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000554 // TODO(quiche): Reconsider giving up immediately. Maybe give
555 // wpa_supplicant some time to retry, first.
556 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
mukesh agrawal568b5c62012-02-28 14:44:47 -0800557 // TOOD(quiche): In the event that the disconnect was deliberate, we
558 // might want to go to SetState(kStateIdle), rather than reporting a
559 // failure. crosbug.com/24700.
560 // TODO(quiche): In the event that we suspect a password failure,
561 // we should not be silent. crosbug.com/23211.
562 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800563 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000564 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000565
566 if (affected_service == pending_service_.get()) {
567 // The attempt to connect to |pending_service_| failed. Clear
568 // |pending_service_|, to indicate we're no longer in the middle
569 // of a connect request.
570 pending_service_ = NULL;
571 } else if (pending_service_.get()) {
572 // We've attributed the disconnection to what was the
573 // |current_service_|, rather than the |pending_service_|.
574 //
575 // If we're wrong about that (i.e. supplicant reported this
576 // CurrentBSS change after attempting to connect to
577 // |pending_service_|), we're depending on supplicant to retry
578 // connecting to |pending_service_|, and delivering another
579 // CurrentBSS change signal in the future.
580 //
581 // Log this fact, to help us debug (in case our assumptions are
582 // wrong).
Ben Chanfad4a0b2012-04-18 15:49:59 -0700583 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to "
584 << pending_service_->friendly_name()
585 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000586 }
587}
588
589// We use the term "Roam" loosely. In particular, we include the case
590// where we "Roam" to a BSS from the disconnected state.
591void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
592 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
593 if (endpoint_it == endpoint_by_rpcid_.end()) {
594 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
595 << new_bss;
596 return;
597 }
598
599 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000600 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000601 if (!service.get()) {
602 LOG(WARNING) << "WiFi " << link_name()
603 << " could not find Service for Endpoint "
604 << endpoint.bssid_string()
605 << " (service will be unchanged)";
606 return;
607 }
608
Ben Chanfad4a0b2012-04-18 15:49:59 -0700609 SLOG(WiFi, 2) << "WiFi " << link_name()
610 << " roamed to Endpoint " << endpoint.bssid_string()
611 << " (SSID " << endpoint.ssid_string() << ")";
mukesh agrawal15908392011-11-16 18:29:25 +0000612
mukesh agrawale1d90e92012-02-15 17:36:08 -0800613 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000614
mukesh agrawal15908392011-11-16 18:29:25 +0000615 if (pending_service_.get() &&
616 service.get() != pending_service_.get()) {
617 // The Service we've roamed on to is not the one we asked for.
618 // We assume that this is transient, and that wpa_supplicant
619 // is trying / will try to connect to |pending_service_|.
620 //
621 // If it succeeds, we'll end up back here, but with |service|
622 // pointing at the same service as |pending_service_|.
623 //
624 // If it fails, we'll process things in HandleDisconnect.
625 //
626 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700627 SLOG(WiFi, 2) << "WiFi " << link_name()
628 << " new current Endpoint "
629 << endpoint.bssid_string()
630 << " is not part of pending service "
631 << pending_service_->friendly_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000632
633 // Sanity check: if we didn't roam onto |pending_service_|, we
634 // should still be on |current_service_|.
635 if (service.get() != current_service_.get()) {
636 LOG(WARNING) << "WiFi " << link_name()
637 << " new current Endpoint "
638 << endpoint.bssid_string()
639 << " is neither part of pending service "
640 << pending_service_->friendly_name()
641 << " nor part of current service "
642 << (current_service_.get() ?
643 current_service_->friendly_name() :
644 "(NULL)");
645 // Although we didn't expect to get here, we should keep
646 // |current_service_| in sync with what supplicant has done.
647 current_service_ = service;
648 }
649 return;
650 }
651
652 if (pending_service_.get()) {
653 // We assume service.get() == pending_service_.get() here, because
654 // of the return in the previous if clause.
655 //
656 // Boring case: we've connected to the service we asked
657 // for. Simply update |current_service_| and |pending_service_|.
658 current_service_ = service;
659 pending_service_ = NULL;
660 return;
661 }
662
663 // |pending_service_| was NULL, so we weren't attempting to connect
664 // to a new Service. Sanity check that we're still on
665 // |current_service_|.
666 if (service.get() != current_service_.get()) {
667 LOG(WARNING)
668 << "WiFi " << link_name()
669 << " new current Endpoint "
670 << endpoint.bssid_string()
671 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000672 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000673 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000674 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000675 // We didn't expect to be here, but let's cope as well as we
676 // can. Update |current_service_| to keep it in sync with
677 // supplicant.
678 current_service_ = service;
679 return;
680 }
681
682 // At this point, we know that |pending_service_| was NULL, and that
683 // we're still on |current_service_|. This is the most boring case
684 // of all, because there's no state to update here.
685 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700686}
687
Paul Stewarta41e38d2011-11-11 07:47:29 -0800688WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
689 const string &mode,
690 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800691 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
692 it != services_.end();
693 ++it) {
694 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
695 (*it)->IsSecurityMatch(security)) {
696 return *it;
697 }
698 }
699 return NULL;
700}
701
mukesh agrawal165e6142011-11-22 02:22:56 +0000702WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
703 return FindService(endpoint.ssid(),
704 endpoint.network_mode(),
705 endpoint.security_mode());
706}
707
Paul Stewartced6a0b2011-11-08 15:32:04 -0800708ByteArrays WiFi::GetHiddenSSIDList() {
709 // Create a unique set of hidden SSIDs.
710 set<ByteArray> hidden_ssids_set;
711 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
712 it != services_.end();
713 ++it) {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800714 if ((*it)->hidden_ssid() && (*it)->favorite()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800715 hidden_ssids_set.insert((*it)->ssid());
716 }
717 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700718 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800719 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
720 if (!hidden_ssids.empty()) {
721 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
722 // not fit in.
723 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
724 hidden_ssids.erase(
725 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
726 hidden_ssids.end());
727 }
728 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
729 // SSIDs to wpa_supplicant, we need to explicitly specify the default
730 // behavior of doing a broadcast probe.
731 hidden_ssids.push_back(ByteArray());
732 }
733 return hidden_ssids;
734}
735
Paul Stewarta41e38d2011-11-11 07:47:29 -0800736bool WiFi::LoadHiddenServices(StoreInterface *storage) {
737 bool created_hidden_service = false;
738 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
739 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
740 bool is_hidden = false;
741 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700742 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
743 << "GetGroupsWithKey failed for GetBool("
744 << flimflam::kWifiHiddenSsid
745 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800746 continue;
747 }
748 if (!is_hidden) {
749 continue;
750 }
751 string ssid_hex;
752 vector<uint8_t> ssid_bytes;
753 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
754 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700755 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
756 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800757 continue;
758 }
759 string device_address;
760 string network_mode;
761 string security;
762 // It is gross that we have to do this, but the only place we can
763 // get information about the service is from its storage name.
764 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
765 &network_mode, &security) ||
766 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700767 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
768 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800769 continue;
770 }
771 if (FindService(ssid_bytes, network_mode, security).get()) {
772 // If service already exists, we have nothing to do, since the
773 // service has already loaded its configuration from storage.
774 // This is guaranteed to happen in both cases where Load() is
775 // called on a device (via a ConfigureDevice() call on the
776 // profile):
777 // - In RegisterDevice() the Device hasn't been started yet,
778 // so it has no services registered, except for those
779 // created by previous iterations of LoadHiddenService().
780 // The latter can happen if another profile in the Manager's
781 // stack defines the same ssid/mode/security. Even this
782 // case is okay, since even if the profiles differ
783 // materially on configuration and credentials, the "right"
784 // one will be configured in the course of the
785 // RegisterService() call below.
786 // - In PushProfile(), all registered services have been
787 // introduced to the profile via ConfigureService() prior
788 // to calling ConfigureDevice() on the profile.
789 continue;
790 }
791 WiFiServiceRefPtr service(new WiFiService(control_interface(),
792 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800793 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800794 manager(),
795 this,
796 ssid_bytes,
797 network_mode,
798 security,
799 true));
800 services_.push_back(service);
801
802 // By registering the service, the rest of the configuration
803 // will be loaded from the profile into the service via ConfigureService().
804 manager()->RegisterService(service);
805
806 created_hidden_service = true;
807 }
808
809 // If we are idle and we created a service as a result of opening the
810 // profile, we should initiate a scan for our new hidden SSID.
811 if (running() && created_hidden_service &&
812 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
813 Scan(NULL);
814 }
815
816 return created_hidden_service;
817}
818
Paul Stewart66c86002012-01-30 18:00:52 -0800819void WiFi::ClearCachedCredentialsTask() {
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700820 try {
821 supplicant_interface_proxy_->ClearCachedCredentials();
822 } catch (const DBus::Error e) { // NOLINT
823 LOG(WARNING) << "Clear of cached credentials failed.";
824 }
Paul Stewart66c86002012-01-30 18:00:52 -0800825 clear_cached_credentials_pending_ = false;
826}
827
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000828void WiFi::BSSAddedTask(
829 const ::DBus::Path &path,
830 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000831 // Note: we assume that BSSIDs are unique across endpoints. This
832 // means that if an AP reuses the same BSSID for multiple SSIDs, we
833 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800834 WiFiEndpointRefPtr endpoint(
835 new WiFiEndpoint(proxy_factory_, this, path, properties));
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000836 LOG(INFO) << "Found endpoint. "
mukesh agrawale9adda12012-02-09 18:33:48 -0800837 << "RPC path: " << path << ", "
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000838 << "ssid: " << endpoint->ssid_string() << ", "
839 << "bssid: " << endpoint->bssid_string() << ", "
840 << "signal: " << endpoint->signal_strength() << ", "
Thieu Lee41a72d2012-02-06 20:46:51 +0000841 << "security: " << endpoint->security_mode() << ", "
842 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000843
844 if (endpoint->ssid_string().empty()) {
845 // Don't bother trying to find or create a Service for an Endpoint
846 // without an SSID. We wouldn't be able to connect to it anyway.
847 return;
848 }
849
mukesh agrawalb3857612012-01-18 16:23:29 -0800850 if (endpoint->ssid()[0] == 0) {
851 // Assume that an SSID starting with NULL is bogus/misconfigured,
852 // and filter it out.
853 return;
854 }
855
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000856 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
857 if (service) {
858 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
859 << " to service " << service->friendly_name() << ".";
860 service->AddEndpoint(endpoint);
861
862 if (manager()->HasService(service)) {
863 manager()->UpdateService(service);
864 } else {
865 DCHECK_EQ(1, service->NumEndpoints()); // Expect registered by now if >1.
866 manager()->RegisterService(service);
867 }
mukesh agrawale9adda12012-02-09 18:33:48 -0800868 } else {
869 const bool hidden_ssid = false;
870 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
871 LOG(INFO) << "New service " << service->GetRpcIdentifier()
872 << " (" << service->friendly_name() << ")";
873 service->AddEndpoint(endpoint);
874 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000875 }
876
mukesh agrawale9adda12012-02-09 18:33:48 -0800877 // Do this last, to maintain the invariant that any Endpoint we
878 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800879 //
880 // TODO(quiche): Write test to verify correct behavior in the case
881 // where we get multiple BSSAdded events for a single endpoint.
882 // (Old Endpoint's refcount should fall to zero, and old Endpoint
883 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800884 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800885 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000886}
887
888void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
889 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
890 if (i == endpoint_by_rpcid_.end()) {
891 LOG(WARNING) << "WiFi " << link_name()
892 << " could not find BSS " << path
893 << " to remove.";
894 return;
895 }
896
897 WiFiEndpointRefPtr endpoint = i->second;
898 CHECK(endpoint);
899 endpoint_by_rpcid_.erase(i);
900
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000901 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -0800902 CHECK(service) << "Can't find Service for Endpoint "
903 << path << " "
904 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -0700905 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
906 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000907 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000908
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000909 bool disconnect_service = !service->HasEndpoints() &&
910 (service->IsConnecting() || service->IsConnected());
911 bool forget_service =
912 // Forget Services without Endpoints, except that we always keep
913 // hidden services around. (We need them around to populate the
914 // hidden SSIDs list.)
915 !service->HasEndpoints() && !service->hidden_ssid();
916 bool deregister_service =
917 // Only deregister a Service if we're forgetting it. Otherwise,
918 // Manager can't keep our configuration up-to-date (as Profiles
919 // change).
920 forget_service;
921
922 if (disconnect_service) {
923 DisconnectFrom(service);
924 }
925
926 if (deregister_service) {
927 manager()->DeregisterService(service);
928 } else {
929 manager()->UpdateService(service);
930 }
931
932 if (forget_service) {
933 vector<WiFiServiceRefPtr>::iterator it;
934 it = std::find(services_.begin(), services_.end(), service);
935 if (it != services_.end()) {
936 services_.erase(it);
937 }
938 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000939}
940
mukesh agrawal15908392011-11-16 18:29:25 +0000941void WiFi::PropertiesChangedTask(
942 const map<string, ::DBus::Variant> &properties) {
943 // TODO(quiche): Handle changes in other properties (e.g. signal
944 // strength).
945
946 // Note that order matters here. In particular, we want to process
947 // changes in the current BSS before changes in state. This is so
948 // that we update the state of the correct Endpoint/Service.
949
950 map<string, ::DBus::Variant>::const_iterator properties_it =
951 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
952 if (properties_it != properties.end()) {
953 CurrentBSSChanged(properties_it->second.reader().get_path());
954 }
955
956 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
957 if (properties_it != properties.end()) {
958 StateChanged(properties_it->second.reader().get_string());
959 }
960}
961
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700962void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700963 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -0800964 if (need_bss_flush_) {
965 CHECK(supplicant_interface_proxy_ != NULL);
966 // Compute |max_age| relative to |resumed_at_|, to account for the
967 // time taken to scan.
968 struct timeval now;
969 uint32_t max_age;
970 time_->GetTimeMonotonic(&now);
971 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
972 supplicant_interface_proxy_->FlushBSS(max_age);
973 need_bss_flush_ = false;
974 }
mukesh agrawalb54601c2011-06-07 17:39:22 -0700975 scan_pending_ = false;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700976}
977
mukesh agrawal32399322011-09-01 10:53:43 -0700978void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700979 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800980 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -0700981 scan_args[wpa_supplicant::kPropertyScanType].writer().
982 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -0800983
984 ByteArrays hidden_ssids = GetHiddenSSIDList();
985 if (!hidden_ssids.empty()) {
986 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
987 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
988 }
989
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800990 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -0800991 // FIXME(gauravsh): A scan can fail if, for example, wpa_supplicant
992 // was restarted. This is done by a few of the 802.1x tests.
993 // crosbug.com/25657
994 try {
995 supplicant_interface_proxy_->Scan(scan_args);
996 scan_pending_ = true;
997 } catch (const DBus::Error e) { // NOLINT
998 LOG(WARNING) << "Scan failed. Attempting to re-connect to the supplicant.";
Eric Shienbrood9a245532012-03-07 14:20:39 -0500999 EnabledStateChangedCallback null_callback;
1000 Stop(NULL, null_callback);
1001 Start(NULL, null_callback);
Gaurav Shah10109f22011-11-11 20:16:22 -08001002 }
mukesh agrawal32399322011-09-01 10:53:43 -07001003}
1004
mukesh agrawal15908392011-11-16 18:29:25 +00001005void WiFi::StateChanged(const string &new_state) {
1006 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001007 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001008 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1009 << old_state << " -> " << new_state;
1010
1011 WiFiService *affected_service;
1012 // Identify the service to which the state change applies. If
1013 // |pending_service_| is non-NULL, then the state change applies to
1014 // |pending_service_|. Otherwise, it applies to |current_service_|.
1015 //
1016 // This policy is driven by the fact that the |pending_service_|
1017 // doesn't become the |current_service_| until wpa_supplicant
1018 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001019 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001020 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1021 affected_service =
1022 pending_service_.get() ? pending_service_.get() : current_service_.get();
1023 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001024 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1025 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001026 return;
1027 }
1028
mukesh agrawalc01f3982012-01-24 13:48:39 -08001029 if (new_state == wpa_supplicant::kInterfaceStateCompleted &&
1030 !affected_service->IsConnected()) {
1031 if (AcquireIPConfig()) {
1032 LOG(INFO) << link_name() << " is up; should start L3!";
1033 affected_service->SetState(Service::kStateConfiguring);
1034 } else {
1035 LOG(ERROR) << "Unable to acquire DHCP config.";
1036 }
mukesh agrawal15908392011-11-16 18:29:25 +00001037 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1038 affected_service->SetState(Service::kStateAssociating);
1039 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1040 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1041 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1042 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1043 // Ignore transitions into these states from Completed, to avoid
1044 // bothering the user when roaming, or re-keying.
1045 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1046 affected_service->SetState(Service::kStateAssociating);
1047 // TOOD(quiche): On backwards transitions, we should probably set
1048 // a timeout for getting back into the completed state. At present,
1049 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001050 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001051 // (crosbug.com/23207)
1052 } else {
1053 // Other transitions do not affect Service state.
1054 //
1055 // Note in particular that we ignore a State change into
1056 // kInterfaceStateDisconnected, in favor of observing the corresponding
1057 // change in CurrentBSS.
1058 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001059}
1060
1061// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001062WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001063 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001064
1065 if (args.ContainsString(flimflam::kModeProperty) &&
1066 args.GetString(flimflam::kModeProperty) !=
1067 flimflam::kModeManaged) {
1068 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceMode);
1069 return NULL;
1070 }
1071
1072 if (!args.ContainsString(flimflam::kSSIDProperty)) {
1073 error->Populate(Error::kInvalidArguments, kManagerErrorSSIDRequired);
1074 return NULL;
1075 }
1076
1077 string ssid = args.GetString(flimflam::kSSIDProperty);
1078 if (ssid.length() < 1) {
1079 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooShort);
1080 return NULL;
1081 }
1082
1083 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
1084 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooLong);
1085 return NULL;
1086 }
1087
1088 string security_method;
1089 if (args.ContainsString(flimflam::kSecurityProperty)) {
1090 security_method = args.GetString(flimflam::kSecurityProperty);
1091 } else {
1092 security_method = flimflam::kSecurityNone;
1093 }
1094
1095 if (security_method != flimflam::kSecurityNone &&
1096 security_method != flimflam::kSecurityWep &&
1097 security_method != flimflam::kSecurityPsk &&
1098 security_method != flimflam::kSecurityWpa &&
1099 security_method != flimflam::kSecurityRsn &&
1100 security_method != flimflam::kSecurity8021x) {
1101 error->Populate(Error::kNotSupported,
1102 kManagerErrorUnsupportedSecurityMode);
1103 return NULL;
1104 }
1105
1106 if ((security_method == flimflam::kSecurityWep ||
1107 security_method == flimflam::kSecurityPsk ||
1108 security_method == flimflam::kSecurityWpa ||
1109 security_method == flimflam::kSecurityRsn) &&
1110 !args.ContainsString(flimflam::kPassphraseProperty)) {
1111 error->Populate(Error::kInvalidArguments,
1112 kManagerErrorPassphraseRequired);
1113 return NULL;
1114 }
1115
Paul Stewart6ab23a92011-11-09 17:17:47 -08001116 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001117 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1118 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001119 } else {
1120 // If the service is not found, and the caller hasn't specified otherwise,
1121 // we assume this is is a hidden network.
1122 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001123 }
1124
Paul Stewart6ab23a92011-11-09 17:17:47 -08001125 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1126 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1127 security_method));
1128 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001129 service = new WiFiService(control_interface(),
1130 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001131 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001132 manager(),
1133 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001134 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001135 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001136 security_method,
1137 hidden_ssid);
1138 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001139 // NB: We do not register the newly created Service with the Manager.
1140 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001141 }
1142
mukesh agrawal1a056262011-10-05 14:36:54 -07001143 if (security_method == flimflam::kSecurityWep ||
1144 security_method == flimflam::kSecurityPsk ||
1145 security_method == flimflam::kSecurityWpa ||
1146 security_method == flimflam::kSecurityRsn) {
1147 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
1148 error);
1149 if (error->IsFailure()) {
1150 return NULL;
1151 }
1152 }
1153
Paul Stewartcb59fed2012-03-21 21:14:46 -07001154 service->Configure(args, error);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001155
Paul Stewart7f61e522012-03-22 11:13:45 -07001156 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1157 // have no endpoints like the one we may have just created. crosbug.com/28224
1158
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001159 return service;
1160}
1161
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001162// static
1163bool WiFi::SanitizeSSID(string *ssid) {
1164 CHECK(ssid);
1165
1166 size_t ssid_len = ssid->length();
1167 size_t i;
1168 bool changed = false;
1169
Gary Morainac1bdb42012-02-16 17:42:29 -08001170 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001171 if (!g_ascii_isprint((*ssid)[i])) {
1172 (*ssid)[i] = '?';
1173 changed = true;
1174 }
1175 }
1176
1177 return changed;
1178}
1179
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001180void WiFi::HelpRegisterDerivedInt32(
1181 PropertyStore *store,
1182 const string &name,
1183 int32(WiFi::*get)(Error *error),
1184 void(WiFi::*set)(const int32 &value, Error *error)) {
1185 store->RegisterDerivedInt32(
1186 name,
1187 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1188}
1189
1190void WiFi::HelpRegisterDerivedString(
1191 PropertyStore *store,
1192 const string &name,
1193 string(WiFi::*get)(Error *error),
1194 void(WiFi::*set)(const string &value, Error *error)) {
1195 store->RegisterDerivedString(
1196 name,
1197 StringAccessor(new CustomAccessor<WiFi, string>(this, get, set)));
1198}
1199
1200void WiFi::HelpRegisterDerivedUint16(
1201 PropertyStore *store,
1202 const string &name,
1203 uint16(WiFi::*get)(Error *error),
1204 void(WiFi::*set)(const uint16 &value, Error *error)) {
1205 store->RegisterDerivedUint16(
1206 name,
1207 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1208}
1209
Gary Morainac1bdb42012-02-16 17:42:29 -08001210void WiFi::HandlePowerStateChange(PowerManager::SuspendState new_state) {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001211 LOG(INFO) << __func__;
1212 if (new_state == PowerManagerProxyDelegate::kOn) {
1213 // We want to flush the BSS cache, but we don't want to conflict
1214 // with a running scan or an active connection attempt. So record
1215 // the need to flush, and take care of flushing when the next scan
1216 // completes.
1217 //
1218 // Note that supplicant will automatically expire old cache
1219 // entries (after, e.g., a BSS is not found in two consecutive
1220 // scans). However, our explicit flush accelerates re-association
1221 // in cases where a BSS disappeared while we were asleep. (See,
1222 // e.g. WiFiRoaming.005SuspendRoam.)
1223 time_->GetTimeMonotonic(&resumed_at_);
1224 need_bss_flush_ = true;
1225
1226 if (!scan_pending_ && IsIdle()) {
1227 // Not scanning/connecting/connected, so let's get things rolling.
1228 Scan(NULL);
1229 }
Gary Morainac1bdb42012-02-16 17:42:29 -08001230 }
1231}
1232
Paul Stewartb50f0b92011-05-16 16:31:42 -07001233} // namespace shill