blob: a102617bed0b9063b70216a03f52d0a2836de4ad [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"
Paul Stewart5581d072012-12-17 17:30:20 -080042#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080043#include "shill/shill_time.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080044#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070045#include "shill/supplicant_interface_proxy_interface.h"
Paul Stewart835934a2012-12-06 19:27:09 -080046#include "shill/supplicant_network_proxy_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070047#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080048#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070049#include "shill/wifi_endpoint.h"
50#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070051#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070052
Eric Shienbrood3e20a232012-02-16 11:35:56 -050053using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000054using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070055using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080056using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070057using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070058using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070059
Paul Stewartb50f0b92011-05-16 16:31:42 -070060namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070061
62// statics
Darin Petkov60ceaf32012-10-18 10:36:01 +020063const char WiFi::kSupplicantConfPath[] = SHIMDIR "/wpa_supplicant.conf";
mukesh agrawal4d0401c2012-01-06 16:05:31 -080064const char *WiFi::kDefaultBgscanMethod =
65 wpa_supplicant::kNetworkBgscanMethodSimple;
66const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
67const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
68const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020069// Scan interval while connected.
70const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070071// Note that WiFi generates some manager-level errors, because it implements
72// the Manager.GetWiFiService flimflam API. The API is implemented here,
73// rather than in manager, to keep WiFi-specific logic in the right place.
mukesh agrawal7a4e4002011-09-06 11:26:05 -070074const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
75const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
76const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070077const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
78 "security mode is unsupported";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070079const char WiFi::kManagerErrorUnsupportedServiceMode[] =
80 "service mode is unsupported";
mukesh agrawal5c05b292012-03-07 10:12:52 -080081// Age (in seconds) beyond which a BSS cache entry will not be preserved,
82// across a suspend/resume.
83const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000084const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070085const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070086const int WiFi::kNumFastScanAttempts = 3;
87const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070088const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070089const int WiFi::kReconnectTimeoutSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070090
Paul Stewartb50f0b92011-05-16 16:31:42 -070091WiFi::WiFi(ControlInterface *control_interface,
92 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080093 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070094 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070095 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080096 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070097 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070098 : Device(control_interface,
99 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -0800100 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -0700101 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700102 link,
Chris Masone626719f2011-08-18 16:58:48 -0700103 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800104 interface_index,
105 Technology::kWifi),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500106 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700107 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800108 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200109 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000110 supplicant_state_(kInterfaceStateUnknown),
111 supplicant_bss_("(unknown)"),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800112 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +0200113 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700114 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700115 has_already_completed_(false),
Paul Stewarta47c3c62012-12-18 12:14:29 -0800116 is_debugging_connection_(false),
Paul Stewart1369c2b2013-01-11 05:41:26 -0800117 is_eap_in_progress_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800118 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
119 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700120 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800121 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700122 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200123 store->RegisterDerivedString(
124 flimflam::kBgscanMethodProperty,
125 StringAccessor(
126 // TODO(petkov): CustomMappedAccessor is used for convenience because
127 // it provides a way to define a custom clearer (unlike
128 // CustomAccessor). We need to implement a fully custom accessor with
129 // no extra argument.
130 new CustomMappedAccessor<WiFi, string, int>(this,
131 &WiFi::ClearBgscanMethod,
132 &WiFi::GetBgscanMethod,
133 &WiFi::SetBgscanMethod,
134 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800135 HelpRegisterDerivedUint16(store,
136 flimflam::kBgscanShortIntervalProperty,
137 &WiFi::GetBgscanShortInterval,
138 &WiFi::SetBgscanShortInterval);
139 HelpRegisterDerivedInt32(store,
140 flimflam::kBgscanSignalThresholdProperty,
141 &WiFi::GetBgscanSignalThreshold,
142 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700143
Chris Masoneb925cc82011-06-22 15:39:57 -0700144 // TODO(quiche): Decide if scan_pending_ is close enough to
145 // "currently scanning" that we don't care, or if we want to track
146 // scan pending/currently scanning/no scan scheduled as a tri-state
147 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700148 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800149 HelpRegisterDerivedUint16(store,
150 flimflam::kScanIntervalProperty,
151 &WiFi::GetScanInterval,
152 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800153 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
154 ScopeLogger::kWiFi,
155 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -0700156 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700157}
158
mukesh agrawalaf571952011-07-14 14:31:12 -0700159WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700160
Eric Shienbrood9a245532012-03-07 14:20:39 -0500161void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200162 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
163 if (enabled()) {
164 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700165 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500166 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200167 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500168 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200169 }
170 if (on_supplicant_appear_.IsCancelled()) {
171 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
172 // device instance.
173 on_supplicant_appear_.Reset(
174 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
175 on_supplicant_vanish_.Reset(
176 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
177 manager()->dbus_manager()->WatchName(wpa_supplicant::kDBusAddr,
178 on_supplicant_appear_.callback(),
179 on_supplicant_vanish_.callback());
180 }
181 // Connect to WPA supplicant if it's already present. If not, we'll connect to
182 // it when it appears.
183 ConnectToSupplicant();
Wade Guthried6153612012-08-23 11:36:14 -0700184 Config80211 *config80211 = Config80211::GetInstance();
185 if (config80211) {
186 config80211->SetWifiState(Config80211::kWifiUp);
187 }
Paul Stewart87a4ae82012-10-26 15:49:32 -0700188
189 // Ensure that hidden services are loaded from profiles. The may have been
190 // removed with a previous call to Stop().
191 manager()->LoadDeviceFromProfiles(this);
mukesh agrawalab87ea42011-05-18 11:44:49 -0700192}
193
Eric Shienbrood9a245532012-03-07 14:20:39 -0500194void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700195 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200196 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700197 StopScanTimer();
mukesh agrawal15908392011-11-16 18:29:25 +0000198 endpoint_by_rpcid_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700199
Paul Stewartced6a0b2011-11-08 15:32:04 -0800200 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
201 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700202 ++it) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700203 SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
Darin Petkov457728b2013-01-09 09:49:08 +0100204 << (*it)->unique_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700205 manager()->DeregisterService(*it);
206 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700207 rpcid_by_service_.clear();
Paul Stewartced6a0b2011-11-08 15:32:04 -0800208 services_.clear(); // breaks reference cycles
Paul Stewart549d44c2012-07-03 12:40:25 -0700209 supplicant_interface_proxy_.reset(); // breaks a reference cycle
210 // TODO(quiche): Remove interface from supplicant.
211 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800212 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000213 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800214 is_debugging_connection_ = false;
Paul Stewartd2db2b12013-01-17 13:11:07 -0800215 SetScanPending(false);
Paul Stewart2b05e622012-07-13 20:38:44 -0700216 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700217 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700218
Eric Shienbrood9a245532012-03-07 14:20:39 -0500219 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
220 if (error)
221 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700222 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700223
Ben Chanfad4a0b2012-04-18 15:49:59 -0700224 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
225 << (supplicant_process_proxy_.get() ?
226 "is set." : "is not set.");
227 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
228 << (supplicant_interface_proxy_.get() ?
229 "is set." : "is not set.");
230 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
231 << (pending_service_.get() ? "is set." : "is not set.");
232 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
233 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
234 SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
235 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700236}
237
Paul Stewarta41e38d2011-11-11 07:47:29 -0800238bool WiFi::Load(StoreInterface *storage) {
239 LoadHiddenServices(storage);
240 return Device::Load(storage);
241}
242
mukesh agrawal1830fa12011-09-26 14:31:40 -0700243void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700244 LOG(INFO) << __func__;
245
mukesh agrawal7ec71312011-11-10 02:08:26 +0000246 // Needs to send a D-Bus message, but may be called from D-Bus
247 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700248 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500249 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700250}
251
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000252void WiFi::BSSAdded(const ::DBus::Path &path,
253 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500254 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000255 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500256 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
257 weak_ptr_factory_.GetWeakPtr(),
258 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000259}
260
261void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500262 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000263 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500264 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
265 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700266}
267
Paul Stewartbc6e7392012-05-24 07:07:48 -0700268void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
269 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
270 weak_ptr_factory_.GetWeakPtr(), properties));
271}
272
Paul Stewartdb0f9172012-11-30 16:48:09 -0800273void WiFi::EAPEvent(const string &status, const string &parameter) {
274 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
275 weak_ptr_factory_.GetWeakPtr(),
276 status,
277 parameter));
278}
279
mukesh agrawal7ec71312011-11-10 02:08:26 +0000280void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200281 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000282 // Called from D-Bus signal handler, but may need to send a D-Bus
283 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500284 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
285 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000286}
287
mukesh agrawalb54601c2011-06-07 17:39:22 -0700288void WiFi::ScanDone() {
289 LOG(INFO) << __func__;
290
mukesh agrawal7ec71312011-11-10 02:08:26 +0000291 // Defer handling of scan result processing, because that processing
292 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700293 // registration can't be done in the context of a D-Bus signal
294 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500295 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
296 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700297}
298
mukesh agrawal6e277772011-09-29 15:04:23 -0700299void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000300 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800301 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700302 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700303
mukesh agrawal7ec71312011-11-10 02:08:26 +0000304 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000305 if (pending_service_ && pending_service_ == service) {
306 // TODO(quiche): Return an error to the caller. crosbug.com/23832
Darin Petkov457728b2013-01-09 09:49:08 +0100307 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
308 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000309 << ", which is already pending.";
310 return;
311 }
312
313 if (pending_service_ && pending_service_ != service) {
314 DisconnectFrom(pending_service_);
315 }
mukesh agrawal32399322011-09-01 10:53:43 -0700316
Paul Stewart835934a2012-12-06 19:27:09 -0800317 Error unused_error;
318 network_path = FindNetworkRpcidForService(service, &unused_error);
319 if (network_path.empty()) {
320 try {
321 const uint32_t scan_ssid = 1; // "True": Use directed probe.
322 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
323 append_uint32(scan_ssid);
324 AppendBgscan(service, &service_params);
325 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
326 CHECK(!network_path.empty()); // No DBus path should be empty.
327 rpcid_by_service_[service] = network_path;
328 } catch (const DBus::Error &e) { // NOLINT
329 LOG(ERROR) << "exception while adding network: " << e.what();
330 return;
331 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700332 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700333
Paul Stewarta47c3c62012-12-18 12:14:29 -0800334 if (service->HasRecentConnectionIssues()) {
335 SetConnectionDebugging(true);
336 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700337 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700338 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000339 CHECK(current_service_.get() != pending_service_.get());
340
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700341 // SelectService here (instead of in LinkEvent, like Ethernet), so
342 // that, if we fail to bring up L2, we can attribute failure correctly.
343 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000344 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700345 // reconsider if this is the right place to change the selected service.
346 // see discussion in crosbug.com/20191.
347 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000348}
349
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000350void WiFi::DisconnectFrom(WiFiService *service) {
351 if (service != current_service_ && service != pending_service_) {
352 // TODO(quiche): Once we have asynchronous reply support, we should
353 // generate a D-Bus error here. (crosbug.com/23832)
354 LOG(WARNING) << "In " << __func__ << "(): "
355 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100356 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000357 << " which is neither current nor pending";
358 return;
359 }
360
361 if (pending_service_ && service != pending_service_) {
362 // TODO(quiche): Once we have asynchronous reply support, we should
363 // generate a D-Bus error here. (crosbug.com/23832)
364 LOG(WARNING) << "In " << __func__ << "(): "
365 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100366 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000367 << " which is not the pending service.";
368 return;
369 }
370
371 if (!pending_service_ && service != current_service_) {
372 // TODO(quiche): Once we have asynchronous reply support, we should
373 // generate a D-Bus error here. (crosbug.com/23832)
374 LOG(WARNING) << "In " << __func__ << "(): "
375 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100376 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000377 << " which is not the current service.";
378 return;
379 }
380
Paul Stewartff96a842012-08-13 15:59:10 -0700381 if (pending_service_) {
382 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
383 // on this to drive the service state back to idle. Do that here.
384 pending_service_->SetState(Service::kStateIdle);
385 }
386
Paul Stewart2b05e622012-07-13 20:38:44 -0700387 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700388 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700389
390 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700391 LOG(ERROR) << "In " << __func__ << "(): "
392 << "wpa_supplicant is not present; silently resetting "
393 << "current_service_.";
394 if (current_service_ == selected_service()) {
395 DropConnection();
396 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700397 current_service_ = NULL;
398 return;
399 }
400
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000401 try {
402 supplicant_interface_proxy_->Disconnect();
403 // We'll call RemoveNetwork and reset |current_service_| after
404 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700405 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000406 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700407 // So effect changes immediately. For instance, this can happen when
408 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800409 Error unused_error;
410 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700411 if (service == selected_service()) {
412 DropConnection();
413 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000414 current_service_ = NULL;
415 }
416
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800417 CHECK(current_service_ == NULL ||
418 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000419}
420
Paul Stewart835934a2012-12-06 19:27:09 -0800421bool WiFi::DisableNetwork(const ::DBus::Path &network) {
422 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
423 proxy_factory_->CreateSupplicantNetworkProxy(
424 network, wpa_supplicant::kDBusAddr));
425 try {
426 supplicant_network_proxy->SetEnabled(false);
427 } catch (const DBus::Error &e) { // NOLINT
428 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
429 return false;
430 }
431 return true;
432}
433
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700434bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
435 try {
436 supplicant_interface_proxy_->RemoveNetwork(network);
437 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700438 // RemoveNetwork can fail with three different errors.
439 //
440 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
441 // already removed the network object, so return true as if
442 // RemoveNetwork removes the network object successfully.
443 //
444 // As shill always passes a valid network object path, RemoveNetwork
445 // should not fail with an InvalidArgs error. Return false in such case
446 // as something weird may have happened. Similarly, return false in case
447 // of an UnknownError.
448 if (strcmp(e.name(), wpa_supplicant::kErrorNetworkUnknown) != 0) {
449 return false;
450 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700451 }
452 return true;
453}
454
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000455bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800456 return !current_service_ && !pending_service_;
457}
458
Paul Stewart835934a2012-12-06 19:27:09 -0800459void WiFi::ClearCachedCredentials(const WiFiService *service) {
460 Error unused_error;
461 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800462}
463
mukesh agrawalb20776f2012-02-10 16:00:36 -0800464void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
465 WiFiService *service = FindServiceForEndpoint(endpoint);
466 DCHECK(service);
467 if (service) {
468 service->NotifyEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800469 }
470}
471
Darin Petkov4a66cc52012-06-15 10:08:29 +0200472void WiFi::AppendBgscan(WiFiService *service,
473 map<string, DBus::Variant> *service_params) const {
474 int scan_interval = kBackgroundScanIntervalSeconds;
475 string method = bgscan_method_;
476 if (method.empty()) {
477 // If multiple APs are detected for this SSID, configure the default method.
478 // Otherwise, disable background scanning completely.
479 if (service->GetEndpointCount() > 1) {
480 method = kDefaultBgscanMethod;
481 } else {
482 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
483 return;
484 }
Christopher Wileya998df22012-07-11 15:14:55 -0700485 } else if (method.compare(wpa_supplicant::kNetworkBgscanMethodNone) == 0) {
486 LOG(INFO) << "Background scan disabled -- chose None method.";
487 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200488 } else {
489 // If the background scan method was explicitly specified, honor the
490 // configured background scan interval.
491 scan_interval = scan_interval_seconds_;
492 }
493 DCHECK(!method.empty());
494 string config_string = StringPrintf("%s:%d:%d:%d",
495 method.c_str(),
496 bgscan_short_interval_seconds_,
497 bgscan_signal_threshold_dbm_,
498 scan_interval);
499 LOG(INFO) << "Background scan: " << config_string;
500 (*service_params)[wpa_supplicant::kNetworkPropertyBgscan].writer()
501 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800502}
503
Darin Petkov4a66cc52012-06-15 10:08:29 +0200504string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
505 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
506}
507
508void WiFi::SetBgscanMethod(
509 const int &/*argument*/, const string &method, Error *error) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800510 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
Christopher Wileya998df22012-07-11 15:14:55 -0700511 method != wpa_supplicant::kNetworkBgscanMethodLearn &&
512 method != wpa_supplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800513 const string error_message =
514 StringPrintf("Unrecognized bgscan method %s", method.c_str());
515 LOG(WARNING) << error_message;
516 error->Populate(Error::kInvalidArguments, error_message);
517 return;
518 }
519
520 bgscan_method_ = method;
521 // We do not update kNetworkPropertyBgscan for |pending_service_| or
522 // |current_service_|, because supplicant does not allow for
523 // reconfiguration without disconnect and reconnect.
524}
525
526void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
527 bgscan_short_interval_seconds_ = seconds;
528 // We do not update kNetworkPropertyBgscan for |pending_service_| or
529 // |current_service_|, because supplicant does not allow for
530 // reconfiguration without disconnect and reconnect.
531}
532
533void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
534 bgscan_signal_threshold_dbm_ = dbm;
535 // We do not update kNetworkPropertyBgscan for |pending_service_| or
536 // |current_service_|, because supplicant does not allow for
537 // reconfiguration without disconnect and reconnect.
538}
539
540void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
541 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700542 if (running()) {
543 StartScanTimer();
544 }
545 // The scan interval affects both foreground scans (handled by
546 // |scan_timer_callback_|), and background scans (handled by
547 // supplicant). However, we do not update |pending_service_| or
548 // |current_service_|, because supplicant does not allow for
549 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800550}
551
Darin Petkov4a66cc52012-06-15 10:08:29 +0200552void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
553 bgscan_method_.clear();
554}
555
mukesh agrawal165e6142011-11-22 02:22:56 +0000556// To avoid creating duplicate services, call FindServiceForEndpoint
557// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000558WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
559 bool hidden_ssid) {
560 WiFiServiceRefPtr service =
561 new WiFiService(control_interface(),
562 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800563 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000564 manager(),
565 this,
566 endpoint.ssid(),
567 endpoint.network_mode(),
568 endpoint.security_mode(),
569 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000570 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000571 return service;
572}
573
574void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700575 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
576 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000577 supplicant_bss_ = new_bss;
Paul Stewartdb0f9172012-11-30 16:48:09 -0800578 supplicant_tls_error_ = "";
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700579 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700580
581 // Any change in CurrentBSS means supplicant is actively changing our
582 // connectivity. We no longer need to track any previously pending
583 // reconnect.
584 StopReconnectTimer();
585
mukesh agrawal15908392011-11-16 18:29:25 +0000586 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
587 HandleDisconnect();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700588 if (!GetHiddenSSIDList().empty()) {
589 // Before disconnecting, wpa_supplicant probably scanned for
590 // APs. So, in the normal case, we defer to the timer for the next scan.
591 //
592 // However, in the case of hidden SSIDs, supplicant knows about
593 // at most one of them. (That would be the hidden SSID we were
594 // connected to, if applicable.)
595 //
596 // So, in this case, we initiate an immediate scan. This scan
597 // will include the hidden SSIDs we know about (up to the limit of
598 // kScanMAxSSIDsPerScan).
599 //
600 // We may want to reconsider this immediate scan, if/when shill
601 // takes greater responsibility for scanning (vs. letting
602 // supplicant handle most of it).
603 Scan(NULL);
604 }
mukesh agrawal15908392011-11-16 18:29:25 +0000605 } else {
606 HandleRoam(new_bss);
607 }
608
Paul Stewart1369c2b2013-01-11 05:41:26 -0800609 // Reset eap_in_progress_ after calling HandleDisconnect() so it can
610 // be used to detect disconnects during EAP authentication.
611 is_eap_in_progress_ = false;
612
Paul Stewart2b05e622012-07-13 20:38:44 -0700613 // If we are selecting a new service, or if we're clearing selection
614 // of a something other than the pending service, call SelectService.
615 // Otherwise skip SelectService, since this will cause the pending
616 // service to be marked as Idle.
617 if (current_service_ || selected_service() != pending_service_) {
618 SelectService(current_service_);
619 }
620
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000621 // Invariant check: a Service can either be current, or pending, but
622 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000623 CHECK(current_service_.get() != pending_service_.get() ||
624 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800625
626 // If we are no longer debugging a problematic WiFi connection, return
627 // to the debugging level indicated by the WiFi debugging scope.
628 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
629 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
630 SetConnectionDebugging(false);
631 }
mukesh agrawal15908392011-11-16 18:29:25 +0000632}
633
634void WiFi::HandleDisconnect() {
635 // Identify the affected service. We expect to get a disconnect
636 // event when we fall off a Service that we were connected
637 // to. However, we also allow for the case where we get a disconnect
638 // event while attempting to connect from a disconnected state.
639 WiFiService *affected_service =
640 current_service_.get() ? current_service_.get() : pending_service_.get();
641
642 current_service_ = NULL;
643 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700644 SLOG(WiFi, 2) << "WiFi " << link_name()
645 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000646 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700647 }
648 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700649 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700650 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000651 }
652
Paul Stewart835934a2012-12-06 19:27:09 -0800653 Error error;
654 if (!DisableNetworkForService(affected_service, &error)) {
655 if (error.type() == Error::kNotFound) {
656 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100657 << " (or failed to connect to) service "
658 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800659 << "but could not find supplicant network to disable.";
660 } else {
661 LOG(FATAL) << "DisableNetwork failed.";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700662 }
mukesh agrawal15908392011-11-16 18:29:25 +0000663 }
664
Ben Chanfad4a0b2012-04-18 15:49:59 -0700665 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100666 << " (or failed to connect to) service "
667 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800668 Service::ConnectFailure failure;
669 if (SuspectCredentials(*affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700670 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700671 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800672 affected_service->SetFailure(failure);
673 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
674 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700675 } else {
676 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700677 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800678 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000679 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000680
681 if (affected_service == pending_service_.get()) {
682 // The attempt to connect to |pending_service_| failed. Clear
683 // |pending_service_|, to indicate we're no longer in the middle
684 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700685 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000686 } else if (pending_service_.get()) {
687 // We've attributed the disconnection to what was the
688 // |current_service_|, rather than the |pending_service_|.
689 //
690 // If we're wrong about that (i.e. supplicant reported this
691 // CurrentBSS change after attempting to connect to
692 // |pending_service_|), we're depending on supplicant to retry
693 // connecting to |pending_service_|, and delivering another
694 // CurrentBSS change signal in the future.
695 //
696 // Log this fact, to help us debug (in case our assumptions are
697 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100698 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
699 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700700 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000701 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700702
703 // If we disconnect, initially scan at a faster frequency, to make sure
704 // we've found all available APs.
705 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000706}
707
708// We use the term "Roam" loosely. In particular, we include the case
709// where we "Roam" to a BSS from the disconnected state.
710void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
711 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
712 if (endpoint_it == endpoint_by_rpcid_.end()) {
713 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
714 << new_bss;
715 return;
716 }
717
718 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000719 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000720 if (!service.get()) {
721 LOG(WARNING) << "WiFi " << link_name()
722 << " could not find Service for Endpoint "
723 << endpoint.bssid_string()
724 << " (service will be unchanged)";
725 return;
726 }
727
Ben Chanfad4a0b2012-04-18 15:49:59 -0700728 SLOG(WiFi, 2) << "WiFi " << link_name()
729 << " roamed to Endpoint " << endpoint.bssid_string()
730 << " (SSID " << endpoint.ssid_string() << ")";
mukesh agrawal15908392011-11-16 18:29:25 +0000731
mukesh agrawale1d90e92012-02-15 17:36:08 -0800732 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000733
mukesh agrawal15908392011-11-16 18:29:25 +0000734 if (pending_service_.get() &&
735 service.get() != pending_service_.get()) {
736 // The Service we've roamed on to is not the one we asked for.
737 // We assume that this is transient, and that wpa_supplicant
738 // is trying / will try to connect to |pending_service_|.
739 //
740 // If it succeeds, we'll end up back here, but with |service|
741 // pointing at the same service as |pending_service_|.
742 //
743 // If it fails, we'll process things in HandleDisconnect.
744 //
745 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700746 SLOG(WiFi, 2) << "WiFi " << link_name()
747 << " new current Endpoint "
748 << endpoint.bssid_string()
749 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100750 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000751
752 // Sanity check: if we didn't roam onto |pending_service_|, we
753 // should still be on |current_service_|.
754 if (service.get() != current_service_.get()) {
755 LOG(WARNING) << "WiFi " << link_name()
756 << " new current Endpoint "
757 << endpoint.bssid_string()
758 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100759 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000760 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100761 << (current_service_ ?
762 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000763 "(NULL)");
764 // Although we didn't expect to get here, we should keep
765 // |current_service_| in sync with what supplicant has done.
766 current_service_ = service;
767 }
768 return;
769 }
770
771 if (pending_service_.get()) {
772 // We assume service.get() == pending_service_.get() here, because
773 // of the return in the previous if clause.
774 //
775 // Boring case: we've connected to the service we asked
776 // for. Simply update |current_service_| and |pending_service_|.
777 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700778 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000779 return;
780 }
781
782 // |pending_service_| was NULL, so we weren't attempting to connect
783 // to a new Service. Sanity check that we're still on
784 // |current_service_|.
785 if (service.get() != current_service_.get()) {
786 LOG(WARNING)
787 << "WiFi " << link_name()
788 << " new current Endpoint "
789 << endpoint.bssid_string()
790 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000791 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100792 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000793 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000794 // We didn't expect to be here, but let's cope as well as we
795 // can. Update |current_service_| to keep it in sync with
796 // supplicant.
797 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700798
799 // If this service isn't already marked as actively connecting (likely,
800 // since this service is a bit of a surprise) set the service as
801 // associating.
802 if (!current_service_->IsConnecting()) {
803 current_service_->SetState(Service::kStateAssociating);
804 }
805
mukesh agrawal15908392011-11-16 18:29:25 +0000806 return;
807 }
808
809 // At this point, we know that |pending_service_| was NULL, and that
810 // we're still on |current_service_|. This is the most boring case
811 // of all, because there's no state to update here.
812 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700813}
814
Paul Stewarta41e38d2011-11-11 07:47:29 -0800815WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
816 const string &mode,
817 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800818 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
819 it != services_.end();
820 ++it) {
821 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
822 (*it)->IsSecurityMatch(security)) {
823 return *it;
824 }
825 }
826 return NULL;
827}
828
mukesh agrawal165e6142011-11-22 02:22:56 +0000829WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
830 return FindService(endpoint.ssid(),
831 endpoint.network_mode(),
832 endpoint.security_mode());
833}
834
Paul Stewart835934a2012-12-06 19:27:09 -0800835string WiFi::FindNetworkRpcidForService(
836 const WiFiService *service, Error *error) {
837 ReverseServiceMap::const_iterator rpcid_it =
838 rpcid_by_service_.find(service);
839 if (rpcid_it == rpcid_by_service_.end()) {
840 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100841 StringPrintf(
842 "WiFi %s cannot find supplicant network rpcid for service %s",
843 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -0800844 // There are contexts where this is not an error, such as when a service
845 // is clearing whatever cached credentials may not exist.
846 SLOG(WiFi, 2) << error_message;
847 if (error) {
848 error->Populate(Error::kNotFound, error_message);
849 }
850 return "";
851 }
852
853 return rpcid_it->second;
854}
855
856bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
857 string rpcid = FindNetworkRpcidForService(service, error);
858 if (rpcid.empty()) {
859 // Error is already populated.
860 return false;
861 }
862
863 if (!DisableNetwork(rpcid)) {
864 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100865 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800866 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100867 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800868 rpcid.c_str());
869 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
870
871 // Make sure that such errored networks are removed, so problems do not
872 // propogate to future connection attempts.
873 RemoveNetwork(rpcid);
874 rpcid_by_service_.erase(service);
875
876 return false;
877 }
878
879 return true;
880}
881
882bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
883 string rpcid = FindNetworkRpcidForService(service, error);
884 if (rpcid.empty()) {
885 // Error is already populated.
886 return false;
887 }
888
889 // Erase the rpcid from our tables regardless of failure below, since even
890 // if in failure, we never want to use this network again.
891 rpcid_by_service_.erase(service);
892
893 // TODO(quiche): Reconsider giving up immediately. Maybe give
894 // wpa_supplicant some time to retry, first.
895 if (!RemoveNetwork(rpcid)) {
896 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100897 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800898 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100899 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800900 rpcid.c_str());
901 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
902 return false;
903 }
904
905 return true;
906}
907
Paul Stewartced6a0b2011-11-08 15:32:04 -0800908ByteArrays WiFi::GetHiddenSSIDList() {
909 // Create a unique set of hidden SSIDs.
910 set<ByteArray> hidden_ssids_set;
911 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
912 it != services_.end();
913 ++it) {
Paul Stewart10ccbb32012-04-26 15:59:30 -0700914 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800915 hidden_ssids_set.insert((*it)->ssid());
916 }
917 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700918 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800919 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
920 if (!hidden_ssids.empty()) {
921 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
922 // not fit in.
923 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
924 hidden_ssids.erase(
925 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
926 hidden_ssids.end());
927 }
928 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
929 // SSIDs to wpa_supplicant, we need to explicitly specify the default
930 // behavior of doing a broadcast probe.
931 hidden_ssids.push_back(ByteArray());
932 }
933 return hidden_ssids;
934}
935
Paul Stewarta41e38d2011-11-11 07:47:29 -0800936bool WiFi::LoadHiddenServices(StoreInterface *storage) {
Paul Stewart85aea152013-01-22 09:31:56 -0800937 // TODO(pstew): This retrofits old (flimflam-based) profile entries for
938 // WiFi Services so they contain the properties that are required to
939 // be searched for by property instead of by group name. I can imagine
940 // a day where we believe that enough users have upgraded so that this
941 // code is no longer necessary. The UMA metric below should help us
942 // understand when this point might be.
943 if (WiFiService::FixupServiceEntries(storage)) {
944 storage->Flush();
945 Metrics::ServiceFixupProfileType profile_type =
946 manager()->IsDefaultProfile(storage) ?
947 Metrics::kMetricServiceFixupDefaultProfile :
948 Metrics::kMetricServiceFixupUserProfile;
949 metrics()->SendEnumToUMA(
950 metrics()->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
951 technology()),
952 profile_type,
953 Metrics::kMetricServiceFixupMax);
954 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800955 bool created_hidden_service = false;
956 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
957 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
958 bool is_hidden = false;
959 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700960 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
961 << "GetGroupsWithKey failed for GetBool("
962 << flimflam::kWifiHiddenSsid
963 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800964 continue;
965 }
966 if (!is_hidden) {
967 continue;
968 }
969 string ssid_hex;
970 vector<uint8_t> ssid_bytes;
971 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
972 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700973 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
974 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800975 continue;
976 }
977 string device_address;
978 string network_mode;
979 string security;
980 // It is gross that we have to do this, but the only place we can
981 // get information about the service is from its storage name.
982 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
983 &network_mode, &security) ||
984 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700985 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
986 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800987 continue;
988 }
989 if (FindService(ssid_bytes, network_mode, security).get()) {
990 // If service already exists, we have nothing to do, since the
991 // service has already loaded its configuration from storage.
992 // This is guaranteed to happen in both cases where Load() is
993 // called on a device (via a ConfigureDevice() call on the
994 // profile):
995 // - In RegisterDevice() the Device hasn't been started yet,
996 // so it has no services registered, except for those
997 // created by previous iterations of LoadHiddenService().
998 // The latter can happen if another profile in the Manager's
999 // stack defines the same ssid/mode/security. Even this
1000 // case is okay, since even if the profiles differ
1001 // materially on configuration and credentials, the "right"
1002 // one will be configured in the course of the
1003 // RegisterService() call below.
1004 // - In PushProfile(), all registered services have been
1005 // introduced to the profile via ConfigureService() prior
1006 // to calling ConfigureDevice() on the profile.
1007 continue;
1008 }
1009 WiFiServiceRefPtr service(new WiFiService(control_interface(),
1010 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001011 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -08001012 manager(),
1013 this,
1014 ssid_bytes,
1015 network_mode,
1016 security,
1017 true));
1018 services_.push_back(service);
1019
1020 // By registering the service, the rest of the configuration
1021 // will be loaded from the profile into the service via ConfigureService().
1022 manager()->RegisterService(service);
1023
1024 created_hidden_service = true;
1025 }
1026
1027 // If we are idle and we created a service as a result of opening the
1028 // profile, we should initiate a scan for our new hidden SSID.
1029 if (running() && created_hidden_service &&
1030 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
1031 Scan(NULL);
1032 }
1033
1034 return created_hidden_service;
1035}
1036
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001037void WiFi::BSSAddedTask(
1038 const ::DBus::Path &path,
1039 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001040 // Note: we assume that BSSIDs are unique across endpoints. This
1041 // means that if an AP reuses the same BSSID for multiple SSIDs, we
1042 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001043 WiFiEndpointRefPtr endpoint(
1044 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -08001045 SLOG(WiFi, 1) << "Found endpoint. "
1046 << "RPC path: " << path << ", "
1047 << "ssid: " << endpoint->ssid_string() << ", "
1048 << "bssid: " << endpoint->bssid_string() << ", "
1049 << "signal: " << endpoint->signal_strength() << ", "
1050 << "security: " << endpoint->security_mode() << ", "
1051 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001052
1053 if (endpoint->ssid_string().empty()) {
1054 // Don't bother trying to find or create a Service for an Endpoint
1055 // without an SSID. We wouldn't be able to connect to it anyway.
1056 return;
1057 }
1058
mukesh agrawalb3857612012-01-18 16:23:29 -08001059 if (endpoint->ssid()[0] == 0) {
1060 // Assume that an SSID starting with NULL is bogus/misconfigured,
1061 // and filter it out.
1062 return;
1063 }
1064
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001065 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
1066 if (service) {
Wade Guthrie592ecd52012-11-12 13:12:30 -08001067 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
Darin Petkov457728b2013-01-09 09:49:08 +01001068 << " to service " << service->unique_name() << ".";
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001069 service->AddEndpoint(endpoint);
1070
1071 if (manager()->HasService(service)) {
1072 manager()->UpdateService(service);
1073 } else {
Darin Petkov4a66cc52012-06-15 10:08:29 +02001074 // Expect registered by now if >1.
1075 DCHECK_EQ(1, service->GetEndpointCount());
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001076 manager()->RegisterService(service);
1077 }
mukesh agrawale9adda12012-02-09 18:33:48 -08001078 } else {
1079 const bool hidden_ssid = false;
1080 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
mukesh agrawale9adda12012-02-09 18:33:48 -08001081 service->AddEndpoint(endpoint);
1082 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001083 }
1084
mukesh agrawale9adda12012-02-09 18:33:48 -08001085 // Do this last, to maintain the invariant that any Endpoint we
1086 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001087 //
1088 // TODO(quiche): Write test to verify correct behavior in the case
1089 // where we get multiple BSSAdded events for a single endpoint.
1090 // (Old Endpoint's refcount should fall to zero, and old Endpoint
1091 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -08001092 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -08001093 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001094}
1095
1096void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
1097 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
1098 if (i == endpoint_by_rpcid_.end()) {
1099 LOG(WARNING) << "WiFi " << link_name()
1100 << " could not find BSS " << path
1101 << " to remove.";
1102 return;
1103 }
1104
1105 WiFiEndpointRefPtr endpoint = i->second;
1106 CHECK(endpoint);
1107 endpoint_by_rpcid_.erase(i);
1108
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001109 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -08001110 CHECK(service) << "Can't find Service for Endpoint "
1111 << path << " "
1112 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -07001113 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
Darin Petkov457728b2013-01-09 09:49:08 +01001114 << " from Service " << service->unique_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001115 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001116
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001117 bool disconnect_service = !service->HasEndpoints() &&
1118 (service->IsConnecting() || service->IsConnected());
1119 bool forget_service =
1120 // Forget Services without Endpoints, except that we always keep
1121 // hidden services around. (We need them around to populate the
1122 // hidden SSIDs list.)
1123 !service->HasEndpoints() && !service->hidden_ssid();
1124 bool deregister_service =
1125 // Only deregister a Service if we're forgetting it. Otherwise,
1126 // Manager can't keep our configuration up-to-date (as Profiles
1127 // change).
1128 forget_service;
1129
1130 if (disconnect_service) {
1131 DisconnectFrom(service);
1132 }
1133
1134 if (deregister_service) {
1135 manager()->DeregisterService(service);
1136 } else {
1137 manager()->UpdateService(service);
1138 }
1139
1140 if (forget_service) {
Paul Stewart835934a2012-12-06 19:27:09 -08001141 Error unused_error;
1142 RemoveNetworkForService(service, &unused_error);
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001143 vector<WiFiServiceRefPtr>::iterator it;
1144 it = std::find(services_.begin(), services_.end(), service);
1145 if (it != services_.end()) {
1146 services_.erase(it);
1147 }
1148 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001149}
1150
Paul Stewartbc6e7392012-05-24 07:07:48 -07001151void WiFi::CertificationTask(
1152 const map<string, ::DBus::Variant> &properties) {
1153 if (!current_service_) {
1154 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1155 << " with no current service.";
1156 return;
1157 }
1158
1159 map<string, ::DBus::Variant>::const_iterator properties_it =
1160 properties.find(wpa_supplicant::kInterfacePropertyDepth);
1161 if (properties_it == properties.end()) {
1162 LOG(ERROR) << __func__ << " no depth parameter.";
1163 return;
1164 }
1165 uint32 depth = properties_it->second.reader().get_uint32();
1166 properties_it = properties.find(wpa_supplicant::kInterfacePropertySubject);
1167 if (properties_it == properties.end()) {
1168 LOG(ERROR) << __func__ << " no subject parameter.";
1169 return;
1170 }
1171 string subject(properties_it->second.reader().get_string());
1172 current_service_->AddEAPCertification(subject, depth);
1173}
1174
Paul Stewartdb0f9172012-11-30 16:48:09 -08001175void WiFi::EAPEventTask(const string &status, const string &parameter) {
1176 Service::ConnectFailure failure = Service::kFailureUnknown;
1177
1178 if (!current_service_) {
1179 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1180 << " with no current service.";
1181 return;
1182 }
1183
1184 if (status == wpa_supplicant::kEAPStatusAcceptProposedMethod) {
1185 LOG(INFO) << link_name() << ": accepted EAP method " << parameter;
1186 } else if (status == wpa_supplicant::kEAPStatusCompletion) {
1187 if (parameter == wpa_supplicant::kEAPParameterSuccess) {
1188 SLOG(WiFi, 2) << link_name() << ": EAP: "
1189 << "Completed authentication.";
Paul Stewart1369c2b2013-01-11 05:41:26 -08001190 is_eap_in_progress_ = false;
Paul Stewartdb0f9172012-11-30 16:48:09 -08001191 } else if (parameter == wpa_supplicant::kEAPParameterFailure) {
1192 // If there was a TLS error, use this instead of the generic failure.
1193 if (supplicant_tls_error_ == wpa_supplicant::kEAPStatusLocalTLSAlert) {
1194 failure = Service::kFailureEAPLocalTLS;
1195 } else if (supplicant_tls_error_ ==
1196 wpa_supplicant::kEAPStatusRemoteTLSAlert) {
1197 failure = Service::kFailureEAPRemoteTLS;
1198 } else {
1199 failure = Service::kFailureEAPAuthentication;
1200 }
1201 } else {
1202 LOG(ERROR) << link_name() << ": EAP: "
1203 << "Unexpected " << status << " parameter: " << parameter;
1204 }
1205 } else if (status == wpa_supplicant::kEAPStatusLocalTLSAlert ||
1206 status == wpa_supplicant::kEAPStatusRemoteTLSAlert) {
1207 supplicant_tls_error_ = status;
1208 } else if (status ==
1209 wpa_supplicant::kEAPStatusRemoteCertificateVerification) {
1210 if (parameter == wpa_supplicant::kEAPParameterSuccess) {
1211 SLOG(WiFi, 2) << link_name() << ": EAP: "
1212 << "Completed remote certificate verification.";
1213 } else {
1214 // wpa_supplicant doesn't currently have a verification failure
1215 // message. We will instead get a RemoteTLSAlert above.
1216 LOG(ERROR) << link_name() << ": EAP: "
1217 << "Unexpected " << status << " parameter: " << parameter;
1218 }
Paul Stewart1369c2b2013-01-11 05:41:26 -08001219 } else if (status == wpa_supplicant::kEAPStatusParameterNeeded) {
1220 LOG(ERROR) << link_name() << ": EAP: "
1221 << "Authentication due to missing authentication parameter: "
1222 << parameter;
1223 failure = Service::kFailureEAPAuthentication;
1224 } else if (status == wpa_supplicant::kEAPStatusStarted) {
1225 SLOG(WiFi, 2) << link_name() << ": EAP authentication starting.";
1226 is_eap_in_progress_ = true;
Paul Stewartdb0f9172012-11-30 16:48:09 -08001227 }
1228
1229 if (failure != Service::kFailureUnknown) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001230 // Avoid a reporting failure twice by clearing eap_in_progress_ early.
1231 is_eap_in_progress_ = false;
Paul Stewartdb0f9172012-11-30 16:48:09 -08001232 current_service_->DisconnectWithFailure(failure, NULL);
1233 }
1234}
1235
mukesh agrawal15908392011-11-16 18:29:25 +00001236void WiFi::PropertiesChangedTask(
1237 const map<string, ::DBus::Variant> &properties) {
1238 // TODO(quiche): Handle changes in other properties (e.g. signal
1239 // strength).
1240
1241 // Note that order matters here. In particular, we want to process
1242 // changes in the current BSS before changes in state. This is so
1243 // that we update the state of the correct Endpoint/Service.
1244
1245 map<string, ::DBus::Variant>::const_iterator properties_it =
1246 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
1247 if (properties_it != properties.end()) {
1248 CurrentBSSChanged(properties_it->second.reader().get_path());
1249 }
1250
1251 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
1252 if (properties_it != properties.end()) {
1253 StateChanged(properties_it->second.reader().get_string());
1254 }
1255}
1256
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001257void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001258 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001259 if (need_bss_flush_) {
1260 CHECK(supplicant_interface_proxy_ != NULL);
1261 // Compute |max_age| relative to |resumed_at_|, to account for the
1262 // time taken to scan.
1263 struct timeval now;
1264 uint32_t max_age;
1265 time_->GetTimeMonotonic(&now);
1266 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1267 supplicant_interface_proxy_->FlushBSS(max_age);
1268 need_bss_flush_ = false;
1269 }
Paul Stewartd2db2b12013-01-17 13:11:07 -08001270 SetScanPending(false);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001271 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001272}
1273
mukesh agrawal32399322011-09-01 10:53:43 -07001274void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001275 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001276 if (!enabled()) {
1277 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
1278 return;
1279 }
1280 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1281 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
1282 return;
1283 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001284 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1285 (current_service_.get() && current_service_->IsConnecting())) {
1286 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1287 return;
1288 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001289 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -07001290 scan_args[wpa_supplicant::kPropertyScanType].writer().
1291 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001292
1293 ByteArrays hidden_ssids = GetHiddenSSIDList();
1294 if (!hidden_ssids.empty()) {
1295 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
1296 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1297 }
1298
Gaurav Shahf8721ee2011-11-07 09:12:46 -08001299 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -08001300 try {
1301 supplicant_interface_proxy_->Scan(scan_args);
Paul Stewartd2db2b12013-01-17 13:11:07 -08001302 SetScanPending(true);
Ben Chan80326f32012-05-04 17:51:32 -07001303 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001304 // A scan may fail if, for example, the wpa_supplicant vanishing
1305 // notification is posted after this task has already started running.
1306 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001307 }
mukesh agrawal32399322011-09-01 10:53:43 -07001308}
1309
Paul Stewartd2db2b12013-01-17 13:11:07 -08001310void WiFi::SetScanPending(bool pending) {
1311 if (scan_pending_ != pending) {
1312 scan_pending_ = pending;
1313 adaptor()->EmitBoolChanged(flimflam::kScanningProperty, pending);
1314 }
1315}
1316
mukesh agrawal15908392011-11-16 18:29:25 +00001317void WiFi::StateChanged(const string &new_state) {
1318 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001319 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001320 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1321 << old_state << " -> " << new_state;
1322
1323 WiFiService *affected_service;
1324 // Identify the service to which the state change applies. If
1325 // |pending_service_| is non-NULL, then the state change applies to
1326 // |pending_service_|. Otherwise, it applies to |current_service_|.
1327 //
1328 // This policy is driven by the fact that the |pending_service_|
1329 // doesn't become the |current_service_| until wpa_supplicant
1330 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001331 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001332 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1333 affected_service =
1334 pending_service_.get() ? pending_service_.get() : current_service_.get();
1335 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001336 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1337 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001338 return;
1339 }
1340
Paul Stewart44663922012-07-30 11:03:03 -07001341 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
1342 if (affected_service->IsConnected()) {
1343 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001344 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001345 } else if (has_already_completed_) {
1346 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001347 } else if (AcquireIPConfigWithLeaseName(
1348 affected_service->GetStorageIdentifier())) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001349 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001350 affected_service->SetState(Service::kStateConfiguring);
1351 } else {
1352 LOG(ERROR) << "Unable to acquire DHCP config.";
1353 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001354 has_already_completed_ = true;
mukesh agrawal15908392011-11-16 18:29:25 +00001355 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1356 affected_service->SetState(Service::kStateAssociating);
1357 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1358 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1359 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1360 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1361 // Ignore transitions into these states from Completed, to avoid
1362 // bothering the user when roaming, or re-keying.
1363 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1364 affected_service->SetState(Service::kStateAssociating);
1365 // TOOD(quiche): On backwards transitions, we should probably set
1366 // a timeout for getting back into the completed state. At present,
1367 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001368 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001369 // (crosbug.com/23207)
Paul Stewart44663922012-07-30 11:03:03 -07001370 } else if (new_state == wpa_supplicant::kInterfaceStateDisconnected &&
1371 affected_service == current_service_ &&
1372 affected_service->IsConnected()) {
1373 // This means that wpa_supplicant failed in a re-connect attempt, but
1374 // may still be reconnecting. Give wpa_supplicant a limited amount of
1375 // time to transition out this condition by either connecting or changing
1376 // CurrentBSS.
1377 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001378 } else {
1379 // Other transitions do not affect Service state.
1380 //
1381 // Note in particular that we ignore a State change into
1382 // kInterfaceStateDisconnected, in favor of observing the corresponding
1383 // change in CurrentBSS.
1384 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001385}
1386
Paul Stewart1369c2b2013-01-11 05:41:26 -08001387bool WiFi::SuspectCredentials(
1388 const WiFiService &service, Service::ConnectFailure *failure) const {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001389 if (service.IsSecurityMatch(flimflam::kSecurityPsk)) {
1390 if (supplicant_state_ == wpa_supplicant::kInterfaceState4WayHandshake &&
1391 !service.has_ever_connected()) {
1392 if (failure) {
1393 *failure = Service::kFailureBadPassphrase;
1394 }
1395 return true;
1396 }
1397 } else if (service.IsSecurityMatch(flimflam::kSecurity8021x)) {
1398 if (is_eap_in_progress_ && !service.has_ever_connected()) {
1399 if (failure) {
1400 *failure = Service::kFailureEAPAuthentication;
1401 }
1402 return true;
1403 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001404 }
1405
Paul Stewart1369c2b2013-01-11 05:41:26 -08001406 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001407}
1408
mukesh agrawal7ec71312011-11-10 02:08:26 +00001409// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001410WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001411 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001412
1413 if (args.ContainsString(flimflam::kModeProperty) &&
1414 args.GetString(flimflam::kModeProperty) !=
1415 flimflam::kModeManaged) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001416 Error::PopulateAndLog(error, Error::kNotSupported,
1417 kManagerErrorUnsupportedServiceMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001418 return NULL;
1419 }
1420
1421 if (!args.ContainsString(flimflam::kSSIDProperty)) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001422 Error::PopulateAndLog(error, Error::kInvalidArguments,
1423 kManagerErrorSSIDRequired);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001424 return NULL;
1425 }
1426
1427 string ssid = args.GetString(flimflam::kSSIDProperty);
1428 if (ssid.length() < 1) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001429 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1430 kManagerErrorSSIDTooShort);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001431 return NULL;
1432 }
1433
1434 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001435 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1436 kManagerErrorSSIDTooLong);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001437 return NULL;
1438 }
1439
1440 string security_method;
1441 if (args.ContainsString(flimflam::kSecurityProperty)) {
1442 security_method = args.GetString(flimflam::kSecurityProperty);
1443 } else {
1444 security_method = flimflam::kSecurityNone;
1445 }
1446
1447 if (security_method != flimflam::kSecurityNone &&
1448 security_method != flimflam::kSecurityWep &&
1449 security_method != flimflam::kSecurityPsk &&
1450 security_method != flimflam::kSecurityWpa &&
1451 security_method != flimflam::kSecurityRsn &&
1452 security_method != flimflam::kSecurity8021x) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001453 Error::PopulateAndLog(error, Error::kNotSupported,
1454 kManagerErrorUnsupportedSecurityMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001455 return NULL;
1456 }
1457
Paul Stewart6ab23a92011-11-09 17:17:47 -08001458 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001459 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1460 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001461 } else {
1462 // If the service is not found, and the caller hasn't specified otherwise,
1463 // we assume this is is a hidden network.
1464 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001465 }
1466
Paul Stewart6ab23a92011-11-09 17:17:47 -08001467 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1468 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1469 security_method));
1470 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001471 service = new WiFiService(control_interface(),
1472 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001473 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001474 manager(),
1475 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001476 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001477 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001478 security_method,
1479 hidden_ssid);
1480 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001481 // NB: We do not register the newly created Service with the Manager.
1482 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001483 }
1484
Paul Stewart7f61e522012-03-22 11:13:45 -07001485 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1486 // have no endpoints like the one we may have just created. crosbug.com/28224
1487
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001488 return service;
1489}
1490
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001491// static
1492bool WiFi::SanitizeSSID(string *ssid) {
1493 CHECK(ssid);
1494
1495 size_t ssid_len = ssid->length();
1496 size_t i;
1497 bool changed = false;
1498
Gary Morainac1bdb42012-02-16 17:42:29 -08001499 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001500 if (!g_ascii_isprint((*ssid)[i])) {
1501 (*ssid)[i] = '?';
1502 changed = true;
1503 }
1504 }
1505
1506 return changed;
1507}
1508
Paul Stewart3c508e12012-08-09 11:40:06 -07001509void WiFi::OnLinkMonitorFailure() {
1510 // If we have never found the gateway, let's be conservative and not
1511 // do anything, in case this network topology does not have a gateway.
1512 if (!link_monitor()->IsGatewayFound()) {
1513 LOG(INFO) << "In " << __func__ << "(): "
1514 << "Skipping reassociate since gateway was never found.";
1515 return;
1516 }
1517
1518 if (!supplicant_present_) {
1519 LOG(ERROR) << "In " << __func__ << "(): "
1520 << "wpa_supplicant is not present. Cannot reassociate.";
1521 return;
1522 }
1523
1524 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001525 // This will force a transition out of connected, if we are actually
1526 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001527 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001528 // If we don't eventually get a transition back into a connected state,
1529 // there is something wrong.
1530 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001531 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1532 } catch (const DBus::Error &e) { // NOLINT
1533 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1534 return;
1535 }
1536}
1537
Arman Ugurayed8e6102012-11-29 14:47:20 -08001538bool WiFi::ShouldUseArpGateway() const {
1539 return true;
1540}
1541
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001542vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1543 vector<GeolocationInfo> objects;
1544 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1545 it != endpoint_by_rpcid_.end();
1546 ++it) {
1547 GeolocationInfo geoinfo;
1548 WiFiEndpointRefPtr endpoint = it->second;
1549 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1550 geoinfo.AddField(kGeoSignalStrengthProperty,
1551 StringPrintf("%d", endpoint->signal_strength()));
1552 geoinfo.AddField(
1553 kGeoChannelProperty,
1554 StringPrintf("%d",
1555 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1556 // TODO(gauravsh): Include age field. crosbug.com/35445
1557 objects.push_back(geoinfo);
1558 }
1559 return objects;
1560}
1561
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001562void WiFi::HelpRegisterDerivedInt32(
1563 PropertyStore *store,
1564 const string &name,
1565 int32(WiFi::*get)(Error *error),
1566 void(WiFi::*set)(const int32 &value, Error *error)) {
1567 store->RegisterDerivedInt32(
1568 name,
1569 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1570}
1571
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001572void WiFi::HelpRegisterDerivedUint16(
1573 PropertyStore *store,
1574 const string &name,
1575 uint16(WiFi::*get)(Error *error),
1576 void(WiFi::*set)(const uint16 &value, Error *error)) {
1577 store->RegisterDerivedUint16(
1578 name,
1579 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1580}
1581
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001582void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001583 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001584 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001585
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001586 // We want to flush the BSS cache, but we don't want to conflict
1587 // with a running scan or an active connection attempt. So record
1588 // the need to flush, and take care of flushing when the next scan
1589 // completes.
1590 //
1591 // Note that supplicant will automatically expire old cache
1592 // entries (after, e.g., a BSS is not found in two consecutive
1593 // scans). However, our explicit flush accelerates re-association
1594 // in cases where a BSS disappeared while we were asleep. (See,
1595 // e.g. WiFiRoaming.005SuspendRoam.)
1596 time_->GetTimeMonotonic(&resumed_at_);
1597 need_bss_flush_ = true;
1598
1599 if (!scan_pending_ && IsIdle()) {
1600 // Not scanning/connecting/connected, so let's get things rolling.
1601 Scan(NULL);
1602 } else {
1603 SLOG(WiFi, 1) << __func__
1604 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001605 }
1606}
1607
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001608void WiFi::OnConnected() {
1609 Device::OnConnected();
1610 EnableHighBitrates();
1611}
1612
Paul Stewarte369ece2012-05-22 09:11:03 -07001613void WiFi::RestartFastScanAttempts() {
1614 fast_scans_remaining_ = kNumFastScanAttempts;
1615 StartScanTimer();
1616}
1617
mukesh agrawalb66c6462012-05-07 11:45:25 -07001618void WiFi::StartScanTimer() {
1619 if (scan_interval_seconds_ == 0) {
1620 StopScanTimer();
1621 return;
1622 }
1623 scan_timer_callback_.Reset(
1624 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001625 // Repeat the first few scans after disconnect relatively quickly so we
1626 // have reasonable trust that no APs we are looking for are present.
1627 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1628 fast_scans_remaining_ > 0 ?
1629 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001630}
1631
1632void WiFi::StopScanTimer() {
1633 scan_timer_callback_.Cancel();
1634}
1635
1636void WiFi::ScanTimerHandler() {
1637 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1638 if (IsIdle() && !scan_pending_) {
1639 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001640 if (fast_scans_remaining_ > 0) {
1641 --fast_scans_remaining_;
1642 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001643 }
1644 StartScanTimer();
1645}
1646
Paul Stewart2b05e622012-07-13 20:38:44 -07001647void WiFi::StartPendingTimer() {
1648 pending_timeout_callback_.Reset(
1649 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1650 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1651 kPendingTimeoutSeconds * 1000);
1652}
1653
1654void WiFi::StopPendingTimer() {
1655 pending_timeout_callback_.Cancel();
1656}
1657
1658void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001659 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001660 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001661 if (service) {
1662 service->SetState(Service::kStateAssociating);
1663 StartPendingTimer();
1664 } else if (pending_service_) {
1665 StopPendingTimer();
1666 }
1667 pending_service_ = service;
1668}
1669
1670void WiFi::PendingTimeoutHandler() {
1671 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1672 CHECK(pending_service_);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001673 pending_service_->DisconnectWithFailure(Service::kFailureOutOfRange, NULL);
Paul Stewart2b05e622012-07-13 20:38:44 -07001674}
1675
Paul Stewart44663922012-07-30 11:03:03 -07001676void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001677 if (!reconnect_timeout_callback_.IsCancelled()) {
1678 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1679 << ": reconnect timer already running.";
1680 return;
1681 }
Paul Stewart44663922012-07-30 11:03:03 -07001682 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1683 reconnect_timeout_callback_.Reset(
1684 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1685 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1686 kReconnectTimeoutSeconds * 1000);
1687}
1688
1689void WiFi::StopReconnectTimer() {
1690 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1691 reconnect_timeout_callback_.Cancel();
1692}
1693
1694void WiFi::ReconnectTimeoutHandler() {
1695 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001696 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001697 CHECK(current_service_);
1698 current_service_->SetFailureSilent(Service::kFailureConnect);
1699 DisconnectFrom(current_service_);
1700}
1701
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001702void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1703 LOG(INFO) << "WPA supplicant appeared.";
1704 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001705 // Restart the WiFi device if it's started already. This will reset the
1706 // state and connect the device to the new WPA supplicant instance.
1707 if (enabled()) {
1708 Restart();
1709 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001710 return;
1711 }
1712 supplicant_present_ = true;
1713 ConnectToSupplicant();
1714}
1715
1716void WiFi::OnSupplicantVanish() {
1717 LOG(INFO) << "WPA supplicant vanished.";
1718 if (!supplicant_present_) {
1719 return;
1720 }
1721 supplicant_present_ = false;
1722 // Restart the WiFi device if it's started already. This will effectively
1723 // suspend the device until the WPA supplicant reappears.
1724 if (enabled()) {
1725 Restart();
1726 }
1727}
1728
Paul Stewart5581d072012-12-17 17:30:20 -08001729void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1730 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1731 if (!supplicant_process_proxy_.get()) {
1732 SLOG(WiFi, 2) << "Suplicant process proxy not present.";
1733 return;
1734 }
1735 string current_level;
1736 try {
1737 current_level = supplicant_process_proxy_->GetDebugLevel();
1738 } catch (const DBus::Error &e) { // NOLINT
1739 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1740 return;
1741 }
1742
1743 if (current_level != wpa_supplicant::kDebugLevelInfo &&
1744 current_level != wpa_supplicant::kDebugLevelDebug) {
1745 SLOG(WiFi, 2) << "WiFi debug level is currently "
1746 << current_level
1747 << "; assuming that it is being controlled elsewhere.";
1748 return;
1749 }
1750 string new_level = enabled ? wpa_supplicant::kDebugLevelDebug :
1751 wpa_supplicant::kDebugLevelInfo;
1752
1753 if (new_level == current_level) {
1754 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1755 << current_level;
1756 return;
1757 }
1758
1759 try {
1760 supplicant_process_proxy_->SetDebugLevel(new_level);
1761 } catch (const DBus::Error &e) { // NOLINT
1762 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1763 }
1764}
1765
Paul Stewarta47c3c62012-12-18 12:14:29 -08001766void WiFi::SetConnectionDebugging(bool enabled) {
1767 if (is_debugging_connection_ == enabled) {
1768 return;
1769 }
1770 OnWiFiDebugScopeChanged(
1771 enabled ||
1772 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1773 is_debugging_connection_ = enabled;
1774}
1775
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001776void WiFi::ConnectToSupplicant() {
1777 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1778 << " supplicant: "
1779 << (supplicant_present_ ? "present" : "absent")
1780 << " proxy: "
1781 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1782 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1783 return;
1784 }
1785 supplicant_process_proxy_.reset(
1786 proxy_factory_->CreateSupplicantProcessProxy(
1787 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001788 OnWiFiDebugScopeChanged(
1789 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001790 ::DBus::Path interface_path;
1791 try {
1792 map<string, DBus::Variant> create_interface_args;
1793 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
1794 append_string(link_name().c_str());
1795 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
1796 append_string(wpa_supplicant::kDriverNL80211);
1797 create_interface_args[
1798 wpa_supplicant::kInterfacePropertyConfigFile].writer().
Darin Petkov60ceaf32012-10-18 10:36:01 +02001799 append_string(kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001800 interface_path =
1801 supplicant_process_proxy_->CreateInterface(create_interface_args);
1802 } catch (const DBus::Error &e) { // NOLINT
1803 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
1804 interface_path =
1805 supplicant_process_proxy_->GetInterface(link_name());
1806 // TODO(quiche): Is it okay to crash here, if device is missing?
1807 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001808 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1809 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001810 }
1811 }
1812
1813 supplicant_interface_proxy_.reset(
1814 proxy_factory_->CreateSupplicantInterfaceProxy(
1815 this, interface_path, wpa_supplicant::kDBusAddr));
1816
1817 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1818 IFF_UP);
1819 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1820
1821 // Clear out any networks that might previously have been configured
1822 // for this interface.
1823 supplicant_interface_proxy_->RemoveAllNetworks();
1824
1825 // Flush interface's BSS cache, so that we get BSSAdded signals for
1826 // all BSSes (not just new ones since the last scan).
1827 supplicant_interface_proxy_->FlushBSS(0);
1828
1829 try {
1830 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1831 // with RADIUS servers that respond strangely to such requests.
1832 // crosbug.com/25630
1833 supplicant_interface_proxy_->SetFastReauth(false);
1834 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001835 LOG(ERROR) << "Failed to disable fast_reauth. "
1836 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001837 }
1838
1839 try {
1840 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1841 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1842 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001843 LOG(ERROR) << "Failed to set scan_interval. "
1844 << "May be running an older version of wpa_supplicant.";
1845 }
1846
1847 try {
1848 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1849 } catch (const DBus::Error &e) { // NOLINT
1850 LOG(ERROR) << "Failed to disable high bitrates. "
1851 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001852 }
1853
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001854 Scan(NULL);
1855 StartScanTimer();
1856}
1857
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001858void WiFi::EnableHighBitrates() {
1859 LOG(INFO) << "Enabling high bitrates.";
1860 try {
1861 supplicant_interface_proxy_->EnableHighBitrates();
1862 } catch (const DBus::Error &e) { // NOLINT
1863 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1864 }
1865}
1866
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001867void WiFi::Restart() {
1868 LOG(INFO) << link_name() << " restarting.";
1869 WiFiRefPtr me = this; // Make sure we don't get destructed.
1870 // Go through the manager rather than starting and stopping the device
1871 // directly so that the device can be configured with the profile.
1872 manager()->DeregisterDevice(me);
1873 manager()->RegisterDevice(me);
1874}
1875
Paul Stewartb50f0b92011-05-16 16:31:42 -07001876} // namespace shill