blob: d37af1e31ce80b037a5f9a309709c08da39ca03f [file] [log] [blame]
Thieu Le3426c8f2012-01-11 17:35:11 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartb50f0b92011-05-16 16:31:42 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Chris Masoneb2e326b2011-07-12 13:28:51 -07005#include "shill/ethernet.h"
6
Paul Stewartf1ce5d22011-05-19 13:10:20 -07007#include <netinet/ether.h>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -07008#include <linux/if.h> // Needs definitions from netinet/ether.h
Paul Stewart9413bcc2013-04-04 16:12:43 -07009#include <stdio.h>
10#include <string.h>
11#include <time.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070012
Paul Stewart9413bcc2013-04-04 16:12:43 -070013#include <map>
Paul Stewartb50f0b92011-05-16 16:31:42 -070014#include <string>
Paul Stewart9413bcc2013-04-04 16:12:43 -070015#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016
Paul Stewartced3ad72013-04-03 13:39:25 -070017#include <base/bind.h>
18
Eric Shienbrood9a245532012-03-07 14:20:39 -050019#include "shill/adaptor_interfaces.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070020#include "shill/control_interface.h"
21#include "shill/device.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070022#include "shill/device_info.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070023#include "shill/eap_credentials.h"
Paul Stewartced3ad72013-04-03 13:39:25 -070024#include "shill/eap_listener.h"
Paul Stewart35eff132013-04-12 12:08:40 -070025#include "shill/ethernet_eap_provider.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070026#include "shill/ethernet_service.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070027#include "shill/event_dispatcher.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070028#include "shill/logging.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070029#include "shill/manager.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070030#include "shill/nss.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070031#include "shill/profile.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070032#include "shill/proxy_factory.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070033#include "shill/rtnl_handler.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070034#include "shill/supplicant_interface_proxy_interface.h"
35#include "shill/supplicant_process_proxy_interface.h"
36#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070037
Paul Stewart9413bcc2013-04-04 16:12:43 -070038using std::map;
Chris Masonea82b7112011-05-25 15:16:29 -070039using std::string;
Paul Stewart9413bcc2013-04-04 16:12:43 -070040using std::vector;
Chris Masonea82b7112011-05-25 15:16:29 -070041
Paul Stewartb50f0b92011-05-16 16:31:42 -070042namespace shill {
Darin Petkovafa6fc42011-06-21 16:21:08 -070043
Paul Stewartb50f0b92011-05-16 16:31:42 -070044Ethernet::Ethernet(ControlInterface *control_interface,
45 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080046 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070047 Manager *manager,
Darin Petkovafa6fc42011-06-21 16:21:08 -070048 const string &link_name,
mukesh agrawal93a29ed2012-04-17 16:13:01 -070049 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070050 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070051 : Device(control_interface,
52 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080053 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070054 manager,
55 link_name,
Chris Masone626719f2011-08-18 16:58:48 -070056 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080057 interface_index,
58 Technology::kEthernet),
Paul Stewartced3ad72013-04-03 13:39:25 -070059 link_up_(false),
Paul Stewart9413bcc2013-04-04 16:12:43 -070060 is_eap_authenticated_(false),
Paul Stewartced3ad72013-04-03 13:39:25 -070061 is_eap_detected_(false),
Paul Stewart9413bcc2013-04-04 16:12:43 -070062 eap_listener_(new EapListener(dispatcher, interface_index)),
63 nss_(NSS::GetInstance()),
64 certificate_file_(manager->glib()),
65 proxy_factory_(ProxyFactory::GetInstance()),
66 weak_ptr_factory_(this) {
Paul Stewartced3ad72013-04-03 13:39:25 -070067 PropertyStore *store = this->mutable_store();
Paul Stewart9413bcc2013-04-04 16:12:43 -070068 store->RegisterConstBool(kEapAuthenticationCompletedProperty,
69 &is_eap_authenticated_);
Paul Stewartced3ad72013-04-03 13:39:25 -070070 store->RegisterConstBool(kEapAuthenticatorDetectedProperty,
71 &is_eap_detected_);
Paul Stewartced3ad72013-04-03 13:39:25 -070072 eap_listener_->set_request_received_callback(
Paul Stewart9413bcc2013-04-04 16:12:43 -070073 base::Bind(&Ethernet::OnEapDetected, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -070074 SLOG(Ethernet, 2) << "Ethernet device " << link_name << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -070075}
76
77Ethernet::~Ethernet() {
Eric Shienbrood9a245532012-03-07 14:20:39 -050078 Stop(NULL, EnabledStateChangedCallback());
Paul Stewartf1ce5d22011-05-19 13:10:20 -070079}
80
Eric Shienbrood9a245532012-03-07 14:20:39 -050081void Ethernet::Start(Error *error,
82 const EnabledStateChangedCallback &callback) {
Paul Stewart2713d6c2011-08-25 15:38:15 -070083 service_ = new EthernetService(control_interface(),
84 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -080085 metrics(),
Paul Stewart2713d6c2011-08-25 15:38:15 -070086 manager(),
87 this);
Paul Stewartced3ad72013-04-03 13:39:25 -070088 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
Eric Shienbrood9a245532012-03-07 14:20:39 -050089 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
90 if (error)
91 error->Reset(); // indicate immediate completion
Paul Stewartf1ce5d22011-05-19 13:10:20 -070092}
93
Eric Shienbrood9a245532012-03-07 14:20:39 -050094void Ethernet::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Paul Stewartf2792e82011-12-12 10:23:11 -080095 if (service_) {
96 manager()->DeregisterService(service_);
97 service_ = NULL;
98 }
Paul Stewart9413bcc2013-04-04 16:12:43 -070099 StopSupplicant();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500100 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
101 if (error)
102 error->Reset(); // indicate immediate completion
Paul Stewartb50f0b92011-05-16 16:31:42 -0700103}
104
Darin Petkovafa6fc42011-06-21 16:21:08 -0700105void Ethernet::LinkEvent(unsigned int flags, unsigned int change) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700106 Device::LinkEvent(flags, change);
107 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700108 link_up_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500109 if (service_) {
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700110 LOG(INFO) << "Registering " << link_name() << " with manager.";
111 // Manager will bring up L3 for us.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500112 manager()->RegisterService(service_);
Darin Petkovafa6fc42011-06-21 16:21:08 -0700113 }
Paul Stewartced3ad72013-04-03 13:39:25 -0700114 eap_listener_->Start();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700115 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
116 link_up_ = false;
Paul Stewartced3ad72013-04-03 13:39:25 -0700117 is_eap_detected_ = false;
Paul Stewart63c93932012-01-06 11:24:12 -0800118 DestroyIPConfig();
Thieu Le9c497072012-10-31 14:06:20 -0700119 if (service_)
120 manager()->DeregisterService(service_);
Paul Stewart03dba0b2011-08-22 16:32:45 -0700121 SelectService(NULL);
Paul Stewart35eff132013-04-12 12:08:40 -0700122 GetEapProvider()->ClearCredentialChangeCallback(this);
123 SetIsEapAuthenticated(false);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700124 StopSupplicant();
Paul Stewartced3ad72013-04-03 13:39:25 -0700125 eap_listener_->Stop();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700126 }
127}
128
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700129void Ethernet::ConnectTo(EthernetService *service) {
130 CHECK(service == service_.get()) << "Ethernet was asked to connect the "
131 << "wrong service?";
132 if (AcquireIPConfigWithLeaseName(service->GetStorageIdentifier())) {
133 SelectService(service);
134 SetServiceState(Service::kStateConfiguring);
135 } else {
136 LOG(ERROR) << "Unable to acquire DHCP config.";
137 SetServiceState(Service::kStateFailure);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700138 DestroyIPConfig();
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700139 }
140}
141
142void Ethernet::DisconnectFrom(EthernetService *service) {
143 CHECK(service == service_.get()) << "Ethernet was asked to disconnect the "
144 << "wrong service?";
145 DropConnection();
146}
147
Paul Stewart9413bcc2013-04-04 16:12:43 -0700148void Ethernet::TryEapAuthentication() {
Paul Stewart35eff132013-04-12 12:08:40 -0700149 try_eap_authentication_callback_.Reset(
150 Bind(&Ethernet::TryEapAuthenticationTask,
151 weak_ptr_factory_.GetWeakPtr()));
152 dispatcher()->PostTask(try_eap_authentication_callback_.callback());
Paul Stewart9413bcc2013-04-04 16:12:43 -0700153}
154
Paul Stewart9413bcc2013-04-04 16:12:43 -0700155void Ethernet::BSSAdded(const ::DBus::Path &path,
156 const map<string, ::DBus::Variant> &properties) {
157 NOTREACHED() << __func__ << " is not implemented for Ethernet";
158}
159
160void Ethernet::BSSRemoved(const ::DBus::Path &path) {
161 NOTREACHED() << __func__ << " is not implemented for Ethernet";
162}
163
164void Ethernet::Certification(const map<string, ::DBus::Variant> &properties) {
165 string subject;
166 uint32 depth;
167 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
168 dispatcher()->PostTask(Bind(&Ethernet::CertificationTask,
169 weak_ptr_factory_.GetWeakPtr(),
170 subject, depth));
171 }
172}
173
174void Ethernet::EAPEvent(const string &status, const string &parameter) {
175 dispatcher()->PostTask(Bind(&Ethernet::EAPEventTask,
176 weak_ptr_factory_.GetWeakPtr(),
177 status,
178 parameter));
179}
180
181void Ethernet::PropertiesChanged(
182 const map<string, ::DBus::Variant> &properties) {
183 const map<string, ::DBus::Variant>::const_iterator properties_it =
184 properties.find(WPASupplicant::kInterfacePropertyState);
185 if (properties_it == properties.end()) {
186 return;
187 }
188 dispatcher()->PostTask(Bind(&Ethernet::SupplicantStateChangedTask,
189 weak_ptr_factory_.GetWeakPtr(),
190 properties_it->second.reader().get_string()));
191}
192
193void Ethernet::ScanDone() {
194 NOTREACHED() << __func__ << " is not implented for Ethernet";
195}
196
Paul Stewart35eff132013-04-12 12:08:40 -0700197EthernetEapProvider *Ethernet::GetEapProvider() {
198 EthernetEapProvider *eap_provider = manager()->ethernet_eap_provider();
199 CHECK(eap_provider);
200 return eap_provider;
201}
202
203ServiceConstRefPtr Ethernet::GetEapService() {
204 ServiceConstRefPtr eap_service = GetEapProvider()->service();
205 CHECK(eap_service);
206 return eap_service;
207}
208
Paul Stewartced3ad72013-04-03 13:39:25 -0700209void Ethernet::OnEapDetected() {
210 is_eap_detected_ = true;
211 eap_listener_->Stop();
Paul Stewart35eff132013-04-12 12:08:40 -0700212 GetEapProvider()->SetCredentialChangeCallback(
213 this,
214 base::Bind(&Ethernet::TryEapAuthentication,
215 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart9413bcc2013-04-04 16:12:43 -0700216 TryEapAuthentication();
217}
218
219bool Ethernet::StartSupplicant() {
220 if (supplicant_process_proxy_.get()) {
221 return true;
222 }
223
224 supplicant_process_proxy_.reset(
225 proxy_factory_->CreateSupplicantProcessProxy(
226 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
227 ::DBus::Path interface_path;
228 try {
229 map<string, DBus::Variant> create_interface_args;
230 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
231 append_string(link_name().c_str());
232 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
233 append_string(WPASupplicant::kDriverWired);
234 create_interface_args[
235 WPASupplicant::kInterfacePropertyConfigFile].writer().
236 append_string(WPASupplicant::kSupplicantConfPath);
237 interface_path =
238 supplicant_process_proxy_->CreateInterface(create_interface_args);
239 } catch (const DBus::Error &e) { // NOLINT
240 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
241 interface_path = supplicant_process_proxy_->GetInterface(link_name());
242 } else {
243 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
244 StopSupplicant();
245 return false;
246 }
247 }
248 supplicant_interface_proxy_.reset(
249 proxy_factory_->CreateSupplicantInterfaceProxy(
250 this, interface_path, WPASupplicant::kDBusAddr));
251 supplicant_interface_path_ = interface_path;
252 return true;
253}
254
255bool Ethernet::StartEapAuthentication() {
256 map<string, DBus::Variant> params;
257 vector<char> nss_identifier(link_name().begin(), link_name().end());
Paul Stewart35eff132013-04-12 12:08:40 -0700258 GetEapService()->eap()->PopulateSupplicantProperties(
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700259 &certificate_file_, nss_, nss_identifier, &params);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700260 params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
261 append_string(WPASupplicant::kKeyManagementIeee8021X);
262 params[WPASupplicant::kNetworkPropertyEapolFlags].writer().
263 append_uint32(0);
264 params[WPASupplicant::kNetworkPropertyScanSSID].writer().
265 append_uint32(0);
266 service_->ClearEAPCertification();
267 eap_state_handler_.Reset();
268
269 try {
270 if (!supplicant_network_path_.empty()) {
271 supplicant_interface_proxy_->RemoveNetwork(supplicant_network_path_);
272 }
273 supplicant_network_path_ = supplicant_interface_proxy_->AddNetwork(params);
274 CHECK(!supplicant_network_path_.empty());
275 } catch (const DBus::Error &e) { // NOLINT
276 LOG(ERROR) << "exception while adding network: " << e.what();
277 return false;
278 }
279
280 supplicant_interface_proxy_->SelectNetwork(supplicant_network_path_);
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700281 supplicant_interface_proxy_->EAPLogon();
Paul Stewart9413bcc2013-04-04 16:12:43 -0700282 return true;
283}
284
285void Ethernet::StopSupplicant() {
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700286 if (supplicant_interface_proxy_.get()) {
287 supplicant_interface_proxy_->EAPLogoff();
288 }
Paul Stewart9413bcc2013-04-04 16:12:43 -0700289 supplicant_interface_proxy_.reset();
290 if (!supplicant_interface_path_.empty() && supplicant_process_proxy_.get()) {
291 try {
292 supplicant_process_proxy_->RemoveInterface(
293 ::DBus::Path(supplicant_interface_path_));
294 } catch (const DBus::Error &e) { // NOLINT
295 LOG(ERROR) << __func__ << ": Failed to remove interface from supplicant.";
296 }
297 }
298 supplicant_network_path_ = "";
299 supplicant_interface_path_ = "";
300 supplicant_process_proxy_.reset();
301 SetIsEapAuthenticated(false);
302}
303
304void Ethernet::SetIsEapAuthenticated(bool is_eap_authenticated) {
305 if (is_eap_authenticated == is_eap_authenticated_) {
306 return;
307 }
308
309 // If our EAP authentication state changes, we have now joined a different
310 // network. Restart the DHCP process and any other connection state.
311 DisconnectFrom(service_);
312 if (service_ && link_up_) {
313 ConnectTo(service_);
314 }
315 is_eap_authenticated_ = is_eap_authenticated;
316 adaptor()->EmitBoolChanged(kEapAuthenticationCompletedProperty,
317 is_eap_authenticated_);
318}
319
320void Ethernet::CertificationTask(const string &subject, uint32 depth) {
321 if (!service_) {
322 LOG(ERROR) << "Ethernet " << link_name() << " " << __func__
323 << " with no service.";
324 return;
325 }
326
327 service_->AddEAPCertification(subject, depth);
328}
329
330void Ethernet::EAPEventTask(const string &status, const string &parameter) {
331 LOG(INFO) << "In " << __func__ << " with status " << status
332 << ", parameter " << parameter;
333 Service::ConnectFailure failure = Service::kFailureUnknown;
334 if (eap_state_handler_.ParseStatus(status, parameter, &failure)) {
335 LOG(INFO) << "EAP authentication succeeded!";
336 SetIsEapAuthenticated(true);
337 } else if (failure != Service::Service::kFailureUnknown) {
338 LOG(INFO) << "EAP authentication failed!";
339 SetIsEapAuthenticated(false);
340 }
341}
342
343void Ethernet::SupplicantStateChangedTask(const string &state) {
344 LOG(INFO) << "Supplicant state changed to " << state;
Paul Stewartced3ad72013-04-03 13:39:25 -0700345}
346
Paul Stewart35eff132013-04-12 12:08:40 -0700347void Ethernet::TryEapAuthenticationTask() {
348 if (!service_) {
349 LOG(INFO) << "Service is missing; not doing EAP authentication.";
350 return;
351 }
352
353 if (!GetEapService()->Is8021xConnectable()) {
354 if (is_eap_authenticated_) {
355 LOG(INFO) << "EAP Service lost 802.1X credentials; "
356 << "terminating EAP authentication.";
357 } else {
358 LOG(INFO) << "EAP Service lacks 802.1X credentials; "
359 << "not doing EAP authentication.";
360 }
361 StopSupplicant();
362 return;
363 }
364
365 if (!is_eap_detected_) {
366 LOG(WARNING) << "EAP authenticator not detected; "
367 << "not doing EAP authentication.";
368 return;
369 }
370 if (!StartSupplicant()) {
371 LOG(ERROR) << "Failed to start supplicant.";
372 return;
373 }
374 StartEapAuthentication();
375}
376
Paul Stewartb50f0b92011-05-16 16:31:42 -0700377} // namespace shill