Ejemplo n.º 1
0
intptr_t os_setup_tun(struct openconnect_info *vpninfo)
{
	static char tun_name[80];
	int unit_nr = 0;
	int tun_fd = -1;

#if defined(__APPLE__) && defined (HAVE_NET_UTUN_H)
	/* OS X (since 10.6) can do this as well as the traditional
	   BSD devices supported via tuntaposx. */
	struct sockaddr_ctl sc;
	struct ctl_info ci;

	if (vpninfo->ifname) {
		char *endp = NULL;

		if (!strncmp(vpninfo->ifname, "tun", 3))
			goto do_bsdtun;

		if (strncmp(vpninfo->ifname, "utun", 4) ||
		    (unit_nr = strtol(vpninfo->ifname + 4, &endp, 10), !endp) ||
		    (unit_nr && vpninfo->ifname[4] == '0') ||
		    *endp) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Invalid interface name '%s'; must match 'utun%%d' or 'tun%%d'\n"),
				     vpninfo->ifname);
			return -EINVAL;
		}
	}

	tun_fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
	if (tun_fd < 0) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to open SYSPROTO_CONTROL socket: %s\n"),
			     strerror(errno));
		goto utun_fail;
	}

	snprintf(ci.ctl_name, sizeof(ci.ctl_name), UTUN_CONTROL_NAME);

	if (ioctl(tun_fd, CTLIOCGINFO, &ci) == -1) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to query utun control id: %s\n"),
			       strerror(errno));
		close(tun_fd);
		goto utun_fail;
	}

	sc.sc_id = ci.ctl_id;
	sc.sc_len = sizeof(sc);
	sc.sc_family = AF_SYSTEM;
	sc.ss_sysaddr = AF_SYS_CONTROL;

	do {
		sc.sc_unit = unit_nr + 1;

		if (!connect(tun_fd, (struct sockaddr * )&sc, sizeof(sc))) {
			if (!vpninfo->ifname &&
			    asprintf(&vpninfo->ifname, "utun%d", unit_nr) == -1) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to allocate utun device name\n"));
				close(tun_fd);
				goto utun_fail;
			}
			return tun_fd;
		}
		unit_nr++;

	} while (sc.sc_unit < 255 && !vpninfo->ifname);

	vpn_progress(vpninfo, PRG_ERR,
		     _("Failed to connect utun unit: %s\n"),
		     strerror(errno));
	close(tun_fd);

 utun_fail:
	/* If we were explicitly asked for a utun device, fail. Else try tuntaposx */
	if (vpninfo->ifname)
		return -EIO;

	tun_fd = -1;
 do_bsdtun:
#endif /* __APPLE__ && HAVE_NET_UTUN_H */

	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 (unit_nr = 0; unit_nr < 255; unit_nr++) {
			sprintf(tun_name, "/dev/tun%d", unit_nr);
			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
	unit_nr = 1;
	if (ioctl(tun_fd, TUNSIFHEAD, &unit_nr) < 0) {
		vpn_perror(vpninfo, _("TUNSIFHEAD"));
		close(tun_fd);
		return -EIO;
	}
#endif

	/* Ancient vpnc-scripts might not get this right */
	set_tun_mtu(vpninfo);

	return tun_fd;
}
Ejemplo n.º 2
0
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;
}