diff options
Diffstat (limited to 'openvpn/tap-win32/tapdrvr.c')
-rwxr-xr-x | openvpn/tap-win32/tapdrvr.c | 2842 |
1 files changed, 2842 insertions, 0 deletions
diff --git a/openvpn/tap-win32/tapdrvr.c b/openvpn/tap-win32/tapdrvr.c new file mode 100755 index 0000000..9c6304b --- /dev/null +++ b/openvpn/tap-win32/tapdrvr.c @@ -0,0 +1,2842 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device + * functionality on Windows. Originally derived + * from the CIPE-Win32 project by Damion K. Wilson, + * with extensive modifications by James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) 2002-2006 OpenVPN Solutions LLC, + * 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 + */ + +//====================================================== +// This driver is designed to work on Win 2000 or higher +// versions of Windows. +// +// It is SMP-safe and handles NDIS 5 power management. +// +// By default we operate as a "tap" virtual ethernet +// 802.3 interface, but we can emulate a "tun" +// interface (point-to-point IPv4) through the +// TAP_IOCTL_CONFIG_POINT_TO_POINT or +// TAP_IOCTL_CONFIG_TUN ioctl. +//====================================================== + +#define NDIS_MINIPORT_DRIVER +#define BINARY_COMPATIBLE 0 +#define NDIS50_MINIPORT 1 +#define NDIS_WDM 0 +#define NDIS50 1 +#define NTSTRSAFE_LIB + +// Debug info output +#define ALSO_DBGPRINT 1 +#define DEBUGP_AT_DISPATCH 0 + +//======================================================== +// Check for truncated IPv4 packets, log errors if found. +//======================================================== +#define PACKET_TRUNCATION_CHECK 1 // JYFIXME + +//======================================================== +// EXPERIMENTAL -- Configure TAP device object to be +// accessible from non-administrative accounts, based +// on an advanced properties setting. +// +// Duplicates the functionality of OpenVPN's +// --allow-nonadmin directive. +//======================================================== +#define ENABLE_NONADMIN 1 // JYFIXME + +#include <ndis.h> +#include <ntstrsafe.h> +#include <ntddk.h> + +#include "lock.h" +#include "constants.h" +#include "common.h" +#include "proto.h" +#include "error.h" +#include "endian.h" +#include "dhcp.h" +#include "types.h" +#include "prototypes.h" + +#include "mem.c" +#include "macinfo.c" +#include "error.c" +#include "dhcp.c" +#include "instance.c" + +#define IS_UP(ta) \ + ((ta)->m_InterfaceIsRunning && (ta)->m_Extension.m_TapIsRunning) + +#define INCREMENT_STAT(s) ++(s) + +#define NAME_BUFFER_SIZE 80 + +//======================================================== +// Globals +//======================================================== + +NDIS_HANDLE g_NdisWrapperHandle; + +const UINT g_SupportedOIDList[] = { + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAC_OPTIONS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_DRIVER_VERSION, + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_GEN_RCV_NO_BUFFER, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_PROTOCOL_OPTIONS, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_SUPPORTED_LIST +}; + +//============================================================ +// Driver Entry +//============================================================ +#pragma NDIS_INIT_FUNCTION (DriverEntry) + +NTSTATUS +DriverEntry (IN PDRIVER_OBJECT p_DriverObject, + IN PUNICODE_STRING p_RegistryPath) +{ + NDIS_STATUS l_Status = NDIS_STATUS_FAILURE; + NDIS_MINIPORT_CHARACTERISTICS *l_Properties = NULL; + + //======================================================== + // Notify NDIS that a new miniport driver is initializing. + //======================================================== + + NdisMInitializeWrapper (&g_NdisWrapperHandle, + p_DriverObject, + p_RegistryPath, NULL); + + //====================== + // Global initialization + //====================== + +#if DBG + MyDebugInit (10000); // Allocate debugging text space +#endif + + if (!InitInstanceList ()) + { + DEBUGP (("[TAP] Allocation failed for adapter instance list\n")); + goto cleanup; + } + + //======================================= + // Set and register miniport entry points + //======================================= + + l_Properties = MemAlloc (sizeof (NDIS_MINIPORT_CHARACTERISTICS), TRUE); + + if (l_Properties == NULL) + { + DEBUGP (("[TAP] Allocation failed for miniport entry points\n")); + goto cleanup; + } + + l_Properties->MajorNdisVersion = TAP_NDIS_MAJOR_VERSION; + l_Properties->MinorNdisVersion = TAP_NDIS_MINOR_VERSION; + l_Properties->InitializeHandler = AdapterCreate; + l_Properties->HaltHandler = AdapterHalt; + l_Properties->ResetHandler = AdapterReset; /* DISPATCH_LEVEL */ + l_Properties->TransferDataHandler = AdapterReceive; /* DISPATCH_LEVEL */ + l_Properties->SendHandler = AdapterTransmit; /* DISPATCH_LEVEL */ + l_Properties->QueryInformationHandler = AdapterQuery; /* DISPATCH_LEVEL */ + l_Properties->SetInformationHandler = AdapterModify; /* DISPATCH_LEVEL */ + + switch (l_Status = + NdisMRegisterMiniport (g_NdisWrapperHandle, l_Properties, + sizeof (NDIS_MINIPORT_CHARACTERISTICS))) + { + case NDIS_STATUS_SUCCESS: + { + DEBUGP (("[TAP] version [%d.%d] %s %s registered miniport successfully\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__)); + DEBUGP (("Registry Path: '%S'\n", p_RegistryPath->Buffer)); + break; + } + + case NDIS_STATUS_BAD_CHARACTERISTICS: + { + DEBUGP (("[TAP] Miniport characteristics were badly defined\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + + case NDIS_STATUS_BAD_VERSION: + { + DEBUGP + (("[TAP] NDIS Version is wrong for the given characteristics\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + + case NDIS_STATUS_RESOURCES: + { + DEBUGP (("[TAP] Insufficient resources\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + + default: + case NDIS_STATUS_FAILURE: + { + DEBUGP (("[TAP] Unknown fatal registration error\n")); + NdisTerminateWrapper (g_NdisWrapperHandle, NULL); + break; + } + } + + cleanup: + if (l_Properties) + MemFree (l_Properties, sizeof (NDIS_MINIPORT_CHARACTERISTICS)); + + if (l_Status == NDIS_STATUS_SUCCESS) + NdisMRegisterUnloadHandler (g_NdisWrapperHandle, TapDriverUnload); + else + TapDriverUnload (p_DriverObject); + + return l_Status; +} + +//============================================================ +// Driver Unload +//============================================================ +VOID +TapDriverUnload (IN PDRIVER_OBJECT p_DriverObject) +{ + DEBUGP (("[TAP] version [%d.%d] %s %s unloaded, instances=%d, imbs=%d\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__, + NInstances(), + InstanceMaxBucketSize())); + + FreeInstanceList (); + + //============================== + // Free debugging text space + //============================== +#if DBG + MyDebugFree (); +#endif +} + +//========================================================== +// Adapter Initialization +//========================================================== +NDIS_STATUS AdapterCreate + (OUT PNDIS_STATUS p_ErrorStatus, + OUT PUINT p_MediaIndex, + IN PNDIS_MEDIUM p_Media, + IN UINT p_MediaCount, + IN NDIS_HANDLE p_AdapterHandle, + IN NDIS_HANDLE p_ConfigurationHandle) +{ + TapAdapterPointer l_Adapter = NULL; + + NDIS_MEDIUM l_PreferredMedium = NdisMedium802_3; // Ethernet + BOOLEAN l_MacFromRegistry = FALSE; + UINT l_Index; + NDIS_STATUS status; + +#if ENABLE_NONADMIN + BOOLEAN enable_non_admin = FALSE; +#endif + + //==================================== + // Make sure adapter type is supported + //==================================== + + for (l_Index = 0; + l_Index < p_MediaCount && p_Media[l_Index] != l_PreferredMedium; + ++l_Index); + + if (l_Index == p_MediaCount) + { + DEBUGP (("[TAP] Unsupported adapter type [wanted: %d]\n", + l_PreferredMedium)); + return NDIS_STATUS_UNSUPPORTED_MEDIA; + } + + *p_MediaIndex = l_Index; + + //========================================= + // Allocate memory for TapAdapter structure + //========================================= + + l_Adapter = MemAlloc (sizeof (TapAdapter), TRUE); + + if (l_Adapter == NULL) + { + DEBUGP (("[TAP] Couldn't allocate adapter memory\n")); + return NDIS_STATUS_RESOURCES; + } + + //========================================== + // Inform the NDIS library about significant + // features of our virtual NIC. + //========================================== + + NdisMSetAttributesEx + (p_AdapterHandle, + (NDIS_HANDLE) l_Adapter, + 16, + NDIS_ATTRIBUTE_DESERIALIZE + | NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT + | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT + | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND, + NdisInterfaceInternal); + + //===================================== + // Initialize simple Adapter parameters + //===================================== + + l_Adapter->m_Lookahead = DEFAULT_PACKET_LOOKAHEAD; + l_Adapter->m_Medium = l_PreferredMedium; + l_Adapter->m_DeviceState = '?'; + l_Adapter->m_MiniportAdapterHandle = p_AdapterHandle; + + //================================== + // Allocate spinlock for controlling + // access to multicast address list. + //================================== + NdisAllocateSpinLock (&l_Adapter->m_MCLock); + l_Adapter->m_MCLockAllocated = TRUE; + + //==================================================== + // Register a shutdown handler which will be called + // on system restart/shutdown to halt our virtual NIC. + //==================================================== + + NdisMRegisterAdapterShutdownHandler (p_AdapterHandle, l_Adapter, + AdapterHalt); + l_Adapter->m_RegisteredAdapterShutdownHandler = TRUE; + + //============================================ + // Get parameters from registry which were set + // in the adapter advanced properties dialog. + //============================================ + { + NDIS_STATUS status; + NDIS_HANDLE configHandle; + NDIS_CONFIGURATION_PARAMETER *parm; + + // set defaults in case our registry query fails + l_Adapter->m_MTU = ETHERNET_MTU; + l_Adapter->m_MediaStateAlwaysConnected = FALSE; + l_Adapter->m_MediaState = FALSE; + + NdisOpenConfiguration (&status, &configHandle, p_ConfigurationHandle); + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP (("[TAP] Couldn't open adapter registry\n")); + AdapterFreeResources (l_Adapter); + return status; + } + + //==================================== + // Allocate and construct adapter name + //==================================== + { + NDIS_STRING key = NDIS_STRING_CONST("MiniportName"); + NdisReadConfiguration (&status, &parm, configHandle, &key, NdisParameterString); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterString) + { + DEBUGP (("[TAP] NdisReadConfiguration (MiniportName=%s)\n", parm->ParameterData.StringData.Buffer)); + + if (RtlUnicodeStringToAnsiString ( + &l_Adapter->m_NameAnsi, + &parm->ParameterData.StringData, + TRUE) != STATUS_SUCCESS) + { + DEBUGP (("[TAP] RtlUnicodeStringToAnsiString MiniportName failed\n")); + status = NDIS_STATUS_RESOURCES; + } + } + } else { + /* "MiniportName" is available only XP and above. Not on Windows 2000. */ + NDIS_STRING key = NDIS_STRING_CONST("NdisVersion"); + NdisReadConfiguration (&status, &parm, configHandle, &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterData.IntegerData == 0x50000) + { + /* Fallback for Windows 2000 with NDIS version 5.00.00 + Don't use this on Vista, 'NDIS_MINIPORT_BLOCK' was changed! */ + DEBUGP (("[TAP] NdisReadConfiguration NdisVersion (Int=%X)\n", parm->ParameterData.IntegerData)); + if (RtlUnicodeStringToAnsiString ( + &l_Adapter->m_NameAnsi, + &((PNDIS_MINIPORT_BLOCK) p_AdapterHandle)->MiniportName, + TRUE) != STATUS_SUCCESS) + { + DEBUGP (("[TAP] RtlUnicodeStringToAnsiString MiniportName (W2K) failed\n")); + status = NDIS_STATUS_RESOURCES; + } + } + } + } + } + + /* Can't continue without name (see macro 'NAME') */ + if (status != NDIS_STATUS_SUCCESS || !l_Adapter->m_NameAnsi.Buffer) + { + NdisCloseConfiguration (configHandle); + AdapterFreeResources (l_Adapter); + return NDIS_STATUS_RESOURCES; + } + + /* Read MTU setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("MTU"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + int mtu = parm->ParameterData.IntegerData; + if (mtu < MINIMUM_MTU) + mtu = MINIMUM_MTU; + if (mtu > MAXIMUM_MTU) + mtu = MAXIMUM_MTU; + l_Adapter->m_MTU = mtu; + } + } + } + + /* Read Media Status setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("MediaStatus"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + if (parm->ParameterData.IntegerData) + { + l_Adapter->m_MediaStateAlwaysConnected = TRUE; + l_Adapter->m_MediaState = TRUE; + } + } + } + } + +#if ENABLE_NONADMIN + /* Read AllowNonAdmin setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("AllowNonAdmin"); + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterInteger); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterInteger) + { + if (parm->ParameterData.IntegerData) + { + enable_non_admin = TRUE; + } + } + } + } +#endif + + /* Read optional MAC setting from registry */ + { + NDIS_STRING key = NDIS_STRING_CONST("MAC"); + ANSI_STRING mac_string; + NdisReadConfiguration (&status, &parm, configHandle, + &key, NdisParameterString); + if (status == NDIS_STATUS_SUCCESS) + { + if (parm->ParameterType == NdisParameterString) + { + if (RtlUnicodeStringToAnsiString (&mac_string, &parm->ParameterData.StringData, TRUE) == STATUS_SUCCESS) + { + l_MacFromRegistry = ParseMAC (l_Adapter->m_MAC, mac_string.Buffer); + RtlFreeAnsiString (&mac_string); + } + } + } + } + + NdisCloseConfiguration (configHandle); + + DEBUGP (("[%s] MTU=%d\n", NAME (l_Adapter), l_Adapter->m_MTU)); + } + + //================================== + // Store and update MAC address info + //================================== + + if (!l_MacFromRegistry) + GenerateRandomMac (l_Adapter->m_MAC, NAME (l_Adapter)); + + DEBUGP (("[%s] Using MAC %x:%x:%x:%x:%x:%x\n", + NAME (l_Adapter), + l_Adapter->m_MAC[0], l_Adapter->m_MAC[1], l_Adapter->m_MAC[2], + l_Adapter->m_MAC[3], l_Adapter->m_MAC[4], l_Adapter->m_MAC[5])); + + //================== + // Set broadcast MAC + //================== + { + int i; + for (i = 0; i < sizeof (MACADDR); ++i) + l_Adapter->m_MAC_Broadcast[i] = 0xFF; + } + + //==================================== + // Initialize TAP device + //==================================== + { + NDIS_STATUS tap_status; + tap_status = CreateTapDevice (&l_Adapter->m_Extension, NAME (l_Adapter)); + if (tap_status != NDIS_STATUS_SUCCESS) + { + AdapterFreeResources (l_Adapter); + return tap_status; + } + } + + if (!AddAdapterToInstanceList (l_Adapter)) + { + NOTE_ERROR (); + TapDeviceFreeResources (&l_Adapter->m_Extension); + AdapterFreeResources (l_Adapter); + return NDIS_STATUS_RESOURCES; + } + + l_Adapter->m_InterfaceIsRunning = TRUE; + +#if ENABLE_NONADMIN + if (enable_non_admin) + AllowNonAdmin (&l_Adapter->m_Extension); +#endif + + return NDIS_STATUS_SUCCESS; +} + +VOID +AdapterHalt (IN NDIS_HANDLE p_AdapterContext) +{ + BOOLEAN status; + + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + + NOTE_ERROR (); + + l_Adapter->m_InterfaceIsRunning = FALSE; + + DEBUGP (("[%s] is being halted\n", NAME (l_Adapter))); + + DestroyTapDevice (&l_Adapter->m_Extension); + + // Free resources + DEBUGP (("[%s] Freeing Resources\n", NAME (l_Adapter))); + AdapterFreeResources (l_Adapter); + + status = RemoveAdapterFromInstanceList (l_Adapter); + DEBUGP (("[TAP] RemoveAdapterFromInstanceList returned %d\n", (int) status)); + + DEBUGP (("[TAP] version [%d.%d] %s %s AdapterHalt returning\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + __DATE__, + __TIME__)); +} + +VOID +AdapterFreeResources (TapAdapterPointer p_Adapter) +{ + MYASSERT (!p_Adapter->m_CalledAdapterFreeResources); + p_Adapter->m_CalledAdapterFreeResources = TRUE; + + if (p_Adapter->m_NameAnsi.Buffer) + RtlFreeAnsiString (&p_Adapter->m_NameAnsi); + + if (p_Adapter->m_RegisteredAdapterShutdownHandler) + NdisMDeregisterAdapterShutdownHandler (p_Adapter->m_MiniportAdapterHandle); + + if (p_Adapter->m_MCLockAllocated) + NdisFreeSpinLock (&l_Adapter->m_MCLock); +} + +VOID +DestroyTapDevice (TapExtensionPointer p_Extension) +{ + DEBUGP (("[%s] Destroying tap device\n", p_Extension->m_TapName)); + + //====================================== + // Let clients know we are shutting down + //====================================== + p_Extension->m_TapIsRunning = FALSE; + p_Extension->m_TapOpens = 0; + p_Extension->m_Halt = TRUE; + + //===================================== + // If we are concurrently executing in + // TapDeviceHook or AdapterTransmit, + // give those calls time to finish. + // Note that we must be running at IRQL + // < DISPATCH_LEVEL in order to call + // NdisMSleep. + //===================================== + NdisMSleep (500000); + + //=========================================================== + // Exhaust IRP and packet queues. Any pending IRPs will + // be cancelled, causing user-space to get this error + // on overlapped reads: + // The I/O operation has been aborted because of either a + // thread exit or an application request. (code=995) + // It's important that user-space close the device handle + // when this code is returned, so that when we finally + // do a NdisMDeregisterDevice, the device reference count + // is 0. Otherwise the driver will not unload even if the + // the last adapter has been halted. + //=========================================================== + FlushQueues (p_Extension); + NdisMSleep (500000); // give user space time to respond to IRP cancel + + TapDeviceFreeResources (p_Extension); +} + +VOID +TapDeviceFreeResources (TapExtensionPointer p_Extension) +{ + MYASSERT (p_Extension); + MYASSERT (!p_Extension->m_CalledTapDeviceFreeResources); + p_Extension->m_CalledTapDeviceFreeResources = TRUE; + + if (p_Extension->m_PacketQueue) + QueueFree (p_Extension->m_PacketQueue); + if (p_Extension->m_IrpQueue) + QueueFree (p_Extension->m_IrpQueue); + + if (p_Extension->m_CreatedUnicodeLinkName) + RtlFreeUnicodeString (&p_Extension->m_UnicodeLinkName); + + //========================================================== + // According to DDK docs, the device is not actually deleted + // until its reference count falls to zero. That means we + // still need to gracefully fail TapDeviceHook requests + // after this point, otherwise ugly things would happen if + // the device was disabled (e.g. in the network connections + // control panel) while a userspace app still held an open + // file handle to it. + //========================================================== + + if (p_Extension->m_TapDevice) + { + BOOLEAN status; + status = (NdisMDeregisterDevice (p_Extension->m_TapDeviceHandle) + == NDIS_STATUS_SUCCESS); + DEBUGP (("[TAP] Deregistering TAP device, status=%d\n", (int)status)); + } + + if (p_Extension->m_TapName) + MemFree (p_Extension->m_TapName, NAME_BUFFER_SIZE); + + if (p_Extension->m_AllocatedSpinlocks) + NdisFreeSpinLock (&p_Extension->m_QueueLock); +} + +//======================================================================== +// Tap Device Initialization +//======================================================================== + +NDIS_STATUS +CreateTapDevice (TapExtensionPointer p_Extension, const char *p_Name) +{ +# define SIZEOF_DISPATCH (sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1)) + PDRIVER_DISPATCH *l_Dispatch = NULL; + ANSI_STRING l_TapString, l_LinkString; + UNICODE_STRING l_TapUnicode; + BOOLEAN l_FreeTapUnicode = FALSE; + NTSTATUS l_Status, l_Return = NDIS_STATUS_SUCCESS; + const char *l_UsableName; + + DEBUGP (("[TAP] version [%d.%d] creating tap device: %s\n", + TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, + p_Name)); + + NdisZeroMemory (p_Extension, sizeof (TapExtension)); + + INIT_MUTEX (&p_Extension->m_OpenCloseMutex); + + l_LinkString.Buffer = NULL; + l_TapString.Buffer = NULL; + + l_TapString.MaximumLength = l_LinkString.MaximumLength = NAME_BUFFER_SIZE; + + //======================================= + // Set TAP device entry points + //======================================= + + if ((l_Dispatch = MemAlloc (SIZEOF_DISPATCH, TRUE)) == NULL) + { + DEBUGP (("[%s] couldn't alloc TAP dispatch table\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + l_Dispatch[IRP_MJ_DEVICE_CONTROL] = TapDeviceHook; + l_Dispatch[IRP_MJ_READ] = TapDeviceHook; + l_Dispatch[IRP_MJ_WRITE] = TapDeviceHook; + l_Dispatch[IRP_MJ_CREATE] = TapDeviceHook; + l_Dispatch[IRP_MJ_CLOSE] = TapDeviceHook; + + //================================== + // Find the beginning of the GUID + //================================== + l_UsableName = p_Name; + while (*l_UsableName != '{') + { + if (*l_UsableName == '\0') + { + DEBUGP (("[%s] couldn't find leading '{' in name\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + ++l_UsableName; + } + + //================================== + // Allocate pool for TAP device name + //================================== + + if ((p_Extension->m_TapName = l_TapString.Buffer = + MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL) + { + DEBUGP (("[%s] couldn't alloc TAP name buffer\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + //================================================ + // Allocate pool for TAP symbolic link name buffer + //================================================ + + if ((l_LinkString.Buffer = + MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL) + { + DEBUGP (("[%s] couldn't alloc TAP symbolic link name buffer\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + //======================================================= + // Set TAP device name + //======================================================= + + l_Status = RtlStringCchPrintfExA + (l_TapString.Buffer, + l_TapString.MaximumLength, + NULL, + NULL, + STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, + "%s%s%s", + SYSDEVICEDIR, + l_UsableName, + TAPSUFFIX); + + if (l_Status != STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't format TAP device name\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + l_TapString.Length = (USHORT) strlen (l_TapString.Buffer); + + DEBUGP (("TAP DEV NAME: '%s'\n", l_TapString.Buffer)); + + //======================================================= + // Set TAP link name + //======================================================= + + l_Status = RtlStringCchPrintfExA + (l_LinkString.Buffer, + l_LinkString.MaximumLength, + NULL, + NULL, + STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, + "%s%s%s", + USERDEVICEDIR, + l_UsableName, + TAPSUFFIX); + + if (l_Status != STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't format TAP device symbolic link\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + l_LinkString.Length = (USHORT) strlen (l_LinkString.Buffer); + + DEBUGP (("TAP LINK NAME: '%s'\n", l_LinkString.Buffer)); + + //================================================== + // Convert strings to unicode + //================================================== + if (RtlAnsiStringToUnicodeString (&l_TapUnicode, &l_TapString, TRUE) != + STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't alloc TAP unicode name buffer\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + l_FreeTapUnicode = TRUE; + + if (RtlAnsiStringToUnicodeString + (&p_Extension->m_UnicodeLinkName, &l_LinkString, TRUE) + != STATUS_SUCCESS) + { + DEBUGP + (("[%s] Couldn't allocate unicode string for symbolic link name\n", + p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + p_Extension->m_CreatedUnicodeLinkName = TRUE; + + //================================================== + // Create new TAP device with symbolic + // link and associate with adapter. + //================================================== + + l_Status = NdisMRegisterDevice + (g_NdisWrapperHandle, + &l_TapUnicode, + &p_Extension->m_UnicodeLinkName, + l_Dispatch, + &p_Extension->m_TapDevice, + &p_Extension->m_TapDeviceHandle + ); + + if (l_Status != STATUS_SUCCESS) + { + DEBUGP (("[%s] couldn't be created\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + /* Set TAP device flags */ + p_Extension->m_TapDevice->Flags |= DO_DIRECT_IO; + + //======================================================== + // Initialize Packet and IRP queues. + // + // The packet queue is used to buffer data which has been + // "transmitted" by the virtual NIC, before user space + // has had a chance to read it. + // + // The IRP queue is used to buffer pending I/O requests + // from userspace, i.e. read requests on the TAP device + // waiting for the system to "transmit" something through + // the virtual NIC. + // + // Basically, packets in the packet queue are used + // to satisfy IRP requests in the IRP queue. + // + // QueueLock is used to lock the packet queue used + // for the TAP-Win32 NIC -> User Space packet flow direction. + // + // All accesses to packet or IRP queues should be + // bracketed by the QueueLock spinlock, + // in order to be SMP-safe. + //======================================================== + + NdisAllocateSpinLock (&p_Extension->m_QueueLock); + p_Extension->m_AllocatedSpinlocks = TRUE; + + p_Extension->m_PacketQueue = QueueInit (PACKET_QUEUE_SIZE); + p_Extension->m_IrpQueue = QueueInit (IRP_QUEUE_SIZE); + + if (!p_Extension->m_PacketQueue + || !p_Extension->m_IrpQueue) + { + DEBUGP (("[%s] couldn't alloc TAP queues\n", p_Name)); + l_Return = NDIS_STATUS_RESOURCES; + goto cleanup; + } + + //======================== + // Finalize initialization + //======================== + + p_Extension->m_TapIsRunning = TRUE; + + DEBUGP (("[%s] successfully created TAP device [%s]\n", p_Name, + p_Extension->m_TapName)); + + cleanup: + if (l_FreeTapUnicode) + RtlFreeUnicodeString (&l_TapUnicode); + if (l_LinkString.Buffer) + MemFree (l_LinkString.Buffer, NAME_BUFFER_SIZE); + if (l_Dispatch) + MemFree (l_Dispatch, SIZEOF_DISPATCH); + + if (l_Return != NDIS_STATUS_SUCCESS) + TapDeviceFreeResources (p_Extension); + + return l_Return; +} +#undef SIZEOF_DISPATCH + +//======================================================== +// Adapter Control +//======================================================== +NDIS_STATUS +AdapterReset (OUT PBOOLEAN p_AddressingReset, IN NDIS_HANDLE p_AdapterContext) +{ + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + DEBUGP (("[%s] is resetting\n", NAME (l_Adapter))); + return NDIS_STATUS_SUCCESS; +} + +NDIS_STATUS AdapterReceive + (OUT PNDIS_PACKET p_Packet, + OUT PUINT p_Transferred, + IN NDIS_HANDLE p_AdapterContext, + IN NDIS_HANDLE p_ReceiveContext, + IN UINT p_Offset, + IN UINT p_ToTransfer) +{ + return NDIS_STATUS_SUCCESS; +} + +//============================================================== +// Adapter Option Query/Modification +//============================================================== +NDIS_STATUS AdapterQuery +(IN NDIS_HANDLE p_AdapterContext, + IN NDIS_OID p_OID, + IN PVOID p_Buffer, + IN ULONG p_BufferLength, + OUT PULONG p_BytesWritten, OUT PULONG p_BytesNeeded) +{ + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + TapAdapterQuery l_Query, *l_QueryPtr = &l_Query; + NDIS_STATUS l_Status = NDIS_STATUS_SUCCESS; + UINT l_QueryLength = 4; + BOOLEAN lock_succeeded; + + NdisZeroMemory (&l_Query, sizeof (l_Query)); + + switch (p_OID) + { + //=================================================================== + // Vendor & Driver version Info + //=================================================================== + case OID_GEN_VENDOR_DESCRIPTION: + l_QueryPtr = (TapAdapterQueryPointer) PRODUCT_STRING; + l_QueryLength = strlen (PRODUCT_STRING) + 1; + break; + + case OID_GEN_VENDOR_ID: + l_Query.m_Long = 0xffffff; + break; + + case OID_GEN_DRIVER_VERSION: + l_Query.m_Short = + (((USHORT) TAP_NDIS_MAJOR_VERSION) << 8 | (USHORT) + TAP_NDIS_MINOR_VERSION); + l_QueryLength = sizeof (unsigned short); + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + l_Query.m_Long = + (((USHORT) TAP_DRIVER_MAJOR_VERSION) << 8 | (USHORT) + TAP_DRIVER_MINOR_VERSION); + break; + + //================================================================= + // Statistics + //================================================================= + case OID_GEN_RCV_NO_BUFFER: + l_Query.m_Long = 0; + break; + + case OID_802_3_RCV_ERROR_ALIGNMENT: + l_Query.m_Long = 0; + break; + + case OID_802_3_XMIT_ONE_COLLISION: + l_Query.m_Long = 0; + break; + + case OID_802_3_XMIT_MORE_COLLISIONS: + l_Query.m_Long = 0; + break; + + case OID_GEN_XMIT_OK: + l_Query.m_Long = l_Adapter->m_Tx; + break; + + case OID_GEN_RCV_OK: + l_Query.m_Long = l_Adapter->m_Rx; + break; + + case OID_GEN_XMIT_ERROR: + l_Query.m_Long = l_Adapter->m_TxErr; + break; + + case OID_GEN_RCV_ERROR: + l_Query.m_Long = l_Adapter->m_RxErr; + break; + + //=================================================================== + // Device & Protocol Options + //=================================================================== + case OID_GEN_SUPPORTED_LIST: + l_QueryPtr = (TapAdapterQueryPointer) g_SupportedOIDList; + l_QueryLength = sizeof (g_SupportedOIDList); + break; + + case OID_GEN_MAC_OPTIONS: + // This MUST be here !!! + l_Query.m_Long = (NDIS_MAC_OPTION_RECEIVE_SERIALIZED + | NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA + | NDIS_MAC_OPTION_NO_LOOPBACK + | NDIS_MAC_OPTION_TRANSFERS_NOT_PEND); + + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + l_Query.m_Long = + (NDIS_PACKET_TYPE_ALL_LOCAL | + NDIS_PACKET_TYPE_BROADCAST | + NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL); + + break; + + case OID_GEN_PROTOCOL_OPTIONS: + l_Query.m_Long = 0; + break; + + //================================================================== + // Device Info + //================================================================== + case OID_GEN_MEDIA_CONNECT_STATUS: + l_Query.m_Long = l_Adapter->m_MediaState + ? NdisMediaStateConnected : NdisMediaStateDisconnected; + break; + + case OID_GEN_HARDWARE_STATUS: + l_Query.m_HardwareStatus = NdisHardwareStatusReady; + l_QueryLength = sizeof (NDIS_HARDWARE_STATUS); + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + l_Query.m_Medium = l_Adapter->m_Medium; + l_QueryLength = sizeof (NDIS_MEDIUM); + break; + + case OID_GEN_PHYSICAL_MEDIUM: + l_Query.m_PhysicalMedium = NdisPhysicalMediumUnspecified; + l_QueryLength = sizeof (NDIS_PHYSICAL_MEDIUM); + break; + + case OID_GEN_LINK_SPEED: + l_Query.m_Long = 100000; // rate / 100 bps + break; + + case OID_802_3_PERMANENT_ADDRESS: + case OID_802_3_CURRENT_ADDRESS: + COPY_MAC (l_Query.m_MacAddress, l_Adapter->m_MAC); + l_QueryLength = sizeof (MACADDR); + break; + + //================================================================== + // Limits + //================================================================== + + case OID_GEN_MAXIMUM_SEND_PACKETS: + l_Query.m_Long = 1; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + l_Query.m_Long = NIC_MAX_MCAST_LIST; + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + l_Query.m_Long = l_Adapter->m_Lookahead; + break; + + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_MAXIMUM_TOTAL_SIZE: + case OID_GEN_RECEIVE_BUFFER_SPACE: + case OID_GEN_RECEIVE_BLOCK_SIZE: + l_Query.m_Long = DEFAULT_PACKET_LOOKAHEAD; + break; + + case OID_GEN_MAXIMUM_FRAME_SIZE: + case OID_GEN_TRANSMIT_BLOCK_SIZE: + case OID_GEN_TRANSMIT_BUFFER_SPACE: + l_Query.m_Long = l_Adapter->m_MTU; + break; + + case OID_PNP_CAPABILITIES: + do + { + PNDIS_PNP_CAPABILITIES pPNPCapabilities; + PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct; + + if (p_BufferLength >= sizeof (NDIS_PNP_CAPABILITIES)) + { + pPNPCapabilities = (PNDIS_PNP_CAPABILITIES) (p_Buffer); + + // + // Setting up the buffer to be returned + // to the Protocol above the Passthru miniport + // + pPMstruct = &pPNPCapabilities->WakeUpCapabilities; + pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified; + pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified; + pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified; + } + l_QueryLength = sizeof (NDIS_PNP_CAPABILITIES); + } + while (FALSE); + break; + case OID_PNP_QUERY_POWER: + break; + + // Required OIDs that we don't support + + case OID_GEN_SUPPORTED_GUIDS: + case OID_GEN_MEDIA_CAPABILITIES: + case OID_TCP_TASK_OFFLOAD: + case OID_FFP_SUPPORT: + l_Status = NDIS_STATUS_INVALID_OID; + break; + + // Optional stats OIDs + + case OID_GEN_DIRECTED_BYTES_XMIT: + case OID_GEN_DIRECTED_FRAMES_XMIT: + case OID_GEN_MULTICAST_BYTES_XMIT: + case OID_GEN_MULTICAST_FRAMES_XMIT: + case OID_GEN_BROADCAST_BYTES_XMIT: + case OID_GEN_BROADCAST_FRAMES_XMIT: + case OID_GEN_DIRECTED_BYTES_RCV: + case OID_GEN_DIRECTED_FRAMES_RCV: + case OID_GEN_MULTICAST_BYTES_RCV: + case OID_GEN_MULTICAST_FRAMES_RCV: + case OID_GEN_BROADCAST_BYTES_RCV: + case OID_GEN_BROADCAST_FRAMES_RCV: + l_Status = NDIS_STATUS_INVALID_OID; + break; + + //=================================================================== + // Not Handled + //=================================================================== + default: + DEBUGP (("[%s] Unhandled OID %lx\n", NAME (l_Adapter), p_OID)); + l_Status = NDIS_STATUS_INVALID_OID; + break; + } + + if (l_Status != NDIS_STATUS_SUCCESS) + ; + else if (l_QueryLength > p_BufferLength) + { + l_Status = NDIS_STATUS_INVALID_LENGTH; + *p_BytesNeeded = l_QueryLength; + } + else + NdisMoveMemory (p_Buffer, (PVOID) l_QueryPtr, + (*p_BytesWritten = l_QueryLength)); + + return l_Status; +} + +NDIS_STATUS AdapterModify +(IN NDIS_HANDLE p_AdapterContext, + IN NDIS_OID p_OID, + IN PVOID p_Buffer, + IN ULONG p_BufferLength, + OUT PULONG p_BytesRead, + OUT PULONG p_BytesNeeded) +{ + TapAdapterQueryPointer l_Query = (TapAdapterQueryPointer) p_Buffer; + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + NDIS_STATUS l_Status = NDIS_STATUS_INVALID_OID; + ULONG l_Long; + + switch (p_OID) + { + //================================================================== + // Device Info + //================================================================== + case OID_802_3_MULTICAST_LIST: + DEBUGP (("[%s] Setting [OID_802_3_MULTICAST_LIST]\n", + NAME (l_Adapter))); + + *p_BytesNeeded = sizeof (ETH_ADDR); + *p_BytesRead = p_BufferLength; + + if (p_BufferLength % sizeof (ETH_ADDR)) + l_Status = NDIS_STATUS_INVALID_LENGTH; + else if (p_BufferLength > sizeof (MC_LIST)) + { + l_Status = NDIS_STATUS_MULTICAST_FULL; + *p_BytesNeeded = sizeof (MC_LIST); + } + else + { + NdisAcquireSpinLock (&l_Adapter->m_MCLock); + + NdisZeroMemory(&l_Adapter->m_MCList, sizeof (MC_LIST)); + + NdisMoveMemory(&l_Adapter->m_MCList, + p_Buffer, + p_BufferLength); + + l_Adapter->m_MCListSize = p_BufferLength / sizeof (ETH_ADDR); + + NdisReleaseSpinLock (&l_Adapter->m_MCLock); + + l_Status = NDIS_STATUS_SUCCESS; + } + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + l_Status = NDIS_STATUS_INVALID_LENGTH; + *p_BytesNeeded = 4; + + if (p_BufferLength >= sizeof (ULONG)) + { + DEBUGP + (("[%s] Setting [OID_GEN_CURRENT_PACKET_FILTER] to [0x%02lx]\n", + NAME (l_Adapter), l_Query->m_Long)); + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = sizeof (ULONG); + } + break; + + case OID_GEN_CURRENT_LOOKAHEAD: + if (p_BufferLength < sizeof (ULONG)) + { + l_Status = NDIS_STATUS_INVALID_LENGTH; + *p_BytesNeeded = 4; + } + else if (l_Query->m_Long > DEFAULT_PACKET_LOOKAHEAD + || l_Query->m_Long <= 0) + { + l_Status = NDIS_STATUS_INVALID_DATA; + } + else + { + DEBUGP (("[%s] Setting [OID_GEN_CURRENT_LOOKAHEAD] to [%d]\n", + NAME (l_Adapter), l_Query->m_Long)); + l_Adapter->m_Lookahead = l_Query->m_Long; + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = sizeof (ULONG); + } + break; + + case OID_GEN_NETWORK_LAYER_ADDRESSES: + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = *p_BytesNeeded = 0; + break; + + case OID_GEN_TRANSPORT_HEADER_OFFSET: + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = *p_BytesNeeded = 0; + break; + + case OID_PNP_SET_POWER: + do + { + NDIS_DEVICE_POWER_STATE NewDeviceState; + + NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE) p_Buffer); + + switch (NewDeviceState) + { + case NdisDeviceStateD0: + l_Adapter->m_DeviceState = '0'; + break; + case NdisDeviceStateD1: + l_Adapter->m_DeviceState = '1'; + break; + case NdisDeviceStateD2: + l_Adapter->m_DeviceState = '2'; + break; + case NdisDeviceStateD3: + l_Adapter->m_DeviceState = '3'; + break; + default: + l_Adapter->m_DeviceState = '?'; + break; + } + + l_Status = NDIS_STATUS_FAILURE; + + // + // Check for invalid length + // + if (p_BufferLength < sizeof (NDIS_DEVICE_POWER_STATE)) + { + l_Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (NewDeviceState > NdisDeviceStateD0) + { + l_Adapter->m_InterfaceIsRunning = FALSE; + DEBUGP (("[%s] Power management device state OFF\n", + NAME (l_Adapter))); + } + else + { + l_Adapter->m_InterfaceIsRunning = TRUE; + DEBUGP (("[%s] Power management device state ON\n", + NAME (l_Adapter))); + } + + l_Status = NDIS_STATUS_SUCCESS; + } + while (FALSE); + + if (l_Status == NDIS_STATUS_SUCCESS) + { + *p_BytesRead = sizeof (NDIS_DEVICE_POWER_STATE); + *p_BytesNeeded = 0; + } + else + { + *p_BytesRead = 0; + *p_BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE); + } + break; + + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + case OID_PNP_ADD_WAKE_UP_PATTERN: + l_Status = NDIS_STATUS_SUCCESS; + *p_BytesRead = *p_BytesNeeded = 0; + break; + + default: + DEBUGP (("[%s] Can't set value for OID %lx\n", NAME (l_Adapter), + p_OID)); + l_Status = NDIS_STATUS_INVALID_OID; + *p_BytesRead = *p_BytesNeeded = 0; + break; + } + + return l_Status; +} + +//==================================================================== +// Adapter Transmission +//==================================================================== +NDIS_STATUS +AdapterTransmit (IN NDIS_HANDLE p_AdapterContext, + IN PNDIS_PACKET p_Packet, + IN UINT p_Flags) +{ + TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext; + ULONG l_Index = 0, l_BufferLength = 0, l_PacketLength = 0; + PIRP l_IRP; + TapPacketPointer l_PacketBuffer; + PNDIS_BUFFER l_NDIS_Buffer; + PUCHAR l_Buffer; + PVOID result; + + NdisQueryPacket (p_Packet, NULL, NULL, &l_NDIS_Buffer, &l_PacketLength); + + //==================================================== + // Here we abandon the transmission attempt if any of + // the parameters is wrong or memory allocation fails + // but we do not indicate failure. The packet is + // silently dropped. + //==================================================== + + if (l_PacketLength < ETHERNET_HEADER_SIZE || l_PacketLength > 65535) + goto exit_fail; + else if (!l_Adapter->m_Extension.m_TapOpens || !l_Adapter->m_MediaState) + goto exit_success; // Nothing is bound to the TAP device + + if (NdisAllocateMemoryWithTag (&l_PacketBuffer, + TAP_PACKET_SIZE (l_PacketLength), + '5PAT') != NDIS_STATUS_SUCCESS) + goto exit_no_resources; + + if (l_PacketBuffer == NULL) + goto exit_no_resources; + + l_PacketBuffer->m_SizeFlags = (l_PacketLength & TP_SIZE_MASK); + + //=========================== + // Reassemble packet contents + //=========================== + + __try + { + l_Index = 0; + while (l_NDIS_Buffer && l_Index < l_PacketLength) + { + ULONG newlen; + NdisQueryBuffer (l_NDIS_Buffer, (PVOID *) & l_Buffer, + &l_BufferLength); + newlen = l_Index + l_BufferLength; + if (newlen > l_PacketLength) + { + NOTE_ERROR (); + goto no_queue; /* overflow */ + } + NdisMoveMemory (l_PacketBuffer->m_Data + l_Index, l_Buffer, + l_BufferLength); + l_Index = newlen; + NdisGetNextBuffer (l_NDIS_Buffer, &l_NDIS_Buffer); + } + if (l_Index != l_PacketLength) + { + NOTE_ERROR (); + goto no_queue; /* underflow */ + } + + DUMP_PACKET ("AdapterTransmit", l_PacketBuffer->m_Data, l_PacketLength); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify (l_PacketBuffer->m_Data, l_PacketLength, FALSE, "TX", &l_Adapter->m_TxTrunc); +#endif + + //===================================================== + // Are we running in DHCP server masquerade mode? + // + // If so, catch both DHCP requests and ARP queries + // to resolve the address of our virtual DHCP server. + //===================================================== + if (l_Adapter->m_dhcp_enabled) + { + const ETH_HEADER *eth = (ETH_HEADER *) l_PacketBuffer->m_Data; + const IPHDR *ip = (IPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER)); + const UDPHDR *udp = (UDPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR)); + + // ARP packet? + if (l_PacketLength == sizeof (ARP_PACKET) + && eth->proto == htons (ETH_P_ARP) + && l_Adapter->m_dhcp_server_arp) + { + if (ProcessARP (l_Adapter, + (PARP_PACKET) l_PacketBuffer->m_Data, + l_Adapter->m_dhcp_addr, + l_Adapter->m_dhcp_server_ip, + ~0, + l_Adapter->m_dhcp_server_mac)) + goto no_queue; + } + + // DHCP packet? + else if (l_PacketLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + && eth->proto == htons (ETH_P_IP) + && ip->version_len == 0x45 // IPv4, 20 byte header + && ip->protocol == IPPROTO_UDP + && udp->dest == htons (BOOTPS_PORT)) + { + const DHCP *dhcp = (DHCP *) (l_PacketBuffer->m_Data + + sizeof (ETH_HEADER) + + sizeof (IPHDR) + + sizeof (UDPHDR)); + + const int optlen = l_PacketLength + - sizeof (ETH_HEADER) + - sizeof (IPHDR) + - sizeof (UDPHDR) + - sizeof (DHCP); + + if (optlen > 0) // we must have at least one DHCP option + { + if (ProcessDHCP (l_Adapter, eth, ip, udp, dhcp, optlen)) + goto no_queue; + } + else + goto no_queue; + } + } + + //=============================================== + // In Point-To-Point mode, check to see whether + // packet is ARP or IPv4 (if neither, then drop). + //=============================================== + if (l_Adapter->m_tun) + { + ETH_HEADER *e; + + if (l_PacketLength < ETHERNET_HEADER_SIZE) + goto no_queue; + + e = (ETH_HEADER *) l_PacketBuffer->m_Data; + + switch (ntohs (e->proto)) + { + case ETH_P_ARP: + + // Make sure that packet is the + // right size for ARP. + if (l_PacketLength != sizeof (ARP_PACKET)) + goto no_queue; + + ProcessARP (l_Adapter, + (PARP_PACKET) l_PacketBuffer->m_Data, + l_Adapter->m_localIP, + l_Adapter->m_remoteNetwork, + l_Adapter->m_remoteNetmask, + l_Adapter->m_TapToUser.dest); + + default: + goto no_queue; + + case ETH_P_IP: + + // Make sure that packet is large + // enough to be IPv4. + if (l_PacketLength + < ETHERNET_HEADER_SIZE + IP_HEADER_SIZE) + goto no_queue; + + // Only accept directed packets, + // not broadcasts. + if (memcmp (e, &l_Adapter->m_TapToUser, ETHERNET_HEADER_SIZE)) + goto no_queue; + + // Packet looks like IPv4, queue it. + l_PacketBuffer->m_SizeFlags |= TP_TUN; + } + } + + //=============================================== + // Push packet onto queue to wait for read from + // userspace. + //=============================================== + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + result = NULL; + if (IS_UP (l_Adapter)) + result = QueuePush (l_Adapter->m_Extension.m_PacketQueue, l_PacketBuffer); + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if ((TapPacketPointer) result != l_PacketBuffer) + { + // adapter receive overrun + INCREMENT_STAT (l_Adapter->m_TxErr); + goto no_queue; + } + else + { + INCREMENT_STAT (l_Adapter->m_Tx); + } + + //============================================================ + // Cycle through IRPs and packets, try to satisfy each pending + // IRP with a queued packet. + //============================================================ + while (TRUE) + { + l_IRP = NULL; + l_PacketBuffer = NULL; + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (IS_UP (l_Adapter) + && QueueCount (l_Adapter->m_Extension.m_PacketQueue) + && QueueCount (l_Adapter->m_Extension.m_IrpQueue)) + { + l_IRP = (PIRP) QueuePop (l_Adapter->m_Extension.m_IrpQueue); + l_PacketBuffer = (TapPacketPointer) + QueuePop (l_Adapter->m_Extension.m_PacketQueue); + } + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + MYASSERT ((l_IRP != NULL) + (l_PacketBuffer != NULL) != 1); + + if (l_IRP && l_PacketBuffer) + { + CompleteIRP (l_IRP, + l_PacketBuffer, + IO_NETWORK_INCREMENT); + } + else + break; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + + return NDIS_STATUS_SUCCESS; + + no_queue: + NdisFreeMemory (l_PacketBuffer, + TAP_PACKET_SIZE (l_PacketLength), + 0); + + exit_success: + return NDIS_STATUS_SUCCESS; + + exit_fail: + return NDIS_STATUS_FAILURE; + + exit_no_resources: + return NDIS_STATUS_RESOURCES; +} + +//====================================================================== +// Hooks for catching TAP device IRP's. +//====================================================================== + +NTSTATUS +TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) +{ + TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject); + PIO_STACK_LOCATION l_IrpSp; + NTSTATUS l_Status = STATUS_SUCCESS; + BOOLEAN accessible; + + l_IrpSp = IoGetCurrentIrpStackLocation (p_IRP); + + p_IRP->IoStatus.Status = STATUS_SUCCESS; + p_IRP->IoStatus.Information = 0; + + if (!l_Adapter || l_Adapter->m_Extension.m_Halt) + { + DEBUGP (("TapDeviceHook called when TAP device is halted, MajorFunction=%d\n", + (int)l_IrpSp->MajorFunction)); + + if (l_IrpSp->MajorFunction == IRP_MJ_CLOSE) + { + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + return STATUS_SUCCESS; + } + else + { + p_IRP->IoStatus.Status = STATUS_NO_SUCH_DEVICE; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + return STATUS_NO_SUCH_DEVICE; + } + } + + switch (l_IrpSp->MajorFunction) + { + //=========================================================== + // Ioctl call handlers + //=========================================================== + case IRP_MJ_DEVICE_CONTROL: + { + switch (l_IrpSp->Parameters.DeviceIoControl.IoControlCode) + { + case TAP_IOCTL_GET_MAC: + { + if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength + >= sizeof (MACADDR)) + { + COPY_MAC (p_IRP->AssociatedIrp.SystemBuffer, + l_Adapter->m_MAC); + p_IRP->IoStatus.Information = sizeof (MACADDR); + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + break; + } + case TAP_IOCTL_GET_VERSION: + { + const ULONG size = sizeof (ULONG) * 3; + if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength + >= size) + { + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0] + = TAP_DRIVER_MAJOR_VERSION; + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[1] + = TAP_DRIVER_MINOR_VERSION; + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[2] +#if DBG + = 1; +#else + = 0; +#endif + p_IRP->IoStatus.Information = size; + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + case TAP_IOCTL_GET_MTU: + { + const ULONG size = sizeof (ULONG) * 1; + if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength + >= size) + { + ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0] + = l_Adapter->m_MTU; + p_IRP->IoStatus.Information = size; + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + case TAP_IOCTL_GET_INFO: + { + char state[16]; + if (l_Adapter->m_InterfaceIsRunning) + state[0] = 'A'; + else + state[0] = 'a'; + if (l_Adapter->m_Extension.m_TapIsRunning) + state[1] = 'T'; + else + state[1] = 't'; + state[2] = l_Adapter->m_DeviceState; + if (l_Adapter->m_MediaStateAlwaysConnected) + state[3] = 'C'; + else + state[3] = 'c'; + state[4] = '\0'; + + p_IRP->IoStatus.Status = l_Status = RtlStringCchPrintfExA ( + ((LPTSTR) (p_IRP->AssociatedIrp.SystemBuffer)), + l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength, + NULL, + NULL, + STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS, +#if PACKET_TRUNCATION_CHECK + "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d,%d] Rx=[%d,%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]", +#else + "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d]", +#endif + state, + g_LastErrorFilename, + g_LastErrorLineNumber, + (int)l_Adapter->m_Extension.m_NumTapOpens, + (int)l_Adapter->m_Tx, + (int)l_Adapter->m_TxErr, +#if PACKET_TRUNCATION_CHECK + (int)l_Adapter->m_TxTrunc, +#endif + (int)l_Adapter->m_Rx, + (int)l_Adapter->m_RxErr, +#if PACKET_TRUNCATION_CHECK + (int)l_Adapter->m_RxTrunc, +#endif + (int)l_Adapter->m_Extension.m_IrpQueue->size, + (int)l_Adapter->m_Extension.m_IrpQueue->max_size, + (int)IRP_QUEUE_SIZE, + (int)l_Adapter->m_Extension.m_PacketQueue->size, + (int)l_Adapter->m_Extension.m_PacketQueue->max_size, + (int)PACKET_QUEUE_SIZE + ); + + p_IRP->IoStatus.Information + = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + break; + } + +#if DBG + case TAP_IOCTL_GET_LOG_LINE: + { + if (GetDebugLine ((LPTSTR)p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength)) + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + else + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + + p_IRP->IoStatus.Information + = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + + break; + } +#endif + + case TAP_IOCTL_CONFIG_TUN: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (IPADDR) * 3)) + { + MACADDR dest; + + l_Adapter->m_tun = FALSE; + + GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1); + + l_Adapter->m_localIP = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + l_Adapter->m_remoteNetmask = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2]; + + // sanity check on network/netmask + if ((l_Adapter->m_remoteNetwork & l_Adapter->m_remoteNetmask) != l_Adapter->m_remoteNetwork) + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + break; + } + + COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC); + COPY_MAC (l_Adapter->m_TapToUser.dest, dest); + COPY_MAC (l_Adapter->m_UserToTap.src, dest); + COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); + + l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); + + l_Adapter->m_tun = TRUE; + + CheckIfDhcpAndTunMode (l_Adapter); + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + case TAP_IOCTL_CONFIG_POINT_TO_POINT: // Obsoleted by TAP_IOCTL_CONFIG_TUN + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (IPADDR) * 2)) + { + MACADDR dest; + + l_Adapter->m_tun = FALSE; + + GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1); + + l_Adapter->m_localIP = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + l_Adapter->m_remoteNetmask = ~0; + + COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC); + COPY_MAC (l_Adapter->m_TapToUser.dest, dest); + COPY_MAC (l_Adapter->m_UserToTap.src, dest); + COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC); + + l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP); + + l_Adapter->m_tun = TRUE; + + CheckIfDhcpAndTunMode (l_Adapter); + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + case TAP_IOCTL_SET_MEDIA_STATUS: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (ULONG) * 1)) + { + ULONG parm = ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + SetMediaStatus (l_Adapter, (BOOLEAN) parm); + p_IRP->IoStatus.Information = 1; + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + break; + } + + case TAP_IOCTL_CONFIG_DHCP_MASQ: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + (sizeof (IPADDR) * 4)) + { + l_Adapter->m_dhcp_enabled = FALSE; + l_Adapter->m_dhcp_server_arp = FALSE; + l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + + // Adapter IP addr / netmask + l_Adapter->m_dhcp_addr = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0]; + l_Adapter->m_dhcp_netmask = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1]; + + // IP addr of DHCP masq server + l_Adapter->m_dhcp_server_ip = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2]; + + // Lease time in seconds + l_Adapter->m_dhcp_lease_time = + ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[3]; + + GenerateRelatedMAC (l_Adapter->m_dhcp_server_mac, l_Adapter->m_MAC, 2); + + l_Adapter->m_dhcp_enabled = TRUE; + l_Adapter->m_dhcp_server_arp = TRUE; + + CheckIfDhcpAndTunMode (l_Adapter); + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + case TAP_IOCTL_CONFIG_DHCP_SET_OPT: + { + if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength <= + DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE + && l_Adapter->m_dhcp_enabled) + { + l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + + NdisMoveMemory (l_Adapter->m_dhcp_user_supplied_options_buffer, + p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.DeviceIoControl.InputBufferLength); + + l_Adapter->m_dhcp_user_supplied_options_buffer_len = + l_IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + p_IRP->IoStatus.Information = 1; // Simple boolean value + } + else + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + } + + break; + } + + default: + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + break; + } + } + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //=========================================================== + // User mode thread issued a read request on the tap device + // If there are packets waiting to be read, then the request + // will be satisfied here. If not, then the request will be + // queued and satisfied by any packet that is not used to + // satisfy requests ahead of it. + //=========================================================== + case IRP_MJ_READ: + { + TapPacketPointer l_PacketBuffer; + BOOLEAN pending = FALSE; + + // Save IRP-accessible copy of buffer length + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Read.Length; + + if (p_IRP->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + else if ((p_IRP->AssociatedIrp.SystemBuffer = + MmGetSystemAddressForMdlSafe + (p_IRP->MdlAddress, NormalPagePriority)) == NULL) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + else if (!l_Adapter->m_InterfaceIsRunning) + { + DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //================================== + // Can we provide immediate service? + //================================== + + l_PacketBuffer = NULL; + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (IS_UP (l_Adapter) + && QueueCount (l_Adapter->m_Extension.m_PacketQueue) + && QueueCount (l_Adapter->m_Extension.m_IrpQueue) == 0) + { + l_PacketBuffer = (TapPacketPointer) + QueuePop (l_Adapter->m_Extension.m_PacketQueue); + } + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (l_PacketBuffer) + { + l_Status = CompleteIRP (p_IRP, + l_PacketBuffer, + IO_NO_INCREMENT); + break; + } + + //============================= + // Attempt to pend read request + //============================= + + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (IS_UP (l_Adapter) + && QueuePush (l_Adapter->m_Extension.m_IrpQueue, p_IRP) == (PIRP) p_IRP) + { + IoSetCancelRoutine (p_IRP, CancelIRPCallback); + l_Status = STATUS_PENDING; + IoMarkIrpPending (p_IRP); + pending = TRUE; + } + + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock); + + if (pending) + break; + + // Can't queue anymore IRP's + DEBUGP (("[%s] TAP [%s] read IRP overrun\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapName)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //============================================================== + // User mode issued a WriteFile request on the TAP file handle. + // The request will always get satisfied here. The call may + // fail if there are too many pending packets (queue full). + //============================================================== + case IRP_MJ_WRITE: + { + if (p_IRP->MdlAddress == NULL) + { + DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; + p_IRP->IoStatus.Information = 0; + } + else if ((p_IRP->AssociatedIrp.SystemBuffer = + MmGetSystemAddressForMdlSafe + (p_IRP->MdlAddress, NormalPagePriority)) == NULL) + { + DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES; + p_IRP->IoStatus.Information = 0; + } + else if (!l_Adapter->m_InterfaceIsRunning) + { + DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + else if (!l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE)) + { + __try + { + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length; + + DUMP_PACKET ("IRP_MJ_WRITE ETH", + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length, + FALSE, + "RX", + &l_Adapter->m_RxTrunc); +#endif + + NdisMEthIndicateReceive + (l_Adapter->m_MiniportAdapterHandle, + (NDIS_HANDLE) l_Adapter, + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + ETHERNET_HEADER_SIZE, + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer + ETHERNET_HEADER_SIZE, + l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE, + l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE); + + NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle); + + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + } + else if (l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) + { + __try + { + p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length; + + DUMP_PACKET2 ("IRP_MJ_WRITE P2P", + &l_Adapter->m_UserToTap, + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length); + + //===================================================== + // If IPv4 packet, check whether or not packet + // was truncated. + //===================================================== +#if PACKET_TRUNCATION_CHECK + IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length, + TRUE, + "RX", + &l_Adapter->m_RxTrunc); +#endif + + NdisMEthIndicateReceive + (l_Adapter->m_MiniportAdapterHandle, + (NDIS_HANDLE) l_Adapter, + (unsigned char *) &l_Adapter->m_UserToTap, + sizeof (l_Adapter->m_UserToTap), + (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer, + l_IrpSp->Parameters.Write.Length, + l_IrpSp->Parameters.Write.Length); + + NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle); + + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE (P2P)\n", + NAME (l_Adapter))); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + } + else + { + DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n", + NAME (l_Adapter), + l_IrpSp->Parameters.Write.Length)); + NOTE_ERROR (); + p_IRP->IoStatus.Information = 0; // ETHERNET_HEADER_SIZE; + p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL; + } + + if (l_Status == STATUS_SUCCESS) + INCREMENT_STAT (l_Adapter->m_Rx); + else + INCREMENT_STAT (l_Adapter->m_RxErr); + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //-------------------------------------------------------------- + // User mode thread has called CreateFile() on the tap device + //-------------------------------------------------------------- + case IRP_MJ_CREATE: + { + BOOLEAN succeeded = FALSE; + BOOLEAN mutex_succeeded; + + DEBUGP + (("[%s] [TAP] release [%d.%d] open request (m_TapOpens=%d)\n", + NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION, l_Adapter->m_Extension.m_TapOpens)); + + ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded); + if (mutex_succeeded) + { + if (l_Adapter->m_Extension.m_TapIsRunning && !l_Adapter->m_Extension.m_TapOpens) + { + ResetTapAdapterState (l_Adapter); + l_Adapter->m_Extension.m_TapOpens = 1; + succeeded = TRUE; + } + + if (succeeded) + { + INCREMENT_STAT (l_Adapter->m_Extension.m_NumTapOpens); + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + p_IRP->IoStatus.Information = 0; + } + else + { + DEBUGP (("[%s] TAP is presently unavailable (m_TapOpens=%d)\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + + RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex); + } + else + { + DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //----------------------------------------------------------- + // User mode thread called CloseHandle() on the tap device + //----------------------------------------------------------- + case IRP_MJ_CLOSE: + { + BOOLEAN mutex_succeeded; + + DEBUGP (("[%s] [TAP] release [%d.%d] close/cleanup request\n", + NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION, + TAP_DRIVER_MINOR_VERSION)); + + ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded); + if (mutex_succeeded) + { + l_Adapter->m_Extension.m_TapOpens = 0; + ResetTapAdapterState (l_Adapter); + FlushQueues (&l_Adapter->m_Extension); + SetMediaStatus (l_Adapter, FALSE); + RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex); + } + else + { + DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n", + NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens)); + NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + + //------------------ + // Strange Request + //------------------ + default: + { + //NOTE_ERROR (); + p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL; + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + break; + } + } + + return l_Status; +} + +//============================================================= +// CompleteIRP is normally called with an adapter -> userspace +// network packet and an IRP (Pending I/O request) from userspace. +// +// The IRP will normally represent a queued overlapped read +// operation from userspace that is in a wait state. +// +// Use the ethernet packet to satisfy the IRP. +//============================================================= + +NTSTATUS +CompleteIRP (IN PIRP p_IRP, + IN TapPacketPointer p_PacketBuffer, + IN CCHAR PriorityBoost) +{ + NTSTATUS l_Status = STATUS_UNSUCCESSFUL; + + int offset; + int len; + + MYASSERT (p_IRP); + MYASSERT (p_PacketBuffer); + + IoSetCancelRoutine (p_IRP, NULL); // Disable cancel routine + + //------------------------------------------- + // While p_PacketBuffer always contains a + // full ethernet packet, including the + // ethernet header, in point-to-point mode, + // we only want to return the IPv4 + // component. + //------------------------------------------- + + if (p_PacketBuffer->m_SizeFlags & TP_TUN) + { + offset = ETHERNET_HEADER_SIZE; + len = (int) (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE; + } + else + { + offset = 0; + len = (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK); + } + + if (len < 0 || (int) p_IRP->IoStatus.Information < len) + { + p_IRP->IoStatus.Information = 0; + p_IRP->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + NOTE_ERROR (); + } + else + { + p_IRP->IoStatus.Information = len; + p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; + + __try + { + NdisMoveMemory (p_IRP->AssociatedIrp.SystemBuffer, + p_PacketBuffer->m_Data + offset, + len); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + NOTE_ERROR (); + p_IRP->IoStatus.Status = STATUS_UNSUCCESSFUL; + p_IRP->IoStatus.Information = 0; + } + } + + __try + { + NdisFreeMemory (p_PacketBuffer, + TAP_PACKET_SIZE (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK), + 0); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } + + if (l_Status == STATUS_SUCCESS) + { + IoCompleteRequest (p_IRP, PriorityBoost); + } + else + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); + + return l_Status; +} + +//============================================== +// IRPs get cancelled for a number of reasons. +// +// The TAP device could be closed by userspace +// when there are still pending read operations. +// +// The user could disable the TAP adapter in the +// network connections control panel, while the +// device is still open by a process. +//============================================== +VOID +CancelIRPCallback (IN PDEVICE_OBJECT p_DeviceObject, + IN PIRP p_IRP) +{ + TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject); + CancelIRP (l_Adapter ? &l_Adapter->m_Extension : NULL, p_IRP, TRUE); +} + +VOID +CancelIRP (TapExtensionPointer p_Extension, + IN PIRP p_IRP, + BOOLEAN callback) +{ + BOOLEAN exists = FALSE; + + MYASSERT (p_IRP); + + if (p_Extension) + { + NdisAcquireSpinLock (&p_Extension->m_QueueLock); + exists = (QueueExtract (p_Extension->m_IrpQueue, p_IRP) == p_IRP); + NdisReleaseSpinLock (&p_Extension->m_QueueLock); + } + else + exists = TRUE; + + if (exists) + { + IoSetCancelRoutine (p_IRP, NULL); + p_IRP->IoStatus.Status = STATUS_CANCELLED; + p_IRP->IoStatus.Information = 0; + } + + if (callback) + IoReleaseCancelSpinLock (p_IRP->CancelIrql); + + if (exists) + IoCompleteRequest (p_IRP, IO_NO_INCREMENT); +} + +//==================================== +// Exhaust packet and IRP queues. +//==================================== +VOID +FlushQueues (TapExtensionPointer p_Extension) +{ + PIRP l_IRP; + TapPacketPointer l_PacketBuffer; + int n_IRP=0, n_Packet=0; + + MYASSERT (p_Extension); + MYASSERT (p_Extension->m_TapDevice); + + while (TRUE) + { + NdisAcquireSpinLock (&p_Extension->m_QueueLock); + l_IRP = QueuePop (p_Extension->m_IrpQueue); + NdisReleaseSpinLock (&p_Extension->m_QueueLock); + if (l_IRP) + { + ++n_IRP; + CancelIRP (NULL, l_IRP, FALSE); + } + else + break; + } + + while (TRUE) + { + NdisAcquireSpinLock (&p_Extension->m_QueueLock); + l_PacketBuffer = QueuePop (p_Extension->m_PacketQueue); + NdisReleaseSpinLock (&p_Extension->m_QueueLock); + if (l_PacketBuffer) + { + ++n_Packet; + MemFree (l_PacketBuffer, TAP_PACKET_SIZE (l_PacketBuffer->m_SizeFlags & TP_SIZE_MASK)); + } + else + break; + } + + DEBUGP (( + "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d]\n", + p_Extension->m_TapName, + n_IRP, + p_Extension->m_IrpQueue->max_size, + IRP_QUEUE_SIZE, + n_Packet, + p_Extension->m_PacketQueue->max_size, + PACKET_QUEUE_SIZE + )); +} + +//=================================================== +// Tell Windows whether the TAP device should be +// considered "connected" or "disconnected". +//=================================================== +VOID +SetMediaStatus (TapAdapterPointer p_Adapter, BOOLEAN state) +{ + if (p_Adapter->m_MediaState != state && !p_Adapter->m_MediaStateAlwaysConnected) + { + if (state) + NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle, + NDIS_STATUS_MEDIA_CONNECT, NULL, 0); + else + NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle, + NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0); + + NdisMIndicateStatusComplete (p_Adapter->m_MiniportAdapterHandle); + p_Adapter->m_MediaState = state; + } +} + + +//====================================================== +// If DHCP mode is used together with tun +// mode, consider the fact that the P2P remote subnet +// might enclose the DHCP masq server address. +//====================================================== +VOID +CheckIfDhcpAndTunMode (TapAdapterPointer p_Adapter) +{ + if (p_Adapter->m_tun && p_Adapter->m_dhcp_enabled) + { + if ((p_Adapter->m_dhcp_server_ip & p_Adapter->m_remoteNetmask) == p_Adapter->m_remoteNetwork) + { + COPY_MAC (p_Adapter->m_dhcp_server_mac, p_Adapter->m_TapToUser.dest); + p_Adapter->m_dhcp_server_arp = FALSE; + } + } +} + +//=================================================== +// Generate an ARP reply message for specific kinds +// ARP queries. +//=================================================== +BOOLEAN +ProcessARP (TapAdapterPointer p_Adapter, + const PARP_PACKET src, + const IPADDR adapter_ip, + const IPADDR ip_network, + const IPADDR ip_netmask, + const MACADDR mac) +{ + //----------------------------------------------- + // Is this the kind of packet we are looking for? + //----------------------------------------------- + if (src->m_Proto == htons (ETH_P_ARP) + && MAC_EQUAL (src->m_MAC_Source, p_Adapter->m_MAC) + && MAC_EQUAL (src->m_ARP_MAC_Source, p_Adapter->m_MAC) + && MAC_EQUAL (src->m_MAC_Destination, p_Adapter->m_MAC_Broadcast) + && src->m_ARP_Operation == htons (ARP_REQUEST) + && src->m_MAC_AddressType == htons (MAC_ADDR_TYPE) + && src->m_MAC_AddressSize == sizeof (MACADDR) + && src->m_PROTO_AddressType == htons (ETH_P_IP) + && src->m_PROTO_AddressSize == sizeof (IPADDR) + && src->m_ARP_IP_Source == adapter_ip + && (src->m_ARP_IP_Destination & ip_netmask) == ip_network + && src->m_ARP_IP_Destination != adapter_ip) + { + ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE); + if (arp) + { + //---------------------------------------------- + // Initialize ARP reply fields + //---------------------------------------------- + arp->m_Proto = htons (ETH_P_ARP); + arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE); + arp->m_PROTO_AddressType = htons (ETH_P_IP); + arp->m_MAC_AddressSize = sizeof (MACADDR); + arp->m_PROTO_AddressSize = sizeof (IPADDR); + arp->m_ARP_Operation = htons (ARP_REPLY); + + //---------------------------------------------- + // ARP addresses + //---------------------------------------------- + COPY_MAC (arp->m_MAC_Source, mac); + COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC); + COPY_MAC (arp->m_ARP_MAC_Source, mac); + COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC); + arp->m_ARP_IP_Source = src->m_ARP_IP_Destination; + arp->m_ARP_IP_Destination = adapter_ip; + + DUMP_PACKET ("ProcessARP", + (unsigned char *) arp, + sizeof (ARP_PACKET)); + + InjectPacket (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); + + MemFree (arp, sizeof (ARP_PACKET)); + } + + return TRUE; + } + else + return FALSE; +} + +//=============================================================== +// Used in cases where internally generated packets such as +// ARP or DHCP replies must be returned to the kernel, to be +// seen as an incoming packet "arriving" on the interface. +//=============================================================== + +VOID +InjectPacket (TapAdapterPointer p_Adapter, + UCHAR *packet, + const unsigned int len) +{ + MYASSERT (len >= ETHERNET_HEADER_SIZE); + + __try + { + //------------------------------------------------------------ + // NdisMEthIndicateReceive and NdisMEthIndicateReceiveComplete + // could potentially be called reentrantly both here and in + // TapDeviceHook/IRP_MJ_WRITE. + // + // The DDK docs imply that this is okay. + //------------------------------------------------------------ + NdisMEthIndicateReceive + (p_Adapter->m_MiniportAdapterHandle, + (NDIS_HANDLE) p_Adapter, + packet, + ETHERNET_HEADER_SIZE, + packet + ETHERNET_HEADER_SIZE, + len - ETHERNET_HEADER_SIZE, + len - ETHERNET_HEADER_SIZE); + + NdisMEthIndicateReceiveComplete (p_Adapter->m_MiniportAdapterHandle); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacket\n", + NAME (p_Adapter))); + NOTE_ERROR (); + } +} + +//=================================================================== +// Go back to default TAP mode from Point-To-Point mode. +// Also reset (i.e. disable) DHCP Masq mode. +//=================================================================== +VOID ResetTapAdapterState (TapAdapterPointer p_Adapter) +{ + // Point-To-Point + p_Adapter->m_tun = FALSE; + p_Adapter->m_localIP = 0; + p_Adapter->m_remoteNetwork = 0; + p_Adapter->m_remoteNetmask = 0; + NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser)); + NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap)); + + // DHCP Masq + p_Adapter->m_dhcp_enabled = FALSE; + p_Adapter->m_dhcp_server_arp = FALSE; + p_Adapter->m_dhcp_user_supplied_options_buffer_len = 0; + p_Adapter->m_dhcp_addr = 0; + p_Adapter->m_dhcp_netmask = 0; + p_Adapter->m_dhcp_server_ip = 0; + p_Adapter->m_dhcp_lease_time = 0; + p_Adapter->m_dhcp_received_discover = FALSE; + p_Adapter->m_dhcp_bad_requests = 0; + NdisZeroMemory (p_Adapter->m_dhcp_server_mac, sizeof (MACADDR)); +} + +#if ENABLE_NONADMIN + +//=================================================================== +// Set TAP device handle to be accessible without admin privileges. +//=================================================================== +VOID AllowNonAdmin (TapExtensionPointer p_Extension) +{ + NTSTATUS stat; + SECURITY_DESCRIPTOR sd; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK isb; + HANDLE hand = NULL; + + NdisZeroMemory (&sd, sizeof (sd)); + NdisZeroMemory (&oa, sizeof (oa)); + NdisZeroMemory (&isb, sizeof (isb)); + + if (!p_Extension->m_CreatedUnicodeLinkName) + { + DEBUGP (("[TAP] AllowNonAdmin: UnicodeLinkName is uninitialized\n")); + NOTE_ERROR (); + return; + } + + stat = RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: RtlCreateSecurityDescriptor failed\n")); + NOTE_ERROR (); + return; + } + + InitializeObjectAttributes ( + &oa, + &p_Extension->m_UnicodeLinkName, + OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + stat = ZwOpenFile ( + &hand, + WRITE_DAC, + &oa, + &isb, + 0, + 0 + ); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwOpenFile failed, status=0x%08x\n", (unsigned int)stat)); + NOTE_ERROR (); + return; + } + + stat = ZwSetSecurityObject (hand, DACL_SECURITY_INFORMATION, &sd); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwSetSecurityObject failed\n")); + NOTE_ERROR (); + return; + } + + stat = ZwClose (hand); + if (stat != STATUS_SUCCESS) + { + DEBUGP (("[TAP] AllowNonAdmin: ZwClose failed\n")); + NOTE_ERROR (); + return; + } + + DEBUGP (("[TAP] AllowNonAdmin: SUCCEEDED\n")); +} + +#endif + +#if PACKET_TRUNCATION_CHECK + +VOID +IPv4PacketSizeVerify (const UCHAR *data, ULONG length, BOOLEAN tun, const char *prefix, LONG *counter) +{ + const IPHDR *ip; + int len = length; + + if (tun) + { + ip = (IPHDR *) data; + } + else + { + if (length >= sizeof (ETH_HEADER)) + { + const ETH_HEADER *eth = (ETH_HEADER *) data; + + if (eth->proto != htons (ETH_P_IP)) + return; + + ip = (IPHDR *) (data + sizeof (ETH_HEADER)); + len -= sizeof (ETH_HEADER); + } + else + return; + } + + if (len >= sizeof (IPHDR)) + { + const int totlen = ntohs (ip->tot_len); + + DEBUGP (("[TAP] IPv4PacketSizeVerify %s len=%d totlen=%d\n", prefix, len, totlen)); + + if (len != totlen) + ++(*counter); + } +} + +#endif + +//====================================================================== +// End of Source +//====================================================================== |