1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/ethernet/ethernet.h"
18
19 #include <linux/ethtool.h>
20 #include <netinet/ether.h>
21 #include <netinet/in.h>
22 #include <linux/if.h> // NOLINT - Needs definitions from netinet/ether.h
23 #include <linux/sockios.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <time.h>
27
28 #include <map>
29 #include <string>
30 #include <vector>
31
32 #include <base/bind.h>
33
34 #include "shill/adaptor_interfaces.h"
35 #include "shill/control_interface.h"
36 #include "shill/device.h"
37 #include "shill/device_info.h"
38 #include "shill/ethernet/ethernet_service.h"
39 #include "shill/event_dispatcher.h"
40 #include "shill/logging.h"
41 #include "shill/manager.h"
42 #include "shill/net/rtnl_handler.h"
43 #include "shill/pppoe/pppoe_service.h"
44 #include "shill/profile.h"
45 #include "shill/property_accessor.h"
46 #include "shill/refptr_types.h"
47 #include "shill/store_interface.h"
48
49 #if !defined(DISABLE_WIRED_8021X)
50 #include "shill/eap_credentials.h"
51 #include "shill/eap_listener.h"
52 #include "shill/ethernet/ethernet_eap_provider.h"
53 #include "shill/supplicant/supplicant_interface_proxy_interface.h"
54 #include "shill/supplicant/supplicant_process_proxy_interface.h"
55 #include "shill/supplicant/wpa_supplicant.h"
56 #endif // DISABLE_WIRED_8021X
57
58 using std::map;
59 using std::string;
60 using std::vector;
61
62 namespace shill {
63
64 namespace Logging {
65 static auto kModuleLogScope = ScopeLogger::kEthernet;
ObjectID(Ethernet * e)66 static string ObjectID(Ethernet* e) { return e->GetRpcIdentifier(); }
67 }
68
Ethernet(ControlInterface * control_interface,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager,const string & link_name,const string & address,int interface_index)69 Ethernet::Ethernet(ControlInterface* control_interface,
70 EventDispatcher* dispatcher,
71 Metrics* metrics,
72 Manager* manager,
73 const string& link_name,
74 const string& address,
75 int interface_index)
76 : Device(control_interface,
77 dispatcher,
78 metrics,
79 manager,
80 link_name,
81 address,
82 interface_index,
83 Technology::kEthernet),
84 control_interface_(control_interface),
85 link_up_(false),
86 #if !defined(DISABLE_WIRED_8021X)
87 is_eap_authenticated_(false),
88 is_eap_detected_(false),
89 eap_listener_(new EapListener(dispatcher, interface_index)),
90 supplicant_process_proxy_(
91 control_interface_->CreateSupplicantProcessProxy(
92 base::Closure(), base::Closure())),
93 #endif // DISABLE_WIRED_8021X
94 sockets_(new Sockets()),
95 weak_ptr_factory_(this) {
96 PropertyStore* store = this->mutable_store();
97 #if !defined(DISABLE_WIRED_8021X)
98 store->RegisterConstBool(kEapAuthenticationCompletedProperty,
99 &is_eap_authenticated_);
100 store->RegisterConstBool(kEapAuthenticatorDetectedProperty,
101 &is_eap_detected_);
102 #endif // DISABLE_WIRED_8021X
103 store->RegisterConstBool(kLinkUpProperty, &link_up_);
104 store->RegisterDerivedBool(kPPPoEProperty, BoolAccessor(
105 new CustomAccessor<Ethernet, bool>(this,
106 &Ethernet::GetPPPoEMode,
107 &Ethernet::ConfigurePPPoEMode,
108 &Ethernet::ClearPPPoEMode)));
109
110 #if !defined(DISABLE_WIRED_8021X)
111 eap_listener_->set_request_received_callback(
112 base::Bind(&Ethernet::OnEapDetected, weak_ptr_factory_.GetWeakPtr()));
113 #endif // DISABLE_WIRED_8021X
114 service_ = CreateEthernetService();
115 SLOG(this, 2) << "Ethernet device " << link_name << " initialized.";
116 }
117
~Ethernet()118 Ethernet::~Ethernet() {
119 }
120
Start(Error * error,const EnabledStateChangedCallback &)121 void Ethernet::Start(Error* error,
122 const EnabledStateChangedCallback& /*callback*/) {
123 rtnl_handler()->SetInterfaceFlags(interface_index(), IFF_UP, IFF_UP);
124 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
125 LOG(INFO) << "Registering " << link_name() << " with manager.";
126 if (!manager()->HasService(service_)) {
127 manager()->RegisterService(service_);
128 }
129 if (error)
130 error->Reset(); // indicate immediate completion
131 }
132
Stop(Error * error,const EnabledStateChangedCallback &)133 void Ethernet::Stop(Error* error,
134 const EnabledStateChangedCallback& /*callback*/) {
135 manager()->DeregisterService(service_);
136 #if !defined(DISABLE_WIRED_8021X)
137 StopSupplicant();
138 #endif // DISABLE_WIRED_8021X
139 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
140 if (error)
141 error->Reset(); // indicate immediate completion
142 }
143
LinkEvent(unsigned int flags,unsigned int change)144 void Ethernet::LinkEvent(unsigned int flags, unsigned int change) {
145 Device::LinkEvent(flags, change);
146 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
147 link_up_ = true;
148 adaptor()->EmitBoolChanged(kLinkUpProperty, link_up_);
149 // We SetupWakeOnLan() here, instead of in Start(), because with
150 // r8139, "ethtool -s eth0 wol g" fails when no cable is plugged
151 // in.
152 manager()->UpdateService(service_);
153 service_->OnVisibilityChanged();
154 SetupWakeOnLan();
155 #if !defined(DISABLE_WIRED_8021X)
156 eap_listener_->Start();
157 #endif // DISABLE_WIRED_8021X
158 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
159 link_up_ = false;
160 adaptor()->EmitBoolChanged(kLinkUpProperty, link_up_);
161 DestroyIPConfig();
162 SelectService(nullptr);
163 manager()->UpdateService(service_);
164 service_->OnVisibilityChanged();
165 #if !defined(DISABLE_WIRED_8021X)
166 is_eap_detected_ = false;
167 GetEapProvider()->ClearCredentialChangeCallback(this);
168 SetIsEapAuthenticated(false);
169 StopSupplicant();
170 eap_listener_->Stop();
171 #endif // DISABLE_WIRED_8021X
172 }
173 }
174
Load(StoreInterface * storage)175 bool Ethernet::Load(StoreInterface* storage) {
176 const string id = GetStorageIdentifier();
177 if (!storage->ContainsGroup(id)) {
178 SLOG(this, 2) << "Device is not available in the persistent store: " << id;
179 return false;
180 }
181
182 bool pppoe = false;
183 storage->GetBool(id, kPPPoEProperty, &pppoe);
184
185 Error error;
186 ConfigurePPPoEMode(pppoe, &error);
187 if (!error.IsSuccess()) {
188 LOG(WARNING) << "Error configuring PPPoE mode. Ignoring!";
189 }
190
191 return Device::Load(storage);
192 }
193
Save(StoreInterface * storage)194 bool Ethernet::Save(StoreInterface* storage) {
195 const string id = GetStorageIdentifier();
196 storage->SetBool(id, kPPPoEProperty, GetPPPoEMode(nullptr));
197 return true;
198 }
199
ConnectTo(EthernetService * service)200 void Ethernet::ConnectTo(EthernetService* service) {
201 CHECK(service == service_.get()) << "Ethernet was asked to connect the "
202 << "wrong service?";
203 CHECK(!GetPPPoEMode(nullptr)) << "We should never connect in PPPoE mode!";
204 if (!link_up_) {
205 return;
206 }
207 SelectService(service);
208 if (AcquireIPConfigWithLeaseName(service->GetStorageIdentifier())) {
209 SetServiceState(Service::kStateConfiguring);
210 } else {
211 LOG(ERROR) << "Unable to acquire DHCP config.";
212 SetServiceState(Service::kStateFailure);
213 DestroyIPConfig();
214 }
215 }
216
DisconnectFrom(EthernetService * service)217 void Ethernet::DisconnectFrom(EthernetService* service) {
218 CHECK(service == service_.get()) << "Ethernet was asked to disconnect the "
219 << "wrong service?";
220 DropConnection();
221 }
222
223 #if !defined(DISABLE_WIRED_8021X)
TryEapAuthentication()224 void Ethernet::TryEapAuthentication() {
225 try_eap_authentication_callback_.Reset(
226 Bind(&Ethernet::TryEapAuthenticationTask,
227 weak_ptr_factory_.GetWeakPtr()));
228 dispatcher()->PostTask(try_eap_authentication_callback_.callback());
229 }
230
BSSAdded(const string & path,const KeyValueStore & properties)231 void Ethernet::BSSAdded(const string& path, const KeyValueStore& properties) {
232 NOTREACHED() << __func__ << " is not implemented for Ethernet";
233 }
234
BSSRemoved(const string & path)235 void Ethernet::BSSRemoved(const string& path) {
236 NOTREACHED() << __func__ << " is not implemented for Ethernet";
237 }
238
Certification(const KeyValueStore & properties)239 void Ethernet::Certification(const KeyValueStore& properties) {
240 string subject;
241 uint32_t depth;
242 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
243 dispatcher()->PostTask(Bind(&Ethernet::CertificationTask,
244 weak_ptr_factory_.GetWeakPtr(),
245 subject, depth));
246 }
247 }
248
EAPEvent(const string & status,const string & parameter)249 void Ethernet::EAPEvent(const string& status, const string& parameter) {
250 dispatcher()->PostTask(Bind(&Ethernet::EAPEventTask,
251 weak_ptr_factory_.GetWeakPtr(),
252 status,
253 parameter));
254 }
255
PropertiesChanged(const KeyValueStore & properties)256 void Ethernet::PropertiesChanged(const KeyValueStore& properties) {
257 if (!properties.ContainsString(WPASupplicant::kInterfacePropertyState)) {
258 return;
259 }
260 dispatcher()->PostTask(
261 Bind(&Ethernet::SupplicantStateChangedTask,
262 weak_ptr_factory_.GetWeakPtr(),
263 properties.GetString(WPASupplicant::kInterfacePropertyState)));
264 }
265
ScanDone(const bool &)266 void Ethernet::ScanDone(const bool& /*success*/) {
267 NOTREACHED() << __func__ << " is not implented for Ethernet";
268 }
269
TDLSDiscoverResponse(const std::string & peer_address)270 void Ethernet::TDLSDiscoverResponse(const std::string& peer_address) {
271 NOTREACHED() << __func__ << " is not implented for Ethernet";
272 }
273
GetEapProvider()274 EthernetEapProvider* Ethernet::GetEapProvider() {
275 EthernetEapProvider* eap_provider = manager()->ethernet_eap_provider();
276 CHECK(eap_provider);
277 return eap_provider;
278 }
279
GetEapService()280 ServiceConstRefPtr Ethernet::GetEapService() {
281 ServiceConstRefPtr eap_service = GetEapProvider()->service();
282 CHECK(eap_service);
283 return eap_service;
284 }
285
OnEapDetected()286 void Ethernet::OnEapDetected() {
287 is_eap_detected_ = true;
288 eap_listener_->Stop();
289 GetEapProvider()->SetCredentialChangeCallback(
290 this,
291 base::Bind(&Ethernet::TryEapAuthentication,
292 weak_ptr_factory_.GetWeakPtr()));
293 TryEapAuthentication();
294 }
295
StartSupplicant()296 bool Ethernet::StartSupplicant() {
297 if (supplicant_interface_proxy_.get()) {
298 return true;
299 }
300
301 string interface_path;
302 KeyValueStore create_interface_args;
303 create_interface_args.SetString(WPASupplicant::kInterfacePropertyName,
304 link_name());
305 create_interface_args.SetString(WPASupplicant::kInterfacePropertyDriver,
306 WPASupplicant::kDriverWired);
307 create_interface_args.SetString(WPASupplicant::kInterfacePropertyConfigFile,
308 WPASupplicant::kSupplicantConfPath);
309 if (!supplicant_process_proxy_->CreateInterface(create_interface_args,
310 &interface_path)) {
311 // Interface might've already been created, try to retrieve it.
312 if (!supplicant_process_proxy_->GetInterface(link_name(),
313 &interface_path)) {
314 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
315 StopSupplicant();
316 return false;
317 }
318 }
319
320 supplicant_interface_proxy_.reset(
321 control_interface_->CreateSupplicantInterfaceProxy(this, interface_path));
322 supplicant_interface_path_ = interface_path;
323 return true;
324 }
325
StartEapAuthentication()326 bool Ethernet::StartEapAuthentication() {
327 KeyValueStore params;
328 GetEapService()->eap()->PopulateSupplicantProperties(
329 &certificate_file_, ¶ms);
330 params.SetString(WPASupplicant::kNetworkPropertyEapKeyManagement,
331 WPASupplicant::kKeyManagementIeee8021X);
332 params.SetUint(WPASupplicant::kNetworkPropertyEapolFlags, 0);
333 params.SetUint(WPASupplicant::kNetworkPropertyScanSSID, 0);
334
335 service_->ClearEAPCertification();
336 eap_state_handler_.Reset();
337
338 if (!supplicant_network_path_.empty()) {
339 if (!supplicant_interface_proxy_->RemoveNetwork(supplicant_network_path_)) {
340 LOG(ERROR) << "Failed to remove network: " << supplicant_network_path_;
341 return false;
342 }
343 }
344 if (!supplicant_interface_proxy_->AddNetwork(params,
345 &supplicant_network_path_)) {
346 LOG(ERROR) << "Failed to add network";
347 return false;
348 }
349 CHECK(!supplicant_network_path_.empty());
350
351 supplicant_interface_proxy_->SelectNetwork(supplicant_network_path_);
352 supplicant_interface_proxy_->EAPLogon();
353 return true;
354 }
355
StopSupplicant()356 void Ethernet::StopSupplicant() {
357 if (supplicant_interface_proxy_.get()) {
358 supplicant_interface_proxy_->EAPLogoff();
359 }
360 supplicant_interface_proxy_.reset();
361 if (!supplicant_interface_path_.empty()) {
362 if (!supplicant_process_proxy_->RemoveInterface(
363 supplicant_interface_path_)) {
364 LOG(ERROR) << __func__ << ": Failed to remove interface from supplicant.";
365 }
366 }
367 supplicant_network_path_ = "";
368 supplicant_interface_path_ = "";
369 SetIsEapAuthenticated(false);
370 }
371
SetIsEapAuthenticated(bool is_eap_authenticated)372 void Ethernet::SetIsEapAuthenticated(bool is_eap_authenticated) {
373 if (is_eap_authenticated == is_eap_authenticated_) {
374 return;
375 }
376
377 // If our EAP authentication state changes, we have now joined a different
378 // network. Restart the DHCP process and any other connection state.
379 DisconnectFrom(service_.get());
380 ConnectTo(service_.get());
381 is_eap_authenticated_ = is_eap_authenticated;
382 adaptor()->EmitBoolChanged(kEapAuthenticationCompletedProperty,
383 is_eap_authenticated_);
384 }
385
CertificationTask(const string & subject,uint32_t depth)386 void Ethernet::CertificationTask(const string& subject, uint32_t depth) {
387 CHECK(service_) << "Ethernet " << link_name() << " " << __func__
388 << " with no service.";
389 service_->AddEAPCertification(subject, depth);
390 }
391
EAPEventTask(const string & status,const string & parameter)392 void Ethernet::EAPEventTask(const string& status, const string& parameter) {
393 LOG(INFO) << "In " << __func__ << " with status " << status
394 << ", parameter " << parameter;
395 Service::ConnectFailure failure = Service::kFailureUnknown;
396 if (eap_state_handler_.ParseStatus(status, parameter, &failure)) {
397 LOG(INFO) << "EAP authentication succeeded!";
398 SetIsEapAuthenticated(true);
399 } else if (failure != Service::Service::kFailureUnknown) {
400 LOG(INFO) << "EAP authentication failed!";
401 SetIsEapAuthenticated(false);
402 }
403 }
404
SupplicantStateChangedTask(const string & state)405 void Ethernet::SupplicantStateChangedTask(const string& state) {
406 LOG(INFO) << "Supplicant state changed to " << state;
407 }
408
TryEapAuthenticationTask()409 void Ethernet::TryEapAuthenticationTask() {
410 if (!GetEapService()->Is8021xConnectable()) {
411 if (is_eap_authenticated_) {
412 LOG(INFO) << "EAP Service lost 802.1X credentials; "
413 << "terminating EAP authentication.";
414 } else {
415 LOG(INFO) << "EAP Service lacks 802.1X credentials; "
416 << "not doing EAP authentication.";
417 }
418 StopSupplicant();
419 return;
420 }
421
422 if (!is_eap_detected_) {
423 LOG(WARNING) << "EAP authenticator not detected; "
424 << "not doing EAP authentication.";
425 return;
426 }
427 if (!StartSupplicant()) {
428 LOG(ERROR) << "Failed to start supplicant.";
429 return;
430 }
431 StartEapAuthentication();
432 }
433 #endif // DISABLE_WIRED_8021X
434
SetupWakeOnLan()435 void Ethernet::SetupWakeOnLan() {
436 int sock;
437 struct ifreq interface_command;
438 struct ethtool_wolinfo wake_on_lan_command;
439
440 if (link_name().length() >= sizeof(interface_command.ifr_name)) {
441 LOG(WARNING) << "Interface name " << link_name() << " too long: "
442 << link_name().size() << " >= "
443 << sizeof(interface_command.ifr_name);
444 return;
445 }
446
447 sock = sockets_->Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
448 if (sock < 0) {
449 LOG(WARNING) << "Failed to allocate socket: "
450 << sockets_->ErrorString() << ".";
451 return;
452 }
453 ScopedSocketCloser socket_closer(sockets_.get(), sock);
454
455 memset(&interface_command, 0, sizeof(interface_command));
456 memset(&wake_on_lan_command, 0, sizeof(wake_on_lan_command));
457 wake_on_lan_command.cmd = ETHTOOL_SWOL;
458 if (manager()->IsWakeOnLanEnabled()) {
459 wake_on_lan_command.wolopts = WAKE_MAGIC;
460 }
461 interface_command.ifr_data = &wake_on_lan_command;
462 memcpy(interface_command.ifr_name,
463 link_name().data(), link_name().length());
464
465 int res = sockets_->Ioctl(sock, SIOCETHTOOL, &interface_command);
466 if (res < 0) {
467 LOG(WARNING) << "Failed to enable wake-on-lan: "
468 << sockets_->ErrorString() << ".";
469 return;
470 }
471 }
472
ConfigurePPPoEMode(const bool & enable,Error * error)473 bool Ethernet::ConfigurePPPoEMode(const bool& enable, Error* error) {
474 #if defined(DISABLE_PPPOE)
475 if (enable) {
476 LOG(WARNING) << "PPPoE support is not implemented. Ignoring attempt "
477 << "to configure " << link_name();
478 error->Populate(Error::kNotSupported);
479 }
480 return false;
481 #else
482 CHECK(service_);
483
484 EthernetServiceRefPtr service = nullptr;
485 if (enable && service_->technology() != Technology::kPPPoE) {
486 service = CreatePPPoEService();
487 } else if (!enable && service_->technology() == Technology::kPPPoE) {
488 service = CreateEthernetService();
489 } else {
490 return false;
491 }
492
493 CHECK(service);
494 service_->Disconnect(error, nullptr);
495 manager()->DeregisterService(service_);
496 service_ = service;
497 manager()->RegisterService(service_);
498
499 return true;
500 #endif // DISABLE_PPPOE
501 }
502
GetPPPoEMode(Error * error)503 bool Ethernet::GetPPPoEMode(Error* error) {
504 if (service_ == nullptr) {
505 return false;
506 }
507 return service_->technology() == Technology::kPPPoE;
508 }
509
ClearPPPoEMode(Error * error)510 void Ethernet::ClearPPPoEMode(Error* error) {
511 ConfigurePPPoEMode(false, error);
512 }
513
CreateEthernetService()514 EthernetServiceRefPtr Ethernet::CreateEthernetService() {
515 return new EthernetService(control_interface_,
516 dispatcher(),
517 metrics(),
518 manager(),
519 weak_ptr_factory_.GetWeakPtr());
520 }
521
CreatePPPoEService()522 EthernetServiceRefPtr Ethernet::CreatePPPoEService() {
523 return new PPPoEService(control_interface_,
524 dispatcher(),
525 metrics(),
526 manager(),
527 weak_ptr_factory_.GetWeakPtr());
528 }
529
530 } // namespace shill
531