mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-05-28 04:03:29 -04:00
dco: introduce low-level code for handling ovpn-dco in the Linux kernel
Signed-off-by: Antonio Quartulli <a@unstable.cc> Acked-by: Arne Schwabe <arne@rfc2549.org> Message-Id: <20220624083809.23487-2-a@unstable.cc> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24512.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
parent
1f7f7d2a89
commit
e34437c26b
12 changed files with 1566 additions and 1 deletions
34
configure.ac
34
configure.ac
|
|
@ -142,6 +142,13 @@ AC_ARG_ENABLE(
|
|||
[enable_small="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[dco],
|
||||
[AS_HELP_STRING([--enable-dco], [enable data channel offload support using ovpn-dco kernel module @<:@default=no@:>@])],
|
||||
,
|
||||
[enable_dco="no"]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[iproute2],
|
||||
[AS_HELP_STRING([--enable-iproute2], [enable support for iproute2 @<:@default=no@:>@])],
|
||||
|
|
@ -760,6 +767,32 @@ PKG_CHECK_MODULES(
|
|||
[]
|
||||
)
|
||||
|
||||
|
||||
if test "$enable_dco" = "yes"; then
|
||||
dnl
|
||||
dnl Include generic netlink library used to talk to ovpn-dco
|
||||
dnl
|
||||
|
||||
case "$host" in
|
||||
*-*-linux*)
|
||||
PKG_CHECK_MODULES([LIBNL_GENL],
|
||||
[libnl-genl-3.0 >= 3.4.0],
|
||||
[have_libnl="yes"],
|
||||
[AC_MSG_ERROR([libnl-genl-3.0 package not found or too old. Is the development package and pkg-config installed? Must be version 3.4.0 or newer])]
|
||||
)
|
||||
|
||||
CFLAGS="${CFLAGS} ${LIBNL_GENL_CFLAGS}"
|
||||
LIBS="${LIBS} ${LIBNL_GENL_LIBS}"
|
||||
|
||||
AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload])
|
||||
AC_MSG_NOTICE([Enabled ovpn-dco support for Linux])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_NOTICE([Ignoring --enable-dco on non Linux platform])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test "${with_crypto_library}" = "openssl"; then
|
||||
AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL])
|
||||
AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL])
|
||||
|
|
@ -1196,6 +1229,7 @@ fi
|
|||
AM_CONDITIONAL([HAVE_SITNL], [false])
|
||||
|
||||
if test "${enable_iproute2}" = "yes"; then
|
||||
test "${enable_dco}" = "yes" && AC_MSG_ERROR([iproute2 support cannot be enabled when using DCO])
|
||||
test -z "${IPROUTE}" && AC_MSG_ERROR([ip utility is required but missing])
|
||||
AC_DEFINE([ENABLE_IPROUTE], [1], [enable iproute2 support])
|
||||
else if test "${have_sitnl}" = "yes"; then
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
E:doc/doxygen/doc_key_generation.h # @verbatim section gets mistreated, exclude it
|
||||
E:src/compat/compat-lz4.c # Preserve LZ4 upstream formatting
|
||||
E:src/compat/compat-lz4.h # Preserve LZ4 upstream formatting
|
||||
E:src/openvpn/ovpn_dco_linux.h # Preserve ovpn-dco upstream formatting
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ openvpn_SOURCES = \
|
|||
crypto.c crypto.h crypto_backend.h \
|
||||
crypto_openssl.c crypto_openssl.h \
|
||||
crypto_mbedtls.c crypto_mbedtls.h \
|
||||
dco.h dco_internal.h \
|
||||
dco_linux.c dco_linux.h \
|
||||
dhcp.c dhcp.h \
|
||||
dns.c dns.h \
|
||||
env_set.c env_set.h \
|
||||
|
|
@ -75,6 +77,7 @@ openvpn_SOURCES = \
|
|||
mbuf.c mbuf.h \
|
||||
memdbg.h \
|
||||
misc.c misc.h \
|
||||
ovpn_dco_linux.h \
|
||||
platform.c platform.h \
|
||||
console.c console.h console_builtin.c console_systemd.c \
|
||||
mroute.c mroute.h \
|
||||
|
|
|
|||
165
src/openvpn/dco.h
Normal file
165
src/openvpn/dco.h
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* OpenVPN -- An application to securely tunnel IP networks
|
||||
* over a single TCP/UDP port, with support for SSL/TLS-based
|
||||
* session authentication and key exchange,
|
||||
* packet encryption, packet authentication, and
|
||||
* packet compression.
|
||||
*
|
||||
* Copyright (C) 2021-2022 Arne Schwabe <arne@rfc2549.org>
|
||||
* Copyright (C) 2021-2022 Antonio Quartulli <a@unstable.cc>
|
||||
* Copyright (C) 2021-2022 OpenVPN Inc <sales@openvpn.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (see the file COPYING included with this
|
||||
* distribution); if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef DCO_H
|
||||
#define DCO_H
|
||||
|
||||
#include "buffer.h"
|
||||
#include "error.h"
|
||||
#include "dco_internal.h"
|
||||
#include "networking.h"
|
||||
|
||||
/* forward declarations (including other headers leads to nasty include
|
||||
* order problems)
|
||||
*/
|
||||
struct event_set;
|
||||
struct options;
|
||||
struct tuntap;
|
||||
|
||||
#if defined(ENABLE_DCO)
|
||||
|
||||
/**
|
||||
* Check whether ovpn-dco is available on this platform (i.e. kernel support is
|
||||
* there)
|
||||
*
|
||||
* @param msglevel level to print messages to
|
||||
* @return true if ovpn-dco is available, false otherwise
|
||||
*/
|
||||
bool dco_available(int msglevel);
|
||||
|
||||
/**
|
||||
* Check whether the options struct has any option that is not supported by
|
||||
* our current dco implementation. If so print a warning at warning level
|
||||
* for the first conflicting option found and return false.
|
||||
*
|
||||
* @param msglevel the msg level to use to print the warnings
|
||||
* @param o the options struct that hold the options
|
||||
* @return true if no conflict was detected, false otherwise
|
||||
*/
|
||||
bool dco_check_option_conflict(int msglevel, const struct options *o);
|
||||
|
||||
/**
|
||||
* Initialize the DCO context
|
||||
*
|
||||
* @param mode the instance operating mode (P2P or multi-peer)
|
||||
* @param dco the context to initialize
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool ovpn_dco_init(int mode, dco_context_t *dco);
|
||||
|
||||
/**
|
||||
* Open/create a DCO interface
|
||||
*
|
||||
* @param tt the tuntap context
|
||||
* @param ctx the networking API context
|
||||
* @param dev the name of the interface to create
|
||||
* @return 0 on success or a negative error code otherwise
|
||||
*/
|
||||
int open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev);
|
||||
|
||||
/**
|
||||
* Close/destroy a DCO interface
|
||||
*
|
||||
* @param tt the tuntap context
|
||||
* @param ctx the networking API context
|
||||
*/
|
||||
void close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* Read data from the DCO communication channel (i.e. a control packet)
|
||||
*
|
||||
* @param dco the DCO context
|
||||
* @return 0 on success or a negative error code otherwise
|
||||
*/
|
||||
int dco_do_read(dco_context_t *dco);
|
||||
|
||||
/**
|
||||
* Write data to the DCO communication channel (control packet expected)
|
||||
*
|
||||
* @param dco the DCO context
|
||||
* @param peer_id the ID of the peer to send the data to
|
||||
* @param buf the buffer containing the data to send
|
||||
*/
|
||||
int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf);
|
||||
|
||||
/**
|
||||
* Install a DCO in the main event loop
|
||||
*/
|
||||
void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg);
|
||||
|
||||
#else /* if defined(ENABLE_DCO) */
|
||||
|
||||
typedef void *dco_context_t;
|
||||
|
||||
static inline bool
|
||||
dco_available(int msglevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
dco_check_option_conflict(int msglevel, const struct options *o)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ovpn_dco_init(int mode, dco_context_t *dco)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int
|
||||
open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int
|
||||
dco_do_read(dco_context_t *dco)
|
||||
{
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
|
||||
{
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* defined(ENABLE_DCO) */
|
||||
#endif /* ifndef DCO_H */
|
||||
78
src/openvpn/dco_internal.h
Normal file
78
src/openvpn/dco_internal.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* OpenVPN -- An application to securely tunnel IP networks
|
||||
* over a single TCP/UDP port, with support for SSL/TLS-based
|
||||
* session authentication and key exchange,
|
||||
* packet encryption, packet authentication, and
|
||||
* packet compression.
|
||||
*
|
||||
* Copyright (C) 2022 Antonio Quartulli <a@unstable.cc>
|
||||
* Copyright (C) 2022 OpenVPN Inc <sales@openvpn.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (see the file COPYING included with this
|
||||
* distribution); if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef DCO_INTERNAL_H
|
||||
#define DCO_INTERNAL_H
|
||||
|
||||
#if defined(ENABLE_DCO)
|
||||
|
||||
#include "dco_linux.h"
|
||||
|
||||
/**
|
||||
* This file contains the internal DCO API definition.
|
||||
* It is expected that this file is included only in dco.h.
|
||||
* The OpenVPN code should never directly include this file
|
||||
*/
|
||||
|
||||
static inline dco_cipher_t
|
||||
dco_get_cipher(const char *cipher)
|
||||
{
|
||||
if (strcmp(cipher, "AES-256-GCM") == 0 || strcmp(cipher, "AES-128-GCM") == 0
|
||||
|| strcmp(cipher, "AES-192-GCM") == 0)
|
||||
{
|
||||
return OVPN_CIPHER_ALG_AES_GCM;
|
||||
}
|
||||
else if (strcmp(cipher, "CHACHA20-POLY1305") == 0)
|
||||
{
|
||||
return OVPN_CIPHER_ALG_CHACHA20_POLY1305;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg(M_FATAL, "DCO: provided unsupported cipher: %s", cipher);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following are the DCO APIs used to control the driver.
|
||||
* They are implemented by dco_linux.c
|
||||
*/
|
||||
|
||||
int dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
|
||||
struct sockaddr *localaddr, struct sockaddr *remoteaddr,
|
||||
struct in_addr *remote_in4, struct in6_addr *remote_in6);
|
||||
|
||||
int dco_del_peer(dco_context_t *dco, unsigned int peerid);
|
||||
|
||||
int dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
|
||||
dco_key_slot_t slot,
|
||||
const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
|
||||
const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
|
||||
const char *ciphername);
|
||||
|
||||
int dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot);
|
||||
|
||||
int dco_swap_keys(dco_context_t *dco, unsigned int peerid);
|
||||
|
||||
#endif /* defined(ENABLE_DCO) */
|
||||
#endif /* ifndef DCO_INTERNAL_H */
|
||||
934
src/openvpn/dco_linux.c
Normal file
934
src/openvpn/dco_linux.c
Normal file
|
|
@ -0,0 +1,934 @@
|
|||
/*
|
||||
* Interface to linux dco networking code
|
||||
*
|
||||
* Copyright (C) 2020-2022 Antonio Quartulli <a@unstable.cc>
|
||||
* Copyright (C) 2020-2022 Arne Schwabe <arne@rfc2549.org>
|
||||
* Copyright (C) 2020-2022 OpenVPN Inc <sales@openvpn.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (see the file COPYING included with this
|
||||
* distribution); if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#elif defined(_MSC_VER)
|
||||
#include "config-msvc.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
|
||||
|
||||
#include "syshead.h"
|
||||
|
||||
#include "dco_linux.h"
|
||||
#include "errlevel.h"
|
||||
#include "buffer.h"
|
||||
#include "networking.h"
|
||||
#include "openvpn.h"
|
||||
|
||||
#include "socket.h"
|
||||
#include "tun.h"
|
||||
#include "ssl.h"
|
||||
#include "fdmisc.h"
|
||||
#include "ssl_verify.h"
|
||||
|
||||
#include "ovpn_dco_linux.h"
|
||||
|
||||
#include <netlink/socket.h>
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/genl/genl.h>
|
||||
#include <netlink/genl/family.h>
|
||||
#include <netlink/genl/ctrl.h>
|
||||
|
||||
|
||||
/* libnl < 3.5.0 does not set the NLA_F_NESTED on its own, therefore we
|
||||
* have to explicitly do it to prevent the kernel from failing upon
|
||||
* parsing of the message
|
||||
*/
|
||||
#define nla_nest_start(_msg, _type) \
|
||||
nla_nest_start(_msg, (_type) | NLA_F_NESTED)
|
||||
|
||||
static int ovpn_get_mcast_id(dco_context_t *dco);
|
||||
|
||||
void dco_check_key_ctx(const struct key_ctx_bi *key);
|
||||
|
||||
typedef int (*ovpn_nl_cb)(struct nl_msg *msg, void *arg);
|
||||
|
||||
/**
|
||||
* @brief resolves the netlink ID for ovpn-dco
|
||||
*
|
||||
* This function queries the kernel via a netlink socket
|
||||
* whether the ovpn-dco netlink namespace is available
|
||||
*
|
||||
* This function can be used to determine if the kernel
|
||||
* supports DCO offloading.
|
||||
*
|
||||
* @return ID on success, negative error code on error
|
||||
*/
|
||||
static int
|
||||
resolve_ovpn_netlink_id(int msglevel)
|
||||
{
|
||||
int ret;
|
||||
struct nl_sock *nl_sock = nl_socket_alloc();
|
||||
|
||||
ret = genl_connect(nl_sock);
|
||||
if (ret)
|
||||
{
|
||||
msg(msglevel, "Cannot connect to generic netlink: %s",
|
||||
nl_geterror(ret));
|
||||
goto err_sock;
|
||||
}
|
||||
set_cloexec(nl_socket_get_fd(nl_sock));
|
||||
|
||||
ret = genl_ctrl_resolve(nl_sock, OVPN_NL_NAME);
|
||||
if (ret < 0)
|
||||
{
|
||||
msg(msglevel, "Cannot find ovpn_dco netlink component: %s",
|
||||
nl_geterror(ret));
|
||||
}
|
||||
|
||||
err_sock:
|
||||
nl_socket_free(nl_sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nl_msg *
|
||||
ovpn_dco_nlmsg_create(dco_context_t *dco, enum ovpn_nl_commands cmd)
|
||||
{
|
||||
struct nl_msg *nl_msg = nlmsg_alloc();
|
||||
if (!nl_msg)
|
||||
{
|
||||
msg(M_ERR, "cannot allocate netlink message");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
genlmsg_put(nl_msg, 0, 0, dco->ovpn_dco_id, 0, 0, cmd, 0);
|
||||
NLA_PUT_U32(nl_msg, OVPN_ATTR_IFINDEX, dco->ifindex);
|
||||
|
||||
return nl_msg;
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
msg(M_INFO, "cannot put into netlink message");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix)
|
||||
{
|
||||
int ret = nl_recvmsgs(dco->nl_sock, dco->nl_cb);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case -NLE_INTR:
|
||||
msg(M_WARN, "%s: netlink received interrupt due to signal - ignoring", prefix);
|
||||
break;
|
||||
|
||||
case -NLE_NOMEM:
|
||||
msg(M_ERR, "%s: netlink out of memory error", prefix);
|
||||
break;
|
||||
|
||||
case -M_ERR:
|
||||
msg(M_WARN, "%s: netlink reports blocking read - aborting wait", prefix);
|
||||
break;
|
||||
|
||||
case -NLE_NODEV:
|
||||
msg(M_ERR, "%s: netlink reports device not found:", prefix);
|
||||
break;
|
||||
|
||||
case -NLE_OBJ_NOTFOUND:
|
||||
msg(M_INFO, "%s: netlink reports object not found, ovpn-dco unloaded?", prefix);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ret)
|
||||
{
|
||||
msg(M_NONFATAL|M_ERRNO, "%s: netlink reports error (%d): %s", prefix, ret, nl_geterror(-ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a prepared netlink message and registers cb as callback if non-null.
|
||||
*
|
||||
* The method will also free nl_msg
|
||||
* @param dco The dco context to use
|
||||
* @param nl_msg the message to use
|
||||
* @param cb An optional callback if the caller expects an answer
|
||||
* @param prefix A prefix to report in the error message to give the user context
|
||||
* @return status of sending the message
|
||||
*/
|
||||
static int
|
||||
ovpn_nl_msg_send(dco_context_t *dco, struct nl_msg *nl_msg, ovpn_nl_cb cb,
|
||||
const char *prefix)
|
||||
{
|
||||
dco->status = 1;
|
||||
|
||||
nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, cb, dco);
|
||||
nl_send_auto(dco->nl_sock, nl_msg);
|
||||
|
||||
while (dco->status == 1)
|
||||
{
|
||||
ovpn_nl_recvmsgs(dco, prefix);
|
||||
}
|
||||
|
||||
if (dco->status < 0)
|
||||
{
|
||||
msg(M_INFO, "%s: failed to send netlink message: %s (%d)",
|
||||
prefix, strerror(-dco->status), dco->status);
|
||||
}
|
||||
|
||||
return dco->status;
|
||||
}
|
||||
|
||||
struct sockaddr *
|
||||
mapped_v4_to_v6(struct sockaddr *sock, struct gc_arena *gc)
|
||||
{
|
||||
struct sockaddr_in6 *sock6 = (struct sockaddr_in6 *)sock;
|
||||
if (sock->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sock6->sin6_addr))
|
||||
{
|
||||
|
||||
struct sockaddr_in *sock4;
|
||||
ALLOC_OBJ_CLEAR_GC(sock4, struct sockaddr_in, gc);
|
||||
memcpy(&sock4->sin_addr, sock6->sin6_addr.s6_addr + 12, 4);
|
||||
sock4->sin_port = sock6->sin6_port;
|
||||
sock4->sin_family = AF_INET;
|
||||
return (struct sockaddr *)sock4;
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
int
|
||||
dco_new_peer(dco_context_t *dco, unsigned int peerid, int sd,
|
||||
struct sockaddr *localaddr, struct sockaddr *remoteaddr,
|
||||
struct in_addr *remote_in4, struct in6_addr *remote_in6)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: peer-id %d, fd %d", __func__, peerid, sd);
|
||||
|
||||
struct gc_arena gc = gc_new();
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_NEW_PEER);
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_PEER);
|
||||
int ret = -EMSGSIZE;
|
||||
|
||||
NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_PEER_ID, peerid);
|
||||
NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_SOCKET, sd);
|
||||
|
||||
/* Set the remote endpoint if defined (for UDP) */
|
||||
if (remoteaddr)
|
||||
{
|
||||
remoteaddr = mapped_v4_to_v6(remoteaddr, &gc);
|
||||
int alen = af_addr_size(remoteaddr->sa_family);
|
||||
|
||||
NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE, alen, remoteaddr);
|
||||
}
|
||||
|
||||
if (localaddr)
|
||||
{
|
||||
localaddr = mapped_v4_to_v6(localaddr, &gc);
|
||||
if (localaddr->sa_family == AF_INET)
|
||||
{
|
||||
NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in_addr),
|
||||
&((struct sockaddr_in *)localaddr)->sin_addr);
|
||||
}
|
||||
else if (localaddr->sa_family == AF_INET6)
|
||||
{
|
||||
NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_LOCAL_IP, sizeof(struct in6_addr),
|
||||
&((struct sockaddr_in6 *)localaddr)->sin6_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the primary VPN IP addresses of the peer */
|
||||
if (remote_in4)
|
||||
{
|
||||
NLA_PUT_U32(nl_msg, OVPN_NEW_PEER_ATTR_IPV4, remote_in4->s_addr);
|
||||
}
|
||||
if (remote_in6)
|
||||
{
|
||||
NLA_PUT(nl_msg, OVPN_NEW_PEER_ATTR_IPV6, sizeof(struct in6_addr),
|
||||
remote_in6);
|
||||
}
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
gc_free(&gc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ovpn_nl_cb_finish(struct nl_msg (*msg) __attribute__ ((unused)), void *arg)
|
||||
{
|
||||
int *status = arg;
|
||||
|
||||
*status = 0;
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
/* This function is used as error callback on the netlink socket.
|
||||
* When something goes wrong and the kernel returns an error, this function is
|
||||
* invoked.
|
||||
*
|
||||
* We pass the error code to the user by means of a variable pointed by *arg
|
||||
* (supplied by the user when setting this callback) and we parse the kernel
|
||||
* reply to see if it contains a human readable error. If found, it is printed.
|
||||
*/
|
||||
static int
|
||||
ovpn_nl_cb_error(struct sockaddr_nl (*nla) __attribute__ ((unused)),
|
||||
struct nlmsgerr *err, void *arg)
|
||||
{
|
||||
struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1;
|
||||
struct nlattr *tb_msg[NLMSGERR_ATTR_MAX + 1];
|
||||
int len = nlh->nlmsg_len;
|
||||
struct nlattr *attrs;
|
||||
int *ret = arg;
|
||||
int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
|
||||
|
||||
*ret = err->error;
|
||||
|
||||
if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
|
||||
{
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
|
||||
{
|
||||
ack_len += err->msg.nlmsg_len - sizeof(*nlh);
|
||||
}
|
||||
|
||||
if (len <= ack_len)
|
||||
{
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
attrs = (void *)((unsigned char *)nlh + ack_len);
|
||||
len -= ack_len;
|
||||
|
||||
nla_parse(tb_msg, NLMSGERR_ATTR_MAX, attrs, len, NULL);
|
||||
if (tb_msg[NLMSGERR_ATTR_MSG])
|
||||
{
|
||||
len = strnlen((char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]),
|
||||
nla_len(tb_msg[NLMSGERR_ATTR_MSG]));
|
||||
msg(M_WARN, "kernel error: %*s\n", len,
|
||||
(char *)nla_data(tb_msg[NLMSGERR_ATTR_MSG]));
|
||||
}
|
||||
|
||||
return NL_STOP;
|
||||
}
|
||||
|
||||
static void
|
||||
ovpn_dco_init_netlink(dco_context_t *dco)
|
||||
{
|
||||
dco->ovpn_dco_id = resolve_ovpn_netlink_id(M_ERR);
|
||||
|
||||
dco->nl_sock = nl_socket_alloc();
|
||||
|
||||
if (!dco->nl_sock)
|
||||
{
|
||||
msg(M_ERR, "Cannot create netlink socket");
|
||||
}
|
||||
|
||||
/* TODO: Why are we setting this buffer size? */
|
||||
nl_socket_set_buffer_size(dco->nl_sock, 8192, 8192);
|
||||
|
||||
int ret = genl_connect(dco->nl_sock);
|
||||
if (ret)
|
||||
{
|
||||
msg(M_ERR, "Cannot connect to generic netlink: %s",
|
||||
nl_geterror(ret));
|
||||
}
|
||||
|
||||
set_cloexec(nl_socket_get_fd(dco->nl_sock));
|
||||
|
||||
dco->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||
if (!dco->nl_cb)
|
||||
{
|
||||
msg(M_ERR, "failed to allocate netlink callback");
|
||||
}
|
||||
|
||||
nl_socket_set_cb(dco->nl_sock, dco->nl_cb);
|
||||
|
||||
nl_cb_err(dco->nl_cb, NL_CB_CUSTOM, ovpn_nl_cb_error, &dco->status);
|
||||
nl_cb_set(dco->nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, ovpn_nl_cb_finish,
|
||||
&dco->status);
|
||||
nl_cb_set(dco->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_finish,
|
||||
&dco->status);
|
||||
|
||||
/* The async PACKET messages confuse libnl and it will drop them with
|
||||
* wrong sequence numbers (NLE_SEQ_MISMATCH), so disable libnl's sequence
|
||||
* number check */
|
||||
nl_socket_disable_seq_check(dco->nl_sock);
|
||||
}
|
||||
|
||||
bool
|
||||
ovpn_dco_init(int mode, dco_context_t *dco)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case CM_TOP:
|
||||
dco->ifmode = OVPN_MODE_MP;
|
||||
break;
|
||||
|
||||
case CM_P2P:
|
||||
dco->ifmode = OVPN_MODE_P2P;
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
ovpn_dco_init_netlink(dco);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ovpn_dco_uninit_netlink(dco_context_t *dco)
|
||||
{
|
||||
nl_socket_free(dco->nl_sock);
|
||||
dco->nl_sock = NULL;
|
||||
|
||||
/* Decrease reference count */
|
||||
nl_cb_put(dco->nl_cb);
|
||||
|
||||
CLEAR(dco);
|
||||
}
|
||||
|
||||
static void
|
||||
ovpn_dco_register(dco_context_t *dco)
|
||||
{
|
||||
msg(D_DCO_DEBUG, __func__);
|
||||
ovpn_get_mcast_id(dco);
|
||||
|
||||
if (dco->ovpn_dco_mcast_id < 0)
|
||||
{
|
||||
msg(M_ERR, "cannot get mcast group: %s", nl_geterror(dco->ovpn_dco_mcast_id));
|
||||
}
|
||||
|
||||
/* Register for ovpn-dco specific multicast messages that the kernel may
|
||||
* send
|
||||
*/
|
||||
int ret = nl_socket_add_membership(dco->nl_sock, dco->ovpn_dco_mcast_id);
|
||||
if (ret)
|
||||
{
|
||||
msg(M_ERR, "%s: failed to join groups: %d", __func__, ret);
|
||||
}
|
||||
|
||||
/* Register for non-data packets that ovpn-dco may receive. They will be
|
||||
* forwarded to userspace
|
||||
*/
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_REGISTER_PACKET);
|
||||
if (!nl_msg)
|
||||
{
|
||||
msg(M_ERR, "%s: cannot allocate message to register for control packets",
|
||||
__func__);
|
||||
}
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
if (ret)
|
||||
{
|
||||
msg(M_ERR, "%s: failed to register for control packets: %d", __func__,
|
||||
ret);
|
||||
}
|
||||
nlmsg_free(nl_msg);
|
||||
}
|
||||
|
||||
int
|
||||
open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: %s", __func__, dev);
|
||||
ASSERT(tt->type == DEV_TYPE_TUN);
|
||||
|
||||
int ret = net_iface_new(ctx, dev, "ovpn-dco", &tt->dco);
|
||||
if (ret < 0)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "Cannot create DCO interface %s: %d", dev, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tt->dco.ifindex = if_nametoindex(dev);
|
||||
if (!tt->dco.ifindex)
|
||||
{
|
||||
msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev);
|
||||
}
|
||||
|
||||
tt->actual_name = string_alloc(dev, NULL);
|
||||
uint8_t *dcobuf = malloc(65536);
|
||||
buf_set_write(&tt->dco.dco_packet_in, dcobuf, 65536);
|
||||
tt->dco.dco_message_peer_id = -1;
|
||||
|
||||
ovpn_dco_register(&tt->dco);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
close_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx)
|
||||
{
|
||||
msg(D_DCO_DEBUG, __func__);
|
||||
|
||||
net_iface_del(ctx, tt->actual_name);
|
||||
ovpn_dco_uninit_netlink(&tt->dco);
|
||||
free(tt->dco.dco_packet_in.data);
|
||||
}
|
||||
|
||||
int
|
||||
dco_swap_keys(dco_context_t *dco, unsigned int peerid)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
|
||||
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_SWAP_KEYS);
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SWAP_KEYS);
|
||||
int ret = -EMSGSIZE;
|
||||
NLA_PUT_U32(nl_msg, OVPN_SWAP_KEYS_ATTR_PEER_ID, peerid);
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dco_del_peer(dco_context_t *dco, unsigned int peerid)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peerid);
|
||||
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_DEL_PEER);
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_PEER);
|
||||
int ret = -EMSGSIZE;
|
||||
NLA_PUT_U32(nl_msg, OVPN_DEL_PEER_ATTR_PEER_ID, peerid);
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dco_del_key(dco_context_t *dco, unsigned int peerid,
|
||||
dco_key_slot_t slot)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: peer-id %d, slot %d", __func__, peerid, slot);
|
||||
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_DEL_KEY);
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_DEL_KEY);
|
||||
int ret = -EMSGSIZE;
|
||||
NLA_PUT_U32(nl_msg, OVPN_DEL_KEY_ATTR_PEER_ID, peerid);
|
||||
NLA_PUT_U8(nl_msg, OVPN_DEL_KEY_ATTR_KEY_SLOT, slot);
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dco_new_key(dco_context_t *dco, unsigned int peerid, int keyid,
|
||||
dco_key_slot_t slot,
|
||||
const uint8_t *encrypt_key, const uint8_t *encrypt_iv,
|
||||
const uint8_t *decrypt_key, const uint8_t *decrypt_iv,
|
||||
const char *ciphername)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s",
|
||||
__func__, slot, keyid, peerid, ciphername);
|
||||
|
||||
const size_t key_len = cipher_kt_key_size(ciphername);
|
||||
const int nonce_tail_len = 8;
|
||||
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_NEW_KEY);
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dco_cipher_t dco_cipher = dco_get_cipher(ciphername);
|
||||
|
||||
int ret = -EMSGSIZE;
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_NEW_KEY);
|
||||
NLA_PUT_U32(nl_msg, OVPN_NEW_KEY_ATTR_PEER_ID, peerid);
|
||||
NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_SLOT, slot);
|
||||
NLA_PUT_U8(nl_msg, OVPN_NEW_KEY_ATTR_KEY_ID, keyid);
|
||||
NLA_PUT_U16(nl_msg, OVPN_NEW_KEY_ATTR_CIPHER_ALG, dco_cipher);
|
||||
|
||||
struct nlattr *key_enc = nla_nest_start(nl_msg,
|
||||
OVPN_NEW_KEY_ATTR_ENCRYPT_KEY);
|
||||
if (dco_cipher != OVPN_CIPHER_ALG_NONE)
|
||||
{
|
||||
NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, encrypt_key);
|
||||
NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len,
|
||||
encrypt_iv);
|
||||
}
|
||||
nla_nest_end(nl_msg, key_enc);
|
||||
|
||||
struct nlattr *key_dec = nla_nest_start(nl_msg,
|
||||
OVPN_NEW_KEY_ATTR_DECRYPT_KEY);
|
||||
if (dco_cipher != OVPN_CIPHER_ALG_NONE)
|
||||
{
|
||||
NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_CIPHER_KEY, key_len, decrypt_key);
|
||||
NLA_PUT(nl_msg, OVPN_KEY_DIR_ATTR_NONCE_TAIL, nonce_tail_len,
|
||||
decrypt_iv);
|
||||
}
|
||||
nla_nest_end(nl_msg, key_dec);
|
||||
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dco_set_peer(dco_context_t *dco, unsigned int peerid,
|
||||
int keepalive_interval, int keepalive_timeout, int mss)
|
||||
{
|
||||
msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d, mss %d", __func__,
|
||||
peerid, keepalive_interval, keepalive_timeout, mss);
|
||||
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_SET_PEER);
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_SET_PEER);
|
||||
int ret = -EMSGSIZE;
|
||||
NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_PEER_ID, peerid);
|
||||
NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL,
|
||||
keepalive_interval);
|
||||
NLA_PUT_U32(nl_msg, OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT,
|
||||
keepalive_timeout);
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function parses the reply provided by the kernel to the CTRL_CMD_GETFAMILY
|
||||
* message. We parse the reply and we retrieve the multicast group ID associated
|
||||
* with the "ovpn-dco" netlink family.
|
||||
*
|
||||
* The ID is later used to subscribe to the multicast group and be notified
|
||||
* about any multicast message sent by the ovpn-dco kernel module.
|
||||
*/
|
||||
static int
|
||||
mcast_family_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
dco_context_t *dco = arg;
|
||||
struct nlattr *tb[CTRL_ATTR_MAX + 1];
|
||||
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||
|
||||
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL);
|
||||
|
||||
if (!tb[CTRL_ATTR_MCAST_GROUPS])
|
||||
{
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
struct nlattr *mcgrp;
|
||||
int rem_mcgrp;
|
||||
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp)
|
||||
{
|
||||
struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
|
||||
|
||||
nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
|
||||
nla_data(mcgrp), nla_len(mcgrp), NULL);
|
||||
|
||||
if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]
|
||||
|| !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
|
||||
OVPN_NL_MULTICAST_GROUP_PEERS,
|
||||
nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
dco->ovpn_dco_mcast_id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
|
||||
break;
|
||||
}
|
||||
|
||||
return NL_SKIP;
|
||||
}
|
||||
/**
|
||||
* Lookup the multicast id for OpenVPN. This method and its help method currently
|
||||
* hardcode the lookup to OVPN_NL_NAME and OVPN_NL_MULTICAST_GROUP_PEERS but
|
||||
* extended in the future if we need to lookup more than one mcast id.
|
||||
*/
|
||||
static int
|
||||
ovpn_get_mcast_id(dco_context_t *dco)
|
||||
{
|
||||
dco->ovpn_dco_mcast_id = -ENOENT;
|
||||
|
||||
/* Even though 'nlctrl' is a constant, there seem to be no library
|
||||
* provided define for it */
|
||||
int ctrlid = genl_ctrl_resolve(dco->nl_sock, "nlctrl");
|
||||
|
||||
struct nl_msg *nl_msg = nlmsg_alloc();
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
genlmsg_put(nl_msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
|
||||
|
||||
int ret = -EMSGSIZE;
|
||||
NLA_PUT_STRING(nl_msg, CTRL_ATTR_FAMILY_NAME, OVPN_NL_NAME);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, mcast_family_handler, __func__);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function parses any netlink message sent by ovpn-dco to userspace */
|
||||
static int
|
||||
ovpn_handle_msg(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
dco_context_t *dco = arg;
|
||||
|
||||
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||
struct nlattr *attrs[OVPN_ATTR_MAX + 1];
|
||||
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||
|
||||
if (!genlmsg_valid_hdr(nlh, 0))
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: invalid header");
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
if (nla_parse(attrs, OVPN_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL))
|
||||
{
|
||||
msg(D_DCO, "received bogus data from ovpn-dco");
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
/* we must know which interface this message is referring to in order to
|
||||
* avoid mixing messages for other instances
|
||||
*/
|
||||
if (!attrs[OVPN_ATTR_IFINDEX])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: Received message without ifindex");
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
uint32_t ifindex = nla_get_u32(attrs[OVPN_ATTR_IFINDEX]);
|
||||
if (ifindex != dco->ifindex)
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: received message type %d with mismatched ifindex %d\n",
|
||||
gnlh->cmd, ifindex);
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
/* based on the message type, we parse the subobject contained in the
|
||||
* message, that stores the type-specific attributes.
|
||||
*
|
||||
* the "dco" object is then filled accordingly with the information
|
||||
* retrieved from the message, so that the rest of the OpenVPN code can
|
||||
* react as need be.
|
||||
*/
|
||||
switch (gnlh->cmd)
|
||||
{
|
||||
case OVPN_CMD_DEL_PEER:
|
||||
{
|
||||
if (!attrs[OVPN_ATTR_DEL_PEER])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: no attributes in OVPN_DEL_PEER message");
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
struct nlattr *dp_attrs[OVPN_DEL_PEER_ATTR_MAX + 1];
|
||||
if (nla_parse_nested(dp_attrs, OVPN_DEL_PEER_ATTR_MAX,
|
||||
attrs[OVPN_ATTR_DEL_PEER], NULL))
|
||||
{
|
||||
msg(D_DCO, "received bogus del peer packet data from ovpn-dco");
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
if (!dp_attrs[OVPN_DEL_PEER_ATTR_REASON])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: no reason in DEL_PEER message");
|
||||
return NL_SKIP;
|
||||
}
|
||||
if (!dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: no peer-id in DEL_PEER message");
|
||||
return NL_SKIP;
|
||||
}
|
||||
int reason = nla_get_u8(dp_attrs[OVPN_DEL_PEER_ATTR_REASON]);
|
||||
unsigned int peerid = nla_get_u32(dp_attrs[OVPN_DEL_PEER_ATTR_PEER_ID]);
|
||||
|
||||
msg(D_DCO_DEBUG, "ovpn-dco: received CMD_DEL_PEER, ifindex: %d, peer-id %d, reason: %d",
|
||||
ifindex, peerid, reason);
|
||||
dco->dco_message_peer_id = peerid;
|
||||
dco->dco_del_peer_reason = reason;
|
||||
dco->dco_message_type = OVPN_CMD_DEL_PEER;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case OVPN_CMD_PACKET:
|
||||
{
|
||||
if (!attrs[OVPN_ATTR_PACKET])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: no packet in OVPN_CMD_PACKET message");
|
||||
return NL_SKIP;
|
||||
}
|
||||
struct nlattr *pkt_attrs[OVPN_PACKET_ATTR_MAX + 1];
|
||||
|
||||
if (nla_parse_nested(pkt_attrs, OVPN_PACKET_ATTR_MAX,
|
||||
attrs[OVPN_ATTR_PACKET], NULL))
|
||||
{
|
||||
msg(D_DCO, "received bogus cmd packet data from ovpn-dco");
|
||||
return NL_SKIP;
|
||||
}
|
||||
if (!pkt_attrs[OVPN_PACKET_ATTR_PEER_ID])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: Received OVPN_CMD_PACKET message without peer id");
|
||||
return NL_SKIP;
|
||||
}
|
||||
if (!pkt_attrs[OVPN_PACKET_ATTR_PACKET])
|
||||
{
|
||||
msg(D_DCO, "ovpn-dco: Received OVPN_CMD_PACKET message without packet");
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
unsigned int peerid = nla_get_u32(pkt_attrs[OVPN_PACKET_ATTR_PEER_ID]);
|
||||
|
||||
uint8_t *data = nla_data(pkt_attrs[OVPN_PACKET_ATTR_PACKET]);
|
||||
int len = nla_len(pkt_attrs[OVPN_PACKET_ATTR_PACKET]);
|
||||
|
||||
msg(D_DCO_DEBUG, "ovpn-dco: received OVPN_PACKET_ATTR_PACKET, ifindex: %d peer-id: %d, len %d",
|
||||
ifindex, peerid, len);
|
||||
if (BLEN(&dco->dco_packet_in) > 0)
|
||||
{
|
||||
msg(D_DCO, "DCO packet buffer still full?!");
|
||||
return NL_SKIP;
|
||||
}
|
||||
buf_init(&dco->dco_packet_in, 0);
|
||||
buf_write(&dco->dco_packet_in, data, len);
|
||||
dco->dco_message_peer_id = peerid;
|
||||
dco->dco_message_type = OVPN_CMD_PACKET;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
msg(D_DCO, "ovpn-dco: received unknown command: %d", gnlh->cmd);
|
||||
dco->dco_message_type = 0;
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
int
|
||||
dco_do_read(dco_context_t *dco)
|
||||
{
|
||||
msg(D_DCO_DEBUG, __func__);
|
||||
nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco);
|
||||
|
||||
return ovpn_nl_recvmsgs(dco, __func__);
|
||||
}
|
||||
|
||||
int
|
||||
dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
|
||||
{
|
||||
packet_size_type len = BLEN(buf);
|
||||
dmsg(D_STREAM_DEBUG, "DCO: WRITE %d offset=%d", (int)len, buf->offset);
|
||||
|
||||
msg(D_DCO_DEBUG, "%s: peer-id %d, len=%d", __func__, peer_id, len);
|
||||
|
||||
struct nl_msg *nl_msg = ovpn_dco_nlmsg_create(dco, OVPN_CMD_PACKET);
|
||||
|
||||
if (!nl_msg)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct nlattr *attr = nla_nest_start(nl_msg, OVPN_ATTR_PACKET);
|
||||
int ret = -EMSGSIZE;
|
||||
NLA_PUT_U32(nl_msg, OVPN_PACKET_ATTR_PEER_ID, peer_id);
|
||||
NLA_PUT(nl_msg, OVPN_PACKET_ATTR_PACKET, len, BSTR(buf));
|
||||
nla_nest_end(nl_msg, attr);
|
||||
|
||||
ret = ovpn_nl_msg_send(dco, nl_msg, NULL, __func__);
|
||||
if (ret)
|
||||
{
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
/* return the length of the written data in case of success */
|
||||
ret = len;
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(nl_msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
dco_available(int msglevel)
|
||||
{
|
||||
if (resolve_ovpn_netlink_id(msglevel) < 0)
|
||||
{
|
||||
msg(msglevel,
|
||||
"Note: Kernel support for ovpn-dco missing, disabling data channel offload.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
|
||||
{
|
||||
if (dco && dco->nl_sock)
|
||||
{
|
||||
event_ctl(es, nl_socket_get_fd(dco->nl_sock), EVENT_READ, arg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
|
||||
60
src/openvpn/dco_linux.h
Normal file
60
src/openvpn/dco_linux.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Interface to linux dco networking code
|
||||
*
|
||||
* Copyright (C) 2020-2022 Antonio Quartulli <a@unstable.cc>
|
||||
* Copyright (C) 2020-2022 Arne Schwabe <arne@rfc2549.org>
|
||||
* Copyright (C) 2020-2022 OpenVPN Inc <sales@openvpn.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (see the file COPYING included with this
|
||||
* distribution); if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef DCO_LINUX_H
|
||||
#define DCO_LINUX_H
|
||||
|
||||
#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#include "ovpn_dco_linux.h"
|
||||
|
||||
#include <netlink/socket.h>
|
||||
#include <netlink/netlink.h>
|
||||
|
||||
typedef enum ovpn_key_slot dco_key_slot_t;
|
||||
typedef enum ovpn_cipher_alg dco_cipher_t;
|
||||
|
||||
#define DCO_SUPPORTED_CIPHERS "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct nl_sock *nl_sock;
|
||||
struct nl_cb *nl_cb;
|
||||
int status;
|
||||
|
||||
enum ovpn_mode ifmode;
|
||||
|
||||
int ovpn_dco_id;
|
||||
int ovpn_dco_mcast_id;
|
||||
|
||||
unsigned int ifindex;
|
||||
|
||||
struct buffer dco_packet_in;
|
||||
|
||||
int dco_message_type;
|
||||
int dco_message_peer_id;
|
||||
int dco_del_peer_reason;
|
||||
} dco_context_t;
|
||||
|
||||
#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
|
||||
#endif /* ifndef DCO_LINUX_H */
|
||||
|
|
@ -91,6 +91,7 @@
|
|||
#define D_OSBUF LOGLEV(3, 43, 0) /* show socket/tun/tap buffer sizes */
|
||||
#define D_PS_PROXY LOGLEV(3, 44, 0) /* messages related to --port-share option */
|
||||
#define D_IFCONFIG LOGLEV(3, 0, 0) /* show ifconfig info (don't mute) */
|
||||
#define D_DCO LOGLEV(3, 0, 0) /* show DCO related messages */
|
||||
|
||||
#define D_SHOW_PARMS LOGLEV(4, 50, 0) /* show all parameters on program initiation */
|
||||
#define D_SHOW_OCC LOGLEV(4, 51, 0) /* show options compatibility string */
|
||||
|
|
@ -114,6 +115,7 @@
|
|||
#define D_TAP_WIN_DEBUG LOGLEV(6, 69, M_DEBUG) /* show TAP-Windows driver debug info */
|
||||
#define D_CLIENT_NAT LOGLEV(6, 69, M_DEBUG) /* show client NAT debug info */
|
||||
#define D_XKEY LOGLEV(6, 69, M_DEBUG) /* show xkey-provider debug info */
|
||||
#define D_DCO_DEBUG LOGLEV(6, 69, M_DEBUG) /* show DCO related lowlevel debug messages */
|
||||
|
||||
#define D_SHOW_KEYS LOGLEV(7, 70, M_DEBUG) /* show data channel encryption keys */
|
||||
#define D_SHOW_KEY_SOURCE LOGLEV(7, 70, M_DEBUG) /* show data channel key source entropy */
|
||||
|
|
|
|||
|
|
@ -276,9 +276,10 @@
|
|||
<ClCompile Include="crypto.c" />
|
||||
<ClCompile Include="crypto_openssl.c" />
|
||||
<ClCompile Include="cryptoapi.c" />
|
||||
<ClCompile Include="env_set.c" />
|
||||
<ClCompile Include="dco_linux.c" />
|
||||
<ClCompile Include="dhcp.c" />
|
||||
<ClCompile Include="dns.c" />
|
||||
<ClCompile Include="env_set.c" />
|
||||
<ClCompile Include="error.c" />
|
||||
<ClCompile Include="event.c" />
|
||||
<ClCompile Include="fdmisc.c" />
|
||||
|
|
@ -362,6 +363,9 @@
|
|||
<ClInclude Include="crypto_backend.h" />
|
||||
<ClInclude Include="crypto_openssl.h" />
|
||||
<ClInclude Include="cryptoapi.h" />
|
||||
<ClInclude Include="dco.h" />
|
||||
<ClInclude Include="dco_internal.h" />
|
||||
<ClInclude Include="dco_linux.h" />
|
||||
<ClInclude Include="dhcp.h" />
|
||||
<ClInclude Include="dns.h" />
|
||||
<ClInclude Include="env_set.h" />
|
||||
|
|
@ -396,6 +400,7 @@
|
|||
<ClInclude Include="openvpn.h" />
|
||||
<ClInclude Include="options.h" />
|
||||
<ClInclude Include="otime.h" />
|
||||
<ClInclude Include="ovpn_dco_linux.h" />
|
||||
<ClInclude Include="packet_id.h" />
|
||||
<ClInclude Include="perf.h" />
|
||||
<ClInclude Include="ping.h" />
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@
|
|||
<ClCompile Include="cryptoapi.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dco_linux.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dhcp.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -299,6 +302,15 @@
|
|||
<ClInclude Include="cryptoapi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="dco.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dco_internal.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dco_linux.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dhcp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -398,6 +410,9 @@
|
|||
<ClInclude Include="otime.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ovpn_dco_linux.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="packet_id.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
265
src/openvpn/ovpn_dco_linux.h
Normal file
265
src/openvpn/ovpn_dco_linux.h
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||
/*
|
||||
* OpenVPN data channel accelerator
|
||||
*
|
||||
* Copyright (C) 2019-2021 OpenVPN, Inc.
|
||||
*
|
||||
* Author: James Yonan <james@openvpn.net>
|
||||
* Antonio Quartulli <antonio@openvpn.net>
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_OVPN_DCO_H_
|
||||
#define _UAPI_LINUX_OVPN_DCO_H_
|
||||
|
||||
#define OVPN_NL_NAME "ovpn-dco"
|
||||
|
||||
#define OVPN_NL_MULTICAST_GROUP_PEERS "peers"
|
||||
|
||||
/**
|
||||
* enum ovpn_nl_commands - supported netlink commands
|
||||
*/
|
||||
enum ovpn_nl_commands {
|
||||
/**
|
||||
* @OVPN_CMD_UNSPEC: unspecified command to catch errors
|
||||
*/
|
||||
OVPN_CMD_UNSPEC = 0,
|
||||
|
||||
/**
|
||||
* @OVPN_CMD_NEW_PEER: Configure peer with its crypto keys
|
||||
*/
|
||||
OVPN_CMD_NEW_PEER,
|
||||
|
||||
/**
|
||||
* @OVPN_CMD_SET_PEER: Tweak parameters for an existing peer
|
||||
*/
|
||||
OVPN_CMD_SET_PEER,
|
||||
|
||||
/**
|
||||
* @OVPN_CMD_DEL_PEER: Remove peer from internal table
|
||||
*/
|
||||
OVPN_CMD_DEL_PEER,
|
||||
|
||||
OVPN_CMD_NEW_KEY,
|
||||
|
||||
OVPN_CMD_SWAP_KEYS,
|
||||
|
||||
OVPN_CMD_DEL_KEY,
|
||||
|
||||
/**
|
||||
* @OVPN_CMD_REGISTER_PACKET: Register for specific packet types to be
|
||||
* forwarded to userspace
|
||||
*/
|
||||
OVPN_CMD_REGISTER_PACKET,
|
||||
|
||||
/**
|
||||
* @OVPN_CMD_PACKET: Send a packet from userspace to kernelspace. Also
|
||||
* used to send to userspace packets for which a process had registered
|
||||
* with OVPN_CMD_REGISTER_PACKET
|
||||
*/
|
||||
OVPN_CMD_PACKET,
|
||||
|
||||
/**
|
||||
* @OVPN_CMD_GET_PEER: Retrieve the status of a peer or all peers
|
||||
*/
|
||||
OVPN_CMD_GET_PEER,
|
||||
};
|
||||
|
||||
enum ovpn_cipher_alg {
|
||||
/**
|
||||
* @OVPN_CIPHER_ALG_NONE: No encryption - reserved for debugging only
|
||||
*/
|
||||
OVPN_CIPHER_ALG_NONE = 0,
|
||||
/**
|
||||
* @OVPN_CIPHER_ALG_AES_GCM: AES-GCM AEAD cipher with any allowed key size
|
||||
*/
|
||||
OVPN_CIPHER_ALG_AES_GCM,
|
||||
/**
|
||||
* @OVPN_CIPHER_ALG_CHACHA20_POLY1305: ChaCha20Poly1305 AEAD cipher
|
||||
*/
|
||||
OVPN_CIPHER_ALG_CHACHA20_POLY1305,
|
||||
};
|
||||
|
||||
enum ovpn_del_peer_reason {
|
||||
__OVPN_DEL_PEER_REASON_FIRST,
|
||||
OVPN_DEL_PEER_REASON_TEARDOWN = __OVPN_DEL_PEER_REASON_FIRST,
|
||||
OVPN_DEL_PEER_REASON_USERSPACE,
|
||||
OVPN_DEL_PEER_REASON_EXPIRED,
|
||||
OVPN_DEL_PEER_REASON_TRANSPORT_ERROR,
|
||||
__OVPN_DEL_PEER_REASON_AFTER_LAST
|
||||
};
|
||||
|
||||
enum ovpn_key_slot {
|
||||
__OVPN_KEY_SLOT_FIRST,
|
||||
OVPN_KEY_SLOT_PRIMARY = __OVPN_KEY_SLOT_FIRST,
|
||||
OVPN_KEY_SLOT_SECONDARY,
|
||||
__OVPN_KEY_SLOT_AFTER_LAST,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_attrs {
|
||||
OVPN_ATTR_UNSPEC = 0,
|
||||
OVPN_ATTR_IFINDEX,
|
||||
OVPN_ATTR_NEW_PEER,
|
||||
OVPN_ATTR_SET_PEER,
|
||||
OVPN_ATTR_DEL_PEER,
|
||||
OVPN_ATTR_NEW_KEY,
|
||||
OVPN_ATTR_SWAP_KEYS,
|
||||
OVPN_ATTR_DEL_KEY,
|
||||
OVPN_ATTR_PACKET,
|
||||
OVPN_ATTR_GET_PEER,
|
||||
|
||||
__OVPN_ATTR_AFTER_LAST,
|
||||
OVPN_ATTR_MAX = __OVPN_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_key_dir_attrs {
|
||||
OVPN_KEY_DIR_ATTR_UNSPEC = 0,
|
||||
OVPN_KEY_DIR_ATTR_CIPHER_KEY,
|
||||
OVPN_KEY_DIR_ATTR_NONCE_TAIL,
|
||||
|
||||
__OVPN_KEY_DIR_ATTR_AFTER_LAST,
|
||||
OVPN_KEY_DIR_ATTR_MAX = __OVPN_KEY_DIR_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_new_key_attrs {
|
||||
OVPN_NEW_KEY_ATTR_UNSPEC = 0,
|
||||
OVPN_NEW_KEY_ATTR_PEER_ID,
|
||||
OVPN_NEW_KEY_ATTR_KEY_SLOT,
|
||||
OVPN_NEW_KEY_ATTR_KEY_ID,
|
||||
OVPN_NEW_KEY_ATTR_CIPHER_ALG,
|
||||
OVPN_NEW_KEY_ATTR_ENCRYPT_KEY,
|
||||
OVPN_NEW_KEY_ATTR_DECRYPT_KEY,
|
||||
|
||||
__OVPN_NEW_KEY_ATTR_AFTER_LAST,
|
||||
OVPN_NEW_KEY_ATTR_MAX = __OVPN_NEW_KEY_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_del_key_attrs {
|
||||
OVPN_DEL_KEY_ATTR_UNSPEC = 0,
|
||||
OVPN_DEL_KEY_ATTR_PEER_ID,
|
||||
OVPN_DEL_KEY_ATTR_KEY_SLOT,
|
||||
|
||||
__OVPN_DEL_KEY_ATTR_AFTER_LAST,
|
||||
OVPN_DEL_KEY_ATTR_MAX = __OVPN_DEL_KEY_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_swap_keys_attrs {
|
||||
OVPN_SWAP_KEYS_ATTR_UNSPEC = 0,
|
||||
OVPN_SWAP_KEYS_ATTR_PEER_ID,
|
||||
|
||||
__OVPN_SWAP_KEYS_ATTR_AFTER_LAST,
|
||||
OVPN_SWAP_KEYS_ATTR_MAX = __OVPN_SWAP_KEYS_ATTR_AFTER_LAST - 1,
|
||||
|
||||
};
|
||||
|
||||
enum ovpn_netlink_new_peer_attrs {
|
||||
OVPN_NEW_PEER_ATTR_UNSPEC = 0,
|
||||
OVPN_NEW_PEER_ATTR_PEER_ID,
|
||||
OVPN_NEW_PEER_ATTR_SOCKADDR_REMOTE,
|
||||
OVPN_NEW_PEER_ATTR_SOCKET,
|
||||
OVPN_NEW_PEER_ATTR_IPV4,
|
||||
OVPN_NEW_PEER_ATTR_IPV6,
|
||||
OVPN_NEW_PEER_ATTR_LOCAL_IP,
|
||||
|
||||
__OVPN_NEW_PEER_ATTR_AFTER_LAST,
|
||||
OVPN_NEW_PEER_ATTR_MAX = __OVPN_NEW_PEER_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_set_peer_attrs {
|
||||
OVPN_SET_PEER_ATTR_UNSPEC = 0,
|
||||
OVPN_SET_PEER_ATTR_PEER_ID,
|
||||
OVPN_SET_PEER_ATTR_KEEPALIVE_INTERVAL,
|
||||
OVPN_SET_PEER_ATTR_KEEPALIVE_TIMEOUT,
|
||||
|
||||
__OVPN_SET_PEER_ATTR_AFTER_LAST,
|
||||
OVPN_SET_PEER_ATTR_MAX = __OVPN_SET_PEER_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_del_peer_attrs {
|
||||
OVPN_DEL_PEER_ATTR_UNSPEC = 0,
|
||||
OVPN_DEL_PEER_ATTR_REASON,
|
||||
OVPN_DEL_PEER_ATTR_PEER_ID,
|
||||
|
||||
__OVPN_DEL_PEER_ATTR_AFTER_LAST,
|
||||
OVPN_DEL_PEER_ATTR_MAX = __OVPN_DEL_PEER_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_get_peer_attrs {
|
||||
OVPN_GET_PEER_ATTR_UNSPEC = 0,
|
||||
OVPN_GET_PEER_ATTR_PEER_ID,
|
||||
|
||||
__OVPN_GET_PEER_ATTR_AFTER_LAST,
|
||||
OVPN_GET_PEER_ATTR_MAX = __OVPN_GET_PEER_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_get_peer_response_attrs {
|
||||
OVPN_GET_PEER_RESP_ATTR_UNSPEC = 0,
|
||||
OVPN_GET_PEER_RESP_ATTR_PEER_ID,
|
||||
OVPN_GET_PEER_RESP_ATTR_SOCKADDR_REMOTE,
|
||||
OVPN_GET_PEER_RESP_ATTR_IPV4,
|
||||
OVPN_GET_PEER_RESP_ATTR_IPV6,
|
||||
OVPN_GET_PEER_RESP_ATTR_LOCAL_IP,
|
||||
OVPN_GET_PEER_RESP_ATTR_LOCAL_PORT,
|
||||
OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_INTERVAL,
|
||||
OVPN_GET_PEER_RESP_ATTR_KEEPALIVE_TIMEOUT,
|
||||
OVPN_GET_PEER_RESP_ATTR_RX_BYTES,
|
||||
OVPN_GET_PEER_RESP_ATTR_TX_BYTES,
|
||||
OVPN_GET_PEER_RESP_ATTR_RX_PACKETS,
|
||||
OVPN_GET_PEER_RESP_ATTR_TX_PACKETS,
|
||||
|
||||
__OVPN_GET_PEER_RESP_ATTR_AFTER_LAST,
|
||||
OVPN_GET_PEER_RESP_ATTR_MAX = __OVPN_GET_PEER_RESP_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_peer_stats_attrs {
|
||||
OVPN_PEER_STATS_ATTR_UNSPEC = 0,
|
||||
OVPN_PEER_STATS_BYTES,
|
||||
OVPN_PEER_STATS_PACKETS,
|
||||
|
||||
__OVPN_PEER_STATS_ATTR_AFTER_LAST,
|
||||
OVPN_PEER_STATS_ATTR_MAX = __OVPN_PEER_STATS_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_peer_attrs {
|
||||
OVPN_PEER_ATTR_UNSPEC = 0,
|
||||
OVPN_PEER_ATTR_PEER_ID,
|
||||
OVPN_PEER_ATTR_SOCKADDR_REMOTE,
|
||||
OVPN_PEER_ATTR_IPV4,
|
||||
OVPN_PEER_ATTR_IPV6,
|
||||
OVPN_PEER_ATTR_LOCAL_IP,
|
||||
OVPN_PEER_ATTR_KEEPALIVE_INTERVAL,
|
||||
OVPN_PEER_ATTR_KEEPALIVE_TIMEOUT,
|
||||
OVPN_PEER_ATTR_ENCRYPT_KEY,
|
||||
OVPN_PEER_ATTR_DECRYPT_KEY,
|
||||
OVPN_PEER_ATTR_RX_STATS,
|
||||
OVPN_PEER_ATTR_TX_STATS,
|
||||
|
||||
__OVPN_PEER_ATTR_AFTER_LAST,
|
||||
OVPN_PEER_ATTR_MAX = __OVPN_PEER_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_netlink_packet_attrs {
|
||||
OVPN_PACKET_ATTR_UNSPEC = 0,
|
||||
OVPN_PACKET_ATTR_PACKET,
|
||||
OVPN_PACKET_ATTR_PEER_ID,
|
||||
|
||||
__OVPN_PACKET_ATTR_AFTER_LAST,
|
||||
OVPN_PACKET_ATTR_MAX = __OVPN_PACKET_ATTR_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_ifla_attrs {
|
||||
IFLA_OVPN_UNSPEC = 0,
|
||||
IFLA_OVPN_MODE,
|
||||
|
||||
__IFLA_OVPN_AFTER_LAST,
|
||||
IFLA_OVPN_MAX = __IFLA_OVPN_AFTER_LAST - 1,
|
||||
};
|
||||
|
||||
enum ovpn_mode {
|
||||
__OVPN_MODE_FIRST = 0,
|
||||
OVPN_MODE_P2P = __OVPN_MODE_FIRST,
|
||||
OVPN_MODE_MP,
|
||||
|
||||
__OVPN_MODE_AFTER_LAST,
|
||||
};
|
||||
|
||||
#endif /* _UAPI_LINUX_OVPN_DCO_H_ */
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
#include "misc.h"
|
||||
#include "networking.h"
|
||||
#include "ring_buffer.h"
|
||||
#include "dco.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WINTUN_COMPONENT_ID "wintun"
|
||||
|
|
@ -214,6 +215,8 @@ struct tuntap
|
|||
#endif
|
||||
/* used for printing status info only */
|
||||
unsigned int rwflags_debug;
|
||||
|
||||
dco_context_t dco;
|
||||
};
|
||||
|
||||
static inline bool
|
||||
|
|
|
|||
Loading…
Reference in a new issue