2020-08-21 13:15:48 +02:00
|
|
|
// OpenVPN -- An application to securely tunnel IP networks
|
|
|
|
// over a single port, with support for SSL/TLS-based
|
|
|
|
// session authentication and key exchange,
|
|
|
|
// packet encryption, packet authentication, and
|
|
|
|
// packet compression.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2012-2020 OpenVPN Inc.
|
|
|
|
// Copyright (C) 2020-2020 Lev Stipakov <lev@openvpn.net>
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License Version 3
|
|
|
|
// as published by the Free Software Foundation.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program in the COPYING file.
|
|
|
|
// If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
// tun/transport client for ovpn-dco
|
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
#include <openvpn/dco/dcocli.hpp>
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
class OvpnDcoClient : public Client,
|
|
|
|
public KoRekey::Receiver,
|
2021-06-08 01:40:38 +02:00
|
|
|
public TransportClientParent,
|
|
|
|
public SessionStats::DCOTransportSource {
|
2020-08-21 13:15:48 +02:00
|
|
|
friend class ClientConfig;
|
2020-08-21 15:06:51 +02:00
|
|
|
friend class GeNL;
|
2020-08-21 13:15:48 +02:00
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
OPENVPN_EXCEPTION(dcocli_error);
|
|
|
|
|
2020-08-21 13:15:48 +02:00
|
|
|
typedef RCPtr<OvpnDcoClient> Ptr;
|
2020-08-21 15:06:51 +02:00
|
|
|
typedef GeNL<OvpnDcoClient *> GeNLImpl;
|
2020-08-21 13:15:48 +02:00
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
struct PacketFrom {
|
|
|
|
typedef std::unique_ptr<PacketFrom> SPtr;
|
|
|
|
BufferAllocated buf;
|
|
|
|
};
|
|
|
|
|
2020-08-21 13:15:48 +02:00
|
|
|
public:
|
2021-05-25 05:47:32 +02:00
|
|
|
static bool available() { return GeNLImpl::available(); }
|
|
|
|
|
2020-08-21 13:15:48 +02:00
|
|
|
virtual void tun_start(const OptionList &opt, TransportClient &transcli,
|
2020-08-21 15:06:51 +02:00
|
|
|
CryptoDCSettings &dc_settings) override {
|
2021-02-16 22:26:18 +01:00
|
|
|
// extract peer ID from pushed options
|
|
|
|
try {
|
|
|
|
const Option *o = opt.get_ptr("peer-id");
|
2021-02-23 01:10:17 +01:00
|
|
|
if (o) {
|
|
|
|
bool status = parse_number_validate<uint32_t>(
|
|
|
|
o->get(1, 16), 16, 0, OVPN_PEER_ID_UNDEF - 1, &peer_id);
|
|
|
|
if (!status)
|
|
|
|
OPENVPN_THROW(dcocli_error, "Parse/range issue with pushed peer-id");
|
|
|
|
} else {
|
|
|
|
OPENVPN_THROW(dcocli_error, "No peer-id pushed by server");
|
2021-02-16 22:26:18 +01:00
|
|
|
}
|
2021-02-23 01:10:17 +01:00
|
|
|
} catch (const std::exception &e) {
|
2021-02-16 22:26:18 +01:00
|
|
|
OPENVPN_THROW(dcocli_error, "Cannot extract peer-id: " << e.what());
|
|
|
|
}
|
|
|
|
|
2020-08-21 15:59:11 +02:00
|
|
|
// notify parent
|
|
|
|
tun_parent->tun_pre_tun_config();
|
|
|
|
|
|
|
|
// parse pushed options
|
|
|
|
TunBuilderCapture::Ptr po;
|
|
|
|
TunBuilderBase *builder;
|
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
if (config->builder) {
|
|
|
|
builder = config->builder;
|
|
|
|
} else {
|
|
|
|
po.reset(new TunBuilderCapture());
|
|
|
|
builder = po.get();
|
|
|
|
}
|
2020-08-21 15:59:11 +02:00
|
|
|
|
|
|
|
TunProp::configure_builder(
|
|
|
|
builder, state.get(), config->transport.stats.get(),
|
|
|
|
server_endpoint_addr(), config->tun.tun_prop, opt, nullptr, false);
|
|
|
|
|
|
|
|
if (po)
|
|
|
|
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());
|
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
if (config->builder) {
|
|
|
|
config->builder->tun_builder_dco_establish();
|
|
|
|
} else {
|
|
|
|
ActionList::Ptr add_cmds = new ActionList();
|
|
|
|
remove_cmds.reset(new ActionListReversed());
|
2020-08-21 15:59:11 +02:00
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
std::vector<IP::Route> rtvec;
|
2020-08-21 15:59:11 +02:00
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
TUN_LINUX::tun_config(config->dev_name, *po, &rtvec, *add_cmds,
|
|
|
|
*remove_cmds, true);
|
2020-08-21 15:59:11 +02:00
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
// execute commands to bring up interface
|
|
|
|
add_cmds->execute_log();
|
|
|
|
}
|
2020-08-21 15:59:11 +02:00
|
|
|
|
2020-08-24 10:21:57 +02:00
|
|
|
// Add a hook so ProtoContext will call back to
|
|
|
|
// rekey() on rekey ops.
|
|
|
|
dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(
|
|
|
|
dc_settings.factory(), this, config->transport.frame)));
|
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
// add peer in ovpn-dco
|
2021-02-23 01:10:17 +01:00
|
|
|
add_peer(peer_id, state->vpn_ip4_gw.to_ipv4_zero(),
|
|
|
|
state->vpn_ip6_gw.to_ipv6_zero());
|
2020-08-21 15:59:11 +02:00
|
|
|
// signal that we are connected
|
2020-08-21 15:06:51 +02:00
|
|
|
tun_parent->tun_connected();
|
|
|
|
}
|
2020-08-21 13:15:48 +02:00
|
|
|
|
|
|
|
virtual std::string tun_name() const override { return "ovpn-dco"; }
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual IP::Addr server_endpoint_addr() const override {
|
2021-02-16 22:26:18 +01:00
|
|
|
if (transport)
|
|
|
|
return transport->server_endpoint_addr();
|
|
|
|
else
|
|
|
|
return IP::Addr();
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual unsigned short server_endpoint_port() const override {
|
2021-02-16 22:26:18 +01:00
|
|
|
if (transport)
|
|
|
|
return transport->server_endpoint_port();
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual Protocol transport_protocol() const override {
|
2021-02-16 22:26:18 +01:00
|
|
|
return transport->transport_protocol();
|
|
|
|
}
|
|
|
|
|
2020-11-02 11:53:47 +01:00
|
|
|
virtual void transport_start() override {
|
2021-02-16 22:26:18 +01:00
|
|
|
TransportClientFactory::Ptr transport_factory;
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
if (!config->transport.protocol.is_tcp()) {
|
|
|
|
UDPTransport::ClientConfig::Ptr udpconf =
|
|
|
|
UDPTransport::ClientConfig::new_obj();
|
2021-02-16 22:26:18 +01:00
|
|
|
udpconf->remote_list = config->transport.remote_list;
|
|
|
|
udpconf->frame = config->transport.frame;
|
|
|
|
udpconf->stats = config->transport.stats;
|
|
|
|
udpconf->socket_protect = config->transport.socket_protect;
|
|
|
|
udpconf->server_addr_float = config->transport.server_addr_float;
|
|
|
|
transport_factory = udpconf;
|
2021-02-23 01:10:17 +01:00
|
|
|
} else {
|
|
|
|
TCPTransport::ClientConfig::Ptr tcpconf =
|
|
|
|
TCPTransport::ClientConfig::new_obj();
|
2021-02-16 22:26:18 +01:00
|
|
|
tcpconf->remote_list = config->transport.remote_list;
|
|
|
|
tcpconf->frame = config->transport.frame;
|
|
|
|
tcpconf->stats = config->transport.stats;
|
|
|
|
tcpconf->socket_protect = config->transport.socket_protect;
|
|
|
|
transport_factory = tcpconf;
|
|
|
|
}
|
|
|
|
|
2021-06-08 01:40:38 +02:00
|
|
|
config->transport.stats->dco_configure(this);
|
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
transport = transport_factory->new_transport_client_obj(io_context, this);
|
|
|
|
transport->transport_start();
|
2020-11-02 11:53:47 +01:00
|
|
|
}
|
2020-08-21 13:15:48 +02:00
|
|
|
|
|
|
|
virtual bool transport_send_const(const Buffer &buf) override {
|
2021-02-16 22:26:18 +01:00
|
|
|
if (peer_id == OVPN_PEER_ID_UNDEF)
|
|
|
|
return transport->transport_send_const(buf);
|
|
|
|
|
2021-02-23 16:25:49 +01:00
|
|
|
if (config->builder) {
|
|
|
|
Buffer tmp(buf);
|
|
|
|
tmp.prepend(&peer_id, sizeof(peer_id));
|
|
|
|
pipe->write_some(tmp.const_buffer());
|
|
|
|
} else {
|
2021-02-16 22:26:18 +01:00
|
|
|
genl->send_data(peer_id, buf.c_data(), buf.size());
|
2021-02-23 16:25:49 +01:00
|
|
|
}
|
2021-02-16 22:26:18 +01:00
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
return true;
|
|
|
|
}
|
2020-08-21 13:15:48 +02:00
|
|
|
|
2021-02-23 16:25:49 +01:00
|
|
|
virtual bool transport_send(BufferAllocated &buf) override {
|
|
|
|
OPENVPN_THROW(dcocli_error,
|
|
|
|
"Non-const send expected for data channel only, but "
|
|
|
|
"ovpndcocli is not expected to handle data packets");
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
void get_remote_sockaddr(struct sockaddr_storage &sa, socklen_t &salen) {
|
2021-02-16 22:26:18 +01:00
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
|
|
|
|
struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa;
|
|
|
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa;
|
|
|
|
|
|
|
|
IP::Addr remote_addr = transport->server_endpoint_addr();
|
|
|
|
if (remote_addr.version() == IP::Addr::V4) {
|
|
|
|
salen = sizeof(*sa4);
|
2021-02-23 01:10:17 +01:00
|
|
|
*sa4 =
|
|
|
|
remote_addr.to_ipv4().to_sockaddr(transport->server_endpoint_port());
|
2021-02-16 22:26:18 +01:00
|
|
|
} else {
|
|
|
|
salen = sizeof(*sa6);
|
2021-02-23 01:10:17 +01:00
|
|
|
*sa6 =
|
|
|
|
remote_addr.to_ipv6().to_sockaddr(transport->server_endpoint_port());
|
2021-02-16 22:26:18 +01:00
|
|
|
}
|
2020-11-02 11:53:47 +01:00
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
void del_peer(uint32_t peer_id) {
|
2021-02-16 22:26:18 +01:00
|
|
|
TunBuilderBase *tb = config->builder;
|
|
|
|
if (tb) {
|
|
|
|
tb->tun_builder_dco_del_peer(peer_id);
|
2020-08-21 15:06:51 +02:00
|
|
|
return;
|
2021-02-16 22:26:18 +01:00
|
|
|
}
|
2020-08-21 15:06:51 +02:00
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
genl->del_peer(peer_id);
|
|
|
|
}
|
2020-11-02 17:06:02 +01:00
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
void add_peer(uint32_t peer_id, IPv4::Addr ipv4, IPv6::Addr ipv6) {
|
2021-02-16 22:26:18 +01:00
|
|
|
struct sockaddr_storage sa;
|
|
|
|
socklen_t salen;
|
2020-08-21 15:06:51 +02:00
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
get_remote_sockaddr(sa, salen);
|
2020-08-24 13:49:37 +02:00
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
TunBuilderBase *tb = config->builder;
|
|
|
|
if (tb) {
|
2021-02-23 01:10:17 +01:00
|
|
|
tb->tun_builder_dco_new_peer(peer_id, transport->native_handle(),
|
|
|
|
(struct sockaddr *)&sa, salen, ipv4, ipv6);
|
2020-08-21 15:06:51 +02:00
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
queue_read_pipe(nullptr);
|
|
|
|
return;
|
|
|
|
}
|
2020-08-24 13:49:37 +02:00
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
genl->new_peer(peer_id, transport->native_handle(), (struct sockaddr *)&sa,
|
|
|
|
salen, ipv4, ipv6);
|
2021-02-16 22:26:18 +01:00
|
|
|
}
|
2020-08-24 13:49:37 +02:00
|
|
|
|
2021-06-08 01:40:38 +02:00
|
|
|
void update_peer_stats(uint32_t peer_id, bool sync) {
|
|
|
|
const SessionStats::DCOTransportSource::Data old_stats = last_stats;
|
|
|
|
|
|
|
|
if (peer_id == OVPN_PEER_ID_UNDEF)
|
|
|
|
return;
|
|
|
|
|
|
|
|
TunBuilderBase *tb = config->builder;
|
|
|
|
if (tb) {
|
|
|
|
tb->tun_builder_dco_get_peer(peer_id, sync);
|
|
|
|
queue_read_pipe(nullptr);
|
|
|
|
} else {
|
|
|
|
genl->get_peer(peer_id, sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
last_delta = last_stats - old_stats;
|
|
|
|
}
|
|
|
|
|
2021-03-18 18:10:30 +01:00
|
|
|
virtual void resolve_callback(const openvpn_io::error_code &error,
|
|
|
|
results_type results) override {}
|
2020-08-21 13:15:48 +02:00
|
|
|
|
|
|
|
virtual void stop_() override {
|
|
|
|
if (!halt) {
|
2021-06-08 01:40:38 +02:00
|
|
|
/* update stats before deleting peer in kernelspace */
|
|
|
|
update_peer_stats(peer_id, true);
|
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
halt = true;
|
2020-08-24 13:49:37 +02:00
|
|
|
|
|
|
|
if (config->builder) {
|
|
|
|
config->builder->tun_builder_teardown(true);
|
|
|
|
if (pipe)
|
|
|
|
pipe->close();
|
|
|
|
} else {
|
|
|
|
std::ostringstream os;
|
|
|
|
if (genl)
|
|
|
|
genl->stop();
|
2021-02-16 22:26:18 +01:00
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
int res = TunNetlink::iface_del(os, config->dev_name);
|
|
|
|
if (res != 0) {
|
|
|
|
OPENVPN_LOG("ovpndcocli: error deleting iface ovpn:" << os.str());
|
|
|
|
}
|
2020-08-21 13:15:48 +02:00
|
|
|
}
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
if (transport)
|
2021-02-23 01:10:17 +01:00
|
|
|
transport->stop();
|
2020-08-21 13:15:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-24 10:21:57 +02:00
|
|
|
virtual void rekey(const CryptoDCInstance::RekeyType rktype,
|
|
|
|
const KoRekey::Info &rkinfo) override {
|
|
|
|
if (halt)
|
|
|
|
return;
|
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
if (config->builder)
|
|
|
|
rekey_impl_tb(rktype, rkinfo);
|
|
|
|
else
|
|
|
|
rekey_impl(rktype, rkinfo);
|
2020-08-24 10:21:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void rekey_impl(const CryptoDCInstance::RekeyType rktype,
|
|
|
|
const KoRekey::Info &rkinfo) {
|
|
|
|
KoRekey::OvpnDcoKey key(rktype, rkinfo);
|
|
|
|
auto kc = key();
|
|
|
|
|
|
|
|
switch (rktype) {
|
|
|
|
case CryptoDCInstance::ACTIVATE_PRIMARY:
|
|
|
|
genl->new_key(OVPN_KEY_SLOT_PRIMARY, kc);
|
2020-08-24 10:48:44 +02:00
|
|
|
|
|
|
|
handle_keepalive();
|
2020-08-24 10:21:57 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::NEW_SECONDARY:
|
2021-02-16 22:26:18 +01:00
|
|
|
|
2020-08-24 10:21:57 +02:00
|
|
|
genl->new_key(OVPN_KEY_SLOT_SECONDARY, kc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
genl->swap_keys(peer_id);
|
2020-08-24 10:21:57 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::DEACTIVATE_SECONDARY:
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
genl->del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
|
2020-08-24 10:21:57 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::DEACTIVATE_ALL:
|
|
|
|
// TODO: deactivate all keys
|
|
|
|
OPENVPN_LOG("ovpndcocli: deactivate all keys");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
void rekey_impl_tb(const CryptoDCInstance::RekeyType rktype,
|
|
|
|
const KoRekey::Info &rkinfo) {
|
|
|
|
KoRekey::OvpnDcoKey key(rktype, rkinfo);
|
|
|
|
auto kc = key();
|
|
|
|
|
|
|
|
TunBuilderBase *tb = config->builder;
|
|
|
|
|
|
|
|
switch (rktype) {
|
|
|
|
case CryptoDCInstance::ACTIVATE_PRIMARY:
|
|
|
|
tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_PRIMARY, kc);
|
|
|
|
|
|
|
|
handle_keepalive();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::NEW_SECONDARY:
|
|
|
|
tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_SECONDARY, kc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
|
2021-02-16 22:26:18 +01:00
|
|
|
tb->tun_builder_dco_swap_keys(peer_id);
|
2020-08-24 13:49:37 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::DEACTIVATE_SECONDARY:
|
2021-02-16 22:26:18 +01:00
|
|
|
tb->tun_builder_dco_del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
|
2020-08-24 13:49:37 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CryptoDCInstance::DEACTIVATE_ALL:
|
|
|
|
// TODO: deactivate all keys
|
|
|
|
OPENVPN_LOG("ovpndcocli: deactivate all keys");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual void transport_recv(BufferAllocated &buf) override {
|
2021-02-16 22:26:18 +01:00
|
|
|
transport_parent->transport_recv(buf);
|
|
|
|
}
|
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
bool tun_read_handler(BufferAllocated &buf) {
|
|
|
|
if (halt)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int8_t cmd = -1;
|
|
|
|
buf.read(&cmd, sizeof(cmd));
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case OVPN_CMD_PACKET:
|
|
|
|
transport_parent->transport_recv(buf);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OVPN_CMD_DEL_PEER: {
|
2021-02-16 22:26:18 +01:00
|
|
|
uint32_t peer_id;
|
|
|
|
buf.read(&peer_id, sizeof(peer_id));
|
|
|
|
uint8_t reason;
|
2020-08-21 15:06:51 +02:00
|
|
|
buf.read(&reason, sizeof(reason));
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
Error::Type err;
|
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
switch (reason) {
|
|
|
|
case OVPN_DEL_PEER_REASON_EXPIRED:
|
2021-02-23 01:10:17 +01:00
|
|
|
err = Error::TRANSPORT_ERROR;
|
|
|
|
os << "keepalive timeout";
|
|
|
|
break;
|
2020-08-21 15:06:51 +02:00
|
|
|
|
2020-11-02 09:52:36 +01:00
|
|
|
case OVPN_DEL_PEER_REASON_TRANSPORT_ERROR:
|
2021-02-23 01:10:17 +01:00
|
|
|
err = Error::TRANSPORT_ERROR;
|
|
|
|
os << "transport error";
|
|
|
|
break;
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
case OVPN_DEL_PEER_REASON_TEARDOWN:
|
2021-02-23 01:10:17 +01:00
|
|
|
err = Error::TRANSPORT_ERROR;
|
|
|
|
os << "peer deleted, id=" << peer_id << ", teardown";
|
|
|
|
break;
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
case OVPN_DEL_PEER_REASON_USERSPACE:
|
2021-02-23 01:10:17 +01:00
|
|
|
// volountary delete - do not stop client
|
|
|
|
OPENVPN_LOG("peer deleted, id=" << peer_id
|
|
|
|
<< ", requested by userspace");
|
|
|
|
peer_id = OVPN_PEER_ID_UNDEF;
|
|
|
|
return true;
|
2020-11-02 09:52:36 +01:00
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
default:
|
2021-02-23 01:10:17 +01:00
|
|
|
err = Error::TUN_HALT;
|
|
|
|
os << "peer deleted, id=" << peer_id << ", reason=" << reason;
|
|
|
|
break;
|
2020-08-21 15:06:51 +02:00
|
|
|
}
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
stop_();
|
|
|
|
transport_parent->transport_error(err, os.str());
|
2020-08-21 15:06:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-06-08 01:39:04 +02:00
|
|
|
case OVPN_CMD_GET_PEER: {
|
|
|
|
struct OvpnDcoPeer peer;
|
|
|
|
buf.read(&peer, sizeof(peer));
|
|
|
|
|
|
|
|
last_stats = SessionStats::DCOTransportSource::Data(peer.rx_bytes, peer.tx_bytes);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
case -1:
|
|
|
|
// consider all errors as fatal
|
|
|
|
stop_();
|
|
|
|
transport_parent->transport_error(Error::TUN_HALT, buf_to_string(buf));
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
OPENVPN_LOG("Unknown ovpn-dco cmd " << cmd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-16 22:26:18 +01:00
|
|
|
virtual void transport_needs_send() override {
|
|
|
|
transport_parent->transport_needs_send();
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual void transport_error(const Error::Type fatal_err,
|
|
|
|
const std::string &err_text) override {
|
2021-02-16 22:26:18 +01:00
|
|
|
transport_parent->transport_error(fatal_err, err_text);
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual void proxy_error(const Error::Type fatal_err,
|
|
|
|
const std::string &err_text) override {
|
2021-02-16 22:26:18 +01:00
|
|
|
transport_parent->proxy_error(fatal_err, err_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool transport_is_openvpn_protocol() override {
|
|
|
|
return transport_parent->transport_is_openvpn_protocol();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void transport_pre_resolve() override {
|
|
|
|
transport_parent->transport_pre_resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void transport_wait_proxy() override {
|
|
|
|
transport_parent->transport_wait_proxy();
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual void transport_wait() override { transport_parent->transport_wait(); }
|
2021-02-16 22:26:18 +01:00
|
|
|
|
|
|
|
virtual void transport_connecting() override {
|
|
|
|
transport_parent->transport_connecting();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool is_keepalive_enabled() const override {
|
|
|
|
return transport_parent->is_keepalive_enabled();
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
virtual void disable_keepalive(unsigned int &keepalive_ping,
|
|
|
|
unsigned int &keepalive_timeout) override {
|
2021-02-16 22:26:18 +01:00
|
|
|
transport_parent->disable_keepalive(keepalive_ping, keepalive_timeout);
|
|
|
|
}
|
|
|
|
|
2020-08-21 13:15:48 +02:00
|
|
|
private:
|
|
|
|
OvpnDcoClient(openvpn_io::io_context &io_context_arg,
|
|
|
|
ClientConfig *config_arg, TransportClientParent *parent_arg)
|
2021-02-23 01:10:17 +01:00
|
|
|
: Client(io_context_arg, config_arg, parent_arg) {
|
2021-02-16 22:26:18 +01:00
|
|
|
TunBuilderBase *tb = config->builder;
|
|
|
|
if (tb) {
|
|
|
|
tb->tun_builder_new();
|
|
|
|
// pipe fd which is used to communicate to kernel
|
|
|
|
int fd = tb->tun_builder_dco_enable(config->dev_name);
|
|
|
|
if (fd == -1) {
|
2021-02-23 01:10:17 +01:00
|
|
|
stop_();
|
|
|
|
transport_parent->transport_error(Error::TUN_IFACE_CREATE,
|
|
|
|
"error creating ovpn-dco device");
|
|
|
|
return;
|
2021-02-16 22:26:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pipe.reset(new openvpn_io::posix::stream_descriptor(io_context, fd));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
int res = TunNetlink::iface_new(os, config->dev_name, "ovpn-dco");
|
|
|
|
if (res != 0) {
|
|
|
|
stop_();
|
|
|
|
transport_parent->transport_error(Error::TUN_IFACE_CREATE, os.str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-23 01:10:17 +01:00
|
|
|
genl.reset(new GeNLImpl(
|
|
|
|
io_context_arg, if_nametoindex(config_arg->dev_name.c_str()), this));
|
2021-02-16 22:26:18 +01:00
|
|
|
genl->register_packet();
|
|
|
|
}
|
2020-08-21 15:06:51 +02:00
|
|
|
|
2020-08-24 10:48:44 +02:00
|
|
|
void handle_keepalive() {
|
|
|
|
// since userspace doesn't know anything about presense or
|
|
|
|
// absense of data channel traffic, ping should be handled in kernel
|
|
|
|
if (transport_parent->is_keepalive_enabled()) {
|
|
|
|
unsigned int keepalive_interval = 0;
|
|
|
|
unsigned int keepalive_timeout = 0;
|
|
|
|
|
|
|
|
// In addition to disabling userspace keepalive,
|
|
|
|
// this call also assigns keepalive values to provided arguments
|
|
|
|
// default keepalive values could be overwritten by config values,
|
|
|
|
// which in turn could be overwritten by pushed options
|
|
|
|
transport_parent->disable_keepalive(keepalive_interval,
|
|
|
|
keepalive_timeout);
|
|
|
|
|
|
|
|
// Allow overide of keepalive timeout
|
|
|
|
if (config->ping_restart_override)
|
|
|
|
keepalive_timeout = config->ping_restart_override;
|
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
if (config->builder) {
|
2021-02-16 22:26:18 +01:00
|
|
|
config->builder->tun_builder_dco_set_peer(peer_id, keepalive_interval,
|
2020-08-24 13:49:37 +02:00
|
|
|
keepalive_timeout);
|
|
|
|
} else {
|
|
|
|
// enable keepalive in kernel
|
2021-02-16 22:26:18 +01:00
|
|
|
genl->set_peer(peer_id, keepalive_interval, keepalive_timeout);
|
2020-08-24 13:49:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void queue_read_pipe(PacketFrom *pkt) {
|
|
|
|
if (!pkt) {
|
|
|
|
pkt = new PacketFrom();
|
2020-08-24 10:48:44 +02:00
|
|
|
}
|
2020-08-24 13:49:37 +02:00
|
|
|
// good enough values for control channel packets
|
|
|
|
pkt->buf.reset(512, 3072,
|
|
|
|
BufferAllocated::GROW | BufferAllocated::CONSTRUCT_ZERO |
|
|
|
|
BufferAllocated::DESTRUCT_ZERO);
|
|
|
|
pipe->async_read_some(
|
|
|
|
pkt->buf.mutable_buffer(),
|
|
|
|
[self = Ptr(this),
|
|
|
|
pkt = PacketFrom::SPtr(pkt)](const openvpn_io::error_code &error,
|
|
|
|
const size_t bytes_recvd) mutable {
|
|
|
|
if (!error) {
|
|
|
|
pkt->buf.set_size(bytes_recvd);
|
|
|
|
if (self->tun_read_handler(pkt->buf))
|
|
|
|
self->queue_read_pipe(pkt.release());
|
|
|
|
} else {
|
|
|
|
if (!self->halt) {
|
|
|
|
OPENVPN_LOG("ovpn-dco pipe read error: " << error.message());
|
|
|
|
self->stop_();
|
|
|
|
self->transport_parent->transport_error(Error::TUN_HALT,
|
|
|
|
error.message());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2020-08-24 10:48:44 +02:00
|
|
|
}
|
|
|
|
|
2021-06-08 01:40:38 +02:00
|
|
|
virtual SessionStats::DCOTransportSource::Data dco_transport_stats_delta() override {
|
|
|
|
if (halt) {
|
|
|
|
/* retrieve the last stats update and erase it to avoid race conditions with other queries */
|
|
|
|
SessionStats::DCOTransportSource::Data delta = last_delta;
|
|
|
|
last_delta = SessionStats::DCOTransportSource::Data(0, 0);
|
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_peer_stats(peer_id, true);
|
|
|
|
return last_delta;
|
|
|
|
}
|
|
|
|
|
2020-08-24 13:49:37 +02:00
|
|
|
// used to communicate to kernel via privileged process
|
|
|
|
std::unique_ptr<openvpn_io::posix::stream_descriptor> pipe;
|
|
|
|
|
2020-08-21 15:06:51 +02:00
|
|
|
GeNLImpl::Ptr genl;
|
2021-02-16 22:26:18 +01:00
|
|
|
TransportClient::Ptr transport;
|
2021-06-08 01:39:04 +02:00
|
|
|
SessionStats::DCOTransportSource::Data last_stats;
|
2021-06-08 01:40:38 +02:00
|
|
|
SessionStats::DCOTransportSource::Data last_delta;
|
2021-02-16 22:26:18 +01:00
|
|
|
};
|