blob: 33a84b5565c217bbb4b18cb7ea4b03c065697aba [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
25#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080026#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070027#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070028#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070029#include "shill/event_dispatcher.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070030#include "shill/key_value_store.h"
31#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070032#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070033#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070034#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000035#include "shill/metrics.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070036#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080037#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070038#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050039#include "shill/rtnl_handler.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080040#include "shill/shill_time.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080041#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070042#include "shill/supplicant_interface_proxy_interface.h"
43#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080044#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070045#include "shill/wifi_endpoint.h"
46#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;
Paul Stewartced6a0b2011-11-08 15:32:04 -080052using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070053using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070054using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070055
Paul Stewartb50f0b92011-05-16 16:31:42 -070056namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070057
58// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080059const char *WiFi::kDefaultBgscanMethod =
60 wpa_supplicant::kNetworkBgscanMethodSimple;
61const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
62const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
63const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020064// Scan interval while connected.
65const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070066// Note that WiFi generates some manager-level errors, because it implements
67// the Manager.GetWiFiService flimflam API. The API is implemented here,
68// rather than in manager, to keep WiFi-specific logic in the right place.
mukesh agrawal7a4e4002011-09-06 11:26:05 -070069const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
70const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
71const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070072const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
73 "security mode is unsupported";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070074const char WiFi::kManagerErrorUnsupportedServiceMode[] =
75 "service mode is unsupported";
mukesh agrawal5c05b292012-03-07 10:12:52 -080076// Age (in seconds) beyond which a BSS cache entry will not be preserved,
77// across a suspend/resume.
78const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000079const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070080const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070081const int WiFi::kNumFastScanAttempts = 3;
82const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070083const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070084const int WiFi::kReconnectTimeoutSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070085
Paul Stewartb50f0b92011-05-16 16:31:42 -070086WiFi::WiFi(ControlInterface *control_interface,
87 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080088 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070089 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070090 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080091 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070092 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070093 : Device(control_interface,
94 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080095 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070096 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070097 link,
Chris Masone626719f2011-08-18 16:58:48 -070098 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080099 interface_index,
100 Technology::kWifi),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500101 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700102 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800103 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200104 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000105 supplicant_state_(kInterfaceStateUnknown),
106 supplicant_bss_("(unknown)"),
Paul Stewart66c86002012-01-30 18:00:52 -0800107 clear_cached_credentials_pending_(false),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800108 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +0200109 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700110 fast_scans_remaining_(kNumFastScanAttempts),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800111 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
112 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700113 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800114 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700115 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200116 store->RegisterDerivedString(
117 flimflam::kBgscanMethodProperty,
118 StringAccessor(
119 // TODO(petkov): CustomMappedAccessor is used for convenience because
120 // it provides a way to define a custom clearer (unlike
121 // CustomAccessor). We need to implement a fully custom accessor with
122 // no extra argument.
123 new CustomMappedAccessor<WiFi, string, int>(this,
124 &WiFi::ClearBgscanMethod,
125 &WiFi::GetBgscanMethod,
126 &WiFi::SetBgscanMethod,
127 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800128 HelpRegisterDerivedUint16(store,
129 flimflam::kBgscanShortIntervalProperty,
130 &WiFi::GetBgscanShortInterval,
131 &WiFi::SetBgscanShortInterval);
132 HelpRegisterDerivedInt32(store,
133 flimflam::kBgscanSignalThresholdProperty,
134 &WiFi::GetBgscanSignalThreshold,
135 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700136
Chris Masoneb925cc82011-06-22 15:39:57 -0700137 // TODO(quiche): Decide if scan_pending_ is close enough to
138 // "currently scanning" that we don't care, or if we want to track
139 // scan pending/currently scanning/no scan scheduled as a tri-state
140 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700141 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800142 HelpRegisterDerivedUint16(store,
143 flimflam::kScanIntervalProperty,
144 &WiFi::GetScanInterval,
145 &WiFi::SetScanInterval);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700146 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700147}
148
mukesh agrawalaf571952011-07-14 14:31:12 -0700149WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700150
Eric Shienbrood9a245532012-03-07 14:20:39 -0500151void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200152 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
153 if (enabled()) {
154 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700155 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500156 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200157 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500158 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200159 }
160 if (on_supplicant_appear_.IsCancelled()) {
161 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
162 // device instance.
163 on_supplicant_appear_.Reset(
164 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
165 on_supplicant_vanish_.Reset(
166 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
167 manager()->dbus_manager()->WatchName(wpa_supplicant::kDBusAddr,
168 on_supplicant_appear_.callback(),
169 on_supplicant_vanish_.callback());
170 }
171 // Connect to WPA supplicant if it's already present. If not, we'll connect to
172 // it when it appears.
173 ConnectToSupplicant();
mukesh agrawalab87ea42011-05-18 11:44:49 -0700174}
175
Eric Shienbrood9a245532012-03-07 14:20:39 -0500176void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700177 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200178 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700179 StopScanTimer();
mukesh agrawal15908392011-11-16 18:29:25 +0000180 endpoint_by_rpcid_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700181
Paul Stewartced6a0b2011-11-08 15:32:04 -0800182 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
183 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700184 ++it) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700185 SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
186 << (*it)->friendly_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700187 manager()->DeregisterService(*it);
188 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700189 rpcid_by_service_.clear();
Paul Stewartced6a0b2011-11-08 15:32:04 -0800190 services_.clear(); // breaks reference cycles
Paul Stewart549d44c2012-07-03 12:40:25 -0700191 supplicant_interface_proxy_.reset(); // breaks a reference cycle
192 // TODO(quiche): Remove interface from supplicant.
193 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800194 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000195 pending_service_ = NULL; // breaks a reference cycle
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200196 scan_pending_ = false;
Paul Stewart2b05e622012-07-13 20:38:44 -0700197 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700198 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700199
Eric Shienbrood9a245532012-03-07 14:20:39 -0500200 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
201 if (error)
202 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700203 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700204
Ben Chanfad4a0b2012-04-18 15:49:59 -0700205 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
206 << (supplicant_process_proxy_.get() ?
207 "is set." : "is not set.");
208 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
209 << (supplicant_interface_proxy_.get() ?
210 "is set." : "is not set.");
211 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
212 << (pending_service_.get() ? "is set." : "is not set.");
213 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
214 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
215 SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
216 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700217}
218
Paul Stewarta41e38d2011-11-11 07:47:29 -0800219bool WiFi::Load(StoreInterface *storage) {
220 LoadHiddenServices(storage);
221 return Device::Load(storage);
222}
223
mukesh agrawal1830fa12011-09-26 14:31:40 -0700224void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700225 LOG(INFO) << __func__;
226
mukesh agrawal7ec71312011-11-10 02:08:26 +0000227 // Needs to send a D-Bus message, but may be called from D-Bus
228 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700229 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500230 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700231}
232
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000233void WiFi::BSSAdded(const ::DBus::Path &path,
234 const map<string, ::DBus::Variant> &properties) {
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::BSSAddedTask,
238 weak_ptr_factory_.GetWeakPtr(),
239 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000240}
241
242void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500243 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000244 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500245 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
246 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700247}
248
Paul Stewartbc6e7392012-05-24 07:07:48 -0700249void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
250 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
251 weak_ptr_factory_.GetWeakPtr(), properties));
252}
253
mukesh agrawal7ec71312011-11-10 02:08:26 +0000254void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200255 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000256 // Called from D-Bus signal handler, but may need to send a D-Bus
257 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500258 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
259 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000260}
261
mukesh agrawalb54601c2011-06-07 17:39:22 -0700262void WiFi::ScanDone() {
263 LOG(INFO) << __func__;
264
mukesh agrawal7ec71312011-11-10 02:08:26 +0000265 // Defer handling of scan result processing, because that processing
266 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700267 // registration can't be done in the context of a D-Bus signal
268 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500269 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
270 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700271}
272
mukesh agrawal6e277772011-09-29 15:04:23 -0700273void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000274 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800275 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700276 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700277
mukesh agrawal7ec71312011-11-10 02:08:26 +0000278 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000279 if (pending_service_ && pending_service_ == service) {
280 // TODO(quiche): Return an error to the caller. crosbug.com/23832
281 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
282 << service->friendly_name()
283 << ", which is already pending.";
284 return;
285 }
286
287 if (pending_service_ && pending_service_ != service) {
Paul Stewart2b05e622012-07-13 20:38:44 -0700288 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
289 // on this to drive the service state back to idle. Do that here.
290 pending_service_->SetState(Service::kStateIdle);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000291 DisconnectFrom(pending_service_);
292 }
mukesh agrawal32399322011-09-01 10:53:43 -0700293
mukesh agrawal6e277772011-09-29 15:04:23 -0700294 try {
mukesh agrawal64896322011-12-01 01:13:10 +0000295 const uint32_t scan_ssid = 1; // "True": Use directed probe.
296 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
297 append_uint32(scan_ssid);
Darin Petkov4a66cc52012-06-15 10:08:29 +0200298 AppendBgscan(service, &service_params);
299 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000300 rpcid_by_service_[service] = network_path;
Ben Chan80326f32012-05-04 17:51:32 -0700301 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700302 LOG(ERROR) << "exception while adding network: " << e.what();
303 return;
304 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700305
mukesh agrawal445e72c2011-06-22 11:13:50 -0700306 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700307 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000308 CHECK(current_service_.get() != pending_service_.get());
309
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700310 // SelectService here (instead of in LinkEvent, like Ethernet), so
311 // that, if we fail to bring up L2, we can attribute failure correctly.
312 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000313 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700314 // reconsider if this is the right place to change the selected service.
315 // see discussion in crosbug.com/20191.
316 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000317}
318
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000319void WiFi::DisconnectFrom(WiFiService *service) {
320 if (service != current_service_ && service != pending_service_) {
321 // TODO(quiche): Once we have asynchronous reply support, we should
322 // generate a D-Bus error here. (crosbug.com/23832)
323 LOG(WARNING) << "In " << __func__ << "(): "
324 << " ignoring request to disconnect from service "
325 << service->friendly_name()
326 << " which is neither current nor pending";
327 return;
328 }
329
330 if (pending_service_ && service != pending_service_) {
331 // TODO(quiche): Once we have asynchronous reply support, we should
332 // generate a D-Bus error here. (crosbug.com/23832)
333 LOG(WARNING) << "In " << __func__ << "(): "
334 << " ignoring request to disconnect from service "
335 << service->friendly_name()
336 << " which is not the pending service.";
337 return;
338 }
339
340 if (!pending_service_ && service != current_service_) {
341 // TODO(quiche): Once we have asynchronous reply support, we should
342 // generate a D-Bus error here. (crosbug.com/23832)
343 LOG(WARNING) << "In " << __func__ << "(): "
344 << " ignoring request to disconnect from service "
345 << service->friendly_name()
346 << " which is not the current service.";
347 return;
348 }
349
Paul Stewart2b05e622012-07-13 20:38:44 -0700350 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700351 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700352
353 if (!supplicant_present_) {
354 LOG(INFO) << "In " << __func__ << "(): "
355 << "wpa_supplicant is not present; silently resetting "
356 << "current_service_.";
357 current_service_ = NULL;
358 return;
359 }
360
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000361 try {
362 supplicant_interface_proxy_->Disconnect();
363 // We'll call RemoveNetwork and reset |current_service_| after
364 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700365 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000366 // Can't depend on getting a notification of CurrentBSS change.
367 // So effect changes immediately.
368 ReverseServiceMap::const_iterator rpcid_it =
369 rpcid_by_service_.find(service);
370 DCHECK(rpcid_it != rpcid_by_service_.end());
371 if (rpcid_it == rpcid_by_service_.end()) {
372 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
373 << service->friendly_name() << ": "
374 << "could not find supplicant network to disable.";
375 } else {
376 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
377 }
378 current_service_ = NULL;
379 }
380
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800381 CHECK(current_service_ == NULL ||
382 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000383}
384
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000385bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800386 return !current_service_ && !pending_service_;
387}
388
Paul Stewart66c86002012-01-30 18:00:52 -0800389void WiFi::ClearCachedCredentials() {
390 LOG(INFO) << __func__;
391
392 // Needs to send a D-Bus message, but may be called from D-Bus
393 // caller context (via Manager::PopProfile). So defer work
394 // to event loop.
395 if (!clear_cached_credentials_pending_) {
396 clear_cached_credentials_pending_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500397 dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask,
398 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart66c86002012-01-30 18:00:52 -0800399 }
400}
401
mukesh agrawalb20776f2012-02-10 16:00:36 -0800402void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
403 WiFiService *service = FindServiceForEndpoint(endpoint);
404 DCHECK(service);
405 if (service) {
406 service->NotifyEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800407 }
408}
409
Darin Petkov4a66cc52012-06-15 10:08:29 +0200410void WiFi::AppendBgscan(WiFiService *service,
411 map<string, DBus::Variant> *service_params) const {
412 int scan_interval = kBackgroundScanIntervalSeconds;
413 string method = bgscan_method_;
414 if (method.empty()) {
415 // If multiple APs are detected for this SSID, configure the default method.
416 // Otherwise, disable background scanning completely.
417 if (service->GetEndpointCount() > 1) {
418 method = kDefaultBgscanMethod;
419 } else {
420 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
421 return;
422 }
Christopher Wileya998df22012-07-11 15:14:55 -0700423 } else if (method.compare(wpa_supplicant::kNetworkBgscanMethodNone) == 0) {
424 LOG(INFO) << "Background scan disabled -- chose None method.";
425 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200426 } else {
427 // If the background scan method was explicitly specified, honor the
428 // configured background scan interval.
429 scan_interval = scan_interval_seconds_;
430 }
431 DCHECK(!method.empty());
432 string config_string = StringPrintf("%s:%d:%d:%d",
433 method.c_str(),
434 bgscan_short_interval_seconds_,
435 bgscan_signal_threshold_dbm_,
436 scan_interval);
437 LOG(INFO) << "Background scan: " << config_string;
438 (*service_params)[wpa_supplicant::kNetworkPropertyBgscan].writer()
439 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800440}
441
Darin Petkov4a66cc52012-06-15 10:08:29 +0200442string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
443 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
444}
445
446void WiFi::SetBgscanMethod(
447 const int &/*argument*/, const string &method, Error *error) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800448 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
Christopher Wileya998df22012-07-11 15:14:55 -0700449 method != wpa_supplicant::kNetworkBgscanMethodLearn &&
450 method != wpa_supplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800451 const string error_message =
452 StringPrintf("Unrecognized bgscan method %s", method.c_str());
453 LOG(WARNING) << error_message;
454 error->Populate(Error::kInvalidArguments, error_message);
455 return;
456 }
457
458 bgscan_method_ = method;
459 // We do not update kNetworkPropertyBgscan for |pending_service_| or
460 // |current_service_|, because supplicant does not allow for
461 // reconfiguration without disconnect and reconnect.
462}
463
464void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
465 bgscan_short_interval_seconds_ = seconds;
466 // We do not update kNetworkPropertyBgscan for |pending_service_| or
467 // |current_service_|, because supplicant does not allow for
468 // reconfiguration without disconnect and reconnect.
469}
470
471void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
472 bgscan_signal_threshold_dbm_ = dbm;
473 // We do not update kNetworkPropertyBgscan for |pending_service_| or
474 // |current_service_|, because supplicant does not allow for
475 // reconfiguration without disconnect and reconnect.
476}
477
478void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
479 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700480 if (running()) {
481 StartScanTimer();
482 }
483 // The scan interval affects both foreground scans (handled by
484 // |scan_timer_callback_|), and background scans (handled by
485 // supplicant). However, we do not update |pending_service_| or
486 // |current_service_|, because supplicant does not allow for
487 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800488}
489
Darin Petkov4a66cc52012-06-15 10:08:29 +0200490void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
491 bgscan_method_.clear();
492}
493
mukesh agrawal165e6142011-11-22 02:22:56 +0000494// To avoid creating duplicate services, call FindServiceForEndpoint
495// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000496WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
497 bool hidden_ssid) {
498 WiFiServiceRefPtr service =
499 new WiFiService(control_interface(),
500 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800501 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000502 manager(),
503 this,
504 endpoint.ssid(),
505 endpoint.network_mode(),
506 endpoint.security_mode(),
507 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000508 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000509 return service;
510}
511
512void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700513 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
514 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000515 supplicant_bss_ = new_bss;
Paul Stewart44663922012-07-30 11:03:03 -0700516
517 // Any change in CurrentBSS means supplicant is actively changing our
518 // connectivity. We no longer need to track any previously pending
519 // reconnect.
520 StopReconnectTimer();
521
mukesh agrawal15908392011-11-16 18:29:25 +0000522 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
523 HandleDisconnect();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700524 if (!GetHiddenSSIDList().empty()) {
525 // Before disconnecting, wpa_supplicant probably scanned for
526 // APs. So, in the normal case, we defer to the timer for the next scan.
527 //
528 // However, in the case of hidden SSIDs, supplicant knows about
529 // at most one of them. (That would be the hidden SSID we were
530 // connected to, if applicable.)
531 //
532 // So, in this case, we initiate an immediate scan. This scan
533 // will include the hidden SSIDs we know about (up to the limit of
534 // kScanMAxSSIDsPerScan).
535 //
536 // We may want to reconsider this immediate scan, if/when shill
537 // takes greater responsibility for scanning (vs. letting
538 // supplicant handle most of it).
539 Scan(NULL);
540 }
mukesh agrawal15908392011-11-16 18:29:25 +0000541 } else {
542 HandleRoam(new_bss);
543 }
544
Paul Stewart2b05e622012-07-13 20:38:44 -0700545 // If we are selecting a new service, or if we're clearing selection
546 // of a something other than the pending service, call SelectService.
547 // Otherwise skip SelectService, since this will cause the pending
548 // service to be marked as Idle.
549 if (current_service_ || selected_service() != pending_service_) {
550 SelectService(current_service_);
551 }
552
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000553 // Invariant check: a Service can either be current, or pending, but
554 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000555 CHECK(current_service_.get() != pending_service_.get() ||
556 current_service_.get() == NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000557}
558
559void WiFi::HandleDisconnect() {
560 // Identify the affected service. We expect to get a disconnect
561 // event when we fall off a Service that we were connected
562 // to. However, we also allow for the case where we get a disconnect
563 // event while attempting to connect from a disconnected state.
564 WiFiService *affected_service =
565 current_service_.get() ? current_service_.get() : pending_service_.get();
566
567 current_service_ = NULL;
568 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700569 SLOG(WiFi, 2) << "WiFi " << link_name()
570 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000571 return;
Paul Stewart20b0a092012-05-22 20:39:57 -0700572 } else if (affected_service == selected_service()) {
573 // If our selected service has disconnected, destroy IP configuration state.
574 DestroyIPConfig();
mukesh agrawal15908392011-11-16 18:29:25 +0000575 }
576
577 ReverseServiceMap::const_iterator rpcid_it =
578 rpcid_by_service_.find(affected_service);
579 if (rpcid_it == rpcid_by_service_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700580 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
581 << " (or failed to connect to) "
582 << affected_service->friendly_name() << ", "
583 << "but could not find supplicant network to disable.";
mukesh agrawalcf24a242012-05-21 16:46:11 -0700584 } else {
585 // TODO(quiche): Reconsider giving up immediately. Maybe give
586 // wpa_supplicant some time to retry, first.
587 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
mukesh agrawal15908392011-11-16 18:29:25 +0000588 }
589
Ben Chanfad4a0b2012-04-18 15:49:59 -0700590 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
591 << " (or failed to connect to) "
592 << affected_service->friendly_name();
mukesh agrawalcf24a242012-05-21 16:46:11 -0700593 if (SuspectCredentials(*affected_service)) {
594 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700595 // mole in Chrome.
Paul Stewartf2d60912012-07-15 08:37:30 -0700596 affected_service->SetFailure(Service::kFailureBadPassphrase);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700597 LOG(ERROR) << "Connection failure during 4-Way Handshake. Bad passphrase?";
Paul Stewartf2d60912012-07-15 08:37:30 -0700598 } else {
599 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700600 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800601 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000602 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000603
604 if (affected_service == pending_service_.get()) {
605 // The attempt to connect to |pending_service_| failed. Clear
606 // |pending_service_|, to indicate we're no longer in the middle
607 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700608 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000609 } else if (pending_service_.get()) {
610 // We've attributed the disconnection to what was the
611 // |current_service_|, rather than the |pending_service_|.
612 //
613 // If we're wrong about that (i.e. supplicant reported this
614 // CurrentBSS change after attempting to connect to
615 // |pending_service_|), we're depending on supplicant to retry
616 // connecting to |pending_service_|, and delivering another
617 // CurrentBSS change signal in the future.
618 //
619 // Log this fact, to help us debug (in case our assumptions are
620 // wrong).
Ben Chanfad4a0b2012-04-18 15:49:59 -0700621 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to "
622 << pending_service_->friendly_name()
623 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000624 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700625
626 // If we disconnect, initially scan at a faster frequency, to make sure
627 // we've found all available APs.
628 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000629}
630
631// We use the term "Roam" loosely. In particular, we include the case
632// where we "Roam" to a BSS from the disconnected state.
633void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
634 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
635 if (endpoint_it == endpoint_by_rpcid_.end()) {
636 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
637 << new_bss;
638 return;
639 }
640
641 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000642 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000643 if (!service.get()) {
644 LOG(WARNING) << "WiFi " << link_name()
645 << " could not find Service for Endpoint "
646 << endpoint.bssid_string()
647 << " (service will be unchanged)";
648 return;
649 }
650
Ben Chanfad4a0b2012-04-18 15:49:59 -0700651 SLOG(WiFi, 2) << "WiFi " << link_name()
652 << " roamed to Endpoint " << endpoint.bssid_string()
653 << " (SSID " << endpoint.ssid_string() << ")";
mukesh agrawal15908392011-11-16 18:29:25 +0000654
mukesh agrawale1d90e92012-02-15 17:36:08 -0800655 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000656
mukesh agrawal15908392011-11-16 18:29:25 +0000657 if (pending_service_.get() &&
658 service.get() != pending_service_.get()) {
659 // The Service we've roamed on to is not the one we asked for.
660 // We assume that this is transient, and that wpa_supplicant
661 // is trying / will try to connect to |pending_service_|.
662 //
663 // If it succeeds, we'll end up back here, but with |service|
664 // pointing at the same service as |pending_service_|.
665 //
666 // If it fails, we'll process things in HandleDisconnect.
667 //
668 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700669 SLOG(WiFi, 2) << "WiFi " << link_name()
670 << " new current Endpoint "
671 << endpoint.bssid_string()
672 << " is not part of pending service "
673 << pending_service_->friendly_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000674
675 // Sanity check: if we didn't roam onto |pending_service_|, we
676 // should still be on |current_service_|.
677 if (service.get() != current_service_.get()) {
678 LOG(WARNING) << "WiFi " << link_name()
679 << " new current Endpoint "
680 << endpoint.bssid_string()
681 << " is neither part of pending service "
682 << pending_service_->friendly_name()
683 << " nor part of current service "
684 << (current_service_.get() ?
685 current_service_->friendly_name() :
686 "(NULL)");
687 // Although we didn't expect to get here, we should keep
688 // |current_service_| in sync with what supplicant has done.
689 current_service_ = service;
690 }
691 return;
692 }
693
694 if (pending_service_.get()) {
695 // We assume service.get() == pending_service_.get() here, because
696 // of the return in the previous if clause.
697 //
698 // Boring case: we've connected to the service we asked
699 // for. Simply update |current_service_| and |pending_service_|.
700 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700701 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000702 return;
703 }
704
705 // |pending_service_| was NULL, so we weren't attempting to connect
706 // to a new Service. Sanity check that we're still on
707 // |current_service_|.
708 if (service.get() != current_service_.get()) {
709 LOG(WARNING)
710 << "WiFi " << link_name()
711 << " new current Endpoint "
712 << endpoint.bssid_string()
713 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000714 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000715 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000716 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000717 // We didn't expect to be here, but let's cope as well as we
718 // can. Update |current_service_| to keep it in sync with
719 // supplicant.
720 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700721
722 // If this service isn't already marked as actively connecting (likely,
723 // since this service is a bit of a surprise) set the service as
724 // associating.
725 if (!current_service_->IsConnecting()) {
726 current_service_->SetState(Service::kStateAssociating);
727 }
728
mukesh agrawal15908392011-11-16 18:29:25 +0000729 return;
730 }
731
732 // At this point, we know that |pending_service_| was NULL, and that
733 // we're still on |current_service_|. This is the most boring case
734 // of all, because there's no state to update here.
735 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700736}
737
Paul Stewarta41e38d2011-11-11 07:47:29 -0800738WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
739 const string &mode,
740 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800741 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
742 it != services_.end();
743 ++it) {
744 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
745 (*it)->IsSecurityMatch(security)) {
746 return *it;
747 }
748 }
749 return NULL;
750}
751
mukesh agrawal165e6142011-11-22 02:22:56 +0000752WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
753 return FindService(endpoint.ssid(),
754 endpoint.network_mode(),
755 endpoint.security_mode());
756}
757
Paul Stewartced6a0b2011-11-08 15:32:04 -0800758ByteArrays WiFi::GetHiddenSSIDList() {
759 // Create a unique set of hidden SSIDs.
760 set<ByteArray> hidden_ssids_set;
761 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
762 it != services_.end();
763 ++it) {
Paul Stewart10ccbb32012-04-26 15:59:30 -0700764 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800765 hidden_ssids_set.insert((*it)->ssid());
766 }
767 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700768 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800769 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
770 if (!hidden_ssids.empty()) {
771 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
772 // not fit in.
773 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
774 hidden_ssids.erase(
775 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
776 hidden_ssids.end());
777 }
778 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
779 // SSIDs to wpa_supplicant, we need to explicitly specify the default
780 // behavior of doing a broadcast probe.
781 hidden_ssids.push_back(ByteArray());
782 }
783 return hidden_ssids;
784}
785
Paul Stewarta41e38d2011-11-11 07:47:29 -0800786bool WiFi::LoadHiddenServices(StoreInterface *storage) {
787 bool created_hidden_service = false;
788 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
789 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
790 bool is_hidden = false;
791 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700792 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
793 << "GetGroupsWithKey failed for GetBool("
794 << flimflam::kWifiHiddenSsid
795 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800796 continue;
797 }
798 if (!is_hidden) {
799 continue;
800 }
801 string ssid_hex;
802 vector<uint8_t> ssid_bytes;
803 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
804 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700805 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
806 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800807 continue;
808 }
809 string device_address;
810 string network_mode;
811 string security;
812 // It is gross that we have to do this, but the only place we can
813 // get information about the service is from its storage name.
814 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
815 &network_mode, &security) ||
816 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700817 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
818 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800819 continue;
820 }
821 if (FindService(ssid_bytes, network_mode, security).get()) {
822 // If service already exists, we have nothing to do, since the
823 // service has already loaded its configuration from storage.
824 // This is guaranteed to happen in both cases where Load() is
825 // called on a device (via a ConfigureDevice() call on the
826 // profile):
827 // - In RegisterDevice() the Device hasn't been started yet,
828 // so it has no services registered, except for those
829 // created by previous iterations of LoadHiddenService().
830 // The latter can happen if another profile in the Manager's
831 // stack defines the same ssid/mode/security. Even this
832 // case is okay, since even if the profiles differ
833 // materially on configuration and credentials, the "right"
834 // one will be configured in the course of the
835 // RegisterService() call below.
836 // - In PushProfile(), all registered services have been
837 // introduced to the profile via ConfigureService() prior
838 // to calling ConfigureDevice() on the profile.
839 continue;
840 }
841 WiFiServiceRefPtr service(new WiFiService(control_interface(),
842 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800843 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800844 manager(),
845 this,
846 ssid_bytes,
847 network_mode,
848 security,
849 true));
850 services_.push_back(service);
851
852 // By registering the service, the rest of the configuration
853 // will be loaded from the profile into the service via ConfigureService().
854 manager()->RegisterService(service);
855
856 created_hidden_service = true;
857 }
858
859 // If we are idle and we created a service as a result of opening the
860 // profile, we should initiate a scan for our new hidden SSID.
861 if (running() && created_hidden_service &&
862 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
863 Scan(NULL);
864 }
865
866 return created_hidden_service;
867}
868
Paul Stewart66c86002012-01-30 18:00:52 -0800869void WiFi::ClearCachedCredentialsTask() {
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700870 try {
871 supplicant_interface_proxy_->ClearCachedCredentials();
Ben Chan80326f32012-05-04 17:51:32 -0700872 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700873 LOG(WARNING) << "Clear of cached credentials failed.";
874 }
Paul Stewart66c86002012-01-30 18:00:52 -0800875 clear_cached_credentials_pending_ = false;
876}
877
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000878void WiFi::BSSAddedTask(
879 const ::DBus::Path &path,
880 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000881 // Note: we assume that BSSIDs are unique across endpoints. This
882 // means that if an AP reuses the same BSSID for multiple SSIDs, we
883 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800884 WiFiEndpointRefPtr endpoint(
885 new WiFiEndpoint(proxy_factory_, this, path, properties));
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000886 LOG(INFO) << "Found endpoint. "
mukesh agrawale9adda12012-02-09 18:33:48 -0800887 << "RPC path: " << path << ", "
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000888 << "ssid: " << endpoint->ssid_string() << ", "
889 << "bssid: " << endpoint->bssid_string() << ", "
890 << "signal: " << endpoint->signal_strength() << ", "
Thieu Lee41a72d2012-02-06 20:46:51 +0000891 << "security: " << endpoint->security_mode() << ", "
892 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000893
894 if (endpoint->ssid_string().empty()) {
895 // Don't bother trying to find or create a Service for an Endpoint
896 // without an SSID. We wouldn't be able to connect to it anyway.
897 return;
898 }
899
mukesh agrawalb3857612012-01-18 16:23:29 -0800900 if (endpoint->ssid()[0] == 0) {
901 // Assume that an SSID starting with NULL is bogus/misconfigured,
902 // and filter it out.
903 return;
904 }
905
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000906 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
907 if (service) {
908 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
909 << " to service " << service->friendly_name() << ".";
910 service->AddEndpoint(endpoint);
911
912 if (manager()->HasService(service)) {
913 manager()->UpdateService(service);
914 } else {
Darin Petkov4a66cc52012-06-15 10:08:29 +0200915 // Expect registered by now if >1.
916 DCHECK_EQ(1, service->GetEndpointCount());
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000917 manager()->RegisterService(service);
918 }
mukesh agrawale9adda12012-02-09 18:33:48 -0800919 } else {
920 const bool hidden_ssid = false;
921 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
922 LOG(INFO) << "New service " << service->GetRpcIdentifier()
923 << " (" << service->friendly_name() << ")";
924 service->AddEndpoint(endpoint);
925 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000926 }
927
mukesh agrawale9adda12012-02-09 18:33:48 -0800928 // Do this last, to maintain the invariant that any Endpoint we
929 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800930 //
931 // TODO(quiche): Write test to verify correct behavior in the case
932 // where we get multiple BSSAdded events for a single endpoint.
933 // (Old Endpoint's refcount should fall to zero, and old Endpoint
934 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800935 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800936 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000937}
938
939void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
940 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
941 if (i == endpoint_by_rpcid_.end()) {
942 LOG(WARNING) << "WiFi " << link_name()
943 << " could not find BSS " << path
944 << " to remove.";
945 return;
946 }
947
948 WiFiEndpointRefPtr endpoint = i->second;
949 CHECK(endpoint);
950 endpoint_by_rpcid_.erase(i);
951
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000952 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -0800953 CHECK(service) << "Can't find Service for Endpoint "
954 << path << " "
955 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -0700956 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
957 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000958 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000959
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000960 bool disconnect_service = !service->HasEndpoints() &&
961 (service->IsConnecting() || service->IsConnected());
962 bool forget_service =
963 // Forget Services without Endpoints, except that we always keep
964 // hidden services around. (We need them around to populate the
965 // hidden SSIDs list.)
966 !service->HasEndpoints() && !service->hidden_ssid();
967 bool deregister_service =
968 // Only deregister a Service if we're forgetting it. Otherwise,
969 // Manager can't keep our configuration up-to-date (as Profiles
970 // change).
971 forget_service;
972
973 if (disconnect_service) {
974 DisconnectFrom(service);
975 }
976
977 if (deregister_service) {
978 manager()->DeregisterService(service);
979 } else {
980 manager()->UpdateService(service);
981 }
982
983 if (forget_service) {
984 vector<WiFiServiceRefPtr>::iterator it;
985 it = std::find(services_.begin(), services_.end(), service);
986 if (it != services_.end()) {
987 services_.erase(it);
988 }
989 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000990}
991
Paul Stewartbc6e7392012-05-24 07:07:48 -0700992void WiFi::CertificationTask(
993 const map<string, ::DBus::Variant> &properties) {
994 if (!current_service_) {
995 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
996 << " with no current service.";
997 return;
998 }
999
1000 map<string, ::DBus::Variant>::const_iterator properties_it =
1001 properties.find(wpa_supplicant::kInterfacePropertyDepth);
1002 if (properties_it == properties.end()) {
1003 LOG(ERROR) << __func__ << " no depth parameter.";
1004 return;
1005 }
1006 uint32 depth = properties_it->second.reader().get_uint32();
1007 properties_it = properties.find(wpa_supplicant::kInterfacePropertySubject);
1008 if (properties_it == properties.end()) {
1009 LOG(ERROR) << __func__ << " no subject parameter.";
1010 return;
1011 }
1012 string subject(properties_it->second.reader().get_string());
1013 current_service_->AddEAPCertification(subject, depth);
1014}
1015
mukesh agrawal15908392011-11-16 18:29:25 +00001016void WiFi::PropertiesChangedTask(
1017 const map<string, ::DBus::Variant> &properties) {
1018 // TODO(quiche): Handle changes in other properties (e.g. signal
1019 // strength).
1020
1021 // Note that order matters here. In particular, we want to process
1022 // changes in the current BSS before changes in state. This is so
1023 // that we update the state of the correct Endpoint/Service.
1024
1025 map<string, ::DBus::Variant>::const_iterator properties_it =
1026 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
1027 if (properties_it != properties.end()) {
1028 CurrentBSSChanged(properties_it->second.reader().get_path());
1029 }
1030
1031 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
1032 if (properties_it != properties.end()) {
1033 StateChanged(properties_it->second.reader().get_string());
1034 }
1035}
1036
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001037void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001038 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001039 if (need_bss_flush_) {
1040 CHECK(supplicant_interface_proxy_ != NULL);
1041 // Compute |max_age| relative to |resumed_at_|, to account for the
1042 // time taken to scan.
1043 struct timeval now;
1044 uint32_t max_age;
1045 time_->GetTimeMonotonic(&now);
1046 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1047 supplicant_interface_proxy_->FlushBSS(max_age);
1048 need_bss_flush_ = false;
1049 }
mukesh agrawalb54601c2011-06-07 17:39:22 -07001050 scan_pending_ = false;
mukesh agrawalb66c6462012-05-07 11:45:25 -07001051 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001052}
1053
mukesh agrawal32399322011-09-01 10:53:43 -07001054void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001055 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewarta41e38d2011-11-11 07:47:29 -08001056 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -07001057 scan_args[wpa_supplicant::kPropertyScanType].writer().
1058 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001059
1060 ByteArrays hidden_ssids = GetHiddenSSIDList();
1061 if (!hidden_ssids.empty()) {
1062 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
1063 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1064 }
1065
Gaurav Shahf8721ee2011-11-07 09:12:46 -08001066 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -08001067 try {
1068 supplicant_interface_proxy_->Scan(scan_args);
1069 scan_pending_ = true;
Ben Chan80326f32012-05-04 17:51:32 -07001070 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001071 // A scan may fail if, for example, the wpa_supplicant vanishing
1072 // notification is posted after this task has already started running.
1073 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001074 }
mukesh agrawal32399322011-09-01 10:53:43 -07001075}
1076
mukesh agrawal15908392011-11-16 18:29:25 +00001077void WiFi::StateChanged(const string &new_state) {
1078 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001079 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001080 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1081 << old_state << " -> " << new_state;
1082
1083 WiFiService *affected_service;
1084 // Identify the service to which the state change applies. If
1085 // |pending_service_| is non-NULL, then the state change applies to
1086 // |pending_service_|. Otherwise, it applies to |current_service_|.
1087 //
1088 // This policy is driven by the fact that the |pending_service_|
1089 // doesn't become the |current_service_| until wpa_supplicant
1090 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001091 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001092 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1093 affected_service =
1094 pending_service_.get() ? pending_service_.get() : current_service_.get();
1095 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001096 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1097 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001098 return;
1099 }
1100
Paul Stewart44663922012-07-30 11:03:03 -07001101 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
1102 if (affected_service->IsConnected()) {
1103 StopReconnectTimer();
1104 } else if (AcquireIPConfigWithLeaseName(
1105 affected_service->GetStorageIdentifier())) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001106 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001107 affected_service->SetState(Service::kStateConfiguring);
1108 } else {
1109 LOG(ERROR) << "Unable to acquire DHCP config.";
1110 }
mukesh agrawal15908392011-11-16 18:29:25 +00001111 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1112 affected_service->SetState(Service::kStateAssociating);
1113 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1114 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1115 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1116 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1117 // Ignore transitions into these states from Completed, to avoid
1118 // bothering the user when roaming, or re-keying.
1119 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1120 affected_service->SetState(Service::kStateAssociating);
1121 // TOOD(quiche): On backwards transitions, we should probably set
1122 // a timeout for getting back into the completed state. At present,
1123 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001124 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001125 // (crosbug.com/23207)
Paul Stewart44663922012-07-30 11:03:03 -07001126 } else if (new_state == wpa_supplicant::kInterfaceStateDisconnected &&
1127 affected_service == current_service_ &&
1128 affected_service->IsConnected()) {
1129 // This means that wpa_supplicant failed in a re-connect attempt, but
1130 // may still be reconnecting. Give wpa_supplicant a limited amount of
1131 // time to transition out this condition by either connecting or changing
1132 // CurrentBSS.
1133 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001134 } else {
1135 // Other transitions do not affect Service state.
1136 //
1137 // Note in particular that we ignore a State change into
1138 // kInterfaceStateDisconnected, in favor of observing the corresponding
1139 // change in CurrentBSS.
1140 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001141}
1142
mukesh agrawalcf24a242012-05-21 16:46:11 -07001143bool WiFi::SuspectCredentials(const WiFiService &service) const {
1144 if (!service.IsSecurityMatch(flimflam::kSecurityPsk)) {
1145 // We can only diagnose credentials for WPA/RSN networks. For
1146 // others, assume the failure was not credential related.
1147 return false;
1148 }
1149
1150 return supplicant_state_ == wpa_supplicant::kInterfaceState4WayHandshake &&
1151 !service.has_ever_connected();
1152}
1153
mukesh agrawal7ec71312011-11-10 02:08:26 +00001154// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001155WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001156 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001157
1158 if (args.ContainsString(flimflam::kModeProperty) &&
1159 args.GetString(flimflam::kModeProperty) !=
1160 flimflam::kModeManaged) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001161 Error::PopulateAndLog(error, Error::kNotSupported,
1162 kManagerErrorUnsupportedServiceMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001163 return NULL;
1164 }
1165
1166 if (!args.ContainsString(flimflam::kSSIDProperty)) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001167 Error::PopulateAndLog(error, Error::kInvalidArguments,
1168 kManagerErrorSSIDRequired);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001169 return NULL;
1170 }
1171
1172 string ssid = args.GetString(flimflam::kSSIDProperty);
1173 if (ssid.length() < 1) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001174 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1175 kManagerErrorSSIDTooShort);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001176 return NULL;
1177 }
1178
1179 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001180 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1181 kManagerErrorSSIDTooLong);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001182 return NULL;
1183 }
1184
1185 string security_method;
1186 if (args.ContainsString(flimflam::kSecurityProperty)) {
1187 security_method = args.GetString(flimflam::kSecurityProperty);
1188 } else {
1189 security_method = flimflam::kSecurityNone;
1190 }
1191
1192 if (security_method != flimflam::kSecurityNone &&
1193 security_method != flimflam::kSecurityWep &&
1194 security_method != flimflam::kSecurityPsk &&
1195 security_method != flimflam::kSecurityWpa &&
1196 security_method != flimflam::kSecurityRsn &&
1197 security_method != flimflam::kSecurity8021x) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001198 Error::PopulateAndLog(error, Error::kNotSupported,
1199 kManagerErrorUnsupportedSecurityMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001200 return NULL;
1201 }
1202
Paul Stewart6ab23a92011-11-09 17:17:47 -08001203 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001204 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1205 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001206 } else {
1207 // If the service is not found, and the caller hasn't specified otherwise,
1208 // we assume this is is a hidden network.
1209 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001210 }
1211
Paul Stewart6ab23a92011-11-09 17:17:47 -08001212 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1213 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1214 security_method));
1215 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001216 service = new WiFiService(control_interface(),
1217 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001218 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001219 manager(),
1220 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001221 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001222 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001223 security_method,
1224 hidden_ssid);
1225 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001226 // NB: We do not register the newly created Service with the Manager.
1227 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001228 }
1229
mukesh agrawalb172e4a2012-04-25 18:15:30 -07001230 if ((security_method == flimflam::kSecurityWep ||
1231 security_method == flimflam::kSecurityPsk ||
1232 security_method == flimflam::kSecurityWpa ||
1233 security_method == flimflam::kSecurityRsn) &&
1234 args.ContainsString(flimflam::kPassphraseProperty)) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001235 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
1236 error);
1237 if (error->IsFailure()) {
1238 return NULL;
1239 }
1240 }
1241
Paul Stewartcb59fed2012-03-21 21:14:46 -07001242 service->Configure(args, error);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001243
Paul Stewart7f61e522012-03-22 11:13:45 -07001244 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1245 // have no endpoints like the one we may have just created. crosbug.com/28224
1246
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001247 return service;
1248}
1249
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001250// static
1251bool WiFi::SanitizeSSID(string *ssid) {
1252 CHECK(ssid);
1253
1254 size_t ssid_len = ssid->length();
1255 size_t i;
1256 bool changed = false;
1257
Gary Morainac1bdb42012-02-16 17:42:29 -08001258 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001259 if (!g_ascii_isprint((*ssid)[i])) {
1260 (*ssid)[i] = '?';
1261 changed = true;
1262 }
1263 }
1264
1265 return changed;
1266}
1267
Paul Stewart3c508e12012-08-09 11:40:06 -07001268void WiFi::OnLinkMonitorFailure() {
1269 // If we have never found the gateway, let's be conservative and not
1270 // do anything, in case this network topology does not have a gateway.
1271 if (!link_monitor()->IsGatewayFound()) {
1272 LOG(INFO) << "In " << __func__ << "(): "
1273 << "Skipping reassociate since gateway was never found.";
1274 return;
1275 }
1276
1277 if (!supplicant_present_) {
1278 LOG(ERROR) << "In " << __func__ << "(): "
1279 << "wpa_supplicant is not present. Cannot reassociate.";
1280 return;
1281 }
1282
1283 try {
1284 supplicant_interface_proxy_->Reassociate();
1285 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1286 } catch (const DBus::Error &e) { // NOLINT
1287 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1288 return;
1289 }
1290}
1291
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001292void WiFi::HelpRegisterDerivedInt32(
1293 PropertyStore *store,
1294 const string &name,
1295 int32(WiFi::*get)(Error *error),
1296 void(WiFi::*set)(const int32 &value, Error *error)) {
1297 store->RegisterDerivedInt32(
1298 name,
1299 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1300}
1301
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001302void WiFi::HelpRegisterDerivedUint16(
1303 PropertyStore *store,
1304 const string &name,
1305 uint16(WiFi::*get)(Error *error),
1306 void(WiFi::*set)(const uint16 &value, Error *error)) {
1307 store->RegisterDerivedUint16(
1308 name,
1309 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1310}
1311
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001312void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001313 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001314 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001315
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001316 // We want to flush the BSS cache, but we don't want to conflict
1317 // with a running scan or an active connection attempt. So record
1318 // the need to flush, and take care of flushing when the next scan
1319 // completes.
1320 //
1321 // Note that supplicant will automatically expire old cache
1322 // entries (after, e.g., a BSS is not found in two consecutive
1323 // scans). However, our explicit flush accelerates re-association
1324 // in cases where a BSS disappeared while we were asleep. (See,
1325 // e.g. WiFiRoaming.005SuspendRoam.)
1326 time_->GetTimeMonotonic(&resumed_at_);
1327 need_bss_flush_ = true;
1328
1329 if (!scan_pending_ && IsIdle()) {
1330 // Not scanning/connecting/connected, so let's get things rolling.
1331 Scan(NULL);
1332 } else {
1333 SLOG(WiFi, 1) << __func__
1334 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001335 }
1336}
1337
Paul Stewarte369ece2012-05-22 09:11:03 -07001338void WiFi::RestartFastScanAttempts() {
1339 fast_scans_remaining_ = kNumFastScanAttempts;
1340 StartScanTimer();
1341}
1342
mukesh agrawalb66c6462012-05-07 11:45:25 -07001343void WiFi::StartScanTimer() {
1344 if (scan_interval_seconds_ == 0) {
1345 StopScanTimer();
1346 return;
1347 }
1348 scan_timer_callback_.Reset(
1349 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001350 // Repeat the first few scans after disconnect relatively quickly so we
1351 // have reasonable trust that no APs we are looking for are present.
1352 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1353 fast_scans_remaining_ > 0 ?
1354 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001355}
1356
1357void WiFi::StopScanTimer() {
1358 scan_timer_callback_.Cancel();
1359}
1360
1361void WiFi::ScanTimerHandler() {
1362 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1363 if (IsIdle() && !scan_pending_) {
1364 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001365 if (fast_scans_remaining_ > 0) {
1366 --fast_scans_remaining_;
1367 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001368 }
1369 StartScanTimer();
1370}
1371
Paul Stewart2b05e622012-07-13 20:38:44 -07001372void WiFi::StartPendingTimer() {
1373 pending_timeout_callback_.Reset(
1374 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1375 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1376 kPendingTimeoutSeconds * 1000);
1377}
1378
1379void WiFi::StopPendingTimer() {
1380 pending_timeout_callback_.Cancel();
1381}
1382
1383void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
1384 if (service) {
1385 service->SetState(Service::kStateAssociating);
1386 StartPendingTimer();
1387 } else if (pending_service_) {
1388 StopPendingTimer();
1389 }
1390 pending_service_ = service;
1391}
1392
1393void WiFi::PendingTimeoutHandler() {
1394 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1395 CHECK(pending_service_);
1396 pending_service_->SetFailure(Service::kFailureOutOfRange);
1397 DisconnectFrom(pending_service_);
1398}
1399
Paul Stewart44663922012-07-30 11:03:03 -07001400void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001401 if (!reconnect_timeout_callback_.IsCancelled()) {
1402 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1403 << ": reconnect timer already running.";
1404 return;
1405 }
Paul Stewart44663922012-07-30 11:03:03 -07001406 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1407 reconnect_timeout_callback_.Reset(
1408 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1409 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1410 kReconnectTimeoutSeconds * 1000);
1411}
1412
1413void WiFi::StopReconnectTimer() {
1414 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1415 reconnect_timeout_callback_.Cancel();
1416}
1417
1418void WiFi::ReconnectTimeoutHandler() {
1419 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001420 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001421 CHECK(current_service_);
1422 current_service_->SetFailureSilent(Service::kFailureConnect);
1423 DisconnectFrom(current_service_);
1424}
1425
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001426void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1427 LOG(INFO) << "WPA supplicant appeared.";
1428 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001429 // Restart the WiFi device if it's started already. This will reset the
1430 // state and connect the device to the new WPA supplicant instance.
1431 if (enabled()) {
1432 Restart();
1433 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001434 return;
1435 }
1436 supplicant_present_ = true;
1437 ConnectToSupplicant();
1438}
1439
1440void WiFi::OnSupplicantVanish() {
1441 LOG(INFO) << "WPA supplicant vanished.";
1442 if (!supplicant_present_) {
1443 return;
1444 }
1445 supplicant_present_ = false;
1446 // Restart the WiFi device if it's started already. This will effectively
1447 // suspend the device until the WPA supplicant reappears.
1448 if (enabled()) {
1449 Restart();
1450 }
1451}
1452
1453void WiFi::ConnectToSupplicant() {
1454 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1455 << " supplicant: "
1456 << (supplicant_present_ ? "present" : "absent")
1457 << " proxy: "
1458 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1459 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1460 return;
1461 }
1462 supplicant_process_proxy_.reset(
1463 proxy_factory_->CreateSupplicantProcessProxy(
1464 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
1465 ::DBus::Path interface_path;
1466 try {
1467 map<string, DBus::Variant> create_interface_args;
1468 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
1469 append_string(link_name().c_str());
1470 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
1471 append_string(wpa_supplicant::kDriverNL80211);
1472 create_interface_args[
1473 wpa_supplicant::kInterfacePropertyConfigFile].writer().
1474 append_string(SCRIPTDIR "/wpa_supplicant.conf");
1475 interface_path =
1476 supplicant_process_proxy_->CreateInterface(create_interface_args);
1477 } catch (const DBus::Error &e) { // NOLINT
1478 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
1479 interface_path =
1480 supplicant_process_proxy_->GetInterface(link_name());
1481 // TODO(quiche): Is it okay to crash here, if device is missing?
1482 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001483 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1484 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001485 }
1486 }
1487
1488 supplicant_interface_proxy_.reset(
1489 proxy_factory_->CreateSupplicantInterfaceProxy(
1490 this, interface_path, wpa_supplicant::kDBusAddr));
1491
1492 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1493 IFF_UP);
1494 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1495
1496 // Clear out any networks that might previously have been configured
1497 // for this interface.
1498 supplicant_interface_proxy_->RemoveAllNetworks();
1499
1500 // Flush interface's BSS cache, so that we get BSSAdded signals for
1501 // all BSSes (not just new ones since the last scan).
1502 supplicant_interface_proxy_->FlushBSS(0);
1503
1504 try {
1505 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1506 // with RADIUS servers that respond strangely to such requests.
1507 // crosbug.com/25630
1508 supplicant_interface_proxy_->SetFastReauth(false);
1509 } catch (const DBus::Error &e) { // NOLINT
1510 LOG(INFO) << "Failed to disable fast_reauth."
1511 << "May be running an older version of wpa_supplicant.";
1512 }
1513
1514 try {
1515 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1516 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1517 } catch (const DBus::Error &e) { // NOLINT
1518 LOG(INFO) << "Failed to set scan_interval. "
1519 << "May be running an older version of wpa_supplicant.";
1520 }
1521
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001522 Scan(NULL);
1523 StartScanTimer();
1524}
1525
1526void WiFi::Restart() {
1527 LOG(INFO) << link_name() << " restarting.";
1528 WiFiRefPtr me = this; // Make sure we don't get destructed.
1529 // Go through the manager rather than starting and stopping the device
1530 // directly so that the device can be configured with the profile.
1531 manager()->DeregisterDevice(me);
1532 manager()->RegisterDevice(me);
1533}
1534
Paul Stewartb50f0b92011-05-16 16:31:42 -07001535} // namespace shill