@@ -56,11 +56,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@v2
- with:
- repository: OpenVPN/ovpn-dco-win
- path: ovpn-dco-win
- name: Checkout OpenVPN
uses: actions/checkout@v2
with:
@@ -148,7 +143,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 --enable-dco
working-directory: openvpn
- name: build OpenVPN
@@ -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
@@ -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
@@ -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"
@@ -773,18 +774,24 @@ 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])]
+ )
- 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}"
+ CFLAGS="${CFLAGS} ${LIBNL_GENL_CFLAGS}"
+ LIBS="${LIBS} ${LIBNL_GENL_LIBS}"
+ AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for Linux])
+ AC_MSG_NOTICE([Enabled ovpn-dco support for Linux])
+ ;;
- 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])
+ ;;
+ esac
fi
if test "${with_crypto_library}" = "openssl"; then
@@ -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
@@ -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
new file mode 100644
@@ -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 Lev Stipakov <lev@openvpn.net>
+ * Copyright (C) 2021 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#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
@@ -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 */
@@ -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" />
@@ -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">
@@ -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 \
@@ -150,5 +151,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
@@ -415,6 +415,25 @@ dco_check_option_conflict_ce(const struct connection_entry *ce, int msglevel)
return false;
}
+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 true;
+ }
+ if (o->persist_tun)
+ {
+ msg(msglevel, "--persist-tun is not supported with ovpn-dco-win.");
+ return true;
+ }
+#endif
+ return false;
+}
+
bool
dco_check_option_conflict(int msglevel, const struct options *o)
{
@@ -429,6 +448,11 @@ dco_check_option_conflict(int msglevel, const struct options *o)
return true;
}
+ if (dco_check_option_conflict_platform(msglevel, o))
+ {
+ return true;
+ }
+
if (dev_type_enum(o->dev, o->dev_type) != DEV_TYPE_TUN)
{
msg(msglevel, "Note: dev-type not tun, disabling data channel offload.");
@@ -437,7 +461,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);
@@ -491,7 +515,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);
@@ -176,6 +176,11 @@ void dco_update_keys(dco_context_t *dco, struct tls_multi *multi);
*/
bool dco_available(int msglevel);
+/**
+ * Return list of colon-separated ciphers supported by platform
+ */
+const char *dco_get_supported_ciphers();
+
/**
* Install a DCO in the main event loop
*/
@@ -28,6 +28,7 @@
#if defined(ENABLE_DCO)
#include "dco_linux.h"
+#include "dco_win.h"
/**
* This file contains the internal DCO API definition.
@@ -930,4 +930,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) */
@@ -36,7 +36,6 @@ typedef enum ovpn_cipher_alg dco_cipher_t;
#define DCO_IROUTE_METRIC 100
#define DCO_DEFAULT_METRIC 200
-#define DCO_SUPPORTED_CIPHERS "AES-128-GCM:AES-256-GCM:AES-192-GCM:CHACHA20-POLY1305"
typedef struct
{
new file mode 100644
@@ -0,0 +1,401 @@
+/*
+ * Interface to ovpn-win-dco networking code
+ *
+ * Copyright (C) 2020 Arne Schwabe <arne@rfc2549.org>
+ * Copyright (C) 2020 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#if defined(_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))
+ {
+ 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) */
new file mode 100644
@@ -0,0 +1,58 @@
+/*
+ * Interface to ovpn-win-dco networking code
+ *
+ * Copyright (C) 2020-2022 Arne Schwabe <arne@rfc2549.org>
+ * Copyright (C) 2020-2022 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DCO_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 */
@@ -859,6 +859,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);
@@ -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;
@@ -1697,7 +1698,11 @@ do_open_tun(struct context *c)
bool ret = false;
#ifndef TARGET_ANDROID
- if (!c->c1.tuntap)
+ if (!c->c1.tuntap
+#ifdef _WIN32
+ || (c->c1.tuntap && !c->c1.tuntap->dco.real_tun_init)
+#endif
+ )
{
#endif
@@ -1776,9 +1781,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 opend 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)
@@ -3531,6 +3539,22 @@ do_close_free_key_schedule(struct context *c, bool free_ssl_ctx)
static void
do_close_link_socket(struct context *c)
{
+#ifdef _WIN32
+ if (c->c2.link_socket && c->c2.link_socket->info.dco_installed && is_windco(c->c1.tuntap))
+ {
+ ASSERT(c->c2.link_socket_owned);
+ ASSERT(c->c1.tuntap);
+
+ /* We rely on the tun close to the handle if also setup
+ * routes etc, since they cannot be delete when the interface
+ * handle has been closed */
+ if (true && !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);
@@ -277,6 +277,7 @@
<ClCompile Include="crypto_openssl.c" />
<ClCompile Include="cryptoapi.c" />
<ClCompile Include="dco.c" />
+ <ClCompile Include="dco_win.c" />
<ClCompile Include="dhcp.c" />
<ClCompile Include="dns.c" />
<ClCompile Include="env_set.c" />
@@ -364,6 +365,7 @@
<ClInclude Include="crypto_openssl.h" />
<ClInclude Include="cryptoapi.h" />
<ClInclude Include="dco.h" />
+ <ClInclude Include="dco_win.h" />
<ClInclude Include="dhcp.h" />
<ClInclude Include="dns.h" />
<ClInclude Include="env_set.h" />
@@ -261,6 +261,9 @@
<ClCompile Include="xkey_provider.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="dco_win.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="base64.h">
@@ -389,6 +392,9 @@
<ClInclude Include="multi.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="networking_windco.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="ntlm.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -539,6 +545,9 @@
<ClInclude Include="xkey_common.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="dco_win.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="openvpn_win32_resources.rc">
@@ -2338,6 +2338,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 */
/*
@@ -3108,8 +3113,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;
@@ -3205,10 +3210,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";
@@ -4033,7 +4040,8 @@ options_string(const struct options *o,
NULL,
false,
NULL,
- ctx);
+ ctx,
+ NULL);
if (tt)
{
tt_local = true;
@@ -4458,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
@@ -879,24 +879,19 @@ void options_string_import(struct options *options,
bool key_is_external(const struct options *options);
-#if defined(ENABLE_DCO)
-
/**
* 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) */
-
-static inline bool
-dco_enabled(const struct options *o)
-{
+#else
return false;
+#endif /* defined(_WIN32) */
}
-#endif
#endif /* ifndef OPTIONS_H */
new file mode 100644
@@ -0,0 +1,108 @@
+/*
+ * ovpn-dco-win OpenVPN protocol accelerator for Windows
+ *
+ * Copyright (C) 2020-2021 OpenVPN Inc <sales@openvpn.net>
+ *
+ * Author: Lev Stipakov <lev@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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)
@@ -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 */
@@ -2221,6 +2270,7 @@ link_socket_init_phase2(struct context *c)
}
phase2_set_socket_flags(sock);
+
linksock_print_addr(sock);
done:
@@ -3459,7 +3509,19 @@ 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 +3574,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 +3626,21 @@ 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 +3701,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 +3736,7 @@ socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct lin
return sock->writes.iostate;
}
+/* Returns the nubmer of bytes successfully read */
int
sockethandle_finalize(sockethandle_t sh,
struct overlapped_io *io,
@@ -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.
@@ -1048,6 +1049,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 +1063,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 +1071,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 +1115,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 +1189,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 */
@@ -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;
@@ -3483,6 +3499,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";
}
@@ -3864,6 +3883,11 @@ 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)
{
@@ -4218,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.");
}
}
@@ -6418,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;
@@ -6438,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,
@@ -6474,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);
@@ -6766,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);
@@ -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);