nexmon – Rev 1
?pathlinks?
/*
* Copyright (c) 2007, 2008, Andrea Bittau <a.bittau@cs.ucl.ac.uk>
*
* OS dependent API for cygwin. TAP routines
*
* 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 Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include "osdep.h"
#include <windows.h>
#include <winioctl.h>
#include <ipexport.h>
#include <iptypes.h>
#include <initguid.h>
#include <devguid.h>
#include <setupapi.h>
#include "network.h"
#include "tap-win32/common.h"
extern DWORD WINAPI GetAdaptersInfo(PIP_ADAPTER_INFO pAdapterInfo,PULONG pOutBufLen);
extern DWORD WINAPI AddIPAddress(IPAddr Address,IPMask IpMask,DWORD IfIndex,PULONG NTEContext,PULONG NTEInstance);
extern DWORD WINAPI DeleteIPAddress(ULONG NTEContext);
extern int cygwin_read_reader(int fd, int plen, void *dst, int len);
static void *ti_reader(void *arg);
struct tip_cygwin {
char tc_name[MAX_IFACE_NAME];
HANDLE tc_h;
pthread_t tc_reader;
volatile int tc_running;
int tc_pipe[2]; /* reader -> parent */
pthread_mutex_t tc_mtx;
HKEY tc_key;
char tc_guid[256];
};
/**
* Stop the reader thread (if it is running)
* @return 0 if stopped or -1 if it failed to stop it
*/
static int stop_reader(struct tip_cygwin *priv)
{
if (priv->tc_running == 1) {
int tries = 3;
priv->tc_running = 0;
while ((priv->tc_running != -1) && tries--)
sleep(1);
if (tries <= 0)
return -1;
}
return 0;
}
/**
* Start reader thread
* @return -1 if failed to start thread or 0 if it is successful
*/
static int start_reader(struct tip_cygwin *priv)
{
priv->tc_running = 2;
if (pthread_create(&priv->tc_reader, NULL, ti_reader, priv))
return -1;
priv->tc_running = 1;
return 0;
}
/**
* Change status (enable/disable) of the device
*/
static int ti_media_status(struct tip_cygwin *priv, int on)
{
ULONG s = on;
DWORD len;
if (!DeviceIoControl(priv->tc_h, TAP_IOCTL_SET_MEDIA_STATUS, &s,
sizeof(s), &s, sizeof(s), &len, NULL))
return -1;
return 0;
}
/**
* Try opening device
*/
static int ti_try_open(struct tip_cygwin *priv, char *guid)
{
int any = priv->tc_guid[0] == 0;
char device[256];
HANDLE h;
if (!any && strcmp(priv->tc_guid, guid) != 0)
return 0;
/* open the device */
snprintf(device, sizeof(device), "%s%s%s",
USERMODEDEVICEDIR, guid, TAPSUFFIX);
h = CreateFile(device, GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM
| FILE_FLAG_OVERLAPPED, 0);
if (h == INVALID_HANDLE_VALUE) {
if (any)
return 0;
else
return -1;
}
priv->tc_h = h;
/* XXX check tap version */
/* bring iface up */
if (ti_media_status(priv, 1) == -1)
return -1;
/* XXX grab printable name */
snprintf(priv->tc_name, sizeof(priv->tc_name)-1, "%s", guid);
if (any)
snprintf(priv->tc_guid, sizeof(priv->tc_guid), "%s", guid);
return 1;
}
/**
* Read registry value
* @param key Registry key
* @return 0 if successful, -1 if it failed
*/
static int ti_read_reg(struct tip_cygwin *priv, char *key, char *res, int len)
{
DWORD dt, l = len;
if (RegQueryValueEx(priv->tc_key, key, NULL, &dt,
(unsigned char*) res, &l) != ERROR_SUCCESS)
return -1;
if (dt != REG_SZ)
return -1;
if ((int)l > len)
return -1;
return 0;
}
static int ti_get_devs_component(struct tip_cygwin *priv, char *name)
{
char key[256];
int rc = 0;
snprintf(key, sizeof(key)-1, "%s\\%s", ADAPTER_KEY, name);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WRITE,
&priv->tc_key) != ERROR_SUCCESS)
return -1;
if (ti_read_reg(priv, "ComponentId", key, sizeof(key)) == -1)
goto out;
/* make sure component id matches */
if (strcmp(key, TAP_COMPONENT_ID) != 0)
goto out;
/* get guid */
if (ti_read_reg(priv, "NetCfgInstanceId", key, sizeof(key)) == -1)
goto out;
rc = ti_try_open(priv, key);
out:
if (rc != 1) {
RegCloseKey(priv->tc_key);
priv->tc_key = 0;
}
return rc;
}
static int ti_do_open_cygwin(struct tip_cygwin *priv)
{
int rc = -1;
HKEY ak47;
int i;
char name[256];
DWORD len;
/* open network driver key */
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &ak47)
!= ERROR_SUCCESS)
return -1;
/* find tap */
for (i = 0;; i++) {
len = sizeof(name);
if (RegEnumKeyEx(ak47, i, name, &len, NULL, NULL, NULL, NULL)
!= ERROR_SUCCESS)
break;
rc = ti_get_devs_component(priv, name);
if (rc)
break;
rc = -1;
}
RegCloseKey(ak47);
if (rc == 1)
rc = 0;
return rc;
}
static void ti_do_free(struct tif *ti)
{
struct tip_cygwin *priv = ti_priv(ti);
/* stop reader */
stop_reader(priv);
if (priv->tc_pipe[0]) {
close(priv->tc_pipe[0]);
close(priv->tc_pipe[1]);
}
/* close card */
if (priv->tc_h) {
ti_media_status(priv, 0);
CloseHandle(priv->tc_h);
}
if (priv->tc_key)
RegCloseKey(priv->tc_key);
free(priv);
free(ti);
}
static void ti_close_cygwin(struct tif *ti)
{
ti_do_free(ti);
}
static char *ti_name_cygwin(struct tif *ti)
{
struct tip_cygwin *priv = ti_priv(ti);
return priv->tc_name;
}
/* XXX */
static int ti_is_us(struct tip_cygwin *priv, HDEVINFO *hdi,
SP_DEVINFO_DATA *did)
{
char buf[256];
DWORD len = sizeof(buf), dt;
if (priv) {} /* XXX unused */
if (!SetupDiGetDeviceRegistryProperty(*hdi, did, SPDRP_DEVICEDESC, &dt,
(unsigned char*)buf, len, &len))
return 0;
if (dt != REG_SZ)
return 0;
return strstr(buf, "TAP-Win32") != NULL;
}
static int ti_reset_state(HDEVINFO *hdi, SP_DEVINFO_DATA *did, DWORD state)
{
SP_PROPCHANGE_PARAMS parm;
parm.ClassInstallHeader.cbSize = sizeof(parm.ClassInstallHeader);
parm.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
parm.Scope = DICS_FLAG_GLOBAL;
parm.StateChange = state;
if (!SetupDiSetClassInstallParams(*hdi, did, (SP_CLASSINSTALL_HEADER*)
&parm, sizeof(parm)))
return -1;
if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, *hdi, did))
return -1;
return 0;
}
/**
* Reset the device
* @return 0 if successful, -1 if it failed
*/
static int ti_do_reset(HDEVINFO *hdi, SP_DEVINFO_DATA *did)
{
int rc;
rc = ti_reset_state(hdi, did, DICS_DISABLE);
if (rc)
return rc;
return ti_reset_state(hdi, did, DICS_ENABLE);
}
static int ti_restart(struct tip_cygwin *priv)
{
/* kill handle to if */
if (priv->tc_h)
CloseHandle(priv->tc_h);
/* stop reader */
if (stop_reader(priv))
return -1;
/* reopen dev */
if (ti_do_open_cygwin(priv))
return -1;
return start_reader(priv);
}
static int ti_reset(struct tip_cygwin *priv)
{
HDEVINFO hdi;
SP_DEVINFO_DATA did;
int i;
int rc = -1;
hdi = SetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL,
DIGCF_PRESENT);
if (hdi == INVALID_HANDLE_VALUE)
return -1;
/* find device */
for (i = 0;; i++) {
did.cbSize = sizeof(did);
if (!SetupDiEnumDeviceInfo(hdi, i, &did))
break;
if (!ti_is_us(priv, &hdi, &did))
continue;
rc = ti_do_reset(&hdi, &did);
if (rc)
break;
rc = ti_restart(priv);
break;
}
SetupDiDestroyDeviceInfoList(hdi);
return rc;
}
static int ti_set_mtu_cygwin(struct tif *ti, int mtu)
{
struct tip_cygwin *priv = ti_priv(ti);
char m[16];
char mold[sizeof(m)];
char *key = "MTU";
/* check if reg remains unchanged to avoid reset */
snprintf(m, sizeof(m)-1, "%d", mtu);
if (ti_read_reg(priv, key, mold, sizeof(mold)) != -1) {
if (strcmp(m, mold) == 0)
return 0;
}
/* change */
if (RegSetValueEx(priv->tc_key, key, 0, REG_SZ,
(unsigned char *) m, strlen(m)+1) != ERROR_SUCCESS)
return -1;
if (ti_reset(priv) == -1)
return -1;
return 0;
}
/**
* Set device MAC address
* @param mac New MAC address
* @return -1 if it failed, 0 on success
*/
static int ti_set_mac_cygwin(struct tif *ti, unsigned char *mac)
{
struct tip_cygwin *priv = ti_priv(ti);
char str[2*6+1];
char strold[sizeof(str)];
int i;
char *key = "MAC";
/* convert */
str[0] = 0;
for (i = 0; i < 6; i++) {
char tmp[3];
if (sprintf(tmp, "%.2X", *mac++) != 2)
return -1;
strcat(str, tmp);
}
/* check if changed */
if (ti_read_reg(priv, key, strold, sizeof(strold)) != -1) {
if (strcmp(str, strold) == 0)
return 0;
}
/* own */
if (RegSetValueEx(priv->tc_key, key, 0, REG_SZ, (unsigned char *)str,
strlen(str)+1) != ERROR_SUCCESS)
return -1;
if (ti_reset(priv) == -1)
return -1;
return 0;
}
/**
* Set device IP address
* @param ip New IP address
* @return -1 if it failed, 0 on success
*/
static int ti_set_ip_cygwin(struct tif *ti, struct in_addr *ip)
{
struct tip_cygwin *priv = ti_priv(ti);
ULONG ctx, inst;
IP_ADAPTER_INFO ai[16];
DWORD len = sizeof(ai);
PIP_ADAPTER_INFO p;
PIP_ADDR_STRING ips;
if (GetAdaptersInfo(ai, &len) != ERROR_SUCCESS)
return -1;
p = ai;
while (p) {
if (strcmp(priv->tc_guid, p->AdapterName) != 0) {
p = p->Next;
continue;
}
/* delete ips */
ips = &p->IpAddressList;
while (ips) {
DeleteIPAddress(ips->Context);
ips = ips->Next;
}
/* add ip */
if (AddIPAddress(ip->s_addr, htonl(0xffffff00),
p->Index, &ctx, &inst) != NO_ERROR)
return -1;
break;
}
return 0;
}
static int ti_fd_cygwin(struct tif *ti)
{
struct tip_cygwin *priv = ti_priv(ti);
return priv->tc_pipe[0];
}
static int ti_read_cygwin(struct tif *ti, void *buf, int len)
{
struct tip_cygwin *priv = ti_priv(ti);
int plen;
if (priv->tc_running != 1)
return -1;
/* read len */
if (net_read_exact(priv->tc_pipe[0], &plen, sizeof(plen)) == -1)
return -1;
return cygwin_read_reader(priv->tc_pipe[0], plen, buf, len);
}
static int ti_wait_complete(struct tip_cygwin *priv, OVERLAPPED *o)
{
DWORD sz;
if (!GetOverlappedResult(priv->tc_h, o, &sz, TRUE))
return -1;
return sz;
}
static int ti_do_io(struct tip_cygwin *priv, void *buf, int len,
OVERLAPPED *o, int wr)
{
BOOL rc;
DWORD sz;
int err;
/* setup overlapped */
memset(o, 0, sizeof(*o));
/* do io */
if (wr)
rc = WriteFile(priv->tc_h, buf, len, &sz, o);
else
rc = ReadFile(priv->tc_h, buf, len, &sz, o);
/* done */
if (rc)
return sz;
if ((err = GetLastError()) != ERROR_IO_PENDING)
return -1;
return 0; /* pending */
}
static int ti_do_io_lock(struct tip_cygwin *priv, void *buf, int len,
OVERLAPPED *o, int wr)
{
int rc;
if (pthread_mutex_lock(&priv->tc_mtx))
return -1;
rc = ti_do_io(priv, buf, len, o, wr);
if (pthread_mutex_unlock(&priv->tc_mtx))
return -1;
/* done */
if (rc)
return rc;
return ti_wait_complete(priv, o);
}
static int ti_write_cygwin(struct tif *ti, void *buf, int len)
{
struct tip_cygwin *priv = ti_priv(ti);
OVERLAPPED o;
return ti_do_io_lock(priv, buf, len, &o, 1);
}
static int ti_read_packet(struct tip_cygwin *priv, void *buf, int len)
{
OVERLAPPED o;
int rc;
while (priv->tc_running) {
rc = ti_do_io_lock(priv, buf, len, &o, 0);
if (rc)
return rc;
}
return -1;
}
static void *ti_reader(void *arg)
{
struct tip_cygwin *priv = arg;
unsigned char buf[2048];
int len;
while (priv->tc_running) {
/* read a packet */
if ((len = ti_read_packet(priv, buf, sizeof(buf))) == -1)
break;
assert(len > 0);
/* write it's length */
if (write(priv->tc_pipe[1], &len, sizeof(len)) != sizeof(len))
break;
/* write payload */
if (write(priv->tc_pipe[1], buf, len) != len)
break;
}
priv->tc_running = -1;
return NULL;
}
static struct tif *ti_open_cygwin(char *iface)
{
struct tif *ti;
struct tip_cygwin *priv;
/* setup ti struct */
ti = ti_alloc(sizeof(*priv));
if (!ti)
return NULL;
priv = ti_priv(ti);
ti->ti_name = ti_name_cygwin;
ti->ti_set_mtu = ti_set_mtu_cygwin;
ti->ti_close = ti_close_cygwin;
ti->ti_fd = ti_fd_cygwin;
ti->ti_read = ti_read_cygwin;
ti->ti_write = ti_write_cygwin;
ti->ti_set_mac = ti_set_mac_cygwin;
ti->ti_set_ip = ti_set_ip_cygwin;
/* setup iface */
if (iface)
snprintf(priv->tc_guid, sizeof(priv->tc_guid), "%s", iface);
if (ti_do_open_cygwin(priv) == -1)
goto err;
/* setup reader */
if (pipe(priv->tc_pipe) == -1)
goto err;
if (pthread_mutex_init(&priv->tc_mtx, NULL))
goto err;
/* launch reader */
if (start_reader(priv))
goto err;
return ti;
err:
ti_do_free(ti);
return NULL;
}
struct tif *ti_open(char *iface)
{
return ti_open_cygwin(iface);
}