Add support for OS X utun interfaces.
authorGuus Sliepen <guus@tinc-vpn.org>
Sat, 9 Apr 2016 22:05:13 +0000 (00:05 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Sat, 9 Apr 2016 22:05:13 +0000 (00:05 +0200)
configure.ac
doc/tinc.conf.5.in
doc/tinc.texi
src/bsd/device.c

index 4db9323..dd66305 100644 (file)
@@ -162,7 +162,7 @@ dnl We do this in multiple stages, because unlike Linux all the other operating
 
 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 time.h sys/uio.h sys/wait.h netdb.h arpa/inet.h arpa/nameser.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 netpacket/packet.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 netpacket/packet.h],
   [], [], [#include "src/have.h"]
 )
 AC_CHECK_HEADERS([netinet/if_ether.h netinet/ip.h netinet/ip6.h resolv.h],
index 0f91b7f..e5b59d7 100644 (file)
@@ -1,4 +1,4 @@
-.Dd 2016-02-27
+.Dd 2016-04-10
 .Dt TINC.CONF 5
 .\" Manual page created by:
 .\" Ivo Timmermans
@@ -250,6 +250,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
index 9b60f31..414c0ca 100644 (file)
@@ -302,9 +302,15 @@ If the @file{net/if_tun.h} header file is missing, install it from the source pa
 @subsection Configuration of Darwin (Mac OS 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.
 
+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 ==================================================================
 @node       Configuration of Windows
@@ -892,6 +898,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
index 653076b..e3148bd 100644 (file)
 #include "tunemu.h"
 #endif
 
+#ifdef HAVE_NET_IF_UTUN_H
+#include <sys/sys_domain.h>
+#include <sys/kern_control.h>
+#include <net/if_utun.h>
+#endif
+
 #define DEFAULT_TUN_DEVICE "/dev/tun0"
 #define DEFAULT_TAP_DEVICE "/dev/tap0"
 
@@ -43,6 +49,9 @@ typedef enum device_type {
 #ifdef ENABLE_TUNEMU
        DEVICE_TYPE_TUNEMU,
 #endif
+#ifdef HAVE_NET_IF_UTUN_H
+       DEVICE_TYPE_UTUN,
+#endif
 } device_type_t;
 
 int device_fd = -1;
@@ -59,6 +68,59 @@ 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(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(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(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(LOG_INFO, "%s is a %s", device, device_info);
+
+       return true;
+}
+#endif
+
 static bool setup_device(void) {
        // Find out which device file to open
 
@@ -80,6 +142,10 @@ static bool setup_device(void) {
                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;
                else if(!strcasecmp(type, "tunifhead"))
@@ -91,10 +157,20 @@ 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(strstr(device, "tap") || routing_mode != RMODE_ROUTER)
                        device_type = DEVICE_TYPE_TAP;
        }
 
+       if(routing_mode == RMODE_SWITCH && device_type != DEVICE_TYPE_TAP) {
+               logger(LOG_ERR, "Only tap devices support switch mode!");
+               return false;
+       }
+
        // Open the device
 
        switch(device_type) {
@@ -105,6 +181,10 @@ static bool setup_device(void) {
                }
                        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);
        }
@@ -267,31 +347,27 @@ static bool read_packet(vpn_packet_t *packet) {
                        packet->len = lenin + 14;
                        break;
 
+               case DEVICE_TYPE_UTUN:
                case DEVICE_TYPE_TUNIFHEAD: {
-                       u_int32_t type;
-                       struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}};
-
-                       if((lenin = readv(device_fd, vector, 2)) <= 0) {
+                       if((lenin = read(device_fd, packet->data + 10, MTU - 10)) <= 0) {
                                logger(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:
                                        packet->data[12] = 0x08;
                                        packet->data[13] = 0x00;
                                        break;
-
-                               case AF_INET6:
+                               case 6:
                                        packet->data[12] = 0x86;
                                        packet->data[13] = 0xDD;
                                        break;
-
                                default:
                                        ifdebug(TRAFFIC) logger(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;
                        }
 
@@ -335,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)}, {packet->data + 14, packet->len - 14}};
-                       int af;
-                       
-                       af = (packet->data[12] << 8) + packet->data[13];
+                       int af = (packet->data[12] << 8) + packet->data[13];
+                       uint32_t type;
 
                        switch (af) {
                                case 0x0800:
@@ -356,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(LOG_ERR, "Can't write to %s %s: %s", device_info, device,
                                           strerror(errno));
                                return false;