* [Openvpn-devel] [PATCH 01/25] dco: introduce low-level code for handling ovpn-dco in the Linux kernel
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:03 ` Arne Schwabe
2022-06-28 15:07 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 02/25] dco: add helper function to detect if DCO is enabled or not Antonio Quartulli
` (23 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
configure.ac | 34 +
dev-tools/special-files.lst | 1 +
src/openvpn/Makefile.am | 3 +
src/openvpn/dco.h | 165 +++++
src/openvpn/dco_internal.h | 78 +++
src/openvpn/dco_linux.c | 934 ++++++++++++++++++++++++++++
src/openvpn/dco_linux.h | 60 ++
src/openvpn/errlevel.h | 2 +
src/openvpn/openvpn.vcxproj | 7 +-
src/openvpn/openvpn.vcxproj.filters | 15 +
src/openvpn/ovpn_dco_linux.h | 265 ++++++++
src/openvpn/tun.h | 3 +
12 files changed, 1566 insertions(+), 1 deletion(-)
create mode 100644 src/openvpn/dco.h
create mode 100644 src/openvpn/dco_internal.h
create mode 100644 src/openvpn/dco_linux.c
create mode 100644 src/openvpn/dco_linux.h
create mode 100644 src/openvpn/ovpn_dco_linux.h
diff --git a/configure.ac b/configure.ac
index 9c898718..353da08c 100644
--- a/configure.ac
+++ b/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
diff --git a/dev-tools/special-files.lst b/dev-tools/special-files.lst
index 64ee9e1a..33e830d7 100644
--- a/dev-tools/special-files.lst
+++ b/dev-tools/special-files.lst
@@ -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
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 57729480..91635b67 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -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 \
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
new file mode 100644
index 00000000..dcadba78
--- /dev/null
+++ b/src/openvpn/dco.h
@@ -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@...1227...>
+ * Copyright (C) 2021-2022 Antonio Quartulli <a@...2181...>
+ * Copyright (C) 2021-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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 */
diff --git a/src/openvpn/dco_internal.h b/src/openvpn/dco_internal.h
new file mode 100644
index 00000000..3ceb26d6
--- /dev/null
+++ b/src/openvpn/dco_internal.h
@@ -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@...2181...>
+ * Copyright (C) 2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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 */
diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c
new file mode 100644
index 00000000..5e77139a
--- /dev/null
+++ b/src/openvpn/dco_linux.c
@@ -0,0 +1,934 @@
+/*
+ * Interface to linux dco networking code
+ *
+ * Copyright (C) 2020-2022 Antonio Quartulli <a@...2181...>
+ * Copyright (C) 2020-2022 Arne Schwabe <arne@...1227...>
+ * Copyright (C) 2020-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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) */
diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h
new file mode 100644
index 00000000..e0e59fa6
--- /dev/null
+++ b/src/openvpn/dco_linux.h
@@ -0,0 +1,60 @@
+/*
+ * Interface to linux dco networking code
+ *
+ * Copyright (C) 2020-2022 Antonio Quartulli <a@...2181...>
+ * Copyright (C) 2020-2022 Arne Schwabe <arne@...1227...>
+ * Copyright (C) 2020-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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 */
diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h
index e616a496..5bb1e65e 100644
--- a/src/openvpn/errlevel.h
+++ b/src/openvpn/errlevel.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 */
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index 860ef892..bc1a0300 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -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" />
diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters
index f76e5923..3c21a4c6 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -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>
diff --git a/src/openvpn/ovpn_dco_linux.h b/src/openvpn/ovpn_dco_linux.h
new file mode 100644
index 00000000..beca1beb
--- /dev/null
+++ b/src/openvpn/ovpn_dco_linux.h
@@ -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@...515...>
+ * Antonio Quartulli <antonio@...515...>
+ */
+
+#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_ */
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 4bc35916..60cd574d 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.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
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 01/25] dco: introduce low-level code for handling ovpn-dco in the Linux kernel
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 01/25] dco: introduce low-level code for handling ovpn-dco in the Linux kernel Antonio Quartulli
@ 2022-06-27 11:03 ` Arne Schwabe
2022-06-28 15:07 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:03 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> Signed-off-by: Antonio Quartulli <a@...2181...>
A lot of the code is still my code so my review not really valid for
those parts but I think Antonio has check this code.
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH applied] Re: dco: introduce low-level code for handling ovpn-dco in the Linux kernel
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 01/25] dco: introduce low-level code for handling ovpn-dco in the Linux kernel Antonio Quartulli
2022-06-27 11:03 ` Arne Schwabe
@ 2022-06-28 15:07 ` Gert Doering
2022-06-28 15:46 ` Heiko Hund
1 sibling, 1 reply; 157+ messages in thread
From: Gert Doering @ 2022-06-28 15:07 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
I have stared at this code before, and it "seems to be reasonable".
What I *can* attest is that it does not break existing "master" functionality
on client or server, Linux or FreeBSD or "make distcheck" (autoconf changes,
new modules, ...). I did not expect anything, but this was sort of the
risk at this point.
Calling configure with --enable-dco does compile the new stuff, so it
is at least "compile safe" (and no warnings).
Some review comments that could be fixed in a followup patch:
- some of the functions (like "ovpn_dco_nlmsg_create()", "dco_new_peer()")
could definitely use a bit of commenting so non-netlink-experts would
understand what these functions do, and why... (and I'm sure I mentioned
that before).
- dco_new_peer() treats "localaddr" and "remoteaddr" differently
when packing, which looks weird -> I think a comment that this is
to reduce kernel code ("we need full sockaddr for the remote, and
only the local IP address for local") would avoid confusion.
- openvpn_dco_init_netlink() carries this TODO
+ /* TODO: Why are we setting this buffer size? */
... so, someone should know, and add an appropriate comment :-)
- a few words of explanation on "... multicast message sent by the
ovpn-dco kernel module" might be good - I see you subscribe to it,
and explain mcast_family_handler(), but I can not find any indication
of "what sort of messages would we expect to see here?".
- ovpn_dco_nlmsg_create() has
+ if (!nl_msg)
+ {
+ msg(M_ERR, "cannot allocate netlink message");
+ return NULL;
+ }
... which never returns, as M_ERR is fatal. OTOH, *some* callers do
+ 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",
... but not *all* of them (dco_new_peer() does not check nl_msg at all).
So this should be made consistent - like, either ovpn_dco_nlmsg_create()
will M_ERR on allocation failure, or return NULL (and never M_ERR). If
it does M_ERR itself, no need to check that in the callers...
- open_tun_dco() reports an *error* top create DCO interface with D_DCO_DEBUG
+ msg(D_DCO_DEBUG, "Cannot create DCO interface %s: %d", dev, ret);
... and two lines down, an error on if_nametoindex() leads to a FATAL
+ msg(M_FATAL, "DCO: cannot retrieve ifindex for interface %s", dev);
... so what? Is this function allowed to fail, and return "ret < 0",
or should it M_ERR/M_FATAL on errors? There is no pre-function comment
that could clarify the intent...
- dco_new_key() has code to support "none"
+ if (dco_cipher != OVPN_CIPHER_ALG_NONE)
+ {
... but dco_cipher can never be OVPN_CIPHER_ALG_NONE anymore... and if
that indent is removed, the NLA_PUT calls for _iv do not need to wrap :-)
- dco_set_peer() does not pass on the mss yet
- in ovpn_handle_msg(), can this be called uninitialized?
+ if (!attrs[OVPN_ATTR_IFINDEX])
+ {
+ msg(D_DCO, "ovpn-dco: Received message without ifindex");
+ return NL_SKIP;
+ }
(not sure if nla_parse() will ensure that there is enough attributes,
or just "well-formed" - and whether *attrs actually gets zeroed
by nla_parse(), as openvpn_handle_msg() has no CLEAR() )
- same function - under which conditions can this fire? Kernel getting
confused?
+ 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",
Uncrustify has complained at me when I merged the patch (because in that
moment, ovpn_dco_linux.h was "newly modified" and the exclusion rule
does not match on the pre-commit-hook) - but as discussed, this is a bit
complicated due to "kernel style" vs "openvpn style", so I've left it
alone for the moment.
Your patch has been applied to the master branch.
commit e34437c26b764851555e4acbe2ccca6bec235c7e
Author: Antonio Quartulli
Date: Fri Jun 24 10:37:45 2022 +0200
dco: introduce low-level code for handling ovpn-dco in the Linux kernel
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Arne Schwabe <arne@...1227...>
Message-Id: <20220624083809.23487-2-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24512.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH applied] Re: dco: introduce low-level code for handling ovpn-dco in the Linux kernel
2022-06-28 15:07 ` [Openvpn-devel] [PATCH applied] " Gert Doering
@ 2022-06-28 15:46 ` Heiko Hund
2022-06-28 15:50 ` Gert Doering
0 siblings, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-06-28 15:46 UTC (permalink / raw)
To: openvpn-devel
On Dienstag, 28. Juni 2022 17:07:14 CEST Gert Doering wrote:
> Uncrustify has complained at me when I merged the patch (because in that
> moment, ovpn_dco_linux.h was "newly modified" and the exclusion rule
> does not match on the pre-commit-hook) - but as discussed, this is a bit
> complicated due to "kernel style" vs "openvpn style", so I've left it
> alone for the moment.
That's something which needs to be improved. Who is dev-tools/special-files.lst
intended for? Can uncrustify read it?
Heiko
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH applied] Re: dco: introduce low-level code for handling ovpn-dco in the Linux kernel
2022-06-28 15:46 ` Heiko Hund
@ 2022-06-28 15:50 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-06-28 15:50 UTC (permalink / raw)
To: Heiko Hund <heiko@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 1435 bytes --]
Hi,
On Tue, Jun 28, 2022 at 05:46:40PM +0200, Heiko Hund wrote:
> On Dienstag, 28. Juni 2022 17:07:14 CEST Gert Doering wrote:
> > Uncrustify has complained at me when I merged the patch (because in that
> > moment, ovpn_dco_linux.h was "newly modified" and the exclusion rule
> > does not match on the pre-commit-hook) - but as discussed, this is a bit
> > complicated due to "kernel style" vs "openvpn style", so I've left it
> > alone for the moment.
>
> That's something which needs to be improved. Who is dev-tools/special-files.lst
> intended for? Can uncrustify read it?
We have dev-tools/reformat-all.sh which was used for the "Grand Reformat"
commits - that one reads the special-files.lst, which can contain
"E: <exclude this file>" or some other magic stuff for "pre/post-patch
because uncrustify is stupid".
Having the pre-commit-hook honour the E: exclusion lines would certainly
make handling these files a bit easier - but then, I do not expect updates
to them very often, so this is not super high priority.
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 02/25] dco: add helper function to detect if DCO is enabled or not
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 01/25] dco: introduce low-level code for handling ovpn-dco in the Linux kernel Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:47 ` Arne Schwabe
` (2 more replies)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 03/25] dco: use specific metric when installing routes Antonio Quartulli
` (22 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/options.h | 20 ++++++++++++++++++++
src/openvpn/tun.h | 1 +
2 files changed, 21 insertions(+)
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index c2937dc3..8152e755 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -879,4 +879,24 @@ void options_string_import(struct options *options,
bool key_is_external(const struct options *options);
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+
+/**
+ * Returns whether the current configuration has dco enabled.
+ */
+static inline bool
+dco_enabled(const struct options *o)
+{
+ return !o->tuntap_options.disable_dco;
+}
+
+#else /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+
+static inline bool
+dco_enabled(const struct options *o)
+{
+ return false;
+}
+
+#endif
#endif /* ifndef OPTIONS_H */
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 60cd574d..5fcea590 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -139,6 +139,7 @@ struct tuntap_options {
struct tuntap_options {
int txqueuelen;
+ bool disable_dco;
};
#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH 03/25] dco: use specific metric when installing routes
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 01/25] dco: introduce low-level code for handling ovpn-dco in the Linux kernel Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 02/25] dco: add helper function to detect if DCO is enabled or not Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:04 ` Arne Schwabe
2022-06-28 18:56 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 04/25] dco: create DCO interface using SITNL Antonio Quartulli
` (21 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
When using DCO iroutes and routes all live in the same routing table,
However, the latter should always come after the former.
for this reason assign a default metric of 200 to routes. iroutes will
later get a metric of 100.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/dco.h | 2 ++
src/openvpn/init.c | 10 ++++++++++
2 files changed, 12 insertions(+)
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index dcadba78..063e5028 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -38,6 +38,8 @@ struct event_set;
struct options;
struct tuntap;
+#define DCO_DEFAULT_METRIC 200
+
#if defined(ENABLE_DCO)
/**
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 638fd10c..f586bfc7 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1385,6 +1385,11 @@ do_init_route_list(const struct options *options,
int dev = dev_type_enum(options->dev, options->dev_type);
int metric = 0;
+ if (dco_enabled(options))
+ {
+ metric = DCO_DEFAULT_METRIC;
+ }
+
if (dev == DEV_TYPE_TUN && (options->topology == TOP_NET30 || options->topology == TOP_P2P))
{
gw = options->ifconfig_remote_netmask;
@@ -1421,6 +1426,11 @@ do_init_route_ipv6_list(const struct options *options,
const char *gw = NULL;
int metric = -1; /* no metric set */
+ if (dco_enabled(options))
+ {
+ metric = DCO_DEFAULT_METRIC;
+ }
+
gw = options->ifconfig_ipv6_remote; /* default GW = remote end */
if (options->route_ipv6_default_gateway)
{
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 03/25] dco: use specific metric when installing routes
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 03/25] dco: use specific metric when installing routes Antonio Quartulli
@ 2022-06-27 11:04 ` Arne Schwabe
2022-06-28 18:56 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:04 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> When using DCO iroutes and routes all live in the same routing table,
> However, the latter should always come after the former.
>
> for this reason assign a default metric of 200 to routes. iroutes will
> later get a metric of 100.
Can you also add documentation of this? (if it is not coming in a later
patch)
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 03/25] dco: use specific metric when installing routes
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 03/25] dco: use specific metric when installing routes Antonio Quartulli
2022-06-27 11:04 ` Arne Schwabe
@ 2022-06-28 18:56 ` Antonio Quartulli
2022-06-28 20:52 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-28 18:56 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
When using DCO iroutes and routes all live in the same routing table,
However, the latter should always come after the former.
for this reason assign a default metric of 200 to routes. iroutes will
later get a metric of 100.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* added comments before "(if (dco_enabled(c))" to explain why we need
a different metric
src/openvpn/dco.h | 2 ++
src/openvpn/init.c | 15 +++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index dcadba78..063e5028 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -38,6 +38,8 @@ struct event_set;
struct options;
struct tuntap;
+#define DCO_DEFAULT_METRIC 200
+
#if defined(ENABLE_DCO)
/**
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 638fd10c..03221cbb 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1385,6 +1385,15 @@ do_init_route_list(const struct options *options,
int dev = dev_type_enum(options->dev, options->dev_type);
int metric = 0;
+ /* if DCO is enabled we have both regular routes and iroutes in the system
+ * routing table, and normal routes must have a higher metric for that to
+ * work so that iroutes are always matched first
+ */
+ if (dco_enabled(options))
+ {
+ metric = DCO_DEFAULT_METRIC;
+ }
+
if (dev == DEV_TYPE_TUN && (options->topology == TOP_NET30 || options->topology == TOP_P2P))
{
gw = options->ifconfig_remote_netmask;
@@ -1421,6 +1430,12 @@ do_init_route_ipv6_list(const struct options *options,
const char *gw = NULL;
int metric = -1; /* no metric set */
+ /* see explanation in do_init_route_list() */
+ if (dco_enabled(options))
+ {
+ metric = DCO_DEFAULT_METRIC;
+ }
+
gw = options->ifconfig_ipv6_remote; /* default GW = remote end */
if (options->route_ipv6_default_gateway)
{
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: use specific metric when installing routes
2022-06-28 18:56 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-06-28 20:52 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-06-28 20:52 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
Arne has already ACKed the functionality (and I'm fine with that), and
I ACK the extra comments. We could spend days on perfect wordsmithing
here, but I think they are good enough.
Please do not forget to include the metric thing in the "overall
documentation" patch that was mentioned :-)
Your patch has been applied to the master branch.
commit 90d9c38b116b8c816596d71a5cf6db7fd068d61a
Author: Antonio Quartulli
Date: Tue Jun 28 20:56:23 2022 +0200
dco: use specific metric when installing routes
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Arne Schwabe <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220628185623.1734-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24599.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 04/25] dco: create DCO interface using SITNL
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (2 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 03/25] dco: use specific metric when installing routes Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:06 ` Arne Schwabe
2022-06-28 15:21 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
` (20 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/networking_sitnl.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c
index bffcb067..0944ad0a 100644
--- a/src/openvpn/networking_sitnl.c
+++ b/src/openvpn/networking_sitnl.c
@@ -28,6 +28,7 @@
#include "syshead.h"
+#include "dco.h"
#include "errlevel.h"
#include "buffer.h"
#include "misc.h"
@@ -1344,6 +1345,16 @@ net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type,
struct rtattr *linkinfo = SITNL_NEST(&req.n, sizeof(req), IFLA_LINKINFO);
SITNL_ADDATTR(&req.n, sizeof(req), IFLA_INFO_KIND, type, strlen(type) + 1);
+#if defined(ENABLE_DCO)
+ if (arg && (strcmp(type, "ovpn-dco") == 0))
+ {
+ dco_context_t *dco = arg;
+ struct rtattr *data = SITNL_NEST(&req.n, sizeof(req), IFLA_INFO_DATA);
+ SITNL_ADDATTR(&req.n, sizeof(req), IFLA_OVPN_MODE, &dco->ifmode,
+ sizeof(uint8_t));
+ SITNL_NEST_END(&req.n, data);
+ }
+#endif
SITNL_NEST_END(&req.n, linkinfo);
req.i.ifi_family = AF_PACKET;
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 04/25] dco: create DCO interface using SITNL
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 04/25] dco: create DCO interface using SITNL Antonio Quartulli
@ 2022-06-27 11:06 ` Arne Schwabe
2022-06-28 15:21 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:06 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> src/openvpn/networking_sitnl.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c
> index bffcb067..0944ad0a 100644
> --- a/src/openvpn/networking_sitnl.c
> +++ b/src/openvpn/networking_sitnl.c
> @@ -28,6 +28,7 @@
>
> #include "syshead.h"
>
> +#include "dco.h"
> #include "errlevel.h"
> #include "buffer.h"
> #include "misc.h"
> @@ -1344,6 +1345,16 @@ net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type,
>
> struct rtattr *linkinfo = SITNL_NEST(&req.n, sizeof(req), IFLA_LINKINFO);
> SITNL_ADDATTR(&req.n, sizeof(req), IFLA_INFO_KIND, type, strlen(type) + 1);
> +#if defined(ENABLE_DCO)
> + if (arg && (strcmp(type, "ovpn-dco") == 0))
> + {
> + dco_context_t *dco = arg;
> + struct rtattr *data = SITNL_NEST(&req.n, sizeof(req), IFLA_INFO_DATA);
> + SITNL_ADDATTR(&req.n, sizeof(req), IFLA_OVPN_MODE, &dco->ifmode,
> + sizeof(uint8_t));
> + SITNL_NEST_END(&req.n, data);
> + }
> +#endif
> SITNL_NEST_END(&req.n, linkinfo);
>
> req.i.ifi_family = AF_PACKET;
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: create DCO interface using SITNL
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 04/25] dco: create DCO interface using SITNL Antonio Quartulli
2022-06-27 11:06 ` Arne Schwabe
@ 2022-06-28 15:21 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-06-28 15:21 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
This does not really do anything yet, and it won't affect "non dco"
builds. For DCO builds, it looks reasonable and compiles without
warning (and all it really does is "if dco, then add IFLA_OVPN_MODE"
to the netlink message, passing in p2p / p2mp mode)
Your patch has been applied to the master branch.
commit 2a02c90a4501b04a43a25824fe9f7b980e10fc40
Author: Antonio Quartulli
Date: Fri Jun 24 10:37:48 2022 +0200
dco: create DCO interface using SITNL
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Arne Schwabe <arne@...1227...>
Message-Id: <20220624083809.23487-5-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24514.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (3 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 04/25] dco: create DCO interface using SITNL Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
[not found] ` <1b657825-faa9-ea35-fb12-84a940c1e0ba@...1227...>
` (2 more replies)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object Antonio Quartulli
` (19 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/init.c | 2 +-
src/openvpn/tun.c | 133 +++++++++++++++++++++++++++++++++------------
src/openvpn/tun.h | 2 +-
3 files changed, 101 insertions(+), 36 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index f586bfc7..7099eba4 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1758,7 +1758,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..5eefb0c6 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1718,10 +1718,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1739,6 +1739,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
if (dev_node)
{
openvpn_snprintf(tunname, sizeof(tunname), "%s", dev_node);
+ strncpynt(dynamic_name, dev_node, sizeof(dynamic_name));
}
else
{
@@ -1780,6 +1781,19 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ strncpynt(tunname, dynamic_name,
+ sizeof(dynamic_name));
+ break;
+ }
+ }
+ else
+#endif
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1798,33 +1812,57 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
else
{
openvpn_snprintf(tunname, sizeof(tunname), "/dev/%s", dev);
+ strncpynt(dynamic_name, dev, sizeof(dynamic_name));
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ int ret = open_tun_dco(tt, ctx, dynamic_name);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end",
+ dynamic_name);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s: %d", dynamic_name, ret);
+ }
+ dynamic_opened = true;
}
-
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ }
+ else
+#endif
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
}
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
msg(M_INFO, "TUN/TAP device %s opened", tunname);
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1880,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +1978,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +1990,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2102,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2128,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,7 +2246,16 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
- close_tun_generic(tt);
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+ else
+#endif
+ {
+ close_tun_generic(tt);
+ }
free(tt);
}
@@ -2222,7 +2278,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2631,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2726,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2867,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +2996,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3225,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3252,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3275,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3333,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6643,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6945,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread[parent not found: <1b657825-faa9-ea35-fb12-84a940c1e0ba@...1227...>]
* Re: [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case
[not found] ` <1b657825-faa9-ea35-fb12-84a940c1e0ba@...1227...>
@ 2022-06-27 11:32 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-27 11:32 UTC (permalink / raw)
To: Arne Schwabe <arne@
Hi,
On 27/06/2022 13:09, Arne Schwabe wrote:
> Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
>> + int ret = open_tun_dco(tt, ctx, dynamic_name);
>> + if (ret == -EEXIST)
>> + {
>> + msg(M_INFO, "TUN/TAP device %s exists previously,
>> keep at program end",
>> + dynamic_name);
>> + tt->persistent_if = true;
>> + }
>
>
> I think here the error message is really confusing. What does "keep at
> program end" mean? And if we fail at opening DCO tun device why do we
> talk about a TUN/TAP device and not about DCO?
IIRC I simply took this message from "the same scenario but without
DCO", so the text was already bad :-D
But I agree we can improve it.
Thanks!
>
> Arne
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
[not found] ` <1b657825-faa9-ea35-fb12-84a940c1e0ba@...1227...>
@ 2022-06-27 11:47 ` Arne Schwabe
2022-06-28 18:58 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:47 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> + int ret = open_tun_dco(tt, ctx, dynamic_name);
> + if (ret == -EEXIST)
> + {
> + msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end",
> + dynamic_name);
> + tt->persistent_if = true;
> + }
I think here the error message is really confusing. What does "keep at
program end" mean? And if we fail at opening DCO tun device why do we
talk about a TUN/TAP device and not about DCO?
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v2 05/25] dco: let open_tun_generic handle the DCO case
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
[not found] ` <1b657825-faa9-ea35-fb12-84a940c1e0ba@...1227...>
2022-06-27 11:47 ` Arne Schwabe
@ 2022-06-28 18:58 ` Antonio Quartulli
2022-06-29 12:29 ` [Openvpn-devel] [PATCH v3] " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-28 18:58 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 2 +-
src/openvpn/tun.c | 133 +++++++++++++++++++++++++++++++++------------
src/openvpn/tun.h | 2 +-
3 files changed, 101 insertions(+), 36 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..c9d05c31 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1763,7 +1763,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..ebab8e6c 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1718,10 +1718,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1739,6 +1739,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
if (dev_node)
{
openvpn_snprintf(tunname, sizeof(tunname), "%s", dev_node);
+ strncpynt(dynamic_name, dev_node, sizeof(dynamic_name));
}
else
{
@@ -1780,6 +1781,19 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ strncpynt(tunname, dynamic_name,
+ sizeof(dynamic_name));
+ break;
+ }
+ }
+ else
+#endif
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1798,33 +1812,57 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
else
{
openvpn_snprintf(tunname, sizeof(tunname), "/dev/%s", dev);
+ strncpynt(dynamic_name, dev, sizeof(dynamic_name));
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ int ret = open_tun_dco(tt, ctx, dynamic_name);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dynamic_name);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s: %d", dynamic_name, ret);
+ }
+ dynamic_opened = true;
}
-
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ }
+ else
+#endif
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
}
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
msg(M_INFO, "TUN/TAP device %s opened", tunname);
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1880,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +1978,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +1990,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2102,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2128,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,7 +2246,16 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
- close_tun_generic(tt);
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+ else
+#endif
+ {
+ close_tun_generic(tt);
+ }
free(tt);
}
@@ -2222,7 +2278,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2631,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2726,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2867,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +2996,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3225,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3252,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3275,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3333,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6643,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6945,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v3] dco: let open_tun_generic handle the DCO case
2022-06-28 18:58 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-06-29 12:29 ` Antonio Quartulli
2022-06-29 12:49 ` [Openvpn-devel] [PATCH v4] " Antonio Quartulli
0 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-29 12:29 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 2 +-
src/openvpn/options.c | 7 +++
src/openvpn/tun.c | 137 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
4 files changed, 113 insertions(+), 35 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..c9d05c31 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1763,7 +1763,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..3fdd8e43 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1718,10 +1718,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1780,22 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ /* tunname is required when printing the message at
+ * the end of the function
+ */
+ strncpynt(tunname, dynamic_name,
+ sizeof(dynamic_name));
+ break;
+ }
+ }
+ else
+#endif
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1817,61 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this scenario.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %d", dev, ret);
+ }
}
-
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ }
+ else
+#endif
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
+
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
}
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
msg(M_INFO, "TUN/TAP device %s opened", tunname);
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1889,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +1987,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +1999,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2111,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2137,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2255,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2284,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2637,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2732,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2873,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3002,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3231,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3258,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3281,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3339,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6649,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6951,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v4] dco: let open_tun_generic handle the DCO case
2022-06-29 12:29 ` [Openvpn-devel] [PATCH v3] " Antonio Quartulli
@ 2022-06-29 12:49 ` Antonio Quartulli
2022-06-30 13:44 ` Heiko Hund
2022-06-30 14:08 ` [Openvpn-devel] [PATCH v5 05/25] " Antonio Quartulli
0 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-29 12:49 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 2 +-
src/openvpn/options.c | 7 +++
src/openvpn/tun.c | 141 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
4 files changed, 116 insertions(+), 36 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..c9d05c31 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1763,7 +1763,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..5e7b8c49 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1718,10 +1718,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1780,22 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ /* tunname is required when printing the message at
+ * the end of the function
+ */
+ strncpynt(tunname, dynamic_name,
+ sizeof(dynamic_name));
+ break;
+ }
+ }
+ else
+#endif
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1817,63 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this scenario.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %d", dev, ret);
+ }
}
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ msg(M_INFO, "DCO device %s opened", tunname);
+ }
+ else
+#endif
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
- }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1891,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +1989,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +2001,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2113,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2139,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2257,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2286,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2639,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2734,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2875,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3004,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3233,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3260,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3283,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3341,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6651,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6953,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v4] dco: let open_tun_generic handle the DCO case
2022-06-29 12:49 ` [Openvpn-devel] [PATCH v4] " Antonio Quartulli
@ 2022-06-30 13:44 ` Heiko Hund
2022-06-30 13:51 ` Antonio Quartulli
2022-06-30 14:08 ` [Openvpn-devel] [PATCH v5 05/25] " Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-06-30 13:44 UTC (permalink / raw)
To: openvpn-devel
On Mittwoch, 29. Juni 2022 14:49:45 CEST Antonio Quartulli wrote:
> name when not specified b the user. For this reason the DCO case can
nit: b -> by
> + strncpynt(tunname, dynamic_name,
> + sizeof(dynamic_name));
This need to be sizeof(tunname).
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH v4] dco: let open_tun_generic handle the DCO case
2022-06-30 13:44 ` Heiko Hund
@ 2022-06-30 13:51 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-30 13:51 UTC (permalink / raw)
To: Heiko Hund <heiko@
On 30/06/2022 15:44, Heiko Hund wrote:
> On Mittwoch, 29. Juni 2022 14:49:45 CEST Antonio Quartulli wrote:
>> name when not specified b the user. For this reason the DCO case can
>
> nit: b -> by
thanks
>
>> + strncpynt(tunname, dynamic_name,
>> + sizeof(dynamic_name));
>
> This need to be sizeof(tunname).
ouch, good catch! Thanks!
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v5 05/25] dco: let open_tun_generic handle the DCO case
2022-06-29 12:49 ` [Openvpn-devel] [PATCH v4] " Antonio Quartulli
2022-06-30 13:44 ` Heiko Hund
@ 2022-06-30 14:08 ` Antonio Quartulli
2022-07-06 14:29 ` [Openvpn-devel] [PATCH pre-05/25] networking: add net_iface_type API Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-30 14:08 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v4:
* in open_tun_generic() use sizeof(tunname) when copying to tunname
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 2 +-
src/openvpn/options.c | 7 +++
src/openvpn/tun.c | 140 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
4 files changed, 115 insertions(+), 36 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..c9d05c31 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1763,7 +1763,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..901cfce0 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1718,10 +1718,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1780,21 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ /* tunname is required when printing the message at
+ * the end of the function
+ */
+ strncpynt(tunname, dynamic_name, sizeof(tunname));
+ break;
+ }
+ }
+ else
+#endif
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1816,63 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this scenario.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %d", dev, ret);
+ }
}
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ msg(M_INFO, "DCO device %s opened", tunname);
+ }
+ else
+#endif
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
- }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1890,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +1988,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +2000,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2112,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2138,7 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2256,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2285,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2638,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2733,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2874,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3003,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3232,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3259,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3282,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3340,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6650,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6952,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH pre-05/25] networking: add net_iface_type API
2022-06-30 14:08 ` [Openvpn-devel] [PATCH v5 05/25] " Antonio Quartulli
@ 2022-07-06 14:29 ` Antonio Quartulli
2022-07-06 14:29 ` [Openvpn-devel] [PATCH v6 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
2022-07-11 8:10 ` [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API Antonio Quartulli
0 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-06 14:29 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This new API can be used to retrieve the type of a specific interface.
It's mostly platform dependant, but right now expected values are
"ovpn-dco", "tun" or "tap".
Other values are possible too, but they are not of interest to us.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
This patch is required by 05/25 v6 as it introduces the networking API
required to retrieve an interafce type. This is then used by 05/25 v6.
src/openvpn/networking.h | 14 ++++
src/openvpn/networking_iproute2.c | 9 +++
src/openvpn/networking_sitnl.c | 109 ++++++++++++++++++++++++++++++
3 files changed, 132 insertions(+)
diff --git a/src/openvpn/networking.h b/src/openvpn/networking.h
index 79963756..cf6d39ac 100644
--- a/src/openvpn/networking.h
+++ b/src/openvpn/networking.h
@@ -23,6 +23,8 @@
#include "syshead.h"
+#define IFACE_TYPE_LEN_MAX 64
+
struct context;
#ifdef ENABLE_SITNL
@@ -100,6 +102,18 @@ void net_ctx_free(openvpn_net_ctx_t *ctx);
int net_iface_new(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface,
const char *type, void *arg);
+/**
+ * Retrieve the interface type
+ *
+ * @param ctx the implementation specific context
+ * @param iface interface to query
+ * @param type buffer where the type will be stored
+ *
+ * @return 0 on success, a negative error code otherwise
+ */
+int net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX]);
+
/**
* Remove an interface
*
diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c
index 4b220576..a81e6b33 100644
--- a/src/openvpn/networking_iproute2.c
+++ b/src/openvpn/networking_iproute2.c
@@ -78,6 +78,15 @@ net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type,
return 0;
}
+int
+net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX])
+{
+ /* not supported by iproute2 */
+ msg(M_WARN, "%s: operation not supported by iproute2 backend");
+ return -1;
+}
+
int
net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
{
diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c
index 0944ad0a..e97db3f7 100644
--- a/src/openvpn/networking_sitnl.c
+++ b/src/openvpn/networking_sitnl.c
@@ -1366,6 +1366,115 @@ err:
return ret;
}
+static int
+sitnl_parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags)
+{
+ unsigned short type;
+
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+
+ while (RTA_OK(rta, len))
+ {
+ type = rta->rta_type & ~flags;
+
+ if ((type <= max) && (!tb[type]))
+ {
+ tb[type] = rta;
+ }
+
+ rta = RTA_NEXT(rta, len);
+ }
+
+ if (len)
+ {
+ msg(D_ROUTE, "%s: %d bytes not parsed! (rta_len=%d)", __func__, len,
+ rta->rta_len);
+ }
+
+ return 0;
+}
+
+static int
+sitnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ return sitnl_parse_rtattr_flags(tb, max, rta, len, 0);
+}
+
+#define sitnl_parse_rtattr_nested(tb, max, rta) \
+ (sitnl_parse_rtattr_flags(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta), \
+ NLA_F_NESTED))
+
+static int
+sitnl_type_save(struct nlmsghdr *n, void *arg)
+{
+ char *type = arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr *tb[IFLA_MAX + 1];
+ int ret;
+
+ ret = sitnl_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (tb[IFLA_LINKINFO])
+ {
+ struct rtattr *tb_link[IFLA_INFO_MAX + 1];
+
+ ret = sitnl_parse_rtattr_nested(tb_link, IFLA_INFO_MAX,
+ tb[IFLA_LINKINFO]);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (!tb_link[IFLA_INFO_KIND])
+ {
+ return -ENOENT;
+ }
+
+ strncpynt(type, RTA_DATA(tb_link[IFLA_INFO_KIND]), IFACE_TYPE_LEN_MAX);
+ }
+
+ return 0;
+}
+
+int
+net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX])
+{
+ struct sitnl_link_req req = { };
+ int ifindex = if_nametoindex(iface);
+
+ if (!ifindex)
+ {
+ return errno;
+ }
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETLINK;
+
+ req.i.ifi_family = AF_PACKET;
+ req.i.ifi_index = ifindex;
+
+ memset(type, 0, IFACE_TYPE_LEN_MAX);
+
+ int ret = sitnl_send(&req.n, 0, 0, sitnl_type_save, type);
+ if (ret < 0)
+ {
+ msg(D_ROUTE, "%s: cannot retrieve iface %s: %s (%d)", __func__, iface,
+ strerror(-ret), ret);
+ return ret;
+ }
+
+ msg(D_ROUTE, "%s: type of %s: %s", __func__, iface, type);
+
+ return 0;
+}
+
int
net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
{
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v6 05/25] dco: let open_tun_generic handle the DCO case
2022-07-06 14:29 ` [Openvpn-devel] [PATCH pre-05/25] networking: add net_iface_type API Antonio Quartulli
@ 2022-07-06 14:29 ` Antonio Quartulli
2022-07-11 13:12 ` [Openvpn-devel] [PATCH v7] " Antonio Quartulli
2022-07-11 8:10 ` [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-06 14:29 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v5:
* create TUN device when invoking --mktun with DCO enabled. Current code
segfaults.
* fix message printing "..device created" for DCO cases
* print error string (and not just error code) when a DCO iface cannot
be created
* when creating DCO device, if a TUN device with the same name is found,
disable DCO and use the existing device
* don't use tunname as it contains the wrong string. Use dev or
dynamic_name instead
Changes from v4:
* in open_tun_generic() use sizeof(tunname) when copying to tunname
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 30 +++++++-
src/openvpn/init.h | 2 +-
src/openvpn/options.c | 7 ++
src/openvpn/tun.c | 159 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
5 files changed, 161 insertions(+), 39 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..99fcda04 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
* Persistent TUN/TAP device management mode?
*/
bool
-do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
+do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
{
if (options->persist_config)
{
@@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
msg(M_FATAL|M_OPTERR,
"options --mktun or --rmtun should only be used together with --dev");
}
+
+#if defined(ENABLE_DCO)
+ if (dco_enabled(options))
+ {
+ /* creating a DCO interface via --mktun is not supported as it does not
+ * make much sense. Since DCO is enabled by default, people may run into
+ * this without knowing, therefore this case should be properly handled.
+ *
+ * Disable DCO if --mktun was provided and print a message to let
+ * user know.
+ */
+ if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
+ {
+ msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
+ }
+
+ options->tuntap_options.disable_dco = true;
+ }
+#endif
+
#ifdef ENABLE_FEATURE_TUN_PERSIST
tuncfg(options->dev, options->dev_type, options->dev_node,
options->persist_mode,
@@ -1763,7 +1783,13 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
+
+ /* DCO may have been disabled by open_tun(). propagate change */
+ c->options.tuntap_options = c->c1.tuntap->options;
+#if defined(ENABLE_DCO)
+ c->c2.tls_multi->opt.disable_dco = !dco_enabled(&c->options);
+#endif
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..5f412a33 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
bool do_genkey(const struct options *options);
-bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
+bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
bool possibly_become_daemon(const struct options *options);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..d75d75e3 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -37,6 +37,7 @@
#include "syshead.h"
+#include "openvpn.h"
#include "tun.h"
#include "fdmisc.h"
#include "common.h"
@@ -1718,10 +1719,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1781,18 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ msg(M_INFO, "DCO device %s opened", dynamic_name);
+ break;
+ }
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1814,81 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
- }
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this context.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ char type[IFACE_TYPE_LEN_MAX];
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
- {
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ ret = net_iface_type(ctx, dev, type);
+ if (ret < 0 || strcmp(type, "ovpn-dco") != 0)
+ {
+ msg(M_WARN, "Existing non-DCO device %s found. Disabling DCO",
+ dev);
+ tt->options.disable_dco = true;
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
+ return;
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ }
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
+ strerror(-ret), ret);
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s opened", dev);
+ }
}
}
+ else
+#endif /* if defined(TARGET_LINUX) */
+ {
+ if (!dynamic_opened)
+ {
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
+ }
+
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1906,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +2004,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +2016,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2128,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2154,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2273,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2302,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2655,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2750,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2891,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3020,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3249,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3276,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3299,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3357,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6667,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6969,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v7] dco: let open_tun_generic handle the DCO case
2022-07-06 14:29 ` [Openvpn-devel] [PATCH v6 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
@ 2022-07-11 13:12 ` Antonio Quartulli
2022-07-11 13:55 ` [Openvpn-devel] [PATCH v8 05/25] " Antonio Quartulli
0 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-11 13:12 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v6:
* do not touch tls_multi in do_open_tun as it's still NULL (caused crash)
* re-assign disable_dco field in pre_connect object, if DCO is disabled
by open_tun() in do_open_tun()
Changes from v5:
* create TUN device when invoking --mktun with DCO enabled. Current code
segfaults.
* fix message printing "..device created" for DCO cases
* print error string (and not just error code) when a DCO iface cannot
be created
* when creating DCO device, if a TUN device with the same name is found,
disable DCO and use the existing device
* don't use tunname as it contains the wrong string. Use dev or
dynamic_name instead
Changes from v4:
* in open_tun_generic() use sizeof(tunname) when copying to tunname
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 31 +++++++-
src/openvpn/init.h | 2 +-
src/openvpn/options.c | 7 ++
src/openvpn/tun.c | 159 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
5 files changed, 162 insertions(+), 39 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..1adc06c9 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
* Persistent TUN/TAP device management mode?
*/
bool
-do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
+do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
{
if (options->persist_config)
{
@@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
msg(M_FATAL|M_OPTERR,
"options --mktun or --rmtun should only be used together with --dev");
}
+
+#if defined(ENABLE_DCO)
+ if (dco_enabled(options))
+ {
+ /* creating a DCO interface via --mktun is not supported as it does not
+ * make much sense. Since DCO is enabled by default, people may run into
+ * this without knowing, therefore this case should be properly handled.
+ *
+ * Disable DCO if --mktun was provided and print a message to let
+ * user know.
+ */
+ if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
+ {
+ msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
+ }
+
+ options->tuntap_options.disable_dco = true;
+ }
+#endif
+
#ifdef ENABLE_FEATURE_TUN_PERSIST
tuncfg(options->dev, options->dev_type, options->dev_node,
options->persist_mode,
@@ -1763,7 +1783,14 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
+
+ /* DCO may have been disabled by open_tun(). propagate change */
+ c->options.tuntap_options.disable_dco = c->c1.tuntap->options.disable_dco;
+ if (c->options.pre_connect->tuntap_options_defined)
+ {
+ c->options.pre_connect->tuntap_options.disable_dco = c->c1.tuntap->options.disable_dco;
+ }
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..5f412a33 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
bool do_genkey(const struct options *options);
-bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
+bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
bool possibly_become_daemon(const struct options *options);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..d75d75e3 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -37,6 +37,7 @@
#include "syshead.h"
+#include "openvpn.h"
#include "tun.h"
#include "fdmisc.h"
#include "common.h"
@@ -1718,10 +1719,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1781,18 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ msg(M_INFO, "DCO device %s opened", dynamic_name);
+ break;
+ }
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1814,81 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
- }
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this context.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ char type[IFACE_TYPE_LEN_MAX];
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
- {
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ ret = net_iface_type(ctx, dev, type);
+ if (ret < 0 || strcmp(type, "ovpn-dco") != 0)
+ {
+ msg(M_WARN, "Existing non-DCO device %s found. Disabling DCO",
+ dev);
+ tt->options.disable_dco = true;
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
+ return;
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ }
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
+ strerror(-ret), ret);
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s opened", dev);
+ }
}
}
+ else
+#endif /* if defined(TARGET_LINUX) */
+ {
+ if (!dynamic_opened)
+ {
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
+ }
+
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1906,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +2004,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +2016,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2128,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2154,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2273,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2302,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2655,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2750,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2891,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3020,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3249,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3276,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3299,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3357,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6667,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6969,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v8 05/25] dco: let open_tun_generic handle the DCO case
2022-07-11 13:12 ` [Openvpn-devel] [PATCH v7] " Antonio Quartulli
@ 2022-07-11 13:55 ` Antonio Quartulli
2022-07-12 21:46 ` [Openvpn-devel] [PATCH v9 " Antonio Quartulli
0 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-11 13:55 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v7:
* enclose setting 'disable_dco' field within ifdef (in do_open_tun) to
ensure code compiles also on platforms with no DCO support at all
Changes from v6:
* do not touch tls_multi in do_open_tun as it's still NULL (caused crash)
* re-assign disable_dco field in pre_connect object, if DCO is disabled
by open_tun() in do_open_tun()
Changes from v5:
* create TUN device when invoking --mktun with DCO enabled. Current code
segfaults.
* fix message printing "..device created" for DCO cases
* print error string (and not just error code) when a DCO iface cannot
be created
* when creating DCO device, if a TUN device with the same name is found,
disable DCO and use the existing device
* don't use tunname as it contains the wrong string. Use dev or
dynamic_name instead
Changes from v4:
* in open_tun_generic() use sizeof(tunname) when copying to tunname
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 33 ++++++++-
src/openvpn/init.h | 2 +-
src/openvpn/options.c | 7 ++
src/openvpn/tun.c | 159 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
5 files changed, 164 insertions(+), 39 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..8b952552 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
* Persistent TUN/TAP device management mode?
*/
bool
-do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
+do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
{
if (options->persist_config)
{
@@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
msg(M_FATAL|M_OPTERR,
"options --mktun or --rmtun should only be used together with --dev");
}
+
+#if defined(ENABLE_DCO)
+ if (dco_enabled(options))
+ {
+ /* creating a DCO interface via --mktun is not supported as it does not
+ * make much sense. Since DCO is enabled by default, people may run into
+ * this without knowing, therefore this case should be properly handled.
+ *
+ * Disable DCO if --mktun was provided and print a message to let
+ * user know.
+ */
+ if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
+ {
+ msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
+ }
+
+ options->tuntap_options.disable_dco = true;
+ }
+#endif
+
#ifdef ENABLE_FEATURE_TUN_PERSIST
tuncfg(options->dev, options->dev_type, options->dev_node,
options->persist_mode,
@@ -1763,7 +1783,16 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
+
+#if defined(ENABLE_DCO)
+ /* DCO may have been disabled by open_tun(). propagate change */
+ c->options.tuntap_options.disable_dco = c->c1.tuntap->options.disable_dco;
+ if (c->options.pre_connect->tuntap_options_defined)
+ {
+ c->options.pre_connect->tuntap_options.disable_dco = c->c1.tuntap->options.disable_dco;
+ }
+#endif
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..5f412a33 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
bool do_genkey(const struct options *options);
-bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
+bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
bool possibly_become_daemon(const struct options *options);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..d75d75e3 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -37,6 +37,7 @@
#include "syshead.h"
+#include "openvpn.h"
#include "tun.h"
#include "fdmisc.h"
#include "common.h"
@@ -1718,10 +1719,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1781,18 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ msg(M_INFO, "DCO device %s opened", dynamic_name);
+ break;
+ }
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1814,81 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
- }
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this context.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ char type[IFACE_TYPE_LEN_MAX];
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
- {
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ ret = net_iface_type(ctx, dev, type);
+ if (ret < 0 || strcmp(type, "ovpn-dco") != 0)
+ {
+ msg(M_WARN, "Existing non-DCO device %s found. Disabling DCO",
+ dev);
+ tt->options.disable_dco = true;
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
+ return;
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ }
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
+ strerror(-ret), ret);
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s opened", dev);
+ }
}
}
+ else
+#endif /* if defined(TARGET_LINUX) */
+ {
+ if (!dynamic_opened)
+ {
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
+ }
+
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1906,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +2004,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +2016,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2128,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2154,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2273,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2302,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2655,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2750,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2891,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3020,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3249,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3276,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3299,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3357,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6667,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6969,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v9 05/25] dco: let open_tun_generic handle the DCO case
2022-07-11 13:55 ` [Openvpn-devel] [PATCH v8 05/25] " Antonio Quartulli
@ 2022-07-12 21:46 ` Antonio Quartulli
2022-07-14 14:11 ` Gert Doering
2022-07-19 14:16 ` [Openvpn-devel] [PATCH v10 " Antonio Quartulli
0 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-12 21:46 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v8:
* remove disabling DCO from within do_open_tun(). Logic has been moved
to dco_check_option* (in a future patch).
Changes from v7:
* enclose setting 'disable_dco' field within ifdef (in do_open_tun) to
ensure code compiles also on platforms with no DCO support at all
Changes from v6:
* do not touch tls_multi in do_open_tun as it's still NULL (caused crash)
* re-assign disable_dco field in pre_connect object, if DCO is disabled
by open_tun() in do_open_tun()
Changes from v5:
* create TUN device when invoking --mktun with DCO enabled. Current code
segfaults.
* fix message printing "..device created" for DCO cases
* print error string (and not just error code) when a DCO iface cannot
be created
* when creating DCO device, if a TUN device with the same name is found,
disable DCO and use the existing device
* don't use tunname as it contains the wrong string. Use dev or
dynamic_name instead
Changes from v4:
* in open_tun_generic() use sizeof(tunname) when copying to tunname
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 24 ++++++-
src/openvpn/init.h | 2 +-
src/openvpn/options.c | 7 ++
src/openvpn/tun.c | 144 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
5 files changed, 140 insertions(+), 39 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 03221cbb..4feb578a 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
* Persistent TUN/TAP device management mode?
*/
bool
-do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
+do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
{
if (options->persist_config)
{
@@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
msg(M_FATAL|M_OPTERR,
"options --mktun or --rmtun should only be used together with --dev");
}
+
+#if defined(ENABLE_DCO)
+ if (dco_enabled(options))
+ {
+ /* creating a DCO interface via --mktun is not supported as it does not
+ * make much sense. Since DCO is enabled by default, people may run into
+ * this without knowing, therefore this case should be properly handled.
+ *
+ * Disable DCO if --mktun was provided and print a message to let
+ * user know.
+ */
+ if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
+ {
+ msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
+ }
+
+ options->tuntap_options.disable_dco = true;
+ }
+#endif
+
#ifdef ENABLE_FEATURE_TUN_PERSIST
tuncfg(options->dev, options->dev_type, options->dev_node,
options->persist_mode,
@@ -1763,7 +1783,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..5f412a33 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
bool do_genkey(const struct options *options);
-bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
+bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
bool possibly_become_daemon(const struct options *options);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 233c02e0..705bb79a 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3528,6 +3528,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index e12f0369..f17db280 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -37,6 +37,7 @@
#include "syshead.h"
+#include "openvpn.h"
#include "tun.h"
#include "fdmisc.h"
#include "common.h"
@@ -1718,10 +1719,10 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1780,6 +1781,18 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ msg(M_INFO, "DCO device %s opened", dynamic_name);
+ break;
+ }
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1801,30 +1814,66 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this context.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
+ strerror(-ret), ret);
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s opened", dev);
+ }
}
-
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
- }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1842,7 +1891,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1939,7 +1989,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1950,6 +2001,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2056,7 +2113,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2081,7 +2139,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2199,6 +2258,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2222,7 +2287,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2574,9 +2640,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2668,9 +2735,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2808,9 +2876,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2936,9 +3005,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3164,7 +3234,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3190,7 +3261,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3213,7 +3284,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3271,7 +3342,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6580,7 +6652,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6881,9 +6954,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 5fcea590..cf02bf43 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v9 05/25] dco: let open_tun_generic handle the DCO case
2022-07-12 21:46 ` [Openvpn-devel] [PATCH v9 " Antonio Quartulli
@ 2022-07-14 14:11 ` Gert Doering
2022-07-19 14:16 ` [Openvpn-devel] [PATCH v10 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-14 14:11 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 2850 bytes --]
Hi,
On Tue, Jul 12, 2022 at 11:46:42PM +0200, Antonio Quartulli wrote:
> open_tun_generic already contains the logic required to find a device
> name when not specified b the user. For this reason the DCO case can
> easily leverage on function and avoid code duplication.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
NAK... I'm not sure what is happening, but if I apply this on top
of current master, and build without(!) DCO, it will kill linux/sitnl
operation hard for the "--dev tun30" case.
Running "openvpn --client ... --dev tun30" leads to
2022-07-14 15:59:43 Incoming Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
2022-07-14 15:59:43 net_route_v4_best_gw query: dst 0.0.0.0
2022-07-14 15:59:43 net_route_v4_best_gw result: via 194.97.140.30 dev eno33554952
2022-07-14 15:59:43 GDG6: remote_host_ipv6=2607:fc50:1001:5200::4
2022-07-14 15:59:43 net_route_v6_best_gw query: dst 2607:fc50:1001:5200::4
2022-07-14 15:59:43 net_route_v6_best_gw result: via 2001:608:0:814::ffff dev eno33554952
2022-07-14 15:59:43 DCO device tun30 opened
2022-07-14 15:59:43 net_iface_mtu_set: rtnl: cannot get ifindex for tun30: No such device (errno=19)
2022-07-14 15:59:43 Linux can't set mtu (1500) on tun30
2022-07-14 15:59:43 Exiting due to fatal error
Building with --enable-dco breaks all operations on linux (because it
gets confused between DCO and non DCO operation modes)...
2022-07-14 16:04:05 Assertion failed at dco_linux.c:453 (tt->type == DEV_TYPE_TUN)
and sometimes
2022-07-14 16:06:26 sitnl_send: rtnl: generic error (-95): Operation not supported
2022-07-14 16:06:26 net_iface_new: add tun255 type ovpn-dco
2022-07-14 16:06:26 sitnl_send: rtnl: generic error (-95): Operation not supported
2022-07-14 16:06:26 Cannot allocate TUN/TAP dev dynamically
(isn't "-95" an indication of "no DCO module available" and we should not
try to iterate up to tun255 in this case, but give up with a clear
error indication instead?)
thus
Test sets succeeded: none.
Test sets failed: 1 1a 1b 1c 1d 1e 2 2a 2b 2c 2d 2e 2f 3 4 4a 4b 5 6 8 8a 9 9a 9b 9x 11 11a.
... but since this is not default, I do not consider this a "this patch
breaks things" problem. It compiles.
There is no --disable-dco option yet, so hard to test "would it work in
that case?" - so I consider this part not really interesting yet.
But the "not built with DCO, --dev tun30" part should really not fail.
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v10 05/25] dco: let open_tun_generic handle the DCO case
2022-07-12 21:46 ` [Openvpn-devel] [PATCH v9 " Antonio Quartulli
2022-07-14 14:11 ` Gert Doering
@ 2022-07-19 14:16 ` Antonio Quartulli
2022-07-21 18:10 ` Gert Doering
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-19 14:16 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
open_tun_generic already contains the logic required to find a device
name when not specified b the user. For this reason the DCO case can
easily leverage on function and avoid code duplication.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v9:
* rebased on top of latest master
Changes from v8:
* remove disabling DCO from within do_open_tun(). Logic has been moved
to dco_check_option* (in a future patch).
Changes from v7:
* enclose setting 'disable_dco' field within ifdef (in do_open_tun) to
ensure code compiles also on platforms with no DCO support at all
Changes from v6:
* do not touch tls_multi in do_open_tun as it's still NULL (caused crash)
* re-assign disable_dco field in pre_connect object, if DCO is disabled
by open_tun() in do_open_tun()
Changes from v5:
* create TUN device when invoking --mktun with DCO enabled. Current code
segfaults.
* fix message printing "..device created" for DCO cases
* print error string (and not just error code) when a DCO iface cannot
be created
* when creating DCO device, if a TUN device with the same name is found,
disable DCO and use the existing device
* don't use tunname as it contains the wrong string. Use dev or
dynamic_name instead
Changes from v4:
* in open_tun_generic() use sizeof(tunname) when copying to tunname
Changes from v3:
* explicitly mention "DCO" in message when DCO device is successfully
opened, as per Arne's request
Changes from v2:
* do not abuse the dynamic_name variable. Rather use 'dev' when no
dynamic name is requested
* ignore dev-node when using DCO, to avoid messing up naming logic in
open_tun_generic. dev-node has sense when using DCO
* add comment as to why we need to fill tunname
Changes from v1:
* improved INFO message when device already exists as per Arne's request
src/openvpn/init.c | 24 ++++++-
src/openvpn/init.h | 2 +-
src/openvpn/options.c | 7 ++
src/openvpn/tun.c | 144 ++++++++++++++++++++++++++++++++----------
src/openvpn/tun.h | 2 +-
5 files changed, 140 insertions(+), 39 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 1bfbf4eb..779fc4a5 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
* Persistent TUN/TAP device management mode?
*/
bool
-do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
+do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
{
if (options->persist_config)
{
@@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
msg(M_FATAL|M_OPTERR,
"options --mktun or --rmtun should only be used together with --dev");
}
+
+#if defined(ENABLE_DCO)
+ if (dco_enabled(options))
+ {
+ /* creating a DCO interface via --mktun is not supported as it does not
+ * make much sense. Since DCO is enabled by default, people may run into
+ * this without knowing, therefore this case should be properly handled.
+ *
+ * Disable DCO if --mktun was provided and print a message to let
+ * user know.
+ */
+ if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
+ {
+ msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
+ }
+
+ options->tuntap_options.disable_dco = true;
+ }
+#endif
+
#ifdef ENABLE_FEATURE_TUN_PERSIST
tuncfg(options->dev, options->dev_type, options->dev_node,
options->persist_mode,
@@ -1763,7 +1783,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..5f412a33 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
bool do_genkey(const struct options *options);
-bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
+bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
bool possibly_become_daemon(const struct options *options);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index b00acf7e..87d6fc31 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3667,6 +3667,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->tuntap_options.disable_dco = !dco_check_option_conflict(D_DCO, o);
#endif
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 4acccbff..108090d0 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -37,6 +37,7 @@
#include "syshead.h"
+#include "openvpn.h"
#include "tun.h"
#include "fdmisc.h"
#include "common.h"
@@ -1723,10 +1724,10 @@ tun_name_is_fixed(const char *dev)
return has_digit(dev);
}
-#if !(defined(_WIN32) || defined(TARGET_LINUX))
+#if !defined(_WIN32)
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
- bool dynamic, struct tuntap *tt)
+ bool dynamic, struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[256];
@@ -1785,6 +1786,18 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
"/dev/%s%d", dev, i);
openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
"%s%d", dev, i);
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
+ {
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ msg(M_INFO, "DCO device %s opened", dynamic_name);
+ break;
+ }
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
if ((tt->fd = open(tunname, O_RDWR)) > 0)
{
dynamic_opened = true;
@@ -1806,30 +1819,66 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
}
- if (!dynamic_opened)
+#if defined(TARGET_LINUX)
+ if (!tt->options.disable_dco)
{
- /* has named device existed before? if so, don't destroy at end */
- if (if_nametoindex( dev ) > 0)
+ if (!dynamic_opened)
{
- msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
- tt->persistent_if = true;
+ /* if dynamic_opened was true, then we already created the
+ * interface named 'dynamic_name', otherwise we have to create
+ * now the interface 'dev'.
+ *
+ * The variable 'dev_node' is totally ignored in the DCO case
+ * because it is unset by options post-processing as it makes no
+ * sense in this context.
+ */
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
+ strerror(-ret), ret);
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s opened", dev);
+ }
}
-
- if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ }
+ else
+#endif /* if defined(TARGET_LINUX) */
+ {
+ if (!dynamic_opened)
{
- msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ /* has named device existed before? if so, don't destroy at end */
+ if (if_nametoindex( dev ) > 0)
+ {
+ msg(M_INFO, "TUN/TAP device %s exists previously, keep at program end", dev );
+ tt->persistent_if = true;
+ }
+
+ if ((tt->fd = open(tunname, O_RDWR)) < 0)
+ {
+ msg(M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
}
- }
- set_nonblock(tt->fd);
- set_cloexec(tt->fd); /* don't pass fd to scripts */
- msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ set_nonblock(tt->fd);
+ set_cloexec(tt->fd); /* don't pass fd to scripts */
+
+ msg(M_INFO, "TUN/TAP device %s opened", tunname);
+ }
/* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
tt->actual_name = string_alloc(dynamic_opened ? dynamic_name : dev, NULL);
}
}
-#endif /* !_WIN32 && !TARGET_LINUX */
+#endif /* !_WIN32 */
#if !defined(_WIN32)
static void
@@ -1847,7 +1896,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1944,7 +1994,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1955,6 +2006,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+#if defined(TARGET_LINUX)
+ else if (!tt->options.disable_dco)
+ {
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
+ }
+#endif
else
{
/*
@@ -2061,7 +2118,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2086,7 +2144,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2204,6 +2263,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (!tt->options.disable_dco)
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2227,7 +2292,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2579,9 +2645,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
/* Enable multicast on the interface */
if (tt->fd >= 0)
@@ -2673,9 +2740,10 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -2813,9 +2881,10 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
{
@@ -2941,9 +3010,10 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
if (tt->fd >= 0)
{
@@ -3169,7 +3239,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3195,7 +3266,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
/* No explicit utun and utun failed, try the generic way) */
msg(M_INFO, "Failed to open utun device. Falling back to /dev/tun device");
- open_tun_generic(dev, dev_type, NULL, true, tt);
+ open_tun_generic(dev, dev_type, NULL, true, tt, ctx);
}
else
{
@@ -3218,7 +3289,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
dev_node = NULL;
}
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
}
@@ -3276,7 +3347,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6585,7 +6657,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6886,9 +6959,10 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
- open_tun_generic(dev, dev_type, dev_node, true, tt);
+ open_tun_generic(dev, dev_type, dev_node, true, tt, ctx);
}
void
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index b7786f46..8ec8f51f 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v10 05/25] dco: let open_tun_generic handle the DCO case
2022-07-19 14:16 ` [Openvpn-devel] [PATCH v10 " Antonio Quartulli
@ 2022-07-21 18:10 ` Gert Doering
2022-07-21 18:24 ` [Openvpn-devel] [PATCH v11 05/25] dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices Gert Doering
0 siblings, 1 reply; 157+ messages in thread
From: Gert Doering @ 2022-07-21 18:10 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 2625 bytes --]
Hi,
On Tue, Jul 19, 2022 at 04:16:39PM +0200, Antonio Quartulli wrote:
> open_tun_generic already contains the logic required to find a device
> name when not specified b the user. For this reason the DCO case can
> easily leverage on function and avoid code duplication.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
*sigh*
First of all, my apologies for letting you jump through all these hoops -
I'm NOT doing this to annoy you or drag my feet, but really to keep the
code readable and maintainable.
That said, I've spent two days now pondering the latest version of this
patch, and I can not ACK it - it's ugly beyond openvpn standards.
My main gripe is that we have this open_tun_generic() function which
is used for "all but linux (and Windows)". So you add a call to it
now for the DCO call (this is okay-ish) but then it's not actually
*using* the code paths in open_tun_generic(), but adding new and
linux-exclusive code paths #ifdef TARGET_LINUX.
So, on Linux with DCO, very little of that function is actually used
(half of it is #ifdef TARGET_LINUX, and the non-#ifdef parts are
"else" branches for "not DCO" - but "for not DCO", we do not call this
in the first place) - all that remains is a for() loop to iterate
device names, and a string_alloc() call...
That said, I've cooked up a v11 of 05, which introduces a much
leaner open_tun_dco_generic() function - to be shared by Linux
and FreeBSD - which does "only DCO things", leaving open_tun_generic()
alone. I've tested this with the usual test cases
--dev tap (works, because options.c will already disable DCO)
--dev tun (iterates to "tun7" on my DCO test system)
--mktun --dev tun471 ; use --dev tun471 (works, falls back to tun)
--dev tun472 (works as DCO device))
Right now this is sitting on top of my clone of your "dco" tree -
I'll see that I can apply this to the right parts in the middle and
send a 05 v11 out later, then we can haggle on the least ugly way
forward.
gert
PS1: I'm fairly sure *I* did the existing #ifdef TARGET_NETBSD in
open_tun_generic() and I should not have done that. Bad precedence.
PS2: 05 v10 works, and does not break anything, But It Is Too Ugly.
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v11 05/25] dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices
2022-07-21 18:10 ` Gert Doering
@ 2022-07-21 18:24 ` Gert Doering
2022-07-27 13:06 ` Antonio Quartulli
2022-07-27 18:00 ` [Openvpn-devel] [PATCH applied] " Gert Doering
0 siblings, 2 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-21 18:24 UTC (permalink / raw)
To: openvpn-devel
From: Antonio Quartulli <a@...2181...>
This function is similar to the essence of open_tun_generic(), but
calling open_tun_dco() instead of trying to do a file open on
"/dev/%s"
Previous attempts to save code duplication by including this into
open_tun_generic() created additional #ifdef plus confusing call
paths. So this is a clean new function, leaving the door open for
a cleanup of open_tun_generic().
Also, introduce tun_dco_enabled(tt) to avoid the negative
"!tt->options.disable_dco" calls.
Signed-off-by: Antonio Quartulli <a@...2181...>
Signed-off-by: Gert Doering <gert@...1296...>
--
v11:
- add new function open_tun_dco_generic() for Linux (and FreeBSD, later)
instead of lumping this into open_tun_generic()
- pick up tun_dco_enabled() from a later patch in the series
(easier to bring this in right now than to convert the code back
and then patch it again later)
---
src/openvpn/init.c | 24 +++++++-
src/openvpn/init.h | 2 +-
src/openvpn/options.c | 7 +++
src/openvpn/tun.c | 129 +++++++++++++++++++++++++++++++++++++-----
src/openvpn/tun.h | 2 +-
5 files changed, 147 insertions(+), 17 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 1bfbf4eb..779fc4a5 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
* Persistent TUN/TAP device management mode?
*/
bool
-do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
+do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
{
if (options->persist_config)
{
@@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
msg(M_FATAL|M_OPTERR,
"options --mktun or --rmtun should only be used together with --dev");
}
+
+#if defined(ENABLE_DCO)
+ if (dco_enabled(options))
+ {
+ /* creating a DCO interface via --mktun is not supported as it does not
+ * make much sense. Since DCO is enabled by default, people may run into
+ * this without knowing, therefore this case should be properly handled.
+ *
+ * Disable DCO if --mktun was provided and print a message to let
+ * user know.
+ */
+ if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
+ {
+ msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
+ }
+
+ options->tuntap_options.disable_dco = true;
+ }
+#endif
+
#ifdef ENABLE_FEATURE_TUN_PERSIST
tuncfg(options->dev, options->dev_type, options->dev_node,
options->persist_mode,
@@ -1763,7 +1783,7 @@ do_open_tun(struct context *c)
#endif
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ c->c1.tuntap, &c->net_ctx);
/* set the hardware address */
if (c->options.lladdr)
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..5f412a33 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
bool do_genkey(const struct options *options);
-bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
+bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
bool possibly_become_daemon(const struct options *options);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index b00acf7e..87d6fc31 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3667,6 +3667,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->tuntap_options.disable_dco = !dco_check_option_conflict(D_DCO, o);
#endif
+ if (dco_enabled(o) && o->dev_node)
+ {
+ msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
+ "data channel offload");
+ o->dev_node = NULL;
+ }
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 3f9aa6ae..30abfdd1 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -37,6 +37,7 @@
#include "syshead.h"
+#include "openvpn.h"
#include "tun.h"
#include "fdmisc.h"
#include "common.h"
@@ -1723,6 +1724,15 @@ tun_name_is_fixed(const char *dev)
return has_digit(dev);
}
+#if defined(TARGET_LINUX)
+static bool
+tun_dco_enabled(struct tuntap *tt)
+{
+ return !tt->options.disable_dco;
+}
+#endif
+
+
#if !(defined(_WIN32) || defined(TARGET_LINUX))
static void
open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
@@ -1831,6 +1841,76 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
}
#endif /* !_WIN32 && !TARGET_LINUX */
+#if defined(TARGET_LINUX)
+static void
+open_tun_dco_generic(const char *dev, const char *dev_type,
+ struct tuntap *tt, openvpn_net_ctx_t *ctx)
+{
+ char dynamic_name[256];
+ bool dynamic_opened = false;
+
+ if (tt->type == DEV_TYPE_NULL)
+ {
+ open_null(tt);
+ return;
+ }
+
+ /*
+ * dynamic open is indicated by --dev specified without
+ * explicit unit number. Try opening /dev/[dev]n
+ * where n = [0, 255].
+ */
+
+ if (!tun_name_is_fixed(dev))
+ {
+ for (int i = 0; i < 256; ++i)
+ {
+ openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
+ "%s%d", dev, i);
+ if (open_tun_dco(tt, ctx, dynamic_name) == 0)
+ {
+ dynamic_opened = true;
+ msg(M_INFO, "DCO device %s opened", dynamic_name);
+ break;
+ }
+ msg(D_READ_WRITE | M_ERRNO, "Tried opening %s (failed)", dynamic_name);
+ }
+ if (!dynamic_opened)
+ {
+ msg(M_FATAL, "Cannot allocate DCO dev dynamically");
+ }
+ /* tt->actual_name is passed to up and down scripts and used as
+ * the ifconfig dev name */
+ tt->actual_name = string_alloc(dynamic_name, NULL);
+ }
+ /*
+ * explicit unit number specified
+ */
+ else
+ {
+ int ret = open_tun_dco(tt, ctx, dev);
+ if (ret == -EEXIST)
+ {
+ msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
+ dev);
+ tt->persistent_if = true;
+ }
+ else if (ret < 0)
+ {
+ msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
+ strerror(-ret), ret);
+ }
+ else
+ {
+ msg(M_INFO, "DCO device %s opened", dev);
+ }
+
+ /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
+ tt->actual_name = string_alloc(dev, NULL);
+ }
+}
+#endif /* TARGET_LINUX */
+
#if !defined(_WIN32)
static void
close_tun_generic(struct tuntap *tt)
@@ -1847,7 +1927,8 @@ close_tun_generic(struct tuntap *tt)
#if defined (TARGET_ANDROID)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#define ANDROID_TUNNAME "vpnservice-tun"
struct user_pass up;
@@ -1944,7 +2025,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#if !PEDANTIC
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
struct ifreq ifr;
@@ -1955,6 +2037,10 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
{
open_null(tt);
}
+ else if (tun_dco_enabled(tt))
+ {
+ open_tun_dco_generic(dev, dev_type, tt, ctx);
+ }
else
{
/*
@@ -2061,7 +2147,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
#else /* if !PEDANTIC */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
ASSERT(0);
}
@@ -2086,7 +2173,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
clear_tuntap(tt);
tt->type = dev_type_enum(dev, dev_type);
tt->options = *options;
- open_tun(dev, dev_type, dev_node, tt);
+
+ open_tun(dev, dev_type, dev_node, tt, ctx);
if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
{
msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
@@ -2204,6 +2292,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
net_ctx_reset(ctx);
}
+#ifdef TARGET_LINUX
+ if (tun_dco_enabled(tt))
+ {
+ close_tun_dco(tt, ctx);
+ }
+#endif
close_tun_generic(tt);
free(tt);
}
@@ -2227,7 +2321,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#endif
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
struct lifreq ifr;
@@ -2579,7 +2674,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_OPENBSD)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
open_tun_generic(dev, dev_type, dev_node, true, tt);
@@ -2673,7 +2769,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
*/
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
open_tun_generic(dev, dev_type, dev_node, true, tt);
@@ -2813,7 +2910,8 @@ freebsd_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
open_tun_generic(dev, dev_type, dev_node, true, tt);
@@ -2941,7 +3039,8 @@ dragonfly_modify_read_write_return(int len)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
open_tun_generic(dev, dev_type, dev_node, true, tt);
@@ -3169,7 +3268,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
#endif /* ifdef HAVE_NET_IF_UTUN_H */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
#ifdef HAVE_NET_IF_UTUN_H
/* If dev_node does not start start with utun assume regular tun/tap */
@@ -3276,7 +3376,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
#elif defined(TARGET_AIX)
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
char tunname[256];
char dynamic_name[20];
@@ -6585,7 +6686,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
}
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
const char *device_guid = NULL;
@@ -6894,7 +6996,8 @@ ipset2ascii_all(struct gc_arena *gc)
#else /* generic */
void
-open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
+ openvpn_net_ctx_t *ctx)
{
open_tun_generic(dev, dev_type, dev_node, true, tt);
}
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index b7786f46..8ec8f51f 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
*/
void open_tun(const char *dev, const char *dev_type, const char *dev_node,
- struct tuntap *tt);
+ struct tuntap *tt, openvpn_net_ctx_t *ctx);
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
--
2.25.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v11 05/25] dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices
2022-07-21 18:24 ` [Openvpn-devel] [PATCH v11 05/25] dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices Gert Doering
@ 2022-07-27 13:06 ` Antonio Quartulli
2022-07-27 18:00 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-27 13:06 UTC (permalink / raw)
To: Gert Doering <gert@
Hi,
On 21/07/2022 20:24, Gert Doering wrote:
> From: Antonio Quartulli <a@...2181...>
>
> This function is similar to the essence of open_tun_generic(), but
> calling open_tun_dco() instead of trying to do a file open on
> "/dev/%s"
>
> Previous attempts to save code duplication by including this into
> open_tun_generic() created additional #ifdef plus confusing call
> paths. So this is a clean new function, leaving the door open for
> a cleanup of open_tun_generic().
>
> Also, introduce tun_dco_enabled(tt) to avoid the negative
> "!tt->options.disable_dco" calls.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> Signed-off-by: Gert Doering <gert@...1296...>
Thanks for providing a new revision of this tremendous patch :-D
>
> --
> v11:
> - add new function open_tun_dco_generic() for Linux (and FreeBSD, later)
> instead of lumping this into open_tun_generic()
> - pick up tun_dco_enabled() from a later patch in the series
> (easier to bring this in right now than to convert the code back
> and then patch it again later)
> ---
> src/openvpn/init.c | 24 +++++++-
> src/openvpn/init.h | 2 +-
> src/openvpn/options.c | 7 +++
> src/openvpn/tun.c | 129 +++++++++++++++++++++++++++++++++++++-----
> src/openvpn/tun.h | 2 +-
> 5 files changed, 147 insertions(+), 17 deletions(-)
>
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index 1bfbf4eb..779fc4a5 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -1054,7 +1054,7 @@ do_genkey(const struct options *options)
> * Persistent TUN/TAP device management mode?
> */
> bool
> -do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
> +do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx)
> {
> if (options->persist_config)
> {
> @@ -1069,6 +1069,26 @@ do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx)
> msg(M_FATAL|M_OPTERR,
> "options --mktun or --rmtun should only be used together with --dev");
> }
> +
> +#if defined(ENABLE_DCO)
> + if (dco_enabled(options))
> + {
> + /* creating a DCO interface via --mktun is not supported as it does not
> + * make much sense. Since DCO is enabled by default, people may run into
> + * this without knowing, therefore this case should be properly handled.
> + *
> + * Disable DCO if --mktun was provided and print a message to let
> + * user know.
> + */
> + if (dev_type_enum(options->dev, options->dev_type) == DEV_TYPE_TUN)
> + {
> + msg(M_WARN, "Note: --mktun does not support DCO. Creating TUN interface.");
> + }
> +
> + options->tuntap_options.disable_dco = true;
> + }
> +#endif
> +
> #ifdef ENABLE_FEATURE_TUN_PERSIST
> tuncfg(options->dev, options->dev_type, options->dev_node,
> options->persist_mode,
> @@ -1763,7 +1783,7 @@ do_open_tun(struct context *c)
> #endif
> /* open the tun device */
> open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
> - c->c1.tuntap);
> + c->c1.tuntap, &c->net_ctx);
>
> /* set the hardware address */
> if (c->options.lladdr)
> diff --git a/src/openvpn/init.h b/src/openvpn/init.h
> index 2b8c2dcc..5f412a33 100644
> --- a/src/openvpn/init.h
> +++ b/src/openvpn/init.h
> @@ -56,7 +56,7 @@ bool print_openssl_info(const struct options *options);
>
> bool do_genkey(const struct options *options);
>
> -bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx);
> +bool do_persist_tuntap(struct options *options, openvpn_net_ctx_t *ctx);
>
> bool possibly_become_daemon(const struct options *options);
>
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index b00acf7e..87d6fc31 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -3667,6 +3667,13 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
> o->tuntap_options.disable_dco = !dco_check_option_conflict(D_DCO, o);
> #endif
>
> + if (dco_enabled(o) && o->dev_node)
> + {
> + msg(M_WARN, "Note: ignoring --dev-node as it has no effect when using "
> + "data channel offload");
> + o->dev_node = NULL;
> + }
> +
> /*
> * Save certain parms before modifying options during connect, especially
> * when using --pull
> diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
> index 3f9aa6ae..30abfdd1 100644
> --- a/src/openvpn/tun.c
> +++ b/src/openvpn/tun.c
> @@ -37,6 +37,7 @@
>
> #include "syshead.h"
>
> +#include "openvpn.h"
> #include "tun.h"
> #include "fdmisc.h"
> #include "common.h"
> @@ -1723,6 +1724,15 @@ tun_name_is_fixed(const char *dev)
> return has_digit(dev);
> }
>
> +#if defined(TARGET_LINUX)
> +static bool
> +tun_dco_enabled(struct tuntap *tt)
> +{
> + return !tt->options.disable_dco;
> +}
> +#endif
> +
> +
> #if !(defined(_WIN32) || defined(TARGET_LINUX))
> static void
> open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
> @@ -1831,6 +1841,76 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
> }
> #endif /* !_WIN32 && !TARGET_LINUX */
>
> +#if defined(TARGET_LINUX)
> +static void
> +open_tun_dco_generic(const char *dev, const char *dev_type,
> + struct tuntap *tt, openvpn_net_ctx_t *ctx)
> +{
> + char dynamic_name[256];
> + bool dynamic_opened = false;
> +
> + if (tt->type == DEV_TYPE_NULL)
> + {
> + open_null(tt);
> + return;
> + }
> +
> + /*
> + * dynamic open is indicated by --dev specified without
> + * explicit unit number. Try opening /dev/[dev]n
I guess "/dev/[dev]n" should just be "[dev]n" ?
> + * where n = [0, 255].
> + */
> +
> + if (!tun_name_is_fixed(dev))
> + {
> + for (int i = 0; i < 256; ++i)
> + {
> + openvpn_snprintf(dynamic_name, sizeof(dynamic_name),
> + "%s%d", dev, i);
> + if (open_tun_dco(tt, ctx, dynamic_name) == 0)
> + {
> + dynamic_opened = true;
> + msg(M_INFO, "DCO device %s opened", dynamic_name);
> + break;
> + }
> + msg(D_READ_WRITE | M_ERRNO, "Tried opening %s (failed)", dynamic_name);
> + }
> + if (!dynamic_opened)
> + {
> + msg(M_FATAL, "Cannot allocate DCO dev dynamically");
> + }
> + /* tt->actual_name is passed to up and down scripts and used as
> + * the ifconfig dev name */
> + tt->actual_name = string_alloc(dynamic_name, NULL);
> + }
> + /*
> + * explicit unit number specified
> + */
> + else
> + {
> + int ret = open_tun_dco(tt, ctx, dev);
> + if (ret == -EEXIST)
> + {
> + msg(M_INFO, "DCO device %s already exists, won't be destroyed at shutdown",
> + dev);
> + tt->persistent_if = true;
> + }
> + else if (ret < 0)
> + {
> + msg(M_ERR, "Cannot open DCO device %s: %s (%d)", dev,
> + strerror(-ret), ret);
> + }
> + else
> + {
> + msg(M_INFO, "DCO device %s opened", dev);
> + }
> +
> + /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
> + tt->actual_name = string_alloc(dev, NULL);
> + }
> +}
> +#endif /* TARGET_LINUX */
> +
> #if !defined(_WIN32)
> static void
> close_tun_generic(struct tuntap *tt)
> @@ -1847,7 +1927,8 @@ close_tun_generic(struct tuntap *tt)
>
> #if defined (TARGET_ANDROID)
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> #define ANDROID_TUNNAME "vpnservice-tun"
> struct user_pass up;
> @@ -1944,7 +2025,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
> #if !PEDANTIC
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> struct ifreq ifr;
>
> @@ -1955,6 +2037,10 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
> {
> open_null(tt);
> }
> + else if (tun_dco_enabled(tt))
> + {
> + open_tun_dco_generic(dev, dev_type, tt, ctx);
> + }
> else
> {
> /*
> @@ -2061,7 +2147,8 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun
> #else /* if !PEDANTIC */
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> ASSERT(0);
> }
> @@ -2086,7 +2173,8 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node,
> clear_tuntap(tt);
> tt->type = dev_type_enum(dev, dev_type);
> tt->options = *options;
> - open_tun(dev, dev_type, dev_node, tt);
> +
> + open_tun(dev, dev_type, dev_node, tt, ctx);
> if (ioctl(tt->fd, TUNSETPERSIST, persist_mode) < 0)
> {
> msg(M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
> @@ -2204,6 +2292,12 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
> net_ctx_reset(ctx);
> }
>
> +#ifdef TARGET_LINUX
> + if (tun_dco_enabled(tt))
> + {
> + close_tun_dco(tt, ctx);
> + }
> +#endif
> close_tun_generic(tt);
> free(tt);
> }
> @@ -2227,7 +2321,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
> #endif
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
> struct lifreq ifr;
> @@ -2579,7 +2674,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
> #elif defined(TARGET_OPENBSD)
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> open_tun_generic(dev, dev_type, dev_node, true, tt);
>
> @@ -2673,7 +2769,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
> */
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> open_tun_generic(dev, dev_type, dev_node, true, tt);
>
> @@ -2813,7 +2910,8 @@ freebsd_modify_read_write_return(int len)
> }
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> open_tun_generic(dev, dev_type, dev_node, true, tt);
>
> @@ -2941,7 +3039,8 @@ dragonfly_modify_read_write_return(int len)
> }
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> open_tun_generic(dev, dev_type, dev_node, true, tt);
>
> @@ -3169,7 +3268,8 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st
> #endif /* ifdef HAVE_NET_IF_UTUN_H */
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> #ifdef HAVE_NET_IF_UTUN_H
> /* If dev_node does not start start with utun assume regular tun/tap */
> @@ -3276,7 +3376,8 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len)
> #elif defined(TARGET_AIX)
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> char tunname[256];
> char dynamic_name[20];
> @@ -6585,7 +6686,8 @@ tuntap_post_open(struct tuntap *tt, const char *device_guid)
> }
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> const char *device_guid = NULL;
>
> @@ -6894,7 +6996,8 @@ ipset2ascii_all(struct gc_arena *gc)
> #else /* generic */
>
> void
> -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
> +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt,
> + openvpn_net_ctx_t *ctx)
> {
> open_tun_generic(dev, dev_type, dev_node, true, tt);
> }
> diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
> index b7786f46..8ec8f51f 100644
> --- a/src/openvpn/tun.h
> +++ b/src/openvpn/tun.h
> @@ -249,7 +249,7 @@ tuntap_ring_empty(struct tuntap *tt)
> */
>
> void open_tun(const char *dev, const char *dev_type, const char *dev_node,
> - struct tuntap *tt);
> + struct tuntap *tt, openvpn_net_ctx_t *ctx);
>
> void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
>
The rest looks good!
Basically the code is the same but it was moved out to a separate
functon in order to avoid more ifdefs ugliness. We are now duplicating
the loop that creates the varios devX strings, but we can live with that.
Not sure my own ACK makes sense, but FWIW:
Acked-by: Antonio Quartulli <a@...2181...>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices
2022-07-21 18:24 ` [Openvpn-devel] [PATCH v11 05/25] dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices Gert Doering
2022-07-27 13:06 ` Antonio Quartulli
@ 2022-07-27 18:00 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-27 18:00 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
This is a joint work with many iterations... so we have Antonio's ACK
on "the changes v10 to v11", and my ACK on the rest.
The patch has been tested on Linux with and without DCO enabled (passes
everything in both cases, as long as the system does not have a DCO
driver -> "no DCO" -> fallback).
With this patch, as it is, it will break on a DCO-in-kernel system if
built with --enable-dco, as half the functionality is missing - but on
any combination, it will fall back to non-DCO and that works. For
"with DCO in kernel", I've tested this before sending the patch (on
top of the DCO tree).
For good measure, since this is touching non-linux code as well
(due to the extra argument to open_tun()) I've thrown it at the
rejuvenated buildbot army - thanks, Dirk! - and that one was happy too.
I have *not* tested the --mktun branch (trivial enough to stare at)
as we want to get rid of that functionality anyway. Until then, it's
at least still working :-)
Your patch has been applied to the master branch.
commit 6a2fe47a482e270936bc1c9713fc55837b4551d5
Author: Antonio Quartulli
Date: Thu Jul 21 20:24:25 2022 +0200
dco: introduce open_tun_dco_generic() to open dynamic or fixed-name DCO devices
Signed-off-by: Antonio Quartulli <a@...2181...>
Signed-off-by: Gert Doering <gert@...1296...>
Acked-by: Antonio Quartulli <antonio@...515...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220721182425.1569798-1-gert@...1296...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24717.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API
2022-07-06 14:29 ` [Openvpn-devel] [PATCH pre-05/25] networking: add net_iface_type API Antonio Quartulli
2022-07-06 14:29 ` [Openvpn-devel] [PATCH v6 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
@ 2022-07-11 8:10 ` Antonio Quartulli
2022-07-13 10:53 ` Gert Doering
2022-07-13 12:43 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-11 8:10 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This new API can be used to retrieve the type of a specific interface.
It's mostly platform dependant, but right now expected values are
"ovpn-dco", "tun" or "tap".
Other values are possible too, but they are not of interest to us.
This commit also extends the networking unit-test by using the newly
introduced API in conjunction with iface_new and iface_del.
The t_next.sh script has been slightly adapted to allow running these
tests in standalone (as they don't require any iproute2 counterpart).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* added unit-test for net_iface_type()
* adjusted t_net.sh script so that iface_new/type/del could be executed
automatically
src/openvpn/networking.h | 14 +++
src/openvpn/networking_iproute2.c | 9 ++
src/openvpn/networking_sitnl.c | 109 +++++++++++++++++++++
tests/t_net.sh | 15 ++-
tests/unit_tests/openvpn/test_networking.c | 25 +++--
5 files changed, 163 insertions(+), 9 deletions(-)
diff --git a/src/openvpn/networking.h b/src/openvpn/networking.h
index 79963756..cf6d39ac 100644
--- a/src/openvpn/networking.h
+++ b/src/openvpn/networking.h
@@ -23,6 +23,8 @@
#include "syshead.h"
+#define IFACE_TYPE_LEN_MAX 64
+
struct context;
#ifdef ENABLE_SITNL
@@ -100,6 +102,18 @@ void net_ctx_free(openvpn_net_ctx_t *ctx);
int net_iface_new(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface,
const char *type, void *arg);
+/**
+ * Retrieve the interface type
+ *
+ * @param ctx the implementation specific context
+ * @param iface interface to query
+ * @param type buffer where the type will be stored
+ *
+ * @return 0 on success, a negative error code otherwise
+ */
+int net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX]);
+
/**
* Remove an interface
*
diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c
index 4b220576..3062c1da 100644
--- a/src/openvpn/networking_iproute2.c
+++ b/src/openvpn/networking_iproute2.c
@@ -78,6 +78,15 @@ net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type,
return 0;
}
+int
+net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX])
+{
+ /* not supported by iproute2 */
+ msg(M_WARN, "%s: operation not supported by iproute2 backend", __func__);
+ return -1;
+}
+
int
net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
{
diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c
index 0944ad0a..e97db3f7 100644
--- a/src/openvpn/networking_sitnl.c
+++ b/src/openvpn/networking_sitnl.c
@@ -1366,6 +1366,115 @@ err:
return ret;
}
+static int
+sitnl_parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags)
+{
+ unsigned short type;
+
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+
+ while (RTA_OK(rta, len))
+ {
+ type = rta->rta_type & ~flags;
+
+ if ((type <= max) && (!tb[type]))
+ {
+ tb[type] = rta;
+ }
+
+ rta = RTA_NEXT(rta, len);
+ }
+
+ if (len)
+ {
+ msg(D_ROUTE, "%s: %d bytes not parsed! (rta_len=%d)", __func__, len,
+ rta->rta_len);
+ }
+
+ return 0;
+}
+
+static int
+sitnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ return sitnl_parse_rtattr_flags(tb, max, rta, len, 0);
+}
+
+#define sitnl_parse_rtattr_nested(tb, max, rta) \
+ (sitnl_parse_rtattr_flags(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta), \
+ NLA_F_NESTED))
+
+static int
+sitnl_type_save(struct nlmsghdr *n, void *arg)
+{
+ char *type = arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr *tb[IFLA_MAX + 1];
+ int ret;
+
+ ret = sitnl_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (tb[IFLA_LINKINFO])
+ {
+ struct rtattr *tb_link[IFLA_INFO_MAX + 1];
+
+ ret = sitnl_parse_rtattr_nested(tb_link, IFLA_INFO_MAX,
+ tb[IFLA_LINKINFO]);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (!tb_link[IFLA_INFO_KIND])
+ {
+ return -ENOENT;
+ }
+
+ strncpynt(type, RTA_DATA(tb_link[IFLA_INFO_KIND]), IFACE_TYPE_LEN_MAX);
+ }
+
+ return 0;
+}
+
+int
+net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX])
+{
+ struct sitnl_link_req req = { };
+ int ifindex = if_nametoindex(iface);
+
+ if (!ifindex)
+ {
+ return errno;
+ }
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETLINK;
+
+ req.i.ifi_family = AF_PACKET;
+ req.i.ifi_index = ifindex;
+
+ memset(type, 0, IFACE_TYPE_LEN_MAX);
+
+ int ret = sitnl_send(&req.n, 0, 0, sitnl_type_save, type);
+ if (ret < 0)
+ {
+ msg(D_ROUTE, "%s: cannot retrieve iface %s: %s (%d)", __func__, iface,
+ strerror(-ret), ret);
+ return ret;
+ }
+
+ msg(D_ROUTE, "%s: type of %s: %s", __func__, iface, type);
+
+ return 0;
+}
+
int
net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
{
diff --git a/tests/t_net.sh b/tests/t_net.sh
index af78152c..bc91f825 100755
--- a/tests/t_net.sh
+++ b/tests/t_net.sh
@@ -2,7 +2,8 @@
IFACE="ovpn-dummy0"
UNIT_TEST="./unit_tests/openvpn/networking_testdriver"
-MAX_TEST=${1:-7}
+LAST_AUTO_TEST=7
+LAST_TEST=8
srcdir="${srcdir:-.}"
top_builddir="${top_builddir:-..}"
@@ -128,7 +129,7 @@ else
fi
fi
-for i in $(seq 0 $MAX_TEST); do
+for i in $(seq 0 $LAST_AUTO_TEST); do
# reload dummy module to cleanup state
reload_dummy
typeset -a STATE_TEST
@@ -168,4 +169,14 @@ done
# remove interface for good
$RUN_SUDO ip link del $IFACE
+for i in $(seq $(($LAST_AUTO_TEST + 1)) ${LAST_TEST}); do
+ $RUN_SUDO $UNIT_TEST $i
+ if [ $? -ne 0 ]; then
+ echo "unit-test $i errored out"
+ exit 1
+ fi
+
+ echo "Test $i: OK"
+done
+
exit 0
diff --git a/tests/unit_tests/openvpn/test_networking.c b/tests/unit_tests/openvpn/test_networking.c
index 10ed2cb5..c3baedb4 100644
--- a/tests/unit_tests/openvpn/test_networking.c
+++ b/tests/unit_tests/openvpn/test_networking.c
@@ -2,6 +2,7 @@
#include "syshead.h"
#include "networking.h"
+#include <assert.h>
static char *iface = "ovpn-dummy0";
@@ -16,14 +17,23 @@ net__iface_up(bool up)
static int
net__iface_new(const char *name, const char *type)
{
- printf("CMD: ip link add %s type %s\n", name, type);
return net_iface_new(NULL, name, type, NULL);
}
+static int
+net__iface_type(const char *name, const char *type)
+{
+ char ret_type[IFACE_TYPE_LEN_MAX];
+
+ assert(net_iface_type(NULL, name, ret_type) == 0);
+ assert(strcmp(type, ret_type) == 0);
+
+ return 0;
+}
+
static int
net__iface_del(const char *name)
{
- printf("CMD: ip link del %s\n", name);
return net_iface_del(NULL, name);
}
@@ -205,7 +215,7 @@ net__route_v6_add_gw(const char *dst_str, int prefixlen, const char *gw_str,
static void
usage(char *name)
{
- printf("Usage: %s <0-9>\n", name);
+ printf("Usage: %s <0-8>\n", name);
}
int
@@ -257,11 +267,12 @@ main(int argc, char *argv[])
case 7:
return net__route_v6_add_gw("2001:cafe:babe::", 48, "2001::2", 600);
+ /* following tests are standalone and do not print any CMD= */
case 8:
- return net__iface_new("dummy0815", "dummy");
-
- case 9:
- return net__iface_del("dummy0815");
+ assert(net__iface_new("dummy0815", "dummy") == 0);
+ assert(net__iface_type("dummy0815", "dummy") == 0);
+ assert(net__iface_del("dummy0815") == 0);
+ return 0;
default:
printf("invalid test: %d\n", test);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API
2022-07-11 8:10 ` [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API Antonio Quartulli
@ 2022-07-13 10:53 ` Gert Doering
2022-07-13 12:05 ` Antonio Quartulli
2022-07-13 12:43 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Gert Doering @ 2022-07-13 10:53 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 1878 bytes --]
Hi,
close, but NAK, due to...
On Mon, Jul 11, 2022 at 10:10:19AM +0200, Antonio Quartulli wrote:
> This new API can be used to retrieve the type of a specific interface.
> It's mostly platform dependant, but right now expected values are
> "ovpn-dco", "tun" or "tap".
>
> Other values are possible too, but they are not of interest to us.
>
> This commit also extends the networking unit-test by using the newly
> introduced API in conjunction with iface_new and iface_del.
>
> The t_next.sh script has been slightly adapted to allow running these
> tests in standalone (as they don't require any iproute2 counterpart).
Typo: t_net.sh (but this is not the crucial bit).
> +int
> +net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
> + char type[IFACE_TYPE_LEN_MAX])
> +{
> + struct sitnl_link_req req = { };
> + int ifindex = if_nametoindex(iface);
> +
> + if (!ifindex)
> + {
> + return errno;
> + }
I think this violates the "errors are returned as negative errno" function
contract (and this is a code change, I won't "fix this on the fly").
> +for i in $(seq $(($LAST_AUTO_TEST + 1)) ${LAST_TEST}); do
Does $(( $A + 1 )) work in POSIX shells?
t_net.sh currenty requires bash, but I wonder if this is really needed,
and a POSIX /bin/sh would do the job.
Also, while at it - maybe add a unit test for "this interface does not exist"
(as we need that functionality for the "--dev tun3" test)?
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API
2022-07-13 10:53 ` Gert Doering
@ 2022-07-13 12:05 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-13 12:05 UTC (permalink / raw)
To: Gert Doering <gert@; +Cc: openvpn-devel
Hi,
On 13/07/2022 12:53, Gert Doering wrote:
> Hi,
>
> close, but NAK, due to...
>
> On Mon, Jul 11, 2022 at 10:10:19AM +0200, Antonio Quartulli wrote:
>> This new API can be used to retrieve the type of a specific interface.
>> It's mostly platform dependant, but right now expected values are
>> "ovpn-dco", "tun" or "tap".
>>
>> Other values are possible too, but they are not of interest to us.
>>
>> This commit also extends the networking unit-test by using the newly
>> introduced API in conjunction with iface_new and iface_del.
>>
>> The t_next.sh script has been slightly adapted to allow running these
>> tests in standalone (as they don't require any iproute2 counterpart).
>
> Typo: t_net.sh (but this is not the crucial bit).
>
>> +int
>> +net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
>> + char type[IFACE_TYPE_LEN_MAX])
>> +{
>> + struct sitnl_link_req req = { };
>> + int ifindex = if_nametoindex(iface);
>> +
>> + if (!ifindex)
>> + {
>> + return errno;
>> + }
>
> I think this violates the "errors are returned as negative errno" function
> contract (and this is a code change, I won't "fix this on the fly").
you're right. I thought I had fixed this...but I must have forgotten to
add it to the commit.
>
>> +for i in $(seq $(($LAST_AUTO_TEST + 1)) ${LAST_TEST}); do
>
> Does $(( $A + 1 )) work in POSIX shells?
>
> t_net.sh currenty requires bash, but I wonder if this is really needed,
> and a POSIX /bin/sh would do the job.
I think there are other bashism in this script (check arrays and stuff
like that). Back then we concluded that "since this script runs only on
linux, it's ok like this".
>
>
> Also, while at it - maybe add a unit test for "this interface does not exist"
> (as we need that functionality for the "--dev tun3" test)?
>
yap yap, can do!
> gert
Thanks!
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v3 pre-05/25] networking: add net_iface_type API
2022-07-11 8:10 ` [Openvpn-devel] [PATCH v2 pre-05/25] networking: add net_iface_type API Antonio Quartulli
2022-07-13 10:53 ` Gert Doering
@ 2022-07-13 12:43 ` Antonio Quartulli
2022-07-13 12:55 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-13 12:43 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This new API can be used to retrieve the type of a specific interface.
It's mostly platform dependant, but right now expected values are
"ovpn-dco", "tun" or "tap".
Other values are possible too, but they are not of interest to us.
This commit also extends the networking unit-test by using the newly
introduced API in conjunction with iface_new and iface_del.
The t_net.sh script has been slightly adapted to allow running these
tests in standalone (as they don't require any iproute2 counterpart).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* extended unit-test to check for "no device exists case"
* fixed typ0 in commit message
* properly return -errno instead of errno
Changes from v1:
* added unit-test for net_iface_type()
* adjusted t_net.sh script so that iface_new/type/del could be executed
automatically
src/openvpn/networking.h | 14 +++
src/openvpn/networking_iproute2.c | 9 ++
src/openvpn/networking_sitnl.c | 109 +++++++++++++++++++++
tests/t_net.sh | 15 ++-
tests/unit_tests/openvpn/test_networking.c | 28 ++++--
5 files changed, 166 insertions(+), 9 deletions(-)
diff --git a/src/openvpn/networking.h b/src/openvpn/networking.h
index 79963756..cf6d39ac 100644
--- a/src/openvpn/networking.h
+++ b/src/openvpn/networking.h
@@ -23,6 +23,8 @@
#include "syshead.h"
+#define IFACE_TYPE_LEN_MAX 64
+
struct context;
#ifdef ENABLE_SITNL
@@ -100,6 +102,18 @@ void net_ctx_free(openvpn_net_ctx_t *ctx);
int net_iface_new(openvpn_net_ctx_t *ctx, const openvpn_net_iface_t *iface,
const char *type, void *arg);
+/**
+ * Retrieve the interface type
+ *
+ * @param ctx the implementation specific context
+ * @param iface interface to query
+ * @param type buffer where the type will be stored
+ *
+ * @return 0 on success, a negative error code otherwise
+ */
+int net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX]);
+
/**
* Remove an interface
*
diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c
index 4b220576..3062c1da 100644
--- a/src/openvpn/networking_iproute2.c
+++ b/src/openvpn/networking_iproute2.c
@@ -78,6 +78,15 @@ net_iface_new(openvpn_net_ctx_t *ctx, const char *iface, const char *type,
return 0;
}
+int
+net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX])
+{
+ /* not supported by iproute2 */
+ msg(M_WARN, "%s: operation not supported by iproute2 backend", __func__);
+ return -1;
+}
+
int
net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
{
diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c
index 0944ad0a..b2f3ac72 100644
--- a/src/openvpn/networking_sitnl.c
+++ b/src/openvpn/networking_sitnl.c
@@ -1366,6 +1366,115 @@ err:
return ret;
}
+static int
+sitnl_parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len, unsigned short flags)
+{
+ unsigned short type;
+
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+
+ while (RTA_OK(rta, len))
+ {
+ type = rta->rta_type & ~flags;
+
+ if ((type <= max) && (!tb[type]))
+ {
+ tb[type] = rta;
+ }
+
+ rta = RTA_NEXT(rta, len);
+ }
+
+ if (len)
+ {
+ msg(D_ROUTE, "%s: %d bytes not parsed! (rta_len=%d)", __func__, len,
+ rta->rta_len);
+ }
+
+ return 0;
+}
+
+static int
+sitnl_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ return sitnl_parse_rtattr_flags(tb, max, rta, len, 0);
+}
+
+#define sitnl_parse_rtattr_nested(tb, max, rta) \
+ (sitnl_parse_rtattr_flags(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta), \
+ NLA_F_NESTED))
+
+static int
+sitnl_type_save(struct nlmsghdr *n, void *arg)
+{
+ char *type = arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr *tb[IFLA_MAX + 1];
+ int ret;
+
+ ret = sitnl_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (tb[IFLA_LINKINFO])
+ {
+ struct rtattr *tb_link[IFLA_INFO_MAX + 1];
+
+ ret = sitnl_parse_rtattr_nested(tb_link, IFLA_INFO_MAX,
+ tb[IFLA_LINKINFO]);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (!tb_link[IFLA_INFO_KIND])
+ {
+ return -ENOENT;
+ }
+
+ strncpynt(type, RTA_DATA(tb_link[IFLA_INFO_KIND]), IFACE_TYPE_LEN_MAX);
+ }
+
+ return 0;
+}
+
+int
+net_iface_type(openvpn_net_ctx_t *ctx, const char *iface,
+ char type[IFACE_TYPE_LEN_MAX])
+{
+ struct sitnl_link_req req = { };
+ int ifindex = if_nametoindex(iface);
+
+ if (!ifindex)
+ {
+ return -errno;
+ }
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETLINK;
+
+ req.i.ifi_family = AF_PACKET;
+ req.i.ifi_index = ifindex;
+
+ memset(type, 0, IFACE_TYPE_LEN_MAX);
+
+ int ret = sitnl_send(&req.n, 0, 0, sitnl_type_save, type);
+ if (ret < 0)
+ {
+ msg(D_ROUTE, "%s: cannot retrieve iface %s: %s (%d)", __func__, iface,
+ strerror(-ret), ret);
+ return ret;
+ }
+
+ msg(D_ROUTE, "%s: type of %s: %s", __func__, iface, type);
+
+ return 0;
+}
+
int
net_iface_del(openvpn_net_ctx_t *ctx, const char *iface)
{
diff --git a/tests/t_net.sh b/tests/t_net.sh
index af78152c..bc91f825 100755
--- a/tests/t_net.sh
+++ b/tests/t_net.sh
@@ -2,7 +2,8 @@
IFACE="ovpn-dummy0"
UNIT_TEST="./unit_tests/openvpn/networking_testdriver"
-MAX_TEST=${1:-7}
+LAST_AUTO_TEST=7
+LAST_TEST=8
srcdir="${srcdir:-.}"
top_builddir="${top_builddir:-..}"
@@ -128,7 +129,7 @@ else
fi
fi
-for i in $(seq 0 $MAX_TEST); do
+for i in $(seq 0 $LAST_AUTO_TEST); do
# reload dummy module to cleanup state
reload_dummy
typeset -a STATE_TEST
@@ -168,4 +169,14 @@ done
# remove interface for good
$RUN_SUDO ip link del $IFACE
+for i in $(seq $(($LAST_AUTO_TEST + 1)) ${LAST_TEST}); do
+ $RUN_SUDO $UNIT_TEST $i
+ if [ $? -ne 0 ]; then
+ echo "unit-test $i errored out"
+ exit 1
+ fi
+
+ echo "Test $i: OK"
+done
+
exit 0
diff --git a/tests/unit_tests/openvpn/test_networking.c b/tests/unit_tests/openvpn/test_networking.c
index 10ed2cb5..04347076 100644
--- a/tests/unit_tests/openvpn/test_networking.c
+++ b/tests/unit_tests/openvpn/test_networking.c
@@ -2,6 +2,7 @@
#include "syshead.h"
#include "networking.h"
+#include <assert.h>
static char *iface = "ovpn-dummy0";
@@ -16,14 +17,25 @@ net__iface_up(bool up)
static int
net__iface_new(const char *name, const char *type)
{
- printf("CMD: ip link add %s type %s\n", name, type);
return net_iface_new(NULL, name, type, NULL);
}
+static int
+net__iface_type(const char *name, const char *type)
+{
+ char ret_type[IFACE_TYPE_LEN_MAX];
+ int ret = net_iface_type(NULL, name, ret_type);
+ if (ret == 0)
+ {
+ assert(strcmp(type, ret_type) == 0);
+ }
+
+ return ret;
+}
+
static int
net__iface_del(const char *name)
{
- printf("CMD: ip link del %s\n", name);
return net_iface_del(NULL, name);
}
@@ -205,7 +217,7 @@ net__route_v6_add_gw(const char *dst_str, int prefixlen, const char *gw_str,
static void
usage(char *name)
{
- printf("Usage: %s <0-9>\n", name);
+ printf("Usage: %s <0-8>\n", name);
}
int
@@ -257,11 +269,13 @@ main(int argc, char *argv[])
case 7:
return net__route_v6_add_gw("2001:cafe:babe::", 48, "2001::2", 600);
+ /* following tests are standalone and do not print any CMD= */
case 8:
- return net__iface_new("dummy0815", "dummy");
-
- case 9:
- return net__iface_del("dummy0815");
+ assert(net__iface_new("dummy0815", "dummy") == 0);
+ assert(net__iface_type("dummy0815", "dummy") == 0);
+ assert(net__iface_del("dummy0815") == 0);
+ assert(net__iface_type("dummy0815", NULL) == -ENODEV);
+ return 0;
default:
printf("invalid test: %d\n", test);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: networking: add net_iface_type API
2022-07-13 12:43 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
@ 2022-07-13 12:55 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-13 12:55 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
Thanks for the v3 changes.
I tested v2 very thoroughly on "non sitnl" builds (FreeBSD, Linux with
--enable-iproute2) to make sure this really only affects sitnl - and
that has not changed with v3. What has changed is "-errno", typo fix
in the commit message, and the extra test for "what happens if the
interface does not exist?" - these have been tested in a sitnl build,
and pass.
OpenVPN itself does not yet use that new function, only the unit test.
As agree on IRC, we do not touch the bash-nature of t_net.sh today -
we might want to move to "more POSIX" one day, but today Linux systems
are assumed to have bash, and this is really linux (+sitnl) only.
Your patch has been applied to the master branch.
commit 26b6b6f0673fa1f3806c447a18bb0f705b231b35
Author: Antonio Quartulli
Date: Wed Jul 13 14:43:32 2022 +0200
networking: add net_iface_type API
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220713124332.16147-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24688.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (4 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 05/25] dco: let open_tun_generic handle the DCO case Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:47 ` Arne Schwabe
` (2 more replies)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected Antonio Quartulli
` (18 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/init.c | 49 ++++++++++++++++++++++++++++++++--------
src/openvpn/ssl_common.h | 23 +++++++++++++++++++
2 files changed, 63 insertions(+), 9 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 7099eba4..7ab2c9a2 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -55,6 +55,7 @@
#include "auth_token.h"
#include "mss.h"
#include "mudp.h"
+#include "dco.h"
#include "memdbg.h"
@@ -1295,15 +1296,23 @@ do_init_timers(struct context *c, bool deferred)
}
/* initialize pings */
-
- if (c->options.ping_send_timeout)
+ if (dco_enabled(&c->options))
{
- event_timeout_init(&c->c2.ping_send_interval, c->options.ping_send_timeout, 0);
+ /* The DCO kernel module will send the pings instead of user space */
+ event_timeout_clear(&c->c2.ping_rec_interval);
+ event_timeout_clear(&c->c2.ping_send_interval);
}
-
- if (c->options.ping_rec_timeout)
+ else
{
- event_timeout_init(&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now);
+ if (c->options.ping_send_timeout)
+ {
+ event_timeout_init(&c->c2.ping_send_interval, c->options.ping_send_timeout, 0);
+ }
+
+ if (c->options.ping_rec_timeout)
+ {
+ event_timeout_init(&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now);
+ }
}
if (!deferred)
@@ -1708,6 +1717,12 @@ do_open_tun(struct context *c)
/* initialize (but do not open) tun/tap object */
do_init_tun(c);
+ /* inherit the dco context from the tuntap object */
+ if (c->c2.tls_multi)
+ {
+ c->c2.tls_multi->dco = &c->c1.tuntap->dco;
+ }
+
#ifdef _WIN32
/* store (hide) interactive service handle in tuntap_options */
c->c1.tuntap->options.msg_channel = c->options.msg_channel;
@@ -1756,6 +1771,11 @@ do_open_tun(struct context *c)
/* Store the old fd inside the fd so open_tun can use it */
c->c1.tuntap->fd = oldtunfd;
#endif
+ if (dco_enabled(&c->options))
+ {
+ ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
+ }
+
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
c->c1.tuntap, &c->net_ctx);
@@ -2979,12 +2999,20 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
}
}
+ /* let the TLS engine know if keys have to be installed in DCO or not */
+ to.disable_dco = !dco_enabled(options);
+
/*
* Initialize OpenVPN's master TLS-mode object.
*/
if (flags & CF_INIT_TLS_MULTI)
{
c->c2.tls_multi = tls_multi_init(&to);
+ /* inherit the dco context from the tuntap object */
+ if (c->c1.tuntap)
+ {
+ c->c2.tls_multi->dco = &c->c1.tuntap->dco;
+ }
}
if (flags & CF_INIT_TLS_AUTH_STANDALONE)
@@ -4365,15 +4393,18 @@ inherit_context_child(struct context *dest,
#endif
/* context init */
+
+ /* inherit tun/tap interface object now as it may be required
+ * to initialize the DCO context in init_instance()
+ */
+ dest->c1.tuntap = src->c1.tuntap;
+
init_instance(dest, src->c2.es, CC_NO_CLOSE | CC_USR1_TO_HUP);
if (IS_SIG(dest))
{
return;
}
- /* inherit tun/tap interface object */
- dest->c1.tuntap = src->c1.tuntap;
-
/* UDP inherits some extra things which TCP does not */
if (dest->mode == CM_CHILD_UDP)
{
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index cef2611b..83373a97 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -167,6 +167,12 @@ enum auth_deferred_result {
ACF_FAILED /**< deferred auth has failed */
};
+enum dco_key_status {
+ DCO_NOT_INSTALLED,
+ DCO_INSTALLED_PRIMARY,
+ DCO_INSTALLED_SECONDARY
+};
+
/**
* Security parameter state of one TLS and data channel %key session.
* @ingroup control_processor
@@ -197,6 +203,12 @@ struct key_state
*/
int key_id;
+ /**
+ * Key id for this key_state, inherited from struct tls_session.
+ * @see tls_multi::peer_id.
+ */
+ uint32_t peer_id;
+
struct key_state_ssl ks_ssl; /* contains SSL object and BIOs for the control channel */
time_t initial; /* when we created this session */
@@ -241,6 +253,8 @@ struct key_state
struct auth_deferred_status plugin_auth;
struct auth_deferred_status script_auth;
+
+ enum dco_key_status dco_status;
};
/** Control channel wrapping (--tls-auth/--tls-crypt) context */
@@ -404,6 +418,8 @@ struct tls_options
const char *ekm_label;
size_t ekm_label_size;
size_t ekm_size;
+
+ bool disable_dco; /**< Whether keys have to be installed in DCO or not */
};
/** @addtogroup control_processor
@@ -636,6 +652,13 @@ struct tls_multi
/**< Array of \c tls_session objects
* representing control channel
* sessions with the remote peer. */
+
+ /* Only used when DCO is used to remember how many keys we installed
+ * for this session */
+ int dco_keys_installed;
+ bool dco_peer_added;
+
+ dco_context_t *dco;
};
/** gets an item of \c key_state objects in the
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object Antonio Quartulli
@ 2022-06-27 11:47 ` Arne Schwabe
2022-07-14 14:27 ` Gert Doering
2022-07-20 12:30 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:47 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> src/openvpn/init.c | 49 ++++++++++++++++++++++++++++++++--------
> src/openvpn/ssl_common.h | 23 +++++++++++++++++++
> 2 files changed, 63 insertions(+), 9 deletions(-)
>
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object Antonio Quartulli
2022-06-27 11:47 ` Arne Schwabe
@ 2022-07-14 14:27 ` Gert Doering
2022-07-18 22:50 ` Antonio Quartulli
2022-07-20 12:30 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Gert Doering @ 2022-07-14 14:27 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 1641 bytes --]
Hi,
On Fri, Jun 24, 2022 at 10:37:50AM +0200, Antonio Quartulli wrote:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> src/openvpn/init.c | 49 ++++++++++++++++++++++++++++++++--------
> src/openvpn/ssl_common.h | 23 +++++++++++++++++++
> 2 files changed, 63 insertions(+), 9 deletions(-)
Without trying to understand the code flow in init.c, is there a
deeper reason why this happens twice?
> @@ -1708,6 +1717,12 @@ do_open_tun(struct context *c)
> /* initialize (but do not open) tun/tap object */
> do_init_tun(c);
>
> + /* inherit the dco context from the tuntap object */
> + if (c->c2.tls_multi)
> + {
> + c->c2.tls_multi->dco = &c->c1.tuntap->dco;
> + }
> +
[..]
> @@ -2979,12 +2999,20 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
[..]
> /*
> * Initialize OpenVPN's master TLS-mode object.
> */
> if (flags & CF_INIT_TLS_MULTI)
> {
> c->c2.tls_multi = tls_multi_init(&to);
> + /* inherit the dco context from the tuntap object */
> + if (c->c1.tuntap)
> + {
> + c->c2.tls_multi->dco = &c->c1.tuntap->dco;
> + }
> }
... and here again, but only sometimes?
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object
2022-07-14 14:27 ` Gert Doering
@ 2022-07-18 22:50 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-18 22:50 UTC (permalink / raw)
To: Gert Doering <gert@; +Cc: openvpn-devel
Hi,
On 14/07/2022 16:27, Gert Doering wrote:
> Hi,
>
> On Fri, Jun 24, 2022 at 10:37:50AM +0200, Antonio Quartulli wrote:
>> Signed-off-by: Antonio Quartulli <a@...2181...>
>> ---
>> src/openvpn/init.c | 49 ++++++++++++++++++++++++++++++++--------
>> src/openvpn/ssl_common.h | 23 +++++++++++++++++++
>> 2 files changed, 63 insertions(+), 9 deletions(-)
>
> Without trying to understand the code flow in init.c, is there a
> deeper reason why this happens twice?
>
>> @@ -1708,6 +1717,12 @@ do_open_tun(struct context *c)
>> /* initialize (but do not open) tun/tap object */
>> do_init_tun(c);
>>
>> + /* inherit the dco context from the tuntap object */
>> + if (c->c2.tls_multi)
>> + {
>> + c->c2.tls_multi->dco = &c->c1.tuntap->dco;
>> + }
>> +
client code path. The server has no tls-multi object when passing here.
> [..]
>
>> @@ -2979,12 +2999,20 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
> [..]
>> /*
>> * Initialize OpenVPN's master TLS-mode object.
>> */
>> if (flags & CF_INIT_TLS_MULTI)
>> {
>> c->c2.tls_multi = tls_multi_init(&to);
>> + /* inherit the dco context from the tuntap object */
>> + if (c->c1.tuntap)
>> + {
>> + c->c2.tls_multi->dco = &c->c1.tuntap->dco;
>> + }
>> }
>
> ... and here again, but only sometimes?
server code path. The client has no tuntap object when passing here
(it's done quite early, so we need the previous snippet).
The server executes this code for each new client.
I hope this clarifies.
Cheers,
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 06/25] dco: initialize context and save pointer in TLS object
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object Antonio Quartulli
2022-06-27 11:47 ` Arne Schwabe
2022-07-14 14:27 ` Gert Doering
@ 2022-07-20 12:30 ` Antonio Quartulli
2022-07-27 18:14 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-20 12:30 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* renamed disable_dco to dco_enabled
src/openvpn/init.c | 49 ++++++++++++++++++++++++++++++++--------
src/openvpn/ssl_common.h | 23 +++++++++++++++++++
2 files changed, 63 insertions(+), 9 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 779fc4a5..b6e1707f 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -55,6 +55,7 @@
#include "auth_token.h"
#include "mss.h"
#include "mudp.h"
+#include "dco.h"
#include "memdbg.h"
@@ -1315,15 +1316,23 @@ do_init_timers(struct context *c, bool deferred)
}
/* initialize pings */
-
- if (c->options.ping_send_timeout)
+ if (dco_enabled(&c->options))
{
- event_timeout_init(&c->c2.ping_send_interval, c->options.ping_send_timeout, 0);
+ /* The DCO kernel module will send the pings instead of user space */
+ event_timeout_clear(&c->c2.ping_rec_interval);
+ event_timeout_clear(&c->c2.ping_send_interval);
}
-
- if (c->options.ping_rec_timeout)
+ else
{
- event_timeout_init(&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now);
+ if (c->options.ping_send_timeout)
+ {
+ event_timeout_init(&c->c2.ping_send_interval, c->options.ping_send_timeout, 0);
+ }
+
+ if (c->options.ping_rec_timeout)
+ {
+ event_timeout_init(&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now);
+ }
}
if (!deferred)
@@ -1733,6 +1742,12 @@ do_open_tun(struct context *c)
/* initialize (but do not open) tun/tap object */
do_init_tun(c);
+ /* inherit the dco context from the tuntap object */
+ if (c->c2.tls_multi)
+ {
+ c->c2.tls_multi->dco = &c->c1.tuntap->dco;
+ }
+
#ifdef _WIN32
/* store (hide) interactive service handle in tuntap_options */
c->c1.tuntap->options.msg_channel = c->options.msg_channel;
@@ -1781,6 +1796,11 @@ do_open_tun(struct context *c)
/* Store the old fd inside the fd so open_tun can use it */
c->c1.tuntap->fd = oldtunfd;
#endif
+ if (dco_enabled(&c->options))
+ {
+ ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
+ }
+
/* open the tun device */
open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
c->c1.tuntap, &c->net_ctx);
@@ -3004,12 +3024,20 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
}
}
+ /* let the TLS engine know if keys have to be installed in DCO or not */
+ to.dco_enabled = dco_enabled(options);
+
/*
* Initialize OpenVPN's master TLS-mode object.
*/
if (flags & CF_INIT_TLS_MULTI)
{
c->c2.tls_multi = tls_multi_init(&to);
+ /* inherit the dco context from the tuntap object */
+ if (c->c1.tuntap)
+ {
+ c->c2.tls_multi->dco = &c->c1.tuntap->dco;
+ }
}
if (flags & CF_INIT_TLS_AUTH_STANDALONE)
@@ -4389,15 +4417,18 @@ inherit_context_child(struct context *dest,
#endif
/* context init */
+
+ /* inherit tun/tap interface object now as it may be required
+ * to initialize the DCO context in init_instance()
+ */
+ dest->c1.tuntap = src->c1.tuntap;
+
init_instance(dest, src->c2.es, CC_NO_CLOSE | CC_USR1_TO_HUP);
if (IS_SIG(dest))
{
return;
}
- /* inherit tun/tap interface object */
- dest->c1.tuntap = src->c1.tuntap;
-
/* UDP inherits some extra things which TCP does not */
if (dest->mode == CM_CHILD_UDP)
{
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index cef2611b..c565d78c 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -167,6 +167,12 @@ enum auth_deferred_result {
ACF_FAILED /**< deferred auth has failed */
};
+enum dco_key_status {
+ DCO_NOT_INSTALLED,
+ DCO_INSTALLED_PRIMARY,
+ DCO_INSTALLED_SECONDARY
+};
+
/**
* Security parameter state of one TLS and data channel %key session.
* @ingroup control_processor
@@ -197,6 +203,12 @@ struct key_state
*/
int key_id;
+ /**
+ * Key id for this key_state, inherited from struct tls_session.
+ * @see tls_multi::peer_id.
+ */
+ uint32_t peer_id;
+
struct key_state_ssl ks_ssl; /* contains SSL object and BIOs for the control channel */
time_t initial; /* when we created this session */
@@ -241,6 +253,8 @@ struct key_state
struct auth_deferred_status plugin_auth;
struct auth_deferred_status script_auth;
+
+ enum dco_key_status dco_status;
};
/** Control channel wrapping (--tls-auth/--tls-crypt) context */
@@ -404,6 +418,8 @@ struct tls_options
const char *ekm_label;
size_t ekm_label_size;
size_t ekm_size;
+
+ bool dco_enabled; /**< Whether keys have to be installed in DCO or not */
};
/** @addtogroup control_processor
@@ -636,6 +652,13 @@ struct tls_multi
/**< Array of \c tls_session objects
* representing control channel
* sessions with the remote peer. */
+
+ /* Only used when DCO is used to remember how many keys we installed
+ * for this session */
+ int dco_keys_installed;
+ bool dco_peer_added;
+
+ dco_context_t *dco;
};
/** gets an item of \c key_state objects in the
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: initialize context and save pointer in TLS object
2022-07-20 12:30 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-07-27 18:14 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-27 18:14 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
The change to init.c is easier viewed with "-w" - wrapping the existing
code to initialize the ping intervals into "if (!dco_enabled)".
The remainder does not really do much yet - introduce structure elements
and enums, but they are not used yet. Also, record the ACK from Arne
on v1.
Tested on linux without --enable-dco (works), and with --enable-dco
but without kernel DCO (works as well).
Your patch has been applied to the master branch.
commit a2d851d4fa69792f2bcde568e43ec0879d757788
Author: Antonio Quartulli
Date: Wed Jul 20 14:30:21 2022 +0200
dco: initialize context and save pointer in TLS object
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-By: Arne Schwabe <arne@...1227...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220720123021.24281-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24714.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (5 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 06/25] dco: initialize context and save pointer in TLS object Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:17 ` Arne Schwabe
` (2 more replies)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime Antonio Quartulli
` (17 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/Makefile.am | 2 +-
src/openvpn/dco.c | 149 ++++++++++++++++++++++++++++
src/openvpn/openvpn.vcxproj | 1 +
src/openvpn/openvpn.vcxproj.filters | 3 +
4 files changed, 154 insertions(+), 1 deletion(-)
create mode 100644 src/openvpn/dco.c
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 91635b67..aaa1dbce 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -53,7 +53,7 @@ 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.c dco.h dco_internal.h \
dco_linux.c dco_linux.h \
dhcp.c dhcp.h \
dns.c dns.h \
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
new file mode 100644
index 00000000..1e45130a
--- /dev/null
+++ b/src/openvpn/dco.c
@@ -0,0 +1,149 @@
+/*
+ * 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@...1227...>
+ * Copyright (C) 2021-2022 Antonio Quartulli <a@...2181...>
+ * Copyright (C) 2021-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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)
+
+#include "syshead.h"
+#include "dco.h"
+
+static bool
+dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
+{
+ if (ce->fragment)
+ {
+ msg(msglevel, "Note: --fragment disables data channel offload.");
+ return false;
+ }
+
+ if (ce->http_proxy_options)
+ {
+ msg(msglevel, "Note: --http-proxy disables data channel offload.");
+ return false;
+ }
+
+ if (ce->socks_proxy_server)
+ {
+ msg(msglevel, "Note: --socks-proxy disables data channel offload.");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+dco_check_option_conflict(int msglevel, const struct options *o)
+{
+ if (o->tuntap_options.disable_dco)
+ {
+ /* already disabled by --disable-dco, no need to print warnings */
+ return false;
+ }
+
+ if (!dco_available(msglevel))
+ {
+ return false;
+ }
+
+ if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
+ {
+ msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
+ return false;
+ }
+
+ /* At this point the ciphers have already been normalised */
+ if (o->enable_ncp_fallback
+ && !tls_item_in_cipher_list(o->ciphername, DCO_SUPPORTED_CIPHERS))
+ {
+ msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
+ "disables data channel offload.", o->ciphername);
+ return false;
+ }
+
+ if (o->connection_list)
+ {
+ const struct connection_list *l = o->connection_list;
+ for (int i = 0; i < l->len; ++i)
+ {
+ if (!dco_check_option_conflict_ce(l->array[i], msglevel))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (!dco_check_option_conflict_ce(&o->ce, msglevel))
+ {
+ return false;
+ }
+ }
+
+ if (o->mode == MODE_SERVER && o->topology != TOP_SUBNET)
+ {
+ msg(msglevel, "Note: NOT using '--topology subnet' disables data channel offload.");
+ return false;
+ }
+
+#if defined(USE_COMP)
+ if (o->comp.alg != COMP_ALG_UNDEF)
+ {
+ msg(msglevel, "Note: Using compression disables data channel offload.");
+
+ if (o->mode == MODE_SERVER && !(o->comp.flags & COMP_F_MIGRATE))
+ {
+ /* We can end up here from the multi.c call, only print the
+ * note if it is not already enabled */
+ msg(msglevel, "Consider using the '--compress migrate' option.");
+ }
+ return false;
+ }
+#endif
+
+ struct gc_arena gc = gc_new();
+ char *tmp_ciphers = string_alloc(o->ncp_ciphers, &gc);
+ const char *token;
+ while ((token = strsep(&tmp_ciphers, ":")))
+ {
+ if (!tls_item_in_cipher_list(token, DCO_SUPPORTED_CIPHERS))
+ {
+ msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
+ "by ovpn-dco, disabling data channel offload.", token);
+ gc_free(&gc);
+ return false;
+ }
+ }
+ gc_free(&gc);
+
+ return true;
+}
+
+#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index bc1a0300..0b3db7c7 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -276,6 +276,7 @@
<ClCompile Include="crypto.c" />
<ClCompile Include="crypto_openssl.c" />
<ClCompile Include="cryptoapi.c" />
+ <ClCompile Include="dco.c" />
<ClCompile Include="dco_linux.c" />
<ClCompile Include="dhcp.c" />
<ClCompile Include="dns.c" />
diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters
index 3c21a4c6..16905079 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -36,6 +36,9 @@
<ClCompile Include="cryptoapi.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dco.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="dco_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected Antonio Quartulli
@ 2022-06-27 11:17 ` Arne Schwabe
2022-07-12 22:13 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-07-12 22:16 ` [Openvpn-devel] [PATCH pre-07/25] tun: create tun_name_is_fixed helper Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:17 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
We will probably find a bit more odd options that needed to be added
here but don't let the perfect stand in the way of the good.
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 07/25] dco: add option check - disable DCO if conflict is detected
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected Antonio Quartulli
2022-06-27 11:17 ` Arne Schwabe
@ 2022-07-12 22:13 ` Antonio Quartulli
2022-07-18 22:17 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2022-07-12 22:16 ` [Openvpn-devel] [PATCH pre-07/25] tun: create tun_name_is_fixed helper Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-12 22:13 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* add 'already existing device check' to dco_check_option_conflict_platform()
so that DCO can be pre-emptively disabled if the following are true:
- an iface with the same name as provided by the user exists
- the iface is non-DCO
src/openvpn/Makefile.am | 2 +-
src/openvpn/dco.c | 189 ++++++++++++++++++++++++++++
src/openvpn/openvpn.vcxproj | 1 +
src/openvpn/openvpn.vcxproj.filters | 3 +
4 files changed, 194 insertions(+), 1 deletion(-)
create mode 100644 src/openvpn/dco.c
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 91635b67..aaa1dbce 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -53,7 +53,7 @@ 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.c dco.h dco_internal.h \
dco_linux.c dco_linux.h \
dhcp.c dhcp.h \
dns.c dns.h \
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
new file mode 100644
index 00000000..8d1ca37e
--- /dev/null
+++ b/src/openvpn/dco.c
@@ -0,0 +1,189 @@
+/*
+ * 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@...1227...>
+ * Copyright (C) 2021-2022 Antonio Quartulli <a@...2181...>
+ * Copyright (C) 2021-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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)
+
+#include "syshead.h"
+#include "dco.h"
+#include "networking.h"
+#include "options.h"
+#include "ssl_ncp.h"
+#include "tun.h"
+
+static bool
+dco_check_option_conflict_platform(int msglevel, const struct options *o)
+{
+#if defined(TARGET_LINUX)
+ /* if the device name is fixed, we need to check if an interface with this
+ * name already exists. IF it does, it must be a DCO interface, otherwise
+ * DCO has to be disabled in order to continue.
+ */
+ if (tun_name_is_fixed(o->dev))
+ {
+ char iftype[IFACE_TYPE_LEN_MAX];
+ /* we pass NULL as net_ctx because using DCO on Linux implies that we
+ * are using SITNL and the latter does not need any context. This way we
+ * don't need to have the net_ctx percolate all the way here
+ */
+ int ret = net_iface_type(NULL, o->dev, iftype);
+ if ((ret == 0) && (strcmp(iftype, "ovpn-dco") != 0))
+ {
+ msg(msglevel, "Interface %s exists and is non-DCO. Disabling data channel offload",
+ o->dev);
+ return false;
+ }
+ else if ((ret < 0) && (ret != -ENODEV))
+ {
+ msg(msglevel, "Cannot retrieve type of device %s: %s (%d)", o->dev,
+ strerror(-ret), ret);
+ }
+#endif /* if defined(TARGET_LINUX) */
+ return true;
+}
+
+static bool
+dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
+{
+ if (ce->fragment)
+ {
+ msg(msglevel, "Note: --fragment disables data channel offload.");
+ return false;
+ }
+
+ if (ce->http_proxy_options)
+ {
+ msg(msglevel, "Note: --http-proxy disables data channel offload.");
+ return false;
+ }
+
+ if (ce->socks_proxy_server)
+ {
+ msg(msglevel, "Note: --socks-proxy disables data channel offload.");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+dco_check_option_conflict(int msglevel, const struct options *o)
+{
+ if (o->tuntap_options.disable_dco)
+ {
+ /* already disabled by --disable-dco, no need to print warnings */
+ return false;
+ }
+
+ if (!dco_available(msglevel))
+ {
+ return false;
+ }
+
+ if (!dco_check_option_conflict_platform(msglevel, o))
+ {
+ return false;
+ }
+
+ if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
+ {
+ msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
+ return false;
+ }
+
+ /* At this point the ciphers have already been normalised */
+ if (o->enable_ncp_fallback
+ && !tls_item_in_cipher_list(o->ciphername, DCO_SUPPORTED_CIPHERS))
+ {
+ msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
+ "disables data channel offload.", o->ciphername);
+ return false;
+ }
+
+ if (o->connection_list)
+ {
+ const struct connection_list *l = o->connection_list;
+ for (int i = 0; i < l->len; ++i)
+ {
+ if (!dco_check_option_conflict_ce(l->array[i], msglevel))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (!dco_check_option_conflict_ce(&o->ce, msglevel))
+ {
+ return false;
+ }
+ }
+
+ if (o->mode == MODE_SERVER && o->topology != TOP_SUBNET)
+ {
+ msg(msglevel, "Note: NOT using '--topology subnet' disables data channel offload.");
+ return false;
+ }
+
+#if defined(USE_COMP)
+ if (o->comp.alg != COMP_ALG_UNDEF)
+ {
+ msg(msglevel, "Note: Using compression disables data channel offload.");
+
+ if (o->mode == MODE_SERVER && !(o->comp.flags & COMP_F_MIGRATE))
+ {
+ /* We can end up here from the multi.c call, only print the
+ * note if it is not already enabled */
+ msg(msglevel, "Consider using the '--compress migrate' option.");
+ }
+ return false;
+ }
+#endif
+
+ struct gc_arena gc = gc_new();
+ char *tmp_ciphers = string_alloc(o->ncp_ciphers, &gc);
+ const char *token;
+ while ((token = strsep(&tmp_ciphers, ":")))
+ {
+ if (!tls_item_in_cipher_list(token, DCO_SUPPORTED_CIPHERS))
+ {
+ msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
+ "by ovpn-dco, disabling data channel offload.", token);
+ gc_free(&gc);
+ return false;
+ }
+ }
+ gc_free(&gc);
+
+ return true;
+}
+
+#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index bc1a0300..0b3db7c7 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -276,6 +276,7 @@
<ClCompile Include="crypto.c" />
<ClCompile Include="crypto_openssl.c" />
<ClCompile Include="cryptoapi.c" />
+ <ClCompile Include="dco.c" />
<ClCompile Include="dco_linux.c" />
<ClCompile Include="dhcp.c" />
<ClCompile Include="dns.c" />
diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters
index 3c21a4c6..16905079 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -36,6 +36,9 @@
<ClCompile Include="cryptoapi.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dco.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="dco_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v3 07/25] dco: add option check - disable DCO if conflict is detected
2022-07-12 22:13 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-07-18 22:17 ` Antonio Quartulli
2022-07-19 9:25 ` [Openvpn-devel] [PATCH applied] " Gert Doering
0 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-18 22:17 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
** this patch should be applied before 05/27 **
Changes from v2:
* add actual invocation to dco_check_option_conflict() in options.c
* add missing '}' in dco_check_option_conflict_ce()
Changes from v1:
* add 'already existing device check' to dco_check_option_conflict_platform()
so that DCO can be pre-emptively disabled if the following are true:
- an iface with the same name as provided by the user exists
- the iface is non-DCO
src/openvpn/Makefile.am | 2 +-
src/openvpn/dco.c | 190 ++++++++++++++++++++++++++++
src/openvpn/openvpn.vcxproj | 1 +
src/openvpn/openvpn.vcxproj.filters | 3 +
src/openvpn/options.c | 5 +
5 files changed, 200 insertions(+), 1 deletion(-)
create mode 100644 src/openvpn/dco.c
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 91635b67..aaa1dbce 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -53,7 +53,7 @@ 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.c dco.h dco_internal.h \
dco_linux.c dco_linux.h \
dhcp.c dhcp.h \
dns.c dns.h \
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
new file mode 100644
index 00000000..b3fd135f
--- /dev/null
+++ b/src/openvpn/dco.c
@@ -0,0 +1,190 @@
+/*
+ * 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@...1227...>
+ * Copyright (C) 2021-2022 Antonio Quartulli <a@...2181...>
+ * Copyright (C) 2021-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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)
+
+#include "syshead.h"
+#include "dco.h"
+#include "networking.h"
+#include "options.h"
+#include "ssl_ncp.h"
+#include "tun.h"
+
+static bool
+dco_check_option_conflict_platform(int msglevel, const struct options *o)
+{
+#if defined(TARGET_LINUX)
+ /* if the device name is fixed, we need to check if an interface with this
+ * name already exists. IF it does, it must be a DCO interface, otherwise
+ * DCO has to be disabled in order to continue.
+ */
+ if (tun_name_is_fixed(o->dev))
+ {
+ char iftype[IFACE_TYPE_LEN_MAX];
+ /* we pass NULL as net_ctx because using DCO on Linux implies that we
+ * are using SITNL and the latter does not need any context. This way we
+ * don't need to have the net_ctx percolate all the way here
+ */
+ int ret = net_iface_type(NULL, o->dev, iftype);
+ if ((ret == 0) && (strcmp(iftype, "ovpn-dco") != 0))
+ {
+ msg(msglevel, "Interface %s exists and is non-DCO. Disabling data channel offload",
+ o->dev);
+ return false;
+ }
+ else if ((ret < 0) && (ret != -ENODEV))
+ {
+ msg(msglevel, "Cannot retrieve type of device %s: %s (%d)", o->dev,
+ strerror(-ret), ret);
+ }
+ }
+#endif /* if defined(TARGET_LINUX) */
+ return true;
+}
+
+static bool
+dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
+{
+ if (ce->fragment)
+ {
+ msg(msglevel, "Note: --fragment disables data channel offload.");
+ return false;
+ }
+
+ if (ce->http_proxy_options)
+ {
+ msg(msglevel, "Note: --http-proxy disables data channel offload.");
+ return false;
+ }
+
+ if (ce->socks_proxy_server)
+ {
+ msg(msglevel, "Note: --socks-proxy disables data channel offload.");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+dco_check_option_conflict(int msglevel, const struct options *o)
+{
+ if (o->tuntap_options.disable_dco)
+ {
+ /* already disabled by --disable-dco, no need to print warnings */
+ return false;
+ }
+
+ if (!dco_available(msglevel))
+ {
+ return false;
+ }
+
+ if (!dco_check_option_conflict_platform(msglevel, o))
+ {
+ return false;
+ }
+
+ if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
+ {
+ msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
+ return false;
+ }
+
+ /* At this point the ciphers have already been normalised */
+ if (o->enable_ncp_fallback
+ && !tls_item_in_cipher_list(o->ciphername, DCO_SUPPORTED_CIPHERS))
+ {
+ msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
+ "disables data channel offload.", o->ciphername);
+ return false;
+ }
+
+ if (o->connection_list)
+ {
+ const struct connection_list *l = o->connection_list;
+ for (int i = 0; i < l->len; ++i)
+ {
+ if (!dco_check_option_conflict_ce(l->array[i], msglevel))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (!dco_check_option_conflict_ce(&o->ce, msglevel))
+ {
+ return false;
+ }
+ }
+
+ if (o->mode == MODE_SERVER && o->topology != TOP_SUBNET)
+ {
+ msg(msglevel, "Note: NOT using '--topology subnet' disables data channel offload.");
+ return false;
+ }
+
+#if defined(USE_COMP)
+ if (o->comp.alg != COMP_ALG_UNDEF)
+ {
+ msg(msglevel, "Note: Using compression disables data channel offload.");
+
+ if (o->mode == MODE_SERVER && !(o->comp.flags & COMP_F_MIGRATE))
+ {
+ /* We can end up here from the multi.c call, only print the
+ * note if it is not already enabled */
+ msg(msglevel, "Consider using the '--compress migrate' option.");
+ }
+ return false;
+ }
+#endif
+
+ struct gc_arena gc = gc_new();
+ char *tmp_ciphers = string_alloc(o->ncp_ciphers, &gc);
+ const char *token;
+ while ((token = strsep(&tmp_ciphers, ":")))
+ {
+ if (!tls_item_in_cipher_list(token, DCO_SUPPORTED_CIPHERS))
+ {
+ msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
+ "by ovpn-dco, disabling data channel offload.", token);
+ gc_free(&gc);
+ return false;
+ }
+ }
+ gc_free(&gc);
+
+ return true;
+}
+
+#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index bc1a0300..0b3db7c7 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -276,6 +276,7 @@
<ClCompile Include="crypto.c" />
<ClCompile Include="crypto_openssl.c" />
<ClCompile Include="cryptoapi.c" />
+ <ClCompile Include="dco.c" />
<ClCompile Include="dco_linux.c" />
<ClCompile Include="dhcp.c" />
<ClCompile Include="dns.c" />
diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters
index 3c21a4c6..16905079 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -36,6 +36,9 @@
<ClCompile Include="cryptoapi.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dco.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="dco_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 95d4008a..7b919a1e 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3645,6 +3645,11 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
o->verify_hash_no_ca = true;
}
+ /* check if any option should force disabling DCO */
+#if defined(TARGET_LINUX)
+ o->tuntap_options.disable_dco = !dco_check_option_conflict(D_DCO, o);
+#endif
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: add option check - disable DCO if conflict is detected
2022-07-18 22:17 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
@ 2022-07-19 9:25 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-19 9:25 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
Discussed this at length on IRC. We put this in first (+08), so
*05* can go in without breaking non-DCO builds - because this one will
ensure "disable_dco" is set there.
Understanding the DCO and non-DCO code is a bit confusing - the whole
dco.c is #ifdef ENABLE_DCO, and dco_check_option_conflict() will
be a "static inline ... return false;" in that case - and the dco.h
bits of this came in via e34437c26b, so not obvious from this patch
how the pieces work together.
This said, I've thrown 07 v3 at the t_server test machinery (Gentoo,
with no "--enable-dco" at configure times) and it passed everything -
unsurprisingly, as the new code is not compiled in.
Testing with --enable-dco also caused no problems - this test basically
is a "will these parts compile correctly?" sanity check - the code
will detect "there is no DCO kernel support here" on my machine and
auto-disable DCO...
2022-07-19 11:06:31 Cannot find ovpn_dco netlink component: Object not found
2022-07-19 11:06:31 Note: Kernel support for ovpn-dco missing, disabling data channel offload.
On a machine *with* kernel support *and* --enable-dco, I assume this will
also do nothing (as open_tun() is not yet DCO'ed - that's 05).
I have only skimmed the actual option check - seems to make sense :-)
(and has an ACK from Arne, on 07 v1).
Whether the linux "fixed" check will work, we'll see after 05 is merged.
Your patch has been applied to the master branch.
commit 8989b0f2833d25c97654c25fa6a49d8fc0ef903d
Author: Antonio Quartulli
Date: Tue Jul 19 00:17:57 2022 +0200
dco: add option check - disable DCO if conflict is detected
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220718221757.545-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24701.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH pre-07/25] tun: create tun_name_is_fixed helper
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected Antonio Quartulli
2022-06-27 11:17 ` Arne Schwabe
2022-07-12 22:13 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-07-12 22:16 ` Antonio Quartulli
2022-07-14 18:20 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-12 22:16 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This helper encloses the (simple) logic used by OpenVPN to determine if
the name passed to --dev has to be considered a fixed interface name or
just a pattern.
Having a helper is useful because when this logic is required elsewhere,
we can just re-use this logic without duplicating the code (which may
mean introducing bugs if a future logic change should not update all
spots).
The logic is actually fairly simple: check if the name contains a number
(i.e. tun0). If so, consider the name a fixed device name.
While at it make has_digit() accept a signed argument because strings
are normally signed (also isdigit() accepts a signed argument).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/buffer.h | 4 ++--
src/openvpn/tun.c | 7 ++++++-
src/openvpn/tun.h | 1 +
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
index 231f1b0d..fece6336 100644
--- a/src/openvpn/buffer.h
+++ b/src/openvpn/buffer.h
@@ -356,9 +356,9 @@ strncpynt(char *dest, const char *src, size_t maxlen)
/* return true if string contains at least one numerical digit */
static inline bool
-has_digit(const unsigned char *src)
+has_digit(const char *src)
{
- unsigned char c;
+ char c;
while ((c = *src++))
{
if (isdigit(c))
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index f17db280..108090d0 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1718,6 +1718,11 @@ read_tun_header(struct tuntap *tt, uint8_t *buf, int len)
}
#endif /* if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) */
+bool
+tun_name_is_fixed(const char *dev)
+{
+ return has_digit(dev);
+}
#if !defined(_WIN32)
static void
@@ -1772,7 +1777,7 @@ open_tun_generic(const char *dev, const char *dev_type, const char *dev_node,
else
#endif
- if (dynamic && !has_digit((unsigned char *)dev))
+ if (dynamic && !tun_name_is_fixed(dev))
{
int i;
for (i = 0; i < 256; ++i)
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index cf02bf43..8ec8f51f 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -694,5 +694,6 @@ tun_set(struct tuntap *tt,
}
const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc);
+bool tun_name_is_fixed(const char *dev);
#endif /* TUN_H */
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: tun: create tun_name_is_fixed helper
2022-07-12 22:16 ` [Openvpn-devel] [PATCH pre-07/25] tun: create tun_name_is_fixed helper Antonio Quartulli
@ 2022-07-14 18:20 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-14 18:20 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
Trivial enough, and confirmed that it does not break anything.
(Having the "has_digit()" in buffer.*h* when it is only ever called
from tun.c smells like "we might eventually include this in
tun_name_is_fixed() and get rid of the inline helper" - but this is
a patch for another day. Also, we might do it the other way round,
"everything that is not exactly 'tun', 'tap' or 'null' is considered
fixed" - this whole open_tun() thing is a hairy mess of historic insanity).
Your patch has been applied to the master branch.
commit 4a88d2fbe73a64f8f3861089a2b6e3b8e583e71b
Author: Antonio Quartulli
Date: Wed Jul 13 00:16:55 2022 +0200
tun: create tun_name_is_fixed helper
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220712221655.19333-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24676.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (6 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 07/25] dco: add option check - disable DCO if conflict is detected Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 11:32 ` Arne Schwabe
` (2 more replies)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 09/25] dco: configure keys in DCO right after generating them Antonio Quartulli
` (16 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/options.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 9a0634a5..7b450296 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -61,6 +61,7 @@
#include "ssl_verify.h"
#include "platform.h"
#include "xkey_common.h"
+#include "dco.h"
#include <ctype.h>
#include "memdbg.h"
@@ -106,6 +107,9 @@ const char title_string[] =
#endif
#endif
" [AEAD]"
+#ifdef ENABLE_DCO
+ " [DCO]"
+#endif
" built on " __DATE__
;
@@ -177,6 +181,9 @@ static const char usage_message[] =
" does not begin with \"tun\" or \"tap\".\n"
"--dev-node node : Explicitly set the device node rather than using\n"
" /dev/net/tun, /dev/tun, /dev/tap, etc.\n"
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ "--disable-dco : Do not attempt using Data Channel Offload.\n"
+#endif
"--lladdr hw : Set the link layer address of the tap device.\n"
"--topology t : Set --dev tun topology: 'net30', 'p2p', or 'subnet'.\n"
#ifdef ENABLE_IPROUTE
@@ -1711,6 +1718,9 @@ show_settings(const struct options *o)
SHOW_STR(dev);
SHOW_STR(dev_type);
SHOW_STR(dev_node);
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ SHOW_BOOL(tuntap_options.disable_dco);
+#endif
SHOW_STR(lladdr);
SHOW_INT(topology);
SHOW_STR(ifconfig_local);
@@ -3210,6 +3220,14 @@ options_postprocess_verify(const struct options *o)
}
dns_options_verify(M_FATAL, &o->dns_options);
+
+ if (dco_enabled(o) && o->enable_c2c)
+ {
+ msg(M_WARN, "Note: --client-to-client has no effect when using data "
+ "channel offload: packets are always sent to the VPN "
+ "interface and then routed based on the system routing "
+ "table");
+ }
}
/**
@@ -3454,6 +3472,11 @@ options_postprocess_mutate(struct options *o)
o->verify_hash_no_ca = true;
}
+ /* check if any option should force disabling DCO */
+#if defined(TARGET_LINUX)
+ o->tuntap_options.disable_dco = !dco_check_option_conflict(D_DCO, o);
+#endif
+
/*
* Save certain parms before modifying options during connect, especially
* when using --pull
@@ -5759,6 +5782,12 @@ add_option(struct options *options,
options->windows_driver = parse_windows_driver(p[1], M_FATAL);
}
#endif
+ else if (streq(p[0], "disable-dco") || streq(p[0], "dco-disable"))
+ {
+#if defined(TARGET_LINUX)
+ options->tuntap_options.disable_dco = true;
+#endif
+ }
else if (streq(p[0], "dev-node") && p[1] && !p[2])
{
VERIFY_PERMISSION(OPT_P_GENERAL);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime Antonio Quartulli
@ 2022-06-27 11:32 ` Arne Schwabe
2022-07-05 12:32 ` Heiko Hund
2022-07-18 22:19 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 11:32 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> src/openvpn/options.c | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index 9a0634a5..7b450296 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -61,6 +61,7 @@
> #include "ssl_verify.h"
> #include "platform.h"
> #include "xkey_common.h"
> +#include "dco.h"
> #include <ctype.h>
>
> #include "memdbg.h"
> @@ -106,6 +107,9 @@ const char title_string[] =
> #endif
> #endif
> " [AEAD]"
> +#ifdef ENABLE_DCO
> + " [DCO]"
> +#endif
> " built on " __DATE__
> ;
>
> @@ -177,6 +181,9 @@ static const char usage_message[] =
> " does not begin with \"tun\" or \"tap\".\n"
> "--dev-node node : Explicitly set the device node rather than using\n"
> " /dev/net/tun, /dev/tun, /dev/tap, etc.\n"
> +#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
> + "--disable-dco : Do not attempt using Data Channel Offload.\n"
> +#endif
> "--lladdr hw : Set the link layer address of the tap device.\n"
> "--topology t : Set --dev tun topology: 'net30', 'p2p', or 'subnet'.\n"
> #ifdef ENABLE_IPROUTE
> @@ -1711,6 +1718,9 @@ show_settings(const struct options *o)
> SHOW_STR(dev);
> SHOW_STR(dev_type);
> SHOW_STR(dev_node);
> +#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
> + SHOW_BOOL(tuntap_options.disable_dco);
> +#endif
> SHOW_STR(lladdr);
> SHOW_INT(topology);
> SHOW_STR(ifconfig_local);
> @@ -3210,6 +3220,14 @@ options_postprocess_verify(const struct options *o)
> }
>
> dns_options_verify(M_FATAL, &o->dns_options);
> +
> + if (dco_enabled(o) && o->enable_c2c)
> + {
> + msg(M_WARN, "Note: --client-to-client has no effect when using data "
> + "channel offload: packets are always sent to the VPN "
> + "interface and then routed based on the system routing "
> + "table");
> + }
You should add at least one of them to the man page and document it.
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime Antonio Quartulli
2022-06-27 11:32 ` Arne Schwabe
@ 2022-07-05 12:32 ` Heiko Hund
2022-07-18 20:31 ` Antonio Quartulli
2022-07-18 22:19 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:32 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:37:52 CEST Antonio Quartulli wrote:
> + else if (streq(p[0], "disable-dco") || streq(p[0], "dco-disable"))
Don't think we need to be backwards compatible here, or do we?
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime
2022-07-05 12:32 ` Heiko Hund
@ 2022-07-18 20:31 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-18 20:31 UTC (permalink / raw)
To: Heiko Hund <heiko@
Hi,
On 05/07/2022 14:32, Heiko Hund wrote:
> On Freitag, 24. Juni 2022 10:37:52 CEST Antonio Quartulli wrote:
>> + else if (streq(p[0], "disable-dco") || streq(p[0], "dco-disable"))
>
> Don't think we need to be backwards compatible here, or do we?
There's nothing to be backwards compatible with.
Thanks for highlighting this.
I am removing "dco-disable" in v2.
Cheers,
>
>
>
>
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 08/25] dco: allow user to disable it at runtime
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime Antonio Quartulli
2022-06-27 11:32 ` Arne Schwabe
2022-07-05 12:32 ` Heiko Hund
@ 2022-07-18 22:19 ` Antonio Quartulli
2022-07-19 9:40 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-18 22:19 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* removed "--dco-disable" option: we just need "--disable-dco"
* added text to manpage about --client-to-client being no-op
* added text to manpage about --disable-dco
* rebased on top of master+"dco: add option check - disable DCO if
conflict is detected"
doc/man-sections/generic-options.rst | 9 +++++++++
doc/man-sections/server-options.rst | 4 ++++
src/openvpn/options.c | 24 ++++++++++++++++++++++++
3 files changed, 37 insertions(+)
diff --git a/doc/man-sections/generic-options.rst b/doc/man-sections/generic-options.rst
index 9060a235..394c2186 100644
--- a/doc/man-sections/generic-options.rst
+++ b/doc/man-sections/generic-options.rst
@@ -171,6 +171,15 @@ which mode OpenVPN is configured as.
on console) and ``--auth-nocache`` will fail as soon as key
renegotiation (and reauthentication) occurs.
+--disable-dco
+ Disable "data channel offload" (DCO).
+
+ On Linux don't use the ovpn-dco device driver, but rather rely on the
+ legacy tun module.
+
+ You may want to use this option if your server needs to allow clients
+ older than version 2.4 to connect.
+
--disable-occ
Disable "options consistency check" (OCC).
diff --git a/doc/man-sections/server-options.rst b/doc/man-sections/server-options.rst
index 08ee7bd3..04f4b4fb 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -146,6 +146,10 @@ fast hardware. SSL/TLS authentication must be used in this mode.
server. Don't use this option if you want to firewall tunnel traffic
using custom, per-client rules.
+ Please note that when using data channel offload this option has no
+ effect. Packets are always sent to the tunnel interface and then
+ routed based on the system routing table.
+
--disable
Disable a particular client (based on the common name) from connecting.
Don't use this option to disable a client due to key or password
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 7b919a1e..d864c6e2 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -61,6 +61,7 @@
#include "ssl_verify.h"
#include "platform.h"
#include "xkey_common.h"
+#include "dco.h"
#include <ctype.h>
#include "memdbg.h"
@@ -106,6 +107,9 @@ const char title_string[] =
#endif
#endif
" [AEAD]"
+#ifdef ENABLE_DCO
+ " [DCO]"
+#endif
" built on " __DATE__
;
@@ -177,6 +181,9 @@ static const char usage_message[] =
" does not begin with \"tun\" or \"tap\".\n"
"--dev-node node : Explicitly set the device node rather than using\n"
" /dev/net/tun, /dev/tun, /dev/tap, etc.\n"
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ "--disable-dco : Do not attempt using Data Channel Offload.\n"
+#endif
"--lladdr hw : Set the link layer address of the tap device.\n"
"--topology t : Set --dev tun topology: 'net30', 'p2p', or 'subnet'.\n"
#ifdef ENABLE_IPROUTE
@@ -1785,6 +1792,9 @@ show_settings(const struct options *o)
SHOW_STR(dev);
SHOW_STR(dev_type);
SHOW_STR(dev_node);
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ SHOW_BOOL(tuntap_options.disable_dco);
+#endif
SHOW_STR(lladdr);
SHOW_INT(topology);
SHOW_STR(ifconfig_local);
@@ -3401,6 +3411,14 @@ options_postprocess_verify(const struct options *o)
}
dns_options_verify(M_FATAL, &o->dns_options);
+
+ if (dco_enabled(o) && o->enable_c2c)
+ {
+ msg(M_WARN, "Note: --client-to-client has no effect when using data "
+ "channel offload: packets are always sent to the VPN "
+ "interface and then routed based on the system routing "
+ "table");
+ }
}
/**
@@ -5839,6 +5857,12 @@ add_option(struct options *options,
options->windows_driver = parse_windows_driver(p[1], M_FATAL);
}
#endif
+ else if (streq(p[0], "disable-dco"))
+ {
+#if defined(TARGET_LINUX)
+ options->tuntap_options.disable_dco = true;
+#endif
+ }
else if (streq(p[0], "dev-node") && p[1] && !p[2])
{
VERIFY_PERMISSION(OPT_P_GENERAL);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: allow user to disable it at runtime
2022-07-18 22:19 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-07-19 9:40 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-19 9:40 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
This does not really *do* much yet, but now you can do an --enable-dco
build, and suppress the "no kernel support!! ey!" message with --disable-dco
again ;-) - also, a build will now show [DCO] on --version
As agreed on IRC, rewrapped the p2p message ("table" on a separate line).
Your patch has been applied to the master branch.
commit 7a4c75927109acc2cd455140db312042781a3949
Author: Antonio Quartulli
Date: Tue Jul 19 00:19:23 2022 +0200
dco: allow user to disable it at runtime
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220718221923.2033-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24702.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 09/25] dco: configure keys in DCO right after generating them
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (7 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 08/25] dco: allow user to disable it at runtime Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-27 12:42 ` Arne Schwabe
2022-07-20 12:32 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 10/25] dco: periodically check and possibly rotate/delete keys Antonio Quartulli
` (15 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
The ovpn-dco kernel module needs to be informed about the keys to be
used to encrypt/decrypt data traffic to/from a peer.
Configure keys in DCO right afte they are generated by the SSL code, to
avoid keeping them in memory longer than needed.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/dco.c | 57 ++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 27 +++++++++++++++++
src/openvpn/init.c | 11 ++++---
src/openvpn/multi.c | 2 +-
src/openvpn/ssl.c | 74 +++++++++++++++++++++++++++++++++------------
src/openvpn/ssl.h | 7 +++--
6 files changed, 152 insertions(+), 26 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 1e45130a..e38614fa 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -33,7 +33,64 @@
#if defined(ENABLE_DCO)
#include "syshead.h"
+#include "crypto.h"
#include "dco.h"
+#include "errlevel.h"
+#include "openvpn.h"
+#include "ssl_common.h"
+#include "ssl_ncp.h"
+#include "tun.h"
+
+static int
+dco_install_key(struct tls_multi *multi, struct key_state *ks,
+ 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: peer_id=%d keyid=%d", __func__, multi->peer_id,
+ ks->key_id);
+
+ /* Install a key in the PRIMARY slot only when no other key exist.
+ * From that moment on, any new key will be installed in the SECONDARY
+ * slot and will be promoted to PRIMARY when userspace says so (a swap
+ * will be performed in that case)
+ */
+ dco_key_slot_t slot = OVPN_KEY_SLOT_PRIMARY;
+ if (multi->dco_keys_installed > 0)
+ {
+ slot = OVPN_KEY_SLOT_SECONDARY;
+ }
+
+ int ret = dco_new_key(multi->dco, multi->peer_id, ks->key_id, slot,
+ encrypt_key, encrypt_iv,
+ decrypt_key, decrypt_iv,
+ ciphername);
+ if ((ret == 0) && (multi->dco_keys_installed < 2))
+ {
+ multi->dco_keys_installed++;
+ ks->dco_status = (slot == OVPN_KEY_SLOT_PRIMARY) ? DCO_INSTALLED_PRIMARY :
+ DCO_INSTALLED_SECONDARY;
+ }
+
+ return ret;
+}
+
+int
+init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server)
+{
+ struct key_direction_state kds;
+ key_direction_state_init(&kds, key_direction);
+
+ return dco_install_key(multi, ks,
+ key2->keys[kds.out_key].cipher,
+ key2->keys[(int)server].hmac,
+ key2->keys[kds.in_key].cipher,
+ key2->keys[1 - (int)server].hmac,
+ ciphername);
+}
static bool
dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 063e5028..b081c6fa 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -35,7 +35,10 @@
* order problems)
*/
struct event_set;
+struct key2;
+struct key_state;
struct options;
+struct tls_multi;
struct tuntap;
#define DCO_DEFAULT_METRIC 200
@@ -111,6 +114,22 @@ int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf);
*/
void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg);
+/**
+ * Install the key material in DCO for the specified peer, at the specified slot
+ *
+ * @param multi the TLS context of the current instance
+ * @param ks the state of the key being installed
+ * @param key2 the container for the raw key material
+ * @param key_direction the key direction to be used to extract the material
+ * @param ciphername the name of the cipher to use the key with
+ * @param server whether we are running on a server instance or not
+ *
+ * @return 0 on success or a negative error code otherwise
+ */
+int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -163,5 +182,13 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
{
}
+static inline int
+init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server)
+{
+ return 0;
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 7ab2c9a2..06911cd0 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2201,8 +2201,9 @@ do_deferred_p2p_ncp(struct context *c)
}
#endif
- if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame,
- frame_fragment, get_link_socket_info(c)))
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
+ &c->c2.frame, frame_fragment,
+ get_link_socket_info(c)))
{
msg(D_TLS_ERRORS, "ERROR: failed to set crypto cipher");
return false;
@@ -2315,8 +2316,10 @@ do_deferred_options(struct context *c, const unsigned int found)
#endif
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame,
- frame_fragment, get_link_socket_info(c)))
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
{
msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
return false;
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index ba2f6d58..c72575ae 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2286,7 +2286,7 @@ multi_client_generate_tls_keys(struct context *c)
}
#endif
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(session, &c->options,
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
&c->c2.frame, frame_fragment,
get_link_socket_info(c)))
{
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 61dea996..9b16d6a3 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -63,6 +63,7 @@
#include "ssl_util.h"
#include "auth_token.h"
#include "mss.h"
+#include "dco.h"
#include "memdbg.h"
@@ -1429,21 +1430,49 @@ openvpn_PRF(const uint8_t *secret,
}
static void
-init_key_contexts(struct key_ctx_bi *key,
+init_key_contexts(struct key_state *ks,
+ struct tls_multi *multi,
const struct key_type *key_type,
bool server,
- struct key2 *key2)
+ struct key2 *key2,
+ bool dco_disabled)
{
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+
/* Initialize key contexts */
int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
- init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
- /* Initialize implicit IVs */
- key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
- MAX_HMAC_KEY_LENGTH);
- key_ctx_update_implicit_iv(&key->decrypt, key2->keys[1 - (int)server].hmac,
- MAX_HMAC_KEY_LENGTH);
+ if (dco_disabled)
+ {
+ init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
+ /* Initialize implicit IVs */
+ key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ key_ctx_update_implicit_iv(&key->decrypt,
+ key2->keys[1 - (int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ }
+
+ if (!dco_disabled)
+ {
+ if (key->encrypt.hmac)
+ {
+ msg(M_FATAL, "FATAL: DCO does not support --auth");
+ }
+
+ int ret = init_key_dco_bi(multi, ks, key2, key_direction,
+ key_type->cipher, server);
+ if (ret < 0)
+ {
+ msg(M_FATAL, "Impossible to install key material in DCO: %s",
+ strerror(-ret));
+ }
+ /* encrypt/decrypt context are unused with DCO */
+ CLEAR(key->encrypt);
+ CLEAR(key->decrypt);
+ key->initialized = true;
+ }
}
static bool
@@ -1519,9 +1548,10 @@ generate_key_expansion_openvpn_prf(const struct tls_session *session, struct key
* master key.
*/
static bool
-generate_key_expansion(struct key_ctx_bi *key,
+generate_key_expansion(struct tls_multi *multi, struct key_state *ks,
struct tls_session *session)
{
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
bool ret = false;
struct key2 key2;
@@ -1562,7 +1592,9 @@ generate_key_expansion(struct key_ctx_bi *key,
goto exit;
}
}
- init_key_contexts(key, &session->opt->key_type, server, &key2);
+
+ init_key_contexts(ks, multi, &session->opt->key_type, server, &key2,
+ session->opt->disable_dco);
ret = true;
exit:
@@ -1594,7 +1626,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
* can thus be called only once per session.
*/
bool
-tls_session_generate_data_channel_keys(struct tls_session *session)
+tls_session_generate_data_channel_keys(struct tls_multi *multi,
+ struct tls_session *session)
{
bool ret = false;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
@@ -1607,7 +1640,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session)
ks->crypto_options.flags = session->opt->crypto_flags;
- if (!generate_key_expansion(&ks->crypto_options.key_ctx_bi, session))
+ if (!generate_key_expansion(multi, ks, session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
goto cleanup;
@@ -1625,8 +1658,10 @@ cleanup:
}
bool
-tls_session_update_crypto_params_do_work(struct tls_session *session,
- struct options *options, struct frame *frame,
+tls_session_update_crypto_params_do_work(struct tls_multi *multi,
+ struct tls_session *session,
+ struct options *options,
+ struct frame *frame,
struct frame *frame_fragment,
struct link_socket_info *lsi)
{
@@ -1669,11 +1704,12 @@ tls_session_update_crypto_params_do_work(struct tls_session *session,
frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms");
}
- return tls_session_generate_data_channel_keys(session);
+ return tls_session_generate_data_channel_keys(multi, session);
}
bool
-tls_session_update_crypto_params(struct tls_session *session,
+tls_session_update_crypto_params(struct tls_multi *multi,
+ struct tls_session *session,
struct options *options, struct frame *frame,
struct frame *frame_fragment,
struct link_socket_info *lsi)
@@ -1695,8 +1731,8 @@ tls_session_update_crypto_params(struct tls_session *session,
/* Import crypto settings that might be set by pull/push */
session->opt->crypto_flags |= options->data_channel_crypto_flags;
- return tls_session_update_crypto_params_do_work(session, options, frame,
- frame_fragment, lsi);
+ return tls_session_update_crypto_params_do_work(multi, session, options,
+ frame, frame_fragment, lsi);
}
@@ -3089,7 +3125,7 @@ tls_multi_process(struct tls_multi *multi,
/* Session is now fully authenticated.
* tls_session_generate_data_channel_keys will move ks->state
* from S_ACTIVE to S_GENERATED_KEYS */
- if (!tls_session_generate_data_channel_keys(session))
+ if (!tls_session_generate_data_channel_keys(multi, session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
ks->authenticated = KS_AUTH_FALSE;
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index 0ba86d3e..ba271971 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -423,6 +423,7 @@ void tls_update_remote_addr(struct tls_multi *multi,
* channel keys based on the supplied options. Does nothing if keys are already
* generated.
*
+ * @param multi The TLS object for this instance.
* @param session The TLS session to update.
* @param options The options to use when updating session.
* @param frame The frame options for this session (frame overhead is
@@ -433,7 +434,8 @@ void tls_update_remote_addr(struct tls_multi *multi,
*
* @return true if updating succeeded or keys are already generated, false otherwise.
*/
-bool tls_session_update_crypto_params(struct tls_session *session,
+bool tls_session_update_crypto_params(struct tls_multi *multi,
+ struct tls_session *session,
struct options *options,
struct frame *frame,
struct frame *frame_fragment,
@@ -548,7 +550,8 @@ show_available_tls_ciphers(const char *cipher_list,
* can thus be called only once per session.
*/
bool
-tls_session_generate_data_channel_keys(struct tls_session *session);
+tls_session_generate_data_channel_keys(struct tls_multi *multi,
+ struct tls_session *session);
/**
* Load ovpn.xkey provider used for external key signing
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 09/25] dco: configure keys in DCO right after generating them
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 09/25] dco: configure keys in DCO right after generating them Antonio Quartulli
@ 2022-06-27 12:42 ` Arne Schwabe
2022-07-20 12:32 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-27 12:42 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
>
> +/**
> + * Install the key material in DCO for the specified peer, at the specified slot
> + *
> + * @param multi the TLS context of the current instance
> + * @param ks the state of the key being installed
> + * @param key2 the container for the raw key material
> + * @param key_direction the key direction to be used to extract the material
> + * @param ciphername the name of the cipher to use the key with
> + * @param server whether we are running on a server instance or not
> + *
> + * @return 0 on success or a negative error code otherwise
> + */
> +int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
> + const struct key2 *key2, int key_direction,
> + const char *ciphername, bool server);
I think here the description might be outdated. Your method does not
have a specified slot anymore. It would be good to document that this
method instead has a hidden that it install the primary key on the first
call and otherwards installs/overwrites the secondary key.
> + if (dco_disabled)
> + {
> + init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
> + /* Initialize implicit IVs */
> + key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
> + MAX_HMAC_KEY_LENGTH);
> + key_ctx_update_implicit_iv(&key->decrypt,
> + key2->keys[1 - (int)server].hmac,
> + MAX_HMAC_KEY_LENGTH);
> + }
> +
> + if (!dco_disabled)
> + {
I think an else here would be better.
> + /* encrypt/decrypt context are unused with DCO */
Do we have actually checked this? IIrc the generation of the explicit
exit notification might still try to generate a data channel key and use
these contexts.
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v2 09/25] dco: configure keys in DCO right after generating them
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 09/25] dco: configure keys in DCO right after generating them Antonio Quartulli
2022-06-27 12:42 ` Arne Schwabe
@ 2022-07-20 12:32 ` Antonio Quartulli
2022-07-28 12:56 ` Arne Schwabe
2022-07-28 15:20 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-20 12:32 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
The ovpn-dco kernel module needs to be informed about the keys to be
used to encrypt/decrypt data traffic to/from a peer.
Configure keys in DCO right afte they are generated by the SSL code, to
avoid keeping them in memory longer than needed.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* adapt to new member name dco_enabled
* invert if blocks and condition in init_key_contexts() [and use 'else']
* fix comment for init_key_contexts()
* disable explicit-exit-notification in mutate_ce() when DCO is enabled
src/openvpn/dco.c | 55 ++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 29 +++++++++++++++++
src/openvpn/init.c | 11 ++++---
src/openvpn/multi.c | 2 +-
src/openvpn/options.c | 10 ++++++
src/openvpn/ssl.c | 73 ++++++++++++++++++++++++++++++++-----------
src/openvpn/ssl.h | 7 +++--
7 files changed, 161 insertions(+), 26 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index b3fd135f..0471e4d0 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -33,12 +33,67 @@
#if defined(ENABLE_DCO)
#include "syshead.h"
+#include "crypto.h"
#include "dco.h"
+#include "errlevel.h"
#include "networking.h"
+#include "openvpn.h"
#include "options.h"
+#include "ssl_common.h"
#include "ssl_ncp.h"
#include "tun.h"
+static int
+dco_install_key(struct tls_multi *multi, struct key_state *ks,
+ 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: peer_id=%d keyid=%d", __func__, multi->peer_id,
+ ks->key_id);
+
+ /* Install a key in the PRIMARY slot only when no other key exist.
+ * From that moment on, any new key will be installed in the SECONDARY
+ * slot and will be promoted to PRIMARY when userspace says so (a swap
+ * will be performed in that case)
+ */
+ dco_key_slot_t slot = OVPN_KEY_SLOT_PRIMARY;
+ if (multi->dco_keys_installed > 0)
+ {
+ slot = OVPN_KEY_SLOT_SECONDARY;
+ }
+
+ int ret = dco_new_key(multi->dco, multi->peer_id, ks->key_id, slot,
+ encrypt_key, encrypt_iv,
+ decrypt_key, decrypt_iv,
+ ciphername);
+ if ((ret == 0) && (multi->dco_keys_installed < 2))
+ {
+ multi->dco_keys_installed++;
+ ks->dco_status = (slot == OVPN_KEY_SLOT_PRIMARY) ? DCO_INSTALLED_PRIMARY :
+ DCO_INSTALLED_SECONDARY;
+ }
+
+ return ret;
+}
+
+int
+init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server)
+{
+ struct key_direction_state kds;
+ key_direction_state_init(&kds, key_direction);
+
+ return dco_install_key(multi, ks,
+ key2->keys[kds.out_key].cipher,
+ key2->keys[(int)server].hmac,
+ key2->keys[kds.in_key].cipher,
+ key2->keys[1 - (int)server].hmac,
+ ciphername);
+}
+
static bool
dco_check_option_conflict_platform(int msglevel, const struct options *o)
{
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 063e5028..1692f5c3 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -35,7 +35,10 @@
* order problems)
*/
struct event_set;
+struct key2;
+struct key_state;
struct options;
+struct tls_multi;
struct tuntap;
#define DCO_DEFAULT_METRIC 200
@@ -111,6 +114,24 @@ int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf);
*/
void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg);
+/**
+ * Install the key material in DCO for the specified peer.
+ * The key is installed in the primary slot when no other key was yet installed.
+ * Any subsequent invocation will install the key in the secondary slot.
+ *
+ * @param multi the TLS context of the current instance
+ * @param ks the state of the key being installed
+ * @param key2 the container for the raw key material
+ * @param key_direction the key direction to be used to extract the material
+ * @param ciphername the name of the cipher to use the key with
+ * @param server whether we are running on a server instance or not
+ *
+ * @return 0 on success or a negative error code otherwise
+ */
+int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -163,5 +184,13 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
{
}
+static inline int
+init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server)
+{
+ return 0;
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index b6e1707f..338d797b 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2226,8 +2226,9 @@ do_deferred_p2p_ncp(struct context *c)
}
#endif
- if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame,
- frame_fragment, get_link_socket_info(c)))
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
+ &c->c2.frame, frame_fragment,
+ get_link_socket_info(c)))
{
msg(D_TLS_ERRORS, "ERROR: failed to set crypto cipher");
return false;
@@ -2340,8 +2341,10 @@ do_deferred_options(struct context *c, const unsigned int found)
#endif
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame,
- frame_fragment, get_link_socket_info(c)))
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
{
msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
return false;
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index ba2f6d58..c72575ae 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2286,7 +2286,7 @@ multi_client_generate_tls_keys(struct context *c)
}
#endif
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(session, &c->options,
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
&c->c2.frame, frame_fragment,
get_link_socket_info(c)))
{
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 87d6fc31..dba9d02c 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3194,6 +3194,16 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
ce->explicit_exit_notification = 0;
}
+ /* when DCO is in use we can't send data channel packets.
+ * EEN needs to be re-implemented over the control channel in order
+ * to work.
+ */
+ if (dco_enabled(o) && ce->explicit_exit_notification)
+ {
+ msg(M_WARN, "NOTICE: --explicit-exit-notify ignored when "
+ "data channel offload is in use");
+ ce->explicit_exit_notification = 0;
+ }
}
#ifdef _WIN32
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 24d7f3f4..fc5a8587 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -63,6 +63,7 @@
#include "ssl_util.h"
#include "auth_token.h"
#include "mss.h"
+#include "dco.h"
#include "memdbg.h"
@@ -1429,21 +1430,48 @@ openvpn_PRF(const uint8_t *secret,
}
static void
-init_key_contexts(struct key_ctx_bi *key,
+init_key_contexts(struct key_state *ks,
+ struct tls_multi *multi,
const struct key_type *key_type,
bool server,
- struct key2 *key2)
+ struct key2 *key2,
+ bool dco_enabled)
{
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+
/* Initialize key contexts */
int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
- init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
- /* Initialize implicit IVs */
- key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
- MAX_HMAC_KEY_LENGTH);
- key_ctx_update_implicit_iv(&key->decrypt, key2->keys[1 - (int)server].hmac,
- MAX_HMAC_KEY_LENGTH);
+ if (dco_enabled)
+ {
+ if (key->encrypt.hmac)
+ {
+ msg(M_FATAL, "FATAL: DCO does not support --auth");
+ }
+
+ int ret = init_key_dco_bi(multi, ks, key2, key_direction,
+ key_type->cipher, server);
+ if (ret < 0)
+ {
+ msg(M_FATAL, "Impossible to install key material in DCO: %s",
+ strerror(-ret));
+ }
+ /* encrypt/decrypt context are unused with DCO */
+ CLEAR(key->encrypt);
+ CLEAR(key->decrypt);
+ key->initialized = true;
+ }
+ else
+ {
+ init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
+ /* Initialize implicit IVs */
+ key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ key_ctx_update_implicit_iv(&key->decrypt,
+ key2->keys[1 - (int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ }
}
static bool
@@ -1519,9 +1547,10 @@ generate_key_expansion_openvpn_prf(const struct tls_session *session, struct key
* master key.
*/
static bool
-generate_key_expansion(struct key_ctx_bi *key,
+generate_key_expansion(struct tls_multi *multi, struct key_state *ks,
struct tls_session *session)
{
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
bool ret = false;
struct key2 key2;
@@ -1562,7 +1591,9 @@ generate_key_expansion(struct key_ctx_bi *key,
goto exit;
}
}
- init_key_contexts(key, &session->opt->key_type, server, &key2);
+
+ init_key_contexts(ks, multi, &session->opt->key_type, server, &key2,
+ session->opt->dco_enabled);
ret = true;
exit:
@@ -1594,7 +1625,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
* can thus be called only once per session.
*/
bool
-tls_session_generate_data_channel_keys(struct tls_session *session)
+tls_session_generate_data_channel_keys(struct tls_multi *multi,
+ struct tls_session *session)
{
bool ret = false;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
@@ -1607,7 +1639,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session)
ks->crypto_options.flags = session->opt->crypto_flags;
- if (!generate_key_expansion(&ks->crypto_options.key_ctx_bi, session))
+ if (!generate_key_expansion(multi, ks, session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
goto cleanup;
@@ -1625,8 +1657,10 @@ cleanup:
}
bool
-tls_session_update_crypto_params_do_work(struct tls_session *session,
- struct options *options, struct frame *frame,
+tls_session_update_crypto_params_do_work(struct tls_multi *multi,
+ struct tls_session *session,
+ struct options *options,
+ struct frame *frame,
struct frame *frame_fragment,
struct link_socket_info *lsi)
{
@@ -1669,11 +1703,12 @@ tls_session_update_crypto_params_do_work(struct tls_session *session,
frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms");
}
- return tls_session_generate_data_channel_keys(session);
+ return tls_session_generate_data_channel_keys(multi, session);
}
bool
-tls_session_update_crypto_params(struct tls_session *session,
+tls_session_update_crypto_params(struct tls_multi *multi,
+ struct tls_session *session,
struct options *options, struct frame *frame,
struct frame *frame_fragment,
struct link_socket_info *lsi)
@@ -1695,8 +1730,8 @@ tls_session_update_crypto_params(struct tls_session *session,
/* Import crypto settings that might be set by pull/push */
session->opt->crypto_flags |= options->data_channel_crypto_flags;
- return tls_session_update_crypto_params_do_work(session, options, frame,
- frame_fragment, lsi);
+ return tls_session_update_crypto_params_do_work(multi, session, options,
+ frame, frame_fragment, lsi);
}
@@ -3092,7 +3127,7 @@ tls_multi_process(struct tls_multi *multi,
/* Session is now fully authenticated.
* tls_session_generate_data_channel_keys will move ks->state
* from S_ACTIVE to S_GENERATED_KEYS */
- if (!tls_session_generate_data_channel_keys(session))
+ if (!tls_session_generate_data_channel_keys(multi, session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
ks->authenticated = KS_AUTH_FALSE;
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index c8802707..76b1b674 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -426,6 +426,7 @@ void tls_update_remote_addr(struct tls_multi *multi,
* channel keys based on the supplied options. Does nothing if keys are already
* generated.
*
+ * @param multi The TLS object for this instance.
* @param session The TLS session to update.
* @param options The options to use when updating session.
* @param frame The frame options for this session (frame overhead is
@@ -436,7 +437,8 @@ void tls_update_remote_addr(struct tls_multi *multi,
*
* @return true if updating succeeded or keys are already generated, false otherwise.
*/
-bool tls_session_update_crypto_params(struct tls_session *session,
+bool tls_session_update_crypto_params(struct tls_multi *multi,
+ struct tls_session *session,
struct options *options,
struct frame *frame,
struct frame *frame_fragment,
@@ -551,7 +553,8 @@ show_available_tls_ciphers(const char *cipher_list,
* can thus be called only once per session.
*/
bool
-tls_session_generate_data_channel_keys(struct tls_session *session);
+tls_session_generate_data_channel_keys(struct tls_multi *multi,
+ struct tls_session *session);
/**
* Load ovpn.xkey provider used for external key signing
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 09/25] dco: configure keys in DCO right after generating them
2022-07-20 12:32 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-07-28 12:56 ` Arne Schwabe
2022-07-28 13:01 ` Antonio Quartulli
2022-07-28 15:20 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Arne Schwabe @ 2022-07-28 12:56 UTC (permalink / raw)
To: Antonio Quartulli <a@
> index 87d6fc31..dba9d02c 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -3194,6 +3194,16 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
> ce->explicit_exit_notification = 0;
> }
>
> + /* when DCO is in use we can't send data channel packets.
> + * EEN needs to be re-implemented over the control channel in order
> + * to work.
> + */
> + if (dco_enabled(o) && ce->explicit_exit_notification)
> + {
> + msg(M_WARN, "NOTICE: --explicit-exit-notify ignored when "
> + "data channel offload is in use");
> + ce->explicit_exit_notification = 0;
> + }
I don't like this. There is already the patch from me that allows this
on the control channel. So we need a better solution than disabling it
when dco is enabled. Did you check what happens if data packets are
tried being sent when DCO is enabled? Maybe that just has a warning now
that no key is active.
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 09/25] dco: configure keys in DCO right after generating them
2022-07-28 12:56 ` Arne Schwabe
@ 2022-07-28 13:01 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-28 13:01 UTC (permalink / raw)
To: Arne Schwabe <arne@
On 28/07/2022 14:56, Arne Schwabe wrote:
>
>> index 87d6fc31..dba9d02c 100644
>> --- a/src/openvpn/options.c
>> +++ b/src/openvpn/options.c
>> @@ -3194,6 +3194,16 @@ options_postprocess_mutate_ce(struct options
>> *o, struct connection_entry *ce)
>> ce->explicit_exit_notification = 0;
>> }
>> + /* when DCO is in use we can't send data channel packets.
>> + * EEN needs to be re-implemented over the control channel in order
>> + * to work.
>> + */
>> + if (dco_enabled(o) && ce->explicit_exit_notification)
>> + {
>> + msg(M_WARN, "NOTICE: --explicit-exit-notify ignored when "
>> + "data channel offload is in use");
>> + ce->explicit_exit_notification = 0;
>> + }
>
> I don't like this. There is already the patch from me that allows this
> on the control channel. So we need a better solution than disabling it
> when dco is enabled. Did you check what happens if data packets are
> tried being sent when DCO is enabled? Maybe that just has a warning now
> that no key is active.
Haven't tried, but by looking at the code it seems OpenVPN will assert
out because it checks for the key context being not-null.
So, since it cannot work the way it is right now, I wanted to at least
disable it.
Once implemented on the control channel it could then be re-enabled again.
Cheers,
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v3 09/25] dco: configure keys in DCO right after generating them
2022-07-20 12:32 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-07-28 12:56 ` Arne Schwabe
@ 2022-07-28 15:20 ` Antonio Quartulli
2022-07-28 15:38 ` Arne Schwabe
` (2 more replies)
1 sibling, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-28 15:20 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
The ovpn-dco kernel module needs to be informed about the keys to be
used to encrypt/decrypt data traffic to/from a peer.
Configure keys in DCO right afte they are generated by the SSL code, to
avoid keeping them in memory longer than needed.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* re-enable explicit-exit-notification in every case
* add check to drop packet when attempting to send data packet and DCO
is enabled (print warning as well)
Changes from v1:
* adapt to new member name dco_enabled
* invert if blocks and condition in init_key_contexts() [and use 'else']
* fix comment for init_key_contexts()
* disable explicit-exit-notification in mutate_ce() when DCO is enabled
---
src/openvpn/dco.c | 55 ++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 29 +++++++++++++++++
src/openvpn/forward.c | 7 +++++
src/openvpn/init.c | 11 ++++---
src/openvpn/multi.c | 2 +-
src/openvpn/options.c | 1 -
src/openvpn/ssl.c | 73 ++++++++++++++++++++++++++++++++-----------
src/openvpn/ssl.h | 7 +++--
8 files changed, 158 insertions(+), 27 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index b3fd135f..0471e4d0 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -33,12 +33,67 @@
#if defined(ENABLE_DCO)
#include "syshead.h"
+#include "crypto.h"
#include "dco.h"
+#include "errlevel.h"
#include "networking.h"
+#include "openvpn.h"
#include "options.h"
+#include "ssl_common.h"
#include "ssl_ncp.h"
#include "tun.h"
+static int
+dco_install_key(struct tls_multi *multi, struct key_state *ks,
+ 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: peer_id=%d keyid=%d", __func__, multi->peer_id,
+ ks->key_id);
+
+ /* Install a key in the PRIMARY slot only when no other key exist.
+ * From that moment on, any new key will be installed in the SECONDARY
+ * slot and will be promoted to PRIMARY when userspace says so (a swap
+ * will be performed in that case)
+ */
+ dco_key_slot_t slot = OVPN_KEY_SLOT_PRIMARY;
+ if (multi->dco_keys_installed > 0)
+ {
+ slot = OVPN_KEY_SLOT_SECONDARY;
+ }
+
+ int ret = dco_new_key(multi->dco, multi->peer_id, ks->key_id, slot,
+ encrypt_key, encrypt_iv,
+ decrypt_key, decrypt_iv,
+ ciphername);
+ if ((ret == 0) && (multi->dco_keys_installed < 2))
+ {
+ multi->dco_keys_installed++;
+ ks->dco_status = (slot == OVPN_KEY_SLOT_PRIMARY) ? DCO_INSTALLED_PRIMARY :
+ DCO_INSTALLED_SECONDARY;
+ }
+
+ return ret;
+}
+
+int
+init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server)
+{
+ struct key_direction_state kds;
+ key_direction_state_init(&kds, key_direction);
+
+ return dco_install_key(multi, ks,
+ key2->keys[kds.out_key].cipher,
+ key2->keys[(int)server].hmac,
+ key2->keys[kds.in_key].cipher,
+ key2->keys[1 - (int)server].hmac,
+ ciphername);
+}
+
static bool
dco_check_option_conflict_platform(int msglevel, const struct options *o)
{
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 063e5028..1692f5c3 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -35,7 +35,10 @@
* order problems)
*/
struct event_set;
+struct key2;
+struct key_state;
struct options;
+struct tls_multi;
struct tuntap;
#define DCO_DEFAULT_METRIC 200
@@ -111,6 +114,24 @@ int dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf);
*/
void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg);
+/**
+ * Install the key material in DCO for the specified peer.
+ * The key is installed in the primary slot when no other key was yet installed.
+ * Any subsequent invocation will install the key in the secondary slot.
+ *
+ * @param multi the TLS context of the current instance
+ * @param ks the state of the key being installed
+ * @param key2 the container for the raw key material
+ * @param key_direction the key direction to be used to extract the material
+ * @param ciphername the name of the cipher to use the key with
+ * @param server whether we are running on a server instance or not
+ *
+ * @return 0 on success or a negative error code otherwise
+ */
+int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -163,5 +184,13 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
{
}
+static inline int
+init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
+ const struct key2 *key2, int key_direction,
+ const char *ciphername, bool server)
+{
+ return 0;
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 6afe152b..28f3c088 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -530,6 +530,13 @@ encrypt_sign(struct context *c, bool comp_frag)
const uint8_t *orig_buf = c->c2.buf.data;
struct crypto_options *co = NULL;
+ if (dco_enabled(&c->options))
+ {
+ msg(M_WARN, "Attempting to send data packet while data channel offload is in use. "
+ "Dropping packet");
+ c->c2.buf.len = 0;
+ }
+
/*
* Drop non-TLS outgoing packet if client-connect script/plugin
* has not yet succeeded. In non-TLS tls_multi mode is not defined
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index b6e1707f..338d797b 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2226,8 +2226,9 @@ do_deferred_p2p_ncp(struct context *c)
}
#endif
- if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame,
- frame_fragment, get_link_socket_info(c)))
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
+ &c->c2.frame, frame_fragment,
+ get_link_socket_info(c)))
{
msg(D_TLS_ERRORS, "ERROR: failed to set crypto cipher");
return false;
@@ -2340,8 +2341,10 @@ do_deferred_options(struct context *c, const unsigned int found)
#endif
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(session, &c->options, &c->c2.frame,
- frame_fragment, get_link_socket_info(c)))
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
{
msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
return false;
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index ba2f6d58..c72575ae 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2286,7 +2286,7 @@ multi_client_generate_tls_keys(struct context *c)
}
#endif
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(session, &c->options,
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
&c->c2.frame, frame_fragment,
get_link_socket_info(c)))
{
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 11919f6d..3eae14a7 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3193,7 +3193,6 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
msg(M_WARN, "NOTICE: --explicit-exit-notify ignored for --proto tcp");
ce->explicit_exit_notification = 0;
}
-
}
#ifdef _WIN32
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 24d7f3f4..fc5a8587 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -63,6 +63,7 @@
#include "ssl_util.h"
#include "auth_token.h"
#include "mss.h"
+#include "dco.h"
#include "memdbg.h"
@@ -1429,21 +1430,48 @@ openvpn_PRF(const uint8_t *secret,
}
static void
-init_key_contexts(struct key_ctx_bi *key,
+init_key_contexts(struct key_state *ks,
+ struct tls_multi *multi,
const struct key_type *key_type,
bool server,
- struct key2 *key2)
+ struct key2 *key2,
+ bool dco_enabled)
{
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+
/* Initialize key contexts */
int key_direction = server ? KEY_DIRECTION_INVERSE : KEY_DIRECTION_NORMAL;
- init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
- /* Initialize implicit IVs */
- key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
- MAX_HMAC_KEY_LENGTH);
- key_ctx_update_implicit_iv(&key->decrypt, key2->keys[1 - (int)server].hmac,
- MAX_HMAC_KEY_LENGTH);
+ if (dco_enabled)
+ {
+ if (key->encrypt.hmac)
+ {
+ msg(M_FATAL, "FATAL: DCO does not support --auth");
+ }
+
+ int ret = init_key_dco_bi(multi, ks, key2, key_direction,
+ key_type->cipher, server);
+ if (ret < 0)
+ {
+ msg(M_FATAL, "Impossible to install key material in DCO: %s",
+ strerror(-ret));
+ }
+ /* encrypt/decrypt context are unused with DCO */
+ CLEAR(key->encrypt);
+ CLEAR(key->decrypt);
+ key->initialized = true;
+ }
+ else
+ {
+ init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
+ /* Initialize implicit IVs */
+ key_ctx_update_implicit_iv(&key->encrypt, key2->keys[(int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ key_ctx_update_implicit_iv(&key->decrypt,
+ key2->keys[1 - (int)server].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ }
}
static bool
@@ -1519,9 +1547,10 @@ generate_key_expansion_openvpn_prf(const struct tls_session *session, struct key
* master key.
*/
static bool
-generate_key_expansion(struct key_ctx_bi *key,
+generate_key_expansion(struct tls_multi *multi, struct key_state *ks,
struct tls_session *session)
{
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
bool ret = false;
struct key2 key2;
@@ -1562,7 +1591,9 @@ generate_key_expansion(struct key_ctx_bi *key,
goto exit;
}
}
- init_key_contexts(key, &session->opt->key_type, server, &key2);
+
+ init_key_contexts(ks, multi, &session->opt->key_type, server, &key2,
+ session->opt->dco_enabled);
ret = true;
exit:
@@ -1594,7 +1625,8 @@ key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, size_t key_len)
* can thus be called only once per session.
*/
bool
-tls_session_generate_data_channel_keys(struct tls_session *session)
+tls_session_generate_data_channel_keys(struct tls_multi *multi,
+ struct tls_session *session)
{
bool ret = false;
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */
@@ -1607,7 +1639,7 @@ tls_session_generate_data_channel_keys(struct tls_session *session)
ks->crypto_options.flags = session->opt->crypto_flags;
- if (!generate_key_expansion(&ks->crypto_options.key_ctx_bi, session))
+ if (!generate_key_expansion(multi, ks, session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
goto cleanup;
@@ -1625,8 +1657,10 @@ cleanup:
}
bool
-tls_session_update_crypto_params_do_work(struct tls_session *session,
- struct options *options, struct frame *frame,
+tls_session_update_crypto_params_do_work(struct tls_multi *multi,
+ struct tls_session *session,
+ struct options *options,
+ struct frame *frame,
struct frame *frame_fragment,
struct link_socket_info *lsi)
{
@@ -1669,11 +1703,12 @@ tls_session_update_crypto_params_do_work(struct tls_session *session,
frame_print(frame_fragment, D_MTU_INFO, "Fragmentation MTU parms");
}
- return tls_session_generate_data_channel_keys(session);
+ return tls_session_generate_data_channel_keys(multi, session);
}
bool
-tls_session_update_crypto_params(struct tls_session *session,
+tls_session_update_crypto_params(struct tls_multi *multi,
+ struct tls_session *session,
struct options *options, struct frame *frame,
struct frame *frame_fragment,
struct link_socket_info *lsi)
@@ -1695,8 +1730,8 @@ tls_session_update_crypto_params(struct tls_session *session,
/* Import crypto settings that might be set by pull/push */
session->opt->crypto_flags |= options->data_channel_crypto_flags;
- return tls_session_update_crypto_params_do_work(session, options, frame,
- frame_fragment, lsi);
+ return tls_session_update_crypto_params_do_work(multi, session, options,
+ frame, frame_fragment, lsi);
}
@@ -3092,7 +3127,7 @@ tls_multi_process(struct tls_multi *multi,
/* Session is now fully authenticated.
* tls_session_generate_data_channel_keys will move ks->state
* from S_ACTIVE to S_GENERATED_KEYS */
- if (!tls_session_generate_data_channel_keys(session))
+ if (!tls_session_generate_data_channel_keys(multi, session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
ks->authenticated = KS_AUTH_FALSE;
diff --git a/src/openvpn/ssl.h b/src/openvpn/ssl.h
index c8802707..76b1b674 100644
--- a/src/openvpn/ssl.h
+++ b/src/openvpn/ssl.h
@@ -426,6 +426,7 @@ void tls_update_remote_addr(struct tls_multi *multi,
* channel keys based on the supplied options. Does nothing if keys are already
* generated.
*
+ * @param multi The TLS object for this instance.
* @param session The TLS session to update.
* @param options The options to use when updating session.
* @param frame The frame options for this session (frame overhead is
@@ -436,7 +437,8 @@ void tls_update_remote_addr(struct tls_multi *multi,
*
* @return true if updating succeeded or keys are already generated, false otherwise.
*/
-bool tls_session_update_crypto_params(struct tls_session *session,
+bool tls_session_update_crypto_params(struct tls_multi *multi,
+ struct tls_session *session,
struct options *options,
struct frame *frame,
struct frame *frame_fragment,
@@ -551,7 +553,8 @@ show_available_tls_ciphers(const char *cipher_list,
* can thus be called only once per session.
*/
bool
-tls_session_generate_data_channel_keys(struct tls_session *session);
+tls_session_generate_data_channel_keys(struct tls_multi *multi,
+ struct tls_session *session);
/**
* Load ovpn.xkey provider used for external key signing
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v3 09/25] dco: configure keys in DCO right after generating them
2022-07-28 15:20 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
@ 2022-07-28 15:38 ` Arne Schwabe
2022-08-01 14:06 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2022-08-01 15:14 ` Gert Doering
2 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-07-28 15:38 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 28.07.22 um 17:20 schrieb Antonio Quartulli:
> The ovpn-dco kernel module needs to be informed about the keys to be
> used to encrypt/decrypt data traffic to/from a peer.
>
> Configure keys in DCO right afte they are generated by the SSL code, to
> avoid keeping them in memory longer than needed.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
>
> Changes from v2:
> * re-enable explicit-exit-notification in every case
> * add check to drop packet when attempting to send data packet and DCO
> is enabled (print warning as well)
>
> Changes from v1:
> * adapt to new member name dco_enabled
> * invert if blocks and condition in init_key_contexts() [and use 'else']
> * fix comment for init_key_contexts()
> * disable explicit-exit-notification in mutate_ce() when DCO is enabled
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH applied] Re: dco: configure keys in DCO right after generating them
2022-07-28 15:20 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2022-07-28 15:38 ` Arne Schwabe
@ 2022-08-01 14:06 ` Gert Doering
2022-08-01 15:14 ` Gert Doering
2 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-01 14:06 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Tests without --enable-dco (full server side test) - passes everything.
Test with --enable-dco but no Kernel support (client side only) - also
passes everything (spurious failure on one of the p2p tests, but that
was likely related to "too many tests running in parallel").
Did not test on a system with DCO kernel support, as we do not have all
bits and pices integrated yet.
I have not tested the "drop packet" case in forward.c (as that needs
a DCO enabled kernel). Putting that on my "test with full DCO!" list
- the code certainly looks good.
Stared at code for a bit (even though it has the ACK).
Not sure I like the call chain ssl.c->dco.c->crypto.c for
init_key_dco_bi() -> key_direction_state_init()... but changing
that would require a bit more ssl.c/crypto.c refactoring.
For the non-DCO cases, the _bi stuff has "key_ctx_update_implicit_iv()"
calls - are these done by the DCO kernel side? Can't find anything
about IVs in the init_key_dco_bi()->... call chain...
Do the calls to "tls_session_update_crypto_params()" really need to
get a "session" parameter passed in now? Since they get c->c2.tls_multi
now, "sesion" is just one pointer deref away... so this might warrant
a cleanup patch later on.
Your patch has been applied to the master branch.
commit 6a5612fe82453915755aca945ff4e876a25f582a
Author: Antonio Quartulli
Date: Thu Jul 28 17:20:12 2022 +0200
dco: configure keys in DCO right after generating them
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Arne Schwabe <arne@...1227...>
Message-Id: <20220728152012.18643-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24758.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: configure keys in DCO right after generating them
2022-07-28 15:20 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2022-07-28 15:38 ` Arne Schwabe
2022-08-01 14:06 ` [Openvpn-devel] [PATCH applied] " Gert Doering
@ 2022-08-01 15:14 ` Gert Doering
2 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-01 15:14 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Tests without --enable-dco (full server side test) - passes everything.
Test with --enable-dco but no Kernel support (client side only) - also
passes everything (spurious failure on one of the p2p tests, but that
was likely related to "too many tests running in parallel").
Did not test on a system with DCO kernel support, as we do not have all
bits and pices integrated yet.
I have not tested the "drop packet" case in forward.c (as that needs
a DCO enabled kernel). Putting that on my "test with full DCO!" list
- the code certainly looks good.
Stared at code for a bit (even though it has the ACK).
Not sure I like the call chain ssl.c->dco.c->crypto.c for
init_key_dco_bi() -> key_direction_state_init()... but changing
that would require a bit more ssl.c/crypto.c refactoring.
For the non-DCO cases, the _bi stuff has "key_ctx_update_implicit_iv()"
calls - are these done by the DCO kernel side? Can't find anything
about IVs in the init_key_dco_bi()->... call chain...
Do the calls to "tls_session_update_crypto_params()" really need to
get a "session" parameter passed in now? Since they get c->c2.tls_multi
now, "sesion" is just one pointer deref away... so this might warrant
a cleanup patch later on.
Your patch has been applied to the master branch.
commit 6a5612fe82453915755aca945ff4e876a25f582a
Author: Antonio Quartulli
Date: Thu Jul 28 17:20:12 2022 +0200
dco: configure keys in DCO right after generating them
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Arne Schwabe <arne@...1227...>
Message-Id: <20220728152012.18643-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24758.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 10/25] dco: periodically check and possibly rotate/delete keys
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (8 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 09/25] dco: configure keys in DCO right after generating them Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-28 14:23 ` Arne Schwabe
2022-07-28 19:35 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 11/25] dco: split option parsing routines Antonio Quartulli
` (14 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Data channel keys are periodically regenarated and installed in
ovpn-dco.
However, there is a certain moment when keys are rotated in order
to elect the new primary one.
Check the key status in userspace so that kernelspace can be informed as
well when rotations happen.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/dco.c | 97 +++++++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 14 +++++++
src/openvpn/forward.c | 19 +++++++++
3 files changed, 130 insertions(+)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index e38614fa..473eb564 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -92,6 +92,103 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
ciphername);
}
+/**
+ * Find a usable key that is not the primary (i.e. the secondary key)
+ *
+ * @param multi The TLS struct to retrieve keys from
+ * @param primary The primary key that should be skipped during the scan
+ *
+ * @return The secondary key or NULL if none could be found
+ */
+static struct key_state *
+dco_get_secondary_key(struct tls_multi *multi, const struct key_state *primary)
+{
+ for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = get_key_scan(multi, i);
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+
+ if (ks == primary)
+ {
+ continue;
+ }
+
+ if (ks->state >= S_GENERATED_KEYS && ks->authenticated == KS_AUTH_TRUE)
+ {
+ ASSERT(key->initialized);
+ return ks;
+ }
+ }
+
+ return NULL;
+}
+
+void
+dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
+{
+ msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
+
+ /* this function checks if keys have to be swapped or erased, therefore it
+ * can't do much if we don't have any key installed
+ */
+ if (multi->dco_keys_installed == 0)
+ {
+ return;
+ }
+
+ struct key_state *primary = tls_select_encryption_key(multi);
+ ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
+
+ /* no primary key available -> no usable key exists, therefore we should
+ * tell DCO to simply wipe all keys
+ */
+ if (!primary)
+ {
+ msg(D_DCO, "No encryption key found. Purging data channel keys");
+
+ dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
+ dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
+ multi->dco_keys_installed = 0;
+ return;
+ }
+
+ struct key_state *secondary = dco_get_secondary_key(multi, primary);
+ ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
+
+ /* the current primary key was installed as secondary in DCO, this means
+ * that userspace has promoted it and we should tell DCO to swap keys
+ */
+ if (primary->dco_status == DCO_INSTALLED_SECONDARY)
+ {
+ msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
+ primary->key_id, secondary ? secondary->key_id : -1);
+
+ dco_swap_keys(dco, multi->peer_id);
+ primary->dco_status = DCO_INSTALLED_PRIMARY;
+ if (secondary)
+ {
+ secondary->dco_status = DCO_INSTALLED_SECONDARY;
+ }
+ }
+
+ /* if we have no secondary key anymore, inform DCO about it */
+ if (!secondary && multi->dco_keys_installed == 2)
+ {
+ dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
+ multi->dco_keys_installed = 1;
+ }
+
+ /* all keys that are not installed are set to NOT installed */
+ for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = get_key_scan(multi, i);
+ if (ks != primary && ks != secondary)
+ {
+ ks->dco_status = DCO_NOT_INSTALLED;
+ }
+ }
+}
+
static bool
dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
{
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index b081c6fa..cb7f7e4f 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -130,6 +130,14 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
const struct key2 *key2, int key_direction,
const char *ciphername, bool server);
+/**
+ * Possibly swap or wipe keys from DCO
+ *
+ * @param dco DCO device context
+ * @param multi TLS multi instance
+ */
+void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -190,5 +198,11 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
return 0;
}
+static inline void
+dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
+{
+ ASSERT(false);
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 6afe152b..99898e01 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -41,6 +41,7 @@
#include "dhcp.h"
#include "common.h"
#include "ssl_verify.h"
+#include "dco.h"
#include "memdbg.h"
@@ -140,6 +141,18 @@ context_reschedule_sec(struct context *c, int sec)
}
}
+void
+check_dco_key_status(struct context *c)
+{
+ /* DCO context is not yet initialised or enabled */
+ if (!dco_enabled(&c->options))
+ {
+ return;
+ }
+
+ dco_update_keys(&c->c1.tuntap->dco, c->c2.tls_multi);
+}
+
/*
* In TLS mode, let TLS level respond to any control-channel
* packets which were received, or prepare any packets for
@@ -182,6 +195,12 @@ check_tls(struct context *c)
interval_schedule_wakeup(&c->c2.tmp_int, &wakeup);
+ /* Our current code has no good hooks in the TLS machinery to update
+ * DCO keys. So we check the key status after the whole TLS machinery
+ * has been completed and potentially update them
+ */
+ check_dco_key_status(c);
+
if (wakeup)
{
context_reschedule_sec(c, wakeup);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 10/25] dco: periodically check and possibly rotate/delete keys
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 10/25] dco: periodically check and possibly rotate/delete keys Antonio Quartulli
@ 2022-06-28 14:23 ` Arne Schwabe
2022-07-28 19:35 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-28 14:23 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> + ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
It would to be good to explain this assertion. I just spend too long
understanding it and I understand the code. Something along the lines
that we expect that primary key has been installed before here.
This code might also be subject to very unlikely (maybe not even
possible) race condition.
When deferred auth is active and the deferred auth just happens at the
edge of the window before timing out tls_select_encryption_key /might/
already return the new key without it having been a valid secondary key
before. But maybe we fail for the timeout check before.
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 10/25] dco: periodically check and possibly rotate/delete keys
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 10/25] dco: periodically check and possibly rotate/delete keys Antonio Quartulli
2022-06-28 14:23 ` Arne Schwabe
@ 2022-07-28 19:35 ` Antonio Quartulli
2022-07-29 7:41 ` Frank Lichtenheld
` (2 more replies)
1 sibling, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-28 19:35 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Data channel keys are periodically regenarated and installed in
ovpn-dco.
However, there is a certain moment when keys are rotated in order
to elect the new primary one.
Check the key status in userspace so that kernelspace can be informed as
well when rotations happen.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* added comments to ASSERT() in dco_update_keys()
---
src/openvpn/dco.c | 101 ++++++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 14 ++++++
src/openvpn/forward.c | 19 ++++++++
3 files changed, 134 insertions(+)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 0471e4d0..07c16de1 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -94,6 +94,107 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
ciphername);
}
+/**
+ * Find a usable key that is not the primary (i.e. the secondary key)
+ *
+ * @param multi The TLS struct to retrieve keys from
+ * @param primary The primary key that should be skipped during the scan
+ *
+ * @return The secondary key or NULL if none could be found
+ */
+static struct key_state *
+dco_get_secondary_key(struct tls_multi *multi, const struct key_state *primary)
+{
+ for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = get_key_scan(multi, i);
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+
+ if (ks == primary)
+ {
+ continue;
+ }
+
+ if (ks->state >= S_GENERATED_KEYS && ks->authenticated == KS_AUTH_TRUE)
+ {
+ ASSERT(key->initialized);
+ return ks;
+ }
+ }
+
+ return NULL;
+}
+
+void
+dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
+{
+ msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
+
+ /* this function checks if keys have to be swapped or erased, therefore it
+ * can't do much if we don't have any key installed
+ */
+ if (multi->dco_keys_installed == 0)
+ {
+ return;
+ }
+
+ struct key_state *primary = tls_select_encryption_key(multi);
+ /* either we have no primary key at all or, if we do, it must have been
+ * installed already (keys are installed upon generation in the TLS code)
+ */
+ ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
+
+ /* no primary key available -> no usable key exists, therefore we should
+ * tell DCO to simply wipe all keys
+ */
+ if (!primary)
+ {
+ msg(D_DCO, "No encryption key found. Purging data channel keys");
+
+ dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
+ dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
+ multi->dco_keys_installed = 0;
+ return;
+ }
+
+ struct key_state *secondary = dco_get_secondary_key(multi, primary);
+ /* same reason as the primary key above */
+ ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
+
+ /* the current primary key was installed as secondary in DCO, this means
+ * that userspace has promoted it and we should tell DCO to swap keys
+ */
+ if (primary->dco_status == DCO_INSTALLED_SECONDARY)
+ {
+ msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
+ primary->key_id, secondary ? secondary->key_id : -1);
+
+ dco_swap_keys(dco, multi->peer_id);
+ primary->dco_status = DCO_INSTALLED_PRIMARY;
+ if (secondary)
+ {
+ secondary->dco_status = DCO_INSTALLED_SECONDARY;
+ }
+ }
+
+ /* if we have no secondary key anymore, inform DCO about it */
+ if (!secondary && multi->dco_keys_installed == 2)
+ {
+ dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
+ multi->dco_keys_installed = 1;
+ }
+
+ /* all keys that are not installed are set to NOT installed */
+ for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = get_key_scan(multi, i);
+ if (ks != primary && ks != secondary)
+ {
+ ks->dco_status = DCO_NOT_INSTALLED;
+ }
+ }
+}
+
static bool
dco_check_option_conflict_platform(int msglevel, const struct options *o)
{
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 1692f5c3..b926e236 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -132,6 +132,14 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
const struct key2 *key2, int key_direction,
const char *ciphername, bool server);
+/**
+ * Possibly swap or wipe keys from DCO
+ *
+ * @param dco DCO device context
+ * @param multi TLS multi instance
+ */
+void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -192,5 +200,11 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
return 0;
}
+static inline void
+dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
+{
+ ASSERT(false);
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 28f3c088..38d2683c 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -41,6 +41,7 @@
#include "dhcp.h"
#include "common.h"
#include "ssl_verify.h"
+#include "dco.h"
#include "memdbg.h"
@@ -140,6 +141,18 @@ context_reschedule_sec(struct context *c, int sec)
}
}
+void
+check_dco_key_status(struct context *c)
+{
+ /* DCO context is not yet initialised or enabled */
+ if (!dco_enabled(&c->options))
+ {
+ return;
+ }
+
+ dco_update_keys(&c->c1.tuntap->dco, c->c2.tls_multi);
+}
+
/*
* In TLS mode, let TLS level respond to any control-channel
* packets which were received, or prepare any packets for
@@ -182,6 +195,12 @@ check_tls(struct context *c)
interval_schedule_wakeup(&c->c2.tmp_int, &wakeup);
+ /* Our current code has no good hooks in the TLS machinery to update
+ * DCO keys. So we check the key status after the whole TLS machinery
+ * has been completed and potentially update them
+ */
+ check_dco_key_status(c);
+
if (wakeup)
{
context_reschedule_sec(c, wakeup);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 10/25] dco: periodically check and possibly rotate/delete keys
2022-07-28 19:35 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-07-29 7:41 ` Frank Lichtenheld
2022-07-29 7:48 ` Antonio Quartulli
2022-08-01 15:44 ` Frank Lichtenheld
2022-08-02 15:16 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Frank Lichtenheld @ 2022-07-29 7:41 UTC (permalink / raw)
To: openvpn-devel
On Thu, Jul 28, 2022 at 09:35:42PM +0200, Antonio Quartulli wrote:
> Data channel keys are periodically regenarated and installed in
> ovpn-dco.
> However, there is a certain moment when keys are rotated in order
> to elect the new primary one.
>
> Check the key status in userspace so that kernelspace can be informed as
> well when rotations happen.
[...]
Small musings about the code comments:
> +void
> +dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
> +{
> + msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
> +
> + /* this function checks if keys have to be swapped or erased, therefore it
> + * can't do much if we don't have any key installed
> + */
> + if (multi->dco_keys_installed == 0)
> + {
> + return;
> + }
> +
> + struct key_state *primary = tls_select_encryption_key(multi);
> + /* either we have no primary key at all or, if we do, it must have been
> + * installed already (keys are installed upon generation in the TLS code)
> + */
> + ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
> +
> + /* no primary key available -> no usable key exists, therefore we should
> + * tell DCO to simply wipe all keys
> + */
> + if (!primary)
> + {
> + msg(D_DCO, "No encryption key found. Purging data channel keys");
> +
> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
> + multi->dco_keys_installed = 0;
> + return;
> + }
> +
> + struct key_state *secondary = dco_get_secondary_key(multi, primary);
> + /* same reason as the primary key above */
> + ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
> +
> + /* the current primary key was installed as secondary in DCO, this means
> + * that userspace has promoted it and we should tell DCO to swap keys
I found the use of "userspace" here a bit confusing. This code is part of userspace,
right? So wouldn't it be easier to say "we have promoted it"?
> + */
> + if (primary->dco_status == DCO_INSTALLED_SECONDARY)
> + {
> + msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
> + primary->key_id, secondary ? secondary->key_id : -1);
> +
> + dco_swap_keys(dco, multi->peer_id);
> + primary->dco_status = DCO_INSTALLED_PRIMARY;
> + if (secondary)
> + {
> + secondary->dco_status = DCO_INSTALLED_SECONDARY;
> + }
> + }
> +
> + /* if we have no secondary key anymore, inform DCO about it */
> + if (!secondary && multi->dco_keys_installed == 2)
> + {
> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
> + multi->dco_keys_installed = 1;
> + }
> +
> + /* all keys that are not installed are set to NOT installed */
> + for (int i = 0; i < KEY_SCAN_SIZE; ++i)
> + {
> + struct key_state *ks = get_key_scan(multi, i);
> + if (ks != primary && ks != secondary)
> + {
> + ks->dco_status = DCO_NOT_INSTALLED;
> + }
> + }
> +}
> +
> static bool
> dco_check_option_conflict_platform(int msglevel, const struct options *o)
> {
> diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
> index 1692f5c3..b926e236 100644
> --- a/src/openvpn/dco.h
> +++ b/src/openvpn/dco.h
> @@ -132,6 +132,14 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
> const struct key2 *key2, int key_direction,
> const char *ciphername, bool server);
>
> +/**
> + * Possibly swap or wipe keys from DCO
I would actually prefer to be a bit more explicit here:
"Sync our key status to DCO, potentially swapping or wiping keys"
> + *
> + * @param dco DCO device context
> + * @param multi TLS multi instance
> + */
> +void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
> +
> #else /* if defined(ENABLE_DCO) */
>
> typedef void *dco_context_t;
[...]
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 10/25] dco: periodically check and possibly rotate/delete keys
2022-07-29 7:41 ` Frank Lichtenheld
@ 2022-07-29 7:48 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-29 7:48 UTC (permalink / raw)
To: Frank Lichtenheld <frank@
Hi,
On 29/07/2022 09:41, Frank Lichtenheld wrote:
> On Thu, Jul 28, 2022 at 09:35:42PM +0200, Antonio Quartulli wrote:
>> Data channel keys are periodically regenarated and installed in
>> ovpn-dco.
>> However, there is a certain moment when keys are rotated in order
>> to elect the new primary one.
>>
>> Check the key status in userspace so that kernelspace can be informed as
>> well when rotations happen.
> [...]
>
> Small musings about the code comments:
>
>> +void
>> +dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
>> +{
>> + msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
>> +
>> + /* this function checks if keys have to be swapped or erased, therefore it
>> + * can't do much if we don't have any key installed
>> + */
>> + if (multi->dco_keys_installed == 0)
>> + {
>> + return;
>> + }
>> +
>> + struct key_state *primary = tls_select_encryption_key(multi);
>> + /* either we have no primary key at all or, if we do, it must have been
>> + * installed already (keys are installed upon generation in the TLS code)
>> + */
>> + ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
>> +
>> + /* no primary key available -> no usable key exists, therefore we should
>> + * tell DCO to simply wipe all keys
>> + */
>> + if (!primary)
>> + {
>> + msg(D_DCO, "No encryption key found. Purging data channel keys");
>> +
>> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
>> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
>> + multi->dco_keys_installed = 0;
>> + return;
>> + }
>> +
>> + struct key_state *secondary = dco_get_secondary_key(multi, primary);
>> + /* same reason as the primary key above */
>> + ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
>> +
>> + /* the current primary key was installed as secondary in DCO, this means
>> + * that userspace has promoted it and we should tell DCO to swap keys
>
> I found the use of "userspace" here a bit confusing. This code is part of userspace,
> right? So wouldn't it be easier to say "we have promoted it"?
Sure - whatever makes it easier to understand.
>
>> + */
>> + if (primary->dco_status == DCO_INSTALLED_SECONDARY)
>> + {
>> + msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
>> + primary->key_id, secondary ? secondary->key_id : -1);
>> +
>> + dco_swap_keys(dco, multi->peer_id);
>> + primary->dco_status = DCO_INSTALLED_PRIMARY;
>> + if (secondary)
>> + {
>> + secondary->dco_status = DCO_INSTALLED_SECONDARY;
>> + }
>> + }
>> +
>> + /* if we have no secondary key anymore, inform DCO about it */
>> + if (!secondary && multi->dco_keys_installed == 2)
>> + {
>> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
>> + multi->dco_keys_installed = 1;
>> + }
>> +
>> + /* all keys that are not installed are set to NOT installed */
>> + for (int i = 0; i < KEY_SCAN_SIZE; ++i)
>> + {
>> + struct key_state *ks = get_key_scan(multi, i);
>> + if (ks != primary && ks != secondary)
>> + {
>> + ks->dco_status = DCO_NOT_INSTALLED;
>> + }
>> + }
>> +}
>> +
>> static bool
>> dco_check_option_conflict_platform(int msglevel, const struct options *o)
>> {
>> diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
>> index 1692f5c3..b926e236 100644
>> --- a/src/openvpn/dco.h
>> +++ b/src/openvpn/dco.h
>> @@ -132,6 +132,14 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
>> const struct key2 *key2, int key_direction,
>> const char *ciphername, bool server);
>>
>> +/**
>> + * Possibly swap or wipe keys from DCO
>
> I would actually prefer to be a bit more explicit here:
> "Sync our key status to DCO, potentially swapping or wiping keys"
Makes sense. Thanks
>
>> + *
>> + * @param dco DCO device context
>> + * @param multi TLS multi instance
>> + */
>> +void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
>> +
>> #else /* if defined(ENABLE_DCO) */
>>
>> typedef void *dco_context_t;
> [...]
>
>
> Regards,
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH v2 10/25] dco: periodically check and possibly rotate/delete keys
2022-07-28 19:35 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-07-29 7:41 ` Frank Lichtenheld
@ 2022-08-01 15:44 ` Frank Lichtenheld
2022-08-02 8:07 ` Antonio Quartulli
2022-08-02 15:16 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Frank Lichtenheld @ 2022-08-01 15:44 UTC (permalink / raw)
To: openvpn-devel
On Thu, Jul 28, 2022 at 09:35:42PM +0200, Antonio Quartulli wrote:
> Data channel keys are periodically regenarated and installed in
> ovpn-dco.
> However, there is a certain moment when keys are rotated in order
> to elect the new primary one.
>
> Check the key status in userspace so that kernelspace can be informed as
> well when rotations happen.
Thinking about the actual code now.
[...]
> +void
> +dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
> +{
> + msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
> +
> + /* this function checks if keys have to be swapped or erased, therefore it
> + * can't do much if we don't have any key installed
> + */
> + if (multi->dco_keys_installed == 0)
> + {
> + return;
> + }
> +
> + struct key_state *primary = tls_select_encryption_key(multi);
> + /* either we have no primary key at all or, if we do, it must have been
> + * installed already (keys are installed upon generation in the TLS code)
> + */
> + ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
> +
> + /* no primary key available -> no usable key exists, therefore we should
> + * tell DCO to simply wipe all keys
> + */
> + if (!primary)
> + {
> + msg(D_DCO, "No encryption key found. Purging data channel keys");
> +
> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
> + multi->dco_keys_installed = 0;
> + return;
> + }
> +
> + struct key_state *secondary = dco_get_secondary_key(multi, primary);
> + /* same reason as the primary key above */
> + ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
> +
> + /* the current primary key was installed as secondary in DCO, this means
> + * that userspace has promoted it and we should tell DCO to swap keys
> + */
> + if (primary->dco_status == DCO_INSTALLED_SECONDARY)
> + {
> + msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
> + primary->key_id, secondary ? secondary->key_id : -1);
> +
> + dco_swap_keys(dco, multi->peer_id);
> + primary->dco_status = DCO_INSTALLED_PRIMARY;
> + if (secondary)
> + {
> + secondary->dco_status = DCO_INSTALLED_SECONDARY;
> + }
Why do we have no error handling? We just assume that DCO swapped the keys
but if dco_swap_keys returns an error that is probably not true?
Also can we really be sure that the kernel state is correct? I.e. we
have determined we have swapped the keys, but can we be sure that
the kernel actually has the two same keys and so a swap will replicate
our state?
> + }
> +
> + /* if we have no secondary key anymore, inform DCO about it */
> + if (!secondary && multi->dco_keys_installed == 2)
> + {
> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
Would error handling make sense here? It is clearly not as critical
as for dco_swap_keys because a delete is idempotent.
> + multi->dco_keys_installed = 1;
> + }
> +
> + /* all keys that are not installed are set to NOT installed */
> + for (int i = 0; i < KEY_SCAN_SIZE; ++i)
> + {
> + struct key_state *ks = get_key_scan(multi, i);
> + if (ks != primary && ks != secondary)
> + {
> + ks->dco_status = DCO_NOT_INSTALLED;
> + }
> + }
> +}
> +
> static bool
> dco_check_option_conflict_platform(int msglevel, const struct options *o)
> {
Regards,
Frank
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 10/25] dco: periodically check and possibly rotate/delete keys
2022-08-01 15:44 ` Frank Lichtenheld
@ 2022-08-02 8:07 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-02 8:07 UTC (permalink / raw)
To: Frank Lichtenheld <frank@
Hi,
On 01/08/2022 17:44, Frank Lichtenheld wrote:
> On Thu, Jul 28, 2022 at 09:35:42PM +0200, Antonio Quartulli wrote:
>> Data channel keys are periodically regenarated and installed in
>> ovpn-dco.
>> However, there is a certain moment when keys are rotated in order
>> to elect the new primary one.
>>
>> Check the key status in userspace so that kernelspace can be informed as
>> well when rotations happen.
>
> Thinking about the actual code now.
>
> [...]
>> +void
>> +dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
>> +{
>> + msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
>> +
>> + /* this function checks if keys have to be swapped or erased, therefore it
>> + * can't do much if we don't have any key installed
>> + */
>> + if (multi->dco_keys_installed == 0)
>> + {
>> + return;
>> + }
>> +
>> + struct key_state *primary = tls_select_encryption_key(multi);
>> + /* either we have no primary key at all or, if we do, it must have been
>> + * installed already (keys are installed upon generation in the TLS code)
>> + */
>> + ASSERT(!primary || primary->dco_status != DCO_NOT_INSTALLED);
>> +
>> + /* no primary key available -> no usable key exists, therefore we should
>> + * tell DCO to simply wipe all keys
>> + */
>> + if (!primary)
>> + {
>> + msg(D_DCO, "No encryption key found. Purging data channel keys");
>> +
>> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
>> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
>> + multi->dco_keys_installed = 0;
>> + return;
>> + }
>> +
>> + struct key_state *secondary = dco_get_secondary_key(multi, primary);
>> + /* same reason as the primary key above */
>> + ASSERT(!secondary || secondary->dco_status != DCO_NOT_INSTALLED);
>> +
>> + /* the current primary key was installed as secondary in DCO, this means
>> + * that userspace has promoted it and we should tell DCO to swap keys
>> + */
>> + if (primary->dco_status == DCO_INSTALLED_SECONDARY)
>> + {
>> + msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
>> + primary->key_id, secondary ? secondary->key_id : -1);
>> +
>> + dco_swap_keys(dco, multi->peer_id);
>> + primary->dco_status = DCO_INSTALLED_PRIMARY;
>> + if (secondary)
>> + {
>> + secondary->dco_status = DCO_INSTALLED_SECONDARY;
>> + }
>
> Why do we have no error handling? We just assume that DCO swapped the keys
> but if dco_swap_keys returns an error that is probably not true?
Right - the call may fail for various reasons and in that case we should
assume that no swap happened.
However, I am not sure what OpenVPN should do at this point. Trigger
SIGUSR1 for this instance? The other end won't notice anything anyway.
>
> Also can we really be sure that the kernel state is correct? I.e. we
> have determined we have swapped the keys, but can we be sure that
> the kernel actually has the two same keys and so a swap will replicate
> our state?
We have no way to confirm that, because we have no way to identify the
keys that have been installed. We can only trust that we injected the
right key at the right place.
>
>> + }
>> +
>> + /* if we have no secondary key anymore, inform DCO about it */
>> + if (!secondary && multi->dco_keys_installed == 2)
>> + {
>> + dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
>
> Would error handling make sense here? It is clearly not as critical
> as for dco_swap_keys because a delete is idempotent.
The question again is "what do we do?" Maybe in this case we simply
print a message but we continue as if it was successful?
>
>> + multi->dco_keys_installed = 1;
>> + }
>> +
>> + /* all keys that are not installed are set to NOT installed */
>> + for (int i = 0; i < KEY_SCAN_SIZE; ++i)
>> + {
>> + struct key_state *ks = get_key_scan(multi, i);
>> + if (ks != primary && ks != secondary)
>> + {
>> + ks->dco_status = DCO_NOT_INSTALLED;
>> + }
>> + }
>> +}
>> +
>> static bool
>> dco_check_option_conflict_platform(int msglevel, const struct options *o)
>> {
>
>
> Regards,
> Frank
>
>
Thanks!
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v3 10/25] dco: periodically check and possibly rotate/delete keys
2022-07-28 19:35 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-07-29 7:41 ` Frank Lichtenheld
2022-08-01 15:44 ` Frank Lichtenheld
@ 2022-08-02 15:16 ` Antonio Quartulli
2022-08-03 9:10 ` Frank Lichtenheld
2022-08-03 14:30 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-02 15:16 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Data channel keys are periodically regenarated and installed in
ovpn-dco.
However, there is a certain moment when keys are rotated in order
to elect the new primary one.
Check the key status in userspace so that kernelspace can be informed as
well when rotations happen.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* reworked dco_update_keys():
** removed existing ASSERTs on keys and converted into something more
logic and related to the flow
** fixed comment about userspace (use "we" for more clarity)
** add error code handling. The idea is to abort operations as soon as a
failure is detected, so that the next iteration can make another
attempt.
Changes from v1:
* added comments to ASSERT() in dco_update_keys()
---
src/openvpn/dco.c | 122 ++++++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 14 +++++
src/openvpn/forward.c | 19 +++++++
3 files changed, 155 insertions(+)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index b0dd922a..dfdf10ea 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -94,6 +94,128 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
ciphername);
}
+/**
+ * Find a usable key that is not the primary (i.e. the secondary key)
+ *
+ * @param multi The TLS struct to retrieve keys from
+ * @param primary The primary key that should be skipped during the scan
+ *
+ * @return The secondary key or NULL if none could be found
+ */
+static struct key_state *
+dco_get_secondary_key(struct tls_multi *multi, const struct key_state *primary)
+{
+ for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = get_key_scan(multi, i);
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+
+ if (ks == primary)
+ {
+ continue;
+ }
+
+ if (ks->state >= S_GENERATED_KEYS && ks->authenticated == KS_AUTH_TRUE)
+ {
+ ASSERT(key->initialized);
+ return ks;
+ }
+ }
+
+ return NULL;
+}
+
+void
+dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
+{
+ msg(D_DCO_DEBUG, "%s: peer_id=%d", __func__, multi->peer_id);
+
+ /* this function checks if keys have to be swapped or erased, therefore it
+ * can't do much if we don't have any key installed
+ */
+ if (multi->dco_keys_installed == 0)
+ {
+ return;
+ }
+
+ struct key_state *primary = tls_select_encryption_key(multi);
+ /* no primary key available -> no usable key exists, therefore we should
+ * tell DCO to simply wipe all keys
+ */
+ if (!primary)
+ {
+ msg(D_DCO, "No encryption key found. Purging data channel keys");
+
+ int ret = dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_PRIMARY);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot delete primary key during wipe: %s (%d)", strerror(-ret), ret);
+ return;
+ }
+
+ ret = dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot delete secondary key during wipe: %s (%d)", strerror(-ret), ret);
+ return;
+ }
+
+ multi->dco_keys_installed = 0;
+ return;
+ }
+
+ /* if we have a primary key, it must have been installed already (keys
+ * are installed upon generation in the TLS code)
+ */
+ ASSERT(primary->dco_status != DCO_NOT_INSTALLED);
+
+ struct key_state *secondary = dco_get_secondary_key(multi, primary);
+ /* the current primary key was installed as secondary in DCO, this means
+ * we have promoted it and we should tell DCO to swap keys
+ */
+ if (primary->dco_status == DCO_INSTALLED_SECONDARY)
+ {
+ msg(D_DCO_DEBUG, "Swapping primary and secondary keys, now: id1=%d id2=%d",
+ primary->key_id, secondary ? secondary->key_id : -1);
+
+ int ret = dco_swap_keys(dco, multi->peer_id);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot swap keys: %s (%d)", strerror(-ret), ret);
+ return;
+ }
+
+ primary->dco_status = DCO_INSTALLED_PRIMARY;
+ if (secondary)
+ {
+ ASSERT(secondary->dco_status == DCO_INSTALLED_PRIMARY);
+ secondary->dco_status = DCO_INSTALLED_SECONDARY;
+ }
+ }
+
+ /* if we have no secondary key anymore, inform DCO about it */
+ if (!secondary && multi->dco_keys_installed == 2)
+ {
+ int ret = dco_del_key(dco, multi->peer_id, OVPN_KEY_SLOT_SECONDARY);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot delete secondary key: %s (%d)", strerror(-ret), ret);
+ return;
+ }
+ multi->dco_keys_installed = 1;
+ }
+
+ /* all keys that are not installed are set to NOT installed */
+ for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = get_key_scan(multi, i);
+ if (ks != primary && ks != secondary)
+ {
+ ks->dco_status = DCO_NOT_INSTALLED;
+ }
+ }
+}
+
static bool
dco_check_option_conflict_platform(int msglevel, const struct options *o)
{
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 1692f5c3..b926e236 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -132,6 +132,14 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
const struct key2 *key2, int key_direction,
const char *ciphername, bool server);
+/**
+ * Possibly swap or wipe keys from DCO
+ *
+ * @param dco DCO device context
+ * @param multi TLS multi instance
+ */
+void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -192,5 +200,11 @@ init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
return 0;
}
+static inline void
+dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
+{
+ ASSERT(false);
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 28f3c088..38d2683c 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -41,6 +41,7 @@
#include "dhcp.h"
#include "common.h"
#include "ssl_verify.h"
+#include "dco.h"
#include "memdbg.h"
@@ -140,6 +141,18 @@ context_reschedule_sec(struct context *c, int sec)
}
}
+void
+check_dco_key_status(struct context *c)
+{
+ /* DCO context is not yet initialised or enabled */
+ if (!dco_enabled(&c->options))
+ {
+ return;
+ }
+
+ dco_update_keys(&c->c1.tuntap->dco, c->c2.tls_multi);
+}
+
/*
* In TLS mode, let TLS level respond to any control-channel
* packets which were received, or prepare any packets for
@@ -182,6 +195,12 @@ check_tls(struct context *c)
interval_schedule_wakeup(&c->c2.tmp_int, &wakeup);
+ /* Our current code has no good hooks in the TLS machinery to update
+ * DCO keys. So we check the key status after the whole TLS machinery
+ * has been completed and potentially update them
+ */
+ check_dco_key_status(c);
+
if (wakeup)
{
context_reschedule_sec(c, wakeup);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v3 10/25] dco: periodically check and possibly rotate/delete keys
2022-08-02 15:16 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
@ 2022-08-03 9:10 ` Frank Lichtenheld
2022-08-03 14:30 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Frank Lichtenheld @ 2022-08-03 9:10 UTC (permalink / raw)
To: openvpn-devel
On Tue, Aug 02, 2022 at 05:16:04PM +0200, Antonio Quartulli wrote:
> Data channel keys are periodically regenarated and installed in
> ovpn-dco.
> However, there is a certain moment when keys are rotated in order
> to elect the new primary one.
>
> Check the key status in userspace so that kernelspace can be informed as
> well when rotations happen.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
>
> Changes from v2:
> * reworked dco_update_keys():
> ** removed existing ASSERTs on keys and converted into something more
> logic and related to the flow
> ** fixed comment about userspace (use "we" for more clarity)
> ** add error code handling. The idea is to abort operations as soon as a
> failure is detected, so that the next iteration can make another
> attempt.
Looks to me like this addresses all my comments.
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH applied] Re: dco: periodically check and possibly rotate/delete keys
2022-08-02 15:16 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2022-08-03 9:10 ` Frank Lichtenheld
@ 2022-08-03 14:30 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-03 14:30 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
The review of that was a combined effort, mostly Frank on mail and
Antonio and I on IRC - but the v3 is now easier to understand, and
"should work nicely".
I have slightly extended the comment before "swapping primary and
secondary keys" to make a bit clearer how we end there (we discussed
that comment on IRC).
This said, I have only tested the "no DCO in kernel" cases, as the
rest is still missing functinality - and for "no DCO", this becomes
fairly much "no change", as the only new code otherwise is guarded
with "if (!dco_enabled(&c->options))".
Your patch has been applied to the master branch.
commit cd4ba9279d9c4cf13cdc3d8c49143c57eb0fe448
Author: Antonio Quartulli
Date: Tue Aug 2 17:16:04 2022 +0200
dco: periodically check and possibly rotate/delete keys
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220802151604.2801-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24785.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 11/25] dco: split option parsing routines
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (9 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 10/25] dco: periodically check and possibly rotate/delete keys Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-28 14:29 ` Arne Schwabe
2022-07-28 19:47 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible Antonio Quartulli
` (13 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
DCO will try to install keys upon generating them, however, this happens
when parsing pushed cipher options (due to NCP).
For this reason we need to postpone parsing pushed cipher options to *after*
the tunnel interface has been opened, otherwise we would have no DCO netdev
object to operate on.
At the same time we split the parsing code, so that we can ensure that
the NEW_PEER call can happen after the received peer-id has been parsed
(it is required by all DCO API calls).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/init.c | 59 ++++++++++++++++++++++++++++-----------------
src/openvpn/init.h | 2 ++
src/openvpn/multi.c | 7 ++++++
3 files changed, 46 insertions(+), 22 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 06911cd0..b0a4b252 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2068,14 +2068,6 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
return false;
}
}
- else if (c->mode == MODE_POINT_TO_POINT)
- {
- if (!do_deferred_p2p_ncp(c))
- {
- msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
- return false;
- }
- }
/* if --up-delay specified, open tun, do ifconfig, and run up script now */
if (c->options.up_delay || PULL_DEFINED(&c->options))
@@ -2102,6 +2094,22 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
}
}
+ if (!pulled_options && c->mode == MODE_POINT_TO_POINT)
+ {
+ if (!do_deferred_p2p_ncp(c))
+ {
+ msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
+ return false;
+ }
+ }
+
+ if (!finish_options(c))
+ {
+ msg(D_TLS_ERRORS, "ERROR: Failed to finish option processing");
+ return false;
+ }
+
+
if (c->c2.did_open_tun)
{
c->c1.pulled_options_digest_save = c->c2.pulled_options_digest;
@@ -2307,23 +2315,30 @@ do_deferred_options(struct context *c, const unsigned int found)
{
return false;
}
- struct frame *frame_fragment = NULL;
+ }
+
+ return true;
+}
+
+bool
+finish_options(struct context *c)
+{
+ struct frame *frame_fragment = NULL;
#ifdef ENABLE_FRAGMENT
- if (c->options.ce.fragment)
- {
- frame_fragment = &c->c2.frame_fragment;
- }
+ if (c->options.ce.fragment)
+ {
+ frame_fragment = &c->c2.frame_fragment;
+ }
#endif
- struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
- &c->options, &c->c2.frame,
- frame_fragment,
- get_link_socket_info(c)))
- {
- msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
- return false;
- }
+ struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
+ {
+ msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
+ return false;
}
return true;
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 2b8c2dcc..98e71d3a 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -97,6 +97,8 @@ void reset_coarse_timers(struct context *c);
bool do_deferred_options(struct context *c, const unsigned int found);
+bool finish_options(struct context *c);
+
void inherit_context_child(struct context *dest,
const struct context *src);
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index c72575ae..34ab90b4 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2405,6 +2405,13 @@ multi_client_connect_late_setup(struct multi_context *m,
{
mi->context.c2.tls_multi->multi_state = CAS_FAILED;
}
+ /* Continue processing options only if authentication hasn't failed.
+ * Otherwise it does not make sense and we may operate on a non-configured
+ * client instance */
+ else
+ {
+ finish_options(&mi->context);
+ }
/* send push reply if ready */
if (mi->context.c2.push_request_received)
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 11/25] dco: split option parsing routines
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 11/25] dco: split option parsing routines Antonio Quartulli
@ 2022-06-28 14:29 ` Arne Schwabe
2022-07-28 19:47 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-28 14:29 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> + if (!finish_options(c))
> + {
> + msg(D_TLS_ERRORS, "ERROR: Failed to finish option processing");
> + return false;
> + }
This error is a bit too generic for my taste. Can we make it more
specific? Like "Failed to apply options when finalising tun setup" or
something?
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v2 11/25] dco: split option parsing routines
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 11/25] dco: split option parsing routines Antonio Quartulli
2022-06-28 14:29 ` Arne Schwabe
@ 2022-07-28 19:47 ` Antonio Quartulli
2022-08-02 10:53 ` Gert Doering
2022-08-03 8:51 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-28 19:47 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
DCO will try to install keys upon generating them, however, this happens
when parsing pushed cipher options (due to NCP).
For this reason we need to postpone parsing pushed cipher options to *after*
the tunnel interface has been opened, otherwise we would have no DCO netdev
object to operate on.
At the same time we split the parsing code, so that we can ensure that
the NEW_PEER call can happen after the received peer-id has been parsed
(it is required by all DCO API calls).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* removed error message in case of failure of finish_options(). The
latter already warns the user about the failure - no need to print
another generic message.
---
src/openvpn/init.c | 57 ++++++++++++++++++++++++++++-----------------
src/openvpn/init.h | 2 ++
src/openvpn/multi.c | 7 ++++++
3 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 338d797b..db42d540 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2093,14 +2093,6 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
return false;
}
}
- else if (c->mode == MODE_POINT_TO_POINT)
- {
- if (!do_deferred_p2p_ncp(c))
- {
- msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
- return false;
- }
- }
/* if --up-delay specified, open tun, do ifconfig, and run up script now */
if (c->options.up_delay || PULL_DEFINED(&c->options))
@@ -2127,6 +2119,20 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
}
}
+ if (!pulled_options && c->mode == MODE_POINT_TO_POINT)
+ {
+ if (!do_deferred_p2p_ncp(c))
+ {
+ msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
+ return false;
+ }
+ }
+
+ if (!finish_options(c))
+ {
+ return false;
+ }
+
if (c->c2.did_open_tun)
{
c->c1.pulled_options_digest_save = c->c2.pulled_options_digest;
@@ -2332,23 +2338,30 @@ do_deferred_options(struct context *c, const unsigned int found)
{
return false;
}
- struct frame *frame_fragment = NULL;
+ }
+
+ return true;
+}
+
+bool
+finish_options(struct context *c)
+{
+ struct frame *frame_fragment = NULL;
#ifdef ENABLE_FRAGMENT
- if (c->options.ce.fragment)
- {
- frame_fragment = &c->c2.frame_fragment;
- }
+ if (c->options.ce.fragment)
+ {
+ frame_fragment = &c->c2.frame_fragment;
+ }
#endif
- struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
- &c->options, &c->c2.frame,
- frame_fragment,
- get_link_socket_info(c)))
- {
- msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
- return false;
- }
+ struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
+ {
+ msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
+ return false;
}
return true;
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 5f412a33..9389e0db 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -97,6 +97,8 @@ void reset_coarse_timers(struct context *c);
bool do_deferred_options(struct context *c, const unsigned int found);
+bool finish_options(struct context *c);
+
void inherit_context_child(struct context *dest,
const struct context *src);
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index c72575ae..34ab90b4 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -2405,6 +2405,13 @@ multi_client_connect_late_setup(struct multi_context *m,
{
mi->context.c2.tls_multi->multi_state = CAS_FAILED;
}
+ /* Continue processing options only if authentication hasn't failed.
+ * Otherwise it does not make sense and we may operate on a non-configured
+ * client instance */
+ else
+ {
+ finish_options(&mi->context);
+ }
/* send push reply if ready */
if (mi->context.c2.push_request_received)
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 11/25] dco: split option parsing routines
2022-07-28 19:47 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-08-02 10:53 ` Gert Doering
2022-08-03 8:51 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-02 10:53 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 4625 bytes --]
Hi,
On Thu, Jul 28, 2022 at 09:47:33PM +0200, Antonio Quartulli wrote:
> DCO will try to install keys upon generating them, however, this happens
> when parsing pushed cipher options (due to NCP).
>
> For this reason we need to postpone parsing pushed cipher options to *after*
> the tunnel interface has been opened, otherwise we would have no DCO netdev
> object to operate on.
>
> At the same time we split the parsing code, so that we can ensure that
> the NEW_PEER call can happen after the received peer-id has been parsed
> (it is required by all DCO API calls).
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
Some aspects of this patch I do not like, and others are broken,
unfortunately.
What I do not like is the naming of finish_options(), because I find
it confusing in relation to the "first half" of that, do_deferred_options()
- so maybe name this do_deferred_options_crypto()? But this is somewhat
minor.
What I'm not sure about is the order of things in multi.c - but that
might be due to misunderstanding. But I wonder why you are calling
"multi_client_generate_tls_keys()" *before* processing the incoming
crypto options in "finish_options()", which call update_crypto_params() -
is there an interdependency? Or does this not matter?
The "broken" bit is: it breaks udp p2p TLS connections - the server
will segfault. I guess this is due to moving p2p_ncp around...
The crash manifests as follows:
gdb openvpn
gdb> run server.conf
...
2022-08-02 12:50:45 us=144084 net_iface_mtu_set: mtu 1500 for tun5
2022-08-02 12:50:45 us=144179 net_iface_up: set tun5 up
2022-08-02 12:50:45 us=144241 net_addr_v6_add: fd00:abcd:204:8::1/64 dev tun5
2022-08-02 12:50:45 us=144396 Data Channel MTU parms [ mss_fix:0 max_frag:0 tun_mtu:1500 headroom:136 payload:1736 tailroom:557 ET:0 ]
2022-08-02 12:50:45 us=144457 Local Options String (VER=V4): 'V4,dev-type tun,link-mtu 1545,tun-mtu 1500,proto UDPv4,tun-ipv6,ifconfig 10.204.8.2 10.204.8.1,comp-lzo,cipher BF-CBC,auth SHA1,keysize 128,secret'
2022-08-02 12:50:45 us=144480 Expected Remote Options String (VER=V4): 'V4,dev-type tun,link-mtu 1545,tun-mtu 1500,proto UDPv4,tun-ipv6,ifconfig 10.204.8.1 10.204.8.2,comp-lzo,cipher BF-CBC,auth SHA1,keysize 128,secret'
2022-08-02 12:50:45 us=144539 Socket Buffers: R=[212992->212992] S=[212992->212992]
2022-08-02 12:50:45 us=144568 setsockopt(IPV6_V6ONLY=0)
2022-08-02 12:50:45 us=144616 UDPv6 link local (bound): [AF_INET6][undef]:51204
2022-08-02 12:50:45 us=144644 UDPv6 link remote: [AF_UNSPEC]
<waiting, client connects>
2022-08-02 12:52:13 us=388007 Peer Connection Initiated with [AF_INET6]::ffff:194.97.140.21:19880
Program received signal SIGSEGV, Segmentation fault0x00005555555d38e1 in check_session_cipher (session=session@...1851...=0x448, options=options@...1851...=0x7fffffffd4d0) at ssl_ncp.c:499
499 && streq(options->ciphername, session->opt->config_ciphername);
(gdb) where
#0 0x00005555555d38e1 in check_session_cipher (session=session@...2686....=0x448,
options=options@...1851...=0x7fffffffd4d0) at ssl_ncp.c:499
#1 0x00005555555cab8b in tls_session_update_crypto_params (multi=0x0,
session=0x448, options=0x7fffffffd4d0, frame=0x7fffffffe0a0,
frame_fragment=0x0, lsi=0x55555562bc80) at ssl.c:1716
#2 0x000055555557899f in finish_options (c=<optimized out>) at init.c:2358
#3 0x0000555555578a65 in do_up (c=c@...1851...=0x7fffffffd4d0,
pulled_options=pulled_options@...1851...=false,
option_types_found=option_types_found@...1851...=0) at init.c:2131
#4 0x0000555555571326 in check_connection_established (c=0x7fffffffd4d0)
at forward.c:311
#5 process_coarse_timers (c=0x7fffffffd4d0) at forward.c:634
#6 check_coarse_timers (c=0x7fffffffd4d0) at forward.c:717
#7 pre_select (c=c@...1851...=0x7fffffffd4d0) at forward.c:1823
#8 0x0000555555597b36 in tunnel_point_to_point (c=0x7fffffffd4d0)
at openvpn.c:79
#9 openvpn_main (argc=2, argv=0x7fffffffe638) at openvpn.c:311
#10 0x00007ffff7cc21ea in ?? () from /lib64/libc.so.6
#11 0x00007ffff7cc229c in __libc_start_main () from /lib64/libc.so.6
#12 0x000055555555c0b1 in _start ()
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v3 11/25] dco: split option parsing routines
2022-07-28 19:47 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-08-02 10:53 ` Gert Doering
@ 2022-08-03 8:51 ` Antonio Quartulli
2022-08-03 9:50 ` [Openvpn-devel] [PATCH v4 " Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-03 8:51 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
DCO will try to install keys upon generating them, however, this happens
when parsing pushed cipher options (due to NCP).
For this reason we need to postpone parsing pushed cipher options to *after*
the tunnel interface has been opened, otherwise we would have no DCO netdev
object to operate on.
At the same time we split the parsing code, so that we can ensure that
the NEW_PEER call can happen after the received peer-id has been parsed
(it is required by all DCO API calls).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* rename finish_options() to do_deferred_options_part2()
* add comments to explain why this new function is required
* remove invocation in multi.c: we already perform key generation as
last step
Changes from v1:
* removed error message in case of failure of finish_options(). The
latter already warns the user about the failure - no need to print
another generic message.
---
src/openvpn/init.c | 70 +++++++++++++++++++++++++++++-----------------
1 file changed, 45 insertions(+), 25 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 338d797b..7ded843e 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2078,6 +2078,37 @@ options_hash_changed_or_zero(const struct sha256_digest *a,
|| !memcmp(a, &zero, sizeof(struct sha256_digest));
}
+/**
+ * This function is expected to be invoked after open_tun() was performed.
+ *
+ * This kind of behaviour is required by DCO, because the following operations
+ * can be done only after the DCO device was created and the new peer was
+ * properly added.
+ */
+static bool
+do_deferred_options_part2(struct context *c)
+{
+ struct frame *frame_fragment = NULL;
+#ifdef ENABLE_FRAGMENT
+ if (c->options.ce.fragment)
+ {
+ frame_fragment = &c->c2.frame_fragment;
+ }
+#endif
+
+ struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
+ {
+ msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
+ return false;
+ }
+
+ return true;
+}
+
bool
do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
{
@@ -2093,14 +2124,6 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
return false;
}
}
- else if (c->mode == MODE_POINT_TO_POINT)
- {
- if (!do_deferred_p2p_ncp(c))
- {
- msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
- return false;
- }
- }
/* if --up-delay specified, open tun, do ifconfig, and run up script now */
if (c->options.up_delay || PULL_DEFINED(&c->options))
@@ -2127,6 +2150,20 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
}
}
+ if (!pulled_options && c->mode == MODE_POINT_TO_POINT)
+ {
+ if (!do_deferred_p2p_ncp(c))
+ {
+ msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
+ return false;
+ }
+ }
+
+ if (!do_deferred_options_part2(c))
+ {
+ return false;
+ }
+
if (c->c2.did_open_tun)
{
c->c1.pulled_options_digest_save = c->c2.pulled_options_digest;
@@ -2332,23 +2369,6 @@ do_deferred_options(struct context *c, const unsigned int found)
{
return false;
}
- struct frame *frame_fragment = NULL;
-#ifdef ENABLE_FRAGMENT
- if (c->options.ce.fragment)
- {
- frame_fragment = &c->c2.frame_fragment;
- }
-#endif
-
- struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
- &c->options, &c->c2.frame,
- frame_fragment,
- get_link_socket_info(c)))
- {
- msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
- return false;
- }
}
return true;
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v4 11/25] dco: split option parsing routines
2022-08-03 8:51 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
@ 2022-08-03 9:50 ` Antonio Quartulli
2022-08-03 15:56 ` [Openvpn-devel] [PATCH applied] " Gert Doering
0 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-03 9:50 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
DCO will try to install keys upon generating them, however, this happens
when parsing pushed cipher options (due to NCP).
For this reason we need to postpone parsing pushed cipher options to *after*
the tunnel interface has been opened, otherwise we would have no DCO netdev
object to operate on.
At the same time we split the parsing code, so that we can ensure that
the NEW_PEER call can happen after the received peer-id has been parsed
(it is required by all DCO API calls).
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v3:
* call do_deferred_options_part2() only if !pulled_options. This is the
same condition that triggered the call to do_deferred_options() in the
first place, therefore part2 must follow the same logic.
* add extra comment in do_up to explain "step2" after open_tun()
Changes from v2:
* rename finish_options() to do_deferred_options_part2()
* add comments to explain why this new function is required
* remove invocation in multi.c: we already perform key generation as
last step
Changes from v1:
* removed error message in case of failure of finish_options(). The
latter already warns the user about the failure - no need to print
another generic message.
---
src/openvpn/init.c | 81 ++++++++++++++++++++++++++++++++--------------
1 file changed, 56 insertions(+), 25 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 338d797b..de8faeb4 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2078,6 +2078,37 @@ options_hash_changed_or_zero(const struct sha256_digest *a,
|| !memcmp(a, &zero, sizeof(struct sha256_digest));
}
+/**
+ * This function is expected to be invoked after open_tun() was performed.
+ *
+ * This kind of behaviour is required by DCO, because the following operations
+ * can be done only after the DCO device was created and the new peer was
+ * properly added.
+ */
+static bool
+do_deferred_options_part2(struct context *c)
+{
+ struct frame *frame_fragment = NULL;
+#ifdef ENABLE_FRAGMENT
+ if (c->options.ce.fragment)
+ {
+ frame_fragment = &c->c2.frame_fragment;
+ }
+#endif
+
+ struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
+ if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
+ &c->options, &c->c2.frame,
+ frame_fragment,
+ get_link_socket_info(c)))
+ {
+ msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
+ return false;
+ }
+
+ return true;
+}
+
bool
do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
{
@@ -2093,14 +2124,6 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
return false;
}
}
- else if (c->mode == MODE_POINT_TO_POINT)
- {
- if (!do_deferred_p2p_ncp(c))
- {
- msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
- return false;
- }
- }
/* if --up-delay specified, open tun, do ifconfig, and run up script now */
if (c->options.up_delay || PULL_DEFINED(&c->options))
@@ -2127,6 +2150,31 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
}
}
+ /* do_deferred_options_part2() and do_deferred_p2p_ncp() *must* be
+ * invoked after open_tun().
+ * This is required by DCO because we must have created the interface
+ * and added the peer before we can fiddle with the keys or any other
+ * data channel per-peer setting.
+ */
+ if (pulled_options)
+ {
+ if (!do_deferred_options_part2(c))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (c->mode == MODE_POINT_TO_POINT)
+ {
+ if (!do_deferred_p2p_ncp(c))
+ {
+ msg(D_TLS_ERRORS, "ERROR: Failed to apply P2P negotiated protocol options");
+ return false;
+ }
+ }
+ }
+
if (c->c2.did_open_tun)
{
c->c1.pulled_options_digest_save = c->c2.pulled_options_digest;
@@ -2332,23 +2380,6 @@ do_deferred_options(struct context *c, const unsigned int found)
{
return false;
}
- struct frame *frame_fragment = NULL;
-#ifdef ENABLE_FRAGMENT
- if (c->options.ce.fragment)
- {
- frame_fragment = &c->c2.frame_fragment;
- }
-#endif
-
- struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
- if (!tls_session_update_crypto_params(c->c2.tls_multi, session,
- &c->options, &c->c2.frame,
- frame_fragment,
- get_link_socket_info(c)))
- {
- msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to import crypto options");
- return false;
- }
}
return true;
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: split option parsing routines
2022-08-03 9:50 ` [Openvpn-devel] [PATCH v4 " Antonio Quartulli
@ 2022-08-03 15:56 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-03 15:56 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
Subjected to the full set of server side tests that so nicely crashed
11/25 v2 :-) (passed) and to client Linux/FreeBSD t_client tests including
P2P NCP (passes as well).
The code looks good, and more correct as well. Thanks for all the
comments and for renaming the function - if it helps my brain, future
contributors might benefit from it as well.
Just for reference: do_deferred_p2p_ncp() is all "part2" type stuff,
so it gets moved as a whole, not split.
NOTE: there is a small change in behaviour, which I'm not sure if
problematic or not. The "part2" behaviour was previously encapsulated
in
if (c->options.pull)
{
...
}
and is no longer checking this in "_part2()".
This makes (as far as I can see) a difference if and only if a client
*without* --pull (-> c->options.pull) will receive a PUSH_REPLY from
a server that is behaving different from what we do - we only send
PUSH_REPLY in reply to a PUSH_REQUEST, or to IV_PROTO=IV_PROTO_REQUEST_PUSH
(and that is only happening if --pull / --client is set on the client).
In the server instance - which is called from multi.c - this would
make a difference *if* we called _part2() from multi.c - but we do not(!),
as that code path calls multi_client_generate_tls_keys(), which does
the tls_session_update_crypto_params() for us... so, server would
not call it before (!options.pull) and doesn't call it now (no call
to _part2()) -> no change here.
Your patch has been applied to the master branch.
commit 6b9f4d71d859c27a9a71699aa899c9a2c5c3b680
Author: Antonio Quartulli
Date: Wed Aug 3 11:50:12 2022 +0200
dco: split option parsing routines
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220803095012.24975-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24789.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (10 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 11/25] dco: split option parsing routines Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-06-28 14:32 ` Arne Schwabe
` (2 more replies)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path Antonio Quartulli
` (12 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
A server may push options that are not compatible with DCO.
In this case we should log a message and bail out.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/init.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index b0a4b252..091cbd24 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2219,6 +2219,19 @@ do_deferred_p2p_ncp(struct context *c)
return true;
}
+
+static bool
+check_dco_pull_options(struct options *o)
+{
+ if (!o->use_peer_id)
+ {
+ msg(D_TLS_ERRORS, "OPTIONS IMPORT: Server did not request DATA_V2 packet "
+ "format required for data channel offload");
+ return false;
+ }
+ return true;
+}
+
/*
* Handle non-tun-related pulled options.
*/
@@ -2341,6 +2354,16 @@ finish_options(struct context *c)
return false;
}
+ /* Check if the pushed options are compatible with DCO if we have
+ * DCO enabled */
+ if (dco_enabled(&c->options) && !check_dco_pull_options(&c->options))
+ {
+ msg(D_TLS_ERRORS, "OPTIONS ERROR: pushed options are incompatible with "
+ "data channel offload. Use --disable-dco to connect"
+ "to this server");
+ return false;
+ }
+
return true;
}
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible Antonio Quartulli
@ 2022-06-28 14:32 ` Arne Schwabe
2022-07-14 20:14 ` Gert Doering
2022-07-18 23:12 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-06-28 14:32 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 24.06.22 um 10:37 schrieb Antonio Quartulli:
> A server may push options that are not compatible with DCO.
> In this case we should log a message and bail out.
Acked-By: Arne Schwabe <arne@...1227...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible Antonio Quartulli
2022-06-28 14:32 ` Arne Schwabe
@ 2022-07-14 20:14 ` Gert Doering
2022-07-18 22:54 ` Antonio Quartulli
2022-07-18 23:12 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Gert Doering @ 2022-07-14 20:14 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 931 bytes --]
Hi,
On Fri, Jun 24, 2022 at 10:37:56AM +0200, Antonio Quartulli wrote:
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index b0a4b252..091cbd24 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -2219,6 +2219,19 @@ do_deferred_p2p_ncp(struct context *c)
> return true;
> }
>
> +
> +static bool
> +check_dco_pull_options(struct options *o)
> +{
Nitpicking a bit - the "other" DCO option check functions are in dco.c
(not yet, but in the patches leading up to this), so why is this one in
init.c?
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible
2022-07-14 20:14 ` Gert Doering
@ 2022-07-18 22:54 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-18 22:54 UTC (permalink / raw)
To: Gert Doering <gert@; +Cc: openvpn-devel
Hi,
On 14/07/2022 22:14, Gert Doering wrote:
> Hi,
>
> On Fri, Jun 24, 2022 at 10:37:56AM +0200, Antonio Quartulli wrote:
>> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
>> index b0a4b252..091cbd24 100644
>> --- a/src/openvpn/init.c
>> +++ b/src/openvpn/init.c
>> @@ -2219,6 +2219,19 @@ do_deferred_p2p_ncp(struct context *c)
>> return true;
>> }
>>
>> +
>> +static bool
>> +check_dco_pull_options(struct options *o)
>> +{
>
> Nitpicking a bit - the "other" DCO option check functions are in dco.c
> (not yet, but in the patches leading up to this), so why is this one in
> init.c?
good point. will move it to dco.c and send v2
Cheers,
>
> gert
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 12/25] dco: check that pulled options are compatible
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible Antonio Quartulli
2022-06-28 14:32 ` Arne Schwabe
2022-07-14 20:14 ` Gert Doering
@ 2022-07-18 23:12 ` Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-18 23:12 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
A server may push options that are not compatible with DCO.
In this case we should log a message and bail out.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* move check_dco_pull_options() to dco.c (renamed to
dco_check_pull_options())
* make options argument const
* add msglevel as first argument
src/openvpn/dco.c | 12 ++++++++++++
src/openvpn/dco.h | 17 +++++++++++++++++
src/openvpn/init.c | 11 +++++++++++
3 files changed, 40 insertions(+)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index b39759e1..fa4a8487 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -339,4 +339,16 @@ dco_check_option_conflict(int msglevel, const struct options *o)
return true;
}
+bool
+dco_check_pull_options(int msglevel, const struct options *o)
+{
+ if (!o->use_peer_id)
+ {
+ msg(msglevel, "OPTIONS IMPORT: Server did not request DATA_V2 packet "
+ "format required for data channel offload");
+ return false;
+ }
+ return true;
+}
+
#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index cb7f7e4f..a4bf6e37 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -65,6 +65,17 @@ bool dco_available(int msglevel);
*/
bool dco_check_option_conflict(int msglevel, const struct options *o);
+/**
+ * Check whether any of the options pushed by the server 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_pull_options(int msglevel, const struct options *o);
+
/**
* Initialize the DCO context
*
@@ -154,6 +165,12 @@ dco_check_option_conflict(int msglevel, const struct options *o)
return false;
}
+static inline bool
+dco_check_pull_options(int msglevel, const struct options *o)
+{
+ return false;
+}
+
static inline bool
ovpn_dco_init(int mode, dco_context_t *dco)
{
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 8e1221dd..ff2787d7 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2366,6 +2366,17 @@ finish_options(struct context *c)
return false;
}
+ /* Check if the pushed options are compatible with DCO if we have
+ * DCO enabled */
+ if (dco_enabled(&c->options) && !dco_check_pull_options(D_TLS_ERRORS,
+ &c->options))
+ {
+ msg(D_TLS_ERRORS, "OPTIONS ERROR: pushed options are incompatible with "
+ "data channel offload. Use --disable-dco to connect"
+ "to this server");
+ return false;
+ }
+
return true;
}
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (11 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 12/25] dco: check that pulled options are compatible Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-07-05 12:30 ` Heiko Hund
2022-08-04 7:14 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server " Antonio Quartulli
` (11 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
With this change we introduce ovpn-dco support only along the p2p/client
code path. Server codebase is still unchanged.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/dco.c | 90 +++++++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 48 +++++++++++++++++++++++
src/openvpn/event.h | 3 ++
src/openvpn/forward.c | 63 ++++++++++++++++++++++++++++--
src/openvpn/init.c | 34 +++++++++++++++-
src/openvpn/init.h | 2 +-
src/openvpn/socket.h | 1 +
7 files changed, 236 insertions(+), 5 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 473eb564..2919c46d 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -300,4 +300,94 @@ dco_check_option_conflict(int msglevel, const struct options *o)
return true;
}
+int
+dco_p2p_add_new_peer(struct context *c)
+{
+ if (!dco_enabled(&c->options))
+ {
+ return 0;
+ }
+
+
+ struct tls_multi *multi = c->c2.tls_multi;
+ struct link_socket *ls = c->c2.link_socket;
+
+ struct in6_addr remote_ip6 = { 0 };
+ struct in_addr remote_ip4 = { 0 };
+
+ struct in6_addr *remote_addr6 = NULL;
+ struct in_addr *remote_addr4 = NULL;
+
+ const char *gw = NULL;
+
+ ASSERT(ls->info.connection_established);
+
+ /* In client mode if a P2P style topology is used we assume the
+ * remote-gateway is the IP of the peer */
+ if (c->options.topology == TOP_NET30 || c->options.topology == TOP_P2P)
+ {
+ gw = c->options.ifconfig_remote_netmask;
+ }
+ if (c->options.route_default_gateway)
+ {
+ gw = c->options.route_default_gateway;
+ }
+
+ /* These inet_pton conversion are fatal since options.c already implements
+ * checks to have only valid addresses when setting the options */
+ if (c->options.ifconfig_ipv6_remote)
+ {
+ if (inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &remote_ip6) != 1)
+ {
+ msg(M_FATAL,
+ "DCO peer init: problem converting IPv6 ifconfig remote address %s to binary",
+ c->options.ifconfig_ipv6_remote);
+ }
+ remote_addr6 = &remote_ip6;
+ }
+
+ if (gw)
+ {
+ if (inet_pton(AF_INET, gw, &remote_ip4) != 1)
+ {
+ msg(M_FATAL, "DCO peer init: problem converting IPv4 ifconfig gateway address %s to binary", gw);
+ }
+ remote_addr4 = &remote_ip4;
+ }
+ else if (c->options.ifconfig_local)
+ {
+ msg(M_INFO, "DCO peer init: Need a peer VPN addresss to setup IPv4 (set --route-gateway)");
+ }
+
+ struct sockaddr *remoteaddr = &ls->info.lsa->actual.dest.addr.sa;
+
+ int ret = dco_new_peer(&c->c1.tuntap->dco, multi->peer_id,
+ c->c2.link_socket->sd, NULL, remoteaddr,
+ remote_addr4, remote_addr6);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ c->c2.tls_multi->dco_peer_added = true;
+ c->c2.link_socket->info.dco_installed = true;
+
+ return 0;
+}
+
+void
+dco_remove_peer(struct context *c)
+{
+ if (!dco_enabled(&c->options))
+ {
+ return;
+ }
+
+ if (c->c1.tuntap && c->c2.tls_multi && c->c2.tls_multi->dco_peer_added)
+ {
+ dco_del_peer(&c->c1.tuntap->dco, c->c2.tls_multi->peer_id);
+ c->c2.tls_multi->dco_peer_added = false;
+ }
+}
+
#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index cb7f7e4f..33b91e29 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -138,6 +138,36 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
*/
void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
+/**
+ * Install a new peer in DCO - to be called by a CLIENT (or P2P) instance
+ *
+ * @param c the main instance context
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_p2p_add_new_peer(struct context *c);
+
+/**
+ * Modify DCO peer options. Special values are 0 (disable)
+ * and -1 (do not touch).
+ *
+ * @param dco DCO device context
+ * @param peer_id the ID of the peer to be modified
+ * @param keepalive_interval keepalive interval in seconds
+ * @param keepalive_timeout keepalive timeout in seconds
+ * @param mss TCP MSS value
+ *
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_set_peer(dco_context_t *dco, unsigned int peerid,
+ int keepalive_interval, int keepalive_timeout, int mss);
+
+/**
+ * Remove a peer from DCO
+ *
+ * @param c the main instance context of the peer to remove
+ */
+void dco_remove_peer(struct context *c);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -204,5 +234,23 @@ dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
ASSERT(false);
}
+static inline bool
+dco_p2p_add_new_peer(struct context *c)
+{
+ return true;
+}
+
+static inline int
+dco_set_peer(dco_context_t *dco, unsigned int peerid,
+ int keepalive_interval, int keepalive_timeout, int mss)
+{
+ return 0;
+}
+
+static inline void
+dco_remove_peer(struct context *c)
+{
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/event.h b/src/openvpn/event.h
index a472afbe..f2438f97 100644
--- a/src/openvpn/event.h
+++ b/src/openvpn/event.h
@@ -72,6 +72,9 @@
#define MANAGEMENT_WRITE (1 << (MANAGEMENT_SHIFT + WRITE_SHIFT))
#define FILE_SHIFT 8
#define FILE_CLOSED (1 << (FILE_SHIFT + READ_SHIFT))
+#define DCO_SHIFT 10
+#define DCO_READ (1 << (DCO_SHIFT + READ_SHIFT))
+#define DCO_WRITE (1 << (DCO_SHIFT + WRITE_SHIFT))
/*
* Initialization flags passed to event_set_init
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 99898e01..15bdbbae 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -1103,6 +1103,39 @@ process_incoming_link(struct context *c)
perf_pop();
}
+static void
+process_incoming_dco(struct context *c)
+{
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ struct link_socket_info *lsi = get_link_socket_info(c);
+ dco_context_t *dco = &c->c1.tuntap->dco;
+
+ dco_do_read(dco);
+
+ if (dco->dco_message_type == OVPN_CMD_DEL_PEER)
+ {
+ trigger_ping_timeout_signal(c);
+ return;
+ }
+
+ if (dco->dco_message_type != OVPN_CMD_PACKET)
+ {
+ msg(D_DCO_DEBUG, "%s: received message of type %u - ignoring", __func__,
+ dco->dco_message_type);
+ return;
+ }
+
+ struct buffer orig_buff = c->c2.buf;
+ c->c2.buf = dco->dco_packet_in;
+ c->c2.from = lsi->lsa->actual;
+
+ process_incoming_link(c);
+
+ c->c2.buf = orig_buff;
+ buf_init(&dco->dco_packet_in, 0);
+#endif /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+}
+
/*
* Output: c->c2.buf
*/
@@ -1626,9 +1659,17 @@ process_outgoing_link(struct context *c)
socks_preprocess_outgoing_link(c, &to_addr, &size_delta);
/* Send packet */
- size = link_socket_write(c->c2.link_socket,
- &c->c2.to_link,
- to_addr);
+ if (c->c2.link_socket->info.dco_installed)
+ {
+ size = dco_do_write(&c->c1.tuntap->dco,
+ c->c2.tls_multi->peer_id,
+ &c->c2.to_link);
+ }
+ else
+ {
+ size = link_socket_write(c->c2.link_socket, &c->c2.to_link,
+ to_addr);
+ }
/* Undo effect of prepend */
link_socket_write_post_size_adjust(&size, size_delta, &c->c2.to_link);
@@ -1898,6 +1939,9 @@ io_wait_dowork(struct context *c, const unsigned int flags)
#ifdef ENABLE_ASYNC_PUSH
static int file_shift = FILE_SHIFT;
#endif
+#ifdef TARGET_LINUX
+ static int dco_shift = DCO_SHIFT; /* Event from DCO linux kernel module */
+#endif
/*
* Decide what kind of events we want to wait for.
@@ -2005,6 +2049,12 @@ io_wait_dowork(struct context *c, const unsigned int flags)
*/
socket_set(c->c2.link_socket, c->c2.event_set, socket, (void *)&socket_shift, NULL);
tun_set(c->c1.tuntap, c->c2.event_set, tuntap, (void *)&tun_shift, NULL);
+#if defined(TARGET_LINUX)
+ if (socket & EVENT_READ && c->c2.did_open_tun)
+ {
+ dco_event_set(&c->c1.tuntap->dco, c->c2.event_set, (void *)&dco_shift);
+ }
+#endif
#ifdef ENABLE_MANAGEMENT
if (management)
@@ -2127,4 +2177,11 @@ process_io(struct context *c)
process_incoming_tun(c);
}
}
+ else if (status & DCO_READ)
+ {
+ if (!IS_SIG(c))
+ {
+ process_incoming_dco(c);
+ }
+ }
}
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 091cbd24..bdd2ad96 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2094,6 +2094,19 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
}
}
+ if (c->mode == MODE_POINT_TO_POINT)
+ {
+ /* ovpn-dco requires adding the peer now, before any option can be set,
+ * but *after* having parsed the pushed peer-id in do_deferred_options()
+ */
+ int ret = dco_p2p_add_new_peer(c);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot add peer to DCO: %s", strerror(-ret));
+ return false;
+ }
+ }
+
if (!pulled_options && c->mode == MODE_POINT_TO_POINT)
{
if (!do_deferred_p2p_ncp(c))
@@ -2109,7 +2122,6 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
return false;
}
-
if (c->c2.did_open_tun)
{
c->c1.pulled_options_digest_save = c->c2.pulled_options_digest;
@@ -2364,6 +2376,22 @@ finish_options(struct context *c)
return false;
}
+ if (dco_enabled(&c->options)
+ && (c->options.ping_send_timeout || c->c2.frame.mss_fix))
+ {
+ int ret = dco_set_peer(&c->c1.tuntap->dco,
+ c->c2.tls_multi->peer_id,
+ c->options.ping_send_timeout,
+ c->options.ping_rec_timeout,
+ c->c2.frame.mss_fix);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot set parameters for DCO peer (id=%u): %s",
+ c->c2.tls_multi->peer_id, strerror(-ret));
+ return false;
+ }
+ }
+
return true;
}
@@ -4337,6 +4365,10 @@ close_instance(struct context *c)
/* free buffers */
do_close_free_buf(c);
+ /* close peer for DCO if enabled, needs peer-id so must be done before
+ * closing TLS contexts */
+ dco_remove_peer(c);
+
/* close TLS */
do_close_tls(c);
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 98e71d3a..5cc2a990 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -30,7 +30,7 @@
* Baseline maximum number of events
* to wait for.
*/
-#define BASE_N_EVENTS 4
+#define BASE_N_EVENTS 5
void context_clear(struct context *c);
diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h
index a75adb00..0d521d22 100644
--- a/src/openvpn/socket.h
+++ b/src/openvpn/socket.h
@@ -120,6 +120,7 @@ struct link_socket_info
sa_family_t af; /* Address family like AF_INET, AF_INET6 or AF_UNSPEC*/
bool bind_ipv6_only;
int mtu_changed; /* Set to true when mtu value is changed */
+ bool dco_installed;
};
/*
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path Antonio Quartulli
@ 2022-07-05 12:30 ` Heiko Hund
2022-07-05 12:38 ` Antonio Quartulli
2022-08-04 7:14 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:30 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:37:57 CEST Antonio Quartulli wrote:
> + /* These inet_pton conversion are fatal since options.c already
> implements
> + * checks to have only valid addresses when setting the
> options */
> + if (c->options.ifconfig_ipv6_remote)
> + {
> + if (inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote,
> &remote_ip6) != 1) + {
> + msg(M_FATAL,
> + "DCO peer init: problem converting IPv6 ifconfig remote
> address %s to binary", + c->options.ifconfig_ipv6_remote);
> + }
> + remote_addr6 = &remote_ip6;
> + }
I'm undecided if these fatal errors are justified with respect to defensive
programming or overly paranoid, because they will never appear.
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path
2022-07-05 12:30 ` Heiko Hund
@ 2022-07-05 12:38 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-05 12:38 UTC (permalink / raw)
To: Heiko Hund <heiko@
Hi,
On 05/07/2022 14:30, Heiko Hund wrote:
> On Freitag, 24. Juni 2022 10:37:57 CEST Antonio Quartulli wrote:
>> + /* These inet_pton conversion are fatal since options.c already
>> implements
>> + * checks to have only valid addresses when setting the
>> options */
>> + if (c->options.ifconfig_ipv6_remote)
>> + {
>> + if (inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote,
>> &remote_ip6) != 1) + {
>> + msg(M_FATAL,
>> + "DCO peer init: problem converting IPv6 ifconfig remote
>> address %s to binary", + c->options.ifconfig_ipv6_remote);
>> + }
>> + remote_addr6 = &remote_ip6;
>> + }
>
> I'm undecided if these fatal errors are justified with respect to defensive
> programming or overly paranoid, because they will never appear.
I'd say they are simply ASSERTs in disguise :-)
When a function returns an error I think it is always good habit to
check it..then why not printing something meaningful at this point?
Cheers,
>
>
>
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 13/25] dco: implement dco support for p2p/client code path
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path Antonio Quartulli
2022-07-05 12:30 ` Heiko Hund
@ 2022-08-04 7:14 ` Antonio Quartulli
2022-08-04 13:30 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-04 7:14 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
With this change we introduce ovpn-dco support only along the p2p/client
code path. Server codebase is still unchanged.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* rebased
---
src/openvpn/dco.c | 91 +++++++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 48 +++++++++++++++++++++++
src/openvpn/event.h | 3 ++
src/openvpn/forward.c | 63 ++++++++++++++++++++++++++++--
src/openvpn/init.c | 33 ++++++++++++++++
src/openvpn/init.h | 2 +-
src/openvpn/socket.h | 1 +
7 files changed, 237 insertions(+), 4 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 8c22b7ea..b5cc8a70 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -36,6 +36,7 @@
#include "crypto.h"
#include "dco.h"
#include "errlevel.h"
+#include "multi.h"
#include "networking.h"
#include "openvpn.h"
#include "options.h"
@@ -382,4 +383,94 @@ dco_check_pull_options(int msglevel, const struct options *o)
return true;
}
+int
+dco_p2p_add_new_peer(struct context *c)
+{
+ if (!dco_enabled(&c->options))
+ {
+ return 0;
+ }
+
+
+ struct tls_multi *multi = c->c2.tls_multi;
+ struct link_socket *ls = c->c2.link_socket;
+
+ struct in6_addr remote_ip6 = { 0 };
+ struct in_addr remote_ip4 = { 0 };
+
+ struct in6_addr *remote_addr6 = NULL;
+ struct in_addr *remote_addr4 = NULL;
+
+ const char *gw = NULL;
+
+ ASSERT(ls->info.connection_established);
+
+ /* In client mode if a P2P style topology is used we assume the
+ * remote-gateway is the IP of the peer */
+ if (c->options.topology == TOP_NET30 || c->options.topology == TOP_P2P)
+ {
+ gw = c->options.ifconfig_remote_netmask;
+ }
+ if (c->options.route_default_gateway)
+ {
+ gw = c->options.route_default_gateway;
+ }
+
+ /* These inet_pton conversion are fatal since options.c already implements
+ * checks to have only valid addresses when setting the options */
+ if (c->options.ifconfig_ipv6_remote)
+ {
+ if (inet_pton(AF_INET6, c->options.ifconfig_ipv6_remote, &remote_ip6) != 1)
+ {
+ msg(M_FATAL,
+ "DCO peer init: problem converting IPv6 ifconfig remote address %s to binary",
+ c->options.ifconfig_ipv6_remote);
+ }
+ remote_addr6 = &remote_ip6;
+ }
+
+ if (gw)
+ {
+ if (inet_pton(AF_INET, gw, &remote_ip4) != 1)
+ {
+ msg(M_FATAL, "DCO peer init: problem converting IPv4 ifconfig gateway address %s to binary", gw);
+ }
+ remote_addr4 = &remote_ip4;
+ }
+ else if (c->options.ifconfig_local)
+ {
+ msg(M_INFO, "DCO peer init: Need a peer VPN addresss to setup IPv4 (set --route-gateway)");
+ }
+
+ struct sockaddr *remoteaddr = &ls->info.lsa->actual.dest.addr.sa;
+
+ int ret = dco_new_peer(&c->c1.tuntap->dco, multi->peer_id,
+ c->c2.link_socket->sd, NULL, remoteaddr,
+ remote_addr4, remote_addr6);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ c->c2.tls_multi->dco_peer_added = true;
+ c->c2.link_socket->info.dco_installed = true;
+
+ return 0;
+}
+
+void
+dco_remove_peer(struct context *c)
+{
+ if (!dco_enabled(&c->options))
+ {
+ return;
+ }
+
+ if (c->c1.tuntap && c->c2.tls_multi && c->c2.tls_multi->dco_peer_added)
+ {
+ dco_del_peer(&c->c1.tuntap->dco, c->c2.tls_multi->peer_id);
+ c->c2.tls_multi->dco_peer_added = false;
+ }
+}
+
#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index fbb35906..602dafb7 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -151,6 +151,36 @@ int init_key_dco_bi(struct tls_multi *multi, struct key_state *ks,
*/
void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
+/**
+ * Install a new peer in DCO - to be called by a CLIENT (or P2P) instance
+ *
+ * @param c the main instance context
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_p2p_add_new_peer(struct context *c);
+
+/**
+ * Modify DCO peer options. Special values are 0 (disable)
+ * and -1 (do not touch).
+ *
+ * @param dco DCO device context
+ * @param peer_id the ID of the peer to be modified
+ * @param keepalive_interval keepalive interval in seconds
+ * @param keepalive_timeout keepalive timeout in seconds
+ * @param mss TCP MSS value
+ *
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_set_peer(dco_context_t *dco, unsigned int peerid,
+ int keepalive_interval, int keepalive_timeout, int mss);
+
+/**
+ * Remove a peer from DCO
+ *
+ * @param c the main instance context of the peer to remove
+ */
+void dco_remove_peer(struct context *c);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -223,5 +253,23 @@ dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
ASSERT(false);
}
+static inline bool
+dco_p2p_add_new_peer(struct context *c)
+{
+ return true;
+}
+
+static inline int
+dco_set_peer(dco_context_t *dco, unsigned int peerid,
+ int keepalive_interval, int keepalive_timeout, int mss)
+{
+ return 0;
+}
+
+static inline void
+dco_remove_peer(struct context *c)
+{
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/event.h b/src/openvpn/event.h
index a472afbe..f2438f97 100644
--- a/src/openvpn/event.h
+++ b/src/openvpn/event.h
@@ -72,6 +72,9 @@
#define MANAGEMENT_WRITE (1 << (MANAGEMENT_SHIFT + WRITE_SHIFT))
#define FILE_SHIFT 8
#define FILE_CLOSED (1 << (FILE_SHIFT + READ_SHIFT))
+#define DCO_SHIFT 10
+#define DCO_READ (1 << (DCO_SHIFT + READ_SHIFT))
+#define DCO_WRITE (1 << (DCO_SHIFT + WRITE_SHIFT))
/*
* Initialization flags passed to event_set_init
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 38d2683c..55c939c4 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -1110,6 +1110,39 @@ process_incoming_link(struct context *c)
perf_pop();
}
+static void
+process_incoming_dco(struct context *c)
+{
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ struct link_socket_info *lsi = get_link_socket_info(c);
+ dco_context_t *dco = &c->c1.tuntap->dco;
+
+ dco_do_read(dco);
+
+ if (dco->dco_message_type == OVPN_CMD_DEL_PEER)
+ {
+ trigger_ping_timeout_signal(c);
+ return;
+ }
+
+ if (dco->dco_message_type != OVPN_CMD_PACKET)
+ {
+ msg(D_DCO_DEBUG, "%s: received message of type %u - ignoring", __func__,
+ dco->dco_message_type);
+ return;
+ }
+
+ struct buffer orig_buff = c->c2.buf;
+ c->c2.buf = dco->dco_packet_in;
+ c->c2.from = lsi->lsa->actual;
+
+ process_incoming_link(c);
+
+ c->c2.buf = orig_buff;
+ buf_init(&dco->dco_packet_in, 0);
+#endif /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+}
+
/*
* Output: c->c2.buf
*/
@@ -1633,9 +1666,17 @@ process_outgoing_link(struct context *c)
socks_preprocess_outgoing_link(c, &to_addr, &size_delta);
/* Send packet */
- size = link_socket_write(c->c2.link_socket,
- &c->c2.to_link,
- to_addr);
+ if (c->c2.link_socket->info.dco_installed)
+ {
+ size = dco_do_write(&c->c1.tuntap->dco,
+ c->c2.tls_multi->peer_id,
+ &c->c2.to_link);
+ }
+ else
+ {
+ size = link_socket_write(c->c2.link_socket, &c->c2.to_link,
+ to_addr);
+ }
/* Undo effect of prepend */
link_socket_write_post_size_adjust(&size, size_delta, &c->c2.to_link);
@@ -1905,6 +1946,9 @@ io_wait_dowork(struct context *c, const unsigned int flags)
#ifdef ENABLE_ASYNC_PUSH
static int file_shift = FILE_SHIFT;
#endif
+#ifdef TARGET_LINUX
+ static int dco_shift = DCO_SHIFT; /* Event from DCO linux kernel module */
+#endif
/*
* Decide what kind of events we want to wait for.
@@ -2012,6 +2056,12 @@ io_wait_dowork(struct context *c, const unsigned int flags)
*/
socket_set(c->c2.link_socket, c->c2.event_set, socket, (void *)&socket_shift, NULL);
tun_set(c->c1.tuntap, c->c2.event_set, tuntap, (void *)&tun_shift, NULL);
+#if defined(TARGET_LINUX)
+ if (socket & EVENT_READ && c->c2.did_open_tun)
+ {
+ dco_event_set(&c->c1.tuntap->dco, c->c2.event_set, (void *)&dco_shift);
+ }
+#endif
#ifdef ENABLE_MANAGEMENT
if (management)
@@ -2134,4 +2184,11 @@ process_io(struct context *c)
process_incoming_tun(c);
}
}
+ else if (status & DCO_READ)
+ {
+ if (!IS_SIG(c))
+ {
+ process_incoming_dco(c);
+ }
+ }
}
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 4423e162..340c75d9 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2106,6 +2106,22 @@ do_deferred_options_part2(struct context *c)
return false;
}
+ if (dco_enabled(&c->options)
+ && (c->options.ping_send_timeout || c->c2.frame.mss_fix))
+ {
+ int ret = dco_set_peer(&c->c1.tuntap->dco,
+ c->c2.tls_multi->peer_id,
+ c->options.ping_send_timeout,
+ c->options.ping_rec_timeout,
+ c->c2.frame.mss_fix);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot set parameters for DCO peer (id=%u): %s",
+ c->c2.tls_multi->peer_id, strerror(-ret));
+ return false;
+ }
+ }
+
return true;
}
@@ -2150,6 +2166,19 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
}
}
+ if (c->mode == MODE_POINT_TO_POINT)
+ {
+ /* ovpn-dco requires adding the peer now, before any option can be set,
+ * but *after* having parsed the pushed peer-id in do_deferred_options()
+ */
+ int ret = dco_p2p_add_new_peer(c);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot add peer to DCO: %s (%d)", strerror(-ret), ret);
+ return false;
+ }
+ }
+
/* do_deferred_options_part2() and do_deferred_p2p_ncp() *must* be
* invoked after open_tun().
* This is required by DCO because we must have created the interface
@@ -4363,6 +4392,10 @@ close_instance(struct context *c)
/* free buffers */
do_close_free_buf(c);
+ /* close peer for DCO if enabled, needs peer-id so must be done before
+ * closing TLS contexts */
+ dco_remove_peer(c);
+
/* close TLS */
do_close_tls(c);
diff --git a/src/openvpn/init.h b/src/openvpn/init.h
index 5f412a33..f53b65ee 100644
--- a/src/openvpn/init.h
+++ b/src/openvpn/init.h
@@ -30,7 +30,7 @@
* Baseline maximum number of events
* to wait for.
*/
-#define BASE_N_EVENTS 4
+#define BASE_N_EVENTS 5
void context_clear(struct context *c);
diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h
index a75adb00..0d521d22 100644
--- a/src/openvpn/socket.h
+++ b/src/openvpn/socket.h
@@ -120,6 +120,7 @@ struct link_socket_info
sa_family_t af; /* Address family like AF_INET, AF_INET6 or AF_UNSPEC*/
bool bind_ipv6_only;
int mtu_changed; /* Set to true when mtu value is changed */
+ bool dco_installed;
};
/*
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: implement dco support for p2p/client code path
2022-08-04 7:14 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-08-04 13:30 ` Gert Doering
2022-08-04 13:51 ` Antonio Quartulli
2022-08-04 15:17 ` Gert Doering
0 siblings, 2 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-04 13:30 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
After all the preliminary infrastructure building, *this* is the beginning
of the real thing :-)
I have tested
- full set of server side tests, without --enable-dco
(this system does not have kernel DCO, so it does not matter)
--> all works
- full set of client side tests, with --enable-dco, but no kernel DCO
("existing setup")
--> all works
- full set of client side tests, with --enable-dco AND kernel DCO
(wohoo!)
--> some test instances disable DCO (like, SOCKS or HTTP proxy, or
TAP mode), and the fallback works ("pings succeed")
1b:openvpn.log Note: --http-proxy disables data channel offload.
1c:openvpn.log Note: --http-proxy disables data channel offload.
1d:openvpn.log Note: --socks-proxy disables data channel offload.
1e:openvpn.log Note: --socks-proxy disables data channel offload.
1z:openvpn.log Note: Using compression disables data channel offload.
2a:openvpn.log Note: cipher 'BF-CBC' in --data-ciphers is not supported by ovpn-dco, disabling data channel offload.
2d:openvpn.log Note: --socks-proxy disables data channel offload.
2e:openvpn.log Note: --socks-proxy disables data channel offload.
2z:openvpn.log Note: Using compression disables data channel offload.
3z:openvpn.log Note: Using compression disables data channel offload.
4:openvpn.log Note: dev-type not tun, disabling data channel offload.
4a:openvpn.log Note: dev-type not tun, disabling data channel offload.
4b:openvpn.log Note: dev-type not tun, disabling data channel offload.
6:openvpn.log Note: --fragment disables data channel offloa .
8:openvpn.log Note: Using compression disables data channel offload.
9:openvpn.log Note: dev-type not tun, disabling data channel offload.
23:openvpn.log Note: --data-cipher-fallback with cipher 'BF-CBC' disables data channel offload.
23a:openvpn.log Note: Using compression disables data channel offload.
23s:openvpn.log Note: --data-cipher-fallback with cipher 'BF-CBC' disables data channel offload.
24:openvpn.log Note: Using compression disables data channel offload.
24a:openvpn.log Note: Using compression disables data channel offload.
--> other instances claim to are using DCO ("ip -d link show"
shows "ovpn-dco") *and* packets are moved, so I guess it's
using DCO...
these tests include "normal --client clients", "p2p --secret",
"p2p --tls-secret", and "p2p --tls-secret with P2P NCP", using
varying ciphers (-> BF-CBC/none forcing non-DCO, etc.)
Test sets succeeded: 1 1a 1b 1c 1d 1e 1z 2 2a 2d 2e 2z 3 3z 4 4a 4b 5 6 8 9 23 23a 23s 24 24a.
Test sets failed: 2b 2c 2f.
The 3 failures (2b, 2c, 2f) are all "IPv6 UDP fragments" (ping -s 3000,
encapsulated in IPv6 UDP), which needs closer investigation. This works
on a "--disable-dco" build, but the whole topic of UDP fragmentation
is "outside OpenVPN", so this is not something a patch to OpenVPN
can affect. tcpdump on an intermediate host can see outgoing fragments
in the DCO case, but no replies - different from the non-DCO case, so
this is going to be an interesting root cause hunt...
- I have not done performance tests, because the current test
environment is not really suited for it yet (server instances
are all non-DCO)
Plus, stared at the code and discussed with Antonio on IRC :-) -
(especially the process_outgoing_link() change confused me a bit - the
obvious answer to this is "these are control channel packets, which are
still created by userland, but the design requires to avoid accessing
the socket directly, so send to DCO module, and that one forwards").
I removed one spurious blank line from dco_p2p_add_new_peer().
Your patch has been applied to the master branch.
commit b6f7b285767e66f5cbd3854cf0ff918e87b31202
Author: Antonio Quartulli
Date: Thu Aug 4 09:14:01 2022 +0200
dco: implement dco support for p2p/client code path
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220804071401.12410-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24798.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH applied] Re: dco: implement dco support for p2p/client code path
2022-08-04 13:30 ` [Openvpn-devel] [PATCH applied] " Gert Doering
@ 2022-08-04 13:51 ` Antonio Quartulli
2022-08-04 15:17 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-04 13:51 UTC (permalink / raw)
To: Gert Doering <gert@; +Cc: openvpn-devel
On 04/08/2022 15:30, Gert Doering wrote:
> Acked-by: Gert Doering <gert@...1296...>
[]>
> Your patch has been applied to the master branch.
Wohooo! Great stuff!
Thanks to everybody who contributed to this first milestone!
However, the party is not over yet :-)
Cheers,
>
> commit b6f7b285767e66f5cbd3854cf0ff918e87b31202
> Author: Antonio Quartulli
> Date: Thu Aug 4 09:14:01 2022 +0200
>
> dco: implement dco support for p2p/client code path
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> Acked-by: Gert Doering <gert@...1296...>
> Message-Id: <20220804071401.12410-1-a@...2181...>
> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24798.html
> Signed-off-by: Gert Doering <gert@...1296...>
>
>
> --
> kind regards,
>
> Gert Doering
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH applied] Re: dco: implement dco support for p2p/client code path
2022-08-04 13:30 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2022-08-04 13:51 ` Antonio Quartulli
@ 2022-08-04 15:17 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-04 15:17 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
[-- Attachment #1: Type: text/plain, Size: 5091 bytes --]
Hi,
On Thu, Aug 04, 2022 at 03:30:25PM +0200, Gert Doering wrote:
> Test sets failed: 2b 2c 2f.
So, this is an interesting one.
To trigger this, you need to connect over UDP + IPv6 transport, and
then you need to inject a packet into "openvpn with DCO" that is
fragmented into two parts, which *both* create an UDP packet after
encapsulation which is bigger than 1500 bytes.
$ ping -c1 -s 2900 10.194.2.1
does this.
Tcpdump on the "tun0" interface shows
15:49:16.433666 IP (tos 0x0, ttl 64, id 18639, offset 0, flags [+], proto ICMP (1), length 1500)
ubuntu2004 > 10.194.2.1: ICMP echo request, id 170, seq 1, length 1480
15:49:16.433703 IP (tos 0x0, ttl 64, id 18639, offset 1480, flags [none], proto ICMP (1), length 1448)
ubuntu2004 > 10.194.2.1: icmp
so this is one "full sized 1500 byte frame", containing the first fragment,
and then a 1448 byte frame containing the rest.
Connecting with --disable-dco yields this on the outside:
17:09:14.763519 IP6 (flowlabel 0x0082a, hlim 62, next-header Fragment (44) payload length: 1456) ubuntu2004 > phillip: frag (0x277a4245:0|1448) 34646 > 51194: UDP, bad length 1524 > 1440
17:09:14.763548 IP6 (flowlabel 0x0082a, hlim 62, next-header Fragment (44) payload length: 92) ubuntu2004 > phillip: frag (0x277a4245:1448|84)
17:09:14.763576 IP6 (flowlabel 0x0082a, hlim 62, next-header Fragment (44) payload length: 1456) ubuntu2004 > phillip: frag (0x1dec474f:0|1448) 34646 > 51194: UDP, bad length 1472 > 1440
17:09:14.763593 IP6 (flowlabel 0x0082a, hlim 62, next-header Fragment (44) payload length: 40) ubuntu2004 > phillip: frag (0x1dec474f:1448|32)
this is "4 packets are sent" (and, subsequently, 4 packets come back).
Both "(close to) full inside packets" are too big for an external 1500 byte
packet, and get fragmented. Is this ugly? Yes. Is it according to IP
specs? Yes. Do I expect this to work? Yes! (t_client excercises this
case with the 3000-byte-pings).
Now, *with* DCO, we get this:
17:10:43.771215 IP6 (hlim 62, next-header Fragment (44) payload length: 1456) ubuntu2004 > phillip: frag (0xe79d84cc:0|1448) 58755 > 51194: UDP, bad length 1524 > 1440
17:10:43.771246 IP6 (hlim 62, next-header Fragment (44) payload length: 92) ubuntu2004 > phillip: frag (0xe79d84cc:1448|84)
(only *two* packets, then only keepalives)
And at the same time, the client (!) complains
2022-08-04 17:10:38 net_iface_new: add tun0 type ovpn-dco
2022-08-04 17:10:38 DCO device tun0 opened
(so, we're using DCO)
2022-08-04 17:10:43 read UDPv6 [CMSG=50|EMSGSIZE Path-MTU=1500]: Message too long (fd=3,code=90)
Now... reducing the ping size to 2500...
gert@...2687...:~$ ping -c1 -s 2500 10.194.2.1
2508 bytes from 10.194.2.1: icmp_seq=1 ttl=64 time=126 ms
... actually works.
What is sent out by DCO in this case is 3 packets, 2 fragments, and
one regular UDP packet
17:12:21.512762 IP6 (hlim 62, next-header Fragment (44) payload length: 1456) ubuntu2004 > phillip: frag (0x12728a31:0|1448) 58755 > 51194: UDP, bad length 1524 > 1440
17:12:21.512806 IP6 (hlim 62, next-header Fragment (44) payload length: 92) ubuntu2004 > phillip: frag (0x12728a31:1448|84)
17:12:21.512864 IP6 (hlim 63, next-header UDP (17) payload length: 1080) ubuntu2004.58755 > phillip.51194: [udp sum ok] UDP, length 1072
... because the second half of the fragmented ICMP packet now fits into
one UDP/1500 byte even after openvpn encapsulation.
So my current guess is that the linux kernel sets *some* flag on
"non-primary ICMP fragments" (inside) that get carried over to the final
encapsulated packet, preventing further fragmentation (outside), but
eliciting an "PMTU fail" error instead - which the newly renovated
extended socket error handler picks up. In userland :-)
To add a data point:
gert@...2687...:~$ ping -c1 -s 3500 10.194.2.1
creates *3* packets in the DCO case
17:14:08.780733 IP6 (hlim 62, next-header Fragment (44) payload length: 1456) ubuntu2004 > phillip: frag (0x16acdc72:0|1448) 58755 > 51194: UDP, bad length 1524 > 1440
17:14:08.780757 IP6 (hlim 62, next-header Fragment (44) payload length: 92) ubuntu2004 > phillip: frag (0x16acdc72:1448|84)
17:14:08.780842 IP6 (hlim 63, next-header UDP (17) payload length: 600) ubuntu2004.58755 > phillip.51194: [udp sum ok] UDP, length 592
... and one error message.
In the non-DCO case, this creates *5* packets - 2 1500-inside packet
that get outside fragmented to 2+2 and one smaller "leftover" packet.
So the 2nd fragment is, again, refusing to be fragmented again...
(ping -s 5000 yields 7 packet without DCO, and only 3 with DCO, so a
pattern starts to form ;-) )
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server code path
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (12 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 13/25] dco: implement dco support for p2p/client code path Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
2022-07-28 19:55 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux Antonio Quartulli
` (10 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This change introduces ovpn-dco support along the p2mp/server code path.
Some code seems to be duplicate of the p2p version, but details are
different, so it couldn't be shared.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/dco.c | 203 ++++++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 49 ++++++++++
src/openvpn/mtcp.c | 59 +++++++++---
src/openvpn/mudp.c | 13 +++
src/openvpn/multi.c | 212 +++++++++++++++++++++++++++++++++++---------
src/openvpn/multi.h | 14 ++-
6 files changed, 494 insertions(+), 56 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 2919c46d..48e007ea 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -36,6 +36,7 @@
#include "crypto.h"
#include "dco.h"
#include "errlevel.h"
+#include "multi.h"
#include "openvpn.h"
#include "ssl_common.h"
#include "ssl_ncp.h"
@@ -390,4 +391,206 @@ dco_remove_peer(struct context *c)
}
}
+static bool
+dco_multi_get_localaddr(struct multi_context *m, struct multi_instance *mi,
+ struct sockaddr_storage *local)
+{
+#if ENABLE_IP_PKTINFO
+ struct context *c = &mi->context;
+
+ if (!(c->options.sockflags & SF_USE_IP_PKTINFO))
+ {
+ return false;
+ }
+
+ struct link_socket_actual *actual = &c->c2.link_socket_info->lsa->actual;
+
+ switch (actual->dest.addr.sa.sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sock_in4 = (struct sockaddr_in *)local;
+#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
+ sock_in4->sin_addr = actual->pi.in4.ipi_addr;
+#elif defined(IP_RECVDSTADDR)
+ sock_in4->sin_addr = actual->pi.in4;
+#else
+ /* source IP not available on this platform */
+ return false;
+#endif
+ sock_in4->sin_family = AF_INET;
+ break;
+ }
+
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)local;
+ sock_in6->sin6_addr = actual->pi.in6.ipi6_addr;
+ sock_in6->sin6_family = AF_INET6;
+ break;
+ }
+
+ default:
+ ASSERT(false);
+ }
+
+ return true;
+#else /* if ENABLE_IP_PKTINFO */
+ return false;
+#endif /* if ENABLE_IP_PKTINFO */
+}
+
+int
+dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
+{
+ struct context *c = &mi->context;
+
+ int peer_id = mi->context.c2.tls_multi->peer_id;
+ struct sockaddr *remoteaddr, *localaddr = NULL;
+ struct sockaddr_storage local = { 0 };
+ int sd = c->c2.link_socket->sd;
+
+ if (c->mode == CM_CHILD_TCP)
+ {
+ /* the remote address will be inferred from the TCP socket endpoint */
+ remoteaddr = NULL;
+ }
+ else
+ {
+ ASSERT(c->c2.link_socket_info->connection_established);
+ remoteaddr = &c->c2.link_socket_info->lsa->actual.dest.addr.sa;
+ }
+
+ struct in_addr remote_ip4 = { 0 };
+ struct in6_addr *remote_addr6 = NULL;
+ struct in_addr *remote_addr4 = NULL;
+
+ /* In server mode we need to fetch the remote addresses from the push config */
+ if (c->c2.push_ifconfig_defined)
+ {
+ remote_ip4.s_addr = htonl(c->c2.push_ifconfig_local);
+ remote_addr4 = &remote_ip4;
+ }
+ if (c->c2.push_ifconfig_ipv6_defined)
+ {
+ remote_addr6 = &c->c2.push_ifconfig_ipv6_local;
+ }
+
+ if (dco_multi_get_localaddr(m, mi, &local))
+ {
+ localaddr = (struct sockaddr *)&local;
+ }
+
+ int ret = dco_new_peer(&c->c1.tuntap->dco, peer_id, sd, localaddr,
+ remoteaddr, remote_addr4, remote_addr6);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ c->c2.tls_multi->dco_peer_added = true;
+
+ if (c->mode == CM_CHILD_TCP)
+ {
+ multi_tcp_dereference_instance(m->mtcp, mi);
+ if (close(sd))
+ {
+ msg(D_DCO|M_ERRNO, "error closing TCP socket after DCO handover");
+ }
+ c->c2.link_socket->info.dco_installed = true;
+ c->c2.link_socket->sd = SOCKET_UNDEFINED;
+ }
+
+ return 0;
+}
+
+void
+dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr)
+{
+#if defined(TARGET_LINUX)
+ if (!dco_enabled(&m->top.options))
+ {
+ return;
+ }
+
+ int addrtype = (addr->type & MR_ADDR_MASK);
+
+ /* If we do not have local IP addr to install, skip the route */
+ if ((addrtype == MR_ADDR_IPV6 && !mi->context.c2.push_ifconfig_ipv6_defined)
+ || (addrtype == MR_ADDR_IPV4 && !mi->context.c2.push_ifconfig_defined))
+ {
+ return;
+ }
+
+ struct context *c = &mi->context;
+ const char *dev = c->c1.tuntap->actual_name;
+
+ if (addrtype == MR_ADDR_IPV6)
+ {
+ int netbits = 128;
+ if (addr->type & MR_WITH_NETBITS)
+ {
+ netbits = addr->netbits;
+ }
+
+ net_route_v6_add(&m->top.net_ctx, &addr->v6.addr, netbits,
+ &mi->context.c2.push_ifconfig_ipv6_local, dev, 0,
+ DCO_IROUTE_METRIC);
+ }
+ else if (addrtype == MR_ADDR_IPV4)
+ {
+ int netbits = 32;
+ if (addr->type & MR_WITH_NETBITS)
+ {
+ netbits = addr->netbits;
+ }
+
+ in_addr_t dest = htonl(addr->v4.addr);
+ net_route_v4_add(&m->top.net_ctx, &dest, netbits,
+ &mi->context.c2.push_ifconfig_local, dev, 0,
+ DCO_IROUTE_METRIC);
+ }
+#endif /* if defined(TARGET_LINUX) */
+}
+
+void
+dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+{
+#if defined(TARGET_LINUX)
+ if (!dco_enabled(&m->top.options))
+ {
+ return;
+ }
+ ASSERT(TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN);
+
+ struct context *c = &mi->context;
+ const char *dev = c->c1.tuntap->actual_name;
+
+ if (mi->context.c2.push_ifconfig_defined)
+ {
+ for (const struct iroute *ir = c->options.iroutes;
+ ir;
+ ir = ir->next)
+ {
+ net_route_v4_del(&m->top.net_ctx, &ir->network, ir->netbits,
+ &mi->context.c2.push_ifconfig_local, dev,
+ 0, DCO_IROUTE_METRIC);
+ }
+ }
+
+ if (mi->context.c2.push_ifconfig_ipv6_defined)
+ {
+ for (const struct iroute_ipv6 *ir6 = c->options.iroutes_ipv6;
+ ir6;
+ ir6 = ir6->next)
+ {
+ net_route_v6_del(&m->top.net_ctx, &ir6->network, ir6->netbits,
+ &mi->context.c2.push_ifconfig_ipv6_local, dev,
+ 0, DCO_IROUTE_METRIC);
+ }
+ }
+#endif /* if defined(TARGET_LINUX) */
+}
+
#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 33b91e29..4b945b50 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -37,10 +37,14 @@
struct event_set;
struct key2;
struct key_state;
+struct multi_context;
+struct multi_instance;
+struct mroute_addr;
struct options;
struct tls_multi;
struct tuntap;
+#define DCO_IROUTE_METRIC 100
#define DCO_DEFAULT_METRIC 200
#if defined(ENABLE_DCO)
@@ -168,6 +172,34 @@ int dco_set_peer(dco_context_t *dco, unsigned int peerid,
*/
void dco_remove_peer(struct context *c);
+/**
+ * Install a new peer in DCO - to be called by a SERVER instance
+ *
+ * @param m the server context
+ * @param mi the client instance
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi);
+
+/**
+ * Install an iroute in DCO, which means adding a route to the system routing
+ * table. To be called by a SERVER instance only.
+ *
+ * @param m the server context
+ * @param mi the client instance acting as nexthop for the route
+ * @param addr the route to add
+ */
+void dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr);
+
+/**
+ * Remove all routes added through the specified client
+ *
+ * @param m the server context
+ * @param mi the client instance for which routes have to be removed
+ */
+void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -252,5 +284,22 @@ dco_remove_peer(struct context *c)
{
}
+static inline bool
+dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
+{
+ return true;
+}
+
+static inline void
+dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr)
+{
+}
+
+static inline void
+dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+{
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c
index b3c153fe..eb88a56a 100644
--- a/src/openvpn/mtcp.c
+++ b/src/openvpn/mtcp.c
@@ -61,6 +61,7 @@
#define MTCP_SIG ((void *)3) /* Only on Windows */
#define MTCP_MANAGEMENT ((void *)4)
#define MTCP_FILE_CLOSE_WRITE ((void *)5)
+#define MTCP_DCO ((void *)6)
#define MTCP_N ((void *)16) /* upper bound on MTCP_x */
@@ -131,6 +132,8 @@ multi_create_instance_tcp(struct multi_context *m)
const uint32_t hv = hash_value(hash, &mi->real);
struct hash_bucket *bucket = hash_bucket(hash, hv);
+ multi_assign_peer_id(m, mi);
+
he = hash_lookup_fast(hash, bucket, &mi->real, hv);
if (he)
@@ -238,6 +241,7 @@ multi_tcp_dereference_instance(struct multi_tcp *mtcp, struct multi_instance *mi
if (ls && mi->socket_set_called)
{
event_del(mtcp->es, socket_event_handle(ls));
+ mi->socket_set_called = false;
}
mtcp->n_esr = 0;
}
@@ -279,6 +283,9 @@ multi_tcp_wait(const struct context *c,
}
#endif
tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, persistent);
+#if defined(TARGET_LINUX)
+ dco_event_set(&c->c1.tuntap->dco, mtcp->es, MTCP_DCO);
+#endif
#ifdef ENABLE_MANAGEMENT
if (management)
@@ -395,6 +402,18 @@ multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const in
tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
+ if (mi && mi->context.c2.link_socket->info.dco_installed)
+ {
+ /* If we got a socket that has been handed over to the kernel
+ * we must not call the normal socket function to figure out
+ * if it is readable or writable */
+ /* Assert that we only have the DCO exptected flags */
+ ASSERT(action & (TA_SOCKET_READ | TA_SOCKET_WRITE));
+
+ /* We are always ready! */
+ return action;
+ }
+
switch (action)
{
case TA_TUN_READ:
@@ -518,7 +537,10 @@ multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int
case TA_INITIAL:
ASSERT(mi);
- multi_tcp_set_global_rw_flags(m, mi);
+ if (!mi->context.c2.link_socket->info.dco_installed)
+ {
+ multi_tcp_set_global_rw_flags(m, mi);
+ }
multi_process_post(m, mi, mpp_flags);
break;
@@ -568,7 +590,10 @@ multi_tcp_post(struct multi_context *m, struct multi_instance *mi, const int act
}
else
{
- multi_tcp_set_global_rw_flags(m, mi);
+ if (!c->c2.link_socket->info.dco_installed)
+ {
+ multi_tcp_set_global_rw_flags(m, mi);
+ }
}
break;
@@ -625,23 +650,22 @@ multi_tcp_action(struct multi_context *m, struct multi_instance *mi, int action,
/*
* Dispatch the action
*/
- {
- struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
+ struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
- /*
- * Signal received or TCP connection
- * reset by peer?
- */
- if (touched && IS_SIG(&touched->context))
+ /*
+ * Signal received or TCP connection
+ * reset by peer?
+ */
+ if (touched && IS_SIG(&touched->context))
+ {
+ if (mi == touched)
{
- if (mi == touched)
- {
- mi = NULL;
- }
- multi_close_instance_on_signal(m, touched);
+ mi = NULL;
}
+ multi_close_instance_on_signal(m, touched);
}
+
/*
* If dispatch produced any pending output
* for a particular instance, point to
@@ -739,6 +763,13 @@ multi_tcp_process_io(struct multi_context *m)
multi_tcp_action(m, mi, TA_INITIAL, false);
}
}
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ /* incoming data on DCO? */
+ else if (e->arg == MTCP_DCO)
+ {
+ multi_process_incoming_dco(m);
+ }
+#endif
/* signal received? */
else if (e->arg == MTCP_SIG)
{
diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c
index 0810fada..14aa7236 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -380,6 +380,19 @@ multi_process_io_udp(struct multi_context *m)
multi_process_file_closed(m, mpp_flags);
}
#endif
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ else if (status & DCO_READ)
+ {
+ if (!IS_SIG(&m->top))
+ {
+ bool ret = true;
+ while (ret)
+ {
+ ret = multi_process_incoming_dco(m);
+ }
+ }
+ }
+#endif
}
/*
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 34ab90b4..23472095 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -51,6 +51,7 @@
#include "crypto_backend.h"
#include "ssl_util.h"
+#include "dco.h"
/*#define MULTI_DEBUG_EVENT_LOOP*/
@@ -519,6 +520,9 @@ multi_del_iroutes(struct multi_context *m,
{
const struct iroute *ir;
const struct iroute_ipv6 *ir6;
+
+ dco_delete_iroutes(m, mi);
+
if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
{
for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
@@ -1224,16 +1228,20 @@ multi_learn_in_addr_t(struct multi_context *m,
addr.netbits = (uint8_t) netbits;
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* We do not want to install IP -> IP dev ovpn-dco0 */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
static struct multi_instance *
@@ -1257,16 +1265,20 @@ multi_learn_in6_addr(struct multi_context *m,
mroute_addr_mask_host_bits( &addr );
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* We do not want to install IP -> IP dev ovpn-dco0 */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
/*
@@ -1765,6 +1777,15 @@ multi_client_set_protocol_options(struct context *c)
tls_multi->use_peer_id = true;
o->use_peer_id = true;
}
+ else if (dco_enabled(o))
+ {
+ msg(M_INFO, "Client does not support DATA_V2. Data channel offloaing "
+ "requires DATA_V2. Dropping client.");
+ auth_set_client_reason(tls_multi, "Data channel negotiation "
+ "failed (missing DATA_V2)");
+ return false;
+ }
+
if (proto & IV_PROTO_REQUEST_PUSH)
{
c->c2.push_request_received = true;
@@ -2276,8 +2297,9 @@ cleanup:
* Generates the data channel keys
*/
static bool
-multi_client_generate_tls_keys(struct context *c)
+multi_client_generate_tls_keys(struct multi_context *m, struct multi_instance *mi)
{
+ struct context *c = &mi->context;
struct frame *frame_fragment = NULL;
#ifdef ENABLE_FRAGMENT
if (c->options.ce.fragment)
@@ -2285,6 +2307,17 @@ multi_client_generate_tls_keys(struct context *c)
frame_fragment = &c->c2.frame_fragment;
}
#endif
+
+ if (dco_enabled(&c->options))
+ {
+ int ret = dco_multi_add_new_peer(m, mi);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot add peer to DCO: %s", strerror(-ret));
+ return false;
+ }
+ }
+
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
&c->c2.frame, frame_fragment,
@@ -2401,7 +2434,7 @@ multi_client_connect_late_setup(struct multi_context *m,
}
/* Generate data channel keys only if setting protocol options
* has not failed */
- else if (!multi_client_generate_tls_keys(&mi->context))
+ else if (!multi_client_generate_tls_keys(m, mi))
{
mi->context.c2.tls_multi->multi_state = CAS_FAILED;
}
@@ -2668,6 +2701,14 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
(*cur_handler_index)++;
}
+ /* Check if we have forbidding options in the current mode */
+ if (dco_enabled(&mi->context.options)
+ && !dco_check_option_conflict(D_MULTI_ERRORS, &mi->context.options))
+ {
+ msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to incompatible DCO options");
+ cc_succeeded = false;
+ }
+
if (cc_succeeded)
{
multi_client_connect_late_setup(m, mi, *option_types_found);
@@ -3086,6 +3127,120 @@ done:
gc_free(&gc);
}
+/*
+ * Called when an instance should be closed due to the
+ * reception of a soft signal.
+ */
+void
+multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
+{
+ remap_signal(&mi->context);
+ set_prefix(mi);
+ print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
+ clear_prefix();
+ multi_close_instance(m, mi, false);
+}
+
+#if (defined(ENABLE_DCO) && defined(TARGET_LINUX)) || defined(ENABLE_MANAGEMENT)
+static void
+multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
+{
+ mi->context.sig->signal_received = sig;
+ multi_close_instance_on_signal(m, mi);
+}
+#endif
+
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+static void
+process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi, dco_context_t *dco)
+{
+ struct buffer orig_buf = mi->context.c2.buf;
+ int peer_id = dco->dco_message_peer_id;
+
+ mi->context.c2.buf = dco->dco_packet_in;
+
+ multi_process_incoming_link(m, mi, 0);
+
+ mi->context.c2.buf = orig_buf;
+ if (BLEN(&dco->dco_packet_in) < 1)
+ {
+ msg(D_DCO, "Received too short packet for peer %d", peer_id);
+ goto done;
+ }
+
+ uint8_t *ptr = BPTR(&dco->dco_packet_in);
+ uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
+ if (op == P_DATA_V2 || op == P_DATA_V2)
+ {
+ msg(D_DCO, "DCO: received data channel packet for peer %d", peer_id);
+ goto done;
+ }
+done:
+ buf_init(&dco->dco_packet_in, 0);
+}
+
+static void
+process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi, dco_context_t *dco)
+{
+ const char *reason = "(unknown reason by ovpn-dco)";
+ switch (dco->dco_del_peer_reason)
+ {
+ case OVPN_DEL_PEER_REASON_EXPIRED:
+ reason = "ovpn-dco: ping expired";
+ break;
+
+ case OVPN_DEL_PEER_REASON_TRANSPORT_ERROR:
+ reason = "ovpn-dco: transport error";
+ break;
+
+ case OVPN_DEL_PEER_REASON_USERSPACE:
+ /* This very likely ourselves but might be another process, so
+ * still process it */
+ reason = "ovpn-dco: userspace request";
+ break;
+ }
+
+ /* When kernel already deleted the peer, the socket is no longer
+ * installed and we don't need to cleanup the state in the kernel */
+ mi->context.c2.tls_multi->dco_peer_added = false;
+ mi->context.sig->signal_text = reason;
+ multi_signal_instance(m, mi, SIGTERM);
+}
+
+bool
+multi_process_incoming_dco(struct multi_context *m)
+{
+ dco_context_t *dco = &m->top.c1.tuntap->dco;
+
+ struct multi_instance *mi = NULL;
+
+ int ret = dco_do_read(&m->top.c1.tuntap->dco);
+
+ int peer_id = dco->dco_message_peer_id;
+
+ if ((peer_id >= 0) && (peer_id < m->max_clients) && (m->instances[peer_id]))
+ {
+ mi = m->instances[peer_id];
+ if (dco->dco_message_type == OVPN_CMD_PACKET)
+ {
+ process_incoming_dco_packet(m, mi, dco);
+ }
+ else if (dco->dco_message_type == OVPN_CMD_DEL_PEER)
+ {
+ process_incoming_del_peer(m, mi, dco);
+ }
+ }
+ else
+ {
+ msg(D_DCO, "Received packet for peer-id unknown to OpenVPN: %d", peer_id);
+ }
+
+ dco->dco_message_type = 0;
+ dco->dco_message_peer_id = -1;
+ return ret > 0;
+}
+#endif /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+
/*
* Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
* i.e. client -> server direction.
@@ -3647,32 +3802,11 @@ multi_process_signal(struct multi_context *m)
return true;
}
-/*
- * Called when an instance should be closed due to the
- * reception of a soft signal.
- */
-void
-multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
-{
- remap_signal(&mi->context);
- set_prefix(mi);
- print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
- clear_prefix();
- multi_close_instance(m, mi, false);
-}
-
/*
* Management subsystem callbacks
*/
#ifdef ENABLE_MANAGEMENT
-static void
-multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
-{
- mi->context.sig->signal_received = sig;
- multi_close_instance_on_signal(m, mi);
-}
-
static void
management_callback_status(void *arg, const int version, struct status_output *so)
{
@@ -3762,10 +3896,6 @@ management_delete_event(void *arg, event_t event)
}
}
-#endif /* ifdef ENABLE_MANAGEMENT */
-
-#ifdef ENABLE_MANAGEMENT
-
static struct multi_instance *
lookup_by_cid(struct multi_context *m, const unsigned long cid)
{
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index f1e9ab91..370d795c 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -98,7 +98,9 @@ struct client_connect_defer_state
* server-mode.
*/
struct multi_instance {
- struct schedule_entry se; /* this must be the first element of the structure */
+ struct schedule_entry se; /* this must be the first element of the structure,
+ * We cast between this and schedule_entry so the
+ * beginning of the struct must be identical */
struct gc_arena gc;
bool halt;
int refcount;
@@ -310,6 +312,16 @@ void multi_process_float(struct multi_context *m, struct multi_instance *mi);
*/
bool multi_process_post(struct multi_context *m, struct multi_instance *mi, const unsigned int flags);
+/**
+ * Process an incoming DCO message (from kernel space).
+ *
+ * @param m - The single \c multi_context structur.e
+ *
+ * @return
+ * - True, if the message was received correctly.
+ * - False, if there was an error while reading the message.
+ */
+bool multi_process_incoming_dco(struct multi_context *m);
/**************************************************************************/
/**
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server code path
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server " Antonio Quartulli
@ 2022-07-05 12:31 ` Heiko Hund
2022-07-05 14:53 ` Antonio Quartulli
2022-07-28 19:55 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:31 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:37:58 CEST Antonio Quartulli wrote:
> + uint8_t *ptr = BPTR(&dco->dco_packet_in);
> + uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
> + if (op == P_DATA_V2 || op == P_DATA_V2)
This looks odd. Seems you wanted to check for a second opcode, or is it
obsolete?
> + const char *reason = "(unknown reason by ovpn-dco)";
Wouldn't "ovpn-dco: unknown reason" be better?
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server code path
2022-07-05 12:31 ` Heiko Hund
@ 2022-07-05 14:53 ` Antonio Quartulli
2022-07-05 15:14 ` Arne Schwabe
0 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-05 14:53 UTC (permalink / raw)
To: Heiko Hund <heiko@
Hi,
On 05/07/2022 14:31, Heiko Hund wrote:
> On Freitag, 24. Juni 2022 10:37:58 CEST Antonio Quartulli wrote:
>> + uint8_t *ptr = BPTR(&dco->dco_packet_in);
>> + uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
>> + if (op == P_DATA_V2 || op == P_DATA_V2)
>
> This looks odd. Seems you wanted to check for a second opcode, or is it
> obsolete?
we wanted want to bail out on DATA packets - not sure why this was
repeated twice. Removing one check.
>
>> + const char *reason = "(unknown reason by ovpn-dco)";
>
> Wouldn't "ovpn-dco: unknown reason" be better?
Yeah, I'll do as you suggested.
While looking at this function, I realized that these checks should be
performed before calling multi_process_incoming_link().
Fixing this in v2...
Thanks!
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server code path
2022-07-05 14:53 ` Antonio Quartulli
@ 2022-07-05 15:14 ` Arne Schwabe
0 siblings, 0 replies; 157+ messages in thread
From: Arne Schwabe @ 2022-07-05 15:14 UTC (permalink / raw)
To: Antonio Quartulli <a@
Am 05.07.2022 um 16:53 schrieb Antonio Quartulli:
> Hi,
>
> On 05/07/2022 14:31, Heiko Hund wrote:
>> On Freitag, 24. Juni 2022 10:37:58 CEST Antonio Quartulli wrote:
>>> + uint8_t *ptr = BPTR(&dco->dco_packet_in);
>>> + uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
>>> + if (op == P_DATA_V2 || op == P_DATA_V2)
>>
>> This looks odd. Seems you wanted to check for a second opcode, or is it
>> obsolete?
>
> we wanted want to bail out on DATA packets - not sure why this was
> repeated twice. Removing one check.
>
The first one should be P_DATA_V1
Arne
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 14/25] dco: implement dco support for p2mp/server code path
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server " Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
@ 2022-07-28 19:55 ` Antonio Quartulli
2022-08-01 13:10 ` Heiko Hund
2022-08-05 6:45 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-28 19:55 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This change introduces ovpn-dco support along the p2mp/server code path.
Some code seems to be duplicate of the p2p version, but details are
different, so it couldn't be shared.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* fix if condition P_DATA_V2 -> P_DATA_V1
* fix unknown reason string
src/openvpn/dco.c | 1 +
src/openvpn/dco.h | 49 ++++++++++
src/openvpn/mtcp.c | 59 +++++++++---
src/openvpn/mudp.c | 13 +++
src/openvpn/multi.c | 216 +++++++++++++++++++++++++++++++++++---------
src/openvpn/multi.h | 14 ++-
6 files changed, 296 insertions(+), 56 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index b5717a77..633dd7fd 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -36,6 +36,7 @@
#include "crypto.h"
#include "dco.h"
#include "errlevel.h"
+#include "multi.h"
#include "networking.h"
#include "openvpn.h"
#include "options.h"
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 602dafb7..72569083 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -37,10 +37,14 @@
struct event_set;
struct key2;
struct key_state;
+struct multi_context;
+struct multi_instance;
+struct mroute_addr;
struct options;
struct tls_multi;
struct tuntap;
+#define DCO_IROUTE_METRIC 100
#define DCO_DEFAULT_METRIC 200
#if defined(ENABLE_DCO)
@@ -181,6 +185,34 @@ int dco_set_peer(dco_context_t *dco, unsigned int peerid,
*/
void dco_remove_peer(struct context *c);
+/**
+ * Install a new peer in DCO - to be called by a SERVER instance
+ *
+ * @param m the server context
+ * @param mi the client instance
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi);
+
+/**
+ * Install an iroute in DCO, which means adding a route to the system routing
+ * table. To be called by a SERVER instance only.
+ *
+ * @param m the server context
+ * @param mi the client instance acting as nexthop for the route
+ * @param addr the route to add
+ */
+void dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr);
+
+/**
+ * Remove all routes added through the specified client
+ *
+ * @param m the server context
+ * @param mi the client instance for which routes have to be removed
+ */
+void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -271,5 +303,22 @@ dco_remove_peer(struct context *c)
{
}
+static inline bool
+dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
+{
+ return true;
+}
+
+static inline void
+dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr)
+{
+}
+
+static inline void
+dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+{
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c
index b3c153fe..eb88a56a 100644
--- a/src/openvpn/mtcp.c
+++ b/src/openvpn/mtcp.c
@@ -61,6 +61,7 @@
#define MTCP_SIG ((void *)3) /* Only on Windows */
#define MTCP_MANAGEMENT ((void *)4)
#define MTCP_FILE_CLOSE_WRITE ((void *)5)
+#define MTCP_DCO ((void *)6)
#define MTCP_N ((void *)16) /* upper bound on MTCP_x */
@@ -131,6 +132,8 @@ multi_create_instance_tcp(struct multi_context *m)
const uint32_t hv = hash_value(hash, &mi->real);
struct hash_bucket *bucket = hash_bucket(hash, hv);
+ multi_assign_peer_id(m, mi);
+
he = hash_lookup_fast(hash, bucket, &mi->real, hv);
if (he)
@@ -238,6 +241,7 @@ multi_tcp_dereference_instance(struct multi_tcp *mtcp, struct multi_instance *mi
if (ls && mi->socket_set_called)
{
event_del(mtcp->es, socket_event_handle(ls));
+ mi->socket_set_called = false;
}
mtcp->n_esr = 0;
}
@@ -279,6 +283,9 @@ multi_tcp_wait(const struct context *c,
}
#endif
tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, persistent);
+#if defined(TARGET_LINUX)
+ dco_event_set(&c->c1.tuntap->dco, mtcp->es, MTCP_DCO);
+#endif
#ifdef ENABLE_MANAGEMENT
if (management)
@@ -395,6 +402,18 @@ multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const in
tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
+ if (mi && mi->context.c2.link_socket->info.dco_installed)
+ {
+ /* If we got a socket that has been handed over to the kernel
+ * we must not call the normal socket function to figure out
+ * if it is readable or writable */
+ /* Assert that we only have the DCO exptected flags */
+ ASSERT(action & (TA_SOCKET_READ | TA_SOCKET_WRITE));
+
+ /* We are always ready! */
+ return action;
+ }
+
switch (action)
{
case TA_TUN_READ:
@@ -518,7 +537,10 @@ multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int
case TA_INITIAL:
ASSERT(mi);
- multi_tcp_set_global_rw_flags(m, mi);
+ if (!mi->context.c2.link_socket->info.dco_installed)
+ {
+ multi_tcp_set_global_rw_flags(m, mi);
+ }
multi_process_post(m, mi, mpp_flags);
break;
@@ -568,7 +590,10 @@ multi_tcp_post(struct multi_context *m, struct multi_instance *mi, const int act
}
else
{
- multi_tcp_set_global_rw_flags(m, mi);
+ if (!c->c2.link_socket->info.dco_installed)
+ {
+ multi_tcp_set_global_rw_flags(m, mi);
+ }
}
break;
@@ -625,23 +650,22 @@ multi_tcp_action(struct multi_context *m, struct multi_instance *mi, int action,
/*
* Dispatch the action
*/
- {
- struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
+ struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
- /*
- * Signal received or TCP connection
- * reset by peer?
- */
- if (touched && IS_SIG(&touched->context))
+ /*
+ * Signal received or TCP connection
+ * reset by peer?
+ */
+ if (touched && IS_SIG(&touched->context))
+ {
+ if (mi == touched)
{
- if (mi == touched)
- {
- mi = NULL;
- }
- multi_close_instance_on_signal(m, touched);
+ mi = NULL;
}
+ multi_close_instance_on_signal(m, touched);
}
+
/*
* If dispatch produced any pending output
* for a particular instance, point to
@@ -739,6 +763,13 @@ multi_tcp_process_io(struct multi_context *m)
multi_tcp_action(m, mi, TA_INITIAL, false);
}
}
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ /* incoming data on DCO? */
+ else if (e->arg == MTCP_DCO)
+ {
+ multi_process_incoming_dco(m);
+ }
+#endif
/* signal received? */
else if (e->arg == MTCP_SIG)
{
diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c
index 0cbca1a9..ddb1efc9 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -381,6 +381,19 @@ multi_process_io_udp(struct multi_context *m)
multi_process_file_closed(m, mpp_flags);
}
#endif
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ else if (status & DCO_READ)
+ {
+ if (!IS_SIG(&m->top))
+ {
+ bool ret = true;
+ while (ret)
+ {
+ ret = multi_process_incoming_dco(m);
+ }
+ }
+ }
+#endif
}
/*
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 34ab90b4..4b671bb3 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -51,6 +51,7 @@
#include "crypto_backend.h"
#include "ssl_util.h"
+#include "dco.h"
/*#define MULTI_DEBUG_EVENT_LOOP*/
@@ -519,6 +520,9 @@ multi_del_iroutes(struct multi_context *m,
{
const struct iroute *ir;
const struct iroute_ipv6 *ir6;
+
+ dco_delete_iroutes(m, mi);
+
if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
{
for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
@@ -1224,16 +1228,20 @@ multi_learn_in_addr_t(struct multi_context *m,
addr.netbits = (uint8_t) netbits;
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* We do not want to install IP -> IP dev ovpn-dco0 */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
static struct multi_instance *
@@ -1257,16 +1265,20 @@ multi_learn_in6_addr(struct multi_context *m,
mroute_addr_mask_host_bits( &addr );
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* We do not want to install IP -> IP dev ovpn-dco0 */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
/*
@@ -1765,6 +1777,15 @@ multi_client_set_protocol_options(struct context *c)
tls_multi->use_peer_id = true;
o->use_peer_id = true;
}
+ else if (dco_enabled(o))
+ {
+ msg(M_INFO, "Client does not support DATA_V2. Data channel offloaing "
+ "requires DATA_V2. Dropping client.");
+ auth_set_client_reason(tls_multi, "Data channel negotiation "
+ "failed (missing DATA_V2)");
+ return false;
+ }
+
if (proto & IV_PROTO_REQUEST_PUSH)
{
c->c2.push_request_received = true;
@@ -2276,8 +2297,9 @@ cleanup:
* Generates the data channel keys
*/
static bool
-multi_client_generate_tls_keys(struct context *c)
+multi_client_generate_tls_keys(struct multi_context *m, struct multi_instance *mi)
{
+ struct context *c = &mi->context;
struct frame *frame_fragment = NULL;
#ifdef ENABLE_FRAGMENT
if (c->options.ce.fragment)
@@ -2285,6 +2307,17 @@ multi_client_generate_tls_keys(struct context *c)
frame_fragment = &c->c2.frame_fragment;
}
#endif
+
+ if (dco_enabled(&c->options))
+ {
+ int ret = dco_multi_add_new_peer(m, mi);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot add peer to DCO: %s", strerror(-ret));
+ return false;
+ }
+ }
+
struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
if (!tls_session_update_crypto_params(c->c2.tls_multi, session, &c->options,
&c->c2.frame, frame_fragment,
@@ -2401,7 +2434,7 @@ multi_client_connect_late_setup(struct multi_context *m,
}
/* Generate data channel keys only if setting protocol options
* has not failed */
- else if (!multi_client_generate_tls_keys(&mi->context))
+ else if (!multi_client_generate_tls_keys(m, mi))
{
mi->context.c2.tls_multi->multi_state = CAS_FAILED;
}
@@ -2668,6 +2701,14 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
(*cur_handler_index)++;
}
+ /* Check if we have forbidding options in the current mode */
+ if (dco_enabled(&mi->context.options)
+ && !dco_check_option_conflict(D_MULTI_ERRORS, &mi->context.options))
+ {
+ msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to incompatible DCO options");
+ cc_succeeded = false;
+ }
+
if (cc_succeeded)
{
multi_client_connect_late_setup(m, mi, *option_types_found);
@@ -3086,6 +3127,124 @@ done:
gc_free(&gc);
}
+/*
+ * Called when an instance should be closed due to the
+ * reception of a soft signal.
+ */
+void
+multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
+{
+ remap_signal(&mi->context);
+ set_prefix(mi);
+ print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
+ clear_prefix();
+ multi_close_instance(m, mi, false);
+}
+
+#if (defined(ENABLE_DCO) && defined(TARGET_LINUX)) || defined(ENABLE_MANAGEMENT)
+static void
+multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
+{
+ mi->context.sig->signal_received = sig;
+ multi_close_instance_on_signal(m, mi);
+}
+#endif
+
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+static void
+process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi,
+ dco_context_t *dco)
+{
+ if (BLEN(&dco->dco_packet_in) < 1)
+ {
+ msg(D_DCO, "Received too short packet for peer %d",
+ dco->dco_message_peer_id);
+ goto done;
+ }
+
+ uint8_t *ptr = BPTR(&dco->dco_packet_in);
+ uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
+ if ((op == P_DATA_V1) || (op == P_DATA_V2))
+ {
+ msg(D_DCO, "DCO: received data channel packet for peer %d",
+ dco->dco_message_peer_id);
+ goto done;
+ }
+
+ struct buffer orig_buf = mi->context.c2.buf;
+ mi->context.c2.buf = dco->dco_packet_in;
+
+ multi_process_incoming_link(m, mi, 0);
+
+ mi->context.c2.buf = orig_buf;
+
+done:
+ buf_init(&dco->dco_packet_in, 0);
+}
+
+static void
+process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi,
+ dco_context_t *dco)
+{
+ const char *reason = "ovpn-dco: unknown reason";
+ switch (dco->dco_del_peer_reason)
+ {
+ case OVPN_DEL_PEER_REASON_EXPIRED:
+ reason = "ovpn-dco: ping expired";
+ break;
+
+ case OVPN_DEL_PEER_REASON_TRANSPORT_ERROR:
+ reason = "ovpn-dco: transport error";
+ break;
+
+ case OVPN_DEL_PEER_REASON_USERSPACE:
+ /* This very likely ourselves but might be another process, so
+ * still process it */
+ reason = "ovpn-dco: userspace request";
+ break;
+ }
+
+ /* When kernel already deleted the peer, the socket is no longer
+ * installed and we don't need to cleanup the state in the kernel */
+ mi->context.c2.tls_multi->dco_peer_added = false;
+ mi->context.sig->signal_text = reason;
+ multi_signal_instance(m, mi, SIGTERM);
+}
+
+bool
+multi_process_incoming_dco(struct multi_context *m)
+{
+ dco_context_t *dco = &m->top.c1.tuntap->dco;
+
+ struct multi_instance *mi = NULL;
+
+ int ret = dco_do_read(&m->top.c1.tuntap->dco);
+
+ int peer_id = dco->dco_message_peer_id;
+
+ if ((peer_id >= 0) && (peer_id < m->max_clients) && (m->instances[peer_id]))
+ {
+ mi = m->instances[peer_id];
+ if (dco->dco_message_type == OVPN_CMD_PACKET)
+ {
+ process_incoming_dco_packet(m, mi, dco);
+ }
+ else if (dco->dco_message_type == OVPN_CMD_DEL_PEER)
+ {
+ process_incoming_del_peer(m, mi, dco);
+ }
+ }
+ else
+ {
+ msg(D_DCO, "Received packet for peer-id unknown to OpenVPN: %d", peer_id);
+ }
+
+ dco->dco_message_type = 0;
+ dco->dco_message_peer_id = -1;
+ return ret > 0;
+}
+#endif /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+
/*
* Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
* i.e. client -> server direction.
@@ -3647,32 +3806,11 @@ multi_process_signal(struct multi_context *m)
return true;
}
-/*
- * Called when an instance should be closed due to the
- * reception of a soft signal.
- */
-void
-multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
-{
- remap_signal(&mi->context);
- set_prefix(mi);
- print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
- clear_prefix();
- multi_close_instance(m, mi, false);
-}
-
/*
* Management subsystem callbacks
*/
#ifdef ENABLE_MANAGEMENT
-static void
-multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
-{
- mi->context.sig->signal_received = sig;
- multi_close_instance_on_signal(m, mi);
-}
-
static void
management_callback_status(void *arg, const int version, struct status_output *so)
{
@@ -3762,10 +3900,6 @@ management_delete_event(void *arg, event_t event)
}
}
-#endif /* ifdef ENABLE_MANAGEMENT */
-
-#ifdef ENABLE_MANAGEMENT
-
static struct multi_instance *
lookup_by_cid(struct multi_context *m, const unsigned long cid)
{
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index f1e9ab91..370d795c 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -98,7 +98,9 @@ struct client_connect_defer_state
* server-mode.
*/
struct multi_instance {
- struct schedule_entry se; /* this must be the first element of the structure */
+ struct schedule_entry se; /* this must be the first element of the structure,
+ * We cast between this and schedule_entry so the
+ * beginning of the struct must be identical */
struct gc_arena gc;
bool halt;
int refcount;
@@ -310,6 +312,16 @@ void multi_process_float(struct multi_context *m, struct multi_instance *mi);
*/
bool multi_process_post(struct multi_context *m, struct multi_instance *mi, const unsigned int flags);
+/**
+ * Process an incoming DCO message (from kernel space).
+ *
+ * @param m - The single \c multi_context structur.e
+ *
+ * @return
+ * - True, if the message was received correctly.
+ * - False, if there was an error while reading the message.
+ */
+bool multi_process_incoming_dco(struct multi_context *m);
/**************************************************************************/
/**
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 14/25] dco: implement dco support for p2mp/server code path
2022-07-28 19:55 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-08-01 13:10 ` Heiko Hund
2022-08-05 6:45 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Heiko Hund @ 2022-08-01 13:10 UTC (permalink / raw)
To: openvpn-devel
On Donnerstag, 28. Juli 2022 21:55:01 CEST Antonio Quartulli wrote:
> This change introduces ovpn-dco support along the p2mp/server code path.
> Some code seems to be duplicate of the p2p version, but details are
> different, so it couldn't be shared.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
>
> Changes from v1:
> * fix if condition P_DATA_V2 -> P_DATA_V1
> * fix unknown reason string
Acked-by: Heiko Hund <heiko@...2662...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v3 14/25] dco: implement dco support for p2mp/server code path
2022-07-28 19:55 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-08-01 13:10 ` Heiko Hund
@ 2022-08-05 6:45 ` Antonio Quartulli
2022-08-05 14:55 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-05 6:45 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
This change introduces ovpn-dco support along the p2mp/server code path.
Some code seems to be duplicate of the p2p version, but details are
different, so it couldn't be shared.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* rebased
Changes from v1:
* fix if condition P_DATA_V2 -> P_DATA_V1
* fix unknown reason string
---
src/openvpn/dco.c | 202 ++++++++++++++++++++++++++++++++++++++
src/openvpn/dco.h | 49 ++++++++++
src/openvpn/mtcp.c | 59 ++++++++---
src/openvpn/mudp.c | 13 +++
src/openvpn/multi.c | 232 ++++++++++++++++++++++++++++++++++++--------
src/openvpn/multi.h | 14 ++-
6 files changed, 513 insertions(+), 56 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index a8735e88..09855643 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -472,4 +472,206 @@ dco_remove_peer(struct context *c)
}
}
+static bool
+dco_multi_get_localaddr(struct multi_context *m, struct multi_instance *mi,
+ struct sockaddr_storage *local)
+{
+#if ENABLE_IP_PKTINFO
+ struct context *c = &mi->context;
+
+ if (!(c->options.sockflags & SF_USE_IP_PKTINFO))
+ {
+ return false;
+ }
+
+ struct link_socket_actual *actual = &c->c2.link_socket_info->lsa->actual;
+
+ switch (actual->dest.addr.sa.sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sock_in4 = (struct sockaddr_in *)local;
+#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST)
+ sock_in4->sin_addr = actual->pi.in4.ipi_addr;
+#elif defined(IP_RECVDSTADDR)
+ sock_in4->sin_addr = actual->pi.in4;
+#else
+ /* source IP not available on this platform */
+ return false;
+#endif
+ sock_in4->sin_family = AF_INET;
+ break;
+ }
+
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)local;
+ sock_in6->sin6_addr = actual->pi.in6.ipi6_addr;
+ sock_in6->sin6_family = AF_INET6;
+ break;
+ }
+
+ default:
+ ASSERT(false);
+ }
+
+ return true;
+#else /* if ENABLE_IP_PKTINFO */
+ return false;
+#endif /* if ENABLE_IP_PKTINFO */
+}
+
+int
+dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
+{
+ struct context *c = &mi->context;
+
+ int peer_id = mi->context.c2.tls_multi->peer_id;
+ struct sockaddr *remoteaddr, *localaddr = NULL;
+ struct sockaddr_storage local = { 0 };
+ int sd = c->c2.link_socket->sd;
+
+ if (c->mode == CM_CHILD_TCP)
+ {
+ /* the remote address will be inferred from the TCP socket endpoint */
+ remoteaddr = NULL;
+ }
+ else
+ {
+ ASSERT(c->c2.link_socket_info->connection_established);
+ remoteaddr = &c->c2.link_socket_info->lsa->actual.dest.addr.sa;
+ }
+
+ struct in_addr remote_ip4 = { 0 };
+ struct in6_addr *remote_addr6 = NULL;
+ struct in_addr *remote_addr4 = NULL;
+
+ /* In server mode we need to fetch the remote addresses from the push config */
+ if (c->c2.push_ifconfig_defined)
+ {
+ remote_ip4.s_addr = htonl(c->c2.push_ifconfig_local);
+ remote_addr4 = &remote_ip4;
+ }
+ if (c->c2.push_ifconfig_ipv6_defined)
+ {
+ remote_addr6 = &c->c2.push_ifconfig_ipv6_local;
+ }
+
+ if (dco_multi_get_localaddr(m, mi, &local))
+ {
+ localaddr = (struct sockaddr *)&local;
+ }
+
+ int ret = dco_new_peer(&c->c1.tuntap->dco, peer_id, sd, localaddr,
+ remoteaddr, remote_addr4, remote_addr6);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ c->c2.tls_multi->dco_peer_added = true;
+
+ if (c->mode == CM_CHILD_TCP)
+ {
+ multi_tcp_dereference_instance(m->mtcp, mi);
+ if (close(sd))
+ {
+ msg(D_DCO|M_ERRNO, "error closing TCP socket after DCO handover");
+ }
+ c->c2.link_socket->info.dco_installed = true;
+ c->c2.link_socket->sd = SOCKET_UNDEFINED;
+ }
+
+ return 0;
+}
+
+void
+dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr)
+{
+#if defined(TARGET_LINUX)
+ if (!dco_enabled(&m->top.options))
+ {
+ return;
+ }
+
+ int addrtype = (addr->type & MR_ADDR_MASK);
+
+ /* If we do not have local IP addr to install, skip the route */
+ if ((addrtype == MR_ADDR_IPV6 && !mi->context.c2.push_ifconfig_ipv6_defined)
+ || (addrtype == MR_ADDR_IPV4 && !mi->context.c2.push_ifconfig_defined))
+ {
+ return;
+ }
+
+ struct context *c = &mi->context;
+ const char *dev = c->c1.tuntap->actual_name;
+
+ if (addrtype == MR_ADDR_IPV6)
+ {
+ int netbits = 128;
+ if (addr->type & MR_WITH_NETBITS)
+ {
+ netbits = addr->netbits;
+ }
+
+ net_route_v6_add(&m->top.net_ctx, &addr->v6.addr, netbits,
+ &mi->context.c2.push_ifconfig_ipv6_local, dev, 0,
+ DCO_IROUTE_METRIC);
+ }
+ else if (addrtype == MR_ADDR_IPV4)
+ {
+ int netbits = 32;
+ if (addr->type & MR_WITH_NETBITS)
+ {
+ netbits = addr->netbits;
+ }
+
+ in_addr_t dest = htonl(addr->v4.addr);
+ net_route_v4_add(&m->top.net_ctx, &dest, netbits,
+ &mi->context.c2.push_ifconfig_local, dev, 0,
+ DCO_IROUTE_METRIC);
+ }
+#endif /* if defined(TARGET_LINUX) */
+}
+
+void
+dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+{
+#if defined(TARGET_LINUX)
+ if (!dco_enabled(&m->top.options))
+ {
+ return;
+ }
+ ASSERT(TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN);
+
+ struct context *c = &mi->context;
+ const char *dev = c->c1.tuntap->actual_name;
+
+ if (mi->context.c2.push_ifconfig_defined)
+ {
+ for (const struct iroute *ir = c->options.iroutes;
+ ir;
+ ir = ir->next)
+ {
+ net_route_v4_del(&m->top.net_ctx, &ir->network, ir->netbits,
+ &mi->context.c2.push_ifconfig_local, dev,
+ 0, DCO_IROUTE_METRIC);
+ }
+ }
+
+ if (mi->context.c2.push_ifconfig_ipv6_defined)
+ {
+ for (const struct iroute_ipv6 *ir6 = c->options.iroutes_ipv6;
+ ir6;
+ ir6 = ir6->next)
+ {
+ net_route_v6_del(&m->top.net_ctx, &ir6->network, ir6->netbits,
+ &mi->context.c2.push_ifconfig_ipv6_local, dev,
+ 0, DCO_IROUTE_METRIC);
+ }
+ }
+#endif /* if defined(TARGET_LINUX) */
+}
+
#endif /* defined(ENABLE_DCO) */
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 602dafb7..72569083 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -37,10 +37,14 @@
struct event_set;
struct key2;
struct key_state;
+struct multi_context;
+struct multi_instance;
+struct mroute_addr;
struct options;
struct tls_multi;
struct tuntap;
+#define DCO_IROUTE_METRIC 100
#define DCO_DEFAULT_METRIC 200
#if defined(ENABLE_DCO)
@@ -181,6 +185,34 @@ int dco_set_peer(dco_context_t *dco, unsigned int peerid,
*/
void dco_remove_peer(struct context *c);
+/**
+ * Install a new peer in DCO - to be called by a SERVER instance
+ *
+ * @param m the server context
+ * @param mi the client instance
+ * @return 0 on success or a negative error code otherwise
+ */
+int dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi);
+
+/**
+ * Install an iroute in DCO, which means adding a route to the system routing
+ * table. To be called by a SERVER instance only.
+ *
+ * @param m the server context
+ * @param mi the client instance acting as nexthop for the route
+ * @param addr the route to add
+ */
+void dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr);
+
+/**
+ * Remove all routes added through the specified client
+ *
+ * @param m the server context
+ * @param mi the client instance for which routes have to be removed
+ */
+void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -271,5 +303,22 @@ dco_remove_peer(struct context *c)
{
}
+static inline bool
+dco_multi_add_new_peer(struct multi_context *m, struct multi_instance *mi)
+{
+ return true;
+}
+
+static inline void
+dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
+ struct mroute_addr *addr)
+{
+}
+
+static inline void
+dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
+{
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/mtcp.c b/src/openvpn/mtcp.c
index b3c153fe..eb88a56a 100644
--- a/src/openvpn/mtcp.c
+++ b/src/openvpn/mtcp.c
@@ -61,6 +61,7 @@
#define MTCP_SIG ((void *)3) /* Only on Windows */
#define MTCP_MANAGEMENT ((void *)4)
#define MTCP_FILE_CLOSE_WRITE ((void *)5)
+#define MTCP_DCO ((void *)6)
#define MTCP_N ((void *)16) /* upper bound on MTCP_x */
@@ -131,6 +132,8 @@ multi_create_instance_tcp(struct multi_context *m)
const uint32_t hv = hash_value(hash, &mi->real);
struct hash_bucket *bucket = hash_bucket(hash, hv);
+ multi_assign_peer_id(m, mi);
+
he = hash_lookup_fast(hash, bucket, &mi->real, hv);
if (he)
@@ -238,6 +241,7 @@ multi_tcp_dereference_instance(struct multi_tcp *mtcp, struct multi_instance *mi
if (ls && mi->socket_set_called)
{
event_del(mtcp->es, socket_event_handle(ls));
+ mi->socket_set_called = false;
}
mtcp->n_esr = 0;
}
@@ -279,6 +283,9 @@ multi_tcp_wait(const struct context *c,
}
#endif
tun_set(c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, persistent);
+#if defined(TARGET_LINUX)
+ dco_event_set(&c->c1.tuntap->dco, mtcp->es, MTCP_DCO);
+#endif
#ifdef ENABLE_MANAGEMENT
if (management)
@@ -395,6 +402,18 @@ multi_tcp_wait_lite(struct multi_context *m, struct multi_instance *mi, const in
tv_clear(&c->c2.timeval); /* ZERO-TIMEOUT */
+ if (mi && mi->context.c2.link_socket->info.dco_installed)
+ {
+ /* If we got a socket that has been handed over to the kernel
+ * we must not call the normal socket function to figure out
+ * if it is readable or writable */
+ /* Assert that we only have the DCO exptected flags */
+ ASSERT(action & (TA_SOCKET_READ | TA_SOCKET_WRITE));
+
+ /* We are always ready! */
+ return action;
+ }
+
switch (action)
{
case TA_TUN_READ:
@@ -518,7 +537,10 @@ multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int
case TA_INITIAL:
ASSERT(mi);
- multi_tcp_set_global_rw_flags(m, mi);
+ if (!mi->context.c2.link_socket->info.dco_installed)
+ {
+ multi_tcp_set_global_rw_flags(m, mi);
+ }
multi_process_post(m, mi, mpp_flags);
break;
@@ -568,7 +590,10 @@ multi_tcp_post(struct multi_context *m, struct multi_instance *mi, const int act
}
else
{
- multi_tcp_set_global_rw_flags(m, mi);
+ if (!c->c2.link_socket->info.dco_installed)
+ {
+ multi_tcp_set_global_rw_flags(m, mi);
+ }
}
break;
@@ -625,23 +650,22 @@ multi_tcp_action(struct multi_context *m, struct multi_instance *mi, int action,
/*
* Dispatch the action
*/
- {
- struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
+ struct multi_instance *touched = multi_tcp_dispatch(m, mi, action);
- /*
- * Signal received or TCP connection
- * reset by peer?
- */
- if (touched && IS_SIG(&touched->context))
+ /*
+ * Signal received or TCP connection
+ * reset by peer?
+ */
+ if (touched && IS_SIG(&touched->context))
+ {
+ if (mi == touched)
{
- if (mi == touched)
- {
- mi = NULL;
- }
- multi_close_instance_on_signal(m, touched);
+ mi = NULL;
}
+ multi_close_instance_on_signal(m, touched);
}
+
/*
* If dispatch produced any pending output
* for a particular instance, point to
@@ -739,6 +763,13 @@ multi_tcp_process_io(struct multi_context *m)
multi_tcp_action(m, mi, TA_INITIAL, false);
}
}
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ /* incoming data on DCO? */
+ else if (e->arg == MTCP_DCO)
+ {
+ multi_process_incoming_dco(m);
+ }
+#endif
/* signal received? */
else if (e->arg == MTCP_SIG)
{
diff --git a/src/openvpn/mudp.c b/src/openvpn/mudp.c
index 0cbca1a9..ddb1efc9 100644
--- a/src/openvpn/mudp.c
+++ b/src/openvpn/mudp.c
@@ -381,6 +381,19 @@ multi_process_io_udp(struct multi_context *m)
multi_process_file_closed(m, mpp_flags);
}
#endif
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+ else if (status & DCO_READ)
+ {
+ if (!IS_SIG(&m->top))
+ {
+ bool ret = true;
+ while (ret)
+ {
+ ret = multi_process_incoming_dco(m);
+ }
+ }
+ }
+#endif
}
/*
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index c72575ae..47ef244c 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -51,6 +51,7 @@
#include "crypto_backend.h"
#include "ssl_util.h"
+#include "dco.h"
/*#define MULTI_DEBUG_EVENT_LOOP*/
@@ -519,6 +520,9 @@ multi_del_iroutes(struct multi_context *m,
{
const struct iroute *ir;
const struct iroute_ipv6 *ir6;
+
+ dco_delete_iroutes(m, mi);
+
if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
{
for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
@@ -1224,16 +1228,20 @@ multi_learn_in_addr_t(struct multi_context *m,
addr.netbits = (uint8_t) netbits;
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* We do not want to install IP -> IP dev ovpn-dco0 */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
static struct multi_instance *
@@ -1257,16 +1265,20 @@ multi_learn_in6_addr(struct multi_context *m,
mroute_addr_mask_host_bits( &addr );
}
- {
- struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
+ struct multi_instance *owner = multi_learn_addr(m, mi, &addr, 0);
#ifdef ENABLE_MANAGEMENT
- if (management && owner)
- {
- management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
- }
+ if (management && owner)
+ {
+ management_learn_addr(management, &mi->context.c2.mda_context, &addr, primary);
+ }
#endif
- return owner;
+ if (!primary)
+ {
+ /* We do not want to install IP -> IP dev ovpn-dco0 */
+ dco_install_iroute(m, mi, &addr);
}
+
+ return owner;
}
/*
@@ -1765,6 +1777,15 @@ multi_client_set_protocol_options(struct context *c)
tls_multi->use_peer_id = true;
o->use_peer_id = true;
}
+ else if (dco_enabled(o))
+ {
+ msg(M_INFO, "Client does not support DATA_V2. Data channel offloaing "
+ "requires DATA_V2. Dropping client.");
+ auth_set_client_reason(tls_multi, "Data channel negotiation "
+ "failed (missing DATA_V2)");
+ return false;
+ }
+
if (proto & IV_PROTO_REQUEST_PUSH)
{
c->c2.push_request_received = true;
@@ -2401,9 +2422,37 @@ multi_client_connect_late_setup(struct multi_context *m,
}
/* Generate data channel keys only if setting protocol options
* has not failed */
- else if (!multi_client_generate_tls_keys(&mi->context))
+ else
{
- mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ if (dco_enabled(&mi->context.options))
+ {
+ int ret = dco_multi_add_new_peer(m, mi);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot add peer to DCO: %s (%d)", strerror(-ret), ret);
+ mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ }
+
+ if (mi->context.options.ping_send_timeout || mi->context.c2.frame.mss_fix)
+ {
+ int ret = dco_set_peer(&mi->context.c1.tuntap->dco,
+ mi->context.c2.tls_multi->peer_id,
+ mi->context.options.ping_send_timeout,
+ mi->context.options.ping_rec_timeout,
+ mi->context.c2.frame.mss_fix);
+ if (ret < 0)
+ {
+ msg(D_DCO, "Cannot set parameters for DCO peer (id=%u): %s",
+ mi->context.c2.tls_multi->peer_id, strerror(-ret));
+ mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ }
+ }
+ }
+
+ if (!multi_client_generate_tls_keys(&mi->context))
+ {
+ mi->context.c2.tls_multi->multi_state = CAS_FAILED;
+ }
}
/* send push reply if ready */
@@ -2661,6 +2710,14 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
(*cur_handler_index)++;
}
+ /* Check if we have forbidding options in the current mode */
+ if (dco_enabled(&mi->context.options)
+ && !dco_check_option_conflict(D_MULTI_ERRORS, &mi->context.options))
+ {
+ msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to incompatible DCO options");
+ cc_succeeded = false;
+ }
+
if (cc_succeeded)
{
multi_client_connect_late_setup(m, mi, *option_types_found);
@@ -3079,6 +3136,124 @@ done:
gc_free(&gc);
}
+/*
+ * Called when an instance should be closed due to the
+ * reception of a soft signal.
+ */
+void
+multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
+{
+ remap_signal(&mi->context);
+ set_prefix(mi);
+ print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
+ clear_prefix();
+ multi_close_instance(m, mi, false);
+}
+
+#if (defined(ENABLE_DCO) && defined(TARGET_LINUX)) || defined(ENABLE_MANAGEMENT)
+static void
+multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
+{
+ mi->context.sig->signal_received = sig;
+ multi_close_instance_on_signal(m, mi);
+}
+#endif
+
+#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
+static void
+process_incoming_dco_packet(struct multi_context *m, struct multi_instance *mi,
+ dco_context_t *dco)
+{
+ if (BLEN(&dco->dco_packet_in) < 1)
+ {
+ msg(D_DCO, "Received too short packet for peer %d",
+ dco->dco_message_peer_id);
+ goto done;
+ }
+
+ uint8_t *ptr = BPTR(&dco->dco_packet_in);
+ uint8_t op = ptr[0] >> P_OPCODE_SHIFT;
+ if ((op == P_DATA_V1) || (op == P_DATA_V2))
+ {
+ msg(D_DCO, "DCO: received data channel packet for peer %d",
+ dco->dco_message_peer_id);
+ goto done;
+ }
+
+ struct buffer orig_buf = mi->context.c2.buf;
+ mi->context.c2.buf = dco->dco_packet_in;
+
+ multi_process_incoming_link(m, mi, 0);
+
+ mi->context.c2.buf = orig_buf;
+
+done:
+ buf_init(&dco->dco_packet_in, 0);
+}
+
+static void
+process_incoming_del_peer(struct multi_context *m, struct multi_instance *mi,
+ dco_context_t *dco)
+{
+ const char *reason = "ovpn-dco: unknown reason";
+ switch (dco->dco_del_peer_reason)
+ {
+ case OVPN_DEL_PEER_REASON_EXPIRED:
+ reason = "ovpn-dco: ping expired";
+ break;
+
+ case OVPN_DEL_PEER_REASON_TRANSPORT_ERROR:
+ reason = "ovpn-dco: transport error";
+ break;
+
+ case OVPN_DEL_PEER_REASON_USERSPACE:
+ /* This very likely ourselves but might be another process, so
+ * still process it */
+ reason = "ovpn-dco: userspace request";
+ break;
+ }
+
+ /* When kernel already deleted the peer, the socket is no longer
+ * installed and we don't need to cleanup the state in the kernel */
+ mi->context.c2.tls_multi->dco_peer_added = false;
+ mi->context.sig->signal_text = reason;
+ multi_signal_instance(m, mi, SIGTERM);
+}
+
+bool
+multi_process_incoming_dco(struct multi_context *m)
+{
+ dco_context_t *dco = &m->top.c1.tuntap->dco;
+
+ struct multi_instance *mi = NULL;
+
+ int ret = dco_do_read(&m->top.c1.tuntap->dco);
+
+ int peer_id = dco->dco_message_peer_id;
+
+ if ((peer_id >= 0) && (peer_id < m->max_clients) && (m->instances[peer_id]))
+ {
+ mi = m->instances[peer_id];
+ if (dco->dco_message_type == OVPN_CMD_PACKET)
+ {
+ process_incoming_dco_packet(m, mi, dco);
+ }
+ else if (dco->dco_message_type == OVPN_CMD_DEL_PEER)
+ {
+ process_incoming_del_peer(m, mi, dco);
+ }
+ }
+ else
+ {
+ msg(D_DCO, "Received packet for peer-id unknown to OpenVPN: %d", peer_id);
+ }
+
+ dco->dco_message_type = 0;
+ dco->dco_message_peer_id = -1;
+ return ret > 0;
+}
+#endif /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
+
/*
* Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
* i.e. client -> server direction.
@@ -3640,32 +3815,11 @@ multi_process_signal(struct multi_context *m)
return true;
}
-/*
- * Called when an instance should be closed due to the
- * reception of a soft signal.
- */
-void
-multi_close_instance_on_signal(struct multi_context *m, struct multi_instance *mi)
-{
- remap_signal(&mi->context);
- set_prefix(mi);
- print_signal(mi->context.sig, "client-instance", D_MULTI_LOW);
- clear_prefix();
- multi_close_instance(m, mi, false);
-}
-
/*
* Management subsystem callbacks
*/
#ifdef ENABLE_MANAGEMENT
-static void
-multi_signal_instance(struct multi_context *m, struct multi_instance *mi, const int sig)
-{
- mi->context.sig->signal_received = sig;
- multi_close_instance_on_signal(m, mi);
-}
-
static void
management_callback_status(void *arg, const int version, struct status_output *so)
{
@@ -3755,10 +3909,6 @@ management_delete_event(void *arg, event_t event)
}
}
-#endif /* ifdef ENABLE_MANAGEMENT */
-
-#ifdef ENABLE_MANAGEMENT
-
static struct multi_instance *
lookup_by_cid(struct multi_context *m, const unsigned long cid)
{
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index f1e9ab91..370d795c 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -98,7 +98,9 @@ struct client_connect_defer_state
* server-mode.
*/
struct multi_instance {
- struct schedule_entry se; /* this must be the first element of the structure */
+ struct schedule_entry se; /* this must be the first element of the structure,
+ * We cast between this and schedule_entry so the
+ * beginning of the struct must be identical */
struct gc_arena gc;
bool halt;
int refcount;
@@ -310,6 +312,16 @@ void multi_process_float(struct multi_context *m, struct multi_instance *mi);
*/
bool multi_process_post(struct multi_context *m, struct multi_instance *mi, const unsigned int flags);
+/**
+ * Process an incoming DCO message (from kernel space).
+ *
+ * @param m - The single \c multi_context structur.e
+ *
+ * @return
+ * - True, if the message was received correctly.
+ * - False, if there was an error while reading the message.
+ */
+bool multi_process_incoming_dco(struct multi_context *m);
/**************************************************************************/
/**
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH applied] Re: dco: implement dco support for p2mp/server code path
2022-08-05 6:45 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
@ 2022-08-05 14:55 ` Gert Doering
0 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-05 14:55 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Acked-by: Gert Doering <gert@...1296...>
v2 has an ACK from Heiko, so recording that. OTOH v3 is substantially
different (the dco.c hunk was missing from v1+v2 - hidden in 13/25 v1 -
and the multi.c tls_keys stuff is quite different), so I gave this my own
stare-at-code, and of course the full test set.
The findings are lengthy, the followup work is non-trivial, but we have
come a long way.
- stare-at-code
- the new additions to dco.c all make sense - the iroute thing, we discussed
a lot beforehand, and the variants (sockaddr/inaddr) in local/remote
address are due to "kernel wants them ready-to-use in target format".
Not sure if we want to consider the "!ENABLE_IP_PKTINFO" case here,
ever, though... DCO requires a recent Linux (or FreeBSD) system,
and those have them. Followup patch, though.
*Naming* some of these bits "remoteaddr" and "remote_addr*" is a bit
unlucky, though... I do understand that "remoteaddr" is the remote
end of the (UDP) socket, and "remote_addr*" is the inside ifconfig
IP for the tunnel peer - but I think this could be made more clear.
The code bits
+ int netbits = 128;
+ if (addr->type & MR_WITH_NETBITS)
+ {
+ netbits = addr->netbits;
+ }
are ugly, and take too much code space. I think we need to fix this,
as in, ensure that addr->netbits is always set, and for "host route
things", MR_WITH_NETBITS would just control whether ".../bits" is
used on printing, not for lookup. Maybe the code already does that,
but we don't know. Another followup patch...
I noticed that we don't do error handling on the iroute installation
- but that is traditional OpenVPN behaviour, for all sorts of route
additions. We log, but we never fail...
- in mtcp.c, there is a new "mi->socket_set_called = false" call,
which seems to be unrelated to DCO, just cleaning up references
(but it looks reasonable).
- there is some nesting cleanup (-> show -w) around mtcp.c line 625
- there is more nesting cleanup in multi.c / multi_learn_in_addr_t()
(but that code is changed anyway, so it makes sense)
The actual changes here are needed for "real route" iroute handling,
and have been discussed and agreed-on months ago already.
- as agreed on IRC, I changed the comment
/* We do not want to install IP -> IP dev ovpn-dco0 */
to
/* "primary" is the VPN ifconfig address of the peer and already
* known to DCO, so only install "extra" iroutes (primary = false)
*/
- I need to test connecting from a 2.3 client to a DCO-enabled
server, to excercise the "Client does not support DATA_V2..." path
(but it looks good).
- I also need to test if "on a DCO enabled server, a ccd/ script
produces something which is not allowed for DCO" (like, cipher BF-CBC
etc.) gets handled correctly. The code is there now (multi.c, around
line 2710).
- multi_close_instance_on_signal() and multi_signal_instance() just
move around (--color-moved=zebra) [#ifdef ENABLE_MANAGEMENT]
- FreeBSD client test (no DCO whatsoever yet)
(everything passes, unsurprisingly)
- Linux client test, --enable-dco, no kernel DCO
(everything passes, unsurprisingly, not hitting these new code paths)
- Linux server test, no --enable-dco
(everything passes, and this is good - so the new code paths in multi.c
do not break existing functionality and "known corner cases")
- Linux client test, --enable-dco, kernel DCO
(everything except 2c, 2d, 2f passes - known kernel issue, unchanged
from before)
- Linux server test, --enable-dco, kernel DCO
- many of my server test instances had DCO incompatible options in there
(like, comp-lzo, or just being TAP instances)
Note: cipher 'BF-CBC' in --data-ciphers is not supported by ovpn-dco, disabling data channel offload.
Note: dev-type not tun, disabling data channel offload.
Note: NOT using '--topology subnet' disables data channel offload.
Note: --fragment disables data channel offload.
Note: Using compression disables data channel offload.
Note: dev-type not tun, disabling data channel offload.
NOTE: the wording should be made more similar (disabling/disables),
I'd say.
- clients connecting will produce this very confusing sequence of
messages...:
Aug 5 16:27:33 ubuntu2004 tun-udp-p2mp[1834587]: freebsd-74-amd64/194.97.140.3:65038 OPTIONS IMPORT: reading client specific options from: ccd/freebsd-74-amd64
Aug 5 16:27:33 ubuntu2004 tun-udp-p2mp[1834587]: freebsd-74-amd64/194.97.140.3:65038 OPTIONS IMPORT: Server did not request DATA_V2 packet format required for data channel offload
Aug 5 16:27:33 ubuntu2004 tun-udp-p2mp[1834587]: freebsd-74-amd64/194.97.140.3:65038 OPTIONS ERROR: pushed options are incompatible with data channel offload. Use --disable-dco to connect to this server
(we are the server, so what did "the Server" do wrong, and we are not
trying to connect to "this server" either???)
... but proceeds!
Aug 5 16:27:33 ubuntu2004 tun-udp-p2mp[1834587]: freebsd-74-amd64/194.97.140.3:65038 MULTI: Learn: 10.220.2.8 -> freebsd-74-amd64/194.97.140.3:65038
...
Aug 5 16:27:33 ubuntu2004 tun-udp-p2mp[1834587]: freebsd-74-amd64/194.97.140.3:65038 Data Channel: using negotiated cipher 'AES-256-GCM'
...
Aug 5 16:27:36 ubuntu2004 tun-udp-p2mp[1834587]: freebsd-74-amd64/194.97.140.3:65038 SENT CONTROL [freebsd-74-amd64]: 'PUSH_REPLY,route 10.220.0.0 255.255.0.0,route-ipv6 fd00:abcd:220::/48,tun-ipv6,route-gateway 10.220.2.1,topology subnet,ping 10,ping-restart 30,compress stub-v2,ifconfig-ipv6 fd00:abcd:220:2::1006/64 fd00:abcd:220:2::1,ifconfig 10.220.2.8 255.255.255.0,peer-id 0,cipher AES-256-GCM' (status=1)
... and pinging 10.220.2.8 works just fine.
The mentioning of ccd/ here is a red herring, if I move away the
ccd/ file, I get the same complaints about DATA_V2.
Seems we shuffled around the option checking code a bit too often
(this is the no-longer _part2() bits, dco_check_pull_options(),
I'm afraid, so, yeah, blame me).
- the udp p2p instance wants to send something "from userland" right after
starting (before a peer is connected, no --remote) - this used to trigger
an ASSERT(), now it just logs this:
ubuntu2004 tun-udp-p2p[1834288]: Attempting to send data packet while data channel offload is in use. Dropping packet
- connecting and disconnecting in rapid sequence (t_client tests on the
other end) triggers these messages
Aug 5 16:38:31 ubuntu2004 tun-tcp-p2mp[1834574]: Received packet for peer-id unknown to OpenVPN: 1
Aug 5 16:38:31 ubuntu2004 tun-udp-p2p-tls-sha256[1834670]: ovpn-dco: received message type 3 with mismatched ifindex 8966
Aug 5 16:38:31 ubuntu2004 tun-udp-p2mp[1834587]: ovpn-dco: received message type 3 with mismatched ifindex 8966
which is interesting, because I am only connecting to one of them at
a time - so if there are multiple DCO interfaces active, messages
seems to bleed over.
- for connections "UDP over IPv6", we bump into the same issue that
we've seen on the p2p DCO test - that is, double-fragmented packets
trigger "packet is drop and extended socket error"
Aug 5 16:42:53 ubuntu2004 tun-udp-p2mp[1834587]: read UDPv6 [EMSGSIZE Path-MTU=1500]: Message too long (fd=7,code=90)
- these issues aside, a good number of tests worked :-) - some that
still fail are stuff like "cipher none" that need adjustment of the
testbed.
Test sets succeeded: 1 1a 1b 1c 1d 1e 1x 2a 2b 2d 2e 2w 2z1 2z2 3 4 4a 4b 5 5a 5b 5c 5d 5v1 5v2 5v3 5w1 5w2 5w3 5w4 5x1 5x2 5x3 5x4 6 9 9a 10 10a 10u 10v 10w 10x 10z.
Test sets failed: 2 2c 2g 2f 2x 2y.
I'll followup with "why is each of them failing, and is this a problem
of (Linux) DCO or not?".
Your patch has been applied to the master branch.
commit a5b4bad46978a01162fb820ea25594d6333aa9db
Author: Antonio Quartulli
Date: Fri Aug 5 08:45:55 2022 +0200
dco: implement dco support for p2mp/server code path
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Heiko Hund <heiko@...2662...>
Acked-by: Gert Doering <gert@...1296...>
Message-Id: <20220805064555.13385-1-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24811.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (13 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 14/25] dco: implement dco support for p2mp/server " Antonio Quartulli
@ 2022-06-24 8:37 ` Antonio Quartulli
2022-07-04 8:29 ` Frank Lichtenheld
` (2 more replies)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 16/25] GitHub Actions: add Linux DCO build (on Ubuntu 20.04) Antonio Quartulli
` (9 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes.rst | 9 ++
README.dco.md | 123 ++++++++++++++++++++++++++
doc/man-sections/advanced-options.rst | 13 +++
doc/man-sections/server-options.rst | 6 ++
4 files changed, 151 insertions(+)
create mode 100644 README.dco.md
diff --git a/Changes.rst b/Changes.rst
index 67a23c79..275f8d64 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -79,6 +79,15 @@ Cookie based handshake for UDP server
shake. The tls-crypt-v2 option allows controlling if older clients are
accepted.
+Data channel offloading with ovpn-dco
+ 2.6.0+ implements support for data-channel offloading where the data packets
+ are directly processed and forwarded in kernel space thanks to the ovpn-dco
+ kernel module. The userspace openvpn program acts purely as a control plane
+ application. Note that DCO will use DATA_V2 packets in P2P mode, therefore,
+ this implies that peers must be running 2.6.0+ in order to have P2P-NCP
+ which brings DATA_V2 packet support.
+
+
Deprecated features
-------------------
``inetd`` has been removed
diff --git a/README.dco.md b/README.dco.md
new file mode 100644
index 00000000..e73e0fc2
--- /dev/null
+++ b/README.dco.md
@@ -0,0 +1,123 @@
+OpenVPN data channel offload
+============================
+2.6.0+ implements support for data-channel offloading where the data packets
+are directly processed and forwarded in kernel space thanks to the ovpn-dco
+kernel module. The userspace openvpn program acts purely as a control plane
+application.
+
+
+Overview of current release
+---------------------------
+- See the "Limitations by design" and "Current limitations" sections for
+ features that are not and/or will not be supported by OpenVPN + ovpn-dco
+
+
+Getting started (Linux)
+-----------------------
+
+- Use a recent Linux kernel. Linux 5.4.0 and newer are known to work with
+ ovpn-dco.
+
+Get the ovpn-dco module from one these urls and build it:
+
+* https://gitlab.com/openvpn/ovpn-dco
+* https://github.com/OpenVPN/ovpn-dco
+
+e.g.
+
+ git clone https://github.com/OpenVPN/ovpn-dco
+ cd ovpn-dco
+ make
+ sudo make install
+
+If you want to report bugs please ensure to compile ovpn-dco with
+`make DEBUG=1` and include any debug message being printed by the
+kernel (you can view those messages with `dmesg`).
+
+Clone OpenVPN and build dco branch. For example:
+
+ git clone -b dco https://github.com/openvpn/openvpn.git
+ cd openvpn
+ autoreconf -vi
+ ./configure --enable-dco
+ make
+ sudo make install # Or run just src/openvpn/openvpn
+
+If you start openvpn it should automatically detect DCO support and use the
+kernel module. Add the option `--disable-dco` to disable data channel offload
+support. If the configuration contains an option that is incompatible with
+data channel offloading OpenVPN will automatically disable DCO support and
+warn the user.
+
+Should OpenVPN be configured to use a feature that is not supported by ovpn-dco
+or should the ovpn-dco kernel module not be available on the system, you will
+see a message like
+
+ Note: Kernel support for ovpn-dco missing, disabling data channel offload.
+
+in your log.
+
+
+DCO and P2P mode
+----------------
+DCO is also available when running OpenVPN in P2P mode without --pull/--client option.
+The P2P mode is useful for scenarios when the OpenVPN tunnel should not interfere with
+overall routing and behave more like a "dumb" tunnel like GRE.
+
+However, DCO requires DATA_V2 to be enabled. This requires P2P with NCP capability, which
+is only available in OpenVPN 2.6 and later.
+
+OpenVPN prints a diagnostic message for the P2P NCP result when running in P2P mode:
+
+ P2P mode NCP negotiation result: TLS_export=1, DATA_v2=1, peer-id 9484735, cipher=AES-256-GCM
+
+Double check that your have `DATA_v2=1` in your output and a supported AEAD cipher
+(AES-XXX-GCM or CHACHA20POLY1305).
+
+
+Routing with ovpn-dco
+---------------------
+The ovpn-dco kernel module implements a more transparent approach to
+configuring routes to clients (aka 'iroutes') and consults the kernel
+routing tables for forwarding decisions.
+
+- Each client has an IPv4 and/or an IPv6 VPN IP assigned to it.
+- Additional IP ranges can be routed to a client by adding a route with
+ a client VPN IP as the gateway/nexthop (i.e. ip route add a.b.c.d/24 via $VPNIP).
+- Due to the point above, there is no real need to add a companion --route for
+ each --iroute directive, unless you want to blackhole traffic when the specific
+ client is not connected.
+- No internal routing is available. If you need truly internal routes, this can be
+ achieved either with filtering using `iptables` or using `ip rule`.
+- client-to-client behaviour, as implemented in userspace, does not exist: packets
+ always reach the tunnel interface and are then re-routed to the destination peer
+ based on the system routing table.
+
+
+Limitations by design
+----------------------
+- Layer 3 (dev tun only)
+- only AEAD ciphers are supported and currently only
+ Chacha20-Poly1305 and AES-GCM-128/192/256
+- no support for compression or compression framing
+ - see also `--compress migrate` option to move to a setup without compression
+- various features not implemented since they have better replacements
+ - --shaper, use tc instead
+ - packet manipulation, use nftables/iptables instead
+- OpenVPN 2.4.0 is the minimum peer version.
+ - older versions are missing support for the AEAD ciphers
+- topology subnet is the only supported `--topology` for servers
+- iroute directives install routes on the host operating system, see also
+ Routing with ovpn-dco
+
+
+Current implementation limitations
+-------------------
+- --persistent-tun not tested/supported
+- fallback to non-dco in client mode missing
+- IPv6 mapped IPv4 addresses need Linux 5.4.189+/5.10.110+/5.12+ to work
+- Some incompatible options may not properly fallback to non-dco
+- TCP performance with ovpn-dco can still exhibit bad behaviour and drop to a
+ few megabits per seconds
+- Not all incompatible options are currently identified
+- No per client statistics. Only total statistics available on the interface
diff --git a/doc/man-sections/advanced-options.rst b/doc/man-sections/advanced-options.rst
index 5157c561..d5a6b4f2 100644
--- a/doc/man-sections/advanced-options.rst
+++ b/doc/man-sections/advanced-options.rst
@@ -91,3 +91,16 @@ used when debugging or testing out special usage scenarios.
*(Linux only)* Set the TX queue length on the TUN/TAP interface.
Currently defaults to operating system default.
+--disable-dco
+ Disables the opportunistic use of data channel offloading if available.
+ Without this option, OpenVPN will opportunistically use DCO mode if
+ the config options and the running kernel supports using DCO.
+
+ Data channel offload currently requires data-ciphers to only contain
+ AEAD ciphers (AES-GCM and Chacha20-Poly1305) and Linux with the
+ ovpn-dco module.
+
+ Note that some options have no effect or cannot be used when DCO mode
+ is enabled.
+
+ On platforms that do not support DCO ``disable-dco`` has no effect.
diff --git a/doc/man-sections/server-options.rst b/doc/man-sections/server-options.rst
index 08ee7bd3..31992732 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -321,6 +321,12 @@ fast hardware. SSL/TLS authentication must be used in this mode.
from the kernel to OpenVPN. Once in OpenVPN, the ``--iroute`` directive
routes to the specific client.
+ However, when using DCO, the ``--iroute`` directive is usually enough
+ for DCO to fully configure the routing table. The extra ``--route``
+ directive is required only if the expected behaviour is to route the
+ traffic for a specific network to the VPN interface also when the
+ responsible client is not connected (traffic will then be dropped).
+
This option must be specified either in a client instance config file
using ``--client-config-dir`` or dynamically generated using a
``--client-connect`` script.
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux Antonio Quartulli
@ 2022-07-04 8:29 ` Frank Lichtenheld
2022-07-04 8:33 ` Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
2022-08-05 6:58 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Frank Lichtenheld @ 2022-07-04 8:29 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
On Fri, Jun 24, 2022 at 10:37:59AM +0200, Antonio Quartulli wrote:
[...]
> +Current implementation limitations
> +-------------------
> +- --persistent-tun not tested/supported
> +- fallback to non-dco in client mode missing
> +- IPv6 mapped IPv4 addresses need Linux 5.4.189+/5.10.110+/5.12+ to work
> +- Some incompatible options may not properly fallback to non-dco
> +- TCP performance with ovpn-dco can still exhibit bad behaviour and drop to a
> + few megabits per seconds
Any way of qualifying this? Should one not use TCP mode at all currently? Or is
this rare? Any workarounds?
> +- Not all incompatible options are currently identified
> +- No per client statistics. Only total statistics available on the interface
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux
2022-07-04 8:29 ` Frank Lichtenheld
@ 2022-07-04 8:33 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-04 8:33 UTC (permalink / raw)
To: Frank Lichtenheld <frank@; +Cc: openvpn-devel
Hi,
On 04/07/2022 10:29, Frank Lichtenheld wrote:
> On Fri, Jun 24, 2022 at 10:37:59AM +0200, Antonio Quartulli wrote:
> [...]
>> +Current implementation limitations
>> +-------------------
>> +- --persistent-tun not tested/supported
>> +- fallback to non-dco in client mode missing
>> +- IPv6 mapped IPv4 addresses need Linux 5.4.189+/5.10.110+/5.12+ to work
>> +- Some incompatible options may not properly fallback to non-dco
>> +- TCP performance with ovpn-dco can still exhibit bad behaviour and drop to a
>> + few megabits per seconds
>
> Any way of qualifying this? Should one not use TCP mode at all currently? Or is
> this rare? Any workarounds?
Good point. It was fairly rare, but we never dug in too much.
However, no extra tests were performed after ovpn-dco got quite
refactored, so the problem may have gone.
More tests would be needed to confirm the issue.
Cheers,
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux Antonio Quartulli
2022-07-04 8:29 ` Frank Lichtenheld
@ 2022-07-05 12:31 ` Heiko Hund
2022-07-05 14:09 ` Antonio Quartulli
2022-08-05 6:58 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:31 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:37:59 CEST Antonio Quartulli wrote:
> + application. Note that DCO will use DATA_V2 packets
> in P2P mode, therefore,
> + this implies that peers must be running 2.6.0+
> in order to have P2P-NCP
> + which brings DATA_V2 packet support.
[...]
> +- OpenVPN 2.4.0 is the minimum peer version.
Now I'm not sure what the minimum version is. Maybe clarify.
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux
2022-07-05 12:31 ` Heiko Hund
@ 2022-07-05 14:09 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-05 14:09 UTC (permalink / raw)
To: Heiko Hund <heiko@
Hi,
On 05/07/2022 14:31, Heiko Hund wrote:
> On Freitag, 24. Juni 2022 10:37:59 CEST Antonio Quartulli wrote:
>> + application. Note that DCO will use DATA_V2 packets
>> in P2P mode, therefore,
>> + this implies that peers must be running 2.6.0+
>> in order to have P2P-NCP
>> + which brings DATA_V2 packet support.
> [...]
>> +- OpenVPN 2.4.0 is the minimum peer version.
>
> Now I'm not sure what the minimum version is. Maybe clarify.
I think you removed the context which should clarify this.
2.6.0 is required for P2P-NCP
2.4.0 is required for standard client/server mode
Cheers,
>
>
>
>
>
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 15/25] dco: add documentation for ovpn-dco-linux
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux Antonio Quartulli
2022-07-04 8:29 ` Frank Lichtenheld
2022-07-05 12:31 ` Heiko Hund
@ 2022-08-05 6:58 ` Antonio Quartulli
2022-08-05 8:12 ` Frank Lichtenheld
2022-08-05 9:37 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
2 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-05 6:58 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* removed text about TCP drop (cannot reproduce at the moment)
* clarified version requirements for p2p and client mode
---
Changes.rst | 9 ++
README.dco.md | 121 ++++++++++++++++++++++++++
doc/man-sections/advanced-options.rst | 13 +++
doc/man-sections/server-options.rst | 6 ++
4 files changed, 149 insertions(+)
create mode 100644 README.dco.md
diff --git a/Changes.rst b/Changes.rst
index 67a23c79..275f8d64 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -79,6 +79,15 @@ Cookie based handshake for UDP server
shake. The tls-crypt-v2 option allows controlling if older clients are
accepted.
+Data channel offloading with ovpn-dco
+ 2.6.0+ implements support for data-channel offloading where the data packets
+ are directly processed and forwarded in kernel space thanks to the ovpn-dco
+ kernel module. The userspace openvpn program acts purely as a control plane
+ application. Note that DCO will use DATA_V2 packets in P2P mode, therefore,
+ this implies that peers must be running 2.6.0+ in order to have P2P-NCP
+ which brings DATA_V2 packet support.
+
+
Deprecated features
-------------------
``inetd`` has been removed
diff --git a/README.dco.md b/README.dco.md
new file mode 100644
index 00000000..a454ef3f
--- /dev/null
+++ b/README.dco.md
@@ -0,0 +1,121 @@
+OpenVPN data channel offload
+============================
+2.6.0+ implements support for data-channel offloading where the data packets
+are directly processed and forwarded in kernel space thanks to the ovpn-dco
+kernel module. The userspace openvpn program acts purely as a control plane
+application.
+
+
+Overview of current release
+---------------------------
+- See the "Limitations by design" and "Current limitations" sections for
+ features that are not and/or will not be supported by OpenVPN + ovpn-dco
+
+
+Getting started (Linux)
+-----------------------
+
+- Use a recent Linux kernel. Linux 5.4.0 and newer are known to work with
+ ovpn-dco.
+
+Get the ovpn-dco module from one these urls and build it:
+
+* https://gitlab.com/openvpn/ovpn-dco
+* https://github.com/OpenVPN/ovpn-dco
+
+e.g.
+
+ git clone https://github.com/OpenVPN/ovpn-dco
+ cd ovpn-dco
+ make
+ sudo make install
+
+If you want to report bugs please ensure to compile ovpn-dco with
+`make DEBUG=1` and include any debug message being printed by the
+kernel (you can view those messages with `dmesg`).
+
+Clone OpenVPN and build dco branch. For example:
+
+ git clone -b dco https://github.com/openvpn/openvpn.git
+ cd openvpn
+ autoreconf -vi
+ ./configure --enable-dco
+ make
+ sudo make install # Or run just src/openvpn/openvpn
+
+If you start openvpn it should automatically detect DCO support and use the
+kernel module. Add the option `--disable-dco` to disable data channel offload
+support. If the configuration contains an option that is incompatible with
+data channel offloading OpenVPN will automatically disable DCO support and
+warn the user.
+
+Should OpenVPN be configured to use a feature that is not supported by ovpn-dco
+or should the ovpn-dco kernel module not be available on the system, you will
+see a message like
+
+ Note: Kernel support for ovpn-dco missing, disabling data channel offload.
+
+in your log.
+
+
+DCO and P2P mode
+----------------
+DCO is also available when running OpenVPN in P2P mode without --pull/--client option.
+The P2P mode is useful for scenarios when the OpenVPN tunnel should not interfere with
+overall routing and behave more like a "dumb" tunnel like GRE.
+
+However, DCO requires DATA_V2 to be enabled, which is available for P2P mode only in
+OpenVPN 2.6 and later.
+
+OpenVPN prints a diagnostic message for the P2P NCP result when running in P2P mode:
+
+ P2P mode NCP negotiation result: TLS_export=1, DATA_v2=1, peer-id 9484735, cipher=AES-256-GCM
+
+Double check that your have `DATA_v2=1` in your output and a supported AEAD cipher
+(AES-XXX-GCM or CHACHA20POLY1305).
+
+
+Routing with ovpn-dco
+---------------------
+The ovpn-dco kernel module implements a more transparent approach to
+configuring routes to clients (aka 'iroutes') and consults the kernel
+routing tables for forwarding decisions.
+
+- Each client has an IPv4 and/or an IPv6 VPN IP assigned to it.
+- Additional IP ranges can be routed to a client by adding a route with
+ a client VPN IP as the gateway/nexthop (i.e. ip route add a.b.c.d/24 via $VPNIP).
+- Due to the point above, there is no real need to add a companion --route for
+ each --iroute directive, unless you want to blackhole traffic when the specific
+ client is not connected.
+- No internal routing is available. If you need truly internal routes, this can be
+ achieved either with filtering using `iptables` or using `ip rule`.
+- client-to-client behaviour, as implemented in userspace, does not exist: packets
+ always reach the tunnel interface and are then re-routed to the destination peer
+ based on the system routing table.
+
+
+Limitations by design
+----------------------
+- Layer 3 (dev tun only)
+- only AEAD ciphers are supported and currently only
+ Chacha20-Poly1305 and AES-GCM-128/192/256
+- no support for compression or compression framing
+ - see also `--compress migrate` option to move to a setup without compression
+- various features not implemented since they have better replacements
+ - --shaper, use tc instead
+ - packet manipulation, use nftables/iptables instead
+- OpenVPN 2.4.0 is the minimum version required for peers to connect
+ - older versions are missing support for the AEAD ciphers
+- topology subnet is the only supported `--topology` for servers
+- iroute directives install routes on the host operating system, see also
+ Routing with ovpn-dco
+
+
+Current implementation limitations
+-------------------
+- --persistent-tun not tested/supported
+- fallback to non-dco in client mode missing
+- IPv6 mapped IPv4 addresses need Linux 5.4.189+/5.10.110+/5.12+ to work
+- Some incompatible options may not properly fallback to non-dco
+- Not all incompatible options are currently identified
+- No per client statistics. Only total statistics available on the interface
diff --git a/doc/man-sections/advanced-options.rst b/doc/man-sections/advanced-options.rst
index 5157c561..d5a6b4f2 100644
--- a/doc/man-sections/advanced-options.rst
+++ b/doc/man-sections/advanced-options.rst
@@ -91,3 +91,16 @@ used when debugging or testing out special usage scenarios.
*(Linux only)* Set the TX queue length on the TUN/TAP interface.
Currently defaults to operating system default.
+--disable-dco
+ Disables the opportunistic use of data channel offloading if available.
+ Without this option, OpenVPN will opportunistically use DCO mode if
+ the config options and the running kernel supports using DCO.
+
+ Data channel offload currently requires data-ciphers to only contain
+ AEAD ciphers (AES-GCM and Chacha20-Poly1305) and Linux with the
+ ovpn-dco module.
+
+ Note that some options have no effect or cannot be used when DCO mode
+ is enabled.
+
+ On platforms that do not support DCO ``disable-dco`` has no effect.
diff --git a/doc/man-sections/server-options.rst b/doc/man-sections/server-options.rst
index 04f4b4fb..54ea8b66 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -325,6 +325,12 @@ fast hardware. SSL/TLS authentication must be used in this mode.
from the kernel to OpenVPN. Once in OpenVPN, the ``--iroute`` directive
routes to the specific client.
+ However, when using DCO, the ``--iroute`` directive is usually enough
+ for DCO to fully configure the routing table. The extra ``--route``
+ directive is required only if the expected behaviour is to route the
+ traffic for a specific network to the VPN interface also when the
+ responsible client is not connected (traffic will then be dropped).
+
This option must be specified either in a client instance config file
using ``--client-config-dir`` or dynamically generated using a
``--client-connect`` script.
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 15/25] dco: add documentation for ovpn-dco-linux
2022-08-05 6:58 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
@ 2022-08-05 8:12 ` Frank Lichtenheld
2022-08-05 9:37 ` [Openvpn-devel] [PATCH v3 " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Frank Lichtenheld @ 2022-08-05 8:12 UTC (permalink / raw)
To: openvpn-devel
On Fri, Aug 05, 2022 at 08:58:44AM +0200, Antonio Quartulli wrote:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
>
> Changes from v1:
> * removed text about TCP drop (cannot reproduce at the moment)
> * clarified version requirements for p2p and client mode
> ---
> Changes.rst | 9 ++
> README.dco.md | 121 ++++++++++++++++++++++++++
> doc/man-sections/advanced-options.rst | 13 +++
> doc/man-sections/server-options.rst | 6 ++
> 4 files changed, 149 insertions(+)
> create mode 100644 README.dco.md
Acked-By: Frank Lichtenheld <frank@...2641...>
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v3 15/25] dco: add documentation for ovpn-dco-linux
2022-08-05 6:58 ` [Openvpn-devel] [PATCH v2 " Antonio Quartulli
2022-08-05 8:12 ` Frank Lichtenheld
@ 2022-08-05 9:37 ` Antonio Quartulli
2022-08-05 10:04 ` Frank Lichtenheld
2022-08-05 10:58 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-05 9:37 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v2:
* more adjustments
Changes from v1:
* removed text about TCP drop (cannot reproduce at the moment)
* clarified version requirements for p2p and client mode
---
Changes.rst | 9 ++
README.dco.md | 122 ++++++++++++++++++++++++++
doc/man-sections/advanced-options.rst | 13 +++
doc/man-sections/server-options.rst | 6 ++
4 files changed, 150 insertions(+)
create mode 100644 README.dco.md
diff --git a/Changes.rst b/Changes.rst
index 67a23c79..275f8d64 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -79,6 +79,15 @@ Cookie based handshake for UDP server
shake. The tls-crypt-v2 option allows controlling if older clients are
accepted.
+Data channel offloading with ovpn-dco
+ 2.6.0+ implements support for data-channel offloading where the data packets
+ are directly processed and forwarded in kernel space thanks to the ovpn-dco
+ kernel module. The userspace openvpn program acts purely as a control plane
+ application. Note that DCO will use DATA_V2 packets in P2P mode, therefore,
+ this implies that peers must be running 2.6.0+ in order to have P2P-NCP
+ which brings DATA_V2 packet support.
+
+
Deprecated features
-------------------
``inetd`` has been removed
diff --git a/README.dco.md b/README.dco.md
new file mode 100644
index 00000000..702c5cf3
--- /dev/null
+++ b/README.dco.md
@@ -0,0 +1,122 @@
+OpenVPN data channel offload
+============================
+2.6.0+ implements support for data-channel offloading where the data packets
+are directly processed and forwarded in kernel space thanks to the ovpn-dco
+kernel module. The userspace openvpn program acts purely as a control plane
+application.
+
+
+Overview of current release
+---------------------------
+- See the "Limitations by design" and "Current limitations" sections for
+ features that are not and/or will not be supported by OpenVPN + ovpn-dco.
+
+
+Getting started (Linux)
+-----------------------
+- Use a recent Linux kernel. Linux 5.4.0 and newer are known to work with
+ ovpn-dco.
+
+Get the ovpn-dco module from one these urls and build it:
+
+* https://gitlab.com/openvpn/ovpn-dco
+* https://github.com/OpenVPN/ovpn-dco
+
+e.g.
+
+ git clone https://github.com/OpenVPN/ovpn-dco
+ cd ovpn-dco
+ make
+ sudo make install
+
+If you want to report bugs please ensure to compile ovpn-dco with
+`make DEBUG=1` and include any debug message being printed by the
+kernel (you can view those messages with `dmesg`).
+
+Clone and build OpenVPN (or use OpenVPN 2.6+). For example:
+
+ git clone https://github.com/openvpn/openvpn.git
+ cd openvpn
+ autoreconf -vi
+ ./configure --enable-dco
+ make
+ sudo make install # Or just run src/openvpn/openvpn
+
+When starting openvpn it will automatically detect DCO support and use the
+kernel module. Add the option `--disable-dco` to disable data channel offload
+support. If the configuration contains an option that is incompatible with
+data channel offloading, OpenVPN will automatically disable DCO support and
+warn the user.
+
+Should OpenVPN be configured to use a feature that is not supported by ovpn-dco
+or should the ovpn-dco kernel module not be available on the system, you will
+see a message like
+
+ Note: Kernel support for ovpn-dco missing, disabling data channel offload.
+
+in your log.
+
+
+DCO and P2P mode
+----------------
+DCO is also available when running OpenVPN in P2P mode without `--pull` /
+`--client` option. P2P mode is useful for scenarios when the OpenVPN tunnel
+should not interfere with overall routing and behave more like a "dumb" tunnel,
+like GRE.
+
+However, DCO requires DATA_V2 to be enabled, which is available for P2P mode
+only in OpenVPN 2.6 and later.
+
+OpenVPN prints a diagnostic message for the P2P NCP result when running in P2P
+mode:
+
+ P2P mode NCP negotiation result: TLS_export=1, DATA_v2=1, peer-id 9484735, cipher=AES-256-GCM
+
+Double check that you have `DATA_v2=1` in your output and a supported AEAD
+cipher (AES-XXX-GCM or CHACHA20POLY1305).
+
+
+Routing with ovpn-dco
+---------------------
+The ovpn-dco kernel module implements a more transparent approach to
+configuring routes to clients (aka "iroutes") and consults the main kernel
+routing tables for forwarding decisions.
+
+- Each client has a VPN IPv4 and/or a VPN IPv6 assigned to it;
+- additional IP ranges can be routed to a client by adding a route with
+ a client VPN IP as the gateway/nexthop (i.e. ip route add a.b.c.d/24 via
+ $VPNIP);
+- due to the point above, there is no real need to add a companion `--route` for
+ each `--iroute` directive, unless you want to blackhole traffic when the
+ specific client is not connected;
+- no internal routing is available. If you need truly internal routes, this can
+ be achieved either with filtering using `iptables` or using `ip rule`;
+- client-to-client behaviour, as implemented in userspace, does not exist:
+ packets always reach the tunnel interface and are then re-routed to the
+ destination peer based on the system routing table.
+
+
+Limitations by design
+----------------------
+- Layer 3 (dev tun) only;
+- only the following AEAD ciphers are currently supported: Chacha20-Poly1305
+ and AES-GCM-128/192/256;
+- no support for compression or compression framing:
+ - see also the `--compress migrate` option to move to a setup without
+ compression;
+- various features not implemented since they have better replacements:
+ - `--shaper`, use tc instead;
+ - packet manipulation, use nftables/iptables instead;
+- OpenVPN 2.4.0 is the minimum version required for peers to connect:
+ - older versions are missing support for the AEAD ciphers;
+- topology subnet is the only supported `--topology` for servers;
+- iroute directives install routes on the host operating system, see also
+ Routing with ovpn-dco.
+
+
+Current implementation limitations
+-------------------
+- `--persist-tun` not tested;
+- IPv6 mapped IPv4 addresses need Linux 5.4.189+/5.10.110+/5.12+ to work;
+- some incompatible options may not properly fallback to non-dco;
+- no per client statistics. Only total statistics available on the interface.
diff --git a/doc/man-sections/advanced-options.rst b/doc/man-sections/advanced-options.rst
index 5157c561..d5a6b4f2 100644
--- a/doc/man-sections/advanced-options.rst
+++ b/doc/man-sections/advanced-options.rst
@@ -91,3 +91,16 @@ used when debugging or testing out special usage scenarios.
*(Linux only)* Set the TX queue length on the TUN/TAP interface.
Currently defaults to operating system default.
+--disable-dco
+ Disables the opportunistic use of data channel offloading if available.
+ Without this option, OpenVPN will opportunistically use DCO mode if
+ the config options and the running kernel supports using DCO.
+
+ Data channel offload currently requires data-ciphers to only contain
+ AEAD ciphers (AES-GCM and Chacha20-Poly1305) and Linux with the
+ ovpn-dco module.
+
+ Note that some options have no effect or cannot be used when DCO mode
+ is enabled.
+
+ On platforms that do not support DCO ``disable-dco`` has no effect.
diff --git a/doc/man-sections/server-options.rst b/doc/man-sections/server-options.rst
index 04f4b4fb..54ea8b66 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -325,6 +325,12 @@ fast hardware. SSL/TLS authentication must be used in this mode.
from the kernel to OpenVPN. Once in OpenVPN, the ``--iroute`` directive
routes to the specific client.
+ However, when using DCO, the ``--iroute`` directive is usually enough
+ for DCO to fully configure the routing table. The extra ``--route``
+ directive is required only if the expected behaviour is to route the
+ traffic for a specific network to the VPN interface also when the
+ responsible client is not connected (traffic will then be dropped).
+
This option must be specified either in a client instance config file
using ``--client-config-dir`` or dynamically generated using a
``--client-connect`` script.
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 16/25] GitHub Actions: add Linux DCO build (on Ubuntu 20.04)
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (14 preceding siblings ...)
2022-06-24 8:37 ` [Openvpn-devel] [PATCH 15/25] dco: add documentation for ovpn-dco-linux Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-04 8:58 ` Frank Lichtenheld
2022-07-19 16:52 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 17/25] tun: extract close_tun_handle into its own fucntion and print correct type Antonio Quartulli
` (8 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Antonio Quartulli <a@...2181...>
---
.github/workflows/build.yaml | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 6c267a61..b905c0d2 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -209,15 +209,22 @@ jobs:
libname: OpenSSL 1.1.1
ssllib: openssl
extraconf: "--disable-lzo --disable-lz4"
+ - os: ubuntu-20.04
+ sslpkg: "libssl-dev"
+ libname: OpenSSL 1.1.1
+ ssllib: openssl
+ extraconf: "--enable-dco"
+ nlpkg: "libnl-genl-3-dev"
name: "gcc - ${{matrix.os}} - ${{matrix.libname}} ${{matrix.extraconf}}"
env:
SSLPKG: "${{matrix.sslpkg}}"
+ NLPKG: "${{matrix.nlpkg}}"
runs-on: ${{matrix.os}}
steps:
- name: Install dependencies
- run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev linux-libc-dev man2html libcmocka-dev python3-docutils libtool automake autoconf ${SSLPKG}
+ run: sudo apt update && sudo apt install -y liblzo2-dev libpam0g-dev liblz4-dev linux-libc-dev man2html libcmocka-dev python3-docutils libtool automake autoconf ${SSLPKG} ${NLPKG}
- name: Checkout OpenVPN
uses: actions/checkout@...2620...
- name: autoconf
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 16/25] GitHub Actions: add Linux DCO build (on Ubuntu 20.04)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 16/25] GitHub Actions: add Linux DCO build (on Ubuntu 20.04) Antonio Quartulli
@ 2022-07-04 8:58 ` Frank Lichtenheld
2022-07-19 16:52 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Frank Lichtenheld @ 2022-07-04 8:58 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
On Fri, Jun 24, 2022 at 10:38:00AM +0200, Antonio Quartulli wrote:
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> .github/workflows/build.yaml | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
Looks good to me. Also note that this can applied on current master
without waiting for the rest of series, since --enable-dco is already
available.
Acked-By: Frank Lichtenheld <frank@...2641...>
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH applied] Re: GitHub Actions: add Linux DCO build (on Ubuntu 20.04)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 16/25] GitHub Actions: add Linux DCO build (on Ubuntu 20.04) Antonio Quartulli
2022-07-04 8:58 ` Frank Lichtenheld
@ 2022-07-19 16:52 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-19 16:52 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Indeed, this one is independent from the rest *and* the current tree
compiles fine with --enable-dco *and* we want to ensure it stays that
way :-) -> in it goes!
Your patch has been applied to the master branch.
commit d2b0c011300c66055764b8f43c272342d2b82584
Author: Antonio Quartulli
Date: Fri Jun 24 10:38:00 2022 +0200
GitHub Actions: add Linux DCO build (on Ubuntu 20.04)
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Frank Lichtenheld <frank@...2641...>
Message-Id: <20220624083809.23487-17-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24523.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 17/25] tun: extract close_tun_handle into its own fucntion and print correct type
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (15 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 16/25] GitHub Actions: add Linux DCO build (on Ubuntu 20.04) Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-04 8:47 ` Frank Lichtenheld
2022-07-19 20:13 ` [Openvpn-devel] [PATCH applied] " Gert Doering
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 18/25] dco: turn supported ciphers list into a function Antonio Quartulli
` (7 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Arne Schwabe <arne@
From: Arne Schwabe <arne@...1227...>
This moves closing the tun handle into its own function and also prints
the adapter type we are operating on, instead hardcoding it to
tap-windows.
While at it, set the handle to NULL after closing, to prevent a double
close due to multiple invocations of this helper.
Signed-off-by: Arne Schwabe <arne@...1227...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/tun.c | 76 ++++++++++++++++++++++++++---------------------
1 file changed, 42 insertions(+), 34 deletions(-)
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 5eefb0c6..f324ac91 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -6767,6 +6767,47 @@ netsh_delete_address_dns(const struct tuntap *tt, bool ipv6, struct gc_arena *gc
argv_free(&argv);
}
+static void
+close_tun_handle(struct tuntap *tt)
+{
+ const char *adaptertype = print_windows_driver(tt->windows_driver);
+
+ if (tt->hand)
+ {
+ dmsg(D_WIN32_IO_LOW, "Attempting CancelIO on %s adapter", adaptertype);
+ if (!CancelIo(tt->hand))
+ {
+ msg(M_WARN | M_ERRNO, "Warning: CancelIO failed on %s adapter", adaptertype);
+ }
+ }
+
+ dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on %s adapter", adaptertype);
+ overlapped_io_close(&tt->reads);
+
+ dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped write event on %s adapter", adaptertype);
+ overlapped_io_close(&tt->writes);
+
+ if (tt->hand)
+ {
+ dmsg(D_WIN32_IO_LOW, "Attempting CloseHandle on %s adapter", adaptertype);
+ if (!CloseHandle(tt->hand))
+ {
+ msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on %s adapter", adaptertype);
+ }
+ tt->hand = NULL;
+ }
+
+ if (tt->windows_driver == WINDOWS_DRIVER_WINTUN)
+ {
+ CloseHandle(tt->rw_handle.read);
+ CloseHandle(tt->rw_handle.write);
+ UnmapViewOfFile(tt->wintun_send_ring);
+ UnmapViewOfFile(tt->wintun_receive_ring);
+ CloseHandle(tt->wintun_send_ring_handle);
+ CloseHandle(tt->wintun_receive_ring_handle);
+ }
+}
+
void
close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
{
@@ -6836,43 +6877,10 @@ close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx)
dhcp_release(tt);
- if (tt->hand != NULL)
- {
- dmsg(D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Windows adapter");
- if (!CancelIo(tt->hand))
- {
- msg(M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Windows adapter");
- }
- }
-
- dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Windows adapter");
- overlapped_io_close(&tt->reads);
-
- dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Windows adapter");
- overlapped_io_close(&tt->writes);
-
- if (tt->hand != NULL)
- {
- dmsg(D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Windows adapter");
- if (!CloseHandle(tt->hand))
- {
- msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Windows adapter");
- }
- }
+ close_tun_handle(tt);
free(tt->actual_name);
- if (tt->windows_driver == WINDOWS_DRIVER_WINTUN)
- {
- CloseHandle(tt->rw_handle.read);
- CloseHandle(tt->rw_handle.write);
- UnmapViewOfFile(tt->wintun_send_ring);
- UnmapViewOfFile(tt->wintun_receive_ring);
- CloseHandle(tt->wintun_send_ring_handle);
- CloseHandle(tt->wintun_receive_ring_handle);
- }
-
-
clear_tuntap(tt);
free(tt);
gc_free(&gc);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 17/25] tun: extract close_tun_handle into its own fucntion and print correct type
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 17/25] tun: extract close_tun_handle into its own fucntion and print correct type Antonio Quartulli
@ 2022-07-04 8:47 ` Frank Lichtenheld
2022-07-19 20:13 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Frank Lichtenheld @ 2022-07-04 8:47 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
On Fri, Jun 24, 2022 at 10:38:01AM +0200, Antonio Quartulli wrote:
> From: Arne Schwabe <arne@...1227...>
>
> This moves closing the tun handle into its own function and also prints
> the adapter type we are operating on, instead hardcoding it to
> tap-windows.
>
> While at it, set the handle to NULL after closing, to prevent a double
> close due to multiple invocations of this helper.
>
> Signed-off-by: Arne Schwabe <arne@...1227...>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> src/openvpn/tun.c | 76 ++++++++++++++++++++++++++---------------------
> 1 file changed, 42 insertions(+), 34 deletions(-)
Trivial enough. Also can be applied independently of all changes
before it in the series!
Acked-By: Frank Lichtenheld <frank@...2641...>
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH applied] Re: tun: extract close_tun_handle into its own fucntion and print correct type
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 17/25] tun: extract close_tun_handle into its own fucntion and print correct type Antonio Quartulli
2022-07-04 8:47 ` Frank Lichtenheld
@ 2022-07-19 20:13 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-19 20:13 UTC (permalink / raw)
To: Arne Schwabe <arne@; +Cc: openvpn-devel
Code change looks good. Did not actually test anything, but it *does*
pass compilation on MinGW / Unbuntu 18.04 :-)
The change to print the actual adapter type confuses "--color-move=zebra",
but is a nice enhancement.
Your patch has been applied to the master branch.
commit abceec65daed9aa6359314a0243246acc1cc25bf
Author: Arne Schwabe
Date: Fri Jun 24 10:38:01 2022 +0200
tun: extract close_tun_handle into its own fucntion and print correct type
Signed-off-by: Arne Schwabe <arne@...1227...>
Signed-off-by: Antonio Quartulli <a@...2181...>
Acked-by: Frank Lichtenheld <frank@...2641...>
Message-Id: <20220624083809.23487-18-a@...2181...>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24527.html
Signed-off-by: Gert Doering <gert@...1296...>
--
kind regards,
Gert Doering
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 18/25] dco: turn supported ciphers list into a function
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (16 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 17/25] tun: extract close_tun_handle into its own fucntion and print correct type Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
2022-08-07 10:04 ` [Openvpn-devel] [PATCH v2 18/18] " Antonio Quartulli
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32 Antonio Quartulli
` (6 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Other platforms may need more complex logic to decide whether a cipher
is supported or not, therefore turn hardcoded list into a function that
can be implemented by each platform independently.
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/dco.c | 4 ++--
src/openvpn/dco.h | 13 +++++++++++++
src/openvpn/dco_linux.c | 6 ++++++
src/openvpn/dco_linux.h | 1 -
4 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 48e007ea..1612a64e 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -236,7 +236,7 @@ dco_check_option_conflict(int msglevel, const struct options *o)
/* At this point the ciphers have already been normalised */
if (o->enable_ncp_fallback
- && !tls_item_in_cipher_list(o->ciphername, DCO_SUPPORTED_CIPHERS))
+ && !tls_item_in_cipher_list(o->ciphername, dco_get_supported_ciphers()))
{
msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
"disables data channel offload.", o->ciphername);
@@ -288,7 +288,7 @@ dco_check_option_conflict(int msglevel, const struct options *o)
const char *token;
while ((token = strsep(&tmp_ciphers, ":")))
{
- if (!tls_item_in_cipher_list(token, DCO_SUPPORTED_CIPHERS))
+ if (!tls_item_in_cipher_list(token, dco_get_supported_ciphers()))
{
msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
"by ovpn-dco, disabling data channel offload.", token);
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 4b945b50..66ae444f 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -200,6 +200,13 @@ void dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
*/
void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
+/**
+ * Retrieve the list of ciphers supported by the current platform
+ *
+ * @return list of colon-separated ciphers
+ */
+const char *dco_get_supported_ciphers();
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -301,5 +308,11 @@ dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
{
}
+static inline const char *
+dco_get_supported_ciphers()
+{
+ return "";
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c
index 5e77139a..f86ea819 100644
--- a/src/openvpn/dco_linux.c
+++ b/src/openvpn/dco_linux.c
@@ -931,4 +931,10 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
}
}
+const char *
+dco_get_supported_ciphers()
+{
+ return "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305";
+}
+
#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h
index e0e59fa6..416ea30a 100644
--- a/src/openvpn/dco_linux.h
+++ b/src/openvpn/dco_linux.h
@@ -34,7 +34,6 @@
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
{
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 18/25] dco: turn supported ciphers list into a function
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 18/25] dco: turn supported ciphers list into a function Antonio Quartulli
@ 2022-07-05 12:31 ` Heiko Hund
2022-08-07 10:04 ` [Openvpn-devel] [PATCH v2 18/18] " Antonio Quartulli
1 sibling, 0 replies; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:31 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:38:02 CEST Antonio Quartulli wrote:
> Other platforms may need more complex logic to decide whether a cipher
> is supported or not, therefore turn hardcoded list into a function that
> can be implemented by each platform independently.
>
> Signed-off-by: Lev Stipakov <lev@...515...>
> Signed-off-by: Antonio Quartulli <a@...2181...>
Trivial and compile-checked.
Acked-by: Heiko Hund <heiko@...2662...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH v2 18/18] dco: turn supported ciphers list into a function
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 18/25] dco: turn supported ciphers list into a function Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
@ 2022-08-07 10:04 ` Antonio Quartulli
2022-08-07 14:58 ` [Openvpn-devel] [PATCH applied] " Gert Doering
1 sibling, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-07 10:04 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Other platforms may need more complex logic to decide whether a cipher
is supported or not, therefore turn hardcoded list into a function that
can be implemented by each platform independently.
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* rebased
---
src/openvpn/dco.c | 4 ++--
src/openvpn/dco.h | 13 +++++++++++++
src/openvpn/dco_linux.c | 6 ++++++
src/openvpn/dco_linux.h | 1 -
4 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 09855643..889ae270 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -306,7 +306,7 @@ dco_check_option_conflict(int msglevel, const struct options *o)
/* At this point the ciphers have already been normalised */
if (o->enable_ncp_fallback
- && !tls_item_in_cipher_list(o->ciphername, DCO_SUPPORTED_CIPHERS))
+ && !tls_item_in_cipher_list(o->ciphername, dco_get_supported_ciphers()))
{
msg(msglevel, "Note: --data-cipher-fallback with cipher '%s' "
"disables data channel offload.", o->ciphername);
@@ -358,7 +358,7 @@ dco_check_option_conflict(int msglevel, const struct options *o)
const char *token;
while ((token = strsep(&tmp_ciphers, ":")))
{
- if (!tls_item_in_cipher_list(token, DCO_SUPPORTED_CIPHERS))
+ if (!tls_item_in_cipher_list(token, dco_get_supported_ciphers()))
{
msg(msglevel, "Note: cipher '%s' in --data-ciphers is not supported "
"by ovpn-dco, disabling data channel offload.", token);
diff --git a/src/openvpn/dco.h b/src/openvpn/dco.h
index 72569083..6b5c016a 100644
--- a/src/openvpn/dco.h
+++ b/src/openvpn/dco.h
@@ -213,6 +213,13 @@ void dco_install_iroute(struct multi_context *m, struct multi_instance *mi,
*/
void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi);
+/**
+ * Retrieve the list of ciphers supported by the current platform
+ *
+ * @return list of colon-separated ciphers
+ */
+const char *dco_get_supported_ciphers();
+
#else /* if defined(ENABLE_DCO) */
typedef void *dco_context_t;
@@ -320,5 +327,11 @@ dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi)
{
}
+static inline const char *
+dco_get_supported_ciphers()
+{
+ return "";
+}
+
#endif /* defined(ENABLE_DCO) */
#endif /* ifndef DCO_H */
diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c
index 5e77139a..f86ea819 100644
--- a/src/openvpn/dco_linux.c
+++ b/src/openvpn/dco_linux.c
@@ -931,4 +931,10 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
}
}
+const char *
+dco_get_supported_ciphers()
+{
+ return "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305";
+}
+
#endif /* defined(ENABLE_DCO) && defined(TARGET_LINUX) */
diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h
index e0e59fa6..416ea30a 100644
--- a/src/openvpn/dco_linux.h
+++ b/src/openvpn/dco_linux.h
@@ -34,7 +34,6 @@
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
{
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (17 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 18/25] dco: turn supported ciphers list into a function Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-06-24 18:53 ` Selva Nair
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options Antonio Quartulli
` (5 subsequent siblings)
24 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
GetOverlappedResultEx is not available on ming32 therefore we must
provide some compat layer before being able to use this function.
Signed-off-by: Antonio Quartulli <a@...2181...>
Signed-off-by: Lev Stipakov <lev@...515...>
---
src/compat/Makefile.am | 3 +-
src/compat/compat-dco_get_overlapped_result.c | 46 +++++++++++++++++++
src/compat/compat.h | 8 ++++
src/compat/compat.vcxproj | 1 +
src/compat/compat.vcxproj.filters | 3 ++
5 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 src/compat/compat-dco_get_overlapped_result.c
diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
index 6eb991dc..6dba08aa 100644
--- a/src/compat/Makefile.am
+++ b/src/compat/Makefile.am
@@ -28,4 +28,5 @@ libcompat_la_SOURCES = \
compat-gettimeofday.c \
compat-daemon.c \
compat-strsep.c \
- compat-versionhelpers.h
+ compat-versionhelpers.h \
+ compat-dco_get_overlapped_result.c
diff --git a/src/compat/compat-dco_get_overlapped_result.c b/src/compat/compat-dco_get_overlapped_result.c
new file mode 100644
index 00000000..e14ce976
--- /dev/null
+++ b/src/compat/compat-dco_get_overlapped_result.c
@@ -0,0 +1,46 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2021-2022 Lev Stipakov <lev@...515...>
+ * Copyright (C) 2021-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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
+
+#include "compat.h"
+
+#if defined(__MINGW32__) && !defined(__MINGW64__)
+BOOL
+dco_get_overlapped_result(HANDLE handle, OVERLAPPED *ov, DWORD *transferred,
+ DWORD delay_millisec, BOOL unused)
+{
+ BOOL res = GetOverlappedResult(handle, ov, transferred, FALSE);
+ if ((res == 0) && (GetLastError() == ERROR_IO_INCOMPLETE))
+ {
+ Sleep(delay_millisec);
+ }
+ return res;
+}
+#endif
diff --git a/src/compat/compat.h b/src/compat/compat.h
index 026974a8..2b1ad6a6 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -62,4 +62,12 @@ char *strsep(char **stringp, const char *delim);
#endif
+#if defined(__MINGW32__) && !defined(__MINGW64__)
+BOOL dco_get_overlapped_result(HANDLE handle, OVERLAPPED *ov, DWORD *transferred,
+ DWORD delay_millisec, BOOL unused);
+
+#else
+#define dco_get_overlapped_result GetOverlappedResultEx
+#endif
+
#endif /* COMPAT_H */
diff --git a/src/compat/compat.vcxproj b/src/compat/compat.vcxproj
index fe03a51a..1dacb503 100644
--- a/src/compat/compat.vcxproj
+++ b/src/compat/compat.vcxproj
@@ -159,6 +159,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="compat-basename.c" />
+ <ClCompile Include="compat-dco_get_overlapped_result.c" />
<ClCompile Include="compat-dirname.c" />
<ClCompile Include="compat-gettimeofday.c" />
<ClCompile Include="compat-daemon.c" />
diff --git a/src/compat/compat.vcxproj.filters b/src/compat/compat.vcxproj.filters
index 96ca026a..73fc9f91 100644
--- a/src/compat/compat.vcxproj.filters
+++ b/src/compat/compat.vcxproj.filters
@@ -30,6 +30,9 @@
<ClCompile Include="compat-strsep.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="compat-dco_get_overlapped_result.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="compat.h">
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32 Antonio Quartulli
@ 2022-06-24 18:53 ` Selva Nair
2022-06-27 10:30 ` Lev Stipakov
0 siblings, 1 reply; 157+ messages in thread
From: Selva Nair @ 2022-06-24 18:53 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel, Lev Stipakov <lev@
[-- Attachment #1: Type: text/plain, Size: 4125 bytes --]
Hi,
On Fri, Jun 24, 2022 at 5:10 AM Antonio Quartulli <a@...2181...> wrote:
> GetOverlappedResultEx is not available on ming32 therefore we must
> provide some compat layer before being able to use this function.
>
I suppose "mingw32" here refers to I mingw-w64 for 32 bit (i686) target.
This symbol has been exported in kernel32.lib since version 7.0 release of
mingw-w64 (~ 2019). Current release is version 10. Do we need to support
older versions given that mingw build is now used only for development by a
few of us?
I use debian which probably is the slowest to get new versions -- bullseye
has version 8 (gcc version 10), on buster I use unstable which gives
version 8. Ubuntu 20.04 has version 7, 22.04 has version 8.
Even if we must support 6.0 and older, let's not unconditionally use the
compat layer proposed here.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> Signed-off-by: Lev Stipakov <lev@...515...>
> ---
> src/compat/Makefile.am | 3 +-
> src/compat/compat-dco_get_overlapped_result.c | 46 +++++++++++++++++++
> src/compat/compat.h | 8 ++++
> src/compat/compat.vcxproj | 1 +
> src/compat/compat.vcxproj.filters | 3 ++
> 5 files changed, 60 insertions(+), 1 deletion(-)
> create mode 100644 src/compat/compat-dco_get_overlapped_result.c
>
> diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
> index 6eb991dc..6dba08aa 100644
> --- a/src/compat/Makefile.am
> +++ b/src/compat/Makefile.am
> @@ -28,4 +28,5 @@ libcompat_la_SOURCES = \
> compat-gettimeofday.c \
> compat-daemon.c \
> compat-strsep.c \
> - compat-versionhelpers.h
> + compat-versionhelpers.h \
> + compat-dco_get_overlapped_result.c
> diff --git a/src/compat/compat-dco_get_overlapped_result.c
> b/src/compat/compat-dco_get_overlapped_result.c
> new file mode 100644
> index 00000000..e14ce976
> --- /dev/null
> +++ b/src/compat/compat-dco_get_overlapped_result.c
> @@ -0,0 +1,46 @@
> +/*
> + * OpenVPN -- An application to securely tunnel IP networks
> + * over a single UDP port, with support for SSL/TLS-based
> + * session authentication and key exchange,
> + * packet encryption, packet authentication, and
> + * packet compression.
> + *
> + * Copyright (C) 2021-2022 Lev Stipakov <lev@...515...>
> + * Copyright (C) 2021-2022 OpenVPN Inc <sales@...515...>
> + *
> + * 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
> +
> +#include "compat.h"
> +
> +#if defined(__MINGW32__) && !defined(__MINGW64__)
> +BOOL
> +dco_get_overlapped_result(HANDLE handle, OVERLAPPED *ov, DWORD
> *transferred,
> + DWORD delay_millisec, BOOL unused)
> +{
> + BOOL res = GetOverlappedResult(handle, ov, transferred, FALSE);
> + if ((res == 0) && (GetLastError() == ERROR_IO_INCOMPLETE))
> + {
> + Sleep(delay_millisec);
While this may be ok assuming it's not inside any performance critical
loops, no need to force this on newer versions of mingw-w64.
Alternatively, we could use AC_CHECK_FUNC to make this conditional or find
the symbol GetOverlappedResultEx at runtime. But, imo, we do not need any
of this as supporting mingw-w64 version 7+ should be enough.
Regards,
Selva
[-- Attachment #2: Type: text/html, Size: 5431 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32
2022-06-24 18:53 ` Selva Nair
@ 2022-06-27 10:30 ` Lev Stipakov
2022-06-27 18:50 ` Antonio Quartulli
0 siblings, 1 reply; 157+ messages in thread
From: Lev Stipakov @ 2022-06-27 10:30 UTC (permalink / raw)
To: Selva Nair <selva.nair@; +Cc: Antonio Quartulli <a@
Hi,
The problem this patch solves appeared on mingw-w64 for 32bit version
6.0.0, which is the latest version of mingw on Windows. When this
patch was written (April 2021), we still used travis/appveyor which
did mingw builds on Windows so back then patch made sense.
Since we moved to GHa since the last summer and mingw builds are done
on Linux with new enough mingw, this patch could be safely removed.
I dropped this commit and pushed changes to my fork and GHa is happy:
https://github.com/lstipakov/openvpn/actions/runs/2567640010
-Lev
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32
2022-06-27 10:30 ` Lev Stipakov
@ 2022-06-27 18:50 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-27 18:50 UTC (permalink / raw)
To: Lev Stipakov <lstipakov@; +Cc: openvpn-devel, Lev Stipakov <lev@
Hi,
On 27/06/2022 12:30, Lev Stipakov wrote:
> Hi,
>
> The problem this patch solves appeared on mingw-w64 for 32bit version
> 6.0.0, which is the latest version of mingw on Windows. When this
> patch was written (April 2021), we still used travis/appveyor which
> did mingw builds on Windows so back then patch made sense.
>
> Since we moved to GHa since the last summer and mingw builds are done
> on Linux with new enough mingw, this patch could be safely removed.
>
> I dropped this commit and pushed changes to my fork and GHa is happy:
> https://github.com/lstipakov/openvpn/actions/runs/2567640010
>
The 'dco' branch has been modified accordingly:
* compat patch 19/25 removed;
* code now uses GetOverlappedResultEx() directly.
Cheers,
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (18 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 19/25] dco-win: implement GetOverlappedResultEx for mingw32 Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
` (2 more replies)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check Antonio Quartulli
` (4 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Some platforms may have different constraints in terms of incompatible
opions, therefore we add a function that explicitly checks those.
Also, add generic option check for when ovpn-dco-win is in use.
Signed-off-by: Antonio Quartulli <a@...2181...>
Signed-off-by: Lev Stipakov <lev@...515...>
---
src/openvpn/dco.c | 25 +++++++++++++++++++++++++
src/openvpn/options.c | 5 +++++
2 files changed, 30 insertions(+)
diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c
index 1612a64e..448f29b5 100644
--- a/src/openvpn/dco.c
+++ b/src/openvpn/dco.c
@@ -190,6 +190,26 @@ dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
}
}
+static bool
+dco_check_option_conflict_platform(int msglevel, const struct options *o)
+{
+#if defined(_WIN32)
+ if (o->mode == MODE_SERVER)
+ {
+ msg(msglevel, "Only client and p2p data channel offload is supported "
+ "with ovpn-dco-win.");
+ return false;
+ }
+
+ if (o->persist_tun)
+ {
+ msg(msglevel, "--persist-tun is not supported with ovpn-dco-win.");
+ return false;
+ }
+#endif
+ return true;
+}
+
static bool
dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
{
@@ -228,6 +248,11 @@ dco_check_option_conflict(int msglevel, const struct options *o)
return false;
}
+ if (!dco_check_option_conflict_platform(msglevel, o))
+ {
+ return false;
+ }
+
if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
{
msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 7b450296..d9da8b8b 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -2376,6 +2376,11 @@ options_postprocess_verify_ce(const struct options *options,
{
msg(M_USAGE, "--windows-driver wintun requires --dev tun");
}
+
+ if (options->windows_driver == WINDOWS_DRIVER_WINDCO)
+ {
+ dco_check_option_conflict(M_USAGE, options);
+ }
#endif /* ifdef _WIN32 */
/*
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options Antonio Quartulli
@ 2022-07-05 12:31 ` Heiko Hund
2022-07-19 20:17 ` Gert Doering
2022-08-11 20:58 ` Gert Doering
2 siblings, 0 replies; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:31 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:38:04 CEST Antonio Quartulli wrote:
> Some platforms may have different constraints in terms of incompatible
> opions, therefore we add a function that explicitly checks those.
>
> Also, add generic option check for when ovpn-dco-win is in use.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> Signed-off-by: Lev Stipakov <lev@...515...>
Acked-by: Heiko Hund <heiko@...2662...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
@ 2022-07-19 20:17 ` Gert Doering
2022-08-11 20:58 ` Gert Doering
2 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-07-19 20:17 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel, Lev Stipakov <lev@
[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]
Hi,
this one has an ACK, but needs a v2 still, because...
On Fri, Jun 24, 2022 at 10:38:04AM +0200, Antonio Quartulli wrote:
> Some platforms may have different constraints in terms of incompatible
> opions, therefore we add a function that explicitly checks those.
>
> Also, add generic option check for when ovpn-dco-win is in use.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> Signed-off-by: Lev Stipakov <lev@...515...>
[..]
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -2376,6 +2376,11 @@ options_postprocess_verify_ce(const struct options *options,
> {
> msg(M_USAGE, "--windows-driver wintun requires --dev tun");
> }
> +
> + if (options->windows_driver == WINDOWS_DRIVER_WINDCO)
> + {
> + dco_check_option_conflict(M_USAGE, options);
> + }
> #endif /* ifdef _WIN32 */
... the WINDOWS_DRIVER_WINDCO enum is only introduced in one of the
later hunks.
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
2022-07-19 20:17 ` Gert Doering
@ 2022-08-11 20:58 ` Gert Doering
2 siblings, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-11 20:58 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel, Lev Stipakov <lev@
[-- Attachment #1: Type: text/plain, Size: 1251 bytes --]
Hi,
On Fri, Jun 24, 2022 at 10:38:04AM +0200, Antonio Quartulli wrote:
> Some platforms may have different constraints in terms of incompatible
> opions, therefore we add a function that explicitly checks those.
>
> Also, add generic option check for when ovpn-dco-win is in use.
Looking more closely at this, this patch needs a carefuly rebase :-)
> --- a/src/openvpn/dco.c
> +++ b/src/openvpn/dco.c
> @@ -190,6 +190,26 @@ dco_update_keys(dco_context_t *dco, struct tls_multi *multi)
> }
> }
>
> +static bool
> +dco_check_option_conflict_platform(int msglevel, const struct options *o)
> +{
> +#if defined(_WIN32)
> + if (o->mode == MODE_SERVER)
> + {
... it *introduces* dco_check_option_conflict_platform() with "only
windows" content, but in the current master tree, this function already
exists, with Linux content...
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (19 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 20/25] dco-win: add platform dependant check on incompatible options Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
2022-08-04 11:49 ` Lev Stipakov
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows Antonio Quartulli
` (3 subsequent siblings)
24 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
The current condition checking if the TUN interface was preserved is
dependant on the platform being Android or not. This makes the code
reasonably ugly, especially because uncrustify can't indent properly.
On top of that, we will require an extra condition only for windows+DCO,
which will make the check even uglier.
For this reason, factor out the check in a separate function which can
keep the ifdefs craziness well hidden, while do_open_tun becomes
(a bit) cleaner.
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/init.c | 283 +++++++++++++++++++++++----------------------
1 file changed, 146 insertions(+), 137 deletions(-)
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index bdd2ad96..f95dcb93 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1690,161 +1690,123 @@ do_init_tun(struct context *c)
* Open tun/tap device, ifconfig, call up script, etc.
*/
+
+static bool
+can_preserve_tun(struct tuntap *tt)
+{
+#ifdef TARGET_ANDROID
+ return false;
+#elif _WIN32
+ return tt && tt->dco.real_tun_init;
+#else
+ return tt;
+#endif
+}
+
static bool
do_open_tun(struct context *c)
{
struct gc_arena gc = gc_new();
bool ret = false;
-#ifndef TARGET_ANDROID
- if (!c->c1.tuntap)
+ if (!can_preserve_tun(c->c1.tuntap))
{
-#endif
#ifdef TARGET_ANDROID
- /* If we emulate persist-tun on android we still have to open a new tun and
- * then close the old */
- int oldtunfd = -1;
- if (c->c1.tuntap)
- {
- oldtunfd = c->c1.tuntap->fd;
- free(c->c1.tuntap);
- c->c1.tuntap = NULL;
- c->c1.tuntap_owned = false;
- }
+ /* If we emulate persist-tun on android we still have to open a new tun and
+ * then close the old */
+ int oldtunfd = -1;
+ if (c->c1.tuntap)
+ {
+ oldtunfd = c->c1.tuntap->fd;
+ free(c->c1.tuntap);
+ c->c1.tuntap = NULL;
+ c->c1.tuntap_owned = false;
+ }
#endif
- /* initialize (but do not open) tun/tap object */
- do_init_tun(c);
+ /* initialize (but do not open) tun/tap object */
+ do_init_tun(c);
- /* inherit the dco context from the tuntap object */
- if (c->c2.tls_multi)
- {
- c->c2.tls_multi->dco = &c->c1.tuntap->dco;
- }
+ /* inherit the dco context from the tuntap object */
+ if (c->c2.tls_multi)
+ {
+ c->c2.tls_multi->dco = &c->c1.tuntap->dco;
+ }
#ifdef _WIN32
- /* store (hide) interactive service handle in tuntap_options */
- c->c1.tuntap->options.msg_channel = c->options.msg_channel;
- msg(D_ROUTE, "interactive service msg_channel=%" PRIu64, (unsigned long long) c->options.msg_channel);
+ /* store (hide) interactive service handle in tuntap_options */
+ c->c1.tuntap->options.msg_channel = c->options.msg_channel;
+ msg(D_ROUTE, "interactive service msg_channel=%" PRIu64, (unsigned long long) c->options.msg_channel);
#endif
- /* allocate route list structure */
- do_alloc_route_list(c);
+ /* allocate route list structure */
+ do_alloc_route_list(c);
- /* parse and resolve the route option list */
- ASSERT(c->c2.link_socket);
- if (c->options.routes && c->c1.route_list)
- {
- do_init_route_list(&c->options, c->c1.route_list,
- &c->c2.link_socket->info, c->c2.es, &c->net_ctx);
- }
- if (c->options.routes_ipv6 && c->c1.route_ipv6_list)
- {
- do_init_route_ipv6_list(&c->options, c->c1.route_ipv6_list,
- &c->c2.link_socket->info, c->c2.es,
- &c->net_ctx);
- }
+ /* parse and resolve the route option list */
+ ASSERT(c->c2.link_socket);
+ if (c->options.routes && c->c1.route_list)
+ {
+ do_init_route_list(&c->options, c->c1.route_list,
+ &c->c2.link_socket->info, c->c2.es, &c->net_ctx);
+ }
+ if (c->options.routes_ipv6 && c->c1.route_ipv6_list)
+ {
+ do_init_route_ipv6_list(&c->options, c->c1.route_ipv6_list,
+ &c->c2.link_socket->info, c->c2.es,
+ &c->net_ctx);
+ }
- /* do ifconfig */
- if (!c->options.ifconfig_noexec
- && ifconfig_order() == IFCONFIG_BEFORE_TUN_OPEN)
- {
- /* guess actual tun/tap unit number that will be returned
- * by open_tun */
- const char *guess = guess_tuntap_dev(c->options.dev,
- c->options.dev_type,
- c->options.dev_node,
- &gc);
- do_ifconfig(c->c1.tuntap, guess, c->c2.frame.tun_mtu, c->c2.es,
- &c->net_ctx);
- }
+ /* do ifconfig */
+ if (!c->options.ifconfig_noexec
+ && ifconfig_order() == IFCONFIG_BEFORE_TUN_OPEN)
+ {
+ /* guess actual tun/tap unit number that will be returned
+ * by open_tun */
+ const char *guess = guess_tuntap_dev(c->options.dev,
+ c->options.dev_type,
+ c->options.dev_node,
+ &gc);
+ do_ifconfig(c->c1.tuntap, guess, c->c2.frame.tun_mtu, c->c2.es,
+ &c->net_ctx);
+ }
- /* possibly add routes */
- if (route_order() == ROUTE_BEFORE_TUN)
- {
- /* Ignore route_delay, would cause ROUTE_BEFORE_TUN to be ignored */
- do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
- c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
- }
+ /* possibly add routes */
+ if (route_order() == ROUTE_BEFORE_TUN)
+ {
+ /* Ignore route_delay, would cause ROUTE_BEFORE_TUN to be ignored */
+ do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
+ c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
+ }
#ifdef TARGET_ANDROID
- /* Store the old fd inside the fd so open_tun can use it */
- c->c1.tuntap->fd = oldtunfd;
-#endif
- if (dco_enabled(&c->options))
- {
- ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
- }
-
- /* open the tun device */
- open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap, &c->net_ctx);
-
- /* set the hardware address */
- if (c->options.lladdr)
- {
- set_lladdr(&c->net_ctx, c->c1.tuntap->actual_name, c->options.lladdr,
- c->c2.es);
- }
-
- /* do ifconfig */
- if (!c->options.ifconfig_noexec
- && ifconfig_order() == IFCONFIG_AFTER_TUN_OPEN)
- {
- do_ifconfig(c->c1.tuntap, c->c1.tuntap->actual_name,
- c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx);
- }
-
- /* run the up script */
- run_up_down(c->options.up_script,
- c->plugins,
- OPENVPN_PLUGIN_UP,
- c->c1.tuntap->actual_name,
-#ifdef _WIN32
- c->c1.tuntap->adapter_index,
+ /* Store the old fd inside the fd so open_tun can use it */
+ c->c1.tuntap->fd = oldtunfd;
#endif
- dev_type_string(c->options.dev, c->options.dev_type),
- c->c2.frame.tun_mtu,
- print_in_addr_t(c->c1.tuntap->local, IA_EMPTY_IF_UNDEF, &gc),
- print_in_addr_t(c->c1.tuntap->remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
- "init",
- NULL,
- "up",
- c->c2.es);
-
-#if defined(_WIN32)
- if (c->options.block_outside_dns)
- {
- dmsg(D_LOW, "Blocking outside DNS");
- if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel))
+ if (dco_enabled(&c->options))
{
- msg(M_FATAL, "Blocking DNS failed!");
+ ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
}
- }
-#endif
- /* possibly add routes */
- if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
- {
- do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
- c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
- }
+ /* open the tun device */
+ open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
+ c->c1.tuntap, &c->net_ctx);
- ret = true;
- static_context = c;
-#ifndef TARGET_ANDROID
-}
-else
-{
- msg(M_INFO, "Preserving previous TUN/TAP instance: %s",
- c->c1.tuntap->actual_name);
+ /* set the hardware address */
+ if (c->options.lladdr)
+ {
+ set_lladdr(&c->net_ctx, c->c1.tuntap->actual_name, c->options.lladdr,
+ c->c2.es);
+ }
- /* explicitly set the ifconfig_* env vars */
- do_ifconfig_setenv(c->c1.tuntap, c->c2.es);
+ /* do ifconfig */
+ if (!c->options.ifconfig_noexec
+ && ifconfig_order() == IFCONFIG_AFTER_TUN_OPEN)
+ {
+ do_ifconfig(c->c1.tuntap, c->c1.tuntap->actual_name,
+ c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx);
+ }
- /* run the up script if user specified --up-restart */
- if (c->options.up_restart)
- {
+ /* run the up script */
run_up_down(c->options.up_script,
c->plugins,
OPENVPN_PLUGIN_UP,
@@ -1856,24 +1818,71 @@ else
c->c2.frame.tun_mtu,
print_in_addr_t(c->c1.tuntap->local, IA_EMPTY_IF_UNDEF, &gc),
print_in_addr_t(c->c1.tuntap->remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
- "restart",
+ "init",
NULL,
"up",
c->c2.es);
- }
+
#if defined(_WIN32)
- if (c->options.block_outside_dns)
- {
- dmsg(D_LOW, "Blocking outside DNS");
- if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel))
+ if (c->options.block_outside_dns)
{
- msg(M_FATAL, "Blocking DNS failed!");
+ dmsg(D_LOW, "Blocking outside DNS");
+ if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel))
+ {
+ msg(M_FATAL, "Blocking DNS failed!");
+ }
}
+#endif
+
+ /* possibly add routes */
+ if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
+ {
+ do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
+ c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
+ }
+
+ ret = true;
+ static_context = c;
}
+ else
+ {
+ msg(M_INFO, "Preserving previous TUN/TAP instance: %s",
+ c->c1.tuntap->actual_name);
+
+ /* explicitly set the ifconfig_* env vars */
+ do_ifconfig_setenv(c->c1.tuntap, c->c2.es);
+
+ /* run the up script if user specified --up-restart */
+ if (c->options.up_restart)
+ {
+ run_up_down(c->options.up_script,
+ c->plugins,
+ OPENVPN_PLUGIN_UP,
+ c->c1.tuntap->actual_name,
+#ifdef _WIN32
+ c->c1.tuntap->adapter_index,
+#endif
+ dev_type_string(c->options.dev, c->options.dev_type),
+ c->c2.frame.tun_mtu,
+ print_in_addr_t(c->c1.tuntap->local, IA_EMPTY_IF_UNDEF, &gc),
+ print_in_addr_t(c->c1.tuntap->remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
+ "restart",
+ NULL,
+ "up",
+ c->c2.es);
+ }
+#if defined(_WIN32)
+ if (c->options.block_outside_dns)
+ {
+ dmsg(D_LOW, "Blocking outside DNS");
+ if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel))
+ {
+ msg(M_FATAL, "Blocking DNS failed!");
+ }
+ }
#endif
-}
-#endif /* ifndef TARGET_ANDROID */
+ }
gc_free(&gc);
return ret;
}
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check Antonio Quartulli
@ 2022-07-05 12:31 ` Heiko Hund
2022-07-05 18:47 ` Antonio Quartulli
2022-08-04 11:49 ` Lev Stipakov
1 sibling, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:31 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:38:05 CEST Antonio Quartulli wrote:
> The current condition checking if the TUN interface was preserved is
> dependant on the platform being Android or not. This makes the code
> reasonably ugly, especially because uncrustify can't indent properly.
>
> On top of that, we will require an extra condition only for windows+DCO,
> which will make the check even uglier.
>
> For this reason, factor out the check in a separate function which can
> keep the ifdefs craziness well hidden, while do_open_tun becomes
> (a bit) cleaner.
>
> Signed-off-by: Antonio Quartulli <a@...2181...>
Looks mighty, but is rather trivial. Two things:
I think the code could benefit from factoring out the call to run_up_down()
and block_outside_dns into two simple functions. Currently it's pretty much
duplicated in the if and else branch. Besides that:
Acked-by: Heiko Hund <heiko@...2662...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check
2022-07-05 12:31 ` Heiko Hund
@ 2022-07-05 18:47 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-05 18:47 UTC (permalink / raw)
To: Heiko Hund <heiko@
Hi,
On 05/07/2022 14:31, Heiko Hund wrote:
> On Freitag, 24. Juni 2022 10:38:05 CEST Antonio Quartulli wrote:
>> The current condition checking if the TUN interface was preserved is
>> dependant on the platform being Android or not. This makes the code
>> reasonably ugly, especially because uncrustify can't indent properly.
>>
>> On top of that, we will require an extra condition only for windows+DCO,
>> which will make the check even uglier.
>>
>> For this reason, factor out the check in a separate function which can
>> keep the ifdefs craziness well hidden, while do_open_tun becomes
>> (a bit) cleaner.
>>
>> Signed-off-by: Antonio Quartulli <a@...2181...>
>
> Looks mighty, but is rather trivial. Two things:
Can also be reviewed with "git show -w" to highlight the real changes.
Most of the diff is reindenting.
>
> I think the code could benefit from factoring out the call to run_up_down()
> and block_outside_dns into two simple functions. Currently it's pretty much
> duplicated in the if and else branch. Besides that:
meat for another patch ;)
>
> Acked-by: Heiko Hund <heiko@...2662...>
Thanks!
>
>
>
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* Re: [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check Antonio Quartulli
2022-07-05 12:31 ` Heiko Hund
@ 2022-08-04 11:49 ` Lev Stipakov
2022-08-04 12:07 ` Antonio Quartulli
1 sibling, 1 reply; 157+ messages in thread
From: Lev Stipakov @ 2022-08-04 11:49 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Hi,
> +static bool
> +can_preserve_tun(struct tuntap *tt)
> +{
> +#ifdef TARGET_ANDROID
> + return false;
> +#elif _WIN32
> + return tt && tt->dco.real_tun_init;
Looks like dco.real_tun_init has been added in the very next patch, 22/25.
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check
2022-08-04 11:49 ` Lev Stipakov
@ 2022-08-04 12:07 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-08-04 12:07 UTC (permalink / raw)
To: Lev Stipakov <lstipakov@; +Cc: openvpn-devel
Hi,
On 04/08/2022 13:49, Lev Stipakov wrote:
> Hi,
>
>> +static bool
>> +can_preserve_tun(struct tuntap *tt)
>> +{
>> +#ifdef TARGET_ANDROID
>> + return false;
>> +#elif _WIN32
>> + return tt && tt->dco.real_tun_init;
>
> Looks like dco.real_tun_init has been added in the very next patch, 22/25.
Darn - must have missed this during the patch split up.
Thanks for pointing this out.
Cheers,
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (20 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 21/25] do_open_tun: restyle "can preserve TUN" check Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-06-27 10:38 ` Lev Stipakov
` (2 more replies)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path Antonio Quartulli
` (2 subsequent siblings)
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Arne Schwabe <arne@...1227...>
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
config-msvc.h | 2 +
configure.ac | 9 +-
dev-tools/special-files.lst | 1 +
src/openvpn/Makefile.am | 4 +-
src/openvpn/dco_internal.h | 1 +
src/openvpn/dco_win.c | 402 ++++++++++++++++++++++++++++
src/openvpn/dco_win.h | 58 ++++
src/openvpn/openvpn.vcxproj | 3 +
src/openvpn/openvpn.vcxproj.filters | 9 +
src/openvpn/ovpn-dco-win.h | 108 ++++++++
10 files changed, 593 insertions(+), 4 deletions(-)
create mode 100644 src/openvpn/dco_win.c
create mode 100644 src/openvpn/dco_win.h
create mode 100644 src/openvpn/ovpn-dco-win.h
diff --git a/config-msvc.h b/config-msvc.h
index b08beb52..b621f3fb 100644
--- a/config-msvc.h
+++ b/config-msvc.h
@@ -87,3 +87,5 @@ typedef uint16_t in_port_t;
#ifdef HAVE_CONFIG_MSVC_LOCAL_H
#include <config-msvc-local.h>
#endif
+
+#define ENABLE_DCO 1
diff --git a/configure.ac b/configure.ac
index 353da08c..bebed1ac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,7 +144,7 @@ AC_ARG_ENABLE(
AC_ARG_ENABLE(
[dco],
- [AS_HELP_STRING([--enable-dco], [enable data channel offload support using ovpn-dco kernel module @<:@default=no@:>@])],
+ [AS_HELP_STRING([--enable-dco], [enable data channel offload support using the ovpn-dco kernel module (always enabled on Windows) @<:@default=no@:>@])],
,
[enable_dco="no"]
)
@@ -328,6 +328,7 @@ case "$host" in
;;
*-mingw*)
AC_DEFINE([TARGET_WIN32], [1], [Are we running WIN32?])
+ AC_DEFINE([ENABLE_DCO], [1], [DCO is always enabled on Windows])
AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["W"], [Target prefix])
CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN"
CPPFLAGS="${CPPFLAGS} -DNTDDI_VERSION=NTDDI_VISTA -D_WIN32_WINNT=_WIN32_WINNT_VISTA"
@@ -772,7 +773,6 @@ 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],
@@ -787,8 +787,11 @@ dnl
AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload])
AC_MSG_NOTICE([Enabled ovpn-dco support for Linux])
;;
+ *-mingw*)
+ AC_MSG_NOTICE([NOTE: --enable-dco ignored on Windows because it's always enabled])
+ ;;
*)
- AC_MSG_NOTICE([Ignoring --enable-dco on non Linux platform])
+ AC_MSG_NOTICE([Ignoring --enable-dco on non supported platform])
;;
esac
fi
diff --git a/dev-tools/special-files.lst b/dev-tools/special-files.lst
index 33e830d7..6d0bc055 100644
--- a/dev-tools/special-files.lst
+++ b/dev-tools/special-files.lst
@@ -2,3 +2,4 @@ E:doc/doxygen/doc_key_generation.h # @verbatim section gets mistreated, excl
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
+E:src/openvpn/ovpn-dco-win.h # Preserve ovpn-dco-win upstream formatting
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index aaa1dbce..8d0e66b4 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -55,6 +55,7 @@ openvpn_SOURCES = \
crypto_mbedtls.c crypto_mbedtls.h \
dco.c dco.h dco_internal.h \
dco_linux.c dco_linux.h \
+ dco_win.c dco_win.h \
dhcp.c dhcp.h \
dns.c dns.h \
env_set.c env_set.h \
@@ -77,6 +78,7 @@ openvpn_SOURCES = \
mbuf.c mbuf.h \
memdbg.h \
misc.c misc.h \
+ ovpn-dco-win.h \
ovpn_dco_linux.h \
platform.c platform.h \
console.c console.h console_builtin.c console_systemd.c \
@@ -151,5 +153,5 @@ openvpn_LDADD = \
$(OPTIONAL_INOTIFY_LIBS)
if WIN32
openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h ring_buffer.h
-openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt -lsetupapi
+openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt -lsetupapi -lbcrypt
endif
diff --git a/src/openvpn/dco_internal.h b/src/openvpn/dco_internal.h
index 3ceb26d6..968d4042 100644
--- a/src/openvpn/dco_internal.h
+++ b/src/openvpn/dco_internal.h
@@ -28,6 +28,7 @@
#if defined(ENABLE_DCO)
#include "dco_linux.h"
+#include "dco_win.h"
/**
* This file contains the internal DCO API definition.
diff --git a/src/openvpn/dco_win.c b/src/openvpn/dco_win.c
new file mode 100644
index 00000000..8dfe8ecb
--- /dev/null
+++ b/src/openvpn/dco_win.c
@@ -0,0 +1,402 @@
+/*
+ * Interface to ovpn-win-dco networking code
+ *
+ * Copyright (C) 2020-2022 Arne Schwabe <arne@...1227...>
+ * Copyright (C) 2020-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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(_WIN32)
+
+#include "syshead.h"
+
+#include "dco.h"
+#include "tun.h"
+#include "crypto.h"
+#include "ssl_common.h"
+
+#include <bcrypt.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#if defined(__MINGW32__)
+const IN_ADDR in4addr_any = { 0 };
+#endif
+
+static struct tuntap
+create_dco_handle(const char *devname, struct gc_arena *gc)
+{
+ struct tuntap tt = { .windows_driver = WINDOWS_DRIVER_WINDCO };
+ const char *device_guid;
+
+ tun_open_device(&tt, devname, &device_guid, gc);
+
+ return tt;
+}
+
+bool
+ovpn_dco_init(int mode, dco_context_t *dco)
+{
+ return true;
+}
+
+int
+open_tun_dco(struct tuntap *tt, openvpn_net_ctx_t *ctx, const char *dev)
+{
+ ASSERT(0);
+ return 0;
+}
+
+static void
+dco_wait_ready(DWORD idx)
+{
+ for (int i = 0; i < 20; ++i)
+ {
+ MIB_IPINTERFACE_ROW row = {.InterfaceIndex = idx, .Family = AF_INET};
+ if (GetIpInterfaceEntry(&row) != ERROR_NOT_FOUND)
+ {
+ break;
+ }
+ msg(D_DCO_DEBUG, "interface %ld not yet ready, retrying", idx);
+ Sleep(50);
+ }
+}
+
+void
+dco_start_tun(struct tuntap *tt)
+{
+ msg(D_DCO_DEBUG, "%s", __func__);
+
+ /* reference the tt object inside the DCO context, because the latter will
+ * be passed around
+ */
+ tt->dco.tt = tt;
+
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(tt->hand, OVPN_IOCTL_START_VPN, NULL, 0, NULL, 0,
+ &bytes_returned, NULL))
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_START_VPN) failed with code %lu",
+ GetLastError());
+ }
+
+ /* Sometimes IP Helper API, which we use for setting IP address etc,
+ * complains that interface is not found. Give it some time to settle
+ */
+ dco_wait_ready(tt->adapter_index);
+}
+
+static int
+dco_connect_wait(HANDLE handle, OVERLAPPED *ov, int timeout, volatile int *signal_received)
+{
+ DWORD timeout_msec = timeout * 1000;
+ const int poll_interval_ms = 50;
+
+ while (timeout_msec > 0)
+ {
+ timeout_msec -= poll_interval_ms;
+
+ DWORD transferred;
+ if (dco_get_overlapped_result(handle, ov, &transferred, poll_interval_ms, FALSE) != 0)
+ {
+ /* TCP connection established by dco */
+ return 0;
+ }
+
+ DWORD err = GetLastError();
+ if ((err != WAIT_TIMEOUT) && (err != ERROR_IO_INCOMPLETE))
+ {
+ /* dco reported connection error */
+ struct gc_arena gc = gc_new();
+ msg(M_NONFATAL, "%s: %s", __func__, strerror_win32(err, &gc));
+ *signal_received = SIGUSR1;
+ gc_free(&gc);
+ return -1;
+ }
+
+ get_signal(signal_received);
+ if (*signal_received)
+ {
+ return -1;
+ }
+
+ management_sleep(0);
+ }
+
+ /* we end up here when timeout occurs in userspace */
+ msg(M_NONFATAL, "%s: dco connect timeout", __func__);
+ *signal_received = SIGUSR1;
+
+ return -1;
+}
+
+struct tuntap
+dco_create_socket(struct addrinfo *remoteaddr, bool bind_local,
+ struct addrinfo *bind, const char *devname,
+ struct gc_arena *gc, int timeout,
+ volatile int *signal_received)
+{
+ msg(D_DCO_DEBUG, "%s", __func__);
+
+ OVPN_NEW_PEER peer = { 0 };
+
+ struct sockaddr *local = NULL;
+ struct sockaddr *remote = remoteaddr->ai_addr;
+
+ if (remoteaddr->ai_protocol == IPPROTO_TCP
+ || remoteaddr->ai_socktype == SOCK_STREAM)
+ {
+ peer.Proto = OVPN_PROTO_TCP;
+ }
+ else
+ {
+ peer.Proto = OVPN_PROTO_UDP;
+ }
+
+ if (bind_local)
+ {
+ /* Use first local address with correct address family */
+ while (bind && !local)
+ {
+ if (bind->ai_family == remote->sa_family)
+ {
+ local = bind->ai_addr;
+ }
+ bind = bind->ai_next;
+ }
+ }
+
+ if (bind_local && !local)
+ {
+ msg(M_FATAL, "DCO: Socket bind failed: Address to bind lacks %s record",
+ addr_family_name(remote->sa_family));
+ }
+
+ if (remote->sa_family == AF_INET6)
+ {
+ peer.Remote.Addr6 = *((SOCKADDR_IN6 *)(remoteaddr->ai_addr));
+ if (local)
+ {
+ peer.Local.Addr6 = *((SOCKADDR_IN6 *)local);
+ }
+ else
+ {
+ peer.Local.Addr6.sin6_addr = in6addr_any;
+ peer.Local.Addr6.sin6_port = 0;
+ peer.Local.Addr6.sin6_family = AF_INET6;
+ }
+ }
+ else if (remote->sa_family == AF_INET)
+ {
+ peer.Remote.Addr4 = *((SOCKADDR_IN *)(remoteaddr->ai_addr));
+ if (local)
+ {
+ peer.Local.Addr4 = *((SOCKADDR_IN *)local);
+ }
+ else
+ {
+ peer.Local.Addr4.sin_addr = in4addr_any;
+ peer.Local.Addr4.sin_port = 0;
+ peer.Local.Addr4.sin_family = AF_INET;
+ }
+ }
+ else
+ {
+ ASSERT(0);
+ }
+
+ struct tuntap tt = create_dco_handle(devname, gc);
+
+ OVERLAPPED ov = { 0 };
+ if (!DeviceIoControl(tt.hand, OVPN_IOCTL_NEW_PEER, &peer, sizeof(peer), NULL, 0, NULL, &ov))
+ {
+ DWORD err = GetLastError();
+ if (err != ERROR_IO_PENDING)
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_PEER) failed with code %lu", err);
+ }
+ else
+ {
+ if (dco_connect_wait(tt.hand, &ov, timeout, signal_received) < 0)
+ {
+ close_tun_handle(&tt);
+ }
+ }
+ }
+ return tt;
+}
+
+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);
+ return 0;
+}
+
+int
+dco_del_peer(dco_context_t *dco, unsigned int peerid)
+{
+ msg(D_DCO_DEBUG, "%s: peer-id %d - not implemented", __func__, peerid);
+ return 0;
+}
+
+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);
+
+ OVPN_SET_PEER peer;
+
+ peer.KeepaliveInterval = keepalive_interval;
+ peer.KeepaliveTimeout = keepalive_timeout;
+ peer.MSS = mss;
+
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SET_PEER, &peer,
+ sizeof(peer), NULL, 0, &bytes_returned, NULL))
+ {
+ msg(M_WARN, "DeviceIoControl(OVPN_IOCTL_SET_PEER) failed with code %lu", GetLastError());
+ return -1;
+ }
+ return 0;
+}
+
+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 int nonce_len = 8;
+ size_t key_len = cipher_kt_key_size(ciphername);
+
+ OVPN_CRYPTO_DATA crypto_data;
+ ZeroMemory(&crypto_data, sizeof(crypto_data));
+
+ crypto_data.CipherAlg = dco_get_cipher(ciphername);
+ crypto_data.KeyId = keyid;
+ crypto_data.PeerId = peerid;
+ crypto_data.KeySlot = slot;
+
+ CopyMemory(crypto_data.Encrypt.Key, encrypt_key, key_len);
+ crypto_data.Encrypt.KeyLen = (char)key_len;
+ CopyMemory(crypto_data.Encrypt.NonceTail, encrypt_iv, nonce_len);
+
+ CopyMemory(crypto_data.Decrypt.Key, decrypt_key, key_len);
+ crypto_data.Decrypt.KeyLen = (char)key_len;
+ CopyMemory(crypto_data.Decrypt.NonceTail, decrypt_iv, nonce_len);
+
+ ASSERT(crypto_data.CipherAlg > 0);
+
+ DWORD bytes_returned = 0;
+
+ if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_NEW_KEY, &crypto_data,
+ sizeof(crypto_data), NULL, 0, &bytes_returned, NULL))
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_KEY) failed with code %lu",
+ GetLastError());
+ return -1;
+ }
+ return 0;
+}
+int
+dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot)
+{
+ msg(D_DCO, "%s: peer-id %d, slot %d called but ignored", __func__, peerid,
+ slot);
+ /* FIXME: Implement in driver first */
+ return 0;
+}
+
+int
+dco_swap_keys(dco_context_t *dco, unsigned int peer_id)
+{
+ msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id);
+
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SWAP_KEYS, NULL, 0, NULL, 0,
+ &bytes_returned, NULL))
+ {
+ msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_SWAP_KEYS) failed with code %lu",
+ GetLastError());
+ return -1;
+ }
+ return 0;
+}
+
+bool
+dco_available(int msglevel)
+{
+ return true;
+}
+
+int
+dco_do_read(dco_context_t *dco)
+{
+ /* no-op on windows */
+ return 0;
+}
+
+int
+dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf)
+{
+ /* no-op on windows */
+ return 0;
+}
+
+void
+dco_event_set(dco_context_t *dco, struct event_set *es, void *arg)
+{
+ /* no-op on windows */
+}
+
+const char *
+dco_get_supported_ciphers()
+{
+ /*
+ * this API can be called either from user mode or kernel mode,
+ * which enables us to probe driver's chachapoly support
+ * (available starting from Windows 11)
+ */
+
+ BCRYPT_ALG_HANDLE h;
+ NTSTATUS status = BCryptOpenAlgorithmProvider(&h, L"CHACHA20_POLY1305", NULL, 0);
+ if (BCRYPT_SUCCESS(status))
+ {
+ BCryptCloseAlgorithmProvider(h, 0);
+ return "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305";
+ }
+ else
+ {
+ return "AES-128-GCM:AES-256-GCM:AES-192-GCM";
+ }
+}
+
+#endif /* defined(_WIN32) */
diff --git a/src/openvpn/dco_win.h b/src/openvpn/dco_win.h
new file mode 100644
index 00000000..3b746dd8
--- /dev/null
+++ b/src/openvpn/dco_win.h
@@ -0,0 +1,58 @@
+/*
+ * Interface to ovpn-win-dco networking code
+ *
+ * Copyright (C) 2020-2022 Arne Schwabe <arne@...1227...>
+ * Copyright (C) 2020-2022 OpenVPN Inc <sales@...515...>
+ *
+ * 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_WIN_H
+#define DCO_WIN_H
+
+#if defined(ENABLE_DCO) && defined(_WIN32)
+
+#include "buffer.h"
+#include "ovpn-dco-win.h"
+
+typedef OVPN_KEY_SLOT dco_key_slot_t;
+typedef OVPN_CIPHER_ALG dco_cipher_t;
+
+struct dco_context {
+ bool real_tun_init;
+ struct tuntap *tt;
+};
+
+typedef struct dco_context dco_context_t;
+
+struct tuntap
+dco_create_socket(struct addrinfo *remoteaddr, bool bind_local,
+ struct addrinfo *bind, const char *devname,
+ struct gc_arena *gc, int timeout,
+ volatile int *signal_received);
+
+void
+dco_start_tun(struct tuntap *tt);
+
+#else /* if defined(ENABLE_DCO) && defined(_WIN32) */
+
+static inline void
+dco_start_tun(struct tuntap *tt)
+{
+ ASSERT(false);
+}
+
+#endif /* defined(_WIN32) */
+#endif /* ifndef DCO_H */
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index 0b3db7c7..51e8fe79 100644
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -278,6 +278,7 @@
<ClCompile Include="cryptoapi.c" />
<ClCompile Include="dco.c" />
<ClCompile Include="dco_linux.c" />
+ <ClCompile Include="dco_win.c" />
<ClCompile Include="dhcp.c" />
<ClCompile Include="dns.c" />
<ClCompile Include="env_set.c" />
@@ -367,6 +368,7 @@
<ClInclude Include="dco.h" />
<ClInclude Include="dco_internal.h" />
<ClInclude Include="dco_linux.h" />
+ <ClInclude Include="dco_win.h" />
<ClInclude Include="dhcp.h" />
<ClInclude Include="dns.h" />
<ClInclude Include="env_set.h" />
@@ -401,6 +403,7 @@
<ClInclude Include="openvpn.h" />
<ClInclude Include="options.h" />
<ClInclude Include="otime.h" />
+ <ClInclude Include="ovpn-dco-win.h" />
<ClInclude Include="ovpn_dco_linux.h" />
<ClInclude Include="packet_id.h" />
<ClInclude Include="perf.h" />
diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters
index 16905079..117270bb 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -42,6 +42,9 @@
<ClCompile Include="dco_linux.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dco_win.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="dhcp.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -314,6 +317,9 @@
<ClInclude Include="dco_linux.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="dco_win.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="dhcp.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -413,6 +419,9 @@
<ClInclude Include="otime.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="ovpn-dco-win.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="ovpn_dco_linux.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/src/openvpn/ovpn-dco-win.h b/src/openvpn/ovpn-dco-win.h
new file mode 100644
index 00000000..1ebd51a7
--- /dev/null
+++ b/src/openvpn/ovpn-dco-win.h
@@ -0,0 +1,108 @@
+/*
+ * ovpn-dco-win OpenVPN protocol accelerator for Windows
+ *
+ * Copyright (C) 2020-2021 OpenVPN Inc <sales@...515...>
+ *
+ * Author: Lev Stipakov <lev@...515...>
+ *
+ * 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; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * This particular file (uapi.h) is also licensed using the MIT license (see COPYRIGHT.MIT).
+ */
+
+#pragma once
+#ifndef _KERNEL_MODE
+#include <winsock2.h>
+#endif
+#include <ws2def.h>
+#include <ws2ipdef.h>
+
+typedef enum {
+ OVPN_PROTO_UDP,
+ OVPN_PROTO_TCP
+} OVPN_PROTO;
+
+typedef struct _OVPN_NEW_PEER {
+ union {
+ SOCKADDR_IN Addr4;
+ SOCKADDR_IN6 Addr6;
+ } Local;
+
+ union {
+ SOCKADDR_IN Addr4;
+ SOCKADDR_IN6 Addr6;
+ } Remote;
+
+ OVPN_PROTO Proto;
+} OVPN_NEW_PEER, * POVPN_NEW_PEER;
+
+typedef struct _OVPN_STATS {
+ LONG LostInControlPackets;
+ LONG LostOutControlPackets;
+
+ LONG LostInDataPackets;
+ LONG LostOutDataPackets;
+
+ LONG ReceivedDataPackets;
+ LONG ReceivedControlPackets;
+
+ LONG SentControlPackets;
+ LONG SentDataPackets;
+
+ LONG64 TransportBytesSent;
+ LONG64 TransportBytesReceived;
+
+ LONG64 TunBytesSent;
+ LONG64 TunBytesReceived;
+} OVPN_STATS, * POVPN_STATS;
+
+typedef enum _OVPN_KEY_SLOT {
+ OVPN_KEY_SLOT_PRIMARY,
+ OVPN_KEY_SLOT_SECONDARY
+} OVPN_KEY_SLOT;
+
+typedef enum _OVPN_CIPHER_ALG {
+ OVPN_CIPHER_ALG_NONE,
+ OVPN_CIPHER_ALG_AES_GCM,
+ OVPN_CIPHER_ALG_CHACHA20_POLY1305
+} OVPN_CIPHER_ALG;
+
+typedef struct _OVPN_KEY_DIRECTION
+{
+ unsigned char Key[32];
+ unsigned char KeyLen; // 16/24/32 -> AES-128-GCM/AES-192-GCM/AES-256-GCM
+ unsigned char NonceTail[8];
+} OVPN_KEY_DIRECTION;
+
+typedef struct _OVPN_CRYPTO_DATA {
+ OVPN_KEY_DIRECTION Encrypt;
+ OVPN_KEY_DIRECTION Decrypt;
+ OVPN_KEY_SLOT KeySlot;
+ OVPN_CIPHER_ALG CipherAlg;
+ unsigned char KeyId;
+ int PeerId;
+} OVPN_CRYPTO_DATA, * POVPN_CRYPTO_DATA;
+
+typedef struct _OVPN_SET_PEER {
+ LONG KeepaliveInterval;
+ LONG KeepaliveTimeout;
+ LONG MSS;
+} OVPN_SET_PEER, * POVPN_SET_PEER;
+
+#define OVPN_IOCTL_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_GET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_NEW_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_SWAP_KEYS CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define OVPN_IOCTL_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows Antonio Quartulli
@ 2022-06-27 10:38 ` Lev Stipakov
2022-06-28 8:36 ` Lev Stipakov
2022-07-05 12:32 ` Heiko Hund
2 siblings, 0 replies; 157+ messages in thread
From: Lev Stipakov @ 2022-06-27 10:38 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel, Lev Stipakov <lev@
Hi,
> + if (dco_get_overlapped_result(handle, ov, &transferred, poll_interval_ms, FALSE) != 0)
With the removal of mingw compat patch ("dco-win: implement
GetOverlappedResultEx for mingw32"), this needs to be changed to
GetOverlappedResultEx (signature is the same).
-Lev
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows Antonio Quartulli
2022-06-27 10:38 ` Lev Stipakov
@ 2022-06-28 8:36 ` Lev Stipakov
2022-07-05 12:32 ` Heiko Hund
2 siblings, 0 replies; 157+ messages in thread
From: Lev Stipakov @ 2022-06-28 8:36 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Hi,
I did some review / testing and suggest following changes to this patch:
> + if (!DeviceIoControl(tt->hand, OVPN_IOCTL_START_VPN, NULL, 0, NULL, 0,
> + &bytes_returned, NULL))
> + {
> + msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_START_VPN) failed with code %lu",
> + GetLastError());
> + }
Since recently openvpn correctly handles Win32 errors (doesn't mix
them with CRT errors)
so this could be simplified like:
msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_START_VPN) failed");
M_ERR prints error message along with error code.
<snip>
> + /* dco reported connection error */
> + struct gc_arena gc = gc_new();
> + msg(M_NONFATAL, "%s: %s", __func__, strerror_win32(err, &gc));
> + *signal_received = SIGUSR1;
> + gc_free(&gc);
Ditto, so
msg(M_NONFATAL | M_ERRNO, "dco connect error");
(no need to instantiate gc_arena)
<snip>
> + /* we end up here when timeout occurs in userspace */
> + msg(M_NONFATAL, "%s: dco connect timeout", __func__);
I would just use
msg(M_NONFATAL, "dco connect timeout");
<snip>
> + if (err != ERROR_IO_PENDING)
> + {
> + msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_PEER) failed with code %lu", err);
> + }
Ditto:
msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_PEER) failed");
<snip>
> + DWORD bytes_returned = 0;
> + if (!DeviceIoControl(dco->tt->hand, OVPN_IOCTL_SET_PEER, &peer,
> + sizeof(peer), NULL, 0, &bytes_returned, NULL))
> + {
> + msg(M_WARN, "DeviceIoControl(OVPN_IOCTL_SET_PEER) failed with code %lu", GetLastError());
> + return -1;
> + }
Ditto:
msg(M_WARN | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_SET_PEER) failed");
<snip>
> + msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_KEY) failed with code %lu",
> + GetLastError());
Same as above:
msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_NEW_KEY) failed");
<snip>
> + msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_SWAP_KEYS) failed with code %lu",
> + GetLastError());
Ditto:
msg(M_ERR, "DeviceIoControl(OVPN_IOCTL_SWAP_KEYS) failed");
We have discussed all those changes in chat and a new version with the
changes is pushed to the dco branch.
-Lev
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows Antonio Quartulli
2022-06-27 10:38 ` Lev Stipakov
2022-06-28 8:36 ` Lev Stipakov
@ 2022-07-05 12:32 ` Heiko Hund
2022-07-05 12:40 ` Antonio Quartulli
2 siblings, 1 reply; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:32 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:38:06 CEST Antonio Quartulli wrote:
> +int
> +dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot)
> +{
> + msg(D_DCO, "%s: peer-id %d, slot %d called but ignored", __func__,
> peerid, + slot);
> + /* FIXME: Implement in driver first */
Suppose this is not critical and the driver gets rid of keys itself?
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows
2022-07-05 12:32 ` Heiko Hund
@ 2022-07-05 12:40 ` Antonio Quartulli
0 siblings, 0 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-05 12:40 UTC (permalink / raw)
To: Heiko Hund <heiko@
Hi,
On 05/07/2022 14:32, Heiko Hund wrote:
> On Freitag, 24. Juni 2022 10:38:06 CEST Antonio Quartulli wrote:
>> +int
>> +dco_del_key(dco_context_t *dco, unsigned int peerid, dco_key_slot_t slot)
>> +{
>> + msg(D_DCO, "%s: peer-id %d, slot %d called but ignored", __func__,
>> peerid, + slot);
>> + /* FIXME: Implement in driver first */
>
> Suppose this is not critical and the driver gets rid of keys itself?
Yeah, the dco-win driver does not offer the same API granularity because
it does not want/need to cover all the usecases that linux does.
So yeah, this simply happens in kernel directly upon disconnection.
Cheers,
>
>
>
>
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
>
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (21 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 22/25] dco-win: introduce low-level code for handling ovpn-dco-win in Windows Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-06-28 8:46 ` Lev Stipakov
` (2 more replies)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 24/25] dco-win: add documentation to README.dco.md Antonio Quartulli
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file Antonio Quartulli
24 siblings, 3 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
With this change it is possible to use ovpn-dco-win when running OpenVPN
in client or P2P mode.
Signed-off-by: Arne Schwabe <arne@...1227...>
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
src/openvpn/forward.c | 7 ++++
src/openvpn/init.c | 29 +++++++++++--
src/openvpn/options.c | 19 ++++++---
src/openvpn/options.h | 15 +++----
src/openvpn/socket.c | 94 ++++++++++++++++++++++++++++++++++++++++---
src/openvpn/socket.h | 25 ++++++++----
src/openvpn/tun.c | 43 +++++++++++++++-----
src/openvpn/tun.h | 58 ++++++++++++++++++--------
8 files changed, 233 insertions(+), 57 deletions(-)
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 15bdbbae..1fbad43f 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -860,6 +860,13 @@ read_incoming_link(struct context *c)
/* check recvfrom status */
check_status(status, "read", c->c2.link_socket, NULL);
+#ifdef _WIN32
+ if (dco_enabled(&c->options) && (status < 0) && (openvpn_errno() == ERROR_NETNAME_DELETED))
+ {
+ trigger_ping_timeout_signal(c);
+ }
+#endif
+
/* Remove socks header if applicable */
socks_postprocess_incoming_link(c);
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index f95dcb93..77f36a1a 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1673,7 +1673,8 @@ do_init_tun(struct context *c)
c->c1.link_socket_addr.remote_list,
!c->options.ifconfig_nowarn,
c->c2.es,
- &c->net_ctx);
+ &c->net_ctx,
+ c->c1.tuntap);
#ifdef _WIN32
c->c1.tuntap->windows_driver = c->options.windows_driver;
@@ -1787,9 +1788,12 @@ do_open_tun(struct context *c)
ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
}
- /* open the tun device */
- open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap, &c->net_ctx);
+ /* open the tun device (ovpn-dco-win already opened the device for the socket) */
+ if (!is_windco(c->c1.tuntap))
+ {
+ open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
+ c->c1.tuntap, &c->net_ctx);
+ }
/* set the hardware address */
if (c->options.lladdr)
@@ -3541,6 +3545,23 @@ do_close_free_key_schedule(struct context *c, bool free_ssl_ctx)
static void
do_close_link_socket(struct context *c)
{
+#if defined(_WIN32)
+ if (is_windco(c->c1.tuntap) && c->c2.link_socket
+ && c->c2.link_socket->info.dco_installed)
+ {
+ ASSERT(c->c2.link_socket_owned);
+
+ /* We rely on the tun_close to close the handle if also setup
+ * routes etc, since they cannot be delete when the interface
+ * handle has been closed */
+ if (!c->c1.tuntap->dco.real_tun_init)
+ {
+ do_close_tun_simple(c);
+ }
+ c->c2.link_socket->sd = SOCKET_UNDEFINED;
+ }
+#endif
+
if (c->c2.link_socket && c->c2.link_socket_owned)
{
link_socket_close(c->c2.link_socket);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index d9da8b8b..00823604 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3151,8 +3151,8 @@ options_postprocess_mutate_invariant(struct options *options)
#ifdef _WIN32
const int dev = dev_type_enum(options->dev, options->dev_type);
- /* when using wintun, kernel doesn't send DHCP requests, so don't use it */
- if (options->windows_driver == WINDOWS_DRIVER_WINTUN
+ /* when using wintun/ovpn-dco-win, kernel doesn't send DHCP requests, so don't use it */
+ if ((options->windows_driver == WINDOWS_DRIVER_WINTUN || options->windows_driver == WINDOWS_DRIVER_WINDCO)
&& (options->tuntap_options.ip_win32_type == IPW32_SET_DHCP_MASQ || options->tuntap_options.ip_win32_type == IPW32_SET_ADAPTIVE))
{
options->tuntap_options.ip_win32_type = IPW32_SET_NETSH;
@@ -3248,10 +3248,12 @@ options_postprocess_setdefault_ncpciphers(struct options *o)
/* custom --data-ciphers set, keep list */
return;
}
+#if !defined(_WIN32)
else if (cipher_valid("CHACHA20-POLY1305"))
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";
}
+#endif
else
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM";
@@ -4076,7 +4078,8 @@ options_string(const struct options *o,
NULL,
false,
NULL,
- ctx);
+ ctx,
+ NULL);
if (tt)
{
tt_local = true;
@@ -4463,13 +4466,19 @@ parse_windows_driver(const char *str, const int msglevel)
{
return WINDOWS_DRIVER_WINTUN;
}
+
+ else if (streq(str, "ovpn-dco-win"))
+ {
+ return WINDOWS_DRIVER_WINDCO;
+ }
else
{
- msg(msglevel, "--windows-driver must be tap-windows6 or wintun");
+ msg(msglevel, "--windows-driver must be tap-windows6, wintun "
+ "or ovpn-dco-win");
return WINDOWS_DRIVER_UNSPECIFIED;
}
}
-#endif
+#endif /* ifdef _WIN32 */
/*
* parse/print topology coding
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 8152e755..3a5b433e 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -879,24 +879,19 @@ void options_string_import(struct options *options,
bool key_is_external(const struct options *options);
-#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
-
/**
* Returns whether the current configuration has dco enabled.
*/
static inline bool
dco_enabled(const struct options *o)
{
+#if defined(_WIN32)
+ return o->windows_driver == WINDOWS_DRIVER_WINDCO;
+#elif defined(ENABLE_DCO)
return !o->tuntap_options.disable_dco;
-}
-
-#else /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
-
-static inline bool
-dco_enabled(const struct options *o)
-{
+#else
return false;
+#endif /* defined(_WIN32) */
}
-#endif
#endif /* ifndef OPTIONS_H */
diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
index 4e4a3a2f..baca86c3 100644
--- a/src/openvpn/socket.c
+++ b/src/openvpn/socket.c
@@ -2119,6 +2119,38 @@ phase2_socks_client(struct link_socket *sock, struct signal_info *sig_info)
resolve_remote(sock, 1, NULL, &sig_info->signal_received);
}
+#if defined(_WIN32)
+static void
+create_socket_windco(struct context *c, struct link_socket *sock,
+ volatile int *signal_received)
+{
+ struct tuntap *tt;
+ /* In this case persist-tun is enabled, which we don't support yet */
+ ASSERT(!c->c1.tuntap);
+
+ ALLOC_OBJ(tt, struct tuntap);
+
+ *tt = dco_create_socket(sock->info.lsa->current_remote,
+ sock->bind_local,
+ sock->info.lsa->bind_local,
+ c->options.dev_node,
+ &c->gc,
+ get_server_poll_remaining_time(sock->server_poll_timeout),
+ signal_received);
+ if (*signal_received)
+ {
+ return;
+ }
+
+ c->c1.tuntap = tt;
+ sock->info.dco_installed = true;
+
+ /* Ensure we can "safely" cast the handle to a socket */
+ static_assert(sizeof(sock->sd) == sizeof(tt->hand), "HANDLE and SOCKET size differs");
+ sock->sd = (SOCKET)tt->hand;
+}
+#endif /* if defined(_WIN32) */
+
/* finalize socket initialization */
void
link_socket_init_phase2(struct context *c)
@@ -2158,7 +2190,24 @@ link_socket_init_phase2(struct context *c)
/* If a valid remote has been found, create the socket with its addrinfo */
if (sock->info.lsa->current_remote)
{
- create_socket(sock, sock->info.lsa->current_remote);
+#if defined(_WIN32)
+ if (dco_enabled(&c->options))
+ {
+ create_socket_windco(c, sock, &sig_info->signal_received);
+ if (sig_info->signal_received)
+ {
+ goto done;
+ }
+
+ linksock_print_addr(sock);
+ goto done;
+ }
+ else
+#endif
+ {
+ create_socket(sock, sock->info.lsa->current_remote);
+ }
+
}
/* If socket has not already been created create it now */
@@ -3459,7 +3508,14 @@ socket_recv_queue(struct link_socket *sock, int maxsize)
ASSERT(ResetEvent(sock->reads.overlapped.hEvent));
sock->reads.flags = 0;
- if (proto_is_udp(sock->info.proto))
+ if (sock->info.dco_installed)
+ {
+ status = ReadFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len,
+ &sock->reads.size, &sock->reads.overlapped);
+ /* Readfile status is inverted from WSARecv */
+ status = !status;
+ }
+ else if (proto_is_udp(sock->info.proto))
{
sock->reads.addr_defined = true;
sock->reads.addrlen = sizeof(sock->reads.addr6);
@@ -3512,7 +3568,14 @@ socket_recv_queue(struct link_socket *sock, int maxsize)
}
else
{
- status = WSAGetLastError();
+ if (sock->info.dco_installed)
+ {
+ status = GetLastError();
+ }
+ else
+ {
+ status = WSAGetLastError();
+ }
if (status == WSA_IO_PENDING) /* operation queued? */
{
sock->reads.iostate = IOSTATE_QUEUED;
@@ -3557,7 +3620,16 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
ASSERT(ResetEvent(sock->writes.overlapped.hEvent));
sock->writes.flags = 0;
- if (proto_is_udp(sock->info.proto))
+ if (sock->info.dco_installed)
+ {
+ status = WriteFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len,
+ &sock->writes.size, &sock->writes.overlapped);
+
+ /* WriteFile status is inverted from WSASendTo */
+ status = !status;
+
+ }
+ else if (proto_is_udp(sock->info.proto))
{
/* set destination address for UDP writes */
sock->writes.addr_defined = true;
@@ -3618,8 +3690,17 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
}
else
{
- status = WSAGetLastError();
- if (status == WSA_IO_PENDING) /* operation queued? */
+ if (sock->info.dco_installed)
+ {
+ status = GetLastError();
+ }
+ else
+ {
+ status = WSAGetLastError();
+ }
+
+ /* both status code have the identical value */
+ if (status == WSA_IO_PENDING || status == ERROR_IO_PENDING) /* operation queued? */
{
sock->writes.iostate = IOSTATE_QUEUED;
sock->writes.status = status;
@@ -3644,6 +3725,7 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
return sock->writes.iostate;
}
+/* Returns the number of bytes successfully read */
int
sockethandle_finalize(sockethandle_t sh,
struct overlapped_io *io,
diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h
index 0d521d22..462afa31 100644
--- a/src/openvpn/socket.h
+++ b/src/openvpn/socket.h
@@ -34,6 +34,7 @@
#include "proxy.h"
#include "socks.h"
#include "misc.h"
+#include "tun.h"
/*
* OpenVPN's default port number as assigned by IANA.
@@ -937,7 +938,8 @@ socket_connection_reset(const struct link_socket *sock, int status)
{
const int err = openvpn_errno();
#ifdef _WIN32
- return err == WSAECONNRESET || err == WSAECONNABORTED;
+ return err == WSAECONNRESET || err == WSAECONNABORTED
+ || err == ERROR_CONNECTION_ABORTED;
#else
return err == ECONNRESET;
#endif
@@ -1048,6 +1050,11 @@ link_socket_read_udp_win32(struct link_socket *sock,
struct link_socket_actual *from)
{
sockethandle_t sh = { .s = sock->sd };
+ if (sock->info.dco_installed)
+ {
+ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest);
+ sh.is_handle = true;
+ }
return sockethandle_finalize(sh, &sock->reads, buf, from);
}
@@ -1057,7 +1064,7 @@ int link_socket_read_udp_posix(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from);
-#endif
+#endif /* ifdef _WIN32 */
/* read a TCP or UDP packet from link */
static inline int
@@ -1065,7 +1072,10 @@ link_socket_read(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from)
{
- if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
+ if (proto_is_udp(sock->info.proto)
+ || sock->info.dco_installed)
+ /* unified UDPv4 and UDPv6, for DCO the kernel
+ * will strip the length header */
{
int res;
@@ -1106,19 +1116,19 @@ link_socket_write_win32(struct link_socket *sock,
{
int err = 0;
int status = 0;
- sockethandle_t sh = { .s = sock->sd };
+ sockethandle_t sh = { .s = sock->sd, .is_handle = sock->info.dco_installed };
if (overlapped_io_active(&sock->writes))
{
status = sockethandle_finalize(sh, &sock->writes, NULL, NULL);
if (status < 0)
{
- err = WSAGetLastError();
+ err = SocketHandleGetLastError(sh);
}
}
socket_send_queue(sock, buf, to);
if (status < 0)
{
- WSASetLastError(err);
+ SocketHandleSetLastError(sh, err);
return status;
}
else
@@ -1180,8 +1190,9 @@ link_socket_write(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to)
{
- if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
+ if (proto_is_udp(sock->info.proto) || sock->info.dco_installed)
{
+ /* unified UDPv4 and UDPv6 and DCO (kernel adds size header) */
return link_socket_write_udp(sock, buf, to);
}
else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index f324ac91..9b9725d7 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -742,13 +742,23 @@ init_tun(const char *dev, /* --dev option */
struct addrinfo *remote_public,
const bool strict_warn,
struct env_set *es,
- openvpn_net_ctx_t *ctx)
+ openvpn_net_ctx_t *ctx,
+ struct tuntap *tt)
{
struct gc_arena gc = gc_new();
- struct tuntap *tt;
- ALLOC_OBJ(tt, struct tuntap);
- clear_tuntap(tt);
+ if (!tt)
+ {
+ ALLOC_OBJ(tt, struct tuntap);
+ clear_tuntap(tt);
+ }
+#if defined(_WIN32)
+ else
+ {
+ ASSERT(!tt->dco.real_tun_init);
+ tt->dco.real_tun_init = true;
+ }
+#endif
tt->type = dev_type_enum(dev, dev_type);
tt->topology = topology;
@@ -891,6 +901,12 @@ init_tun_post(struct tuntap *tt,
{
tt->options = *options;
#ifdef _WIN32
+ if (tt->windows_driver == WINDOWS_DRIVER_WINDCO)
+ {
+ dco_start_tun(tt);
+ return;
+ }
+
overlapped_io_init(&tt->reads, frame, FALSE, true);
overlapped_io_init(&tt->writes, frame, TRUE, true);
tt->adapter_index = TUN_ADAPTER_INDEX_INVALID;
@@ -3484,6 +3500,9 @@ print_windows_driver(enum windows_driver_type windows_driver)
case WINDOWS_DRIVER_WINTUN:
return "wintun";
+ case WINDOWS_DRIVER_WINDCO:
+ return "ovpn-dco-win";
+
default:
return "unspecified";
}
@@ -3865,6 +3884,10 @@ get_tap_reg(struct gc_arena *gc)
{
windows_driver = WINDOWS_DRIVER_WINTUN;
}
+ else if (strcasecmp(component_id, "ovpn-dco") == 0)
+ {
+ windows_driver = WINDOWS_DRIVER_WINDCO;
+ }
if (windows_driver != WINDOWS_DRIVER_UNSPECIFIED)
{
@@ -4219,7 +4242,9 @@ at_least_one_tap_win(const struct tap_reg *tap_reg)
{
if (!tap_reg)
{
- msg(M_FATAL, "There are no TAP-Windows nor Wintun adapters on this system. You should be able to create an adapter by using tapctl.exe utility.");
+ msg(M_FATAL, "There are no TAP-Windows, Wintun or ovpn-dco-win adapters "
+ "on this system. You should be able to create an adapter "
+ "by using tapctl.exe utility.");
}
}
@@ -6419,7 +6444,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev
const char *path = NULL;
char tuntap_device_path[256];
- if (tt->windows_driver == WINDOWS_DRIVER_WINTUN)
+ if (tt->windows_driver == WINDOWS_DRIVER_WINTUN || tt->windows_driver == WINDOWS_DRIVER_WINDCO)
{
const struct device_instance_id_interface *dev_if;
@@ -6439,7 +6464,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev
}
else
{
- /* Open TAP-Windows adapter */
+ /* Open TAP-Windows or dco-win adapter */
openvpn_snprintf(tuntap_device_path, sizeof(tuntap_device_path), "%s%s%s",
USERMODEDEVICEDIR,
device_guid,
@@ -6475,7 +6500,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev
return true;
}
-static void
+void
tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_guid, struct gc_arena *gc)
{
const struct tap_reg *tap_reg = get_tap_reg(gc);
@@ -6767,7 +6792,7 @@ netsh_delete_address_dns(const struct tuntap *tt, bool ipv6, struct gc_arena *gc
argv_free(&argv);
}
-static void
+void
close_tun_handle(struct tuntap *tt)
{
const char *adaptertype = print_windows_driver(tt->windows_driver);
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index cf02bf43..c4a61316 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -48,7 +48,8 @@
enum windows_driver_type {
WINDOWS_DRIVER_UNSPECIFIED,
WINDOWS_DRIVER_TAP_WINDOWS6,
- WINDOWS_DRIVER_WINTUN
+ WINDOWS_DRIVER_WINTUN,
+ WINDOWS_DRIVER_WINDCO
};
#endif
@@ -64,6 +65,8 @@ struct tuntap_options {
/* --ip-win32 options */
bool ip_win32_defined;
+ bool disable_dco;
+
#define IPW32_SET_MANUAL 0 /* "--ip-win32 manual" */
#define IPW32_SET_NETSH 1 /* "--ip-win32 netsh" */
#define IPW32_SET_IPAPI 2 /* "--ip-win32 ipapi" */
@@ -242,6 +245,10 @@ tuntap_ring_empty(struct tuntap *tt)
{
return tuntap_is_wintun(tt) && (tt->wintun_send_ring->head == tt->wintun_send_ring->tail);
}
+
+/* Low level function to open tun handle, used by DCO to create a handle for DCO*/
+void
+tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_guid, struct gc_arena *gc);
#endif
/*
@@ -253,6 +260,8 @@ void open_tun(const char *dev, const char *dev_type, const char *dev_node,
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
+void close_tun_handle(struct tuntap *tt);
+
int write_tun(struct tuntap *tt, uint8_t *buf, int len);
int read_tun(struct tuntap *tt, uint8_t *buf, int len);
@@ -279,7 +288,8 @@ struct tuntap *init_tun(const char *dev, /* --dev option */
struct addrinfo *remote_public,
const bool strict_warn,
struct env_set *es,
- openvpn_net_ctx_t *ctx);
+ openvpn_net_ctx_t *ctx,
+ struct tuntap *tt);
void init_tun_post(struct tuntap *tt,
const struct frame *frame,
@@ -624,6 +634,12 @@ write_tun_buffered(struct tuntap *tt, struct buffer *buf)
}
}
+static inline bool
+is_windco(struct tuntap *tt)
+{
+ return tt->windows_driver == WINDOWS_DRIVER_WINDCO;
+}
+
#else /* ifdef _WIN32 */
static inline bool
@@ -649,6 +665,13 @@ tun_standby(struct tuntap *tt)
return true;
}
+
+static inline bool
+is_windco(struct tuntap *tt)
+{
+ return false;
+}
+
#endif /* ifdef _WIN32 */
/*
@@ -672,25 +695,28 @@ tun_set(struct tuntap *tt,
void *arg,
unsigned int *persistent)
{
- if (tuntap_defined(tt))
+ if (!tuntap_defined(tt) || is_windco(tt))
+ {
+ return;
+ }
+
+ /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
+ if (!persistent || *persistent != rwflags)
{
- /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
- if (!persistent || *persistent != rwflags)
+ event_ctl(es, tun_event_handle(tt), rwflags, arg);
+ if (persistent)
{
- event_ctl(es, tun_event_handle(tt), rwflags, arg);
- if (persistent)
- {
- *persistent = rwflags;
- }
+ *persistent = rwflags;
}
+ }
#ifdef _WIN32
- if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ))
- {
- tun_read_queue(tt, 0);
- }
-#endif
- tt->rwflags_debug = rwflags;
+ if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ))
+ {
+ tun_read_queue(tt, 0);
}
+#endif
+ tt->rwflags_debug = rwflags;
+
}
const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path Antonio Quartulli
@ 2022-06-28 8:46 ` Lev Stipakov
2022-07-05 12:32 ` Heiko Hund
2022-07-11 14:19 ` [Openvpn-devel] [PATCH v2 23/23] " Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Lev Stipakov @ 2022-06-28 8:46 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel
Hi,
I did some testing and found out that this breaks when establishing
TCP connection to a server which cannot be reached:
> + *tt = dco_create_socket(sock->info.lsa->current_remote,
> + sock->bind_local,
> + sock->info.lsa->bind_local,
> + c->options.dev_node,
> + &c->gc,
> + get_server_poll_remaining_time(sock->server_poll_timeout),
> + signal_received);
> + if (*signal_received)
> + {
> + return;
> + }
> + c->c1.tuntap = tt;
> + sock->info.dco_installed = true;
If TCP server cannot be reached, the kernel reports it to userspace
which triggers a signal. Signal handler calls
teardown code which accesses c->c1.tuntap, which is not set (since
return is above the assignment), causing access violation.
dco_installed flag should also be moved above return, since if not
set, c->c1.tuntap won't be cleared
which triggers this assert after reconnect:
/* In this case persist-tun is enabled, which we don't support yet */
ASSERT(!c->c1.tuntap);
I suggest to change code like this:
/* This state is used by signal handler which does teardown,
* so it has to be set before return */
c->c1.tuntap = tt;
sock->info.dco_installed = true;
if (*signal_received)
{
return;
}
Again, we discussed this in chat and suggested changes are already
pushed to the dco branch.
-Lev
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path Antonio Quartulli
2022-06-28 8:46 ` Lev Stipakov
@ 2022-07-05 12:32 ` Heiko Hund
2022-07-11 14:19 ` [Openvpn-devel] [PATCH v2 23/23] " Antonio Quartulli
2 siblings, 0 replies; 157+ messages in thread
From: Heiko Hund @ 2022-07-05 12:32 UTC (permalink / raw)
To: openvpn-devel
On Freitag, 24. Juni 2022 10:38:07 CEST Antonio Quartulli wrote:
> + if (!is_windco(c->c1.tuntap))
[...]
> + if ((options->windows_driver ==
> WINDOWS_DRIVER_WINTUN || options->windows_driver == WINDOWS_DRIVER_WINDCO)
[...]
> +create_socket_windco(struct context *c, struct link_socket *sock,
Thinking "_dco_win" would be a better suffix for consistency.
> - status = WSAGetLastError();
> + if (sock->info.dco_installed)
> + {
> + status = GetLastError();
> + }
> + else
> + {
> + status = WSAGetLastError();
> + }
Small wrapper would be good as this construct appears twice.
^ permalink raw reply [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH v2 23/23] dco-win: implement ovpn-dco support in P2P Windows code path
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path Antonio Quartulli
2022-06-28 8:46 ` Lev Stipakov
2022-07-05 12:32 ` Heiko Hund
@ 2022-07-11 14:19 ` Antonio Quartulli
2022-07-11 15:04 ` Heiko Hund
2 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-11 14:19 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
With this change it is possible to use ovpn-dco-win when running OpenVPN
in client or P2P mode.
Signed-off-by: Arne Schwabe <arne@...1227...>
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
Changes from v1:
* use suffix _dco_win instead of _windco
* create helper function to retrieve last error from socket object
src/openvpn/forward.c | 7 ++++
src/openvpn/init.c | 29 ++++++++++++--
src/openvpn/options.c | 19 ++++++---
src/openvpn/options.h | 15 +++----
src/openvpn/socket.c | 93 ++++++++++++++++++++++++++++++++++++++++---
src/openvpn/socket.h | 25 ++++++++----
src/openvpn/tun.c | 43 +++++++++++++++-----
src/openvpn/tun.h | 58 +++++++++++++++++++--------
8 files changed, 232 insertions(+), 57 deletions(-)
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 15bdbbae..1fbad43f 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -860,6 +860,13 @@ read_incoming_link(struct context *c)
/* check recvfrom status */
check_status(status, "read", c->c2.link_socket, NULL);
+#ifdef _WIN32
+ if (dco_enabled(&c->options) && (status < 0) && (openvpn_errno() == ERROR_NETNAME_DELETED))
+ {
+ trigger_ping_timeout_signal(c);
+ }
+#endif
+
/* Remove socks header if applicable */
socks_postprocess_incoming_link(c);
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index a6ee1fa8..b4276130 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -1698,7 +1698,8 @@ do_init_tun(struct context *c)
c->c1.link_socket_addr.remote_list,
!c->options.ifconfig_nowarn,
c->c2.es,
- &c->net_ctx);
+ &c->net_ctx,
+ c->c1.tuntap);
#ifdef _WIN32
c->c1.tuntap->windows_driver = c->options.windows_driver;
@@ -1811,9 +1812,12 @@ do_open_tun(struct context *c)
ovpn_dco_init(c->mode, &c->c1.tuntap->dco);
}
- /* open the tun device */
- open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap, &c->net_ctx);
+ /* open the tun device (ovpn-dco-win already opened the device for the socket) */
+ if (!is_dco_win(c->c1.tuntap))
+ {
+ open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
+ c->c1.tuntap, &c->net_ctx);
+ }
#if defined(ENABLE_DCO)
/* DCO may have been disabled by open_tun(). propagate change */
@@ -3574,6 +3578,23 @@ do_close_free_key_schedule(struct context *c, bool free_ssl_ctx)
static void
do_close_link_socket(struct context *c)
{
+#if defined(_WIN32)
+ if (is_dco_win(c->c1.tuntap) && c->c2.link_socket
+ && c->c2.link_socket->info.dco_installed)
+ {
+ ASSERT(c->c2.link_socket_owned);
+
+ /* We rely on the tun_close to close the handle if also setup
+ * routes etc, since they cannot be delete when the interface
+ * handle has been closed */
+ if (!c->c1.tuntap->dco.real_tun_init)
+ {
+ do_close_tun_simple(c);
+ }
+ c->c2.link_socket->sd = SOCKET_UNDEFINED;
+ }
+#endif
+
if (c->c2.link_socket && c->c2.link_socket_owned)
{
link_socket_close(c->c2.link_socket);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 85bdd3f6..fa66b513 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3225,8 +3225,8 @@ options_postprocess_mutate_invariant(struct options *options)
#ifdef _WIN32
const int dev = dev_type_enum(options->dev, options->dev_type);
- /* when using wintun, kernel doesn't send DHCP requests, so don't use it */
- if (options->windows_driver == WINDOWS_DRIVER_WINTUN
+ /* when using wintun/ovpn-dco-win, kernel doesn't send DHCP requests, so don't use it */
+ if ((options->windows_driver == WINDOWS_DRIVER_WINTUN || options->windows_driver == WINDOWS_DRIVER_WINDCO)
&& (options->tuntap_options.ip_win32_type == IPW32_SET_DHCP_MASQ || options->tuntap_options.ip_win32_type == IPW32_SET_ADAPTIVE))
{
options->tuntap_options.ip_win32_type = IPW32_SET_NETSH;
@@ -3322,10 +3322,12 @@ options_postprocess_setdefault_ncpciphers(struct options *o)
/* custom --data-ciphers set, keep list */
return;
}
+#if !defined(_WIN32)
else if (cipher_valid("CHACHA20-POLY1305"))
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";
}
+#endif
else
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM";
@@ -4161,7 +4163,8 @@ options_string(const struct options *o,
NULL,
false,
NULL,
- ctx);
+ ctx,
+ NULL);
if (tt)
{
tt_local = true;
@@ -4548,13 +4551,19 @@ parse_windows_driver(const char *str, const int msglevel)
{
return WINDOWS_DRIVER_WINTUN;
}
+
+ else if (streq(str, "ovpn-dco-win"))
+ {
+ return WINDOWS_DRIVER_WINDCO;
+ }
else
{
- msg(msglevel, "--windows-driver must be tap-windows6 or wintun");
+ msg(msglevel, "--windows-driver must be tap-windows6, wintun "
+ "or ovpn-dco-win");
return WINDOWS_DRIVER_UNSPECIFIED;
}
}
-#endif
+#endif /* ifdef _WIN32 */
/*
* parse/print topology coding
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index f7e3f265..1b30cedd 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -879,24 +879,19 @@ void options_string_import(struct options *options,
bool key_is_external(const struct options *options);
-#if defined(ENABLE_DCO) && defined(TARGET_LINUX)
-
/**
* Returns whether the current configuration has dco enabled.
*/
static inline bool
dco_enabled(const struct options *o)
{
+#if defined(_WIN32)
+ return o->windows_driver == WINDOWS_DRIVER_WINDCO;
+#elif defined(ENABLE_DCO)
return !o->tuntap_options.disable_dco;
-}
-
-#else /* if defined(ENABLE_DCO) && defined(TARGET_LINUX) */
-
-static inline bool
-dco_enabled(const struct options *o)
-{
+#else
return false;
+#endif /* defined(_WIN32) */
}
-#endif
#endif /* ifndef OPTIONS_H */
diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
index 4e4a3a2f..2e6fc0ca 100644
--- a/src/openvpn/socket.c
+++ b/src/openvpn/socket.c
@@ -2119,6 +2119,41 @@ phase2_socks_client(struct link_socket *sock, struct signal_info *sig_info)
resolve_remote(sock, 1, NULL, &sig_info->signal_received);
}
+#if defined(_WIN32)
+static void
+create_socket_dco_win(struct context *c, struct link_socket *sock,
+ volatile int *signal_received)
+{
+ struct tuntap *tt;
+ /* In this case persist-tun is enabled, which we don't support yet */
+ ASSERT(!c->c1.tuntap);
+
+ ALLOC_OBJ(tt, struct tuntap);
+
+ *tt = dco_create_socket(sock->info.lsa->current_remote,
+ sock->bind_local,
+ sock->info.lsa->bind_local,
+ c->options.dev_node,
+ &c->gc,
+ get_server_poll_remaining_time(sock->server_poll_timeout),
+ signal_received);
+
+ /* This state is used by signal handler which does teardown,
+ * so it has to be set before return */
+ c->c1.tuntap = tt;
+ sock->info.dco_installed = true;
+
+ if (*signal_received)
+ {
+ return;
+ }
+
+ /* Ensure we can "safely" cast the handle to a socket */
+ static_assert(sizeof(sock->sd) == sizeof(tt->hand), "HANDLE and SOCKET size differs");
+ sock->sd = (SOCKET)tt->hand;
+}
+#endif /* if defined(_WIN32) */
+
/* finalize socket initialization */
void
link_socket_init_phase2(struct context *c)
@@ -2158,7 +2193,24 @@ link_socket_init_phase2(struct context *c)
/* If a valid remote has been found, create the socket with its addrinfo */
if (sock->info.lsa->current_remote)
{
- create_socket(sock, sock->info.lsa->current_remote);
+#if defined(_WIN32)
+ if (dco_enabled(&c->options))
+ {
+ create_socket_dco_win(c, sock, &sig_info->signal_received);
+ if (sig_info->signal_received)
+ {
+ goto done;
+ }
+
+ linksock_print_addr(sock);
+ goto done;
+ }
+ else
+#endif
+ {
+ create_socket(sock, sock->info.lsa->current_remote);
+ }
+
}
/* If socket has not already been created create it now */
@@ -3426,6 +3478,17 @@ link_socket_write_udp_posix_sendmsg(struct link_socket *sock,
#ifdef _WIN32
+static int
+socket_get_last_error(const struct link_socket *sock)
+{
+ if (sock->info.dco_installed)
+ {
+ return GetLastError();
+ }
+
+ return WSAGetLastError();
+}
+
int
socket_recv_queue(struct link_socket *sock, int maxsize)
{
@@ -3459,7 +3522,14 @@ socket_recv_queue(struct link_socket *sock, int maxsize)
ASSERT(ResetEvent(sock->reads.overlapped.hEvent));
sock->reads.flags = 0;
- if (proto_is_udp(sock->info.proto))
+ if (sock->info.dco_installed)
+ {
+ status = ReadFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len,
+ &sock->reads.size, &sock->reads.overlapped);
+ /* Readfile status is inverted from WSARecv */
+ status = !status;
+ }
+ else if (proto_is_udp(sock->info.proto))
{
sock->reads.addr_defined = true;
sock->reads.addrlen = sizeof(sock->reads.addr6);
@@ -3512,7 +3582,7 @@ socket_recv_queue(struct link_socket *sock, int maxsize)
}
else
{
- status = WSAGetLastError();
+ status = socket_get_last_error(sock);
if (status == WSA_IO_PENDING) /* operation queued? */
{
sock->reads.iostate = IOSTATE_QUEUED;
@@ -3557,7 +3627,16 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
ASSERT(ResetEvent(sock->writes.overlapped.hEvent));
sock->writes.flags = 0;
- if (proto_is_udp(sock->info.proto))
+ if (sock->info.dco_installed)
+ {
+ status = WriteFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len,
+ &sock->writes.size, &sock->writes.overlapped);
+
+ /* WriteFile status is inverted from WSASendTo */
+ status = !status;
+
+ }
+ else if (proto_is_udp(sock->info.proto))
{
/* set destination address for UDP writes */
sock->writes.addr_defined = true;
@@ -3618,8 +3697,9 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
}
else
{
- status = WSAGetLastError();
- if (status == WSA_IO_PENDING) /* operation queued? */
+ status = socket_get_last_error(sock);
+ /* both status code have the identical value */
+ if (status == WSA_IO_PENDING || status == ERROR_IO_PENDING) /* operation queued? */
{
sock->writes.iostate = IOSTATE_QUEUED;
sock->writes.status = status;
@@ -3644,6 +3724,7 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
return sock->writes.iostate;
}
+/* Returns the number of bytes successfully read */
int
sockethandle_finalize(sockethandle_t sh,
struct overlapped_io *io,
diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h
index 0d521d22..462afa31 100644
--- a/src/openvpn/socket.h
+++ b/src/openvpn/socket.h
@@ -34,6 +34,7 @@
#include "proxy.h"
#include "socks.h"
#include "misc.h"
+#include "tun.h"
/*
* OpenVPN's default port number as assigned by IANA.
@@ -937,7 +938,8 @@ socket_connection_reset(const struct link_socket *sock, int status)
{
const int err = openvpn_errno();
#ifdef _WIN32
- return err == WSAECONNRESET || err == WSAECONNABORTED;
+ return err == WSAECONNRESET || err == WSAECONNABORTED
+ || err == ERROR_CONNECTION_ABORTED;
#else
return err == ECONNRESET;
#endif
@@ -1048,6 +1050,11 @@ link_socket_read_udp_win32(struct link_socket *sock,
struct link_socket_actual *from)
{
sockethandle_t sh = { .s = sock->sd };
+ if (sock->info.dco_installed)
+ {
+ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest);
+ sh.is_handle = true;
+ }
return sockethandle_finalize(sh, &sock->reads, buf, from);
}
@@ -1057,7 +1064,7 @@ int link_socket_read_udp_posix(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from);
-#endif
+#endif /* ifdef _WIN32 */
/* read a TCP or UDP packet from link */
static inline int
@@ -1065,7 +1072,10 @@ link_socket_read(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from)
{
- if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
+ if (proto_is_udp(sock->info.proto)
+ || sock->info.dco_installed)
+ /* unified UDPv4 and UDPv6, for DCO the kernel
+ * will strip the length header */
{
int res;
@@ -1106,19 +1116,19 @@ link_socket_write_win32(struct link_socket *sock,
{
int err = 0;
int status = 0;
- sockethandle_t sh = { .s = sock->sd };
+ sockethandle_t sh = { .s = sock->sd, .is_handle = sock->info.dco_installed };
if (overlapped_io_active(&sock->writes))
{
status = sockethandle_finalize(sh, &sock->writes, NULL, NULL);
if (status < 0)
{
- err = WSAGetLastError();
+ err = SocketHandleGetLastError(sh);
}
}
socket_send_queue(sock, buf, to);
if (status < 0)
{
- WSASetLastError(err);
+ SocketHandleSetLastError(sh, err);
return status;
}
else
@@ -1180,8 +1190,9 @@ link_socket_write(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to)
{
- if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */
+ if (proto_is_udp(sock->info.proto) || sock->info.dco_installed)
{
+ /* unified UDPv4 and UDPv6 and DCO (kernel adds size header) */
return link_socket_write_udp(sock, buf, to);
}
else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 9076a9b7..0b01a1d0 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -743,13 +743,23 @@ init_tun(const char *dev, /* --dev option */
struct addrinfo *remote_public,
const bool strict_warn,
struct env_set *es,
- openvpn_net_ctx_t *ctx)
+ openvpn_net_ctx_t *ctx,
+ struct tuntap *tt)
{
struct gc_arena gc = gc_new();
- struct tuntap *tt;
- ALLOC_OBJ(tt, struct tuntap);
- clear_tuntap(tt);
+ if (!tt)
+ {
+ ALLOC_OBJ(tt, struct tuntap);
+ clear_tuntap(tt);
+ }
+#if defined(_WIN32)
+ else
+ {
+ ASSERT(!tt->dco.real_tun_init);
+ tt->dco.real_tun_init = true;
+ }
+#endif
tt->type = dev_type_enum(dev, dev_type);
tt->topology = topology;
@@ -892,6 +902,12 @@ init_tun_post(struct tuntap *tt,
{
tt->options = *options;
#ifdef _WIN32
+ if (tt->windows_driver == WINDOWS_DRIVER_WINDCO)
+ {
+ dco_start_tun(tt);
+ return;
+ }
+
overlapped_io_init(&tt->reads, frame, FALSE, true);
overlapped_io_init(&tt->writes, frame, TRUE, true);
tt->adapter_index = TUN_ADAPTER_INDEX_INVALID;
@@ -3508,6 +3524,9 @@ print_windows_driver(enum windows_driver_type windows_driver)
case WINDOWS_DRIVER_WINTUN:
return "wintun";
+ case WINDOWS_DRIVER_WINDCO:
+ return "ovpn-dco-win";
+
default:
return "unspecified";
}
@@ -3889,6 +3908,10 @@ get_tap_reg(struct gc_arena *gc)
{
windows_driver = WINDOWS_DRIVER_WINTUN;
}
+ else if (strcasecmp(component_id, "ovpn-dco") == 0)
+ {
+ windows_driver = WINDOWS_DRIVER_WINDCO;
+ }
if (windows_driver != WINDOWS_DRIVER_UNSPECIFIED)
{
@@ -4243,7 +4266,9 @@ at_least_one_tap_win(const struct tap_reg *tap_reg)
{
if (!tap_reg)
{
- msg(M_FATAL, "There are no TAP-Windows nor Wintun adapters on this system. You should be able to create an adapter by using tapctl.exe utility.");
+ msg(M_FATAL, "There are no TAP-Windows, Wintun or ovpn-dco-win adapters "
+ "on this system. You should be able to create an adapter "
+ "by using tapctl.exe utility.");
}
}
@@ -6443,7 +6468,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev
const char *path = NULL;
char tuntap_device_path[256];
- if (tt->windows_driver == WINDOWS_DRIVER_WINTUN)
+ if (tt->windows_driver == WINDOWS_DRIVER_WINTUN || tt->windows_driver == WINDOWS_DRIVER_WINDCO)
{
const struct device_instance_id_interface *dev_if;
@@ -6463,7 +6488,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev
}
else
{
- /* Open TAP-Windows adapter */
+ /* Open TAP-Windows or dco-win adapter */
openvpn_snprintf(tuntap_device_path, sizeof(tuntap_device_path), "%s%s%s",
USERMODEDEVICEDIR,
device_guid,
@@ -6499,7 +6524,7 @@ tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct dev
return true;
}
-static void
+void
tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_guid, struct gc_arena *gc)
{
const struct tap_reg *tap_reg = get_tap_reg(gc);
@@ -6791,7 +6816,7 @@ netsh_delete_address_dns(const struct tuntap *tt, bool ipv6, struct gc_arena *gc
argv_free(&argv);
}
-static void
+void
close_tun_handle(struct tuntap *tt)
{
const char *adaptertype = print_windows_driver(tt->windows_driver);
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index cf02bf43..a4902a78 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -48,7 +48,8 @@
enum windows_driver_type {
WINDOWS_DRIVER_UNSPECIFIED,
WINDOWS_DRIVER_TAP_WINDOWS6,
- WINDOWS_DRIVER_WINTUN
+ WINDOWS_DRIVER_WINTUN,
+ WINDOWS_DRIVER_WINDCO
};
#endif
@@ -64,6 +65,8 @@ struct tuntap_options {
/* --ip-win32 options */
bool ip_win32_defined;
+ bool disable_dco;
+
#define IPW32_SET_MANUAL 0 /* "--ip-win32 manual" */
#define IPW32_SET_NETSH 1 /* "--ip-win32 netsh" */
#define IPW32_SET_IPAPI 2 /* "--ip-win32 ipapi" */
@@ -242,6 +245,10 @@ tuntap_ring_empty(struct tuntap *tt)
{
return tuntap_is_wintun(tt) && (tt->wintun_send_ring->head == tt->wintun_send_ring->tail);
}
+
+/* Low level function to open tun handle, used by DCO to create a handle for DCO*/
+void
+tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_guid, struct gc_arena *gc);
#endif
/*
@@ -253,6 +260,8 @@ void open_tun(const char *dev, const char *dev_type, const char *dev_node,
void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
+void close_tun_handle(struct tuntap *tt);
+
int write_tun(struct tuntap *tt, uint8_t *buf, int len);
int read_tun(struct tuntap *tt, uint8_t *buf, int len);
@@ -279,7 +288,8 @@ struct tuntap *init_tun(const char *dev, /* --dev option */
struct addrinfo *remote_public,
const bool strict_warn,
struct env_set *es,
- openvpn_net_ctx_t *ctx);
+ openvpn_net_ctx_t *ctx,
+ struct tuntap *tt);
void init_tun_post(struct tuntap *tt,
const struct frame *frame,
@@ -624,6 +634,12 @@ write_tun_buffered(struct tuntap *tt, struct buffer *buf)
}
}
+static inline bool
+is_dco_win(struct tuntap *tt)
+{
+ return tt->windows_driver == WINDOWS_DRIVER_WINDCO;
+}
+
#else /* ifdef _WIN32 */
static inline bool
@@ -649,6 +665,13 @@ tun_standby(struct tuntap *tt)
return true;
}
+
+static inline bool
+is_dco_win(struct tuntap *tt)
+{
+ return false;
+}
+
#endif /* ifdef _WIN32 */
/*
@@ -672,25 +695,28 @@ tun_set(struct tuntap *tt,
void *arg,
unsigned int *persistent)
{
- if (tuntap_defined(tt))
+ if (!tuntap_defined(tt) || is_dco_win(tt))
+ {
+ return;
+ }
+
+ /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
+ if (!persistent || *persistent != rwflags)
{
- /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
- if (!persistent || *persistent != rwflags)
+ event_ctl(es, tun_event_handle(tt), rwflags, arg);
+ if (persistent)
{
- event_ctl(es, tun_event_handle(tt), rwflags, arg);
- if (persistent)
- {
- *persistent = rwflags;
- }
+ *persistent = rwflags;
}
+ }
#ifdef _WIN32
- if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ))
- {
- tun_read_queue(tt, 0);
- }
-#endif
- tt->rwflags_debug = rwflags;
+ if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ))
+ {
+ tun_read_queue(tt, 0);
}
+#endif
+ tt->rwflags_debug = rwflags;
+
}
const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc);
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH v2 23/23] dco-win: implement ovpn-dco support in P2P Windows code path
2022-07-11 14:19 ` [Openvpn-devel] [PATCH v2 23/23] " Antonio Quartulli
@ 2022-07-11 15:04 ` Heiko Hund
0 siblings, 0 replies; 157+ messages in thread
From: Heiko Hund @ 2022-07-11 15:04 UTC (permalink / raw)
To: openvpn-devel
On Montag, 11. Juli 2022 16:19:44 CEST Antonio Quartulli wrote:
> With this change it is possible to use ovpn-dco-win when running OpenVPN
> in client or P2P mode.
>
> Signed-off-by: Arne Schwabe <arne@...1227...>
> Signed-off-by: Lev Stipakov <lev@...515...>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
>
> Changes from v1:
> * use suffix _dco_win instead of _windco
> * create helper function to retrieve last error from socket object
Acked-by: Heiko Hund <heiko@...2662...>
^ permalink raw reply [flat|nested] 157+ messages in thread
* [Openvpn-devel] [PATCH 24/25] dco-win: add documentation to README.dco.md
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (22 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 23/25] dco-win: implement ovpn-dco support in P2P Windows code path Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-05 12:32 ` Heiko Hund
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file Antonio Quartulli
24 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Arne Schwabe <arne@...1227...>
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
README.dco.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/README.dco.md b/README.dco.md
index e73e0fc2..ef56f0fe 100644
--- a/README.dco.md
+++ b/README.dco.md
@@ -58,6 +58,13 @@ see a message like
in your log.
+Getting started (Windows)
+-------------------------
+Getting started under windows is currently for brave people having experience
+with windows development. You need to compile openvpn yourself and also need
+to get the test driver installed on your system.
+
+
DCO and P2P mode
----------------
DCO is also available when running OpenVPN in P2P mode without --pull/--client option.
@@ -109,6 +116,8 @@ Limitations by design
- topology subnet is the only supported `--topology` for servers
- iroute directives install routes on the host operating system, see also
Routing with ovpn-dco
+- (ovpn-dco-win) client and p2p mode only
+- (ovpn-dco-win) Chacha20-Poly1305 support available starting with Windows 11
Current implementation limitations
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file
2022-06-24 8:37 [Openvpn-devel] [PATCH 00/25] ovpn-dco: introduce data-channel offload support Antonio Quartulli
` (23 preceding siblings ...)
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 24/25] dco-win: add documentation to README.dco.md Antonio Quartulli
@ 2022-06-24 8:38 ` Antonio Quartulli
2022-07-05 9:32 ` Frank Lichtenheld
24 siblings, 1 reply; 157+ messages in thread
From: Antonio Quartulli @ 2022-06-24 8:38 UTC (permalink / raw)
To: openvpn-devel; +Cc: Antonio Quartulli <a@
Signed-off-by: Lev Stipakov <lev@...515...>
Signed-off-by: Antonio Quartulli <a@...2181...>
---
.github/workflows/build.yaml | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index b905c0d2..536dd9d6 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -59,11 +59,6 @@ jobs:
steps:
- name: Install dependencies
run: sudo apt update && sudo apt install -y mingw-w64 libtool automake autoconf man2html unzip
- - name: Checkout ovpn-dco-win
- uses: actions/checkout@...2620...
- with:
- repository: OpenVPN/ovpn-dco-win
- path: ovpn-dco-win
- name: Checkout OpenVPN
uses: actions/checkout@...2620...
with:
@@ -151,7 +146,7 @@ jobs:
run: cp ./tap-windows-${TAP_WINDOWS_VERSION}/include/tap-windows.h ${HOME}/mingw/opt/include/
- name: configure OpenVPN
- run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig DCO_SOURCEDIR=$(realpath ../ovpn-dco-win) LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4 --enable-dco
+ run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4
working-directory: openvpn
- name: build OpenVPN
--
2.35.1
^ permalink raw reply related [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file
2022-06-24 8:38 ` [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file Antonio Quartulli
@ 2022-07-05 9:32 ` Frank Lichtenheld
2022-07-05 9:36 ` Antonio Quartulli
0 siblings, 1 reply; 157+ messages in thread
From: Frank Lichtenheld @ 2022-07-05 9:32 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: openvpn-devel, Lev Stipakov <lev@
I think this commit could use an additional sentence
in the commit message on why exactly you remove this?
Doesn't immediately make sense to me.
On Fri, Jun 24, 2022 at 10:38:09AM +0200, Antonio Quartulli wrote:
> Signed-off-by: Lev Stipakov <lev@...515...>
> Signed-off-by: Antonio Quartulli <a@...2181...>
> ---
> .github/workflows/build.yaml | 7 +------
> 1 file changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
> index b905c0d2..536dd9d6 100644
> --- a/.github/workflows/build.yaml
> +++ b/.github/workflows/build.yaml
> @@ -59,11 +59,6 @@ jobs:
> steps:
> - name: Install dependencies
> run: sudo apt update && sudo apt install -y mingw-w64 libtool automake autoconf man2html unzip
> - - name: Checkout ovpn-dco-win
> - uses: actions/checkout@...2620...
> - with:
> - repository: OpenVPN/ovpn-dco-win
> - path: ovpn-dco-win
> - name: Checkout OpenVPN
> uses: actions/checkout@...2620...
> with:
> @@ -151,7 +146,7 @@ jobs:
> run: cp ./tap-windows-${TAP_WINDOWS_VERSION}/include/tap-windows.h ${HOME}/mingw/opt/include/
>
> - name: configure OpenVPN
> - run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig DCO_SOURCEDIR=$(realpath ../ovpn-dco-win) LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4 --enable-dco
> + run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4
> working-directory: openvpn
>
> - name: build OpenVPN
Regards,
--
Frank Lichtenheld
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file
2022-07-05 9:32 ` Frank Lichtenheld
@ 2022-07-05 9:36 ` Antonio Quartulli
2022-07-05 10:38 ` Lev Stipakov
2022-08-04 15:28 ` Gert Doering
0 siblings, 2 replies; 157+ messages in thread
From: Antonio Quartulli @ 2022-07-05 9:36 UTC (permalink / raw)
To: Frank Lichtenheld <frank@; +Cc: openvpn-devel, Lev Stipakov <lev@
Hi,
On 05/07/2022 11:32, Frank Lichtenheld wrote:
> I think this commit could use an additional sentence
> in the commit message on why exactly you remove this?
>
> Doesn't immediately make sense to me.
To be honest, I am not even sure why we have a build with DCO in the
first place. There is no DCO code yet in master but somehow we have the
GH action for that :-D
Anyway, can add a sentence to explain what is going on.
Cheers,
>
> On Fri, Jun 24, 2022 at 10:38:09AM +0200, Antonio Quartulli wrote:
>> Signed-off-by: Lev Stipakov <lev@...515...>
>> Signed-off-by: Antonio Quartulli <a@...2181...>
>> ---
>> .github/workflows/build.yaml | 7 +------
>> 1 file changed, 1 insertion(+), 6 deletions(-)
>>
>> diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
>> index b905c0d2..536dd9d6 100644
>> --- a/.github/workflows/build.yaml
>> +++ b/.github/workflows/build.yaml
>> @@ -59,11 +59,6 @@ jobs:
>> steps:
>> - name: Install dependencies
>> run: sudo apt update && sudo apt install -y mingw-w64 libtool automake autoconf man2html unzip
>> - - name: Checkout ovpn-dco-win
>> - uses: actions/checkout@...2620...
>> - with:
>> - repository: OpenVPN/ovpn-dco-win
>> - path: ovpn-dco-win
>> - name: Checkout OpenVPN
>> uses: actions/checkout@...2620...
>> with:
>> @@ -151,7 +146,7 @@ jobs:
>> run: cp ./tap-windows-${TAP_WINDOWS_VERSION}/include/tap-windows.h ${HOME}/mingw/opt/include/
>>
>> - name: configure OpenVPN
>> - run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig DCO_SOURCEDIR=$(realpath ../ovpn-dco-win) LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4 --enable-dco
>> + run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4
>> working-directory: openvpn
>>
>> - name: build OpenVPN
>
> Regards,
--
Antonio Quartulli
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file
2022-07-05 9:36 ` Antonio Quartulli
@ 2022-07-05 10:38 ` Lev Stipakov
2022-08-04 15:28 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Lev Stipakov @ 2022-07-05 10:38 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: Frank Lichtenheld <frank@
We have moved dco-win uapi header to openvpn repo, so we don't have to
checkout ovpn-dco-win repo to get it - this is what this commit does.
But yeah, good point on having dco builds without dco.
ti 5. heinäk. 2022 klo 12.36 Antonio Quartulli (a@...2181...) kirjoitti:
>
> Hi,
>
> On 05/07/2022 11:32, Frank Lichtenheld wrote:
> > I think this commit could use an additional sentence
> > in the commit message on why exactly you remove this?
> >
> > Doesn't immediately make sense to me.
>
> To be honest, I am not even sure why we have a build with DCO in the
> first place. There is no DCO code yet in master but somehow we have the
> GH action for that :-D
>
> Anyway, can add a sentence to explain what is going on.
>
> Cheers,
>
> >
> > On Fri, Jun 24, 2022 at 10:38:09AM +0200, Antonio Quartulli wrote:
> >> Signed-off-by: Lev Stipakov <lev@...515...>
> >> Signed-off-by: Antonio Quartulli <a@...2181...>
> >> ---
> >> .github/workflows/build.yaml | 7 +------
> >> 1 file changed, 1 insertion(+), 6 deletions(-)
> >>
> >> diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
> >> index b905c0d2..536dd9d6 100644
> >> --- a/.github/workflows/build.yaml
> >> +++ b/.github/workflows/build.yaml
> >> @@ -59,11 +59,6 @@ jobs:
> >> steps:
> >> - name: Install dependencies
> >> run: sudo apt update && sudo apt install -y mingw-w64 libtool automake autoconf man2html unzip
> >> - - name: Checkout ovpn-dco-win
> >> - uses: actions/checkout@...2620...
> >> - with:
> >> - repository: OpenVPN/ovpn-dco-win
> >> - path: ovpn-dco-win
> >> - name: Checkout OpenVPN
> >> uses: actions/checkout@...2620...
> >> with:
> >> @@ -151,7 +146,7 @@ jobs:
> >> run: cp ./tap-windows-${TAP_WINDOWS_VERSION}/include/tap-windows.h ${HOME}/mingw/opt/include/
> >>
> >> - name: configure OpenVPN
> >> - run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig DCO_SOURCEDIR=$(realpath ../ovpn-dco-win) LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4 --enable-dco
> >> + run: PKG_CONFIG_PATH=${HOME}/mingw/opt/lib/pkgconfig LDFLAGS=-L$HOME/mingw/opt/lib CFLAGS=-I$HOME/mingw/opt/include OPENSSL_LIBS="-L${HOME}/opt/lib -lssl -lcrypto" OPENSSL_CFLAGS=-I$HOME/mingw/opt/include PREFIX=$HOME/mingw/opt LZO_CFLAGS=-I$HOME/mingw/opt/include LZO_LIBS="-L${HOME}/mingw/opt/lib -llzo2" ./configure --host=${CHOST} --disable-lz4
> >> working-directory: openvpn
> >>
> >> - name: build OpenVPN
> >
> > Regards,
>
> --
> Antonio Quartulli
>
>
> _______________________________________________
> Openvpn-devel mailing list
> Openvpn-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/openvpn-devel
--
-Lev
^ permalink raw reply [flat|nested] 157+ messages in thread* Re: [Openvpn-devel] [PATCH 25/25] dco-win: update GH Actions config file
2022-07-05 9:36 ` Antonio Quartulli
2022-07-05 10:38 ` Lev Stipakov
@ 2022-08-04 15:28 ` Gert Doering
1 sibling, 0 replies; 157+ messages in thread
From: Gert Doering @ 2022-08-04 15:28 UTC (permalink / raw)
To: Antonio Quartulli <a@; +Cc: Frank Lichtenheld <frank@
[-- Attachment #1: Type: text/plain, Size: 1029 bytes --]
Hi,
On Tue, Jul 05, 2022 at 11:36:37AM +0200, Antonio Quartulli wrote:
> On 05/07/2022 11:32, Frank Lichtenheld wrote:
> > I think this commit could use an additional sentence
> > in the commit message on why exactly you remove this?
> >
> > Doesn't immediately make sense to me.
>
> To be honest, I am not even sure why we have a build with DCO in the
> first place. There is no DCO code yet in master but somehow we have the
> GH action for that :-D
Now there *is* DCO code in master, so why removing the --enable-dco?
So this patch definitely needs some more thought :-)
(And I thought there is an easy one to apply)
gert
--
"If was one thing all people took for granted, was conviction that if you
feed honest figures into a computer, honest figures come out. Never doubted
it myself till I met a computer with a sense of humor."
Robert A. Heinlein, The Moon is a Harsh Mistress
Gert Doering - Munich, Germany gert@...1296...
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 630 bytes --]
^ permalink raw reply [flat|nested] 157+ messages in thread