blob: 3f1021dd3aab914b4a340f03bae9f16cca09985d [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 Stewartb50f0b92011-05-16 16:31:42 -070014#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070015#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016
Eric Shienbrood3e20a232012-02-16 11:35:56 -050017#include <base/bind.h>
mukesh agrawal15908392011-11-16 18:29:25 +000018#include <base/stringprintf.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070019#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070020#include <chromeos/dbus/service_constants.h>
mukesh agrawal16bc1b82012-02-09 18:38:26 -080021#include <glib.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070022
Wade Guthried6153612012-08-23 11:36:14 -070023#include "shill/config80211.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070024#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080025#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070026#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070027#include "shill/error.h"
Gaurav Shah6d2c72d2012-10-16 16:30:44 -070028#include "shill/geolocation_info.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070029#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070030#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070031#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070032#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000033#include "shill/metrics.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080034#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070035#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050036#include "shill/rtnl_handler.h"
Paul Stewart5581d072012-12-17 17:30:20 -080037#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080038#include "shill/shill_time.h"
Paul Stewart735eab52013-03-29 09:19:23 -070039#include "shill/supplicant_eap_state_handler.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070040#include "shill/supplicant_interface_proxy_interface.h"
Paul Stewart835934a2012-12-06 19:27:09 -080041#include "shill/supplicant_network_proxy_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070042#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080043#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070044#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080045#include "shill/wifi_provider.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070046#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070047#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070048
Eric Shienbrood3e20a232012-02-16 11:35:56 -050049using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000050using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070051using std::map;
mukesh agrawalab87ea42011-05-18 11:44:49 -070052using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070053using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070054
Paul Stewartb50f0b92011-05-16 16:31:42 -070055namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070056
57// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080058const char *WiFi::kDefaultBgscanMethod =
Paul Stewart0654ece2013-03-26 15:21:26 -070059 WPASupplicant::kNetworkBgscanMethodSimple;
mukesh agrawal4d0401c2012-01-06 16:05:31 -080060const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
61const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
62const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020063// Scan interval while connected.
64const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal5c05b292012-03-07 10:12:52 -080065// Age (in seconds) beyond which a BSS cache entry will not be preserved,
66// across a suspend/resume.
67const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000068const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070069const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070070const int WiFi::kNumFastScanAttempts = 3;
71const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070072const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070073const int WiFi::kReconnectTimeoutSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070074
Paul Stewartb50f0b92011-05-16 16:31:42 -070075WiFi::WiFi(ControlInterface *control_interface,
76 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080077 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070078 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070079 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080080 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070081 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070082 : Device(control_interface,
83 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080084 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070085 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070086 link,
Chris Masone626719f2011-08-18 16:58:48 -070087 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080088 interface_index,
89 Technology::kWifi),
Paul Stewart3c504012013-01-17 17:49:58 -080090 provider_(manager->wifi_provider()),
Eric Shienbrood9a245532012-03-07 14:20:39 -050091 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -070092 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -080093 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +020094 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +000095 supplicant_state_(kInterfaceStateUnknown),
96 supplicant_bss_("(unknown)"),
mukesh agrawal5c05b292012-03-07 10:12:52 -080097 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +020098 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -070099 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700100 has_already_completed_(false),
Paul Stewarta47c3c62012-12-18 12:14:29 -0800101 is_debugging_connection_(false),
Paul Stewart735eab52013-03-29 09:19:23 -0700102 eap_state_handler_(new SupplicantEAPStateHandler()),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800103 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
104 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700105 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800106 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700107 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200108 store->RegisterDerivedString(
109 flimflam::kBgscanMethodProperty,
110 StringAccessor(
111 // TODO(petkov): CustomMappedAccessor is used for convenience because
112 // it provides a way to define a custom clearer (unlike
113 // CustomAccessor). We need to implement a fully custom accessor with
114 // no extra argument.
115 new CustomMappedAccessor<WiFi, string, int>(this,
116 &WiFi::ClearBgscanMethod,
117 &WiFi::GetBgscanMethod,
118 &WiFi::SetBgscanMethod,
119 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800120 HelpRegisterDerivedUint16(store,
121 flimflam::kBgscanShortIntervalProperty,
122 &WiFi::GetBgscanShortInterval,
123 &WiFi::SetBgscanShortInterval);
124 HelpRegisterDerivedInt32(store,
125 flimflam::kBgscanSignalThresholdProperty,
126 &WiFi::GetBgscanSignalThreshold,
127 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700128
Chris Masoneb925cc82011-06-22 15:39:57 -0700129 // TODO(quiche): Decide if scan_pending_ is close enough to
130 // "currently scanning" that we don't care, or if we want to track
131 // scan pending/currently scanning/no scan scheduled as a tri-state
132 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700133 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800134 HelpRegisterDerivedUint16(store,
135 flimflam::kScanIntervalProperty,
136 &WiFi::GetScanInterval,
137 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800138 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
139 ScopeLogger::kWiFi,
140 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -0700141 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700142}
143
mukesh agrawalaf571952011-07-14 14:31:12 -0700144WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700145
Eric Shienbrood9a245532012-03-07 14:20:39 -0500146void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200147 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
148 if (enabled()) {
149 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700150 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500151 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200152 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500153 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200154 }
155 if (on_supplicant_appear_.IsCancelled()) {
156 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
157 // device instance.
158 on_supplicant_appear_.Reset(
159 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
160 on_supplicant_vanish_.Reset(
161 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
Paul Stewart0654ece2013-03-26 15:21:26 -0700162 manager()->dbus_manager()->WatchName(WPASupplicant::kDBusAddr,
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200163 on_supplicant_appear_.callback(),
164 on_supplicant_vanish_.callback());
165 }
166 // Connect to WPA supplicant if it's already present. If not, we'll connect to
167 // it when it appears.
168 ConnectToSupplicant();
Wade Guthried6153612012-08-23 11:36:14 -0700169 Config80211 *config80211 = Config80211::GetInstance();
170 if (config80211) {
171 config80211->SetWifiState(Config80211::kWifiUp);
172 }
mukesh agrawalab87ea42011-05-18 11:44:49 -0700173}
174
Eric Shienbrood9a245532012-03-07 14:20:39 -0500175void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700176 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200177 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700178 StopScanTimer();
Paul Stewart3c504012013-01-17 17:49:58 -0800179 for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
180 it != endpoint_by_rpcid_.end(); ++it) {
181 provider_->OnEndpointRemoved(it->second);
182 }
mukesh agrawal15908392011-11-16 18:29:25 +0000183 endpoint_by_rpcid_.clear();
Paul Stewart3c504012013-01-17 17:49:58 -0800184 for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
185 it != rpcid_by_service_.end(); ++it) {
186 RemoveNetwork(it->second);
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700187 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700188 rpcid_by_service_.clear();
Paul Stewart549d44c2012-07-03 12:40:25 -0700189 supplicant_interface_proxy_.reset(); // breaks a reference cycle
190 // TODO(quiche): Remove interface from supplicant.
191 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800192 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000193 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800194 is_debugging_connection_ = false;
Paul Stewartd2db2b12013-01-17 13:11:07 -0800195 SetScanPending(false);
Paul Stewart2b05e622012-07-13 20:38:44 -0700196 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700197 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700198
Eric Shienbrood9a245532012-03-07 14:20:39 -0500199 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
200 if (error)
201 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700202 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700203
Ben Chanfad4a0b2012-04-18 15:49:59 -0700204 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
205 << (supplicant_process_proxy_.get() ?
206 "is set." : "is not set.");
207 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
208 << (supplicant_interface_proxy_.get() ?
209 "is set." : "is not set.");
210 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
211 << (pending_service_.get() ? "is set." : "is not set.");
212 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
213 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800214}
215
mukesh agrawal1830fa12011-09-26 14:31:40 -0700216void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700217 LOG(INFO) << __func__;
218
mukesh agrawal7ec71312011-11-10 02:08:26 +0000219 // Needs to send a D-Bus message, but may be called from D-Bus
220 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700221 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500222 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700223}
224
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000225void WiFi::BSSAdded(const ::DBus::Path &path,
226 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500227 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000228 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500229 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
230 weak_ptr_factory_.GetWeakPtr(),
231 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000232}
233
234void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500235 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000236 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500237 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
238 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700239}
240
Paul Stewartbc6e7392012-05-24 07:07:48 -0700241void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
242 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
243 weak_ptr_factory_.GetWeakPtr(), properties));
244}
245
Paul Stewartdb0f9172012-11-30 16:48:09 -0800246void WiFi::EAPEvent(const string &status, const string &parameter) {
247 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
248 weak_ptr_factory_.GetWeakPtr(),
249 status,
250 parameter));
251}
252
mukesh agrawal7ec71312011-11-10 02:08:26 +0000253void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200254 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000255 // Called from D-Bus signal handler, but may need to send a D-Bus
256 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500257 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
258 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000259}
260
mukesh agrawalb54601c2011-06-07 17:39:22 -0700261void WiFi::ScanDone() {
262 LOG(INFO) << __func__;
263
mukesh agrawal7ec71312011-11-10 02:08:26 +0000264 // Defer handling of scan result processing, because that processing
265 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700266 // registration can't be done in the context of a D-Bus signal
267 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500268 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
269 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700270}
271
mukesh agrawal6e277772011-09-29 15:04:23 -0700272void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000273 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800274 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700275 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700276
mukesh agrawal7ec71312011-11-10 02:08:26 +0000277 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000278 if (pending_service_ && pending_service_ == service) {
279 // TODO(quiche): Return an error to the caller. crosbug.com/23832
Darin Petkov457728b2013-01-09 09:49:08 +0100280 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
281 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000282 << ", which is already pending.";
283 return;
284 }
285
286 if (pending_service_ && pending_service_ != service) {
287 DisconnectFrom(pending_service_);
288 }
mukesh agrawal32399322011-09-01 10:53:43 -0700289
Paul Stewart835934a2012-12-06 19:27:09 -0800290 Error unused_error;
291 network_path = FindNetworkRpcidForService(service, &unused_error);
292 if (network_path.empty()) {
293 try {
294 const uint32_t scan_ssid = 1; // "True": Use directed probe.
Paul Stewart0654ece2013-03-26 15:21:26 -0700295 service_params[WPASupplicant::kNetworkPropertyScanSSID].writer().
Paul Stewart835934a2012-12-06 19:27:09 -0800296 append_uint32(scan_ssid);
297 AppendBgscan(service, &service_params);
298 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
299 CHECK(!network_path.empty()); // No DBus path should be empty.
300 rpcid_by_service_[service] = network_path;
301 } catch (const DBus::Error &e) { // NOLINT
302 LOG(ERROR) << "exception while adding network: " << e.what();
303 return;
304 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700305 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700306
Paul Stewarta47c3c62012-12-18 12:14:29 -0800307 if (service->HasRecentConnectionIssues()) {
308 SetConnectionDebugging(true);
309 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700310 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700311 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000312 CHECK(current_service_.get() != pending_service_.get());
313
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700314 // SelectService here (instead of in LinkEvent, like Ethernet), so
315 // that, if we fail to bring up L2, we can attribute failure correctly.
316 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000317 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700318 // reconsider if this is the right place to change the selected service.
319 // see discussion in crosbug.com/20191.
320 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000321}
322
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000323void WiFi::DisconnectFrom(WiFiService *service) {
324 if (service != current_service_ && service != pending_service_) {
325 // TODO(quiche): Once we have asynchronous reply support, we should
326 // generate a D-Bus error here. (crosbug.com/23832)
327 LOG(WARNING) << "In " << __func__ << "(): "
328 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100329 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000330 << " which is neither current nor pending";
331 return;
332 }
333
334 if (pending_service_ && service != pending_service_) {
335 // TODO(quiche): Once we have asynchronous reply support, we should
336 // generate a D-Bus error here. (crosbug.com/23832)
337 LOG(WARNING) << "In " << __func__ << "(): "
338 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100339 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000340 << " which is not the pending service.";
341 return;
342 }
343
344 if (!pending_service_ && service != current_service_) {
345 // TODO(quiche): Once we have asynchronous reply support, we should
346 // generate a D-Bus error here. (crosbug.com/23832)
347 LOG(WARNING) << "In " << __func__ << "(): "
348 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100349 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000350 << " which is not the current service.";
351 return;
352 }
353
Paul Stewartff96a842012-08-13 15:59:10 -0700354 if (pending_service_) {
355 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
356 // on this to drive the service state back to idle. Do that here.
357 pending_service_->SetState(Service::kStateIdle);
358 }
359
Paul Stewart2b05e622012-07-13 20:38:44 -0700360 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700361 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700362
363 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700364 LOG(ERROR) << "In " << __func__ << "(): "
365 << "wpa_supplicant is not present; silently resetting "
366 << "current_service_.";
367 if (current_service_ == selected_service()) {
368 DropConnection();
369 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700370 current_service_ = NULL;
371 return;
372 }
373
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000374 try {
375 supplicant_interface_proxy_->Disconnect();
376 // We'll call RemoveNetwork and reset |current_service_| after
377 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700378 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000379 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700380 // So effect changes immediately. For instance, this can happen when
381 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800382 Error unused_error;
383 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700384 if (service == selected_service()) {
385 DropConnection();
386 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000387 current_service_ = NULL;
388 }
389
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800390 CHECK(current_service_ == NULL ||
391 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000392}
393
Paul Stewart835934a2012-12-06 19:27:09 -0800394bool WiFi::DisableNetwork(const ::DBus::Path &network) {
395 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
396 proxy_factory_->CreateSupplicantNetworkProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -0700397 network, WPASupplicant::kDBusAddr));
Paul Stewart835934a2012-12-06 19:27:09 -0800398 try {
399 supplicant_network_proxy->SetEnabled(false);
400 } catch (const DBus::Error &e) { // NOLINT
401 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
402 return false;
403 }
404 return true;
405}
406
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700407bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
408 try {
409 supplicant_interface_proxy_->RemoveNetwork(network);
410 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700411 // RemoveNetwork can fail with three different errors.
412 //
413 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
414 // already removed the network object, so return true as if
415 // RemoveNetwork removes the network object successfully.
416 //
417 // As shill always passes a valid network object path, RemoveNetwork
418 // should not fail with an InvalidArgs error. Return false in such case
419 // as something weird may have happened. Similarly, return false in case
420 // of an UnknownError.
Paul Stewart0654ece2013-03-26 15:21:26 -0700421 if (strcmp(e.name(), WPASupplicant::kErrorNetworkUnknown) != 0) {
Ben Chan381fdcc2012-10-14 21:10:36 -0700422 return false;
423 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700424 }
425 return true;
426}
427
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000428bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800429 return !current_service_ && !pending_service_;
430}
431
Paul Stewart835934a2012-12-06 19:27:09 -0800432void WiFi::ClearCachedCredentials(const WiFiService *service) {
433 Error unused_error;
434 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800435}
436
Paul Stewart3c504012013-01-17 17:49:58 -0800437void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700438 provider_->OnEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800439}
440
Darin Petkov4a66cc52012-06-15 10:08:29 +0200441void WiFi::AppendBgscan(WiFiService *service,
442 map<string, DBus::Variant> *service_params) const {
443 int scan_interval = kBackgroundScanIntervalSeconds;
444 string method = bgscan_method_;
445 if (method.empty()) {
446 // If multiple APs are detected for this SSID, configure the default method.
447 // Otherwise, disable background scanning completely.
448 if (service->GetEndpointCount() > 1) {
449 method = kDefaultBgscanMethod;
450 } else {
451 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
452 return;
453 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700454 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
Christopher Wileya998df22012-07-11 15:14:55 -0700455 LOG(INFO) << "Background scan disabled -- chose None method.";
456 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200457 } else {
458 // If the background scan method was explicitly specified, honor the
459 // configured background scan interval.
460 scan_interval = scan_interval_seconds_;
461 }
462 DCHECK(!method.empty());
463 string config_string = StringPrintf("%s:%d:%d:%d",
464 method.c_str(),
465 bgscan_short_interval_seconds_,
466 bgscan_signal_threshold_dbm_,
467 scan_interval);
468 LOG(INFO) << "Background scan: " << config_string;
Paul Stewart0654ece2013-03-26 15:21:26 -0700469 (*service_params)[WPASupplicant::kNetworkPropertyBgscan].writer()
Darin Petkov4a66cc52012-06-15 10:08:29 +0200470 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800471}
472
Darin Petkov4a66cc52012-06-15 10:08:29 +0200473string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
474 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
475}
476
477void WiFi::SetBgscanMethod(
478 const int &/*argument*/, const string &method, Error *error) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700479 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
480 method != WPASupplicant::kNetworkBgscanMethodLearn &&
481 method != WPASupplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800482 const string error_message =
483 StringPrintf("Unrecognized bgscan method %s", method.c_str());
484 LOG(WARNING) << error_message;
485 error->Populate(Error::kInvalidArguments, error_message);
486 return;
487 }
488
489 bgscan_method_ = method;
490 // We do not update kNetworkPropertyBgscan for |pending_service_| or
491 // |current_service_|, because supplicant does not allow for
492 // reconfiguration without disconnect and reconnect.
493}
494
495void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
496 bgscan_short_interval_seconds_ = seconds;
497 // We do not update kNetworkPropertyBgscan for |pending_service_| or
498 // |current_service_|, because supplicant does not allow for
499 // reconfiguration without disconnect and reconnect.
500}
501
502void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
503 bgscan_signal_threshold_dbm_ = dbm;
504 // We do not update kNetworkPropertyBgscan for |pending_service_| or
505 // |current_service_|, because supplicant does not allow for
506 // reconfiguration without disconnect and reconnect.
507}
508
509void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
510 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700511 if (running()) {
512 StartScanTimer();
513 }
514 // The scan interval affects both foreground scans (handled by
515 // |scan_timer_callback_|), and background scans (handled by
516 // supplicant). However, we do not update |pending_service_| or
517 // |current_service_|, because supplicant does not allow for
518 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800519}
520
Darin Petkov4a66cc52012-06-15 10:08:29 +0200521void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
522 bgscan_method_.clear();
523}
524
mukesh agrawal15908392011-11-16 18:29:25 +0000525void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700526 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
527 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000528 supplicant_bss_ = new_bss;
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700529 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700530
531 // Any change in CurrentBSS means supplicant is actively changing our
532 // connectivity. We no longer need to track any previously pending
533 // reconnect.
534 StopReconnectTimer();
535
Paul Stewart0654ece2013-03-26 15:21:26 -0700536 if (new_bss == WPASupplicant::kCurrentBSSNull) {
mukesh agrawal15908392011-11-16 18:29:25 +0000537 HandleDisconnect();
Paul Stewart3c504012013-01-17 17:49:58 -0800538 if (!provider_->GetHiddenSSIDList().empty()) {
mukesh agrawalb66c6462012-05-07 11:45:25 -0700539 // Before disconnecting, wpa_supplicant probably scanned for
540 // APs. So, in the normal case, we defer to the timer for the next scan.
541 //
542 // However, in the case of hidden SSIDs, supplicant knows about
543 // at most one of them. (That would be the hidden SSID we were
544 // connected to, if applicable.)
545 //
546 // So, in this case, we initiate an immediate scan. This scan
547 // will include the hidden SSIDs we know about (up to the limit of
548 // kScanMAxSSIDsPerScan).
549 //
550 // We may want to reconsider this immediate scan, if/when shill
551 // takes greater responsibility for scanning (vs. letting
552 // supplicant handle most of it).
553 Scan(NULL);
554 }
mukesh agrawal15908392011-11-16 18:29:25 +0000555 } else {
556 HandleRoam(new_bss);
557 }
558
Paul Stewart735eab52013-03-29 09:19:23 -0700559 // Reset the EAP handler only after calling HandleDisconnect() above
560 // so our EAP state could be used to detect a failed authentication.
561 eap_state_handler_->Reset();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800562
Paul Stewart2b05e622012-07-13 20:38:44 -0700563 // If we are selecting a new service, or if we're clearing selection
564 // of a something other than the pending service, call SelectService.
565 // Otherwise skip SelectService, since this will cause the pending
566 // service to be marked as Idle.
567 if (current_service_ || selected_service() != pending_service_) {
568 SelectService(current_service_);
569 }
570
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000571 // Invariant check: a Service can either be current, or pending, but
572 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000573 CHECK(current_service_.get() != pending_service_.get() ||
574 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800575
576 // If we are no longer debugging a problematic WiFi connection, return
577 // to the debugging level indicated by the WiFi debugging scope.
578 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
579 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
580 SetConnectionDebugging(false);
581 }
mukesh agrawal15908392011-11-16 18:29:25 +0000582}
583
584void WiFi::HandleDisconnect() {
585 // Identify the affected service. We expect to get a disconnect
586 // event when we fall off a Service that we were connected
587 // to. However, we also allow for the case where we get a disconnect
588 // event while attempting to connect from a disconnected state.
589 WiFiService *affected_service =
590 current_service_.get() ? current_service_.get() : pending_service_.get();
591
592 current_service_ = NULL;
593 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700594 SLOG(WiFi, 2) << "WiFi " << link_name()
595 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000596 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700597 }
598 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700599 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700600 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000601 }
602
Paul Stewart835934a2012-12-06 19:27:09 -0800603 Error error;
604 if (!DisableNetworkForService(affected_service, &error)) {
605 if (error.type() == Error::kNotFound) {
606 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100607 << " (or failed to connect to) service "
608 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800609 << "but could not find supplicant network to disable.";
610 } else {
611 LOG(FATAL) << "DisableNetwork failed.";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700612 }
mukesh agrawal15908392011-11-16 18:29:25 +0000613 }
614
Ben Chanfad4a0b2012-04-18 15:49:59 -0700615 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100616 << " (or failed to connect to) service "
617 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800618 Service::ConnectFailure failure;
619 if (SuspectCredentials(*affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700620 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700621 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800622 affected_service->SetFailure(failure);
623 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
624 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700625 } else {
626 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700627 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800628 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000629 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000630
631 if (affected_service == pending_service_.get()) {
632 // The attempt to connect to |pending_service_| failed. Clear
633 // |pending_service_|, to indicate we're no longer in the middle
634 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700635 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000636 } else if (pending_service_.get()) {
637 // We've attributed the disconnection to what was the
638 // |current_service_|, rather than the |pending_service_|.
639 //
640 // If we're wrong about that (i.e. supplicant reported this
641 // CurrentBSS change after attempting to connect to
642 // |pending_service_|), we're depending on supplicant to retry
643 // connecting to |pending_service_|, and delivering another
644 // CurrentBSS change signal in the future.
645 //
646 // Log this fact, to help us debug (in case our assumptions are
647 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100648 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
649 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700650 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000651 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700652
653 // If we disconnect, initially scan at a faster frequency, to make sure
654 // we've found all available APs.
655 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000656}
657
658// We use the term "Roam" loosely. In particular, we include the case
659// where we "Roam" to a BSS from the disconnected state.
660void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
661 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
662 if (endpoint_it == endpoint_by_rpcid_.end()) {
663 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
664 << new_bss;
665 return;
666 }
667
Paul Stewart3c504012013-01-17 17:49:58 -0800668 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
669 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000670 if (!service.get()) {
671 LOG(WARNING) << "WiFi " << link_name()
672 << " could not find Service for Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800673 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000674 << " (service will be unchanged)";
675 return;
676 }
677
Ben Chanfad4a0b2012-04-18 15:49:59 -0700678 SLOG(WiFi, 2) << "WiFi " << link_name()
Paul Stewart3c504012013-01-17 17:49:58 -0800679 << " roamed to Endpoint " << endpoint->bssid_string()
680 << " " << LogSSID(endpoint->ssid_string());
mukesh agrawal15908392011-11-16 18:29:25 +0000681
Paul Stewart3c504012013-01-17 17:49:58 -0800682 service->NotifyCurrentEndpoint(endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000683
mukesh agrawal15908392011-11-16 18:29:25 +0000684 if (pending_service_.get() &&
685 service.get() != pending_service_.get()) {
686 // The Service we've roamed on to is not the one we asked for.
687 // We assume that this is transient, and that wpa_supplicant
688 // is trying / will try to connect to |pending_service_|.
689 //
690 // If it succeeds, we'll end up back here, but with |service|
691 // pointing at the same service as |pending_service_|.
692 //
693 // If it fails, we'll process things in HandleDisconnect.
694 //
695 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700696 SLOG(WiFi, 2) << "WiFi " << link_name()
697 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800698 << endpoint->bssid_string()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700699 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100700 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000701
702 // Sanity check: if we didn't roam onto |pending_service_|, we
703 // should still be on |current_service_|.
704 if (service.get() != current_service_.get()) {
705 LOG(WARNING) << "WiFi " << link_name()
706 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800707 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000708 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100709 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000710 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100711 << (current_service_ ?
712 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000713 "(NULL)");
714 // Although we didn't expect to get here, we should keep
715 // |current_service_| in sync with what supplicant has done.
716 current_service_ = service;
717 }
718 return;
719 }
720
721 if (pending_service_.get()) {
722 // We assume service.get() == pending_service_.get() here, because
723 // of the return in the previous if clause.
724 //
725 // Boring case: we've connected to the service we asked
726 // for. Simply update |current_service_| and |pending_service_|.
727 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700728 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000729 return;
730 }
731
732 // |pending_service_| was NULL, so we weren't attempting to connect
733 // to a new Service. Sanity check that we're still on
734 // |current_service_|.
735 if (service.get() != current_service_.get()) {
736 LOG(WARNING)
737 << "WiFi " << link_name()
738 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800739 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000740 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000741 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100742 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000743 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000744 // We didn't expect to be here, but let's cope as well as we
745 // can. Update |current_service_| to keep it in sync with
746 // supplicant.
747 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700748
749 // If this service isn't already marked as actively connecting (likely,
750 // since this service is a bit of a surprise) set the service as
751 // associating.
752 if (!current_service_->IsConnecting()) {
753 current_service_->SetState(Service::kStateAssociating);
754 }
755
mukesh agrawal15908392011-11-16 18:29:25 +0000756 return;
757 }
758
759 // At this point, we know that |pending_service_| was NULL, and that
760 // we're still on |current_service_|. This is the most boring case
761 // of all, because there's no state to update here.
762 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700763}
764
Paul Stewart835934a2012-12-06 19:27:09 -0800765string WiFi::FindNetworkRpcidForService(
766 const WiFiService *service, Error *error) {
767 ReverseServiceMap::const_iterator rpcid_it =
768 rpcid_by_service_.find(service);
769 if (rpcid_it == rpcid_by_service_.end()) {
770 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100771 StringPrintf(
772 "WiFi %s cannot find supplicant network rpcid for service %s",
773 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -0800774 // There are contexts where this is not an error, such as when a service
775 // is clearing whatever cached credentials may not exist.
776 SLOG(WiFi, 2) << error_message;
777 if (error) {
778 error->Populate(Error::kNotFound, error_message);
779 }
780 return "";
781 }
782
783 return rpcid_it->second;
784}
785
786bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
787 string rpcid = FindNetworkRpcidForService(service, error);
788 if (rpcid.empty()) {
789 // Error is already populated.
790 return false;
791 }
792
793 if (!DisableNetwork(rpcid)) {
794 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100795 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800796 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100797 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800798 rpcid.c_str());
799 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
800
801 // Make sure that such errored networks are removed, so problems do not
802 // propogate to future connection attempts.
803 RemoveNetwork(rpcid);
804 rpcid_by_service_.erase(service);
805
806 return false;
807 }
808
809 return true;
810}
811
812bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
813 string rpcid = FindNetworkRpcidForService(service, error);
814 if (rpcid.empty()) {
815 // Error is already populated.
816 return false;
817 }
818
819 // Erase the rpcid from our tables regardless of failure below, since even
820 // if in failure, we never want to use this network again.
821 rpcid_by_service_.erase(service);
822
823 // TODO(quiche): Reconsider giving up immediately. Maybe give
824 // wpa_supplicant some time to retry, first.
825 if (!RemoveNetwork(rpcid)) {
826 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100827 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800828 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100829 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800830 rpcid.c_str());
831 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
832 return false;
833 }
834
835 return true;
836}
837
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000838void WiFi::BSSAddedTask(
839 const ::DBus::Path &path,
840 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000841 // Note: we assume that BSSIDs are unique across endpoints. This
842 // means that if an AP reuses the same BSSID for multiple SSIDs, we
843 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800844 WiFiEndpointRefPtr endpoint(
845 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -0800846 SLOG(WiFi, 1) << "Found endpoint. "
847 << "RPC path: " << path << ", "
Darin Petkov50cb78a2013-02-06 16:17:49 +0100848 << LogSSID(endpoint->ssid_string()) << ", "
Wade Guthrie592ecd52012-11-12 13:12:30 -0800849 << "bssid: " << endpoint->bssid_string() << ", "
850 << "signal: " << endpoint->signal_strength() << ", "
851 << "security: " << endpoint->security_mode() << ", "
852 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000853
854 if (endpoint->ssid_string().empty()) {
855 // Don't bother trying to find or create a Service for an Endpoint
856 // without an SSID. We wouldn't be able to connect to it anyway.
857 return;
858 }
859
mukesh agrawalb3857612012-01-18 16:23:29 -0800860 if (endpoint->ssid()[0] == 0) {
861 // Assume that an SSID starting with NULL is bogus/misconfigured,
862 // and filter it out.
863 return;
864 }
865
Paul Stewart3c504012013-01-17 17:49:58 -0800866 provider_->OnEndpointAdded(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000867
mukesh agrawale9adda12012-02-09 18:33:48 -0800868 // Do this last, to maintain the invariant that any Endpoint we
869 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800870 //
871 // TODO(quiche): Write test to verify correct behavior in the case
872 // where we get multiple BSSAdded events for a single endpoint.
873 // (Old Endpoint's refcount should fall to zero, and old Endpoint
874 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800875 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800876 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000877}
878
879void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
880 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
881 if (i == endpoint_by_rpcid_.end()) {
882 LOG(WARNING) << "WiFi " << link_name()
883 << " could not find BSS " << path
884 << " to remove.";
885 return;
886 }
887
888 WiFiEndpointRefPtr endpoint = i->second;
889 CHECK(endpoint);
890 endpoint_by_rpcid_.erase(i);
891
Paul Stewart3c504012013-01-17 17:49:58 -0800892 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
893 if (!service) {
894 return;
895 }
896 Error unused_error;
897 RemoveNetworkForService(service, &unused_error);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000898
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000899 bool disconnect_service = !service->HasEndpoints() &&
900 (service->IsConnecting() || service->IsConnected());
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000901
902 if (disconnect_service) {
903 DisconnectFrom(service);
904 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000905}
906
Paul Stewartbc6e7392012-05-24 07:07:48 -0700907void WiFi::CertificationTask(
908 const map<string, ::DBus::Variant> &properties) {
909 if (!current_service_) {
910 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
911 << " with no current service.";
912 return;
913 }
914
Paul Stewart735eab52013-03-29 09:19:23 -0700915 string subject;
916 uint32 depth;
917 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
918 current_service_->AddEAPCertification(subject, depth);
Paul Stewartbc6e7392012-05-24 07:07:48 -0700919 }
Paul Stewartbc6e7392012-05-24 07:07:48 -0700920}
921
Paul Stewartdb0f9172012-11-30 16:48:09 -0800922void WiFi::EAPEventTask(const string &status, const string &parameter) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800923 if (!current_service_) {
924 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
925 << " with no current service.";
926 return;
927 }
Paul Stewart735eab52013-03-29 09:19:23 -0700928 Service::ConnectFailure failure = Service::kFailureUnknown;
929 eap_state_handler_->ParseStatus(status, parameter, &failure);
Paul Stewartdb0f9172012-11-30 16:48:09 -0800930 if (failure != Service::kFailureUnknown) {
Paul Stewart735eab52013-03-29 09:19:23 -0700931 // Avoid a reporting failure twice by resetting EAP state handler early.
932 eap_state_handler_->Reset();
mukesh agrawald4dc0832013-03-25 14:38:26 -0700933 Error unused_error;
mukesh agrawald4dc0832013-03-25 14:38:26 -0700934 current_service_->DisconnectWithFailure(failure, &unused_error);
Paul Stewartdb0f9172012-11-30 16:48:09 -0800935 }
936}
937
mukesh agrawal15908392011-11-16 18:29:25 +0000938void WiFi::PropertiesChangedTask(
939 const map<string, ::DBus::Variant> &properties) {
940 // TODO(quiche): Handle changes in other properties (e.g. signal
941 // strength).
942
943 // Note that order matters here. In particular, we want to process
944 // changes in the current BSS before changes in state. This is so
945 // that we update the state of the correct Endpoint/Service.
946
947 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -0700948 properties.find(WPASupplicant::kInterfacePropertyCurrentBSS);
mukesh agrawal15908392011-11-16 18:29:25 +0000949 if (properties_it != properties.end()) {
950 CurrentBSSChanged(properties_it->second.reader().get_path());
951 }
952
Paul Stewart0654ece2013-03-26 15:21:26 -0700953 properties_it = properties.find(WPASupplicant::kInterfacePropertyState);
mukesh agrawal15908392011-11-16 18:29:25 +0000954 if (properties_it != properties.end()) {
955 StateChanged(properties_it->second.reader().get_string());
956 }
957}
958
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700959void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700960 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -0800961 if (need_bss_flush_) {
962 CHECK(supplicant_interface_proxy_ != NULL);
963 // Compute |max_age| relative to |resumed_at_|, to account for the
964 // time taken to scan.
965 struct timeval now;
966 uint32_t max_age;
967 time_->GetTimeMonotonic(&now);
968 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
969 supplicant_interface_proxy_->FlushBSS(max_age);
970 need_bss_flush_ = false;
971 }
Paul Stewartd2db2b12013-01-17 13:11:07 -0800972 SetScanPending(false);
mukesh agrawalb66c6462012-05-07 11:45:25 -0700973 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -0700974}
975
mukesh agrawal32399322011-09-01 10:53:43 -0700976void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700977 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -0700978 if (!enabled()) {
979 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
980 return;
981 }
982 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
983 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
984 return;
985 }
Christopher Wileyc68c8672012-11-20 16:52:21 -0800986 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
987 (current_service_.get() && current_service_->IsConnecting())) {
988 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
989 return;
990 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800991 map<string, DBus::Variant> scan_args;
Paul Stewart0654ece2013-03-26 15:21:26 -0700992 scan_args[WPASupplicant::kPropertyScanType].writer().
993 append_string(WPASupplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -0800994
Paul Stewart3c504012013-01-17 17:49:58 -0800995 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
Paul Stewartced6a0b2011-11-08 15:32:04 -0800996 if (!hidden_ssids.empty()) {
Paul Stewart3c504012013-01-17 17:49:58 -0800997 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
998 // not fit in.
Paul Stewart0654ece2013-03-26 15:21:26 -0700999 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
Paul Stewart3c504012013-01-17 17:49:58 -08001000 hidden_ssids.erase(
Paul Stewart0654ece2013-03-26 15:21:26 -07001001 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
Paul Stewart3c504012013-01-17 17:49:58 -08001002 hidden_ssids.end());
1003 }
1004 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1005 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1006 // behavior of doing a broadcast probe.
1007 hidden_ssids.push_back(ByteArray());
1008
Paul Stewart0654ece2013-03-26 15:21:26 -07001009 scan_args[WPASupplicant::kPropertyScanSSIDs] =
Paul Stewartced6a0b2011-11-08 15:32:04 -08001010 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1011 }
1012
Gaurav Shah10109f22011-11-11 20:16:22 -08001013 try {
1014 supplicant_interface_proxy_->Scan(scan_args);
Paul Stewartd2db2b12013-01-17 13:11:07 -08001015 SetScanPending(true);
Ben Chan80326f32012-05-04 17:51:32 -07001016 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001017 // A scan may fail if, for example, the wpa_supplicant vanishing
1018 // notification is posted after this task has already started running.
1019 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001020 }
mukesh agrawal32399322011-09-01 10:53:43 -07001021}
1022
Paul Stewartd2db2b12013-01-17 13:11:07 -08001023void WiFi::SetScanPending(bool pending) {
1024 if (scan_pending_ != pending) {
1025 scan_pending_ = pending;
1026 adaptor()->EmitBoolChanged(flimflam::kScanningProperty, pending);
1027 }
1028}
1029
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001030string WiFi::GetServiceLeaseName(const WiFiService &service) {
1031 return service.GetStorageIdentifier();
1032}
1033
1034void WiFi::DestroyServiceLease(const WiFiService &service) {
1035 DestroyIPConfigLease(GetServiceLeaseName(service));
1036}
1037
mukesh agrawal15908392011-11-16 18:29:25 +00001038void WiFi::StateChanged(const string &new_state) {
1039 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001040 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001041 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1042 << old_state << " -> " << new_state;
1043
1044 WiFiService *affected_service;
1045 // Identify the service to which the state change applies. If
1046 // |pending_service_| is non-NULL, then the state change applies to
1047 // |pending_service_|. Otherwise, it applies to |current_service_|.
1048 //
1049 // This policy is driven by the fact that the |pending_service_|
1050 // doesn't become the |current_service_| until wpa_supplicant
1051 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001052 // CurrentBSS change won't be reported until the |pending_service_|
Paul Stewart0654ece2013-03-26 15:21:26 -07001053 // reaches the WPASupplicant::kInterfaceStateCompleted state.
mukesh agrawal15908392011-11-16 18:29:25 +00001054 affected_service =
1055 pending_service_.get() ? pending_service_.get() : current_service_.get();
1056 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001057 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1058 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001059 return;
1060 }
1061
Paul Stewart0654ece2013-03-26 15:21:26 -07001062 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
Paul Stewart44663922012-07-30 11:03:03 -07001063 if (affected_service->IsConnected()) {
1064 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001065 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001066 } else if (has_already_completed_) {
1067 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001068 } else if (AcquireIPConfigWithLeaseName(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001069 GetServiceLeaseName(*affected_service))) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001070 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001071 affected_service->SetState(Service::kStateConfiguring);
1072 } else {
1073 LOG(ERROR) << "Unable to acquire DHCP config.";
1074 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001075 has_already_completed_ = true;
Paul Stewart0654ece2013-03-26 15:21:26 -07001076 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
mukesh agrawal15908392011-11-16 18:29:25 +00001077 affected_service->SetState(Service::kStateAssociating);
Paul Stewart0654ece2013-03-26 15:21:26 -07001078 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1079 new_state == WPASupplicant::kInterfaceStateAssociating ||
1080 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1081 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
mukesh agrawal15908392011-11-16 18:29:25 +00001082 // Ignore transitions into these states from Completed, to avoid
1083 // bothering the user when roaming, or re-keying.
Paul Stewart0654ece2013-03-26 15:21:26 -07001084 if (old_state != WPASupplicant::kInterfaceStateCompleted)
mukesh agrawal15908392011-11-16 18:29:25 +00001085 affected_service->SetState(Service::kStateAssociating);
1086 // TOOD(quiche): On backwards transitions, we should probably set
1087 // a timeout for getting back into the completed state. At present,
1088 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001089 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001090 // (crosbug.com/23207)
Paul Stewart0654ece2013-03-26 15:21:26 -07001091 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
Paul Stewart44663922012-07-30 11:03:03 -07001092 affected_service == current_service_ &&
1093 affected_service->IsConnected()) {
1094 // This means that wpa_supplicant failed in a re-connect attempt, but
1095 // may still be reconnecting. Give wpa_supplicant a limited amount of
1096 // time to transition out this condition by either connecting or changing
1097 // CurrentBSS.
1098 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001099 } else {
1100 // Other transitions do not affect Service state.
1101 //
1102 // Note in particular that we ignore a State change into
1103 // kInterfaceStateDisconnected, in favor of observing the corresponding
1104 // change in CurrentBSS.
1105 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001106}
1107
Paul Stewart1369c2b2013-01-11 05:41:26 -08001108bool WiFi::SuspectCredentials(
1109 const WiFiService &service, Service::ConnectFailure *failure) const {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001110 if (service.IsSecurityMatch(flimflam::kSecurityPsk)) {
Paul Stewart0654ece2013-03-26 15:21:26 -07001111 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
Paul Stewart1369c2b2013-01-11 05:41:26 -08001112 !service.has_ever_connected()) {
1113 if (failure) {
1114 *failure = Service::kFailureBadPassphrase;
1115 }
1116 return true;
1117 }
1118 } else if (service.IsSecurityMatch(flimflam::kSecurity8021x)) {
Paul Stewart735eab52013-03-29 09:19:23 -07001119 if (eap_state_handler_->is_eap_in_progress() &&
1120 !service.has_ever_connected()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001121 if (failure) {
1122 *failure = Service::kFailureEAPAuthentication;
1123 }
1124 return true;
1125 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001126 }
1127
Paul Stewart1369c2b2013-01-11 05:41:26 -08001128 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001129}
1130
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001131// static
1132bool WiFi::SanitizeSSID(string *ssid) {
1133 CHECK(ssid);
1134
1135 size_t ssid_len = ssid->length();
1136 size_t i;
1137 bool changed = false;
1138
Gary Morainac1bdb42012-02-16 17:42:29 -08001139 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001140 if (!g_ascii_isprint((*ssid)[i])) {
1141 (*ssid)[i] = '?';
1142 changed = true;
1143 }
1144 }
1145
1146 return changed;
1147}
1148
Darin Petkov50cb78a2013-02-06 16:17:49 +01001149// static
1150string WiFi::LogSSID(const string &ssid) {
1151 string out;
1152 for (string::const_iterator it = ssid.begin(); it != ssid.end(); ++it) {
1153 // Replace '[' and ']' (in addition to non-printable characters) so that
1154 // it's easy to match the right substring through a non-greedy regex.
1155 if (*it == '[' || *it == ']' || !g_ascii_isprint(*it)) {
1156 base::StringAppendF(&out, "\\x%02x", *it);
1157 } else {
1158 out += *it;
1159 }
1160 }
1161 return StringPrintf("[SSID=%s]", out.c_str());
1162}
1163
Paul Stewart3c508e12012-08-09 11:40:06 -07001164void WiFi::OnLinkMonitorFailure() {
1165 // If we have never found the gateway, let's be conservative and not
1166 // do anything, in case this network topology does not have a gateway.
1167 if (!link_monitor()->IsGatewayFound()) {
1168 LOG(INFO) << "In " << __func__ << "(): "
1169 << "Skipping reassociate since gateway was never found.";
1170 return;
1171 }
1172
1173 if (!supplicant_present_) {
1174 LOG(ERROR) << "In " << __func__ << "(): "
1175 << "wpa_supplicant is not present. Cannot reassociate.";
1176 return;
1177 }
1178
1179 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001180 // This will force a transition out of connected, if we are actually
1181 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001182 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001183 // If we don't eventually get a transition back into a connected state,
1184 // there is something wrong.
1185 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001186 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1187 } catch (const DBus::Error &e) { // NOLINT
1188 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1189 return;
1190 }
1191}
1192
Arman Ugurayed8e6102012-11-29 14:47:20 -08001193bool WiFi::ShouldUseArpGateway() const {
1194 return true;
1195}
1196
Paul Stewart3c504012013-01-17 17:49:58 -08001197void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
1198 DisconnectFrom(service);
1199 if (service == selected_service()) {
1200 DropConnection();
1201 }
1202 Error unused_error;
1203 RemoveNetworkForService(service, &unused_error);
1204}
1205
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001206vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1207 vector<GeolocationInfo> objects;
1208 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1209 it != endpoint_by_rpcid_.end();
1210 ++it) {
1211 GeolocationInfo geoinfo;
1212 WiFiEndpointRefPtr endpoint = it->second;
1213 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1214 geoinfo.AddField(kGeoSignalStrengthProperty,
1215 StringPrintf("%d", endpoint->signal_strength()));
1216 geoinfo.AddField(
1217 kGeoChannelProperty,
1218 StringPrintf("%d",
1219 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1220 // TODO(gauravsh): Include age field. crosbug.com/35445
1221 objects.push_back(geoinfo);
1222 }
1223 return objects;
1224}
1225
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001226void WiFi::HelpRegisterDerivedInt32(
1227 PropertyStore *store,
1228 const string &name,
1229 int32(WiFi::*get)(Error *error),
1230 void(WiFi::*set)(const int32 &value, Error *error)) {
1231 store->RegisterDerivedInt32(
1232 name,
1233 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1234}
1235
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001236void WiFi::HelpRegisterDerivedUint16(
1237 PropertyStore *store,
1238 const string &name,
1239 uint16(WiFi::*get)(Error *error),
1240 void(WiFi::*set)(const uint16 &value, Error *error)) {
1241 store->RegisterDerivedUint16(
1242 name,
1243 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1244}
1245
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001246void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001247 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001248 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001249
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001250 // We want to flush the BSS cache, but we don't want to conflict
1251 // with a running scan or an active connection attempt. So record
1252 // the need to flush, and take care of flushing when the next scan
1253 // completes.
1254 //
1255 // Note that supplicant will automatically expire old cache
1256 // entries (after, e.g., a BSS is not found in two consecutive
1257 // scans). However, our explicit flush accelerates re-association
1258 // in cases where a BSS disappeared while we were asleep. (See,
1259 // e.g. WiFiRoaming.005SuspendRoam.)
1260 time_->GetTimeMonotonic(&resumed_at_);
1261 need_bss_flush_ = true;
1262
1263 if (!scan_pending_ && IsIdle()) {
1264 // Not scanning/connecting/connected, so let's get things rolling.
1265 Scan(NULL);
1266 } else {
1267 SLOG(WiFi, 1) << __func__
1268 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001269 }
1270}
1271
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001272void WiFi::OnConnected() {
1273 Device::OnConnected();
1274 EnableHighBitrates();
1275}
1276
Paul Stewarte369ece2012-05-22 09:11:03 -07001277void WiFi::RestartFastScanAttempts() {
1278 fast_scans_remaining_ = kNumFastScanAttempts;
1279 StartScanTimer();
1280}
1281
mukesh agrawalb66c6462012-05-07 11:45:25 -07001282void WiFi::StartScanTimer() {
1283 if (scan_interval_seconds_ == 0) {
1284 StopScanTimer();
1285 return;
1286 }
1287 scan_timer_callback_.Reset(
1288 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001289 // Repeat the first few scans after disconnect relatively quickly so we
1290 // have reasonable trust that no APs we are looking for are present.
1291 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1292 fast_scans_remaining_ > 0 ?
1293 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001294}
1295
1296void WiFi::StopScanTimer() {
1297 scan_timer_callback_.Cancel();
1298}
1299
1300void WiFi::ScanTimerHandler() {
1301 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1302 if (IsIdle() && !scan_pending_) {
1303 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001304 if (fast_scans_remaining_ > 0) {
1305 --fast_scans_remaining_;
1306 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001307 }
1308 StartScanTimer();
1309}
1310
Paul Stewart2b05e622012-07-13 20:38:44 -07001311void WiFi::StartPendingTimer() {
1312 pending_timeout_callback_.Reset(
1313 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1314 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1315 kPendingTimeoutSeconds * 1000);
1316}
1317
1318void WiFi::StopPendingTimer() {
1319 pending_timeout_callback_.Cancel();
1320}
1321
1322void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001323 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001324 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001325 if (service) {
1326 service->SetState(Service::kStateAssociating);
1327 StartPendingTimer();
1328 } else if (pending_service_) {
1329 StopPendingTimer();
1330 }
1331 pending_service_ = service;
1332}
1333
1334void WiFi::PendingTimeoutHandler() {
mukesh agrawald4dc0832013-03-25 14:38:26 -07001335 Error unused_error;
Paul Stewart2b05e622012-07-13 20:38:44 -07001336 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1337 CHECK(pending_service_);
mukesh agrawald4dc0832013-03-25 14:38:26 -07001338 pending_service_->DisconnectWithFailure(
1339 Service::kFailureOutOfRange, &unused_error);
Paul Stewart2b05e622012-07-13 20:38:44 -07001340}
1341
Paul Stewart44663922012-07-30 11:03:03 -07001342void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001343 if (!reconnect_timeout_callback_.IsCancelled()) {
1344 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1345 << ": reconnect timer already running.";
1346 return;
1347 }
Paul Stewart44663922012-07-30 11:03:03 -07001348 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1349 reconnect_timeout_callback_.Reset(
1350 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1351 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1352 kReconnectTimeoutSeconds * 1000);
1353}
1354
1355void WiFi::StopReconnectTimer() {
1356 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1357 reconnect_timeout_callback_.Cancel();
1358}
1359
1360void WiFi::ReconnectTimeoutHandler() {
1361 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001362 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001363 CHECK(current_service_);
1364 current_service_->SetFailureSilent(Service::kFailureConnect);
1365 DisconnectFrom(current_service_);
1366}
1367
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001368void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1369 LOG(INFO) << "WPA supplicant appeared.";
1370 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001371 // Restart the WiFi device if it's started already. This will reset the
1372 // state and connect the device to the new WPA supplicant instance.
1373 if (enabled()) {
1374 Restart();
1375 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001376 return;
1377 }
1378 supplicant_present_ = true;
1379 ConnectToSupplicant();
1380}
1381
1382void WiFi::OnSupplicantVanish() {
1383 LOG(INFO) << "WPA supplicant vanished.";
1384 if (!supplicant_present_) {
1385 return;
1386 }
1387 supplicant_present_ = false;
1388 // Restart the WiFi device if it's started already. This will effectively
1389 // suspend the device until the WPA supplicant reappears.
1390 if (enabled()) {
1391 Restart();
1392 }
1393}
1394
Paul Stewart5581d072012-12-17 17:30:20 -08001395void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1396 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1397 if (!supplicant_process_proxy_.get()) {
1398 SLOG(WiFi, 2) << "Suplicant process proxy not present.";
1399 return;
1400 }
1401 string current_level;
1402 try {
1403 current_level = supplicant_process_proxy_->GetDebugLevel();
1404 } catch (const DBus::Error &e) { // NOLINT
1405 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1406 return;
1407 }
1408
Paul Stewart0654ece2013-03-26 15:21:26 -07001409 if (current_level != WPASupplicant::kDebugLevelInfo &&
1410 current_level != WPASupplicant::kDebugLevelDebug) {
Paul Stewart5581d072012-12-17 17:30:20 -08001411 SLOG(WiFi, 2) << "WiFi debug level is currently "
1412 << current_level
1413 << "; assuming that it is being controlled elsewhere.";
1414 return;
1415 }
Paul Stewart0654ece2013-03-26 15:21:26 -07001416 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
1417 WPASupplicant::kDebugLevelInfo;
Paul Stewart5581d072012-12-17 17:30:20 -08001418
1419 if (new_level == current_level) {
1420 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1421 << current_level;
1422 return;
1423 }
1424
1425 try {
1426 supplicant_process_proxy_->SetDebugLevel(new_level);
1427 } catch (const DBus::Error &e) { // NOLINT
1428 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1429 }
1430}
1431
Paul Stewarta47c3c62012-12-18 12:14:29 -08001432void WiFi::SetConnectionDebugging(bool enabled) {
1433 if (is_debugging_connection_ == enabled) {
1434 return;
1435 }
1436 OnWiFiDebugScopeChanged(
1437 enabled ||
1438 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1439 is_debugging_connection_ = enabled;
1440}
1441
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001442void WiFi::ConnectToSupplicant() {
1443 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1444 << " supplicant: "
1445 << (supplicant_present_ ? "present" : "absent")
1446 << " proxy: "
1447 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1448 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1449 return;
1450 }
1451 supplicant_process_proxy_.reset(
1452 proxy_factory_->CreateSupplicantProcessProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001453 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001454 OnWiFiDebugScopeChanged(
1455 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001456 ::DBus::Path interface_path;
1457 try {
1458 map<string, DBus::Variant> create_interface_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001459 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001460 append_string(link_name().c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -07001461 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
1462 append_string(WPASupplicant::kDriverNL80211);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001463 create_interface_args[
Paul Stewart0654ece2013-03-26 15:21:26 -07001464 WPASupplicant::kInterfacePropertyConfigFile].writer().
Paul Stewart196f50f2013-03-27 18:02:11 -07001465 append_string(WPASupplicant::kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001466 interface_path =
1467 supplicant_process_proxy_->CreateInterface(create_interface_args);
1468 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart0654ece2013-03-26 15:21:26 -07001469 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001470 interface_path =
1471 supplicant_process_proxy_->GetInterface(link_name());
1472 // TODO(quiche): Is it okay to crash here, if device is missing?
1473 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001474 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1475 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001476 }
1477 }
1478
1479 supplicant_interface_proxy_.reset(
1480 proxy_factory_->CreateSupplicantInterfaceProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001481 this, interface_path, WPASupplicant::kDBusAddr));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001482
1483 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1484 IFF_UP);
1485 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1486
1487 // Clear out any networks that might previously have been configured
1488 // for this interface.
1489 supplicant_interface_proxy_->RemoveAllNetworks();
1490
1491 // Flush interface's BSS cache, so that we get BSSAdded signals for
1492 // all BSSes (not just new ones since the last scan).
1493 supplicant_interface_proxy_->FlushBSS(0);
1494
1495 try {
1496 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1497 // with RADIUS servers that respond strangely to such requests.
1498 // crosbug.com/25630
1499 supplicant_interface_proxy_->SetFastReauth(false);
1500 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001501 LOG(ERROR) << "Failed to disable fast_reauth. "
1502 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001503 }
1504
1505 try {
1506 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1507 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1508 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001509 LOG(ERROR) << "Failed to set scan_interval. "
1510 << "May be running an older version of wpa_supplicant.";
1511 }
1512
1513 try {
1514 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1515 } catch (const DBus::Error &e) { // NOLINT
1516 LOG(ERROR) << "Failed to disable high bitrates. "
1517 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001518 }
1519
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001520 Scan(NULL);
1521 StartScanTimer();
1522}
1523
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001524void WiFi::EnableHighBitrates() {
1525 LOG(INFO) << "Enabling high bitrates.";
1526 try {
1527 supplicant_interface_proxy_->EnableHighBitrates();
1528 } catch (const DBus::Error &e) { // NOLINT
1529 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1530 }
1531}
1532
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001533void WiFi::Restart() {
1534 LOG(INFO) << link_name() << " restarting.";
1535 WiFiRefPtr me = this; // Make sure we don't get destructed.
1536 // Go through the manager rather than starting and stopping the device
1537 // directly so that the device can be configured with the profile.
1538 manager()->DeregisterDevice(me);
1539 manager()->RegisterDevice(me);
1540}
1541
Paul Stewartb50f0b92011-05-16 16:31:42 -07001542} // namespace shill