blob: 6e7602b287afe6da91780bd4305a4308513768a1 [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
Paul Stewart3c504012013-01-17 17:49:58 -08007#include <set>
8#include <string>
9#include <vector>
10
11#include <base/bind.h>
12#include <base/string_number_conversions.h>
13
Paul Stewart21f2aae2013-01-17 17:10:08 -080014#include "shill/error.h"
15#include "shill/event_dispatcher.h"
Paul Stewart3c504012013-01-17 17:49:58 -080016#include "shill/ieee80211.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080017#include "shill/key_value_store.h"
Paul Stewart3c504012013-01-17 17:49:58 -080018#include "shill/logging.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080019#include "shill/manager.h"
20#include "shill/metrics.h"
Paul Stewart3c504012013-01-17 17:49:58 -080021#include "shill/profile.h"
22#include "shill/store_interface.h"
23#include "shill/technology.h"
Paul Stewart21f2aae2013-01-17 17:10:08 -080024#include "shill/wifi_endpoint.h"
25#include "shill/wifi_service.h"
26
Paul Stewart3c504012013-01-17 17:49:58 -080027using base::Bind;
28using std::set;
29using std::string;
30using std::vector;
31
Paul Stewart21f2aae2013-01-17 17:10:08 -080032namespace shill {
33
Paul Stewart3c504012013-01-17 17:49:58 -080034// Note that WiFiProvider generates some manager-level errors, because it
35// implements the WiFi portion of the Manager.GetService flimflam API. The
36// API is implemented here, rather than in manager, to keep WiFi-specific
37// logic in the right place.
38const char WiFiProvider::kManagerErrorSSIDRequired[] = "must specify SSID";
39const char WiFiProvider::kManagerErrorSSIDTooLong[] = "SSID is too long";
40const char WiFiProvider::kManagerErrorSSIDTooShort[] = "SSID is too short";
41const char WiFiProvider::kManagerErrorUnsupportedSecurityMode[] =
42 "security mode is unsupported";
43const char WiFiProvider::kManagerErrorUnsupportedServiceMode[] =
44 "service mode is unsupported";
45
Paul Stewart21f2aae2013-01-17 17:10:08 -080046WiFiProvider::WiFiProvider(ControlInterface *control_interface,
47 EventDispatcher *dispatcher,
48 Metrics *metrics,
49 Manager *manager)
50 : control_interface_(control_interface),
51 dispatcher_(dispatcher),
52 metrics_(metrics),
53 manager_(manager) {}
54
55WiFiProvider::~WiFiProvider() {}
56
Paul Stewart3c504012013-01-17 17:49:58 -080057void WiFiProvider::Start() {}
Paul Stewart21f2aae2013-01-17 17:10:08 -080058
59void WiFiProvider::Stop() {
Paul Stewart3c504012013-01-17 17:49:58 -080060 SLOG(WiFi, 2) << __func__;
61 while (!services_.empty()) {
62 WiFiServiceRefPtr service = services_.back();
63 ForgetService(service);
64 SLOG(WiFi, 3) << "WiFiProvider deregistering service "
65 << service->unique_name();
66 manager_->DeregisterService(service);
67 }
Paul Stewart21f2aae2013-01-17 17:10:08 -080068}
69
Paul Stewart3c504012013-01-17 17:49:58 -080070void WiFiProvider::CreateServicesFromProfile(const ProfileRefPtr &profile) {
71 const StoreInterface *storage = profile->GetConstStorage();
72 KeyValueStore args;
73 args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
74 set<string> groups = storage->GetGroupsWithProperties(args);
75 bool created_hidden_service = false;
76 for (set<string>::const_iterator it = groups.begin(); it != groups.end();
77 ++it) {
78 string ssid_hex;
79 vector<uint8_t> ssid_bytes;
80 if (!storage->GetString(*it, WiFiService::kStorageSSID, &ssid_hex) ||
81 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
82 SLOG(WiFi, 2) << "Storage group " << *it << " is missing valid \""
83 << WiFiService::kStorageSSID << "\" property";
84 continue;
85 }
86 string network_mode;
87 if (!storage->GetString(*it, WiFiService::kStorageMode, &network_mode) ||
88 network_mode.empty()) {
89 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
90 << WiFiService::kStorageMode << "\" property";
91 continue;
92 }
93 string security;
94 if (!storage->GetString(*it, WiFiService::kStorageSecurity, &security) ||
95 !WiFiService::IsValidSecurityMethod(security)) {
96 SLOG(WiFi, 2) << "Storage group " << *it << " has missing or invalid \""
97 << WiFiService::kStorageSecurity << "\" property";
98 continue;
99 }
100 bool is_hidden = false;
101 if (!storage->GetBool(*it, WiFiService::kStorageHiddenSSID, &is_hidden)) {
102 SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
103 << WiFiService::kStorageHiddenSSID << "\" property";
104 continue;
105 }
106
107 if (FindService(ssid_bytes, network_mode, security)) {
108 // If service already exists, we have nothing to do, since the
109 // service has already loaded its configuration from storage.
110 // This is guaranteed to happen in the single case where
111 // CreateServicesFromProfile() is called on a WiFiProvider from
112 // Manager::PushProfile():
113 continue;
114 }
115
116 AddService(ssid_bytes, network_mode, security, is_hidden);
117
118 // By registering the service in AddService, the rest of the configuration
119 // will be loaded from the profile into the service via ConfigureService().
120
121 if (is_hidden) {
122 created_hidden_service = true;
123 }
124 }
125
126 // If WiFi is unconnected and we created a hidden service as a result
127 // of opening the profile, we should initiate a WiFi scan, which will
128 // allow us to find any hidden services that we may have created.
129 if (created_hidden_service &&
130 !manager_->IsTechnologyConnected(Technology::kWifi)) {
131 Error unused_error;
132 manager_->RequestScan(flimflam::kTypeWifi, &unused_error);
133 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800134}
135
136WiFiServiceRefPtr WiFiProvider::GetService(
137 const KeyValueStore &args, Error *error) {
Paul Stewart3c504012013-01-17 17:49:58 -0800138 CHECK_EQ(args.LookupString(flimflam::kTypeProperty, ""), flimflam::kTypeWifi);
139
140 if (args.LookupString(flimflam::kModeProperty, "") !=
141 flimflam::kModeManaged) {
142 Error::PopulateAndLog(error, Error::kNotSupported,
143 kManagerErrorUnsupportedServiceMode);
144 return NULL;
145 }
146
147 if (!args.ContainsString(flimflam::kSSIDProperty)) {
148 Error::PopulateAndLog(error, Error::kInvalidArguments,
149 kManagerErrorSSIDRequired);
150 return NULL;
151 }
152
153 string ssid = args.GetString(flimflam::kSSIDProperty);
154 if (ssid.length() < 1) {
155 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
156 kManagerErrorSSIDTooShort);
157 return NULL;
158 }
159
160 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
161 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
162 kManagerErrorSSIDTooLong);
163 return NULL;
164 }
165
166 string security_method = args.LookupString(flimflam::kSecurityProperty,
167 flimflam::kSecurityNone);
168
169 if (!WiFiService::IsValidSecurityMethod(security_method)) {
170 Error::PopulateAndLog(error, Error::kNotSupported,
171 kManagerErrorUnsupportedSecurityMode);
172 return NULL;
173 }
174
175 // If the service is not found, and the caller hasn't specified otherwise,
176 // we assume it is a hidden service.
177 bool hidden_ssid = args.LookupBool(flimflam::kWifiHiddenSsid, true);
178
179 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
180 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
181 security_method));
182 if (!service) {
183 service = AddService(ssid_bytes,
184 flimflam::kModeManaged,
185 security_method,
186 hidden_ssid);
187 }
188
189 return service;
Paul Stewart21f2aae2013-01-17 17:10:08 -0800190}
191
192WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
Paul Stewart3c504012013-01-17 17:49:58 -0800193 const WiFiEndpointConstRefPtr &endpoint) {
194 return FindService(endpoint->ssid(),
195 endpoint->network_mode(),
196 endpoint->security_mode());
197}
198
199void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint) {
200 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
201 if (!service) {
202 const bool hidden_ssid = false;
203 service = AddService(endpoint->ssid(),
204 endpoint->network_mode(),
205 endpoint->security_mode(),
206 hidden_ssid);
207 }
208
209 service->AddEndpoint(endpoint);
210
211 SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
212 << " to service " << service->unique_name() << ".";
213
214 manager_->UpdateService(service);
215}
216
217WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
218 const WiFiEndpointConstRefPtr &endpoint) {
219 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
220
221 CHECK(service) << "Can't find Service for Endpoint "
222 << "(with BSSID " << endpoint->bssid_string() << ").";
223 SLOG(WiFi, 1) << "Removing endpoint " << endpoint->bssid_string()
224 << " from Service " << service->unique_name();
225 service->RemoveEndpoint(endpoint);
226
227 if (service->HasEndpoints() || service->IsRemembered()) {
228 // Keep services around if they are in a profile or have remaining
229 // endpoints.
230 manager_->UpdateService(service);
231 return NULL;
232 }
233
234 ForgetService(service);
235 manager_->DeregisterService(service);
236
237 return service;
238}
239
240bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr &service) {
241 // If the service still has endpoints, it should remain in the service list.
242 if (service->HasEndpoints()) {
243 return false;
244 }
245
246 // This is the one place where we forget the service but do not also
247 // deregister the service with the manager. However, by returning
248 // true below, the manager will do so itself.
249 ForgetService(service);
250 return true;
251}
252
253void WiFiProvider::FixupServiceEntries(
254 StoreInterface *storage, bool is_default_profile) {
255 if (WiFiService::FixupServiceEntries(storage)) {
256 storage->Flush();
257 Metrics::ServiceFixupProfileType profile_type =
258 is_default_profile ?
259 Metrics::kMetricServiceFixupDefaultProfile :
260 Metrics::kMetricServiceFixupUserProfile;
261 metrics_->SendEnumToUMA(
262 metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
263 Technology::kWifi),
264 profile_type,
265 Metrics::kMetricServiceFixupMax);
266 }
267}
268
269WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t> &ssid,
270 const string &mode,
271 const string &security,
272 bool is_hidden) {
273 WiFiServiceRefPtr service = new WiFiService(control_interface_,
274 dispatcher_,
275 metrics_,
276 manager_,
277 this,
278 ssid,
279 mode,
280 security,
281 is_hidden);
282
283 services_.push_back(service);
284 manager_->RegisterService(service);
285 return service;
286}
287
288WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t> &ssid,
289 const string &mode,
290 const string &security) const {
291 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
292 it != services_.end();
293 ++it) {
294 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
295 (*it)->IsSecurityMatch(security)) {
296 return *it;
297 }
298 }
Paul Stewart21f2aae2013-01-17 17:10:08 -0800299 return NULL;
300}
301
Paul Stewart3c504012013-01-17 17:49:58 -0800302ByteArrays WiFiProvider::GetHiddenSSIDList() {
303 // Create a unique set of hidden SSIDs.
304 set<ByteArray> hidden_ssids_set;
305 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
306 it != services_.end();
307 ++it) {
308 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
309 hidden_ssids_set.insert((*it)->ssid());
310 }
311 }
312 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
313 return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
314}
315
316void WiFiProvider::ForgetService(const WiFiServiceRefPtr &service) {
317 vector<WiFiServiceRefPtr>::iterator it;
318 it = std::find(services_.begin(), services_.end(), service);
319 if (it == services_.end()) {
320 return;
321 }
322 (*it)->ResetWiFi();
323 services_.erase(it);
324}
325
Paul Stewart21f2aae2013-01-17 17:10:08 -0800326} // namespace shill