From patchwork Sat Aug 13 10:42:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 2669 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director15.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net with LMTP id eIkKNQIN+GKbAwAAIUCqbw (envelope-from ) for ; Sat, 13 Aug 2022 16:43:46 -0400 Received: from proxy11.mail.ord1d.rsapps.net ([172.30.191.6]) by director15.mail.ord1d.rsapps.net with LMTP id kOX6NAIN+GIsHwAAIcMcQg (envelope-from ) for ; Sat, 13 Aug 2022 16:43:46 -0400 Received: from smtp4.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy11.mail.ord1d.rsapps.net with LMTPS id iD6gNAIN+GKuNgAAgKDEHA (envelope-from ) for ; Sat, 13 Aug 2022 16:43:46 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp4.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=unstable.cc; dmarc=fail (p=none; dis=none) header.from=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: a02b8cdc-1b48-11ed-b83e-525400760ffc-1-1 Received: from [216.105.38.7] ([216.105.38.7:49426] helo=lists.sourceforge.net) by smtp4.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.38.62370 r(:)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id DE/A7-20838-20D08F26; Sat, 13 Aug 2022 16:43:46 -0400 Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1oMxy2-0005lR-Jl; Sat, 13 Aug 2022 20:42:46 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1oMxy0-0005l9-TE for openvpn-devel@lists.sourceforge.net; Sat, 13 Aug 2022 20:42:44 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:Message-Id: Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=j3AHxXa1XkPMynrcyJnUrIwNhB5Aal4VWicsclXieuc=; b=BRG5Pr8XGNDCtrwqjWOyDCDOlz trV3s1e8CDZN3Q6fNSqPXAW27JUmaqCicgMt1eiIjBPvLc/Wx3RnTI28Kx3Y9+d2rqf7xJyMEgswp U/Qg3s9uQ3MsKW9UZeFB3i1keyEY8f6NxQjY617U1A3k7QSMQ+qeWTeAvlIJ48D8l3oM=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:Message-Id:Date:Subject:Cc:To:From :Sender:Reply-To:Content-Type:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To: References:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post: List-Owner:List-Archive; bh=j3AHxXa1XkPMynrcyJnUrIwNhB5Aal4VWicsclXieuc=; b=T kpH/pZuzRUfuR6MuOWhe0/x65CXXuw4kcNb8SZxUFKOrc4aHU+/I2v59adGqzqUu3+KXNn8oQkJXQ ax8pmPL3ee5PUazkBVRRN8zuYyLs/u5+frBjxzyiqkNF2MQG9BasX6TRSU4eRIwYNeywajjTtmIm/ 7Gd09HahqCFAlEU0=; Received: from wilbur.contactoffice.com ([212.3.242.68]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1oMxxy-0012Dc-Ez for openvpn-devel@lists.sourceforge.net; Sat, 13 Aug 2022 20:42:44 +0000 Received: from smtpauth2.co-bxl (smtpauth2.co-bxl [10.2.0.24]) by wilbur.contactoffice.com (Postfix) with ESMTP id 1F79516FF; Sat, 13 Aug 2022 22:42:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1660423356; s=20220809-q8oc; d=unstable.cc; i=a@unstable.cc; h=From:Cc:Date:Message-Id:MIME-Version:Content-Transfer-Encoding; l=24319; bh=j3AHxXa1XkPMynrcyJnUrIwNhB5Aal4VWicsclXieuc=; b=cVnS9uB9qYb2s+63rtYMbwuuwiRFijbD6frHV+6Y8KSC9NHyizQkZBSGs6sANvqs ftLgDMyNntxDIQDA7PA7lPy+eS/jxS2QmdcHe6fzGW4yIMv2aA08koHjuT83kOT3JSV Ys4vPOz8QvBDYvtGoIAGwTkt3UkU0h9T04+EUjQyeTBvV0iTWTSzAoPKiuFK2IeeLre C0qbXG5NuxsufwNfzhK4jH4lUas47w8G5DULktIR2nRPZ1/vfp0+IlHJ7PZjROdQB2V Y9/WeI0ddyUJXhpfKlNV9g8Ju90oOA+8Zr62xMhfskt6/RZGi/I6Fk3IExDJzStjeTa SQoSdPkwtQ== Received: by smtp.mailfence.com with ESMTPSA ; Sat, 13 Aug 2022 22:42:27 +0200 (CEST) From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Sat, 13 Aug 2022 22:42:18 +0200 Message-Id: <20220813204224.22576-1-a@unstable.cc> X-Mailer: git-send-email 2.35.1 MIME-Version: 1.0 X-Spam-Status: No, hits=-2.9 required=4.7 symbols=ALL_TRUSTED, BAYES_00, T_SCC_BODY_TEXT_LINE device=10.2.0.1 X-ContactOffice-Account: com:375058688 X-Spam-Report: Spam detection software, running on the system "util-spamd-2.v13.lw.sourceforge.com", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Signed-off-by: Arne Schwabe Signed-off-by: Lev Stipakov Signed-off-by: Antonio Quartulli --- Changes from v100: * rebased (fixed conflict in configure.ac) * fixed access to disable_dco member in dco.c * renamed ovpn-dco-win.h to ovpn_dco_win.h * make tun_open_device and close_tun_handle non s [...] Content analysis details: (-0.9 points, 6.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at https://www.dnswl.org/, low trust [212.3.242.68 listed in list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.0 T_SCC_BODY_TEXT_LINE No description available. X-Headers-End: 1oMxxy-0012Dc-Ez Subject: [Openvpn-devel] [PATCH v101 1/7] dco-win: introduce low-level code for handling ovpn-dco-win in Windows X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lev Stipakov , Antonio Quartulli Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox Signed-off-by: Arne Schwabe Signed-off-by: Lev Stipakov Signed-off-by: Antonio Quartulli Acked-by: Lev Stipakov --- Changes from v100: * rebased (fixed conflict in configure.ac) * fixed access to disable_dco member in dco.c * renamed ovpn-dco-win.h to ovpn_dco_win.h * make tun_open_device and close_tun_handle non static * add ASSERT(0) to functions that should not be called on Windows * remove real_tun_init member (unused now) --- config-msvc.h | 2 + configure.ac | 9 +- dev-tools/special-files.lst | 1 + src/openvpn/Makefile.am | 4 +- src/openvpn/dco.c | 2 +- src/openvpn/dco_internal.h | 1 + src/openvpn/dco_win.c | 400 ++++++++++++++++++++++++++++ src/openvpn/dco_win.h | 57 ++++ src/openvpn/openvpn.vcxproj | 3 + src/openvpn/openvpn.vcxproj.filters | 9 + src/openvpn/ovpn_dco_win.h | 108 ++++++++ src/openvpn/tun.c | 4 +- src/openvpn/tun.h | 10 +- 13 files changed, 602 insertions(+), 8 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 #endif + +#define ENABLE_DCO 1 diff --git a/configure.ac b/configure.ac index f715b404..be31889e 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], @@ -792,8 +792,11 @@ dnl AC_DEFINE(ENABLE_DCO, 1, [Enable data channel offload for FreeBSD]) AC_MSG_NOTICE([Enabled ovpn-dco support for FreeBSD]) ;; + *-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..e5f2fc27 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 2a139b23..936d038c 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -56,6 +56,7 @@ openvpn_SOURCES = \ dco.c dco.h dco_internal.h \ dco_freebsd.c dco_freebsd.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 \ @@ -79,6 +80,7 @@ openvpn_SOURCES = \ memdbg.h \ misc.c misc.h \ ovpn_dco_linux.h \ + ovpn_dco_win.h \ platform.c platform.h \ console.c console.h console_builtin.c console_systemd.c \ mroute.c mroute.h \ @@ -152,5 +154,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.c b/src/openvpn/dco.c index ef204767..b342bee1 100644 --- a/src/openvpn/dco.c +++ b/src/openvpn/dco.c @@ -289,7 +289,7 @@ dco_check_option_conflict(int msglevel, const struct options *o) * specified at all. In the latter case, later logic will most likely stop * OpenVPN, so no need to print any message here. */ - if (o->tuntap_options.disable_dco || !o->dev) + if (!dco_enabled(o) || !o->dev) { return false; } diff --git a/src/openvpn/dco_internal.h b/src/openvpn/dco_internal.h index 728e3092..b973d743 100644 --- a/src/openvpn/dco_internal.h +++ b/src/openvpn/dco_internal.h @@ -29,6 +29,7 @@ #include "dco_freebsd.h" #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..28bcccd4 --- /dev/null +++ b/src/openvpn/dco_win.c @@ -0,0 +1,400 @@ +/* + * Interface to ovpn-win-dco networking code + * + * Copyright (C) 2020-2022 Arne Schwabe + * Copyright (C) 2020-2022 OpenVPN Inc + * + * 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 +#include +#include + +#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_DCO }; + 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"); + } + + /* 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 (GetOverlappedResultEx(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 */ + msg(M_NONFATAL | M_ERRNO, "dco connect error"); + *signal_received = SIGUSR1; + 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, "dco connect timeout"); + *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"); + } + 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 | M_ERRNO, "DeviceIoControl(OVPN_IOCTL_SET_PEER) failed"); + 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"); + 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"); + return -1; + } + return 0; +} + +bool +dco_available(int msglevel) +{ + return true; +} + +int +dco_do_read(dco_context_t *dco) +{ + /* no-op on windows */ + ASSERT(0); + return 0; +} + +int +dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf) +{ + /* no-op on windows */ + ASSERT(0); + return 0; +} + +void +dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) +{ + /* no-op on windows */ + ASSERT(0); +} + +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..348fc568 --- /dev/null +++ b/src/openvpn/dco_win.h @@ -0,0 +1,57 @@ +/* + * Interface to ovpn-win-dco networking code + * + * Copyright (C) 2020-2022 Arne Schwabe + * Copyright (C) 2020-2022 OpenVPN Inc + * + * 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 { + 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..ccd29cd8 100644 --- a/src/openvpn/openvpn.vcxproj +++ b/src/openvpn/openvpn.vcxproj @@ -278,6 +278,7 @@ + @@ -367,6 +368,7 @@ + @@ -402,6 +404,7 @@ + diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters index 16905079..bf0ba708 100644 --- a/src/openvpn/openvpn.vcxproj.filters +++ b/src/openvpn/openvpn.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + Source Files @@ -314,6 +317,9 @@ Header Files + + Header Files + Header Files @@ -416,6 +422,9 @@ Header Files + + Header Files + Header Files 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 + * + * Author: Lev Stipakov + * + * 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 +#endif +#include +#include + +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) diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 1651eeea..93cf0301 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -6532,7 +6532,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); @@ -6824,7 +6824,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 661e4d01..de2f68fc 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_DCO }; #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" */ @@ -259,6 +262,11 @@ 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 tun_open_device(struct tuntap *tt, const char *dev_node, + const char **device_guid, struct gc_arena *gc); + +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);