diff options
Diffstat (limited to 'src/libstrongswan/utils')
-rw-r--r-- | src/libstrongswan/utils/tun_device.c | 148 |
1 files changed, 128 insertions, 20 deletions
diff --git a/src/libstrongswan/utils/tun_device.c b/src/libstrongswan/utils/tun_device.c index 889fe6247..36f3359c0 100644 --- a/src/libstrongswan/utils/tun_device.c +++ b/src/libstrongswan/utils/tun_device.c @@ -3,6 +3,7 @@ * 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 @@ -24,8 +25,17 @@ #include <sys/socket.h> #include <sys/stat.h> #include <unistd.h> -#include <linux/if.h> +#include <net/if.h> + +#ifdef __APPLE__ +#include <net/if_utun.h> +#include <netinet/in_var.h> +#include <sys/kern_control.h> +#elif defined(__linux__) #include <linux/if_tun.h> +#else +#include <net/if_tun.h> +#endif #include "tun_device.h" @@ -127,6 +137,14 @@ METHOD(tun_device_t, set_address, bool, 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); @@ -176,6 +194,8 @@ METHOD(tun_device_t, set_mtu, bool, 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; @@ -269,6 +289,20 @@ METHOD(tun_device_t, destroy, void, 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) { @@ -278,18 +312,70 @@ METHOD(tun_device_t, destroy, void, } /** - * Allocate a TUN device + * Initialize the tun device */ -static int tun_alloc(char dev[IFNAMSIZ]) +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; - int fd; - fd = open("/dev/net/tun", O_RDWR); - if (fd < 0) + 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 fd; + return FALSE; } memset(&ifr, 0, sizeof(ifr)); @@ -297,16 +383,42 @@ static int tun_alloc(char dev[IFNAMSIZ]) /* TUN device, no packet info */ ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - strncpy(ifr.ifr_name, dev, IFNAMSIZ); - - if (ioctl(fd, TUNSETIFF, (void*)&ifr) < 0) + 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(fd); - return -1; + 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"); } - strncpy(dev, ifr.ifr_name, IFNAMSIZ); - return fd; + + 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__ */ } /* @@ -331,13 +443,9 @@ tun_device_t *tun_device_create(const char *name_tmpl) .sock = -1, ); - strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ); - this->if_name[IFNAMSIZ-1] = '\0'; - - this->tunfd = tun_alloc(this->if_name); - if (this->tunfd < 0) + if (!init_tun(this, name_tmpl)) { - destroy(this); + free(this); return NULL; } DBG1(DBG_LIB, "created TUN device: %s", this->if_name); |