Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions
+389
View File
@@ -0,0 +1,389 @@
// 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-2018 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 <http://www.gnu.org/licenses/>.
// kovpn crypto wrappers
#ifndef OPENVPN_KOVPN_KOCRYPTO_H
#define OPENVPN_KOVPN_KOCRYPTO_H
#include <cstring> // for std::memset, std::memcpy
#include <utility> // for std::move
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/crypto/cryptodc.hpp>
#include <openvpn/crypto/bs64_data_limit.hpp>
#include <openvpn/kovpn/kovpn.hpp>
namespace openvpn {
namespace KoRekey {
OPENVPN_EXCEPTION(korekey_error);
struct Info {
Info() {}
Info(const CryptoDCContext::Ptr& dc_context_delegate_arg,
const unsigned int key_id_arg,
const Frame::Ptr& frame_arg)
: dc_context_delegate(dc_context_delegate_arg),
key_id(key_id_arg),
frame(frame_arg)
{
}
CryptoDCContext::Ptr dc_context_delegate;
CompressContext comp_ctx;
unsigned int key_id = 0;
int remote_peer_id = -1;
bool tcp_linear = false;
StaticKey encrypt_cipher;
StaticKey encrypt_hmac;
StaticKey decrypt_cipher;
StaticKey decrypt_hmac;
Frame::Ptr frame;
};
class Key
{
// noncopyable because of "opk.primary = &key" below
Key(const Key&) = delete;
Key& operator=(const Key&) = delete;
public:
static void validate(const CryptoAlgs::Type cipher,
const CryptoAlgs::Type digest)
{
const CryptoAlgs::Alg& calg = CryptoAlgs::get(cipher);
const CryptoAlgs::Alg& halg = CryptoAlgs::get(digest);
switch (cipher)
{
case CryptoAlgs::AES_128_GCM:
case CryptoAlgs::AES_192_GCM:
case CryptoAlgs::AES_256_GCM:
case CryptoAlgs::AES_128_CBC:
case CryptoAlgs::AES_192_CBC:
case CryptoAlgs::AES_256_CBC:
case CryptoAlgs::BF_CBC:
break;
default:
OPENVPN_THROW(korekey_error, "cipher alg " << calg.name() << " is not currently supported by kovpn");
}
if (calg.mode() == CryptoAlgs::CBC_HMAC)
{
switch (digest)
{
case CryptoAlgs::SHA1:
case CryptoAlgs::SHA256:
break;
default:
OPENVPN_THROW(korekey_error, "HMAC alg " << halg.name() << " is not currently supported by kovpn");
}
}
}
Key(const CryptoDCInstance::RekeyType rktype,
const Info& rkinfo, // must remain in scope for duration of Key lifetime
const int peer_id,
const bool verbose)
{
std::memset(&opk, 0, sizeof(opk));
// set peer ID
opk.peer_id = peer_id;
// set rekey op
bool new_key = false;
bool secondary_key = false; // only relevant for non-deactivate ops to secondary
switch (rktype)
{
case CryptoDCInstance::ACTIVATE_PRIMARY:
{
new_key = true;
opk.op = OVPN_KEYS_PRIMARY_ONLY;
break;
}
case CryptoDCInstance::ACTIVATE_PRIMARY_MOVE:
{
new_key = true;
opk.op = OVPN_KEYS_PRIMARY_ASSIGN_MOVE;
break;
}
case CryptoDCInstance::NEW_SECONDARY:
{
new_key = true;
secondary_key = true;
opk.op = OVPN_KEYS_SECONDARY_ONLY;
break;
}
case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
{
opk.op = OVPN_KEYS_PRIMARY_SECONDARY_SWAP;
break;
}
case CryptoDCInstance::DEACTIVATE_SECONDARY:
{
opk.op = OVPN_KEYS_SECONDARY_ONLY;
break;
}
case CryptoDCInstance::DEACTIVATE_ALL:
{
opk.op = OVPN_KEYS_BOTH;
break;
}
default:
OPENVPN_THROW(korekey_error, "unrecognized rekey type=" << (int)rktype);
}
if (new_key)
{
const CryptoDCContext::Info ci = rkinfo.dc_context_delegate->crypto_info();
const CryptoAlgs::Alg& calg = CryptoAlgs::get(ci.cipher_alg);
// set crypto family
switch (calg.mode())
{
case CryptoAlgs::CBC_HMAC:
opk.crypto_family = OVPN_CRYPTO_FAMILY_CBC_HMAC;
break;
case CryptoAlgs::AEAD:
opk.crypto_family = OVPN_CRYPTO_FAMILY_AEAD;
break;
default:
opk.crypto_family = OVPN_CRYPTO_FAMILY_UNDEF;
break;
}
std::memset(&key, 0, sizeof(key));
key.key_id = rkinfo.key_id;
key.remote_peer_id = rkinfo.remote_peer_id;
switch (ci.cipher_alg)
{
case CryptoAlgs::AES_128_GCM:
key.cipher_alg = OVPN_ALG_AES_GCM;
key.encrypt.cipher_key_size = 128 / 8;
break;
case CryptoAlgs::AES_192_GCM:
key.cipher_alg = OVPN_ALG_AES_GCM;
key.encrypt.cipher_key_size = 192 / 8;
break;
case CryptoAlgs::AES_256_GCM:
key.cipher_alg = OVPN_ALG_AES_GCM;
key.encrypt.cipher_key_size = 256 / 8;
break;
case CryptoAlgs::AES_128_CBC:
key.cipher_alg = OVPN_ALG_AES_CBC;
key.encrypt.cipher_key_size = 128 / 8;
break;
case CryptoAlgs::AES_192_CBC:
key.cipher_alg = OVPN_ALG_AES_CBC;
key.encrypt.cipher_key_size = 192 / 8;
break;
case CryptoAlgs::AES_256_CBC:
key.cipher_alg = OVPN_ALG_AES_CBC;
key.encrypt.cipher_key_size = 256 / 8;
break;
case CryptoAlgs::BF_CBC:
key.cipher_alg = OVPN_ALG_BF_CBC;
key.encrypt.cipher_key_size = 128 / 8;
// special data limits for 64-bit block-size ciphers (CVE-2016-6329)
key.encrypt.data_limit = key.decrypt.data_limit = OPENVPN_BS64_DATA_LIMIT;
break;
default:
key.cipher_alg = OVPN_ALG_UNDEF;
break;
}
key.decrypt.cipher_key_size = key.encrypt.cipher_key_size;
// make sure that chosen cipher/family is supported
if (opk.crypto_family == OVPN_CRYPTO_FAMILY_UNDEF
|| key.cipher_alg == OVPN_ALG_UNDEF)
OPENVPN_THROW(korekey_error, "cipher alg " << calg.name() << " is not currently supported by kovpn");
// set cipher keys
key.encrypt.cipher_key = verify_key("cipher encrypt",
rkinfo.encrypt_cipher,
key.encrypt.cipher_key_size);
key.decrypt.cipher_key = verify_key("cipher decrypt",
rkinfo.decrypt_cipher,
key.decrypt.cipher_key_size);
switch (calg.mode())
{
case CryptoAlgs::CBC_HMAC:
{
// if CBC mode, process HMAC digest
const CryptoAlgs::Alg& halg = CryptoAlgs::get(ci.hmac_alg);
switch (ci.hmac_alg)
{
case CryptoAlgs::SHA1:
key.hmac_alg = OVPN_ALG_HMAC_SHA1;
break;
case CryptoAlgs::SHA256:
key.hmac_alg = OVPN_ALG_HMAC_SHA256;
break;
default:
OPENVPN_THROW(korekey_error, "HMAC alg " << halg.name() << " is not currently supported by kovpn");
}
key.encrypt.hmac_key_size = halg.size();
key.decrypt.hmac_key_size = key.encrypt.hmac_key_size;
// set hmac keys
key.encrypt.hmac_key = verify_key("hmac encrypt",
rkinfo.encrypt_hmac,
key.encrypt.hmac_key_size);
key.decrypt.hmac_key = verify_key("hmac decrypt",
rkinfo.decrypt_hmac,
key.decrypt.hmac_key_size);
// handle compression V1
switch (rkinfo.comp_ctx.type())
{
case CompressContext::LZO_STUB:
key.compress.alg = OVPN_COMP_NONE;
key.compress.swap = false;
break;
case CompressContext::COMP_STUB:
key.compress.alg = OVPN_COMP_NONE;
key.compress.swap = true;
break;
case CompressContext::LZ4:
key.compress.alg = OVPN_COMP_LZ4;
key.compress.swap = true;
break;
default:
OPENVPN_THROW(korekey_error, "Compression alg " << rkinfo.comp_ctx.str() << " is not supported by kovpn in CBC/HMAC mode");
}
break;
}
case CryptoAlgs::AEAD:
{
// if AEAD mode, copy nonce tail from the HMAC key material
set_nonce_tail("AEAD nonce tail encrypt",
key.encrypt.nonce_tail,
sizeof(key.encrypt.nonce_tail),
rkinfo.encrypt_hmac);
set_nonce_tail("AEAD nonce tail decrypt",
key.decrypt.nonce_tail,
sizeof(key.decrypt.nonce_tail),
rkinfo.decrypt_hmac);
// handle compression V2
switch (rkinfo.comp_ctx.type())
{
case CompressContext::COMP_STUBv2:
key.compress.alg = OVPN_COMP_NONE;
break;
case CompressContext::LZ4v2:
key.compress.alg = OVPN_COMP_LZ4;
break;
default:
OPENVPN_THROW(korekey_error, "Compression alg " << rkinfo.comp_ctx.str() << " is not supported by kovpn in AEAD mode");
}
key.compress.swap = false;
break;
}
default:
{
// should have been caught above
throw korekey_error("internal error");
}
}
// handle compression
key.compress.asym = rkinfo.comp_ctx.asym();
key.compress.max_decompress_size = (*rkinfo.frame)[Frame::DECOMPRESS_WORK].payload();
// handle TCP linear
key.tcp_linear = rkinfo.tcp_linear;
if (verbose)
OPENVPN_LOG("KOREKEY"
<< " op=" << int(rktype) << '/' << opk.op
<< " rpid=" << key.remote_peer_id
<< " pri=" << key.key_id
<< " cipher=" << key.cipher_alg
<< "[e=" << render_hex(key.encrypt.cipher_key, 8)
<< " d=" << render_hex(key.decrypt.cipher_key, 8) << ']'
<< " hmac=" << key.hmac_alg
<< "[e=" << render_hex(key.encrypt.hmac_key, 8)
<< " d=" << render_hex(key.decrypt.hmac_key, 8) << ']'
<< " comp=" << key.compress.alg
<< " swap=" << key.compress.swap
<< " asym=" << key.compress.asym
<< " tcp_linear=" << key.tcp_linear
<< " dl=[e=" << key.encrypt.data_limit
<< " d=" << key.decrypt.data_limit << ']');
// set key
if (secondary_key)
opk.secondary = &key;
else
opk.primary = &key;
}
else if (verbose)
{
OPENVPN_LOG("KOREKEY" << " op=" << int(rktype) << '/' << opk.op);
}
}
const struct ovpn_peer_keys_reset *operator()() const
{
return &opk;
}
private:
const unsigned char *verify_key(const char *title, const StaticKey& sk, const size_t size_required)
{
if (sk.size() < size_required)
OPENVPN_THROW(korekey_error, title << ": insufficient key material, provided=" << sk.size() << " required=" << size_required);
return sk.data();
}
void set_nonce_tail(const char *title, unsigned char *dest, const size_t dest_size, const StaticKey& src)
{
const int NONCE_TAIL_SIZE = CryptoAlgs::AEAD_NONCE_TAIL_SIZE;
const unsigned char *k = verify_key(title, src, NONCE_TAIL_SIZE);
if (dest_size < NONCE_TAIL_SIZE)
OPENVPN_THROW(korekey_error, title << ": cannot set");
std::memcpy(dest, k, NONCE_TAIL_SIZE);
// if dest is larger than NONCE_TAIL_SIZE, zero remaining bytes
if (dest_size > NONCE_TAIL_SIZE)
std::memset(dest + NONCE_TAIL_SIZE, 0, dest_size - NONCE_TAIL_SIZE);
}
struct ovpn_peer_keys_reset opk;
struct ovpn_key_config key;
};
}
}
#endif
+391
View File
@@ -0,0 +1,391 @@
// 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-2018 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 <http://www.gnu.org/licenses/>.
// OpenVPN 3 kovpn-based tun interface
#ifndef OPENVPN_KOVPN_KODEV_H
#define OPENVPN_KOVPN_KODEV_H
#include <sys/ioctl.h>
#include <string>
#include <cerrno>
#include <cstring>
#include <memory>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/scoped_fd.hpp>
#include <openvpn/common/to_string.hpp>
#include <openvpn/common/strerror.hpp>
#include <openvpn/common/valgrind.hpp>
#include <openvpn/time/timestr.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/tun/tunio.hpp>
#include <openvpn/kovpn/kovpn.hpp>
#include <openvpn/kovpn/koroute.hpp>
#include <openvpn/kovpn/kostats.hpp>
#include <openvpn/linux/procfs.hpp>
namespace openvpn {
namespace KoTun {
OPENVPN_EXCEPTION(kotun_error);
struct DevConf
{
DevConf()
{
std::memset(&dc, 0, sizeof(dc));
}
void set_dev_name(const std::string& name)
{
if (name.length() < IFNAMSIZ)
::strcpy(dc.dev_name, name.c_str());
else
OPENVPN_THROW(kotun_error, "ovpn dev name too long");
}
struct ovpn_dev_init dc;
};
// kovpn API methods
namespace API {
// Attach UDP socket to ovpn instance
inline void socket_attach_udp(const int kovpn_fd,
const int sock_fd)
{
struct ovpn_socket_attach_udp asock;
asock.fd = sock_fd;
if (::ioctl(kovpn_fd, OVPN_SOCKET_ATTACH_UDP, &asock) < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_SOCKET_ATTACH_UDP failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// New UDP client
inline int peer_new_udp_client(const int kovpn_fd,
int fd,
const __u64 notify_per,
const unsigned int notify_seconds)
{
int peer_id = -1;
// attach UDP socket fd
{
struct ovpn_socket_attach_udp asock;
asock.fd = fd;
if (::ioctl(kovpn_fd, OVPN_SOCKET_ATTACH_UDP, &asock) < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_SOCKET_ATTACH_UDP failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// get a new Peer ID
{
struct ovpn_peer_new opn;
opn.peer_float = OVPN_PF_DISABLED;
opn.ovpn_file_bind = true;
opn.notify_per = notify_per;
opn.notify_seconds = notify_seconds;
peer_id = ::ioctl(kovpn_fd, OVPN_PEER_NEW, &opn);
if (peer_id < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_PEER_NEW failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// set up endpoints for peer
{
struct ovpn_peer_sockaddr_reset psr;
std::memset(&psr, 0, sizeof(psr));
psr.peer_id = peer_id;
psr.fd = fd;
if (::ioctl(kovpn_fd, OVPN_PEER_SOCKADDR_RESET, &psr) < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_PEER_SOCKADDR_RESET failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
return peer_id;
}
// Send explicit-exit-notify message to peer
inline void peer_xmit_explicit_exit_notify(const int kovpn_fd,
const int peer_id)
{
if (::ioctl(kovpn_fd, OVPN_PEER_XMIT_EXPLICIT_EXIT_NOTIFY, peer_id) < 0)
{
const int eno = errno;
OPENVPN_LOG("kotun: OVPN_PEER_XMIT_EXPLICIT_EXIT_NOTIFY failed, id=" << peer_id << " errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// Set peer crypto keys
inline void peer_keys_reset(const int kovpn_fd,
const struct ovpn_peer_keys_reset *opk)
{
if (::ioctl(kovpn_fd, OVPN_PEER_KEYS_RESET, opk) < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_PEER_KEYS_RESET failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// Set keepalive
inline void peer_set_keepalive(const int kovpn_fd,
const struct ovpn_peer_keepalive *ka)
{
if (::ioctl(kovpn_fd, OVPN_PEER_KEEPALIVE, ka) < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_PEER_KEEPALIVE failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// Add routes
inline void peer_add_routes(const int kovpn_fd,
const int peer_id,
const std::vector<IP::Route>& rtvec)
{
std::unique_ptr<struct ovpn_route[]> routes(KoRoute::from_routes(rtvec));
struct ovpn_peer_routes_add r;
r.peer_id = peer_id;
r.usurp = true;
r.n_routes = rtvec.size();
r.routes = routes.get();
if (::ioctl(kovpn_fd, OVPN_PEER_ROUTES_ADD, &r) < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_PEER_ROUTES_ADD failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
}
}
// Get status info
inline bool peer_get_status(const int kovpn_fd,
const struct ovpn_peer_status* ops)
{
if (::ioctl(kovpn_fd, OVPN_PEER_STATUS, ops) >= 0)
{
OPENVPN_MAKE_MEM_DEFINED(ops, sizeof(*ops));
return true;
}
else
{
const int eno = errno;
OPENVPN_LOG("kotun: OVPN_PEER_STATUS failed, errno=" << eno << ' ' << KovpnStats::errstr(eno));
return false;
}
}
}
struct PacketFrom
{
typedef std::unique_ptr<PacketFrom> SPtr;
BufferAllocated buf;
};
class KovpnBase
{
public:
static ScopedFD open_kovpn(DevConf& devconf,
KovpnStats* kovpn_stats,
bool* first)
{
if (first)
*first = false;
// Open kovpn device
static const char node[] = "/dev/net/ovpn";
ScopedFD fd(open(node, O_RDWR));
if (!fd.defined())
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "error opening ovpn tunnel device " << node << ": " << strerror_str(eno));
}
// Check kovpn version
const int ver_packed = ::ioctl(fd(), OVPN_GET_VERSION, nullptr);
if (ver_packed < 0)
OPENVPN_THROW(kotun_error, "OVPN_GET_VERSION failed");
if (ver_major(ver_packed) != OVPN_VER_MAJOR
|| ver_minor(ver_packed) != OVPN_VER_MINOR)
OPENVPN_THROW(kotun_error, "version mismatch, pg=" << ver_string() << " installed=" << ver_string(ver_packed));
// Configure tun
const int status = ::ioctl(fd(), OVPN_DEV_INIT, &devconf.dc);
if (status < 0)
{
const int eno = errno;
OPENVPN_THROW(kotun_error, "OVPN_DEV_INIT failed: " << KovpnStats::errstr(eno));
}
if (devconf.dc.expire)
OPENVPN_LOG("NOTE: this evaluation build expires on " << date_time(devconf.dc.expire));
if (status == 1)
{
if (kovpn_stats)
kovpn_stats->set_fd(fd());
if (first)
*first = true;
OPENVPN_LOG("KVER pg=" << ver_string() << " installed=" << ver_string(ver_packed));
OPENVPN_LOG("IE_NAT=" << devconf.dc.ie_nat);
}
return fd;
}
static void set_rps_xps(const std::string& dev_name, const unsigned int dev_queue_index, Stop* async_stop)
{
// set RPS/XPS on iface
ProcFS::write_sys(fmt_qfn(dev_name, "rx", dev_queue_index, "rps_cpus"), "ffffffff\n", async_stop);
ProcFS::write_sys(fmt_qfn(dev_name, "rx", dev_queue_index, "rps_cpus"), "ffffffff\n", async_stop);
ProcFS::write_sys(fmt_qfn(dev_name, "rx", dev_queue_index, "rps_flow_cnt"), "1024\n", async_stop);
ProcFS::write_sys(fmt_qfn(dev_name, "tx", dev_queue_index, "xps_cpus"), "0\n", async_stop);
}
static void disable_reverse_path_filter(const std::string& dev_name, Stop* async_stop)
{
// disable reverse path filter on iface
IPv4ReversePathFilter::write(dev_name, 0, async_stop);
}
protected:
static int ver_major(const int ver_packed)
{
return (ver_packed >> 16) & 0xFF;
}
static int ver_minor(const int ver_packed)
{
return (ver_packed >> 8) & 0xFF;
}
static int ver_build(const int ver_packed)
{
return ver_packed & 0xFF;
}
static std::string ver_string(const int major, const int minor, const int build)
{
return std::to_string(major) + '.' + std::to_string(minor) + '.' + std::to_string(build);
}
static std::string ver_string(const int ver_packed)
{
return ver_string(ver_major(ver_packed), ver_minor(ver_packed), ver_build(ver_packed));
}
static std::string ver_string()
{
return ver_string(OVPN_VER_MAJOR, OVPN_VER_MINOR, OVPN_VER_BUILD);
}
static std::string fmt_qfn(const std::string& dev, const std::string& type, int qnum, const std::string& bn)
{
std::ostringstream os;
os << "/sys/class/net/" << dev << "/queues/" << type << "-" << qnum << '/' << bn;
return os.str();
}
};
template <typename ReadHandler>
struct TunClient : public TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor>, public virtual KovpnBase
{
typedef TunIO<ReadHandler, PacketFrom, openvpn_io::posix::stream_descriptor> Base;
typedef RCPtr<TunClient> Ptr;
// constructed by start() in in koudp.c/kotcp.c
TunClient(openvpn_io::io_context& io_context,
DevConf& devconf,
ReadHandler read_handler,
const Frame::Ptr& frame,
KovpnStats* kovpn_stats, // not persisted
bool *first)
: Base(read_handler, frame, SessionStats::Ptr())
{
ScopedFD fd(open_kovpn(devconf, kovpn_stats, first));
Base::name_ = devconf.dc.dev_name;
Base::stream = new openvpn_io::posix::stream_descriptor(io_context, fd.release());
}
// Attach UDP socket to ovpn instance
void socket_attach_udp(const int sock_fd)
{
API::socket_attach_udp(native_handle(), sock_fd);
}
// New UDP client (used by dcocli)
int peer_new_udp_client(int fd,
const __u64 notify_per,
const unsigned int notify_seconds)
{
return API::peer_new_udp_client(native_handle(), fd, notify_per, notify_seconds);
}
// Add routes (used by dcocli)
void peer_add_routes(const int peer_id,
const std::vector<IP::Route>& rtvec)
{
API::peer_add_routes(native_handle(), peer_id, rtvec);
}
// Send explicit-exit-notify message to peer
void peer_xmit_explicit_exit_notify(const int peer_id)
{
API::peer_xmit_explicit_exit_notify(native_handle(), peer_id);
}
// Set peer crypto keys
void peer_keys_reset(const struct ovpn_peer_keys_reset *opk)
{
API::peer_keys_reset(native_handle(), opk);
}
// Set keepalive
void peer_set_keepalive(const struct ovpn_peer_keepalive *ka)
{
API::peer_set_keepalive(native_handle(), ka);
}
// Get status info
bool peer_get_status(struct ovpn_peer_status* ops)
{
return API::peer_get_status(native_handle(), ops);
}
// Return kovpn fd
int native_handle() const
{
return Base::stream->native_handle();
}
};
}
}
#endif
+195
View File
@@ -0,0 +1,195 @@
// 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-2018 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 <http://www.gnu.org/licenses/>.
// OpenVPN 3 wrapper for kovpn crypto
#ifndef OPENVPN_KOVPN_KOREKEY_H
#define OPENVPN_KOVPN_KOREKEY_H
#include <openvpn/kovpn/kocrypto.hpp>
namespace openvpn {
namespace KoRekey {
class Receiver : public virtual RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<Receiver> Ptr;
virtual void rekey(const CryptoDCInstance::RekeyType type,
const Info& info) = 0;
virtual void explicit_exit_notify() {}
};
class Instance : public CryptoDCInstance
{
public:
Instance(const Receiver::Ptr& rcv_arg,
const CryptoDCContext::Ptr& dc_context_delegate,
const unsigned int key_id,
const Frame::Ptr& frame)
: rcv(rcv_arg),
info(dc_context_delegate, key_id, frame)
{
}
// Initialization
virtual unsigned int defined() const override
{
return CIPHER_DEFINED|HMAC_DEFINED|EXPLICIT_EXIT_NOTIFY_DEFINED;
}
virtual void init_cipher(StaticKey&& encrypt_key,
StaticKey&& decrypt_key) override
{
info.encrypt_cipher = std::move(encrypt_key);
info.decrypt_cipher = std::move(decrypt_key);
}
virtual void init_hmac(StaticKey&& encrypt_key,
StaticKey&& decrypt_key) override
{
info.encrypt_hmac = std::move(encrypt_key);
info.decrypt_hmac = std::move(decrypt_key);
}
virtual void init_pid(const int send_form,
const int recv_mode,
const int recv_form,
const char *recv_name,
const int recv_unit,
const SessionStats::Ptr& recv_stats_arg) override
{
info.tcp_linear = (recv_mode == PacketIDReceive::TCP_MODE);
}
virtual void init_remote_peer_id(const int remote_peer_id) override
{
info.remote_peer_id = remote_peer_id;
}
virtual bool consider_compression(const CompressContext& comp_ctx) override
{
info.comp_ctx = comp_ctx;
return false;
}
// Rekeying
virtual void rekey(const RekeyType type) override
{
rcv->rekey(type, info);
}
virtual void explicit_exit_notify() override
{
rcv->explicit_exit_notify();
}
// Encrypt/Decrypt -- data channel handled by kernel, so these methods
// should never be reached.
// returns true if packet ID is close to wrapping
virtual bool encrypt(BufferAllocated& buf, const PacketID::time_t now, const unsigned char *op32) override
{
throw korekey_error("encrypt");
}
virtual Error::Type decrypt(BufferAllocated& buf, const PacketID::time_t now, const unsigned char *op32) override
{
throw korekey_error("decrypt");
}
private:
Receiver::Ptr rcv;
Info info;
};
class Context : public CryptoDCContext
{
public:
Context(const CryptoAlgs::Type cipher,
const CryptoAlgs::Type digest,
CryptoDCFactory& dc_factory_delegate,
const Receiver::Ptr& rcv_arg,
const Frame::Ptr& frame_arg)
: rcv(rcv_arg),
dc_context_delegate(dc_factory_delegate.new_obj(cipher, digest)),
frame(frame_arg)
{
Key::validate(cipher, digest);
}
virtual CryptoDCInstance::Ptr new_obj(const unsigned int key_id) override
{
return new Instance(rcv, dc_context_delegate, key_id, frame);
}
// Info for ProtoContext::options_string
virtual Info crypto_info() override
{
return dc_context_delegate->crypto_info();
}
// Info for ProtoContext::link_mtu_adjust
virtual size_t encap_overhead() const override
{
return dc_context_delegate->encap_overhead();
}
private:
Receiver::Ptr rcv;
CryptoDCContext::Ptr dc_context_delegate;
Frame::Ptr frame;
};
class Factory : public CryptoDCFactory
{
public:
Factory(const CryptoDCFactory::Ptr& dc_factory_delegate_arg,
const Receiver::Ptr& rcv_arg,
const Frame::Ptr& frame_arg)
: dc_factory_delegate(dc_factory_delegate_arg),
rcv(rcv_arg),
frame(frame_arg)
{
}
virtual CryptoDCContext::Ptr new_obj(const CryptoAlgs::Type cipher,
const CryptoAlgs::Type digest) override
{
return new Context(cipher, digest, *dc_factory_delegate, rcv, frame);
}
private:
CryptoDCFactory::Ptr dc_factory_delegate;
Receiver::Ptr rcv;
Frame::Ptr frame;
};
}
}
#endif
@@ -0,0 +1,69 @@
// 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-2018 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 <http://www.gnu.org/licenses/>.
// OpenVPN 3 wrapper for kovpn
#ifndef OPENVPN_KOVPN_KOROUTE_H
#define OPENVPN_KOVPN_KOROUTE_H
#include <vector>
#include <memory>
#include <openvpn/kovpn/kovpn.hpp>
#include <openvpn/addr/route.hpp>
namespace openvpn {
namespace KoRoute {
inline struct ovpn_route from_route(const IP::Route& r)
{
struct ovpn_route ret;
ret.prefix_len = r.prefix_len;
ret.addr.v6 = (r.addr.version() == IP::Addr::V6);
switch (r.addr.version())
{
case IP::Addr::V6:
ret.addr.u.a6 = r.addr.to_ipv6_nocheck().to_in6_addr();
break;
case IP::Addr::V4:
ret.addr.u.a4 = r.addr.to_ipv4_nocheck().to_in_addr();
break;
default:
throw IP::ip_exception("route address unspecified");
}
return ret;
}
inline struct ovpn_route *from_routes(const std::vector<IP::Route>& rtvec)
{
if (rtvec.size())
{
std::unique_ptr<struct ovpn_route[]> routes(new ovpn_route[rtvec.size()]);
for (size_t i = 0; i < rtvec.size(); ++i)
routes[i] = from_route(rtvec[i]);
return routes.release();
}
else
return nullptr;
}
}
}
#endif
+166
View File
@@ -0,0 +1,166 @@
// 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-2018 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 <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_KOVPN_KOSTATS_H
#define OPENVPN_KOVPN_KOSTATS_H
#include <algorithm> // for std::min, std::max
#include <memory>
#include <atomic>
#include <sys/ioctl.h>
#include <openvpn/common/size.hpp>
#include <openvpn/common/arraysize.hpp>
#include <openvpn/common/core.hpp>
#include <openvpn/kovpn/kovpn.hpp>
namespace openvpn {
namespace kostats_private {
# include "kovpn/ovpnerrstr.h"
}
class KovpnStats
{
public:
void set_fd(const int fd)
{
kovpn_fd.store(fd, std::memory_order_relaxed);
}
void output_stats(std::ostream& os) const
{
struct ovpn_stats stats;
if (::ioctl(get_fd(), OVPN_DEV_STATS, &stats) < 0)
return;
os << "STAT.BYTES_IN," << (stats.rx_bytes + cc_rx_bytes.load(std::memory_order_relaxed)) << '\n';
os << "STAT.BYTES_OUT," << stats.tx_bytes << '\n';
}
void output_percpu(std::ostream& os) const
{
std::unique_ptr<struct ovpn_percpu_stats> pcs;
unsigned int stats_cap = std::max(16, n_cores());
for (int i = 0; i < 2; ++i)
{
const size_t pcs_size = sizeof(struct ovpn_percpu_stats) +
sizeof(struct ovpn_percpu_stat) * stats_cap;
pcs.reset((struct ovpn_percpu_stats *) ::operator new(pcs_size));
pcs->total_stats = 0;
pcs->n_stats = stats_cap;
if (::ioctl(get_fd(), OVPN_PERCPU_STATS, (void *)pcs.get()) < 0)
return;
stats_cap = std::max(stats_cap, pcs->total_stats);
if (pcs->total_stats <= pcs->n_stats)
break;
}
const size_t n = std::min(pcs->total_stats, pcs->n_stats);
for (size_t i = 0; i < n; ++i)
{
const struct ovpn_percpu_stat *s = &pcs->stats[i];
if (s->rx_bytes || s->tx_bytes)
{
os << "KOVPN.STAT.CPU-" << i << ".BYTES_IN," << s->rx_bytes << '\n';
os << "KOVPN.STAT.CPU-" << i << ".BYTES_OUT," << s->tx_bytes << '\n';
}
}
}
void output_err_counters(std::ostream& os) const
{
std::unique_ptr<struct ovpn_err_stats> esp;
unsigned int stats_cap = 128;
for (int i = 0; i < 2; ++i)
{
const size_t es_size = sizeof(struct ovpn_err_stats) +
sizeof(struct ovpn_err_stat) * stats_cap;
esp.reset((struct ovpn_err_stats *) ::operator new(es_size));
esp->total_stats = 0;
esp->n_stats = stats_cap;
if (::ioctl(get_fd(), OVPN_ERR_STATS, (void *)esp.get()) < 0)
return;
stats_cap = std::max(stats_cap, esp->total_stats);
if (esp->total_stats <= esp->n_stats)
break;
}
const size_t n = std::min(esp->total_stats, esp->n_stats);
for (size_t i = 0; i < n; ++i)
{
const struct ovpn_err_stat *s = &esp->stats[i];
os << "KOVPN";
const char *cat = cat_name(s->category);
if (cat)
{
os << '.';
os << cat;
}
const char *err = err_name(s->errcode);
if (err)
{
os << '.';
os << err;
}
os << ',' << s->count << '\n';
}
}
void increment_cc_rx_bytes(const std::uint64_t value)
{
cc_rx_bytes.fetch_add(value, std::memory_order_relaxed);
}
static const char *errstr(const size_t i)
{
const char *ret = err_name(i);
if (ret)
return ret;
else
return "";
}
private:
static const char *err_name(const size_t i)
{
if (i < array_size(kostats_private::ovpn_err_names))
return kostats_private::ovpn_err_names[i];
else
return nullptr;
}
static const char *cat_name(const size_t i)
{
if (i < array_size(kostats_private::ovpn_errcat_names))
return kostats_private::ovpn_errcat_names[i];
else
return nullptr;
}
int get_fd() const
{
return kovpn_fd.load(std::memory_order_relaxed);
}
std::atomic<int> kovpn_fd{-1};
std::atomic<uint_fast64_t> cc_rx_bytes{0};
};
}
#endif
+38
View File
@@ -0,0 +1,38 @@
// 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-2018 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 <http://www.gnu.org/licenses/>.
// OpenVPN 3 wrapper for kovpn
#ifndef OPENVPN_KOVPN_KOVPN_HPP
#define OPENVPN_KOVPN_KOVPN_HPP
// Not including this file causes redefinition errors
// when the sys/ and linux/ headers below are included
// before Asio.
#include <openvpn/io/io.hpp>
#include <sys/socket.h>
extern "C" {
#include <kovpn/kovpn.h>
}
#endif