Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add basic support for opening Windows TAP driver
We open it but can't return a file descriptor for it because Windows is...
different. We can't get a file descriptor, and if we could we couldn't
select() on it. We're probably going to have to play tricks with threads
and a socketpair.

It's either that or revamp the whole event loop to cope with the events
that Windows *can* manage to deal with.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Feb 10, 2014
1 parent ce55d92 commit 7c7ad37
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 4 deletions.
6 changes: 5 additions & 1 deletion Makefile.am
Expand Up @@ -22,14 +22,18 @@ library_srcs = ssl.c http.c auth.c library.c compat.c dtls.c cstp.c mainloop.c t
lib_srcs_gnutls = gnutls.c gnutls_pkcs12.c gnutls_tpm.c
lib_srcs_openssl = openssl.c

POTFILES = $(openconnect_SOURCES) $(lib_srcs_openssl) $(lib_srcs_gnutls) $(library_srcs)
POTFILES = $(openconnect_SOURCES) $(lib_srcs_openssl) $(lib_srcs_gnutls) \
$(library_srcs) tun-win32.c

if OPENCONNECT_GNUTLS
library_srcs += $(lib_srcs_gnutls)
endif
if OPENCONNECT_OPENSSL
library_srcs += $(lib_srcs_openssl)
endif
if OPENCONNECT_WIN32
library_srcs += tun-win32.c
endif
libopenconnect_la_SOURCES = version.c $(library_srcs)
libopenconnect_la_CFLAGS = $(AM_CFLAGS) $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS) $(P11KIT_CFLAGS) $(TSS_CFLAGS) $(LIBSTOKEN_CFLAGS) $(LIBOATH_CFLAGS)
libopenconnect_la_LIBADD = $(SSL_LIBS) $(DTLS_SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL) $(P11KIT_LIBS) $(TSS_LIBS) $(LIBSTOKEN_LIBS) $(LIBOATH_LIBS)
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Expand Up @@ -96,6 +96,7 @@ case $host_os in
# u_long and other types don't get defined. OpenBSD is similar.
;;
esac
AM_CONDITIONAL(OPENCONNECT_WIN32, [ test "$have_win" = "yes" ])

AC_CHECK_FUNC(fdevname_r, [AC_DEFINE(HAVE_FDEVNAME_R, 1)], [])
AC_CHECK_FUNC(getline, [AC_DEFINE(HAVE_GETLINE, 1)], [symver_getline="openconnect__getline;"])
Expand Down
5 changes: 5 additions & 0 deletions openconnect-internal.h
Expand Up @@ -277,6 +277,9 @@ struct openconnect_info {
#ifdef __sun__
int ip_fd;
int ip6_fd;
#endif
#ifdef _WIN32
HANDLE tun_fh;
#endif
int tun_fd;
int ssl_fd;
Expand Down Expand Up @@ -408,6 +411,8 @@ ssize_t openconnect__win32_sock_write(gnutls_transport_ptr_t ptr, const void *da
} while (0)

/****************************************************************************/
/* tun-win32.c */
int win32_setup_tun(struct openconnect_info *vpninfo);

/* tun.c */
int tun_mainloop(struct openconnect_info *vpninfo, int *timeout);
Expand Down
188 changes: 188 additions & 0 deletions tun-win32.c
@@ -0,0 +1,188 @@
/*
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2008-2014 Intel Corporation.
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1, as published by the Free Software Foundation.
*
* 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
* Lesser General Public License for more details.
*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winioctl.h>

#include <errno.h>
#include <stdio.h>

#include <openconnect-internal.h>

/*
* TAP-Windows support inspired by http://i3.cs.berkeley.edu/ with
* permission.
*/
#define _TAP_IOCTL(nr) CTL_CODE(FILE_DEVICE_UNKNOWN, nr, METHOD_BUFFERED, \
FILE_ANY_ACCESS)

#define TAP_IOCTL_GET_MAC _TAP_IOCTL(1)
#define TAP_IOCTL_GET_VERSION _TAP_IOCTL(2)
#define TAP_IOCTL_GET_MTU _TAP_IOCTL(3)
#define TAP_IOCTL_GET_INFO _TAP_IOCTL(4)
#define TAP_IOCTL_CONFIG_POINT_TO_POINT _TAP_IOCTL(5)
#define TAP_IOCTL_SET_MEDIA_STATUS _TAP_IOCTL(6)
#define TAP_IOCTL_CONFIG_DHCP_MASQ _TAP_IOCTL(7)
#define TAP_IOCTL_GET_LOG_LINE _TAP_IOCTL(8)
#define TAP_IOCTL_CONFIG_DHCP_SET_OPT _TAP_IOCTL(9)
#define TAP_IOCTL_CONFIG_TUN _TAP_IOCTL(10)

#define TAP_COMPONENT_ID "tap0901"

#define DEVTEMPLATE "\\\\.\\Global\\%s.tap"

#define NETDEV_GUID "{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\"

#define ADAPTERS_KEY CONTROL_KEY "Class\\" NETDEV_GUID
#define CONNECTIONS_KEY CONTROL_KEY "Network\\" NETDEV_GUID

typedef int (tap_callback)(struct openconnect_info *vpninfo, char *idx, char *name);

