From 5c30df12ae98c0289cdfb2a24aefba2a9d2cc52e Mon Sep 17 00:00:00 2001 From: james Date: Thu, 19 Nov 2009 16:42:51 +0000 Subject: Fixed a client-side bug that occurred when the "dhcp-pre-release" or "dhcp-renew" options were combined with "route-gateway dhcp". The problem is that the IP Helper functions for DHCP release and renew are blocking, and so calling them from a single-threaded client stops tunnel traffic forwarding, and hence breaks "route-gateway dhcp" which requires an active tunnel. The fix is to call the IP Helper functions for DHCP release and renew from another process. Version 2.1_rc21b. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5164 e7ae566f-a301-0410-adde-c780ea21d3b5 --- forward.c | 3 +- options.c | 21 ++++++++++++++ tun.c | 96 +++++++++++++++++++++++++++++++++++++++++++------------------- tun.h | 4 +-- version.m4 | 2 +- win32.c | 45 +++++++++++++++++++++++++++++ win32.h | 3 ++ 7 files changed, 141 insertions(+), 33 deletions(-) diff --git a/forward.c b/forward.c index 661ac5c..207b876 100644 --- a/forward.c +++ b/forward.c @@ -1040,7 +1040,8 @@ process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf) if (flags & PIPV4_EXTRACT_DHCP_ROUTER) { const in_addr_t dhcp_router = dhcp_extract_router_msg (&ipbuf); - route_list_add_default_gateway (c->c1.route_list, c->c2.es, dhcp_router); + if (dhcp_router) + route_list_add_default_gateway (c->c1.route_list, c->c2.es, dhcp_router); } } } diff --git a/options.c b/options.c index 183c21a..ae4825c 100644 --- a/options.c +++ b/options.c @@ -2780,6 +2780,14 @@ positive_atoi (const char *str) return i < 0 ? 0 : i; } +static unsigned int +atou (const char *str) +{ + unsigned int val = 0; + sscanf (str, "%u", &val); + return val; +} + static inline bool space (unsigned char c) { @@ -5097,6 +5105,19 @@ add_option (struct options *options, VERIFY_PERMISSION (OPT_P_IPWIN32); options->tuntap_options.dhcp_release = true; } + else if (streq (p[0], "dhcp-rr") && p[1]) /* standalone method for internal use */ + { + unsigned int adapter_index; + VERIFY_PERMISSION (OPT_P_GENERAL); + set_debug_level (options->verbosity, SDL_CONSTRAIN); + adapter_index = atou (p[1]); + sleep (options->tuntap_options.tap_sleep); + if (options->tuntap_options.dhcp_pre_release) + dhcp_release_by_adapter_index (adapter_index); + if (options->tuntap_options.dhcp_renew) + dhcp_renew_by_adapter_index (adapter_index); + openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */ + } else if (streq (p[0], "show-valid-subnets")) { VERIFY_PERMISSION (OPT_P_GENERAL); diff --git a/tun.c b/tun.c index 29278b8..d82ac49 100644 --- a/tun.c +++ b/tun.c @@ -3292,59 +3292,73 @@ tap_allow_nonadmin_access (const char *dev_node) /* * DHCP release/renewal */ - bool -dhcp_release (const struct tuntap *tt) +dhcp_release_by_adapter_index(const DWORD adapter_index) { struct gc_arena gc = gc_new (); bool ret = false; - if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0) + const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (adapter_index, &gc); + + if (inter) { - const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (tt->adapter_index, &gc); - if (inter) + DWORD status = IpReleaseAddress ((IP_ADAPTER_INDEX_MAP *)inter); + if (status == NO_ERROR) { - DWORD status = IpReleaseAddress ((IP_ADAPTER_INDEX_MAP *)inter); - if (status == NO_ERROR) - { - msg (D_TUNTAP_INFO, "TAP: DHCP address released"); - ret = true; - } - else - msg (M_WARN, "NOTE: Release of DHCP-assigned IP address lease on TAP-Win32 adapter failed: %s (code=%u)", - strerror_win32 (status, &gc), - (unsigned int)status); + msg (D_TUNTAP_INFO, "TAP: DHCP address released"); + ret = true; } + else + msg (M_WARN, "NOTE: Release of DHCP-assigned IP address lease on TAP-Win32 adapter failed: %s (code=%u)", + strerror_win32 (status, &gc), + (unsigned int)status); } + gc_free (&gc); return ret; } +static bool +dhcp_release (const struct tuntap *tt) +{ + if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0) + return dhcp_release_by_adapter_index (tt->adapter_index); + else + return false; +} + bool -dhcp_renew (const struct tuntap *tt) +dhcp_renew_by_adapter_index (const DWORD adapter_index) { struct gc_arena gc = gc_new (); bool ret = false; - if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0) + const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (adapter_index, &gc); + + if (inter) { - const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (tt->adapter_index, &gc); - if (inter) + DWORD status = IpRenewAddress ((IP_ADAPTER_INDEX_MAP *)inter); + if (status == NO_ERROR) { - DWORD status = IpRenewAddress ((IP_ADAPTER_INDEX_MAP *)inter); - if (status == NO_ERROR) - { - msg (D_TUNTAP_INFO, "TAP: DHCP address renewal succeeded"); - ret = true; - } - else - msg (M_WARN, "WARNING: Failed to renew DHCP IP address lease on TAP-Win32 adapter: %s (code=%u)", - strerror_win32 (status, &gc), - (unsigned int)status); + msg (D_TUNTAP_INFO, "TAP: DHCP address renewal succeeded"); + ret = true; } + else + msg (M_WARN, "WARNING: Failed to renew DHCP IP address lease on TAP-Win32 adapter: %s (code=%u)", + strerror_win32 (status, &gc), + (unsigned int)status); } gc_free (&gc); return ret; } +static bool +dhcp_renew (const struct tuntap *tt) +{ + if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0) + return dhcp_renew_by_adapter_index (tt->adapter_index); + else + return false; +} + /* * netsh functions */ @@ -3788,6 +3802,28 @@ build_dhcp_options_string (struct buffer *buf, const struct tuntap_options *o) return !error; } +static void +fork_dhcp_action (struct tuntap *tt) +{ + if (tt->options.dhcp_pre_release || tt->options.dhcp_renew) + { + struct gc_arena gc = gc_new (); + struct buffer cmd = alloc_buf_gc (256, &gc); + const int verb = 3; + const int pre_sleep = 1; + + buf_printf (&cmd, "openvpn --verb %d --tap-sleep %d", verb, pre_sleep); + if (tt->options.dhcp_pre_release) + buf_printf (&cmd, " --dhcp-pre-release"); + if (tt->options.dhcp_renew) + buf_printf (&cmd, " --dhcp-renew"); + buf_printf (&cmd, " --dhcp-rr %u", (unsigned int)tt->adapter_index); + + fork_to_self (BSTR (&cmd)); + gc_free (&gc); + } +} + void open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt) { @@ -4152,6 +4188,8 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6 if (tt->options.dhcp_renew) dhcp_renew (tt); } + else + fork_dhcp_action (tt); if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI) { diff --git a/tun.h b/tun.h index 16598d0..210278a 100644 --- a/tun.h +++ b/tun.h @@ -332,8 +332,8 @@ void show_valid_win32_tun_subnets (void); const char *tap_win32_getinfo (const struct tuntap *tt, struct gc_arena *gc); void tun_show_debug (struct tuntap *tt); -bool dhcp_release (const struct tuntap *tt); -bool dhcp_renew (const struct tuntap *tt); +bool dhcp_release_by_adapter_index(const DWORD adapter_index); +bool dhcp_renew_by_adapter_index (const DWORD adapter_index); void tun_standby_init (struct tuntap *tt); bool tun_standby (struct tuntap *tt); diff --git a/version.m4 b/version.m4 index 6e4ab9f..63fc338 100644 --- a/version.m4 +++ b/version.m4 @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1_rc21a]) +define(PRODUCT_VERSION,[2.1_rc21b]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) diff --git a/win32.c b/win32.c index 26e81a8..eb94eb8 100644 --- a/win32.c +++ b/win32.c @@ -1016,6 +1016,51 @@ openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned i return ret; } +/* + * call ourself in another process + */ +void +fork_to_self (const char *cmdline) +{ + STARTUPINFO start_info; + PROCESS_INFORMATION proc_info; + char self_exe[256]; + char *cl = string_alloc (cmdline, NULL); + DWORD status; + + CLEAR (start_info); + CLEAR (proc_info); + CLEAR (self_exe); + + status = GetModuleFileName (NULL, self_exe, sizeof(self_exe)); + if (status == 0 || status == sizeof(self_exe)) + { + msg (M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: cannot get module name via GetModuleFileName"); + goto done; + } + + /* fill in STARTUPINFO struct */ + GetStartupInfo(&start_info); + start_info.cb = sizeof(start_info); + start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + start_info.wShowWindow = SW_HIDE; + start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE); + + if (CreateProcess (self_exe, cl, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info)) + { + CloseHandle (proc_info.hThread); + CloseHandle (proc_info.hProcess); + } + else + { + msg (M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: %s", cmdline); + } + + done: + free (cl); +} + char * get_win_sys_path (void) { diff --git a/win32.h b/win32.h index 8a79ac3..a972a88 100644 --- a/win32.h +++ b/win32.h @@ -265,5 +265,8 @@ void set_win_sys_path (const char *newpath, struct env_set *es); void set_win_sys_path_via_env (struct env_set *es); char *get_win_sys_path (void); +/* call self in a subprocess */ +void fork_to_self (const char *cmdline); + #endif #endif -- cgit