1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/wifi/wifi.h"
18
19 #include <linux/if.h> // Needs definitions from netinet/ether.h
20 #include <netinet/ether.h>
21 #include <stdio.h>
22 #include <string.h>
23
24 #include <limits>
25 #include <map>
26 #include <set>
27 #include <string>
28 #include <vector>
29
30 #include <base/bind.h>
31 #include <base/files/file_util.h>
32 #include <base/files/file_path.h>
33 #include <base/strings/string_util.h>
34 #include <base/strings/stringprintf.h>
35 #if defined(__ANDROID__)
36 #include <dbus/service_constants.h>
37 #else
38 #include <chromeos/dbus/service_constants.h>
39 #endif // __ANDROID__
40
41 #include "shill/control_interface.h"
42 #include "shill/device.h"
43 #include "shill/eap_credentials.h"
44 #include "shill/error.h"
45 #include "shill/file_reader.h"
46 #include "shill/geolocation_info.h"
47 #include "shill/link_monitor.h"
48 #include "shill/logging.h"
49 #include "shill/manager.h"
50 #include "shill/metrics.h"
51 #include "shill/net/ieee80211.h"
52 #include "shill/net/ip_address.h"
53 #include "shill/net/netlink_manager.h"
54 #include "shill/net/netlink_message.h"
55 #include "shill/net/nl80211_message.h"
56 #include "shill/net/rtnl_handler.h"
57 #include "shill/net/shill_time.h"
58 #include "shill/property_accessor.h"
59 #include "shill/scope_logger.h"
60 #include "shill/supplicant/supplicant_eap_state_handler.h"
61 #include "shill/supplicant/supplicant_interface_proxy_interface.h"
62 #include "shill/supplicant/supplicant_network_proxy_interface.h"
63 #include "shill/supplicant/supplicant_process_proxy_interface.h"
64 #include "shill/supplicant/wpa_supplicant.h"
65 #include "shill/technology.h"
66 #include "shill/wifi/mac80211_monitor.h"
67 #include "shill/wifi/scan_session.h"
68 #include "shill/wifi/tdls_manager.h"
69 #include "shill/wifi/wake_on_wifi.h"
70 #include "shill/wifi/wifi_endpoint.h"
71 #include "shill/wifi/wifi_provider.h"
72 #include "shill/wifi/wifi_service.h"
73
74 using base::Bind;
75 using base::FilePath;
76 using base::StringPrintf;
77 using std::map;
78 using std::set;
79 using std::string;
80 using std::vector;
81
82 namespace shill {
83
84 namespace Logging {
85 static auto kModuleLogScope = ScopeLogger::kWiFi;
ObjectID(WiFi * w)86 static string ObjectID(WiFi* w) { return w->GetRpcIdentifier(); }
87 }
88
89 // statics
90 const char* WiFi::kDefaultBgscanMethod =
91 WPASupplicant::kNetworkBgscanMethodSimple;
92 const uint16_t WiFi::kDefaultBgscanShortIntervalSeconds = 30;
93 const int32_t WiFi::kDefaultBgscanSignalThresholdDbm = -50;
94 const uint16_t WiFi::kDefaultScanIntervalSeconds = 60;
95 const uint16_t WiFi::kDefaultRoamThresholdDb = 18; // Supplicant's default.
96
97 // Scan interval while connected.
98 const uint16_t WiFi::kBackgroundScanIntervalSeconds = 3601;
99 // Age (in seconds) beyond which a BSS cache entry will not be preserved,
100 // across a suspend/resume.
101 const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
102 const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
103 const time_t WiFi::kRescanIntervalSeconds = 1;
104 const int WiFi::kNumFastScanAttempts = 3;
105 const int WiFi::kFastScanIntervalSeconds = 10;
106 const int WiFi::kPendingTimeoutSeconds = 15;
107 const int WiFi::kReconnectTimeoutSeconds = 10;
108 const int WiFi::kRequestStationInfoPeriodSeconds = 20;
109 const size_t WiFi::kMinumumFrequenciesToScan = 4; // Arbitrary but > 0.
110 const float WiFi::kDefaultFractionPerScan = 0.34;
111 const size_t WiFi::kStuckQueueLengthThreshold = 40; // ~1 full-channel scan
112 // 1 second is less than the time it takes to scan and establish a new
113 // connection after waking, but should be enough time for supplicant to update
114 // its state.
115 const int WiFi::kPostWakeConnectivityReportDelayMilliseconds = 1000;
116 const uint32_t WiFi::kDefaultWiphyIndex = UINT32_MAX;
117 const int WiFi::kPostScanFailedDelayMilliseconds = 10000;
118 // Invalid 802.11 disconnect reason code.
119 const int WiFi::kDefaultDisconnectReason = INT32_MAX;
120
121 namespace {
IsPrintableAsciiChar(char c)122 bool IsPrintableAsciiChar(char c) {
123 return (c >= ' ' && c <= '~');
124 }
125 } // namespace
126
WiFi(ControlInterface * control_interface,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager,const string & link,const string & address,int interface_index)127 WiFi::WiFi(ControlInterface* control_interface,
128 EventDispatcher* dispatcher,
129 Metrics* metrics,
130 Manager* manager,
131 const string& link,
132 const string& address,
133 int interface_index)
134 : Device(control_interface,
135 dispatcher,
136 metrics,
137 manager,
138 link,
139 address,
140 interface_index,
141 Technology::kWifi),
142 provider_(manager->wifi_provider()),
143 weak_ptr_factory_(this),
144 time_(Time::GetInstance()),
145 supplicant_present_(false),
146 supplicant_process_proxy_(
147 control_interface->CreateSupplicantProcessProxy(
148 Bind(&WiFi::OnSupplicantAppear, Unretained(this)),
149 Bind(&WiFi::OnSupplicantVanish, Unretained(this)))),
150 supplicant_state_(kInterfaceStateUnknown),
151 supplicant_bss_("(unknown)"),
152 supplicant_disconnect_reason_(kDefaultDisconnectReason),
153 need_bss_flush_(false),
154 resumed_at_((struct timeval){0}),
155 fast_scans_remaining_(kNumFastScanAttempts),
156 has_already_completed_(false),
157 is_roaming_in_progress_(false),
158 is_debugging_connection_(false),
159 eap_state_handler_(new SupplicantEAPStateHandler()),
160 mac80211_monitor_(new Mac80211Monitor(
161 dispatcher,
162 link,
163 kStuckQueueLengthThreshold,
164 base::Bind(&WiFi::RestartFastScanAttempts,
165 weak_ptr_factory_.GetWeakPtr()),
166 metrics)),
167 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
168 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
169 roam_threshold_db_(kDefaultRoamThresholdDb),
170 scan_interval_seconds_(kDefaultScanIntervalSeconds),
171 progressive_scan_enabled_(false),
172 scan_configuration_("Full scan"),
173 netlink_manager_(NetlinkManager::GetInstance()),
174 min_frequencies_to_scan_(kMinumumFrequenciesToScan),
175 max_frequencies_to_scan_(std::numeric_limits<int>::max()),
176 scan_all_frequencies_(true),
177 fraction_per_scan_(kDefaultFractionPerScan),
178 scan_state_(kScanIdle),
179 scan_method_(kScanMethodNone),
180 receive_byte_count_at_connect_(0),
181 wiphy_index_(kDefaultWiphyIndex),
182 wake_on_wifi_(new WakeOnWiFi(netlink_manager_,
183 dispatcher,
184 metrics,
185 Bind(&Manager::RecordDarkResumeWakeReason,
186 manager->AsWeakPtr()))) {
187 PropertyStore* store = this->mutable_store();
188 store->RegisterDerivedString(
189 kBgscanMethodProperty,
190 StringAccessor(
191 // TODO(petkov): CustomMappedAccessor is used for convenience because
192 // it provides a way to define a custom clearer (unlike
193 // CustomAccessor). We need to implement a fully custom accessor with
194 // no extra argument.
195 new CustomMappedAccessor<WiFi, string, int>(this,
196 &WiFi::ClearBgscanMethod,
197 &WiFi::GetBgscanMethod,
198 &WiFi::SetBgscanMethod,
199 0))); // Unused.
200 HelpRegisterDerivedUint16(store,
201 kBgscanShortIntervalProperty,
202 &WiFi::GetBgscanShortInterval,
203 &WiFi::SetBgscanShortInterval);
204 HelpRegisterDerivedInt32(store,
205 kBgscanSignalThresholdProperty,
206 &WiFi::GetBgscanSignalThreshold,
207 &WiFi::SetBgscanSignalThreshold);
208
209 store->RegisterDerivedKeyValueStore(
210 kLinkStatisticsProperty,
211 KeyValueStoreAccessor(
212 new CustomAccessor<WiFi, KeyValueStore>(
213 this, &WiFi::GetLinkStatistics, nullptr)));
214
215 // TODO(quiche): Decide if scan_pending_ is close enough to
216 // "currently scanning" that we don't care, or if we want to track
217 // scan pending/currently scanning/no scan scheduled as a tri-state
218 // kind of thing.
219 HelpRegisterConstDerivedBool(store,
220 kScanningProperty,
221 &WiFi::GetScanPending);
222 HelpRegisterDerivedUint16(store,
223 kRoamThresholdProperty,
224 &WiFi::GetRoamThreshold,
225 &WiFi::SetRoamThreshold);
226 HelpRegisterDerivedUint16(store,
227 kScanIntervalProperty,
228 &WiFi::GetScanInterval,
229 &WiFi::SetScanInterval);
230 wake_on_wifi_->InitPropertyStore(store);
231 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
232 ScopeLogger::kWiFi,
233 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
234 CHECK(netlink_manager_);
235 netlink_manager_->AddBroadcastHandler(Bind(
236 &WiFi::OnScanStarted, weak_ptr_factory_.GetWeakPtr()));
237 SLOG(this, 2) << "WiFi device " << link_name() << " initialized.";
238 }
239
~WiFi()240 WiFi::~WiFi() {}
241
Start(Error * error,const EnabledStateChangedCallback &)242 void WiFi::Start(Error* error,
243 const EnabledStateChangedCallback& /*callback*/) {
244 SLOG(this, 2) << "WiFi " << link_name() << " starting.";
245 if (enabled()) {
246 return;
247 }
248 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
249 if (error) {
250 error->Reset(); // indicate immediate completion
251 }
252
253 // Subscribe to multicast events.
254 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
255 NetlinkManager::kEventTypeConfig);
256 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
257 NetlinkManager::kEventTypeScan);
258 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
259 NetlinkManager::kEventTypeRegulatory);
260 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
261 NetlinkManager::kEventTypeMlme);
262 GetPhyInfo();
263 // Connect to WPA supplicant if it's already present. If not, we'll connect to
264 // it when it appears.
265 ConnectToSupplicant();
266 wake_on_wifi_->StartMetricsTimer();
267 }
268
Stop(Error * error,const EnabledStateChangedCallback &)269 void WiFi::Stop(Error* error, const EnabledStateChangedCallback& /*callback*/) {
270 SLOG(this, 2) << "WiFi " << link_name() << " stopping.";
271 // Unlike other devices, we leave the DBus name watcher in place here, because
272 // WiFi callbacks expect notifications even if the device is disabled.
273 DropConnection();
274 StopScanTimer();
275 for (const auto& endpoint : endpoint_by_rpcid_) {
276 provider_->OnEndpointRemoved(endpoint.second);
277 }
278 endpoint_by_rpcid_.clear();
279 for (const auto& map_entry : rpcid_by_service_) {
280 RemoveNetwork(map_entry.second);
281 }
282 rpcid_by_service_.clear();
283 // Remove interface from supplicant.
284 if (supplicant_present_ &&
285 supplicant_interface_proxy_) {
286 supplicant_process_proxy_->RemoveInterface(supplicant_interface_path_);
287 }
288 supplicant_interface_path_ = "";
289 SetSupplicantInterfaceProxy(nullptr);
290 pending_scan_results_.reset();
291 tdls_manager_.reset();
292 current_service_ = nullptr; // breaks a reference cycle
293 pending_service_ = nullptr; // breaks a reference cycle
294 is_debugging_connection_ = false;
295 SetScanState(kScanIdle, kScanMethodNone, __func__);
296 StopPendingTimer();
297 StopReconnectTimer();
298 StopRequestingStationInfo();
299 mac80211_monitor_->Stop();
300
301 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
302 if (error)
303 error->Reset(); // indicate immediate completion
304 weak_ptr_factory_.InvalidateWeakPtrs();
305
306 SLOG(this, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
307 << (supplicant_process_proxy_.get() ?
308 "is set." : "is not set.");
309 SLOG(this, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
310 << (supplicant_interface_proxy_.get() ?
311 "is set." : "is not set.");
312 SLOG(this, 3) << "WiFi " << link_name() << " pending_service_ "
313 << (pending_service_.get() ? "is set." : "is not set.");
314 SLOG(this, 3) << "WiFi " << link_name() << " has "
315 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
316 }
317
Scan(ScanType scan_type,Error *,const string & reason)318 void WiFi::Scan(ScanType scan_type, Error* /*error*/, const string& reason) {
319 if ((scan_state_ != kScanIdle) ||
320 (current_service_.get() && current_service_->IsConnecting())) {
321 SLOG(this, 2) << "Ignoring scan request while scanning or connecting.";
322 return;
323 }
324 if (progressive_scan_enabled_ && scan_type == kProgressiveScan) {
325 LOG(INFO) << __func__ << " [progressive] on " << link_name() << " from "
326 << reason;
327 LOG(INFO) << scan_configuration_;
328 if (!scan_session_) {
329 // TODO(wdg): Perform in-depth testing to determine the best values for
330 // the different scans. chromium:235293
331 ScanSession::FractionList scan_fractions;
332 float total_fraction = 0.0;
333 do {
334 total_fraction += fraction_per_scan_;
335 scan_fractions.push_back(fraction_per_scan_);
336 } while (total_fraction < 1.0);
337 scan_session_.reset(
338 new ScanSession(netlink_manager_,
339 dispatcher(),
340 provider_->GetScanFrequencies(),
341 (scan_all_frequencies_ ? all_scan_frequencies_ :
342 set<uint16_t>()),
343 interface_index(),
344 scan_fractions,
345 min_frequencies_to_scan_,
346 max_frequencies_to_scan_,
347 Bind(&WiFi::OnFailedProgressiveScan,
348 weak_ptr_factory_.GetWeakPtr()),
349 metrics()));
350 for (const auto& ssid : provider_->GetHiddenSSIDList()) {
351 scan_session_->AddSsid(ByteString(&ssid.front(), ssid.size()));
352 }
353 }
354 dispatcher()->PostTask(
355 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
356 } else {
357 LOG(INFO) << __func__ << " [full] on " << link_name()
358 << " (progressive scan "
359 << (progressive_scan_enabled_ ? "ENABLED" : "DISABLED")
360 << ") from " << reason;
361 // Needs to send a D-Bus message, but may be called from D-Bus
362 // signal handler context (via Manager::RequestScan). So defer work
363 // to event loop.
364 dispatcher()->PostTask(
365 Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
366 }
367 }
368
SetSchedScan(bool enable,Error *)369 void WiFi::SetSchedScan(bool enable, Error* /*error*/) {
370 // Needs to send a D-Bus message, but may be called from D-Bus
371 // signal handler context (via Manager::SetSchedScan). So defer work
372 // to event loop.
373 dispatcher()->PostTask(
374 Bind(&WiFi::SetSchedScanTask, weak_ptr_factory_.GetWeakPtr(), enable));
375 }
376
AddPendingScanResult(const string & path,const KeyValueStore & properties,bool is_removal)377 void WiFi::AddPendingScanResult(const string& path,
378 const KeyValueStore& properties,
379 bool is_removal) {
380 if (!pending_scan_results_) {
381 pending_scan_results_.reset(new PendingScanResults(
382 Bind(&WiFi::PendingScanResultsHandler,
383 weak_ptr_factory_.GetWeakPtr())));
384 dispatcher()->PostTask(pending_scan_results_->callback.callback());
385 }
386 pending_scan_results_->results.emplace_back(path, properties, is_removal);
387 }
388
BSSAdded(const string & path,const KeyValueStore & properties)389 void WiFi::BSSAdded(const string& path, const KeyValueStore& properties) {
390 // Called from a D-Bus signal handler, and may need to send a D-Bus
391 // message. So defer work to event loop.
392 AddPendingScanResult(path, properties, false);
393 }
394
BSSRemoved(const string & path)395 void WiFi::BSSRemoved(const string& path) {
396 // Called from a D-Bus signal handler, and may need to send a D-Bus
397 // message. So defer work to event loop.
398 AddPendingScanResult(path, {}, true);
399 }
400
Certification(const KeyValueStore & properties)401 void WiFi::Certification(const KeyValueStore& properties) {
402 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
403 weak_ptr_factory_.GetWeakPtr(), properties));
404 }
405
EAPEvent(const string & status,const string & parameter)406 void WiFi::EAPEvent(const string& status, const string& parameter) {
407 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
408 weak_ptr_factory_.GetWeakPtr(),
409 status,
410 parameter));
411 }
412
PropertiesChanged(const KeyValueStore & properties)413 void WiFi::PropertiesChanged(const KeyValueStore& properties) {
414 SLOG(this, 2) << __func__;
415 // Called from D-Bus signal handler, but may need to send a D-Bus
416 // message. So defer work to event loop.
417 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
418 weak_ptr_factory_.GetWeakPtr(), properties));
419 }
420
ScanDone(const bool & success)421 void WiFi::ScanDone(const bool& success) {
422 LOG(INFO) << __func__;
423
424 // Defer handling of scan result processing, because that processing
425 // may require the the registration of new D-Bus objects. And such
426 // registration can't be done in the context of a D-Bus signal
427 // handler.
428 if (pending_scan_results_) {
429 pending_scan_results_->is_complete = true;
430 return;
431 }
432 if (success) {
433 scan_failed_callback_.Cancel();
434 dispatcher()->PostTask(
435 Bind(&WiFi::ScanDoneTask, weak_ptr_factory_.GetWeakPtr()));
436 } else {
437 scan_failed_callback_.Reset(
438 Bind(&WiFi::ScanFailedTask, weak_ptr_factory_.GetWeakPtr()));
439 dispatcher()->PostDelayedTask(scan_failed_callback_.callback(),
440 kPostScanFailedDelayMilliseconds);
441 }
442 }
443
ConnectTo(WiFiService * service)444 void WiFi::ConnectTo(WiFiService* service) {
445 CHECK(service) << "Can't connect to NULL service.";
446 string network_path;
447
448 // Ignore this connection attempt if suppplicant is not present.
449 // This is possible when we try to connect right after WiFi
450 // boostrapping is completed (through weaved). Refer to b/24605760
451 // for more information.
452 // Once supplicant is detected, shill will auto-connect to this
453 // service (if this service is configured for auto-connect) when
454 // it is discovered in the scan.
455 if (!supplicant_present_) {
456 LOG(ERROR) << "Trying to connect before supplicant is present";
457 return;
458 }
459
460 // TODO(quiche): Handle cases where already connected.
461 if (pending_service_ && pending_service_ == service) {
462 // TODO(quiche): Return an error to the caller. crbug.com/206812
463 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
464 << service->unique_name()
465 << ", which is already pending.";
466 return;
467 }
468
469 if (pending_service_ && pending_service_ != service) {
470 LOG(INFO) << "Connecting to service. "
471 << LogSSID(service->unique_name()) << ", "
472 << "bssid: " << service->bssid() << ", "
473 << "mode: " << service->mode() << ", "
474 << "key management: " << service->key_management() << ", "
475 << "physical mode: " << service->physical_mode() << ", "
476 << "frequency: " << service->frequency();
477 // This is a signal to SetPendingService(nullptr) to not modify the scan
478 // state since the overall story arc isn't reflected by the disconnect.
479 // It is, instead, described by the transition to either kScanFoundNothing
480 // or kScanConnecting (made by |SetPendingService|, below).
481 if (scan_method_ != kScanMethodNone) {
482 SetScanState(kScanTransitionToConnecting, scan_method_, __func__);
483 }
484 // Explicitly disconnect pending service.
485 pending_service_->set_expecting_disconnect(true);
486 DisconnectFrom(pending_service_.get());
487 }
488
489 Error unused_error;
490 network_path = FindNetworkRpcidForService(service, &unused_error);
491 if (network_path.empty()) {
492 KeyValueStore service_params =
493 service->GetSupplicantConfigurationParameters();
494 const uint32_t scan_ssid = 1; // "True": Use directed probe.
495 service_params.SetUint(WPASupplicant::kNetworkPropertyScanSSID, scan_ssid);
496 AppendBgscan(service, &service_params);
497 service_params.SetUint(WPASupplicant::kNetworkPropertyDisableVHT,
498 provider_->disable_vht());
499 if (!supplicant_interface_proxy_->AddNetwork(service_params,
500 &network_path)) {
501 LOG(ERROR) << "Failed to add network";
502 SetScanState(kScanIdle, scan_method_, __func__);
503 return;
504 }
505 CHECK(!network_path.empty()); // No DBus path should be empty.
506 rpcid_by_service_[service] = network_path;
507 }
508
509 if (service->HasRecentConnectionIssues()) {
510 SetConnectionDebugging(true);
511 }
512
513 // Enable HT40 for this network in case if it was disabled previously due to
514 // unreliable link.
515 supplicant_interface_proxy_->SetHT40Enable(network_path, true);
516
517 supplicant_interface_proxy_->SelectNetwork(network_path);
518 SetPendingService(service);
519 CHECK(current_service_.get() != pending_service_.get());
520
521 // SelectService here (instead of in LinkEvent, like Ethernet), so
522 // that, if we fail to bring up L2, we can attribute failure correctly.
523 //
524 // TODO(quiche): When we add code for dealing with connection failures,
525 // reconsider if this is the right place to change the selected service.
526 // see discussion in crbug.com/203282.
527 SelectService(service);
528 }
529
DisconnectFromIfActive(WiFiService * service)530 void WiFi::DisconnectFromIfActive(WiFiService* service) {
531 SLOG(this, 2) << __func__ << " service " << service->unique_name();
532
533 if (service != current_service_ && service != pending_service_) {
534 if (!service->IsActive(nullptr)) {
535 SLOG(this, 2) << "In " << __func__ << "(): service "
536 << service->unique_name()
537 << " is not active, no need to initiate disconnect";
538 return;
539 }
540 }
541
542 DisconnectFrom(service);
543 }
544
DisconnectFrom(WiFiService * service)545 void WiFi::DisconnectFrom(WiFiService* service) {
546 SLOG(this, 2) << __func__ << " service " << service->unique_name();
547
548 if (service != current_service_ && service != pending_service_) {
549 // TODO(quiche): Once we have asynchronous reply support, we should
550 // generate a D-Bus error here. (crbug.com/206812)
551 LOG(WARNING) << "In " << __func__ << "(): "
552 << " ignoring request to disconnect from service "
553 << service->unique_name()
554 << " which is neither current nor pending";
555 return;
556 }
557
558 if (pending_service_ && service != pending_service_) {
559 // TODO(quiche): Once we have asynchronous reply support, we should
560 // generate a D-Bus error here. (crbug.com/206812)
561 LOG(WARNING) << "In " << __func__ << "(): "
562 << " ignoring request to disconnect from service "
563 << service->unique_name()
564 << " which is not the pending service.";
565 return;
566 }
567
568 if (!pending_service_ && service != current_service_) {
569 // TODO(quiche): Once we have asynchronous reply support, we should
570 // generate a D-Bus error here. (crbug.com/206812)
571 LOG(WARNING) << "In " << __func__ << "(): "
572 << " ignoring request to disconnect from service "
573 << service->unique_name()
574 << " which is not the current service.";
575 return;
576 }
577
578 if (pending_service_) {
579 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
580 // on this to drive the service state back to idle. Do that here.
581 // Update service state for pending service.
582 ServiceDisconnected(pending_service_);
583 }
584
585 SetPendingService(nullptr);
586 StopReconnectTimer();
587 StopRequestingStationInfo();
588
589 if (!supplicant_present_) {
590 LOG(ERROR) << "In " << __func__ << "(): "
591 << "wpa_supplicant is not present; silently resetting "
592 << "current_service_.";
593 if (current_service_ == selected_service()) {
594 DropConnection();
595 }
596 current_service_ = nullptr;
597 return;
598 }
599
600 bool disconnect_in_progress = true;
601 // We'll call RemoveNetwork and reset |current_service_| after
602 // supplicant notifies us that the CurrentBSS has changed.
603 if (!supplicant_interface_proxy_->Disconnect()) {
604 disconnect_in_progress = false;
605 }
606
607 if (supplicant_state_ != WPASupplicant::kInterfaceStateCompleted ||
608 !disconnect_in_progress) {
609 // Can't depend on getting a notification of CurrentBSS change.
610 // So effect changes immediately. For instance, this can happen when
611 // a disconnect is triggered by a BSS going away.
612 Error unused_error;
613 RemoveNetworkForService(service, &unused_error);
614 if (service == selected_service()) {
615 DropConnection();
616 } else {
617 SLOG(this, 5) << __func__ << " skipping DropConnection, "
618 << "selected_service is "
619 << (selected_service() ?
620 selected_service()->unique_name() : "(null)");
621 }
622 current_service_ = nullptr;
623 }
624
625 CHECK(current_service_ == nullptr ||
626 current_service_.get() != pending_service_.get());
627 }
628
DisableNetwork(const string & network)629 bool WiFi::DisableNetwork(const string& network) {
630 std::unique_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
631 control_interface()->CreateSupplicantNetworkProxy(network));
632 if (!supplicant_network_proxy->SetEnabled(false)) {
633 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
634 return false;
635 }
636 return true;
637 }
638
RemoveNetwork(const string & network)639 bool WiFi::RemoveNetwork(const string& network) {
640 return supplicant_interface_proxy_->RemoveNetwork(network);
641 }
642
SetHT40EnableForService(const WiFiService * service,bool enable)643 void WiFi::SetHT40EnableForService(const WiFiService* service, bool enable) {
644 if (!supplicant_present_) {
645 LOG(ERROR) << "In " << __func__ << "(): "
646 << "wpa_supplicant is not present. Cannot SetHT40Enable.";
647 return;
648 }
649
650 Error error;
651 string rpcid = FindNetworkRpcidForService(service, &error);
652 if (rpcid.empty()) {
653 LOG(ERROR) << "Unable to find supplicant network.";
654 return;
655 }
656
657 if (!supplicant_interface_proxy_->SetHT40Enable(rpcid, enable)) {
658 LOG(ERROR) << "SetHT40Enable for " << rpcid << " failed.";
659 }
660 }
661
IsIdle() const662 bool WiFi::IsIdle() const {
663 return !current_service_ && !pending_service_;
664 }
665
ClearCachedCredentials(const WiFiService * service)666 void WiFi::ClearCachedCredentials(const WiFiService* service) {
667 Error unused_error;
668 RemoveNetworkForService(service, &unused_error);
669
670 // Give up on the connection attempt for the pending service immediately since
671 // the credential for it had already changed. This will allow the Manager to
672 // start a new connection attempt for the pending service immediately without
673 // waiting for the pending connection timeout.
674 // current_service_ will get disconnect notification from the CurrentBSS
675 // change event, so no need to explicitly disconnect here.
676 if (service == pending_service_) {
677 LOG(INFO) << "Disconnect pending service: credential changed";
678 DisconnectFrom(pending_service_.get());
679 }
680 }
681
NotifyEndpointChanged(const WiFiEndpointConstRefPtr & endpoint)682 void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr& endpoint) {
683 provider_->OnEndpointUpdated(endpoint);
684 }
685
AppendBgscan(WiFiService * service,KeyValueStore * service_params) const686 void WiFi::AppendBgscan(WiFiService* service,
687 KeyValueStore* service_params) const {
688 int scan_interval = kBackgroundScanIntervalSeconds;
689 string method = bgscan_method_;
690 if (method.empty()) {
691 // If multiple APs are detected for this SSID, configure the default method.
692 // Otherwise, disable background scanning completely.
693 if (service->GetEndpointCount() > 1) {
694 method = kDefaultBgscanMethod;
695 } else {
696 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
697 return;
698 }
699 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
700 LOG(INFO) << "Background scan disabled -- chose None method.";
701 return;
702 } else {
703 // If the background scan method was explicitly specified, honor the
704 // configured background scan interval.
705 scan_interval = scan_interval_seconds_;
706 }
707 DCHECK(!method.empty());
708 string config_string = StringPrintf("%s:%d:%d:%d",
709 method.c_str(),
710 bgscan_short_interval_seconds_,
711 bgscan_signal_threshold_dbm_,
712 scan_interval);
713 LOG(INFO) << "Background scan: " << config_string;
714 service_params->SetString(WPASupplicant::kNetworkPropertyBgscan,
715 config_string);
716 }
717
GetBgscanMethod(const int &,Error *)718 string WiFi::GetBgscanMethod(const int& /*argument*/, Error* /* error */) {
719 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
720 }
721
SetBgscanMethod(const int &,const string & method,Error * error)722 bool WiFi::SetBgscanMethod(
723 const int& /*argument*/, const string& method, Error* error) {
724 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
725 method != WPASupplicant::kNetworkBgscanMethodLearn &&
726 method != WPASupplicant::kNetworkBgscanMethodNone) {
727 const string error_message =
728 StringPrintf("Unrecognized bgscan method %s", method.c_str());
729 LOG(WARNING) << error_message;
730 error->Populate(Error::kInvalidArguments, error_message);
731 return false;
732 }
733 if (bgscan_method_ == method) {
734 return false;
735 }
736 bgscan_method_ = method;
737 // We do not update kNetworkPropertyBgscan for |pending_service_| or
738 // |current_service_|, because supplicant does not allow for
739 // reconfiguration without disconnect and reconnect.
740 return true;
741 }
742
SetBgscanShortInterval(const uint16_t & seconds,Error *)743 bool WiFi::SetBgscanShortInterval(const uint16_t& seconds, Error* /*error*/) {
744 if (bgscan_short_interval_seconds_ == seconds) {
745 return false;
746 }
747 bgscan_short_interval_seconds_ = seconds;
748 // We do not update kNetworkPropertyBgscan for |pending_service_| or
749 // |current_service_|, because supplicant does not allow for
750 // reconfiguration without disconnect and reconnect.
751 return true;
752 }
753
SetBgscanSignalThreshold(const int32_t & dbm,Error *)754 bool WiFi::SetBgscanSignalThreshold(const int32_t& dbm, Error* /*error*/) {
755 if (bgscan_signal_threshold_dbm_ == dbm) {
756 return false;
757 }
758 bgscan_signal_threshold_dbm_ = dbm;
759 // We do not update kNetworkPropertyBgscan for |pending_service_| or
760 // |current_service_|, because supplicant does not allow for
761 // reconfiguration without disconnect and reconnect.
762 return true;
763 }
764
SetRoamThreshold(const uint16_t & threshold,Error *)765 bool WiFi::SetRoamThreshold(const uint16_t& threshold, Error* /*error*/) {
766 roam_threshold_db_ = threshold;
767 if (!current_service_ || !current_service_->roam_threshold_db_set()) {
768 supplicant_interface_proxy_->SetRoamThreshold(threshold);
769 }
770 return true;
771 }
772
SetScanInterval(const uint16_t & seconds,Error *)773 bool WiFi::SetScanInterval(const uint16_t& seconds, Error* /*error*/) {
774 if (scan_interval_seconds_ == seconds) {
775 return false;
776 }
777 scan_interval_seconds_ = seconds;
778 if (running()) {
779 StartScanTimer();
780 }
781 // The scan interval affects both foreground scans (handled by
782 // |scan_timer_callback_|), and background scans (handled by
783 // supplicant). However, we do not update |pending_service_| or
784 // |current_service_|, because supplicant does not allow for
785 // reconfiguration without disconnect and reconnect.
786 return true;
787 }
788
ClearBgscanMethod(const int &,Error *)789 void WiFi::ClearBgscanMethod(const int& /*argument*/, Error* /*error*/) {
790 bgscan_method_.clear();
791 }
792
CurrentBSSChanged(const string & new_bss)793 void WiFi::CurrentBSSChanged(const string& new_bss) {
794 SLOG(this, 3) << "WiFi " << link_name() << " CurrentBSS "
795 << supplicant_bss_ << " -> " << new_bss;
796 supplicant_bss_ = new_bss;
797 has_already_completed_ = false;
798 is_roaming_in_progress_ = false;
799
800 // Any change in CurrentBSS means supplicant is actively changing our
801 // connectivity. We no longer need to track any previously pending
802 // reconnect.
803 StopReconnectTimer();
804 StopRequestingStationInfo();
805
806 if (new_bss == WPASupplicant::kCurrentBSSNull) {
807 HandleDisconnect();
808 if (!provider_->GetHiddenSSIDList().empty()) {
809 // Before disconnecting, wpa_supplicant probably scanned for
810 // APs. So, in the normal case, we defer to the timer for the next scan.
811 //
812 // However, in the case of hidden SSIDs, supplicant knows about
813 // at most one of them. (That would be the hidden SSID we were
814 // connected to, if applicable.)
815 //
816 // So, in this case, we initiate an immediate scan. This scan
817 // will include the hidden SSIDs we know about (up to the limit of
818 // kScanMAxSSIDsPerScan).
819 //
820 // We may want to reconsider this immediate scan, if/when shill
821 // takes greater responsibility for scanning (vs. letting
822 // supplicant handle most of it).
823 Scan(kProgressiveScan, nullptr, __func__);
824 }
825 } else {
826 HandleRoam(new_bss);
827 }
828
829 // Reset the EAP handler only after calling HandleDisconnect() above
830 // so our EAP state could be used to detect a failed authentication.
831 eap_state_handler_->Reset();
832
833 // If we are selecting a new service, or if we're clearing selection
834 // of a something other than the pending service, call SelectService.
835 // Otherwise skip SelectService, since this will cause the pending
836 // service to be marked as Idle.
837 if (current_service_ || selected_service() != pending_service_) {
838 SelectService(current_service_);
839 }
840
841 // Invariant check: a Service can either be current, or pending, but
842 // not both.
843 CHECK(current_service_.get() != pending_service_.get() ||
844 current_service_.get() == nullptr);
845
846 // If we are no longer debugging a problematic WiFi connection, return
847 // to the debugging level indicated by the WiFi debugging scope.
848 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
849 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
850 SetConnectionDebugging(false);
851 }
852 }
853
DisconnectReasonChanged(const int32_t new_disconnect_reason)854 void WiFi::DisconnectReasonChanged(const int32_t new_disconnect_reason) {
855 if (new_disconnect_reason == kDefaultDisconnectReason) {
856 SLOG(this, 3) << "WiFi clearing DisconnectReason for " << link_name();
857 } else {
858 string update = "";
859 if (supplicant_disconnect_reason_ != kDefaultDisconnectReason) {
860 update = StringPrintf(" (was %d)", supplicant_disconnect_reason_);
861 }
862 LOG(INFO) << "WiFi " << link_name()
863 << " supplicant updated DisconnectReason to "
864 << new_disconnect_reason << update;
865 }
866 supplicant_disconnect_reason_ = new_disconnect_reason;
867 }
868
HandleDisconnect()869 void WiFi::HandleDisconnect() {
870 // Identify the affected service. We expect to get a disconnect
871 // event when we fall off a Service that we were connected
872 // to. However, we also allow for the case where we get a disconnect
873 // event while attempting to connect from a disconnected state.
874 WiFiService* affected_service =
875 current_service_.get() ? current_service_.get() : pending_service_.get();
876
877 if (!affected_service) {
878 SLOG(this, 2) << "WiFi " << link_name()
879 << " disconnected while not connected or connecting";
880 return;
881 }
882
883 SLOG(this, 2) << "WiFi " << link_name() << " disconnected from "
884 << " (or failed to connect to) service "
885 << affected_service->unique_name();
886
887 if (affected_service == current_service_.get() && pending_service_.get()) {
888 // Current service disconnected intentionally for network switching,
889 // set service state to idle.
890 affected_service->SetState(Service::kStateIdle);
891 } else {
892 // Perform necessary handling for disconnected service.
893 ServiceDisconnected(affected_service);
894 }
895
896 current_service_ = nullptr;
897
898 if (affected_service == selected_service()) {
899 // If our selected service has disconnected, destroy IP configuration state.
900 DropConnection();
901 }
902
903 Error error;
904 if (!DisableNetworkForService(affected_service, &error)) {
905 if (error.type() == Error::kNotFound) {
906 SLOG(this, 2) << "WiFi " << link_name() << " disconnected from "
907 << " (or failed to connect to) service "
908 << affected_service->unique_name() << ", "
909 << "but could not find supplicant network to disable.";
910 } else {
911 LOG(FATAL) << "DisableNetwork failed on " << link_name()
912 << "for service " << affected_service->unique_name() << ".";
913 }
914 }
915
916 metrics()->NotifySignalAtDisconnect(*affected_service,
917 affected_service->SignalLevel());
918 affected_service->NotifyCurrentEndpoint(nullptr);
919 metrics()->NotifyServiceDisconnect(*affected_service);
920
921 if (affected_service == pending_service_.get()) {
922 // The attempt to connect to |pending_service_| failed. Clear
923 // |pending_service_|, to indicate we're no longer in the middle
924 // of a connect request.
925 SetPendingService(nullptr);
926 } else if (pending_service_.get()) {
927 // We've attributed the disconnection to what was the
928 // |current_service_|, rather than the |pending_service_|.
929 //
930 // If we're wrong about that (i.e. supplicant reported this
931 // CurrentBSS change after attempting to connect to
932 // |pending_service_|), we're depending on supplicant to retry
933 // connecting to |pending_service_|, and delivering another
934 // CurrentBSS change signal in the future.
935 //
936 // Log this fact, to help us debug (in case our assumptions are
937 // wrong).
938 SLOG(this, 2) << "WiFi " << link_name() << " pending connection to service "
939 << pending_service_->unique_name()
940 << " after disconnect";
941 }
942
943 // If we disconnect, initially scan at a faster frequency, to make sure
944 // we've found all available APs.
945 RestartFastScanAttempts();
946 }
947
ServiceDisconnected(WiFiServiceRefPtr affected_service)948 void WiFi::ServiceDisconnected(WiFiServiceRefPtr affected_service) {
949 SLOG(this, 2) << __func__ << " service " << affected_service->unique_name();
950
951 // Check if service was explicitly disconnected due to failure or
952 // is explicitly disconnected by user.
953 if (!affected_service->IsInFailState() &&
954 !affected_service->explicitly_disconnected() &&
955 !affected_service->expecting_disconnect()) {
956 // Determine disconnect failure reason.
957 Service::ConnectFailure failure;
958 if (SuspectCredentials(affected_service, &failure)) {
959 // If we suspect bad credentials, set failure, to trigger an error
960 // mole in Chrome.
961 affected_service->SetFailure(failure);
962 LOG(ERROR) << "Connection failure is due to suspect credentials: "
963 << "returning "
964 << Service::ConnectFailureToString(failure);
965 } else {
966 // Disconnected due to inability to connect to service, most likely
967 // due to roaming out of range.
968 LOG(ERROR) << "Disconnected due to inability to connect to the service.";
969 affected_service->SetFailure(Service::kFailureOutOfRange);
970 }
971 }
972
973 // Set service state back to idle, so this service can be used for
974 // future connections.
975 affected_service->SetState(Service::kStateIdle);
976 }
977
978 // We use the term "Roam" loosely. In particular, we include the case
979 // where we "Roam" to a BSS from the disconnected state.
HandleRoam(const string & new_bss)980 void WiFi::HandleRoam(const string& new_bss) {
981 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
982 if (endpoint_it == endpoint_by_rpcid_.end()) {
983 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
984 << new_bss;
985 return;
986 }
987
988 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
989 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
990 if (!service.get()) {
991 LOG(WARNING) << "WiFi " << link_name()
992 << " could not find Service for Endpoint "
993 << endpoint->bssid_string()
994 << " (service will be unchanged)";
995 return;
996 }
997
998 SLOG(this, 2) << "WiFi " << link_name()
999 << " roamed to Endpoint " << endpoint->bssid_string()
1000 << " " << LogSSID(endpoint->ssid_string());
1001
1002 service->NotifyCurrentEndpoint(endpoint);
1003
1004 if (pending_service_.get() &&
1005 service.get() != pending_service_.get()) {
1006 // The Service we've roamed on to is not the one we asked for.
1007 // We assume that this is transient, and that wpa_supplicant
1008 // is trying / will try to connect to |pending_service_|.
1009 //
1010 // If it succeeds, we'll end up back here, but with |service|
1011 // pointing at the same service as |pending_service_|.
1012 //
1013 // If it fails, we'll process things in HandleDisconnect.
1014 //
1015 // So we leave |pending_service_| untouched.
1016 SLOG(this, 2) << "WiFi " << link_name()
1017 << " new current Endpoint "
1018 << endpoint->bssid_string()
1019 << " is not part of pending service "
1020 << pending_service_->unique_name();
1021
1022 // Sanity check: if we didn't roam onto |pending_service_|, we
1023 // should still be on |current_service_|.
1024 if (service.get() != current_service_.get()) {
1025 LOG(WARNING) << "WiFi " << link_name()
1026 << " new current Endpoint "
1027 << endpoint->bssid_string()
1028 << " is neither part of pending service "
1029 << pending_service_->unique_name()
1030 << " nor part of current service "
1031 << (current_service_ ?
1032 current_service_->unique_name() :
1033 "(nullptr)");
1034 // wpa_supplicant has no knowledge of the pending_service_ at this point.
1035 // Disconnect the pending_service_, so that it can be connectable again.
1036 // Otherwise, we'd have to wait for the pending timeout to trigger the
1037 // disconnect. This will speed up the connection attempt process for
1038 // the pending_service_.
1039 DisconnectFrom(pending_service_.get());
1040 }
1041 return;
1042 }
1043
1044 if (pending_service_.get()) {
1045 // We assume service.get() == pending_service_.get() here, because
1046 // of the return in the previous if clause.
1047 //
1048 // Boring case: we've connected to the service we asked
1049 // for. Simply update |current_service_| and |pending_service_|.
1050 current_service_ = service;
1051 SetScanState(kScanConnected, scan_method_, __func__);
1052 SetPendingService(nullptr);
1053 // Use WiFi service-specific roam threshold if it is set, otherwise use WiFi
1054 // device-wide roam threshold.
1055 if (current_service_->roam_threshold_db_set()) {
1056 supplicant_interface_proxy_->SetRoamThreshold(
1057 current_service_->roam_threshold_db());
1058 } else {
1059 supplicant_interface_proxy_->SetRoamThreshold(roam_threshold_db_);
1060 }
1061 return;
1062 }
1063
1064 // |pending_service_| was nullptr, so we weren't attempting to connect
1065 // to a new Service. Sanity check that we're still on
1066 // |current_service_|.
1067 if (service.get() != current_service_.get()) {
1068 LOG(WARNING)
1069 << "WiFi " << link_name()
1070 << " new current Endpoint "
1071 << endpoint->bssid_string()
1072 << (current_service_.get() ?
1073 StringPrintf(" is not part of current service %s",
1074 current_service_->unique_name().c_str()) :
1075 " with no current service");
1076 // We didn't expect to be here, but let's cope as well as we
1077 // can. Update |current_service_| to keep it in sync with
1078 // supplicant.
1079 current_service_ = service;
1080
1081 // If this service isn't already marked as actively connecting (likely,
1082 // since this service is a bit of a surprise) set the service as
1083 // associating.
1084 if (!current_service_->IsConnecting()) {
1085 current_service_->SetState(Service::kStateAssociating);
1086 }
1087
1088 return;
1089 }
1090
1091 // At this point, we know that |pending_service_| was nullptr, and that
1092 // we're still on |current_service_|. We should track this roaming
1093 // event so we can refresh our IPConfig if it succeeds.
1094 is_roaming_in_progress_ = true;
1095
1096 return;
1097 }
1098
FindNetworkRpcidForService(const WiFiService * service,Error * error)1099 string WiFi::FindNetworkRpcidForService(
1100 const WiFiService* service, Error* error) {
1101 ReverseServiceMap::const_iterator rpcid_it = rpcid_by_service_.find(service);
1102 if (rpcid_it == rpcid_by_service_.end()) {
1103 const string error_message =
1104 StringPrintf(
1105 "WiFi %s cannot find supplicant network rpcid for service %s",
1106 link_name().c_str(), service->unique_name().c_str());
1107 // There are contexts where this is not an error, such as when a service
1108 // is clearing whatever cached credentials may not exist.
1109 SLOG(this, 2) << error_message;
1110 if (error) {
1111 error->Populate(Error::kNotFound, error_message);
1112 }
1113 return "";
1114 }
1115
1116 return rpcid_it->second;
1117 }
1118
DisableNetworkForService(const WiFiService * service,Error * error)1119 bool WiFi::DisableNetworkForService(const WiFiService* service, Error* error) {
1120 string rpcid = FindNetworkRpcidForService(service, error);
1121 if (rpcid.empty()) {
1122 // Error is already populated.
1123 return false;
1124 }
1125
1126 if (!DisableNetwork(rpcid)) {
1127 const string error_message =
1128 StringPrintf("WiFi %s cannot disable network for service %s: "
1129 "DBus operation failed for rpcid %s.",
1130 link_name().c_str(), service->unique_name().c_str(),
1131 rpcid.c_str());
1132 Error::PopulateAndLog(
1133 FROM_HERE, error, Error::kOperationFailed, error_message);
1134
1135 // Make sure that such errored networks are removed, so problems do not
1136 // propagate to future connection attempts.
1137 RemoveNetwork(rpcid);
1138 rpcid_by_service_.erase(service);
1139
1140 return false;
1141 }
1142
1143 return true;
1144 }
1145
RemoveNetworkForService(const WiFiService * service,Error * error)1146 bool WiFi::RemoveNetworkForService(const WiFiService* service, Error* error) {
1147 string rpcid = FindNetworkRpcidForService(service, error);
1148 if (rpcid.empty()) {
1149 // Error is already populated.
1150 return false;
1151 }
1152
1153 // Erase the rpcid from our tables regardless of failure below, since even
1154 // if in failure, we never want to use this network again.
1155 rpcid_by_service_.erase(service);
1156
1157 // TODO(quiche): Reconsider giving up immediately. Maybe give
1158 // wpa_supplicant some time to retry, first.
1159 if (!RemoveNetwork(rpcid)) {
1160 const string error_message =
1161 StringPrintf("WiFi %s cannot remove network for service %s: "
1162 "DBus operation failed for rpcid %s.",
1163 link_name().c_str(), service->unique_name().c_str(),
1164 rpcid.c_str());
1165 Error::PopulateAndLog(
1166 FROM_HERE, error, Error::kOperationFailed, error_message);
1167 return false;
1168 }
1169
1170 return true;
1171 }
1172
PendingScanResultsHandler()1173 void WiFi::PendingScanResultsHandler() {
1174 CHECK(pending_scan_results_);
1175 SLOG(this, 2) << __func__ << " with " << pending_scan_results_->results.size()
1176 << " results and is_complete set to "
1177 << pending_scan_results_->is_complete;
1178 for (const auto result : pending_scan_results_->results) {
1179 if (result.is_removal) {
1180 BSSRemovedTask(result.path);
1181 } else {
1182 BSSAddedTask(result.path, result.properties);
1183 }
1184 }
1185 if (pending_scan_results_->is_complete) {
1186 ScanDoneTask();
1187 }
1188 pending_scan_results_.reset();
1189 }
1190
ParseWiphyIndex(const Nl80211Message & nl80211_message)1191 bool WiFi::ParseWiphyIndex(const Nl80211Message& nl80211_message) {
1192 // Verify NL80211_CMD_NEW_WIPHY.
1193 if (nl80211_message.command() != NewWiphyMessage::kCommand) {
1194 LOG(ERROR) << "Received unexpected command: " << nl80211_message.command();
1195 return false;
1196 }
1197 if (!nl80211_message.const_attributes()->GetU32AttributeValue(
1198 NL80211_ATTR_WIPHY, &wiphy_index_)) {
1199 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY";
1200 return false;
1201 }
1202 return true;
1203 }
1204
OnScanStarted(const NetlinkMessage & netlink_message)1205 void WiFi::OnScanStarted(const NetlinkMessage& netlink_message) {
1206 // We only handle scan triggers in this handler, which is are nl80211 messages
1207 // with the NL80211_CMD_TRIGGER_SCAN command.
1208 if (netlink_message.message_type() != Nl80211Message::GetMessageType()) {
1209 SLOG(this, 7) << __func__ << ": "
1210 << "Not a NL80211 Message";
1211 return;
1212 }
1213 const Nl80211Message& scan_trigger_msg =
1214 *reinterpret_cast<const Nl80211Message*>(&netlink_message);
1215 if (scan_trigger_msg.command() != TriggerScanMessage::kCommand) {
1216 SLOG(this, 7) << __func__ << ": "
1217 << "Not a NL80211_CMD_TRIGGER_SCAN message";
1218 return;
1219 }
1220 uint32_t wiphy_index;
1221 if (!scan_trigger_msg.const_attributes()->GetU32AttributeValue(
1222 NL80211_ATTR_WIPHY, &wiphy_index)) {
1223 LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN had no NL80211_ATTR_WIPHY";
1224 return;
1225 }
1226 if (wiphy_index != wiphy_index_) {
1227 SLOG(this, 7) << __func__ << ": "
1228 << "Scan trigger not meant for this interface";
1229 return;
1230 }
1231 bool is_active_scan = false;
1232 AttributeListConstRefPtr ssids;
1233 if (scan_trigger_msg.const_attributes()->ConstGetNestedAttributeList(
1234 NL80211_ATTR_SCAN_SSIDS, &ssids)) {
1235 AttributeIdIterator ssid_iter(*ssids);
1236 // If any SSIDs (even the empty wild card) are reported, an active scan was
1237 // launched. Otherwise, a passive scan was launched.
1238 is_active_scan = !ssid_iter.AtEnd();
1239 }
1240 wake_on_wifi_->OnScanStarted(is_active_scan);
1241 }
1242
BSSAddedTask(const string & path,const KeyValueStore & properties)1243 void WiFi::BSSAddedTask(const string& path, const KeyValueStore& properties) {
1244 // Note: we assume that BSSIDs are unique across endpoints. This
1245 // means that if an AP reuses the same BSSID for multiple SSIDs, we
1246 // lose.
1247 WiFiEndpointRefPtr endpoint(
1248 new WiFiEndpoint(control_interface(), this, path, properties));
1249 SLOG(this, 5) << "Found endpoint. "
1250 << "RPC path: " << path << ", "
1251 << LogSSID(endpoint->ssid_string()) << ", "
1252 << "bssid: " << endpoint->bssid_string() << ", "
1253 << "signal: " << endpoint->signal_strength() << ", "
1254 << "security: " << endpoint->security_mode() << ", "
1255 << "frequency: " << endpoint->frequency();
1256
1257 if (endpoint->ssid_string().empty()) {
1258 // Don't bother trying to find or create a Service for an Endpoint
1259 // without an SSID. We wouldn't be able to connect to it anyway.
1260 return;
1261 }
1262
1263 if (endpoint->ssid()[0] == 0) {
1264 // Assume that an SSID starting with nullptr is bogus/misconfigured,
1265 // and filter it out.
1266 return;
1267 }
1268
1269 provider_->OnEndpointAdded(endpoint);
1270
1271 // Do this last, to maintain the invariant that any Endpoint we
1272 // know about has a corresponding Service.
1273 //
1274 // TODO(quiche): Write test to verify correct behavior in the case
1275 // where we get multiple BSSAdded events for a single endpoint.
1276 // (Old Endpoint's refcount should fall to zero, and old Endpoint
1277 // should be destroyed.)
1278 endpoint_by_rpcid_[path] = endpoint;
1279 endpoint->Start();
1280 }
1281
BSSRemovedTask(const string & path)1282 void WiFi::BSSRemovedTask(const string& path) {
1283 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
1284 if (i == endpoint_by_rpcid_.end()) {
1285 SLOG(this, 1) << "WiFi " << link_name()
1286 << " could not find BSS " << path
1287 << " to remove.";
1288 return;
1289 }
1290
1291 WiFiEndpointRefPtr endpoint = i->second;
1292 CHECK(endpoint);
1293 endpoint_by_rpcid_.erase(i);
1294
1295 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
1296 if (!service) {
1297 return;
1298 }
1299 Error unused_error;
1300 RemoveNetworkForService(service.get(), &unused_error);
1301
1302 bool disconnect_service = !service->HasEndpoints() &&
1303 (service->IsConnecting() || service->IsConnected());
1304
1305 if (disconnect_service) {
1306 LOG(INFO) << "Disconnecting from service " << service->unique_name()
1307 << ": BSSRemoved";
1308 DisconnectFrom(service.get());
1309 }
1310 }
1311
CertificationTask(const KeyValueStore & properties)1312 void WiFi::CertificationTask(const KeyValueStore& properties) {
1313 if (!current_service_) {
1314 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1315 << " with no current service.";
1316 return;
1317 }
1318
1319 string subject;
1320 uint32_t depth;
1321 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
1322 current_service_->AddEAPCertification(subject, depth);
1323 }
1324 }
1325
EAPEventTask(const string & status,const string & parameter)1326 void WiFi::EAPEventTask(const string& status, const string& parameter) {
1327 if (!current_service_) {
1328 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1329 << " with no current service.";
1330 return;
1331 }
1332 Service::ConnectFailure failure = Service::kFailureUnknown;
1333 eap_state_handler_->ParseStatus(status, parameter, &failure);
1334 if (failure == Service::kFailurePinMissing) {
1335 // wpa_supplicant can sometimes forget the PIN on disconnect from the AP.
1336 const string& pin = current_service_->eap()->pin();
1337 Error unused_error;
1338 string rpcid = FindNetworkRpcidForService(current_service_.get(),
1339 &unused_error);
1340 if (!pin.empty() && !rpcid.empty()) {
1341 // We have a PIN configured, so we can provide it back to wpa_supplicant.
1342 LOG(INFO) << "Re-supplying PIN parameter to wpa_supplicant.";
1343 supplicant_interface_proxy_->NetworkReply(
1344 rpcid, WPASupplicant::kEAPRequestedParameterPIN, pin);
1345 failure = Service::kFailureUnknown;
1346 }
1347 }
1348 if (failure != Service::kFailureUnknown) {
1349 // Avoid a reporting failure twice by resetting EAP state handler early.
1350 eap_state_handler_->Reset();
1351 Error unused_error;
1352 current_service_->DisconnectWithFailure(failure, &unused_error, __func__);
1353 }
1354 }
1355
PropertiesChangedTask(const KeyValueStore & properties)1356 void WiFi::PropertiesChangedTask(
1357 const KeyValueStore& properties) {
1358 // TODO(quiche): Handle changes in other properties (e.g. signal
1359 // strength).
1360
1361 // Note that order matters here. In particular, we want to process
1362 // changes in the current BSS before changes in state. This is so
1363 // that we update the state of the correct Endpoint/Service.
1364 if (properties.ContainsRpcIdentifier(
1365 WPASupplicant::kInterfacePropertyCurrentBSS)) {
1366 CurrentBSSChanged(
1367 properties.GetRpcIdentifier(
1368 WPASupplicant::kInterfacePropertyCurrentBSS));
1369 }
1370
1371 if (properties.ContainsString(WPASupplicant::kInterfacePropertyState)) {
1372 StateChanged(properties.GetString(WPASupplicant::kInterfacePropertyState));
1373 }
1374
1375 if (properties.ContainsInt(
1376 WPASupplicant::kInterfacePropertyDisconnectReason)) {
1377 DisconnectReasonChanged(
1378 properties.GetInt(WPASupplicant::kInterfacePropertyDisconnectReason));
1379 }
1380 }
1381
ScanDoneTask()1382 void WiFi::ScanDoneTask() {
1383 SLOG(this, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
1384 // Unsets this flag if it was set in InitiateScanInDarkResume since that scan
1385 // has completed.
1386 manager()->set_suppress_autoconnect(false);
1387 if (wake_on_wifi_->in_dark_resume()) {
1388 metrics()->NotifyDarkResumeScanResultsReceived();
1389 }
1390 if (scan_session_) {
1391 // Post |ProgressiveScanTask| so it runs after any pending scan results
1392 // have been processed. This allows connections on new BSSes to be
1393 // started before we decide whether to abort the progressive scan or
1394 // continue scanning.
1395 dispatcher()->PostTask(
1396 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
1397 } else {
1398 // Post |UpdateScanStateAfterScanDone| so it runs after any pending scan
1399 // results have been processed. This allows connections on new BSSes to be
1400 // started before we decide whether the scan was fruitful.
1401 dispatcher()->PostTask(Bind(&WiFi::UpdateScanStateAfterScanDone,
1402 weak_ptr_factory_.GetWeakPtr()));
1403 if ((provider_->NumAutoConnectableServices() < 1) && IsIdle()) {
1404 // Ensure we are also idle in case we are in the midst of connecting to
1405 // the only service that was available for auto-connect on the previous
1406 // scan (which will cause it to show up as unavailable for auto-connect
1407 // when we query the WiFiProvider this time).
1408 wake_on_wifi_->OnNoAutoConnectableServicesAfterScan(
1409 provider_->GetSsidsConfiguredForAutoConnect(),
1410 Bind(&WiFi::RemoveSupplicantNetworks, weak_ptr_factory_.GetWeakPtr()),
1411 Bind(&WiFi::TriggerPassiveScan, weak_ptr_factory_.GetWeakPtr()));
1412 }
1413 }
1414 if (need_bss_flush_) {
1415 CHECK(supplicant_interface_proxy_);
1416 // Compute |max_age| relative to |resumed_at_|, to account for the
1417 // time taken to scan.
1418 struct timeval now;
1419 uint32_t max_age;
1420 time_->GetTimeMonotonic(&now);
1421 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1422 supplicant_interface_proxy_->FlushBSS(max_age);
1423 need_bss_flush_ = false;
1424 }
1425 StartScanTimer();
1426 }
1427
ScanFailedTask()1428 void WiFi::ScanFailedTask() {
1429 SLOG(this, 2) << __func__;
1430 SetScanState(kScanIdle, kScanMethodNone, __func__);
1431 }
1432
UpdateScanStateAfterScanDone()1433 void WiFi::UpdateScanStateAfterScanDone() {
1434 if (scan_method_ == kScanMethodFull) {
1435 // Only notify the Manager on completion of full scans, since the manager
1436 // will replace any cached geolocation info with the BSSes we have right
1437 // now.
1438 manager()->OnDeviceGeolocationInfoUpdated(this);
1439 }
1440 if (scan_state_ == kScanBackgroundScanning) {
1441 // Going directly to kScanIdle (instead of to kScanFoundNothing) inhibits
1442 // some UMA reporting in SetScanState. That's desired -- we don't want
1443 // to report background scan results to UMA since the drivers may play
1444 // background scans over a longer period in order to not interfere with
1445 // traffic.
1446 SetScanState(kScanIdle, kScanMethodNone, __func__);
1447 } else if (scan_state_ != kScanIdle && IsIdle()) {
1448 SetScanState(kScanFoundNothing, scan_method_, __func__);
1449 }
1450 }
1451
ScanTask()1452 void WiFi::ScanTask() {
1453 SLOG(this, 2) << "WiFi " << link_name() << " scan requested.";
1454 if (!enabled()) {
1455 SLOG(this, 2) << "Ignoring scan request while device is not enabled.";
1456 SetScanState(kScanIdle, kScanMethodNone, __func__); // Probably redundant.
1457 return;
1458 }
1459 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1460 SLOG(this, 2) << "Ignoring scan request while supplicant is not present.";
1461 SetScanState(kScanIdle, kScanMethodNone, __func__);
1462 return;
1463 }
1464 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1465 (current_service_.get() && current_service_->IsConnecting())) {
1466 SLOG(this, 2) << "Ignoring scan request while connecting to an AP.";
1467 return;
1468 }
1469 KeyValueStore scan_args;
1470 scan_args.SetString(WPASupplicant::kPropertyScanType,
1471 WPASupplicant::kScanTypeActive);
1472
1473 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
1474 if (!hidden_ssids.empty()) {
1475 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
1476 // not fit in.
1477 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
1478 hidden_ssids.erase(
1479 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
1480 hidden_ssids.end());
1481 }
1482 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1483 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1484 // behavior of doing a broadcast probe.
1485 hidden_ssids.push_back(ByteArray());
1486
1487 scan_args.SetByteArrays(WPASupplicant::kPropertyScanSSIDs, hidden_ssids);
1488 }
1489
1490 if (!supplicant_interface_proxy_->Scan(scan_args)) {
1491 // A scan may fail if, for example, the wpa_supplicant vanishing
1492 // notification is posted after this task has already started running.
1493 LOG(WARNING) << "Scan failed";
1494 return;
1495 }
1496
1497 // Only set the scan state/method if we are starting a full scan from
1498 // scratch. Keep the existing method if this is a failover from a
1499 // progressive scan.
1500 if (scan_state_ != kScanScanning) {
1501 SetScanState(IsIdle() ? kScanScanning : kScanBackgroundScanning,
1502 kScanMethodFull, __func__);
1503 }
1504 }
1505
ProgressiveScanTask()1506 void WiFi::ProgressiveScanTask() {
1507 SLOG(this, 2) << __func__ << " - scan requested for " << link_name();
1508 if (!enabled()) {
1509 LOG(INFO) << "Ignoring scan request while device is not enabled.";
1510 SetScanState(kScanIdle, kScanMethodNone, __func__); // Probably redundant.
1511 return;
1512 }
1513 if (!scan_session_) {
1514 SLOG(this, 2) << "No scan session -- returning";
1515 SetScanState(kScanIdle, kScanMethodNone, __func__);
1516 return;
1517 }
1518 // TODO(wdg): We don't currently support progressive background scans. If
1519 // we did, we couldn't bail out, here, if we're connected. Progressive scan
1520 // state will have to be modified to include whether there was a connection
1521 // when the scan started. Then, this code would only bail out if we didn't
1522 // start with a connection but one exists at this point.
1523 if (!IsIdle()) {
1524 SLOG(this, 2) << "Ignoring scan request while connecting to an AP.";
1525 scan_session_.reset();
1526 return;
1527 }
1528 if (scan_session_->HasMoreFrequencies()) {
1529 SLOG(this, 2) << "Initiating a scan -- returning";
1530 SetScanState(kScanScanning, kScanMethodProgressive, __func__);
1531 // After us initiating a scan, supplicant will gather the scan results and
1532 // send us zero or more |BSSAdded| events followed by a |ScanDone|.
1533 scan_session_->InitiateScan();
1534 return;
1535 }
1536 LOG(ERROR) << "A complete progressive scan turned-up nothing -- "
1537 << "do a regular scan";
1538 scan_session_.reset();
1539 SetScanState(kScanScanning, kScanMethodProgressiveFinishedToFull, __func__);
1540 LOG(INFO) << "Scan [full] on " << link_name()
1541 << " (connected to nothing on progressive scan) from " << __func__;
1542 ScanTask();
1543 }
1544
SetSchedScanTask(bool enable)1545 void WiFi::SetSchedScanTask(bool enable) {
1546 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1547 SLOG(this, 2) << "Ignoring sched scan configure request "
1548 << "while supplicant is not present.";
1549 return;
1550 }
1551 if (!supplicant_interface_proxy_->SetSchedScan(enable)) {
1552 LOG(WARNING) << "Failed to set SchedScan";
1553 }
1554 }
1555
OnFailedProgressiveScan()1556 void WiFi::OnFailedProgressiveScan() {
1557 LOG(ERROR) << "Couldn't issue a scan on " << link_name()
1558 << " -- doing a regular scan";
1559 scan_session_.reset();
1560 SetScanState(kScanScanning, kScanMethodProgressiveErrorToFull, __func__);
1561 LOG(INFO) << "Scan [full] on " << link_name()
1562 << " (failover from progressive scan) from " << __func__;
1563 ScanTask();
1564 }
1565
GetServiceLeaseName(const WiFiService & service)1566 string WiFi::GetServiceLeaseName(const WiFiService& service) {
1567 return service.GetStorageIdentifier();
1568 }
1569
DestroyServiceLease(const WiFiService & service)1570 void WiFi::DestroyServiceLease(const WiFiService& service) {
1571 DestroyIPConfigLease(GetServiceLeaseName(service));
1572 }
1573
StateChanged(const string & new_state)1574 void WiFi::StateChanged(const string& new_state) {
1575 const string old_state = supplicant_state_;
1576 supplicant_state_ = new_state;
1577 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1578 << old_state << " -> " << new_state;
1579
1580 if (new_state == WPASupplicant::kInterfaceStateCompleted ||
1581 new_state == WPASupplicant::kInterfaceState4WayHandshake) {
1582 mac80211_monitor_->UpdateConnectedState(true);
1583 } else {
1584 mac80211_monitor_->UpdateConnectedState(false);
1585 }
1586
1587 if (old_state == WPASupplicant::kInterfaceStateDisconnected &&
1588 new_state != WPASupplicant::kInterfaceStateDisconnected) {
1589 // The state has been changed from disconnect to something else, clearing
1590 // out disconnect reason to avoid confusion about future disconnects.
1591 DisconnectReasonChanged(kDefaultDisconnectReason);
1592 }
1593
1594 // Identify the service to which the state change applies. If
1595 // |pending_service_| is non-NULL, then the state change applies to
1596 // |pending_service_|. Otherwise, it applies to |current_service_|.
1597 //
1598 // This policy is driven by the fact that the |pending_service_|
1599 // doesn't become the |current_service_| until wpa_supplicant
1600 // reports a CurrentBSS change to the |pending_service_|. And the
1601 // CurrentBSS change won't be reported until the |pending_service_|
1602 // reaches the WPASupplicant::kInterfaceStateCompleted state.
1603 WiFiService* affected_service =
1604 pending_service_.get() ? pending_service_.get() : current_service_.get();
1605 if (!affected_service) {
1606 SLOG(this, 2) << "WiFi " << link_name() << " " << __func__
1607 << " with no service";
1608 return;
1609 }
1610
1611 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
1612 if (affected_service->IsConnected()) {
1613 StopReconnectTimer();
1614 EnableHighBitrates();
1615 if (is_roaming_in_progress_) {
1616 // This means wpa_supplicant completed a roam without an intervening
1617 // disconnect. We should renew our DHCP lease just in case the new
1618 // AP is on a different subnet than where we started.
1619 is_roaming_in_progress_ = false;
1620 const IPConfigRefPtr& ip_config = ipconfig();
1621 if (ip_config) {
1622 LOG(INFO) << link_name() << " renewing L3 configuration after roam.";
1623 ip_config->RenewIP();
1624 }
1625 }
1626 } else if (has_already_completed_) {
1627 LOG(INFO) << link_name() << " L3 configuration already started.";
1628 } else {
1629 provider_->IncrementConnectCount(affected_service->frequency());
1630 if (AcquireIPConfigWithLeaseName(
1631 GetServiceLeaseName(*affected_service))) {
1632 LOG(INFO) << link_name() << " is up; started L3 configuration.";
1633 affected_service->SetState(Service::kStateConfiguring);
1634 if (affected_service->IsSecurityMatch(kSecurityWep)) {
1635 // With the overwhelming majority of WEP networks, we cannot assume
1636 // our credentials are correct just because we have successfully
1637 // connected. It is more useful to track received data as the L3
1638 // configuration proceeds to see if we can decrypt anything.
1639 receive_byte_count_at_connect_ = GetReceiveByteCount();
1640 } else {
1641 affected_service->ResetSuspectedCredentialFailures();
1642 }
1643 } else {
1644 LOG(ERROR) << "Unable to acquire DHCP config.";
1645 }
1646 }
1647 has_already_completed_ = true;
1648 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
1649 affected_service->SetState(Service::kStateAssociating);
1650 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1651 new_state == WPASupplicant::kInterfaceStateAssociating ||
1652 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1653 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
1654 // Ignore transitions into these states from Completed, to avoid
1655 // bothering the user when roaming, or re-keying.
1656 if (old_state != WPASupplicant::kInterfaceStateCompleted)
1657 affected_service->SetState(Service::kStateAssociating);
1658 // TODO(quiche): On backwards transitions, we should probably set
1659 // a timeout for getting back into the completed state. At present,
1660 // we depend on wpa_supplicant eventually reporting that CurrentBSS
1661 // has changed. But there may be cases where that signal is not sent.
1662 // (crbug.com/206208)
1663 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
1664 affected_service == current_service_ &&
1665 affected_service->IsConnected()) {
1666 // This means that wpa_supplicant failed in a re-connect attempt, but
1667 // may still be reconnecting. Give wpa_supplicant a limited amount of
1668 // time to transition out this condition by either connecting or changing
1669 // CurrentBSS.
1670 StartReconnectTimer();
1671 } else {
1672 // Other transitions do not affect Service state.
1673 //
1674 // Note in particular that we ignore a State change into
1675 // kInterfaceStateDisconnected, in favor of observing the corresponding
1676 // change in CurrentBSS.
1677 }
1678 }
1679
SuspectCredentials(WiFiServiceRefPtr service,Service::ConnectFailure * failure) const1680 bool WiFi::SuspectCredentials(
1681 WiFiServiceRefPtr service, Service::ConnectFailure* failure) const {
1682 if (service->IsSecurityMatch(kSecurityPsk)) {
1683 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
1684 service->AddSuspectedCredentialFailure()) {
1685 if (failure) {
1686 *failure = Service::kFailureBadPassphrase;
1687 }
1688 return true;
1689 }
1690 } else if (service->IsSecurityMatch(kSecurity8021x)) {
1691 if (eap_state_handler_->is_eap_in_progress() &&
1692 service->AddSuspectedCredentialFailure()) {
1693 if (failure) {
1694 *failure = Service::kFailureEAPAuthentication;
1695 }
1696 return true;
1697 }
1698 }
1699
1700 return false;
1701 }
1702
1703 // static
SanitizeSSID(string * ssid)1704 bool WiFi::SanitizeSSID(string* ssid) {
1705 CHECK(ssid);
1706
1707 size_t ssid_len = ssid->length();
1708 size_t i;
1709 bool changed = false;
1710
1711 for (i = 0; i < ssid_len; ++i) {
1712 if (!IsPrintableAsciiChar((*ssid)[i])) {
1713 (*ssid)[i] = '?';
1714 changed = true;
1715 }
1716 }
1717
1718 return changed;
1719 }
1720
1721 // static
LogSSID(const string & ssid)1722 string WiFi::LogSSID(const string& ssid) {
1723 string out;
1724 for (const auto& chr : ssid) {
1725 // Replace '[' and ']' (in addition to non-printable characters) so that
1726 // it's easy to match the right substring through a non-greedy regex.
1727 if (chr == '[' || chr == ']' || !IsPrintableAsciiChar(chr)) {
1728 base::StringAppendF(&out, "\\x%02x", chr);
1729 } else {
1730 out += chr;
1731 }
1732 }
1733 return StringPrintf("[SSID=%s]", out.c_str());
1734 }
1735
OnLinkMonitorFailure()1736 void WiFi::OnLinkMonitorFailure() {
1737 // Invoke base class call first to allow it to determine the reliability of
1738 // the link.
1739 Device::OnLinkMonitorFailure();
1740
1741 // If we have never found the gateway, let's be conservative and not
1742 // do anything, in case this network topology does not have a gateway.
1743 if (!link_monitor()->IsGatewayFound()) {
1744 LOG(INFO) << "In " << __func__ << "(): "
1745 << "Skipping reassociate since gateway was never found.";
1746 return;
1747 }
1748
1749 if (!supplicant_present_) {
1750 LOG(ERROR) << "In " << __func__ << "(): "
1751 << "wpa_supplicant is not present. Cannot reassociate.";
1752 return;
1753 }
1754
1755 // Skip reassociate attempt if service is not reliable, meaning multiple link
1756 // failures in short period of time.
1757 if (current_service_->unreliable()) {
1758 LOG(INFO) << "Current service is unreliable, skipping reassociate attempt.";
1759 return;
1760 }
1761
1762 // This will force a transition out of connected, if we are actually
1763 // connected.
1764 if (!supplicant_interface_proxy_->Reattach()) {
1765 LOG(ERROR) << "In " << __func__ << "(): failed to call Reattach().";
1766 return;
1767 }
1768
1769 // If we don't eventually get a transition back into a connected state,
1770 // there is something wrong.
1771 StartReconnectTimer();
1772 LOG(INFO) << "In " << __func__ << "(): Called Reattach().";
1773 }
1774
OnUnreliableLink()1775 void WiFi::OnUnreliableLink() {
1776 Device::OnUnreliableLink();
1777
1778 // Disable HT40 for the current network.
1779 SetHT40EnableForService(current_service_.get(), false);
1780 }
1781
ShouldUseArpGateway() const1782 bool WiFi::ShouldUseArpGateway() const {
1783 return !IsUsingStaticIP();
1784 }
1785
DisassociateFromService(const WiFiServiceRefPtr & service)1786 void WiFi::DisassociateFromService(const WiFiServiceRefPtr& service) {
1787 SLOG(this, 2) << "In " << __func__ << " for service: "
1788 << service->unique_name();
1789 DisconnectFromIfActive(service.get());
1790 if (service == selected_service()) {
1791 DropConnection();
1792 }
1793 Error unused_error;
1794 RemoveNetworkForService(service.get(), &unused_error);
1795 }
1796
GetGeolocationObjects() const1797 vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1798 vector<GeolocationInfo> objects;
1799 for (const auto& endpoint_entry : endpoint_by_rpcid_) {
1800 GeolocationInfo geoinfo;
1801 const WiFiEndpointRefPtr& endpoint = endpoint_entry.second;
1802 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1803 geoinfo.AddField(kGeoSignalStrengthProperty,
1804 StringPrintf("%d", endpoint->signal_strength()));
1805 geoinfo.AddField(
1806 kGeoChannelProperty,
1807 StringPrintf("%d",
1808 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1809 // TODO(gauravsh): Include age field. crbug.com/217554
1810 objects.push_back(geoinfo);
1811 }
1812 return objects;
1813 }
1814
HelpRegisterDerivedInt32(PropertyStore * store,const string & name,int32_t (WiFi::* get)(Error * error),bool (WiFi::* set)(const int32_t & value,Error * error))1815 void WiFi::HelpRegisterDerivedInt32(
1816 PropertyStore* store,
1817 const string& name,
1818 int32_t(WiFi::*get)(Error* error),
1819 bool(WiFi::*set)(const int32_t& value, Error* error)) {
1820 store->RegisterDerivedInt32(
1821 name,
1822 Int32Accessor(new CustomAccessor<WiFi, int32_t>(this, get, set)));
1823 }
1824
HelpRegisterDerivedUint16(PropertyStore * store,const string & name,uint16_t (WiFi::* get)(Error * error),bool (WiFi::* set)(const uint16_t & value,Error * error))1825 void WiFi::HelpRegisterDerivedUint16(
1826 PropertyStore* store,
1827 const string& name,
1828 uint16_t(WiFi::*get)(Error* error),
1829 bool(WiFi::*set)(const uint16_t& value, Error* error)) {
1830 store->RegisterDerivedUint16(
1831 name,
1832 Uint16Accessor(new CustomAccessor<WiFi, uint16_t>(this, get, set)));
1833 }
1834
HelpRegisterConstDerivedBool(PropertyStore * store,const string & name,bool (WiFi::* get)(Error * error))1835 void WiFi::HelpRegisterConstDerivedBool(
1836 PropertyStore* store,
1837 const string& name,
1838 bool(WiFi::*get)(Error* error)) {
1839 store->RegisterDerivedBool(
1840 name,
1841 BoolAccessor(new CustomAccessor<WiFi, bool>(this, get, nullptr)));
1842 }
1843
OnBeforeSuspend(const ResultCallback & callback)1844 void WiFi::OnBeforeSuspend(const ResultCallback& callback) {
1845 if (!enabled()) {
1846 callback.Run(Error(Error::kSuccess));
1847 return;
1848 }
1849 LOG(INFO) << __func__ << ": "
1850 << (IsConnectedToCurrentService() ? "connected" : "not connected");
1851 StopScanTimer();
1852 supplicant_process_proxy_->ExpectDisconnect();
1853 uint32_t time_to_next_lease_renewal;
1854 bool have_dhcp_lease =
1855 TimeToNextDHCPLeaseRenewal(&time_to_next_lease_renewal);
1856 wake_on_wifi_->OnBeforeSuspend(
1857 IsConnectedToCurrentService(),
1858 provider_->GetSsidsConfiguredForAutoConnect(),
1859 callback,
1860 Bind(&Device::RenewDHCPLease, weak_ptr_factory_.GetWeakPtr()),
1861 Bind(&WiFi::RemoveSupplicantNetworks, weak_ptr_factory_.GetWeakPtr()),
1862 have_dhcp_lease,
1863 time_to_next_lease_renewal);
1864 }
1865
OnDarkResume(const ResultCallback & callback)1866 void WiFi::OnDarkResume(const ResultCallback& callback) {
1867 if (!enabled()) {
1868 callback.Run(Error(Error::kSuccess));
1869 return;
1870 }
1871 LOG(INFO) << __func__ << ": "
1872 << (IsConnectedToCurrentService() ? "connected" : "not connected");
1873 StopScanTimer();
1874 wake_on_wifi_->OnDarkResume(
1875 IsConnectedToCurrentService(),
1876 provider_->GetSsidsConfiguredForAutoConnect(),
1877 callback,
1878 Bind(&Device::RenewDHCPLease, weak_ptr_factory_.GetWeakPtr()),
1879 Bind(&WiFi::InitiateScanInDarkResume, weak_ptr_factory_.GetWeakPtr()),
1880 Bind(&WiFi::RemoveSupplicantNetworks, weak_ptr_factory_.GetWeakPtr()));
1881 }
1882
OnAfterResume()1883 void WiFi::OnAfterResume() {
1884 LOG(INFO) << __func__ << ": "
1885 << (IsConnectedToCurrentService() ? "connected" : "not connected");
1886 Device::OnAfterResume(); // May refresh ipconfig_
1887 dispatcher()->PostDelayedTask(Bind(&WiFi::ReportConnectedToServiceAfterWake,
1888 weak_ptr_factory_.GetWeakPtr()),
1889 kPostWakeConnectivityReportDelayMilliseconds);
1890 wake_on_wifi_->OnAfterResume();
1891
1892 // We want to flush the BSS cache, but we don't want to conflict
1893 // with an active connection attempt. So record the need to flush,
1894 // and take care of flushing when the next scan completes.
1895 //
1896 // Note that supplicant will automatically expire old cache
1897 // entries (after, e.g., a BSS is not found in two consecutive
1898 // scans). However, our explicit flush accelerates re-association
1899 // in cases where a BSS disappeared while we were asleep. (See,
1900 // e.g. WiFiRoaming.005SuspendRoam.)
1901 time_->GetTimeMonotonic(&resumed_at_);
1902 need_bss_flush_ = true;
1903
1904 if (!IsConnectedToCurrentService()) {
1905 InitiateScan(kProgressiveScan);
1906 }
1907
1908 // Since we stopped the scan timer before suspending, start it again here.
1909 StartScanTimer();
1910
1911 // Enable HT40 for current service in case if it was disabled previously due
1912 // to unreliable link.
1913 if (current_service_) {
1914 SetHT40EnableForService(current_service_.get(), true);
1915 }
1916 }
1917
AbortScan()1918 void WiFi::AbortScan() {
1919 if (scan_session_) {
1920 scan_session_.reset();
1921 }
1922 SetScanState(kScanIdle, kScanMethodNone, __func__);
1923 }
1924
InitiateScan(ScanType scan_type)1925 void WiFi::InitiateScan(ScanType scan_type) {
1926 LOG(INFO) << __func__;
1927 // Abort any current scan (at the shill-level; let any request that's
1928 // already gone out finish) since we don't know when it started.
1929 AbortScan();
1930
1931 if (IsIdle()) {
1932 // Not scanning/connecting/connected, so let's get things rolling.
1933 Scan(scan_type, nullptr, __func__);
1934 RestartFastScanAttempts();
1935 } else {
1936 SLOG(this, 1) << __func__
1937 << " skipping scan, already connecting or connected.";
1938 }
1939 }
1940
InitiateScanInDarkResume(const FreqSet & freqs)1941 void WiFi::InitiateScanInDarkResume(const FreqSet& freqs) {
1942 LOG(INFO) << __func__;
1943 AbortScan();
1944 if (!IsIdle()) {
1945 SLOG(this, 1) << __func__
1946 << " skipping scan, already connecting or connected.";
1947 return;
1948 }
1949
1950 CHECK(supplicant_interface_proxy_);
1951 // Force complete flush of BSS cache since we want WPA supplicant and shill to
1952 // have an accurate view of what endpoints are available in dark resume. This
1953 // prevents either from performing incorrect actions that can prolong dark
1954 // resume (e.g. attempting to auto-connect to a WiFi service whose endpoint
1955 // disappeared before the dark resume).
1956 if (!supplicant_interface_proxy_->FlushBSS(0)) {
1957 LOG(WARNING) << __func__
1958 << ": Failed to flush wpa_supplicant BSS cache";
1959 }
1960 // Suppress any autoconnect attempts until this scan is done and endpoints
1961 // are updated.
1962 manager()->set_suppress_autoconnect(true);
1963
1964 TriggerPassiveScan(freqs);
1965 }
1966
TriggerPassiveScan(const FreqSet & freqs)1967 void WiFi::TriggerPassiveScan(const FreqSet& freqs) {
1968 LOG(INFO) << __func__;
1969 TriggerScanMessage trigger_scan;
1970 trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
1971 interface_index());
1972 if (!freqs.empty()) {
1973 SLOG(this, 3) << __func__ << ": " << "Scanning on specific channels";
1974 trigger_scan.attributes()->CreateNl80211Attribute(
1975 NL80211_ATTR_SCAN_FREQUENCIES, NetlinkMessage::MessageContext());
1976
1977 AttributeListRefPtr frequency_list;
1978 if (!trigger_scan.attributes()->GetNestedAttributeList(
1979 NL80211_ATTR_SCAN_FREQUENCIES, &frequency_list) ||
1980 !frequency_list) {
1981 LOG(ERROR) << __func__ << ": "
1982 << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES";
1983 }
1984 trigger_scan.attributes()->SetNestedAttributeHasAValue(
1985 NL80211_ATTR_SCAN_FREQUENCIES);
1986
1987 string attribute_name;
1988 int i = 0;
1989 for (uint32_t freq : freqs) {
1990 SLOG(this, 7) << __func__ << ": "
1991 << "Frequency-" << i << ": " << freq;
1992 attribute_name = StringPrintf("Frequency-%d", i);
1993 frequency_list->CreateU32Attribute(i, attribute_name.c_str());
1994 frequency_list->SetU32AttributeValue(i, freq);
1995 ++i;
1996 }
1997 }
1998
1999 netlink_manager_->SendNl80211Message(
2000 &trigger_scan,
2001 Bind(&WiFi::OnTriggerPassiveScanResponse, weak_ptr_factory_.GetWeakPtr()),
2002 Bind(&NetlinkManager::OnAckDoNothing),
2003 Bind(&NetlinkManager::OnNetlinkMessageError));
2004 }
2005
OnConnected()2006 void WiFi::OnConnected() {
2007 Device::OnConnected();
2008 EnableHighBitrates();
2009 if (current_service_ &&
2010 current_service_->IsSecurityMatch(kSecurityWep)) {
2011 // With a WEP network, we are now reasonably certain the credentials are
2012 // correct, whereas with other network types we were able to determine
2013 // this earlier when the association process succeeded.
2014 current_service_->ResetSuspectedCredentialFailures();
2015 }
2016 RequestStationInfo();
2017 }
2018
OnIPConfigFailure()2019 void WiFi::OnIPConfigFailure() {
2020 if (!current_service_) {
2021 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
2022 << " with no current service.";
2023 return;
2024 }
2025 if (current_service_->IsSecurityMatch(kSecurityWep) &&
2026 GetReceiveByteCount() == receive_byte_count_at_connect_ &&
2027 current_service_->AddSuspectedCredentialFailure()) {
2028 // If we've connected to a WEP network and haven't successfully
2029 // decrypted any bytes at all during the configuration process,
2030 // it is fair to suspect that our credentials to this network
2031 // may not be correct.
2032 Error error;
2033 current_service_->DisconnectWithFailure(Service::kFailureBadPassphrase,
2034 &error,
2035 __func__);
2036 return;
2037 }
2038
2039 Device::OnIPConfigFailure();
2040 }
2041
AddWakeOnPacketConnection(const string & ip_endpoint,Error * error)2042 void WiFi::AddWakeOnPacketConnection(const string& ip_endpoint, Error* error) {
2043 wake_on_wifi_->AddWakeOnPacketConnection(ip_endpoint, error);
2044 }
2045
RemoveWakeOnPacketConnection(const string & ip_endpoint,Error * error)2046 void WiFi::RemoveWakeOnPacketConnection(const string& ip_endpoint,
2047 Error* error) {
2048 wake_on_wifi_->RemoveWakeOnPacketConnection(ip_endpoint, error);
2049 }
2050
RemoveAllWakeOnPacketConnections(Error * error)2051 void WiFi::RemoveAllWakeOnPacketConnections(Error* error) {
2052 wake_on_wifi_->RemoveAllWakeOnPacketConnections(error);
2053 }
2054
RestartFastScanAttempts()2055 void WiFi::RestartFastScanAttempts() {
2056 fast_scans_remaining_ = kNumFastScanAttempts;
2057 StartScanTimer();
2058 }
2059
StartScanTimer()2060 void WiFi::StartScanTimer() {
2061 SLOG(this, 2) << __func__;
2062 if (scan_interval_seconds_ == 0) {
2063 StopScanTimer();
2064 return;
2065 }
2066 scan_timer_callback_.Reset(
2067 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
2068 // Repeat the first few scans after disconnect relatively quickly so we
2069 // have reasonable trust that no APs we are looking for are present.
2070 size_t wait_time_milliseconds = fast_scans_remaining_ > 0 ?
2071 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000;
2072 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
2073 wait_time_milliseconds);
2074 SLOG(this, 5) << "Next scan scheduled for " << wait_time_milliseconds << "ms";
2075 }
2076
StopScanTimer()2077 void WiFi::StopScanTimer() {
2078 SLOG(this, 2) << __func__;
2079 scan_timer_callback_.Cancel();
2080 }
2081
ScanTimerHandler()2082 void WiFi::ScanTimerHandler() {
2083 SLOG(this, 2) << "WiFi Device " << link_name() << ": " << __func__;
2084 if (manager()->IsSuspending()) {
2085 SLOG(this, 5) << "Not scanning: still in suspend";
2086 return;
2087 }
2088 if (scan_state_ == kScanIdle && IsIdle()) {
2089 Scan(kProgressiveScan, nullptr, __func__);
2090 if (fast_scans_remaining_ > 0) {
2091 --fast_scans_remaining_;
2092 }
2093 } else {
2094 if (scan_state_ != kScanIdle) {
2095 SLOG(this, 5) << "Skipping scan: scan_state_ is " << scan_state_;
2096 }
2097 if (current_service_) {
2098 SLOG(this, 5) << "Skipping scan: current_service_ is service "
2099 << current_service_->unique_name();
2100 }
2101 if (pending_service_) {
2102 SLOG(this, 5) << "Skipping scan: pending_service_ is service"
2103 << pending_service_->unique_name();
2104 }
2105 }
2106 StartScanTimer();
2107 }
2108
StartPendingTimer()2109 void WiFi::StartPendingTimer() {
2110 pending_timeout_callback_.Reset(
2111 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
2112 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
2113 kPendingTimeoutSeconds * 1000);
2114 }
2115
StopPendingTimer()2116 void WiFi::StopPendingTimer() {
2117 SLOG(this, 2) << "WiFi Device " << link_name() << ": " << __func__;
2118 pending_timeout_callback_.Cancel();
2119 }
2120
SetPendingService(const WiFiServiceRefPtr & service)2121 void WiFi::SetPendingService(const WiFiServiceRefPtr& service) {
2122 SLOG(this, 2) << "WiFi " << link_name() << " setting pending service to "
2123 << (service ? service->unique_name(): "NULL");
2124 if (service) {
2125 SetScanState(kScanConnecting, scan_method_, __func__);
2126 service->SetState(Service::kStateAssociating);
2127 StartPendingTimer();
2128 } else {
2129 // SetPendingService(nullptr) is called in the following cases:
2130 // a) |ConnectTo|->|DisconnectFrom|. Connecting to a service, disconnect
2131 // the old service (scan_state_ == kScanTransitionToConnecting). No
2132 // state transition is needed here.
2133 // b) |HandleRoam|. Connected to a service, it's no longer pending
2134 // (scan_state_ == kScanIdle). No state transition is needed here.
2135 // c) |DisconnectFrom| and |HandleDisconnect|. Disconnected/disconnecting
2136 // from a service not during a scan (scan_state_ == kScanIdle). No
2137 // state transition is needed here.
2138 // d) |DisconnectFrom| and |HandleDisconnect|. Disconnected/disconnecting
2139 // from a service during a scan (scan_state_ == kScanScanning or
2140 // kScanConnecting). This is an odd case -- let's discard any
2141 // statistics we're gathering by transitioning directly into kScanIdle.
2142 if (scan_state_ == kScanScanning ||
2143 scan_state_ == kScanBackgroundScanning ||
2144 scan_state_ == kScanConnecting) {
2145 SetScanState(kScanIdle, kScanMethodNone, __func__);
2146 }
2147 if (pending_service_) {
2148 StopPendingTimer();
2149 }
2150 }
2151 pending_service_ = service;
2152 }
2153
PendingTimeoutHandler()2154 void WiFi::PendingTimeoutHandler() {
2155 Error unused_error;
2156 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
2157 CHECK(pending_service_);
2158 SetScanState(kScanFoundNothing, scan_method_, __func__);
2159 WiFiServiceRefPtr pending_service = pending_service_;
2160 pending_service_->DisconnectWithFailure(
2161 Service::kFailureOutOfRange, &unused_error, __func__);
2162
2163 // A hidden service may have no endpoints, since wpa_supplicant
2164 // failed to attain a CurrentBSS. If so, the service has no
2165 // reference to |this| device and cannot call WiFi::DisconnectFrom()
2166 // to reset pending_service_. In this case, we must perform the
2167 // disconnect here ourselves.
2168 if (pending_service_) {
2169 CHECK(!pending_service_->HasEndpoints());
2170 LOG(INFO) << "Hidden service was not found.";
2171 DisconnectFrom(pending_service_.get());
2172 }
2173
2174 // DisconnectWithFailure will leave the pending service's state in failure
2175 // state. Reset its state back to idle, to allow it to be connectable again.
2176 pending_service->SetState(Service::kStateIdle);
2177 }
2178
StartReconnectTimer()2179 void WiFi::StartReconnectTimer() {
2180 if (!reconnect_timeout_callback_.IsCancelled()) {
2181 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
2182 << ": reconnect timer already running.";
2183 return;
2184 }
2185 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
2186 reconnect_timeout_callback_.Reset(
2187 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
2188 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
2189 kReconnectTimeoutSeconds * 1000);
2190 }
2191
StopReconnectTimer()2192 void WiFi::StopReconnectTimer() {
2193 SLOG(this, 2) << "WiFi Device " << link_name() << ": " << __func__;
2194 reconnect_timeout_callback_.Cancel();
2195 }
2196
ReconnectTimeoutHandler()2197 void WiFi::ReconnectTimeoutHandler() {
2198 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
2199 reconnect_timeout_callback_.Cancel();
2200 CHECK(current_service_);
2201 current_service_->SetFailure(Service::kFailureConnect);
2202 DisconnectFrom(current_service_.get());
2203 }
2204
OnSupplicantAppear()2205 void WiFi::OnSupplicantAppear() {
2206 LOG(INFO) << "WPA supplicant appeared.";
2207 if (supplicant_present_) {
2208 // Restart the WiFi device if it's started already. This will reset the
2209 // state and connect the device to the new WPA supplicant instance.
2210 if (enabled()) {
2211 Restart();
2212 }
2213 return;
2214 }
2215 supplicant_present_ = true;
2216 ConnectToSupplicant();
2217 }
2218
OnSupplicantVanish()2219 void WiFi::OnSupplicantVanish() {
2220 LOG(INFO) << "WPA supplicant vanished.";
2221 if (!supplicant_present_) {
2222 return;
2223 }
2224 supplicant_present_ = false;
2225 // Restart the WiFi device if it's started already. This will effectively
2226 // suspend the device until the WPA supplicant reappears.
2227 if (enabled()) {
2228 Restart();
2229 }
2230 }
2231
OnWiFiDebugScopeChanged(bool enabled)2232 void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
2233 SLOG(this, 2) << "WiFi debug scope changed; enable is now " << enabled;
2234 if (!Device::enabled() || !supplicant_present_) {
2235 SLOG(this, 2) << "Supplicant process proxy not connected.";
2236 return;
2237 }
2238 string current_level;
2239 if (!supplicant_process_proxy_->GetDebugLevel(¤t_level)) {
2240 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
2241 return;
2242 }
2243
2244 if (current_level != WPASupplicant::kDebugLevelInfo &&
2245 current_level != WPASupplicant::kDebugLevelDebug) {
2246 SLOG(this, 2) << "WiFi debug level is currently "
2247 << current_level
2248 << "; assuming that it is being controlled elsewhere.";
2249 return;
2250 }
2251 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
2252 WPASupplicant::kDebugLevelInfo;
2253
2254 if (new_level == current_level) {
2255 SLOG(this, 2) << "WiFi debug level is already the desired level "
2256 << current_level;
2257 return;
2258 }
2259
2260 if (!supplicant_process_proxy_->SetDebugLevel(new_level)) {
2261 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
2262 }
2263 }
2264
SetConnectionDebugging(bool enabled)2265 void WiFi::SetConnectionDebugging(bool enabled) {
2266 if (is_debugging_connection_ == enabled) {
2267 return;
2268 }
2269 OnWiFiDebugScopeChanged(
2270 enabled ||
2271 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
2272 is_debugging_connection_ = enabled;
2273 }
2274
SetSupplicantInterfaceProxy(SupplicantInterfaceProxyInterface * supplicant_interface_proxy)2275 void WiFi::SetSupplicantInterfaceProxy(
2276 SupplicantInterfaceProxyInterface* supplicant_interface_proxy) {
2277 if (supplicant_interface_proxy) {
2278 supplicant_interface_proxy_.reset(supplicant_interface_proxy);
2279 tdls_manager_.reset(new TDLSManager(dispatcher(),
2280 supplicant_interface_proxy,
2281 link_name()));
2282 } else {
2283 supplicant_interface_proxy_.reset();
2284 tdls_manager_.reset();
2285 }
2286 }
2287
ConnectToSupplicant()2288 void WiFi::ConnectToSupplicant() {
2289 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
2290 << " supplicant: "
2291 << (supplicant_present_ ? "present" : "absent")
2292 << " proxy: "
2293 << (supplicant_interface_proxy_.get() ? "non-null" : "null");
2294 // The check for |supplicant_interface_proxy_| is mainly for testing,
2295 // to avoid recreation of supplicant interface proxy.
2296 if (!enabled() || !supplicant_present_ || supplicant_interface_proxy_) {
2297 return;
2298 }
2299 OnWiFiDebugScopeChanged(
2300 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
2301
2302 KeyValueStore create_interface_args;
2303 create_interface_args.SetString(WPASupplicant::kInterfacePropertyName,
2304 link_name());
2305 create_interface_args.SetString(WPASupplicant::kInterfacePropertyDriver,
2306 WPASupplicant::kDriverNL80211);
2307 create_interface_args.SetString(WPASupplicant::kInterfacePropertyConfigFile,
2308 WPASupplicant::kSupplicantConfPath);
2309 if (!supplicant_process_proxy_->CreateInterface(
2310 create_interface_args, &supplicant_interface_path_)) {
2311 // Interface might've already been created, attempt to retrieve it.
2312 if (!supplicant_process_proxy_->GetInterface(link_name(),
2313 &supplicant_interface_path_)) {
2314 // TODO(quiche): Is it okay to crash here, if device is missing?
2315 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
2316 return;
2317 }
2318 }
2319
2320 SetSupplicantInterfaceProxy(
2321 control_interface()->CreateSupplicantInterfaceProxy(
2322 this, supplicant_interface_path_));
2323
2324 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
2325 IFF_UP);
2326 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
2327
2328 // Clear out any networks that might previously have been configured
2329 // for this interface.
2330 supplicant_interface_proxy_->RemoveAllNetworks();
2331
2332 // Flush interface's BSS cache, so that we get BSSAdded signals for
2333 // all BSSes (not just new ones since the last scan).
2334 supplicant_interface_proxy_->FlushBSS(0);
2335
2336 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
2337 // with RADIUS servers that respond strangely to such requests.
2338 // crbug.com/208561
2339 if (!supplicant_interface_proxy_->SetFastReauth(false)) {
2340 LOG(ERROR) << "Failed to disable fast_reauth. "
2341 << "May be running an older version of wpa_supplicant.";
2342 }
2343
2344 if (!supplicant_interface_proxy_->SetRoamThreshold(roam_threshold_db_)) {
2345 LOG(ERROR) << "Failed to set roam_threshold. "
2346 << "May be running an older version of wpa_supplicant.";
2347 }
2348
2349 // Helps with passing WiFiRoaming.001SSIDSwitchBack.
2350 if (!supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds)) {
2351 LOG(ERROR) << "Failed to set scan_interval. "
2352 << "May be running an older version of wpa_supplicant.";
2353 }
2354
2355 if (!supplicant_interface_proxy_->SetDisableHighBitrates(true)) {
2356 LOG(ERROR) << "Failed to disable high bitrates. "
2357 << "May be running an older version of wpa_supplicant.";
2358 }
2359
2360 Scan(kProgressiveScan, nullptr, __func__);
2361 StartScanTimer();
2362 }
2363
EnableHighBitrates()2364 void WiFi::EnableHighBitrates() {
2365 LOG(INFO) << "Enabling high bitrates.";
2366 if (!supplicant_interface_proxy_->EnableHighBitrates()) {
2367 LOG(ERROR) << "Failed to enable high rates";
2368 }
2369 }
2370
Restart()2371 void WiFi::Restart() {
2372 LOG(INFO) << link_name() << " restarting.";
2373 WiFiRefPtr me = this; // Make sure we don't get destructed.
2374 // Go through the manager rather than starting and stopping the device
2375 // directly so that the device can be configured with the profile.
2376 manager()->DeregisterDevice(me);
2377 manager()->RegisterDevice(me);
2378 }
2379
GetPhyInfo()2380 void WiFi::GetPhyInfo() {
2381 GetWiphyMessage get_wiphy;
2382 get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
2383 interface_index());
2384 netlink_manager_->SendNl80211Message(
2385 &get_wiphy,
2386 Bind(&WiFi::OnNewWiphy, weak_ptr_factory_.GetWeakPtr()),
2387 Bind(&NetlinkManager::OnAckDoNothing),
2388 Bind(&NetlinkManager::OnNetlinkMessageError));
2389 }
2390
OnNewWiphy(const Nl80211Message & nl80211_message)2391 void WiFi::OnNewWiphy(const Nl80211Message& nl80211_message) {
2392 // Verify NL80211_CMD_NEW_WIPHY.
2393 if (nl80211_message.command() != NewWiphyMessage::kCommand) {
2394 LOG(ERROR) << "Received unexpected command:"
2395 << nl80211_message.command();
2396 return;
2397 }
2398
2399 if (!nl80211_message.const_attributes()->GetStringAttributeValue(
2400 NL80211_ATTR_WIPHY_NAME, &phy_name_)) {
2401 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_NAME";
2402 return;
2403 }
2404 mac80211_monitor_->Start(phy_name_);
2405
2406 wake_on_wifi_->ParseWakeOnWiFiCapabilities(nl80211_message);
2407 if (ParseWiphyIndex(nl80211_message)) {
2408 wake_on_wifi_->OnWiphyIndexReceived(wiphy_index_);
2409 }
2410
2411 // The attributes, for this message, are complicated.
2412 // NL80211_ATTR_BANDS contains an array of bands...
2413 AttributeListConstRefPtr wiphy_bands;
2414 if (!nl80211_message.const_attributes()->ConstGetNestedAttributeList(
2415 NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) {
2416 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS";
2417 return;
2418 }
2419
2420 AttributeIdIterator band_iter(*wiphy_bands);
2421 for (; !band_iter.AtEnd(); band_iter.Advance()) {
2422 AttributeListConstRefPtr wiphy_band;
2423 if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(),
2424 &wiphy_band)) {
2425 LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found";
2426 continue;
2427 }
2428
2429 // ...Each band has a FREQS attribute...
2430 AttributeListConstRefPtr frequencies;
2431 if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
2432 &frequencies)) {
2433 LOG(ERROR) << "BAND " << band_iter.GetId()
2434 << " had no 'frequencies' attribute";
2435 continue;
2436 }
2437
2438 // ...And each FREQS attribute contains an array of information about the
2439 // frequency...
2440 AttributeIdIterator freq_iter(*frequencies);
2441 for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
2442 AttributeListConstRefPtr frequency;
2443 if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(),
2444 &frequency)) {
2445 // ...Including the frequency, itself (the part we want).
2446 uint32_t frequency_value = 0;
2447 if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
2448 &frequency_value)) {
2449 SLOG(this, 7) << "Found frequency[" << freq_iter.GetId()
2450 << "] = " << frequency_value;
2451 all_scan_frequencies_.insert(frequency_value);
2452 }
2453 }
2454 }
2455 }
2456 }
2457
OnTriggerPassiveScanResponse(const Nl80211Message & netlink_message)2458 void WiFi::OnTriggerPassiveScanResponse(const Nl80211Message& netlink_message) {
2459 LOG(WARNING) << "Didn't expect _this_netlink message ("
2460 << netlink_message.command() << " here:";
2461 netlink_message.Print(0, 0);
2462 return;
2463 }
2464
GetLinkStatistics(Error *)2465 KeyValueStore WiFi::GetLinkStatistics(Error* /*error*/) {
2466 return link_statistics_;
2467 }
2468
GetScanPending(Error *)2469 bool WiFi::GetScanPending(Error* /* error */) {
2470 return scan_state_ == kScanScanning || scan_state_ == kScanBackgroundScanning;
2471 }
2472
SetScanState(ScanState new_state,ScanMethod new_method,const char * reason)2473 void WiFi::SetScanState(ScanState new_state,
2474 ScanMethod new_method,
2475 const char* reason) {
2476 if (new_state == kScanIdle)
2477 new_method = kScanMethodNone;
2478 if (new_state == kScanConnected) {
2479 // The scan method shouldn't be changed by the connection process, so
2480 // we'll put a CHECK, here, to verify. NOTE: this assumption is also
2481 // enforced by the parameters to the call to |ReportScanResultToUma|.
2482 CHECK(new_method == scan_method_);
2483 }
2484
2485 int log_level = 6;
2486 bool state_or_method_changed = true;
2487 bool is_terminal_state = false;
2488 if (new_state == scan_state_ && new_method == scan_method_) {
2489 log_level = 7;
2490 state_or_method_changed = false;
2491 } else if (new_state == kScanConnected || new_state == kScanFoundNothing) {
2492 // These 'terminal' states are slightly more interesting than the
2493 // intermediate states.
2494 // NOTE: Since background scan goes directly to kScanIdle (skipping over
2495 // the states required to set |is_terminal_state|), ReportScanResultToUma,
2496 // below, doesn't get called. That's intentional.
2497 log_level = 5;
2498 is_terminal_state = true;
2499 }
2500
2501 base::TimeDelta elapsed_time;
2502 if (new_state == kScanScanning || new_state == kScanBackgroundScanning) {
2503 if (!scan_timer_.Start()) {
2504 LOG(ERROR) << "Scan start unreliable";
2505 }
2506 } else {
2507 if (!scan_timer_.GetElapsedTime(&elapsed_time)) {
2508 LOG(ERROR) << "Scan time unreliable";
2509 }
2510 }
2511 SLOG(this, log_level) << (reason ? reason : "<unknown>")
2512 << " - " << link_name()
2513 << ": Scan state: "
2514 << ScanStateString(scan_state_, scan_method_)
2515 << " -> " << ScanStateString(new_state, new_method)
2516 << " @ " << elapsed_time.InMillisecondsF()
2517 << " ms into scan.";
2518 if (!state_or_method_changed)
2519 return;
2520
2521 // Actually change the state.
2522 ScanState old_state = scan_state_;
2523 ScanMethod old_method = scan_method_;
2524 bool old_scan_pending = GetScanPending(nullptr);
2525 scan_state_ = new_state;
2526 scan_method_ = new_method;
2527 bool new_scan_pending = GetScanPending(nullptr);
2528 if (old_scan_pending != new_scan_pending) {
2529 adaptor()->EmitBoolChanged(kScanningProperty, new_scan_pending);
2530 }
2531 switch (new_state) {
2532 case kScanIdle:
2533 metrics()->ResetScanTimer(interface_index());
2534 metrics()->ResetConnectTimer(interface_index());
2535 if (scan_session_) {
2536 scan_session_.reset();
2537 }
2538 break;
2539 case kScanScanning: // FALLTHROUGH
2540 case kScanBackgroundScanning:
2541 if (new_state != old_state) {
2542 metrics()->NotifyDeviceScanStarted(interface_index());
2543 }
2544 break;
2545 case kScanConnecting:
2546 metrics()->NotifyDeviceScanFinished(interface_index());
2547 // TODO(wdg): Provide |is_auto_connecting| to this interface. For now,
2548 // I'll lie (because I don't care about the auto-connect metrics).
2549 metrics()->NotifyDeviceConnectStarted(interface_index(), false);
2550 break;
2551 case kScanConnected:
2552 metrics()->NotifyDeviceConnectFinished(interface_index());
2553 break;
2554 case kScanFoundNothing:
2555 // Note that finishing a scan that hasn't started (if, for example, we
2556 // get here when we fail to complete a connection) does nothing.
2557 metrics()->NotifyDeviceScanFinished(interface_index());
2558 metrics()->ResetConnectTimer(interface_index());
2559 break;
2560 case kScanTransitionToConnecting: // FALLTHROUGH
2561 default:
2562 break;
2563 }
2564 if (is_terminal_state) {
2565 ReportScanResultToUma(new_state, old_method);
2566 // Now that we've logged a terminal state, let's call ourselves to
2567 // transition to the idle state.
2568 SetScanState(kScanIdle, kScanMethodNone, reason);
2569 }
2570 }
2571
2572 // static
ScanStateString(ScanState state,ScanMethod method)2573 string WiFi::ScanStateString(ScanState state, ScanMethod method) {
2574 switch (state) {
2575 case kScanIdle:
2576 return "IDLE";
2577 case kScanScanning:
2578 DCHECK(method != kScanMethodNone) << "Scanning with no scan method.";
2579 switch (method) {
2580 case kScanMethodFull:
2581 return "FULL_START";
2582 case kScanMethodProgressive:
2583 return "PROGRESSIVE_START";
2584 case kScanMethodProgressiveErrorToFull:
2585 return "PROGRESSIVE_ERROR_FULL_START";
2586 case kScanMethodProgressiveFinishedToFull:
2587 return "PROGRESSIVE_FINISHED_FULL_START";
2588 default:
2589 NOTREACHED();
2590 }
2591 case kScanBackgroundScanning:
2592 return "BACKGROUND_START";
2593 case kScanTransitionToConnecting:
2594 return "TRANSITION_TO_CONNECTING";
2595 case kScanConnecting:
2596 switch (method) {
2597 case kScanMethodNone:
2598 return "CONNECTING (not scan related)";
2599 case kScanMethodFull:
2600 return "FULL_CONNECTING";
2601 case kScanMethodProgressive:
2602 return "PROGRESSIVE_CONNECTING";
2603 case kScanMethodProgressiveErrorToFull:
2604 return "PROGRESSIVE_ERROR_FULL_CONNECTING";
2605 case kScanMethodProgressiveFinishedToFull:
2606 return "PROGRESSIVE_FINISHED_FULL_CONNECTING";
2607 default:
2608 NOTREACHED();
2609 }
2610 case kScanConnected:
2611 switch (method) {
2612 case kScanMethodNone:
2613 return "CONNECTED (not scan related; e.g., from a supplicant roam)";
2614 case kScanMethodFull:
2615 return "FULL_CONNECTED";
2616 case kScanMethodProgressive:
2617 return "PROGRESSIVE_CONNECTED";
2618 case kScanMethodProgressiveErrorToFull:
2619 return "PROGRESSIVE_ERROR_FULL_CONNECTED";
2620 case kScanMethodProgressiveFinishedToFull:
2621 return "PROGRESSIVE_FINISHED_FULL_CONNECTED";
2622 default:
2623 NOTREACHED();
2624 }
2625 case kScanFoundNothing:
2626 switch (method) {
2627 case kScanMethodNone:
2628 return "CONNECT FAILED (not scan related)";
2629 case kScanMethodFull:
2630 return "FULL_NOCONNECTION";
2631 case kScanMethodProgressive:
2632 // This is possible if shill started to connect but timed out before
2633 // the connection was completed.
2634 return "PROGRESSIVE_FINISHED_NOCONNECTION";
2635 case kScanMethodProgressiveErrorToFull:
2636 return "PROGRESSIVE_ERROR_FULL_NOCONNECTION";
2637 case kScanMethodProgressiveFinishedToFull:
2638 return "PROGRESSIVE_FINISHED_FULL_NOCONNECTION";
2639 default:
2640 NOTREACHED();
2641 }
2642 default:
2643 NOTREACHED();
2644 }
2645 return ""; // To shut up the compiler (that doesn't understand NOTREACHED).
2646 }
2647
ReportScanResultToUma(ScanState state,ScanMethod method)2648 void WiFi::ReportScanResultToUma(ScanState state, ScanMethod method) {
2649 Metrics::WiFiScanResult result = Metrics::kScanResultMax;
2650 if (state == kScanConnected) {
2651 switch (method) {
2652 case kScanMethodFull:
2653 result = Metrics::kScanResultFullScanConnected;
2654 break;
2655 case kScanMethodProgressive:
2656 result = Metrics::kScanResultProgressiveConnected;
2657 break;
2658 case kScanMethodProgressiveErrorToFull:
2659 result = Metrics::kScanResultProgressiveErrorButFullConnected;
2660 break;
2661 case kScanMethodProgressiveFinishedToFull:
2662 result = Metrics::kScanResultProgressiveAndFullConnected;
2663 break;
2664 default:
2665 // OK: Connect resulting from something other than scan.
2666 break;
2667 }
2668 } else if (state == kScanFoundNothing) {
2669 switch (method) {
2670 case kScanMethodFull:
2671 result = Metrics::kScanResultFullScanFoundNothing;
2672 break;
2673 case kScanMethodProgressiveErrorToFull:
2674 result = Metrics::kScanResultProgressiveErrorAndFullFoundNothing;
2675 break;
2676 case kScanMethodProgressiveFinishedToFull:
2677 result = Metrics::kScanResultProgressiveAndFullFoundNothing;
2678 break;
2679 default:
2680 // OK: Connect failed, not scan related.
2681 break;
2682 }
2683 }
2684
2685 if (result != Metrics::kScanResultMax) {
2686 metrics()->SendEnumToUMA(Metrics::kMetricScanResult,
2687 result,
2688 Metrics::kScanResultMax);
2689 }
2690 }
2691
RequestStationInfo()2692 void WiFi::RequestStationInfo() {
2693 if (!IsConnectedToCurrentService()) {
2694 LOG(ERROR) << "Not collecting station info because we are not connected.";
2695 return;
2696 }
2697
2698 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(supplicant_bss_);
2699 if (endpoint_it == endpoint_by_rpcid_.end()) {
2700 LOG(ERROR) << "Can't get endpoint for current supplicant BSS "
2701 << supplicant_bss_;
2702 return;
2703 }
2704
2705 GetStationMessage get_station;
2706 if (!get_station.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
2707 interface_index())) {
2708 LOG(ERROR) << "Could not add IFINDEX attribute for GetStation message.";
2709 return;
2710 }
2711
2712 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
2713 if (!get_station.attributes()->SetRawAttributeValue(
2714 NL80211_ATTR_MAC,
2715 ByteString::CreateFromHexString(endpoint->bssid_hex()))) {
2716 LOG(ERROR) << "Could not add MAC attribute for GetStation message.";
2717 return;
2718 }
2719
2720 netlink_manager_->SendNl80211Message(
2721 &get_station,
2722 Bind(&WiFi::OnReceivedStationInfo, weak_ptr_factory_.GetWeakPtr()),
2723 Bind(&NetlinkManager::OnAckDoNothing),
2724 Bind(&NetlinkManager::OnNetlinkMessageError));
2725
2726 request_station_info_callback_.Reset(
2727 Bind(&WiFi::RequestStationInfo, weak_ptr_factory_.GetWeakPtr()));
2728 dispatcher()->PostDelayedTask(request_station_info_callback_.callback(),
2729 kRequestStationInfoPeriodSeconds * 1000);
2730 }
2731
OnReceivedStationInfo(const Nl80211Message & nl80211_message)2732 void WiFi::OnReceivedStationInfo(const Nl80211Message& nl80211_message) {
2733 // Verify NL80211_CMD_NEW_STATION
2734 if (nl80211_message.command() != NewStationMessage::kCommand) {
2735 LOG(ERROR) << "Received unexpected command:"
2736 << nl80211_message.command();
2737 return;
2738 }
2739
2740 if (!IsConnectedToCurrentService()) {
2741 LOG(ERROR) << "Not accepting station info because we are not connected.";
2742 return;
2743 }
2744
2745 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(supplicant_bss_);
2746 if (endpoint_it == endpoint_by_rpcid_.end()) {
2747 LOG(ERROR) << "Can't get endpoint for current supplicant BSS."
2748 << supplicant_bss_;
2749 return;
2750 }
2751
2752 ByteString station_bssid;
2753 if (!nl80211_message.const_attributes()->GetRawAttributeValue(
2754 NL80211_ATTR_MAC, &station_bssid)) {
2755 LOG(ERROR) << "Unable to get MAC attribute from received station info.";
2756 return;
2757 }
2758
2759 WiFiEndpointRefPtr endpoint(endpoint_it->second);
2760
2761 if (!station_bssid.Equals(
2762 ByteString::CreateFromHexString(endpoint->bssid_hex()))) {
2763 LOG(ERROR) << "Received station info for a non-current BSS.";
2764 return;
2765 }
2766
2767 AttributeListConstRefPtr station_info;
2768 if (!nl80211_message.const_attributes()->ConstGetNestedAttributeList(
2769 NL80211_ATTR_STA_INFO, &station_info)) {
2770 LOG(ERROR) << "Received station info had no NL80211_ATTR_STA_INFO.";
2771 return;
2772 }
2773
2774 uint8_t signal;
2775 if (!station_info->GetU8AttributeValue(NL80211_STA_INFO_SIGNAL, &signal)) {
2776 LOG(ERROR) << "Received station info had no NL80211_STA_INFO_SIGNAL.";
2777 return;
2778 }
2779
2780 endpoint->UpdateSignalStrength(static_cast<signed char>(signal));
2781
2782 link_statistics_.Clear();
2783
2784 map<int, string> u32_property_map = {
2785 { NL80211_STA_INFO_INACTIVE_TIME, kInactiveTimeMillisecondsProperty },
2786 { NL80211_STA_INFO_RX_PACKETS, kPacketReceiveSuccessesProperty },
2787 { NL80211_STA_INFO_TX_FAILED, kPacketTransmitFailuresProperty },
2788 { NL80211_STA_INFO_TX_PACKETS, kPacketTransmitSuccessesProperty },
2789 { NL80211_STA_INFO_TX_RETRIES, kTransmitRetriesProperty }
2790 };
2791
2792 for (const auto& kv : u32_property_map) {
2793 uint32_t value;
2794 if (station_info->GetU32AttributeValue(kv.first, &value)) {
2795 link_statistics_.SetUint(kv.second, value);
2796 }
2797 }
2798
2799 map<int, string> s8_property_map = {
2800 { NL80211_STA_INFO_SIGNAL, kLastReceiveSignalDbmProperty },
2801 { NL80211_STA_INFO_SIGNAL_AVG, kAverageReceiveSignalDbmProperty }
2802 };
2803
2804 for (const auto& kv : s8_property_map) {
2805 uint8_t value;
2806 if (station_info->GetU8AttributeValue(kv.first, &value)) {
2807 // Despite these values being reported as a U8 by the kernel, these
2808 // should be interpreted as signed char.
2809 link_statistics_.SetInt(kv.second, static_cast<signed char>(value));
2810 }
2811 }
2812
2813 AttributeListConstRefPtr transmit_info;
2814 if (station_info->ConstGetNestedAttributeList(
2815 NL80211_STA_INFO_TX_BITRATE, &transmit_info)) {
2816 uint32_t rate = 0; // In 100Kbps.
2817 uint16_t u16_rate = 0; // In 100Kbps.
2818 uint8_t mcs = 0;
2819 uint8_t nss = 0;
2820 bool band_flag = false;
2821 bool is_short_gi = false;
2822 string mcs_info;
2823 string nss_info;
2824 string band_info;
2825
2826 if (transmit_info->GetU16AttributeValue(
2827 NL80211_RATE_INFO_BITRATE, &u16_rate)) {
2828 rate = static_cast<uint32_t>(u16_rate);
2829 } else {
2830 transmit_info->GetU32AttributeValue(NL80211_RATE_INFO_BITRATE32, &rate);
2831 }
2832
2833 if (transmit_info->GetU8AttributeValue(NL80211_RATE_INFO_MCS, &mcs)) {
2834 mcs_info = StringPrintf(" MCS %d", mcs);
2835 } else if (transmit_info->GetU8AttributeValue(
2836 NL80211_RATE_INFO_VHT_MCS, &mcs)) {
2837 mcs_info = StringPrintf(" VHT-MCS %d", mcs);
2838 }
2839
2840 if (transmit_info->GetU8AttributeValue(NL80211_RATE_INFO_VHT_NSS, &nss)) {
2841 nss_info = StringPrintf(" VHT-NSS %d", nss);
2842 }
2843
2844 if (transmit_info->GetFlagAttributeValue(NL80211_RATE_INFO_40_MHZ_WIDTH,
2845 &band_flag) && band_flag) {
2846 band_info = StringPrintf(" 40MHz");
2847 } else if (transmit_info->GetFlagAttributeValue(
2848 NL80211_RATE_INFO_80_MHZ_WIDTH, &band_flag) && band_flag) {
2849 band_info = StringPrintf(" 80MHz");
2850 } else if (transmit_info->GetFlagAttributeValue(
2851 NL80211_RATE_INFO_80P80_MHZ_WIDTH, &band_flag) && band_flag) {
2852 band_info = StringPrintf(" 80+80MHz");
2853 } else if (transmit_info->GetFlagAttributeValue(
2854 NL80211_RATE_INFO_160_MHZ_WIDTH, &band_flag) && band_flag) {
2855 band_info = StringPrintf(" 160MHz");
2856 }
2857
2858 transmit_info->GetFlagAttributeValue(NL80211_RATE_INFO_SHORT_GI,
2859 &is_short_gi);
2860 if (rate) {
2861 link_statistics_.SetString(kTransmitBitrateProperty,
2862 StringPrintf("%d.%d MBit/s%s%s%s%s",
2863 rate / 10, rate % 10,
2864 mcs_info.c_str(),
2865 band_info.c_str(),
2866 is_short_gi ? " short GI" : "",
2867 nss_info.c_str()));
2868 metrics()->NotifyWifiTxBitrate(rate/10);
2869 }
2870 }
2871 }
2872
StopRequestingStationInfo()2873 void WiFi::StopRequestingStationInfo() {
2874 SLOG(this, 2) << "WiFi Device " << link_name() << ": " << __func__;
2875 request_station_info_callback_.Cancel();
2876 link_statistics_.Clear();
2877 }
2878
TDLSDiscoverResponse(const string & peer_address)2879 void WiFi::TDLSDiscoverResponse(const string& peer_address) {
2880 LOG(INFO) << __func__ << " TDLS discover response from " << peer_address;
2881
2882 if (!tdls_manager_) {
2883 LOG(ERROR) << "TDLS manager not setup - not connected to supplicant";
2884 return;
2885 }
2886 tdls_manager_->OnDiscoverResponseReceived(peer_address);
2887 }
2888
PerformTDLSOperation(const string & operation,const string & peer,Error * error)2889 string WiFi::PerformTDLSOperation(const string& operation,
2890 const string& peer,
2891 Error* error) {
2892 SLOG(this, 2) << "TDLS command received: " << operation
2893 << " for peer " << peer;
2894 if (!tdls_manager_) {
2895 LOG(ERROR) << "TDLS manager not setup - not connected to supplicant";
2896 return "";
2897 }
2898
2899 string peer_mac_address;
2900 if (!ResolvePeerMacAddress(peer, &peer_mac_address, error)) {
2901 return "";
2902 }
2903
2904 return tdls_manager_->PerformOperation(peer_mac_address, operation, error);
2905 }
2906
2907 // Traffic monitor is enabled for wifi.
IsTrafficMonitorEnabled() const2908 bool WiFi::IsTrafficMonitorEnabled() const {
2909 return true;
2910 }
2911
RemoveSupplicantNetworks()2912 void WiFi::RemoveSupplicantNetworks() {
2913 for (const auto& map_entry : rpcid_by_service_) {
2914 RemoveNetwork(map_entry.second);
2915 }
2916 rpcid_by_service_.clear();
2917 }
2918
OnIPConfigUpdated(const IPConfigRefPtr & ipconfig,bool new_lease_acquired)2919 void WiFi::OnIPConfigUpdated(const IPConfigRefPtr& ipconfig,
2920 bool new_lease_acquired) {
2921 Device::OnIPConfigUpdated(ipconfig, new_lease_acquired);
2922 if (new_lease_acquired) {
2923 SLOG(this, 3) << __func__ << ": "
2924 << "IPv4 DHCP lease obtained";
2925 uint32_t time_to_next_lease_renewal;
2926 bool have_dhcp_lease =
2927 TimeToNextDHCPLeaseRenewal(&time_to_next_lease_renewal);
2928 wake_on_wifi_->OnConnectedAndReachable(have_dhcp_lease,
2929 time_to_next_lease_renewal);
2930 } else {
2931 SLOG(this, 3) << __func__ << ": "
2932 << "Gateway ARP received";
2933 // Do nothing since we are waiting until the DHCP lease is actually
2934 // obtained.
2935 return;
2936 }
2937 }
2938
OnIPv6ConfigUpdated()2939 void WiFi::OnIPv6ConfigUpdated() {
2940 Device::OnIPv6ConfigUpdated();
2941 if (!IsConnectedToCurrentService()) {
2942 return;
2943 }
2944 SLOG(this, 3) << __func__ << ": "
2945 << "IPv6 configuration obtained";
2946 uint32_t time_to_next_lease_renewal;
2947 bool have_dhcp_lease =
2948 TimeToNextDHCPLeaseRenewal(&time_to_next_lease_renewal);
2949 wake_on_wifi_->OnConnectedAndReachable(have_dhcp_lease,
2950 time_to_next_lease_renewal);
2951 }
2952
IsConnectedToCurrentService()2953 bool WiFi::IsConnectedToCurrentService() {
2954 return (current_service_ && current_service_->IsConnected());
2955 }
2956
ReportConnectedToServiceAfterWake()2957 void WiFi::ReportConnectedToServiceAfterWake() {
2958 wake_on_wifi_->ReportConnectedToServiceAfterWake(
2959 IsConnectedToCurrentService());
2960 }
2961
RequestRoam(const std::string & addr,Error * error)2962 bool WiFi::RequestRoam(const std::string& addr, Error* error) {
2963 if (!supplicant_interface_proxy_->Roam(addr)) {
2964 LOG(WARNING) << "Request roam to " << addr << " failed.";
2965 return false;
2966 }
2967 return true;
2968 }
2969
2970 } // namespace shill
2971