blob: a7e8c4610aa3be2855099043d1c5c59dae4f7d07 [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
mukesh agrawal8bda7962014-04-01 17:09:35 -07007#include <linux/ethtool.h>
Paul Stewartf1ce5d22011-05-19 13:10:20 -07008#include <netinet/ether.h>
mukesh agrawal8bda7962014-04-01 17:09:35 -07009#include <netinet/in.h>
Alex Vakulenkoa41ab512014-07-23 14:24:23 -070010#include <linux/if.h> // NOLINT - Needs definitions from netinet/ether.h
mukesh agrawal8bda7962014-04-01 17:09:35 -070011#include <linux/sockios.h>
Paul Stewart9413bcc2013-04-04 16:12:43 -070012#include <stdio.h>
13#include <string.h>
14#include <time.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070015
Paul Stewart9413bcc2013-04-04 16:12:43 -070016#include <map>
Paul Stewartb50f0b92011-05-16 16:31:42 -070017#include <string>
Paul Stewart9413bcc2013-04-04 16:12:43 -070018#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070019
Paul Stewartced3ad72013-04-03 13:39:25 -070020#include <base/bind.h>
21
Eric Shienbrood9a245532012-03-07 14:20:39 -050022#include "shill/adaptor_interfaces.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070023#include "shill/control_interface.h"
24#include "shill/device.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070025#include "shill/device_info.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070026#include "shill/eap_credentials.h"
Paul Stewartced3ad72013-04-03 13:39:25 -070027#include "shill/eap_listener.h"
Paul Stewart35eff132013-04-12 12:08:40 -070028#include "shill/ethernet_eap_provider.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070029#include "shill/ethernet_service.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070030#include "shill/event_dispatcher.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070031#include "shill/logging.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070032#include "shill/manager.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070033#include "shill/profile.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070034#include "shill/proxy_factory.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070035#include "shill/rtnl_handler.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070036#include "shill/supplicant_interface_proxy_interface.h"
37#include "shill/supplicant_process_proxy_interface.h"
38#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070039
Paul Stewart9413bcc2013-04-04 16:12:43 -070040using std::map;
Chris Masonea82b7112011-05-25 15:16:29 -070041using std::string;
Paul Stewart9413bcc2013-04-04 16:12:43 -070042using std::vector;
Chris Masonea82b7112011-05-25 15:16:29 -070043
Paul Stewartb50f0b92011-05-16 16:31:42 -070044namespace shill {
Darin Petkovafa6fc42011-06-21 16:21:08 -070045
Paul Stewartb50f0b92011-05-16 16:31:42 -070046Ethernet::Ethernet(ControlInterface *control_interface,
47 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080048 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070049 Manager *manager,
Darin Petkovafa6fc42011-06-21 16:21:08 -070050 const string &link_name,
mukesh agrawal93a29ed2012-04-17 16:13:01 -070051 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070052 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070053 : Device(control_interface,
54 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080055 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070056 manager,
57 link_name,
Chris Masone626719f2011-08-18 16:58:48 -070058 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080059 interface_index,
60 Technology::kEthernet),
Paul Stewartced3ad72013-04-03 13:39:25 -070061 link_up_(false),
Paul Stewart9413bcc2013-04-04 16:12:43 -070062 is_eap_authenticated_(false),
Paul Stewartced3ad72013-04-03 13:39:25 -070063 is_eap_detected_(false),
Paul Stewart9413bcc2013-04-04 16:12:43 -070064 eap_listener_(new EapListener(dispatcher, interface_index)),
Paul Stewart9413bcc2013-04-04 16:12:43 -070065 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal8bda7962014-04-01 17:09:35 -070066 sockets_(new Sockets()),
Paul Stewart9413bcc2013-04-04 16:12:43 -070067 weak_ptr_factory_(this) {
Paul Stewartced3ad72013-04-03 13:39:25 -070068 PropertyStore *store = this->mutable_store();
Paul Stewart9413bcc2013-04-04 16:12:43 -070069 store->RegisterConstBool(kEapAuthenticationCompletedProperty,
70 &is_eap_authenticated_);
Paul Stewartced3ad72013-04-03 13:39:25 -070071 store->RegisterConstBool(kEapAuthenticatorDetectedProperty,
72 &is_eap_detected_);
Paul Stewartced3ad72013-04-03 13:39:25 -070073 eap_listener_->set_request_received_callback(
Paul Stewart9413bcc2013-04-04 16:12:43 -070074 base::Bind(&Ethernet::OnEapDetected, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -070075 SLOG(Ethernet, 2) << "Ethernet device " << link_name << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -070076}
77
78Ethernet::~Ethernet() {
Eric Shienbrood9a245532012-03-07 14:20:39 -050079 Stop(NULL, EnabledStateChangedCallback());
Paul Stewartf1ce5d22011-05-19 13:10:20 -070080}
81
Eric Shienbrood9a245532012-03-07 14:20:39 -050082void Ethernet::Start(Error *error,
mukesh agrawal6813e762013-07-10 19:05:08 -070083 const EnabledStateChangedCallback &/*callback*/) {
Paul Stewart2713d6c2011-08-25 15:38:15 -070084 service_ = new EthernetService(control_interface(),
85 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -080086 metrics(),
Paul Stewart2713d6c2011-08-25 15:38:15 -070087 manager(),
88 this);
Paul Stewartced3ad72013-04-03 13:39:25 -070089 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
Eric Shienbrood9a245532012-03-07 14:20:39 -050090 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
91 if (error)
92 error->Reset(); // indicate immediate completion
Paul Stewartf1ce5d22011-05-19 13:10:20 -070093}
94
mukesh agrawal6813e762013-07-10 19:05:08 -070095void Ethernet::Stop(Error *error,
96 const EnabledStateChangedCallback &/*callback*/) {
Paul Stewartf2792e82011-12-12 10:23:11 -080097 if (service_) {
98 manager()->DeregisterService(service_);
99 service_ = NULL;
100 }
Paul Stewart9413bcc2013-04-04 16:12:43 -0700101 StopSupplicant();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500102 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
103 if (error)
104 error->Reset(); // indicate immediate completion
Paul Stewartb50f0b92011-05-16 16:31:42 -0700105}
106
Darin Petkovafa6fc42011-06-21 16:21:08 -0700107void Ethernet::LinkEvent(unsigned int flags, unsigned int change) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700108 Device::LinkEvent(flags, change);
109 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700110 link_up_ = true;
Paul Stewart8e9e41c2014-08-07 14:55:47 -0700111 // We SetupWakeOnLan() here, instead of in Start(), because with
mukesh agrawal8bda7962014-04-01 17:09:35 -0700112 // r8139, "ethtool -s eth0 wol g" fails when no cable is plugged
113 // in.
Paul Stewart8e9e41c2014-08-07 14:55:47 -0700114 SetupWakeOnLan();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500115 if (service_) {
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700116 LOG(INFO) << "Registering " << link_name() << " with manager.";
117 // Manager will bring up L3 for us.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500118 manager()->RegisterService(service_);
Darin Petkovafa6fc42011-06-21 16:21:08 -0700119 }
Paul Stewartced3ad72013-04-03 13:39:25 -0700120 eap_listener_->Start();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700121 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
122 link_up_ = false;
Paul Stewartced3ad72013-04-03 13:39:25 -0700123 is_eap_detected_ = false;
Paul Stewart63c93932012-01-06 11:24:12 -0800124 DestroyIPConfig();
Thieu Le9c497072012-10-31 14:06:20 -0700125 if (service_)
126 manager()->DeregisterService(service_);
Paul Stewart03dba0b2011-08-22 16:32:45 -0700127 SelectService(NULL);
Paul Stewart35eff132013-04-12 12:08:40 -0700128 GetEapProvider()->ClearCredentialChangeCallback(this);
129 SetIsEapAuthenticated(false);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700130 StopSupplicant();
Paul Stewartced3ad72013-04-03 13:39:25 -0700131 eap_listener_->Stop();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700132 }
133}
134
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700135void Ethernet::ConnectTo(EthernetService *service) {
136 CHECK(service == service_.get()) << "Ethernet was asked to connect the "
137 << "wrong service?";
Paul Stewartdded0072013-10-24 12:38:54 -0700138 SelectService(service);
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700139 if (AcquireIPConfigWithLeaseName(service->GetStorageIdentifier())) {
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700140 SetServiceState(Service::kStateConfiguring);
141 } else {
142 LOG(ERROR) << "Unable to acquire DHCP config.";
143 SetServiceState(Service::kStateFailure);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700144 DestroyIPConfig();
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700145 }
146}
147
148void Ethernet::DisconnectFrom(EthernetService *service) {
149 CHECK(service == service_.get()) << "Ethernet was asked to disconnect the "
150 << "wrong service?";
151 DropConnection();
152}
153
Paul Stewart9413bcc2013-04-04 16:12:43 -0700154void Ethernet::TryEapAuthentication() {
Paul Stewart35eff132013-04-12 12:08:40 -0700155 try_eap_authentication_callback_.Reset(
156 Bind(&Ethernet::TryEapAuthenticationTask,
157 weak_ptr_factory_.GetWeakPtr()));
158 dispatcher()->PostTask(try_eap_authentication_callback_.callback());
Paul Stewart9413bcc2013-04-04 16:12:43 -0700159}
160
Paul Stewart9413bcc2013-04-04 16:12:43 -0700161void Ethernet::BSSAdded(const ::DBus::Path &path,
162 const map<string, ::DBus::Variant> &properties) {
163 NOTREACHED() << __func__ << " is not implemented for Ethernet";
164}
165
166void Ethernet::BSSRemoved(const ::DBus::Path &path) {
167 NOTREACHED() << __func__ << " is not implemented for Ethernet";
168}
169
170void Ethernet::Certification(const map<string, ::DBus::Variant> &properties) {
171 string subject;
Ben Chan7fab8972014-08-10 17:14:46 -0700172 uint32_t depth;
Paul Stewart9413bcc2013-04-04 16:12:43 -0700173 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
174 dispatcher()->PostTask(Bind(&Ethernet::CertificationTask,
175 weak_ptr_factory_.GetWeakPtr(),
176 subject, depth));
177 }
178}
179
180void Ethernet::EAPEvent(const string &status, const string &parameter) {
181 dispatcher()->PostTask(Bind(&Ethernet::EAPEventTask,
182 weak_ptr_factory_.GetWeakPtr(),
183 status,
184 parameter));
185}
186
187void Ethernet::PropertiesChanged(
188 const map<string, ::DBus::Variant> &properties) {
189 const map<string, ::DBus::Variant>::const_iterator properties_it =
190 properties.find(WPASupplicant::kInterfacePropertyState);
191 if (properties_it == properties.end()) {
192 return;
193 }
194 dispatcher()->PostTask(Bind(&Ethernet::SupplicantStateChangedTask,
195 weak_ptr_factory_.GetWeakPtr(),
196 properties_it->second.reader().get_string()));
197}
198
199void Ethernet::ScanDone() {
200 NOTREACHED() << __func__ << " is not implented for Ethernet";
201}
202
Paul Stewart35eff132013-04-12 12:08:40 -0700203EthernetEapProvider *Ethernet::GetEapProvider() {
204 EthernetEapProvider *eap_provider = manager()->ethernet_eap_provider();
205 CHECK(eap_provider);
206 return eap_provider;
207}
208
209ServiceConstRefPtr Ethernet::GetEapService() {
210 ServiceConstRefPtr eap_service = GetEapProvider()->service();
211 CHECK(eap_service);
212 return eap_service;
213}
214
Paul Stewartced3ad72013-04-03 13:39:25 -0700215void Ethernet::OnEapDetected() {
216 is_eap_detected_ = true;
217 eap_listener_->Stop();
Paul Stewart35eff132013-04-12 12:08:40 -0700218 GetEapProvider()->SetCredentialChangeCallback(
219 this,
220 base::Bind(&Ethernet::TryEapAuthentication,
221 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart9413bcc2013-04-04 16:12:43 -0700222 TryEapAuthentication();
223}
224
225bool Ethernet::StartSupplicant() {
226 if (supplicant_process_proxy_.get()) {
227 return true;
228 }
229
230 supplicant_process_proxy_.reset(
231 proxy_factory_->CreateSupplicantProcessProxy(
232 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
233 ::DBus::Path interface_path;
234 try {
235 map<string, DBus::Variant> create_interface_args;
236 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
237 append_string(link_name().c_str());
238 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
239 append_string(WPASupplicant::kDriverWired);
240 create_interface_args[
241 WPASupplicant::kInterfacePropertyConfigFile].writer().
242 append_string(WPASupplicant::kSupplicantConfPath);
243 interface_path =
244 supplicant_process_proxy_->CreateInterface(create_interface_args);
245 } catch (const DBus::Error &e) { // NOLINT
246 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
247 interface_path = supplicant_process_proxy_->GetInterface(link_name());
248 } else {
249 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
250 StopSupplicant();
251 return false;
252 }
253 }
254 supplicant_interface_proxy_.reset(
255 proxy_factory_->CreateSupplicantInterfaceProxy(
256 this, interface_path, WPASupplicant::kDBusAddr));
257 supplicant_interface_path_ = interface_path;
258 return true;
259}
260
261bool Ethernet::StartEapAuthentication() {
262 map<string, DBus::Variant> params;
Paul Stewart35eff132013-04-12 12:08:40 -0700263 GetEapService()->eap()->PopulateSupplicantProperties(
Paul Stewartc350e682014-06-19 15:44:30 -0700264 &certificate_file_, &params);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700265 params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
266 append_string(WPASupplicant::kKeyManagementIeee8021X);
267 params[WPASupplicant::kNetworkPropertyEapolFlags].writer().
268 append_uint32(0);
269 params[WPASupplicant::kNetworkPropertyScanSSID].writer().
270 append_uint32(0);
271 service_->ClearEAPCertification();
272 eap_state_handler_.Reset();
273
274 try {
275 if (!supplicant_network_path_.empty()) {
276 supplicant_interface_proxy_->RemoveNetwork(supplicant_network_path_);
277 }
278 supplicant_network_path_ = supplicant_interface_proxy_->AddNetwork(params);
279 CHECK(!supplicant_network_path_.empty());
280 } catch (const DBus::Error &e) { // NOLINT
281 LOG(ERROR) << "exception while adding network: " << e.what();
282 return false;
283 }
284
285 supplicant_interface_proxy_->SelectNetwork(supplicant_network_path_);
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700286 supplicant_interface_proxy_->EAPLogon();
Paul Stewart9413bcc2013-04-04 16:12:43 -0700287 return true;
288}
289
290void Ethernet::StopSupplicant() {
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700291 if (supplicant_interface_proxy_.get()) {
292 supplicant_interface_proxy_->EAPLogoff();
293 }
Paul Stewart9413bcc2013-04-04 16:12:43 -0700294 supplicant_interface_proxy_.reset();
295 if (!supplicant_interface_path_.empty() && supplicant_process_proxy_.get()) {
296 try {
297 supplicant_process_proxy_->RemoveInterface(
298 ::DBus::Path(supplicant_interface_path_));
299 } catch (const DBus::Error &e) { // NOLINT
300 LOG(ERROR) << __func__ << ": Failed to remove interface from supplicant.";
301 }
302 }
303 supplicant_network_path_ = "";
304 supplicant_interface_path_ = "";
305 supplicant_process_proxy_.reset();
306 SetIsEapAuthenticated(false);
307}
308
309void Ethernet::SetIsEapAuthenticated(bool is_eap_authenticated) {
310 if (is_eap_authenticated == is_eap_authenticated_) {
311 return;
312 }
313
314 // If our EAP authentication state changes, we have now joined a different
315 // network. Restart the DHCP process and any other connection state.
316 DisconnectFrom(service_);
317 if (service_ && link_up_) {
318 ConnectTo(service_);
319 }
320 is_eap_authenticated_ = is_eap_authenticated;
321 adaptor()->EmitBoolChanged(kEapAuthenticationCompletedProperty,
322 is_eap_authenticated_);
323}
324
Ben Chan7fab8972014-08-10 17:14:46 -0700325void Ethernet::CertificationTask(const string &subject, uint32_t depth) {
Paul Stewart9413bcc2013-04-04 16:12:43 -0700326 if (!service_) {
327 LOG(ERROR) << "Ethernet " << link_name() << " " << __func__
328 << " with no service.";
329 return;
330 }
331
332 service_->AddEAPCertification(subject, depth);
333}
334
335void Ethernet::EAPEventTask(const string &status, const string &parameter) {
336 LOG(INFO) << "In " << __func__ << " with status " << status
337 << ", parameter " << parameter;
338 Service::ConnectFailure failure = Service::kFailureUnknown;
339 if (eap_state_handler_.ParseStatus(status, parameter, &failure)) {
340 LOG(INFO) << "EAP authentication succeeded!";
341 SetIsEapAuthenticated(true);
342 } else if (failure != Service::Service::kFailureUnknown) {
343 LOG(INFO) << "EAP authentication failed!";
344 SetIsEapAuthenticated(false);
345 }
346}
347
348void Ethernet::SupplicantStateChangedTask(const string &state) {
349 LOG(INFO) << "Supplicant state changed to " << state;
Paul Stewartced3ad72013-04-03 13:39:25 -0700350}
351
Paul Stewart35eff132013-04-12 12:08:40 -0700352void Ethernet::TryEapAuthenticationTask() {
353 if (!service_) {
354 LOG(INFO) << "Service is missing; not doing EAP authentication.";
355 return;
356 }
357
358 if (!GetEapService()->Is8021xConnectable()) {
359 if (is_eap_authenticated_) {
360 LOG(INFO) << "EAP Service lost 802.1X credentials; "
361 << "terminating EAP authentication.";
362 } else {
363 LOG(INFO) << "EAP Service lacks 802.1X credentials; "
364 << "not doing EAP authentication.";
365 }
366 StopSupplicant();
367 return;
368 }
369
370 if (!is_eap_detected_) {
371 LOG(WARNING) << "EAP authenticator not detected; "
372 << "not doing EAP authentication.";
373 return;
374 }
375 if (!StartSupplicant()) {
376 LOG(ERROR) << "Failed to start supplicant.";
377 return;
378 }
379 StartEapAuthentication();
380}
381
Paul Stewart8e9e41c2014-08-07 14:55:47 -0700382void Ethernet::SetupWakeOnLan() {
mukesh agrawal8bda7962014-04-01 17:09:35 -0700383 int sock;
384 struct ifreq interface_command;
385 struct ethtool_wolinfo wake_on_lan_command;
386
387 if (link_name().length() >= sizeof(interface_command.ifr_name)) {
388 LOG(WARNING) << "Interface name " << link_name() << " too long: "
389 << link_name().size() << " >= "
390 << sizeof(interface_command.ifr_name);
391 return;
392 }
393
394 sock = sockets_->Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
395 if (sock < 0) {
396 LOG(WARNING) << "Failed to allocate socket: "
397 << sockets_->ErrorString() << ".";
398 return;
399 }
400 ScopedSocketCloser socket_closer(sockets_.get(), sock);
401
402 memset(&interface_command, 0, sizeof(interface_command));
403 memset(&wake_on_lan_command, 0, sizeof(wake_on_lan_command));
404 wake_on_lan_command.cmd = ETHTOOL_SWOL;
Paul Stewart8e9e41c2014-08-07 14:55:47 -0700405 if (manager()->IsWakeOnLanEnabled()) {
406 wake_on_lan_command.wolopts = WAKE_MAGIC;
407 }
mukesh agrawal8bda7962014-04-01 17:09:35 -0700408 interface_command.ifr_data = &wake_on_lan_command;
409 memcpy(interface_command.ifr_name,
410 link_name().data(), link_name().length());
411
412 int res = sockets_->Ioctl(sock, SIOCETHTOOL, &interface_command);
413 if (res < 0) {
414 LOG(WARNING) << "Failed to enable wake-on-lan: "
415 << sockets_->ErrorString() << ".";
416 return;
417 }
418}
419
Paul Stewartb50f0b92011-05-16 16:31:42 -0700420} // namespace shill