Home | History | Annotate | Download | only in ethernet
      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;
     66 static string ObjectID(Ethernet* e) { return e->GetRpcIdentifier(); }
     67 }
     68 
     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 
    118 Ethernet::~Ethernet() {
    119 }
    120 
    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 
    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 
    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 
    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 
    194 bool Ethernet::Save(StoreInterface* storage) {
    195   const string id = GetStorageIdentifier();
    196   storage->SetBool(id, kPPPoEProperty, GetPPPoEMode(nullptr));
    197   return true;
    198 }
    199 
    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 
    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)
    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 
    231 void Ethernet::BSSAdded(const string& path, const KeyValueStore& properties) {
    232   NOTREACHED() << __func__ << " is not implemented for Ethernet";
    233 }
    234 
    235 void Ethernet::BSSRemoved(const string& path) {
    236   NOTREACHED() << __func__ << " is not implemented for Ethernet";
    237 }
    238 
    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 
    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 
    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 
    266 void Ethernet::ScanDone(const bool& /*success*/) {
    267   NOTREACHED() << __func__ << " is not implented for Ethernet";
    268 }
    269 
    270 void Ethernet::TDLSDiscoverResponse(const std::string& peer_address) {
    271   NOTREACHED() << __func__ << " is not implented for Ethernet";
    272 }
    273 
    274 EthernetEapProvider* Ethernet::GetEapProvider() {
    275   EthernetEapProvider* eap_provider = manager()->ethernet_eap_provider();
    276   CHECK(eap_provider);
    277   return eap_provider;
    278 }
    279 
    280 ServiceConstRefPtr Ethernet::GetEapService() {
    281   ServiceConstRefPtr eap_service = GetEapProvider()->service();
    282   CHECK(eap_service);
    283   return eap_service;
    284 }
    285 
    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 
    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 
    326 bool Ethernet::StartEapAuthentication() {
    327   KeyValueStore params;
    328   GetEapService()->eap()->PopulateSupplicantProperties(
    329       &certificate_file_, &params);
    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 
    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 
    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 
    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 
    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 
    405 void Ethernet::SupplicantStateChangedTask(const string& state) {
    406   LOG(INFO) << "Supplicant state changed to " << state;
    407 }
    408 
    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 
    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 
    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 
    503 bool Ethernet::GetPPPoEMode(Error* error) {
    504   if (service_ == nullptr) {
    505     return false;
    506   }
    507   return service_->technology() == Technology::kPPPoE;
    508 }
    509 
    510 void Ethernet::ClearPPPoEMode(Error* error) {
    511   ConfigurePPPoEMode(false, error);
    512 }
    513 
    514 EthernetServiceRefPtr Ethernet::CreateEthernetService() {
    515   return new EthernetService(control_interface_,
    516                              dispatcher(),
    517                              metrics(),
    518                              manager(),
    519                              weak_ptr_factory_.GetWeakPtr());
    520 }
    521 
    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