static int search_taps(struct openconnect_info *vpninfo, tap_callback *cb)
{
LONG status;
HKEY adapters_key;
DWORD len;
char buf[40], name[40];
char keyname[strlen(CONNECTIONS_KEY) + sizeof(buf) + 1 + strlen("\\Connection")];
int i = 0, ret = 0;

status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTERS_KEY, 0,
KEY_READ, &adapters_key);
if (status) {
fprintf(stderr, "Error accessing registry\n");
return -EIO;
}
while (!ret) {
len = sizeof(buf);
status = RegEnumKeyEx(adapters_key, i++, buf, &len,
NULL, NULL, NULL, NULL);
if (status) {
if (status != ERROR_NO_MORE_ITEMS)
ret = -EIO;
break;
}

snprintf(keyname, sizeof(keyname), "%s\\%s",
ADAPTERS_KEY, buf);

len = sizeof(buf);
status = RegGetValue(HKEY_LOCAL_MACHINE, keyname,
"ComponentId", RRF_RT_REG_SZ,
NULL, buf, &len);
if (status || strcmp(buf, TAP_COMPONENT_ID))
continue;

len = sizeof(buf);
status = RegGetValue(HKEY_LOCAL_MACHINE, keyname,
"NetCfgInstanceId", RRF_RT_REG_SZ,
NULL, buf, &len);
if (status)
continue;

snprintf(keyname, sizeof(keyname), "%s\\%s\\Connection",
CONNECTIONS_KEY, buf);

len = sizeof(name);
status = RegGetValue(HKEY_LOCAL_MACHINE, keyname, "Name",
RRF_RT_REG_SZ, NULL, name, &len);
if (status)
continue;

ret = cb(vpninfo, buf, name);
}

RegCloseKey(adapters_key);
return ret;
}

static int open_tun(struct openconnect_info *vpninfo, char *guid, char *name)
{
char devname[80];
HANDLE tun_fh;
ULONG data[3];
DWORD len;

if (vpninfo->ifname && strcmp(name, vpninfo->ifname))
return 0;

snprintf(devname, sizeof(devname), DEVTEMPLATE, guid);
tun_fh = CreateFile(devname, GENERIC_WRITE|GENERIC_READ, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if (tun_fh == INVALID_HANDLE_VALUE) {
vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s\n"),
devname);
return -1;

}
vpn_progress(vpninfo, PRG_TRACE, _("Opened tun device %s\n"), devname);

if (!DeviceIoControl(tun_fh, TAP_IOCTL_GET_VERSION,
NULL, 0, data, sizeof(data), &len, NULL)) {
DWORD err = GetLastError();

vpn_progress(vpninfo, PRG_ERR,
_("Failed to obtain TAP driver version: %lx\n"), err);
return -1;
}
if (data[0] < 9 || (data[0] == 9 && data[1] < 9)) {
vpn_progress(vpninfo, PRG_ERR,
_("Error: TAP-Windows driver v9.9 or greater is required (found %ld.%ld)\n"),
data[0], data[1]);
return -1;
}
vpn_progress(vpninfo, PRG_TRACE, "TAP-Windows driver v%ld.%ld (%ld)\n",
data[0], data[1], data[2]);

data[0] = inet_addr(vpninfo->ip_info.addr);
data[2] = inet_addr(vpninfo->ip_info.netmask);
data[1] = data[0] & data[2];

if (!DeviceIoControl(tun_fh, TAP_IOCTL_CONFIG_TUN,
data, sizeof(data), NULL, 0, &len, NULL)) {
DWORD err = GetLastError();

vpn_progress(vpninfo, PRG_ERR,
_("Failed to set TAP IP addresses: %lx\n"), err);
return -1;
}

data[0] = 1;
if (!DeviceIoControl(tun_fh, TAP_IOCTL_SET_MEDIA_STATUS,
data, sizeof(data[0]), NULL, 0, &len, NULL)) {
DWORD err = GetLastError();

vpn_progress(vpninfo, PRG_ERR,
_("Failed to set TAP media status: %lx\n"), err);
return -1;
}
if (!vpninfo->ifname)
vpninfo->ifname = strdup(name);

vpninfo->tun_fh = tun_fh;
return 1;
}

int win32_setup_tun(struct openconnect_info *vpninfo)
{
if (search_taps(vpninfo, open_tun) != 1)
return -1;

return 0;
}
7 changes: 4 additions & 3 deletions tun.c
Expand Up @@ -487,8 +487,9 @@ static int bsd_open_tun(char *tun_name)
static int os_setup_tun(struct openconnect_info *vpninfo)
{
int tun_fd = -1;

#ifdef IFF_TUN /* Linux */
#ifdef _WIN32
tun_fd = win32_setup_tun(vpninfo);
#elif defined(IFF_TUN) /* Linux */
struct ifreq ifr;
int tunerr;

Expand Down Expand Up @@ -630,7 +631,7 @@ static int os_setup_tun(struct openconnect_info *vpninfo)
return -EIO;
}
#endif
#endif
#endif /* BSD-style */
return tun_fd;
}

Expand Down

0 comments on commit 7c7ad37

Please sign in to comment.