blob: 38786d21cca218a6cfcda3f688b0ba60e608b385 [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()),
Paul Stewart9413bcc2013-04-04 16:12:43 -070064 proxy_factory_(ProxyFactory::GetInstance()),
65 weak_ptr_factory_(this) {
Paul Stewartced3ad72013-04-03 13:39:25 -070066 PropertyStore *store = this->mutable_store();
Paul Stewart9413bcc2013-04-04 16:12:43 -070067 store->RegisterConstBool(kEapAuthenticationCompletedProperty,
68 &is_eap_authenticated_);
Paul Stewartced3ad72013-04-03 13:39:25 -070069 store->RegisterConstBool(kEapAuthenticatorDetectedProperty,
70 &is_eap_detected_);
Paul Stewartced3ad72013-04-03 13:39:25 -070071 eap_listener_->set_request_received_callback(
Paul Stewart9413bcc2013-04-04 16:12:43 -070072 base::Bind(&Ethernet::OnEapDetected, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -070073 SLOG(Ethernet, 2) << "Ethernet device " << link_name << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -070074}
75
76Ethernet::~Ethernet() {
Eric Shienbrood9a245532012-03-07 14:20:39 -050077 Stop(NULL, EnabledStateChangedCallback());
Paul Stewartf1ce5d22011-05-19 13:10:20 -070078}
79
Eric Shienbrood9a245532012-03-07 14:20:39 -050080void Ethernet::Start(Error *error,
81 const EnabledStateChangedCallback &callback) {
Paul Stewart2713d6c2011-08-25 15:38:15 -070082 service_ = new EthernetService(control_interface(),
83 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -080084 metrics(),
Paul Stewart2713d6c2011-08-25 15:38:15 -070085 manager(),
86 this);
Paul Stewartced3ad72013-04-03 13:39:25 -070087 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
Eric Shienbrood9a245532012-03-07 14:20:39 -050088 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
89 if (error)
90 error->Reset(); // indicate immediate completion
Paul Stewartf1ce5d22011-05-19 13:10:20 -070091}
92
Eric Shienbrood9a245532012-03-07 14:20:39 -050093void Ethernet::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Paul Stewartf2792e82011-12-12 10:23:11 -080094 if (service_) {
95 manager()->DeregisterService(service_);
96 service_ = NULL;
97 }
Paul Stewart9413bcc2013-04-04 16:12:43 -070098 StopSupplicant();
Eric Shienbrood9a245532012-03-07 14:20:39 -050099 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
100 if (error)
101 error->Reset(); // indicate immediate completion
Paul Stewartb50f0b92011-05-16 16:31:42 -0700102}
103
Darin Petkovafa6fc42011-06-21 16:21:08 -0700104void Ethernet::LinkEvent(unsigned int flags, unsigned int change) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700105 Device::LinkEvent(flags, change);
106 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700107 link_up_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500108 if (service_) {
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700109 LOG(INFO) << "Registering " << link_name() << " with manager.";
110 // Manager will bring up L3 for us.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500111 manager()->RegisterService(service_);
Darin Petkovafa6fc42011-06-21 16:21:08 -0700112 }
Paul Stewartced3ad72013-04-03 13:39:25 -0700113 eap_listener_->Start();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700114 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
115 link_up_ = false;
Paul Stewartced3ad72013-04-03 13:39:25 -0700116 is_eap_detected_ = false;
Paul Stewart63c93932012-01-06 11:24:12 -0800117 DestroyIPConfig();
Thieu Le9c497072012-10-31 14:06:20 -0700118 if (service_)
119 manager()->DeregisterService(service_);
Paul Stewart03dba0b2011-08-22 16:32:45 -0700120 SelectService(NULL);
Paul Stewart35eff132013-04-12 12:08:40 -0700121 GetEapProvider()->ClearCredentialChangeCallback(this);
122 SetIsEapAuthenticated(false);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700123 StopSupplicant();
Paul Stewartced3ad72013-04-03 13:39:25 -0700124 eap_listener_->Stop();
Paul Stewartf1ce5d22011-05-19 13:10:20 -0700125 }
126}
127
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700128void Ethernet::ConnectTo(EthernetService *service) {
129 CHECK(service == service_.get()) << "Ethernet was asked to connect the "
130 << "wrong service?";
131 if (AcquireIPConfigWithLeaseName(service->GetStorageIdentifier())) {
132 SelectService(service);
133 SetServiceState(Service::kStateConfiguring);
134 } else {
135 LOG(ERROR) << "Unable to acquire DHCP config.";
136 SetServiceState(Service::kStateFailure);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700137 DestroyIPConfig();
Christopher Wiley2f1bbf02012-10-25 15:31:13 -0700138 }
139}
140
141void Ethernet::DisconnectFrom(EthernetService *service) {
142 CHECK(service == service_.get()) << "Ethernet was asked to disconnect the "
143 << "wrong service?";
144 DropConnection();
145}
146
Paul Stewart9413bcc2013-04-04 16:12:43 -0700147void Ethernet::TryEapAuthentication() {
Paul Stewart35eff132013-04-12 12:08:40 -0700148 try_eap_authentication_callback_.Reset(
149 Bind(&Ethernet::TryEapAuthenticationTask,
150 weak_ptr_factory_.GetWeakPtr()));
151 dispatcher()->PostTask(try_eap_authentication_callback_.callback());
Paul Stewart9413bcc2013-04-04 16:12:43 -0700152}
153
Paul Stewart9413bcc2013-04-04 16:12:43 -0700154void Ethernet::BSSAdded(const ::DBus::Path &path,
155 const map<string, ::DBus::Variant> &properties) {
156 NOTREACHED() << __func__ << " is not implemented for Ethernet";
157}
158
159void Ethernet::BSSRemoved(const ::DBus::Path &path) {
160 NOTREACHED() << __func__ << " is not implemented for Ethernet";
161}
162
163void Ethernet::Certification(const map<string, ::DBus::Variant> &properties) {
164 string subject;
165 uint32 depth;
166 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
167 dispatcher()->PostTask(Bind(&Ethernet::CertificationTask,
168 weak_ptr_factory_.GetWeakPtr(),
169 subject, depth));
170 }
171}
172
173void Ethernet::EAPEvent(const string &status, const string &parameter) {
174 dispatcher()->PostTask(Bind(&Ethernet::EAPEventTask,
175 weak_ptr_factory_.GetWeakPtr(),
176 status,
177 parameter));
178}
179
180void Ethernet::PropertiesChanged(
181 const map<string, ::DBus::Variant> &properties) {
182 const map<string, ::DBus::Variant>::const_iterator properties_it =
183 properties.find(WPASupplicant::kInterfacePropertyState);
184 if (properties_it == properties.end()) {
185 return;
186 }
187 dispatcher()->PostTask(Bind(&Ethernet::SupplicantStateChangedTask,
188 weak_ptr_factory_.GetWeakPtr(),
189 properties_it->second.reader().get_string()));
190}
191
192void Ethernet::ScanDone() {
193 NOTREACHED() << __func__ << " is not implented for Ethernet";
194}
195
Paul Stewart35eff132013-04-12 12:08:40 -0700196EthernetEapProvider *Ethernet::GetEapProvider() {
197 EthernetEapProvider *eap_provider = manager()->ethernet_eap_provider();
198 CHECK(eap_provider);
199 return eap_provider;
200}
201
202ServiceConstRefPtr Ethernet::GetEapService() {
203 ServiceConstRefPtr eap_service = GetEapProvider()->service();
204 CHECK(eap_service);
205 return eap_service;
206}
207
Paul Stewartced3ad72013-04-03 13:39:25 -0700208void Ethernet::OnEapDetected() {
209 is_eap_detected_ = true;
210 eap_listener_->Stop();
Paul Stewart35eff132013-04-12 12:08:40 -0700211 GetEapProvider()->SetCredentialChangeCallback(
212 this,
213 base::Bind(&Ethernet::TryEapAuthentication,
214 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart9413bcc2013-04-04 16:12:43 -0700215 TryEapAuthentication();
216}
217
218bool Ethernet::StartSupplicant() {
219 if (supplicant_process_proxy_.get()) {
220 return true;
221 }
222
223 supplicant_process_proxy_.reset(
224 proxy_factory_->CreateSupplicantProcessProxy(
225 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
226 ::DBus::Path interface_path;
227 try {
228 map<string, DBus::Variant> create_interface_args;
229 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
230 append_string(link_name().c_str());
231 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
232 append_string(WPASupplicant::kDriverWired);
233 create_interface_args[
234 WPASupplicant::kInterfacePropertyConfigFile].writer().
235 append_string(WPASupplicant::kSupplicantConfPath);
236 interface_path =
237 supplicant_process_proxy_->CreateInterface(create_interface_args);
238 } catch (const DBus::Error &e) { // NOLINT
239 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
240 interface_path = supplicant_process_proxy_->GetInterface(link_name());
241 } else {
242 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
243 StopSupplicant();
244 return false;
245 }
246 }
247 supplicant_interface_proxy_.reset(
248 proxy_factory_->CreateSupplicantInterfaceProxy(
249 this, interface_path, WPASupplicant::kDBusAddr));
250 supplicant_interface_path_ = interface_path;
251 return true;
252}
253
254bool Ethernet::StartEapAuthentication() {
255 map<string, DBus::Variant> params;
256 vector<char> nss_identifier(link_name().begin(), link_name().end());
Paul Stewart35eff132013-04-12 12:08:40 -0700257 GetEapService()->eap()->PopulateSupplicantProperties(
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700258 &certificate_file_, nss_, nss_identifier, &params);
Paul Stewart9413bcc2013-04-04 16:12:43 -0700259 params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
260 append_string(WPASupplicant::kKeyManagementIeee8021X);
261 params[WPASupplicant::kNetworkPropertyEapolFlags].writer().
262 append_uint32(0);
263 params[WPASupplicant::kNetworkPropertyScanSSID].writer().
264 append_uint32(0);
265 service_->ClearEAPCertification();
266 eap_state_handler_.Reset();
267
268 try {
269 if (!supplicant_network_path_.empty()) {
270 supplicant_interface_proxy_->RemoveNetwork(supplicant_network_path_);
271 }
272 supplicant_network_path_ = supplicant_interface_proxy_->AddNetwork(params);
273 CHECK(!supplicant_network_path_.empty());
274 } catch (const DBus::Error &e) { // NOLINT
275 LOG(ERROR) << "exception while adding network: " << e.what();
276 return false;
277 }
278
279 supplicant_interface_proxy_->SelectNetwork(supplicant_network_path_);
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700280 supplicant_interface_proxy_->EAPLogon();
Paul Stewart9413bcc2013-04-04 16:12:43 -0700281 return true;
282}
283
284void Ethernet::StopSupplicant() {
Paul Stewartbe9abfd2013-04-22 12:18:48 -0700285 if (supplicant_interface_proxy_.get()) {
286 supplicant_interface_proxy_->EAPLogoff();
287 }
Paul Stewart9413bcc2013-04-04 16:12:43 -0700288 supplicant_interface_proxy_.reset();
289 if (!supplicant_interface_path_.empty() && supplicant_process_proxy_.get()) {
290 try {
291 supplicant_process_proxy_->RemoveInterface(
292 ::DBus::Path(supplicant_interface_path_));
293 } catch (const DBus::Error &e) { // NOLINT
294 LOG(ERROR) << __func__ << ": Failed to remove interface from supplicant.";
295 }
296 }
297 supplicant_network_path_ = "";
298 supplicant_interface_path_ = "";
299 supplicant_process_proxy_.reset();
300 SetIsEapAuthenticated(false);
301}
302
303void Ethernet::SetIsEapAuthenticated(bool is_eap_authenticated) {
304 if (is_eap_authenticated == is_eap_authenticated_) {
305 return;
306 }
307
308 // If our EAP authentication state changes, we have now joined a different
309 // network. Restart the DHCP process and any other connection state.
310 DisconnectFrom(service_);
311 if (service_ && link_up_) {
312 ConnectTo(service_);
313 }
314 is_eap_authenticated_ = is_eap_authenticated;
315 adaptor()->EmitBoolChanged(kEapAuthenticationCompletedProperty,
316 is_eap_authenticated_);
317}
318
319void Ethernet::CertificationTask(const string &subject, uint32 depth) {
320 if (!service_) {
321 LOG(ERROR) << "Ethernet " << link_name() << " " << __func__
322 << " with no service.";
323 return;
324 }
325
326 service_->AddEAPCertification(subject, depth);
327}
328
329void Ethernet::EAPEventTask(const string &status, const string &parameter) {
330 LOG(INFO) << "In " << __func__ << " with status " << status
331 << ", parameter " << parameter;
332 Service::ConnectFailure failure = Service::kFailureUnknown;
333 if (eap_state_handler_.ParseStatus(status, parameter, &failure)) {
334 LOG(INFO) << "EAP authentication succeeded!";
335 SetIsEapAuthenticated(true);
336 } else if (failure != Service::Service::kFailureUnknown) {
337 LOG(INFO) << "EAP authentication failed!";
338 SetIsEapAuthenticated(false);
339 }
340}
341
342void Ethernet::SupplicantStateChangedTask(const string &state) {
343 LOG(INFO) << "Supplicant state changed to " << state;
Paul Stewartced3ad72013-04-03 13:39:25 -0700344}
345
Paul Stewart35eff132013-04-12 12:08:40 -0700346void Ethernet::TryEapAuthenticationTask() {
347 if (!service_) {
348 LOG(INFO) << "Service is missing; not doing EAP authentication.";
349 return;
350 }
351
352 if (!GetEapService()->Is8021xConnectable()) {
353 if (is_eap_authenticated_) {
354 LOG(INFO) << "EAP Service lost 802.1X credentials; "
355 << "terminating EAP authentication.";
356 } else {
357 LOG(INFO) << "EAP Service lacks 802.1X credentials; "
358 << "not doing EAP authentication.";
359 }
360 StopSupplicant();
361 return;
362 }
363
364 if (!is_eap_detected_) {
365 LOG(WARNING) << "EAP authenticator not detected; "
366 << "not doing EAP authentication.";
367 return;
368 }
369 if (!StartSupplicant()) {
370 LOG(ERROR) << "Failed to start supplicant.";
371 return;
372 }
373 StartEapAuthentication();
374}
375
Paul Stewartb50f0b92011-05-16 16:31:42 -0700376} // namespace shill