/* * 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 */ //====================================================== // 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_WIN_IOCTL_CONFIG_POINT_TO_POINT or // TAP_WIN_IOCTL_CONFIG_TUN ioctl. //====================================================== #include "tap-windows.h" #include "config.h" #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 #if defined(DDKVER_MAJOR) && DDKVER_MAJOR < 5600 #include #include #include #else #include #include #include #endif #include "lock.h" #include "constants.h" #include "proto.h" #include "error.h" #include "endian.h" #include "types.h" #include "prototypes.h" #include "mem.c" #include "macinfo.c" #include "error.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) DRIVER_INITIALIZE 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 = (NDIS_MINIPORT_CHARACTERISTICS *)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->Length/2, 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 //============================================================ DRIVER_UNLOAD TapDriverUnload; 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; DEBUGP (("[TAP] AdapterCreate called\n")); //==================================== // 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 mkey = NDIS_STRING_CONST("MiniportName"); NDIS_STRING vkey = NDIS_STRING_CONST("NdisVersion"); NDIS_STATUS vstatus; NDIS_CONFIGURATION_PARAMETER *vparm; NdisReadConfiguration (&vstatus, &vparm, configHandle, &vkey, NdisParameterInteger); if (vstatus == NDIS_STATUS_SUCCESS) DEBUGP (("[TAP] NdisReadConfiguration NdisVersion=%X\n", vparm->ParameterData.IntegerData)); NdisReadConfiguration (&status, &parm, configHandle, &mkey, NdisParameterString); if (status == NDIS_STATUS_SUCCESS) { if (parm->ParameterType == NdisParameterString) { DEBUGP (("[TAP] NdisReadConfiguration (MiniportName=%.*S)\n", parm->ParameterData.StringData.Length/2, parm->ParameterData.StringData.Buffer)); if (RtlUnicodeStringToAnsiString ( &l_Adapter->m_NameAnsi, &parm->ParameterData.StringData, TRUE) != STATUS_SUCCESS) { DEBUGP (("[TAP] MiniportName failed\n")); status = NDIS_STATUS_RESOURCES; } } } else { /* "MiniportName" is available only XP and above. Not on Windows 2000. */ if (vstatus == NDIS_STATUS_SUCCESS && vparm->ParameterData.IntegerData == 0x50000) { /* Fallback for Windows 2000 with NDIS version 5.00.00 Don't use this on Vista, 'NDIS_MINIPORT_BLOCK' was changed! */ if (RtlUnicodeStringToAnsiString (&l_Adapter->m_NameAnsi, &((struct WIN2K_NDIS_MINIPORT_BLOCK *) p_AdapterHandle)->MiniportName, TRUE) != STATUS_SUCCESS) { DEBUGP (("[TAP] MiniportName (W2K) failed\n")); status = NDIS_STATUS_RESOURCES; } else { DEBUGP (("[TAP] MiniportName (W2K) succeeded: %s\n", l_Adapter->m_NameAnsi.Buffer)); status = NDIS_STATUS_SUCCESS; } } } } /* Can't continue without name (see macro 'NAME') */ if (status != NDIS_STATUS_SUCCESS || !l_Adapter->m_NameAnsi.Buffer) { NdisCloseConfiguration (configHandle); AdapterFreeResources (l_Adapter); DEBUGP (("[TAP] failed to get miniport name\n")); 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; } } } } /* 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, (const unsigned char *)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])); //==================================== // 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); DEBUGP (("[TAP] CreateTapDevice failed\n")); return tap_status; } } if (!AddAdapterToInstanceList (l_Adapter)) { NOTE_ERROR (); TapDeviceFreeResources (&l_Adapter->m_Extension); AdapterFreeResources (l_Adapter); DEBUGP (("[TAP] AddAdapterToInstanceList failed\n")); return NDIS_STATUS_RESOURCES; } l_Adapter->m_InterfaceIsRunning = TRUE; 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 (&p_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 = (PCHAR)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, TAP_WIN_SUFFIX); 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, TAP_WIN_SUFFIX); 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-Windows 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_TAP_WIN_DEVICE_DESCRIPTION; l_QueryLength = strlen (PRODUCT_TAP_WIN_DEVICE_DESCRIPTION) + 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) PRODUCT_TAP_WIN_MAJOR) << 8 | (USHORT) PRODUCT_TAP_WIN_MINOR); 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_PacketLength = 0; UINT l_BufferLength = 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); //=============================================== // 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. //====================================================================== DRIVER_DISPATCH TapDeviceHook; 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; ULONG i,j; 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_WIN_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_WIN_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_WIN_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; } #if DBG case TAP_WIN_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 // Allow ZeroTier One to get multicast memberships at the L2 level in a // protocol-neutral manner. case TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS: { if (&l_Adapter->m_MCLockAllocated) NdisAcquireSpinLock (&l_Adapter->m_MCLock); if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength < TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE) { /* output buffer too small */ NOTE_ERROR (); p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER; } else { char *out = (char *)p_IRP->AssociatedIrp.SystemBuffer; char *end = out + TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE; for(i=0;im_MCListSize;++i) { if (i >= TAP_MAX_MCAST_LIST) break; for(j=0;j<6;++j) *(out++) = l_Adapter->m_MCList.list[i].addr[j]; if (out >= end) break; } while (out < end) *(out++) = (char)0; p_IRP->IoStatus.Information = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength; p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS; } if (&l_Adapter->m_MCLockAllocated) NdisReleaseSpinLock (&l_Adapter->m_MCLock); break; } case TAP_WIN_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; } 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_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); NdisMEthIndicateReceive (l_Adapter->m_MiniportAdapterHandle, (NDIS_HANDLE) l_Adapter, (PCHAR)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 { 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) { 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; 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, IRP, and injection queues. //=========================================== VOID FlushQueues (TapExtensionPointer p_Extension) { PIRP l_IRP; TapPacketPointer l_PacketBuffer; MYASSERT (p_Extension); MYASSERT (p_Extension->m_TapDevice); while (TRUE) { NdisAcquireSpinLock (&p_Extension->m_QueueLock); l_IRP = (PIRP)QueuePop (p_Extension->m_IrpQueue); NdisReleaseSpinLock (&p_Extension->m_QueueLock); if (l_IRP) { CancelIRP (NULL, l_IRP, FALSE); } else break; } while (TRUE) { NdisAcquireSpinLock (&p_Extension->m_QueueLock); l_PacketBuffer = (TapPacketPointer)QueuePop (p_Extension->m_PacketQueue); NdisReleaseSpinLock (&p_Extension->m_QueueLock); if (l_PacketBuffer) { MemFree (l_PacketBuffer, TAP_PACKET_SIZE (l_PacketBuffer->m_SizeFlags & TP_SIZE_MASK)); } else break; } } //=================================================== // 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; } }