Fo the reference - I have tested the installer https://github.com/lstipakov/openvpn-build/actions/runs/2902240643
(which includes openvpn-build dco changes and openvpn master branch with this patch) on Windows 11 (VMware ESXi) - dco-win/tap-windows6/wintun drivers work as expected. I also verified that ChaCha20-Poly1305 (cipher supported starting from Windows 11) works fine with the dco-win driver , as well as the good old AES-256-GCM. More testers are very welcomed. ma 22. elok. 2022 klo 11.56 Lev Stipakov (lstipa...@gmail.com) kirjoitti: > > From: Antonio Quartulli <a...@unstable.cc> > > With this change it is possible to use ovpn-dco-win when running OpenVPN > in client or P2P mode. > > Signed-off-by: Arne Schwabe <a...@rfc2549.org> > Signed-off-by: Lev Stipakov <l...@openvpn.net> > Signed-off-by: Antonio Quartulli <a...@unstable.cc> > --- > Changes from v102: > * use "windows-driver ovpn-dco" without trailing "-win", since > "windows" is already implied by option name. > > Changes from v101: > * move tuntap_is_dco_win() check from init.c to open_tun() in tun.c > * move linksock_print_addr() into create_socket_dco_win() to simplify > link_socket_init_phase2() > * fix chachapoly support on Windows for non-DCO case > > Changes from v100: > * rebased (fixed conflicts in options.h and tun.h) > > Changes from v3: > * rename WINDOWS_DRIVER_WINDCO to WINDOWS_DRIVER_DCO > * add reference string check > > Changes from v2: > * added is_tun_type_set() and removed real_tun_init flag > * moved link-close to do_close_tun() > > Changes from v1: > * use suffix _dco_win instead of _windco > * create helper function to retrieve last error from socket object > > src/openvpn/dco.c | 4 +- > src/openvpn/forward.c | 8 ++++ > src/openvpn/init.c | 24 +++++++++++- > src/openvpn/options.c | 25 +++++++++--- > src/openvpn/options.h | 15 +++----- > src/openvpn/socket.c | 89 ++++++++++++++++++++++++++++++++++++++++--- > src/openvpn/socket.h | 25 ++++++++---- > src/openvpn/tun.c | 58 ++++++++++++++++++++++------ > src/openvpn/tun.h | 66 ++++++++++++++++++++++++-------- > 9 files changed, 255 insertions(+), 59 deletions(-) > > diff --git a/src/openvpn/dco.c b/src/openvpn/dco.c > index 99b544a5..ad35fee8 100644 > --- a/src/openvpn/dco.c > +++ b/src/openvpn/dco.c > @@ -229,13 +229,13 @@ dco_check_startup_option_conflict(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."); > + "with ovpn-dco."); > return false; > } > > if (o->persist_tun) > { > - msg(msglevel, "--persist-tun is not supported with ovpn-dco-win."); > + msg(msglevel, "--persist-tun is not supported with ovpn-dco."); > return false; > } > #elif defined(TARGET_LINUX) > diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c > index d70b4f52..e45aa0f9 100644 > --- a/src/openvpn/forward.c > +++ b/src/openvpn/forward.c > @@ -864,9 +864,17 @@ read_incoming_link(struct context *c) > return; > } > > + /* check_status() call below resets last-error code */ > + bool dco_win_timeout = tuntap_is_dco_win_timeout(c->c1.tuntap, status); > + > /* check recvfrom status */ > check_status(status, "read", c->c2.link_socket, NULL); > > + if (dco_win_timeout) > + { > + trigger_ping_timeout_signal(c); > + } > + > /* Remove socks header if applicable */ > socks_postprocess_incoming_link(c); > > diff --git a/src/openvpn/init.c b/src/openvpn/init.c > index 1da21710..9917cefe 100644 > --- a/src/openvpn/init.c > +++ b/src/openvpn/init.c > @@ -1699,7 +1699,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; > @@ -1723,7 +1724,7 @@ can_preserve_tun(struct tuntap *tt) > #ifdef TARGET_ANDROID > return false; > #else > - return tt; > + return is_tun_type_set(tt); > #endif > } > > @@ -1934,6 +1935,16 @@ do_close_tun_simple(struct context *c) > static void > do_close_tun(struct context *c, bool force) > { > + /* With dco-win we open tun handle in the very beginning. > + * In case when tun wasn't opened - like we haven't connected, > + * we still need to close tun handle > + */ > + if (tuntap_is_dco_win(c->c1.tuntap) && !is_tun_type_set(c->c1.tuntap)) > + { > + do_close_tun_simple(c); > + return; > + } > + > if (!c->c1.tuntap || !c->c1.tuntap_owned) > { > return; > @@ -3574,6 +3585,15 @@ do_close_free_key_schedule(struct context *c, bool > free_ssl_ctx) > static void > do_close_link_socket(struct context *c) > { > + /* in dco-win case, link socket is a tun handle which is > + * closed in do_close_tun(). Set it to UNDEFINED so > + * we won't use WinSock API to close it. */ > + if (tuntap_is_dco_win(c->c1.tuntap) && c->c2.link_socket > + && c->c2.link_socket->info.dco_installed) > + { > + c->c2.link_socket->sd = SOCKET_UNDEFINED; > + } > + > if (c->c2.link_socket && c->c2.link_socket_owned) > { > link_socket_close(c->c2.link_socket); > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index 2b0bb20c..703dca29 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -3338,9 +3338,11 @@ 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 > - && (options->tuntap_options.ip_win32_type == IPW32_SET_DHCP_MASQ || > options->tuntap_options.ip_win32_type == IPW32_SET_ADAPTIVE)) > + /* when using wintun/ovpn-dco, kernel doesn't send DHCP requests, so > don't use it */ > + if ((options->windows_driver == WINDOWS_DRIVER_WINTUN > + || options->windows_driver == WINDOWS_DRIVER_DCO) > + && (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; > } > @@ -3434,6 +3436,10 @@ options_postprocess_setdefault_ncpciphers(struct > options *o) > /* custom --data-ciphers set, keep list */ > return; > } > + else if (dco_enabled(o)) > + { > + o->ncp_ciphers = dco_get_supported_ciphers(); > + } > else if (cipher_valid("CHACHA20-POLY1305")) > { > o->ncp_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305"; > @@ -4167,7 +4173,8 @@ options_string(const struct options *o, > NULL, > false, > NULL, > - ctx); > + ctx, > + NULL); > if (tt) > { > tt_local = true; > @@ -4554,13 +4561,19 @@ parse_windows_driver(const char *str, const int > msglevel) > { > return WINDOWS_DRIVER_WINTUN; > } > + > + else if (streq(str, "ovpn-dco")) > + { > + return WINDOWS_DRIVER_DCO; > + } > else > { > - msg(msglevel, "--windows-driver must be tap-windows6 or wintun"); > + msg(msglevel, "--windows-driver must be tap-windows6, wintun " > + "or ovpn-dco"); > return WINDOWS_DRIVER_UNSPECIFIED; > } > } > -#endif > +#endif /* ifdef _WIN32 */ > > /* > * parse/print topology coding > diff --git a/src/openvpn/options.h b/src/openvpn/options.h > index 83c97ded..6d9174a4 100644 > --- a/src/openvpn/options.h > +++ b/src/openvpn/options.h > @@ -876,24 +876,19 @@ void options_string_import(struct options *options, > > bool key_is_external(const struct options *options); > > -#if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || defined(TARGET_FREEBSD)) > - > /** > * 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_DCO; > +#elif defined(ENABLE_DCO) > return !o->tuntap_options.disable_dco; > -} > - > -#else /* if defined(ENABLE_DCO) && (defined(TARGET_LINUX) || > defined(TARGET_FREEBSD))*/ > - > -static inline bool > -dco_enabled(const struct options *o) > -{ > +#else > return false; > +#endif /* defined(_WIN32) */ > } > > -#endif > #endif /* ifndef OPTIONS_H */ > diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c > index b4c20f69..4e29327b 100644 > --- a/src/openvpn/socket.c > +++ b/src/openvpn/socket.c > @@ -2123,6 +2123,43 @@ phase2_socks_client(struct link_socket *sock, struct > signal_info *sig_info) > resolve_remote(sock, 1, NULL, &sig_info->signal_received); > } > > +#if defined(_WIN32) > +static void > +create_socket_dco_win(struct context *c, struct link_socket *sock, > + volatile int *signal_received) > +{ > + struct tuntap *tt; > + /* In this case persist-tun is enabled, which we don't support yet */ > + ASSERT(!c->c1.tuntap); > + > + ALLOC_OBJ(tt, struct tuntap); > + > + *tt = dco_create_socket(sock->info.lsa->current_remote, > + sock->bind_local, > + sock->info.lsa->bind_local, > + c->options.dev_node, > + &c->gc, > + > get_server_poll_remaining_time(sock->server_poll_timeout), > + signal_received); > + > + /* This state is used by signal handler which does teardown, > + * so it has to be set before return */ > + c->c1.tuntap = tt; > + sock->info.dco_installed = true; > + > + if (*signal_received) > + { > + return; > + } > + > + /* Ensure we can "safely" cast the handle to a socket */ > + static_assert(sizeof(sock->sd) == sizeof(tt->hand), "HANDLE and SOCKET > size differs"); > + sock->sd = (SOCKET)tt->hand; > + > + linksock_print_addr(sock); > +} > +#endif /* if defined(_WIN32) */ > + > /* finalize socket initialization */ > void > link_socket_init_phase2(struct context *c) > @@ -2162,7 +2199,18 @@ link_socket_init_phase2(struct context *c) > /* If a valid remote has been found, create the socket with its addrinfo > */ > if (sock->info.lsa->current_remote) > { > - create_socket(sock, sock->info.lsa->current_remote); > +#if defined(_WIN32) > + if (dco_enabled(&c->options)) > + { > + create_socket_dco_win(c, sock, &sig_info->signal_received); > + goto done; > + } > + else > +#endif > + { > + create_socket(sock, sock->info.lsa->current_remote); > + } > + > } > > /* If socket has not already been created create it now */ > @@ -3430,6 +3478,17 @@ link_socket_write_udp_posix_sendmsg(struct link_socket > *sock, > > #ifdef _WIN32 > > +static int > +socket_get_last_error(const struct link_socket *sock) > +{ > + if (sock->info.dco_installed) > + { > + return GetLastError(); > + } > + > + return WSAGetLastError(); > +} > + > int > socket_recv_queue(struct link_socket *sock, int maxsize) > { > @@ -3463,7 +3522,14 @@ socket_recv_queue(struct link_socket *sock, int > maxsize) > ASSERT(ResetEvent(sock->reads.overlapped.hEvent)); > sock->reads.flags = 0; > > - if (proto_is_udp(sock->info.proto)) > + if (sock->info.dco_installed) > + { > + status = ReadFile((HANDLE)sock->sd, wsabuf[0].buf, wsabuf[0].len, > + &sock->reads.size, &sock->reads.overlapped); > + /* Readfile status is inverted from WSARecv */ > + status = !status; > + } > + else if (proto_is_udp(sock->info.proto)) > { > sock->reads.addr_defined = true; > sock->reads.addrlen = sizeof(sock->reads.addr6); > @@ -3516,7 +3582,7 @@ socket_recv_queue(struct link_socket *sock, int maxsize) > } > else > { > - status = WSAGetLastError(); > + status = socket_get_last_error(sock); > if (status == WSA_IO_PENDING) /* operation queued? */ > { > sock->reads.iostate = IOSTATE_QUEUED; > @@ -3561,7 +3627,16 @@ socket_send_queue(struct link_socket *sock, struct > buffer *buf, const struct lin > ASSERT(ResetEvent(sock->writes.overlapped.hEvent)); > sock->writes.flags = 0; > > - if (proto_is_udp(sock->info.proto)) > + if (sock->info.dco_installed) > + { > + status = WriteFile((HANDLE)sock->sd, wsabuf[0].buf, > wsabuf[0].len, > + &sock->writes.size, &sock->writes.overlapped); > + > + /* WriteFile status is inverted from WSASendTo */ > + status = !status; > + > + } > + else if (proto_is_udp(sock->info.proto)) > { > /* set destination address for UDP writes */ > sock->writes.addr_defined = true; > @@ -3622,8 +3697,9 @@ socket_send_queue(struct link_socket *sock, struct > buffer *buf, const struct lin > } > else > { > - status = WSAGetLastError(); > - if (status == WSA_IO_PENDING) /* operation queued? */ > + status = socket_get_last_error(sock); > + /* both status code have the identical value */ > + if (status == WSA_IO_PENDING || status == ERROR_IO_PENDING) /* > operation queued? */ > { > sock->writes.iostate = IOSTATE_QUEUED; > sock->writes.status = status; > @@ -3648,6 +3724,7 @@ socket_send_queue(struct link_socket *sock, struct > buffer *buf, const struct lin > return sock->writes.iostate; > } > > +/* Returns the number of bytes successfully read */ > int > sockethandle_finalize(sockethandle_t sh, > struct overlapped_io *io, > diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h > index 0d521d22..462afa31 100644 > --- a/src/openvpn/socket.h > +++ b/src/openvpn/socket.h > @@ -34,6 +34,7 @@ > #include "proxy.h" > #include "socks.h" > #include "misc.h" > +#include "tun.h" > > /* > * OpenVPN's default port number as assigned by IANA. > @@ -937,7 +938,8 @@ socket_connection_reset(const struct link_socket *sock, > int status) > { > const int err = openvpn_errno(); > #ifdef _WIN32 > - return err == WSAECONNRESET || err == WSAECONNABORTED; > + return err == WSAECONNRESET || err == WSAECONNABORTED > + || err == ERROR_CONNECTION_ABORTED; > #else > return err == ECONNRESET; > #endif > @@ -1048,6 +1050,11 @@ link_socket_read_udp_win32(struct link_socket *sock, > struct link_socket_actual *from) > { > sockethandle_t sh = { .s = sock->sd }; > + if (sock->info.dco_installed) > + { > + addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest); > + sh.is_handle = true; > + } > return sockethandle_finalize(sh, &sock->reads, buf, from); > } > > @@ -1057,7 +1064,7 @@ int link_socket_read_udp_posix(struct link_socket *sock, > struct buffer *buf, > struct link_socket_actual *from); > > -#endif > +#endif /* ifdef _WIN32 */ > > /* read a TCP or UDP packet from link */ > static inline int > @@ -1065,7 +1072,10 @@ link_socket_read(struct link_socket *sock, > struct buffer *buf, > struct link_socket_actual *from) > { > - if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ > + if (proto_is_udp(sock->info.proto) > + || sock->info.dco_installed) > + /* unified UDPv4 and UDPv6, for DCO the kernel > + * will strip the length header */ > { > int res; > > @@ -1106,19 +1116,19 @@ link_socket_write_win32(struct link_socket *sock, > { > int err = 0; > int status = 0; > - sockethandle_t sh = { .s = sock->sd }; > + sockethandle_t sh = { .s = sock->sd, .is_handle = > sock->info.dco_installed }; > if (overlapped_io_active(&sock->writes)) > { > status = sockethandle_finalize(sh, &sock->writes, NULL, NULL); > if (status < 0) > { > - err = WSAGetLastError(); > + err = SocketHandleGetLastError(sh); > } > } > socket_send_queue(sock, buf, to); > if (status < 0) > { > - WSASetLastError(err); > + SocketHandleSetLastError(sh, err); > return status; > } > else > @@ -1180,8 +1190,9 @@ link_socket_write(struct link_socket *sock, > struct buffer *buf, > struct link_socket_actual *to) > { > - if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ > + if (proto_is_udp(sock->info.proto) || sock->info.dco_installed) > { > + /* unified UDPv4 and UDPv6 and DCO (kernel adds size header) */ > return link_socket_write_udp(sock, buf, to); > } > else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ > diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c > index 7a320512..94803acd 100644 > --- a/src/openvpn/tun.c > +++ b/src/openvpn/tun.c > @@ -743,12 +743,14 @@ 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 tuntap *tt; > - > - ALLOC_OBJ(tt, struct tuntap); > - clear_tuntap(tt); > + if (!tt) > + { > + ALLOC_OBJ(tt, struct tuntap); > + clear_tuntap(tt); > + } > > tt->type = dev_type_enum(dev, dev_type); > tt->topology = topology; > @@ -890,6 +892,12 @@ init_tun_post(struct tuntap *tt, > { > tt->options = *options; > #ifdef _WIN32 > + if (tt->windows_driver == WINDOWS_DRIVER_DCO) > + { > + 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; > @@ -3542,6 +3550,9 @@ print_windows_driver(enum windows_driver_type > windows_driver) > case WINDOWS_DRIVER_WINTUN: > return "wintun"; > > + case WINDOWS_DRIVER_DCO: > + return "ovpn-dco"; > + > default: > return "unspecified"; > } > @@ -3930,6 +3941,10 @@ get_tap_reg(struct gc_arena *gc) > { > windows_driver = WINDOWS_DRIVER_WINTUN; > } > + else if (strcasecmp(component_id, "ovpn-dco") == 0) > + { > + windows_driver = WINDOWS_DRIVER_DCO; > + } > > if (windows_driver != WINDOWS_DRIVER_UNSPECIFIED) > { > @@ -4284,7 +4299,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 adapters " > + "on this system. You should be able to create an adapter " > + "by using tapctl.exe utility."); > } > } > > @@ -6484,17 +6501,30 @@ 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_DCO) > { > const struct device_instance_id_interface *dev_if; > > for (dev_if = device_instance_id_interface; dev_if != NULL; dev_if = > dev_if->next) > { > - if (strcmp((const char *)dev_if->net_cfg_instance_id, > device_guid) == 0) > + if (strcmp((const char *)dev_if->net_cfg_instance_id, > device_guid) != 0) > { > - path = dev_if->device_interface; > - break; > + continue; > + } > + > + if (tt->windows_driver == WINDOWS_DRIVER_DCO) > + { > + char *last_sep = strrchr(dev_if->device_interface, '\\'); > + if (!last_sep > + || strcmp(last_sep + 1, DCO_WIN_REFERENCE_STRING) != 0) > + { > + continue; > + } > } > + > + path = dev_if->device_interface; > + break; > } > if (path == NULL) > { > @@ -6503,7 +6533,7 @@ tun_try_open_device(struct tuntap *tt, const char > *device_guid, const struct dev > } > else > { > - /* Open TAP-Windows adapter */ > + /* Open TAP-Windows */ > openvpn_snprintf(tuntap_device_path, sizeof(tuntap_device_path), > "%s%s%s", > USERMODEDEVICEDIR, > device_guid, > @@ -6710,6 +6740,12 @@ void > open_tun(const char *dev, const char *dev_type, const char *dev_node, struct > tuntap *tt, > openvpn_net_ctx_t *ctx) > { > + /* dco-win already opened the device, which handle we treat as socket */ > + if (tuntap_is_dco_win(tt)) > + { > + return; > + } > + > const char *device_guid = NULL; > > /*netcmd_semaphore_lock ();*/ > diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h > index de9a09f8..1cca1cfb 100644 > --- a/src/openvpn/tun.h > +++ b/src/openvpn/tun.h > @@ -44,6 +44,7 @@ > > #ifdef _WIN32 > #define WINTUN_COMPONENT_ID "wintun" > +#define DCO_WIN_REFERENCE_STRING "ovpn-dco" > > enum windows_driver_type { > WINDOWS_DRIVER_UNSPECIFIED, > @@ -293,7 +294,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, > @@ -646,6 +648,18 @@ write_tun_buffered(struct tuntap *tt, struct buffer *buf) > } > } > > +static inline bool > +tuntap_is_dco_win(struct tuntap *tt) > +{ > + return tt && tt->windows_driver == WINDOWS_DRIVER_DCO; > +} > + > +static inline bool > +tuntap_is_dco_win_timeout(struct tuntap *tt, int status) > +{ > + return tuntap_is_dco_win(tt) && (status < 0) && (openvpn_errno() == > ERROR_NETNAME_DELETED); > +} > + > #else /* ifdef _WIN32 */ > > static inline bool > @@ -671,6 +685,19 @@ tun_standby(struct tuntap *tt) > return true; > } > > + > +static inline bool > +tuntap_is_dco_win(struct tuntap *tt) > +{ > + return false; > +} > + > +static inline bool > +tuntap_is_dco_win_timeout(struct tuntap *tt, int status) > +{ > + return false; > +} > + > #endif /* ifdef _WIN32 */ > > /* > @@ -694,28 +721,37 @@ tun_set(struct tuntap *tt, > void *arg, > unsigned int *persistent) > { > - if (tuntap_defined(tt)) > + if (!tuntap_defined(tt) || tuntap_is_dco_win(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); > bool tun_name_is_fixed(const char *dev); > > +static inline bool > +is_tun_type_set(const struct tuntap *tt) > +{ > + return tt && tt->type != DEV_TYPE_UNDEF; > +} > + > #endif /* TUN_H */ > -- > 2.23.0.windows.1 > -- -Lev _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel