// 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-2022 OpenVPN Inc. // // 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 . #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(ENABLE_OVPNDCOWIN) #include #endif #if defined(ENABLE_KOVPN) #include #include #include #include #elif defined(ENABLE_OVPNDCO) #include #include #include #include #include #elif defined(ENABLE_OVPNDCOWIN) #include #include #include #include #else #error either ENABLE_KOVPN, ENABLE_OVPNDCO or ENABLE_OVPNDCOWIN must be defined #endif #include // client-side DCO (Data Channel Offload) module for Linux/kovpn namespace openvpn::DCOTransport { enum { OVPN_PEER_ID_UNDEF = 0x00FFFFFF, }; class ClientConfig : public DCO, public TransportClientFactory, public TunClientFactory { public: typedef RCPtr Ptr; std::string dev_name; DCO::TransportConfig transport; DCO::TunConfig tun; unsigned int ping_restart_override = 0; void process_push(const OptionList &opt) override { transport.remote_list->process_push(opt); } void finalize(const bool disconnected) override { #if defined(ENABLE_OVPNDCOWIN) if (disconnected) tun.tun_persist.reset(); #endif } TunClientFactory::Ptr new_tun_factory(const DCO::TunConfig &conf, const OptionList &opt) override { tun = conf; // set a default MTU if (!tun.tun_prop.mtu) tun.tun_prop.mtu = TUN_MTU_DEFAULT; // parse "dev" option { const Option *dev = opt.get_ptr("dev"); if (dev) dev_name = dev->get(1, 64); else dev_name = "ovpnc"; } // parse ping-restart-override ping_restart_override = opt.get_num( "ping-restart-override", 1, ping_restart_override, 0, 3600); return TunClientFactory::Ptr(this); } TransportClientFactory::Ptr new_transport_factory(const DCO::TransportConfig &conf) override { transport = conf; return TransportClientFactory::Ptr(this); } TunClient::Ptr new_tun_client_obj(openvpn_io::io_context &io_context, TunClientParent &parent, TransportClient *transcli) override; TransportClient::Ptr new_transport_client_obj(openvpn_io::io_context &io_context, TransportClientParent *parent) override; static DCO::Ptr new_controller(TunBuilderBase *tb) { auto ctrl = new ClientConfig(); if (ctrl) ctrl->builder = tb; return ctrl; } bool supports_proto_v3() override { /* Currently, there is no version of ovpn-dco for Linux or Windows that supports * the new features, so we always return false here */ return false; } protected: ClientConfig() = default; }; class Client : public TransportClient, public TunClient, public AsyncResolvableUDP { friend class ClientConfig; typedef RCPtr Ptr; public: // transport methods bool transport_send_queue_empty() override { return false; } bool transport_has_send_queue() override { return false; } size_t transport_send_queue_size() override { return 0; } void reset_align_adjust(const size_t align_adjust) override { } void transport_stop_requeueing() override { } void server_endpoint_info(std::string &host, std::string &port, std::string &proto, std::string &ip_addr) const override { host = server_host; port = server_port; const IP::Addr addr = server_endpoint_addr(); proto = std::string(transport_protocol().str()); proto += "-DCO"; ip_addr = addr.to_string(); } void stop() override { stop_(); } // tun methods void set_disconnect() override { } bool tun_send(BufferAllocated &buf) override // return true if send succeeded { return false; } std::string vpn_ip4() const override { if (state->vpn_ip4_addr.specified()) return state->vpn_ip4_addr.to_string(); else return ""; } std::string vpn_ip6() const override { if (state->vpn_ip6_addr.specified()) return state->vpn_ip6_addr.to_string(); else return ""; } std::string vpn_gw4() const override { if (state->vpn_ip4_gw.specified()) return state->vpn_ip4_gw.to_string(); else return ""; } std::string vpn_gw6() const override { if (state->vpn_ip6_gw.specified()) return state->vpn_ip6_gw.to_string(); else return ""; } int vpn_mtu() const override { return state->mtu; } protected: Client(openvpn_io::io_context &io_context_arg, ClientConfig *config_arg, TransportClientParent *parent_arg) : AsyncResolvableUDP(io_context_arg), io_context(io_context_arg), halt(false), state(new TunProp::State()), config(config_arg), transport_parent(parent_arg), tun_parent(nullptr), peer_id(OVPN_PEER_ID_UNDEF) { } void transport_reparent(TransportClientParent *parent_arg) override { transport_parent = parent_arg; } virtual void stop_() = 0; openvpn_io::io_context &io_context; bool halt; TunProp::State::Ptr state; ClientConfig::Ptr config; TransportClientParent *transport_parent; TunClientParent *tun_parent; ActionList::Ptr remove_cmds; std::string server_host; std::string server_port; uint32_t peer_id; }; #if defined(ENABLE_KOVPN) #include inline DCO::Ptr new_controller(TunBuilderBase *) { return KovpnClientConfig::new_controller(); } inline TransportClient::Ptr ClientConfig::new_transport_client_obj(openvpn_io::io_context &io_context, TransportClientParent *parent) { return TransportClient::Ptr(new KovpnClient(io_context, this, parent)); } #elif defined(ENABLE_OVPNDCO) #include inline DCO::Ptr new_controller(TunBuilderBase *tb) { if (!OvpnDcoClient::available(tb)) return nullptr; CryptoAlgs::allow_dc_algs({CryptoAlgs::CHACHA20_POLY1305, CryptoAlgs::AES_128_GCM, CryptoAlgs::AES_192_GCM, CryptoAlgs::AES_256_GCM}); return ClientConfig::new_controller(tb); } inline TransportClient::Ptr ClientConfig::new_transport_client_obj(openvpn_io::io_context &io_context, TransportClientParent *parent) { return TransportClient::Ptr(new OvpnDcoClient(io_context, this, parent)); } #elif defined(ENABLE_OVPNDCOWIN) #include inline DCO::Ptr new_controller(TunBuilderBase *tb) { if (!OvpnDcoWinClient::available()) return nullptr; std::list algs{CryptoAlgs::AES_128_GCM, CryptoAlgs::AES_192_GCM, CryptoAlgs::AES_256_GCM}; BCRYPT_ALG_HANDLE h; NTSTATUS status = BCryptOpenAlgorithmProvider(&h, L"CHACHA20_POLY1305", NULL, 0); if (BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(h, 0); algs.push_back(CryptoAlgs::CHACHA20_POLY1305); } CryptoAlgs::allow_dc_algs(algs); return ClientConfig::new_controller(nullptr); } inline TransportClient::Ptr ClientConfig::new_transport_client_obj(openvpn_io::io_context &io_context, TransportClientParent *parent) { return TransportClient::Ptr(new OvpnDcoWinClient(io_context, this, parent)); } #endif inline TunClient::Ptr ClientConfig::new_tun_client_obj(openvpn_io::io_context &io_context, TunClientParent &parent, TransportClient *transcli) { Client *cli = static_cast(transcli); cli->tun_parent = &parent; return TunClient::Ptr(cli); } } // namespace openvpn::DCOTransport