From: Lev Stipakov Date: Sat, 10 May 2025 18:19:30 +0000 (+0200) Subject: win: create adapter on demand X-Git-Tag: v2.7_alpha1~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1c330b6d53bd78e2fd1731e886863c6fe099192;p=thirdparty%2Fopenvpn.git win: create adapter on demand The installer currently creates one adapter per driver. When a user tries to start a second VPN connection while another is active, the client fails with an unclear error message: "All ovpn-dco adapters on this system are currently in use or disabled." This message does not guide the user toward resolving the issue, such as by running the shortcut "Add a new dco-win virtual network adapter." To improve user experience, the client will now create an adapter on demand when no available adapters exist. The client sends a command specifying the adapter type to the interactive service, which then executes tapctl.exe to create a new adapter. This feature requires the interactive service, but this should not pose a problem since even our automatic service has recently started relying on the interactive service. GitHub: OpenVPN/openvpn#728 Change-Id: I621d44ec6b0facc524875c15ddfd11ec47b06c15 Signed-off-by: Lev Stipakov Acked-by: Selva Nair Message-Id: <20250510181937.2993-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg31617.html Signed-off-by: Gert Doering --- diff --git a/include/openvpn-msg.h b/include/openvpn-msg.h index 8b4805370..2cf8d401b 100644 --- a/include/openvpn-msg.h +++ b/include/openvpn-msg.h @@ -47,7 +47,8 @@ typedef enum { msg_register_ring_buffers, msg_set_mtu, msg_add_wins_cfg, - msg_del_wins_cfg + msg_del_wins_cfg, + msg_create_adapter } message_type_t; typedef struct { @@ -172,4 +173,15 @@ typedef struct { int mtu; } set_mtu_message_t; +typedef enum { + ADAPTER_TYPE_DCO, + ADAPTER_TYPE_TAP, + ADAPTER_TYPE_WINTUN +} adapter_type_t; + +typedef struct { + message_header_t header; + adapter_type_t adapter_type; +} create_adapter_message_t; + #endif /* ifndef OPENVPN_MSG_H_ */ diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index a0c22b1cf..34a049e88 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -2231,6 +2231,7 @@ create_socket_dco_win(struct context *c, struct link_socket *sock, ALLOC_OBJ_CLEAR(tt, struct tuntap); tt->backend_driver = DRIVER_DCO; + tt->options.msg_channel = c->options.msg_channel; const char *device_guid = NULL; /* not used */ tun_open_device(tt, c->options.dev_node, &device_guid, &c->gc); diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 5186afa77..6a2043e43 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -451,6 +451,71 @@ do_dns_domain_wmic(bool add, const struct tuntap *tt) argv_free(&argv); } +/** + * Requests the interactive service to create a VPN adapter of the specified type. + * + * @param msg_channel Handle to the interactive service communication pipe. + * @param driver_type Adapter type to create (e.g., TAP, Wintun, DCO). + * + * @return true on success, false on failure. + */ +static bool +do_create_adapter_service(HANDLE msg_channel, enum tun_driver_type driver_type) +{ + bool ret = false; + ack_message_t ack; + struct gc_arena gc = gc_new(); + + adapter_type_t t; + switch (driver_type) + { + case WINDOWS_DRIVER_TAP_WINDOWS6: + t = ADAPTER_TYPE_TAP; + break; + + case WINDOWS_DRIVER_WINTUN: + t = ADAPTER_TYPE_WINTUN; + break; + + case DRIVER_DCO: + t = ADAPTER_TYPE_DCO; + break; + + default: + msg(M_NONFATAL, "Invalid backend driver %s", print_tun_backend_driver(driver_type)); + goto out; + } + + create_adapter_message_t msg = { + .header = { + msg_create_adapter, + sizeof(create_adapter_message_t), + 0 + }, + .adapter_type = t + }; + + if (!send_msg_iservice(msg_channel, &msg, sizeof(msg), &ack, "create_adapter")) + { + goto out; + } + + if (ack.error_number != NO_ERROR) + { + msg(M_NONFATAL, "TUN: creating %s adapter using service failed: %s [status=%u]", + print_tun_backend_driver(driver_type), strerror_win32(ack.error_number, &gc), ack.error_number); + } + else + { + msg(M_INFO, "%s adapter created using service", print_tun_backend_driver(driver_type)); + ret = true; + } + +out: + gc_free(&gc); + return ret; +} + #endif /* ifdef _WIN32 */ #ifdef TARGET_SOLARIS @@ -6585,9 +6650,8 @@ tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_gui const struct tap_reg *tap_reg = get_tap_reg(gc); const struct panel_reg *panel_reg = get_panel_reg(gc); const struct device_instance_id_interface *device_instance_id_interface = get_device_instance_id_interface(gc); - uint8_t actual_buffer[256]; - at_least_one_tap_win(tap_reg); + uint8_t actual_buffer[256]; /* * Lookup the device name in the registry, using the --dev-node high level name. @@ -6618,6 +6682,7 @@ tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_gui else { int device_number = 0; + int adapters_created = 0; /* Try opening all TAP devices until we find one available */ while (true) @@ -6633,7 +6698,22 @@ tun_open_device(struct tuntap *tt, const char *dev_node, const char **device_gui if (!*device_guid) { - msg(M_FATAL, "All %s adapters on this system are currently in use or disabled.", print_tun_backend_driver(tt->backend_driver)); + /* try to create an adapter a few times if we have a service pipe handle */ + if ((++adapters_created > 10) || !do_create_adapter_service(tt->options.msg_channel, tt->backend_driver)) + { + msg(M_FATAL, "All %s adapters on this system are currently in use or disabled.", print_tun_backend_driver(tt->backend_driver)); + } + else + { + /* we have created a new adapter so we must reinitialize adapters structs */ + tap_reg = get_tap_reg(gc); + panel_reg = get_panel_reg(gc); + device_instance_id_interface = get_device_instance_id_interface(gc); + + device_number = 0; + + continue; + } } if (tt->backend_driver != windows_driver) diff --git a/src/openvpnserv/common.c b/src/openvpnserv/common.c index 4a11e6cd3..198835e7e 100644 --- a/src/openvpnserv/common.c +++ b/src/openvpnserv/common.c @@ -96,6 +96,14 @@ GetOpenvpnSettings(settings_t *s) goto out; } + swprintf(default_value, _countof(default_value), L"%ls\\bin", install_path); + error = GetRegString(key, L"bin_dir", s->bin_dir, sizeof(s->bin_dir), + default_value); + if (error != ERROR_SUCCESS) + { + goto out; + } + error = GetRegString(key, L"config_ext", s->ext_string, sizeof(s->ext_string), L".ovpn"); if (error != ERROR_SUCCESS) diff --git a/src/openvpnserv/interactive.c b/src/openvpnserv/interactive.c index f06d38655..8a7b50d50 100644 --- a/src/openvpnserv/interactive.c +++ b/src/openvpnserv/interactive.c @@ -125,6 +125,7 @@ typedef union { register_ring_buffers_message_t rrb; set_mtu_message_t mtu; wins_cfg_message_t wins; + create_adapter_message_t create_adapter; } pipe_message_t; typedef struct { @@ -3107,6 +3108,52 @@ HandleMTUMessage(const set_mtu_message_t *mtu) return err; } +/** + * Creates a VPN adapter of the specified type by invoking tapctl.exe. + * + * @param msg Adapter creation request specifying the type. + * + * @return NO_ERROR on success, otherwise a Windows error code. + */ +static DWORD +HandleCreateAdapterMessage(const create_adapter_message_t *msg) +{ + const WCHAR *hwid; + + switch (msg->adapter_type) + { + case ADAPTER_TYPE_DCO: + hwid = L"ovpn-dco"; + break; + + case ADAPTER_TYPE_TAP: + hwid = L"root\\tap0901"; + break; + + case ADAPTER_TYPE_WINTUN: + hwid = L"wintun"; + break; + + default: + return ERROR_INVALID_PARAMETER; + } + + WCHAR cmd[MAX_PATH]; + WCHAR args[MAX_PATH]; + + if (swprintf_s(cmd, _countof(cmd), L"%s\\tapctl.exe", settings.bin_dir) < 0) + { + return ERROR_BUFFER_OVERFLOW; + } + + if (swprintf_s(args, _countof(args), L"tapctl create --hwid %s", hwid) < 0) + { + return ERROR_BUFFER_OVERFLOW; + } + + return ExecCommand(cmd, args, 10000); +} + static VOID HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists) @@ -3206,6 +3253,13 @@ HandleMessage(HANDLE pipe, PPROCESS_INFORMATION proc_info, } break; + case msg_create_adapter: + if (msg.header.size == sizeof(msg.create_adapter)) + { + ack.error_number = HandleCreateAdapterMessage(&msg.create_adapter); + } + break; + default: ack.error_number = ERROR_MESSAGE_TYPE; MsgToEventLog(MSG_FLAGS_ERROR, L"Unknown message type %d", msg.header.type); diff --git a/src/openvpnserv/service.h b/src/openvpnserv/service.h index cbe213b1e..cebc67f05 100644 --- a/src/openvpnserv/service.h +++ b/src/openvpnserv/service.h @@ -63,6 +63,7 @@ typedef struct { typedef struct { WCHAR exe_path[MAX_PATH]; WCHAR config_dir[MAX_PATH]; + WCHAR bin_dir[MAX_PATH]; WCHAR ext_string[16]; WCHAR log_dir[MAX_PATH]; WCHAR ovpn_admin_group[MAX_NAME];