blob: 106b54e2697f7c42b8a861f2eebd9cb81cf7f72b [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>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -070010#include <linux/if.h> // 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"
Paul Stewart9413bcc2013-04-04 16:12:43 -070033#include "shill/nss.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070034#include "shill/profile.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070035#include "shill/proxy_factory.h"
Paul Stewartf1ce5d22011-05-19 13:10:20 -070036#include "shill/rtnl_handler.h"
Paul Stewart9413bcc2013-04-04 16:12:43 -070037#include "shill/supplicant_interface_proxy_interface.h"
38#include "shill/supplicant_process_proxy_interface.h"
39#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070040
Paul Stewart9413bcc2013-04-04 16:12:43 -070041using std::map;
Chris Masonea82b7112011-05-25 15:16:29 -070042using std::string;
Paul Stewart9413bcc2013-04-04 16:12:43 -070043using std::vector;
Chris Masonea82b7112011-05-25 15:16:29 -070044
Paul Stewartb50f0b92011-05-16 16:31:42 -070045namespace shill {
Darin Petkovafa6fc42011-06-21 16:21:08 -070046
Paul Stewartb50f0b92011-05-16 16:31:42 -070047Ethernet::Ethernet(ControlInterface *control_interface,
48 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080049 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070050 Manager *manager,
Darin Petkovafa6fc42011-06-21 16:21:08 -070051 const string &link_name,
mukesh agrawal93a29ed2012-04-17 16:13:01 -070052 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070053 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070054 : Device(control_interface,
55 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080056 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070057 manager,
58 link_name,
Chris Masone626719f2011-08-18 16:58:48 -070059 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080060 interface_index,
61 Technology::kEthernet),
Paul Stewartced3ad72013-04-03 13:39:25 -070062 link_up_(false),
Paul Stewart9413bcc2013-04-04 16:12:43 -070063 is_eap_authenticated_(false),
Paul Stewartced3ad72013-04-03 13:39:25 -070064 is_eap_detected_(false),
Paul Stewart9413bcc2013-04-04 16:12:43 -070065 eap_listener_(new EapListener(dispatcher, interface_index)),
66 nss_(NSS::GetInstance()),
Paul Stewart9413bcc2013-04-04 16:12:43 -070067 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal8bda7962014-04-01 17:09:35 -070068 sockets_(new Sockets()),
Paul Stewart9413bcc2013-04-04 16:12:43 -070069 weak_ptr_factory_(this) {
Paul Stewartced3ad72013-04-03 13:39:25 -070070 PropertyStore *store = this->mutable_store();
Paul Stewart9413bcc2013-04-04 16:12:43 -070071 store->RegisterConstBool(kEapAuthenticationCompletedProperty,
72 &is_eap_authenticated_);
Paul Stewartced3ad72013-04-03 13:39:25 -070073 store->RegisterConstBool(kEapAuthenticatorDetectedProperty,
74 &is_eap_detected_);
Paul Stewartced3ad72013-04-03 13:39:25 -070075 eap_listener_->set_request_received_callback(
Paul Stewart9413bcc2013-04-04 16:12:43 -070076 base::Bind(&Ethernet::OnEapDetected, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -070077 SLOG(Ethernet, 2) << "Ethernet device " << link_name << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -070078}
79
80Ethernet::~Ethernet() {
Eric Shienbrood9a245532012-03-07 14:20:39 -050081 Stop(NULL, EnabledStateChangedCallback());
Paul Stewartf1ce5d22011-05-19 13:10:20 -070082}
83
Eric Shienbrood9a245532012-03-07 14:20:39 -050084void Ethernet::Start(Error *error,
mukesh agrawal6813e762013-07-10 19:05:08 -070085 const EnabledStateChangedCallback &/*callback*/) {
Paul Stewart2713d6c2011-08-25 15:38:15 -070086 service_ = new EthernetService(control_interface(),
87 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -080088 metrics(),
Paul Stewart2713d6c2011-08-25 15:38:15 -070089 manager(),
90 this);
Paul Stewartced3ad72013-04-03 13:39:25 -070091 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
Eric Shienbrood9a245532012-03-07 14:20:39 -050092 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
93 if (error)
94 error->Reset(); // indicate immediate completion
Paul Stewartf1ce5d22011-05-19 13:10:20 -070095}
96
mukesh agrawal6813e762013-07-10 19:05:08 -070097void Ethernet::Stop(Error *error,
98 const EnabledStateChangedCallback &/*callback*/) {
Paul Stewartf2792e82011-12-12 10:23:11 -080099 if (service_) {
100 manager()->DeregisterService(service_);
101 service_ = NULL;
102 }
Paul Stewart9413bcc2013-04-04 16:12:43 -0700103 StopSupplicant();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500104 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
105 if (error)
106 error->Reset(); // indicate immediate completion
Paul Stewartb50f0b92011-05-16 16:31:42 -0700107}
108
Darin Petkovafa6fc42011-06-21 16:21:08 -0700109void Ethernet::LinkEvent(unsigned int flags, unsigned int change) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700110 Device::LinkEvent(flags, change);
111 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700112 link_up_ = true;
mukesh agrawal8bda7962014-04-01 17:09:35 -0700113 // We EnableWakeOnLan() here, instead of in Start(), because with
114 // r8139, "ethtool -s eth0 wol g" fails when no cable is plugged
115 // in.
116 EnableWakeOnLan();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500117 if (service_) {
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700118 LOG(INFO) << "Registering " << link_name() << " with manager.";
119 // Manager will bring up L3 for us.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500120 manager()->RegisterService(service_);
Darin Petkovafa6fc42011-06-21 16:21:08 -0700121 }
Paul Stewartced3ad72013-04-03 13:39:25 -0700122 eap_listener_->Start();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700123 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
124 link_up_ = false;
Paul Stewartced3ad72013-04-03 13:39:25 -0700125 is_eap_detected_ = false;
Paul Stewart63c93932012-01-06 11:24:12 -0800126 DestroyIPConfig();
Thieu Le9c497072012-10-31 14:06:20 -0700127 if (service_)
128 manager()->DeregisterService(service_);
Paul Stewart03dba0b2011-08-22 16:32:45 -0700129 SelectService(NULL);
Paul Stewart35eff132013-04-12 12:08:40 -0700130 GetEapProvider()->ClearCredentialChangeCallback(this);
131 SetIsEapAuthenticated(false);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700132 StopSupplicant();
Paul Stewartced3ad72013-04-03 13:39:25 -0700133 eap_listener_->Stop();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700134 }
135}
136
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700137void Ethernet::ConnectTo(EthernetService *service) {
138 CHECK(service == service_.get()) << "Ethernet was asked to connect the "
139 << "wrong service?";
Paul Stewartdded0072013-10-24 12:38:54 -0700140 SelectService(service);
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700141 if (AcquireIPConfigWithLeaseName(service->GetStorageIdentifier())) {
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700142 SetServiceState(Service::kStateConfiguring);
143 } else {
144 LOG(ERROR) << "Unable to acquire DHCP config.";
145 SetServiceState(Service::kStateFailure);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700146 DestroyIPConfig();
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700147 }
148}
149
150void Ethernet::DisconnectFrom(EthernetService *service) {
151 CHECK(service == service_.get()) << "Ethernet was asked to disconnect the "
152 << "wrong service?";
153 DropConnection();
154}
155
Paul Stewart9413bcc2013-04-04 16:12:43 -0700156void Ethernet::TryEapAuthentication() {
Paul Stewart35eff132013-04-12 12:08:40 -0700157 try_eap_authentication_callback_.Reset(
158 Bind(&Ethernet::TryEapAuthenticationTask,
159 weak_ptr_factory_.GetWeakPtr()));
160 dispatcher()->PostTask(try_eap_authentication_callback_.callback());
Paul Stewart9413bcc2013-04-04 16:12:43 -0700161}
162
Paul Stewart9413bcc2013-04-04 16:12:43 -0700163void Ethernet::BSSAdded(const ::DBus::Path &path,
164 const map<string, ::DBus::Variant> &properties) {
165 NOTREACHED() << __func__ << " is not implemented for Ethernet";
166}
167
168void Ethernet::BSSRemoved(const ::DBus::Path &path) {
169 NOTREACHED() << __func__ << " is not implemented for Ethernet";
170}
171
172void Ethernet::Certification(const map<string, ::DBus::Variant> &properties) {
173 string subject;
174 uint32 depth;
175 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
176 dispatcher()->PostTask(Bind(&Ethernet::CertificationTask,
177 weak_ptr_factory_.GetWeakPtr(),
178 subject, depth));
179 }
180}
181
182void Ethernet::EAPEvent(const string &status, const string &parameter) {
183 dispatcher()->PostTask(Bind(&Ethernet::EAPEventTask,
184 weak_ptr_factory_.GetWeakPtr(),
185 status,
186 parameter));
187}
188
189void Ethernet::PropertiesChanged(
190 const map<string, ::DBus::Variant> &properties) {
191 const map<string, ::DBus::Variant>::const_iterator properties_it =
192 properties.find(WPASupplicant::kInterfacePropertyState);
193 if (properties_it == properties.end()) {
194 return;
195 }
196 dispatcher()->PostTask(Bind(&Ethernet::SupplicantStateChangedTask,
197 weak_ptr_factory_.GetWeakPtr(),
198 properties_it->second.reader().get_string()));
199}
200
201void Ethernet::ScanDone() {
202 NOTREACHED() << __func__ << " is not implented for Ethernet";
203}
204
Paul Stewart35eff132013-04-12 12:08:40 -0700205EthernetEapProvider *Ethernet::GetEapProvider() {
206 EthernetEapProvider *eap_provider = manager()->ethernet_eap_provider();
207 CHECK(eap_provider);
208 return eap_provider;
209}
210
211ServiceConstRefPtr Ethernet::GetEapService() {
212 ServiceConstRefPtr eap_service = GetEapProvider()->service();
213 CHECK(eap_service);
214 return eap_service;
215}
216
Paul Stewartced3ad72013-04-03 13:39:25 -0700217void Ethernet::OnEapDetected() {
218 is_eap_detected_ = true;
219 eap_listener_->Stop();
Paul Stewart35eff132013-04-12 12:08:40 -0700220 GetEapProvider()->SetCredentialChangeCallback(
221 this,
222 base::Bind(&Ethernet::TryEapAuthentication,
223 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart9413bcc2013-04-04 16:12:43 -0700224 TryEapAuthentication();
225}
226
227bool Ethernet::StartSupplicant() {
228 if (supplicant_process_proxy_.get()) {
229 return true;
230 }
231
232 supplicant_process_proxy_.reset(
233 proxy_factory_->CreateSupplicantProcessProxy(
234 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
235 ::DBus::Path interface_path;
236 try {
237 map<string, DBus::Variant> create_interface_args;
238 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
239 append_string(link_name().c_str());
240 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
241 append_string(WPASupplicant::kDriverWired);
242 create_interface_args[
243 WPASupplicant::kInterfacePropertyConfigFile].writer().
244 append_string(WPASupplicant::kSupplicantConfPath);
245 interface_path =
246 supplicant_process_proxy_->CreateInterface(create_interface_args);
247 } catch (const DBus::Error &e) { // NOLINT
248 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
249 interface_path = supplicant_process_proxy_->GetInterface(link_name());
250 } else {
251 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
252 StopSupplicant();
253 return false;
254 }
255 }
256 supplicant_interface_proxy_.reset(
257 proxy_factory_->CreateSupplicantInterfaceProxy(
258 this, interface_path, WPASupplicant::kDBusAddr));
259 supplicant_interface_path_ = interface_path;
260 return true;
261}
262
263bool Ethernet::StartEapAuthentication() {
264 map<string, DBus::Variant> params;
265 vector<char> nss_identifier(link_name().begin(), link_name().end());
Paul Stewart35eff132013-04-12 12:08:40 -0700266 GetEapService()->eap()->PopulateSupplicantProperties(
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700267 &certificate_file_, nss_, nss_identifier, &params);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700268 params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
269 append_string(WPASupplicant::kKeyManagementIeee8021X);
270 params[WPASupplicant::kNetworkPropertyEapolFlags].writer().
271 append_uint32(0);
272 params[WPASupplicant::kNetworkPropertyScanSSID].writer().
273 append_uint32(0);
274 service_->ClearEAPCertification();
275 eap_state_handler_.Reset();
276
277 try {
278 if (!supplicant_network_path_.empty()) {
279 supplicant_interface_proxy_->RemoveNetwork(supplicant_network_path_);
280 }
281 supplicant_network_path_ = supplicant_interface_proxy_->AddNetwork(params);
282 CHECK(!supplicant_network_path_.empty());
283 } catch (const DBus::Error &e) { // NOLINT
284 LOG(ERROR) << "exception while adding network: " << e.what();
285 return false;
286 }
287
288 supplicant_interface_proxy_->SelectNetwork(supplicant_network_path_);
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700289 supplicant_interface_proxy_->EAPLogon();
Paul Stewart9413bcc2013-04-04 16:12:43 -0700290 return true;
291}
292
293void Ethernet::StopSupplicant() {
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700294 if (supplicant_interface_proxy_.get()) {
295 supplicant_interface_proxy_->EAPLogoff();
296 }
Paul Stewart9413bcc2013-04-04 16:12:43 -0700297 supplicant_interface_proxy_.reset();
298 if (!supplicant_interface_path_.empty() && supplicant_process_proxy_.get()) {
299 try {
300 supplicant_process_proxy_->RemoveInterface(
301 ::DBus::Path(supplicant_interface_path_));
302 } catch (const DBus::Error &e) { // NOLINT
303 LOG(ERROR) << __func__ << ": Failed to remove interface from supplicant.";
304 }
305 }
306 supplicant_network_path_ = "";
307 supplicant_interface_path_ = "";
308 supplicant_process_proxy_.reset();
309 SetIsEapAuthenticated(false);
310}
311
312void Ethernet::SetIsEapAuthenticated(bool is_eap_authenticated) {
313 if (is_eap_authenticated == is_eap_authenticated_) {
314 return;
315 }
316
317 // If our EAP authentication state changes, we have now joined a different
318 // network. Restart the DHCP process and any other connection state.
319 DisconnectFrom(service_);
320 if (service_ && link_up_) {
321 ConnectTo(service_);
322 }
323 is_eap_authenticated_ = is_eap_authenticated;
324 adaptor()->EmitBoolChanged(kEapAuthenticationCompletedProperty,
325 is_eap_authenticated_);
326}
327
328void Ethernet::CertificationTask(const string &subject, uint32 depth) {
329 if (!service_) {
330 LOG(ERROR) << "Ethernet " << link_name() << " " << __func__
331 << " with no service.";
332 return;
333 }
334
335 service_->AddEAPCertification(subject, depth);
336}
337
338void Ethernet::EAPEventTask(const string &status, const string &parameter) {
339 LOG(INFO) << "In " << __func__ << " with status " << status
340 << ", parameter " << parameter;
341 Service::ConnectFailure failure = Service::kFailureUnknown;
342 if (eap_state_handler_.ParseStatus(status, parameter, &failure)) {
343 LOG(INFO) << "EAP authentication succeeded!";
344 SetIsEapAuthenticated(true);
345 } else if (failure != Service::Service::kFailureUnknown) {
346 LOG(INFO) << "EAP authentication failed!";
347 SetIsEapAuthenticated(false);
348 }
349}
350
351void Ethernet::SupplicantStateChangedTask(const string &state) {
352 LOG(INFO) << "Supplicant state changed to " << state;
Paul Stewartced3ad72013-04-03 13:39:25 -0700353}
354
Paul Stewart35eff132013-04-12 12:08:40 -0700355void Ethernet::TryEapAuthenticationTask() {
356 if (!service_) {
357 LOG(INFO) << "Service is missing; not doing EAP authentication.";
358 return;
359 }
360
361 if (!GetEapService()->Is8021xConnectable()) {
362 if (is_eap_authenticated_) {
363 LOG(INFO) << "EAP Service lost 802.1X credentials; "
364 << "terminating EAP authentication.";
365 } else {
366 LOG(INFO) << "EAP Service lacks 802.1X credentials; "
367 << "not doing EAP authentication.";
368 }
369 StopSupplicant();
370 return;
371 }
372
373 if (!is_eap_detected_) {
374 LOG(WARNING) << "EAP authenticator not detected; "
375 << "not doing EAP authentication.";
376 return;
377 }
378 if (!StartSupplicant()) {
379 LOG(ERROR) << "Failed to start supplicant.";
380 return;
381 }
382 StartEapAuthentication();
383}
384
mukesh agrawal8bda7962014-04-01 17:09:35 -0700385void Ethernet::EnableWakeOnLan() {
386 int sock;
387 struct ifreq interface_command;
388 struct ethtool_wolinfo wake_on_lan_command;
389
390 if (link_name().length() >= sizeof(interface_command.ifr_name)) {
391 LOG(WARNING) << "Interface name " << link_name() << " too long: "
392 << link_name().size() << " >= "
393 << sizeof(interface_command.ifr_name);
394 return;
395 }
396
397 sock = sockets_->Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
398 if (sock < 0) {
399 LOG(WARNING) << "Failed to allocate socket: "
400 << sockets_->ErrorString() << ".";
401 return;
402 }
403 ScopedSocketCloser socket_closer(sockets_.get(), sock);
404
405 memset(&interface_command, 0, sizeof(interface_command));
406 memset(&wake_on_lan_command, 0, sizeof(wake_on_lan_command));
407 wake_on_lan_command.cmd = ETHTOOL_SWOL;
408 wake_on_lan_command.wolopts = WAKE_MAGIC;
409 interface_command.ifr_data = &wake_on_lan_command;
410 memcpy(interface_command.ifr_name,
411 link_name().data(), link_name().length());
412
413 int res = sockets_->Ioctl(sock, SIOCETHTOOL, &interface_command);
414 if (res < 0) {
415 LOG(WARNING) << "Failed to enable wake-on-lan: "
416 << sockets_->ErrorString() << ".";
417 return;
418 }
419}
420
Paul Stewartb50f0b92011-05-16 16:31:42 -0700421} // namespace shill