diff options
author | Bernhard Schmidt <berni@debian.org> | 2020-08-15 21:29:50 +0200 |
---|---|---|
committer | Bernhard Schmidt <berni@debian.org> | 2020-08-15 21:29:50 +0200 |
commit | 1079962e4c06f88a54e50d997c1b7e84303d30b4 (patch) | |
tree | 4d019426928435425214ccedd6f89b70dbdf035d /src/openvpn/tun.c | |
parent | 620785fe268a1221c1ba7a9cb5a70f3140a4f1ca (diff) |
New upstream version 2.5~beta1upstream/2.5_beta1
Diffstat (limited to 'src/openvpn/tun.c')
-rw-r--r-- | src/openvpn/tun.c | 3282 |
1 files changed, 1857 insertions, 1425 deletions
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 80eaa2c..3045445 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -40,12 +40,13 @@ #include "tun.h" #include "fdmisc.h" #include "common.h" -#include "misc.h" +#include "run_command.h" #include "socket.h" #include "manage.h" #include "route.h" #include "win32.h" #include "block_dns.h" +#include "networking.h" #include "memdbg.h" @@ -57,6 +58,9 @@ #ifdef _WIN32 +const static GUID GUID_DEVCLASS_NET = { 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } }; +const static GUID GUID_DEVINTERFACE_NET = { 0xcac88484, 0x7515, 0x4c03, { 0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61 } }; + /* #define SIMULATE_DHCP_FAILED */ /* simulate bad DHCP negotiation */ #define NI_TEST_FIRST (1<<0) @@ -69,6 +73,10 @@ static void netsh_ifconfig(const struct tuntap_options *to, const in_addr_t netmask, const unsigned int flags); +static void windows_set_mtu(const int iface_index, + const short family, + const int mtu); + static void netsh_set_dns6_servers(const struct in6_addr *addr_list, const int addr_len, const char *flex_name); @@ -82,7 +90,6 @@ static DWORD get_adapter_index_flexible(const char *name); static bool do_address_service(const bool add, const short family, const struct tuntap *tt) { - DWORD len; bool ret = false; ack_message_t ack; struct gc_arena gc = gc_new(); @@ -106,8 +113,8 @@ do_address_service(const bool add, const short family, const struct tuntap *tt) if (addr.family == AF_INET) { - addr.address.ipv4.s_addr = tt->local; - addr.prefix_len = 32; + addr.address.ipv4.s_addr = htonl(tt->local); + addr.prefix_len = netmask_to_netbits2(tt->adapter_netmask); } else { @@ -115,11 +122,8 @@ do_address_service(const bool add, const short family, const struct tuntap *tt) addr.prefix_len = tt->netbits_ipv6; } - if (!WriteFile(pipe, &addr, sizeof(addr), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &addr, sizeof(addr), &ack, "TUN")) { - msg(M_WARN, "TUN: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -139,14 +143,15 @@ out: } static bool -do_dns6_service(bool add, const struct tuntap *tt) +do_dns_service(bool add, const short family, const struct tuntap *tt) { - DWORD len; bool ret = false; ack_message_t ack; struct gc_arena gc = gc_new(); HANDLE pipe = tt->options.msg_channel; - int addr_len = add ? tt->options.dns6_len : 0; + int len = family == AF_INET6 ? tt->options.dns6_len : tt->options.dns_len; + int addr_len = add ? len : 0; + const char *ip_proto_name = family == AF_INET6 ? "IPv6" : "IPv4"; if (addr_len == 0 && add) /* no addresses to add */ { @@ -161,7 +166,7 @@ do_dns6_service(bool add, const struct tuntap *tt) }, .iface = { .index = tt->adapter_index, .name = "" }, .domains = "", - .family = AF_INET6, + .family = family, .addr_len = addr_len }; @@ -173,35 +178,39 @@ do_dns6_service(bool add, const struct tuntap *tt) { addr_len = _countof(dns.addr); dns.addr_len = addr_len; - msg(M_WARN, "Number of IPv6 DNS addresses sent to service truncated to %d", - addr_len); + msg(M_WARN, "Number of %s DNS addresses sent to service truncated to %d", + ip_proto_name, addr_len); } for (int i = 0; i < addr_len; ++i) { - dns.addr[i].ipv6 = tt->options.dns6[i]; + if (family == AF_INET6) + { + dns.addr[i].ipv6 = tt->options.dns6[i]; + } + else + { + dns.addr[i].ipv4.s_addr = htonl(tt->options.dns[i]); + } } - msg(D_LOW, "%s IPv6 dns servers on '%s' (if_index = %d) using service", - (add ? "Setting" : "Deleting"), dns.iface.name, dns.iface.index); + msg(D_LOW, "%s %s dns servers on '%s' (if_index = %d) using service", + (add ? "Setting" : "Deleting"), ip_proto_name, dns.iface.name, dns.iface.index); - if (!WriteFile(pipe, &dns, sizeof(dns), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &dns, sizeof(dns), &ack, "TUN")) { - msg(M_WARN, "TUN: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } if (ack.error_number != NO_ERROR) { - msg(M_WARN, "TUN: %s IPv6 dns failed using service: %s [status=%u if_name=%s]", - (add ? "adding" : "deleting"), strerror_win32(ack.error_number, &gc), + msg(M_WARN, "TUN: %s %s dns failed using service: %s [status=%u if_name=%s]", + (add ? "adding" : "deleting"), ip_proto_name, strerror_win32(ack.error_number, &gc), ack.error_number, dns.iface.name); goto out; } - msg(M_INFO, "IPv6 dns servers %s using service", (add ? "set" : "deleted")); + msg(M_INFO, "%s dns servers %s using service", ip_proto_name, (add ? "set" : "deleted")); ret = true; out: @@ -209,6 +218,52 @@ out: return ret; } +static bool +do_set_mtu_service(const struct tuntap *tt, const short family, const int mtu) +{ + DWORD len; + bool ret = false; + ack_message_t ack; + struct gc_arena gc = gc_new(); + HANDLE pipe = tt->options.msg_channel; + const char *family_name = (family == AF_INET6) ? "IPv6" : "IPv4"; + set_mtu_message_t mtu_msg = { + .header = { + msg_set_mtu, + sizeof(set_mtu_message_t), + 0 + }, + .iface = {.index = tt->adapter_index}, + .mtu = mtu, + .family = family + }; + strncpynt(mtu_msg.iface.name, tt->actual_name, sizeof(mtu_msg.iface.name)); + if (family == AF_INET6 && mtu < 1280) + { + msg(M_INFO, "NOTE: IPv6 interface MTU < 1280 conflicts with IETF standards and might not work"); + } + + if (!send_msg_iservice(pipe, &mtu_msg, sizeof(mtu_msg), &ack, "Set_mtu")) + { + goto out; + } + + if (ack.error_number != NO_ERROR) + { + msg(M_NONFATAL, "TUN: setting %s mtu using service failed: %s [status=%u if_index=%d]", + family_name, strerror_win32(ack.error_number, &gc), ack.error_number, mtu_msg.iface.index); + } + else + { + msg(M_INFO, "%s MTU set to %d on interface %d using service", family_name, mtu, mtu_msg.iface.index); + ret = true; + } + +out: + gc_free(&gc); + return ret; +} + #endif /* ifdef _WIN32 */ #ifdef TARGET_SOLARIS @@ -342,16 +397,6 @@ ifconfig_sanity_check(bool tun, in_addr_t addr, int topology) } /* - * For TAP-style devices, generate a broadcast address. - */ -static in_addr_t -generate_ifconfig_broadcast_addr(in_addr_t local, - in_addr_t netmask) -{ - return local | ~netmask; -} - -/* * Check that --local and --remote addresses do not * clash with ifconfig addresses or subnet. */ @@ -460,13 +505,13 @@ check_subnet_conflict(const in_addr_t ip, } void -warn_on_use_of_common_subnets(void) +warn_on_use_of_common_subnets(openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); struct route_gateway_info rgi; const int needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED); - get_default_gateway(&rgi); + get_default_gateway(&rgi, ctx); if ((rgi.flags & needed) == needed) { const in_addr_t lan_network = rgi.gateway.addr & rgi.gateway.netmask; @@ -561,8 +606,8 @@ is_tun_p2p(const struct tuntap *tt) bool tun = false; if (tt->type == DEV_TYPE_TAP - || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - || tt->type == DEV_TYPE_NULL ) + || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) + || tt->type == DEV_TYPE_NULL) { tun = false; } @@ -602,9 +647,7 @@ do_ifconfig_setenv(const struct tuntap *tt, struct env_set *es) } else { - const char *ifconfig_broadcast = print_in_addr_t(tt->broadcast, 0, &gc); setenv_str(es, "ifconfig_netmask", ifconfig_remote_netmask); - setenv_str(es, "ifconfig_broadcast", ifconfig_broadcast); } } @@ -639,7 +682,8 @@ init_tun(const char *dev, /* --dev option */ struct addrinfo *local_public, struct addrinfo *remote_public, const bool strict_warn, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); struct tuntap *tt; @@ -730,14 +774,6 @@ init_tun(const char *dev, /* --dev option */ } } - /* - * If TAP-style interface, generate broadcast address. - */ - if (!tun) - { - tt->broadcast = generate_ifconfig_broadcast_addr(tt->local, tt->remote_netmask); - } - #ifdef _WIN32 /* * Make sure that both ifconfig addresses are part of the @@ -798,10 +834,40 @@ init_tun_post(struct tuntap *tt, #ifdef _WIN32 overlapped_io_init(&tt->reads, frame, FALSE, true); overlapped_io_init(&tt->writes, frame, TRUE, true); - tt->rw_handle.read = tt->reads.overlapped.hEvent; - tt->rw_handle.write = tt->writes.overlapped.hEvent; tt->adapter_index = TUN_ADAPTER_INDEX_INVALID; -#endif + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + tt->wintun_send_ring_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, + 0, + sizeof(struct tun_ring), + NULL); + tt->wintun_receive_ring_handle = CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + sizeof(struct tun_ring), + NULL); + if ((tt->wintun_send_ring_handle == NULL) || (tt->wintun_receive_ring_handle == NULL)) + { + msg(M_FATAL, "Cannot allocate memory for ring buffer"); + } + + tt->rw_handle.read = CreateEvent(NULL, FALSE, FALSE, NULL); + tt->rw_handle.write = CreateEvent(NULL, FALSE, FALSE, NULL); + + if ((tt->rw_handle.read == NULL) || (tt->rw_handle.write == NULL)) + { + msg(M_FATAL, "Cannot create events for ring buffer"); + } + } + else + { + tt->rw_handle.read = tt->reads.overlapped.hEvent; + tt->rw_handle.write = tt->writes.overlapped.hEvent; + } +#endif /* ifdef _WIN32 */ } #if defined(_WIN32) \ @@ -812,7 +878,7 @@ init_tun_post(struct tuntap *tt, * an extra call to "route add..." * -> helper function to simplify code below */ -void +static void add_route_connected_v6_net(struct tuntap *tt, const struct env_set *es) { @@ -824,12 +890,11 @@ add_route_connected_v6_net(struct tuntap *tt, r6.gateway = tt->local_ipv6; r6.metric = 0; /* connected route */ r6.flags = RT_DEFINED | RT_METRIC_DEFINED; - add_route_ipv6(&r6, tt, 0, es); + add_route_ipv6(&r6, tt, 0, es, NULL); } void -delete_route_connected_v6_net(struct tuntap *tt, - const struct env_set *es) +delete_route_connected_v6_net(const struct tuntap *tt) { struct route_ipv6 r6; @@ -840,7 +905,7 @@ delete_route_connected_v6_net(struct tuntap *tt, r6.metric = 0; /* connected route */ r6.flags = RT_DEFINED | RT_ADDED | RT_METRIC_DEFINED; route_ipv6_clear_host_bits(&r6); - delete_route_ipv6(&r6, tt, 0, es); + delete_route_ipv6(&r6, tt, 0, NULL, NULL); } #endif /* if defined(_WIN32) || defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD) */ @@ -871,739 +936,576 @@ create_arbitrary_remote( struct tuntap *tt ) } #endif -/* execute the ifconfig command through the shell */ -void -do_ifconfig(struct tuntap *tt, - const char *actual, /* actual device name */ - int tun_mtu, - const struct env_set *es) +/** + * do_ifconfig_ipv6 - perform platform specific ifconfig6 commands + * + * @param tt the tuntap interface context + * @param ifname the human readable interface name + * @param mtu the MTU value to set the interface to + * @param es the environment to be used when executing the commands + * @param ctx the networking API opaque context + */ +static void +do_ifconfig_ipv6(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx) { +#if !defined(TARGET_LINUX) + struct argv argv = argv_new(); struct gc_arena gc = gc_new(); + const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); +#endif - if (tt->did_ifconfig_setup) +#if defined(TARGET_LINUX) + if (net_iface_mtu_set(ctx, ifname, tun_mtu) < 0) { - bool tun = false; - const char *ifconfig_local = NULL; - const char *ifconfig_remote_netmask = NULL; - const char *ifconfig_broadcast = NULL; - const char *ifconfig_ipv6_local = NULL; - bool do_ipv6 = false; - struct argv argv = argv_new(); + msg(M_FATAL, "Linux can't set mtu (%d) on %s", tun_mtu, ifname); + } - msg( D_LOW, "do_ifconfig, tt->did_ifconfig_ipv6_setup=%d", - tt->did_ifconfig_ipv6_setup ); + if (net_iface_up(ctx, ifname, true) < 0) + { + msg(M_FATAL, "Linux can't bring %s up", ifname); + } - /* - * We only handle TUN/TAP devices here, not --dev null devices. - */ - tun = is_tun_p2p(tt); + if (net_addr_v6_add(ctx, ifname, &tt->local_ipv6, + tt->netbits_ipv6) < 0) + { + msg(M_FATAL, "Linux can't add IPv6 to interface %s", ifname); + } +#elif defined(TARGET_ANDROID) + char out6[64]; - /* - * Set ifconfig parameters - */ - ifconfig_local = print_in_addr_t(tt->local, 0, &gc); - ifconfig_remote_netmask = print_in_addr_t(tt->remote_netmask, 0, &gc); + openvpn_snprintf(out6, sizeof(out6), "%s/%d %d", + ifconfig_ipv6_local, tt->netbits_ipv6, tun_mtu); + management_android_control(management, "IFCONFIG6", out6); +#elif defined(TARGET_SOLARIS) + argv_printf(&argv, "%s %s inet6 unplumb", IFCONFIG_PATH, ifname); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, 0, NULL); - if (tt->did_ifconfig_ipv6_setup) - { - ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); - do_ipv6 = true; - } + if (tt->type == DEV_TYPE_TUN) + { + const char *ifconfig_ipv6_remote = print_in6_addr(tt->remote_ipv6, 0, &gc); - /* - * If TAP-style device, generate broadcast address. - */ - if (!tun) - { - ifconfig_broadcast = print_in_addr_t(tt->broadcast, 0, &gc); - } + argv_printf(&argv, "%s %s inet6 plumb %s/%d %s mtu %d up", + IFCONFIG_PATH, ifname, ifconfig_ipv6_local, + tt->netbits_ipv6, ifconfig_ipv6_remote, tun_mtu); + } + else /* tap mode */ + { + /* base IPv6 tap interface needs to be brought up first */ + argv_printf(&argv, "%s %s inet6 plumb up", IFCONFIG_PATH, ifname); + argv_msg(M_INFO, &argv); -#ifdef ENABLE_MANAGEMENT - if (management) + if (!openvpn_execve_check(&argv, es, 0, + "Solaris ifconfig IPv6 (prepare) failed")) { - management_set_state(management, - OPENVPN_STATE_ASSIGN_IP, - NULL, - &tt->local, - &tt->local_ipv6, - NULL, - NULL); + solaris_error_close(tt, es, ifname, true); } -#endif + /* we might need to do "ifconfig %s inet6 auto-dhcp drop" + * after the system has noticed the interface and fired up + * the DHCPv6 client - but this takes quite a while, and the + * server will ignore the DHCPv6 packets anyway. So we don't. + */ -#if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - /* - * Set the MTU for the device + /* static IPv6 addresses need to go to a subinterface (tap0:1) + * and we cannot set an mtu here (must go to the "parent") */ - argv_printf(&argv, - "%s link set dev %s up mtu %d", - iproute_path, - actual, - tun_mtu - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip link set failed"); + argv_printf(&argv, "%s %s inet6 addif %s/%d up", IFCONFIG_PATH, + ifname, ifconfig_ipv6_local, tt->netbits_ipv6 ); + } + argv_msg(M_INFO, &argv); - if (tun) - { + if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 failed")) + { + solaris_error_close(tt, es, ifname, true); + } - /* - * Set the address for the device - */ - argv_printf(&argv, - "%s addr add dev %s local %s peer %s", - iproute_path, - actual, - ifconfig_local, - ifconfig_remote_netmask - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip addr add failed"); - } - else - { - argv_printf(&argv, - "%s addr add dev %s %s/%d broadcast %s", - iproute_path, - actual, - ifconfig_local, - netmask_to_netbits2(tt->remote_netmask), - ifconfig_broadcast - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip addr add failed"); - } - if (do_ipv6) - { - argv_printf( &argv, - "%s -6 addr add %s/%d dev %s", - iproute_path, - ifconfig_ipv6_local, - tt->netbits_ipv6, - actual - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ip -6 addr add failed"); - } - tt->did_ifconfig = true; -#else /* ifdef ENABLE_IPROUTE */ - if (tun) - { - argv_printf(&argv, - "%s %s %s pointopoint %s mtu %d", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d broadcast %s", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu, - ifconfig_broadcast - ); - } + if (tt->type != DEV_TYPE_TUN) + { + argv_printf(&argv, "%s %s inet6 mtu %d", IFCONFIG_PATH, + ifname, tun_mtu); argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ifconfig failed"); - if (do_ipv6) - { - argv_printf(&argv, - "%s %s add %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Linux ifconfig inet6 failed"); - } - tt->did_ifconfig = true; - -#endif /*ENABLE_IPROUTE*/ -#elif defined(TARGET_ANDROID) + openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 mtu failed"); + } +#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) \ + || defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) \ + || defined(TARGET_DRAGONFLY) + argv_printf(&argv, "%s %s inet6 %s/%d mtu %d up", IFCONFIG_PATH, ifname, + ifconfig_ipv6_local, tt->netbits_ipv6, tun_mtu); + argv_msg(M_INFO, &argv); - if (do_ipv6) - { - struct buffer out6 = alloc_buf_gc(64, &gc); - buf_printf(&out6, "%s/%d", ifconfig_ipv6_local,tt->netbits_ipv6); - management_android_control(management, "IFCONFIG6",buf_bptr(&out6)); - } + openvpn_execve_check(&argv, es, S_FATAL, + "generic BSD ifconfig inet6 failed"); - struct buffer out = alloc_buf_gc(64, &gc); +#if defined(TARGET_OPENBSD) || defined(TARGET_NETBSD) \ + || defined(TARGET_DARWIN) + /* and, hooray, we explicitly need to add a route... */ + add_route_connected_v6_net(tt, es); +#endif +#elif defined(TARGET_AIX) + argv_printf(&argv, "%s %s inet6 %s/%d mtu %d up", IFCONFIG_PATH, ifname, + ifconfig_ipv6_local, tt->netbits_ipv6, tun_mtu); + argv_msg(M_INFO, &argv); - char *top; - switch (tt->topology) - { - case TOP_NET30: - top = "net30"; - break; + /* AIX ifconfig will complain if it can't find ODM path in env */ + es = env_set_create(NULL); + env_set_add(es, "ODMDIR=/etc/objrepos"); - case TOP_P2P: - top = "p2p"; - break; + openvpn_execve_check(&argv, es, S_FATAL, + "generic BSD ifconfig inet6 failed"); - case TOP_SUBNET: - top = "subnet"; - break; + env_set_destroy(es); +#elif defined (_WIN32) + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) + { + msg(M_INFO, "******** NOTE: Please manually set the v6 IP of '%s' to %s (if it is not already set)", + ifname, ifconfig_ipv6_local); + } + else if (tt->options.msg_channel) + { + do_address_service(true, AF_INET6, tt); + add_route_connected_v6_net(tt, es); + do_dns_service(true, AF_INET6, tt); + do_set_mtu_service(tt, AF_INET6, tun_mtu); + } + else + { + /* example: netsh interface ipv6 set address interface=42 + * 2001:608:8003::d store=active + */ + char iface[64]; - default: - top = "undef"; - } + openvpn_snprintf(iface, sizeof(iface), "interface=%lu", + tt->adapter_index); + argv_printf(&argv, "%s%s interface ipv6 set address %s %s store=active", + get_win_sys_path(), NETSH_PATH_SUFFIX, iface, + ifconfig_ipv6_local); + netsh_command(&argv, 4, M_FATAL); + add_route_connected_v6_net(tt, es); + /* set ipv6 dns servers if any are specified */ + netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, ifname); + windows_set_mtu(tt->adapter_index, AF_INET6, tun_mtu); + } +#else /* platforms we have no IPv6 code for */ + msg(M_FATAL, "Sorry, but I don't know how to do IPv6 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script."); +#endif /* outer "if defined(TARGET_xxx)" conditional */ - buf_printf(&out, "%s %s %d %s", ifconfig_local, ifconfig_remote_netmask, tun_mtu, top); - management_android_control(management, "IFCONFIG", buf_bptr(&out)); +#if !defined(TARGET_LINUX) + gc_free(&gc); + argv_free(&argv); +#endif +} -#elif defined(TARGET_SOLARIS) - /* Solaris 2.6 (and 7?) cannot set all parameters in one go... - * example: - * ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 up - * ifconfig tun2 netmask 255.255.255.255 - */ - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-1 failed")) - { - solaris_error_close(tt, es, actual, false); - } +/** + * do_ifconfig_ipv4 - perform platform specific ifconfig commands + * + * @param tt the tuntap interface context + * @param ifname the human readable interface name + * @param mtu the MTU value to set the interface to + * @param es the environment to be used when executing the commands + * @param ctx the networking API opaque context + */ +static void +do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx) +{ + /* + * We only handle TUN/TAP devices here, not --dev null devices. + */ + bool tun = is_tun_p2p(tt); - argv_printf(&argv, - "%s %s netmask 255.255.255.255", - IFCONFIG_PATH, - actual - ); - } - else if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - argv_printf(&argv, - "%s %s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else - { - argv_printf(&argv, - " %s %s %s netmask %s broadcast + up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask - ); - } +#if !defined(TARGET_LINUX) + const char *ifconfig_local = NULL; + const char *ifconfig_remote_netmask = NULL; + struct argv argv = argv_new(); + struct gc_arena gc = gc_new(); - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-2 failed")) - { - solaris_error_close(tt, es, actual, false); - } + /* + * Set ifconfig parameters + */ + ifconfig_local = print_in_addr_t(tt->local, 0, &gc); + ifconfig_remote_netmask = print_in_addr_t(tt->remote_netmask, 0, &gc); +#endif - if (do_ipv6) - { - argv_printf(&argv, "%s %s inet6 unplumb", - IFCONFIG_PATH, actual ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, 0, NULL); +#if defined(TARGET_LINUX) + if (net_iface_mtu_set(ctx, ifname, tun_mtu) < 0) + { + msg(M_FATAL, "Linux can't set mtu (%d) on %s", tun_mtu, ifname); + } - if (tt->type == DEV_TYPE_TUN) - { - const char *ifconfig_ipv6_remote = - print_in6_addr(tt->remote_ipv6, 0, &gc); - - argv_printf(&argv, - "%s %s inet6 plumb %s/%d %s up", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6, - ifconfig_ipv6_remote - ); - } - else /* tap mode */ - { - /* base IPv6 tap interface needs to be brought up first - */ - argv_printf(&argv, "%s %s inet6 plumb up", - IFCONFIG_PATH, actual ); - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 (prepare) failed")) - { - solaris_error_close(tt, es, actual, true); - } + if (net_iface_up(ctx, ifname, true) < 0) + { + msg(M_FATAL, "Linux can't bring %s up", ifname); + } - /* we might need to do "ifconfig %s inet6 auto-dhcp drop" - * after the system has noticed the interface and fired up - * the DHCPv6 client - but this takes quite a while, and the - * server will ignore the DHCPv6 packets anyway. So we don't. - */ - - /* static IPv6 addresses need to go to a subinterface (tap0:1) - */ - argv_printf(&argv, - "%s %s inet6 addif %s/%d up", - IFCONFIG_PATH, actual, - ifconfig_ipv6_local, tt->netbits_ipv6 ); - } - argv_msg(M_INFO, &argv); - if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig IPv6 failed")) - { - solaris_error_close(tt, es, actual, true); - } + if (tun) + { + if (net_addr_ptp_v4_add(ctx, ifname, &tt->local, + &tt->remote_netmask) < 0) + { + msg(M_FATAL, "Linux can't add IP to interface %s", ifname); } - - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) + } + else + { + if (net_addr_v4_add(ctx, ifname, &tt->local, + netmask_to_netbits2(tt->remote_netmask)) < 0) { - /* Add a network route for the local tun interface */ - struct route_ipv4 r; - CLEAR(r); - r.flags = RT_DEFINED | RT_METRIC_DEFINED; - r.network = tt->local & tt->remote_netmask; - r.netmask = tt->remote_netmask; - r.gateway = tt->local; - r.metric = 0; - add_route(&r, tt, 0, NULL, es); + msg(M_FATAL, "Linux can't add IP to interface %s", ifname); } + } +#elif defined(TARGET_ANDROID) + char out[64]; - tt->did_ifconfig = true; + char *top; + switch (tt->topology) + { + case TOP_NET30: + top = "net30"; + break; -#elif defined(TARGET_OPENBSD) + case TOP_P2P: + top = "p2p"; + break; - in_addr_t remote_end; /* for "virtual" subnet topology */ + case TOP_SUBNET: + top = "subnet"; + break; - /* - * On OpenBSD, tun interfaces are persistent if created with - * "ifconfig tunX create", and auto-destroyed if created by - * opening "/dev/tunX" (so we just use the /dev/tunX) - */ + default: + top = "undef"; + } - /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up -link0", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - remote_end = create_arbitrary_remote( tt ); - argv_printf(&argv, - "%s %s %s %s mtu %d netmask %s up -link0", - IFCONFIG_PATH, - actual, - ifconfig_local, - print_in_addr_t(remote_end, 0, &gc), - tun_mtu, - ifconfig_remote_netmask - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d broadcast %s link0", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu, - ifconfig_broadcast - ); - } - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "OpenBSD ifconfig failed"); + openvpn_snprintf(out, sizeof(out), "%s %s %d %s", ifconfig_local, + ifconfig_remote_netmask, tun_mtu, top); + management_android_control(management, "IFCONFIG", out); - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) +#elif defined(TARGET_SOLARIS) + /* Solaris 2.6 (and 7?) cannot set all parameters in one go... + * example: + * ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 up + * ifconfig tun2 netmask 255.255.255.255 + */ + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d up", IFCONFIG_PATH, ifname, + ifconfig_local, ifconfig_remote_netmask, tun_mtu); + + argv_msg(M_INFO, &argv); + if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-1 failed")) { - struct route_ipv4 r; - CLEAR(r); - r.flags = RT_DEFINED; - r.network = tt->local & tt->remote_netmask; - r.netmask = tt->remote_netmask; - r.gateway = remote_end; - add_route(&r, tt, 0, NULL, es); + solaris_error_close(tt, es, ifname, false); } - if (do_ipv6) - { - argv_printf(&argv, - "%s %s inet6 %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "OpenBSD ifconfig inet6 failed"); + argv_printf(&argv, "%s %s netmask 255.255.255.255", IFCONFIG_PATH, + ifname); + } + else if (tt->topology == TOP_SUBNET) + { + argv_printf(&argv, "%s %s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else + { + argv_printf(&argv, "%s %s %s netmask %s up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask); + } - /* and, hooray, we explicitely need to add a route... */ - add_route_connected_v6_net(tt, es); - } - tt->did_ifconfig = true; + argv_msg(M_INFO, &argv); + if (!openvpn_execve_check(&argv, es, 0, "Solaris ifconfig phase-2 failed")) + { + solaris_error_close(tt, es, ifname, false); + } -#elif defined(TARGET_NETBSD) + if (!tun && tt->topology == TOP_SUBNET) + { + /* Add a network route for the local tun interface */ + struct route_ipv4 r; + CLEAR(r); + r.flags = RT_DEFINED | RT_METRIC_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = tt->local; + r.metric = 0; + add_route(&r, tt, 0, NULL, es, NULL); + } - in_addr_t remote_end; /* for "virtual" subnet topology */ +#elif defined(TARGET_OPENBSD) - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - remote_end = create_arbitrary_remote( tt ); - argv_printf(&argv, - "%s %s %s %s mtu %d netmask %s up", - IFCONFIG_PATH, - actual, - ifconfig_local, - print_in_addr_t(remote_end, 0, &gc), - tun_mtu, - ifconfig_remote_netmask - ); - } - else - { - /* - * NetBSD has distinct tun and tap devices - * so we don't need the "link0" extra parameter to specify we want to do - * tunneling at the ethernet level - */ - argv_printf(&argv, - "%s %s %s netmask %s mtu %d broadcast %s", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu, - ifconfig_broadcast - ); - } - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "NetBSD ifconfig failed"); + in_addr_t remote_end; /* for "virtual" subnet topology */ - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - struct route_ipv4 r; - CLEAR(r); - r.flags = RT_DEFINED; - r.network = tt->local & tt->remote_netmask; - r.netmask = tt->remote_netmask; - r.gateway = remote_end; - add_route(&r, tt, 0, NULL, es); - } + /* + * On OpenBSD, tun interfaces are persistent if created with + * "ifconfig tunX create", and auto-destroyed if created by + * opening "/dev/tunX" (so we just use the /dev/tunX) + */ - if (do_ipv6) - { - argv_printf(&argv, - "%s %s inet6 %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "NetBSD ifconfig inet6 failed"); + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ + if (tun) + { + argv_printf(&argv, + "%s %s %s %s mtu %d netmask 255.255.255.255 up -link0", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else if (tt->topology == TOP_SUBNET) + { + remote_end = create_arbitrary_remote( tt ); + argv_printf(&argv, "%s %s %s %s mtu %d netmask %s up -link0", + IFCONFIG_PATH, ifname, ifconfig_local, + print_in_addr_t(remote_end, 0, &gc), tun_mtu, + ifconfig_remote_netmask); + } + else + { + argv_printf(&argv, "%s %s %s netmask %s mtu %d link0", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "OpenBSD ifconfig failed"); - /* and, hooray, we explicitely need to add a route... */ - add_route_connected_v6_net(tt, es); - } - tt->did_ifconfig = true; + /* Add a network route for the local tun interface */ + if (!tun && tt->topology == TOP_SUBNET) + { + struct route_ipv4 r; + CLEAR(r); + r.flags = RT_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = remote_end; + add_route(&r, tt, 0, NULL, es, NULL); + } -#elif defined(TARGET_DARWIN) +#elif defined(TARGET_NETBSD) + in_addr_t remote_end; /* for "virtual" subnet topology */ + + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else if (tt->topology == TOP_SUBNET) + { + remote_end = create_arbitrary_remote(tt); + argv_printf(&argv, "%s %s %s %s mtu %d netmask %s up", IFCONFIG_PATH, + ifname, ifconfig_local, print_in_addr_t(remote_end, 0, &gc), + tun_mtu, ifconfig_remote_netmask); + } + else + { /* - * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD... + * NetBSD has distinct tun and tap devices + * so we don't need the "link0" extra parameter to specify we want to do + * tunneling at the ethernet level */ + argv_printf(&argv, "%s %s %s netmask %s mtu %d", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "NetBSD ifconfig failed"); - argv_printf(&argv, - "%s %s delete", - IFCONFIG_PATH, - actual); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, 0, NULL); - msg(M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure"); + /* Add a network route for the local tun interface */ + if (!tun && tt->topology == TOP_SUBNET) + { + struct route_ipv4 r; + CLEAR(r); + r.flags = RT_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = remote_end; + add_route(&r, tt, 0, NULL, es, NULL); + } + +#elif defined(TARGET_DARWIN) + /* + * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD... + */ + argv_printf(&argv, "%s %s delete", IFCONFIG_PATH, ifname); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, 0, NULL); + msg(M_INFO, + "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure"); - /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ - if (tun) + + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else + { + if (tt->topology == TOP_SUBNET) { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); + argv_printf(&argv, "%s %s %s %s netmask %s mtu %d up", + IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); } else { - if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - argv_printf(&argv, - "%s %s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - } - - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "Mac OS X ifconfig failed"); - tt->did_ifconfig = true; - - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - struct route_ipv4 r; - CLEAR(r); - r.flags = RT_DEFINED; - r.network = tt->local & tt->remote_netmask; - r.netmask = tt->remote_netmask; - r.gateway = tt->local; - add_route(&r, tt, 0, NULL, es); + argv_printf(&argv, "%s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_remote_netmask, + tun_mtu); } + } - if (do_ipv6) - { - argv_printf(&argv, - "%s %s inet6 %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed"); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "Mac OS X ifconfig failed"); - /* and, hooray, we explicitely need to add a route... */ - add_route_connected_v6_net(tt, es); - } + /* Add a network route for the local tun interface */ + if (!tun && tt->topology == TOP_SUBNET) + { + struct route_ipv4 r; + CLEAR(r); + r.flags = RT_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = tt->local; + add_route(&r, tt, 0, NULL, es, NULL); + } #elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) - in_addr_t remote_end; /* for "virtual" subnet topology */ + in_addr_t remote_end; /* for "virtual" subnet topology */ - /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ - if (tun) - { - argv_printf(&argv, - "%s %s %s %s mtu %d netmask 255.255.255.255 up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } - else if (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - remote_end = create_arbitrary_remote( tt ); - argv_printf(&argv, - "%s %s %s %s mtu %d netmask %s up", - IFCONFIG_PATH, - actual, - ifconfig_local, - print_in_addr_t(remote_end, 0, &gc), - tun_mtu, - ifconfig_remote_netmask - ); - } - else - { - argv_printf(&argv, - "%s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - } + /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */ + if (tun) + { + argv_printf(&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", + IFCONFIG_PATH, ifname, ifconfig_local, + ifconfig_remote_netmask, tun_mtu); + } + else if (tt->topology == TOP_SUBNET) + { + remote_end = create_arbitrary_remote( tt ); + argv_printf(&argv, "%s %s %s %s mtu %d netmask %s up", IFCONFIG_PATH, + ifname, ifconfig_local, print_in_addr_t(remote_end, 0, &gc), + tun_mtu, ifconfig_remote_netmask); + } + else + { + argv_printf(&argv, "%s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); + } - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "FreeBSD ifconfig failed"); - tt->did_ifconfig = true; + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, es, S_FATAL, "FreeBSD ifconfig failed"); - /* Add a network route for the local tun interface */ - if (!tun && tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) - { - struct route_ipv4 r; - CLEAR(r); - r.flags = RT_DEFINED; - r.network = tt->local & tt->remote_netmask; - r.netmask = tt->remote_netmask; - r.gateway = remote_end; - add_route(&r, tt, 0, NULL, es); - } + /* Add a network route for the local tun interface */ + if (!tun && tt->topology == TOP_SUBNET) + { + struct route_ipv4 r; + CLEAR(r); + r.flags = RT_DEFINED; + r.network = tt->local & tt->remote_netmask; + r.netmask = tt->remote_netmask; + r.gateway = remote_end; + add_route(&r, tt, 0, NULL, es, NULL); + } - if (do_ipv6) +#elif defined(TARGET_AIX) + { + /* AIX ifconfig will complain if it can't find ODM path in env */ + struct env_set *aix_es = env_set_create(NULL); + env_set_add( aix_es, "ODMDIR=/etc/objrepos" ); + + if (tun) { - argv_printf(&argv, - "%s %s inet6 %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed"); + msg(M_FATAL, "no tun support on AIX (canthappen)"); } -#elif defined(TARGET_AIX) - { - /* AIX ifconfig will complain if it can't find ODM path in env */ - struct env_set *aix_es = env_set_create(NULL); - env_set_add( aix_es, "ODMDIR=/etc/objrepos" ); + /* example: ifconfig tap0 172.30.1.1 netmask 255.255.254.0 up */ + argv_printf(&argv, "%s %s %s netmask %s mtu %d up", IFCONFIG_PATH, + ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); - if (tun) - { - msg(M_FATAL, "no tun support on AIX (canthappen)"); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, aix_es, S_FATAL, "AIX ifconfig failed"); - /* example: ifconfig tap0 172.30.1.1 netmask 255.255.254.0 up */ - argv_printf(&argv, - "%s %s %s netmask %s mtu %d up", - IFCONFIG_PATH, - actual, - ifconfig_local, - ifconfig_remote_netmask, - tun_mtu - ); - - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, aix_es, S_FATAL, "AIX ifconfig failed"); - tt->did_ifconfig = true; - - if (do_ipv6) - { - argv_printf(&argv, - "%s %s inet6 %s/%d", - IFCONFIG_PATH, - actual, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, aix_es, S_FATAL, "AIX ifconfig inet6 failed"); - } - env_set_destroy(aix_es); - } + env_set_destroy(aix_es); + } #elif defined (_WIN32) - { - ASSERT(actual != NULL); + ASSERT(ifname != NULL); - switch (tt->options.ip_win32_type) - { - case IPW32_SET_MANUAL: - msg(M_INFO, "******** NOTE: Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)", - actual, - ifconfig_local, - print_in_addr_t(tt->adapter_netmask, 0, &gc)); - break; + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) + { + msg(M_INFO, + "******** NOTE: Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)", + ifname, ifconfig_local, + print_in_addr_t(tt->adapter_netmask, 0, &gc)); + } + else if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ || tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + { + /* Let the DHCP configure the interface. */ + } + else if (tt->options.msg_channel) + { + do_address_service(true, AF_INET, tt); + do_dns_service(true, AF_INET, tt); + } + else if (tt->options.ip_win32_type == IPW32_SET_NETSH) + { + netsh_ifconfig(&tt->options, ifname, tt->local, + tt->adapter_netmask, NI_IP_NETMASK|NI_OPTIONS); + } + if (tt->options.msg_channel) + { + do_set_mtu_service(tt, AF_INET, tun_mtu); + } + else + { + windows_set_mtu(tt->adapter_index, AF_INET, tun_mtu); + } +#else /* if defined(TARGET_LINUX) */ + msg(M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script."); +#endif /* if defined(TARGET_LINUX) */ - case IPW32_SET_NETSH: - netsh_ifconfig(&tt->options, - actual, - tt->local, - tt->adapter_netmask, - NI_IP_NETMASK|NI_OPTIONS); +#if !defined(TARGET_LINUX) + gc_free(&gc); + argv_free(&argv); +#endif +} - break; - } - tt->did_ifconfig = true; - } +/* execute the ifconfig command through the shell */ +void +do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu, + const struct env_set *es, openvpn_net_ctx_t *ctx) +{ + msg(D_LOW, "do_ifconfig, ipv4=%d, ipv6=%d", tt->did_ifconfig_setup, + tt->did_ifconfig_ipv6_setup); - if (do_ipv6) - { - if (tt->options.ip_win32_type == IPW32_SET_MANUAL) - { - msg(M_INFO, "******** NOTE: Please manually set the v6 IP of '%s' to %s (if it is not already set)", - actual, - ifconfig_ipv6_local); - } - else if (tt->options.msg_channel) - { - do_address_service(true, AF_INET6, tt); - do_dns6_service(true, tt); - } - else - { - /* example: netsh interface ipv6 set address interface=42 2001:608:8003::d store=active */ - char iface[64]; - openvpn_snprintf(iface, sizeof(iface), "interface=%lu", tt->adapter_index ); - argv_printf(&argv, - "%s%sc interface ipv6 set address %s %s store=active", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - iface, - ifconfig_ipv6_local ); - netsh_command(&argv, 4, M_FATAL); - /* set ipv6 dns servers if any are specified */ - netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, actual); - } +#ifdef ENABLE_MANAGEMENT + if (management) + { + management_set_state(management, + OPENVPN_STATE_ASSIGN_IP, + NULL, + &tt->local, + &tt->local_ipv6, + NULL, + NULL); + } +#endif - /* explicit route needed */ - if (tt->options.ip_win32_type != IPW32_SET_MANUAL) - { - add_route_connected_v6_net(tt, es); - } - } -#else /* if defined(TARGET_LINUX) */ - msg(M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script."); -#endif /* if defined(TARGET_LINUX) */ - argv_reset(&argv); + if (tt->did_ifconfig_setup) + { + do_ifconfig_ipv4(tt, ifname, tun_mtu, es, ctx); } - gc_free(&gc); + + if (tt->did_ifconfig_ipv6_setup) + { + do_ifconfig_ipv6(tt, ifname, tun_mtu, es, ctx); + } + + /* release resources potentially allocated during interface setup */ + net_ctx_free(ctx); } static void @@ -1913,13 +1815,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - close_tun_generic(tt); - free(tt); - } + ASSERT(tt); + + close_tun_generic(tt); + free(tt); } int @@ -2065,12 +1966,14 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun ASSERT(0); } -#endif /* !PENDANTIC */ +#endif /* !PEDANTIC */ #ifdef ENABLE_FEATURE_TUN_PERSIST void -tuncfg(const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options) +tuncfg(const char *dev, const char *dev_type, const char *dev_node, + int persist_mode, const char *username, const char *groupname, + const struct tuntap_options *options, openvpn_net_ctx_t *ctx) { struct tuntap *tt; @@ -2109,86 +2012,95 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node, int persist_ msg(M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", groupname, dev); } } - close_tun(tt); + close_tun(tt, ctx); msg(M_INFO, "Persist state set to: %s", (persist_mode ? "ON" : "OFF")); } #endif /* ENABLE_FEATURE_TUN_PERSIST */ -void -close_tun(struct tuntap *tt) +static void +undo_ifconfig_ipv4(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) +#if defined(TARGET_LINUX) + int netbits = netmask_to_netbits2(tt->remote_netmask); + + if (is_tun_p2p(tt)) + { + if (net_addr_ptp_v4_del(ctx, tt->actual_name, &tt->local, + &tt->remote_netmask) < 0) + { + msg(M_WARN, "Linux can't del IP from iface %s", + tt->actual_name); + } + } + else { - if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig) + if (net_addr_v4_del(ctx, tt->actual_name, &tt->local, netbits) < 0) { - struct argv argv = argv_new(); - struct gc_arena gc = gc_new(); + msg(M_WARN, "Linux can't del IP from iface %s", + tt->actual_name); + } + } +#else /* ifndef TARGET_LINUX */ + struct argv argv = argv_new(); -#ifdef ENABLE_IPROUTE - if (is_tun_p2p(tt)) - { - argv_printf(&argv, - "%s addr del dev %s local %s peer %s", - iproute_path, - tt->actual_name, - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->remote_netmask, 0, &gc) - ); - } - else - { - argv_printf(&argv, - "%s addr del dev %s %s/%d", - iproute_path, - tt->actual_name, - print_in_addr_t(tt->local, 0, &gc), - netmask_to_netbits2(tt->remote_netmask) - ); - } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, - "%s %s 0.0.0.0", - IFCONFIG_PATH, - tt->actual_name - ); -#endif /* ifdef ENABLE_IPROUTE */ - - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Linux ip addr del failed"); - - if (tt->did_ifconfig_ipv6_setup) - { - const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); - -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s -6 addr del %s/%d dev %s", - iproute_path, - ifconfig_ipv6_local, - tt->netbits_ipv6, - tt->actual_name - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed"); -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, - "%s %s del %s/%d", - IFCONFIG_PATH, - tt->actual_name, - ifconfig_ipv6_local, - tt->netbits_ipv6 - ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Linux ifconfig inet6 del failed"); -#endif - } + argv_printf(&argv, "%s %s 0.0.0.0", IFCONFIG_PATH, tt->actual_name); + + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "Generic ip addr del failed"); + + argv_free(&argv); +#endif /* ifdef TARGET_LINUX */ +} - argv_reset(&argv); - gc_free(&gc); +static void +undo_ifconfig_ipv6(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ +#if defined(TARGET_LINUX) + if (net_addr_v6_del(ctx, tt->actual_name, &tt->local_ipv6, + tt->netbits_ipv6) < 0) + { + msg(M_WARN, "Linux can't del IPv6 from iface %s", tt->actual_name); + } +#else /* ifndef TARGET_LINUX */ + struct gc_arena gc = gc_new(); + const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, gc); + struct argv argv = argv_new(); + + argv_printf(&argv, "%s %s del %s/%d", IFCONFIG_PATH, tt->actual_name, + ifconfig_ipv6_local, tt->netbits_ipv6); + + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed"); + + argv_free(&argv); + gc_free(&gc); +#endif /* ifdef TARGET_LINUX */ +} + +void +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ + ASSERT(tt); + + if (tt->type != DEV_TYPE_NULL) + { + if (tt->did_ifconfig_setup) + { + undo_ifconfig_ipv4(tt, ctx); } - close_tun_generic(tt); - free(tt); + + if (tt->did_ifconfig_ipv6_setup) + { + undo_ifconfig_ipv6(tt, ctx); + } + + /* release resources potentially allocated during undo */ + net_ctx_reset(ctx); } + + close_tun_generic(tt); + free(tt); } int @@ -2446,57 +2358,54 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun static void solaris_close_tun(struct tuntap *tt) { - if (tt) + /* IPv6 interfaces need to be 'manually' de-configured */ + if (tt->did_ifconfig_ipv6_setup) { - /* IPv6 interfaces need to be 'manually' de-configured */ - if (tt->did_ifconfig_ipv6_setup) + struct argv argv = argv_new(); + argv_printf( &argv, "%s %s inet6 unplumb", + IFCONFIG_PATH, tt->actual_name ); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed"); + argv_free(&argv); + } + + if (tt->ip_fd >= 0) + { + struct lifreq ifr; + CLEAR(ifr); + strncpynt(ifr.lifr_name, tt->actual_name, sizeof(ifr.lifr_name)); + + if (ioctl(tt->ip_fd, SIOCGLIFFLAGS, &ifr) < 0) { - struct argv argv = argv_new(); - argv_printf( &argv, "%s %s inet6 unplumb", - IFCONFIG_PATH, tt->actual_name ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "Solaris ifconfig inet6 unplumb failed"); - argv_reset(&argv); + msg(M_WARN | M_ERRNO, "Can't get iface flags"); } - if (tt->ip_fd >= 0) + if (ioctl(tt->ip_fd, SIOCGLIFMUXID, &ifr) < 0) { - struct lifreq ifr; - CLEAR(ifr); - strncpynt(ifr.lifr_name, tt->actual_name, sizeof(ifr.lifr_name)); - - if (ioctl(tt->ip_fd, SIOCGLIFFLAGS, &ifr) < 0) - { - msg(M_WARN | M_ERRNO, "Can't get iface flags"); - } - - if (ioctl(tt->ip_fd, SIOCGLIFMUXID, &ifr) < 0) - { - msg(M_WARN | M_ERRNO, "Can't get multiplexor id"); - } - - if (tt->type == DEV_TYPE_TAP) - { - if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_arp_muxid) < 0) - { - msg(M_WARN | M_ERRNO, "Can't unlink interface(arp)"); - } - } + msg(M_WARN | M_ERRNO, "Can't get multiplexor id"); + } - if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_ip_muxid) < 0) + if (tt->type == DEV_TYPE_TAP) + { + if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_arp_muxid) < 0) { - msg(M_WARN | M_ERRNO, "Can't unlink interface(ip)"); + msg(M_WARN | M_ERRNO, "Can't unlink interface(arp)"); } - - close(tt->ip_fd); - tt->ip_fd = -1; } - if (tt->fd >= 0) + if (ioctl(tt->ip_fd, I_PUNLINK, ifr.lifr_ip_muxid) < 0) { - close(tt->fd); - tt->fd = -1; + msg(M_WARN | M_ERRNO, "Can't unlink interface(ip)"); } + + close(tt->ip_fd); + tt->ip_fd = -1; + } + + if (tt->fd >= 0) + { + close(tt->fd); + tt->fd = -1; } } @@ -2504,20 +2413,19 @@ solaris_close_tun(struct tuntap *tt) * Close TUN device. */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - solaris_close_tun(tt); + ASSERT(tt); - if (tt->actual_name) - { - free(tt->actual_name); - } + solaris_close_tun(tt); - clear_tuntap(tt); - free(tt); + if (tt->actual_name) + { + free(tt->actual_name); } + + clear_tuntap(tt); + free(tt); } static void @@ -2541,9 +2449,9 @@ solaris_error_close(struct tuntap *tt, const struct env_set *es, argv_msg(M_INFO, &argv); openvpn_execve_check(&argv, es, 0, "Solaris ifconfig unplumb failed"); - close_tun(tt); + close_tun(tt, NULL); msg(M_FATAL, "Solaris ifconfig failed"); - argv_reset(&argv); + argv_free(&argv); } int @@ -2604,33 +2512,34 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { + ASSERT(tt); + /* only *TAP* devices need destroying, tun devices auto-self-destruct */ - if (tt && (tt->type == DEV_TYPE_TUN || tt->persistent_if ) ) + if (tt->type == DEV_TYPE_TUN || tt->persistent_if) { close_tun_generic(tt); free(tt); + return; } - else if (tt) - { - struct gc_arena gc = gc_new(); - struct argv argv = argv_new(); - /* setup command, close tun dev (clears tt->actual_name!), run command - */ + struct argv argv = argv_new(); - argv_printf(&argv, "%s %s destroy", - IFCONFIG_PATH, tt->actual_name); + /* setup command, close tun dev (clears tt->actual_name!), run command + */ - close_tun_generic(tt); + argv_printf(&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)"); + close_tun_generic(tt); - free(tt); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "OpenBSD 'destroy tun interface' failed (non-critical)"); + + free(tt); + argv_free(&argv); } int @@ -2686,36 +2595,37 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun /* the current way OpenVPN handles tun devices on NetBSD leads to * lingering tunX interfaces after close -> for a full cleanup, they - * need to be explicitely destroyed + * need to be explicitly destroyed */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { + ASSERT(tt); + /* only tun devices need destroying, tap devices auto-self-destruct */ - if (tt && ( tt->type != DEV_TYPE_TUN || tt->persistent_if ) ) + if (tt->type != DEV_TYPE_TUN || tt->persistent_if) { close_tun_generic(tt); free(tt); + return; } - else if (tt) - { - struct gc_arena gc = gc_new(); - struct argv argv = argv_new(); - /* setup command, close tun dev (clears tt->actual_name!), run command - */ + struct argv argv = argv_new(); - argv_printf(&argv, "%s %s destroy", - IFCONFIG_PATH, tt->actual_name); + /* setup command, close tun dev (clears tt->actual_name!), run command + */ - close_tun_generic(tt); + argv_printf(&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "NetBSD 'destroy tun interface' failed (non-critical)"); + close_tun_generic(tt); - free(tt); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "NetBSD 'destroy tun interface' failed (non-critical)"); + + free(tt); + argv_free(&argv); } static inline int @@ -2829,30 +2739,34 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun * we need to call "ifconfig ... destroy" for cleanup */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt && tt->persistent_if) /* keep pre-existing if around */ + ASSERT(tt); + + if (tt->persistent_if) /* keep pre-existing if around */ { close_tun_generic(tt); free(tt); + return; } - else if (tt) /* close and destroy */ - { - struct argv argv = argv_new(); - /* setup command, close tun dev (clears tt->actual_name!), run command - */ + /* close and destroy */ + struct argv argv = argv_new(); - argv_printf(&argv, "%s %s destroy", - IFCONFIG_PATH, tt->actual_name); + /* setup command, close tun dev (clears tt->actual_name!), run command + */ - close_tun_generic(tt); + argv_printf(&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "FreeBSD 'destroy tun interface' failed (non-critical)"); + close_tun_generic(tt); - free(tt); - } + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, + "FreeBSD 'destroy tun interface' failed (non-critical)"); + + free(tt); + argv_free(&argv); } int @@ -2941,13 +2855,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - close_tun_generic(tt); - free(tt); - } + ASSERT(tt); + + close_tun_generic(tt); + free(tt); } int @@ -3037,14 +2950,16 @@ utun_open_helper(struct ctl_info ctlInfo, int utunnum) if (fd < 0) { - msg(M_INFO | M_ERRNO, "Opening utun (socket(SYSPROTO_CONTROL))"); + msg(M_INFO | M_ERRNO, "Opening utun%d failed (socket(SYSPROTO_CONTROL))", + utunnum); return -2; } if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) { close(fd); - msg(M_INFO | M_ERRNO, "Opening utun (ioctl(CTLIOCGINFO))"); + msg(M_INFO | M_ERRNO, "Opening utun%d failed (ioctl(CTLIOCGINFO))", + utunnum); return -2; } @@ -3062,7 +2977,8 @@ utun_open_helper(struct ctl_info ctlInfo, int utunnum) if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) < 0) { - msg(M_INFO | M_ERRNO, "Opening utun (connect(AF_SYS_CONTROL))"); + msg(M_INFO | M_ERRNO, "Opening utun%d failed (connect(AF_SYS_CONTROL))", + utunnum); close(fd); return -1; } @@ -3105,11 +3021,18 @@ open_darwin_utun(const char *dev, const char *dev_type, const char *dev_node, st /* try to open first available utun device if no specific utun is requested */ if (utunnum == -1) { - for (utunnum = 0; utunnum<255; utunnum++) + for (utunnum = 0; utunnum < 255; utunnum++) { + char ifname[20]; + /* if the interface exists silently skip it */ + ASSERT(snprintf(ifname, sizeof(ifname), "utun%d", utunnum) > 0); + if (if_nametoindex(ifname)) + { + continue; + } fd = utun_open_helper(ctlInfo, utunnum); /* Break if the fd is valid, - * or if early initalization failed (-2) */ + * or if early initialization failed (-2) */ if (fd !=-1) { break; @@ -3198,29 +3121,28 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - struct gc_arena gc = gc_new(); - struct argv argv = argv_new(); + ASSERT(tt); - if (tt->did_ifconfig_ipv6_setup) - { - const char *ifconfig_ipv6_local = - print_in6_addr(tt->local_ipv6, 0, &gc); + struct gc_arena gc = gc_new(); + struct argv argv = argv_new(); - argv_printf(&argv, "%s delete -inet6 %s", - ROUTE_PATH, ifconfig_ipv6_local ); - argv_msg(M_INFO, &argv); - openvpn_execve_check(&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)"); - } + if (tt->did_ifconfig_ipv6_setup) + { + const char *ifconfig_ipv6_local = + print_in6_addr(tt->local_ipv6, 0, &gc); - close_tun_generic(tt); - free(tt); - argv_reset(&argv); - gc_free(&gc); + argv_printf(&argv, "%s delete -inet6 %s", + ROUTE_PATH, ifconfig_ipv6_local ); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)"); } + + close_tun_generic(tt); + free(tt); + argv_free(&argv); + gc_free(&gc); } int @@ -3323,6 +3245,7 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun env_set_add( es, "ODMDIR=/etc/objrepos" ); openvpn_execve_check(&argv, es, S_FATAL, "AIX 'create tun interface' failed"); env_set_destroy(es); + argv_free(&argv); } else { @@ -3346,17 +3269,13 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun /* tap devices need to be manually destroyed on AIX */ void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - struct gc_arena gc = gc_new(); + ASSERT(tt); + struct argv argv = argv_new(); struct env_set *es = env_set_create(NULL); - if (!tt) - { - return; - } - /* persistent devices need IP address unconfig, others need destroyal */ if (tt->persistent_if) @@ -3377,6 +3296,7 @@ close_tun(struct tuntap *tt) free(tt); env_set_destroy(es); + argv_free(&argv); } int @@ -3393,6 +3313,22 @@ read_tun(struct tuntap *tt, uint8_t *buf, int len) #elif defined(_WIN32) +static const char * +print_windows_driver(enum windows_driver_type windows_driver) +{ + switch (windows_driver) + { + case WINDOWS_DRIVER_TAP_WINDOWS6: + return "tap-windows6"; + + case WINDOWS_DRIVER_WINTUN: + return "wintun"; + + default: + return "unspecified"; + } +} + int tun_read_queue(struct tuntap *tt, int maxsize) { @@ -3604,7 +3540,123 @@ tun_finalize( return ret; } -const struct tap_reg * +static const struct device_instance_id_interface * +get_device_instance_id_interface(struct gc_arena *gc) +{ + HDEVINFO dev_info_set; + DWORD err; + struct device_instance_id_interface *first = NULL; + struct device_instance_id_interface *last = NULL; + + dev_info_set = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + if (dev_info_set == INVALID_HANDLE_VALUE) + { + err = GetLastError(); + msg(M_FATAL, "Error [%u] opening device information set key: %s", (unsigned int)err, strerror_win32(err, gc)); + } + + for (DWORD i = 0;; ++i) + { + SP_DEVINFO_DATA device_info_data; + BOOL res; + HKEY dev_key; + char net_cfg_instance_id_string[] = "NetCfgInstanceId"; + char net_cfg_instance_id[256]; + char device_instance_id[256]; + DWORD len; + DWORD data_type; + LONG status; + ULONG dev_interface_list_size; + CONFIGRET cr; + struct buffer dev_interface_list; + + ZeroMemory(&device_info_data, sizeof(SP_DEVINFO_DATA)); + device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + res = SetupDiEnumDeviceInfo(dev_info_set, i, &device_info_data); + if (!res) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + { + break; + } + else + { + continue; + } + } + + dev_key = SetupDiOpenDevRegKey(dev_info_set, &device_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (dev_key == INVALID_HANDLE_VALUE) + { + continue; + } + + len = sizeof(net_cfg_instance_id); + data_type = REG_SZ; + status = RegQueryValueEx(dev_key, + net_cfg_instance_id_string, + NULL, + &data_type, + net_cfg_instance_id, + &len); + if (status != ERROR_SUCCESS) + { + goto next; + } + + len = sizeof(device_instance_id); + res = SetupDiGetDeviceInstanceId(dev_info_set, &device_info_data, device_instance_id, len, &len); + if (!res) + { + goto next; + } + + cr = CM_Get_Device_Interface_List_Size(&dev_interface_list_size, + (LPGUID)&GUID_DEVINTERFACE_NET, + device_instance_id, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + + if (cr != CR_SUCCESS) + { + goto next; + } + + dev_interface_list = alloc_buf_gc(dev_interface_list_size, gc); + cr = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_NET, device_instance_id, + BPTR(&dev_interface_list), + dev_interface_list_size, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + { + goto next; + } + + struct device_instance_id_interface *dev_if; + ALLOC_OBJ_CLEAR_GC(dev_if, struct device_instance_id_interface, gc); + dev_if->net_cfg_instance_id = string_alloc(net_cfg_instance_id, gc); + dev_if->device_interface_list = string_alloc(BSTR(&dev_interface_list), gc); + + /* link into return list */ + if (!first) + { + first = dev_if; + } + if (last) + { + last->next = dev_if; + } + last = dev_if; + +next: + RegCloseKey(dev_key); + } + + SetupDiDestroyDeviceInfoList(dev_info_set); + + return first; +} + +static const struct tap_reg * get_tap_reg(struct gc_arena *gc) { HKEY adapter_key; @@ -3700,12 +3752,24 @@ get_tap_reg(struct gc_arena *gc) if (status == ERROR_SUCCESS && data_type == REG_SZ) { - if (!strcmp(component_id, TAP_WIN_COMPONENT_ID) || - !strcmp(component_id, "root\\" TAP_WIN_COMPONENT_ID)) + /* Is this adapter supported? */ + enum windows_driver_type windows_driver = WINDOWS_DRIVER_UNSPECIFIED; + if (strcasecmp(component_id, TAP_WIN_COMPONENT_ID) == 0 + || strcasecmp(component_id, "root\\" TAP_WIN_COMPONENT_ID) == 0) + { + windows_driver = WINDOWS_DRIVER_TAP_WINDOWS6; + } + else if (strcasecmp(component_id, WINTUN_COMPONENT_ID) == 0) + { + windows_driver = WINDOWS_DRIVER_WINTUN; + } + + if (windows_driver != WINDOWS_DRIVER_UNSPECIFIED) { struct tap_reg *reg; ALLOC_OBJ_CLEAR_GC(reg, struct tap_reg, gc); reg->guid = string_alloc(net_cfg_instance_id, gc); + reg->windows_driver = windows_driver; /* link into return list */ if (!first) @@ -3729,7 +3793,7 @@ get_tap_reg(struct gc_arena *gc) return first; } -const struct panel_reg * +static const struct panel_reg * get_panel_reg(struct gc_arena *gc) { LONG status; @@ -3936,7 +4000,7 @@ show_tap_win_adapters(int msglev, int warnlev) const struct tap_reg *tap_reg = get_tap_reg(&gc); const struct panel_reg *panel_reg = get_panel_reg(&gc); - msg(msglev, "Available TAP-WIN32 adapters [name, GUID]:"); + msg(msglev, "Available TAP-WIN32 / Wintun adapters [name, GUID, driver]:"); /* loop through each TAP-Windows adapter registry entry */ for (tr = tap_reg; tr != NULL; tr = tr->next) @@ -3948,7 +4012,7 @@ show_tap_win_adapters(int msglev, int warnlev) { if (!strcmp(tr->guid, pr->guid)) { - msg(msglev, "'%s' %s", pr->name, tr->guid); + msg(msglev, "'%s' %s %s", pr->name, tr->guid, print_windows_driver(tr->windows_driver)); ++links; } } @@ -3998,10 +4062,10 @@ show_tap_win_adapters(int msglev, int warnlev) } /* - * Confirm that GUID is a TAP-Windows adapter. + * Lookup a TAP-Windows or Wintun adapter by GUID. */ -static bool -is_tap_win(const char *guid, const struct tap_reg *tap_reg) +static const struct tap_reg * +get_adapter_by_guid(const char *guid, const struct tap_reg *tap_reg) { const struct tap_reg *tr; @@ -4009,11 +4073,11 @@ is_tap_win(const char *guid, const struct tap_reg *tap_reg) { if (guid && !strcmp(tr->guid, guid)) { - return true; + return tr; } } - return false; + return NULL; } static const char * @@ -4032,16 +4096,16 @@ guid_to_name(const char *guid, const struct panel_reg *panel_reg) return NULL; } -static const char * -name_to_guid(const char *name, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg) +static const struct tap_reg * +get_adapter_by_name(const char *name, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg) { const struct panel_reg *pr; for (pr = panel_reg; pr != NULL; pr = pr->next) { - if (name && !strcmp(pr->name, name) && is_tap_win(pr->guid, tap_reg)) + if (name && !strcmp(pr->name, name)) { - return pr->guid; + return get_adapter_by_guid(pr->guid, tap_reg); } } @@ -4053,7 +4117,7 @@ at_least_one_tap_win(const struct tap_reg *tap_reg) { if (!tap_reg) { - msg(M_FATAL, "There are no TAP-Windows adapters on this system. You should be able to create a TAP-Windows adapter by going to Start -> All Programs -> TAP-Windows -> Utilities -> Add a new TAP-Windows virtual ethernet adapter."); + 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."); } } @@ -4067,6 +4131,7 @@ get_unspecified_device_guid(const int device_number, int actual_name_size, const struct tap_reg *tap_reg_src, const struct panel_reg *panel_reg_src, + enum windows_driver_type *windows_driver, struct gc_arena *gc) { const struct tap_reg *tap_reg = tap_reg_src; @@ -4116,23 +4181,29 @@ get_unspecified_device_guid(const int device_number, /* Save GUID for return value */ ret = alloc_buf_gc(256, gc); buf_printf(&ret, "%s", tap_reg->guid); + if (windows_driver != NULL) + { + *windows_driver = tap_reg->windows_driver; + } return BSTR(&ret); } /* * Lookup a --dev-node adapter name in the registry - * returning the GUID and optional actual_name. + * returning the GUID and optional actual_name and device type */ static const char * get_device_guid(const char *name, char *actual_name, int actual_name_size, + enum windows_driver_type *windows_driver, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg, struct gc_arena *gc) { struct buffer ret = alloc_buf_gc(256, gc); struct buffer actual = clear_buf(); + const struct tap_reg *tr; /* Make sure we have at least one TAP adapter */ if (!tap_reg) @@ -4148,7 +4219,8 @@ get_device_guid(const char *name, } /* Check if GUID was explicitly specified as --dev-node parameter */ - if (is_tap_win(name, tap_reg)) + tr = get_adapter_by_guid(name, tap_reg); + if (tr) { const char *act = guid_to_name(name, panel_reg); buf_printf(&ret, "%s", name); @@ -4160,16 +4232,24 @@ get_device_guid(const char *name, { buf_printf(&actual, "%s", name); } + if (windows_driver) + { + *windows_driver = tr->windows_driver; + } return BSTR(&ret); } /* Lookup TAP adapter in network connections list */ { - const char *guid = name_to_guid(name, tap_reg, panel_reg); - if (guid) + tr = get_adapter_by_name(name, tap_reg, panel_reg); + if (tr) { buf_printf(&actual, "%s", name); - buf_printf(&ret, "%s", guid); + if (windows_driver) + { + *windows_driver = tr->windows_driver; + } + buf_printf(&ret, "%s", tr->guid); return BSTR(&ret); } } @@ -4649,8 +4729,7 @@ get_adapter_index_method_1(const char *guid) DWORD index; ULONG aindex; wchar_t wbuf[256]; - swprintf(wbuf, SIZE(wbuf), L"\\DEVICE\\TCPIP_%S", guid); - wbuf [SIZE(wbuf) - 1] = 0; + openvpn_swprintf(wbuf, SIZE(wbuf), L"\\DEVICE\\TCPIP_%S", guid); if (GetAdapterIndex(wbuf, &aindex) != NO_ERROR) { index = TUN_ADAPTER_INDEX_INVALID; @@ -4714,11 +4793,14 @@ get_adapter_index_flexible(const char *name) /* actual name or GUID */ { const struct tap_reg *tap_reg = get_tap_reg(&gc); const struct panel_reg *panel_reg = get_panel_reg(&gc); - const char *guid = name_to_guid(name, tap_reg, panel_reg); - index = get_adapter_index_method_1(guid); - if (index == TUN_ADAPTER_INDEX_INVALID) + const struct tap_reg *tr = get_adapter_by_name(name, tap_reg, panel_reg); + if (tr) { - index = get_adapter_index_method_2(guid); + index = get_adapter_index_method_1(tr->guid); + if (index == TUN_ADAPTER_INDEX_INVALID) + { + index = get_adapter_index_method_2(tr->guid); + } } } if (index == TUN_ADAPTER_INDEX_INVALID) @@ -4851,7 +4933,7 @@ tap_allow_nonadmin_access(const char *dev_node) if (dev_node) { /* Get the device GUID for the device specified with --dev-node. */ - device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), tap_reg, panel_reg, &gc); + device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), NULL, tap_reg, panel_reg, &gc); if (!device_guid) { @@ -4894,6 +4976,7 @@ tap_allow_nonadmin_access(const char *dev_node) sizeof(actual_buffer), tap_reg, panel_reg, + NULL, &gc); if (!device_guid) @@ -5049,19 +5132,19 @@ ipconfig_register_dns(const struct env_set *es) msg(D_TUNTAP_INFO, "Start ipconfig commands for register-dns..."); netcmd_semaphore_lock(); - argv_printf(&argv, "%s%sc /flushdns", + argv_printf(&argv, "%s%s /flushdns", get_win_sys_path(), WIN_IPCONFIG_PATH_SUFFIX); argv_msg(D_TUNTAP_INFO, &argv); openvpn_execve_check(&argv, es, 0, err); - argv_reset(&argv); + argv_free(&argv); - argv_printf(&argv, "%s%sc /registerdns", + argv_printf(&argv, "%s%s /registerdns", get_win_sys_path(), WIN_IPCONFIG_PATH_SUFFIX); argv_msg(D_TUNTAP_INFO, &argv); openvpn_execve_check(&argv, es, 0, err); - argv_reset(&argv); + argv_free(&argv); netcmd_semaphore_release(); msg(D_TUNTAP_INFO, "End ipconfig commands for register-dns..."); @@ -5170,8 +5253,8 @@ netsh_set_dns6_servers(const struct in6_addr *addr_list, for (int i = 0; i < addr_len; ++i) { const char *fmt = (i == 0) ? - "%s%sc interface ipv6 set dns %s static %s" - : "%s%sc interface ipv6 add dns %s %s"; + "%s%s interface ipv6 set dns %s static %s" + : "%s%s interface ipv6 add dns %s %s"; argv_printf(&argv, fmt, get_win_sys_path(), NETSH_PATH_SUFFIX, flex_name, print_in6_addr(addr_list[i], 0, &gc)); @@ -5186,7 +5269,7 @@ netsh_set_dns6_servers(const struct in6_addr *addr_list, netsh_command(&argv, 1, (i==0) ? M_FATAL : M_NONFATAL); } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); } @@ -5201,6 +5284,7 @@ netsh_ifconfig_options(const char *type, struct gc_arena gc = gc_new(); struct argv argv = argv_new(); bool delete_first = false; + bool is_dns = !strcmp(type, "dns"); /* first check if we should delete existing DNS/WINS settings from TAP interface */ if (test_first) @@ -5218,7 +5302,7 @@ netsh_ifconfig_options(const char *type, /* delete existing DNS/WINS settings from TAP interface */ if (delete_first) { - argv_printf(&argv, "%s%sc interface ip delete %s %s all", + argv_printf(&argv, "%s%s interface ip delete %s %s all", get_win_sys_path(), NETSH_PATH_SUFFIX, type, @@ -5235,8 +5319,8 @@ netsh_ifconfig_options(const char *type, if (delete_first || !test_first || !ip_addr_member_of(addr_list[i], current)) { const char *fmt = count ? - "%s%sc interface ip add %s %s %s" - : "%s%sc interface ip set %s %s static %s"; + "%s%s interface ip add %s %s %s" + : "%s%s interface ip set %s %s static %s"; argv_printf(&argv, fmt, get_win_sys_path(), @@ -5244,6 +5328,14 @@ netsh_ifconfig_options(const char *type, type, flex_name, print_in_addr_t(addr_list[i], 0, &gc)); + + /* disable slow address validation on Windows 7 and higher */ + /* only for DNS */ + if (is_dns && win32_version_info() >= WIN_7) + { + argv_printf_cat(&argv, "%s", "validate=no"); + } + netsh_command(&argv, 2, M_FATAL); ++count; @@ -5258,7 +5350,7 @@ netsh_ifconfig_options(const char *type, } } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); } @@ -5312,7 +5404,7 @@ netsh_ifconfig(const struct tuntap_options *to, else { /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */ - argv_printf(&argv, "%s%sc interface ip set address %s static %s %s", + argv_printf(&argv, "%s%s interface ip set address %s static %s %s", get_win_sys_path(), NETSH_PATH_SUFFIX, flex_name, @@ -5349,7 +5441,7 @@ netsh_ifconfig(const struct tuntap_options *to, BOOL_CAST(flags & NI_TEST_FIRST)); } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); } @@ -5360,21 +5452,20 @@ netsh_enable_dhcp(const char *actual_name) /* example: netsh interface ip set address my-tap dhcp */ argv_printf(&argv, - "%s%sc interface ip set address %s dhcp", + "%s%s interface ip set address %s dhcp", get_win_sys_path(), NETSH_PATH_SUFFIX, actual_name); netsh_command(&argv, 4, M_FATAL); - argv_reset(&argv); + argv_free(&argv); } /* Enable dhcp on tap adapter using iservice */ static bool service_enable_dhcp(const struct tuntap *tt) { - DWORD len; bool ret = false; ack_message_t ack; struct gc_arena gc = gc_new(); @@ -5389,11 +5480,8 @@ service_enable_dhcp(const struct tuntap *tt) .iface = { .index = tt->adapter_index, .name = "" } }; - if (!WriteFile(pipe, &dhcp, sizeof(dhcp), &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, &dhcp, sizeof(dhcp), &ack, "Enable_dhcp")) { - msg(M_WARN, "Enable_dhcp: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -5413,6 +5501,45 @@ out: return ret; } +static void +windows_set_mtu(const int iface_index, const short family, + const int mtu) +{ + DWORD err = 0; + struct gc_arena gc = gc_new(); + MIB_IPINTERFACE_ROW ipiface; + InitializeIpInterfaceEntry(&ipiface); + const char *family_name = (family == AF_INET6) ? "IPv6" : "IPv4"; + ipiface.Family = family; + ipiface.InterfaceIndex = iface_index; + if (family == AF_INET6 && mtu < 1280) + { + msg(M_INFO, "NOTE: IPv6 interface MTU < 1280 conflicts with IETF standards and might not work"); + } + + err = GetIpInterfaceEntry(&ipiface); + if (err == NO_ERROR) + { + if (family == AF_INET) + { + ipiface.SitePrefixLength = 0; + } + ipiface.NlMtu = mtu; + err = SetIpInterfaceEntry(&ipiface); + } + + if (err != NO_ERROR) + { + msg(M_WARN, "TUN: Setting %s mtu failed: %s [status=%u if_index=%d]", + family_name, strerror_win32(err, &gc), err, iface_index); + } + else + { + msg(M_INFO, "%s MTU set to %d on interface %d using SetIpInterfaceEntry()", family_name, mtu, iface_index); + } +} + + /* * Return a TAP name for netsh commands. */ @@ -5428,13 +5555,13 @@ netsh_get_id(const char *dev_node, struct gc_arena *gc) if (dev_node) { - guid = get_device_guid(dev_node, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, gc); + guid = get_device_guid(dev_node, BPTR(&actual), BCAP(&actual), NULL, tap_reg, panel_reg, gc); } else { - guid = get_unspecified_device_guid(0, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, gc); + guid = get_unspecified_device_guid(0, BPTR(&actual), BCAP(&actual), tap_reg, panel_reg, NULL, gc); - if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Windows adapter */ + if (get_unspecified_device_guid(1, NULL, 0, tap_reg, panel_reg, NULL, gc)) /* ambiguous if more than one TAP-Windows adapter */ { guid = NULL; } @@ -5556,6 +5683,75 @@ write_dhcp_str(struct buffer *buf, const int type, const char *str, bool *error) buf_write(buf, str, len); } +/* + * RFC3397 states that multiple searchdomains are encoded as follows: + * - at start the length of the entire option is given + * - each subdomain is preceded by its length + * - each searchdomain is separated by a NUL character + * e.g. if you want "openvpn.net" and "duckduckgo.com" then you end up with + * 0x1D 0x7 openvpn 0x3 net 0x00 0x0A duckduckgo 0x3 com 0x00 + */ +static void +write_dhcp_search_str(struct buffer *buf, const int type, const char * const *str_array, + int array_len, bool *error) +{ + char tmp_buf[256]; + int i; + int len = 0; + int label_length_pos; + + for (i=0; i < array_len; i++) + { + const char *ptr = str_array[i]; + + if (strlen(ptr) + len + 1 > sizeof(tmp_buf)) + { + *error = true; + msg(M_WARN, "write_dhcp_search_str: temp buffer overflow building DHCP options"); + return; + } + /* Loop over all subdomains separated by a dot and replace the dot + with the length of the subdomain */ + + /* label_length_pos points to the byte to be replaced by the length + * of the following domain label */ + label_length_pos = len++; + + while (true) + { + if (*ptr == '.' || *ptr == '\0' ) + { + tmp_buf[label_length_pos] = (len-label_length_pos)-1; + label_length_pos = len; + if (*ptr == '\0') + { + break; + } + } + tmp_buf[len++] = *ptr++; + } + /* And close off with an extra NUL char */ + tmp_buf[len++] = 0; + } + + if (!buf_safe(buf, 2 + len)) + { + *error = true; + msg(M_WARN, "write_search_dhcp_str: buffer overflow building DHCP options"); + return; + } + if (len > 255) + { + *error = true; + msg(M_WARN, "write_dhcp_search_str: search domain string must be <= 255 bytes"); + return; + } + + buf_write_u8(buf, type); + buf_write_u8(buf, len); + buf_write(buf, tmp_buf, len); +} + static bool build_dhcp_options_string(struct buffer *buf, const struct tuntap_options *o) { @@ -5580,6 +5776,13 @@ build_dhcp_options_string(struct buffer *buf, const struct tuntap_options *o) write_dhcp_u32_array(buf, 42, (uint32_t *)o->ntp, o->ntp_len, &error); write_dhcp_u32_array(buf, 45, (uint32_t *)o->nbdd, o->nbdd_len, &error); + if (o->domain_search_list_len > 0) + { + write_dhcp_search_str(buf, 119, o->domain_search_list, + o->domain_search_list_len, + &error); + } + /* the MS DHCP server option 'Disable Netbios-over-TCP/IP * is implemented as vendor option 001, value 002. * A value of 001 means 'leave NBT alone' which is the default */ @@ -5618,7 +5821,7 @@ fork_dhcp_action(struct tuntap *tt) { buf_printf(&cmd, " --dhcp-renew"); } - buf_printf(&cmd, " --dhcp-internal %u", (unsigned int)tt->adapter_index); + buf_printf(&cmd, " --dhcp-internal %lu", tt->adapter_index); fork_to_self(BSTR(&cmd)); gc_free(&gc); @@ -5628,18 +5831,16 @@ fork_dhcp_action(struct tuntap *tt) static void register_dns_service(const struct tuntap *tt) { - DWORD len; HANDLE msg_channel = tt->options.msg_channel; ack_message_t ack; struct gc_arena gc = gc_new(); message_header_t rdns = { msg_register_dns, sizeof(message_header_t), 0 }; - if (!WriteFile(msg_channel, &rdns, sizeof(rdns), &len, NULL) - || !ReadFile(msg_channel, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(msg_channel, &rdns, sizeof(rdns), &ack, "Register_dns")) { - msg(M_WARN, "Register_dns: could not talk to service: %s [status=0x%lx]", - strerror_win32(GetLastError(), &gc), GetLastError()); + gc_free(&gc); + return; } else if (ack.error_number != NO_ERROR) @@ -5656,6 +5857,46 @@ register_dns_service(const struct tuntap *tt) gc_free(&gc); } +static bool +service_register_ring_buffers(const struct tuntap *tt) +{ + HANDLE msg_channel = tt->options.msg_channel; + ack_message_t ack; + bool ret = true; + struct gc_arena gc = gc_new(); + + register_ring_buffers_message_t msg = { + .header = { + msg_register_ring_buffers, + sizeof(register_ring_buffers_message_t), + 0 + }, + .device = tt->hand, + .send_ring_handle = tt->wintun_send_ring_handle, + .receive_ring_handle = tt->wintun_receive_ring_handle, + .send_tail_moved = tt->rw_handle.read, + .receive_tail_moved = tt->rw_handle.write + }; + + if (!send_msg_iservice(msg_channel, &msg, sizeof(msg), &ack, "Register ring buffers")) + { + ret = false; + } + else if (ack.error_number != NO_ERROR) + { + msg(M_NONFATAL, "Register ring buffers failed using service: %s [status=0x%x]", + strerror_win32(ack.error_number, &gc), ack.error_number); + ret = false; + } + else + { + msg(M_INFO, "Ring buffers registered via service"); + } + + gc_free(&gc); + return ret; +} + void fork_register_dns_action(struct tuntap *tt) { @@ -5704,511 +5945,650 @@ dhcp_masq_addr(const in_addr_t local, const in_addr_t netmask, const int offset) return htonl(dsa); } -void -open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +static void +tuntap_get_version_info(const struct tuntap *tt) { - struct gc_arena gc = gc_new(); - char device_path[256]; - const char *device_guid = NULL; + ULONG info[3]; DWORD len; - bool dhcp_masq = false; - bool dhcp_masq_post = false; - - /*netcmd_semaphore_lock ();*/ - - msg( M_INFO, "open_tun"); - - if (tt->type == DEV_TYPE_NULL) + CLEAR(info); + if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION, + &info, sizeof(info), + &info, sizeof(info), &len, NULL)) { - open_null(tt); - gc_free(&gc); - return; + msg(D_TUNTAP_INFO, "TAP-Windows Driver Version %d.%d %s", + (int)info[0], + (int)info[1], + (info[2] ? "(DEBUG)" : "")); + } - else if (tt->type == DEV_TYPE_TAP || tt->type == DEV_TYPE_TUN) + if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR)) { + msg(M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Windows driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.", + TAP_WIN_MIN_MAJOR, + TAP_WIN_MIN_MINOR); } - else + + /* usage of numeric constants is ugly, but this is really tied to + * *this* version of the driver + */ + if (tt->type == DEV_TYPE_TUN + && info[0] == 9 && info[1] < 8) { - msg(M_FATAL|M_NOPREFIX, "Unknown virtual device type: '%s'", dev); + msg(M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will not work. Upgrade your Tap-Win32 driver.", (int)info[0], (int)info[1]); } - /* - * Lookup the device name in the registry, using the --dev-node high level name. + /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy */ + if (tt->type == DEV_TYPE_TUN + && info[0] == 9 && info[1] == 8) { - const struct tap_reg *tap_reg = get_tap_reg(&gc); - const struct panel_reg *panel_reg = get_panel_reg(&gc); - char actual_buffer[256]; - - at_least_one_tap_win(tap_reg); + msg(M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade your Tap-Win32 driver.", (int)info[0], (int)info[1]); + } +} - if (dev_node) - { - /* Get the device GUID for the device specified with --dev-node. */ - device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), tap_reg, panel_reg, &gc); +static void +tuntap_get_mtu(struct tuntap *tt) +{ + ULONG mtu = 0; + DWORD len; + if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU, + &mtu, sizeof(mtu), + &mtu, sizeof(mtu), &len, NULL)) + { + tt->post_open_mtu = (int)mtu; + msg(D_MTU_INFO, "TAP-Windows MTU=%d", (int)mtu); + } +} - if (!device_guid) - { - msg(M_FATAL, "TAP-Windows adapter '%s' not found", dev_node); - } +static void +tuntap_set_ip_addr(struct tuntap *tt, + const char *device_guid, + bool dhcp_masq_post) +{ + struct gc_arena gc = gc_new(); + const DWORD index = tt->adapter_index; - /* Open Windows TAP-Windows adapter */ - openvpn_snprintf(device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAP_WIN_SUFFIX); + /* flush arp cache */ + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 + && index != TUN_ADAPTER_INDEX_INVALID) + { + DWORD status = -1; - tt->hand = CreateFile( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 - ); + if (tt->options.msg_channel) + { + ack_message_t ack; + flush_neighbors_message_t msg = { + .header = { + msg_flush_neighbors, + sizeof(flush_neighbors_message_t), + 0 + }, + .family = AF_INET, + .iface = {.index = index, .name = "" } + }; - if (tt->hand == INVALID_HANDLE_VALUE) + if (send_msg_iservice(tt->options.msg_channel, &msg, sizeof(msg), + &ack, "TUN")) { - msg(M_ERR, "CreateFile failed on TAP device: %s", device_path); + status = ack.error_number; } } else { - int device_number = 0; - - /* Try opening all TAP devices until we find one available */ - while (true) - { - device_guid = get_unspecified_device_guid(device_number, - actual_buffer, - sizeof(actual_buffer), - tap_reg, - panel_reg, - &gc); - - if (!device_guid) - { - msg(M_FATAL, "All TAP-Windows adapters on this system are currently in use."); - } - - /* Open Windows TAP-Windows adapter */ - openvpn_snprintf(device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAP_WIN_SUFFIX); - - tt->hand = CreateFile( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 - ); - - if (tt->hand == INVALID_HANDLE_VALUE) - { - msg(D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path); - } - else - { - break; - } - - device_number++; - } + status = FlushIpNetTable(index); } - /* translate high-level device name into a device instance - * GUID using the registry */ - tt->actual_name = string_alloc(actual_buffer, NULL); - } - - msg(M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path); - tt->adapter_index = get_adapter_index(device_guid); - - /* get driver version info */ - { - ULONG info[3]; - CLEAR(info); - if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_VERSION, - &info, sizeof(info), - &info, sizeof(info), &len, NULL)) + if (status == NO_ERROR) { - msg(D_TUNTAP_INFO, "TAP-Windows Driver Version %d.%d %s", - (int) info[0], - (int) info[1], - (info[2] ? "(DEBUG)" : "")); - + msg(M_INFO, "Successful ARP Flush on interface [%lu] %s", + index, + device_guid); } - if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR)) + else if (status != -1) { - msg(M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Windows driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.", - TAP_WIN_MIN_MAJOR, - TAP_WIN_MIN_MINOR); + msg(D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%lu] %s (status=%lu) : %s", + index, + device_guid, + status, + strerror_win32(status, &gc)); } + } - /* usage of numeric constants is ugly, but this is really tied to - * *this* version of the driver - */ - if (tt->type == DEV_TYPE_TUN - && info[0] == 9 && info[1] < 8) + /* + * If the TAP-Windows driver is masquerading as a DHCP server + * make sure the TCP/IP properties for the adapter are + * set correctly. + */ + if (dhcp_masq_post) + { + /* check dhcp enable status */ + if (dhcp_status(index) == DHCP_STATUS_DISABLED) { - msg( M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will not work. Upgrade your Tap-Win32 driver.", (int) info[0], (int) info[1] ); + msg(M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); } - /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy - */ - if (tt->type == DEV_TYPE_TUN - && info[0] == 9 && info[1] == 8) + /* force an explicit DHCP lease renewal on TAP adapter? */ + if (tt->options.dhcp_pre_release) { - msg( M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade your Tap-Win32 driver.", (int) info[0], (int) info[1] ); + dhcp_release(tt); } + if (tt->options.dhcp_renew) + { + dhcp_renew(tt); + } + } + else + { + fork_dhcp_action(tt); } - /* get driver MTU */ + if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI) { - ULONG mtu; - if (DeviceIoControl(tt->hand, TAP_WIN_IOCTL_GET_MTU, - &mtu, sizeof(mtu), - &mtu, sizeof(mtu), &len, NULL)) + DWORD status; + const char *error_suffix = "I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi')"; + + /* couldn't get adapter index */ + if (index == TUN_ADAPTER_INDEX_INVALID) { - tt->post_open_mtu = (int) mtu; - msg(D_MTU_INFO, "TAP-Windows MTU=%d", (int) mtu); + msg(M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s", + device_guid, + error_suffix); } - } - /* - * Preliminaries for setting TAP-Windows adapter TCP/IP - * properties via --ip-win32 dynamic or --ip-win32 adaptive. - */ - if (tt->did_ifconfig_setup) - { - if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ) + /* check dhcp enable status */ + if (dhcp_status(index) == DHCP_STATUS_DISABLED) { - /* - * If adapter is set to non-DHCP, set to DHCP mode. - */ - if (dhcp_status(tt->adapter_index) == DHCP_STATUS_DISABLED) - { - /* try using the service if available, else directly execute netsh */ - if (tt->options.msg_channel) - { - service_enable_dhcp(tt); - } - else - { - netsh_enable_dhcp(tt->actual_name); - } - } - dhcp_masq = true; - dhcp_masq_post = true; + msg(M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); } - else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + + /* delete previously added IP addresses which were not + * correctly deleted */ + delete_temp_addresses(index); + + /* add a new IP address */ + if ((status = AddIPAddress(htonl(tt->local), + htonl(tt->adapter_netmask), + index, + &tt->ipapi_context, + &tt->ipapi_instance)) == NO_ERROR) { - /* - * If adapter is set to non-DHCP, use netsh right away. - */ - if (dhcp_status(tt->adapter_index) != DHCP_STATUS_ENABLED) - { - netsh_ifconfig(&tt->options, - tt->actual_name, - tt->local, - tt->adapter_netmask, - NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS); - } - else - { - dhcp_masq = true; - } + msg(M_INFO, "Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid + ); + } + else + { + msg(M_FATAL, "ERROR: AddIPAddress %s/%s failed on interface %s, index=%lu, status=%lu (windows error: '%s') -- %s", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid, + index, + status, + strerror_win32(status, &gc), + error_suffix); } + tt->ipapi_context_defined = true; } - /* set point-to-point mode if TUN device */ + gc_free(&gc); +} - if (tt->type == DEV_TYPE_TUN) +static bool +wintun_register_ring_buffer(struct tuntap *tt, const char *device_guid) +{ + bool ret = true; + + tt->wintun_send_ring = (struct tun_ring *)MapViewOfFile(tt->wintun_send_ring_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof(struct tun_ring)); + + tt->wintun_receive_ring = (struct tun_ring *)MapViewOfFile(tt->wintun_receive_ring_handle, + FILE_MAP_ALL_ACCESS, + 0, + 0, + sizeof(struct tun_ring)); + + if (tt->options.msg_channel) { - if (!tt->did_ifconfig_setup) - { - msg(M_FATAL, "ERROR: --dev tun also requires --ifconfig"); - } + ret = service_register_ring_buffers(tt); + } + else + { + msg(M_FATAL, "ERROR: Wintun requires SYSTEM privileges and therefore " + "should be used with interactive service. If you want to " + "use openvpn from command line, you need to do SYSTEM " + "elevation yourself (for example with psexec)."); + } - if (tt->topology == TOP_SUBNET) - { - in_addr_t ep[3]; - BOOL status; + return ret; +} - ep[0] = htonl(tt->local); - ep[1] = htonl(tt->local & tt->remote_netmask); - ep[2] = htonl(tt->remote_netmask); +static void +tuntap_set_connected(const struct tuntap *tt) +{ + ULONG status = TRUE; + DWORD len; + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, + &status, sizeof(status), + &status, sizeof(status), &len, NULL)) + { + msg(M_WARN, "WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); + } - status = DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_TUN, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL); + int s = tt->options.tap_sleep; + if (s > 0) + { + msg(M_INFO, "Sleeping for %d seconds...", s); + management_sleep(s); + } +} +static void +tuntap_set_ptp(const struct tuntap *tt) +{ + DWORD len; + struct gc_arena gc = gc_new(); + + if (!tt->did_ifconfig_setup && !tt->did_ifconfig_ipv6_setup) + { + msg(M_FATAL, "ERROR: --dev tun also requires --ifconfig"); + } + + /* send 0/0/0 to the TAP driver even if we have no IPv4 configured to + * ensure it is somehow initialized. + */ + if (!tt->did_ifconfig_setup || tt->topology == TOP_SUBNET) + { + in_addr_t ep[3]; + BOOL status; + + ep[0] = htonl(tt->local); + ep[1] = htonl(tt->local & tt->remote_netmask); + ep[2] = htonl(tt->remote_netmask); + + status = DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_TUN, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL); + + if (tt->did_ifconfig_setup) + { msg(status ? M_INFO : M_FATAL, "Set TAP-Windows TUN subnet mode network/local/netmask = %s/%s/%s [%s]", print_in_addr_t(ep[1], IA_NET_ORDER, &gc), print_in_addr_t(ep[0], IA_NET_ORDER, &gc), print_in_addr_t(ep[2], IA_NET_ORDER, &gc), status ? "SUCCEEDED" : "FAILED"); - } else { + msg(status ? M_INFO : M_FATAL, "Set TAP-Windows TUN with fake IPv4 [%s]", + status ? "SUCCEEDED" : "FAILED"); + } + } + else + { + in_addr_t ep[2]; + ep[0] = htonl(tt->local); + ep[1] = htonl(tt->remote_netmask); - in_addr_t ep[2]; - ep[0] = htonl(tt->local); - ep[1] = htonl(tt->remote_netmask); - - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun"); - } + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL)) + { + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun"); } } - /* should we tell the TAP-Windows driver to masquerade as a DHCP server as a means - * of setting the adapter address? */ - if (dhcp_masq) - { - uint32_t ep[4]; + gc_free(&gc); +} - /* We will answer DHCP requests with a reply to set IP/subnet to these values */ - ep[0] = htonl(tt->local); - ep[1] = htonl(tt->adapter_netmask); +static void +tuntap_dhcp_mask(const struct tuntap *tt, const char *device_guid) +{ + struct gc_arena gc = gc_new(); + DWORD len; + uint32_t ep[4]; - /* At what IP address should the DHCP server masquerade at? */ - if (tt->type == DEV_TYPE_TUN) + /* We will answer DHCP requests with a reply to set IP/subnet to these values */ + ep[0] = htonl(tt->local); + ep[1] = htonl(tt->adapter_netmask); + + /* At what IP address should the DHCP server masquerade at? */ + if (tt->type == DEV_TYPE_TUN) + { + if (tt->topology == TOP_SUBNET) { - if (tt->topology == TOP_SUBNET) + if (tt->options.dhcp_masq_custom_offset) { - if (tt->options.dhcp_masq_custom_offset) - { - ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, tt->options.dhcp_masq_offset); - } - else - { - ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, -1); - } + ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, tt->options.dhcp_masq_offset); } else { - ep[2] = htonl(tt->remote_netmask); + ep[2] = dhcp_masq_addr(tt->local, tt->remote_netmask, -1); } } else { - ASSERT(tt->type == DEV_TYPE_TAP); - ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask, tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0); + ep[2] = htonl(tt->remote_netmask); } + } + else + { + ASSERT(tt->type == DEV_TYPE_TAP); + ep[2] = dhcp_masq_addr(tt->local, tt->adapter_netmask, tt->options.dhcp_masq_custom_offset ? tt->options.dhcp_masq_offset : 0); + } - /* lease time in seconds */ - ep[3] = (uint32_t) tt->options.dhcp_lease_time; + /* lease time in seconds */ + ep[3] = (uint32_t)tt->options.dhcp_lease_time; - ASSERT(ep[3] > 0); + ASSERT(ep[3] > 0); #ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad DHCP negotiation */ - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, - ep, sizeof(ep), - ep, sizeof(ep), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); - } + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_MASQ, + ep, sizeof(ep), + ep, sizeof(ep), &len, NULL)) + { + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a DeviceIoControl call to set TAP_WIN_IOCTL_CONFIG_DHCP_MASQ mode"); + } - msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid, - print_in_addr_t(ep[2], IA_NET_ORDER, &gc), - ep[3] - ); + msg(M_INFO, "Notified TAP-Windows driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]", + print_in_addr_t(tt->local, 0, &gc), + print_in_addr_t(tt->adapter_netmask, 0, &gc), + device_guid, + print_in_addr_t(ep[2], IA_NET_ORDER, &gc), + ep[3] + ); - /* user-supplied DHCP options capability */ - if (tt->options.dhcp_options) + /* user-supplied DHCP options capability */ + if (tt->options.dhcp_options) + { + struct buffer buf = alloc_buf(256); + if (build_dhcp_options_string(&buf, &tt->options)) { - struct buffer buf = alloc_buf(256); - if (build_dhcp_options_string(&buf, &tt->options)) + msg(D_DHCP_OPT, "DHCP option string: %s", format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); + if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, + BPTR(&buf), BLEN(&buf), + BPTR(&buf), BLEN(&buf), &len, NULL)) { - msg(D_DHCP_OPT, "DHCP option string: %s", format_hex(BPTR(&buf), BLEN(&buf), 0, &gc)); - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT, - BPTR(&buf), BLEN(&buf), - BPTR(&buf), BLEN(&buf), &len, NULL)) - { - msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); - } - } - else - { - msg(M_WARN, "DHCP option string not set due to error"); + msg(M_FATAL, "ERROR: The TAP-Windows driver rejected a TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call"); } - free_buf(&buf); } -#endif /* ifndef SIMULATE_DHCP_FAILED */ + else + { + msg(M_WARN, "DHCP option string not set due to error"); + } + free_buf(&buf); } +#endif /* ifndef SIMULATE_DHCP_FAILED */ - /* set driver media status to 'connected' */ + gc_free(&gc); +} + +static bool +tun_try_open_device(struct tuntap *tt, const char *device_guid, const struct device_instance_id_interface *device_instance_id_interface) +{ + const char *path = NULL; + char tuntap_device_path[256]; + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) { - ULONG status = TRUE; - if (!DeviceIoControl(tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, - &status, sizeof(status), - &status, sizeof(status), &len, NULL)) + const struct device_instance_id_interface *dev_if; + + /* Open Wintun adapter */ + for (dev_if = device_instance_id_interface; dev_if != NULL; dev_if = dev_if->next) { - msg(M_WARN, "WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); + if (strcmp(dev_if->net_cfg_instance_id, device_guid) == 0) + { + path = dev_if->device_interface_list; + break; + } } + if (path == NULL) + { + return false; + } + } + else + { + /* Open TAP-Windows adapter */ + openvpn_snprintf(tuntap_device_path, sizeof(tuntap_device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAP_WIN_SUFFIX); + path = tuntap_device_path; } - /* possible wait for adapter to come up */ + tt->hand = CreateFile(path, + GENERIC_READ | GENERIC_WRITE, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0); + if (tt->hand == INVALID_HANDLE_VALUE) { - int s = tt->options.tap_sleep; - if (s > 0) + msg(D_TUNTAP_INFO, "CreateFile failed on %s device: %s", print_windows_driver(tt->windows_driver), path); + return false; + } + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + /* Wintun adapter may be considered "open" after ring buffers are successfuly registered. */ + if (!wintun_register_ring_buffer(tt, device_guid)) { - msg(M_INFO, "Sleeping for %d seconds...", s); - management_sleep(s); + msg(D_TUNTAP_INFO, "Failed to register %s adapter ring buffers", device_guid); + CloseHandle(tt->hand); + tt->hand = NULL; + return false; } } - /* possibly use IP Helper API to set IP address on adapter */ + return true; +} + +static 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); + 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); + char actual_buffer[256]; + + at_least_one_tap_win(tap_reg); + + /* + * Lookup the device name in the registry, using the --dev-node high level name. + */ + if (dev_node) { - const DWORD index = tt->adapter_index; + enum windows_driver_type windows_driver = WINDOWS_DRIVER_UNSPECIFIED; + + /* Get the device GUID for the device specified with --dev-node. */ + *device_guid = get_device_guid(dev_node, actual_buffer, sizeof(actual_buffer), &windows_driver, tap_reg, panel_reg, gc); - /* flush arp cache */ - if (index != TUN_ADAPTER_INDEX_INVALID) + if (!*device_guid) { - DWORD status = -1; + msg(M_FATAL, "Adapter '%s' not found", dev_node); + } - if (tt->options.msg_channel) - { - ack_message_t ack; - flush_neighbors_message_t msg = { - .header = { - msg_flush_neighbors, - sizeof(flush_neighbors_message_t), - 0 - }, - .family = AF_INET, - .iface = { .index = index, .name = "" } - }; - - if (!WriteFile(tt->options.msg_channel, &msg, sizeof(msg), &len, NULL) - || !ReadFile(tt->options.msg_channel, &ack, sizeof(ack), &len, NULL)) - { - msg(M_WARN, "TUN: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); - } + if (tt->windows_driver != windows_driver) + { + msg(M_FATAL, "Adapter '%s' is using %s driver, %s expected. If you want to use this device, adjust --windows-driver.", + dev_node, print_windows_driver(windows_driver), print_windows_driver(tt->windows_driver)); + } - status = ack.error_number; - } - else + if (!tun_try_open_device(tt, *device_guid, device_instance_id_interface)) + { + msg(M_FATAL, "Failed to open %s adapter: %s", print_windows_driver(tt->windows_driver), dev_node); + } + } + else + { + int device_number = 0; + + /* Try opening all TAP devices until we find one available */ + while (true) + { + enum windows_driver_type windows_driver = WINDOWS_DRIVER_UNSPECIFIED; + *device_guid = get_unspecified_device_guid(device_number, + actual_buffer, + sizeof(actual_buffer), + tap_reg, + panel_reg, + &windows_driver, + gc); + + if (!*device_guid) { - status = FlushIpNetTable(index); + msg(M_FATAL, "All %s adapters on this system are currently in use.", print_windows_driver(tt->windows_driver)); } - if (status == NO_ERROR) + if (tt->windows_driver != windows_driver) { - msg(M_INFO, "Successful ARP Flush on interface [%u] %s", - (unsigned int)index, - device_guid); + goto next; } - else if (status != -1) + + if (tun_try_open_device(tt, *device_guid, device_instance_id_interface)) { - msg(D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s", - (unsigned int)index, - device_guid, - (unsigned int)status, - strerror_win32(status, &gc)); + break; } + +next: + device_number++; } + } + + /* translate high-level device name into a device instance + * GUID using the registry */ + tt->actual_name = string_alloc(actual_buffer, NULL); + msg(M_INFO, "%s device [%s] opened", print_windows_driver(tt->windows_driver), tt->actual_name); + tt->adapter_index = get_adapter_index(*device_guid); +} + +static void +tuntap_set_ip_props(const struct tuntap *tt, bool *dhcp_masq, bool *dhcp_masq_post) +{ + if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ) + { /* - * If the TAP-Windows driver is masquerading as a DHCP server - * make sure the TCP/IP properties for the adapter are - * set correctly. + * If adapter is set to non-DHCP, set to DHCP mode. */ - if (dhcp_masq_post) + if (dhcp_status(tt->adapter_index) == DHCP_STATUS_DISABLED) { - /* check dhcp enable status */ - if (dhcp_status(index) == DHCP_STATUS_DISABLED) - { - msg(M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); - } - - /* force an explicit DHCP lease renewal on TAP adapter? */ - if (tt->options.dhcp_pre_release) + /* try using the service if available, else directly execute netsh */ + if (tt->options.msg_channel) { - dhcp_release(tt); + service_enable_dhcp(tt); } - if (tt->options.dhcp_renew) + else { - dhcp_renew(tt); + netsh_enable_dhcp(tt->actual_name); } } + *dhcp_masq = true; + *dhcp_masq_post = true; + } + else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + { + /* + * If adapter is set to non-DHCP, use netsh right away. + */ + if (dhcp_status(tt->adapter_index) != DHCP_STATUS_ENABLED) + { + netsh_ifconfig(&tt->options, + tt->actual_name, + tt->local, + tt->adapter_netmask, + NI_TEST_FIRST | NI_IP_NETMASK | NI_OPTIONS); + } else { - fork_dhcp_action(tt); + *dhcp_masq = true; } + } +} - if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI) - { - DWORD status; - const char *error_suffix = "I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi')"; +static void +tuntap_post_open(struct tuntap *tt, const char *device_guid) +{ + bool dhcp_masq = false; + bool dhcp_masq_post = false; - /* couldn't get adapter index */ - if (index == TUN_ADAPTER_INDEX_INVALID) - { - msg(M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s", - device_guid, - error_suffix); - } + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6) + { + /* get driver version info */ + tuntap_get_version_info(tt); - /* check dhcp enable status */ - if (dhcp_status(index) == DHCP_STATUS_DISABLED) - { - msg(M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Windows TCP/IP properties are set to 'Obtain an IP address automatically'"); - } + /* get driver MTU */ + tuntap_get_mtu(tt); - /* delete previously added IP addresses which were not - * correctly deleted */ - delete_temp_addresses(index); + /* + * Preliminaries for setting TAP-Windows adapter TCP/IP + * properties via --ip-win32 dynamic or --ip-win32 adaptive. + */ + if (tt->did_ifconfig_setup) + { + tuntap_set_ip_props(tt, &dhcp_masq, &dhcp_masq_post); + } - /* add a new IP address */ - if ((status = AddIPAddress(htonl(tt->local), - htonl(tt->adapter_netmask), - index, - &tt->ipapi_context, - &tt->ipapi_instance)) == NO_ERROR) - { - msg(M_INFO, "Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid - ); - } - else - { - msg(M_FATAL, "ERROR: AddIPAddress %s/%s failed on interface %s, index=%d, status=%u (windows error: '%s') -- %s", - print_in_addr_t(tt->local, 0, &gc), - print_in_addr_t(tt->adapter_netmask, 0, &gc), - device_guid, - (int)index, - (unsigned int)status, - strerror_win32(status, &gc), - error_suffix); - } - tt->ipapi_context_defined = true; + /* set point-to-point mode if TUN device */ + if (tt->type == DEV_TYPE_TUN) + { + tuntap_set_ptp(tt); } + + /* should we tell the TAP-Windows driver to masquerade as a DHCP server as a means + * of setting the adapter address? */ + if (dhcp_masq) + { + tuntap_dhcp_mask(tt, device_guid); + } + + /* set driver media status to 'connected' */ + tuntap_set_connected(tt); } - /*netcmd_semaphore_release ();*/ + + /* possibly use IP Helper API to set IP address on adapter */ + tuntap_set_ip_addr(tt, device_guid, dhcp_masq_post); +} + +void +open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ + const char *device_guid = NULL; + + /*netcmd_semaphore_lock ();*/ + + msg( M_INFO, "open_tun"); + + if (tt->type == DEV_TYPE_NULL) + { + open_null(tt); + return; + } + else if (tt->type != DEV_TYPE_TAP && tt->type != DEV_TYPE_TUN) + { + msg(M_FATAL|M_NOPREFIX, "Unknown virtual device type: '%s'", dev); + } + + struct gc_arena gc = gc_new(); /* used also for device_guid allocation */ + tun_open_device(tt, dev_node, &device_guid, &gc); + + tuntap_post_open(tt, device_guid); + gc_free(&gc); + + /*netcmd_semaphore_release ();*/ } const char * tap_win_getinfo(const struct tuntap *tt, struct gc_arena *gc) { - if (tt && tt->hand != NULL) + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6) { struct buffer out = alloc_buf_gc(256, gc); DWORD len; @@ -6226,7 +6606,7 @@ tap_win_getinfo(const struct tuntap *tt, struct gc_arena *gc) void tun_show_debug(struct tuntap *tt) { - if (tt && tt->hand != NULL) + if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6) { struct buffer out = alloc_buf(1024); DWORD len; @@ -6241,107 +6621,160 @@ tun_show_debug(struct tuntap *tt) } } -void -close_tun(struct tuntap *tt) +static void +netsh_delete_address_dns(const struct tuntap *tt, bool ipv6, struct gc_arena *gc) { - struct gc_arena gc = gc_new(); + const char *ifconfig_ip_local; + struct argv argv = argv_new(); - if (tt) + /* delete ipvX dns servers if any were set */ + int len = ipv6 ? tt->options.dns6_len : tt->options.dns_len; + if (len > 0) { - if (tt->did_ifconfig_ipv6_setup) - { - /* remove route pointing to interface */ - delete_route_connected_v6_net(tt, NULL); + argv_printf(&argv, + "%s%s interface %s delete dns %s all", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + ipv6 ? "ipv6" : "ipv4", + tt->actual_name); + netsh_command(&argv, 1, M_WARN); + } - if (tt->options.msg_channel) - { - do_address_service(false, AF_INET6, tt); - if (tt->options.dns6_len > 0) - { - do_dns6_service(false, tt); - } - } - else - { - const char *ifconfig_ipv6_local; - struct argv argv = argv_new(); + if (ipv6) + { + delete_route_connected_v6_net(tt); + } - /* "store=active" is needed in Windows 8(.1) to delete the - * address we added (pointed out by Cedric Tabary). - */ + /* "store=active" is needed in Windows 8(.1) to delete the + * address we added (pointed out by Cedric Tabary). + */ - /* netsh interface ipv6 delete address \"%s\" %s */ - ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc); - argv_printf(&argv, - "%s%sc interface ipv6 delete address %s %s store=active", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - tt->actual_name, - ifconfig_ipv6_local); + /* netsh interface ipvX delete address \"%s\" %s */ + if (ipv6) + { + ifconfig_ip_local = print_in6_addr(tt->local_ipv6, 0, gc); + } + else + { + ifconfig_ip_local = print_in_addr_t(tt->local, 0, gc); + } + argv_printf(&argv, + "%s%s interface %s delete address %s %s store=active", + get_win_sys_path(), + NETSH_PATH_SUFFIX, + ipv6 ? "ipv6" : "ipv4", + tt->actual_name, + ifconfig_ip_local); + netsh_command(&argv, 1, M_WARN); - netsh_command(&argv, 1, M_WARN); + argv_free(&argv); +} - /* delete ipv6 dns servers if any were set */ - if (tt->options.dns6_len > 0) - { - argv_printf(&argv, - "%s%sc interface ipv6 delete dns %s all", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - tt->actual_name); - netsh_command(&argv, 1, M_WARN); - } - argv_reset(&argv); - } +void +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) +{ + ASSERT(tt); + + struct gc_arena gc = gc_new(); + + if (tt->did_ifconfig_ipv6_setup) + { + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) + { + /* We didn't do ifconfig. */ } -#if 1 - if (tt->ipapi_context_defined) + else if (tt->options.msg_channel) { - DWORD status; - if ((status = DeleteIPAddress(tt->ipapi_context)) != NO_ERROR) + if (tt->options.dns6_len > 0) { - msg(M_WARN, "Warning: DeleteIPAddress[%u] failed on TAP-Windows adapter, status=%u : %s", - (unsigned int)tt->ipapi_context, - (unsigned int)status, - strerror_win32(status, &gc)); + do_dns_service(false, AF_INET6, tt); } + delete_route_connected_v6_net(tt); + do_address_service(false, AF_INET6, tt); } -#endif - - dhcp_release(tt); + else + { + netsh_delete_address_dns(tt, true, &gc); + } + } - if (tt->hand != NULL) + if (tt->did_ifconfig_setup) + { + if (tt->options.ip_win32_type == IPW32_SET_MANUAL) { - dmsg(D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Windows adapter"); - if (!CancelIo(tt->hand)) - { - msg(M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Windows adapter"); - } + /* We didn't do ifconfig. */ } + else if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ || tt->options.ip_win32_type == IPW32_SET_ADAPTIVE) + { + /* We don't have to clean the configuration with DHCP. */ + } + else if (tt->options.msg_channel) + { + do_dns_service(false, AF_INET, tt); + do_address_service(false, AF_INET, tt); + } + else if (tt->options.ip_win32_type == IPW32_SET_NETSH) + { + netsh_delete_address_dns(tt, false, &gc); + } + } - dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Windows adapter"); - overlapped_io_close(&tt->reads); + if (tt->ipapi_context_defined) + { + DWORD status; + if ((status = DeleteIPAddress(tt->ipapi_context)) != NO_ERROR) + { + msg(M_WARN, "Warning: DeleteIPAddress[%u] failed on TAP-Windows adapter, status=%u : %s", + (unsigned int)tt->ipapi_context, + (unsigned int)status, + strerror_win32(status, &gc)); + } + } - dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Windows adapter"); - overlapped_io_close(&tt->writes); + dhcp_release(tt); - if (tt->hand != NULL) + if (tt->hand != NULL) + { + dmsg(D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Windows adapter"); + if (!CancelIo(tt->hand)) { - dmsg(D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Windows adapter"); - if (!CloseHandle(tt->hand)) - { - msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Windows adapter"); - } + msg(M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Windows adapter"); } + } - if (tt->actual_name) + dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Windows adapter"); + overlapped_io_close(&tt->reads); + + dmsg(D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Windows adapter"); + overlapped_io_close(&tt->writes); + + if (tt->hand != NULL) + { + dmsg(D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Windows adapter"); + if (!CloseHandle(tt->hand)) { - free(tt->actual_name); + msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Windows adapter"); } + } - clear_tuntap(tt); - free(tt); + if (tt->actual_name) + { + free(tt->actual_name); + } + + if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) + { + CloseHandle(tt->rw_handle.read); + CloseHandle(tt->rw_handle.write); + UnmapViewOfFile(tt->wintun_send_ring); + UnmapViewOfFile(tt->wintun_receive_ring); + CloseHandle(tt->wintun_send_ring_handle); + CloseHandle(tt->wintun_receive_ring_handle); } + + + clear_tuntap(tt); + free(tt); gc_free(&gc); } @@ -6418,13 +6851,12 @@ open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tun } void -close_tun(struct tuntap *tt) +close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx) { - if (tt) - { - close_tun_generic(tt); - free(tt); - } + ASSERT(tt); + + close_tun_generic(tt); + free(tt); } int |