diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-11-21 09:37:33 +0100 |
commit | 93b77cacdbb7e6f310c4e20f85c3a24ed5ba18ba (patch) | |
tree | 55a7688c9969ef4d01625caa58c7f679098c76eb /src/openvpn/tun.c | |
parent | daa9ef0efeb5e10a1b43820fbab3a4ff5fbd22f1 (diff) | |
parent | 20c8675ba46bda97330a4117c459a59a9f1c465e (diff) |
Merge tag 'upstream/2.4_beta1'
Upstream version 2.4~beta1
Diffstat (limited to 'src/openvpn/tun.c')
-rw-r--r-- | src/openvpn/tun.c | 830 |
1 files changed, 562 insertions, 268 deletions
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index b70410d..77ae72f 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -49,7 +49,13 @@ #include "memdbg.h" -#ifdef WIN32 +#ifdef _WIN32 +#include "openvpn-msg.h" +#endif + +#include <string.h> + +#ifdef _WIN32 /* #define SIMULATE_DHCP_FAILED */ /* simulate bad DHCP negotiation */ @@ -62,12 +68,70 @@ static void netsh_ifconfig (const struct tuntap_options *to, const in_addr_t ip, const in_addr_t netmask, const unsigned int flags); -static void netsh_command (const struct argv *a, int n); +static void netsh_command (const struct argv *a, int n, int msglevel); static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc); 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 (); + HANDLE pipe = tt->options.msg_channel; + + address_message_t addr = { + .header = { + (add ? msg_add_address : msg_del_address), + sizeof (address_message_t), + 0 }, + .family = family, + .iface = { .index = tt->adapter_index, .name = "" } + }; + + if (addr.iface.index == TUN_ADAPTER_INDEX_INVALID) + { + strncpy (addr.iface.name, tt->actual_name, sizeof (addr.iface.name)); + addr.iface.name[sizeof (addr.iface.name) - 1] = '\0'; + } + + if (addr.family == AF_INET) + { + addr.address.ipv4.s_addr = tt->local; + addr.prefix_len = 32; + } + else + { + addr.address.ipv6 = tt->local_ipv6; + addr.prefix_len = tt->netbits_ipv6; + } + + if (!WriteFile (pipe, &addr, sizeof (addr), &len, NULL) || + !ReadFile (pipe, &ack, sizeof (ack), &len, NULL)) + { + 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 address failed using service: %s [status=%u if_index=%lu]", + (add ? "adding" : "deleting"), strerror_win32 (ack.error_number, &gc), + ack.error_number, addr.iface.index); + goto out; + } + + ret = true; + +out: + gc_free (&gc); + return ret; +} + #endif #ifdef TARGET_SOLARIS @@ -134,7 +198,7 @@ guess_tuntap_dev (const char *dev, const char *dev_node, struct gc_arena *gc) { -#ifdef WIN32 +#ifdef _WIN32 const int dt = dev_type_enum (dev, dev_type); if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP) { @@ -357,7 +421,7 @@ tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc) { buf_printf (&out, "T%s", (tt->rwflags_debug & EVENT_READ) ? "R" : "r"); -#ifdef WIN32 +#ifdef _WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&tt->reads)); #endif @@ -366,7 +430,7 @@ tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc) { buf_printf (&out, "T%s", (tt->rwflags_debug & EVENT_WRITE) ? "W" : "w"); -#ifdef WIN32 +#ifdef _WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&tt->writes)); #endif @@ -455,8 +519,8 @@ init_tun (const char *dev, /* --dev option */ const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 IPv6 */ int ifconfig_ipv6_netbits_parm, const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 IPv6 */ - in_addr_t local_public, - in_addr_t remote_public, + struct addrinfo *local_public, + struct addrinfo *remote_public, const bool strict_warn, struct env_set *es) { @@ -507,6 +571,7 @@ init_tun (const char *dev, /* --dev option */ */ if (strict_warn) { + struct addrinfo *curele; ifconfig_sanity_check (tt->type == DEV_TYPE_TUN, tt->remote_netmask, tt->topology); /* @@ -514,17 +579,23 @@ init_tun (const char *dev, /* --dev option */ * make sure they do not clash with our virtual subnet. */ - check_addr_clash ("local", + for(curele=local_public;curele;curele=curele->ai_next) { + if(curele->ai_family == AF_INET) + check_addr_clash ("local", tt->type, - local_public, + ((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr, tt->local, tt->remote_netmask); + } - check_addr_clash ("remote", - tt->type, - remote_public, - tt->local, - tt->remote_netmask); + for (curele=remote_public;curele;curele=curele->ai_next) { + if (curele->ai_family == AF_INET) + check_addr_clash ("remote", + tt->type, + ((struct sockaddr_in*)curele->ai_addr)->sin_addr.s_addr, + tt->local, + tt->remote_netmask); + } if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET)) check_subnet_conflict (tt->local, tt->remote_netmask, "TUN/TAP adapter"); @@ -540,6 +611,21 @@ init_tun (const char *dev, /* --dev option */ tt->broadcast = generate_ifconfig_broadcast_addr (tt->local, tt->remote_netmask); } +#ifdef _WIN32 + /* + * Make sure that both ifconfig addresses are part of the + * same .252 subnet. + */ + if (tun) + { + verify_255_255_255_252 (tt->local, tt->remote_netmask); + tt->adapter_netmask = ~3; + } + else + { + tt->adapter_netmask = tt->remote_netmask; + } +#endif tt->did_ifconfig_setup = true; } @@ -579,7 +665,7 @@ init_tun_post (struct tuntap *tt, const struct tuntap_options *options) { tt->options = *options; -#ifdef WIN32 +#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; @@ -588,7 +674,7 @@ init_tun_post (struct tuntap *tt, #endif } -#if defined(WIN32) || \ +#if defined(_WIN32) || \ defined(TARGET_DARWIN) || defined(TARGET_NETBSD) || defined(TARGET_OPENBSD) /* some of the platforms will auto-add a "network route" pointing @@ -601,12 +687,12 @@ void add_route_connected_v6_net(struct tuntap * tt, { struct route_ipv6 r6; - r6.defined = true; + CLEAR(r6); r6.network = tt->local_ipv6; r6.netbits = tt->netbits_ipv6; r6.gateway = tt->local_ipv6; r6.metric = 0; /* connected route */ - r6.metric_defined = true; + r6.flags = RT_DEFINED | RT_METRIC_DEFINED; add_route_ipv6 (&r6, tt, 0, es); } @@ -615,17 +701,18 @@ void delete_route_connected_v6_net(struct tuntap * tt, { struct route_ipv6 r6; - r6.defined = true; + CLEAR(r6); r6.network = tt->local_ipv6; r6.netbits = tt->netbits_ipv6; r6.gateway = tt->local_ipv6; r6.metric = 0; /* connected route */ - r6.metric_defined = true; + r6.flags = RT_DEFINED | RT_ADDED | RT_METRIC_DEFINED; delete_route_ipv6 (&r6, tt, 0, es); } #endif -#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) +#if defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)||\ + defined(TARGET_OPENBSD) /* we can't use true subnet mode on tun on all platforms, as that * conflicts with IPv6 (wants to use ND then, which we don't do), * but the OSes want "a remote address that is different from ours" @@ -635,8 +722,8 @@ void delete_route_connected_v6_net(struct tuntap * tt, * is still point to point and no layer 2 resolution is done... */ -const char * -create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) +in_addr_t +create_arbitrary_remote( struct tuntap *tt ) { in_addr_t remote; @@ -644,7 +731,7 @@ create_arbitrary_remote( struct tuntap *tt, struct gc_arena * gc ) if ( remote == tt->local ) remote ++; - return print_in_addr_t (remote, 0, gc); + return remote; } #endif @@ -664,14 +751,11 @@ do_ifconfig (struct tuntap *tt, const char *ifconfig_remote_netmask = NULL; const char *ifconfig_broadcast = NULL; const char *ifconfig_ipv6_local = NULL; - const char *ifconfig_ipv6_remote = NULL; bool do_ipv6 = false; - struct argv argv; - - argv_init (&argv); + struct argv argv = argv_new (); - msg( M_INFO, "do_ifconfig, tt->ipv6=%d, tt->did_ifconfig_ipv6_setup=%d", - tt->ipv6, tt->did_ifconfig_ipv6_setup ); + msg( M_DEBUG, "do_ifconfig, tt->did_ifconfig_ipv6_setup=%d", + tt->did_ifconfig_ipv6_setup ); /* * We only handle TUN/TAP devices here, not --dev null devices. @@ -684,10 +768,9 @@ do_ifconfig (struct tuntap *tt, ifconfig_local = print_in_addr_t (tt->local, 0, &gc); ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if (tt->did_ifconfig_ipv6_setup ) { ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); - ifconfig_ipv6_remote = print_in6_addr (tt->remote_ipv6, 0, &gc); do_ipv6 = true; } @@ -703,8 +786,10 @@ do_ifconfig (struct tuntap *tt, management_set_state (management, OPENVPN_STATE_ASSIGN_IP, NULL, - tt->local, - 0); + &tt->local, + &tt->local_ipv6, + NULL, + NULL); } #endif @@ -743,7 +828,7 @@ do_ifconfig (struct tuntap *tt, iproute_path, actual, ifconfig_local, - count_netmask_bits(ifconfig_remote_netmask), + netmask_to_netbits2(tt->remote_netmask), ifconfig_broadcast ); argv_msg (M_INFO, &argv); @@ -799,8 +884,35 @@ do_ifconfig (struct tuntap *tt, tt->did_ifconfig = true; #endif /*ENABLE_IPROUTE*/ -#elif defined(TARGET_SOLARIS) +#elif defined(TARGET_ANDROID) + + 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)); + } + + struct buffer out = alloc_buf_gc (64, &gc); + + char* top; + switch(tt->topology) { + case TOP_NET30: + top="net30"; + break; + case TOP_P2P: + top="p2p"; + break; + case TOP_SUBNET: + top="subnet"; + break; + default: + top="undef"; + } + + buf_printf (&out, "%s %s %d %s", ifconfig_local, ifconfig_remote_netmask, tun_mtu, top); + management_android_control (management, "IFCONFIG", buf_bptr(&out)); +#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 @@ -862,6 +974,9 @@ do_ifconfig (struct tuntap *tt, 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, @@ -916,6 +1031,8 @@ do_ifconfig (struct tuntap *tt, #elif defined(TARGET_OPENBSD) + in_addr_t remote_end; /* for "virtual" subnet topology */ + /* * On OpenBSD, tun interfaces are persistent if created with * "ifconfig tunX create", and auto-destroyed if created by @@ -935,12 +1052,13 @@ do_ifconfig (struct tuntap *tt, 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, actual, ifconfig_local, - ifconfig_local, + print_in_addr_t (remote_end, 0, &gc), tun_mtu, ifconfig_remote_netmask ); @@ -957,6 +1075,19 @@ do_ifconfig (struct tuntap *tt, ); argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed"); + + /* 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); + } + if ( do_ipv6 ) { argv_printf (&argv, @@ -976,13 +1107,6 @@ do_ifconfig (struct tuntap *tt, #elif defined(TARGET_NETBSD) -/* whether or not NetBSD can do IPv6 can be seen by the availability of - * the TUNSIFHEAD ioctl() - see next TARGET_NETBSD block for more details - */ -#ifdef TUNSIFHEAD -# define NETBSD_MULTI_AF -#endif - if (tun) argv_printf (&argv, "%s %s %s %s mtu %d netmask 255.255.255.255 up", @@ -1025,7 +1149,6 @@ do_ifconfig (struct tuntap *tt, if ( do_ipv6 ) { -#ifdef NETBSD_MULTI_AF argv_printf (&argv, "%s %s inet6 %s/%d", IFCONFIG_PATH, @@ -1038,10 +1161,6 @@ do_ifconfig (struct tuntap *tt, /* and, hooray, we explicitely need to add a route... */ add_route_connected_v6_net(tt, es); -#else - msg( M_INFO, "no IPv6 support for tun interfaces on NetBSD before 4.0 (if your system is newer, recompile openvpn)" ); - tt->ipv6 = false; -#endif } tt->did_ifconfig = true; @@ -1126,6 +1245,8 @@ do_ifconfig (struct tuntap *tt, #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY) + 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, @@ -1138,12 +1259,13 @@ do_ifconfig (struct tuntap *tt, ); 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, actual, ifconfig_local, - create_arbitrary_remote( tt, &gc ), + print_in_addr_t (remote_end, 0, &gc), tun_mtu, ifconfig_remote_netmask ); @@ -1170,7 +1292,7 @@ do_ifconfig (struct tuntap *tt, r.flags = RT_DEFINED; r.network = tt->local & tt->remote_netmask; r.netmask = tt->remote_netmask; - r.gateway = tt->local; + r.gateway = remote_end; add_route (&r, tt, 0, NULL, es); } @@ -1187,21 +1309,46 @@ do_ifconfig (struct tuntap *tt, openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig inet6 failed"); } -#elif defined (WIN32) +#elif defined(TARGET_AIX) { - /* - * Make sure that both ifconfig addresses are part of the - * same .252 subnet. - */ + /* 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) + msg(M_FATAL, "no tun support on AIX (canthappen)"); + + /* 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 ) { - verify_255_255_255_252 (tt->local, tt->remote_netmask); - tt->adapter_netmask = ~3; - } - else - { - tt->adapter_netmask = tt->remote_netmask; + 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); + } +#elif defined (_WIN32) + { + ASSERT (actual != NULL); switch (tt->options.ip_win32_type) { @@ -1212,9 +1359,6 @@ do_ifconfig (struct tuntap *tt, print_in_addr_t (tt->adapter_netmask, 0, &gc)); break; case IPW32_SET_NETSH: - if (!strcmp (actual, "NULL")) - msg (M_FATAL, "Error: When using --ip-win32 netsh, if you have more than one TAP-Windows adapter, you must also specify --dev-node"); - netsh_ifconfig (&tt->options, actual, tt->local, @@ -1226,39 +1370,28 @@ do_ifconfig (struct tuntap *tt, tt->did_ifconfig = true; } - /* IPv6 always uses "netsh" interface */ if ( do_ipv6 ) { - char * saved_actual; - char iface[64]; - DWORD idx; - - if (!strcmp (actual, "NULL")) - msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Windows adapter, you must also specify --dev-node"); - - idx = get_adapter_index_flexible(actual); - openvpn_snprintf(iface, sizeof(iface), "interface=%lu", idx); - - /* example: netsh interface ipv6 set address interface=42 2001:608:8003::d store=active */ - argv_printf (&argv, - "%s%sc interface ipv6 set address %s %s store=active", - get_win_sys_path(), - NETSH_PATH_SUFFIX, - win32_version_info() == WIN_XP ? actual : iface, - ifconfig_ipv6_local); - netsh_command (&argv, 4); + if (tt->options.msg_channel) + { + do_address_service (true, AF_INET6, 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); + } /* explicit route needed */ - /* on windows, OpenVPN does ifconfig first, open_tun later, so - * tt->actual_name might not yet be initialized, but routing code - * needs to know interface name - point to "actual", restore later - */ - saved_actual = tt->actual_name; - tt->actual_name = (char*) actual; - /* we use adapter_index in add_route_ipv6 */ - tt->adapter_index = idx; add_route_connected_v6_net(tt, es); - tt->actual_name = saved_actual; } #else 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."); @@ -1272,7 +1405,7 @@ static void clear_tuntap (struct tuntap *tuntap) { CLEAR (*tuntap); -#ifdef WIN32 +#ifdef _WIN32 tuntap->hand = NULL; #else tuntap->fd = -1; @@ -1280,7 +1413,6 @@ clear_tuntap (struct tuntap *tuntap) #ifdef TARGET_SOLARIS tuntap->ip_fd = -1; #endif - tuntap->ipv6 = false; } static void @@ -1333,7 +1465,7 @@ write_tun_header (struct tuntap* tt, uint8_t *buf, int len) iph = (struct ip *) buf; - if (tt->ipv6 && iph->ip_v == 6) + if (iph->ip_v == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -1370,20 +1502,15 @@ read_tun_header (struct tuntap* tt, uint8_t *buf, int len) #endif -#ifndef WIN32 +#if !(defined(_WIN32) || defined(TARGET_LINUX)) static void open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, - bool ipv6_explicitly_supported, bool dynamic, - struct tuntap *tt) + bool dynamic, struct tuntap *tt) { char tunname[256]; char dynamic_name[256]; bool dynamic_opened = false; - - if ( tt->ipv6 && ! ipv6_explicitly_supported ) - msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS"); - if (tt->type == DEV_TYPE_NULL) { open_null (tt); @@ -1477,7 +1604,9 @@ open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, tt->actual_name = string_alloc (dynamic_opened ? dynamic_name : dev, NULL); } } +#endif /* !_WIN32 && !TARGET_LINUX */ +#if !defined(_WIN32) static void close_tun_generic (struct tuntap *tt) { @@ -1487,12 +1616,83 @@ close_tun_generic (struct tuntap *tt) free (tt->actual_name); clear_tuntap (tt); } +#endif /* !_WIN32 */ -#endif +#if defined (TARGET_ANDROID) +void +open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ +#define ANDROID_TUNNAME "vpnservice-tun" + int i; + struct user_pass up; + struct gc_arena gc = gc_new (); + bool opentun; -#if defined(TARGET_LINUX) + int oldtunfd = tt->fd; + + for (i = 0; i < tt->options.dns_len; ++i) { + management_android_control (management, "DNSSERVER", + print_in_addr_t(tt->options.dns[i], 0, &gc)); + } -#ifdef HAVE_LINUX_IF_TUN_H /* New driver support */ + if(tt->options.domain) + management_android_control (management, "DNSDOMAIN", tt->options.domain); + + int android_method = managment_android_persisttun_action (management); + + /* Android 4.4 workaround */ + if (oldtunfd >=0 && android_method == ANDROID_OPEN_AFTER_CLOSE) + { + close(oldtunfd); + openvpn_sleep(2); + } + + if (oldtunfd >=0 && android_method == ANDROID_KEEP_OLD_TUN) { + /* keep the old fd */ + opentun = true; + } else { + opentun = management_android_control (management, "OPENTUN", dev); + /* Pick up the fd from management interface after calling the + * OPENTUN command */ + tt->fd = management->connection.lastfdreceived; + management->connection.lastfdreceived=-1; + } + + if (oldtunfd>=0 && android_method == ANDROID_OPEN_BEFORE_CLOSE) + close(oldtunfd); + + /* Set the actual name to a dummy name */ + tt->actual_name = string_alloc (ANDROID_TUNNAME, NULL); + + if ((tt->fd < 0) || !opentun) + msg (M_ERR, "ERROR: Cannot open TUN"); + + gc_free (&gc); +} + +void +close_tun (struct tuntap *tt) +{ + if (tt) + { + close_tun_generic (tt); + free (tt); + } +} + +int +write_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return write (tt->fd, buf, len); +} + +int +read_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return read (tt->fd, buf, len); +} + +#elif defined(TARGET_LINUX) #ifndef HAVE_LINUX_SOCKIOS_H #error header file linux/sockios.h required @@ -1533,8 +1733,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu * Process --tun-ipv6 */ CLEAR (ifr); - if (!tt->ipv6) - ifr.ifr_flags = IFF_NO_PI; + ifr.ifr_flags = IFF_NO_PI; #if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN) ifr.ifr_flags |= IFF_ONE_QUEUE; @@ -1615,32 +1814,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu ASSERT (0); } -#endif - -#else - -void -open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) -{ - open_tun_generic (dev, dev_type, dev_node, false, true, tt); -} - -#endif /* HAVE_LINUX_IF_TUN_H */ +#endif /* !PENDANTIC */ #ifdef ENABLE_FEATURE_TUN_PERSIST -/* - * This can be removed in future - * when all systems will use newer - * linux-headers - */ -#ifndef TUNSETOWNER -#define TUNSETOWNER _IOW('T', 204, int) -#endif -#ifndef TUNSETGROUP -#define TUNSETGROUP _IOW('T', 206, int) -#endif - 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) { @@ -1686,9 +1863,8 @@ close_tun (struct tuntap *tt) { if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig) { - struct argv argv; + struct argv argv = argv_new (); struct gc_arena gc = gc_new (); - argv_init (&argv); #ifdef ENABLE_IPROUTE if (is_tun_p2p (tt)) @@ -1708,7 +1884,7 @@ close_tun (struct tuntap *tt) iproute_path, tt->actual_name, print_in_addr_t (tt->local, 0, &gc), - count_netmask_bits(print_in_addr_t (tt->remote_netmask, 0, &gc)) + netmask_to_netbits2(tt->remote_netmask) ); } #else @@ -1722,7 +1898,7 @@ close_tun (struct tuntap *tt) argv_msg (M_INFO, &argv); openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed"); - if (tt->ipv6 && tt->did_ifconfig_ipv6_setup) + if (tt->did_ifconfig_ipv6_setup) { const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); @@ -1759,53 +1935,13 @@ close_tun (struct tuntap *tt) int write_tun (struct tuntap* tt, uint8_t *buf, int len) { - if (tt->ipv6) - { - struct tun_pi pi; - struct iphdr *iph; - struct iovec vect[2]; - int ret; - - iph = (struct iphdr *)buf; - - pi.flags = 0; - - if(iph->version == 6) - pi.proto = htons(OPENVPN_ETH_P_IPV6); - else - pi.proto = htons(OPENVPN_ETH_P_IPV4); - - vect[0].iov_len = sizeof(pi); - vect[0].iov_base = π - vect[1].iov_len = len; - vect[1].iov_base = buf; - - ret = writev(tt->fd, vect, 2); - return(ret - sizeof(pi)); - } - else - return write (tt->fd, buf, len); + return write (tt->fd, buf, len); } int read_tun (struct tuntap* tt, uint8_t *buf, int len) { - if (tt->ipv6) - { - struct iovec vect[2]; - struct tun_pi pi; - int ret; - - vect[0].iov_len = sizeof(pi); - vect[0].iov_base = π - vect[1].iov_len = len; - vect[1].iov_base = buf; - - ret = readv(tt->fd, vect, 2); - return(ret - sizeof(pi)); - } - else - return read (tt->fd, buf, len); + return read (tt->fd, buf, len); } #elif defined(TARGET_SOLARIS) @@ -2009,10 +2145,9 @@ solaris_close_tun (struct tuntap *tt) if (tt) { /* IPv6 interfaces need to be 'manually' de-configured */ - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if ( tt->did_ifconfig_ipv6_setup ) { - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); argv_printf( &argv, "%s %s inet6 unplumb", IFCONFIG_PATH, tt->actual_name ); argv_msg (M_INFO, &argv); @@ -2075,8 +2210,7 @@ static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual, bool unplumb_inet6 ) { - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); if (unplumb_inet6) { @@ -2123,7 +2257,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); /* Enable multicast on the interface */ if (tt->fd >= 0) @@ -2168,12 +2302,11 @@ close_tun (struct tuntap* tt) else if (tt) { struct gc_arena gc = gc_new (); - struct argv argv; + struct argv argv = argv_new (); /* setup command, close tun dev (clears tt->actual_name!), run command */ - argv_init (&argv); argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, tt->actual_name); @@ -2217,11 +2350,7 @@ read_tun (struct tuntap *tt, uint8_t *buf, int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { -#ifdef NETBSD_MULTI_AF - open_tun_generic (dev, dev_type, dev_node, true, true, tt); -#else - open_tun_generic (dev, dev_type, dev_node, false, true, tt); -#endif + open_tun_generic (dev, dev_type, dev_node, true, tt); if (tt->fd >= 0) { @@ -2230,7 +2359,6 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu i = 0; ioctl (tt->fd, TUNSLMODE, &i); /* link layer mode off */ -#ifdef NETBSD_MULTI_AF if ( tt->type == DEV_TYPE_TUN ) { i = 1; @@ -2239,7 +2367,6 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno)); } } -#endif } } @@ -2260,12 +2387,11 @@ close_tun (struct tuntap *tt) else if (tt) { struct gc_arena gc = gc_new (); - struct argv argv; + struct argv argv = argv_new (); /* setup command, close tun dev (clears tt->actual_name!), run command */ - argv_init (&argv); argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, tt->actual_name); @@ -2278,8 +2404,6 @@ close_tun (struct tuntap *tt) } } -#ifdef NETBSD_MULTI_AF - static inline int netbsd_modify_read_write_return (int len) { @@ -2300,7 +2424,7 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) iph = (struct openvpn_iphdr *) buf; - if (tt->ipv6 && OPENVPN_IPH_GET_VER(iph->version_len) == 6) + if (OPENVPN_IPH_GET_VER(iph->version_len) == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -2335,21 +2459,6 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) return read (tt->fd, buf, len); } -#else /* not NETBSD_MULTI_AF -> older code, IPv4 only */ - -int -write_tun (struct tuntap* tt, uint8_t *buf, int len) -{ - return write (tt->fd, buf, len); -} - -int -read_tun (struct tuntap* tt, uint8_t *buf, int len) -{ - return read (tt->fd, buf, len); -} -#endif /* NETBSD_MULTI_AF */ - #elif defined(TARGET_FREEBSD) static inline int @@ -2364,7 +2473,7 @@ freebsd_modify_read_write_return (int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN) { @@ -2397,12 +2506,11 @@ close_tun (struct tuntap *tt) } else if (tt) /* close and destroy */ { - struct argv argv; + struct argv argv = argv_new (); /* setup command, close tun dev (clears tt->actual_name!), run command */ - argv_init (&argv); argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, tt->actual_name); @@ -2426,7 +2534,7 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) iph = (struct ip *) buf; - if (tt->ipv6 && iph->ip_v == 6) + if (iph->ip_v == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -2475,7 +2583,7 @@ dragonfly_modify_read_write_return (int len) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); if (tt->fd >= 0) { @@ -2509,7 +2617,7 @@ write_tun (struct tuntap* tt, uint8_t *buf, int len) iph = (struct ip *) buf; - if (tt->ipv6 && iph->ip_v == 6) + if (iph->ip_v == 6) type = htonl (AF_INET6); else type = htonl (AF_INET); @@ -2702,7 +2810,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu { /* No explicit utun and utun failed, try the generic way) */ msg (M_INFO, "Failed to open utun device. Falling back to /dev/tun device"); - open_tun_generic (dev, dev_type, NULL, true, true, tt); + open_tun_generic (dev, dev_type, NULL, true, tt); } else { @@ -2723,7 +2831,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu if (dev_node && strcmp (dev_node, "tun")==0) dev_node=NULL; - open_tun_generic (dev, dev_type, dev_node, true, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); } } @@ -2733,10 +2841,9 @@ close_tun (struct tuntap* tt) if (tt) { struct gc_arena gc = gc_new (); - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if (tt->did_ifconfig_ipv6_setup ) { const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc); @@ -2776,7 +2883,137 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) return read (tt->fd, buf, len); } -#elif defined(WIN32) +#elif defined(TARGET_AIX) + +void +open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ + char tunname[256]; + char dynamic_name[20]; + const char *p; + + if (tt->type == DEV_TYPE_NULL) + { + open_null (tt); + return; + } + + if ( tt->type == DEV_TYPE_TUN) + { + msg(M_FATAL, "no support for 'tun' devices on AIX" ); + } + + if ( strncmp( dev, "tap", 3 ) != 0 || dev_node ) + { + msg(M_FATAL, "'--dev %s' and/or '--dev-node' not supported on AIX, use '--dev tap0', 'tap1', etc.", dev ); + } + + if ( strcmp( dev, "tap" ) == 0 ) /* find first free tap dev */ + { /* (= no /dev/tapN node) */ + int i; + for (i=0; i<99; i++ ) + { + openvpn_snprintf (tunname, sizeof (tunname), "/dev/tap%d", i); + if ( access( tunname, F_OK ) < 0 && errno == ENOENT ) + { break; } + } + if ( i >= 99 ) + msg( M_FATAL, "cannot find unused tap device" ); + + openvpn_snprintf( dynamic_name, sizeof(dynamic_name), "tap%d", i ); + dev = dynamic_name; + } + else /* name given, sanity check */ + { + /* ensure that dev name is "tap+<digits>" *only* */ + p = &dev[3]; + while( isdigit(*p) ) p++; + if ( *p != '\0' ) + msg( M_FATAL, "TAP device name must be '--dev tapNNNN'" ); + + openvpn_snprintf (tunname, sizeof (tunname), "/dev/%s", dev); + } + + /* pre-existing device? + */ + if ( access( tunname, F_OK ) < 0 && errno == ENOENT ) + { + + /* tunnel device must be created with 'ifconfig tapN create' + */ + struct argv argv = argv_new (); + struct env_set *es = env_set_create (NULL); + argv_printf (&argv, "%s %s create", IFCONFIG_PATH, dev); + argv_msg (M_INFO, &argv); + env_set_add( es, "ODMDIR=/etc/objrepos" ); + openvpn_execve_check (&argv, es, S_FATAL, "AIX 'create tun interface' failed"); + env_set_destroy (es); + } + else + { + /* we didn't make it, we're not going to break it */ + tt->persistent_if = TRUE; + } + + if ((tt->fd = open (tunname, O_RDWR)) < 0) + { + msg (M_ERR, "Cannot open TAP device '%s'", tunname); + } + + set_nonblock (tt->fd); + set_cloexec (tt->fd); /* don't pass fd to scripts */ + msg (M_INFO, "TUN/TAP device %s opened", tunname); + + /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */ + tt->actual_name = string_alloc(dev, NULL); +} + +/* tap devices need to be manually destroyed on AIX + */ +void +close_tun (struct tuntap* tt) +{ + struct gc_arena gc = gc_new (); + 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) + { + argv_printf (&argv, "%s %s 0.0.0.0 down", + IFCONFIG_PATH, tt->actual_name); + } + else + { + argv_printf (&argv, "%s %s destroy", + IFCONFIG_PATH, tt->actual_name); + } + + close_tun_generic (tt); + argv_msg (M_INFO, &argv); + env_set_add( es, "ODMDIR=/etc/objrepos" ); + openvpn_execve_check (&argv, es, 0, "AIX 'destroy tap interface' failed (non-critical)"); + + free(tt); + env_set_destroy (es); +} + +int +write_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return write (tt->fd, buf, len); +} + +int +read_tun (struct tuntap* tt, uint8_t *buf, int len) +{ + return read (tt->fd, buf, len); +} + +#elif defined(_WIN32) int tun_read_queue (struct tuntap *tt, int maxsize) @@ -4246,7 +4483,7 @@ dhcp_renew (const struct tuntap *tt) */ static void -netsh_command (const struct argv *a, int n) +netsh_command (const struct argv *a, int n, int msglevel) { int i; for (i = 0; i < n; ++i) @@ -4261,21 +4498,19 @@ netsh_command (const struct argv *a, int n) return; openvpn_sleep (4); } - msg (M_FATAL, "NETSH: command failed"); + msg (msglevel, "NETSH: command failed"); } void ipconfig_register_dns (const struct env_set *es) { - struct argv argv; + struct argv argv = argv_new (); bool status; const char err[] = "ERROR: Windows ipconfig command failed"; msg (D_TUNTAP_INFO, "Start net commands..."); netcmd_semaphore_lock (); - argv_init (&argv); - argv_printf (&argv, "%s%sc stop dnscache", get_win_sys_path(), WIN_NET_PATH_SUFFIX); @@ -4411,7 +4646,7 @@ netsh_ifconfig_options (const char *type, NETSH_PATH_SUFFIX, type, flex_name); - netsh_command (&argv, 2); + netsh_command (&argv, 2, M_FATAL); } /* add new DNS/WINS settings to TAP interface */ @@ -4432,7 +4667,7 @@ netsh_ifconfig_options (const char *type, type, flex_name, print_in_addr_t (addr_list[i], 0, &gc)); - netsh_command (&argv, 2); + netsh_command (&argv, 2, M_FATAL); ++count; } @@ -4507,7 +4742,7 @@ netsh_ifconfig (const struct tuntap_options *to, print_in_addr_t (ip, 0, &gc), print_in_addr_t (netmask, 0, &gc)); - netsh_command (&argv, 4); + netsh_command (&argv, 4, M_FATAL); } } @@ -4543,8 +4778,7 @@ static void netsh_enable_dhcp (const struct tuntap_options *to, const char *actual_name) { - struct argv argv; - argv_init (&argv); + struct argv argv = argv_new (); /* example: netsh interface ip set address my-tap dhcp */ argv_printf (&argv, @@ -4553,7 +4787,7 @@ netsh_enable_dhcp (const struct tuntap_options *to, NETSH_PATH_SUFFIX, actual_name); - netsh_command (&argv, 4); + netsh_command (&argv, 4, M_FATAL); argv_reset (&argv); } @@ -4750,10 +4984,43 @@ 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)) + { + msg (M_WARN, "Register_dns: could not talk to service: %s [status=0x%lx]", + strerror_win32 (GetLastError (), &gc), GetLastError ()); + } + + else if (ack.error_number != NO_ERROR) + { + msg (M_WARN, "Register_dns failed using service: %s [status=0x%x]", + strerror_win32 (ack.error_number, &gc), ack.error_number); + } + + else + msg (M_INFO, "Register_dns request sent to the service"); + + gc_free (&gc); +} + void fork_register_dns_action (struct tuntap *tt) { - if (tt && tt->options.register_dns) + if (tt && tt->options.register_dns && tt->options.msg_channel) + { + register_dns_service (tt); + } + else if (tt && tt->options.register_dns) { struct gc_arena gc = gc_new (); struct buffer cmd = alloc_buf_gc (256, &gc); @@ -4798,7 +5065,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu /*netcmd_semaphore_lock ();*/ - msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 ); + msg( M_INFO, "open_tun"); if (tt->type == DEV_TYPE_NULL) { @@ -4924,11 +5191,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu /* usage of numeric constants is ugly, but this is really tied to * *this* version of the driver */ - if ( tt->ipv6 && tt->type == DEV_TYPE_TUN && + if (tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] < 8) { - msg( M_INFO, "WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will be disabled. Upgrade to Tap-Win32 9.8 (2.2-beta3 release or later) or use TAP mode to get IPv6", (int) info[0], (int) info[1] ); - tt->ipv6 = false; + 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] ); } /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy @@ -4936,7 +5202,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu if ( tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] == 8) { - msg( M_FATAL, "ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade to Tap-Win32 9.9 (2.2.2 release or later) or use TAP mode", (int) info[0], (int) info[1] ); + 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] ); } } @@ -5122,13 +5388,35 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu /* flush arp cache */ if (index != TUN_ADAPTER_INDEX_INVALID) { - DWORD status; + DWORD status = -1; + + 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 ()); + + status = ack.error_number; + } + else + status = FlushIpNetTable (index); - if ((status = FlushIpNetTable (index)) == NO_ERROR) + if (status == NO_ERROR) msg (M_INFO, "Successful ARP Flush on interface [%u] %s", (unsigned int)index, device_guid); - else + else if (status != -1) msg (D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s", (unsigned int)index, device_guid, @@ -5247,30 +5535,36 @@ close_tun (struct tuntap *tt) if (tt) { - if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup ) + if ( tt->did_ifconfig_ipv6_setup ) { - const char *ifconfig_ipv6_local; - struct argv argv; - argv_init (&argv); - - /* remove route pointing to interface */ - delete_route_connected_v6_net(tt, NULL); - - /* "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_command (&argv, 1); - argv_reset (&argv); + if (tt->options.msg_channel) + { + do_address_service (false, AF_INET6, tt); + } + else + { + const char *ifconfig_ipv6_local; + struct argv argv = argv_new (); + + /* remove route pointing to interface */ + delete_route_connected_v6_net(tt, NULL); + + /* "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_command (&argv, 1, M_WARN); + argv_reset (&argv); + } } #if 1 if (tt->ipapi_context_defined) @@ -5377,7 +5671,7 @@ ipset2ascii_all (struct gc_arena *gc) void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, false, true, tt); + open_tun_generic (dev, dev_type, dev_node, true, tt); } void |