/* * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Ralf Sager * HSR Hochschule fuer Technik Rapperswil * Copyright (C) 2012 Martin Willi * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "tun_device.h" #include #include #if defined(__APPLE__) #include "TargetConditionals.h" #if !TARGET_OS_OSX #define TUN_DEVICE_NOT_SUPPORTED #endif #elif !defined(__linux__) && !defined(HAVE_NET_IF_TUN_H) #define TUN_DEVICE_NOT_SUPPORTED #endif #ifdef TUN_DEVICE_NOT_SUPPORTED tun_device_t *tun_device_create(const char *name_tmpl) { DBG1(DBG_LIB, "TUN devices are not supported"); return NULL; } #else /* TUN devices supported */ #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #elif defined(__linux__) #include #include #elif __FreeBSD__ >= 10 #include #include #include #else #include #endif #define TUN_DEFAULT_MTU 1500 typedef struct private_tun_device_t private_tun_device_t; struct private_tun_device_t { /** * Public interface */ tun_device_t public; /** * The TUN device's file descriptor */ int tunfd; /** * Name of the TUN device */ char if_name[IFNAMSIZ]; /** * Socket used for ioctl() to set interface addr, ... */ int sock; /** * The current MTU */ int mtu; /** * Associated address */ host_t *address; /** * Netmask for address */ uint8_t netmask; }; /** * FreeBSD 10 deprecated the SIOCSIFADDR etc. commands. */ #if __FreeBSD__ >= 10 static bool set_address_and_mask(struct in_aliasreq *ifra, host_t *addr, uint8_t netmask) { host_t *mask; memcpy(&ifra->ifra_addr, addr->get_sockaddr(addr), *addr->get_sockaddr_len(addr)); /* set the same address as destination address */ memcpy(&ifra->ifra_dstaddr, addr->get_sockaddr(addr), *addr->get_sockaddr_len(addr)); mask = host_create_netmask(addr->get_family(addr), netmask); if (!mask) { DBG1(DBG_LIB, "invalid netmask: %d", netmask); return FALSE; } memcpy(&ifra->ifra_mask, mask->get_sockaddr(mask), *mask->get_sockaddr_len(mask)); mask->destroy(mask); return TRUE; } /** * Set the address using the more flexible SIOCAIFADDR/SIOCDIFADDR commands * on FreeBSD 10 an newer. */ static bool set_address_impl(private_tun_device_t *this, host_t *addr, uint8_t netmask) { struct in_aliasreq ifra; memset(&ifra, 0, sizeof(ifra)); strncpy(ifra.ifra_name, this->if_name, IFNAMSIZ); if (this->address) { /* remove the existing address first */ if (!set_address_and_mask(&ifra, this->address, this->netmask)) { return FALSE; } if (ioctl(this->sock, SIOCDIFADDR, &ifra) < 0) { DBG1(DBG_LIB, "failed to remove existing address on %s: %s", this->if_name, strerror(errno)); return FALSE; } } if (!set_address_and_mask(&ifra, addr, netmask)) { return FALSE; } if (ioctl(this->sock, SIOCAIFADDR, &ifra) < 0) { DBG1(DBG_LIB, "failed to add address on %s: %s", this->if_name, strerror(errno)); return FALSE; } return TRUE; } #else /* __FreeBSD__ */ /** * Set the address using the classic SIOCSIFADDR etc. commands on other systems. */ static bool set_address_impl(private_tun_device_t *this, host_t *addr, uint8_t netmask) { struct ifreq ifr; host_t *mask; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), *addr->get_sockaddr_len(addr)); if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0) { DBG1(DBG_LIB, "failed to set address on %s: %s", this->if_name, strerror(errno)); return FALSE; } #ifdef __APPLE__ if (ioctl(this->sock, SIOCSIFDSTADDR, &ifr) < 0) { DBG1(DBG_LIB, "failed to set dest address on %s: %s", this->if_name, strerror(errno)); return FALSE; } #endif /* __APPLE__ */ mask = host_create_netmask(addr->get_family(addr), netmask); if (!mask) { DBG1(DBG_LIB, "invalid netmask: %d", netmask); return FALSE; } memcpy(&ifr.ifr_addr, mask->get_sockaddr(mask), *mask->get_sockaddr_len(mask)); mask->destroy(mask); if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0) { DBG1(DBG_LIB, "failed to set netmask on %s: %s", this->if_name, strerror(errno)); return FALSE; } return TRUE; } #endif /* __FreeBSD__ */ METHOD(tun_device_t, set_address, bool, private_tun_device_t *this, host_t *addr, uint8_t netmask) { if (!set_address_impl(this, addr, netmask)) { return FALSE; } DESTROY_IF(this->address); this->address = addr->clone(addr); this->netmask = netmask; return TRUE; } METHOD(tun_device_t, get_address, host_t*, private_tun_device_t *this, uint8_t *netmask) { if (netmask && this->address) { *netmask = this->netmask; } return this->address; } METHOD(tun_device_t, up, bool, private_tun_device_t *this) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); if (ioctl(this->sock, SIOCGIFFLAGS, &ifr) < 0) { DBG1(DBG_LIB, "failed to get interface flags for %s: %s", this->if_name, strerror(errno)); return FALSE; } ifr.ifr_flags |= IFF_RUNNING | IFF_UP; if (ioctl(this->sock, SIOCSIFFLAGS, &ifr) < 0) { DBG1(DBG_LIB, "failed to set interface flags on %s: %s", this->if_name, strerror(errno)); return FALSE; } return TRUE; } METHOD(tun_device_t, set_mtu, bool, private_tun_device_t *this, int mtu) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); ifr.ifr_mtu = mtu; if (ioctl(this->sock, SIOCSIFMTU, &ifr) < 0) { DBG1(DBG_LIB, "failed to set MTU on %s: %s", this->if_name, strerror(errno)); return FALSE; } this->mtu = mtu; return TRUE; } METHOD(tun_device_t, get_mtu, int, private_tun_device_t *this) { struct ifreq ifr; if (this->mtu > 0) { return this->mtu; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); this->mtu = TUN_DEFAULT_MTU; if (ioctl(this->sock, SIOCGIFMTU, &ifr) == 0) { this->mtu = ifr.ifr_mtu; } return this->mtu; } METHOD(tun_device_t, get_name, char*, private_tun_device_t *this) { return this->if_name; } METHOD(tun_device_t, get_fd, int, private_tun_device_t *this) { return this->tunfd; } METHOD(tun_device_t, write_packet, bool, private_tun_device_t *this, chunk_t packet) { ssize_t s; #ifdef __APPLE__ /* UTUN's expect the packets to be prepended by a 32-bit protocol number * instead of parsing the packet again, we assume IPv4 for now */ uint32_t proto = htonl(AF_INET); packet = chunk_cata("cc", chunk_from_thing(proto), packet); #endif s = write(this->tunfd, packet.ptr, packet.len); if (s < 0) { DBG1(DBG_LIB, "failed to write packet to TUN device %s: %s", this->if_name, strerror(errno)); return FALSE; } else if (s != packet.len) { return FALSE; } return TRUE; } METHOD(tun_device_t, read_packet, bool, private_tun_device_t *this, chunk_t *packet) { chunk_t data; ssize_t len; bool old; data = chunk_alloca(get_mtu(this)); old = thread_cancelability(TRUE); len = read(this->tunfd, data.ptr, data.len); thread_cancelability(old); if (len < 0) { DBG1(DBG_LIB, "reading from TUN device %s failed: %s", this->if_name, strerror(errno)); return FALSE; } data.len = len; #ifdef __APPLE__ /* UTUN's prepend packets with a 32-bit protocol number */ data = chunk_skip(data, sizeof(uint32_t)); #endif *packet = chunk_clone(data); return TRUE; } METHOD(tun_device_t, destroy, void, private_tun_device_t *this) { if (this->tunfd > 0) { close(this->tunfd); #ifdef __FreeBSD__ /* tun(4) says the following: "These network interfaces persist until * the if_tun.ko module is unloaded, or until removed with the * ifconfig(8) command." So simply closing the FD is not enough. */ struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); if (ioctl(this->sock, SIOCIFDESTROY, &ifr) < 0) { DBG1(DBG_LIB, "failed to destroy %s: %s", this->if_name, strerror(errno)); } #endif /* __FreeBSD__ */ } if (this->sock > 0) { close(this->sock); } DESTROY_IF(this->address); free(this); } /** * Initialize the tun device */ static bool init_tun(private_tun_device_t *this, const char *name_tmpl) { #ifdef __APPLE__ struct ctl_info info; struct sockaddr_ctl addr; socklen_t size = IFNAMSIZ; memset(&info, 0, sizeof(info)); memset(&addr, 0, sizeof(addr)); this->tunfd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); if (this->tunfd < 0) { DBG1(DBG_LIB, "failed to open tundevice PF_SYSTEM socket: %s", strerror(errno)); return FALSE; } /* get a control identifier for the utun kernel extension */ strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME)); if (ioctl(this->tunfd, CTLIOCGINFO, &info) < 0) { DBG1(DBG_LIB, "failed to ioctl tundevice: %s", strerror(errno)); close(this->tunfd); return FALSE; } addr.sc_id = info.ctl_id; addr.sc_len = sizeof(addr); addr.sc_family = AF_SYSTEM; addr.ss_sysaddr = AF_SYS_CONTROL; /* allocate identifier dynamically */ addr.sc_unit = 0; if (connect(this->tunfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { DBG1(DBG_LIB, "failed to connect tundevice: %s", strerror(errno)); close(this->tunfd); return FALSE; } if (getsockopt(this->tunfd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, this->if_name, &size) < 0) { DBG1(DBG_LIB, "getting tundevice name failed: %s", strerror(errno)); close(this->tunfd); return FALSE; } return TRUE; #elif defined(IFF_TUN) struct ifreq ifr; strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ); this->if_name[IFNAMSIZ-1] = '\0'; this->tunfd = open("/dev/net/tun", O_RDWR); if (this->tunfd < 0) { DBG1(DBG_LIB, "failed to open /dev/net/tun: %s", strerror(errno)); return FALSE; } memset(&ifr, 0, sizeof(ifr)); /* TUN device, no packet info */ ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); if (ioctl(this->tunfd, TUNSETIFF, (void*)&ifr) < 0) { DBG1(DBG_LIB, "failed to configure TUN device: %s", strerror(errno)); close(this->tunfd); return FALSE; } strncpy(this->if_name, ifr.ifr_name, IFNAMSIZ); return TRUE; #elif defined(__FreeBSD__) if (name_tmpl) { DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported"); } this->tunfd = open("/dev/tun", O_RDWR); if (this->tunfd < 0) { DBG1(DBG_LIB, "failed to open /dev/tun: %s", strerror(errno)); return FALSE; } fdevname_r(this->tunfd, this->if_name, IFNAMSIZ); return TRUE; #else /* !__FreeBSD__ */ /* this might work on Linux with older TUN driver versions (no IFF_TUN) */ char devname[IFNAMSIZ]; /* the same process is allowed to open a device again, but that's not what * we want (unless we previously closed a device, which we don't know at * this point). therefore, this counter is static so we don't accidentally * open a device twice */ static int i = -1; if (name_tmpl) { DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported"); } for (; ++i < 256; ) { snprintf(devname, IFNAMSIZ, "/dev/tun%d", i); this->tunfd = open(devname, O_RDWR); if (this->tunfd > 0) { /* for ioctl(2) calls only the interface name is used */ snprintf(this->if_name, IFNAMSIZ, "tun%d", i); break; } DBG1(DBG_LIB, "failed to open %s: %s", this->if_name, strerror(errno)); } return this->tunfd > 0; #endif /* !__APPLE__ */ } /* * Described in header */ tun_device_t *tun_device_create(const char *name_tmpl) { private_tun_device_t *this; INIT(this, .public = { .read_packet = _read_packet, .write_packet = _write_packet, .get_mtu = _get_mtu, .set_mtu = _set_mtu, .get_name = _get_name, .get_fd = _get_fd, .set_address = _set_address, .get_address = _get_address, .up = _up, .destroy = _destroy, }, .tunfd = -1, .sock = -1, ); if (!init_tun(this, name_tmpl)) { free(this); return NULL; } DBG1(DBG_LIB, "created TUN device: %s", this->if_name); this->sock = socket(AF_INET, SOCK_DGRAM, 0); if (this->sock < 0) { DBG1(DBG_LIB, "failed to open socket to configure TUN device"); destroy(this); return NULL; } return &this->public; } #endif /* TUN devices supported */