blob: bccd2c4503ce9aafafcfc4bec6bcfafe94a494d5 [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"
Gaurav Shah6d2c72d2012-10-16 16:30:44 -070031#include "shill/geolocation_info.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070032#include "shill/key_value_store.h"
33#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070034#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070035#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070036#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000037#include "shill/metrics.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070038#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080039#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070040#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050041#include "shill/rtnl_handler.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080042#include "shill/shill_time.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080043#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070044#include "shill/supplicant_interface_proxy_interface.h"
45#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080046#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070047#include "shill/wifi_endpoint.h"
48#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070049#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070050
Eric Shienbrood3e20a232012-02-16 11:35:56 -050051using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000052using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070053using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080054using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070055using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070056using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070057
Paul Stewartb50f0b92011-05-16 16:31:42 -070058namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070059
60// statics
Darin Petkov60ceaf32012-10-18 10:36:01 +020061const char WiFi::kSupplicantConfPath[] = SHIMDIR "/wpa_supplicant.conf";
mukesh agrawal4d0401c2012-01-06 16:05:31 -080062const char *WiFi::kDefaultBgscanMethod =
63 wpa_supplicant::kNetworkBgscanMethodSimple;
64const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
65const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
66const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020067// Scan interval while connected.
68const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070069// Note that WiFi generates some manager-level errors, because it implements
70// the Manager.GetWiFiService flimflam API. The API is implemented here,
71// rather than in manager, to keep WiFi-specific logic in the right place.
mukesh agrawal7a4e4002011-09-06 11:26:05 -070072const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
73const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
74const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070075const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
76 "security mode is unsupported";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070077const char WiFi::kManagerErrorUnsupportedServiceMode[] =
78 "service mode is unsupported";
mukesh agrawal5c05b292012-03-07 10:12:52 -080079// Age (in seconds) beyond which a BSS cache entry will not be preserved,
80// across a suspend/resume.
81const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000082const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070083const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070084const int WiFi::kNumFastScanAttempts = 3;
85const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070086const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070087const int WiFi::kReconnectTimeoutSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070088
Paul Stewartb50f0b92011-05-16 16:31:42 -070089WiFi::WiFi(ControlInterface *control_interface,
90 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080091 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070092 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070093 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080094 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070095 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070096 : Device(control_interface,
97 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080098 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070099 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700100 link,
Chris Masone626719f2011-08-18 16:58:48 -0700101 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800102 interface_index,
103 Technology::kWifi),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500104 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700105 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800106 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200107 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000108 supplicant_state_(kInterfaceStateUnknown),
109 supplicant_bss_("(unknown)"),
Paul Stewart66c86002012-01-30 18:00:52 -0800110 clear_cached_credentials_pending_(false),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800111 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +0200112 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700113 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700114 has_already_completed_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800115 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
116 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700117 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800118 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700119 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200120 store->RegisterDerivedString(
121 flimflam::kBgscanMethodProperty,
122 StringAccessor(
123 // TODO(petkov): CustomMappedAccessor is used for convenience because
124 // it provides a way to define a custom clearer (unlike
125 // CustomAccessor). We need to implement a fully custom accessor with
126 // no extra argument.
127 new CustomMappedAccessor<WiFi, string, int>(this,
128 &WiFi::ClearBgscanMethod,
129 &WiFi::GetBgscanMethod,
130 &WiFi::SetBgscanMethod,
131 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800132 HelpRegisterDerivedUint16(store,
133 flimflam::kBgscanShortIntervalProperty,
134 &WiFi::GetBgscanShortInterval,
135 &WiFi::SetBgscanShortInterval);
136 HelpRegisterDerivedInt32(store,
137 flimflam::kBgscanSignalThresholdProperty,
138 &WiFi::GetBgscanSignalThreshold,
139 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700140
Chris Masoneb925cc82011-06-22 15:39:57 -0700141 // TODO(quiche): Decide if scan_pending_ is close enough to
142 // "currently scanning" that we don't care, or if we want to track
143 // scan pending/currently scanning/no scan scheduled as a tri-state
144 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700145 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800146 HelpRegisterDerivedUint16(store,
147 flimflam::kScanIntervalProperty,
148 &WiFi::GetScanInterval,
149 &WiFi::SetScanInterval);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700150 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700151}
152
mukesh agrawalaf571952011-07-14 14:31:12 -0700153WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700154
Eric Shienbrood9a245532012-03-07 14:20:39 -0500155void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200156 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
157 if (enabled()) {
158 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700159 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500160 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200161 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500162 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200163 }
164 if (on_supplicant_appear_.IsCancelled()) {
165 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
166 // device instance.
167 on_supplicant_appear_.Reset(
168 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
169 on_supplicant_vanish_.Reset(
170 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
171 manager()->dbus_manager()->WatchName(wpa_supplicant::kDBusAddr,
172 on_supplicant_appear_.callback(),
173 on_supplicant_vanish_.callback());
174 }
175 // Connect to WPA supplicant if it's already present. If not, we'll connect to
176 // it when it appears.
177 ConnectToSupplicant();
Wade Guthried6153612012-08-23 11:36:14 -0700178 Config80211 *config80211 = Config80211::GetInstance();
179 if (config80211) {
180 config80211->SetWifiState(Config80211::kWifiUp);
181 }
Paul Stewart87a4ae82012-10-26 15:49:32 -0700182
183 // Ensure that hidden services are loaded from profiles. The may have been
184 // removed with a previous call to Stop().
185 manager()->LoadDeviceFromProfiles(this);
mukesh agrawalab87ea42011-05-18 11:44:49 -0700186}
187
Eric Shienbrood9a245532012-03-07 14:20:39 -0500188void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700189 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200190 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700191 StopScanTimer();
mukesh agrawal15908392011-11-16 18:29:25 +0000192 endpoint_by_rpcid_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700193
Paul Stewartced6a0b2011-11-08 15:32:04 -0800194 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
195 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700196 ++it) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700197 SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
198 << (*it)->friendly_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700199 manager()->DeregisterService(*it);
200 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700201 rpcid_by_service_.clear();
Paul Stewartced6a0b2011-11-08 15:32:04 -0800202 services_.clear(); // breaks reference cycles
Paul Stewart549d44c2012-07-03 12:40:25 -0700203 supplicant_interface_proxy_.reset(); // breaks a reference cycle
204 // TODO(quiche): Remove interface from supplicant.
205 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800206 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000207 pending_service_ = NULL; // breaks a reference cycle
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200208 scan_pending_ = false;
Paul Stewart2b05e622012-07-13 20:38:44 -0700209 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700210 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700211
Eric Shienbrood9a245532012-03-07 14:20:39 -0500212 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
213 if (error)
214 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700215 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700216
Ben Chanfad4a0b2012-04-18 15:49:59 -0700217 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
218 << (supplicant_process_proxy_.get() ?
219 "is set." : "is not set.");
220 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
221 << (supplicant_interface_proxy_.get() ?
222 "is set." : "is not set.");
223 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
224 << (pending_service_.get() ? "is set." : "is not set.");
225 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
226 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
227 SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
228 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700229}
230
Paul Stewarta41e38d2011-11-11 07:47:29 -0800231bool WiFi::Load(StoreInterface *storage) {
232 LoadHiddenServices(storage);
233 return Device::Load(storage);
234}
235
mukesh agrawal1830fa12011-09-26 14:31:40 -0700236void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700237 LOG(INFO) << __func__;
238
mukesh agrawal7ec71312011-11-10 02:08:26 +0000239 // Needs to send a D-Bus message, but may be called from D-Bus
240 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700241 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500242 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700243}
244
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000245void WiFi::BSSAdded(const ::DBus::Path &path,
246 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500247 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000248 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500249 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
250 weak_ptr_factory_.GetWeakPtr(),
251 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000252}
253
254void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500255 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000256 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500257 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
258 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700259}
260
Paul Stewartbc6e7392012-05-24 07:07:48 -0700261void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
262 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
263 weak_ptr_factory_.GetWeakPtr(), properties));
264}
265
mukesh agrawal7ec71312011-11-10 02:08:26 +0000266void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200267 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000268 // Called from D-Bus signal handler, but may need to send a D-Bus
269 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500270 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
271 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000272}
273
mukesh agrawalb54601c2011-06-07 17:39:22 -0700274void WiFi::ScanDone() {
275 LOG(INFO) << __func__;
276
mukesh agrawal7ec71312011-11-10 02:08:26 +0000277 // Defer handling of scan result processing, because that processing
278 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700279 // registration can't be done in the context of a D-Bus signal
280 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500281 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
282 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700283}
284
mukesh agrawal6e277772011-09-29 15:04:23 -0700285void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000286 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800287 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700288 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700289
mukesh agrawal7ec71312011-11-10 02:08:26 +0000290 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000291 if (pending_service_ && pending_service_ == service) {
292 // TODO(quiche): Return an error to the caller. crosbug.com/23832
293 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
294 << service->friendly_name()
295 << ", which is already pending.";
296 return;
297 }
298
299 if (pending_service_ && pending_service_ != service) {
300 DisconnectFrom(pending_service_);
301 }
mukesh agrawal32399322011-09-01 10:53:43 -0700302
mukesh agrawal6e277772011-09-29 15:04:23 -0700303 try {
mukesh agrawal64896322011-12-01 01:13:10 +0000304 const uint32_t scan_ssid = 1; // "True": Use directed probe.
305 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
306 append_uint32(scan_ssid);
Darin Petkov4a66cc52012-06-15 10:08:29 +0200307 AppendBgscan(service, &service_params);
308 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000309 rpcid_by_service_[service] = network_path;
Ben Chan80326f32012-05-04 17:51:32 -0700310 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700311 LOG(ERROR) << "exception while adding network: " << e.what();
312 return;
313 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700314
mukesh agrawal445e72c2011-06-22 11:13:50 -0700315 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700316 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000317 CHECK(current_service_.get() != pending_service_.get());
318
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700319 // SelectService here (instead of in LinkEvent, like Ethernet), so
320 // that, if we fail to bring up L2, we can attribute failure correctly.
321 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000322 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700323 // reconsider if this is the right place to change the selected service.
324 // see discussion in crosbug.com/20191.
325 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000326}
327
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000328void WiFi::DisconnectFrom(WiFiService *service) {
329 if (service != current_service_ && service != pending_service_) {
330 // TODO(quiche): Once we have asynchronous reply support, we should
331 // generate a D-Bus error here. (crosbug.com/23832)
332 LOG(WARNING) << "In " << __func__ << "(): "
333 << " ignoring request to disconnect from service "
334 << service->friendly_name()
335 << " which is neither current nor pending";
336 return;
337 }
338
339 if (pending_service_ && service != pending_service_) {
340 // TODO(quiche): Once we have asynchronous reply support, we should
341 // generate a D-Bus error here. (crosbug.com/23832)
342 LOG(WARNING) << "In " << __func__ << "(): "
343 << " ignoring request to disconnect from service "
344 << service->friendly_name()
345 << " which is not the pending service.";
346 return;
347 }
348
349 if (!pending_service_ && service != current_service_) {
350 // TODO(quiche): Once we have asynchronous reply support, we should
351 // generate a D-Bus error here. (crosbug.com/23832)
352 LOG(WARNING) << "In " << __func__ << "(): "
353 << " ignoring request to disconnect from service "
354 << service->friendly_name()
355 << " which is not the current service.";
356 return;
357 }
358
Paul Stewartff96a842012-08-13 15:59:10 -0700359 if (pending_service_) {
360 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
361 // on this to drive the service state back to idle. Do that here.
362 pending_service_->SetState(Service::kStateIdle);
363 }
364
Paul Stewart2b05e622012-07-13 20:38:44 -0700365 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700366 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700367
368 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700369 LOG(ERROR) << "In " << __func__ << "(): "
370 << "wpa_supplicant is not present; silently resetting "
371 << "current_service_.";
372 if (current_service_ == selected_service()) {
373 DropConnection();
374 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700375 current_service_ = NULL;
376 return;
377 }
378
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000379 try {
380 supplicant_interface_proxy_->Disconnect();
381 // We'll call RemoveNetwork and reset |current_service_| after
382 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700383 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000384 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700385 // So effect changes immediately. For instance, this can happen when
386 // a disconnect is triggered by a BSS going away.
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000387 ReverseServiceMap::const_iterator rpcid_it =
388 rpcid_by_service_.find(service);
389 DCHECK(rpcid_it != rpcid_by_service_.end());
390 if (rpcid_it == rpcid_by_service_.end()) {
391 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
392 << service->friendly_name() << ": "
393 << "could not find supplicant network to disable.";
394 } else {
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700395 RemoveNetwork(rpcid_it->second);
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000396 }
Christopher Wileyc6184482012-10-24 15:31:56 -0700397 if (service == selected_service()) {
398 DropConnection();
399 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000400 current_service_ = NULL;
401 }
402
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800403 CHECK(current_service_ == NULL ||
404 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000405}
406
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700407bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
408 try {
409 supplicant_interface_proxy_->RemoveNetwork(network);
410 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700411 // RemoveNetwork can fail with three different errors.
412 //
413 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
414 // already removed the network object, so return true as if
415 // RemoveNetwork removes the network object successfully.
416 //
417 // As shill always passes a valid network object path, RemoveNetwork
418 // should not fail with an InvalidArgs error. Return false in such case
419 // as something weird may have happened. Similarly, return false in case
420 // of an UnknownError.
421 if (strcmp(e.name(), wpa_supplicant::kErrorNetworkUnknown) != 0) {
422 return false;
423 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700424 }
425 return true;
426}
427
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000428bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800429 return !current_service_ && !pending_service_;
430}
431
Paul Stewart66c86002012-01-30 18:00:52 -0800432void WiFi::ClearCachedCredentials() {
433 LOG(INFO) << __func__;
434
435 // Needs to send a D-Bus message, but may be called from D-Bus
436 // caller context (via Manager::PopProfile). So defer work
437 // to event loop.
438 if (!clear_cached_credentials_pending_) {
439 clear_cached_credentials_pending_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500440 dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask,
441 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart66c86002012-01-30 18:00:52 -0800442 }
443}
444
mukesh agrawalb20776f2012-02-10 16:00:36 -0800445void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
446 WiFiService *service = FindServiceForEndpoint(endpoint);
447 DCHECK(service);
448 if (service) {
449 service->NotifyEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800450 }
451}
452
Darin Petkov4a66cc52012-06-15 10:08:29 +0200453void WiFi::AppendBgscan(WiFiService *service,
454 map<string, DBus::Variant> *service_params) const {
455 int scan_interval = kBackgroundScanIntervalSeconds;
456 string method = bgscan_method_;
457 if (method.empty()) {
458 // If multiple APs are detected for this SSID, configure the default method.
459 // Otherwise, disable background scanning completely.
460 if (service->GetEndpointCount() > 1) {
461 method = kDefaultBgscanMethod;
462 } else {
463 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
464 return;
465 }
Christopher Wileya998df22012-07-11 15:14:55 -0700466 } else if (method.compare(wpa_supplicant::kNetworkBgscanMethodNone) == 0) {
467 LOG(INFO) << "Background scan disabled -- chose None method.";
468 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200469 } else {
470 // If the background scan method was explicitly specified, honor the
471 // configured background scan interval.
472 scan_interval = scan_interval_seconds_;
473 }
474 DCHECK(!method.empty());
475 string config_string = StringPrintf("%s:%d:%d:%d",
476 method.c_str(),
477 bgscan_short_interval_seconds_,
478 bgscan_signal_threshold_dbm_,
479 scan_interval);
480 LOG(INFO) << "Background scan: " << config_string;
481 (*service_params)[wpa_supplicant::kNetworkPropertyBgscan].writer()
482 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800483}
484
Darin Petkov4a66cc52012-06-15 10:08:29 +0200485string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
486 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
487}
488
489void WiFi::SetBgscanMethod(
490 const int &/*argument*/, const string &method, Error *error) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800491 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
Christopher Wileya998df22012-07-11 15:14:55 -0700492 method != wpa_supplicant::kNetworkBgscanMethodLearn &&
493 method != wpa_supplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800494 const string error_message =
495 StringPrintf("Unrecognized bgscan method %s", method.c_str());
496 LOG(WARNING) << error_message;
497 error->Populate(Error::kInvalidArguments, error_message);
498 return;
499 }
500
501 bgscan_method_ = method;
502 // We do not update kNetworkPropertyBgscan for |pending_service_| or
503 // |current_service_|, because supplicant does not allow for
504 // reconfiguration without disconnect and reconnect.
505}
506
507void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
508 bgscan_short_interval_seconds_ = seconds;
509 // We do not update kNetworkPropertyBgscan for |pending_service_| or
510 // |current_service_|, because supplicant does not allow for
511 // reconfiguration without disconnect and reconnect.
512}
513
514void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
515 bgscan_signal_threshold_dbm_ = dbm;
516 // We do not update kNetworkPropertyBgscan for |pending_service_| or
517 // |current_service_|, because supplicant does not allow for
518 // reconfiguration without disconnect and reconnect.
519}
520
521void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
522 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700523 if (running()) {
524 StartScanTimer();
525 }
526 // The scan interval affects both foreground scans (handled by
527 // |scan_timer_callback_|), and background scans (handled by
528 // supplicant). However, we do not update |pending_service_| or
529 // |current_service_|, because supplicant does not allow for
530 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800531}
532
Darin Petkov4a66cc52012-06-15 10:08:29 +0200533void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
534 bgscan_method_.clear();
535}
536
mukesh agrawal165e6142011-11-22 02:22:56 +0000537// To avoid creating duplicate services, call FindServiceForEndpoint
538// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000539WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
540 bool hidden_ssid) {
541 WiFiServiceRefPtr service =
542 new WiFiService(control_interface(),
543 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800544 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000545 manager(),
546 this,
547 endpoint.ssid(),
548 endpoint.network_mode(),
549 endpoint.security_mode(),
550 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000551 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000552 return service;
553}
554
555void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700556 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
557 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000558 supplicant_bss_ = new_bss;
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700559 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700560
561 // Any change in CurrentBSS means supplicant is actively changing our
562 // connectivity. We no longer need to track any previously pending
563 // reconnect.
564 StopReconnectTimer();
565
mukesh agrawal15908392011-11-16 18:29:25 +0000566 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
567 HandleDisconnect();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700568 if (!GetHiddenSSIDList().empty()) {
569 // Before disconnecting, wpa_supplicant probably scanned for
570 // APs. So, in the normal case, we defer to the timer for the next scan.
571 //
572 // However, in the case of hidden SSIDs, supplicant knows about
573 // at most one of them. (That would be the hidden SSID we were
574 // connected to, if applicable.)
575 //
576 // So, in this case, we initiate an immediate scan. This scan
577 // will include the hidden SSIDs we know about (up to the limit of
578 // kScanMAxSSIDsPerScan).
579 //
580 // We may want to reconsider this immediate scan, if/when shill
581 // takes greater responsibility for scanning (vs. letting
582 // supplicant handle most of it).
583 Scan(NULL);
584 }
mukesh agrawal15908392011-11-16 18:29:25 +0000585 } else {
586 HandleRoam(new_bss);
587 }
588
Paul Stewart2b05e622012-07-13 20:38:44 -0700589 // If we are selecting a new service, or if we're clearing selection
590 // of a something other than the pending service, call SelectService.
591 // Otherwise skip SelectService, since this will cause the pending
592 // service to be marked as Idle.
593 if (current_service_ || selected_service() != pending_service_) {
594 SelectService(current_service_);
595 }
596
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000597 // Invariant check: a Service can either be current, or pending, but
598 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000599 CHECK(current_service_.get() != pending_service_.get() ||
600 current_service_.get() == NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000601}
602
603void WiFi::HandleDisconnect() {
604 // Identify the affected service. We expect to get a disconnect
605 // event when we fall off a Service that we were connected
606 // to. However, we also allow for the case where we get a disconnect
607 // event while attempting to connect from a disconnected state.
608 WiFiService *affected_service =
609 current_service_.get() ? current_service_.get() : pending_service_.get();
610
611 current_service_ = NULL;
612 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700613 SLOG(WiFi, 2) << "WiFi " << link_name()
614 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000615 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700616 }
617 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700618 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700619 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000620 }
621
622 ReverseServiceMap::const_iterator rpcid_it =
623 rpcid_by_service_.find(affected_service);
624 if (rpcid_it == rpcid_by_service_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700625 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
626 << " (or failed to connect to) "
627 << affected_service->friendly_name() << ", "
628 << "but could not find supplicant network to disable.";
mukesh agrawalcf24a242012-05-21 16:46:11 -0700629 } else {
630 // TODO(quiche): Reconsider giving up immediately. Maybe give
631 // wpa_supplicant some time to retry, first.
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700632 if (!RemoveNetwork(rpcid_it->second)) {
633 LOG(FATAL) << "RemoveNetwork for " << rpcid_it->second << " failed.";
634 }
mukesh agrawal15908392011-11-16 18:29:25 +0000635 }
636
Ben Chanfad4a0b2012-04-18 15:49:59 -0700637 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
638 << " (or failed to connect to) "
639 << affected_service->friendly_name();
mukesh agrawalcf24a242012-05-21 16:46:11 -0700640 if (SuspectCredentials(*affected_service)) {
641 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700642 // mole in Chrome.
Paul Stewartf2d60912012-07-15 08:37:30 -0700643 affected_service->SetFailure(Service::kFailureBadPassphrase);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700644 LOG(ERROR) << "Connection failure during 4-Way Handshake. Bad passphrase?";
Paul Stewartf2d60912012-07-15 08:37:30 -0700645 } else {
646 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700647 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800648 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000649 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000650
651 if (affected_service == pending_service_.get()) {
652 // The attempt to connect to |pending_service_| failed. Clear
653 // |pending_service_|, to indicate we're no longer in the middle
654 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700655 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000656 } else if (pending_service_.get()) {
657 // We've attributed the disconnection to what was the
658 // |current_service_|, rather than the |pending_service_|.
659 //
660 // If we're wrong about that (i.e. supplicant reported this
661 // CurrentBSS change after attempting to connect to
662 // |pending_service_|), we're depending on supplicant to retry
663 // connecting to |pending_service_|, and delivering another
664 // CurrentBSS change signal in the future.
665 //
666 // Log this fact, to help us debug (in case our assumptions are
667 // wrong).
Ben Chanfad4a0b2012-04-18 15:49:59 -0700668 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to "
669 << pending_service_->friendly_name()
670 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000671 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700672
673 // If we disconnect, initially scan at a faster frequency, to make sure
674 // we've found all available APs.
675 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000676}
677
678// We use the term "Roam" loosely. In particular, we include the case
679// where we "Roam" to a BSS from the disconnected state.
680void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
681 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
682 if (endpoint_it == endpoint_by_rpcid_.end()) {
683 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
684 << new_bss;
685 return;
686 }
687
688 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000689 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000690 if (!service.get()) {
691 LOG(WARNING) << "WiFi " << link_name()
692 << " could not find Service for Endpoint "
693 << endpoint.bssid_string()
694 << " (service will be unchanged)";
695 return;
696 }
697
Ben Chanfad4a0b2012-04-18 15:49:59 -0700698 SLOG(WiFi, 2) << "WiFi " << link_name()
699 << " roamed to Endpoint " << endpoint.bssid_string()
700 << " (SSID " << endpoint.ssid_string() << ")";
mukesh agrawal15908392011-11-16 18:29:25 +0000701
mukesh agrawale1d90e92012-02-15 17:36:08 -0800702 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000703
mukesh agrawal15908392011-11-16 18:29:25 +0000704 if (pending_service_.get() &&
705 service.get() != pending_service_.get()) {
706 // The Service we've roamed on to is not the one we asked for.
707 // We assume that this is transient, and that wpa_supplicant
708 // is trying / will try to connect to |pending_service_|.
709 //
710 // If it succeeds, we'll end up back here, but with |service|
711 // pointing at the same service as |pending_service_|.
712 //
713 // If it fails, we'll process things in HandleDisconnect.
714 //
715 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700716 SLOG(WiFi, 2) << "WiFi " << link_name()
717 << " new current Endpoint "
718 << endpoint.bssid_string()
719 << " is not part of pending service "
720 << pending_service_->friendly_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000721
722 // Sanity check: if we didn't roam onto |pending_service_|, we
723 // should still be on |current_service_|.
724 if (service.get() != current_service_.get()) {
725 LOG(WARNING) << "WiFi " << link_name()
726 << " new current Endpoint "
727 << endpoint.bssid_string()
728 << " is neither part of pending service "
729 << pending_service_->friendly_name()
730 << " nor part of current service "
731 << (current_service_.get() ?
732 current_service_->friendly_name() :
733 "(NULL)");
734 // Although we didn't expect to get here, we should keep
735 // |current_service_| in sync with what supplicant has done.
736 current_service_ = service;
737 }
738 return;
739 }
740
741 if (pending_service_.get()) {
742 // We assume service.get() == pending_service_.get() here, because
743 // of the return in the previous if clause.
744 //
745 // Boring case: we've connected to the service we asked
746 // for. Simply update |current_service_| and |pending_service_|.
747 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700748 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000749 return;
750 }
751
752 // |pending_service_| was NULL, so we weren't attempting to connect
753 // to a new Service. Sanity check that we're still on
754 // |current_service_|.
755 if (service.get() != current_service_.get()) {
756 LOG(WARNING)
757 << "WiFi " << link_name()
758 << " new current Endpoint "
759 << endpoint.bssid_string()
760 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000761 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000762 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000763 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000764 // We didn't expect to be here, but let's cope as well as we
765 // can. Update |current_service_| to keep it in sync with
766 // supplicant.
767 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700768
769 // If this service isn't already marked as actively connecting (likely,
770 // since this service is a bit of a surprise) set the service as
771 // associating.
772 if (!current_service_->IsConnecting()) {
773 current_service_->SetState(Service::kStateAssociating);
774 }
775
mukesh agrawal15908392011-11-16 18:29:25 +0000776 return;
777 }
778
779 // At this point, we know that |pending_service_| was NULL, and that
780 // we're still on |current_service_|. This is the most boring case
781 // of all, because there's no state to update here.
782 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700783}
784
Paul Stewarta41e38d2011-11-11 07:47:29 -0800785WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
786 const string &mode,
787 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800788 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
789 it != services_.end();
790 ++it) {
791 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
792 (*it)->IsSecurityMatch(security)) {
793 return *it;
794 }
795 }
796 return NULL;
797}
798
mukesh agrawal165e6142011-11-22 02:22:56 +0000799WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
800 return FindService(endpoint.ssid(),
801 endpoint.network_mode(),
802 endpoint.security_mode());
803}
804
Paul Stewartced6a0b2011-11-08 15:32:04 -0800805ByteArrays WiFi::GetHiddenSSIDList() {
806 // Create a unique set of hidden SSIDs.
807 set<ByteArray> hidden_ssids_set;
808 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
809 it != services_.end();
810 ++it) {
Paul Stewart10ccbb32012-04-26 15:59:30 -0700811 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800812 hidden_ssids_set.insert((*it)->ssid());
813 }
814 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700815 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800816 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
817 if (!hidden_ssids.empty()) {
818 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
819 // not fit in.
820 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
821 hidden_ssids.erase(
822 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
823 hidden_ssids.end());
824 }
825 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
826 // SSIDs to wpa_supplicant, we need to explicitly specify the default
827 // behavior of doing a broadcast probe.
828 hidden_ssids.push_back(ByteArray());
829 }
830 return hidden_ssids;
831}
832
Paul Stewarta41e38d2011-11-11 07:47:29 -0800833bool WiFi::LoadHiddenServices(StoreInterface *storage) {
834 bool created_hidden_service = false;
835 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
836 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
837 bool is_hidden = false;
838 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700839 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
840 << "GetGroupsWithKey failed for GetBool("
841 << flimflam::kWifiHiddenSsid
842 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800843 continue;
844 }
845 if (!is_hidden) {
846 continue;
847 }
848 string ssid_hex;
849 vector<uint8_t> ssid_bytes;
850 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
851 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700852 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
853 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800854 continue;
855 }
856 string device_address;
857 string network_mode;
858 string security;
859 // It is gross that we have to do this, but the only place we can
860 // get information about the service is from its storage name.
861 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
862 &network_mode, &security) ||
863 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700864 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
865 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800866 continue;
867 }
868 if (FindService(ssid_bytes, network_mode, security).get()) {
869 // If service already exists, we have nothing to do, since the
870 // service has already loaded its configuration from storage.
871 // This is guaranteed to happen in both cases where Load() is
872 // called on a device (via a ConfigureDevice() call on the
873 // profile):
874 // - In RegisterDevice() the Device hasn't been started yet,
875 // so it has no services registered, except for those
876 // created by previous iterations of LoadHiddenService().
877 // The latter can happen if another profile in the Manager's
878 // stack defines the same ssid/mode/security. Even this
879 // case is okay, since even if the profiles differ
880 // materially on configuration and credentials, the "right"
881 // one will be configured in the course of the
882 // RegisterService() call below.
883 // - In PushProfile(), all registered services have been
884 // introduced to the profile via ConfigureService() prior
885 // to calling ConfigureDevice() on the profile.
886 continue;
887 }
888 WiFiServiceRefPtr service(new WiFiService(control_interface(),
889 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800890 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800891 manager(),
892 this,
893 ssid_bytes,
894 network_mode,
895 security,
896 true));
897 services_.push_back(service);
898
899 // By registering the service, the rest of the configuration
900 // will be loaded from the profile into the service via ConfigureService().
901 manager()->RegisterService(service);
902
903 created_hidden_service = true;
904 }
905
906 // If we are idle and we created a service as a result of opening the
907 // profile, we should initiate a scan for our new hidden SSID.
908 if (running() && created_hidden_service &&
909 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
910 Scan(NULL);
911 }
912
913 return created_hidden_service;
914}
915
Paul Stewart66c86002012-01-30 18:00:52 -0800916void WiFi::ClearCachedCredentialsTask() {
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700917 try {
Paul Stewartf48eca42012-10-11 12:10:02 -0700918 // Supplicant may have disappeared by the time this task gets to run.
919 if (supplicant_interface_proxy_.get()) {
920 supplicant_interface_proxy_->ClearCachedCredentials();
921 } else {
922 SLOG(WiFi, 1) << "In " << __func__ << ": supplicant proxy is NULL.";
923 }
Ben Chan80326f32012-05-04 17:51:32 -0700924 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700925 LOG(WARNING) << "Clear of cached credentials failed.";
926 }
Paul Stewart66c86002012-01-30 18:00:52 -0800927 clear_cached_credentials_pending_ = false;
928}
929
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000930void WiFi::BSSAddedTask(
931 const ::DBus::Path &path,
932 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000933 // Note: we assume that BSSIDs are unique across endpoints. This
934 // means that if an AP reuses the same BSSID for multiple SSIDs, we
935 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800936 WiFiEndpointRefPtr endpoint(
937 new WiFiEndpoint(proxy_factory_, this, path, properties));
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000938 LOG(INFO) << "Found endpoint. "
mukesh agrawale9adda12012-02-09 18:33:48 -0800939 << "RPC path: " << path << ", "
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000940 << "ssid: " << endpoint->ssid_string() << ", "
941 << "bssid: " << endpoint->bssid_string() << ", "
942 << "signal: " << endpoint->signal_strength() << ", "
Thieu Lee41a72d2012-02-06 20:46:51 +0000943 << "security: " << endpoint->security_mode() << ", "
944 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000945
946 if (endpoint->ssid_string().empty()) {
947 // Don't bother trying to find or create a Service for an Endpoint
948 // without an SSID. We wouldn't be able to connect to it anyway.
949 return;
950 }
951
mukesh agrawalb3857612012-01-18 16:23:29 -0800952 if (endpoint->ssid()[0] == 0) {
953 // Assume that an SSID starting with NULL is bogus/misconfigured,
954 // and filter it out.
955 return;
956 }
957
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000958 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
959 if (service) {
960 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
961 << " to service " << service->friendly_name() << ".";
962 service->AddEndpoint(endpoint);
963
964 if (manager()->HasService(service)) {
965 manager()->UpdateService(service);
966 } else {
Darin Petkov4a66cc52012-06-15 10:08:29 +0200967 // Expect registered by now if >1.
968 DCHECK_EQ(1, service->GetEndpointCount());
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000969 manager()->RegisterService(service);
970 }
mukesh agrawale9adda12012-02-09 18:33:48 -0800971 } else {
972 const bool hidden_ssid = false;
973 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
974 LOG(INFO) << "New service " << service->GetRpcIdentifier()
975 << " (" << service->friendly_name() << ")";
976 service->AddEndpoint(endpoint);
977 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000978 }
979
mukesh agrawale9adda12012-02-09 18:33:48 -0800980 // Do this last, to maintain the invariant that any Endpoint we
981 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800982 //
983 // TODO(quiche): Write test to verify correct behavior in the case
984 // where we get multiple BSSAdded events for a single endpoint.
985 // (Old Endpoint's refcount should fall to zero, and old Endpoint
986 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800987 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800988 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000989}
990
991void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
992 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
993 if (i == endpoint_by_rpcid_.end()) {
994 LOG(WARNING) << "WiFi " << link_name()
995 << " could not find BSS " << path
996 << " to remove.";
997 return;
998 }
999
1000 WiFiEndpointRefPtr endpoint = i->second;
1001 CHECK(endpoint);
1002 endpoint_by_rpcid_.erase(i);
1003
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001004 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -08001005 CHECK(service) << "Can't find Service for Endpoint "
1006 << path << " "
1007 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -07001008 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
1009 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001010 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001011
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001012 bool disconnect_service = !service->HasEndpoints() &&
1013 (service->IsConnecting() || service->IsConnected());
1014 bool forget_service =
1015 // Forget Services without Endpoints, except that we always keep
1016 // hidden services around. (We need them around to populate the
1017 // hidden SSIDs list.)
1018 !service->HasEndpoints() && !service->hidden_ssid();
1019 bool deregister_service =
1020 // Only deregister a Service if we're forgetting it. Otherwise,
1021 // Manager can't keep our configuration up-to-date (as Profiles
1022 // change).
1023 forget_service;
1024
1025 if (disconnect_service) {
1026 DisconnectFrom(service);
1027 }
1028
1029 if (deregister_service) {
1030 manager()->DeregisterService(service);
1031 } else {
1032 manager()->UpdateService(service);
1033 }
1034
1035 if (forget_service) {
1036 vector<WiFiServiceRefPtr>::iterator it;
1037 it = std::find(services_.begin(), services_.end(), service);
1038 if (it != services_.end()) {
1039 services_.erase(it);
1040 }
1041 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001042}
1043
Paul Stewartbc6e7392012-05-24 07:07:48 -07001044void WiFi::CertificationTask(
1045 const map<string, ::DBus::Variant> &properties) {
1046 if (!current_service_) {
1047 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1048 << " with no current service.";
1049 return;
1050 }
1051
1052 map<string, ::DBus::Variant>::const_iterator properties_it =
1053 properties.find(wpa_supplicant::kInterfacePropertyDepth);
1054 if (properties_it == properties.end()) {
1055 LOG(ERROR) << __func__ << " no depth parameter.";
1056 return;
1057 }
1058 uint32 depth = properties_it->second.reader().get_uint32();
1059 properties_it = properties.find(wpa_supplicant::kInterfacePropertySubject);
1060 if (properties_it == properties.end()) {
1061 LOG(ERROR) << __func__ << " no subject parameter.";
1062 return;
1063 }
1064 string subject(properties_it->second.reader().get_string());
1065 current_service_->AddEAPCertification(subject, depth);
1066}
1067
mukesh agrawal15908392011-11-16 18:29:25 +00001068void WiFi::PropertiesChangedTask(
1069 const map<string, ::DBus::Variant> &properties) {
1070 // TODO(quiche): Handle changes in other properties (e.g. signal
1071 // strength).
1072
1073 // Note that order matters here. In particular, we want to process
1074 // changes in the current BSS before changes in state. This is so
1075 // that we update the state of the correct Endpoint/Service.
1076
1077 map<string, ::DBus::Variant>::const_iterator properties_it =
1078 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
1079 if (properties_it != properties.end()) {
1080 CurrentBSSChanged(properties_it->second.reader().get_path());
1081 }
1082
1083 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
1084 if (properties_it != properties.end()) {
1085 StateChanged(properties_it->second.reader().get_string());
1086 }
1087}
1088
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001089void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001090 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001091 if (need_bss_flush_) {
1092 CHECK(supplicant_interface_proxy_ != NULL);
1093 // Compute |max_age| relative to |resumed_at_|, to account for the
1094 // time taken to scan.
1095 struct timeval now;
1096 uint32_t max_age;
1097 time_->GetTimeMonotonic(&now);
1098 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1099 supplicant_interface_proxy_->FlushBSS(max_age);
1100 need_bss_flush_ = false;
1101 }
mukesh agrawalb54601c2011-06-07 17:39:22 -07001102 scan_pending_ = false;
mukesh agrawalb66c6462012-05-07 11:45:25 -07001103 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001104}
1105
mukesh agrawal32399322011-09-01 10:53:43 -07001106void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001107 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001108 if (!enabled()) {
1109 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
1110 return;
1111 }
1112 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1113 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
1114 return;
1115 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001116 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -07001117 scan_args[wpa_supplicant::kPropertyScanType].writer().
1118 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001119
1120 ByteArrays hidden_ssids = GetHiddenSSIDList();
1121 if (!hidden_ssids.empty()) {
1122 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
1123 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1124 }
1125
Gaurav Shahf8721ee2011-11-07 09:12:46 -08001126 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -08001127 try {
1128 supplicant_interface_proxy_->Scan(scan_args);
1129 scan_pending_ = true;
Ben Chan80326f32012-05-04 17:51:32 -07001130 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001131 // A scan may fail if, for example, the wpa_supplicant vanishing
1132 // notification is posted after this task has already started running.
1133 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001134 }
mukesh agrawal32399322011-09-01 10:53:43 -07001135}
1136
mukesh agrawal15908392011-11-16 18:29:25 +00001137void WiFi::StateChanged(const string &new_state) {
1138 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001139 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001140 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1141 << old_state << " -> " << new_state;
1142
1143 WiFiService *affected_service;
1144 // Identify the service to which the state change applies. If
1145 // |pending_service_| is non-NULL, then the state change applies to
1146 // |pending_service_|. Otherwise, it applies to |current_service_|.
1147 //
1148 // This policy is driven by the fact that the |pending_service_|
1149 // doesn't become the |current_service_| until wpa_supplicant
1150 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001151 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001152 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1153 affected_service =
1154 pending_service_.get() ? pending_service_.get() : current_service_.get();
1155 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001156 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1157 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001158 return;
1159 }
1160
Paul Stewart44663922012-07-30 11:03:03 -07001161 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
1162 if (affected_service->IsConnected()) {
1163 StopReconnectTimer();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001164 } else if (has_already_completed_) {
1165 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001166 } else if (AcquireIPConfigWithLeaseName(
1167 affected_service->GetStorageIdentifier())) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001168 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001169 affected_service->SetState(Service::kStateConfiguring);
1170 } else {
1171 LOG(ERROR) << "Unable to acquire DHCP config.";
1172 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001173 has_already_completed_ = true;
mukesh agrawal15908392011-11-16 18:29:25 +00001174 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1175 affected_service->SetState(Service::kStateAssociating);
1176 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1177 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1178 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1179 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1180 // Ignore transitions into these states from Completed, to avoid
1181 // bothering the user when roaming, or re-keying.
1182 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1183 affected_service->SetState(Service::kStateAssociating);
1184 // TOOD(quiche): On backwards transitions, we should probably set
1185 // a timeout for getting back into the completed state. At present,
1186 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001187 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001188 // (crosbug.com/23207)
Paul Stewart44663922012-07-30 11:03:03 -07001189 } else if (new_state == wpa_supplicant::kInterfaceStateDisconnected &&
1190 affected_service == current_service_ &&
1191 affected_service->IsConnected()) {
1192 // This means that wpa_supplicant failed in a re-connect attempt, but
1193 // may still be reconnecting. Give wpa_supplicant a limited amount of
1194 // time to transition out this condition by either connecting or changing
1195 // CurrentBSS.
1196 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001197 } else {
1198 // Other transitions do not affect Service state.
1199 //
1200 // Note in particular that we ignore a State change into
1201 // kInterfaceStateDisconnected, in favor of observing the corresponding
1202 // change in CurrentBSS.
1203 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001204}
1205
mukesh agrawalcf24a242012-05-21 16:46:11 -07001206bool WiFi::SuspectCredentials(const WiFiService &service) const {
1207 if (!service.IsSecurityMatch(flimflam::kSecurityPsk)) {
1208 // We can only diagnose credentials for WPA/RSN networks. For
1209 // others, assume the failure was not credential related.
1210 return false;
1211 }
1212
1213 return supplicant_state_ == wpa_supplicant::kInterfaceState4WayHandshake &&
1214 !service.has_ever_connected();
1215}
1216
mukesh agrawal7ec71312011-11-10 02:08:26 +00001217// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001218WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001219 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001220
1221 if (args.ContainsString(flimflam::kModeProperty) &&
1222 args.GetString(flimflam::kModeProperty) !=
1223 flimflam::kModeManaged) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001224 Error::PopulateAndLog(error, Error::kNotSupported,
1225 kManagerErrorUnsupportedServiceMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001226 return NULL;
1227 }
1228
1229 if (!args.ContainsString(flimflam::kSSIDProperty)) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001230 Error::PopulateAndLog(error, Error::kInvalidArguments,
1231 kManagerErrorSSIDRequired);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001232 return NULL;
1233 }
1234
1235 string ssid = args.GetString(flimflam::kSSIDProperty);
1236 if (ssid.length() < 1) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001237 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1238 kManagerErrorSSIDTooShort);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001239 return NULL;
1240 }
1241
1242 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001243 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1244 kManagerErrorSSIDTooLong);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001245 return NULL;
1246 }
1247
1248 string security_method;
1249 if (args.ContainsString(flimflam::kSecurityProperty)) {
1250 security_method = args.GetString(flimflam::kSecurityProperty);
1251 } else {
1252 security_method = flimflam::kSecurityNone;
1253 }
1254
1255 if (security_method != flimflam::kSecurityNone &&
1256 security_method != flimflam::kSecurityWep &&
1257 security_method != flimflam::kSecurityPsk &&
1258 security_method != flimflam::kSecurityWpa &&
1259 security_method != flimflam::kSecurityRsn &&
1260 security_method != flimflam::kSecurity8021x) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001261 Error::PopulateAndLog(error, Error::kNotSupported,
1262 kManagerErrorUnsupportedSecurityMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001263 return NULL;
1264 }
1265
Paul Stewart6ab23a92011-11-09 17:17:47 -08001266 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001267 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1268 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001269 } else {
1270 // If the service is not found, and the caller hasn't specified otherwise,
1271 // we assume this is is a hidden network.
1272 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001273 }
1274
Paul Stewart6ab23a92011-11-09 17:17:47 -08001275 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1276 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1277 security_method));
1278 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001279 service = new WiFiService(control_interface(),
1280 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001281 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001282 manager(),
1283 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001284 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001285 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001286 security_method,
1287 hidden_ssid);
1288 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001289 // NB: We do not register the newly created Service with the Manager.
1290 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001291 }
1292
Paul Stewartcb59fed2012-03-21 21:14:46 -07001293 service->Configure(args, error);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001294
Paul Stewart7f61e522012-03-22 11:13:45 -07001295 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1296 // have no endpoints like the one we may have just created. crosbug.com/28224
1297
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001298 return service;
1299}
1300
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001301// static
1302bool WiFi::SanitizeSSID(string *ssid) {
1303 CHECK(ssid);
1304
1305 size_t ssid_len = ssid->length();
1306 size_t i;
1307 bool changed = false;
1308
Gary Morainac1bdb42012-02-16 17:42:29 -08001309 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001310 if (!g_ascii_isprint((*ssid)[i])) {
1311 (*ssid)[i] = '?';
1312 changed = true;
1313 }
1314 }
1315
1316 return changed;
1317}
1318
Paul Stewart3c508e12012-08-09 11:40:06 -07001319void WiFi::OnLinkMonitorFailure() {
1320 // If we have never found the gateway, let's be conservative and not
1321 // do anything, in case this network topology does not have a gateway.
1322 if (!link_monitor()->IsGatewayFound()) {
1323 LOG(INFO) << "In " << __func__ << "(): "
1324 << "Skipping reassociate since gateway was never found.";
1325 return;
1326 }
1327
1328 if (!supplicant_present_) {
1329 LOG(ERROR) << "In " << __func__ << "(): "
1330 << "wpa_supplicant is not present. Cannot reassociate.";
1331 return;
1332 }
1333
1334 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001335 // This will force a transition out of connected, if we are actually
1336 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001337 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001338 // If we don't eventually get a transition back into a connected state,
1339 // there is something wrong.
1340 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001341 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1342 } catch (const DBus::Error &e) { // NOLINT
1343 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1344 return;
1345 }
1346}
1347
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001348vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1349 vector<GeolocationInfo> objects;
1350 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1351 it != endpoint_by_rpcid_.end();
1352 ++it) {
1353 GeolocationInfo geoinfo;
1354 WiFiEndpointRefPtr endpoint = it->second;
1355 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1356 geoinfo.AddField(kGeoSignalStrengthProperty,
1357 StringPrintf("%d", endpoint->signal_strength()));
1358 geoinfo.AddField(
1359 kGeoChannelProperty,
1360 StringPrintf("%d",
1361 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1362 // TODO(gauravsh): Include age field. crosbug.com/35445
1363 objects.push_back(geoinfo);
1364 }
1365 return objects;
1366}
1367
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001368void WiFi::HelpRegisterDerivedInt32(
1369 PropertyStore *store,
1370 const string &name,
1371 int32(WiFi::*get)(Error *error),
1372 void(WiFi::*set)(const int32 &value, Error *error)) {
1373 store->RegisterDerivedInt32(
1374 name,
1375 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1376}
1377
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001378void WiFi::HelpRegisterDerivedUint16(
1379 PropertyStore *store,
1380 const string &name,
1381 uint16(WiFi::*get)(Error *error),
1382 void(WiFi::*set)(const uint16 &value, Error *error)) {
1383 store->RegisterDerivedUint16(
1384 name,
1385 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1386}
1387
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001388void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001389 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001390 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001391
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001392 // We want to flush the BSS cache, but we don't want to conflict
1393 // with a running scan or an active connection attempt. So record
1394 // the need to flush, and take care of flushing when the next scan
1395 // completes.
1396 //
1397 // Note that supplicant will automatically expire old cache
1398 // entries (after, e.g., a BSS is not found in two consecutive
1399 // scans). However, our explicit flush accelerates re-association
1400 // in cases where a BSS disappeared while we were asleep. (See,
1401 // e.g. WiFiRoaming.005SuspendRoam.)
1402 time_->GetTimeMonotonic(&resumed_at_);
1403 need_bss_flush_ = true;
1404
1405 if (!scan_pending_ && IsIdle()) {
1406 // Not scanning/connecting/connected, so let's get things rolling.
1407 Scan(NULL);
1408 } else {
1409 SLOG(WiFi, 1) << __func__
1410 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001411 }
1412}
1413
Paul Stewarte369ece2012-05-22 09:11:03 -07001414void WiFi::RestartFastScanAttempts() {
1415 fast_scans_remaining_ = kNumFastScanAttempts;
1416 StartScanTimer();
1417}
1418
mukesh agrawalb66c6462012-05-07 11:45:25 -07001419void WiFi::StartScanTimer() {
1420 if (scan_interval_seconds_ == 0) {
1421 StopScanTimer();
1422 return;
1423 }
1424 scan_timer_callback_.Reset(
1425 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001426 // Repeat the first few scans after disconnect relatively quickly so we
1427 // have reasonable trust that no APs we are looking for are present.
1428 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1429 fast_scans_remaining_ > 0 ?
1430 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001431}
1432
1433void WiFi::StopScanTimer() {
1434 scan_timer_callback_.Cancel();
1435}
1436
1437void WiFi::ScanTimerHandler() {
1438 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1439 if (IsIdle() && !scan_pending_) {
1440 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001441 if (fast_scans_remaining_ > 0) {
1442 --fast_scans_remaining_;
1443 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001444 }
1445 StartScanTimer();
1446}
1447
Paul Stewart2b05e622012-07-13 20:38:44 -07001448void WiFi::StartPendingTimer() {
1449 pending_timeout_callback_.Reset(
1450 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1451 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1452 kPendingTimeoutSeconds * 1000);
1453}
1454
1455void WiFi::StopPendingTimer() {
1456 pending_timeout_callback_.Cancel();
1457}
1458
1459void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001460 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
1461 << (service ? service->friendly_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001462 if (service) {
1463 service->SetState(Service::kStateAssociating);
1464 StartPendingTimer();
1465 } else if (pending_service_) {
1466 StopPendingTimer();
1467 }
1468 pending_service_ = service;
1469}
1470
1471void WiFi::PendingTimeoutHandler() {
1472 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1473 CHECK(pending_service_);
Paul Stewartff96a842012-08-13 15:59:10 -07001474 // Take a reference since pending_service_ will reset below.
1475 ServiceRefPtr service = pending_service_;
Paul Stewart2b05e622012-07-13 20:38:44 -07001476 DisconnectFrom(pending_service_);
Paul Stewartff96a842012-08-13 15:59:10 -07001477 service->SetFailure(Service::kFailureOutOfRange);
Paul Stewart2b05e622012-07-13 20:38:44 -07001478}
1479
Paul Stewart44663922012-07-30 11:03:03 -07001480void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001481 if (!reconnect_timeout_callback_.IsCancelled()) {
1482 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1483 << ": reconnect timer already running.";
1484 return;
1485 }
Paul Stewart44663922012-07-30 11:03:03 -07001486 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1487 reconnect_timeout_callback_.Reset(
1488 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1489 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1490 kReconnectTimeoutSeconds * 1000);
1491}
1492
1493void WiFi::StopReconnectTimer() {
1494 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1495 reconnect_timeout_callback_.Cancel();
1496}
1497
1498void WiFi::ReconnectTimeoutHandler() {
1499 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001500 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001501 CHECK(current_service_);
1502 current_service_->SetFailureSilent(Service::kFailureConnect);
1503 DisconnectFrom(current_service_);
1504}
1505
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001506void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1507 LOG(INFO) << "WPA supplicant appeared.";
1508 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001509 // Restart the WiFi device if it's started already. This will reset the
1510 // state and connect the device to the new WPA supplicant instance.
1511 if (enabled()) {
1512 Restart();
1513 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001514 return;
1515 }
1516 supplicant_present_ = true;
1517 ConnectToSupplicant();
1518}
1519
1520void WiFi::OnSupplicantVanish() {
1521 LOG(INFO) << "WPA supplicant vanished.";
1522 if (!supplicant_present_) {
1523 return;
1524 }
1525 supplicant_present_ = false;
1526 // Restart the WiFi device if it's started already. This will effectively
1527 // suspend the device until the WPA supplicant reappears.
1528 if (enabled()) {
1529 Restart();
1530 }
1531}
1532
1533void WiFi::ConnectToSupplicant() {
1534 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1535 << " supplicant: "
1536 << (supplicant_present_ ? "present" : "absent")
1537 << " proxy: "
1538 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1539 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1540 return;
1541 }
1542 supplicant_process_proxy_.reset(
1543 proxy_factory_->CreateSupplicantProcessProxy(
1544 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
1545 ::DBus::Path interface_path;
1546 try {
1547 map<string, DBus::Variant> create_interface_args;
1548 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
1549 append_string(link_name().c_str());
1550 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
1551 append_string(wpa_supplicant::kDriverNL80211);
1552 create_interface_args[
1553 wpa_supplicant::kInterfacePropertyConfigFile].writer().
Darin Petkov60ceaf32012-10-18 10:36:01 +02001554 append_string(kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001555 interface_path =
1556 supplicant_process_proxy_->CreateInterface(create_interface_args);
1557 } catch (const DBus::Error &e) { // NOLINT
1558 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
1559 interface_path =
1560 supplicant_process_proxy_->GetInterface(link_name());
1561 // TODO(quiche): Is it okay to crash here, if device is missing?
1562 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001563 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1564 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001565 }
1566 }
1567
1568 supplicant_interface_proxy_.reset(
1569 proxy_factory_->CreateSupplicantInterfaceProxy(
1570 this, interface_path, wpa_supplicant::kDBusAddr));
1571
1572 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1573 IFF_UP);
1574 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1575
1576 // Clear out any networks that might previously have been configured
1577 // for this interface.
1578 supplicant_interface_proxy_->RemoveAllNetworks();
1579
1580 // Flush interface's BSS cache, so that we get BSSAdded signals for
1581 // all BSSes (not just new ones since the last scan).
1582 supplicant_interface_proxy_->FlushBSS(0);
1583
1584 try {
1585 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1586 // with RADIUS servers that respond strangely to such requests.
1587 // crosbug.com/25630
1588 supplicant_interface_proxy_->SetFastReauth(false);
1589 } catch (const DBus::Error &e) { // NOLINT
1590 LOG(INFO) << "Failed to disable fast_reauth."
1591 << "May be running an older version of wpa_supplicant.";
1592 }
1593
1594 try {
1595 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1596 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1597 } catch (const DBus::Error &e) { // NOLINT
1598 LOG(INFO) << "Failed to set scan_interval. "
1599 << "May be running an older version of wpa_supplicant.";
1600 }
1601
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001602 Scan(NULL);
1603 StartScanTimer();
1604}
1605
1606void WiFi::Restart() {
1607 LOG(INFO) << link_name() << " restarting.";
1608 WiFiRefPtr me = this; // Make sure we don't get destructed.
1609 // Go through the manager rather than starting and stopping the device
1610 // directly so that the device can be configured with the profile.
1611 manager()->DeregisterDevice(me);
1612 manager()->RegisterDevice(me);
1613}
1614
Paul Stewartb50f0b92011-05-16 16:31:42 -07001615} // namespace shill