blob: 00f8d547ab97dd9f75a74812c16a9b5a5cd78032 [file] [log] [blame]
Paul Stewart21f2aae2013-01-17 17:10:08 -08001// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/wifi_provider.h"
6
Wade Guthrie60a37062013-04-02 11:39:09 -07007#include <stdlib.h>
8
9#include <limits>
Paul Stewart3c504012013-01-17 17:49:58 -080010#include <set>
11#include <string>
12#include <vector>
13
14#include <base/bind.h>
15#include <base/string_number_conversions.h>
16
Paul Stewart21f2aae2013-01-17 17:10:08 -080017#include "shill/error.h"
18#include "shill/event_dispatcher.h"
Paul Stewart3c504012013-01-17 17:49:58 -080019#include "shill/ieee80211.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080020#include "shill/key_value_store.h"
Paul Stewart3c504012013-01-17 17:49:58 -080021#include "shill/logging.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080022#include "shill/manager.h"
23#include "shill/metrics.h"
Paul Stewart3c504012013-01-17 17:49:58 -080024#include "shill/profile.h"
25#include "shill/store_interface.h"
26#include "shill/technology.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080027#include "shill/wifi_endpoint.h"
28#include "shill/wifi_service.h"
29
Paul Stewart3c504012013-01-17 17:49:58 -080030using base::Bind;
31using std::set;
32using std::string;
33using std::vector;
34
Paul Stewart21f2aae2013-01-17 17:10:08 -080035namespace shill {
36
Paul Stewart3c504012013-01-17 17:49:58 -080037// Note that WiFiProvider generates some manager-level errors, because it
38// implements the WiFi portion of the Manager.GetService flimflam API. The
39// API is implemented here, rather than in manager, to keep WiFi-specific
40// logic in the right place.
41const char WiFiProvider::kManagerErrorSSIDRequired[] = "must specify SSID";
42const char WiFiProvider::kManagerErrorSSIDTooLong[] = "SSID is too long";
43const char WiFiProvider::kManagerErrorSSIDTooShort[] = "SSID is too short";
44const char WiFiProvider::kManagerErrorUnsupportedSecurityMode[] =
45 "security mode is unsupported";
46const char WiFiProvider::kManagerErrorUnsupportedServiceMode[] =
47 "service mode is unsupported";
Wade Guthrie60a37062013-04-02 11:39:09 -070048const char WiFiProvider::kFrequencyDelimiter[] = ":";
49const char WiFiProvider::kStorageId[] = "wifi_provider";
50const char WiFiProvider::kStorageFrequencies[] = "Frequencies";
Paul Stewart3c504012013-01-17 17:49:58 -080051
Paul Stewart21f2aae2013-01-17 17:10:08 -080052WiFiProvider::WiFiProvider(ControlInterface *control_interface,
53 EventDispatcher *dispatcher,
54 Metrics *metrics,
55 Manager *manager)
56 : control_interface_(control_interface),
57 dispatcher_(dispatcher),
58 metrics_(metrics),
Paul Stewart6c351ff2013-02-25 15:13:03 -080059 manager_(manager),
Wade Guthrie60a37062013-04-02 11:39:09 -070060 running_(false),
61 total_frequency_connections_(-1L) {}
Paul Stewart21f2aae2013-01-17 17:10:08 -080062
63WiFiProvider::~WiFiProvider() {}
64
Paul Stewart6c351ff2013-02-25 15:13:03 -080065void WiFiProvider::Start() {
66 running_ = true;
67}
Paul Stewart21f2aae2013-01-17 17:10:08 -080068
69void WiFiProvider::Stop() {
Paul Stewart3c504012013-01-17 17:49:58 -080070 SLOG(WiFi, 2) << __func__;
71 while (!services_.empty()) {
72 WiFiServiceRefPtr service = services_.back();
73 ForgetService(service);
74 SLOG(WiFi, 3) << "WiFiProvider deregistering service "
75 << service->unique_name();
76 manager_->DeregisterService(service);
77 }
Paul Stewart0427cc12013-03-25 13:50:39 -070078 service_by_endpoint_.clear();
Paul Stewart6c351ff2013-02-25 15:13:03 -080079 running_ = false;
Paul Stewart21f2aae2013-01-17 17:10:08 -080080}
81
Paul Stewart3c504012013-01-17 17:49:58 -080082void WiFiProvider::CreateServicesFromProfile(const ProfileRefPtr &profile) {
83 const StoreInterface *storage = profile->GetConstStorage();
84 KeyValueStore args;
85 args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
86 set<string> groups = storage->GetGroupsWithProperties(args);
87 bool created_hidden_service = false;
88 for (set<string>::const_iterator it = groups.begin(); it != groups.end();
89 ++it) {
90 string ssid_hex;
91 vector<uint8_t> ssid_bytes;
92 if (!storage->GetString(*it, WiFiService::kStorageSSID, &ssid_hex) ||
93 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
94 SLOG(WiFi, 2) << "Storage group " << *it << " is missing valid \""
95 << WiFiService::kStorageSSID << "\" property";
96 continue;
97 }
98 string network_mode;
99 if (!storage->GetString(*it, WiFiService::kStorageMode, &network_mode) ||
100 network_mode.empty()) {
101 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
102 << WiFiService::kStorageMode << "\" property";
103 continue;
104 }
105 string security;
106 if (!storage->GetString(*it, WiFiService::kStorageSecurity, &security) ||
107 !WiFiService::IsValidSecurityMethod(security)) {
108 SLOG(WiFi, 2) << "Storage group " << *it << " has missing or invalid \""
109 << WiFiService::kStorageSecurity << "\" property";
110 continue;
111 }
112 bool is_hidden = false;
113 if (!storage->GetBool(*it, WiFiService::kStorageHiddenSSID, &is_hidden)) {
114 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
115 << WiFiService::kStorageHiddenSSID << "\" property";
116 continue;
117 }
118
119 if (FindService(ssid_bytes, network_mode, security)) {
120 // If service already exists, we have nothing to do, since the
121 // service has already loaded its configuration from storage.
122 // This is guaranteed to happen in the single case where
123 // CreateServicesFromProfile() is called on a WiFiProvider from
124 // Manager::PushProfile():
125 continue;
126 }
127
128 AddService(ssid_bytes, network_mode, security, is_hidden);
129
130 // By registering the service in AddService, the rest of the configuration
131 // will be loaded from the profile into the service via ConfigureService().
132
133 if (is_hidden) {
134 created_hidden_service = true;
135 }
136 }
137
138 // If WiFi is unconnected and we created a hidden service as a result
139 // of opening the profile, we should initiate a WiFi scan, which will
140 // allow us to find any hidden services that we may have created.
141 if (created_hidden_service &&
142 !manager_->IsTechnologyConnected(Technology::kWifi)) {
143 Error unused_error;
Wade Guthrie68d41092013-04-02 12:56:02 -0700144 manager_->RequestScan(Device::kProgressiveScan, flimflam::kTypeWifi,
145 &unused_error);
Paul Stewart3c504012013-01-17 17:49:58 -0800146 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800147}
148
Paul Stewartd2e1c362013-03-03 19:06:07 -0800149WiFiServiceRefPtr WiFiProvider::FindSimilarService(
150 const KeyValueStore &args, Error *error) const {
151 vector<uint8_t> ssid;
152 string mode;
153 string security;
154 bool hidden_ssid;
155
156 if (!GetServiceParametersFromArgs(
157 args, &ssid, &mode, &security, &hidden_ssid, error)) {
158 return NULL;
159 }
160
161 WiFiServiceRefPtr service(FindService(ssid, mode, security));
162 if (!service) {
163 error->Populate(Error::kNotFound, "Matching service was not found");
164 }
165
166 return service;
167}
168
169WiFiServiceRefPtr WiFiProvider::CreateTemporaryService(
170 const KeyValueStore &args, Error *error) {
171 vector<uint8_t> ssid;
172 string mode;
173 string security;
174 bool hidden_ssid;
175
176 if (!GetServiceParametersFromArgs(
177 args, &ssid, &mode, &security, &hidden_ssid, error)) {
178 return NULL;
179 }
180
181 return new WiFiService(control_interface_,
182 dispatcher_,
183 metrics_,
184 manager_,
185 this,
186 ssid,
187 mode,
188 security,
189 hidden_ssid);
190}
191
Paul Stewart21f2aae2013-01-17 17:10:08 -0800192WiFiServiceRefPtr WiFiProvider::GetService(
193 const KeyValueStore &args, Error *error) {
Paul Stewartd2e1c362013-03-03 19:06:07 -0800194 vector<uint8_t> ssid_bytes;
195 string mode;
196 string security_method;
197 bool hidden_ssid;
Paul Stewart3c504012013-01-17 17:49:58 -0800198
Paul Stewartd2e1c362013-03-03 19:06:07 -0800199 if (!GetServiceParametersFromArgs(
200 args, &ssid_bytes, &mode, &security_method, &hidden_ssid, error)) {
Paul Stewart3c504012013-01-17 17:49:58 -0800201 return NULL;
202 }
203
Paul Stewartd2e1c362013-03-03 19:06:07 -0800204 WiFiServiceRefPtr service(FindService(ssid_bytes, mode, security_method));
Paul Stewart3c504012013-01-17 17:49:58 -0800205 if (!service) {
206 service = AddService(ssid_bytes,
Paul Stewartd2e1c362013-03-03 19:06:07 -0800207 mode,
Paul Stewart3c504012013-01-17 17:49:58 -0800208 security_method,
209 hidden_ssid);
210 }
211
212 return service;
Paul Stewart21f2aae2013-01-17 17:10:08 -0800213}
214
215WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
Paul Stewart3c504012013-01-17 17:49:58 -0800216 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700217 EndpointServiceMap::iterator service_it =
218 service_by_endpoint_.find(endpoint);
219 if (service_it == service_by_endpoint_.end())
220 return NULL;
221 return service_it->second;
Paul Stewart3c504012013-01-17 17:49:58 -0800222}
223
224void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800225 if (!running_) {
226 return;
227 }
228
Paul Stewart0427cc12013-03-25 13:50:39 -0700229 WiFiServiceRefPtr service = FindService(endpoint->ssid(),
230 endpoint->network_mode(),
231 endpoint->security_mode());
Paul Stewart3c504012013-01-17 17:49:58 -0800232 if (!service) {
233 const bool hidden_ssid = false;
Paul Stewart08a54eb2013-03-11 12:07:36 -0700234 service = AddService(
235 endpoint->ssid(),
236 endpoint->network_mode(),
237 WiFiService::GetSecurityClass(endpoint->security_mode()),
238 hidden_ssid);
Paul Stewart3c504012013-01-17 17:49:58 -0800239 }
240
241 service->AddEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700242 service_by_endpoint_[endpoint] = service;
Paul Stewart3c504012013-01-17 17:49:58 -0800243
244 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
245 << " to service " << service->unique_name() << ".";
246
247 manager_->UpdateService(service);
248}
249
250WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
251 const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart6c351ff2013-02-25 15:13:03 -0800252 if (!running_) {
253 return NULL;
254 }
255
Paul Stewart3c504012013-01-17 17:49:58 -0800256 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
257
258 CHECK(service) << "Can't find Service for Endpoint "
259 << "(with BSSID " << endpoint->bssid_string() << ").";
260 SLOG(WiFi, 1) << "Removing endpoint " << endpoint->bssid_string()
261 << " from Service " << service->unique_name();
262 service->RemoveEndpoint(endpoint);
Paul Stewart0427cc12013-03-25 13:50:39 -0700263 service_by_endpoint_.erase(endpoint);
Paul Stewart3c504012013-01-17 17:49:58 -0800264
265 if (service->HasEndpoints() || service->IsRemembered()) {
266 // Keep services around if they are in a profile or have remaining
267 // endpoints.
268 manager_->UpdateService(service);
269 return NULL;
270 }
271
272 ForgetService(service);
273 manager_->DeregisterService(service);
274
275 return service;
276}
277
Paul Stewart0427cc12013-03-25 13:50:39 -0700278void WiFiProvider::OnEndpointUpdated(const WiFiEndpointConstRefPtr &endpoint) {
279 WiFiService *service = FindServiceForEndpoint(endpoint);
280 CHECK(service);
281
282 // If the service still matches the endpoint in its new configuration,
283 // we need only to update the service.
284 if (service->ssid() == endpoint->ssid() &&
285 service->mode() == endpoint->network_mode() &&
286 service->IsSecurityMatch(endpoint->security_mode())) {
287 service->NotifyEndpointUpdated(endpoint);
288 return;
289 }
290
291 // The endpoint no longer matches the associated service. Remove the
292 // endpoint, so current references to the endpoint are reset, then add
293 // it again so it can be associated with a new service.
294 OnEndpointRemoved(endpoint);
295 OnEndpointAdded(endpoint);
296}
297
Paul Stewart3c504012013-01-17 17:49:58 -0800298bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr &service) {
299 // If the service still has endpoints, it should remain in the service list.
300 if (service->HasEndpoints()) {
301 return false;
302 }
303
304 // This is the one place where we forget the service but do not also
305 // deregister the service with the manager. However, by returning
306 // true below, the manager will do so itself.
307 ForgetService(service);
308 return true;
309}
310
Wade Guthrie60a37062013-04-02 11:39:09 -0700311void WiFiProvider::LoadAndFixupServiceEntries(
Paul Stewart3c504012013-01-17 17:49:58 -0800312 StoreInterface *storage, bool is_default_profile) {
313 if (WiFiService::FixupServiceEntries(storage)) {
314 storage->Flush();
315 Metrics::ServiceFixupProfileType profile_type =
316 is_default_profile ?
317 Metrics::kMetricServiceFixupDefaultProfile :
318 Metrics::kMetricServiceFixupUserProfile;
319 metrics_->SendEnumToUMA(
320 metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
321 Technology::kWifi),
322 profile_type,
323 Metrics::kMetricServiceFixupMax);
324 }
Wade Guthrie60a37062013-04-02 11:39:09 -0700325 // TODO(wdg): Determine how this should be structured for, currently
326 // non-existant, autotests. |kStorageFrequencies| should only exist in the
327 // default profile except for autotests where a test_profile is pushed. This
328 // may need to be modified for that case.
329 if (is_default_profile) {
330 vector<string> frequencies;
331 if (storage->GetStringList(kStorageId,
332 kStorageFrequencies,
333 &frequencies)) {
334 StringListToFrequencyMap(frequencies, &connect_count_by_frequency_);
335 total_frequency_connections_ = 0L;
336 WiFiProvider::ConnectFrequencyMap::const_iterator i;
337 for (i = connect_count_by_frequency_.begin();
338 i != connect_count_by_frequency_.end();
339 ++i) {
340 total_frequency_connections_ += i->second;
341 }
342 }
343 }
344}
345
346bool WiFiProvider::Save(StoreInterface *storage) const {
347 vector<string> frequencies;
348 FrequencyMapToStringList(connect_count_by_frequency_, &frequencies);
349 storage->SetStringList(kStorageId, kStorageFrequencies, frequencies);
350 return true;
Paul Stewart3c504012013-01-17 17:49:58 -0800351}
352
353WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t> &ssid,
354 const string &mode,
355 const string &security,
356 bool is_hidden) {
357 WiFiServiceRefPtr service = new WiFiService(control_interface_,
358 dispatcher_,
359 metrics_,
360 manager_,
361 this,
362 ssid,
363 mode,
364 security,
365 is_hidden);
366
367 services_.push_back(service);
368 manager_->RegisterService(service);
369 return service;
370}
371
372WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t> &ssid,
373 const string &mode,
374 const string &security) const {
375 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
376 it != services_.end();
377 ++it) {
378 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
379 (*it)->IsSecurityMatch(security)) {
380 return *it;
381 }
382 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800383 return NULL;
384}
385
Paul Stewart3c504012013-01-17 17:49:58 -0800386ByteArrays WiFiProvider::GetHiddenSSIDList() {
387 // Create a unique set of hidden SSIDs.
388 set<ByteArray> hidden_ssids_set;
389 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
390 it != services_.end();
391 ++it) {
392 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
393 hidden_ssids_set.insert((*it)->ssid());
394 }
395 }
396 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
397 return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
398}
399
400void WiFiProvider::ForgetService(const WiFiServiceRefPtr &service) {
401 vector<WiFiServiceRefPtr>::iterator it;
402 it = std::find(services_.begin(), services_.end(), service);
403 if (it == services_.end()) {
404 return;
405 }
406 (*it)->ResetWiFi();
407 services_.erase(it);
408}
409
Paul Stewartd2e1c362013-03-03 19:06:07 -0800410// static
411bool WiFiProvider::GetServiceParametersFromArgs(const KeyValueStore &args,
412 vector<uint8_t> *ssid_bytes,
413 string *mode,
414 string *security_method,
415 bool *hidden_ssid,
416 Error *error) {
417 CHECK_EQ(args.LookupString(flimflam::kTypeProperty, ""), flimflam::kTypeWifi);
418
419 string mode_test =
420 args.LookupString(flimflam::kModeProperty, flimflam::kModeManaged);
421 if (!WiFiService::IsValidMode(mode_test)) {
422 Error::PopulateAndLog(error, Error::kNotSupported,
423 kManagerErrorUnsupportedServiceMode);
424 return false;
425 }
426
427 if (!args.ContainsString(flimflam::kSSIDProperty)) {
428 Error::PopulateAndLog(error, Error::kInvalidArguments,
429 kManagerErrorSSIDRequired);
430 return false;
431 }
432
433 string ssid = args.GetString(flimflam::kSSIDProperty);
434
435 if (ssid.length() < 1) {
436 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
437 kManagerErrorSSIDTooShort);
438 return false;
439 }
440
441 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
442 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
443 kManagerErrorSSIDTooLong);
444 return false;
445 }
446
447 string security_method_test = args.LookupString(flimflam::kSecurityProperty,
448 flimflam::kSecurityNone);
449
450 if (!WiFiService::IsValidSecurityMethod(security_method_test)) {
451 Error::PopulateAndLog(error, Error::kNotSupported,
452 kManagerErrorUnsupportedSecurityMode);
453 return false;
454 }
455
456 *ssid_bytes = vector<uint8_t>(ssid.begin(), ssid.end());
457 *mode = mode_test;
458 *security_method = security_method_test;
459
460 // If the caller hasn't specified otherwise, we assume it is a hidden service.
461 *hidden_ssid = args.LookupBool(flimflam::kWifiHiddenSsid, true);
462
463 return true;
464}
465
Wade Guthrie60a37062013-04-02 11:39:09 -0700466// static
467void WiFiProvider::StringListToFrequencyMap(const vector<string> &strings,
468 ConnectFrequencyMap *numbers) {
469 if (!numbers) {
470 LOG(ERROR) << "Null |numbers| parameter";
471 return;
472 }
473
474 vector<string>::const_iterator i;
475 for (i = strings.begin(); i != strings.end(); ++i) {
476 size_t delimiter = i->find(kFrequencyDelimiter);
477 if (delimiter == i->npos) {
478 LOG(WARNING) << "Found no '" << kFrequencyDelimiter << "' in '"
479 << *i << "'";
480 continue;
481 }
482 uint16 freq = atoi(i->c_str());
483 uint64 connections = atoll(i->c_str() + delimiter + 1);
484 (*numbers)[freq] = connections;
485 }
486}
487
488// static
489void WiFiProvider::FrequencyMapToStringList(const ConnectFrequencyMap &numbers,
490 vector<string> *strings) {
491 if (!strings) {
492 LOG(ERROR) << "Null |strings| parameter";
493 return;
494 }
495
496 ConnectFrequencyMap::const_iterator i;
497 for (i = numbers.begin(); i != numbers.end(); ++i) {
498 // Use base::Int64ToString() instead of using something like "%llu"
499 // (not correct for native 64 bit architectures) or PRId64 (does not
500 // work correctly using cros_workon_make due to include intricacies).
501 string result = StringPrintf("%u%s%s", i->first, kFrequencyDelimiter,
502 base::Int64ToString(i->second).c_str());
503 strings->push_back(result);
504 }
505}
506
507void WiFiProvider::IncrementConnectCount(uint16 frequency_mhz) {
508 // Freeze the accumulation of frequency counts when the total maxes-out.
509 // This ensures that no count wraps and that the relative values are
510 // consistent.
511 // TODO(wdg): In future CL, |total_frequency_connections_| is used to
512 // calculate percentiles for progressive scan. This check needs to be in
513 // place so _that_ value doesn't wrap, either.
514 // TODO(wdg): Replace this, simple, 'forever' collection of connection
515 // statistics with a more clever 'aging' algorithm. crbug.com/227233
516 if (total_frequency_connections_ + 1 == std::numeric_limits<int64_t>::max()) {
517 LOG(ERROR) << "Shill has logged " << total_frequency_connections_
518 << " connections -- must be an error. Resetting connection "
519 << "count.";
520 connect_count_by_frequency_.clear();
521 }
522
523 int64 previous_value = 0;
524 if (ContainsKey(connect_count_by_frequency_, frequency_mhz)) {
525 previous_value = connect_count_by_frequency_[frequency_mhz];
526 }
527
528 connect_count_by_frequency_[frequency_mhz] = ++previous_value;
529 ++total_frequency_connections_;
530 manager_->UpdateWiFiProvider();
531
532 metrics_->SendToUMA(
533 Metrics::kMetricFrequenciesConnectedEver,
534 connect_count_by_frequency_.size(),
535 Metrics::kMetricFrequenciesConnectedMin,
536 Metrics::kMetricFrequenciesConnectedMax,
537 Metrics::kMetricFrequenciesConnectedNumBuckets);
538}
539
Paul Stewart21f2aae2013-01-17 17:10:08 -0800540} // namespace shill