/* * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Ralf Sager * 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 #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #elif defined(__linux__) #include #else #include #endif #include "tun_device.h" #include #include #include #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; }; /** * Set the sockaddr_t from the given netmask */ static void set_netmask(struct ifreq *ifr, int family, u_int8_t netmask) { int len, bytes, bits; char *target; switch (family) { case AF_INET: { struct sockaddr_in *addr = (struct sockaddr_in*)&ifr->ifr_addr; addr->sin_family = AF_INET; target = (char*)&addr->sin_addr; len = 4; break; } case AF_INET6: { struct sockaddr_in6 *addr = (struct sockaddr_in6*)&ifr->ifr_addr; addr->sin6_family = AF_INET6; target = (char*)&addr->sin6_addr; len = 16; break; } default: return; } bytes = (netmask + 7) / 8; bits = (bytes * 8) - netmask; memset(target, 0xff, bytes); memset(target + bytes, 0x00, len - bytes); target[bytes - 1] = bits ? (u_int8_t)(0xff << bits) : 0xff; } METHOD(tun_device_t, set_address, bool, private_tun_device_t *this, host_t *addr, u_int8_t netmask) { struct ifreq ifr; int family; family = addr->get_family(addr); if ((netmask > 32 && family == AF_INET) || netmask > 128) { DBG1(DBG_LIB, "failed to set address on %s: invalid netmask", this->if_name); return FALSE; } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ); memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr), sizeof(sockaddr_t)); 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__ */ set_netmask(&ifr, family, netmask); 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; } 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, write_packet, bool, private_tun_device_t *this, chunk_t packet) { ssize_t s; 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) { ssize_t len; fd_set set; bool old; FD_ZERO(&set); FD_SET(this->tunfd, &set); old = thread_cancelability(TRUE); len = select(this->tunfd + 1, &set, NULL, NULL, NULL); thread_cancelability(old); if (len < 0) { DBG1(DBG_LIB, "select on TUN device %s failed: %s", this->if_name, strerror(errno)); return FALSE; } /* FIXME: this is quite expensive for lots of small packets, copy from * local buffer instead? */ *packet = chunk_alloc(get_mtu(this)); len = read(this->tunfd, packet->ptr, packet->len); if (len < 0) { DBG1(DBG_LIB, "reading from TUN device %s failed: %s", this->if_name, strerror(errno)); chunk_free(packet); return FALSE; } packet->len = len; 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); } 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; #else /* !IFF_TUN */ /* this works on FreeBSD and might also work on Linux with older TUN * driver versions (no IFF_TUN) */ char devname[IFNAMSIZ]; int i; if (name_tmpl) { DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported"); } for (i = 0; i < 256; i++) { 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, .set_address = _set_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; }