diff options
Diffstat (limited to 'openvpn/src/openvpn/tun.c')
-rw-r--r-- | openvpn/src/openvpn/tun.c | 338 |
1 files changed, 271 insertions, 67 deletions
diff --git a/openvpn/src/openvpn/tun.c b/openvpn/src/openvpn/tun.c index 98cb16f3..bd62b392 100644 --- a/openvpn/src/openvpn/tun.c +++ b/openvpn/src/openvpn/tun.c @@ -74,6 +74,12 @@ static void solaris_error_close (struct tuntap *tt, const struct env_set *es, co #include <stropts.h> #endif +#if defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H +#include <sys/kern_control.h> +#include <net/if_utun.h> +#include <sys/sys_domain.h> +#endif + static void clear_tuntap (struct tuntap *tuntap); bool @@ -1284,6 +1290,87 @@ open_null (struct tuntap *tt) tt->actual_name = string_alloc ("null", NULL); } + +#if defined (TARGET_OPENBSD) || (defined(TARGET_DARWIN) && HAVE_NET_IF_UTUN_H) + +/* + * OpenBSD and Mac OS X when using utun + * have a slightly incompatible TUN device from + * the rest of the world, in that it prepends a + * uint32 to the beginning of the IP header + * to designate the protocol (why not just + * look at the version field in the IP header to + * determine v4 or v6?). + * + * We strip off this field on reads and + * put it back on writes. + * + * I have not tested TAP devices on OpenBSD, + * but I have conditionalized the special + * TUN handling code described above to + * go away for TAP devices. + */ + +#include <netinet/ip.h> +#include <sys/uio.h> + +static inline int +header_modify_read_write_return (int len) +{ + if (len > 0) + return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0; + else + return len; +} + +int +write_tun_header (struct tuntap* tt, uint8_t *buf, int len) +{ + if (tt->type == DEV_TYPE_TUN) + { + u_int32_t type; + struct iovec iv[2]; + struct ip *iph; + + iph = (struct ip *) buf; + + if (tt->ipv6 && iph->ip_v == 6) + type = htonl (AF_INET6); + else + type = htonl (AF_INET); + + iv[0].iov_base = &type; + iv[0].iov_len = sizeof (type); + iv[1].iov_base = buf; + iv[1].iov_len = len; + + return header_modify_read_write_return (writev (tt->fd, iv, 2)); + } + else + return write (tt->fd, buf, len); +} + +int +read_tun_header (struct tuntap* tt, uint8_t *buf, int len) +{ + if (tt->type == DEV_TYPE_TUN) + { + u_int32_t type; + struct iovec iv[2]; + + iv[0].iov_base = &type; + iv[0].iov_len = sizeof (type); + iv[1].iov_base = buf; + iv[1].iov_len = len; + + return header_modify_read_write_return (readv (tt->fd, iv, 2)); + } + else + return read (tt->fd, buf, len); +} +#endif + + #ifndef WIN32 static void open_tun_generic (const char *dev, const char *dev_type, const char *dev_node, @@ -2062,23 +2149,6 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) #elif defined(TARGET_OPENBSD) -/* - * OpenBSD has a slightly incompatible TUN device from - * the rest of the world, in that it prepends a - * uint32 to the beginning of the IP header - * to designate the protocol (why not just - * look at the version field in the IP header to - * determine v4 or v6?). - * - * We strip off this field on reads and - * put it back on writes. - * - * I have not tested TAP devices on OpenBSD, - * but I have conditionalized the special - * TUN handling code described above to - * go away for TAP devices. - */ - void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { @@ -2145,59 +2215,16 @@ close_tun (struct tuntap* tt) } } -static inline int -openbsd_modify_read_write_return (int len) -{ - if (len > 0) - return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0; - else - return len; -} - int -write_tun (struct tuntap* tt, uint8_t *buf, int len) +write_tun(struct tuntap *tt, uint8_t *buf, int len) { - if (tt->type == DEV_TYPE_TUN) - { - u_int32_t type; - struct iovec iv[2]; - struct ip *iph; - - iph = (struct ip *) buf; - - if (tt->ipv6 && iph->ip_v == 6) - type = htonl (AF_INET6); - else - type = htonl (AF_INET); - - iv[0].iov_base = &type; - iv[0].iov_len = sizeof (type); - iv[1].iov_base = buf; - iv[1].iov_len = len; - - return openbsd_modify_read_write_return (writev (tt->fd, iv, 2)); - } - else - return write (tt->fd, buf, len); + return write_tun_header (tt, buf, len); } int -read_tun (struct tuntap* tt, uint8_t *buf, int len) +read_tun (struct tuntap *tt, uint8_t *buf, int len) { - if (tt->type == DEV_TYPE_TUN) - { - u_int32_t type; - struct iovec iv[2]; - - iv[0].iov_base = &type; - iv[0].iov_len = sizeof (type); - iv[1].iov_base = buf; - iv[1].iov_len = len; - - return openbsd_modify_read_write_return (readv (tt->fd, iv, 2)); - } - else - return read (tt->fd, buf, len); + return read_tun_header (tt, buf, len); } #elif defined(TARGET_NETBSD) @@ -2557,10 +2584,177 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len) * pointing to lo0. Need to unconfigure... (observed on 10.5) */ +/* + * utun is the native Darwin tun driver present since at least 10.7 + * Thanks goes to Jonathan Levin for providing an example how to utun + * (http://newosxbook.com/src.jl?tree=listings&file=17-15-utun.c) + */ + +#ifdef HAVE_NET_IF_UTUN_H + +/* Helper functions that tries to open utun device + return -2 on early initialization failures (utun not supported + at all (old OS X) and -1 on initlization failure of utun + device (utun works but utunX is already used */ +static +int utun_open_helper (struct ctl_info ctlInfo, int utunnum) +{ + struct sockaddr_ctl sc; + int fd; + + fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + + if (fd < 0) + { + msg (M_INFO, "Opening utun (%s): %s", "socket(SYSPROTO_CONTROL)", + strerror (errno)); + return -2; + } + + if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) + { + close (fd); + msg (M_INFO, "Opening utun (%s): %s", "ioctl(CTLIOCGINFO)", + strerror (errno)); + return -2; + } + + + sc.sc_id = ctlInfo.ctl_id; + sc.sc_len = sizeof(sc); + sc.sc_family = AF_SYSTEM; + sc.ss_sysaddr = AF_SYS_CONTROL; + + sc.sc_unit = utunnum+1; + + + /* If the connect is successful, a utun%d device will be created, where "%d" + * is (sc.sc_unit - 1) */ + + if (connect (fd, (struct sockaddr *)&sc, sizeof(sc)) < 0) + { + msg (M_INFO, "Opening utun (%s): %s", "connect(AF_SYS_CONTROL)", + strerror (errno)); + close(fd); + return -1; + } + + set_nonblock (fd); + set_cloexec (fd); /* don't pass fd to scripts */ + + return fd; +} + +void +open_darwin_utun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) +{ + struct ctl_info ctlInfo; + int fd; + char utunname[20]; + int utunnum =-1; + socklen_t utunname_len = sizeof(utunname); + + /* dev_node is simply utun, do the normal dynamic utun + * otherwise try to parse the utun number */ + if (dev_node && !strcmp ("utun", dev_node)==0) + { + if (!sscanf (dev_node, "utun%d", &utunnum)==1) + msg (M_FATAL, "Cannot parse 'dev-node %s' please use 'dev-node utunX'" + "to use a utun device number X", dev_node); + } + + + + CLEAR (ctlInfo); + if (strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)) >= + sizeof(ctlInfo.ctl_name)) + { + msg (M_ERR, "Opening utun: UTUN_CONTROL_NAME too long"); + } + + /* try to open first available utun device if no specific utun is requested */ + if (utunnum == -1) + { + for (utunnum=0; utunnum<255; utunnum++) + { + fd = utun_open_helper (ctlInfo, utunnum); + /* Break if the fd is valid, + * or if early initalization failed (-2) */ + if (fd !=-1) + break; + } + } + else + { + fd = utun_open_helper (ctlInfo, utunnum); + } + + /* opening an utun device failed */ + tt->fd = fd; + + if (fd < 0) + return; + + /* Retrieve the assigned interface name. */ + if (getsockopt (fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len)) + msg (M_ERR | M_ERRNO, "Error retrieving utun interface name"); + + tt->actual_name = string_alloc (utunname, NULL); + + msg (M_INFO, "Opened utun device %s", utunname); + tt->is_utun = true; +} + +#endif + void open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) { - open_tun_generic (dev, dev_type, dev_node, true, true, tt); +#ifdef HAVE_NET_IF_UTUN_H + /* If dev_node does not start start with utun assume regular tun/tap */ + if ((!dev_node && tt->type==DEV_TYPE_TUN) || + (dev_node && !strncmp (dev_node, "utun", 4))) + { + + /* Check if user has specific dev_type tap and forced utun with + dev-node utun */ + if (tt->type!=DEV_TYPE_TUN) + msg (M_FATAL, "Cannot use utun devices with --dev-type %s", + dev_type_string (dev, dev_type)); + + /* Try utun first and fall back to normal tun if utun fails + and dev_node is not specified */ + open_darwin_utun(dev, dev_type, dev_node, tt); + + if (!tt->is_utun) + { + if (!dev_node) + { + /* No explicit utun and utun failed, try the generic way) */ + msg (M_INFO, "Failed to open utun device. Falling back to /dev/tun device"); + open_tun_generic (dev, dev_type, NULL, true, true, tt); + } + else + { + /* Specific utun device or generic utun request with no tun + fall back failed, consider this a fatal failure */ + msg (M_FATAL, "Cannot open utun device"); + } + } + } + else +#endif + { + + /* Use plain dev-node tun to select /dev/tun style + * Unset dev_node variable prior to passing to open_tun_generic to + * let open_tun_generic pick the first available tun device */ + + if (dev_node && strcmp (dev_node, "tun")==0) + dev_node=NULL; + + open_tun_generic (dev, dev_type, dev_node, true, true, tt); + } } void @@ -2593,13 +2787,23 @@ close_tun (struct tuntap* tt) int write_tun (struct tuntap* tt, uint8_t *buf, int len) { - return write (tt->fd, buf, len); +#ifdef HAVE_NET_IF_UTUN_H + if (tt->is_utun) + return write_tun_header (tt, buf, len); + else +#endif + return write (tt->fd, buf, len); } int read_tun (struct tuntap* tt, uint8_t *buf, int len) { - return read (tt->fd, buf, len); +#ifdef HAVE_NET_IF_UTUN_H + if (tt->is_utun) + return read_tun_header (tt, buf, len); + else +#endif + return read (tt->fd, buf, len); } #elif defined(WIN32) |