普通文本  |  270行  |  8.36 KB

//
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "shill/pppoe/pppoe_service.h"

#include <algorithm>
#include <map>
#include <memory>
#include <string>

#include <base/callback.h>
#include <base/logging.h>
#if defined(__ANDROID__)
#include <dbus/service_constants.h>
#else
#include <chromeos/dbus/service_constants.h>
#endif  // __ANDROID__

#include "shill/control_interface.h"
#include "shill/ethernet/ethernet.h"
#include "shill/event_dispatcher.h"
#include "shill/manager.h"
#include "shill/metrics.h"
#include "shill/ppp_daemon.h"
#include "shill/ppp_device.h"
#include "shill/ppp_device_factory.h"
#include "shill/process_manager.h"
#include "shill/store_interface.h"

using base::StringPrintf;
using std::map;
using std::string;
using std::unique_ptr;

namespace shill {

const int PPPoEService::kDefaultLCPEchoInterval = 30;
const int PPPoEService::kDefaultLCPEchoFailure = 3;
const int PPPoEService::kDefaultMaxAuthFailure = 3;

PPPoEService::PPPoEService(ControlInterface* control_interface,
                           EventDispatcher* dispatcher,
                           Metrics* metrics,
                           Manager* manager,
                           base::WeakPtr<Ethernet> ethernet)
    : EthernetService(control_interface, dispatcher, metrics, manager,
                      Technology::kPPPoE, ethernet),
      control_interface_(control_interface),
      ppp_device_factory_(PPPDeviceFactory::GetInstance()),
      process_manager_(ProcessManager::GetInstance()),
      lcp_echo_interval_(kDefaultLCPEchoInterval),
      lcp_echo_failure_(kDefaultLCPEchoFailure),
      max_auth_failure_(kDefaultMaxAuthFailure),
      authenticating_(false),
      weak_ptr_factory_(this) {
  PropertyStore* store = this->mutable_store();
  store->RegisterString(kPPPoEUsernameProperty, &username_);
  store->RegisterString(kPPPoEPasswordProperty, &password_);
  store->RegisterInt32(kPPPoELCPEchoIntervalProperty, &lcp_echo_interval_);
  store->RegisterInt32(kPPPoELCPEchoFailureProperty, &lcp_echo_failure_);
  store->RegisterInt32(kPPPoEMaxAuthFailureProperty, &max_auth_failure_);

  set_friendly_name("PPPoE");
  SetConnectable(true);
  SetAutoConnect(true);
  NotifyPropertyChanges();
}

PPPoEService::~PPPoEService() {}

void PPPoEService::Connect(Error* error, const char* reason) {
  Service::Connect(error, reason);

  CHECK(ethernet());

  if (!ethernet()->link_up()) {
    Error::PopulateAndLog(
        FROM_HERE, error, Error::kOperationFailed, StringPrintf(
            "PPPoE Service %s does not have Ethernet link.",
            unique_name().c_str()));
    return;
  }

  if (IsConnected()) {
    Error::PopulateAndLog(FROM_HERE, error, Error::kAlreadyConnected,
                          StringPrintf("PPPoE service %s already connected.",
                                       unique_name().c_str()));
    return;
  }

  if (IsConnecting()) {
    Error::PopulateAndLog(FROM_HERE, error, Error::kInProgress,
                          StringPrintf("PPPoE service %s already connecting.",
                                       unique_name().c_str()));
    return;
  }

  PPPDaemon::DeathCallback callback(base::Bind(&PPPoEService::OnPPPDied,
                                               weak_ptr_factory_.GetWeakPtr()));

  PPPDaemon::Options options;
  options.no_detach = true;
  options.no_default_route = true;
  options.use_peer_dns = true;
  options.use_pppoe_plugin = true;
  options.lcp_echo_interval = lcp_echo_interval_;
  options.lcp_echo_failure = lcp_echo_failure_;
  options.max_fail = max_auth_failure_;
  options.use_ipv6 = true;

  pppd_ = PPPDaemon::Start(
      control_interface_, process_manager_, weak_ptr_factory_.GetWeakPtr(),
      options, ethernet()->link_name(), callback, error);
  if (pppd_ == nullptr) {
    Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError,
                          StringPrintf("PPPoE service %s can't start pppd.",
                                       unique_name().c_str()));
    return;
  }

  SetState(Service::kStateAssociating);
}

