intptr_t os_setup_tun(struct openconnect_info *vpninfo) { int tun_fd = -1; static char tun_name[80]; int unit_nr; tun_fd = open("/dev/tun", O_RDWR); if (tun_fd < 0) { vpn_perror(vpninfo, _("open /dev/tun")); return -EIO; } unit_nr = ioctl(tun_fd, TUNNEWPPA, -1); if (unit_nr < 0) { vpn_perror(vpninfo, _("Failed to create new tun")); close(tun_fd); return -EIO; } if (ioctl(tun_fd, I_SRDOPT, RMSGD) < 0) { vpn_perror(vpninfo, _("Failed to put tun file descriptor into message-discard mode")); close(tun_fd); return -EIO; } sprintf(tun_name, "tun%d", unit_nr); vpninfo->ifname = strdup(tun_name); vpninfo->ip_fd = link_proto(vpninfo, unit_nr, "/dev/udp", IFF_IPV4); if (vpninfo->ip_fd < 0) { close(tun_fd); return -EIO; } if (vpninfo->ip_info.addr6 || vpninfo->ip_info.netmask6) { vpninfo->ip6_fd = link_proto(vpninfo, unit_nr, "/dev/udp6", IFF_IPV6); if (vpninfo->ip6_fd < 0) { close(tun_fd); close(vpninfo->ip_fd); vpninfo->ip_fd = -1; return -EIO; } } else vpninfo->ip6_fd = -1; return tun_fd; }
intptr_t os_setup_tun(struct openconnect_info *vpninfo) { int tun_fd = -1; #ifdef IFF_TUN /* Linux */ struct ifreq ifr; int tunerr; tun_fd = open("/dev/net/tun", O_RDWR); if (tun_fd < 0) { /* Android has /dev/tun instead of /dev/net/tun Since other systems might have too, just try it as a fallback instead of using ifdef __ANDROID__ */ tunerr = errno; tun_fd = open("/dev/tun", O_RDWR); } if (tun_fd < 0) { /* If the error on /dev/tun is ENOENT, that's boring. Use the error we got on /dev/net/tun instead */ if (errno != ENOENT) tunerr = errno; vpn_progress(vpninfo, PRG_ERR, _("Failed to open tun device: %s\n"), strerror(tunerr)); return -EIO; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; if (vpninfo->ifname) strncpy(ifr.ifr_name, vpninfo->ifname, sizeof(ifr.ifr_name) - 1); if (ioctl(tun_fd, TUNSETIFF, (void *) &ifr) < 0) { vpn_progress(vpninfo, PRG_ERR, _("TUNSETIFF failed: %s\n"), strerror(errno)); close(tun_fd); return -EIO; } if (!vpninfo->ifname) vpninfo->ifname = strdup(ifr.ifr_name); #elif defined(__sun__) static char tun_name[80]; int unit_nr; tun_fd = open("/dev/tun", O_RDWR); if (tun_fd < 0) { perror(_("open /dev/tun")); return -EIO; } unit_nr = ioctl(tun_fd, TUNNEWPPA, -1); if (unit_nr < 0) { perror(_("Failed to create new tun")); close(tun_fd); return -EIO; } if (ioctl(tun_fd, I_SRDOPT, RMSGD) < 0) { perror(_("Failed to put tun file descriptor into message-discard mode")); close(tun_fd); return -EIO; } sprintf(tun_name, "tun%d", unit_nr); vpninfo->ifname = strdup(tun_name); vpninfo->ip_fd = link_proto(unit_nr, "/dev/udp", IFF_IPV4); if (vpninfo->ip_fd < 0) { close(tun_fd); return -EIO; } if (vpninfo->ip_info.addr6) { vpninfo->ip6_fd = link_proto(unit_nr, "/dev/udp6", IFF_IPV6); if (vpninfo->ip6_fd < 0) { close(tun_fd); close(vpninfo->ip_fd); vpninfo->ip_fd = -1; return -EIO; } } else vpninfo->ip6_fd = -1; #else /* BSD et al have /dev/tun$x devices */ static char tun_name[80]; int i; if (vpninfo->ifname) { char *endp = NULL; if (strncmp(vpninfo->ifname, "tun", 3) || ((void)strtol(vpninfo->ifname + 3, &endp, 10), !endp) || *endp) { vpn_progress(vpninfo, PRG_ERR, _("Invalid interface name '%s'; must match 'tun%%d'\n"), vpninfo->ifname); return -EINVAL; } snprintf(tun_name, sizeof(tun_name), "/dev/%s", vpninfo->ifname); tun_fd = bsd_open_tun(tun_name); if (tun_fd < 0) { int err = errno; vpn_progress(vpninfo, PRG_ERR, _("Cannot open '%s': %s\n"), tun_name, strerror(err)); return -EINVAL; } } #ifdef HAVE_FDEVNAME_R /* We don't have to iterate over the possible devices; on FreeBSD at least, opening /dev/tun will give us the next available device. */ if (tun_fd < 0) { tun_fd = open("/dev/tun", O_RDWR); if (tun_fd >= 0) { if (!fdevname_r(tun_fd, tun_name, sizeof(tun_name)) || strncmp(tun_name, "tun", 3)) { close(tun_fd); tun_fd = -1; } else vpninfo->ifname = strdup(tun_name); } } #endif if (tun_fd < 0) { for (i = 0; i < 255; i++) { sprintf(tun_name, "/dev/tun%d", i); tun_fd = bsd_open_tun(tun_name); if (tun_fd >= 0) break; } if (tun_fd < 0) { vpn_progress(vpninfo, PRG_ERR, _("Failed to open tun device: %s\n"), strerror(errno)); return -EIO; } vpninfo->ifname = strdup(tun_name + 5); } #ifdef TUNSIFHEAD i = 1; if (ioctl(tun_fd, TUNSIFHEAD, &i) < 0) { perror(_("TUNSIFHEAD")); close(tun_fd); return -EIO; } #endif #endif /* BSD-style */ /* Ancient vpnc-scripts might not get this right */ set_tun_mtu(vpninfo); return tun_fd; }