X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=blobdiff_plain;f=src%2Fbsd%2Fdevice.c;h=4a63e840010a3f0797feae5eb1b8aa911b9fd894;hp=a9e39d4a4239f3503999a201f181706ec51194a1;hb=af81c436d6e11a53803747af7cc8ecfd449ccd4c;hpb=d6c50eb73ad49bd2eac67214995dff76b7a20661 diff --git a/src/bsd/device.c b/src/bsd/device.c index a9e39d4a..4a63e840 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-2009 Guus Sliepen + 2001-2017 Guus Sliepen 2009 Grzegorz Dymarek This program is free software; you can redistribute it and/or modify @@ -19,59 +19,121 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "system.h" +#include "../system.h" -#include "conf.h" -#include "logger.h" -#include "net.h" -#include "route.h" -#include "utils.h" -#include "xalloc.h" +#include "../conf.h" +#include "../device.h" +#include "../logger.h" +#include "../names.h" +#include "../net.h" +#include "../route.h" +#include "../utils.h" +#include "../xalloc.h" -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU #include "bsd/tunemu.h" #endif -#define DEFAULT_DEVICE "/dev/tun0" +#ifdef HAVE_NET_IF_UTUN_H +#include +#include +#include +#endif + +#define DEFAULT_TUN_DEVICE "/dev/tun0" +#define DEFAULT_TAP_DEVICE "/dev/tap0" typedef enum device_type { DEVICE_TYPE_TUN, DEVICE_TYPE_TUNIFHEAD, DEVICE_TYPE_TAP, -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU DEVICE_TYPE_TUNEMU, #endif + DEVICE_TYPE_UTUN, } device_type_t; int device_fd = -1; char *device = NULL; char *iface = NULL; static char *device_info = NULL; -static int device_total_in = 0; -static int device_total_out = 0; -#if defined(TUNEMU) +#if defined(ENABLE_TUNEMU) static device_type_t device_type = DEVICE_TYPE_TUNEMU; -#elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) +#elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD) || defined(HAVE_DRAGONFLY) static device_type_t device_type = DEVICE_TYPE_TUNIFHEAD; #else static device_type_t device_type = DEVICE_TYPE_TUN; #endif -bool setup_device(void) { - char *type; +#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; + } - if(!get_config_string(lookup_config(config_tree, "Device"), &device)) - device = xstrdup(DEFAULT_DEVICE); + struct ctl_info info = {}; + strlcpy(info.ctl_name, UTUN_CONTROL_NAME, sizeof info.ctl_name); - if(!get_config_string(lookup_config(config_tree, "Interface"), &iface)) - iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device); + 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")) - /* use default */; -#ifdef HAVE_TUNEMU + /* use default */; +#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; @@ -80,40 +142,91 @@ bool setup_device(void) { else if(!strcasecmp(type, "tap")) device_type = DEVICE_TYPE_TAP; else { - logger(LOG_ERR, "Unknown device type %s!", type); + logger(DEBUG_ALWAYS, LOG_ERR, "Unknown device type %s!", type); return false; } } else { - if(strstr(device, "tap") || routing_mode != RMODE_ROUTER) +#ifdef HAVE_NET_IF_UTUN_H + if(device && (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); + else + device = xstrdup(DEFAULT_TUN_DEVICE); + } + + // Open the device + switch(device_type) { -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU case DEVICE_TYPE_TUNEMU: { char dynamic_name[256] = ""; - device_fd = tunemu_open(dynamic_name); + 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); } if(device_fd < 0) { - logger(LOG_ERR, "Could not open %s: %s", device, strerror(errno)); + logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", device, strerror(errno)); return false; } +#ifdef FD_CLOEXEC + fcntl(device_fd, F_SETFD, FD_CLOEXEC); +#endif + + // Guess what the corresponding interface is called + + char *realname = NULL; + +#if defined(HAVE_FDEVNAME) + realname = fdevname(device_fd); +#elif defined(HAVE_DEVNAME) + struct stat buf; + if(!fstat(device_fd, &buf)) + realname = devname(buf.st_rdev, S_IFCHR); +#endif + + if(!realname) + realname = device; + + 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; case DEVICE_TYPE_TUN: #ifdef TUNSIFHEAD - { + { const int zero = 0; if(ioctl(device_fd, TUNSIFHEAD, &zero, sizeof zero) == -1) { - logger(LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno)); + logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno)); return false; } } @@ -132,7 +245,7 @@ bool setup_device(void) { { const int one = 1; if(ioctl(device_fd, TUNSIFHEAD, &one, sizeof one) == -1) { - logger(LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno)); + logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "ioctl", strerror(errno)); return false; } } @@ -159,24 +272,29 @@ bool setup_device(void) { iface = xstrdup(ifr.ifr_name); } } - + #endif break; -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU case DEVICE_TYPE_TUNEMU: - device_info = "BSD tunemu device"; + device_info = "BSD tunemu device"; break; #endif } - logger(LOG_INFO, "%s is a %s", device, device_info); +#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; } -void close_device(void) { +static void close_device(void) { switch(device_type) { -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU case DEVICE_TYPE_TUNEMU: tunemu_close(device_fd); break; @@ -184,85 +302,86 @@ void close_device(void) { default: close(device_fd); } + device_fd = -1; - free(device); - free(iface); + free(device); device = NULL; + free(iface); iface = NULL; + device_info = NULL; } -bool read_packet(vpn_packet_t *packet) { +static bool read_packet(vpn_packet_t *packet) { int inlen; switch(device_type) { case DEVICE_TYPE_TUN: -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU case DEVICE_TYPE_TUNEMU: if(device_type == DEVICE_TYPE_TUNEMU) - inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14); + inlen = tunemu_read(device_fd, DATA(packet) + 14, MTU - 14); else -#else - inlen = read(device_fd, packet->data + 14, MTU - 14); #endif + inlen = read(device_fd, DATA(packet) + 14, MTU - 14); if(inlen <= 0) { - logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, + logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; } - switch(packet->data[14] >> 4) { + switch(DATA(packet)[14] >> 4) { case 4: - packet->data[12] = 0x08; - packet->data[13] = 0x00; + DATA(packet)[12] = 0x08; + DATA(packet)[13] = 0x00; break; case 6: - packet->data[12] = 0x86; - packet->data[13] = 0xDD; + DATA(packet)[12] = 0x86; + DATA(packet)[13] = 0xDD; break; default: - ifdebug(TRAFFIC) logger(LOG_ERR, + logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown IP version %d while reading packet from %s %s", - packet->data[14] >> 4, device_info, device); + DATA(packet)[14] >> 4, device_info, device); return false; } + memset(DATA(packet), 0, 12); packet->len = inlen + 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((inlen = readv(device_fd, vector, 2)) <= 0) { - logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, + if((inlen = read(device_fd, DATA(packet) + 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: - packet->data[12] = 0x08; - packet->data[13] = 0x00; + switch (DATA(packet)[14] >> 4) { + case 4: + DATA(packet)[12] = 0x08; + DATA(packet)[13] = 0x00; break; - case AF_INET6: - packet->data[12] = 0x86; - packet->data[13] = 0xDD; + case 6: + DATA(packet)[12] = 0x86; + DATA(packet)[13] = 0xDD; break; default: - ifdebug(TRAFFIC) logger(LOG_ERR, - "Unknown address family %x while reading packet from %s %s", - ntohl(type), device_info, device); + logger(DEBUG_TRAFFIC, LOG_ERR, + "Unknown IP version %d while reading packet from %s %s", + DATA(packet)[14] >> 4, device_info, device); return false; } + memset(DATA(packet), 0, 12); packet->len = inlen + 10; break; } case DEVICE_TYPE_TAP: - if((inlen = read(device_fd, packet->data, MTU)) <= 0) { - logger(LOG_ERR, "Error while reading from %s %s: %s", device_info, + if((inlen = read(device_fd, DATA(packet), MTU)) <= 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info, device, strerror(errno)); return false; } @@ -273,34 +392,30 @@ bool read_packet(vpn_packet_t *packet) { default: return false; } - - device_total_in += packet->len; - ifdebug(TRAFFIC) logger(LOG_DEBUG, "Read packet of %d bytes from %s", + logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len, device_info); return true; } -bool write_packet(vpn_packet_t *packet) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s", +static bool write_packet(vpn_packet_t *packet) { + logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s", packet->len, device_info); switch(device_type) { case DEVICE_TYPE_TUN: - if(write(device_fd, packet->data + 14, packet->len - 14) < 0) { - logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, + if(write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, strerror(errno)); return false; } 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 = (DATA(packet)[12] << 8) + DATA(packet)[13]; + uint32_t type; switch (af) { case 0x0800: @@ -310,32 +425,34 @@ bool write_packet(vpn_packet_t *packet) { type = htonl(AF_INET6); break; default: - ifdebug(TRAFFIC) logger(LOG_ERR, + logger(DEBUG_TRAFFIC, LOG_ERR, "Unknown address family %x while writing packet to %s %s", af, device_info, device); return false; } - if(writev(device_fd, vector, 2) < 0) { - logger(LOG_ERR, "Can't write to %s %s: %s", device_info, device, + memcpy(DATA(packet) + 10, &type, sizeof type); + + if(write(device_fd, DATA(packet) + 10, packet->len - 10) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device, strerror(errno)); return false; } break; } - + case DEVICE_TYPE_TAP: - if(write(device_fd, packet->data, packet->len) < 0) { - logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, + if(write(device_fd, DATA(packet), packet->len) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, strerror(errno)); return false; } break; -#ifdef HAVE_TUNEMU +#ifdef ENABLE_TUNEMU case DEVICE_TYPE_TUNEMU: - if(tunemu_write(device_fd, packet->data + 14, packet->len - 14) < 0) { - logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, + if(tunemu_write(device_fd, DATA(packet) + 14, packet->len - 14) < 0) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, strerror(errno)); return false; } @@ -346,13 +463,12 @@ bool write_packet(vpn_packet_t *packet) { return false; } - device_total_out += packet->len; - return true; } -void dump_device_stats(void) { - logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device); - logger(LOG_DEBUG, " total bytes in: %10d", device_total_in); - logger(LOG_DEBUG, " total bytes out: %10d", device_total_out); -} +const devops_t os_devops = { + .setup = setup_device, + .close = close_device, + .read = read_packet, + .write = write_packet, +};