From d7f6737cfcae75e8c2f522c68aaedee0519a6131 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 11 Apr 2016 14:49:51 +0200 Subject: [PATCH] Update support for BSD tun/tap devices, add support for OS X utun interfaces. --- configure.ac | 6 +- doc/tinc.conf.5.in | 6 +- doc/tinc.texi | 19 +++--- src/bsd/device.c | 154 ++++++++++++++++++++++++++++++++++++--------- src/have.h | 4 -- 5 files changed, 143 insertions(+), 46 deletions(-) diff --git a/configure.ac b/configure.ac index 5065901..a1d63c8 100644 --- a/configure.ac +++ b/configure.ac @@ -170,8 +170,8 @@ dnl Checks for header files. dnl We do this in multiple stages, because unlike Linux all the other operating systems really suck and don't include their own dependencies. AC_HEADER_STDC -AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h sys/uio.h sys/un.h sys/wait.h netdb.h arpa/inet.h dirent.h getopt.h]) -AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h netpacket/packet.h], +AC_CHECK_HEADERS([stdbool.h syslog.h sys/file.h sys/ioctl.h sys/mman.h sys/param.h sys/resource.h sys/socket.h sys/time.h sys/un.h sys/wait.h netdb.h arpa/inet.h dirent.h getopt.h]) +AC_CHECK_HEADERS([net/if.h net/if_types.h linux/if_tun.h net/if_tun.h net/if_utun.h net/tun/if_tun.h net/if_tap.h net/tap/if_tap.h net/ethernet.h net/if_arp.h netinet/in_systm.h netinet/in.h netinet/in6.h time.h netpacket/packet.h], [], [], [#include "$srcdir/src/have.h"] ) AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h resolv.h], @@ -198,7 +198,7 @@ AC_CHECK_TYPES([socklen_t, struct ether_header, struct arphdr, struct ether_arp, dnl Checks for library functions. AC_TYPE_SIGNAL -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random recvmmsg select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev], +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random recvmmsg select strdup strerror strsignal strtol system time usleep unsetenv vsyslog devname fdevname], [], [], [#include "$srcdir/src/have.h"] ) diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 4fc70cf..5769469 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -1,4 +1,4 @@ -.Dd 2014-01-29 +.Dd 2016-04-11 .Dt TINC.CONF 5 .\" Manual page created by: .\" Ivo Timmermans @@ -266,6 +266,10 @@ Tinc will expect packets read from the virtual network device to start with a four byte header containing the address family, followed by an IP header. This mode should support both IPv4 and IPv6 packets. +.It utun Pq OS X +Set type to utun. +This is only supported on OS X version 10.6.8 and higher, but doesn't require the tuntaposx module. +This mode should support both IPv4 and IPv6 packets. .It tap Pq BSD and Linux Set type to tap. Tinc will expect packets read from the virtual network device diff --git a/doc/tinc.texi b/doc/tinc.texi index 53ce4a3..f3bb1d2 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -307,15 +307,14 @@ If the @file{net/if_tun.h} header file is missing, install it from the source pa @subsection Configuration of Darwin (MacOS/X) kernels Tinc on Darwin relies on a tunnel driver for its data acquisition from the kernel. -Tinc supports either the driver from @uref{http://tuntaposx.sourceforge.net/}, +OS X version 10.6.8 and later have a built-in tun driver called "utun". +Tinc also supports the driver from @uref{http://tuntaposx.sourceforge.net/}, which supports both tun and tap style devices, -and also the driver from from @uref{http://chrisp.de/en/projects/tunnel.html}. -The former driver is recommended. -The tunnel driver must be loaded before starting tinc with the following command: -@example -kmodload tunnel -@end example +By default, tinc expects the tuntaposx driver to be installed. +To use the utun driver, set add @code{Device = utunX} to @file{tinc.conf}, +where X is the desired number for the utun interface. +You can also omit the number, in which case the first free number will be chosen. @c ================================================================== @@ -999,6 +998,12 @@ to start with a four byte header containing the address family, followed by an IP header. This mode should support both IPv4 and IPv6 packets. +@cindex utun +@item utun (OS X) +Set type to utun. +This is only supported on OS X version 10.6.8 and higher, but doesn't require the tuntaposx module. +This mode should support both IPv4 and IPv6 packets. + @item tap (BSD and Linux) Set type to tap. Tinc will expect packets read from the virtual network device diff --git a/src/bsd/device.c b/src/bsd/device.c index d1a993b..303e9b6 100644 --- a/src/bsd/device.c +++ b/src/bsd/device.c @@ -1,7 +1,7 @@ /* device.c -- Interaction BSD tun/tap device Copyright (C) 2001-2005 Ivo Timmermans, - 2001-2014 Guus Sliepen + 2001-2016 Guus Sliepen 2009 Grzegorz Dymarek This program is free software; you can redistribute it and/or modify @@ -34,12 +34,14 @@ #include "bsd/tunemu.h" #endif +#ifdef HAVE_NET_IF_UTUN_H +#include +#include +#include +#endif + #define DEFAULT_TUN_DEVICE "/dev/tun0" -#if defined(HAVE_DARWIN) || defined(HAVE_FREEBSD) || defined(HAVE_NETBSD) #define DEFAULT_TAP_DEVICE "/dev/tap0" -#else -#define DEFAULT_TAP_DEVICE "/dev/tun0" -#endif typedef enum device_type { DEVICE_TYPE_TUN, @@ -48,6 +50,7 @@ typedef enum device_type { #ifdef ENABLE_TUNEMU DEVICE_TYPE_TUNEMU, #endif + DEVICE_TYPE_UTUN, } device_type_t; int device_fd = -1; @@ -62,9 +65,64 @@ static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD; static device_type_t device_type = DEVICE_TYPE_TUN; #endif +#ifdef HAVE_NET_IF_UTUN_H +static bool setup_utun(void) { + device_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if(device_fd == -1) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not open PF_SYSTEM socket: %s\n", strerror(errno)); + return false; + } + + struct ctl_info info = {}; + strlcpy(info.ctl_name, UTUN_CONTROL_NAME, sizeof info.ctl_name); + + if(ioctl(device_fd, CTLIOCGINFO, &info) == -1) { + logger(DEBUG_ALWAYS, LOG_ERR, "ioctl(CTLIOCGINFO) failed: %s", strerror(errno)); + return false; + } + + int unit = -1; + char *p = strstr(device, "utun"), *e = NULL; + if(p) { + unit = strtol(p + 4, &e, 10); + if(!e) + unit = -1; + } + + struct sockaddr_ctl sc = { + .sc_id = info.ctl_id, + .sc_len = sizeof sc, + .sc_family = AF_SYSTEM, + .ss_sysaddr = AF_SYS_CONTROL, + .sc_unit = unit + 1, + }; + + if(connect(device_fd, (struct sockaddr *)&sc, sizeof(sc)) == -1) { + logger(DEBUG_ALWAYS, LOG_ERR, "Could not connect utun socket: %s\n", strerror(errno)); + return false; + } + + char name[64] = ""; + socklen_t len = sizeof name; + if(getsockopt(device_fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, name, &len)) { + iface = xstrdup(device); + } else { + iface = xstrdup(name); + } + + device_info = "OS X utun device"; + + logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info); + + return true; +} +#endif + static bool setup_device(void) { get_config_string(lookup_config(config_tree, "Device"), &device); + // Find out if it's supposed to be a tun or a tap device + char *type; if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) { if(!strcasecmp(type, "tun")) @@ -72,6 +130,10 @@ static bool setup_device(void) { #ifdef ENABLE_TUNEMU else if(!strcasecmp(type, "tunemu")) device_type = DEVICE_TYPE_TUNEMU; +#endif +#ifdef HAVE_NET_IF_UTUN_H + else if(!strcasecmp(type, "utun")) + device_type = DEVICE_TYPE_UTUN; #endif else if(!strcasecmp(type, "tunnohead")) device_type = DEVICE_TYPE_TUN; @@ -84,10 +146,22 @@ static bool setup_device(void) { return false; } } else { +#ifdef HAVE_NET_IF_UTUN_H + if(strncmp(device, "utun", 4) == 0 || strncmp(device, "/dev/utun", 9) == 0) + device_type = DEVICE_TYPE_UTUN; + else +#endif if((device && strstr(device, "tap")) || routing_mode != RMODE_ROUTER) device_type = DEVICE_TYPE_TAP; } + if(routing_mode == RMODE_SWITCH && device_type != DEVICE_TYPE_TAP) { + logger(DEBUG_ALWAYS, LOG_ERR, "Only tap devices support switch mode!"); + return false; + } + + // Find out which device file to open + if(!device) { if(device_type == DEVICE_TYPE_TAP) device = xstrdup(DEFAULT_TAP_DEVICE); @@ -95,17 +169,7 @@ static bool setup_device(void) { device = xstrdup(DEFAULT_TUN_DEVICE); } - if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) - iface = NULL; -#ifndef TAPGIFNAME - if (iface) { - logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring specified interface name '%s' as device rename is not supported on this platform", iface); - free(iface); - iface = NULL; - } -#endif - if (!iface) - iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device); + // Open the device switch(device_type) { #ifdef ENABLE_TUNEMU @@ -114,6 +178,10 @@ static bool setup_device(void) { device_fd = tunemu_open(dynamic_name); } break; +#endif +#ifdef HAVE_NET_IF_UTUN_H + case DEVICE_TYPE_UTUN: + return setup_utun(); #endif default: device_fd = open(device, O_RDWR | O_NONBLOCK); @@ -128,6 +196,27 @@ static bool setup_device(void) { fcntl(device_fd, F_SETFD, FD_CLOEXEC); #endif + // Guess what the corresponding interface is called + + char *realname; + +#if defined(HAVE_FDEVNAME) + realname = fdevname(device_fd) ? : device; +#elif defined(HAVE_DEVNAME) + struct stat buf; + if(!fstat(device_fd, &buf)) + realname = devname(buf.st_rdev, S_IFCHR) ? : device; +#else + realname = device; +#endif + + if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) + iface = xstrdup(strrchr(realname, '/') ? strrchr(realname, '/') + 1 : realname); + else if(strcmp(iface, strrchr(realname, '/') ? strrchr(realname, '/') + 1 : realname)) + logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: Interface does not match Device. $INTERFACE might be set incorrectly."); + + // Configure the device as best as we can + switch(device_type) { default: device_type = DEVICE_TYPE_TUN; @@ -192,6 +281,11 @@ static bool setup_device(void) { #endif } +#ifdef SIOCGIFADDR + if(overwrite_mac) + ioctl(device_fd, SIOCGIFADDR, mymac.x); +#endif + logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info); return true; @@ -253,31 +347,29 @@ static bool read_packet(vpn_packet_t *packet) { packet->len = inlen + 14; break; + case DEVICE_TYPE_UTUN: case DEVICE_TYPE_TUNIFHEAD: { - u_int32_t type; - struct iovec vector[2] = {{&type, sizeof type}, {DATA(packet) + 14, MTU - 14}}; - - if((inlen = readv(device_fd, vector, 2)) <= 0) { + if((inlen = read(device_fd, packet->data + 10, MTU - 10)) <= 0) { logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; } - switch (ntohl(type)) { - case AF_INET: + switch (packet->data[14] >> 4) { + case 4: DATA(packet)[12] = 0x08; DATA(packet)[13] = 0x00; break; - case AF_INET6: + case 6: DATA(packet)[12] = 0x86; DATA(packet)[13] = 0xDD; break; default: logger(DEBUG_TRAFFIC, LOG_ERR, - "Unknown address family %x while reading packet from %s %s", - ntohl(type), device_info, device); + "Unknown IP version %d while reading packet from %s %s", + packet->data[14] >> 4, device_info, device); return false; } @@ -319,12 +411,10 @@ static bool write_packet(vpn_packet_t *packet) { } break; + case DEVICE_TYPE_UTUN: case DEVICE_TYPE_TUNIFHEAD: { - u_int32_t type; - struct iovec vector[2] = {{&type, sizeof type}, {DATA(packet) + 14, packet->len - 14}}; - int af; - - af = (DATA(packet)[12] << 8) + DATA(packet)[13]; + int af = (DATA(packet)[12] << 8) + DATA(packet)[13]; + uint32_t type; switch (af) { case 0x0800: @@ -340,7 +430,9 @@ static bool write_packet(vpn_packet_t *packet) { return false; } - if(writev(device_fd, vector, 2) < 0) { + memcpy(packet->data + 10, &type, sizeof type); + + if(write(device_fd, packet->data + 10, packet->len - 10) < 0) { logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); return false; diff --git a/src/have.h b/src/have.h index 57de6e0..a054c56 100644 --- a/src/have.h +++ b/src/have.h @@ -103,10 +103,6 @@ #include #endif -#ifdef HAVE_SYS_UIO_H -#include -#endif - #ifdef HAVE_SYS_UN_H #include #endif -- 2.25.1