diff --git a/src/adapter.c b/src/adapter.c index a62444d..78616d9 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -113,7 +113,7 @@ tapAdapterContextAllocate( adapter->ReceiveNblPool = NdisAllocateNetBufferListPool( adapter->MiniportAdapterHandle, - &nblPoolParameters); + &nblPoolParameters); if (adapter->ReceiveNblPool == NULL) { @@ -137,6 +137,11 @@ tapAdapterContextAllocate( // NBL pool for making TAP receive indications. NdisZeroMemory(&nblPoolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS)); + // Initialize event used to determine when all receive NBLs have been returned. + NdisInitializeEvent(&adapter->ReceiveNblInFlightCountZeroEvent); + + + // Add initial reference. Normally removed in AdapterHalt. adapter->RefCount = 1; @@ -176,7 +181,7 @@ tapReadCurrentAddress( if (status == NDIS_STATUS_SUCCESS && configAddressLength == 6) { - + if ((ETH_IS_MULTICAST(configAddress) || ETH_IS_BROADCAST(configAddress)) || !NIC_ADDR_IS_LOCALLY_ADMINISTERED(configAddress)) @@ -246,7 +251,7 @@ tapReadConfiguration( // // NetCfgInstanceId is a GUID string provided by NDIS that identifies // the adapter instance. An example is: - // + // // NetCfgInstanceId={410EB49D-2381-4FE7-9B36-498E22619DF0} // // Other names are derived from NetCfgInstanceId. For example, MiniportName: @@ -276,7 +281,7 @@ tapReadConfiguration( Adapter->NetCfgInstanceId.Buffer = Adapter->NetCfgInstanceIdBuffer; NdisMoveMemory( - Adapter->NetCfgInstanceId.Buffer, + Adapter->NetCfgInstanceId.Buffer, configParameter->ParameterData.StringData.Buffer, Adapter->NetCfgInstanceId.Length ); @@ -792,7 +797,7 @@ AdapterCreate( genAttributes.IfType = TAP_IFTYPE; genAttributes.IfConnectorPresent = TAP_HAS_PHYSICAL_CONNECTOR; genAttributes.SupportedStatistics = TAP_SUPPORTED_STATISTICS; - genAttributes.SupportedPauseFunctions = NdisPauseFunctionsUnsupported; // IEEE 802.3 pause frames + genAttributes.SupportedPauseFunctions = NdisPauseFunctionsUnsupported; // IEEE 802.3 pause frames genAttributes.DataBackFillSize = 0; genAttributes.ContextBackFillSize = 0; @@ -954,6 +959,63 @@ Return Value: DEBUGP (("[TAP] <-- AdapterHalt\n")); } +VOID +tapWaitForReceiveNblInFlightCountZeroEvent( + __in PTAP_ADAPTER_CONTEXT Adapter + ) +{ + LONG nblCount; + + // + // Wait until higher-level protocol has returned all NBLs + // to the driver. + // + + // Add one NBL "bias" to insure allow event to be reset safely. + nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + NdisResetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); + + // + // Now remove the bias and wait for the ReceiveNblInFlightCountZeroEvent + // if the count returned is not zero. + // + nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount >= 0); + + if(nblCount) + { + LARGE_INTEGER startTime, currentTime; + + NdisGetSystemUpTimeEx(&startTime); + + for (;;) + { + BOOLEAN waitResult = NdisWaitEvent( + &Adapter->ReceiveNblInFlightCountZeroEvent, + TAP_WAIT_POLL_LOOP_TIMEOUT + ); + + NdisGetSystemUpTimeEx(¤tTime); + + if (waitResult) + { + break; + } + + DEBUGP (("[%s] Waiting for %d in-flight receive NBLs to be returned.\n", + MINIPORT_INSTANCE_ID (Adapter), + Adapter->ReceiveNblInFlightCount + )); + } + + DEBUGP (("[%s] Waited %d ms for all in-flight NBLs to be returned.\n", + MINIPORT_INSTANCE_ID (Adapter), + (currentTime.LowPart - startTime.LowPart) + )); + } +} + NDIS_STATUS AdapterPause( __in NDIS_HANDLE MiniportAdapterContext, @@ -1016,6 +1078,21 @@ Return Value: adapter->Locked.AdapterState = MiniportPausingState; tapAdapterReleaseLock(adapter,FALSE); + // + // Stop the flow of network data through the receive path + // ------------------------------------------------------ + // In the Pausing and Paused state tapAdapterSendAndReceiveReady + // will prevent new calls to NdisMIndicateReceiveNetBufferLists + // to indicate additional receive NBLs to the host. + // + // However, there may be some in-flight NBLs owned by the driver + // that have been indicated to the host but have not yet been + // returned. + // + // Wait here for all in-flight receive indications to be returned. + // + tapWaitForReceiveNblInFlightCountZeroEvent(adapter); + // // Stop the flow of network data through the send path // --------------------------------------------------- @@ -1566,7 +1643,7 @@ Return Value: { return 0; } - + PETH_HEADER ethernetHeader = (PETH_HEADER)PacketBuffer; ASSERT(ethernetHeader); @@ -1582,10 +1659,10 @@ Return Value: { // Note: Counterintutive match code from this macro. 0 = match ETH_COMPARE_NETWORK_ADDRESSES_EQ( - Adapter->MCList[i], - ethernetHeader->dest, + Adapter->MCList[i], + ethernetHeader->dest, &address_match); - + if(address_match == 0) { // Address is in multicast list @@ -1600,13 +1677,13 @@ Return Value: // Determine if packet is directed to our address or not. int address_match = 0; ETH_COMPARE_NETWORK_ADDRESSES_EQ( - Adapter->CurrentAddress, - ethernetHeader->dest, + Adapter->CurrentAddress, + ethernetHeader->dest, &address_match); if(address_match == 0) { - return NDIS_PACKET_TYPE_DIRECTED; + return NDIS_PACKET_TYPE_DIRECTED; } else { @@ -1738,7 +1815,7 @@ tapAdapterAcquireLock( ) { ASSERT(!DispatchLevel || (DISPATCH_LEVEL == KeGetCurrentIrql())); - + if (DispatchLevel) { NdisDprAcquireSpinLock(&Adapter->AdapterLock); @@ -1760,7 +1837,7 @@ tapAdapterReleaseLock( ) { ASSERT(!DispatchLevel || (DISPATCH_LEVEL == KeGetCurrentIrql())); - + if (DispatchLevel) { NdisDprReleaseSpinLock(&Adapter->AdapterLock); diff --git a/src/adapter.h b/src/adapter.h index c62249f..f3840f9 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -114,7 +114,7 @@ typedef struct _TAP_ADAPTER_CONTEXT // ---------------------------------- // This a GUID string provided by NDIS that identifies the adapter instance. // An example is: - // + // // NetCfgInstanceId={410EB49D-2381-4FE7-9B36-498E22619DF0} // // Other names are derived from NetCfgInstanceId. For example, MiniportName: @@ -184,6 +184,10 @@ typedef struct _TAP_ADAPTER_CONTEXT // NBL pool for making TAP receive indications. NDIS_HANDLE ReceiveNblPool; + volatile LONG ReceiveNblInFlightCount; +#define TAP_WAIT_POLL_LOOP_TIMEOUT 3000 // 3 seconds + NDIS_EVENT ReceiveNblInFlightCountZeroEvent; + // Info for point-to-point mode BOOLEAN m_tun; IPADDR m_localIP; diff --git a/src/rxpath.c b/src/rxpath.c index 3204d26..b63394e 100644 --- a/src/rxpath.c +++ b/src/rxpath.c @@ -36,104 +36,6 @@ #pragma alloc_text( PAGE, TapDeviceWrite) #endif // ALLOC_PRAGMA -VOID -tapFreeReceiveNetBufferList( - __in PTAP_ADAPTER_CONTEXT Adapter, - __in PNET_BUFFER_LIST NetBufferList // Only one NB here... -) -{ - ULONG frameType, netBufferCount, byteCount; - - // Fetch NB frame type. - frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(NetBufferList)); - - // Fetch statistics for all NBs linked to the NB. - netBufferCount = tapGetNetBufferCountsFromNetBufferList( - NetBufferList, - &byteCount - ); - - // Update statistics by frame type - switch (frameType) - { - case NDIS_PACKET_TYPE_DIRECTED: - Adapter->FramesRxDirected += netBufferCount; - Adapter->BytesRxDirected += byteCount; - break; - - case NDIS_PACKET_TYPE_BROADCAST: - Adapter->FramesRxBroadcast += netBufferCount; - Adapter->BytesRxBroadcast += byteCount; - break; - - case NDIS_PACKET_TYPE_MULTICAST: - Adapter->FramesRxMulticast += netBufferCount; - Adapter->BytesRxMulticast += byteCount; - break; - - default: - ASSERT(FALSE); - break; - } - - // - // Handle P2P Packet - // ----------------- - // Free MDL allocated for P2P Ethernet header. - // - if (TAP_RX_NBL_FLAG_TEST(NetBufferList, TAP_RX_NBL_FLAGS_IS_P2P)) - { - PNET_BUFFER netBuffer; - PMDL mdl; - - netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); - mdl = NET_BUFFER_FIRST_MDL(netBuffer); - mdl->Next = NULL; - - NdisFreeMdl(mdl); - } - - // - // Handle Injected Packet - // ----------------------- - // Free MDL and data buffer allocated for injected packet. - // - if (TAP_RX_NBL_FLAG_TEST(NetBufferList, TAP_RX_NBL_FLAGS_IS_INJECTED)) - { - ULONG pagePriority; - PNET_BUFFER netBuffer; - PMDL mdl; - PUCHAR injectBuffer; - - netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); - mdl = NET_BUFFER_FIRST_MDL(netBuffer); - - // - // On Windows versions 8 and above, the MDL can be marked as not executable. - // This is required for the driver to function under HyperVisor-enforced - // Code Integrity (HVCI). - // - - pagePriority = NormalPagePriority; - - if (GlobalData.RunningWindows8OrGreater != FALSE) { - pagePriority |= MdlMappingNoExecute; - } - - injectBuffer = (PUCHAR)MmGetSystemAddressForMdlSafe(mdl, pagePriority); - - if (injectBuffer) - { - NdisFreeMemory(injectBuffer, 0, 0); - } - - NdisFreeMdl(mdl); - } - - // Free the NBL - NdisFreeNetBufferList(NetBufferList); -} - //=============================================================== // Used in cases where internally generated packets such as // ARP or DHCP replies must be returned to the kernel, to be @@ -224,7 +126,8 @@ IndicateReceivePacket( if(netBufferList != NULL) { - ULONG receiveFlags = NDIS_RECEIVE_FLAGS_RESOURCES; + ULONG receiveFlags = 0; + LONG nblCount; NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL @@ -240,6 +143,10 @@ IndicateReceivePacket( netBufferList->MiniportReserved[0] = NULL; netBufferList->MiniportReserved[1] = NULL; + // Increment in-flight receive NBL count. + nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + netBufferList->SourceHandle = Adapter->MiniportAdapterHandle; // @@ -256,8 +163,6 @@ IndicateReceivePacket( receiveFlags ); - tapFreeReceiveNetBufferList(Adapter->MiniportAdapterHandle, netBufferList); - return; } else @@ -287,6 +192,129 @@ IndicateReceivePacket( } } +VOID +tapCompleteIrpAndFreeReceiveNetBufferList( + __in PTAP_ADAPTER_CONTEXT Adapter, + __in PNET_BUFFER_LIST NetBufferList, // Only one NB here... + __in NTSTATUS IoCompletionStatus + ) +{ + PIRP irp; + ULONG frameType, netBufferCount, byteCount; + LONG nblCount; + + // Fetch NB frame type. + frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(NetBufferList)); + + // Fetch statistics for all NBs linked to the NB. + netBufferCount = tapGetNetBufferCountsFromNetBufferList( + NetBufferList, + &byteCount + ); + + // Update statistics by frame type + if(IoCompletionStatus == STATUS_SUCCESS) + { + switch(frameType) + { + case NDIS_PACKET_TYPE_DIRECTED: + Adapter->FramesRxDirected += netBufferCount; + Adapter->BytesRxDirected += byteCount; + break; + + case NDIS_PACKET_TYPE_BROADCAST: + Adapter->FramesRxBroadcast += netBufferCount; + Adapter->BytesRxBroadcast += byteCount; + break; + + case NDIS_PACKET_TYPE_MULTICAST: + Adapter->FramesRxMulticast += netBufferCount; + Adapter->BytesRxMulticast += byteCount; + break; + + default: + ASSERT(FALSE); + break; + } + } + + // + // Handle P2P Packet + // ----------------- + // Free MDL allocated for P2P Ethernet header. + // + if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_P2P)) + { + PNET_BUFFER netBuffer; + PMDL mdl; + + netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + mdl = NET_BUFFER_FIRST_MDL(netBuffer); + mdl->Next = NULL; + + NdisFreeMdl(mdl); + } + + // + // Handle Injected Packet + // ----------------------- + // Free MDL and data buffer allocated for injected packet. + // + if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED)) + { + ULONG pagePriority; + PNET_BUFFER netBuffer; + PMDL mdl; + PUCHAR injectBuffer; + + netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); + mdl = NET_BUFFER_FIRST_MDL(netBuffer); + + // + // On Windows versions 8 and above, the MDL can be marked as not executable. + // This is required for the driver to function under HyperVisor-enforced + // Code Integrity (HVCI). + // + + pagePriority = NormalPagePriority; + + if (GlobalData.RunningWindows8OrGreater != FALSE) { + pagePriority |= MdlMappingNoExecute; + } + + injectBuffer = (PUCHAR )MmGetSystemAddressForMdlSafe(mdl,pagePriority); + + if(injectBuffer) + { + NdisFreeMemory(injectBuffer,0,0); + } + + NdisFreeMdl(mdl); + } + + // + // Complete the IRP + // + irp = (PIRP )NetBufferList->MiniportReserved[0]; + + if(irp) + { + irp->IoStatus.Status = IoCompletionStatus; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } + + // Decrement in-flight receive NBL count. + nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount >= 0 ); + if (0 == nblCount) + { + NdisSetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); + } + + // Free the NBL + NdisFreeNetBufferList(NetBufferList); +} + VOID AdapterReturnNetBufferLists( __in NDIS_HANDLE MiniportAdapterContext, @@ -295,11 +323,34 @@ AdapterReturnNetBufferLists( ) { PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + PNET_BUFFER_LIST currentNbl; + + UNREFERENCED_PARAMETER(ReturnFlags); + + // + // Process each NBL individually + // + currentNbl = NetBufferLists; + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + NET_BUFFER_LIST_NEXT_NBL(currentNbl) = NULL; + + // Complete write IRP and free NBL and associated resources. + tapCompleteIrpAndFreeReceiveNetBufferList( + adapter, + currentNbl, + STATUS_SUCCESS + ); - DEBUGP(("[%s] Unexpected AdapterReturnNetBufferLists() call\n", MINIPORT_INSTANCE_ID(adapter))); + // Move to next NBL + currentNbl = nextNbl; + } } -static PVOID +static PVOID TapStrip8021Q( __inout unsigned char ** PacketBuffer, __inout ULONG *PacketLength @@ -350,6 +401,8 @@ TapSharedSendPacket( unsigned int fullLength; PNET_BUFFER_LIST netBufferList = NULL; PMDL mdl = NULL; // Head of MDL chain. + LONG nblCount; + irpSp = IoGetCurrentIrpStackLocation( Irp ); fullLength = PacketLength + PrefixLength; @@ -403,10 +456,10 @@ TapSharedSendPacket( NOTE_ERROR (); NdisFreeMemory(allocBuffer,0,0); - + // Fail the IRP Irp->IoStatus.Information = 0; - return STATUS_INSUFFICIENT_RESOURCES; + return STATUS_INSUFFICIENT_RESOURCES; } mdl->Next = NULL; // No next MDL @@ -429,11 +482,11 @@ TapSharedSendPacket( NdisFreeMdl(mdl); NdisFreeMemory(allocBuffer,0,0); - + // Fail the IRP Irp->IoStatus.Information = 0; - return STATUS_INSUFFICIENT_RESOURCES; - } + return STATUS_INSUFFICIENT_RESOURCES; + } // Set flag indicating that this is an injected packet // In particular, it has the same cleanup path, and that is all the flag is used for currently. @@ -441,7 +494,7 @@ TapSharedSendPacket( TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED); } else - { + { if(PrefixLength > 0) { // @@ -457,7 +510,7 @@ TapSharedSendPacket( PrefixLength ); - if(mdl == NULL) + if(mdl == NULL) { DEBUGP (("[%s] NdisAllocateMdl failed in IRP_MJ_WRITE\n", MINIPORT_INSTANCE_ID (Adapter))); @@ -512,8 +565,22 @@ TapSharedSendPacket( NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL + // This IRP is pended. + IoMarkIrpPending(Irp); + + // This IRP cannot be cancelled while in-flight. + IoSetCancelRoutine(Irp,NULL); + + // Stash IRP pointer in NBL MiniportReserved[0] field. + netBufferList->MiniportReserved[0] = Irp; + netBufferList->MiniportReserved[1] = NULL; + NET_BUFFER_LIST_INFO(netBufferList, Ieee8021QNetBufferListInfo) = PacketPriority; + // Increment in-flight receive NBL count. + nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0 ); + // // Indicate the packet // ------------------- @@ -524,12 +591,10 @@ TapSharedSendPacket( netBufferList, NDIS_DEFAULT_PORT_NUMBER, 1, // NumberOfNetBufferLists - NDIS_RECEIVE_FLAGS_RESOURCES // ReceiveFlags + 0 // ReceiveFlags ); - tapFreeReceiveNetBufferList(Adapter->MiniportAdapterHandle, netBufferList); - - return STATUS_SUCCESS; + return STATUS_PENDING; } // IRP_MJ_WRITE callback. @@ -683,7 +748,7 @@ TapDeviceWrite( packetLength); } - if((adapter->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) || + if((adapter->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) || (frameType & adapter->PacketFilter)) { // frame type bit is enabled in the packet filter. @@ -713,7 +778,7 @@ TapDeviceWrite( } else if (adapter->m_tun && ((irpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) { - // TUN mode - Prepend an ethernet header + // TUN mode - Prepend an ethernet header PETH_HEADER p_UserToTap = &adapter->m_UserToTap; // For IPv6, need to use Ethernet header with IPv6 proto @@ -782,8 +847,12 @@ TapDeviceWrite( ntStatus = STATUS_SUCCESS; } - Irp->IoStatus.Status = ntStatus; - IoCompleteRequest(Irp, IO_NO_INCREMENT); + if (ntStatus != STATUS_PENDING) + { + Irp->IoStatus.Status = ntStatus; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } return ntStatus; } + diff --git a/version.m4 b/version.m4 index 7991b4c..2d2259b 100644 --- a/version.m4 +++ b/version.m4 @@ -2,14 +2,14 @@ dnl define the TAP version define([PRODUCT_NAME], [TAP-Windows]) define([PRODUCT_PACKAGE_NAME], [tap-windows]) define([PRODUCT_PUBLISHER], [OpenVPN Technologies, Inc.]) -define([PRODUCT_VERSION], [9.24.8]) -define([PRODUCT_VERSION_RESOURCE], [9,24,8,601]) +define([PRODUCT_VERSION], [9.24.6]) +define([PRODUCT_VERSION_RESOURCE], [9,24,6,601]) define([PRODUCT_TAP_WIN_COMPONENT_ID], [tap0901]) define([PRODUCT_TAP_WIN_MAJOR], [9]) define([PRODUCT_TAP_WIN_MINOR], [24]) -define([PRODUCT_TAP_WIN_REVISION], [8]) +define([PRODUCT_TAP_WIN_REVISION], [6]) define([PRODUCT_TAP_WIN_BUILD], [601]) define([PRODUCT_TAP_WIN_PROVIDER], [TAP-Windows Provider V9]) define([PRODUCT_TAP_WIN_CHARACTERISTICS], [0x1]) define([PRODUCT_TAP_WIN_DEVICE_DESCRIPTION], [TAP-Windows Adapter V9]) -define([PRODUCT_TAP_WIN_RELDATE], [11/18/2022]) +define([PRODUCT_TAP_WIN_RELDATE], [10/08/2020])