/*++ Copyright(c) 1999-2000 Microsoft Corporation Module Name: brdgfwd.c Abstract: Ethernet MAC level bridge. Forwarding engine section Author: Mark Aiken (original bridge by Jameel Hyder) Environment: Kernel mode driver Revision History: Feb 2000 - Original version --*/ #define NDIS_MINIPORT_DRIVER #define NDIS50_MINIPORT 1 #define NDIS_WDM 1 #pragma warning( push, 3 ) #include #include #pragma warning( pop ) #include #include "bridge.h" #include "brdgprot.h" #include "brdgmini.h" #include "brdgtbl.h" #include "brdgfwd.h" #include "brdgbuf.h" #include "brdgctl.h" #include "brdgsta.h" #include "brdgcomp.h" // =========================================================================== // // CONSTANTS // // =========================================================================== // // Number of queued packets we will process back-to-back at DISPATCH level before // dropping back to PASSIVE to let the scheduler run // #define MAX_PACKETS_AT_DPC 10 // The STA multicast address UCHAR STA_MAC_ADDR[ETH_LENGTH_OF_ADDRESS] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }; // The flags we change on packet descriptor when sending them. For a fast-track // send, these flags should be put back the way they were before returning // the overlying protocol's packet descriptor. #define CHANGED_PACKET_FLAGS (NDIS_FLAGS_LOOPBACK_ONLY | NDIS_FLAGS_DONT_LOOPBACK) // =========================================================================== // // GLOBALS // // =========================================================================== // // Pointers to the KTHREAD structure for each active thread // PVOID gThreadPtrs[MAXIMUM_PROCESSORS]; // // The number of created threads // UINT gNumThreads = 0L; // Global kill signal for threads KEVENT gKillThreads; // // These auto-reset events signal the queue-draining threads to re-enumerate the // adapter list (strobed when the adapter list is changed) // KEVENT gThreadsCheckAdapters[MAXIMUM_PROCESSORS]; // // Whether or not we should hang on to NIC's packets when they are indicated on the // copy path. If FALSE, we always copy packets. // BOOLEAN gRetainNICPackets = FALSE; // // DEBUG-ONLY: Set this to a particular MAC address to break when receiving a packet // from that address. // #if DBG BOOLEAN gBreakOnMACAddress = FALSE; UCHAR gBreakMACAddress[ETH_LENGTH_OF_ADDRESS] = {0, 0, 0, 0, 0, 0}; BOOLEAN gBreakIfNullPPI = FALSE; #endif // // XPSP1: 565471 // We start out with this disabled. Once we know that it's allowed, we re-allow bridging to // take place. // BOOLEAN gBridging = FALSE; BOOLEAN gPrintPacketTypes = FALSE; extern BOOLEAN gHaveID; // =========================================================================== // // STATISTICS // // =========================================================================== LARGE_INTEGER gStatTransmittedFrames = { 0L, 0L }; // Local-source frames sent successfully to at least // one adapter LARGE_INTEGER gStatTransmittedErrorFrames = { 0L, 0L }; // Local-source frames not sent AT ALL due to errors LARGE_INTEGER gStatTransmittedBytes = { 0L, 0L }; // Local-source bytes sent successfully to at least // one adapter // Breakdown of transmitted frames LARGE_INTEGER gStatDirectedTransmittedFrames = { 0L, 0L }; LARGE_INTEGER gStatMulticastTransmittedFrames = { 0L, 0L }; LARGE_INTEGER gStatBroadcastTransmittedFrames = { 0L, 0L }; // Breakdown of transmitted bytes LARGE_INTEGER gStatDirectedTransmittedBytes = { 0L, 0L }; LARGE_INTEGER gStatMulticastTransmittedBytes = { 0L, 0L }; LARGE_INTEGER gStatBroadcastTransmittedBytes = { 0L, 0L }; LARGE_INTEGER gStatIndicatedFrames = { 0L, 0L }; // # of inbound frames indicated up LARGE_INTEGER gStatIndicatedDroppedFrames = { 0L, 0L }; // # of inbound frames we would have indicated but couldn't // because of resources / error LARGE_INTEGER gStatIndicatedBytes = { 0L, 0L }; // # of inbound bytes indicated up // Breakdown of indicated frames LARGE_INTEGER gStatDirectedIndicatedFrames = { 0L, 0L }; LARGE_INTEGER gStatMulticastIndicatedFrames = { 0L, 0L }; LARGE_INTEGER gStatBroadcastIndicatedFrames = { 0L, 0L }; // Breakdown of indicated bytes LARGE_INTEGER gStatDirectedIndicatedBytes = { 0L, 0L }; LARGE_INTEGER gStatMulticastIndicatedBytes = { 0L, 0L }; LARGE_INTEGER gStatBroadcastIndicatedBytes = { 0L, 0L }; // // The following stats are not reported to NDIS; they're here for our own amusement // LARGE_INTEGER gStatReceivedFrames = { 0L, 0L }; // Total # of processed inbound packets LARGE_INTEGER gStatReceivedBytes = { 0L, 0L }; // Total inbound processed bytes LARGE_INTEGER gStatReceivedCopyFrames = { 0L, 0L }; // Total # of processed inbound packets WITH COPY LARGE_INTEGER gStatReceivedCopyBytes = { 0L, 0L }; // Total inbound processed bytes WITH COPY LARGE_INTEGER gStatReceivedNoCopyFrames = { 0L, 0L }; // Total # of processed inbound packets WITHOUT COPY LARGE_INTEGER gStatReceivedNoCopyBytes = { 0L, 0L }; // Total inbound processed bytes WITHOUT COPY // =========================================================================== // // PRIVATE PROTOTYPES // // =========================================================================== // Undocumented kernel function extern KAFFINITY KeSetAffinityThread ( IN PKTHREAD Thread, IN KAFFINITY Affinity ); VOID BrdgFwdSendOnLink( IN PADAPT pAdapt, IN PNDIS_PACKET pPacket ); VOID BrdgFwdReleaseBasePacket( IN PNDIS_PACKET pPacket, PPACKET_INFO ppi, PACKET_OWNERSHIP Own, NDIS_STATUS Status ); // This is the type of function to be passed to BrdgFwdHandlePacket() typedef PNDIS_PACKET (*PPACKET_BUILD_FUNC)(PPACKET_INFO*, PADAPT, PVOID, PVOID, UINT, UINT); NDIS_STATUS BrdgFwdHandlePacket( IN PACKET_DIRECTION PacketDirection, IN PADAPT pTargetAdapt, IN PADAPT pOriginalAdapt, IN BOOLEAN bShouldIndicate, IN NDIS_HANDLE MiniportHandle, IN PNDIS_PACKET pBasePacket, IN PPACKET_INFO ppi, IN PPACKET_BUILD_FUNC pFunc, IN PVOID Param1, IN PVOID Param2, IN UINT Param3, IN UINT Param4 ); VOID BrdgFwdWrapPacketForReceive( IN PNDIS_PACKET pOriginalPacket, IN PNDIS_PACKET pNewPacket ); VOID BrdgFwdWrapPacketForSend( IN PNDIS_PACKET pOriginalPacket, IN PNDIS_PACKET pNewPacket ); // The type of function to pass to BrdgFwdCommonAllocAndWrapPacket typedef VOID (*PWRAPPER_FUNC)(PNDIS_PACKET, PNDIS_PACKET); PNDIS_PACKET BrdgFwdCommonAllocAndWrapPacket( IN PNDIS_PACKET pBasePacket, OUT PPACKET_INFO *pppi, IN PADAPT pTargetAdapt, IN PWRAPPER_FUNC pFunc ); VOID BrdgFwdTransferComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status, IN UINT BytesTransferred ); BOOLEAN BrdgFwdNoCopyFastTrackReceive( IN PNDIS_PACKET pPacket, IN PADAPT pAdapt, IN NDIS_HANDLE MiniportHandle, IN PUCHAR DstAddr, OUT BOOLEAN *bRetainPacket ); PNDIS_PACKET BrdgFwdMakeCopyBasePacket( OUT PPACKET_INFO *pppi, IN PVOID pHeader, IN PVOID pData, IN UINT HeaderSize, IN UINT DataSize, IN UINT SizeOfPacket, IN BOOLEAN bCountAsReceived, IN PADAPT pOwnerAdapt, PVOID *ppBuf ); PNDIS_PACKET BrdgFwdMakeNoCopyBasePacket( OUT PPACKET_INFO *pppi, IN PADAPT Target, IN PVOID Param1, IN PVOID Param2, IN UINT Param3, IN UINT Param4 ); PNDIS_PACKET BrdgFwdMakeSendBasePacket( OUT PPACKET_INFO *pppi, IN PADAPT Target, IN PVOID Param1, IN PVOID Param2, IN UINT Param3, IN UINT Param4 ); // This is the per-processor queue-draining function VOID BrdgFwdProcessQueuedPackets( IN PVOID Param1 ); // =========================================================================== // // INLINES / MACROS // // =========================================================================== // // Tells us if we're allowed to bridge, or if GPO's are currently disallowing // bridging // __forceinline BOOLEAN BrdgFwdBridgingNetworks() { return gBridging; } // // Frees a packet that was used to wrap a base packet // __forceinline VOID BrdgFwdFreeWrapperPacket( IN PNDIS_PACKET pPacket, IN PPACKET_INFO ppi, IN PADAPT pQuotaOwner ) { SAFEASSERT( BrdgBufIsWrapperPacket(pPacket) ); BrdgBufUnchainCopyBuffers( pPacket ); BrdgBufFreeWrapperPacket( pPacket, ppi, pQuotaOwner ); } // // Frees a base packet that wraps a packet descriptor from an overlying protocol // or underlying NIC that we were allowed to hang on to // __forceinline VOID BrdgFwdFreeBaseWrapperPacket( IN PNDIS_PACKET pPacket, IN PPACKET_INFO ppi ) { SAFEASSERT( BrdgBufIsWrapperPacket(pPacket) ); BrdgBufUnchainCopyBuffers( pPacket ); BrdgBufFreeBaseWrapperPacket( pPacket, ppi ); } // // Allocates a new wrapper packet, chains on buffer descriptors so that the new // packet points to the same data buffers as the old packet, and copies per-packet // information appropriate for using the new packet for indications // __forceinline PNDIS_PACKET BrdgFwdAllocAndWrapPacketForReceive( IN PNDIS_PACKET pPacket, OUT PPACKET_INFO *pppi, IN PADAPT pTargetAdapt ) { return BrdgFwdCommonAllocAndWrapPacket( pPacket, pppi, pTargetAdapt, BrdgFwdWrapPacketForReceive ); } // // Allocates a new wrapper packet, chains on buffer descriptors so that the new // packet points to the same data buffers as the old packet, and copies per-packet // information appropriate for using the new packet for transmits // __forceinline PNDIS_PACKET BrdgFwdAllocAndWrapPacketForSend( IN PNDIS_PACKET pPacket, OUT PPACKET_INFO *pppi, IN PADAPT pTargetAdapt ) { return BrdgFwdCommonAllocAndWrapPacket( pPacket, pppi, pTargetAdapt, BrdgFwdWrapPacketForSend ); } // // Checks if an address is one of the reserved group addresses for the Spanning Tree Algorithm. // __forceinline BOOLEAN BrdgFwdIsSTAGroupAddress( IN PUCHAR pAddr ) { return( (pAddr[0] == STA_MAC_ADDR[0]) && (pAddr[1] == STA_MAC_ADDR[1]) && (pAddr[2] == STA_MAC_ADDR[2]) && (pAddr[3] == STA_MAC_ADDR[4]) && (pAddr[4] == STA_MAC_ADDR[4]) ); } // // Checks that PacketDirection has been assigned // __forceinline VOID BrdgFwdValidatePacketDirection( IN PACKET_DIRECTION Direction ) { SAFEASSERT( (Direction == BrdgPacketInbound) || (Direction == BrdgPacketOutbound) || (Direction == BrdgPacketCreatedInBridge) ); } // // Queues a packet for deferred processing // _inline VOID BrdgFwdQueuePacket( IN PPACKET_Q_INFO ppqi, IN PADAPT pAdapt ) { BOOLEAN bSchedule = FALSE, bIncremented; // The queue lock protects the bServiceInProgress flag NdisAcquireSpinLock( &pAdapt->QueueLock ); // Add the packet to the queue BrdgInsertTailSingleList( &pAdapt->Queue, &ppqi->List ); bIncremented = BrdgIncrementWaitRef( &pAdapt->QueueRefcount ); SAFEASSERT( bIncremented ); SAFEASSERT( (ULONG)pAdapt->QueueRefcount.Refcount == pAdapt->Queue.Length ); // Check if anyone is already working on the queue if( ! pAdapt->bServiceInProgress ) { // Signal the queue event so someone will wake up pAdapt->bServiceInProgress = TRUE; bSchedule = TRUE; } NdisReleaseSpinLock( &pAdapt->QueueLock ); if( bSchedule ) { KeSetEvent( &pAdapt->QueueEvent, EVENT_INCREMENT, FALSE ); } } // // Decrements a base packet's refcount and calls BrdgFwdReleaseBasePacket if the refcount // reaches zero // _inline BOOLEAN BrdgFwdDerefBasePacket( IN PADAPT pQuotaOwner, // Can be NULL to not count quota IN PNDIS_PACKET pBasePacket, IN PPACKET_INFO ppi, IN NDIS_STATUS Status ) { BOOLEAN rc = FALSE; LONG RefCount; SAFEASSERT( pBasePacket != NULL ); SAFEASSERT( ppi != NULL ); RefCount = NdisInterlockedDecrement( &ppi->u.BasePacketInfo.RefCount ); SAFEASSERT( RefCount >= 0 ); if( RefCount == 0 ) { BrdgFwdReleaseBasePacket( pBasePacket, ppi, BrdgBufGetPacketOwnership( pBasePacket ), Status ); rc = TRUE; } // Do quota bookkeeping if necessary if( pQuotaOwner != NULL ) { BrdgBufReleaseBasePacketQuota( pBasePacket, pQuotaOwner ); } return rc; } // // Updates statistics to reflect a transmitted packet // _inline VOID BrdgFwdCountTransmittedPacket( IN PADAPT pAdapt, IN PUCHAR DstAddr, IN ULONG PacketSize ) { SAFEASSERT( DstAddr != NULL ); ExInterlockedAddLargeStatistic( &gStatTransmittedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatTransmittedBytes, PacketSize ); ExInterlockedAddLargeStatistic( &pAdapt->SentFrames, 1L ); ExInterlockedAddLargeStatistic( &pAdapt->SentBytes, PacketSize ); ExInterlockedAddLargeStatistic( &pAdapt->SentLocalFrames, 1L ); ExInterlockedAddLargeStatistic( &pAdapt->SentLocalBytes, PacketSize ); if( ETH_IS_MULTICAST(DstAddr) ) { ExInterlockedAddLargeStatistic( &gStatMulticastTransmittedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatMulticastTransmittedBytes, PacketSize ); if( ETH_IS_BROADCAST(DstAddr) ) { ExInterlockedAddLargeStatistic( &gStatBroadcastTransmittedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatBroadcastTransmittedBytes, PacketSize ); } } else { ExInterlockedAddLargeStatistic( &gStatDirectedTransmittedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatDirectedTransmittedBytes, PacketSize ); } } // // Updates statistics to reflect an indicated packet // _inline VOID BrdgFwdCountIndicatedPacket( IN PUCHAR DstAddr, IN ULONG PacketSize ) { ExInterlockedAddLargeStatistic( &gStatIndicatedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatIndicatedBytes, PacketSize ); if( ETH_IS_MULTICAST(DstAddr) ) { ExInterlockedAddLargeStatistic( &gStatMulticastIndicatedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatMulticastIndicatedBytes, PacketSize ); if( ETH_IS_BROADCAST(DstAddr) ) { ExInterlockedAddLargeStatistic( &gStatBroadcastIndicatedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatBroadcastIndicatedBytes, PacketSize ); } } else { ExInterlockedAddLargeStatistic( &gStatDirectedIndicatedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatDirectedIndicatedBytes, PacketSize ); } } // // Indicates a packet, counting it as such. // _inline VOID BrdgFwdIndicatePacket( IN PNDIS_PACKET pPacket, IN NDIS_HANDLE MiniportHandle ) { PVOID pHeader = BrdgBufGetPacketHeader(pPacket); SAFEASSERT( MiniportHandle != NULL ); if( pHeader != NULL ) { BrdgFwdCountIndicatedPacket( pHeader, BrdgBufTotalPacketSize(pPacket) ); } // pHeader can only == NULL under heavy system stress NdisMIndicateReceivePacket( MiniportHandle, &pPacket, 1 ); } // =========================================================================== // // PUBLIC FUNCTIONS // // =========================================================================== NTSTATUS BrdgFwdDriverInit() /*++ Routine Description: Initialization code. A return status other than STATUS_SUCCESS causes the driver load to abort. Any event causing an error return code must be logged. Must be called at PASSIVE_LEVEL Arguments: None Return Value: None --*/ { INT i; HANDLE ThreadHandle; NTSTATUS Status; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); // Initialize our thread synchronization primitives KeInitializeEvent( &gKillThreads, NotificationEvent, FALSE ); for(i = 0; i < KeNumberProcessors; i++) { KeInitializeEvent( &gThreadsCheckAdapters[i], SynchronizationEvent, FALSE ); // Spin up a thread for this processor Status = PsCreateSystemThread( &ThreadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, BrdgFwdProcessQueuedPackets, (PVOID)(INT_PTR)i ); if(! NT_SUCCESS(Status) ) { // Abort startup NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_THREAD_CREATION_FAILED, 0L, 0L, NULL, sizeof(NTSTATUS), &Status ); DBGPRINT(FWD, ("Failed to create a system thread: %08x\n", Status)); BrdgFwdCleanup(); return Status; } // Retrieve a pointer to the thread object and reference it so we can wait for // its termination safely. Status = ObReferenceObjectByHandle( ThreadHandle, STANDARD_RIGHTS_ALL, NULL, KernelMode, &gThreadPtrs[i], NULL ); if(! NT_SUCCESS(Status) ) { // Abort startup NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_THREAD_REF_FAILED, 0L, 0L, NULL, sizeof(NTSTATUS), &Status ); DBGPRINT(FWD, ("Couldn't retrieve a thread pointer: %08x\n", Status)); BrdgFwdCleanup(); return Status; } gNumThreads++; } return STATUS_SUCCESS; } VOID BrdgFwdCleanup() /*++ Routine Description: Unload-time orderly cleanup This function is guaranteed to be called exactly once Must be called at < DISPATCH_LEVEL since we wait on an event Arguments: None Return Value: None --*/ { KWAIT_BLOCK WaitBlocks[MAXIMUM_WAIT_OBJECTS]; NTSTATUS Status; UINT i; SAFEASSERT(CURRENT_IRQL < DISPATCH_LEVEL); // Signal the threads to exit KeSetEvent( &gKillThreads, EVENT_INCREMENT, FALSE ); // Block waiting for all threads to exit Status = KeWaitForMultipleObjects( gNumThreads, gThreadPtrs, WaitAll, Executive, KernelMode, FALSE, NULL, WaitBlocks ); if( ! NT_SUCCESS(Status) ) { // This really shouldn't happen DBGPRINT(FWD, ("KeWaitForMultipleObjects failed in BrdgFwdCleanup! %08x\n", Status)); SAFEASSERT(FALSE); } // Dereference all thread objects to allow them to be destroyed for( i = 0; i < gNumThreads; i++ ) { ObDereferenceObject( gThreadPtrs[i] ); } } PNDIS_PACKET BrdgFwdMakeCompatCopyPacket( IN PNDIS_PACKET pBasePacket, OUT PUCHAR *pPacketData, OUT PUINT packetDataSize, BOOLEAN bCountAsLocalSend ) /*++ Routine Description: Allocates a copy packet and fills it with a copy of the data from the given base packet. Used by the compatibility-mode code to make a copy packet that it can edit easily Arguments: pBasePacket Packet to copy from pPacketData Receives a pointer to the flat data buffer of the new packet packetDataSize Receives the size of the copied data bBasePacketIsInbound TRUE if the packet being copied is outbound from higher-level protocols; the packet will be counted as a miniport transmission if / when it is send out an adapter. FALSE causes the packet to not be counted as a local transmission. Return Value: The new packet --*/ { PNDIS_PACKET pCopyPacket; PPACKET_INFO ppi; UINT copiedBytes; // Find out how much data is in the base packet NdisQueryPacket( pBasePacket, NULL, NULL, NULL, packetDataSize ); // Make a base copy packet with no data in it pCopyPacket = BrdgFwdMakeCopyBasePacket( &ppi, NULL, NULL, 0, 0, *packetDataSize, FALSE, NULL, pPacketData ); if( pCopyPacket == NULL ) { return NULL; } SAFEASSERT( ppi != NULL ); SAFEASSERT( *pPacketData != NULL ); // Set the original direction flags if( bCountAsLocalSend ) { ppi->Flags.OriginalDirection = BrdgPacketOutbound; } else { ppi->Flags.OriginalDirection = BrdgPacketCreatedInBridge; } // Copy the data from the base packet to the copy packet NdisCopyFromPacketToPacket( pCopyPacket, 0, *packetDataSize, pBasePacket, 0, &copiedBytes ); if( copiedBytes != *packetDataSize ) { // We couldn't copy all the data. Bail out. THROTTLED_DBGPRINT(FWD, ("Failed to copy into a copy packet for compatibility processing\n")); BrdgFwdReleaseBasePacket(pCopyPacket, ppi, BrdgBufGetPacketOwnership(pCopyPacket), NDIS_STATUS_RESOURCES); return NULL; } // Put a pointer to the ppi where we expect to find it on completion *((PPACKET_INFO*)pCopyPacket->ProtocolReserved) = ppi; *((PPACKET_INFO*)pCopyPacket->MiniportReserved) = ppi; // Do fixups usually performed by BrdgFwdHandlePacket() ppi->u.BasePacketInfo.RefCount = 1L; ppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_FAILURE; // The packet is now ready to be sent. We expect the compatibility code to do its work // and call BrdgFwdSendPacketForComp() to transmit the packet. return pCopyPacket; } VOID BrdgFwdSendPacketForCompat( IN PNDIS_PACKET pPacket, IN PADAPT pAdapt ) /*++ Routine Description: Transmits a packet on behalf of the compatibility-mode module. The packet must have been previously allocated with BrdgFwdMakeCompatCopyPacket. Arguments: pPacket The packet to transmit pAdapt The adapter to transmit on Return Value: None --*/ { PPACKET_INFO ppi; NDIS_STATUS status; // Make sure the packet hasn't been monkeyed with inappropriately ppi = *((PPACKET_INFO*)pPacket->ProtocolReserved); SAFEASSERT( ppi->pOwnerPacket == pPacket ); SAFEASSERT( ppi->Flags.bIsBasePacket ); // Make sure this is a one-shot packet ppi->u.BasePacketInfo.RefCount = 1L; // We must do a quota check before sending the packet, as the packet completion // logic assumes all sent packets have been assigned to their outbound adapters if( BrdgBufAssignBasePacketQuota(pPacket, pAdapt) ) { // We passed quota. Transmit the packet. BrdgFwdSendOnLink( pAdapt, pPacket ); } else { // We didn't pass quota. Fail the transmission. DBGPRINT(FWD, ("Failed to send a compatibility packet because of quota failure\n")); status = NDIS_STATUS_RESOURCES; BrdgFwdReleaseBasePacket(pPacket, ppi, BrdgBufGetPacketOwnership(pPacket), NDIS_STATUS_RESOURCES); } } VOID BrdgFwdIndicatePacketForCompat( IN PNDIS_PACKET pPacket ) /*++ Routine Description: Indicates a packet on behalf of the compatibility-mode module. The packet must be a base copy packet that we own. Arguments: pPacket The packet to indicate Return Value: None --*/ { PPACKET_INFO ppi; NDIS_STATUS status; NDIS_HANDLE MiniportHandle; // Make sure the packet is a base packet and isn't out of // whack ppi = *((PPACKET_INFO*)pPacket->MiniportReserved); SAFEASSERT( ppi->pOwnerPacket == pPacket ); SAFEASSERT( ppi->Flags.bIsBasePacket ); // Packets that come to us for indication from the compatibility // module are our own base packets that haven't had their refcount // set yet. Set the packet's refcount to 1, since its buffers // should never be shared. ppi->u.BasePacketInfo.RefCount = 1L; MiniportHandle = BrdgMiniAcquireMiniportForIndicate(); if( MiniportHandle != NULL ) { // Check the quota for the local miniport if( BrdgBufAssignBasePacketQuota(pPacket, LOCAL_MINIPORT) ) { // We passed quota. BrdgFwdIndicatePacket( pPacket, MiniportHandle ); } else { // We didn't pass quota. Fail the transmission. DBGPRINT(FWD, ("Failed to indicate a compatibility packet because of quota failure\n")); status = NDIS_STATUS_RESOURCES; BrdgFwdReleaseBasePacket(pPacket, ppi, BrdgBufGetPacketOwnership(pPacket), NDIS_STATUS_RESOURCES); } BrdgMiniReleaseMiniportForIndicate(); } else { // No miniport. Ditch the packet. BrdgFwdReleaseBasePacket(pPacket, ppi, BrdgBufGetPacketOwnership(pPacket), NDIS_STATUS_SUCCESS); } } VOID BrdgFwdReleaseCompatPacket( IN PNDIS_PACKET pPacket ) /*++ Routine Description: Releases a packet previously allocated with BrdgFwdMakeCompatCopyPacket. Arguments: pPacket The packet to release Return Value: None --*/ { PPACKET_INFO ppi; // Retrieve the PACKET_INFO pointer ppi = *((PPACKET_INFO*)pPacket->ProtocolReserved); SAFEASSERT( ppi->pOwnerPacket == pPacket ); SAFEASSERT( ppi->Flags.bIsBasePacket ); BrdgFwdReleaseBasePacket(pPacket, ppi, BrdgBufGetPacketOwnership(pPacket), NDIS_STATUS_SUCCESS); } NDIS_STATUS BrdgFwdSendBuffer( IN PADAPT pAdapt, IN PUCHAR pPacketData, IN UINT DataSize ) /*++ Routine Description: Sends a raw buffer on a particular adapter. Used to send frames in response to user-mode requests. Arguments: pAdapt The adapter to send on pPacketData The frame DataSize The size of the supplied frame Return Value: Status of the packet transmission --*/ { PNDIS_PACKET pPacket; PPACKET_INFO ppi; // Build a packet around this buffer pPacket = BrdgFwdMakeCopyBasePacket( &ppi, pPacketData, NULL, DataSize, 0, DataSize, FALSE, NULL, NULL ); if( pPacket == NULL ) { return NDIS_STATUS_RESOURCES; } SAFEASSERT( ppi != NULL ); // We must do a quota check before sending the packet, as the packet completion // logic assumes all sent packets have been assigned to their outbound adapters if( ! BrdgBufAssignBasePacketQuota(pPacket, pAdapt) ) { // We didn't pass quota. Fail the transmission. DBGPRINT(FWD, ("Failed to send a raw buffer because of quota failure\n")); BrdgFwdReleaseBasePacket(pPacket, ppi, BrdgBufGetPacketOwnership(pPacket), NDIS_STATUS_RESOURCES); return NDIS_STATUS_RESOURCES; } // Put a pointer to the ppi where we expect to find it on completion *((PPACKET_INFO*)pPacket->ProtocolReserved) = ppi; // Do fixups usually performed by BrdgFwdHandlePacket() ppi->u.BasePacketInfo.RefCount = 1L; ppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_FAILURE; // Send the packet BrdgFwdSendOnLink( pAdapt, pPacket ); return NDIS_STATUS_SUCCESS; } NDIS_STATUS BrdgFwdReceive( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE MacReceiveContext, IN PVOID pHeader, IN UINT HeaderSize, IN PVOID pLookAheadBuffer, IN UINT LookAheadSize, IN UINT PacketSize ) /*++ Routine Description: NDIS copy-path entry point. Receives an inbound packet on the copy path. Because the indicated data buffers are valid only for the duration of this function, we must copy the indicated data to our own packet descriptor before proceeding. Arguments: ProtocolBindingContext The receiving adapter MacReceiveContext Must be passed as a param to certain Ndis APIs pHeader Packet header buffer HeaderSize Size of pHeader pLookAheadBuffer Buffer with packet data LookAheadSize Size of pLookAheadBuffer PacketSize Total packet size Return Value: Status of the receive (cannot be NDIS_STATUS_PENDING) --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext, TargetAdapt = NULL; PUCHAR SrcAddr = ((PUCHAR)pHeader) + ETH_LENGTH_OF_ADDRESS, DstAddr = pHeader; PNDIS_PACKET pNewPacket; PPACKET_INFO ppi; PPACKET_Q_INFO ppqi; UINT SizeOfPacket = HeaderSize + PacketSize; BOOLEAN bIsSTAPacket = FALSE, bIsUnicastToBridge = FALSE, bRequiresCompatWork = FALSE; #if DBG // Paranoia check for incorrectly looped-back packets { PNDIS_PACKET pPacket = NdisGetReceivedPacket(pAdapt->BindingHandle, MacReceiveContext); if( pPacket != NULL ) { SAFEASSERT( BrdgBufGetPacketOwnership(pPacket) == BrdgNotOwned ); } } // Break on packets from gBreakMACAddress if( gBreakOnMACAddress ) { UINT result; ETH_COMPARE_NETWORK_ADDRESSES_EQ( SrcAddr, gBreakMACAddress, &result ); if( result == 0 ) { KdBreakPoint(); } } #endif // Don't accept packets if we are shutting down or this adapter is being torn down or reset if( (gShuttingDown) || (pAdapt->bResetting) || (! BrdgAcquireAdapter(pAdapt)) ) { return NDIS_STATUS_NOT_ACCEPTED; } // Must have at least a complete Ethernet header! if( HeaderSize < ETHERNET_HEADER_SIZE ) { THROTTLED_DBGPRINT(FWD, ("Too-small header seen in BrdgFwdReceive!\n")); BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } // Packet can't be larger than the maximum size we can handle if( SizeOfPacket > MAX_PACKET_SIZE ) { THROTTLED_DBGPRINT(FWD, ("Too-large packet seen in BrdgFwdReceive!\n")); BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } // // If this is an STA packet, we go through all the receive motions regardless of // our state // if( BrdgFwdIsSTAGroupAddress(DstAddr) ) { if( DstAddr[5] == STA_MAC_ADDR[5] ) { bIsSTAPacket = TRUE; TargetAdapt = NULL; } else { // Packet was sent to a reserved multicast address that we don't use. // We mustn't forward the frame. BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } } if( ! bIsSTAPacket ) { // Note the MAC address of the frame if this adapter is learning if( (pAdapt->State == Learning) || (pAdapt->State == Forwarding) ) { BrdgTblNoteAddress(SrcAddr, pAdapt); } // // Check if we are accepting packets or not // if( pAdapt->State != Forwarding ) { BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } // // Look up the target in our table. // ** TargetAdapt comes back with its refcount incremented! // TargetAdapt = BrdgTblFindTargetAdapter( DstAddr ); // If the target host is known to be on the same segment as the received // packet, there is no need to forward the packet. // // Also bail out here if the target adapter is resetting if( (TargetAdapt == pAdapt) || ((TargetAdapt != NULL) && (TargetAdapt->bResetting)) ) { BrdgReleaseAdapter( TargetAdapt ); BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } // Learn if this packet requires compatibility-mode processing later // (this forces us to copy the packet to our own buffers and queue it) bRequiresCompatWork = BrdgCompRequiresCompatWork( pAdapt, pHeader, HeaderSize ); // If the packet came in on a compatibility adapter, or is going to // a compatibility-mode adapter, but the compatibility code is not // interested in it, there is nothing further to be done with the packet. if( (pAdapt->bCompatibilityMode || ((TargetAdapt != NULL) && (TargetAdapt->bCompatibilityMode))) && (! bRequiresCompatWork) ) { if( TargetAdapt != NULL ) { BrdgReleaseAdapter( TargetAdapt ); } BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } bIsUnicastToBridge = BrdgMiniIsUnicastToBridge(DstAddr); // Sanity if( bIsUnicastToBridge && (TargetAdapt != NULL) ) { // // This indicates that someone else on the network is using our MAC address, // or that there is an undetected loop such that we are seeing our own traffic! // Either way, this is very bad. // THROTTLED_DBGPRINT(FWD, ("*** Have a table entry for our own MAC address! PROBABLE NET LOOP!\n")); // Ditch the target adapter since we won't be using it BrdgReleaseAdapter( TargetAdapt ); TargetAdapt = NULL; } } // else was STA packet; continue processing below // // There is no fast-track on the copy-receive path. Copy the packet data into our // own descriptor and queue the packet for processing later. // if (LookAheadSize == PacketSize) { // A normal, non-fragmented indicate. Copy the data to a new packet. pNewPacket = BrdgFwdMakeCopyBasePacket( &ppi, pHeader, pLookAheadBuffer, HeaderSize, LookAheadSize, SizeOfPacket, TRUE, pAdapt, NULL ); if( pNewPacket == NULL ) { // We failed to get a copy packet to wrap this data goto failure; } SAFEASSERT( ppi != NULL ); // Queue the new packet for processing ppqi = (PPACKET_Q_INFO)&pNewPacket->ProtocolReserved; ppqi->u.pTargetAdapt = TargetAdapt; ppqi->pInfo = ppi; ppqi->Flags.bIsSTAPacket = bIsSTAPacket; ppqi->Flags.bFastTrackReceive = FALSE; ppqi->Flags.bRequiresCompatWork = bRequiresCompatWork; if( bIsUnicastToBridge ) { SAFEASSERT( TargetAdapt == NULL ); ppqi->Flags.bIsUnicastToBridge = TRUE; ppqi->Flags.bShouldIndicate = TRUE; } else { ppqi->Flags.bIsUnicastToBridge = FALSE; ppqi->Flags.bShouldIndicate = BrdgMiniShouldIndicatePacket(DstAddr); } BrdgFwdQueuePacket( ppqi, pAdapt ); } else { NDIS_STATUS Status; UINT transferred; PNDIS_BUFFER pBufDesc; PUCHAR pBuf; // // This is an unusual code path in this day and age; the underlying driver // is an old NDIS driver that still does fragmented receives. // SAFEASSERT( LookAheadSize < PacketSize ); // Get copy packet and copy in the header data (but NOT the lookahead) pNewPacket = BrdgFwdMakeCopyBasePacket( &ppi, pHeader, NULL, HeaderSize, 0, SizeOfPacket, TRUE, pAdapt, &pBuf ); if( pNewPacket == NULL ) { // We failed to get a copy packet goto failure; } SAFEASSERT( ppi != NULL ); SAFEASSERT( pBuf != NULL ); // // NdisTransferData is kind of a crummy API; it won't copy the entire packet // (i.e., you have to copy the header separately), and it won't let you specify // an offset in the receiving packet to copy to. The NIC wants to copy into the // beginning of the first buffer chained to the packet. // // Because of this silliness, we copy the header into the beginning of our copy // packet's data buffer (done in the call to BrdgFwdMakeCopyBasePacket above). // // Then we grab a NEW buffer descriptor, point it to the area of the data buffer // *after* the header, and chain it to the front of the packet. Then we request // that all data (other than the header) be copied. // // In BrdgFwdTransferComplete, we rip off the leading buffer descriptor and // dispose of it, leaving a single buffer descriptor that correctly describes // the (single) data buffer, now containing all data. // pBufDesc = BrdgBufAllocateBuffer( pBuf + HeaderSize, PacketSize ); if( pBufDesc == NULL ) { BrdgFwdReleaseBasePacket( pNewPacket, ppi, BrdgBufGetPacketOwnership(pNewPacket), NDIS_STATUS_FAILURE ); goto failure; } // Chain this to the front of the packet where it will be used during the copy NdisChainBufferAtFront( pNewPacket, pBufDesc ); // Set up the queuing structure in the packet's ProtocolReserved area ppqi = (PPACKET_Q_INFO)&pNewPacket->ProtocolReserved; ppqi->u.pTargetAdapt = TargetAdapt; ppqi->pInfo = ppi; ppqi->Flags.bIsSTAPacket = bIsSTAPacket; ppqi->Flags.bFastTrackReceive = FALSE; ppqi->Flags.bRequiresCompatWork = bRequiresCompatWork; if( bIsUnicastToBridge ) { SAFEASSERT( TargetAdapt == NULL ); ppqi->Flags.bIsUnicastToBridge = TRUE; ppqi->Flags.bShouldIndicate = TRUE; } else { ppqi->Flags.bIsUnicastToBridge = FALSE; ppqi->Flags.bShouldIndicate = BrdgMiniShouldIndicatePacket(DstAddr); } // Ask the NIC to copy the packet's data into the new packet NdisTransferData( &Status, pAdapt->BindingHandle, MacReceiveContext, 0, PacketSize, pNewPacket, &transferred ); if( Status == NDIS_STATUS_SUCCESS ) { // Call BrdgFwdTransferComplete by hand to postprocess the packet. BrdgFwdTransferComplete( (NDIS_HANDLE)pAdapt, pNewPacket, Status, transferred ); } else if( Status != NDIS_STATUS_PENDING ) { // The transfer failed for some reason. NdisUnchainBufferAtFront( pNewPacket, &pBufDesc ); if( pBufDesc != NULL ) { NdisFreeBuffer( pBufDesc ); } else { SAFEASSERT( FALSE ); } BrdgFwdReleaseBasePacket( pNewPacket, ppi, BrdgBufGetPacketOwnership(pNewPacket), NDIS_STATUS_FAILURE ); goto failure; } // else BrdgFwdTransferComplete will be called to postprocess the packet. } BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_SUCCESS; failure: if( TargetAdapt != NULL ) { BrdgReleaseAdapter( TargetAdapt ); } if( BrdgMiniShouldIndicatePacket(DstAddr) ) { ExInterlockedAddLargeStatistic( &gStatIndicatedDroppedFrames, 1L ); } BrdgReleaseAdapter( pAdapt ); return NDIS_STATUS_NOT_ACCEPTED; } VOID BrdgFwdTransferComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status, IN UINT BytesTransferred ) /*++ Routine Description: NDIS entry point, registered in BrdgProtRegisterProtocol. Called when a call to NdisTransferData() that returned NDIS_STATUS_PENDING completes (we also call this by hand to postprocess a call that completes immediately). If the data copy from the underlying NIC was successful, the packet is queued for processing on the owner adapter's queue. Otherwise the packet is released. Arguments: ProtocolBindingContext The receiving adapter pPacket The base packet into which data was being copied Status Status of the copy BytesTransferred Number of transferred bytes (unused) Return Value: None --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; PPACKET_Q_INFO ppqi = (PPACKET_Q_INFO)&pPacket->ProtocolReserved; PNDIS_BUFFER pBuf; SAFEASSERT( pAdapt != NULL ); SAFEASSERT( pPacket != NULL ); SAFEASSERT( ppqi->pInfo != NULL ); SAFEASSERT( ppqi->pInfo->pOwnerPacket == pPacket ); SAFEASSERT( ppqi->Flags.bFastTrackReceive == FALSE ); // Remove the extra buffer descriptor on the front of the packet and dispose of it // (see comments in BrdgFwdReceive() for details) NdisUnchainBufferAtFront( pPacket, &pBuf ); if( pBuf != NULL ) { NdisFreeBuffer( pBuf ); } else { // Should never happen SAFEASSERT( FALSE ); } // We should still have the original buffer descriptor describing the entire data buffer // chained to the packet SAFEASSERT( BrdgBufPacketHeadBuffer(pPacket) != NULL ); if( Status != NDIS_STATUS_SUCCESS ) { // The copy failed. Undo everything. if( ppqi->u.pTargetAdapt != NULL ) { BrdgReleaseAdapter( ppqi->u.pTargetAdapt ); } if( ppqi->Flags.bShouldIndicate ) { ExInterlockedAddLargeStatistic( &gStatIndicatedDroppedFrames, 1L ); } BrdgFwdReleaseBasePacket( pPacket, ppqi->pInfo, BrdgBufGetPacketOwnership(pPacket), Status ); } else { // Success! Queue the packet for processing BrdgFwdQueuePacket( ppqi, pAdapt ); } } INT BrdgFwdReceivePacket( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket ) /*++ Routine Description: NDIS no-copy entry point Receives a packet on the no-copy path Arguments: ProtocolBindingContext The adapter on which the packet is received pPacket The received packet Return Value: The number of times we will call NdisReturnPackets() to free this packet. We return 0 to complete immediately or 1 to pend. --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext, TargetAdapt; UINT PacketSize; PNDIS_BUFFER Buffer; PUCHAR DstAddr, SrcAddr; UINT Size; PPACKET_Q_INFO ppqi; INT rc; BOOLEAN bForceCopy = FALSE, bFastTrack = FALSE, bIsUnicastToBridge = FALSE, bRequiresCompatWork = FALSE; // Paranoia check for incorrectly looped-back packets SAFEASSERT( BrdgBufGetPacketOwnership(pPacket) == BrdgNotOwned ); // Don't receive packets if we are shutting down or this adapter is being torn down or reset if ( gShuttingDown || (pAdapt->bResetting) || (! BrdgAcquireAdapter(pAdapt)) ) { return 0; } NdisQueryPacket(pPacket, NULL, NULL, &Buffer, &PacketSize); NdisQueryBufferSafe(Buffer, &DstAddr, &Size, NormalPagePriority); if( DstAddr == NULL ) { BrdgReleaseAdapter( pAdapt ); return 0; } // Must have at least a complete Ethernet header! if( Size < ETHERNET_HEADER_SIZE ) { THROTTLED_DBGPRINT(FWD, ("Packet smaller than Ethernet header seen!\n")); BrdgReleaseAdapter( pAdapt ); return 0; } // Packet can't be larger than the maximum we can handle if( Size > MAX_PACKET_SIZE ) { THROTTLED_DBGPRINT(FWD, ("Over-large packet seen!\n")); BrdgReleaseAdapter( pAdapt ); return 0; } SrcAddr = DstAddr + ETH_LENGTH_OF_ADDRESS; #if DBG // Break on packets from gBreakMACAddress if( gBreakOnMACAddress ) { UINT result; ETH_COMPARE_NETWORK_ADDRESSES_EQ( SrcAddr, gBreakMACAddress, &result ); if( result == 0 ) { KdBreakPoint(); } } #endif // // If this is an STA packet, don't process it, but hand it off // if( BrdgFwdIsSTAGroupAddress(DstAddr) ) { if( (! gDisableSTA) && (DstAddr[5] == STA_MAC_ADDR[5])) { if (BrdgFwdBridgingNetworks()) { // Hand off this packet for processing BrdgSTAReceivePacket( pAdapt, pPacket ); } } BrdgReleaseAdapter( pAdapt ); return 0; } // Note the MAC address of the frame if this adapter is learning if( (pAdapt->State == Learning) || (pAdapt->State == Forwarding) ) { BrdgTblNoteAddress(SrcAddr, pAdapt); } // // Check if we are accepting packets or not // if( pAdapt->State != Forwarding ) { BrdgReleaseAdapter( pAdapt ); return 0; } // Look up the target in our table TargetAdapt = BrdgTblFindTargetAdapter( DstAddr ); // If the target host is known to be on the same segment as the received // packet, there is no need to forward the packet. // // Also bail out if the target adapter is resetting if( (TargetAdapt == pAdapt) || ((TargetAdapt != NULL) && (TargetAdapt->bResetting)) ) { BrdgReleaseAdapter( TargetAdapt ); BrdgReleaseAdapter( pAdapt ); return 0; } // Check if the packet will require compatibility-mode processing bRequiresCompatWork = BrdgCompRequiresCompatWork( pAdapt, DstAddr, Size ); // If a packet requires compatibility work, we MUST copy it so the // compatibility code has a flat, editable buffer to work with. if( bRequiresCompatWork ) { bForceCopy = TRUE; } // If the packet came in on a compatibility adapter, or is going to // a compatibility-mode adapter, but the compatibility code is not // interested in it, there is nothing further to be done with the packet. if( (pAdapt->bCompatibilityMode || ((TargetAdapt != NULL) && (TargetAdapt->bCompatibilityMode))) && (! bRequiresCompatWork) ) { if( TargetAdapt != NULL ) { BrdgReleaseAdapter( TargetAdapt ); } BrdgReleaseAdapter( pAdapt ); return 0; } // // If this packet is a unicast packet ONLY for the local machine, // we can fast-track it by just passing through the indication to // upper-layer protocols. // // We can't pull this stunt if the packet requires compatibility-mode // processing. // bIsUnicastToBridge = BrdgMiniIsUnicastToBridge(DstAddr); if( bIsUnicastToBridge && (!bRequiresCompatWork) ) { NDIS_HANDLE MiniportHandle; BOOLEAN bRemaining, bRetain; if( TargetAdapt != NULL ) { // // This indicates that someone else on the network is using our MAC address, // or that there is an undetected loop such that we are seeing our own traffic! // Either way, this is very bad. // THROTTLED_DBGPRINT(FWD, ("** Have a table entry for our own MAC address! PROBABLE NET LOOP!\n")); // We won't be needing the target adapter BrdgReleaseAdapter( TargetAdapt ); TargetAdapt = NULL; } MiniportHandle = BrdgMiniAcquireMiniportForIndicate(); if( MiniportHandle == NULL ) { // Nothing to do with this packet since we don't have a miniport to // indicate it with! BrdgReleaseAdapter( pAdapt ); return 0; } // // Figure out if it's possible to fast-track this packet // NdisIMGetCurrentPacketStack(pPacket, &bRemaining); if( bRemaining ) { // // We can fast-track right away if the packet queue for this adapter // is empty. Otherwise, we would be cutting ahead of other packets from this // adapter. // if( ! pAdapt->bServiceInProgress ) { // We can fast-track this packet right now. if( BrdgFwdNoCopyFastTrackReceive(pPacket, pAdapt, MiniportHandle, DstAddr, &bRetain) ) { // bRetain tells us whether to retain ownership of this packet or not BrdgReleaseAdapter( pAdapt ); BrdgMiniReleaseMiniportForIndicate(); return bRetain ? 1 : 0; } else { // This should never happen since we checked to see if there was stack room SAFEASSERT( FALSE ); bForceCopy = TRUE; bFastTrack = FALSE; } } else { // We want to fast-track this packet but the processing queue is not // empty. Flag it for fast-tracking in the queue draining thread. bFastTrack = TRUE; bForceCopy = FALSE; } } else { // Force this packet to be copied into a base packet since // we know it can't be fast-tracked. bForceCopy = TRUE; bFastTrack = FALSE; } // Allow the miniport to shut down BrdgMiniReleaseMiniportForIndicate(); } // // We couldn't fast-track the packet. We will have to queue it for processing. // if( bForceCopy || ((!bFastTrack) && (!gRetainNICPackets)) || (NDIS_GET_PACKET_STATUS(pPacket) == NDIS_STATUS_RESOURCES) ) { // We must copy this packet's data. PNDIS_PACKET pNewPacket; PPACKET_INFO ppi; UINT copied; // Get a new copy packet with nothing copied in yet. pNewPacket = BrdgFwdMakeCopyBasePacket( &ppi, NULL, NULL, 0, 0, PacketSize, TRUE, pAdapt, NULL ); if( pNewPacket == NULL ) { // Failed to get a copy packet to hold the data. goto failure; } SAFEASSERT( ppi != NULL ); // Copy data out of the old packet into the new one NdisCopyFromPacketToPacket( pNewPacket, 0, PacketSize, pPacket, 0, &copied ); if( copied != PacketSize ) { BrdgFwdReleaseBasePacket( pNewPacket, ppi, BrdgBufGetPacketOwnership(pNewPacket), NDIS_STATUS_FAILURE ); goto failure; } // Queue the new base packet for processing ppqi = (PPACKET_Q_INFO)&pNewPacket->ProtocolReserved; ppqi->pInfo = ppi; ppqi->u.pTargetAdapt = TargetAdapt; ppqi->Flags.bIsSTAPacket = FALSE; ppqi->Flags.bFastTrackReceive = FALSE; ppqi->Flags.bRequiresCompatWork = bRequiresCompatWork; if( bIsUnicastToBridge ) { SAFEASSERT( TargetAdapt == NULL ); ppqi->Flags.bIsUnicastToBridge = TRUE; ppqi->Flags.bShouldIndicate = TRUE; } else { ppqi->Flags.bIsUnicastToBridge = FALSE; ppqi->Flags.bShouldIndicate = BrdgMiniShouldIndicatePacket(DstAddr); } // The NIC gets its packet back immediately since we copied its data rc = 0; } else { // Queue the original packet for processing ppqi = (PPACKET_Q_INFO)&pPacket->ProtocolReserved; ppqi->pInfo = NULL; ppqi->Flags.bIsSTAPacket = FALSE; ppqi->Flags.bIsUnicastToBridge = bIsUnicastToBridge; ppqi->Flags.bRequiresCompatWork = bRequiresCompatWork; if( bFastTrack ) { SAFEASSERT( bIsUnicastToBridge ); SAFEASSERT( TargetAdapt == NULL ); ppqi->Flags.bFastTrackReceive = TRUE; ppqi->Flags.bShouldIndicate = TRUE; ppqi->u.pOriginalAdapt = pAdapt; } else { ppqi->Flags.bFastTrackReceive = FALSE; ppqi->u.pTargetAdapt = TargetAdapt; if( bIsUnicastToBridge ) { SAFEASSERT( TargetAdapt == NULL ); ppqi->Flags.bShouldIndicate = TRUE; } else { ppqi->Flags.bShouldIndicate = BrdgMiniShouldIndicatePacket(DstAddr); } } // We require the use of the packet until our processing is complete rc = 1; } // Queue the packet for processing BrdgFwdQueuePacket( ppqi, pAdapt ); BrdgReleaseAdapter( pAdapt ); return rc; failure: if( TargetAdapt != NULL ) { BrdgReleaseAdapter( TargetAdapt ); } if( BrdgMiniShouldIndicatePacket(DstAddr) ) { ExInterlockedAddLargeStatistic( &gStatIndicatedDroppedFrames, 1L ); } BrdgReleaseAdapter( pAdapt ); // We are done with this packet return 0; } NDIS_STATUS BrdgFwdSendPacket( IN PNDIS_PACKET pPacket ) /*++ Routine Description: Called to handle the transmission of a packet from an overlying protocol Arguments: pPacket The packet to send Return Value: Status of the send (NDIS_STATUS_PENDING means the send will be completed later) --*/ { PNDIS_BUFFER Buffer; PUCHAR DstAddr; UINT Size; PADAPT TargetAdapt; BOOLEAN bRemaining; NDIS_STATUS Status; PNDIS_PACKET_STACK pStack; NdisQueryPacket(pPacket, NULL, NULL, &Buffer, NULL); NdisQueryBufferSafe(Buffer, &DstAddr, &Size, NormalPagePriority); if( DstAddr == NULL ) { return NDIS_STATUS_RESOURCES; } // // See if we know the adapter to reach the target through // TargetAdapt = BrdgTblFindTargetAdapter( DstAddr ); // Fail silently if the target adapter is resetting if( (TargetAdapt != NULL) && (TargetAdapt->bResetting) ) { BrdgReleaseAdapter( TargetAdapt ); return NDIS_STATUS_SUCCESS; } // Do compatibility processing, unless the packet is going to // a known target that isn't on a compatibility adapter (in // which case no compatibility processing is required). if( (TargetAdapt == NULL) || (TargetAdapt->bCompatibilityMode) ) { BrdgCompProcessOutboundPacket( pPacket, TargetAdapt ); } // If the target adapter is in compatibility-mode, no processing // other than the compatibility processing is required. if( (TargetAdapt != NULL) && (TargetAdapt->bCompatibilityMode) ) { // We're done with this packet! BrdgReleaseAdapter( TargetAdapt ); return NDIS_STATUS_SUCCESS; } // // We can fast-track the packet if there is an NDIS stack slot available // for use and there is a single target adapter to send on. // pStack = NdisIMGetCurrentPacketStack(pPacket, &bRemaining); if( (TargetAdapt != NULL) && bRemaining && (pStack != NULL) ) { // We fiddle with some of the packet flags when sending a packet. Remember the // state of the flags we change so we can restore them before handing back the // packet when the send completes. *((PUINT)(pStack->IMReserved)) = NdisGetPacketFlags(pPacket) & CHANGED_PACKET_FLAGS; // Just fast-track it out the target adapter BrdgFwdSendOnLink( TargetAdapt, pPacket ); // Done with the adapter pointer BrdgReleaseAdapter( TargetAdapt ); // We retain the buffers until we're done return NDIS_STATUS_PENDING; } // // Can't fast-track for whatever reason. We need to take the slow path through BrdgFwdHandlePacket // Status = BrdgFwdHandlePacket( BrdgPacketOutbound, TargetAdapt, NULL /* No source adapter */, FALSE /* Do not indicate */, NULL /*No miniport handle because no indication*/, NULL, NULL, /*No base packet yet*/ BrdgFwdMakeSendBasePacket, pPacket, NULL, 0, 0 ); if( TargetAdapt != NULL ) { // We're done with this adapter pointer BrdgReleaseAdapter( TargetAdapt ); } return Status; } VOID BrdgFwdCleanupPacket( IN PADAPT pAdapt, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status ) /*++ Routine Description: NDIS entry point called when a packet transmission has completed Arguments: ProtocolBindingContext The adapter on which the packet was send pPacket The transmitted packet Status The status of the send Return Value: None --*/ { PACKET_OWNERSHIP Own; // Find out whether we own this packet Own = BrdgBufGetPacketOwnership(pPacket); if( Own == BrdgNotOwned ) { NDIS_HANDLE MiniportHandle; PNDIS_PACKET_STACK pStack; BOOLEAN bRemaining; // This packet must have been a fast-track send. Return it to // its upper-layer owner. // Restore the flags that we change on a packet send by retrieving the // stored state of these flags that we stashed in IMReserved in // BrdgFwdSendPacket. pStack = NdisIMGetCurrentPacketStack(pPacket, &bRemaining); if( (pStack != NULL) && bRemaining ) { NdisClearPacketFlags( pPacket, CHANGED_PACKET_FLAGS ); NdisSetPacketFlags( pPacket, *((PUINT)(pStack->IMReserved)) ); } else { // There was stack room on the way down so this shouldn't happen. SAFEASSERT( FALSE ); } if( Status == NDIS_STATUS_SUCCESS ) { PVOID pHeader = BrdgBufGetPacketHeader(pPacket); if( pHeader != NULL ) { BrdgFwdCountTransmittedPacket( pAdapt, pHeader, BrdgBufTotalPacketSize(pPacket) ); } // pHeader can only be NULL under heavy system stress } else { ExInterlockedAddLargeStatistic( &gStatTransmittedErrorFrames, 1L ); } // NDIS should prevent the miniport from shutting down while // there is still a send pending. MiniportHandle = BrdgMiniAcquireMiniport(); SAFEASSERT( MiniportHandle != NULL ); NdisMSendComplete( MiniportHandle, pPacket, Status ); BrdgMiniReleaseMiniport(); } else { // // We allocated this packet ourselves. // // Recover the info pointer from our reserved area in the packet header PPACKET_INFO ppi = *((PPACKET_INFO*)pPacket->ProtocolReserved), baseppi; PNDIS_PACKET pBasePacket; if( ppi->Flags.bIsBasePacket == FALSE ) { // This packet is using buffers from another packet. baseppi = ppi->u.pBasePacketInfo; SAFEASSERT( baseppi != NULL ); pBasePacket = baseppi->pOwnerPacket; SAFEASSERT( pBasePacket != NULL ); } else { // This packet tracks its own buffers. pBasePacket = pPacket; baseppi = ppi; } // Contribute to the composite status of this packet if( Status == NDIS_STATUS_SUCCESS ) { baseppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_SUCCESS; } { UCHAR DstAddr[ETH_LENGTH_OF_ADDRESS]; UINT PacketSize; PVOID pHeader = BrdgBufGetPacketHeader(pBasePacket); NDIS_STATUS PacketStatus; PACKET_DIRECTION PacketDirection; // Pull out some information before we try to free the packet if( pHeader != NULL ) { ETH_COPY_NETWORK_ADDRESS( DstAddr, pHeader ); } // pHeader can only == NULL under heavy system stress PacketStatus = baseppi->u.BasePacketInfo.CompositeStatus; PacketDirection = baseppi->Flags.OriginalDirection; BrdgFwdValidatePacketDirection( PacketDirection ); PacketSize = BrdgBufTotalPacketSize(pBasePacket); // Now deref the packet if( BrdgFwdDerefBasePacket( pAdapt, pBasePacket, baseppi, PacketStatus ) ) { // The base packet was freed. Now ILLEGAL to reference pHeader, baseppi or pBasepacket if( PacketDirection == BrdgPacketOutbound ) { // This was a local-source packet. if( PacketStatus == NDIS_STATUS_SUCCESS ) { if( pHeader != NULL ) { BrdgFwdCountTransmittedPacket( pAdapt, DstAddr, PacketSize ); } } else { ExInterlockedAddLargeStatistic( &gStatTransmittedErrorFrames, 1L ); } } else { // This was a relayed packet. ExInterlockedAddLargeStatistic( &pAdapt->SentFrames, 1L ); ExInterlockedAddLargeStatistic( &pAdapt->SentBytes, PacketSize ); } } } if( pBasePacket != pPacket ) { // Owned copy packets are always base packets, so this should be a no-copy packet. SAFEASSERT( Own == BrdgOwnWrapperPacket ); BrdgFwdFreeWrapperPacket( pPacket, ppi, pAdapt ); } } } VOID BrdgFwdSendComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pPacket, IN NDIS_STATUS Status ) /*++ Routine Description: NDIS entry point called when a packet transmission has completed Arguments: ProtocolBindingContext The adapter on which the packet was send pPacket The transmitted packet Status The status of the send Return Value: None --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; SAFEASSERT( pAdapt != NULL ); if (pAdapt) { if( Status != NDIS_STATUS_SUCCESS ) { THROTTLED_DBGPRINT(FWD, ("Packet send failed with %08x\n", Status)); } BrdgFwdCleanupPacket(pAdapt, pPacket, Status); BrdgDecrementWaitRef(&pAdapt->Refcount); } } VOID BrdgFwdReturnIndicatedPacket( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET pPacket ) /*++ Routine Description: NDIS entry point called when a packet indication has completed Arguments: MiniportAdapterContext Ignored pPacket The transmitted packet Return Value: None --*/ { PACKET_OWNERSHIP Own; // Find out whether we own this packet Own = BrdgBufGetPacketOwnership(pPacket); if( Own == BrdgNotOwned ) { // This packet must have been a fast-track receive. Return it to // its lower-layer owner. BOOLEAN bRemaining; PNDIS_PACKET_STACK pStack = NdisIMGetCurrentPacketStack(pPacket, &bRemaining); PADAPT pOwnerAdapt; // If we fast-tracked this packet, it MUST have had room for us to stash our // pointer to the owning adapter SAFEASSERT( pStack != NULL ); SAFEASSERT( bRemaining ); // We incremented the owning adapter's refcount when we first received the packet pOwnerAdapt = (PADAPT)pStack->IMReserved[0]; SAFEASSERT( pOwnerAdapt != NULL ); // Here you go NdisReturnPackets( &pPacket, 1 ); // Release the owning NIC after the packet release BrdgReleaseAdapter( pOwnerAdapt ); // Illegal to refer to pPacket now pPacket = NULL; } else { // Recover our packet info block from our reserved area in the packet header PPACKET_INFO ppi = *((PPACKET_INFO*)pPacket->MiniportReserved); // Indications are always made with the base packet SAFEASSERT( ppi->Flags.bIsBasePacket ); // Let go of the base packet BrdgFwdDerefBasePacket( LOCAL_MINIPORT, pPacket, ppi, ppi->u.BasePacketInfo.CompositeStatus ); } } // =========================================================================== // // PRIVATE FUNCTIONS // // =========================================================================== BOOLEAN BrdgFwdServiceQueue( IN PADAPT pAdapt ) /*++ Routine Description: Services the inbound packet queue of a particular adapter This routine raises IRQL to DISPATCH to service the queue. It will service up to MAX_PACKETS_AT_DPC packets at DISPATCH and then return, even if the adapter's queue has not been drained. The bServiceInProgress flag is cleared if this routine manages to drain the adapter's queue. If the queue is non-empty when the routine exits, the bServiceInProgress flag is left set. Arguments: pAdapt The adapter to service Return Value: TRUE == the adapter's queue was drained FALSE == there are still queued packets to be serviced in the adapter's queue. --*/ { PPACKET_Q_INFO pqi; NDIS_HANDLE MiniportHandle = NULL; KIRQL oldIrql; ULONG HandledPackets = 0L; BOOLEAN bQueueWasEmptied; SAFEASSERT( pAdapt != NULL ); KeRaiseIrql( DISPATCH_LEVEL, &oldIrql ); // We should only be scheduled when there's something to deal with SAFEASSERT( BrdgQuerySingleListLength(&pAdapt->Queue) > 0 ); SAFEASSERT( pAdapt->bServiceInProgress ); // Get a handle on the miniport for the entire function duration MiniportHandle = BrdgMiniAcquireMiniportForIndicate(); // // The queue lock protects bServiceInProgress as well. Use it to dequeue // packets and update the flag atomically. // NdisDprAcquireSpinLock( &pAdapt->QueueLock); pqi = (PPACKET_Q_INFO)BrdgRemoveHeadSingleList(&pAdapt->Queue); while( pqi != NULL ) { PNDIS_PACKET pPacket; PADAPT TargetAdapt = NULL, OriginalAdapt = NULL; // // QueueRefcount reflects the number of elements in the processing queue // so people can block on it becoming empty // BrdgDecrementWaitRef( &pAdapt->QueueRefcount ); SAFEASSERT( (ULONG)pAdapt->QueueRefcount.Refcount == pAdapt->Queue.Length ); NdisDprReleaseSpinLock( &pAdapt->QueueLock ); // Demultiplex the union if( pqi->Flags.bFastTrackReceive ) { OriginalAdapt = pqi->u.pOriginalAdapt; } else { TargetAdapt = pqi->u.pTargetAdapt; } // Recover the packet pointer from the ProtocolReserved offset pPacket = CONTAINING_RECORD(pqi, NDIS_PACKET, ProtocolReserved); // Deal with this packet if( pqi->pInfo != NULL ) { if( pqi->Flags.bIsSTAPacket ) { if( ! gDisableSTA && BrdgFwdBridgingNetworks() ) { // Hand this packet off to the STA code BrdgSTAReceivePacket( pAdapt, pPacket ); } // We're done with this packet BrdgFwdReleaseBasePacket( pPacket, pqi->pInfo, BrdgBufGetPacketOwnership(pPacket), NDIS_STATUS_SUCCESS ); // It is an error to use any of these variables now pPacket = NULL; pqi = NULL; } else { BOOLEAN bShouldIndicate = pqi->Flags.bShouldIndicate, bIsUnicastToBridge = pqi->Flags.bIsUnicastToBridge, bRequiresCompatWork = pqi->Flags.bRequiresCompatWork, bCompatOnly; NDIS_STATUS Status; PPACKET_INFO ppi = pqi->pInfo; BOOLEAN bRetained = FALSE; // // This is an already-wrapped packet from the copy path. // SAFEASSERT( ! pqi->Flags.bFastTrackReceive ); // Before passing this packet along for processing, we must put a pointer to the packet's // info block back into its MiniportReserved and ProtocolReserved areas so completion // routines can recover the info block. // SAFEASSERT( ppi->pOwnerPacket == pPacket ); *((PPACKET_INFO*)pPacket->ProtocolReserved) = ppi; *((PPACKET_INFO*)pPacket->MiniportReserved) = ppi; // It is an error to use pqi anymore since it points into the ProtocolReserved area pqi = NULL; // If this packet arrived on a compatibility adapter or is bound for a // compatibility adapter, only compatibility-mode work is required. bCompatOnly = (BOOLEAN)((pAdapt->bCompatibilityMode) || ((TargetAdapt != NULL) && (TargetAdapt->bCompatibilityMode))); // Do compatibility work first if required if( bRequiresCompatWork ) { bRetained = BrdgCompProcessInboundPacket( pPacket, pAdapt, bCompatOnly ); Status = NDIS_STATUS_SUCCESS; } else { // Packet shouldn't have gotten here if there's nothing to do with it SAFEASSERT( ! bCompatOnly ); bRetained = FALSE; Status = NDIS_STATUS_SUCCESS; } if( ! bCompatOnly ) { // We told the compatibility module not to retain the packet SAFEASSERT( ! bRetained ); if( bIsUnicastToBridge ) { SAFEASSERT( TargetAdapt == NULL ); bRetained = FALSE; Status = NDIS_STATUS_FAILURE; if( MiniportHandle != NULL ) { if( BrdgBufAssignBasePacketQuota(pPacket, LOCAL_MINIPORT) ) { // Do fixups usually done in BrdgFwdHandlePacket ppi->u.BasePacketInfo.RefCount = 1L; ppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_FAILURE; // Indicate the packet up BrdgFwdIndicatePacket( pPacket, MiniportHandle ); bRetained = TRUE; } else { THROTTLED_DBGPRINT(FWD, ("Local miniport over quota on queued receive!\n")); } } } else { // Hand off this packet for general processing Status = BrdgFwdHandlePacket( BrdgPacketInbound, TargetAdapt, pAdapt, bShouldIndicate, MiniportHandle, pPacket, ppi, NULL, NULL, NULL, 0, 0 ); if( Status == NDIS_STATUS_PENDING ) { bRetained = TRUE; } else { // The base packet we previously created was not actually used by BrdgFwdHandlePacket. bRetained = FALSE; } } } // If our processing did not retain the packet for later release, release it now. if( ! bRetained ) { BrdgFwdReleaseBasePacket( pPacket, ppi, BrdgBufGetPacketOwnership(pPacket), Status ); } } } else { // Can't have unwrapped STA packets SAFEASSERT( ! pqi->Flags.bIsSTAPacket ); // Can't have unwrapped packets for compatibility processing SAFEASSERT( ! pqi->Flags.bRequiresCompatWork ); // Packet should not be here (unwrapped) if it arrived from a compatibility-mode // adapter. SAFEASSERT( ! pAdapt->bCompatibilityMode ); // BrdgFwdReceivePacket should copy unicast packets that can't be fast-tracked // into base packets before queuing them; we shouldn't end up with unwrapped // packets that are unicast to the bridge but aren't tagged for fast-tracking. if( pqi->Flags.bIsUnicastToBridge ) { SAFEASSERT( pqi->Flags.bFastTrackReceive ); } if( pqi->Flags.bFastTrackReceive ) { BOOLEAN bRetained = FALSE; SAFEASSERT( pqi->Flags.bIsUnicastToBridge ); if( MiniportHandle != NULL ) { PUCHAR DstAddr = BrdgBufGetPacketHeader(pPacket); if( DstAddr != NULL ) { // This is unicast to the bridge only; we are asked to try to fast-track it straight up to // overlying protocols. if( BrdgFwdNoCopyFastTrackReceive(pPacket, OriginalAdapt, MiniportHandle, DstAddr, &bRetained ) ) { // We had better be able to retain ownership of the original packet because we've already // hung on to it past the return of FwdReceivePacket! SAFEASSERT( bRetained ); } else { // BrdgFwdReceivePacket is supposed to make sure packets can be fast-tracked // before queuing them up SAFEASSERT( FALSE ); } } // DstAddr can only == NULL under heavy system stress } if( !bRetained ) { // Error of some sort or the miniport isn't available for indications. Ditch the packet. NdisReturnPackets( &pPacket, 1 ); // Illegal to refer to the packet now pPacket = NULL; } } else { NDIS_STATUS Status; // Packet should not be here (unwrapped) if it is bound for a compatibility-mode adapter. SAFEASSERT( ! TargetAdapt->bCompatibilityMode ); // This is not a packet unicast to the bridge. Do the more general processing. Status = BrdgFwdHandlePacket( BrdgPacketInbound, TargetAdapt, pAdapt, pqi->Flags.bShouldIndicate, MiniportHandle, NULL, NULL, BrdgFwdMakeNoCopyBasePacket, pPacket, pAdapt, 0, 0 ); if( Status != NDIS_STATUS_PENDING ) { // The unwrapped packet from the underlying NIC was not used. Release it now. NdisReturnPackets( &pPacket, 1 ); // Illegal to refer to the packet now pPacket = NULL; } } } // Release the target adapter if there was one if( TargetAdapt ) { BrdgReleaseAdapter( TargetAdapt ); } // Acquire the spin lock before either exiting or grabbing the next packet NdisDprAcquireSpinLock( &pAdapt->QueueLock ); // If we've processed too many packets, bail out even if the queue is not empty HandledPackets++; if( HandledPackets >= MAX_PACKETS_AT_DPC ) { break; } // Get the next packet off the queue pqi = (PPACKET_Q_INFO)BrdgRemoveHeadSingleList(&pAdapt->Queue); } // // Clear bServiceInProgress only if we emptied the queue. Otherwise, leave it set to // prevent spurious signalling of the QueueEvent, which would cause more than one // draining thread to service the same queue! // if( BrdgQuerySingleListLength(&pAdapt->Queue) == 0L ) { bQueueWasEmptied = TRUE; pAdapt->bServiceInProgress = FALSE; } else { bQueueWasEmptied = FALSE; } NdisDprReleaseSpinLock( &pAdapt->QueueLock ); // Let go of the miniport until next time if( MiniportHandle != NULL ) { BrdgMiniReleaseMiniportForIndicate(); } KeLowerIrql(oldIrql); return bQueueWasEmptied; } VOID BrdgFwdProcessQueuedPackets( IN PVOID Param1 ) /*++ Routine Description: Per-adapter inbound packet queue draining function There is one instance of this function running per processor. This routine sleeps until there is work to be done, and then calls BrdgFwdServiceQueue to service whichever adapter needs attention. It does this by blocking against the QueueEvent object for each adapter's queue, as well as the global gKillThreads and the gThreadsCheckAdapters event for this processor. When the block returns, there is an event needing attention; it may be the fact that the thread has been signaled to exit, that this thread is supposed to re-enumerate adapters, or that an adapter needs its inbound queue serviced. This routine increments the refcount of every adapter that it sleeps against; the gThreadsCheckAdapters event causes the thread to re-examine the adapter list and release its refcount on any adapters that were removed (or notice new additions). Must be called at < DISPATCH_LEVEL since we wait on an event Arguments: Param1 The processor on which we should execute (is not necessarily the processor on which we are first scheduled) Return Value: None --*/ { // Double cast to tell the IA64 compiler we really mean to truncate UINT Processor = (UINT)(ULONG_PTR)Param1; PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS]; KWAIT_BLOCK WaitBlocks[MAXIMUM_WAIT_OBJECTS]; ULONG numWaitObjects; BOOLEAN bDie = FALSE; PVOID pThread = KeGetCurrentThread(); // Constants const ULONG KILL_EVENT = 0L, CHECK_EVENT = 1L; DBGPRINT(FWD, ("Spinning up a thread on processor %i\n", Processor)); // Elevate our priority KeSetPriorityThread(pThread, LOW_REALTIME_PRIORITY); // Attach ourselves to our designated processor KeSetAffinityThread(pThread, (KAFFINITY)(1<Next ) { // We will be using this adapter outside the list lock BrdgAcquireAdapterInLock(pAdapt); WaitObjects[numWaitObjects] = &pAdapt->QueueEvent; numWaitObjects++; } NdisReleaseReadWriteLock( &gAdapterListLock, &LockState ); } else { // An adapter needs queue servicing. PADAPT pAdapt = CONTAINING_RECORD( WaitObjects[firedObject], ADAPT, QueueEvent ); if( ! BrdgFwdServiceQueue( pAdapt ) ) { // The adapter's queue was serviced but not emptied. Signal the queue event so // someone (maybe us!) will be scheduled to service the queue KeSetEvent( &pAdapt->QueueEvent, EVENT_INCREMENT, FALSE ); } } } // Shoot ourselves in the head PsTerminateSystemThread( STATUS_SUCCESS ); } PNDIS_PACKET BrdgFwdMakeSendBasePacket( OUT PPACKET_INFO *pppi, IN PADAPT Target, IN PVOID Param1, IN PVOID Param2, IN UINT Param3, IN UINT Param4 ) /*++ Routine Description: Passed as a parameter to BrdgFwdHandlePacket and called back as necessary Builds a base packet from a packet outbound from overlying protocols Arguments: pppi The info block of the new base packet or NULL if the allocation failed Target The adapter to "charge" the new base packet to Param1 The outbound packet Param2 - Param4 Unused Return Value: The new base packet or NULL if the allocation failed (usually because the target adapter didn't pass quota) --*/ { PNDIS_PACKET pPacket = (PNDIS_PACKET)Param1; PNDIS_PACKET pNewPacket; SAFEASSERT( pPacket != NULL ); // Get a wrapper packet to be the base packet pNewPacket = BrdgFwdAllocAndWrapPacketForSend( pPacket, pppi, Target ); if( pNewPacket == NULL ) { // We didn't pass quota for this target return NULL; } // Stuff a pointer to the packet's info block into both the ProtocolReserved // and the MiniportReserved areas so we can recover the info block no matter // how we plan to use this packet *((PPACKET_INFO*)pNewPacket->ProtocolReserved) = *pppi; *((PPACKET_INFO*)pNewPacket->MiniportReserved) = *pppi; SAFEASSERT( *pppi != NULL ); (*pppi)->u.BasePacketInfo.pOriginalPacket = pPacket; (*pppi)->u.BasePacketInfo.pOwnerAdapter = NULL; (*pppi)->Flags.OriginalDirection = BrdgPacketOutbound; (*pppi)->Flags.bIsBasePacket = TRUE; // Signal that the underlying NIC can hang on to the buffers NDIS_SET_PACKET_STATUS( pNewPacket, NDIS_STATUS_SUCCESS ); return pNewPacket; } PNDIS_PACKET BrdgFwdMakeNoCopyBasePacket( OUT PPACKET_INFO *pppi, IN PADAPT Target, IN PVOID Param1, IN PVOID Param2, IN UINT Param3, IN UINT Param4 ) /*++ Routine Description: Passed as a parameter to BrdgFwdHandlePacket and called back as necessary Builds a new base packet from a packet received on the no-copy path Arguments: pppi The info block for the new packet or NULL if the alloc failed Target The adapter to "charge" the new packet to Param1 The originally indicated packet descriptor Param2 The adapter on which the packet was received Param3, Param4 Unused Return Value: A new base packet or NULL if the allocation failed (usually because the target adapter did not pass quota) --*/ { PNDIS_PACKET pPacket = (PNDIS_PACKET)Param1; PADAPT pOwnerAdapt = (PADAPT)Param2; PNDIS_PACKET NewPacket; SAFEASSERT( pPacket != NULL ); SAFEASSERT( pOwnerAdapt != NULL ); // Get a new wrapper packet NewPacket = BrdgFwdAllocAndWrapPacketForReceive( pPacket, pppi, Target ); if (NewPacket == NULL) { // We didn't pass quota for this target return NULL; } SAFEASSERT( *pppi != NULL ); // Stuff a pointer to the packet's info block into both the ProtocolReserved // and the MiniportReserved areas so we can recover the info block no matter // how we plan to use this packet *((PPACKET_INFO*)NewPacket->ProtocolReserved) = *pppi; *((PPACKET_INFO*)NewPacket->MiniportReserved) = *pppi; // // We must ensure that the adapter we just got this packet from is not unbound until we are // done with its packet. Bump the adapter's refcount here. The adapter's refcount will be // decremented again when this base packet is freed. // BrdgReacquireAdapter( pOwnerAdapt ); (*pppi)->u.BasePacketInfo.pOwnerAdapter = pOwnerAdapt; (*pppi)->u.BasePacketInfo.pOriginalPacket = pPacket; (*pppi)->Flags.OriginalDirection = BrdgPacketInbound; (*pppi)->Flags.bIsBasePacket = TRUE; // Make sure the packet indicates that it's OK to hang on to buffers NDIS_SET_PACKET_STATUS( NewPacket, NDIS_STATUS_SUCCESS ); // Count this packet as received ExInterlockedAddLargeStatistic( &gStatReceivedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatReceivedBytes, BrdgBufTotalPacketSize(pPacket) ); ExInterlockedAddLargeStatistic( &gStatReceivedNoCopyFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatReceivedNoCopyBytes, BrdgBufTotalPacketSize(pPacket) ); ExInterlockedAddLargeStatistic( &pOwnerAdapt->ReceivedFrames, 1L ); ExInterlockedAddLargeStatistic( &pOwnerAdapt->ReceivedBytes, BrdgBufTotalPacketSize(pPacket) ); return NewPacket; } PNDIS_PACKET BrdgFwdMakeCopyBasePacket( OUT PPACKET_INFO *pppi, IN PVOID pHeader, IN PVOID pData, IN UINT HeaderSize, IN UINT DataSize, IN UINT SizeOfPacket, IN BOOLEAN bCountAsReceived, IN PADAPT pOwnerAdapt, PVOID *ppBuf ) /*++ Routine Description: Builds a new copy packet to hold inbound data on the copy path or on the no-copy path if a packet arrives with STATUS_RESOURCES. The new packet has NO ATTRIBUTED QUOTA to any adapter. This is because at the time of the initial receive, a target adapter is not yet known and the inbound data must be wrapped in a copy packet to be queued for processing. The cost of the base packet is assigned to target adapters as it is processed in the queue-draining thread. Arguments: pppi Output of the new info block associated with the new packet (NULL if alloc failed) pHeader Pointer to the header buffer originally indicated Can be NULL to not copy the header pData Pointer to the data buffer originally indicated Can be NULL to not copy the data buffer HeaderSize Size of the header buffer DataSize Size of the data buffer SizeOfPacket Size to set the packet's buffer to. Can be different from HeaderSize+DataSize if the caller plans to copy more data in later bCountAsReceived Whether to count this packet as received pOwnerAdapt Adapter this packet was received on (purely for statistics purposes). Can be NULL if bCountAsReceived == FALSE ppBuf (optionally) receives a pointer to the data buffer of the freshly allocated packet Return Value: A new base packet or NULL if the allocation failed --*/ { PNDIS_PACKET NewPacket; PNDIS_BUFFER pBuffer; PVOID pvBuf; UINT bufLength; // Get a copy packet to carry the data NewPacket = BrdgBufGetBaseCopyPacket( pppi ); if (NewPacket == NULL) { // Our copy packet pool is full! return NULL; } SAFEASSERT( *pppi != NULL ); // Get a pointer to the preallocated buffer in this packet pBuffer = BrdgBufPacketHeadBuffer(NewPacket); SAFEASSERT( pBuffer != NULL ); NdisQueryBufferSafe( pBuffer, &pvBuf, &bufLength, NormalPagePriority ); if( pvBuf == NULL ) { // This shouldn't be possible because the data buffer should have been // alloced from kernel space SAFEASSERT(FALSE); BrdgBufFreeBaseCopyPacket( NewPacket, *pppi ); *pppi = NULL; return NULL; } SAFEASSERT( bufLength == MAX_PACKET_SIZE ); if( ppBuf != NULL ) { *ppBuf = pvBuf; } // Copy the packet data into our own preallocated buffers if( pHeader != NULL ) { NdisMoveMemory(pvBuf, pHeader, HeaderSize); } else { SAFEASSERT( HeaderSize == 0 ); } if( pData != NULL ) { NdisMoveMemory((PUCHAR)pvBuf + HeaderSize, pData, DataSize); } else { SAFEASSERT( DataSize == 0 ); } // Tweak the size of the buffer so it looks like the right length NdisAdjustBufferLength(pBuffer, SizeOfPacket); (*pppi)->u.BasePacketInfo.pOriginalPacket = NULL; (*pppi)->u.BasePacketInfo.pOwnerAdapter = NULL; (*pppi)->Flags.OriginalDirection = BrdgPacketInbound; (*pppi)->Flags.bIsBasePacket = TRUE; // Make the header size correct NDIS_SET_PACKET_HEADER_SIZE(NewPacket, ETHERNET_HEADER_SIZE); // Indicate that upper-layer protocols can hang on to these buffers NDIS_SET_PACKET_STATUS( NewPacket, NDIS_STATUS_SUCCESS ); // Count this packet as received if( bCountAsReceived ) { ExInterlockedAddLargeStatistic( &gStatReceivedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatReceivedBytes, SizeOfPacket ); ExInterlockedAddLargeStatistic( &gStatReceivedCopyFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatReceivedCopyBytes, SizeOfPacket ); SAFEASSERT( pOwnerAdapt != NULL ); ExInterlockedAddLargeStatistic( &pOwnerAdapt->ReceivedFrames, 1L ); ExInterlockedAddLargeStatistic( &pOwnerAdapt->ReceivedBytes, SizeOfPacket ); } return NewPacket; } VOID BrdgFwdSendOnLink( IN PADAPT pAdapt, IN PNDIS_PACKET pPacket ) /*++ Routine Description: Sends a packet to a particular adapter Arguments: pAdapt The adapter to send to pPacket The packet to send Return Value: None --*/ { PPACKET_INFO ppi = *((PPACKET_INFO*)pPacket->MiniportReserved); PACKET_DIRECTION PacketDirection = BrdgPacketImpossible; BOOLEAN Bridging = BrdgFwdBridgingNetworks(); BOOLEAN Incremented = FALSE; // Make sure this doesn't loop back NdisClearPacketFlags( pPacket, NDIS_FLAGS_LOOPBACK_ONLY ); NdisSetPacketFlags( pPacket, NDIS_FLAGS_DONT_LOOPBACK ); // // Logic is like this: // If the packet is an Outbound packet then we send it. // If the packet has been created in the bridge, then we check // the base packet to see if it is outbound, if it is then we send the packet. // if (!Bridging) { if (!ppi) { ppi = *((PPACKET_INFO*)pPacket->ProtocolReserved); } if (ppi) { if (((ppi->Flags.OriginalDirection == BrdgPacketOutbound) || ((ppi->Flags.OriginalDirection == BrdgPacketCreatedInBridge) && (ppi->u.pBasePacketInfo != NULL && ppi->u.pBasePacketInfo->Flags.OriginalDirection == BrdgPacketOutbound) ) ) ) { PacketDirection = BrdgPacketOutbound; } } #if DBG else { if (gBreakIfNullPPI) { KdBreakPoint(); } } #endif // DBG } Incremented = BrdgIncrementWaitRef(&pAdapt->Refcount); if (Incremented && (PacketDirection == BrdgPacketOutbound || Bridging)) { #if DBG if (gPrintPacketTypes) { if (PacketDirection == BrdgPacketOutbound) { THROTTLED_DBGPRINT(FWD, ("Sending Outbound packet\r\n")); } else { THROTTLED_DBGPRINT(FWD, ("Forwarding packet\r\n")); } } #endif // DBG // Send! NdisSendPackets( pAdapt->BindingHandle, &pPacket, 1 ); } else { #if DBG if (Bridging && gPrintPacketTypes) { THROTTLED_DBGPRINT(FWD, ("Not allowed to send packet\r\n")); } #endif // DBG // // We incremented this, but we're not going to be going through any path that // decrements this, so we need to do this here. // if (Incremented) { BrdgDecrementWaitRef(&pAdapt->Refcount); } BrdgFwdCleanupPacket(pAdapt, pPacket, NDIS_STATUS_CLOSING); } } VOID BrdgFwdReleaseBasePacket( IN PNDIS_PACKET pPacket, PPACKET_INFO ppi, IN PACKET_OWNERSHIP Own, IN NDIS_STATUS Status ) /*++ Routine Description: Frees a base packet. Called when a base packet's refcount reaches zero. Arguments: pPacket The base packet to free ppi The packet's info block Own The result of a call to BrdgBufGetPacketOwnership(pPacket) Status The status to be returned to the entity owning the original packet wrapped by the base packet (if any) Return Value: None --*/ { SAFEASSERT( ppi->Flags.bIsBasePacket ); if( Own == BrdgOwnCopyPacket ) { // This packet was allocated to wrap copied buffers. Free it back to our pool. BrdgFwdValidatePacketDirection( ppi->Flags.OriginalDirection ); BrdgBufFreeBaseCopyPacket( pPacket, ppi ); } else { // This packet was allocated to wrap a protocol or miniport's buffers. // Return the packet to its original owner. SAFEASSERT( Own == BrdgOwnWrapperPacket ); SAFEASSERT( ppi->u.BasePacketInfo.pOriginalPacket != NULL ); if( ppi->Flags.OriginalDirection == BrdgPacketInbound ) { // Wraps a lower-layer miniport packet. NdisReturnPackets( &ppi->u.BasePacketInfo.pOriginalPacket, 1 ); // We incremented the adapter's refcount when we first received the packet // to prevent the adapter from shutting down while we still held some of // its packets SAFEASSERT( ppi->u.BasePacketInfo.pOwnerAdapter != NULL ); BrdgReleaseAdapter( ppi->u.BasePacketInfo.pOwnerAdapter ); } else { NDIS_HANDLE MiniportHandle; // Wraps a higher-layer protocol packet SAFEASSERT( ppi->Flags.OriginalDirection == BrdgPacketOutbound ); // Shuttle back per-packet information before returning the original descriptor NdisIMCopySendCompletePerPacketInfo (ppi->u.BasePacketInfo.pOriginalPacket, pPacket); // Give back the original descriptor. // NDIS should prevent the miniport from shutting down while there is still an // indicate pending. MiniportHandle = BrdgMiniAcquireMiniport(); SAFEASSERT( MiniportHandle != NULL ); NdisMSendComplete( MiniportHandle, ppi->u.BasePacketInfo.pOriginalPacket, Status ); BrdgMiniReleaseMiniport(); } // Don't forget to free the wrapper packet as well BrdgFwdFreeBaseWrapperPacket( pPacket, ppi ); } } VOID BrdgFwdWrapPacketForReceive( IN PNDIS_PACKET pOriginalPacket, IN PNDIS_PACKET pNewPacket ) /*++ Routine Description: Copies state information into a wrapper packet for the purposes of indicating the new packet up to overlying protocols Arguments: pOriginalPacket The packet to copy state out of pNewPacket The wrapper packet to copy state into Return Value: None --*/ { NDIS_STATUS Status; // Copy other header and OOB data NDIS_SET_ORIGINAL_PACKET(pNewPacket, NDIS_GET_ORIGINAL_PACKET(pOriginalPacket)); NdisSetPacketFlags( pNewPacket, NdisGetPacketFlags(pOriginalPacket) ); Status = NDIS_GET_PACKET_STATUS(pOriginalPacket); NDIS_SET_PACKET_STATUS(pNewPacket, Status); NDIS_SET_PACKET_HEADER_SIZE(pNewPacket, NDIS_GET_PACKET_HEADER_SIZE(pOriginalPacket)); } VOID BrdgFwdWrapPacketForSend( IN PNDIS_PACKET pOriginalPacket, IN PNDIS_PACKET pNewPacket ) /*++ Routine Description: Copies state information into a wrapper packet for the purposes of transmitting the new packet to underlying NICs Arguments: pOriginalPacket The packet to copy state out of pNewPacket The wrapper packet to copy state into Return Value: None --*/ { PVOID MediaSpecificInfo = NULL; ULONG MediaSpecificInfoSize = 0; NdisSetPacketFlags( pNewPacket, NdisGetPacketFlags(pOriginalPacket) ); // // Copy the OOB Offset from the original packet to the new // packet. // NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(pNewPacket), NDIS_OOB_DATA_FROM_PACKET(pOriginalPacket), sizeof(NDIS_PACKET_OOB_DATA)); // // Copy the per packet info into the new packet // This includes ClassificationHandle, etc. // Make sure other stuff is not copied !!! // NdisIMCopySendPerPacketInfo(pNewPacket, pOriginalPacket); // // Copy the Media specific information // NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(pOriginalPacket, &MediaSpecificInfo, &MediaSpecificInfoSize); if (MediaSpecificInfo || MediaSpecificInfoSize) { NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(pNewPacket, MediaSpecificInfo, MediaSpecificInfoSize); } } PNDIS_PACKET BrdgFwdCommonAllocAndWrapPacket( IN PNDIS_PACKET pBasePacket, OUT PPACKET_INFO *pppi, IN PADAPT pTargetAdapt, IN PWRAPPER_FUNC pFunc ) /*++ Routine Description: Common logic for creating a wrapper packet Creates a new wrapper packet and calls the supplied function to copy state information from the original packet into the wrapper Arguments: pBasePacket The packet to wrap pppi Returns the new wrapper packet's info block or NULL if the allocation fails pTargetAdapt The adapter to charge the new wrapper packet (and the cost of hanging onto the base packet) to pFunc The function to call to copy state from the original packet to the new wrapper Return Value: The newly allocated wrapper packet or NULL if the allocation failed (usually because the target adapter did not pass quota) --*/ { PNDIS_PACKET pNewPacket; NDIS_STATUS Status; SAFEASSERT( pTargetAdapt != NULL ); // Must first determine if the target can handle the quota of // holding onto the base packet. // // If we do not own the base packet, this has no effect. if( ! BrdgBufAssignBasePacketQuota(pBasePacket, pTargetAdapt) ) { *pppi = NULL; return NULL; } // Try to get a wrapper packet pNewPacket = BrdgBufGetWrapperPacket( pppi, pTargetAdapt ); if( pNewPacket == NULL ) { SAFEASSERT( *pppi == NULL ); // Reverse the previous accounting for holding onto the base packet BrdgBufReleaseBasePacketQuota( pBasePacket, pTargetAdapt ); return NULL; } SAFEASSERT( *pppi != NULL ); // Point the new packet to the old buffers Status = BrdgBufChainCopyBuffers( pNewPacket, pBasePacket ); if( Status != NDIS_STATUS_SUCCESS ) { BrdgBufReleaseBasePacketQuota( pBasePacket, pTargetAdapt ); BrdgBufFreeWrapperPacket( pNewPacket, *pppi, pTargetAdapt ); *pppi = NULL; return NULL; } // Stuff a pointer to the packet's info block into both the ProtocolReserved // and the MiniportReserved areas so we can recover the info block no matter // how we plan to use this packet *((PPACKET_INFO*)pNewPacket->ProtocolReserved) = *pppi; *((PPACKET_INFO*)pNewPacket->MiniportReserved) = *pppi; // Copy whatever state needs to be copied for the direction this packet is heading (*pFunc)(pBasePacket, pNewPacket); return pNewPacket; } // // Paranoid checking of base packets // #if DBG _inline VOID BrdgFwdCheckBasePacket( IN PNDIS_PACKET pPacket, IN PPACKET_INFO ppi ) { SAFEASSERT( ppi != NULL ); // Packets should come prepared so the PACKET_INFO structure is recoverable from // both the MiniportReserved and ProtocolReserved areas, so it won't matter whether // we use the packet for a send or an indicate. SAFEASSERT( *((PPACKET_INFO*)pPacket->ProtocolReserved) == ppi ); SAFEASSERT( *((PPACKET_INFO*)pPacket->MiniportReserved) == ppi ); // The base packet refcounts its own buffers SAFEASSERT( ppi->Flags.bIsBasePacket ); // The base packet must allow the upper-layer protocol to hang onto its buffers SAFEASSERT( NDIS_GET_PACKET_STATUS( pPacket ) == NDIS_STATUS_SUCCESS ); } #else #define BrdgFwdCheckBasePacket(A,B) {} #endif NDIS_STATUS BrdgFwdHandlePacket( IN PACKET_DIRECTION PacketDirection, IN PADAPT pTargetAdapt, IN PADAPT pOriginalAdapt, IN BOOLEAN bShouldIndicate, IN NDIS_HANDLE MiniportHandle, IN PNDIS_PACKET pBasePacket, IN PPACKET_INFO baseppi, IN PPACKET_BUILD_FUNC pFunc, IN PVOID Param1, IN PVOID Param2, IN UINT Param3, IN UINT Param4 ) /*++ Routine Description: Common logic for handling packets that cannot be fast-tracked A base packet can optionally be passed in. This is only done when handling packets from the copy path or the no-copy path when packets arrived with STATUS_RESOURCES set, since those types of packets must be wrapped just to be queued for processing. If a base packet is passed in, it is assumed that NO QUOTA has been assigned to any adapter for that base packet. The cost of the base packet is assigned to any prospective target via BrdgBufAssignBasePacketQuota(). If a base packet is not passed in, a function pointer must be supplied that can build a base packet on demand from the supplied parameters. When base packets are built on the fly, they DO require immediate quota assignments. Note that if a base packet is passed in, it is possible for this function to release the base packet itself (via BrdgFwdReleaseBasePacket) and return NDIS_STATUS_PENDING. Arguments: PacketDirection The original direction of the packet being handled pTargetAdapt The adapter corresponding to the packet's target MAC address, or NULL if not known. A non-NULL value implies that bShouldIndicate == FALSE, since it doesn't make sense for a unicast packet bound for another adapter to require indication to the local machine. pOriginalAdapt The adapter on which the original packet was received bShouldIndicate Whether the packet should be indicated to overlying protocols MiniportHandle The handle to our local miniport (CALLER IS RESPONSIBLE FOR ENSURING THE MINIPORT'S EXISTENCE DURING THIS CALL!) pBasePacket The base packet to use if one has already been built (this occurs on the copy-receive path) baseppi The base packet's PACKET_INFO if one exists. pFunc A function that, when passed Param1 - Param4, can build a base packet from the originally received packet for a particular target adapter. Param1 - Param4 Parameters to pass to pFunc If a base packet is not supplied, pFunc must be non-NULL. Conversely, if a base packet is supplied, pFunc should be NULL because it will never be called. Return Value: NDIS_STATUS_PENDING Indicates that the base packet passed in was used successfully or that a base packet was successfully built and used with the help of pFunc. The base packet and any wrapper packets build by BrdgFwdHandlePacket will be automatically deallocated in the future; the caller need not take any additional action. OTHER RETURN CODE No targets were found or none passed quota check. If a base packet was passed in, the caller should free it. If there is an underlying packet that was to be used to build a base packet, the caller should free it. --*/ { BOOLEAN dataRetained = FALSE; PACKET_DIRECTION tmpPacketDirection; tmpPacketDirection = PacketDirection; SAFEASSERT( (PacketDirection == BrdgPacketInbound) || (PacketDirection == BrdgPacketOutbound) ); SAFEASSERT( (pBasePacket != NULL) || (pFunc != NULL) ); SAFEASSERT( (pTargetAdapt == NULL) || (bShouldIndicate == FALSE) ); if( pBasePacket != NULL ) { SAFEASSERT( baseppi != NULL ); BrdgFwdCheckBasePacket( pBasePacket, baseppi ); } if( bShouldIndicate ) { // Don't try to indicate if the miniport doesn't exist if( MiniportHandle == NULL ) { // Count this as a failed indicate ExInterlockedAddLargeStatistic( &gStatIndicatedDroppedFrames, 1L ); bShouldIndicate = FALSE; } } // Not allowed to pass in a target adapter in compatibility-mode, since // only the compatibility-mode code is supposed to deal with those. if( pTargetAdapt != NULL ) { SAFEASSERT( !pTargetAdapt->bCompatibilityMode ); } if( (pTargetAdapt != NULL) && (! bShouldIndicate) ) { // This packet is going to a single destination. if( pBasePacket != NULL ) { // We were passed in a base packet. See if the target adapter can accept // the quota of the base packet. if( ! BrdgBufAssignBasePacketQuota(pBasePacket, pTargetAdapt) ) { // The target is over quota and can't accept this packet. We will // return an error code to indicate that we never used the caller's base // packet. pBasePacket = NULL; baseppi = NULL; } // else we continue processing below } else { // Alloc a base packet with the supplied function SAFEASSERT( pFunc != NULL ); pBasePacket = (*pFunc)(&baseppi, pTargetAdapt, Param1, Param2, Param3, Param4); } if( pBasePacket != NULL ) { // Paranoia BrdgFwdCheckBasePacket( pBasePacket, baseppi ); baseppi->u.BasePacketInfo.RefCount = 1L; baseppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_FAILURE; BrdgFwdSendOnLink( pTargetAdapt, pBasePacket ); // We're using the base packet or the underlying packet used to build the // base packet dataRetained = TRUE; } else { THROTTLED_DBGPRINT(FWD, ("Over quota for single target adapter\n")); if( PacketDirection == BrdgPacketOutbound ) { // This was a failed local-source transmit ExInterlockedAddLargeStatistic( &gStatTransmittedErrorFrames, 1L ); } } } else { // // Our packet isn't bound for a single destination. Do the slow processing. // UINT numTargets = 0L, actualTargets, i; PADAPT pAdapt; PADAPT SendList[MAX_ADAPTERS]; LOCK_STATE LockState; BOOLEAN sentBase = FALSE; // Whether we have sent the base packet yet // // First we need a list of the adapters we intend to send this packet to // NdisAcquireReadWriteLock( &gAdapterListLock, FALSE /*Read only*/, &LockState ); // Always indicate with the base packet if( bShouldIndicate ) { SendList[0] = LOCAL_MINIPORT; numTargets = 1L; } if( pTargetAdapt != NULL ) { BrdgReacquireAdapter( pTargetAdapt ); SendList[numTargets] = pTargetAdapt; numTargets++; } else { // Note each adapter to send to for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next ) { // Don't need to acquire the global adapter characteristics lock to read the // media state because we don't care about the global consistency of the // adapters' characteristics here if( (pAdapt != pOriginalAdapt) && (pAdapt->MediaState == NdisMediaStateConnected) && // Don't send to disconnected adapters (pAdapt->State == Forwarding) && // Adapter must be in relaying state (! pAdapt->bResetting) && // Adapter must not be resetting (! pAdapt->bCompatibilityMode) ) // Adapter can't be in compat-mode { if( numTargets < MAX_ADAPTERS ) { // We will use this adapter outside the list lock; bump its refcount BrdgAcquireAdapterInLock(pAdapt); SendList[numTargets] = pAdapt; numTargets++; } else { // Too many copies to send! SAFEASSERT( FALSE ); } } } } // Can let go of the adapter list now; we have copied out all the target adapters // and incremented the refcount for the adapters we will be using. NdisReleaseReadWriteLock( &gAdapterListLock, &LockState ); if( numTargets == 0 ) { // // Nowhere to send the packet! Nothing to do. // // This should not happen often. If the packet is a local send, our media status // should be DISCONNECTED, so there should be no transmits from above. // if( PacketDirection == BrdgPacketOutbound ) { // This was a failed local-source transmit (although the caller probably // shouldn't have sent the packet in the first place since our media status // should be DISCONNECTED ExInterlockedAddLargeStatistic( &gStatTransmittedErrorFrames, 1L ); } // // Indicate to the caller that no send occurred // return NDIS_STATUS_NO_CABLE; } actualTargets = numTargets; // If we had a base packet passed in, set its refcount now that we know how many // adapters we will be targeting if( pBasePacket != NULL ) { baseppi->u.BasePacketInfo.RefCount = actualTargets; baseppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_FAILURE; // We now need ownership of the base packet passed in; even if all our send // attempts fail, we will release the base packet ourselves below, so the // caller should not dispose of the base packet himself. dataRetained = TRUE; } // // Walk the list of targets and try to send to each // for( i = 0L; i < numTargets; i++ ) { PADAPT OutAdapt = SendList[i]; PNDIS_PACKET pPacketToSend = NULL; PPACKET_INFO ppiToSend = NULL; SAFEASSERT(tmpPacketDirection == PacketDirection); if( pBasePacket == NULL ) { // // We weren't passed in a base packet and we haven't built one yet. Build one now // that we have a specific target adapter. // pBasePacket = (*pFunc)(&baseppi, OutAdapt, Param1, Param2, Param3, Param4); if( pBasePacket != NULL ) { // Paranoia BrdgFwdCheckBasePacket( pBasePacket, baseppi ); SAFEASSERT( actualTargets > 0L ); baseppi->u.BasePacketInfo.RefCount = actualTargets; baseppi->u.BasePacketInfo.CompositeStatus = NDIS_STATUS_FAILURE; pPacketToSend = pBasePacket; ppiToSend = baseppi; sentBase = TRUE; } else { // We failed to build a base packet. Just pretend there was one less target // for the next time through actualTargets--; } } else { if( ! sentBase ) { // // We have a base packet but we haven't sent it yet. Send to this target if quota allows. // if( BrdgBufAssignBasePacketQuota(pBasePacket, OutAdapt) ) { // This target can accept the base packet. pPacketToSend = pBasePacket; ppiToSend = baseppi; sentBase = TRUE; } else { // The target is over quota and can't accept this packet. pPacketToSend = NULL; ppiToSend = NULL; // bookkeeping on the base packet done below } } else { // // We have a base packet and we have already sent it. Use wrapper packets for each additional // send. // if( baseppi->Flags.OriginalDirection == BrdgPacketInbound ) { pPacketToSend = BrdgFwdAllocAndWrapPacketForReceive( pBasePacket, &ppiToSend, OutAdapt ); } else { SAFEASSERT( baseppi->Flags.OriginalDirection == BrdgPacketOutbound ); pPacketToSend = BrdgFwdAllocAndWrapPacketForSend( pBasePacket, &ppiToSend, OutAdapt ); } if( pPacketToSend != NULL ) { // Signal that the upper-layer protocol can hang on to these buffers NDIS_SET_PACKET_STATUS(pPacketToSend, NDIS_STATUS_SUCCESS); // Set up the wrapper's info block SAFEASSERT( ppiToSend != NULL ); ppiToSend->Flags.OriginalDirection = BrdgPacketCreatedInBridge; ppiToSend->Flags.bIsBasePacket = FALSE; ppiToSend->u.pBasePacketInfo = baseppi; } // else bookkeeping done below } } if( pPacketToSend == NULL ) { // Record the failed attempt as appropriate SAFEASSERT( ppiToSend == NULL ); if( OutAdapt == LOCAL_MINIPORT ) { THROTTLED_DBGPRINT(FWD, ("Over quota for local miniport during processing\n")); ExInterlockedAddLargeStatistic( &gStatIndicatedDroppedFrames, 1L ); } else { THROTTLED_DBGPRINT(FWD, ("Over quota for adapter during processing\n")); } if( pBasePacket != NULL ) { // We failed to send or wrap the base packet to this target. Do bookkeeping. SAFEASSERT( baseppi != NULL ); if( BrdgFwdDerefBasePacket( NULL/*The cost of the base packet never got assigned to OutAdapt*/, pBasePacket, baseppi, NDIS_STATUS_FAILURE ) ) { // We should have been the last target in the list to cause the base packet // to actually be freed. SAFEASSERT( i == numTargets - 1 ); pBasePacket = NULL; baseppi = NULL; // We just disposed of the caller's base packet, so we should not cause him to // try to do that again on return SAFEASSERT( dataRetained ); } } } else { // We have a packet to send. SAFEASSERT( ppiToSend != NULL ); if( OutAdapt == LOCAL_MINIPORT ) { // We are indicating this packet SAFEASSERT( MiniportHandle != NULL ); BrdgFwdIndicatePacket( pPacketToSend, MiniportHandle ); } else { // We are sending to an adapter, not the local miniport BrdgFwdSendOnLink( OutAdapt, pPacketToSend ); } // We definitely need ownership of the underlying data since we just handed it off // to a target dataRetained = TRUE; } if( OutAdapt != LOCAL_MINIPORT ) { // We're done with this adapter now BrdgReleaseAdapter( OutAdapt ); } } if( ! dataRetained ) { // If we're not claiming ownership of the underlying data, we had better not have // actually used it SAFEASSERT( ! sentBase ); if( PacketDirection == BrdgPacketOutbound ) { // This was a failed local-source transmit ExInterlockedAddLargeStatistic( &gStatTransmittedErrorFrames, 1L ); } } else { // If we are claiming owernship of the underlying data, we must have used it or // disposed of the base packet ourselves. SAFEASSERT( sentBase || (pBasePacket == NULL) ); } } // Tell the caller whether we are hanging into his data or not return dataRetained ? NDIS_STATUS_PENDING : NDIS_STATUS_FAILURE; } BOOLEAN BrdgFwdNoCopyFastTrackReceive( IN PNDIS_PACKET pPacket, IN PADAPT pAdapt, IN NDIS_HANDLE MiniportHandle, IN PUCHAR DstAddr, OUT BOOLEAN *bRetainPacket ) /*++ Routine Description: Called to indicate a packet descriptor from an underlying NIC straight up to overlying protocols without wrapping. Arguments: pPacket The packet to indicate pAdapt The adapter that owns this packet descriptor MiniportHandle The miniport handle (must be != NULL) DstAddr The target MAC address of the packet bRetainPacket Whether the caller should retain ownership of the given packet descriptor or not. TRUE if the original packet's status was not STATUS_RESOURCES, FALSE otherwise. Undefined if return value != TRUE Return Value: TRUE if the indication succeeded, FALSE otherwise. --*/ { BOOLEAN bRemaining; NDIS_STATUS Status; PNDIS_PACKET_STACK pStack; SAFEASSERT( pPacket != NULL ); SAFEASSERT( pAdapt != NULL ); SAFEASSERT( MiniportHandle != NULL ); SAFEASSERT( bRetainPacket != NULL ); *bRetainPacket = FALSE; // The fast-track is possible only if NDIS has room left in its packet stack pStack = NdisIMGetCurrentPacketStack(pPacket, &bRemaining); if ( bRemaining ) { Status = NDIS_GET_PACKET_STATUS(pPacket); if( Status != NDIS_STATUS_RESOURCES ) { SAFEASSERT( (Status == NDIS_STATUS_SUCCESS) || (Status == NDIS_STATUS_PENDING) ); // // The upper-layer protocol gets to hang on to this packet until it's done. // We must ensure that this adapter is not allowed to shut down while we are // still holding its packet. As a special case for fast-track receives, we // stash a pointer to the adapter's PADAPT struct in the magic NDIS stack // area reserved for intermediate drivers. This allows us to decrement the // adapter's refcount when the indication completes. // BrdgReacquireAdapter( pAdapt ); pStack->IMReserved[0] = (ULONG_PTR)pAdapt; // Tell the caller to retain ownership of the packet *bRetainPacket = TRUE; } else { // Paranoia: zero out the area we use to stash the PADAPT pointer in case // we get confused about the path this packet took pStack->IMReserved[0] = 0L; // Tell the owner not to retain ownership of the packet *bRetainPacket = FALSE; } // Count the packet as received ExInterlockedAddLargeStatistic( &gStatReceivedFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatReceivedBytes, BrdgBufTotalPacketSize(pPacket) ); ExInterlockedAddLargeStatistic( &gStatReceivedNoCopyFrames, 1L ); ExInterlockedAddLargeStatistic( &gStatReceivedNoCopyBytes, BrdgBufTotalPacketSize(pPacket) ); ExInterlockedAddLargeStatistic( &pAdapt->ReceivedFrames, 1L ); ExInterlockedAddLargeStatistic( &pAdapt->ReceivedBytes, BrdgBufTotalPacketSize(pPacket) ); // Hand up to overlying protocols BrdgFwdIndicatePacket( pPacket, MiniportHandle ); // Fast-track succeeded. return TRUE; } // Can't fast-track. return FALSE; } // // Changes Bridging Status due to a GPO change // VOID BrdgFwdChangeBridging( IN BOOLEAN Bridging ) { // // Since we don't want to empty our tables if the settings are the same, we check // this before updating anything. If nothing has changed, we just return // if (gBridging != Bridging) { gBridging = Bridging; // Remove all MAC addresses from tables BrdgTblScrubAllAdapters(); // Remove all IP addresses from tables BrdgCompScrubAllAdapters(); if (!Bridging) { DBGPRINT(FWD, ("Bridging is now OFF.\r\n")); if (gHaveID) { BrdgSTACancelTimersGPO(); } } else { DBGPRINT(FWD, ("Bridging is now ON.\r\n")); if (gHaveID) { BrdgSTARestartTimersGPO(); BrdgSTAResetSTAInfoGPO(); } } } }