@@ -60,6 +60,12 @@ 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.
@@ -105,6 +111,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) only AES-GCM-128/192/256 cipher support
Current limitations
-------------------
@@ -6,10 +6,10 @@
#define ENABLE_CRYPTO_OPENSSL 1
#define ENABLE_FRAGMENT 1
#define ENABLE_HTTP_PROXY 1
-#define ENABLE_LZO 1
+//#define ENABLE_LZO 1
#define ENABLE_LZ4 1
#define ENABLE_MANAGEMENT 1
-#define ENABLE_PKCS11 1
+//#define ENABLE_PKCS11 0
#define ENABLE_PLUGIN 1
#define ENABLE_PORT_SHARE 1
#define ENABLE_SOCKS 1
@@ -30,8 +30,8 @@
#define HAVE_IO_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_SYS_STAT_H 1
-#define HAVE_LZO_LZO1X_H 1
-#define HAVE_LZO_LZOUTIL_H 1
+//#define HAVE_LZO_LZO1X_H 1
+//#define HAVE_LZO_LZOUTIL_H 1
#define HAVE_VERSIONHELPERS_H 1
#define HAVE_ACCESS 1
@@ -86,3 +86,7 @@ typedef uint16_t in_port_t;
#ifdef HAVE_CONFIG_MSVC_LOCAL_H
#include <config-msvc-local.h>
#endif
+
+#define ENABLE_WINDCO 1
+#define ENABLE_DCO 1
+
@@ -784,41 +784,63 @@ dnl version in this tree which will be used by default. The
dnl git checkout inside the ovpn-dco/ directory is managed via git
dnl submodule.
dnl
-AC_ARG_VAR([DCO_SOURCEDIR], [Alternative ovpn-dco kernel module source directory])
-if test -z "${DCO_SOURCEDIR}"; then
- DCO_SOURCEDIR="${srcdir}/../ovpn-dco"
-fi
-AC_MSG_NOTICE([Using ovpn-dco source directory: ${DCO_SOURCEDIR}])
-AC_SUBST([DCO_SOURCEDIR])
+ AC_ARG_VAR([DCO_SOURCEDIR], [Alternative ovpn-dco kernel module source directory])
+ if test -z "${DCO_SOURCEDIR}"; then
+ case "$host" in
+ *-mingw*) DCO_SOURCEDIR="${srcdir}/../ovpn-dco-win";;
+ *) DCO_SOURCEDIR="${srcdir}/../ovpn-dco";;
+ esac
+ fi
+ AC_MSG_NOTICE([Using ovpn-dco source directory: ${DCO_SOURCEDIR}])
+ AC_SUBST([DCO_SOURCEDIR])
+ case "$host" in
+ *-*-linux*)
dnl
dnl Include generic netlink library used to talk to ovpn-dco
dnl
- saved_CFLAGS="${CFLAGS}"
- PKG_CHECK_MODULES(
- [LIBNL_GENL],
- [libnl-genl-3.0 >= 3.2.29],
- [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])]
- )
-
- DCO_CFLAGS="-I${DCO_SOURCEDIR}/include/uapi ${LIBNL_GENL_CFLAGS}"
-
- CFLAGS="${CFLAGS} ${DCO_CFLAGS}"
- AC_CHECK_HEADERS(
- [linux/ovpn_dco.h],
- ,
- [AC_MSG_ERROR([linux/ovpn_dco.h is missing (use DCO_SOURCE to set path to it, CFLAGS=${CFLAGS})])]
- )
- CFLAGS=${saved_CFLAGS}
- OPTIONAL_DCO_CFLAGS="${DCO_CFLAGS}"
- OPTIONAL_DCO_LIBS="${LIBNL_GENL_LIBS}"
-
- AC_DEFINE(ENABLE_LINUXDCO, 1, [Enable linux data channel offload])
- AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload])
-fi
-AM_CONDITIONAL([ENABLE_OVPNDCO], [test "${enable_dco}" = "yes"])
+ saved_CFLAGS="${CFLAGS}"
+ PKG_CHECK_MODULES(
+ [LIBNL_GENL],
+ [libnl-genl-3.0 >= 3.2.29],
+ [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])]
+ )
+ DCO_CFLAGS="-I${DCO_SOURCEDIR}/include/uapi ${LIBNL_GENL_CFLAGS}"
+
+ CFLAGS="${CFLAGS} ${DCO_CFLAGS}"
+ AC_CHECK_HEADERS(
+ [linux/ovpn_dco.h],
+ ,
+ [AC_MSG_ERROR([linux/ovpn_dco.h is missing (use DCO_SOURCE to set path to it, CFLAGS=${CFLAGS})])]
+ )
+ CFLAGS=${saved_CFLAGS}
+
+ OPTIONAL_DCO_LIBS="${LIBNL_GENL_LIBS}"
+
+ AC_DEFINE(ENABLE_LINUXDCO, 1, [Enable linux data channel offload])
+ ;;
+
+ *-mingw*)
+ DCO_CFLAGS="-I${DCO_SOURCEDIR}"
+
+ saved_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} ${DCO_CFLAGS}"
+ AC_CHECK_HEADERS(
+ [uapi/ovpn-dco.h],
+ ,
+ [AC_MSG_ERROR([uapi/ovpn-dco.h is missing (use DCO_SOURCEDIR to set path to it, CFLAGS=${CFLAGS})])]
+ )
+ CFLAGS=${saved_CFLAGS}
+
+ AC_DEFINE(ENABLE_WINDCO, 1, [Enable shared data channel offload for Windows])
+ ;;
+ esac
+
+ OPTIONAL_DCO_CFLAGS="${DCO_CFLAGS}"
+ AC_DEFINE(ENABLE_DCO, 1, [Enable shared data channel offload])
+fi
if test "${with_crypto_library}" = "openssl"; then
AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL])
@@ -88,6 +88,7 @@ openvpn_SOURCES = \
networking_iproute2.c networking_iproute2.h \
networking_sitnl.c networking_sitnl.h \
networking_linuxdco.c networking_linuxdco.h \
+ networking_windco.c networking_windco.h \
networking.h \
ntlm.c ntlm.h \
occ.c occ.h \
@@ -41,6 +41,7 @@ static inline void open_tun_dco(struct tuntap *tt, const char* dev) { ASSERT(fal
static inline void close_tun_dco(struct tuntap *tt) { ASSERT(false); }
#else
+#include "networking_windco.h"
#include "networking_linuxdco.h"
#include "crypto.h"
@@ -1737,7 +1737,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;
@@ -1761,7 +1762,15 @@ 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
+#ifdef ENABLE_WINDCO
+ && !c->c1.tuntap->dco.real_tun_init
+#endif
+ )
+#endif
+ )
{
#endif
@@ -1829,9 +1838,12 @@ 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
- /* open the tun device */
- open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap);
+ /* 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);
+ }
/* set the hardware address */
if (c->options.lladdr)
@@ -3646,6 +3658,26 @@ 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
+#ifdef ENABLE_WINDCO
+ && !c->c1.tuntap->dco.real_tun_init
+#endif
+ )
+ {
+ 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);
new file mode 100644
@@ -0,0 +1,301 @@
+/*
+ * 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(ENABLE_WINDCO)
+#include "syshead.h"
+
+#include "networking_windco.h"
+#include "dco.h"
+#include "tun.h"
+#include "crypto.h"
+#include "ssl_common.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 = { 0 };
+
+ tt.windows_driver = WINDOWS_DRIVER_WINDCO;
+
+ const char* device_guid;
+ tun_open_device(&tt, devname, &device_guid, gc);
+ tt.windows_driver = WINDOWS_DRIVER_WINDCO;
+
+ return tt;
+}
+
+void open_tun_dco(struct tuntap *tt, const char* dev)
+{
+ ASSERT(0);
+}
+
+void dco_start_tun(struct tuntap* tt)
+{
+ msg(D_DCO_DEBUG, "%s", __func__);
+
+ 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());
+ }
+}
+
+int dco_connect_wait(HANDLE handle, OVERLAPPED* ov, int timeout, volatile int* signal_received)
+{
+ while (timeout-- > 0)
+ {
+ DWORD transferred;
+ if (GetOverlappedResultEx(handle, ov, &transferred, 1000, FALSE) != 0)
+ {
+ /* TCP connection established by dco */
+ return 0;
+ }
+
+ DWORD err = GetLastError();
+ if (err != WAIT_TIMEOUT)
+ {
+ /* 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(struct tuntap *tt, 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 ovpn_set_peer(struct tuntap *tt, unsigned int peerid,
+ unsigned int keepalive_interval,
+ unsigned int keepalive_timeout)
+{
+ msg(D_DCO_DEBUG, "%s: peer-id %d, keepalive %d/%d", __func__, peerid,
+ keepalive_interval, keepalive_timeout);
+
+ OVPN_SET_PEER peer;
+
+ peer.KeepaliveInterval = keepalive_interval;
+ peer.KeepaliveTimeout = keepalive_timeout;
+
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(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(struct tuntap *tt, unsigned int peerid, ovpn_key_slot_t slot,
+ struct key_state *ks, const char* ciphername)
+{
+ msg(D_DCO_DEBUG, "%s: slot %d, key-id %d, peer-id %d, cipher %s",
+ __func__, slot, ks->key_id, peerid, ciphername);
+
+ struct key_ctx_bi *key = &ks->crypto_options.key_ctx_bi;
+ dco_check_key_ctx(key);
+
+ const int nonce_len = 8;
+ size_t key_len = cipher_kt_key_size(cipher_kt_get(ciphername));
+
+ OVPN_CRYPTO_DATA crypto_data;
+ ZeroMemory(&crypto_data, sizeof(crypto_data));
+
+ crypto_data.CipherAlg = get_dco_cipher(ciphername);
+ crypto_data.KeyId = ks->key_id;
+ crypto_data.PeerId = peerid;
+ crypto_data.KeySlot = slot;
+
+ CopyMemory(crypto_data.Encrypt.Key, key->encrypt.aead_key, key_len);
+ crypto_data.Encrypt.KeyLen = (char)key_len;
+ CopyMemory(crypto_data.Encrypt.NonceTail, key->encrypt.implicit_iv, nonce_len);
+
+ CopyMemory(crypto_data.Decrypt.Key, key->decrypt.aead_key, key_len);
+ crypto_data.Decrypt.KeyLen = (char)key_len;
+ CopyMemory(crypto_data.Decrypt.NonceTail, key->decrypt.implicit_iv, nonce_len);
+
+ ASSERT(crypto_data.CipherAlg > 0);
+
+ DWORD bytes_returned = 0;
+
+ secure_memzero(key->encrypt.aead_key, sizeof (key->encrypt.aead_key));
+ secure_memzero(key->decrypt.aead_key, sizeof (key->decrypt.aead_key));
+
+ if (!DeviceIoControl(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(struct tuntap *tt, unsigned int peerid, ovpn_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(struct tuntap *tt, unsigned int peer_id)
+{
+ msg(D_DCO_DEBUG, "%s: peer-id %d", __func__, peer_id);
+
+ DWORD bytes_returned = 0;
+ if (!DeviceIoControl(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 0;
+}
+#endif
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,47 @@
+/*
+ * 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
+ */
+
+#ifndef OPENVPN_NETWORKING_WINDCO_H
+#define OPENVPN_NETWORKING_WINDCO_H
+#if defined(ENABLE_WINDCO)
+#include "uapi/ovpn-dco.h"
+#include "buffer.h"
+
+
+typedef OVPN_KEY_SLOT ovpn_key_slot_t;
+
+#define DCO_SUPPORTED_CIPHERS "AES-128-GCM:AES-256-GCM:AES-192-GCM"
+
+struct dco_context {
+ bool real_tun_init;
+};
+
+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);
+
+typedef struct dco_context dco_context_t;
+
+#endif
+#endif //OPENVPN_NETWORKING_WINDCO_H
@@ -279,6 +279,7 @@
<ClCompile Include="mtu.c" />
<ClCompile Include="mudp.c" />
<ClCompile Include="multi.c" />
+ <ClCompile Include="networking_windco.c" />
<ClCompile Include="ntlm.c" />
<ClCompile Include="occ.c" />
<ClCompile Include="openvpn.c" />
@@ -365,6 +366,7 @@
<ClInclude Include="mtu.h" />
<ClInclude Include="mudp.h" />
<ClInclude Include="multi.h" />
+ <ClInclude Include="networking_windco.h" />
<ClInclude Include="ntlm.h" />
<ClInclude Include="occ.h" />
<ClInclude Include="openvpn.h" />
@@ -249,6 +249,9 @@
<ClCompile Include="ssl_util.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="networking_windco.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="base64.h">
@@ -374,6 +377,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>
@@ -2249,6 +2249,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)
+ {
+ check_option_conflict_dco(M_USAGE, options);
+ }
#endif /* ifdef _WIN32 */
/*
@@ -3001,8 +3006,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;
@@ -3088,7 +3093,7 @@ options_postprocess_setdefault_ncpciphers(struct options *o)
/* custom --data-ciphers set, keep list */
return;
}
- else if (cipher_kt_get("CHACHA20-POLY1305"))
+ else if (cipher_kt_get("CHACHA20-POLY1305") && !dco_win_enabled(o))
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";
}
@@ -3260,6 +3265,22 @@ static bool check_option_conflict_dco_platform(int msglevel, const struct option
}
return false;
}
+#elif defined(ENABLE_WINDCO)
+static bool check_option_conflict_dco_platform(int msglevel, const struct options *o)
+{
+ 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;
+ }
+ return false;
+}
#endif
bool check_option_conflict_dco(int msglevel, const struct options *o)
@@ -4050,7 +4071,8 @@ options_string(const struct options *o,
NULL,
false,
NULL,
- ctx);
+ ctx,
+ NULL);
if (tt)
{
tt_local = true;
@@ -4472,9 +4494,15 @@ 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;
}
}
@@ -865,10 +865,30 @@ void options_string_import(struct options *options,
/**
* Returns whether the current configuration has dco enabled.
*/
-#ifdef ENABLE_LINUXDCO
+#if defined(ENABLE_LINUXDCO)
static inline bool
dco_enabled(struct options *o) { return !o->tuntap_options.disable_dco; }
+#elif defined(ENABLE_WINDCO)
+static inline bool dco_enabled(struct options *o)
+{
+ return o->windows_driver == WINDOWS_DRIVER_WINDCO;
+}
+#else
+/* Dummy functions to avoid ifdefs in the other code */
+static inline bool
+dco_enabled(struct options *o) { return false; }
+#endif
+
+#if defined(ENABLE_WINDCO)
+static inline bool
+dco_win_enabled(struct options *o) { return dco_enabled(o); }
+#else
+static inline bool
+dco_win_enabled(struct options *o) { return false; }
+#endif
+
+#if defined(ENABLE_DCO)
/**
* Checks whether the options struct has any option that is not supported by
* our current dco implementation. If so it prints a warning at warning level
@@ -880,11 +900,6 @@ dco_enabled(struct options *o) { return !o->tuntap_options.disable_dco; }
bool
check_option_conflict_dco(int msglevel, const struct options *o);
#else
-/* Dummy functions to avoid ifdefs in the other code */
-
-static inline bool
-dco_enabled(struct options *o) { return false; }
-
static inline bool
check_option_conflict_dco(int msglevel, struct options *o) { return false; }
#endif
@@ -2141,6 +2141,38 @@ phase2_socks_client(struct link_socket *sock, struct signal_info *sig_info)
resolve_remote(sock, 1, NULL, &sig_info->signal_received);
}
+#if defined(ENABLE_WINDCO)
+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)
+ {
+ goto done;
+ }
+ 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;
+done:
+ ;
+}
+#endif
+
/* finalize socket initialization */
void
link_socket_init_phase2(struct context *c)
@@ -2180,7 +2212,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(ENABLE_WINDCO)
+ if (dco_win_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 */
@@ -2243,6 +2292,7 @@ link_socket_init_phase2(struct context *c)
}
phase2_set_socket_flags(sock);
+
linksock_print_addr(sock);
done:
@@ -3199,7 +3249,14 @@ link_socket_read_tcp(struct link_socket *sock,
if (!sock->stream_buf.residual_fully_formed)
{
#ifdef _WIN32
- len = socket_finalize(sock->sd, &sock->reads, buf, NULL);
+ if (sock->info.dco_installed)
+ {
+ len = tun_finalize((HANDLE)sock->sd, &sock->reads, buf);
+ }
+ else
+ {
+ len = socket_finalize(sock->sd, &sock->reads, buf, NULL);
+ }
#else
struct buffer frag;
stream_buf_get_next(&sock->stream_buf, &frag);
@@ -3355,7 +3412,14 @@ link_socket_write_tcp(struct link_socket *sock,
len = htonps(len);
ASSERT(buf_write_prepend(buf, &len, sizeof(len)));
#ifdef _WIN32
- return link_socket_write_win32(sock, buf, to);
+ if (sock->info.dco_installed)
+ {
+ return link_socket_write_win32_dco(sock, buf, to);
+ }
+ else
+ {
+ return link_socket_write_win32(sock, buf, to);
+ }
#else
return link_socket_write_tcp_posix(sock, buf, to);
#endif
@@ -3478,8 +3542,20 @@ socket_recv_queue(struct link_socket *sock, int maxsize)
/* the overlapped read will signal this event on I/O completion */
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);
@@ -3532,7 +3608,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;
@@ -3577,7 +3660,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;
@@ -3638,8 +3735,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;
@@ -3664,6 +3770,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
socket_finalize(SOCKET s,
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.
@@ -1021,7 +1022,17 @@ link_socket_read_udp_win32(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from)
{
- return socket_finalize(sock->sd, &sock->reads, buf, from);
+ if (sock->info.dco_installed)
+ {
+ /* from address was set on socket creation and the kernel
+ * checks that it matches for us */
+ addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest);
+ return tun_finalize((HANDLE)sock->sd, &sock->reads, buf);
+ }
+ else
+ {
+ return socket_finalize(sock->sd, &sock->reads, buf, from);
+ }
}
#else /* ifdef _WIN32 */
@@ -1038,7 +1049,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;
@@ -1082,10 +1096,6 @@ link_socket_write_win32(struct link_socket *sock,
if (overlapped_io_active(&sock->writes))
{
status = socket_finalize(sock->sd, &sock->writes, NULL, NULL);
- if (status < 0)
- {
- err = WSAGetLastError();
- }
}
socket_send_queue(sock, buf, to);
if (status < 0)
@@ -1099,6 +1109,29 @@ link_socket_write_win32(struct link_socket *sock,
}
}
+static inline int
+link_socket_write_win32_dco(struct link_socket* sock,
+ struct buffer* buf,
+ struct link_socket_actual* to)
+{
+ int err = 0;
+ int status = 0;
+ if (overlapped_io_active(&sock->writes))
+ {
+ status = tun_finalize((HANDLE)sock->sd, &sock->writes, NULL);
+ }
+ socket_send_queue(sock, buf, to);
+ if (status < 0)
+ {
+ SetLastError(err);
+ return status;
+ }
+ else
+ {
+ return BLEN(buf);
+ }
+}
+
#else /* ifdef _WIN32 */
size_t link_socket_write_udp_posix_sendmsg(struct link_socket *sock,
@@ -1140,7 +1173,14 @@ link_socket_write_udp(struct link_socket *sock,
struct link_socket_actual *to)
{
#ifdef _WIN32
- return link_socket_write_win32(sock, buf, to);
+ if (sock->info.dco_installed)
+ {
+ return link_socket_write_win32_dco(sock, buf, to);
+ }
+ else
+ {
+ return link_socket_write_win32(sock, buf, to);
+ }
#else
return link_socket_write_udp_posix(sock, buf, to);
#endif
@@ -1152,8 +1192,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);
+ }
+#ifdef ENABLE_WINDCO
+ 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,13 @@ init_tun_post(struct tuntap *tt,
{
tt->options = *options;
#ifdef _WIN32
+#ifdef ENABLED_WINDCO
+ if (tt->windows_driver == WINDOWS_DRIVER_WINDCO)
+ {
+ dco_start_tun(tt);
+ return;
+ }
+#endif
overlapped_io_init(&tt->reads, frame, FALSE, true);
overlapped_io_init(&tt->writes, frame, TRUE, true);
tt->adapter_index = TUN_ADAPTER_INDEX_INVALID;
@@ -3438,6 +3455,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";
@@ -3878,6 +3898,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)
{
@@ -4232,7 +4257,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.");
}
}
@@ -6433,7 +6460,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;
@@ -6453,7 +6480,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,
@@ -6489,7 +6516,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);
@@ -6780,7 +6807,6 @@ 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);
@@ -40,7 +40,7 @@
#include "misc.h"
#include "networking.h"
#include "ring_buffer.h"
-#include "networking_linuxdco.h"
+#include "dco.h"
#ifdef _WIN32
#define WINTUN_COMPONENT_ID "wintun"
@@ -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" */
@@ -246,6 +249,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
/*
@@ -257,6 +264,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);
@@ -283,7 +292,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,
@@ -659,6 +669,11 @@ 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
@@ -684,6 +699,12 @@ tun_standby(struct tuntap *tt)
return true;
}
+
+static inline bool is_windco(struct tuntap *tt)
+{
+ return false;
+}
+
#endif /* ifdef _WIN32 */
/*
@@ -707,25 +728,28 @@ tun_set(struct tuntap *tt,
void *arg,
unsigned int *persistent)
{
- if (tuntap_defined(tt))
+ if (!tuntap_defined(tt) || is_windco(tt))
{
- /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
- if (!persistent || *persistent != rwflags)
+ return;
+ }
+
+ /* 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);