blob: 99c122eadccb6328f12dcd1958938d71ca48bd04 [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) {
937 bool created_hidden_service = false;
938 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
939 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
940 bool is_hidden = false;
941 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700942 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
943 << "GetGroupsWithKey failed for GetBool("
944 << flimflam::kWifiHiddenSsid
945 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800946 continue;
947 }
948 if (!is_hidden) {
949 continue;
950 }
951 string ssid_hex;
952 vector<uint8_t> ssid_bytes;
953 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
954 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700955 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
956 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800957 continue;
958 }
959 string device_address;
960 string network_mode;
961 string security;
962 // It is gross that we have to do this, but the only place we can
963 // get information about the service is from its storage name.
964 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
965 &network_mode, &security) ||
966 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700967 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
968 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800969 continue;
970 }
971 if (FindService(ssid_bytes, network_mode, security).get()) {
972 // If service already exists, we have nothing to do, since the
973 // service has already loaded its configuration from storage.
974 // This is guaranteed to happen in both cases where Load() is
975 // called on a device (via a ConfigureDevice() call on the
976 // profile):
977 // - In RegisterDevice() the Device hasn't been started yet,
978 // so it has no services registered, except for those
979 // created by previous iterations of LoadHiddenService().
980 // The latter can happen if another profile in the Manager's
981 // stack defines the same ssid/mode/security. Even this
982 // case is okay, since even if the profiles differ
983 // materially on configuration and credentials, the "right"
984 // one will be configured in the course of the
985 // RegisterService() call below.
986 // - In PushProfile(), all registered services have been
987 // introduced to the profile via ConfigureService() prior
988 // to calling ConfigureDevice() on the profile.
989 continue;
990 }
991 WiFiServiceRefPtr service(new WiFiService(control_interface(),
992 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800993 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800994 manager(),
995 this,
996 ssid_bytes,
997 network_mode,
998 security,
999 true));
1000 services_.push_back(service);
1001
1002 // By registering the service, the rest of the configuration
1003 // will be loaded from the profile into the service via ConfigureService().
1004 manager()->RegisterService(service);
1005
1006 created_hidden_service = true;
1007 }
1008
1009 // If we are idle and we created a service as a result of opening the
1010 // profile, we should initiate a scan for our new hidden SSID.
1011 if (running() && created_hidden_service &&
1012 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
1013 Scan(NULL);
1014 }
1015
1016 return created_hidden_service;
1017}
1018
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001019void WiFi::BSSAddedTask(
1020 const ::DBus::Path &path,
1021 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001022 // Note: we assume that BSSIDs are unique across endpoints. This
1023 // means that if an AP reuses the same BSSID for multiple SSIDs, we
1024 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001025 WiFiEndpointRefPtr endpoint(
1026 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -08001027 SLOG(WiFi, 1) << "Found endpoint. "
1028 << "RPC path: " << path << ", "
1029 << "ssid: " << endpoint->ssid_string() << ", "
1030 << "bssid: " << endpoint->bssid_string() << ", "
1031 << "signal: " << endpoint->signal_strength() << ", "
1032 << "security: " << endpoint->security_mode() << ", "
1033 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001034
1035 if (endpoint->ssid_string().empty()) {
1036 // Don't bother trying to find or create a Service for an Endpoint
1037 // without an SSID. We wouldn't be able to connect to it anyway.
1038 return;
1039 }
1040
mukesh agrawalb3857612012-01-18 16:23:29 -08001041 if (endpoint->ssid()[0] == 0) {
1042 // Assume that an SSID starting with NULL is bogus/misconfigured,
1043 // and filter it out.
1044 return;
1045 }
1046
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001047 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
1048 if (service) {
Wade Guthrie592ecd52012-11-12 13:12:30 -08001049 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
Darin Petkov457728b2013-01-09 09:49:08 +01001050 << " to service " << service->unique_name() << ".";
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001051 service->AddEndpoint(endpoint);
1052
1053 if (manager()->HasService(service)) {
1054 manager()->UpdateService(service);
1055 } else {
Darin Petkov4a66cc52012-06-15 10:08:29 +02001056 // Expect registered by now if >1.
1057 DCHECK_EQ(1, service->GetEndpointCount());
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001058 manager()->RegisterService(service);
1059 }
mukesh agrawale9adda12012-02-09 18:33:48 -08001060 } else {
1061 const bool hidden_ssid = false;
1062 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
mukesh agrawale9adda12012-02-09 18:33:48 -08001063 service->AddEndpoint(endpoint);
1064 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001065 }
1066
mukesh agrawale9adda12012-02-09 18:33:48 -08001067 // Do this last, to maintain the invariant that any Endpoint we
1068 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001069 //
1070 // TODO(quiche): Write test to verify correct behavior in the case
1071 // where we get multiple BSSAdded events for a single endpoint.
1072 // (Old Endpoint's refcount should fall to zero, and old Endpoint
1073 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -08001074 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -08001075 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001076}
1077
1078void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
1079 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
1080 if (i == endpoint_by_rpcid_.end()) {
1081 LOG(WARNING) << "WiFi " << link_name()
1082 << " could not find BSS " << path
1083 << " to remove.";
1084 return;
1085 }
1086
1087 WiFiEndpointRefPtr endpoint = i->second;
1088 CHECK(endpoint);
1089 endpoint_by_rpcid_.erase(i);
1090
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001091 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -08001092 CHECK(service) << "Can't find Service for Endpoint "
1093 << path << " "
1094 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -07001095 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
Darin Petkov457728b2013-01-09 09:49:08 +01001096 << " from Service " << service->unique_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001097 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001098
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001099 bool disconnect_service = !service->HasEndpoints() &&
1100 (service->IsConnecting() || service->IsConnected());
1101 bool forget_service =
1102 // Forget Services without Endpoints, except that we always keep
1103 // hidden services around. (We need them around to populate the
1104 // hidden SSIDs list.)
1105 !service->HasEndpoints() && !service->hidden_ssid();
1106 bool deregister_service =
1107 // Only deregister a Service if we're forgetting it. Otherwise,
1108 // Manager can't keep our configuration up-to-date (as Profiles
1109 // change).
1110 forget_service;
1111
1112 if (disconnect_service) {
1113 DisconnectFrom(service);
1114 }
1115
1116 if (deregister_service) {
1117 manager()->DeregisterService(service);
1118 } else {
1119 manager()->UpdateService(service);
1120 }
1121
1122 if (forget_service) {
Paul Stewart835934a2012-12-06 19:27:09 -08001123 Error unused_error;
1124 RemoveNetworkForService(service, &unused_error);
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001125 vector<WiFiServiceRefPtr>::iterator it;
1126 it = std::find(services_.begin(), services_.end(), service);
1127 if (it != services_.end()) {
1128 services_.erase(it);
1129 }
1130 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001131}
1132
Paul Stewartbc6e7392012-05-24 07:07:48 -07001133void WiFi::CertificationTask(
1134 const map<string, ::DBus::Variant> &properties) {
1135 if (!current_service_) {
1136 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1137 << " with no current service.";
1138 return;
1139 }
1140
1141 map<string, ::DBus::Variant>::const_iterator properties_it =
1142 properties.find(wpa_supplicant::kInterfacePropertyDepth);
1143 if (properties_it == properties.end()) {
1144 LOG(ERROR) << __func__ << " no depth parameter.";
1145 return;
1146 }
1147 uint32 depth = properties_it->second.reader().get_uint32();
1148 properties_it = properties.find(wpa_supplicant::kInterfacePropertySubject);
1149 if (properties_it == properties.end()) {
1150 LOG(ERROR) << __func__ << " no subject parameter.";
1151 return;
1152 }
1153 string subject(properties_it->second.reader().get_string());
1154 current_service_->AddEAPCertification(subject, depth);
1155}
1156
Paul Stewartdb0f9172012-11-30 16:48:09 -08001157void WiFi::EAPEventTask(const string &status, const string &parameter) {
1158 Service::ConnectFailure failure = Service::kFailureUnknown;
1159
1160 if (!current_service_) {
1161 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1162 << " with no current service.";
1163 return;
1164 }
1165
1166 if (status == wpa_supplicant::kEAPStatusAcceptProposedMethod) {
1167 LOG(INFO) << link_name() << ": accepted EAP method " << parameter;
1168 } else if (status == wpa_supplicant::kEAPStatusCompletion) {
1169 if (parameter == wpa_supplicant::kEAPParameterSuccess) {
1170 SLOG(WiFi, 2) << link_name() << ": EAP: "
1171 << "Completed authentication.";
Paul Stewart1369c2b2013-01-11 05:41:26 -08001172 is_eap_in_progress_ = false;
Paul Stewartdb0f9172012-11-30 16:48:09 -08001173 } else if (parameter == wpa_supplicant::kEAPParameterFailure) {
1174 // If there was a TLS error, use this instead of the generic failure.
1175 if (supplicant_tls_error_ == wpa_supplicant::kEAPStatusLocalTLSAlert) {
1176 failure = Service::kFailureEAPLocalTLS;
1177 } else if (supplicant_tls_error_ ==
1178 wpa_supplicant::kEAPStatusRemoteTLSAlert) {
1179 failure = Service::kFailureEAPRemoteTLS;
1180 } else {
1181 failure = Service::kFailureEAPAuthentication;
1182 }
1183 } else {
1184 LOG(ERROR) << link_name() << ": EAP: "
1185 << "Unexpected " << status << " parameter: " << parameter;
1186 }
1187 } else if (status == wpa_supplicant::kEAPStatusLocalTLSAlert ||
1188 status == wpa_supplicant::kEAPStatusRemoteTLSAlert) {
1189 supplicant_tls_error_ = status;
1190 } else if (status ==
1191 wpa_supplicant::kEAPStatusRemoteCertificateVerification) {
1192 if (parameter == wpa_supplicant::kEAPParameterSuccess) {
1193 SLOG(WiFi, 2) << link_name() << ": EAP: "
1194 << "Completed remote certificate verification.";
1195 } else {
1196 // wpa_supplicant doesn't currently have a verification failure
1197 // message. We will instead get a RemoteTLSAlert above.
1198 LOG(ERROR) << link_name() << ": EAP: "
1199 << "Unexpected " << status << " parameter: " << parameter;
1200 }
Paul Stewart1369c2b2013-01-11 05:41:26 -08001201 } else if (status == wpa_supplicant::kEAPStatusParameterNeeded) {
1202 LOG(ERROR) << link_name() << ": EAP: "
1203 << "Authentication due to missing authentication parameter: "
1204 << parameter;
1205 failure = Service::kFailureEAPAuthentication;
1206 } else if (status == wpa_supplicant::kEAPStatusStarted) {
1207 SLOG(WiFi, 2) << link_name() << ": EAP authentication starting.";
1208 is_eap_in_progress_ = true;
Paul Stewartdb0f9172012-11-30 16:48:09 -08001209 }
1210
1211 if (failure != Service::kFailureUnknown) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001212 // Avoid a reporting failure twice by clearing eap_in_progress_ early.
1213 is_eap_in_progress_ = false;
Paul Stewartdb0f9172012-11-30 16:48:09 -08001214 current_service_->DisconnectWithFailure(failure, NULL);
1215 }
1216}
1217
mukesh agrawal15908392011-11-16 18:29:25 +00001218void WiFi::PropertiesChangedTask(
1219 const map<string, ::DBus::Variant> &properties) {
1220 // TODO(quiche): Handle changes in other properties (e.g. signal
1221 // strength).
1222
1223 // Note that order matters here. In particular, we want to process
1224 // changes in the current BSS before changes in state. This is so
1225 // that we update the state of the correct Endpoint/Service.
1226
1227 map<string, ::DBus::Variant>::const_iterator properties_it =
1228 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
1229 if (properties_it != properties.end()) {
1230 CurrentBSSChanged(properties_it->second.reader().get_path());
1231 }
1232
1233 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
1234 if (properties_it != properties.end()) {
1235 StateChanged(properties_it->second.reader().get_string());
1236 }
1237}
1238
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001239void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001240 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001241 if (need_bss_flush_) {
1242 CHECK(supplicant_interface_proxy_ != NULL);
1243 // Compute |max_age| relative to |resumed_at_|, to account for the
1244 // time taken to scan.
1245 struct timeval now;
1246 uint32_t max_age;
1247 time_->GetTimeMonotonic(&now);
1248 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1249 supplicant_interface_proxy_->FlushBSS(max_age);
1250 need_bss_flush_ = false;
1251 }
Paul Stewartd2db2b12013-01-17 13:11:07 -08001252 SetScanPending(false);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001253 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001254}
1255
mukesh agrawal32399322011-09-01 10:53:43 -07001256void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001257 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001258 if (!enabled()) {
1259 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
1260 return;
1261 }
1262 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1263 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
1264 return;
1265 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001266 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1267 (current_service_.get() && current_service_->IsConnecting())) {
1268 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1269 return;
1270 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001271 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -07001272 scan_args[wpa_supplicant::kPropertyScanType].writer().
1273 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001274
1275 ByteArrays hidden_ssids = GetHiddenSSIDList();
1276 if (!hidden_ssids.empty()) {
1277 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
1278 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1279 }
1280
Gaurav Shahf8721ee2011-11-07 09:12:46 -08001281 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -08001282 try {
1283 supplicant_interface_proxy_->Scan(scan_args);
Paul Stewartd2db2b12013-01-17 13:11:07 -08001284 SetScanPending(true);
Ben Chan80326f32012-05-04 17:51:32 -07001285 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001286 // A scan may fail if, for example, the wpa_supplicant vanishing
1287 // notification is posted after this task has already started running.
1288 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001289 }
mukesh agrawal32399322011-09-01 10:53:43 -07001290}
1291
Paul Stewartd2db2b12013-01-17 13:11:07 -08001292void WiFi::SetScanPending(bool pending) {
1293 if (scan_pending_ != pending) {
1294 scan_pending_ = pending;
1295 adaptor()->EmitBoolChanged(flimflam::kScanningProperty, pending);
1296 }
1297}
1298
mukesh agrawal15908392011-11-16 18:29:25 +00001299void WiFi::StateChanged(const string &new_state) {
1300 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001301 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001302 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1303 << old_state << " -> " << new_state;
1304
1305 WiFiService *affected_service;
1306 // Identify the service to which the state change applies. If
1307 // |pending_service_| is non-NULL, then the state change applies to
1308 // |pending_service_|. Otherwise, it applies to |current_service_|.
1309 //
1310 // This policy is driven by the fact that the |pending_service_|
1311 // doesn't become the |current_service_| until wpa_supplicant
1312 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001313 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001314 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1315 affected_service =
1316 pending_service_.get() ? pending_service_.get() : current_service_.get();
1317 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001318 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1319 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001320 return;
1321 }
1322
Paul Stewart44663922012-07-30 11:03:03 -07001323 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
1324 if (affected_service->IsConnected()) {
1325 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001326 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001327 } else if (has_already_completed_) {
1328 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001329 } else if (AcquireIPConfigWithLeaseName(
1330 affected_service->GetStorageIdentifier())) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001331 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001332 affected_service->SetState(Service::kStateConfiguring);
1333 } else {
1334 LOG(ERROR) << "Unable to acquire DHCP config.";
1335 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001336 has_already_completed_ = true;
mukesh agrawal15908392011-11-16 18:29:25 +00001337 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1338 affected_service->SetState(Service::kStateAssociating);
1339 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1340 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1341 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1342 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1343 // Ignore transitions into these states from Completed, to avoid
1344 // bothering the user when roaming, or re-keying.
1345 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1346 affected_service->SetState(Service::kStateAssociating);
1347 // TOOD(quiche): On backwards transitions, we should probably set
1348 // a timeout for getting back into the completed state. At present,
1349 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001350 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001351 // (crosbug.com/23207)
Paul Stewart44663922012-07-30 11:03:03 -07001352 } else if (new_state == wpa_supplicant::kInterfaceStateDisconnected &&
1353 affected_service == current_service_ &&
1354 affected_service->IsConnected()) {
1355 // This means that wpa_supplicant failed in a re-connect attempt, but
1356 // may still be reconnecting. Give wpa_supplicant a limited amount of
1357 // time to transition out this condition by either connecting or changing
1358 // CurrentBSS.
1359 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001360 } else {
1361 // Other transitions do not affect Service state.
1362 //
1363 // Note in particular that we ignore a State change into
1364 // kInterfaceStateDisconnected, in favor of observing the corresponding
1365 // change in CurrentBSS.
1366 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001367}
1368
Paul Stewart1369c2b2013-01-11 05:41:26 -08001369bool WiFi::SuspectCredentials(
1370 const WiFiService &service, Service::ConnectFailure *failure) const {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001371 if (service.IsSecurityMatch(flimflam::kSecurityPsk)) {
1372 if (supplicant_state_ == wpa_supplicant::kInterfaceState4WayHandshake &&
1373 !service.has_ever_connected()) {
1374 if (failure) {
1375 *failure = Service::kFailureBadPassphrase;
1376 }
1377 return true;
1378 }
1379 } else if (service.IsSecurityMatch(flimflam::kSecurity8021x)) {
1380 if (is_eap_in_progress_ && !service.has_ever_connected()) {
1381 if (failure) {
1382 *failure = Service::kFailureEAPAuthentication;
1383 }
1384 return true;
1385 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001386 }
1387
Paul Stewart1369c2b2013-01-11 05:41:26 -08001388 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001389}
1390
mukesh agrawal7ec71312011-11-10 02:08:26 +00001391// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001392WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001393 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001394
1395 if (args.ContainsString(flimflam::kModeProperty) &&
1396 args.GetString(flimflam::kModeProperty) !=
1397 flimflam::kModeManaged) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001398 Error::PopulateAndLog(error, Error::kNotSupported,
1399 kManagerErrorUnsupportedServiceMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001400 return NULL;
1401 }
1402
1403 if (!args.ContainsString(flimflam::kSSIDProperty)) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001404 Error::PopulateAndLog(error, Error::kInvalidArguments,
1405 kManagerErrorSSIDRequired);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001406 return NULL;
1407 }
1408
1409 string ssid = args.GetString(flimflam::kSSIDProperty);
1410 if (ssid.length() < 1) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001411 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1412 kManagerErrorSSIDTooShort);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001413 return NULL;
1414 }
1415
1416 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001417 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1418 kManagerErrorSSIDTooLong);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001419 return NULL;
1420 }
1421
1422 string security_method;
1423 if (args.ContainsString(flimflam::kSecurityProperty)) {
1424 security_method = args.GetString(flimflam::kSecurityProperty);
1425 } else {
1426 security_method = flimflam::kSecurityNone;
1427 }
1428
1429 if (security_method != flimflam::kSecurityNone &&
1430 security_method != flimflam::kSecurityWep &&
1431 security_method != flimflam::kSecurityPsk &&
1432 security_method != flimflam::kSecurityWpa &&
1433 security_method != flimflam::kSecurityRsn &&
1434 security_method != flimflam::kSecurity8021x) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001435 Error::PopulateAndLog(error, Error::kNotSupported,
1436 kManagerErrorUnsupportedSecurityMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001437 return NULL;
1438 }
1439
Paul Stewart6ab23a92011-11-09 17:17:47 -08001440 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001441 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1442 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001443 } else {
1444 // If the service is not found, and the caller hasn't specified otherwise,
1445 // we assume this is is a hidden network.
1446 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001447 }
1448
Paul Stewart6ab23a92011-11-09 17:17:47 -08001449 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1450 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1451 security_method));
1452 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001453 service = new WiFiService(control_interface(),
1454 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001455 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001456 manager(),
1457 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001458 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001459 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001460 security_method,
1461 hidden_ssid);
1462 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001463 // NB: We do not register the newly created Service with the Manager.
1464 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001465 }
1466
Paul Stewart7f61e522012-03-22 11:13:45 -07001467 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1468 // have no endpoints like the one we may have just created. crosbug.com/28224
1469
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001470 return service;
1471}
1472
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001473// static
1474bool WiFi::SanitizeSSID(string *ssid) {
1475 CHECK(ssid);
1476
1477 size_t ssid_len = ssid->length();
1478 size_t i;
1479 bool changed = false;
1480
Gary Morainac1bdb42012-02-16 17:42:29 -08001481 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001482 if (!g_ascii_isprint((*ssid)[i])) {
1483 (*ssid)[i] = '?';
1484 changed = true;
1485 }
1486 }
1487
1488 return changed;
1489}
1490
Paul Stewart3c508e12012-08-09 11:40:06 -07001491void WiFi::OnLinkMonitorFailure() {
1492 // If we have never found the gateway, let's be conservative and not
1493 // do anything, in case this network topology does not have a gateway.
1494 if (!link_monitor()->IsGatewayFound()) {
1495 LOG(INFO) << "In " << __func__ << "(): "
1496 << "Skipping reassociate since gateway was never found.";
1497 return;
1498 }
1499
1500 if (!supplicant_present_) {
1501 LOG(ERROR) << "In " << __func__ << "(): "
1502 << "wpa_supplicant is not present. Cannot reassociate.";
1503 return;
1504 }
1505
1506 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001507 // This will force a transition out of connected, if we are actually
1508 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001509 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001510 // If we don't eventually get a transition back into a connected state,
1511 // there is something wrong.
1512 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001513 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1514 } catch (const DBus::Error &e) { // NOLINT
1515 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1516 return;
1517 }
1518}
1519
Arman Ugurayed8e6102012-11-29 14:47:20 -08001520bool WiFi::ShouldUseArpGateway() const {
1521 return true;
1522}
1523
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001524vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1525 vector<GeolocationInfo> objects;
1526 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1527 it != endpoint_by_rpcid_.end();
1528 ++it) {
1529 GeolocationInfo geoinfo;
1530 WiFiEndpointRefPtr endpoint = it->second;
1531 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1532 geoinfo.AddField(kGeoSignalStrengthProperty,
1533 StringPrintf("%d", endpoint->signal_strength()));
1534 geoinfo.AddField(
1535 kGeoChannelProperty,
1536 StringPrintf("%d",
1537 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1538 // TODO(gauravsh): Include age field. crosbug.com/35445
1539 objects.push_back(geoinfo);
1540 }
1541 return objects;
1542}
1543
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001544void WiFi::HelpRegisterDerivedInt32(
1545 PropertyStore *store,
1546 const string &name,
1547 int32(WiFi::*get)(Error *error),
1548 void(WiFi::*set)(const int32 &value, Error *error)) {
1549 store->RegisterDerivedInt32(
1550 name,
1551 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1552}
1553
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001554void WiFi::HelpRegisterDerivedUint16(
1555 PropertyStore *store,
1556 const string &name,
1557 uint16(WiFi::*get)(Error *error),
1558 void(WiFi::*set)(const uint16 &value, Error *error)) {
1559 store->RegisterDerivedUint16(
1560 name,
1561 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1562}
1563
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001564void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001565 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001566 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001567
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001568 // We want to flush the BSS cache, but we don't want to conflict
1569 // with a running scan or an active connection attempt. So record
1570 // the need to flush, and take care of flushing when the next scan
1571 // completes.
1572 //
1573 // Note that supplicant will automatically expire old cache
1574 // entries (after, e.g., a BSS is not found in two consecutive
1575 // scans). However, our explicit flush accelerates re-association
1576 // in cases where a BSS disappeared while we were asleep. (See,
1577 // e.g. WiFiRoaming.005SuspendRoam.)
1578 time_->GetTimeMonotonic(&resumed_at_);
1579 need_bss_flush_ = true;
1580
1581 if (!scan_pending_ && IsIdle()) {
1582 // Not scanning/connecting/connected, so let's get things rolling.
1583 Scan(NULL);
1584 } else {
1585 SLOG(WiFi, 1) << __func__
1586 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001587 }
1588}
1589
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001590void WiFi::OnConnected() {
1591 Device::OnConnected();
1592 EnableHighBitrates();
1593}
1594
Paul Stewarte369ece2012-05-22 09:11:03 -07001595void WiFi::RestartFastScanAttempts() {
1596 fast_scans_remaining_ = kNumFastScanAttempts;
1597 StartScanTimer();
1598}
1599
mukesh agrawalb66c6462012-05-07 11:45:25 -07001600void WiFi::StartScanTimer() {
1601 if (scan_interval_seconds_ == 0) {
1602 StopScanTimer();
1603 return;
1604 }
1605 scan_timer_callback_.Reset(
1606 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001607 // Repeat the first few scans after disconnect relatively quickly so we
1608 // have reasonable trust that no APs we are looking for are present.
1609 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1610 fast_scans_remaining_ > 0 ?
1611 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001612}
1613
1614void WiFi::StopScanTimer() {
1615 scan_timer_callback_.Cancel();
1616}
1617
1618void WiFi::ScanTimerHandler() {
1619 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1620 if (IsIdle() && !scan_pending_) {
1621 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001622 if (fast_scans_remaining_ > 0) {
1623 --fast_scans_remaining_;
1624 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001625 }
1626 StartScanTimer();
1627}
1628
Paul Stewart2b05e622012-07-13 20:38:44 -07001629void WiFi::StartPendingTimer() {
1630 pending_timeout_callback_.Reset(
1631 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1632 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1633 kPendingTimeoutSeconds * 1000);
1634}
1635
1636void WiFi::StopPendingTimer() {
1637 pending_timeout_callback_.Cancel();
1638}
1639
1640void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001641 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001642 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001643 if (service) {
1644 service->SetState(Service::kStateAssociating);
1645 StartPendingTimer();
1646 } else if (pending_service_) {
1647 StopPendingTimer();
1648 }
1649 pending_service_ = service;
1650}
1651
1652void WiFi::PendingTimeoutHandler() {
1653 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1654 CHECK(pending_service_);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001655 pending_service_->DisconnectWithFailure(Service::kFailureOutOfRange, NULL);
Paul Stewart2b05e622012-07-13 20:38:44 -07001656}
1657
Paul Stewart44663922012-07-30 11:03:03 -07001658void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001659 if (!reconnect_timeout_callback_.IsCancelled()) {
1660 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1661 << ": reconnect timer already running.";
1662 return;
1663 }
Paul Stewart44663922012-07-30 11:03:03 -07001664 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1665 reconnect_timeout_callback_.Reset(
1666 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1667 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1668 kReconnectTimeoutSeconds * 1000);
1669}
1670
1671void WiFi::StopReconnectTimer() {
1672 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1673 reconnect_timeout_callback_.Cancel();
1674}
1675
1676void WiFi::ReconnectTimeoutHandler() {
1677 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001678 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001679 CHECK(current_service_);
1680 current_service_->SetFailureSilent(Service::kFailureConnect);
1681 DisconnectFrom(current_service_);
1682}
1683
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001684void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1685 LOG(INFO) << "WPA supplicant appeared.";
1686 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001687 // Restart the WiFi device if it's started already. This will reset the
1688 // state and connect the device to the new WPA supplicant instance.
1689 if (enabled()) {
1690 Restart();
1691 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001692 return;
1693 }
1694 supplicant_present_ = true;
1695 ConnectToSupplicant();
1696}
1697
1698void WiFi::OnSupplicantVanish() {
1699 LOG(INFO) << "WPA supplicant vanished.";
1700 if (!supplicant_present_) {
1701 return;
1702 }
1703 supplicant_present_ = false;
1704 // Restart the WiFi device if it's started already. This will effectively
1705 // suspend the device until the WPA supplicant reappears.
1706 if (enabled()) {
1707 Restart();
1708 }
1709}
1710
Paul Stewart5581d072012-12-17 17:30:20 -08001711void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1712 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1713 if (!supplicant_process_proxy_.get()) {
1714 SLOG(WiFi, 2) << "Suplicant process proxy not present.";
1715 return;
1716 }
1717 string current_level;
1718 try {
1719 current_level = supplicant_process_proxy_->GetDebugLevel();
1720 } catch (const DBus::Error &e) { // NOLINT
1721 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1722 return;
1723 }
1724
1725 if (current_level != wpa_supplicant::kDebugLevelInfo &&
1726 current_level != wpa_supplicant::kDebugLevelDebug) {
1727 SLOG(WiFi, 2) << "WiFi debug level is currently "
1728 << current_level
1729 << "; assuming that it is being controlled elsewhere.";
1730 return;
1731 }
1732 string new_level = enabled ? wpa_supplicant::kDebugLevelDebug :
1733 wpa_supplicant::kDebugLevelInfo;
1734
1735 if (new_level == current_level) {
1736 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1737 << current_level;
1738 return;
1739 }
1740
1741 try {
1742 supplicant_process_proxy_->SetDebugLevel(new_level);
1743 } catch (const DBus::Error &e) { // NOLINT
1744 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1745 }
1746}
1747
Paul Stewarta47c3c62012-12-18 12:14:29 -08001748void WiFi::SetConnectionDebugging(bool enabled) {
1749 if (is_debugging_connection_ == enabled) {
1750 return;
1751 }
1752 OnWiFiDebugScopeChanged(
1753 enabled ||
1754 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1755 is_debugging_connection_ = enabled;
1756}
1757
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001758void WiFi::ConnectToSupplicant() {
1759 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1760 << " supplicant: "
1761 << (supplicant_present_ ? "present" : "absent")
1762 << " proxy: "
1763 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1764 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1765 return;
1766 }
1767 supplicant_process_proxy_.reset(
1768 proxy_factory_->CreateSupplicantProcessProxy(
1769 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001770 OnWiFiDebugScopeChanged(
1771 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001772 ::DBus::Path interface_path;
1773 try {
1774 map<string, DBus::Variant> create_interface_args;
1775 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
1776 append_string(link_name().c_str());
1777 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
1778 append_string(wpa_supplicant::kDriverNL80211);
1779 create_interface_args[
1780 wpa_supplicant::kInterfacePropertyConfigFile].writer().
Darin Petkov60ceaf32012-10-18 10:36:01 +02001781 append_string(kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001782 interface_path =
1783 supplicant_process_proxy_->CreateInterface(create_interface_args);
1784 } catch (const DBus::Error &e) { // NOLINT
1785 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
1786 interface_path =
1787 supplicant_process_proxy_->GetInterface(link_name());
1788 // TODO(quiche): Is it okay to crash here, if device is missing?
1789 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001790 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1791 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001792 }
1793 }
1794
1795 supplicant_interface_proxy_.reset(
1796 proxy_factory_->CreateSupplicantInterfaceProxy(
1797 this, interface_path, wpa_supplicant::kDBusAddr));
1798
1799 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1800 IFF_UP);
1801 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1802
1803 // Clear out any networks that might previously have been configured
1804 // for this interface.
1805 supplicant_interface_proxy_->RemoveAllNetworks();
1806
1807 // Flush interface's BSS cache, so that we get BSSAdded signals for
1808 // all BSSes (not just new ones since the last scan).
1809 supplicant_interface_proxy_->FlushBSS(0);
1810
1811 try {
1812 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1813 // with RADIUS servers that respond strangely to such requests.
1814 // crosbug.com/25630
1815 supplicant_interface_proxy_->SetFastReauth(false);
1816 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001817 LOG(ERROR) << "Failed to disable fast_reauth. "
1818 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001819 }
1820
1821 try {
1822 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1823 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1824 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001825 LOG(ERROR) << "Failed to set scan_interval. "
1826 << "May be running an older version of wpa_supplicant.";
1827 }
1828
1829 try {
1830 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1831 } catch (const DBus::Error &e) { // NOLINT
1832 LOG(ERROR) << "Failed to disable high bitrates. "
1833 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001834 }
1835
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001836 Scan(NULL);
1837 StartScanTimer();
1838}
1839
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001840void WiFi::EnableHighBitrates() {
1841 LOG(INFO) << "Enabling high bitrates.";
1842 try {
1843 supplicant_interface_proxy_->EnableHighBitrates();
1844 } catch (const DBus::Error &e) { // NOLINT
1845 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1846 }
1847}
1848
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001849void WiFi::Restart() {
1850 LOG(INFO) << link_name() << " restarting.";
1851 WiFiRefPtr me = this; // Make sure we don't get destructed.
1852 // Go through the manager rather than starting and stopping the device
1853 // directly so that the device can be configured with the profile.
1854 manager()->DeregisterDevice(me);
1855 manager()->RegisterDevice(me);
1856}
1857
Paul Stewartb50f0b92011-05-16 16:31:42 -07001858} // namespace shill