From 7ea8261049d8897b46e26f62dec3f6768e0b3f9a Mon Sep 17 00:00:00 2001 From: james Date: Mon, 22 Jun 2009 20:48:35 +0000 Subject: In Windows TAP driver, refactor DHCP/ARP packet injection code to use a DPC (deferred procedure call) to defer packet injection until IRQL < DISPATCH_LEVEL, rather than calling NdisMEthIndicateReceive in the context of AdapterTransmit. This is an attempt to reduce kernel stack usage, and prevent EXCEPTION_DOUBLE_FAULT BSODs that have been observed on Vista. Updated TAP driver version number to 9.6. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@4606 e7ae566f-a301-0410-adde-c780ea21d3b5 --- install-win32/settings.in | 12 ++--- tap-win32/constants.h | 1 + tap-win32/dhcp.c | 6 +-- tap-win32/prototypes.h | 21 +++++++- tap-win32/tapdrvr.c | 125 +++++++++++++++++++++++++++++++++++++++------- tap-win32/types.h | 15 ++++++ version.m4 | 2 +- 7 files changed, 154 insertions(+), 28 deletions(-) diff --git a/install-win32/settings.in b/install-win32/settings.in index 742fd31..cbfe58d 100644 --- a/install-win32/settings.in +++ b/install-win32/settings.in @@ -30,20 +30,20 @@ # Optional directory of prebuilt OpenVPN binary components, # to be used as a source when build-from-scratch prerequisites # are not met. -!define GENOUT_PREBUILT "../gen-prebuilt" +;!define GENOUT_PREBUILT "../gen-prebuilt" # tapinstall.exe source code. # Not needed if DRVBINSRC is defined # (or if using pre-built mode). -;!define TISRC "../tapinstall" +!define TISRC "../tapinstall" # TAP Adapter parameters. Note that PRODUCT_TAP_ID is # defined in version.m4. !define PRODUCT_TAP_DEVICE_DESCRIPTION "TAP-Win32 Adapter V9" !define PRODUCT_TAP_PROVIDER "TAP-Win32 Provider V9" !define PRODUCT_TAP_MAJOR_VER 9 -!define PRODUCT_TAP_MINOR_VER 5 -!define PRODUCT_TAP_RELDATE "05/13/2009" +!define PRODUCT_TAP_MINOR_VER 6 +!define PRODUCT_TAP_RELDATE "06/22/2009" # TAP adapter icon -- visible=0x81 or hidden=0x89 !define PRODUCT_TAP_CHARACTERISTICS 0x81 @@ -53,8 +53,8 @@ # DDK Version. # DDK distribution is assumed to be in C:\WINDDK\${DDKVER} -;!define DDKVER 6001.18002 -;!define DDKVER_MAJOR 6001 +!define DDKVER 6001.18002 +!define DDKVER_MAJOR 6001 # Code Signing. # If undefined, don't sign any files. diff --git a/tap-win32/constants.h b/tap-win32/constants.h index b6a3d56..fc7855d 100755 --- a/tap-win32/constants.h +++ b/tap-win32/constants.h @@ -51,5 +51,6 @@ #define PACKET_QUEUE_SIZE 64 // tap -> userspace queue size #define IRP_QUEUE_SIZE 16 // max number of simultaneous i/o operations from userspace +#define INJECT_QUEUE_SIZE 16 // DHCP/ARP -> tap injection queue #define TAP_LITTLE_ENDIAN // affects ntohs, htonl, etc. functions diff --git a/tap-win32/dhcp.c b/tap-win32/dhcp.c index 47e2995..b6b28bb 100755 --- a/tap-win32/dhcp.c +++ b/tap-win32/dhcp.c @@ -379,9 +379,9 @@ SendDHCPMsg (const TapAdapterPointer a, DHCPMSG_LEN_FULL (pkt)); // Return DHCP response to kernel - InjectPacket (a, - DHCPMSG_BUF (pkt), - DHCPMSG_LEN_FULL (pkt)); + InjectPacketDeferred (a, + DHCPMSG_BUF (pkt), + DHCPMSG_LEN_FULL (pkt)); } else { diff --git a/tap-win32/prototypes.h b/tap-win32/prototypes.h index c342ad3..29502d6 100755 --- a/tap-win32/prototypes.h +++ b/tap-win32/prototypes.h @@ -171,13 +171,32 @@ VOID SetMediaStatus BOOLEAN state ); -VOID InjectPacket +VOID InjectPacketDeferred ( TapAdapterPointer p_Adapter, UCHAR *packet, const unsigned int len ); +VOID InjectPacketNow + ( + TapAdapterPointer p_Adapter, + UCHAR *packet, + const unsigned int len + ); + +// for KDEFERRED_ROUTINE and Static Driver Verifier +//#include +//KDEFERRED_ROUTINE InjectPacketDpc; + +VOID InjectPacketDpc + ( + KDPC *Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2 + ); + VOID CheckIfDhcpAndTunMode ( TapAdapterPointer p_Adapter diff --git a/tap-win32/tapdrvr.c b/tap-win32/tapdrvr.c index cd4d179..a856734 100755 --- a/tap-win32/tapdrvr.c +++ b/tap-win32/tapdrvr.c @@ -692,6 +692,8 @@ TapDeviceFreeResources (TapExtensionPointer p_Extension) QueueFree (p_Extension->m_PacketQueue); if (p_Extension->m_IrpQueue) QueueFree (p_Extension->m_IrpQueue); + if (p_Extension->m_InjectQueue) + QueueFree (p_Extension->m_InjectQueue); if (p_Extension->m_CreatedUnicodeLinkName) RtlFreeUnicodeString (&p_Extension->m_UnicodeLinkName); @@ -717,8 +719,14 @@ TapDeviceFreeResources (TapExtensionPointer p_Extension) if (p_Extension->m_TapName) MemFree (p_Extension->m_TapName, NAME_BUFFER_SIZE); + if (p_Extension->m_InjectDpcInitialized) + KeRemoveQueueDpc (&p_Extension->m_InjectDpc); + if (p_Extension->m_AllocatedSpinlocks) - NdisFreeSpinLock (&p_Extension->m_QueueLock); + { + NdisFreeSpinLock (&p_Extension->m_QueueLock); + NdisFreeSpinLock (&p_Extension->m_InjectLock); + } } //======================================================================== @@ -932,19 +940,28 @@ CreateTapDevice (TapExtensionPointer p_Extension, const char *p_Name) //======================================================== NdisAllocateSpinLock (&p_Extension->m_QueueLock); + NdisAllocateSpinLock (&p_Extension->m_InjectLock); p_Extension->m_AllocatedSpinlocks = TRUE; p_Extension->m_PacketQueue = QueueInit (PACKET_QUEUE_SIZE); p_Extension->m_IrpQueue = QueueInit (IRP_QUEUE_SIZE); - + p_Extension->m_InjectQueue = QueueInit (INJECT_QUEUE_SIZE); if (!p_Extension->m_PacketQueue - || !p_Extension->m_IrpQueue) + || !p_Extension->m_IrpQueue + || !p_Extension->m_InjectQueue) { DEBUGP (("[%s] couldn't alloc TAP queues\n", p_Name)); l_Return = NDIS_STATUS_RESOURCES; goto cleanup; } + //================================================================= + // Initialize deferred procedure call for DHCP/ARP packet injection + //================================================================= + + KeInitializeDpc (&p_Extension->m_InjectDpc, InjectPacketDpc, NULL); + p_Extension->m_InjectDpcInitialized = TRUE; + //======================== // Finalize initialization //======================== @@ -1808,9 +1825,9 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) 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]", + "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d,%d] Rx=[%d,%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d] InjQ=[%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]", + "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d] InjQ=[%d,%d,%d]", #endif state, g_LastErrorFilename, @@ -1831,7 +1848,10 @@ TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP) (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 + (int)PACKET_QUEUE_SIZE, + (int)l_Adapter->m_Extension.m_InjectQueue->size, + (int)l_Adapter->m_Extension.m_InjectQueue->max_size, + (int)INJECT_QUEUE_SIZE ); p_IRP->IoStatus.Information @@ -2519,15 +2539,16 @@ CancelIRP (TapExtensionPointer p_Extension, IoCompleteRequest (p_IRP, IO_NO_INCREMENT); } -//==================================== -// Exhaust packet and IRP queues. -//==================================== +//=========================================== +// Exhaust packet, IRP, and injection queues. +//=========================================== VOID FlushQueues (TapExtensionPointer p_Extension) { PIRP l_IRP; TapPacketPointer l_PacketBuffer; - int n_IRP=0, n_Packet=0; + InjectPacketPointer l_InjectBuffer; + int n_IRP=0, n_Packet=0, n_Inject=0; MYASSERT (p_Extension); MYASSERT (p_Extension->m_TapDevice); @@ -2560,15 +2581,32 @@ FlushQueues (TapExtensionPointer p_Extension) break; } + while (TRUE) + { + NdisAcquireSpinLock (&p_Extension->m_InjectLock); + l_InjectBuffer = QueuePop (p_Extension->m_InjectQueue); + NdisReleaseSpinLock (&p_Extension->m_InjectLock); + if (l_InjectBuffer) + { + ++n_Inject; + INJECT_PACKET_FREE(l_InjectBuffer); + } + else + break; + } + DEBUGP (( - "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d]\n", + "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d] n_Inject=[%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 + PACKET_QUEUE_SIZE, + n_Inject, + p_Extension->m_InjectQueue->max_size, + INJECT_QUEUE_SIZE )); } @@ -2667,7 +2705,7 @@ ProcessARP (TapAdapterPointer p_Adapter, (unsigned char *) arp, sizeof (ARP_PACKET)); - InjectPacket (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); + InjectPacketDeferred (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET)); MemFree (arp, sizeof (ARP_PACKET)); } @@ -2684,10 +2722,60 @@ ProcessARP (TapAdapterPointer p_Adapter, // seen as an incoming packet "arriving" on the interface. //=============================================================== +// Defer packet injection till IRQL < DISPATCH_LEVEL VOID -InjectPacket (TapAdapterPointer p_Adapter, - UCHAR *packet, - const unsigned int len) +InjectPacketDeferred (TapAdapterPointer p_Adapter, + UCHAR *packet, + const unsigned int len) +{ + InjectPacketPointer l_InjectBuffer; + PVOID result; + + if (NdisAllocateMemoryWithTag (&l_InjectBuffer, + INJECT_PACKET_SIZE (len), + 'IPAT') == NDIS_STATUS_SUCCESS) + { + l_InjectBuffer->m_Size = len; + NdisMoveMemory (l_InjectBuffer->m_Data, packet, len); + NdisAcquireSpinLock (&p_Adapter->m_Extension.m_InjectLock); + result = QueuePush (p_Adapter->m_Extension.m_InjectQueue, l_InjectBuffer); + NdisReleaseSpinLock (&p_Adapter->m_Extension.m_InjectLock); + if (result) + KeInsertQueueDpc (&p_Adapter->m_Extension.m_InjectDpc, p_Adapter, NULL); + else + INJECT_PACKET_FREE(l_InjectBuffer); + } +} + +// Handle the injection of previously deferred packets +VOID +InjectPacketDpc(KDPC *Dpc, + PVOID DeferredContext, + PVOID SystemArgument1, + PVOID SystemArgument2) +{ + InjectPacketPointer l_InjectBuffer; + TapAdapterPointer l_Adapter = (TapAdapterPointer)SystemArgument1; + while (TRUE) + { + NdisAcquireSpinLock (&l_Adapter->m_Extension.m_InjectLock); + l_InjectBuffer = QueuePop (l_Adapter->m_Extension.m_InjectQueue); + NdisReleaseSpinLock (&l_Adapter->m_Extension.m_InjectLock); + if (l_InjectBuffer) + { + InjectPacketNow(l_Adapter, l_InjectBuffer->m_Data, l_InjectBuffer->m_Size); + INJECT_PACKET_FREE(l_InjectBuffer); + } + else + break; + } +} + +// Do packet injection now +VOID +InjectPacketNow (TapAdapterPointer p_Adapter, + UCHAR *packet, + const unsigned int len) { MYASSERT (len >= ETHERNET_HEADER_SIZE); @@ -2699,6 +2787,9 @@ InjectPacket (TapAdapterPointer p_Adapter, // TapDeviceHook/IRP_MJ_WRITE. // // The DDK docs imply that this is okay. + // + // Note that reentrant behavior could only occur if the + // non-deferred version of InjectPacket is used. //------------------------------------------------------------ NdisMEthIndicateReceive (p_Adapter->m_MiniportAdapterHandle, @@ -2713,7 +2804,7 @@ InjectPacket (TapAdapterPointer p_Adapter, } __except (EXCEPTION_EXECUTE_HANDLER) { - DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacket\n", + DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacketNow\n", NAME (p_Adapter))); NOTE_ERROR (); } diff --git a/tap-win32/types.h b/tap-win32/types.h index d424521..4adee6a 100755 --- a/tap-win32/types.h +++ b/tap-win32/types.h @@ -86,6 +86,12 @@ typedef struct _TapExtension // Flags BOOLEAN m_TapIsRunning; BOOLEAN m_CalledTapDeviceFreeResources; + + // DPC queue for deferred packet injection + BOOLEAN m_InjectDpcInitialized; + KDPC m_InjectDpc; + NDIS_SPIN_LOCK m_InjectLock; + Queue *m_InjectQueue; } TapExtension, *TapExtensionPointer; @@ -99,6 +105,15 @@ typedef struct _TapPacket } TapPacket, *TapPacketPointer; +typedef struct _InjectPacket + { +# define INJECT_PACKET_SIZE(data_size) (sizeof (InjectPacket) + (data_size)) +# define INJECT_PACKET_FREE(ib) NdisFreeMemory ((ib), INJECT_PACKET_SIZE ((ib)->m_Size), 0) + ULONG m_Size; + UCHAR m_Data []; // m_Data must be the last struct member + } +InjectPacket, *InjectPacketPointer; + typedef struct _TapAdapter { # define NAME(a) ((a)->m_NameAnsi.Buffer) diff --git a/version.m4 b/version.m4 index 370f7eb..d843c4c 100644 --- a/version.m4 +++ b/version.m4 @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1_rc18]) +define(PRODUCT_VERSION,[2.1_rc18a]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) -- cgit