blob: a75c2b4cc011efc9a1d5e4c816db423ab51ac962 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/vpn/new_l2tp_ipsec_driver.h"
#include <memory>
#include <string>
#include <utility>
#include <base/bind.h>
#include <chromeos/dbus/service_constants.h>
#include "shill/error.h"
#include "shill/ipconfig.h"
#include "shill/manager.h"
#include "shill/vpn/ipsec_connection.h"
#include "shill/vpn/l2tp_connection.h"
#include "shill/vpn/vpn_service.h"
namespace shill {
namespace {
const char kL2TPIPsecLeftProtoPortProperty[] = "L2TPIPsec.LeftProtoPort";
const char kL2TPIPsecLengthBitProperty[] = "L2TPIPsec.LengthBit";
const char kL2TPIPsecRefusePapProperty[] = "L2TPIPsec.RefusePap";
const char kL2TPIPsecRequireAuthProperty[] = "L2TPIPsec.RequireAuth";
const char kL2TPIPsecRequireChapProperty[] = "L2TPIPsec.RequireChap";
const char kL2TPIPsecRightProtoPortProperty[] = "L2TPIPsec.RightProtoPort";
std::unique_ptr<IPsecConnection::Config> MakeIPsecConfig(
const KeyValueStore& args) {
auto config = std::make_unique<IPsecConnection::Config>();
config->remote = args.Lookup<std::string>(kProviderHostProperty, "");
if (!args.Lookup<std::string>(kL2TPIPsecPskProperty, "").empty()) {
config->psk = args.Get<std::string>(kL2TPIPsecPskProperty);
}
if (!args.Lookup<Strings>(kL2TPIPsecCaCertPemProperty, Strings{}).empty()) {
config->ca_cert_pem_strings =
args.Get<Strings>(kL2TPIPsecCaCertPemProperty);
}
if (!args.Lookup<std::string>(kL2TPIPsecClientCertIdProperty, "").empty()) {
config->client_cert_id =
args.Get<std::string>(kL2TPIPsecClientCertIdProperty);
}
if (!args.Lookup<std::string>(kL2TPIPsecClientCertSlotProperty, "").empty()) {
config->client_cert_slot =
args.Get<std::string>(kL2TPIPsecClientCertSlotProperty);
}
if (!args.Lookup<std::string>(kL2TPIPsecPinProperty, "").empty()) {
config->client_cert_pin = args.Get<std::string>(kL2TPIPsecPinProperty);
}
// 17 = UDP, 1701 = L2TP.
config->local_proto_port =
args.Lookup<std::string>(kL2TPIPsecLeftProtoPortProperty, "17/1701");
config->remote_proto_port =
args.Lookup<std::string>(kL2TPIPsecRightProtoPortProperty, "17/1701");
return config;
}
std::unique_ptr<L2TPConnection::Config> MakeL2TPConfig(
const KeyValueStore& args) {
auto config = std::make_unique<L2TPConnection::Config>();
// TODO(b/178454141): Add fields for xl2tpd.
// Fields for pppd.
config->lcp_echo = args.Lookup<std::string>(kL2TPIPsecLcpEchoDisabledProperty,
"false") != "true";
config->user = args.Lookup<std::string>(kL2TPIPsecUserProperty, "");
config->password = args.Lookup<std::string>(kL2TPIPsecPasswordProperty, "");
return config;
}
} // namespace
const VPNDriver::Property NewL2TPIPsecDriver::kProperties[] = {
{kL2TPIPsecClientCertIdProperty, 0},
{kL2TPIPsecClientCertSlotProperty, 0},
{kL2TPIPsecPasswordProperty, Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecPinProperty, Property::kCredential},
{kL2TPIPsecPskProperty, Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecUseLoginPasswordProperty, 0},
{kL2TPIPsecUserProperty, 0},
{kProviderHostProperty, 0},
{kProviderTypeProperty, 0},
{kL2TPIPsecCaCertPemProperty, Property::kArray},
{kL2TPIPsecTunnelGroupProperty, 0},
{kL2TPIPsecLeftProtoPortProperty, 0},
{kL2TPIPsecLengthBitProperty, 0},
{kL2TPIPsecRefusePapProperty, 0},
{kL2TPIPsecRequireAuthProperty, 0},
{kL2TPIPsecRequireChapProperty, 0},
{kL2TPIPsecRightProtoPortProperty, 0},
{kL2TPIPsecXauthUserProperty, Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecXauthPasswordProperty,
Property::kCredential | Property::kWriteOnly},
{kL2TPIPsecLcpEchoDisabledProperty, 0},
};
NewL2TPIPsecDriver::NewL2TPIPsecDriver(Manager* manager,
ProcessManager* process_manager)
: VPNDriver(
manager, process_manager, kProperties, base::size(kProperties)) {}
NewL2TPIPsecDriver::~NewL2TPIPsecDriver() {}
base::TimeDelta NewL2TPIPsecDriver::ConnectAsync(EventHandler* handler) {
event_handler_ = handler;
dispatcher()->PostTask(
FROM_HERE, base::BindOnce(&NewL2TPIPsecDriver::StartIPsecConnection,
weak_factory_.GetWeakPtr()));
// TODO(165170125): Use a large value for debugging now.
return base::TimeDelta::FromSeconds(120);
}
void NewL2TPIPsecDriver::StartIPsecConnection() {
if (ipsec_connection_) {
LOG(ERROR) << "The previous IPsecConnection is still running.";
NotifyServiceOfFailure(Service::kFailureInternal);
return;
}
// Callbacks for L2TP will be set and handled in IPsecConnection.
auto l2tp_connection = std::make_unique<L2TPConnection>(
MakeL2TPConfig(*const_args()), /*callbacks=*/nullptr, control_interface(),
manager()->device_info(), manager()->dispatcher(), process_manager());
auto callbacks = std::make_unique<IPsecConnection::Callbacks>(
base::BindRepeating(&NewL2TPIPsecDriver::OnIPsecConnected,
weak_factory_.GetWeakPtr()),
base::BindOnce(&NewL2TPIPsecDriver::OnIPsecFailure,
weak_factory_.GetWeakPtr()),
base::BindOnce(&NewL2TPIPsecDriver::OnIPsecStopped,
weak_factory_.GetWeakPtr()));
ipsec_connection_ = std::make_unique<IPsecConnection>(
MakeIPsecConfig(*const_args()), std::move(callbacks),
std::move(l2tp_connection), manager()->dispatcher(), process_manager());
ipsec_connection_->Connect();
}
void NewL2TPIPsecDriver::Disconnect() {
event_handler_ = nullptr;
if (!ipsec_connection_) {
LOG(ERROR) << "Disconnect() called but IPsecConnection is not running";
return;
}
if (!ipsec_connection_->IsConnectingOrConnected()) {
LOG(ERROR) << "Disconnect() called but IPsecConnection is in "
<< ipsec_connection_->state() << " state";
return;
}
ipsec_connection_->Disconnect();
}
IPConfig::Properties NewL2TPIPsecDriver::GetIPProperties() const {
return ip_properties_;
}
std::string NewL2TPIPsecDriver::GetProviderType() const {
return kProviderL2tpIpsec;
}
void NewL2TPIPsecDriver::OnConnectTimeout() {
LOG(INFO) << "Connect timeout";
if (!ipsec_connection_) {
LOG(ERROR)
<< "OnConnectTimeout() called but IPsecConnection is not running";
return;
}
if (!ipsec_connection_->IsConnectingOrConnected()) {
LOG(ERROR) << "OnConnectTimeout() called but IPsecConnection is in "
<< ipsec_connection_->state() << " state";
return;
}
ipsec_connection_->Disconnect();
NotifyServiceOfFailure(Service::kFailureConnect);
}
void NewL2TPIPsecDriver::OnBeforeSuspend(const ResultCallback& callback) {
if (ipsec_connection_ && ipsec_connection_->IsConnectingOrConnected()) {
ipsec_connection_->Disconnect();
}
callback.Run(Error(Error::kSuccess));
}
void NewL2TPIPsecDriver::OnDefaultPhysicalServiceEvent(
DefaultPhysicalServiceEvent event) {
if (!ipsec_connection_ || !ipsec_connection_->IsConnectingOrConnected()) {
return;
}
switch (event) {
case kDefaultPhysicalServiceUp:
return;
case kDefaultPhysicalServiceDown:
ipsec_connection_->Disconnect();
return;
case kDefaultPhysicalServiceChanged:
ipsec_connection_->Disconnect();
return;
default:
NOTREACHED();
}
}
void NewL2TPIPsecDriver::NotifyServiceOfFailure(
Service::ConnectFailure failure) {
LOG(ERROR) << "Driver failure due to "
<< Service::ConnectFailureToString(failure);
if (event_handler_) {
event_handler_->OnDriverFailure(failure, Service::kErrorDetailsNone);
event_handler_ = nullptr;
}
}
void NewL2TPIPsecDriver::OnIPsecConnected(
const std::string& link_name,
int interface_index,
const IPConfig::Properties& ip_properties) {
if (!event_handler_) {
LOG(ERROR) << "OnIPsecConnected() triggered in illegal service state";
return;
}
ip_properties_ = ip_properties;
event_handler_->OnDriverConnected(link_name, interface_index);
}
void NewL2TPIPsecDriver::OnIPsecFailure(Service::ConnectFailure failure) {
NotifyServiceOfFailure(failure);
}
void NewL2TPIPsecDriver::OnIPsecStopped() {
ipsec_connection_ = nullptr;
}
} // namespace shill