blob: dc8a55ec8936bd01604ee2adaeffd725d7faffd7 [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>
mukesh agrawal15908392011-11-16 18:29:25 +000019#include <base/stringprintf.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070020#include <base/string_number_conversions.h>
21#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070022#include <chromeos/dbus/service_constants.h>
mukesh agrawal16bc1b82012-02-09 18:38:26 -080023#include <glib.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070024
Wade Guthried6153612012-08-23 11:36:14 -070025#include "shill/config80211.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070026#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"
Paul Stewart3c508e12012-08-09 11:40:06 -070033#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070034#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070035#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000036#include "shill/metrics.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070037#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080038#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070039#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050040#include "shill/rtnl_handler.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;
Darin Petkov4a66cc52012-06-15 10:08:29 +020065// Scan interval while connected.
66const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070067// Note that WiFi generates some manager-level errors, because it implements
68// the Manager.GetWiFiService flimflam API. The API is implemented here,
69// rather than in manager, to keep WiFi-specific logic in the right place.
mukesh agrawal7a4e4002011-09-06 11:26:05 -070070const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
71const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
72const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070073const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
74 "security mode is unsupported";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070075const char WiFi::kManagerErrorUnsupportedServiceMode[] =
76 "service mode is unsupported";
mukesh agrawal5c05b292012-03-07 10:12:52 -080077// Age (in seconds) beyond which a BSS cache entry will not be preserved,
78// across a suspend/resume.
79const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000080const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070081const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070082const int WiFi::kNumFastScanAttempts = 3;
83const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070084const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070085const int WiFi::kReconnectTimeoutSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070086
Paul Stewartb50f0b92011-05-16 16:31:42 -070087WiFi::WiFi(ControlInterface *control_interface,
88 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080089 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070090 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070091 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080092 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070093 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070094 : Device(control_interface,
95 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080096 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070097 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070098 link,
Chris Masone626719f2011-08-18 16:58:48 -070099 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800100 interface_index,
101 Technology::kWifi),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500102 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700103 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800104 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200105 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000106 supplicant_state_(kInterfaceStateUnknown),
107 supplicant_bss_("(unknown)"),
Paul Stewart66c86002012-01-30 18:00:52 -0800108 clear_cached_credentials_pending_(false),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800109 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +0200110 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700111 fast_scans_remaining_(kNumFastScanAttempts),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800112 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
113 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700114 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800115 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700116 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200117 store->RegisterDerivedString(
118 flimflam::kBgscanMethodProperty,
119 StringAccessor(
120 // TODO(petkov): CustomMappedAccessor is used for convenience because
121 // it provides a way to define a custom clearer (unlike
122 // CustomAccessor). We need to implement a fully custom accessor with
123 // no extra argument.
124 new CustomMappedAccessor<WiFi, string, int>(this,
125 &WiFi::ClearBgscanMethod,
126 &WiFi::GetBgscanMethod,
127 &WiFi::SetBgscanMethod,
128 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800129 HelpRegisterDerivedUint16(store,
130 flimflam::kBgscanShortIntervalProperty,
131 &WiFi::GetBgscanShortInterval,
132 &WiFi::SetBgscanShortInterval);
133 HelpRegisterDerivedInt32(store,
134 flimflam::kBgscanSignalThresholdProperty,
135 &WiFi::GetBgscanSignalThreshold,
136 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700137
Chris Masoneb925cc82011-06-22 15:39:57 -0700138 // TODO(quiche): Decide if scan_pending_ is close enough to
139 // "currently scanning" that we don't care, or if we want to track
140 // scan pending/currently scanning/no scan scheduled as a tri-state
141 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700142 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800143 HelpRegisterDerivedUint16(store,
144 flimflam::kScanIntervalProperty,
145 &WiFi::GetScanInterval,
146 &WiFi::SetScanInterval);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700147 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700148}
149
mukesh agrawalaf571952011-07-14 14:31:12 -0700150WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700151
Eric Shienbrood9a245532012-03-07 14:20:39 -0500152void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200153 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
154 if (enabled()) {
155 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700156 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500157 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200158 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500159 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200160 }
161 if (on_supplicant_appear_.IsCancelled()) {
162 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
163 // device instance.
164 on_supplicant_appear_.Reset(
165 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
166 on_supplicant_vanish_.Reset(
167 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
168 manager()->dbus_manager()->WatchName(wpa_supplicant::kDBusAddr,
169 on_supplicant_appear_.callback(),
170 on_supplicant_vanish_.callback());
171 }
172 // Connect to WPA supplicant if it's already present. If not, we'll connect to
173 // it when it appears.
174 ConnectToSupplicant();
Wade Guthried6153612012-08-23 11:36:14 -0700175 Config80211 *config80211 = Config80211::GetInstance();
176 if (config80211) {
177 config80211->SetWifiState(Config80211::kWifiUp);
178 }
mukesh agrawalab87ea42011-05-18 11:44:49 -0700179}
180
Eric Shienbrood9a245532012-03-07 14:20:39 -0500181void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700182 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200183 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700184 StopScanTimer();
mukesh agrawal15908392011-11-16 18:29:25 +0000185 endpoint_by_rpcid_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700186
Paul Stewartced6a0b2011-11-08 15:32:04 -0800187 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
188 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700189 ++it) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700190 SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
191 << (*it)->friendly_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700192 manager()->DeregisterService(*it);
193 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700194 rpcid_by_service_.clear();
Paul Stewartced6a0b2011-11-08 15:32:04 -0800195 services_.clear(); // breaks reference cycles
Paul Stewart549d44c2012-07-03 12:40:25 -0700196 supplicant_interface_proxy_.reset(); // breaks a reference cycle
197 // TODO(quiche): Remove interface from supplicant.
198 supplicant_process_proxy_.reset();
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
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200201 scan_pending_ = false;
Paul Stewart2b05e622012-07-13 20:38:44 -0700202 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700203 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700204
Eric Shienbrood9a245532012-03-07 14:20:39 -0500205 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
206 if (error)
207 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700208 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700209
Ben Chanfad4a0b2012-04-18 15:49:59 -0700210 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
211 << (supplicant_process_proxy_.get() ?
212 "is set." : "is not set.");
213 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
214 << (supplicant_interface_proxy_.get() ?
215 "is set." : "is not set.");
216 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
217 << (pending_service_.get() ? "is set." : "is not set.");
218 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
219 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
220 SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
221 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700222}
223
Paul Stewarta41e38d2011-11-11 07:47:29 -0800224bool WiFi::Load(StoreInterface *storage) {
225 LoadHiddenServices(storage);
226 return Device::Load(storage);
227}
228
mukesh agrawal1830fa12011-09-26 14:31:40 -0700229void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700230 LOG(INFO) << __func__;
231
mukesh agrawal7ec71312011-11-10 02:08:26 +0000232 // Needs to send a D-Bus message, but may be called from D-Bus
233 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700234 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500235 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700236}
237
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000238void WiFi::BSSAdded(const ::DBus::Path &path,
239 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500240 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000241 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500242 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
243 weak_ptr_factory_.GetWeakPtr(),
244 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000245}
246
247void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500248 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000249 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500250 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
251 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700252}
253
Paul Stewartbc6e7392012-05-24 07:07:48 -0700254void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
255 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
256 weak_ptr_factory_.GetWeakPtr(), properties));
257}
258
mukesh agrawal7ec71312011-11-10 02:08:26 +0000259void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200260 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000261 // Called from D-Bus signal handler, but may need to send a D-Bus
262 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500263 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
264 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000265}
266
mukesh agrawalb54601c2011-06-07 17:39:22 -0700267void WiFi::ScanDone() {
268 LOG(INFO) << __func__;
269
mukesh agrawal7ec71312011-11-10 02:08:26 +0000270 // Defer handling of scan result processing, because that processing
271 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700272 // registration can't be done in the context of a D-Bus signal
273 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500274 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
275 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700276}
277
mukesh agrawal6e277772011-09-29 15:04:23 -0700278void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000279 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800280 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700281 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700282
mukesh agrawal7ec71312011-11-10 02:08:26 +0000283 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000284 if (pending_service_ && pending_service_ == service) {
285 // TODO(quiche): Return an error to the caller. crosbug.com/23832
286 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
287 << service->friendly_name()
288 << ", which is already pending.";
289 return;
290 }
291
292 if (pending_service_ && pending_service_ != service) {
293 DisconnectFrom(pending_service_);
294 }
mukesh agrawal32399322011-09-01 10:53:43 -0700295
mukesh agrawal6e277772011-09-29 15:04:23 -0700296 try {
mukesh agrawal64896322011-12-01 01:13:10 +0000297 const uint32_t scan_ssid = 1; // "True": Use directed probe.
298 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
299 append_uint32(scan_ssid);
Darin Petkov4a66cc52012-06-15 10:08:29 +0200300 AppendBgscan(service, &service_params);
301 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000302 rpcid_by_service_[service] = network_path;
Ben Chan80326f32012-05-04 17:51:32 -0700303 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700304 LOG(ERROR) << "exception while adding network: " << e.what();
305 return;
306 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700307
mukesh agrawal445e72c2011-06-22 11:13:50 -0700308 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700309 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000310 CHECK(current_service_.get() != pending_service_.get());
311
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700312 // SelectService here (instead of in LinkEvent, like Ethernet), so
313 // that, if we fail to bring up L2, we can attribute failure correctly.
314 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000315 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700316 // reconsider if this is the right place to change the selected service.
317 // see discussion in crosbug.com/20191.
318 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000319}
320
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000321void WiFi::DisconnectFrom(WiFiService *service) {
322 if (service != current_service_ && service != pending_service_) {
323 // TODO(quiche): Once we have asynchronous reply support, we should
324 // generate a D-Bus error here. (crosbug.com/23832)
325 LOG(WARNING) << "In " << __func__ << "(): "
326 << " ignoring request to disconnect from service "
327 << service->friendly_name()
328 << " which is neither current nor pending";
329 return;
330 }
331
332 if (pending_service_ && service != pending_service_) {
333 // TODO(quiche): Once we have asynchronous reply support, we should
334 // generate a D-Bus error here. (crosbug.com/23832)
335 LOG(WARNING) << "In " << __func__ << "(): "
336 << " ignoring request to disconnect from service "
337 << service->friendly_name()
338 << " which is not the pending service.";
339 return;
340 }
341
342 if (!pending_service_ && service != current_service_) {
343 // TODO(quiche): Once we have asynchronous reply support, we should
344 // generate a D-Bus error here. (crosbug.com/23832)
345 LOG(WARNING) << "In " << __func__ << "(): "
346 << " ignoring request to disconnect from service "
347 << service->friendly_name()
348 << " which is not the current service.";
349 return;
350 }
351
Paul Stewartff96a842012-08-13 15:59:10 -0700352 if (pending_service_) {
353 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
354 // on this to drive the service state back to idle. Do that here.
355 pending_service_->SetState(Service::kStateIdle);
356 }
357
Paul Stewart2b05e622012-07-13 20:38:44 -0700358 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700359 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700360
361 if (!supplicant_present_) {
362 LOG(INFO) << "In " << __func__ << "(): "
363 << "wpa_supplicant is not present; silently resetting "
364 << "current_service_.";
365 current_service_ = NULL;
366 return;
367 }
368
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000369 try {
370 supplicant_interface_proxy_->Disconnect();
371 // We'll call RemoveNetwork and reset |current_service_| after
372 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700373 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000374 // Can't depend on getting a notification of CurrentBSS change.
375 // So effect changes immediately.
376 ReverseServiceMap::const_iterator rpcid_it =
377 rpcid_by_service_.find(service);
378 DCHECK(rpcid_it != rpcid_by_service_.end());
379 if (rpcid_it == rpcid_by_service_.end()) {
380 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
381 << service->friendly_name() << ": "
382 << "could not find supplicant network to disable.";
383 } else {
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700384 RemoveNetwork(rpcid_it->second);
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000385 }
386 current_service_ = NULL;
387 }
388
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800389 CHECK(current_service_ == NULL ||
390 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000391}
392
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700393bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
394 try {
395 supplicant_interface_proxy_->RemoveNetwork(network);
396 } catch (const DBus::Error &e) { // NOLINT
397 return false;
398 }
399 return true;
400}
401
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000402bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800403 return !current_service_ && !pending_service_;
404}
405
Paul Stewart66c86002012-01-30 18:00:52 -0800406void WiFi::ClearCachedCredentials() {
407 LOG(INFO) << __func__;
408
409 // Needs to send a D-Bus message, but may be called from D-Bus
410 // caller context (via Manager::PopProfile). So defer work
411 // to event loop.
412 if (!clear_cached_credentials_pending_) {
413 clear_cached_credentials_pending_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500414 dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask,
415 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart66c86002012-01-30 18:00:52 -0800416 }
417}
418
mukesh agrawalb20776f2012-02-10 16:00:36 -0800419void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
420 WiFiService *service = FindServiceForEndpoint(endpoint);
421 DCHECK(service);
422 if (service) {
423 service->NotifyEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800424 }
425}
426
Darin Petkov4a66cc52012-06-15 10:08:29 +0200427void WiFi::AppendBgscan(WiFiService *service,
428 map<string, DBus::Variant> *service_params) const {
429 int scan_interval = kBackgroundScanIntervalSeconds;
430 string method = bgscan_method_;
431 if (method.empty()) {
432 // If multiple APs are detected for this SSID, configure the default method.
433 // Otherwise, disable background scanning completely.
434 if (service->GetEndpointCount() > 1) {
435 method = kDefaultBgscanMethod;
436 } else {
437 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
438 return;
439 }
Christopher Wileya998df22012-07-11 15:14:55 -0700440 } else if (method.compare(wpa_supplicant::kNetworkBgscanMethodNone) == 0) {
441 LOG(INFO) << "Background scan disabled -- chose None method.";
442 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200443 } else {
444 // If the background scan method was explicitly specified, honor the
445 // configured background scan interval.
446 scan_interval = scan_interval_seconds_;
447 }
448 DCHECK(!method.empty());
449 string config_string = StringPrintf("%s:%d:%d:%d",
450 method.c_str(),
451 bgscan_short_interval_seconds_,
452 bgscan_signal_threshold_dbm_,
453 scan_interval);
454 LOG(INFO) << "Background scan: " << config_string;
455 (*service_params)[wpa_supplicant::kNetworkPropertyBgscan].writer()
456 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800457}
458
Darin Petkov4a66cc52012-06-15 10:08:29 +0200459string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
460 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
461}
462
463void WiFi::SetBgscanMethod(
464 const int &/*argument*/, const string &method, Error *error) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800465 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
Christopher Wileya998df22012-07-11 15:14:55 -0700466 method != wpa_supplicant::kNetworkBgscanMethodLearn &&
467 method != wpa_supplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800468 const string error_message =
469 StringPrintf("Unrecognized bgscan method %s", method.c_str());
470 LOG(WARNING) << error_message;
471 error->Populate(Error::kInvalidArguments, error_message);
472 return;
473 }
474
475 bgscan_method_ = method;
476 // We do not update kNetworkPropertyBgscan for |pending_service_| or
477 // |current_service_|, because supplicant does not allow for
478 // reconfiguration without disconnect and reconnect.
479}
480
481void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
482 bgscan_short_interval_seconds_ = seconds;
483 // We do not update kNetworkPropertyBgscan for |pending_service_| or
484 // |current_service_|, because supplicant does not allow for
485 // reconfiguration without disconnect and reconnect.
486}
487
488void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
489 bgscan_signal_threshold_dbm_ = dbm;
490 // We do not update kNetworkPropertyBgscan for |pending_service_| or
491 // |current_service_|, because supplicant does not allow for
492 // reconfiguration without disconnect and reconnect.
493}
494
495void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
496 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700497 if (running()) {
498 StartScanTimer();
499 }
500 // The scan interval affects both foreground scans (handled by
501 // |scan_timer_callback_|), and background scans (handled by
502 // supplicant). However, we do not update |pending_service_| or
503 // |current_service_|, because supplicant does not allow for
504 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800505}
506
Darin Petkov4a66cc52012-06-15 10:08:29 +0200507void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
508 bgscan_method_.clear();
509}
510
mukesh agrawal165e6142011-11-22 02:22:56 +0000511// To avoid creating duplicate services, call FindServiceForEndpoint
512// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000513WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
514 bool hidden_ssid) {
515 WiFiServiceRefPtr service =
516 new WiFiService(control_interface(),
517 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800518 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000519 manager(),
520 this,
521 endpoint.ssid(),
522 endpoint.network_mode(),
523 endpoint.security_mode(),
524 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000525 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000526 return service;
527}
528
529void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700530 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
531 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000532 supplicant_bss_ = new_bss;
Paul Stewart44663922012-07-30 11:03:03 -0700533
534 // Any change in CurrentBSS means supplicant is actively changing our
535 // connectivity. We no longer need to track any previously pending
536 // reconnect.
537 StopReconnectTimer();
538
mukesh agrawal15908392011-11-16 18:29:25 +0000539 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
540 HandleDisconnect();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700541 if (!GetHiddenSSIDList().empty()) {
542 // Before disconnecting, wpa_supplicant probably scanned for
543 // APs. So, in the normal case, we defer to the timer for the next scan.
544 //
545 // However, in the case of hidden SSIDs, supplicant knows about
546 // at most one of them. (That would be the hidden SSID we were
547 // connected to, if applicable.)
548 //
549 // So, in this case, we initiate an immediate scan. This scan
550 // will include the hidden SSIDs we know about (up to the limit of
551 // kScanMAxSSIDsPerScan).
552 //
553 // We may want to reconsider this immediate scan, if/when shill
554 // takes greater responsibility for scanning (vs. letting
555 // supplicant handle most of it).
556 Scan(NULL);
557 }
mukesh agrawal15908392011-11-16 18:29:25 +0000558 } else {
559 HandleRoam(new_bss);
560 }
561
Paul Stewart2b05e622012-07-13 20:38:44 -0700562 // If we are selecting a new service, or if we're clearing selection
563 // of a something other than the pending service, call SelectService.
564 // Otherwise skip SelectService, since this will cause the pending
565 // service to be marked as Idle.
566 if (current_service_ || selected_service() != pending_service_) {
567 SelectService(current_service_);
568 }
569
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000570 // Invariant check: a Service can either be current, or pending, but
571 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000572 CHECK(current_service_.get() != pending_service_.get() ||
573 current_service_.get() == NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000574}
575
576void WiFi::HandleDisconnect() {
577 // Identify the affected service. We expect to get a disconnect
578 // event when we fall off a Service that we were connected
579 // to. However, we also allow for the case where we get a disconnect
580 // event while attempting to connect from a disconnected state.
581 WiFiService *affected_service =
582 current_service_.get() ? current_service_.get() : pending_service_.get();
583
584 current_service_ = NULL;
585 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700586 SLOG(WiFi, 2) << "WiFi " << link_name()
587 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000588 return;
Paul Stewart20b0a092012-05-22 20:39:57 -0700589 } else if (affected_service == selected_service()) {
590 // If our selected service has disconnected, destroy IP configuration state.
591 DestroyIPConfig();
mukesh agrawal15908392011-11-16 18:29:25 +0000592 }
593
594 ReverseServiceMap::const_iterator rpcid_it =
595 rpcid_by_service_.find(affected_service);
596 if (rpcid_it == rpcid_by_service_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700597 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
598 << " (or failed to connect to) "
599 << affected_service->friendly_name() << ", "
600 << "but could not find supplicant network to disable.";
mukesh agrawalcf24a242012-05-21 16:46:11 -0700601 } else {
602 // TODO(quiche): Reconsider giving up immediately. Maybe give
603 // wpa_supplicant some time to retry, first.
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700604 if (!RemoveNetwork(rpcid_it->second)) {
605 LOG(FATAL) << "RemoveNetwork for " << rpcid_it->second << " failed.";
606 }
mukesh agrawal15908392011-11-16 18:29:25 +0000607 }
608
Ben Chanfad4a0b2012-04-18 15:49:59 -0700609 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
610 << " (or failed to connect to) "
611 << affected_service->friendly_name();
mukesh agrawalcf24a242012-05-21 16:46:11 -0700612 if (SuspectCredentials(*affected_service)) {
613 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700614 // mole in Chrome.
Paul Stewartf2d60912012-07-15 08:37:30 -0700615 affected_service->SetFailure(Service::kFailureBadPassphrase);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700616 LOG(ERROR) << "Connection failure during 4-Way Handshake. Bad passphrase?";
Paul Stewartf2d60912012-07-15 08:37:30 -0700617 } else {
618 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700619 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800620 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000621 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000622
623 if (affected_service == pending_service_.get()) {
624 // The attempt to connect to |pending_service_| failed. Clear
625 // |pending_service_|, to indicate we're no longer in the middle
626 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700627 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000628 } else if (pending_service_.get()) {
629 // We've attributed the disconnection to what was the
630 // |current_service_|, rather than the |pending_service_|.
631 //
632 // If we're wrong about that (i.e. supplicant reported this
633 // CurrentBSS change after attempting to connect to
634 // |pending_service_|), we're depending on supplicant to retry
635 // connecting to |pending_service_|, and delivering another
636 // CurrentBSS change signal in the future.
637 //
638 // Log this fact, to help us debug (in case our assumptions are
639 // wrong).
Ben Chanfad4a0b2012-04-18 15:49:59 -0700640 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to "
641 << pending_service_->friendly_name()
642 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000643 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700644
645 // If we disconnect, initially scan at a faster frequency, to make sure
646 // we've found all available APs.
647 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000648}
649
650// We use the term "Roam" loosely. In particular, we include the case
651// where we "Roam" to a BSS from the disconnected state.
652void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
653 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
654 if (endpoint_it == endpoint_by_rpcid_.end()) {
655 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
656 << new_bss;
657 return;
658 }
659
660 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000661 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000662 if (!service.get()) {
663 LOG(WARNING) << "WiFi " << link_name()
664 << " could not find Service for Endpoint "
665 << endpoint.bssid_string()
666 << " (service will be unchanged)";
667 return;
668 }
669
Ben Chanfad4a0b2012-04-18 15:49:59 -0700670 SLOG(WiFi, 2) << "WiFi " << link_name()
671 << " roamed to Endpoint " << endpoint.bssid_string()
672 << " (SSID " << endpoint.ssid_string() << ")";
mukesh agrawal15908392011-11-16 18:29:25 +0000673
mukesh agrawale1d90e92012-02-15 17:36:08 -0800674 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000675
mukesh agrawal15908392011-11-16 18:29:25 +0000676 if (pending_service_.get() &&
677 service.get() != pending_service_.get()) {
678 // The Service we've roamed on to is not the one we asked for.
679 // We assume that this is transient, and that wpa_supplicant
680 // is trying / will try to connect to |pending_service_|.
681 //
682 // If it succeeds, we'll end up back here, but with |service|
683 // pointing at the same service as |pending_service_|.
684 //
685 // If it fails, we'll process things in HandleDisconnect.
686 //
687 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700688 SLOG(WiFi, 2) << "WiFi " << link_name()
689 << " new current Endpoint "
690 << endpoint.bssid_string()
691 << " is not part of pending service "
692 << pending_service_->friendly_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000693
694 // Sanity check: if we didn't roam onto |pending_service_|, we
695 // should still be on |current_service_|.
696 if (service.get() != current_service_.get()) {
697 LOG(WARNING) << "WiFi " << link_name()
698 << " new current Endpoint "
699 << endpoint.bssid_string()
700 << " is neither part of pending service "
701 << pending_service_->friendly_name()
702 << " nor part of current service "
703 << (current_service_.get() ?
704 current_service_->friendly_name() :
705 "(NULL)");
706 // Although we didn't expect to get here, we should keep
707 // |current_service_| in sync with what supplicant has done.
708 current_service_ = service;
709 }
710 return;
711 }
712
713 if (pending_service_.get()) {
714 // We assume service.get() == pending_service_.get() here, because
715 // of the return in the previous if clause.
716 //
717 // Boring case: we've connected to the service we asked
718 // for. Simply update |current_service_| and |pending_service_|.
719 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700720 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000721 return;
722 }
723
724 // |pending_service_| was NULL, so we weren't attempting to connect
725 // to a new Service. Sanity check that we're still on
726 // |current_service_|.
727 if (service.get() != current_service_.get()) {
728 LOG(WARNING)
729 << "WiFi " << link_name()
730 << " new current Endpoint "
731 << endpoint.bssid_string()
732 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000733 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000734 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000735 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000736 // We didn't expect to be here, but let's cope as well as we
737 // can. Update |current_service_| to keep it in sync with
738 // supplicant.
739 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700740
741 // If this service isn't already marked as actively connecting (likely,
742 // since this service is a bit of a surprise) set the service as
743 // associating.
744 if (!current_service_->IsConnecting()) {
745 current_service_->SetState(Service::kStateAssociating);
746 }
747
mukesh agrawal15908392011-11-16 18:29:25 +0000748 return;
749 }
750
751 // At this point, we know that |pending_service_| was NULL, and that
752 // we're still on |current_service_|. This is the most boring case
753 // of all, because there's no state to update here.
754 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700755}
756
Paul Stewarta41e38d2011-11-11 07:47:29 -0800757WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
758 const string &mode,
759 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800760 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
761 it != services_.end();
762 ++it) {
763 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
764 (*it)->IsSecurityMatch(security)) {
765 return *it;
766 }
767 }
768 return NULL;
769}
770
mukesh agrawal165e6142011-11-22 02:22:56 +0000771WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
772 return FindService(endpoint.ssid(),
773 endpoint.network_mode(),
774 endpoint.security_mode());
775}
776
Paul Stewartced6a0b2011-11-08 15:32:04 -0800777ByteArrays WiFi::GetHiddenSSIDList() {
778 // Create a unique set of hidden SSIDs.
779 set<ByteArray> hidden_ssids_set;
780 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
781 it != services_.end();
782 ++it) {
Paul Stewart10ccbb32012-04-26 15:59:30 -0700783 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800784 hidden_ssids_set.insert((*it)->ssid());
785 }
786 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700787 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800788 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
789 if (!hidden_ssids.empty()) {
790 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
791 // not fit in.
792 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
793 hidden_ssids.erase(
794 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
795 hidden_ssids.end());
796 }
797 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
798 // SSIDs to wpa_supplicant, we need to explicitly specify the default
799 // behavior of doing a broadcast probe.
800 hidden_ssids.push_back(ByteArray());
801 }
802 return hidden_ssids;
803}
804
Paul Stewarta41e38d2011-11-11 07:47:29 -0800805bool WiFi::LoadHiddenServices(StoreInterface *storage) {
806 bool created_hidden_service = false;
807 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
808 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
809 bool is_hidden = false;
810 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700811 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
812 << "GetGroupsWithKey failed for GetBool("
813 << flimflam::kWifiHiddenSsid
814 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800815 continue;
816 }
817 if (!is_hidden) {
818 continue;
819 }
820 string ssid_hex;
821 vector<uint8_t> ssid_bytes;
822 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
823 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700824 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
825 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800826 continue;
827 }
828 string device_address;
829 string network_mode;
830 string security;
831 // It is gross that we have to do this, but the only place we can
832 // get information about the service is from its storage name.
833 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
834 &network_mode, &security) ||
835 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700836 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
837 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800838 continue;
839 }
840 if (FindService(ssid_bytes, network_mode, security).get()) {
841 // If service already exists, we have nothing to do, since the
842 // service has already loaded its configuration from storage.
843 // This is guaranteed to happen in both cases where Load() is
844 // called on a device (via a ConfigureDevice() call on the
845 // profile):
846 // - In RegisterDevice() the Device hasn't been started yet,
847 // so it has no services registered, except for those
848 // created by previous iterations of LoadHiddenService().
849 // The latter can happen if another profile in the Manager's
850 // stack defines the same ssid/mode/security. Even this
851 // case is okay, since even if the profiles differ
852 // materially on configuration and credentials, the "right"
853 // one will be configured in the course of the
854 // RegisterService() call below.
855 // - In PushProfile(), all registered services have been
856 // introduced to the profile via ConfigureService() prior
857 // to calling ConfigureDevice() on the profile.
858 continue;
859 }
860 WiFiServiceRefPtr service(new WiFiService(control_interface(),
861 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800862 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800863 manager(),
864 this,
865 ssid_bytes,
866 network_mode,
867 security,
868 true));
869 services_.push_back(service);
870
871 // By registering the service, the rest of the configuration
872 // will be loaded from the profile into the service via ConfigureService().
873 manager()->RegisterService(service);
874
875 created_hidden_service = true;
876 }
877
878 // If we are idle and we created a service as a result of opening the
879 // profile, we should initiate a scan for our new hidden SSID.
880 if (running() && created_hidden_service &&
881 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
882 Scan(NULL);
883 }
884
885 return created_hidden_service;
886}
887
Paul Stewart66c86002012-01-30 18:00:52 -0800888void WiFi::ClearCachedCredentialsTask() {
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700889 try {
Paul Stewartf48eca42012-10-11 12:10:02 -0700890 // Supplicant may have disappeared by the time this task gets to run.
891 if (supplicant_interface_proxy_.get()) {
892 supplicant_interface_proxy_->ClearCachedCredentials();
893 } else {
894 SLOG(WiFi, 1) << "In " << __func__ << ": supplicant proxy is NULL.";
895 }
Ben Chan80326f32012-05-04 17:51:32 -0700896 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700897 LOG(WARNING) << "Clear of cached credentials failed.";
898 }
Paul Stewart66c86002012-01-30 18:00:52 -0800899 clear_cached_credentials_pending_ = false;
900}
901
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000902void WiFi::BSSAddedTask(
903 const ::DBus::Path &path,
904 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000905 // Note: we assume that BSSIDs are unique across endpoints. This
906 // means that if an AP reuses the same BSSID for multiple SSIDs, we
907 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800908 WiFiEndpointRefPtr endpoint(
909 new WiFiEndpoint(proxy_factory_, this, path, properties));
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000910 LOG(INFO) << "Found endpoint. "
mukesh agrawale9adda12012-02-09 18:33:48 -0800911 << "RPC path: " << path << ", "
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000912 << "ssid: " << endpoint->ssid_string() << ", "
913 << "bssid: " << endpoint->bssid_string() << ", "
914 << "signal: " << endpoint->signal_strength() << ", "
Thieu Lee41a72d2012-02-06 20:46:51 +0000915 << "security: " << endpoint->security_mode() << ", "
916 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000917
918 if (endpoint->ssid_string().empty()) {
919 // Don't bother trying to find or create a Service for an Endpoint
920 // without an SSID. We wouldn't be able to connect to it anyway.
921 return;
922 }
923
mukesh agrawalb3857612012-01-18 16:23:29 -0800924 if (endpoint->ssid()[0] == 0) {
925 // Assume that an SSID starting with NULL is bogus/misconfigured,
926 // and filter it out.
927 return;
928 }
929
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000930 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
931 if (service) {
932 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
933 << " to service " << service->friendly_name() << ".";
934 service->AddEndpoint(endpoint);
935
936 if (manager()->HasService(service)) {
937 manager()->UpdateService(service);
938 } else {
Darin Petkov4a66cc52012-06-15 10:08:29 +0200939 // Expect registered by now if >1.
940 DCHECK_EQ(1, service->GetEndpointCount());
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000941 manager()->RegisterService(service);
942 }
mukesh agrawale9adda12012-02-09 18:33:48 -0800943 } else {
944 const bool hidden_ssid = false;
945 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
946 LOG(INFO) << "New service " << service->GetRpcIdentifier()
947 << " (" << service->friendly_name() << ")";
948 service->AddEndpoint(endpoint);
949 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000950 }
951
mukesh agrawale9adda12012-02-09 18:33:48 -0800952 // Do this last, to maintain the invariant that any Endpoint we
953 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800954 //
955 // TODO(quiche): Write test to verify correct behavior in the case
956 // where we get multiple BSSAdded events for a single endpoint.
957 // (Old Endpoint's refcount should fall to zero, and old Endpoint
958 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800959 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800960 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000961}
962
963void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
964 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
965 if (i == endpoint_by_rpcid_.end()) {
966 LOG(WARNING) << "WiFi " << link_name()
967 << " could not find BSS " << path
968 << " to remove.";
969 return;
970 }
971
972 WiFiEndpointRefPtr endpoint = i->second;
973 CHECK(endpoint);
974 endpoint_by_rpcid_.erase(i);
975
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000976 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -0800977 CHECK(service) << "Can't find Service for Endpoint "
978 << path << " "
979 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -0700980 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
981 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000982 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000983
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000984 bool disconnect_service = !service->HasEndpoints() &&
985 (service->IsConnecting() || service->IsConnected());
986 bool forget_service =
987 // Forget Services without Endpoints, except that we always keep
988 // hidden services around. (We need them around to populate the
989 // hidden SSIDs list.)
990 !service->HasEndpoints() && !service->hidden_ssid();
991 bool deregister_service =
992 // Only deregister a Service if we're forgetting it. Otherwise,
993 // Manager can't keep our configuration up-to-date (as Profiles
994 // change).
995 forget_service;
996
997 if (disconnect_service) {
998 DisconnectFrom(service);
999 }
1000
1001 if (deregister_service) {
1002 manager()->DeregisterService(service);
1003 } else {
1004 manager()->UpdateService(service);
1005 }
1006
1007 if (forget_service) {
1008 vector<WiFiServiceRefPtr>::iterator it;
1009 it = std::find(services_.begin(), services_.end(), service);
1010 if (it != services_.end()) {
1011 services_.erase(it);
1012 }
1013 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001014}
1015
Paul Stewartbc6e7392012-05-24 07:07:48 -07001016void WiFi::CertificationTask(
1017 const map<string, ::DBus::Variant> &properties) {
1018 if (!current_service_) {
1019 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1020 << " with no current service.";
1021 return;
1022 }
1023
1024 map<string, ::DBus::Variant>::const_iterator properties_it =
1025 properties.find(wpa_supplicant::kInterfacePropertyDepth);
1026 if (properties_it == properties.end()) {
1027 LOG(ERROR) << __func__ << " no depth parameter.";
1028 return;
1029 }
1030 uint32 depth = properties_it->second.reader().get_uint32();
1031 properties_it = properties.find(wpa_supplicant::kInterfacePropertySubject);
1032 if (properties_it == properties.end()) {
1033 LOG(ERROR) << __func__ << " no subject parameter.";
1034 return;
1035 }
1036 string subject(properties_it->second.reader().get_string());
1037 current_service_->AddEAPCertification(subject, depth);
1038}
1039
mukesh agrawal15908392011-11-16 18:29:25 +00001040void WiFi::PropertiesChangedTask(
1041 const map<string, ::DBus::Variant> &properties) {
1042 // TODO(quiche): Handle changes in other properties (e.g. signal
1043 // strength).
1044
1045 // Note that order matters here. In particular, we want to process
1046 // changes in the current BSS before changes in state. This is so
1047 // that we update the state of the correct Endpoint/Service.
1048
1049 map<string, ::DBus::Variant>::const_iterator properties_it =
1050 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
1051 if (properties_it != properties.end()) {
1052 CurrentBSSChanged(properties_it->second.reader().get_path());
1053 }
1054
1055 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
1056 if (properties_it != properties.end()) {
1057 StateChanged(properties_it->second.reader().get_string());
1058 }
1059}
1060
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001061void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001062 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001063 if (need_bss_flush_) {
1064 CHECK(supplicant_interface_proxy_ != NULL);
1065 // Compute |max_age| relative to |resumed_at_|, to account for the
1066 // time taken to scan.
1067 struct timeval now;
1068 uint32_t max_age;
1069 time_->GetTimeMonotonic(&now);
1070 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1071 supplicant_interface_proxy_->FlushBSS(max_age);
1072 need_bss_flush_ = false;
1073 }
mukesh agrawalb54601c2011-06-07 17:39:22 -07001074 scan_pending_ = false;
mukesh agrawalb66c6462012-05-07 11:45:25 -07001075 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001076}
1077
mukesh agrawal32399322011-09-01 10:53:43 -07001078void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001079 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001080 if (!enabled()) {
1081 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
1082 return;
1083 }
1084 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1085 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
1086 return;
1087 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001088 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -07001089 scan_args[wpa_supplicant::kPropertyScanType].writer().
1090 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001091
1092 ByteArrays hidden_ssids = GetHiddenSSIDList();
1093 if (!hidden_ssids.empty()) {
1094 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
1095 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1096 }
1097
Gaurav Shahf8721ee2011-11-07 09:12:46 -08001098 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -08001099 try {
1100 supplicant_interface_proxy_->Scan(scan_args);
1101 scan_pending_ = true;
Ben Chan80326f32012-05-04 17:51:32 -07001102 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001103 // A scan may fail if, for example, the wpa_supplicant vanishing
1104 // notification is posted after this task has already started running.
1105 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001106 }
mukesh agrawal32399322011-09-01 10:53:43 -07001107}
1108
mukesh agrawal15908392011-11-16 18:29:25 +00001109void WiFi::StateChanged(const string &new_state) {
1110 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001111 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001112 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1113 << old_state << " -> " << new_state;
1114
1115 WiFiService *affected_service;
1116 // Identify the service to which the state change applies. If
1117 // |pending_service_| is non-NULL, then the state change applies to
1118 // |pending_service_|. Otherwise, it applies to |current_service_|.
1119 //
1120 // This policy is driven by the fact that the |pending_service_|
1121 // doesn't become the |current_service_| until wpa_supplicant
1122 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001123 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001124 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1125 affected_service =
1126 pending_service_.get() ? pending_service_.get() : current_service_.get();
1127 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001128 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1129 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001130 return;
1131 }
1132
Paul Stewart44663922012-07-30 11:03:03 -07001133 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
1134 if (affected_service->IsConnected()) {
1135 StopReconnectTimer();
1136 } else if (AcquireIPConfigWithLeaseName(
1137 affected_service->GetStorageIdentifier())) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001138 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001139 affected_service->SetState(Service::kStateConfiguring);
1140 } else {
1141 LOG(ERROR) << "Unable to acquire DHCP config.";
1142 }
mukesh agrawal15908392011-11-16 18:29:25 +00001143 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1144 affected_service->SetState(Service::kStateAssociating);
1145 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1146 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1147 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1148 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1149 // Ignore transitions into these states from Completed, to avoid
1150 // bothering the user when roaming, or re-keying.
1151 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1152 affected_service->SetState(Service::kStateAssociating);
1153 // TOOD(quiche): On backwards transitions, we should probably set
1154 // a timeout for getting back into the completed state. At present,
1155 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001156 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001157 // (crosbug.com/23207)
Paul Stewart44663922012-07-30 11:03:03 -07001158 } else if (new_state == wpa_supplicant::kInterfaceStateDisconnected &&
1159 affected_service == current_service_ &&
1160 affected_service->IsConnected()) {
1161 // This means that wpa_supplicant failed in a re-connect attempt, but
1162 // may still be reconnecting. Give wpa_supplicant a limited amount of
1163 // time to transition out this condition by either connecting or changing
1164 // CurrentBSS.
1165 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001166 } else {
1167 // Other transitions do not affect Service state.
1168 //
1169 // Note in particular that we ignore a State change into
1170 // kInterfaceStateDisconnected, in favor of observing the corresponding
1171 // change in CurrentBSS.
1172 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001173}
1174
mukesh agrawalcf24a242012-05-21 16:46:11 -07001175bool WiFi::SuspectCredentials(const WiFiService &service) const {
1176 if (!service.IsSecurityMatch(flimflam::kSecurityPsk)) {
1177 // We can only diagnose credentials for WPA/RSN networks. For
1178 // others, assume the failure was not credential related.
1179 return false;
1180 }
1181
1182 return supplicant_state_ == wpa_supplicant::kInterfaceState4WayHandshake &&
1183 !service.has_ever_connected();
1184}
1185
mukesh agrawal7ec71312011-11-10 02:08:26 +00001186// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001187WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001188 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001189
1190 if (args.ContainsString(flimflam::kModeProperty) &&
1191 args.GetString(flimflam::kModeProperty) !=
1192 flimflam::kModeManaged) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001193 Error::PopulateAndLog(error, Error::kNotSupported,
1194 kManagerErrorUnsupportedServiceMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001195 return NULL;
1196 }
1197
1198 if (!args.ContainsString(flimflam::kSSIDProperty)) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001199 Error::PopulateAndLog(error, Error::kInvalidArguments,
1200 kManagerErrorSSIDRequired);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001201 return NULL;
1202 }
1203
1204 string ssid = args.GetString(flimflam::kSSIDProperty);
1205 if (ssid.length() < 1) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001206 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1207 kManagerErrorSSIDTooShort);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001208 return NULL;
1209 }
1210
1211 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001212 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1213 kManagerErrorSSIDTooLong);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001214 return NULL;
1215 }
1216
1217 string security_method;
1218 if (args.ContainsString(flimflam::kSecurityProperty)) {
1219 security_method = args.GetString(flimflam::kSecurityProperty);
1220 } else {
1221 security_method = flimflam::kSecurityNone;
1222 }
1223
1224 if (security_method != flimflam::kSecurityNone &&
1225 security_method != flimflam::kSecurityWep &&
1226 security_method != flimflam::kSecurityPsk &&
1227 security_method != flimflam::kSecurityWpa &&
1228 security_method != flimflam::kSecurityRsn &&
1229 security_method != flimflam::kSecurity8021x) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001230 Error::PopulateAndLog(error, Error::kNotSupported,
1231 kManagerErrorUnsupportedSecurityMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001232 return NULL;
1233 }
1234
Paul Stewart6ab23a92011-11-09 17:17:47 -08001235 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001236 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1237 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001238 } else {
1239 // If the service is not found, and the caller hasn't specified otherwise,
1240 // we assume this is is a hidden network.
1241 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001242 }
1243
Paul Stewart6ab23a92011-11-09 17:17:47 -08001244 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1245 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1246 security_method));
1247 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001248 service = new WiFiService(control_interface(),
1249 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001250 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001251 manager(),
1252 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001253 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001254 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001255 security_method,
1256 hidden_ssid);
1257 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001258 // NB: We do not register the newly created Service with the Manager.
1259 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001260 }
1261
Paul Stewartcb59fed2012-03-21 21:14:46 -07001262 service->Configure(args, error);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001263
Paul Stewart7f61e522012-03-22 11:13:45 -07001264 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1265 // have no endpoints like the one we may have just created. crosbug.com/28224
1266
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001267 return service;
1268}
1269
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001270// static
1271bool WiFi::SanitizeSSID(string *ssid) {
1272 CHECK(ssid);
1273
1274 size_t ssid_len = ssid->length();
1275 size_t i;
1276 bool changed = false;
1277
Gary Morainac1bdb42012-02-16 17:42:29 -08001278 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001279 if (!g_ascii_isprint((*ssid)[i])) {
1280 (*ssid)[i] = '?';
1281 changed = true;
1282 }
1283 }
1284
1285 return changed;
1286}
1287
Paul Stewart3c508e12012-08-09 11:40:06 -07001288void WiFi::OnLinkMonitorFailure() {
1289 // If we have never found the gateway, let's be conservative and not
1290 // do anything, in case this network topology does not have a gateway.
1291 if (!link_monitor()->IsGatewayFound()) {
1292 LOG(INFO) << "In " << __func__ << "(): "
1293 << "Skipping reassociate since gateway was never found.";
1294 return;
1295 }
1296
1297 if (!supplicant_present_) {
1298 LOG(ERROR) << "In " << __func__ << "(): "
1299 << "wpa_supplicant is not present. Cannot reassociate.";
1300 return;
1301 }
1302
1303 try {
1304 supplicant_interface_proxy_->Reassociate();
1305 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1306 } catch (const DBus::Error &e) { // NOLINT
1307 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1308 return;
1309 }
1310}
1311
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001312void WiFi::HelpRegisterDerivedInt32(
1313 PropertyStore *store,
1314 const string &name,
1315 int32(WiFi::*get)(Error *error),
1316 void(WiFi::*set)(const int32 &value, Error *error)) {
1317 store->RegisterDerivedInt32(
1318 name,
1319 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1320}
1321
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001322void WiFi::HelpRegisterDerivedUint16(
1323 PropertyStore *store,
1324 const string &name,
1325 uint16(WiFi::*get)(Error *error),
1326 void(WiFi::*set)(const uint16 &value, Error *error)) {
1327 store->RegisterDerivedUint16(
1328 name,
1329 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1330}
1331
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001332void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001333 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001334 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001335
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001336 // We want to flush the BSS cache, but we don't want to conflict
1337 // with a running scan or an active connection attempt. So record
1338 // the need to flush, and take care of flushing when the next scan
1339 // completes.
1340 //
1341 // Note that supplicant will automatically expire old cache
1342 // entries (after, e.g., a BSS is not found in two consecutive
1343 // scans). However, our explicit flush accelerates re-association
1344 // in cases where a BSS disappeared while we were asleep. (See,
1345 // e.g. WiFiRoaming.005SuspendRoam.)
1346 time_->GetTimeMonotonic(&resumed_at_);
1347 need_bss_flush_ = true;
1348
1349 if (!scan_pending_ && IsIdle()) {
1350 // Not scanning/connecting/connected, so let's get things rolling.
1351 Scan(NULL);
1352 } else {
1353 SLOG(WiFi, 1) << __func__
1354 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001355 }
1356}
1357
Paul Stewarte369ece2012-05-22 09:11:03 -07001358void WiFi::RestartFastScanAttempts() {
1359 fast_scans_remaining_ = kNumFastScanAttempts;
1360 StartScanTimer();
1361}
1362
mukesh agrawalb66c6462012-05-07 11:45:25 -07001363void WiFi::StartScanTimer() {
1364 if (scan_interval_seconds_ == 0) {
1365 StopScanTimer();
1366 return;
1367 }
1368 scan_timer_callback_.Reset(
1369 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001370 // Repeat the first few scans after disconnect relatively quickly so we
1371 // have reasonable trust that no APs we are looking for are present.
1372 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1373 fast_scans_remaining_ > 0 ?
1374 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001375}
1376
1377void WiFi::StopScanTimer() {
1378 scan_timer_callback_.Cancel();
1379}
1380
1381void WiFi::ScanTimerHandler() {
1382 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1383 if (IsIdle() && !scan_pending_) {
1384 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001385 if (fast_scans_remaining_ > 0) {
1386 --fast_scans_remaining_;
1387 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001388 }
1389 StartScanTimer();
1390}
1391
Paul Stewart2b05e622012-07-13 20:38:44 -07001392void WiFi::StartPendingTimer() {
1393 pending_timeout_callback_.Reset(
1394 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1395 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1396 kPendingTimeoutSeconds * 1000);
1397}
1398
1399void WiFi::StopPendingTimer() {
1400 pending_timeout_callback_.Cancel();
1401}
1402
1403void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001404 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
1405 << (service ? service->friendly_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001406 if (service) {
1407 service->SetState(Service::kStateAssociating);
1408 StartPendingTimer();
1409 } else if (pending_service_) {
1410 StopPendingTimer();
1411 }
1412 pending_service_ = service;
1413}
1414
1415void WiFi::PendingTimeoutHandler() {
1416 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1417 CHECK(pending_service_);
Paul Stewartff96a842012-08-13 15:59:10 -07001418 // Take a reference since pending_service_ will reset below.
1419 ServiceRefPtr service = pending_service_;
Paul Stewart2b05e622012-07-13 20:38:44 -07001420 DisconnectFrom(pending_service_);
Paul Stewartff96a842012-08-13 15:59:10 -07001421 service->SetFailure(Service::kFailureOutOfRange);
Paul Stewart2b05e622012-07-13 20:38:44 -07001422}
1423
Paul Stewart44663922012-07-30 11:03:03 -07001424void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001425 if (!reconnect_timeout_callback_.IsCancelled()) {
1426 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1427 << ": reconnect timer already running.";
1428 return;
1429 }
Paul Stewart44663922012-07-30 11:03:03 -07001430 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1431 reconnect_timeout_callback_.Reset(
1432 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1433 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1434 kReconnectTimeoutSeconds * 1000);
1435}
1436
1437void WiFi::StopReconnectTimer() {
1438 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1439 reconnect_timeout_callback_.Cancel();
1440}
1441
1442void WiFi::ReconnectTimeoutHandler() {
1443 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001444 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001445 CHECK(current_service_);
1446 current_service_->SetFailureSilent(Service::kFailureConnect);
1447 DisconnectFrom(current_service_);
1448}
1449
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001450void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1451 LOG(INFO) << "WPA supplicant appeared.";
1452 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001453 // Restart the WiFi device if it's started already. This will reset the
1454 // state and connect the device to the new WPA supplicant instance.
1455 if (enabled()) {
1456 Restart();
1457 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001458 return;
1459 }
1460 supplicant_present_ = true;
1461 ConnectToSupplicant();
1462}
1463
1464void WiFi::OnSupplicantVanish() {
1465 LOG(INFO) << "WPA supplicant vanished.";
1466 if (!supplicant_present_) {
1467 return;
1468 }
1469 supplicant_present_ = false;
1470 // Restart the WiFi device if it's started already. This will effectively
1471 // suspend the device until the WPA supplicant reappears.
1472 if (enabled()) {
1473 Restart();
1474 }
1475}
1476
1477void WiFi::ConnectToSupplicant() {
1478 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1479 << " supplicant: "
1480 << (supplicant_present_ ? "present" : "absent")
1481 << " proxy: "
1482 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1483 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1484 return;
1485 }
1486 supplicant_process_proxy_.reset(
1487 proxy_factory_->CreateSupplicantProcessProxy(
1488 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
1489 ::DBus::Path interface_path;
1490 try {
1491 map<string, DBus::Variant> create_interface_args;
1492 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
1493 append_string(link_name().c_str());
1494 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
1495 append_string(wpa_supplicant::kDriverNL80211);
1496 create_interface_args[
1497 wpa_supplicant::kInterfacePropertyConfigFile].writer().
1498 append_string(SCRIPTDIR "/wpa_supplicant.conf");
1499 interface_path =
1500 supplicant_process_proxy_->CreateInterface(create_interface_args);
1501 } catch (const DBus::Error &e) { // NOLINT
1502 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
1503 interface_path =
1504 supplicant_process_proxy_->GetInterface(link_name());
1505 // TODO(quiche): Is it okay to crash here, if device is missing?
1506 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001507 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1508 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001509 }
1510 }
1511
1512 supplicant_interface_proxy_.reset(
1513 proxy_factory_->CreateSupplicantInterfaceProxy(
1514 this, interface_path, wpa_supplicant::kDBusAddr));
1515
1516 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1517 IFF_UP);
1518 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1519
1520 // Clear out any networks that might previously have been configured
1521 // for this interface.
1522 supplicant_interface_proxy_->RemoveAllNetworks();
1523
1524 // Flush interface's BSS cache, so that we get BSSAdded signals for
1525 // all BSSes (not just new ones since the last scan).
1526 supplicant_interface_proxy_->FlushBSS(0);
1527
1528 try {
1529 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1530 // with RADIUS servers that respond strangely to such requests.
1531 // crosbug.com/25630
1532 supplicant_interface_proxy_->SetFastReauth(false);
1533 } catch (const DBus::Error &e) { // NOLINT
1534 LOG(INFO) << "Failed to disable fast_reauth."
1535 << "May be running an older version of wpa_supplicant.";
1536 }
1537
1538 try {
1539 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1540 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1541 } catch (const DBus::Error &e) { // NOLINT
1542 LOG(INFO) << "Failed to set scan_interval. "
1543 << "May be running an older version of wpa_supplicant.";
1544 }
1545
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001546 Scan(NULL);
1547 StartScanTimer();
1548}
1549
1550void WiFi::Restart() {
1551 LOG(INFO) << link_name() << " restarting.";
1552 WiFiRefPtr me = this; // Make sure we don't get destructed.
1553 // Go through the manager rather than starting and stopping the device
1554 // directly so that the device can be configured with the profile.
1555 manager()->DeregisterDevice(me);
1556 manager()->RegisterDevice(me);
1557}
1558
Paul Stewartb50f0b92011-05-16 16:31:42 -07001559} // namespace shill