/* * TAP-Windows -- A kernel driver to provide virtual tap * device functionality on Windows. * * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson. * * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc., * and is released under the GPL version 2 (see below). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define INSTANCE_KEY(a) ((PVOID)((a)->m_Extension.m_TapDevice)) #define N_INSTANCE_BUCKETS 256 typedef struct _INSTANCE { struct _INSTANCE *next; TapAdapterPointer m_Adapter; } INSTANCE; typedef struct { INSTANCE *list; MUTEX lock; } INSTANCE_BUCKET; typedef struct { INSTANCE_BUCKET buckets[N_INSTANCE_BUCKETS]; } INSTANCE_HASH; INSTANCE_HASH *g_InstanceHash = NULL; // must return a hash >= 0 and < N_INSTANCE_BUCKETS int InstanceHashValue (PVOID addr) { UCHAR *p = (UCHAR *) &addr; if (sizeof (addr) == 4) return p[0] ^ p[1] ^ p[2] ^ p[3]; else if (sizeof (addr) == 8) return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4] ^ p[5] ^ p[6] ^ p[7]; else { MYASSERT (0); } } BOOLEAN InitInstanceList (VOID) { MYASSERT (g_InstanceHash == NULL); g_InstanceHash = MemAlloc (sizeof (INSTANCE_HASH), TRUE); if (g_InstanceHash) { int i; for (i = 0; i < N_INSTANCE_BUCKETS; ++i) INIT_MUTEX (&g_InstanceHash->buckets[i].lock); return TRUE; } else return FALSE; } int NInstances (VOID) { int i, n = 0; if (g_InstanceHash) { for (i = 0; i < N_INSTANCE_BUCKETS; ++i) { BOOLEAN got_lock; INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i]; ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); if (got_lock) { INSTANCE *current; for (current = ib->list; current != NULL; current = current->next) ++n; RELEASE_MUTEX (&ib->lock); } else return -1; } } return n; } int InstanceMaxBucketSize (VOID) { int i, n = 0; if (g_InstanceHash) { for (i = 0; i < N_INSTANCE_BUCKETS; ++i) { BOOLEAN got_lock; int bucket_size = 0; INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i]; ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); if (got_lock) { INSTANCE *current; for (current = ib->list; current != NULL; current = current->next) ++bucket_size; if (bucket_size > n) n = bucket_size; RELEASE_MUTEX (&ib->lock); } else return -1; } } return n; } VOID FreeInstanceList (VOID) { if (g_InstanceHash) { MYASSERT (NInstances() == 0); MemFree (g_InstanceHash, sizeof (INSTANCE_HASH)); g_InstanceHash = NULL; } } BOOLEAN AddAdapterToInstanceList (TapAdapterPointer p_Adapter) { BOOLEAN got_lock; BOOLEAN ret = FALSE; const int hash = InstanceHashValue(INSTANCE_KEY(p_Adapter)); INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[hash]; DEBUGP (("[TAP] AddAdapterToInstanceList hash=%d\n", hash)); ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); if (got_lock) { INSTANCE *i = MemAlloc (sizeof (INSTANCE), FALSE); if (i) { MYASSERT (p_Adapter); i->m_Adapter = p_Adapter; i->next = ib->list; ib->list = i; ret = TRUE; } RELEASE_MUTEX (&ib->lock); } return ret; } BOOLEAN RemoveAdapterFromInstanceList (TapAdapterPointer p_Adapter) { BOOLEAN got_lock; BOOLEAN ret = FALSE; INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue(INSTANCE_KEY(p_Adapter))]; ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); if (got_lock) { INSTANCE *current, *prev=NULL; for (current = ib->list; current != NULL; current = current->next) { if (current->m_Adapter == p_Adapter) // found match { if (prev) prev->next = current->next; else ib->list = current->next; MemFree (current->m_Adapter, sizeof (TapAdapter)); MemFree (current, sizeof (INSTANCE)); ret = TRUE; break; } prev = current; } RELEASE_MUTEX (&ib->lock); } return ret; } TapAdapterPointer LookupAdapterInInstanceList (PDEVICE_OBJECT p_DeviceObject) { BOOLEAN got_lock; TapAdapterPointer ret = NULL; INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue((PVOID)p_DeviceObject)]; ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock); if (got_lock) { INSTANCE *current, *prev=NULL; for (current = ib->list; current != NULL; current = current->next) { if (p_DeviceObject == INSTANCE_KEY (current->m_Adapter)) // found match { // move it to head of list if (prev) { prev->next = current->next; current->next = ib->list; ib->list = current; } ret = ib->list->m_Adapter; break; } prev = current; } RELEASE_MUTEX (&ib->lock); } return ret; }