void PPPoEService::Disconnect(Error* error, const char* reason) {
  EthernetService::Disconnect(error, reason);
  if (ppp_device_) {
    ppp_device_->DropConnection();
  } else {
    // If no PPPDevice has been associated with this service then nothing will
    // drive this service's transition into the idle state.  This must be forced
    // here to ensure that the service is not left in any intermediate state.
    SetState(Service::kStateIdle);
  }
  ppp_device_ = nullptr;
  pppd_.reset();
  manager()->OnInnerDevicesChanged();
}

bool PPPoEService::Load(StoreInterface* storage) {
  if (!Service::Load(storage)) {
    return false;
  }

  const string id = GetStorageIdentifier();
  storage->GetString(id, kPPPoEUsernameProperty, &username_);
  storage->GetString(id, kPPPoEPasswordProperty, &password_);
  storage->GetInt(id, kPPPoELCPEchoIntervalProperty, &lcp_echo_interval_);
  storage->GetInt(id, kPPPoELCPEchoFailureProperty, &lcp_echo_failure_);
  storage->GetInt(id, kPPPoEMaxAuthFailureProperty, &max_auth_failure_);

  return true;
}

bool PPPoEService::Save(StoreInterface* storage) {
  if (!Service::Save(storage)) {
    return false;
  }

  const string id = GetStorageIdentifier();
  storage->SetString(id, kPPPoEUsernameProperty, username_);
  storage->SetString(id, kPPPoEPasswordProperty, password_);
  storage->SetInt(id, kPPPoELCPEchoIntervalProperty, lcp_echo_interval_);
  storage->SetInt(id, kPPPoELCPEchoFailureProperty, lcp_echo_failure_);
  storage->SetInt(id, kPPPoEMaxAuthFailureProperty, max_auth_failure_);

  return true;
}

bool PPPoEService::Unload() {
  username_.clear();
  password_.clear();
  return Service::Unload();
}

string PPPoEService::GetInnerDeviceRpcIdentifier() const {
  return ppp_device_ ? ppp_device_->GetRpcIdentifier() : "";
}

void PPPoEService::GetLogin(string* user, string* password) {
  CHECK(user && password);
  *user = username_;
  *password = password_;
}

void PPPoEService::Notify(const string& reason,
                          const map<string, string>& dict) {
  if (reason == kPPPReasonAuthenticating) {
    OnPPPAuthenticating();
  } else if (reason == kPPPReasonAuthenticated) {
    OnPPPAuthenticated();
  } else if (reason == kPPPReasonConnect) {
    OnPPPConnected(dict);
  } else if (reason == kPPPReasonDisconnect) {
    OnPPPDisconnected();
  } else {
    NOTREACHED();
  }
}

void PPPoEService::OnPPPDied(pid_t pid, int exit) {
  OnPPPDisconnected();
}

void PPPoEService::OnPPPAuthenticating() {
  authenticating_ = true;
}

void PPPoEService::OnPPPAuthenticated() {
  authenticating_ = false;
}

void PPPoEService::OnPPPConnected(const map<string, string>& params) {
  const string interface_name = PPPDevice::GetInterfaceName(params);

  DeviceInfo* device_info = manager()->device_info();
  const int interface_index = device_info->GetIndex(interface_name);
  if (interface_index < 0) {
    NOTIMPLEMENTED() << ": No device info for " << interface_name;
    return;
  }

  if (ppp_device_) {
    ppp_device_->SelectService(nullptr);
  }

  ppp_device_ = ppp_device_factory_->CreatePPPDevice(
      control_interface_, dispatcher(), metrics(), manager(), interface_name,
      interface_index);
  device_info->RegisterDevice(ppp_device_);
  ppp_device_->SetEnabled(true);
  ppp_device_->SelectService(this);
  ppp_device_->UpdateIPConfigFromPPP(params, false);
#ifndef DISABLE_DHCPV6
  // Acquire DHCPv6 configurations through the PPPoE (virtual) interface
  // if it is enabled for DHCPv6.
  if (manager()->IsDHCPv6EnabledForDevice(ppp_device_->link_name())) {
    ppp_device_->AcquireIPv6Config();
  }
#endif
  manager()->OnInnerDevicesChanged();
}

void PPPoEService::OnPPPDisconnected() {
  pppd_.release()->DestroyLater(dispatcher());

  Error unused_error;
  Disconnect(&unused_error, __func__);

  if (authenticating_) {
    SetFailure(Service::kFailurePPPAuth);
  } else {
    SetFailure(Service::kFailureUnknown);
  }
}

}  // namespace shill