// Used on PortReturn to free the RX_PACKETs // allocated via BasePortTranslateRxPacketsToRxNBLs VOID BasePortFreeTranslatedRxNBLs( __in PMP_PORT Port, __out PNET_BUFFER_LIST NetBufferLists ) { PNET_BUFFER_LIST currentNetBufferList, nextNetBufferList; PMDL currentMdl, nextMdl; UNREFERENCED_PARAMETER(Port); currentNetBufferList = NetBufferLists; while (currentNetBufferList != NULL) { nextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList); NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList) = NULL; currentMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(currentNetBufferList)); while (currentMdl != NULL) { nextMdl = NDIS_MDL_LINKAGE(currentMdl); // Free the MDL NdisFreeMdl(currentMdl); currentMdl = nextMdl; } // Free the NET_BUFFER and NET_BUFFER_LIST NdisFreeNetBufferList(currentNetBufferList); currentNetBufferList = nextNetBufferList; } }
VOID AdapterReturnNetBufferLists( __in NDIS_HANDLE MiniportAdapterContext, __in PNET_BUFFER_LIST NetBufferLists, __in ULONG ReturnFlags ) { PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; PNET_BUFFER_LIST currentNbl, nextNbl; UNREFERENCED_PARAMETER(ReturnFlags); // // Process each NBL individually // currentNbl = NetBufferLists; while (currentNbl) { PNET_BUFFER_LIST nextNbl; nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); NET_BUFFER_LIST_NEXT_NBL(currentNbl) = NULL; // Complete write IRP and free NBL and associated resources. tapCompleteIrpAndFreeReceiveNetBufferList( adapter, currentNbl, STATUS_SUCCESS ); // Move to next NBL currentNbl = nextNbl; } }
void CParaNdisTX::Send(PNET_BUFFER_LIST NBL) { PNET_BUFFER_LIST nextNBL = nullptr; for(auto currNBL = NBL; currNBL != nullptr; currNBL = nextNBL) { nextNBL = NET_BUFFER_LIST_NEXT_NBL(currNBL); NET_BUFFER_LIST_NEXT_NBL(currNBL) = nullptr; auto NBLHolder = new (m_Context->MiniportHandle) CNBL(currNBL, m_Context, *this); if (NBLHolder == nullptr) { CNBL OnStack(currNBL, m_Context, *this); OnStack.SetStatus(NDIS_STATUS_RESOURCES); DPrintf(0, ("ERROR: Failed to allocate CNBL instance\n")); continue; } if(NBLHolder->Prepare() && ParaNdis_IsSendPossible(m_Context)) { NBLHolder->StartMapping(); } else { NBLHolder->SetStatus(ParaNdis_ExactSendFailureStatus(m_Context)); NBLHolder->Release(); } } }
/********************************************************** Required NDIS handler called at IRQL <= DISPATCH_LEVEL ***********************************************************/ static VOID ParaNdis6_SendNetBufferLists( NDIS_HANDLE miniportAdapterContext, PNET_BUFFER_LIST pNBL, NDIS_PORT_NUMBER portNumber, ULONG /* sendFlags */) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext; UNREFERENCED_PARAMETER(portNumber); #ifdef PARANDIS_SUPPORT_RSS if (pContext->RSS2QueueMap != nullptr) { while (pNBL) { ULONG RSSHashValue = NET_BUFFER_LIST_GET_HASH_VALUE(pNBL); ULONG indirectionIndex = RSSHashValue & (pContext->RSSParameters.ActiveRSSScalingSettings.RSSHashMask); PNET_BUFFER_LIST nextNBL = NET_BUFFER_LIST_NEXT_NBL(pNBL); NET_BUFFER_LIST_NEXT_NBL(pNBL) = NULL; pContext->RSS2QueueMap[indirectionIndex]->txPath.Send(pNBL); pNBL = nextNBL; } } else { pContext->pPathBundles[0].txPath.Send(pNBL); } #else pContext->pPathBundles[0].txPath.Send(pNBL); #endif }
// NDIS packet transmission completion notification procedure void SlNdisSendNetBufferListsCompleteProc(NDIS_HANDLE protocol_binding_context, NET_BUFFER_LIST *net_buffer_lists, ULONG send_complete_flags) { NET_BUFFER_LIST *nbl; nbl = net_buffer_lists; while (nbl != NULL) { NET_BUFFER_LIST *current_nbl = nbl; SL_FILE *f; NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); NdisAdvanceNetBufferDataStart(nb, size, false, NULL); } // Get a file context f = *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)); nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); NET_BUFFER_LIST_NEXT_NBL(current_nbl) = NULL; // Release the NET_BUFFER_LIST NdisFreeNetBufferList(current_nbl); // Reduce the number of packets being sent by 1 InterlockedExchangeAdd(&f->NumSendingPacketets, (LONG)-1); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, (LONG)-1); } }
/* Routine Description: MPSendPackets is called by NDIS miniport to transmit packets. Arguments: MiniportAdapterContext : pointer to Adapter object PacketArray : packets send from protocol driver NumberOfPackets : number of packets send from protocol drivers Return Value: None. IRQL: <= DISPATCH_LEVEL */ VOID MPSendNetBufferLists( IN NDIS_HANDLE MiniportAdapterContext, IN PNET_BUFFER_LIST pNetBufferList, IN NDIS_PORT_NUMBER PortNumber, IN ULONG SendFlags) { PMP_ADAPTER Adapter = (PMP_ADAPTER)MiniportAdapterContext; PNET_BUFFER_LIST pCurrentNetBufferList = pNetBufferList; PNET_BUFFER_LIST pNextNetBufferList = NULL; while(pCurrentNetBufferList) { pNextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(pCurrentNetBufferList); NET_BUFFER_LIST_NEXT_NBL(pCurrentNetBufferList) = NULL; if(!NIC_IS_DEVICE_CAN_WORK(Adapter)) { DEBUGP(MP_WARNING, ("[MPSend] Adapter can't work, drop the packet\n")); NicDropPacket(Adapter, pCurrentNetBufferList); } else { SdrLLSendPacket(&Adapter->SdrContext, pCurrentNetBufferList); //hand it over to link layer. } pCurrentNetBufferList = pNextNetBufferList; } return; }
/* * -------------------------------------------------------------------------- * OvsPartialCopyToMultipleNBLs -- * * This is similar to OvsPartialCopyNBL() except that each NB will * have its own NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsPartialCopyToMultipleNBLs(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 copySize, UINT32 headRoom, BOOLEAN copyNblInfo) { PNET_BUFFER nb, nextNb = NULL, firstNb, prevNb; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST firstNbl = NULL, newNbl, prevNbl = NULL; nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (NET_BUFFER_NEXT_NB(nb) == NULL) { return OvsPartialCopyNBL(context, nbl, copySize, headRoom, copyNblInfo); } firstNb = nb; prevNb = nb; while (nb) { nextNb = NET_BUFFER_NEXT_NB(nb); NET_BUFFER_NEXT_NB(nb) = NULL; NET_BUFFER_LIST_FIRST_NB(nbl) = nb; newNbl = OvsPartialCopyNBL(context, nbl, copySize, headRoom, copyNblInfo); if (newNbl == NULL) { goto cleanup; } if (prevNbl == NULL) { firstNbl = newNbl; } else { NET_BUFFER_LIST_NEXT_NBL(prevNbl) = newNbl; NET_BUFFER_NEXT_NB(prevNb) = nb; } prevNbl = newNbl; prevNb = nb; nb = nextNb; } NET_BUFFER_LIST_FIRST_NB(nbl) = firstNb; return firstNbl; cleanup: NET_BUFFER_NEXT_NB(prevNb) = nb; NET_BUFFER_NEXT_NB(nb) = nextNb; NET_BUFFER_LIST_FIRST_NB(nbl) = firstNb; newNbl = firstNbl; while (newNbl) { firstNbl = NET_BUFFER_LIST_NEXT_NBL(newNbl); NET_BUFFER_LIST_NEXT_NBL(newNbl) = NULL; OvsCompleteNBL(context, newNbl, TRUE); newNbl = firstNbl; } return NULL; }
VOID MPReturnNetBufferLists( NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST NetBufferLists, ULONG ReturnFlags ) { PADAPTER adapter = (PADAPTER)MiniportAdapterContext; PMP_PORT destinationPort = NULL; PNET_BUFFER_LIST currentNetBufferList = NetBufferLists, nextNetBufferList; // // We increment the port list refcount. This would avoid a pause from finishing // while we are processing this NBL and that ensures that the port list does not // change // MP_INCREMENT_PORTLIST_REFCOUNT(adapter); // // Walk the chain of netbuffer lists // while (currentNetBufferList != NULL) { // // Currently we return 1 NET_BUFFER_LIST at a time since we may be getting // back NET_BUFFER_LISTS indicated by different ports on 1 call. // This can be optimized // nextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList); NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList) = NULL; destinationPort = MP_NBL_SOURCE_PORT(currentNetBufferList); MPASSERT(destinationPort != NULL); // // Pass it to the appropriate port for processing // Port11HandleReturnNetBufferLists( destinationPort, currentNetBufferList, ReturnFlags ); currentNetBufferList = nextNetBufferList; } // // We were protecting the port list only until we hand it to the port. After this point, the port // is responsible for ensuring that it does not let the port get deleted while // it has packets pending on it // MP_DECREMENT_PORTLIST_REFCOUNT(adapter); }
/* * -------------------------------------------------------------------------- * OvsFullCopyToMultipleNBLs -- * * Copy NBL to multiple NBLs, each NB will have its own NBL * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsFullCopyToMultipleNBLs(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST firstNbl, currNbl, newNbl; PNET_BUFFER nb; POVS_BUFFER_CONTEXT srcCtx; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); if (newNbl == NULL || NET_BUFFER_NEXT_NB(nb) == NULL) { return newNbl; } else { firstNbl = newNbl; currNbl = newNbl; } while (nb) { newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); if (newNbl == NULL) { goto copymultiple_error; } NET_BUFFER_LIST_NEXT_NBL(currNbl) = newNbl; currNbl = newNbl; nb = NET_BUFFER_NEXT_NB(nb); } return firstNbl; copymultiple_error: while (firstNbl) { currNbl = firstNbl; firstNbl = NET_BUFFER_LIST_NEXT_NBL(firstNbl); NET_BUFFER_LIST_NEXT_NBL(currNbl) = NULL; OvsCompleteNBL(context, currNbl, TRUE); } return NULL; }
//NOTE: this function does almost the same work as SendIngressBasic./_DropAll_MultipleSourcesIngress(...) //(i.e. except that it calls Nbls_CompleteEgress). TODO: We must refactor. static VOID _DropMultipleSourcesEgress(_In_ const OVS_SWITCH_INFO* pSwitchInfo, _In_ NET_BUFFER_LIST* pNetBufferLists, _In_ const NDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO* pForwardDetail, NDIS_STRING filterReason, ULONG returnFlags) { NDIS_SWITCH_PORT_ID curSourcePort = pForwardDetail->SourcePortId; ULONG numNbls = 0; NET_BUFFER_LIST* pCurNbl = NULL, *pNextNbl = NULL; NET_BUFFER_LIST* pDropNbl = NULL; NET_BUFFER_LIST** ppCurDropNbl = &pDropNbl; for (pCurNbl = pNetBufferLists; pCurNbl != NULL; pCurNbl = pNextNbl) { pNextNbl = NET_BUFFER_LIST_NEXT_NBL(pCurNbl); NET_BUFFER_LIST_NEXT_NBL(pCurNbl) = NULL; pForwardDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pCurNbl); //the first time it will be true. if (curSourcePort == pForwardDetail->SourcePortId) { *ppCurDropNbl = pCurNbl; ppCurDropNbl = &(NET_BUFFER_LIST_NEXT_NBL(pCurNbl)); ++numNbls; } //if source port of pNetBufferLists != source port of pCurNbl => drop? else { OVS_CHECK(pDropNbl); OVS_CHECK(numNbls > 0); pSwitchInfo->switchHandlers.ReportFilteredNetBufferLists(pSwitchInfo->switchContext, &g_extensionGuid, &g_extensionFriendlyName, curSourcePort, NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, numNbls, pDropNbl, &filterReason); /*fwd ext here*/ Nbls_CompleteEgress(pSwitchInfo, pSwitchInfo->pForwardInfo, pDropNbl, returnFlags); numNbls = 1; pDropNbl = pCurNbl; ppCurDropNbl = &(NET_BUFFER_LIST_NEXT_NBL(pCurNbl)); curSourcePort = pForwardDetail->SourcePortId; } } //either way drop? (if paused) pSwitchInfo->switchHandlers.ReportFilteredNetBufferLists(pSwitchInfo->switchContext, &g_extensionGuid, &g_extensionFriendlyName, curSourcePort, NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, numNbls, pDropNbl, &filterReason); Nbls_CompleteEgress(pSwitchInfo, pSwitchInfo->pForwardInfo, pDropNbl, returnFlags); }
VOID BasePortHandleReturnNetBufferLists( __in PMP_PORT Port, __in PNET_BUFFER_LIST NetBufferLists, __in ULONG ReturnFlags ) { PNET_BUFFER_LIST currentNetBufferList; PMP_RX_MSDU packetListToReturn = NULL, currentPacket; // Create a RX_PACKET chain to return to the next currentNetBufferList = NetBufferLists; while (currentNetBufferList != NULL) { currentPacket = MP_NBL_WRAPPED_RX_MSDU(currentNetBufferList); // We return out of order, but it does not matter if (packetListToReturn == NULL) { packetListToReturn = currentPacket; // // On an indicate we may not have cleared the next pointers // To ensure that we are only going to return one at a time, clear it now // MP_RX_MSDU_NEXT_MSDU(currentPacket) = NULL; } else { MP_RX_MSDU_NEXT_MSDU(currentPacket) = packetListToReturn; packetListToReturn = currentPacket; } // // Decrement the counter for the number of packets we have submitted // to the OS. This would enable us to unblock the port // PORT_DECREMENT_PNP_REFCOUNT(Port); currentNetBufferList = NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList); } // Free the NET_BUFFER_LIST chain BasePortFreeTranslatedRxNBLs(Port, NetBufferLists); // // Inform the port about the packets that have been returned // Port11NotifyReturn(Port, packetListToReturn, ReturnFlags); // Return the packets back to the driver VNic11ReturnPackets(Port->VNic, packetListToReturn, ReturnFlags ); }
/********************************************************** NDIS procedure of returning us buffer of previously indicated packets Parameters: context PNET_BUFFER_LIST pNBL - list of buffers to free returnFlags - is dpc The procedure frees: received buffer descriptors back to list of RX buffers all the allocated MDL structures all the received NBLs back to our pool ***********************************************************/ VOID ParaNdis6_ReturnNetBufferLists( NDIS_HANDLE miniportAdapterContext, PNET_BUFFER_LIST pNBL, ULONG returnFlags) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext; UNREFERENCED_PARAMETER(returnFlags); DEBUG_ENTRY(5); while (pNBL) { PNET_BUFFER_LIST pTemp = pNBL; pRxNetDescriptor pBuffersDescriptor = (pRxNetDescriptor)pNBL->MiniportReserved[0]; DPrintf(3, (" Returned NBL of pBuffersDescriptor %p!\n", pBuffersDescriptor)); pNBL = NET_BUFFER_LIST_NEXT_NBL(pNBL); NET_BUFFER_LIST_NEXT_NBL(pTemp) = NULL; NdisFreeNetBufferList(pTemp); pBuffersDescriptor->Queue->ReuseReceiveBuffer(pBuffersDescriptor); } ParaNdis_TestPausing(pContext); }
void CParaNdisTX::Send(PNET_BUFFER_LIST NBL) { PNET_BUFFER_LIST nextNBL = nullptr; NDIS_STATUS RejectionStatus = NDIS_STATUS_FAILURE; if (!m_StateMachine.RegisterOutstandingItems(ParaNdis_CountNBLs(NBL), &RejectionStatus)) { ParaNdis_CompleteNBLChainWithStatus(m_Context->MiniportHandle, NBL, RejectionStatus); return; } for(auto currNBL = NBL; currNBL != nullptr; currNBL = nextNBL) { nextNBL = NET_BUFFER_LIST_NEXT_NBL(currNBL); NET_BUFFER_LIST_NEXT_NBL(currNBL) = nullptr; auto NBLHolder = new (m_Context->MiniportHandle) CNBL(currNBL, m_Context, *this); if (NBLHolder == nullptr) { currNBL->Status = NDIS_STATUS_RESOURCES; CompleteOutstandingNBLChain(currNBL); DPrintf(0, ("ERROR: Failed to allocate CNBL instance\n")); continue; } if(NBLHolder->Prepare() && ParaNdis_IsSendPossible(m_Context)) { NBLHolder->StartMapping(); } else { NBLHolder->SetStatus(ParaNdis_ExactSendFailureStatus(m_Context)); NBLHolder->Release(); } } }
VOID ParaNdis_IndicateReceivedBatch( PARANDIS_ADAPTER *pContext, tPacketIndicationType *pBatch, ULONG nofPackets) { ULONG i; PNET_BUFFER_LIST pPrev = pBatch[0]; NET_BUFFER_LIST_NEXT_NBL(pPrev) = NULL; for (i = 1; i < nofPackets; ++i) { PNET_BUFFER_LIST pNBL = pBatch[i]; NET_BUFFER_LIST_NEXT_NBL(pPrev) = pNBL; NET_BUFFER_LIST_NEXT_NBL(pNBL) = NULL; pPrev = pNBL; } NdisMIndicateReceiveNetBufferLists( pContext->MiniportHandle, pBatch[0], 0, nofPackets, 0); }
CNBL::~CNBL() { CDpcIrqlRaiser OnDpc; m_MappedBuffers.ForEachDetached([this](CNB *NB) { CNB::Destroy(NB, m_Context->MiniportHandle); }); m_Buffers.ForEachDetached([this](CNB *NB) { CNB::Destroy(NB, m_Context->MiniportHandle); }); if(m_NBL) { auto NBL = DetachInternalObject(); NETKVM_ASSERT(NET_BUFFER_LIST_NEXT_NBL(NBL) == nullptr); m_ParentTXPath->CompleteOutstandingNBLChain(NBL); } }
PNET_BUFFER_LIST CParaNdisTX::BuildCancelList(PVOID CancelId) { PNET_BUFFER_LIST CanceledNBLs = nullptr; TSpinLocker LockedContext(m_Lock); m_SendList.ForEachDetachedIf([CancelId](CNBL* NBL){ return NBL->MatchCancelID(CancelId) && !NBL->HaveDetachedBuffers(); }, [this, &CanceledNBLs](CNBL* NBL) { NBL->SetStatus(NDIS_STATUS_SEND_ABORTED); auto RawNBL = NBL->DetachInternalObject(); NBL->Release(); NET_BUFFER_LIST_NEXT_NBL(RawNBL) = CanceledNBLs; CanceledNBLs = RawNBL; }); return CanceledNBLs; }
PNET_BUFFER_LIST CParaNdisTX::ProcessWaitingList() { PNET_BUFFER_LIST CompletedNBLs = nullptr; m_WaitingList.ForEachDetachedIf([](CNBL* NBL) { return NBL->IsSendDone(); }, [&](CNBL* NBL) { NBL->SetStatus(NDIS_STATUS_SUCCESS); auto RawNBL = NBL->DetachInternalObject(); NBL->Release(); NET_BUFFER_LIST_NEXT_NBL(RawNBL) = CompletedNBLs; CompletedNBLs = RawNBL; }); return CompletedNBLs; }
CNBL::~CNBL() { CDpcIrqlRaiser OnDpc; m_MappedBuffers.ForEachDetached([this](CNB *NB) { CNB::Destroy(NB, m_Context->MiniportHandle); }); m_Buffers.ForEachDetached([this](CNB *NB) { CNB::Destroy(NB, m_Context->MiniportHandle); }); if(m_NBL) { auto NBL = DetachInternalObject(); NET_BUFFER_LIST_NEXT_NBL(NBL) = nullptr; NdisMSendNetBufferListsComplete(m_Context->MiniportHandle, NBL, 0); } }
VOID TXNblRelease( _In_ PMP_ADAPTER Adapter, _In_ PNET_BUFFER_LIST NetBufferList, _In_ BOOLEAN fAtDispatch) /*++ Routine Description: Releases a reference on a NBL that is being transmitted. If the last reference is released, the NBL is returned to the protocol. Runs at IRQL <= DISPATCH_LEVEL. Arguments: Adapter Pointer to our adapter NetBufferList The NBL to release fAtDispatch TRUE if the current IRQL is DISPATCH_LEVEL Return Value: None. --*/ { if (0 == NdisInterlockedDecrement(&SEND_REF_FROM_NBL(NetBufferList))) { DEBUGP(MP_TRACE, "[%p] Send NBL %p complete.\n", Adapter, NetBufferList); NET_BUFFER_LIST_NEXT_NBL(NetBufferList) = NULL; NdisMSendNetBufferListsComplete( Adapter->AdapterHandle, NetBufferList, fAtDispatch ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL:0); } else { DEBUGP(MP_TRACE, "[%p] Send NBL %p not complete. RefCount: %i.\n", Adapter, NetBufferList, SEND_REF_FROM_NBL(NetBufferList)); } NdisInterlockedDecrement(&Adapter->nBusySend); }
VOID MPReturnNetBufferLists( _In_ NDIS_HANDLE MiniportAdapterContext, _In_ PNET_BUFFER_LIST NetBufferLists, _In_ ULONG ReturnFlags) /*++ Routine Description: NDIS Miniport entry point called whenever protocols are done with one or NBLs that we indicated up with NdisMIndicateReceiveNetBufferLists. Note that the list of NBLs may be chained together from multiple separate lists that were indicated up individually. Arguments: MiniportAdapterContext Pointer to our adapter NetBufferLists NBLs being returned ReturnFlags May contain the NDIS_RETURN_FLAGS_DISPATCH_LEVEL flag, which if is set, indicates we can get a small perf win by not checking or raising the IRQL Return Value: None. --*/ { PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); UNREFERENCED_PARAMETER(ReturnFlags); DEBUGP(MP_TRACE, "[%p] ---> MPReturnNetBufferLists\n", Adapter); while (NetBufferLists) { PRCB Rcb = RCB_FROM_NBL(NetBufferLists); ReturnRCB(Adapter, Rcb); NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists); } DEBUGP(MP_TRACE, "[%p] <--- MPReturnNetBufferLists\n", Adapter); }
//TODO: Needs review PNET_BUFFER_LIST CParaNdisTX::RemoveAllNonWaitingNBLs() { PNET_BUFFER_LIST RemovedNBLs = nullptr; auto status = ParaNdis_ExactSendFailureStatus(m_Context); m_SendList.ForEachDetachedIf([](CNBL *NBL) { return !NBL->HaveDetachedBuffers(); }, [&](CNBL *NBL) { NBL->SetStatus(status); auto RawNBL = NBL->DetachInternalObject(); NBL->Release(); NET_BUFFER_LIST_NEXT_NBL(RawNBL) = RemovedNBLs; RemovedNBLs = RawNBL; }); m_SendList.ForEach([](CNBL *NBL) { NBL->CompleteMappedBuffers(); }); return RemovedNBLs; }
_IRQL_requires_same_ UINT32 KrnlHlprNBLGetRequiredRefCount(_In_ const NET_BUFFER_LIST* pNBL, _In_ BOOLEAN isChained) /* FALSE */ { #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " ---> KrnlHlprNBLGetRequiredRefCount()\n"); #endif /// DBG NT_ASSERT(pNBL); UINT32 requiredRefCount = 0; if(isChained) { for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pNBL; pCurrentNBL; pCurrentNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL)) { requiredRefCount++; } } else requiredRefCount = 1; #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " <--- KrnlHlprNBLGetRequiredRefCount() [refCount: %#d]\n", requiredRefCount); #endif /// DBG return requiredRefCount; }
NDIS_STATUS BasePortTranslateRxPacketsToRxNBLs( __in PMP_PORT Port, __in PMP_RX_MSDU PacketList, __out PNET_BUFFER_LIST* NetBufferLists ) { PNET_BUFFER_LIST outputNetBufferList = NULL; PNET_BUFFER_LIST currentNetBufferList, prevNetBufferList = NULL; USHORT fragmentIndex = 0; PMP_RX_MSDU currentPacket = PacketList, nextPacket; PMP_RX_MPDU currentFragment; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PDOT11_EXTSTA_RECV_CONTEXT osRecvContext; // This is same for ExtAP & ExtSTA PMDL currentMdl, prevMdl = NULL, mdlChain = NULL; ULONG totalLength = 0; *NetBufferLists = NULL; // Convert each PACKET and FRAGMENT to OS structures while (currentPacket != NULL) { nextPacket = MP_RX_MSDU_NEXT_MSDU(currentPacket); // Go through the FRAGMENTs in this PACKET & create an MDL chain mdlChain = NULL; totalLength = 0; for (fragmentIndex = 0; fragmentIndex < MP_RX_MSDU_MPDU_COUNT(currentPacket); fragmentIndex++) { currentFragment = MP_RX_MSDU_MPDU_AT(currentPacket, fragmentIndex); totalLength += MP_RX_MPDU_LENGTH(currentFragment); // Populate the MDL with Fragment contents currentMdl = NdisAllocateMdl(Port->MiniportAdapterHandle, MP_RX_MPDU_DATA(currentFragment), MP_RX_MPDU_LENGTH(currentFragment) ); if (currentMdl == NULL) { MpTrace(COMP_RECV, DBG_SERIOUS, ("Failed to allocate MDL for a MP_RX_MPDU data\n")); ndisStatus = NDIS_STATUS_RESOURCES; break; } // Add this to the MDL chain if (mdlChain == NULL) { // Add this to the head mdlChain = currentMdl; } else { NDIS_MDL_LINKAGE(prevMdl) = currentMdl; } prevMdl = currentMdl; } if (ndisStatus != NDIS_STATUS_SUCCESS) { while (mdlChain != NULL) { currentMdl = mdlChain; mdlChain = NDIS_MDL_LINKAGE(currentMdl); // MDL was allocated NdisFreeMdl(currentMdl); } break; } // Allocate the NET_BUFFER_LIST and the NET_BUFFER currentNetBufferList = NdisAllocateNetBufferAndNetBufferList( Port->RxNetBufferListPool, 0, 0, mdlChain, 0, totalLength ); if (currentNetBufferList == NULL) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate NET_BUFFER_LIST for MP_RX_MSDU \n")); ndisStatus = NDIS_STATUS_RESOURCES; break; } // Populate the RX_PACKET MP_NBL_WRAPPED_RX_MSDU(currentNetBufferList) = currentPacket; MP_NBL_SOURCE_PORT(currentNetBufferList) = Port; if (outputNetBufferList == NULL) { outputNetBufferList = currentNetBufferList; } else { NET_BUFFER_LIST_NEXT_NBL(prevNetBufferList) = currentNetBufferList; } // The Next PACKET's NBL would be added after the current PACKET's NBL prevNetBufferList = currentNetBufferList; osRecvContext = MP_RX_MSDU_RECV_CONTEXT(currentPacket); MP_ASSIGN_NDIS_OBJECT_HEADER(osRecvContext->Header, NDIS_OBJECT_TYPE_DEFAULT, DOT11_EXTSTA_RECV_CONTEXT_REVISION_1, sizeof(DOT11_EXTSTA_RECV_CONTEXT)); MP_SET_RECEIVE_CONTEXT(currentNetBufferList, osRecvContext); currentPacket = nextPacket; } if (ndisStatus != NDIS_STATUS_SUCCESS) { if (outputNetBufferList != NULL) { BasePortFreeTranslatedRxNBLs(Port, outputNetBufferList); outputNetBufferList = NULL; } } *NetBufferLists = outputNetBufferList; return ndisStatus; }
VOID NdisprotSendComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNET_BUFFER_LIST pNetBufferList, IN ULONG SendCompleteFlags ) /*++ Routine Description: NDIS entry point called to signify completion of a packet send. We pick up and complete the Write IRP corresponding to this packet. Arguments: ProtocolBindingContext - pointer to open context pNetBufferList - NetBufferList that completed send SendCompleteFlags - Specifies if the caller is at DISPATCH level Return Value: None --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; PNET_BUFFER_LIST CurrNetBufferList = NULL; PNET_BUFFER_LIST NextNetBufferList; NDIS_STATUS CompletionStatus; BOOLEAN DispatchLevel; WDFREQUEST request; PREQUEST_CONTEXT reqContext; pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; NPROT_STRUCT_ASSERT(pOpenContext, oc); DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendCompleteFlags); for (CurrNetBufferList = pNetBufferList; CurrNetBufferList != NULL; CurrNetBufferList = NextNetBufferList) { NextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(CurrNetBufferList); request = NPROT_REQUEST_FROM_SEND_NBL(CurrNetBufferList); reqContext = GetRequestContext(request); CompletionStatus = NET_BUFFER_LIST_STATUS(CurrNetBufferList); DEBUGP(DL_INFO, ("SendComplete: NetBufferList %p/IRP %p/Length %d " "completed with status %x\n", CurrNetBufferList, request, reqContext->Length, CompletionStatus)); // // We are done with the NDIS_PACKET: // NPROT_DEREF_SEND_NBL(CurrNetBufferList, DispatchLevel); CurrNetBufferList = NULL; if (CompletionStatus == NDIS_STATUS_SUCCESS) { WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, reqContext->Length); } else { WdfRequestCompleteWithInformation(request, STATUS_UNSUCCESSFUL, 0); } NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); pOpenContext->PendedSendCount--; if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) && (pOpenContext->PendedSendCount == 0)) { NPROT_ASSERT(pOpenContext->ClosingEvent != NULL); NPROT_SIGNAL_EVENT(pOpenContext->ClosingEvent); pOpenContext->ClosingEvent = NULL; } NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); NPROT_DEREF_OPEN(pOpenContext); // send complete - dequeued send IRP } }
// Process the received packet void NeoWrite(void *buf) { UINT num, i, size; UCHAR *packet_buf; NET_BUFFER_LIST *nbl_chain = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_nbl_chain = 0; // Validate arguments if (buf == NULL) { return; } // Number of packets num = NEO_NUM_PACKET(buf); if (num > NEO_MAX_PACKET_EXCHANGE) { // Number of packets is too many return; } if (num == 0) { // No packet return; } if (ctx->Halting != FALSE) { // Stopping return; } if (ctx->Paused) { // Paused return; } if (ctx->Opened == FALSE) { // Not connected return; } for (i = 0;i < num;i++) { PACKET_BUFFER *p = ctx->PacketBuffer[i]; void *dst; NET_BUFFER_LIST *nbl = ctx->PacketBuffer[i]->NetBufferList; NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); nbl->SourceHandle = ctx->NdisMiniport; NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; size = NEO_SIZE_OF_PACKET(buf, i); if (size > NEO_MAX_PACKET_SIZE) { size = NEO_MAX_PACKET_SIZE; } if (size < NEO_PACKET_HEADER_SIZE) { size = NEO_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)(NEO_ADDR_OF_PACKET(buf, i)); if (OK(NdisRetreatNetBufferDataStart(nb, size, 0, NULL))) { // Buffer copy dst = NdisGetDataBuffer(nb, size, NULL, 1, 0); if (dst != NULL) { NeoCopy(dst, packet_buf, size); if (nbl_chain == NULL) { nbl_chain = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; num_nbl_chain++; } } nbl->Status = NDIS_STATUS_RESOURCES; ctx->Status.Int64BytesRecvTotal += (UINT64)size; if (packet_buf[0] & 0x40) { ctx->Status.Int64NumRecvBroadcast++; ctx->Status.Int64BytesRecvBroadcast += (UINT64)size; } else { ctx->Status.Int64NumRecvUnicast++; ctx->Status.Int64BytesRecvUnicast += (UINT64)size; } } if (nbl_chain == NULL) { return; } // Notify that it has received ctx->Status.NumPacketRecv += num_nbl_chain; NdisMIndicateReceiveNetBufferLists(ctx->NdisMiniport, nbl_chain, 0, num_nbl_chain, NDIS_RECEIVE_FLAGS_RESOURCES); if (true) { // Restore the packet buffer NET_BUFFER_LIST *nbl = nbl_chain; while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); NdisAdvanceNetBufferDataStart(nb, size, false, NULL); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } } }
VOID NdisprotReceiveNetBufferLists( IN NDIS_HANDLE ProtocolBindingContext, IN PNET_BUFFER_LIST pNetBufferLists, IN NDIS_PORT_NUMBER PortNumber, IN ULONG NumberOfNetBufferLists, IN ULONG ReceiveFlags ) /*++ Routine Description: Protocol entry point called by NDIS if the driver below uses NDIS 6 net buffer list indications. If the miniport allows us to hold on to this net buffer list, we use it as is, otherwise we make a copy. Arguments: ProtocolBindingContext - pointer to open context pNetBufferLists - a list of the Net Buffer lists being indicated up. PortNumber - Port on which the Net Bufer list was received NumberOfNetBufferLists - the number of NetBufferLists in this indication ReceiveFlags - indicates whether the NetBufferLists can be pended in the protocol driver. Return Value: --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; PMDL pMdl = NULL; UINT BufferLength; PNDISPROT_ETH_HEADER pEthHeader = NULL; PNET_BUFFER_LIST pCopyNetBufList; PUCHAR pCopyBuf; ULONG TotalLength; ULONG BytesCopied; PNET_BUFFER_LIST pNetBufList; PNET_BUFFER_LIST pNetBufListOrig = NULL; PNET_BUFFER_LIST pNextNetBufList; PNET_BUFFER_LIST pReturnNetBufList = NULL; PNET_BUFFER_LIST pLastReturnNetBufList = NULL; NTSTATUS NtStatus; BOOLEAN bAcceptedReceive; ULONG Offset; ULONG ReturnFlags = 0; BOOLEAN DispatchLevel; BOOLEAN NoReadIRP = FALSE; UNREFERENCED_PARAMETER(PortNumber); UNREFERENCED_PARAMETER(NumberOfNetBufferLists); pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags)) { NDIS_SET_RETURN_FLAG(ReturnFlags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); } NPROT_STRUCT_ASSERT(pOpenContext, oc); if ((pOpenContext->State == NdisprotPausing) || (pOpenContext->State == NdisprotPaused)) { if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE) { NdisReturnNetBufferLists(pOpenContext->BindingHandle, pNetBufferLists, ReturnFlags); } return; } pNetBufList = pNetBufferLists; while (pNetBufList != NULL) { pNextNetBufList = NET_BUFFER_LIST_NEXT_NBL (pNetBufList); NBL_CLEAR_PROT_RSVD_FLAG(pNetBufList, NBL_PROT_RSVD_FLAGS); bAcceptedReceive = FALSE; // // Get first MDL and data length in the list // pMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); TotalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); Offset = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(pNetBufList)); BufferLength = 0; do { ASSERT(pMdl != NULL); if (pMdl) { NdisQueryMdl( pMdl, &pEthHeader, &BufferLength, NormalPagePriority); } if (pEthHeader == NULL) { // // The system is low on resources. Set up to handle failure // below. // BufferLength = 0; break; } if (BufferLength == 0) { break; } ASSERT(BufferLength > Offset); BufferLength -= Offset; pEthHeader = (PNDISPROT_ETH_HEADER)((PUCHAR)pEthHeader + Offset); if (BufferLength < sizeof(NDISPROT_ETH_HEADER)) { DEBUGP(DL_WARN, ("ReceiveNetBufferList: Open %p, runt nbl %p, first buffer length %d\n", pOpenContext, pNetBufList, BufferLength)); break; } // // Check the EtherType. If the Ether type indicates presence of // a tag, then the "real" Ether type is 4 bytes further down. // if (pEthHeader->EthType == NPROT_8021P_TAG_TYPE) { USHORT UNALIGNED *pEthType; if (BufferLength < (sizeof(NDISPROT_ETH_HEADER) + 4)) { break; } pEthType = (USHORT UNALIGNED *)((PUCHAR)&pEthHeader->EthType + 4); if (*pEthType != Globals.EthType) { break; } } else if (pEthHeader->EthType != Globals.EthType) { break; } bAcceptedReceive = TRUE; DEBUGP(DL_LOUD, ("ReceiveNetBufferList: Open %p, interesting nbl %p\n", pOpenContext, pNetBufList)); // // If the miniport is out of resources, we can't queue // this list of net buffer list - make a copy if this is so. // DispatchLevel = NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags); NoReadIRP = NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads); if (NoReadIRP || NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags)) { bAcceptedReceive = FALSE; pCopyNetBufList = ndisprotAllocateReceiveNetBufferList( pOpenContext, TotalLength, &pCopyBuf); if (pCopyNetBufList == NULL) { DEBUGP(DL_FATAL, ("ReceiveNetBufferList: Open %p, failed to" " alloc copy, %d bytes\n", pOpenContext, TotalLength)); break; } NBL_SET_PROT_RSVD_FLAG(pCopyNetBufList, NPROT_ALLOCATED_NBL); // // Copy the data to the new allocated NetBufferList // NtStatus = NdisCopyFromNetBufferToNetBuffer(NET_BUFFER_LIST_FIRST_NB(pCopyNetBufList), 0, TotalLength, NET_BUFFER_LIST_FIRST_NB(pNetBufList), 0, &BytesCopied); if (NtStatus != STATUS_SUCCESS) { DEBUGP(DL_FATAL, ("ReceiveNetBufferList: Open %p, failed to" " copy the data, %d bytes\n", pOpenContext, TotalLength)); // // Free the NetBufferList and memory allocate before // ndisprotFreeReceiveNetBufferList(pOpenContext, pCopyNetBufList, DispatchLevel); break; } NPROT_ASSERT(BytesCopied == TotalLength); // // The other members of NET_BUFFER_DATA structure are already initialized properly during allocation. // NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pCopyNetBufList)) = BytesCopied; // //save a copy for no Read IRP case // if(NoReadIRP) { pNetBufListOrig = pNetBufList; } pNetBufList = pCopyNetBufList; } // // Queue this up and service any pending Read IRPs. // ndisprotQueueReceiveNetBufferList(pOpenContext, pNetBufList, DispatchLevel); } while (FALSE); // // Ndisprot is not interested this NetBufferList, return the // NetBufferList back to the miniport if the miniport gave us // ownership of it // if ((bAcceptedReceive == FALSE) && (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags) == TRUE)) { // Restore pNetBufList if it was overwritten earlier if (pNetBufListOrig != NULL) { pNetBufList = pNetBufListOrig; pNetBufListOrig = NULL; } if (pReturnNetBufList == NULL) { pReturnNetBufList = pNetBufList; } else { NET_BUFFER_LIST_NEXT_NBL(pLastReturnNetBufList) = pNetBufList; } pLastReturnNetBufList = pNetBufList; NET_BUFFER_LIST_NEXT_NBL(pNetBufList) = NULL; } pNetBufList = pNextNetBufList; } // end of the for loop if (pReturnNetBufList != NULL) { NdisReturnNetBufferLists(pOpenContext->BindingHandle, pReturnNetBufList, ReturnFlags); } }
_IRQL_requires_same_ inline VOID KrnlHlprClassifyDataReleaseLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData) { #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " ---> KrnlHlprClassifyDataReleaseLocalCopy()\n"); #endif /// DBG NT_ASSERT(pClassifyData); KrnlHlprFwpsClassifyOutDestroyLocalCopy((FWPS_CLASSIFY_OUT**)&(pClassifyData->pClassifyOut)); pClassifyData->flowContext = 0; KrnlHlprFwpsFilterDestroyLocalCopy((FWPS_FILTER**)&(pClassifyData->pFilter)); #if(NTDDI_VERSION >= NTDDI_WIN7) if(pClassifyData->classifyContextHandle) FwpsReleaseClassifyHandle(pClassifyData->classifyContextHandle); #endif /// (NTDDI_VERSION >= NTDDI_WIN7) if(pClassifyData->pPacket) { if(pClassifyData->pClassifyValues) { if(pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 || pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD) KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy((FWPS_STREAM_CALLOUT_IO_PACKET**)&(pClassifyData->pPacket)); else if(pClassifyData->chainedNBL) { BOOLEAN isDispatch = (KeGetCurrentIrql() == DISPATCH_LEVEL) ? TRUE : FALSE; UINT32 numChainedNBLs = pClassifyData->numChainedNBLs; for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pClassifyData->pPacket; pCurrentNBL && numChainedNBLs; numChainedNBLs--) { NET_BUFFER_LIST* pNextNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL); FwpsDereferenceNetBufferList(pCurrentNBL, isDispatch); pCurrentNBL = pNextNBL; #if DBG InterlockedDecrement64((LONG64*)&(g_OutstandingNBLReferences)); #endif /// DBG } } else { BOOLEAN isDispatch = (KeGetCurrentIrql() == DISPATCH_LEVEL) ? TRUE : FALSE; FwpsDereferenceNetBufferList((NET_BUFFER_LIST*)pClassifyData->pPacket, isDispatch); #if DBG InterlockedDecrement64((LONG64*)&(g_OutstandingNBLReferences)); #endif /// DBG } } pClassifyData->pPacket = 0; } KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy((FWPS_INCOMING_METADATA_VALUES**)&(pClassifyData->pMetadataValues)); if(pClassifyData->pClassifyValues) KrnlHlprFwpsIncomingValuesDestroyLocalCopy((FWPS_INCOMING_VALUES**)&(pClassifyData->pClassifyValues)); RtlZeroMemory(pClassifyData, sizeof(CLASSIFY_DATA)); #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " <--- KrnlHlprClassifyDataReleaseLocalCopy()\n"); #endif /// DBG return; }
VOID MPSendNetBufferLists( NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST NetBufferLists, NDIS_PORT_NUMBER PortNumber, ULONG SendFlags ) { PADAPTER adapter = (PADAPTER)MiniportAdapterContext; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PMP_PORT destinationPort = NULL; PNET_BUFFER_LIST currentNetBufferList; // // We increment the port list refcount. This would avoid a pause from finishing // while we are processing this NBL and that ensures that the port list does not // change // MP_INCREMENT_PORTLIST_REFCOUNT(adapter); do { // // If the adapter is paused, surprise removed, etc, fail the send // if (MP_TEST_ADAPTER_STATUS(adapter, MP_ADAPTER_CANNOT_SEND_MASK)) { ndisStatus = MpGetAdapterStatus(adapter); MpTrace(COMP_SEND, DBG_NORMAL, ("Sends failed as adapter is not in a valid send state\n")); break; } // // First we would need to translate from the NDIS_PORT_NUMBER // to our port structure. This is done by walking the PortList // destinationPort = Port11TranslatePortNumberToPort( adapter, PortNumber ); if (destinationPort == NULL) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Unable to find Port corresponding to PortNumber %d\n", PortNumber)); ndisStatus = NDIS_STATUS_INVALID_PORT; } else { // // Pass it to the appropriate port for processing // Port11HandleSendNetBufferLists( destinationPort, NetBufferLists, SendFlags ); } } while (FALSE); // // We were protecting the port list only until we hand it to the port. After this point, the port // is responsible for ensuring that it does not let the port get deleted while // it has packets pending on it // MP_DECREMENT_PORTLIST_REFCOUNT(adapter); if (ndisStatus != NDIS_STATUS_SUCCESS) { #if DBG ULONG ulNumFailedNBLs = 0; #endif // // Send failed. Complete the NBLs back to NDIS // for(currentNetBufferList = NetBufferLists; currentNetBufferList != NULL; currentNetBufferList = NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList)) { #if DBG ulNumFailedNBLs++; #endif NET_BUFFER_LIST_STATUS(currentNetBufferList) = ndisStatus; } #if DBG MpTrace(COMP_SEND, DBG_NORMAL, ("NdisMSendNetBufferListsComplete called with %d NBLs\n", ulNumFailedNBLs)); #endif if (NetBufferLists != NULL) { NdisMSendNetBufferListsComplete( adapter->MiniportAdapterHandle, NetBufferLists, (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags) ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0) ); } } }
NTSTATUS KrnlHlprClassifyDataAcquireLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData, _In_ const FWPS_INCOMING_VALUES* pClassifyValues, _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, _In_opt_ VOID* pPacket, _In_opt_ const VOID* pClassifyContext, _In_ const FWPS_FILTER* pFilter, _In_ const UINT64 flowContext, _In_ FWPS_CLASSIFY_OUT* pClassifyOut) { #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " ---> KrnlHlprClassifyDataAcquireLocalCopy()\n"); #endif /// DBG NT_ASSERT(pClassifyData); NT_ASSERT(pClassifyValues); NT_ASSERT(pMetadata); NT_ASSERT(pFilter); NT_ASSERT(pClassifyOut); NTSTATUS status = STATUS_SUCCESS; pClassifyData->pClassifyValues = KrnlHlprFwpsIncomingValuesCreateLocalCopy(pClassifyValues); HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pClassifyValues, status); pClassifyData->pMetadataValues = KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy(pMetadata); HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pMetadataValues, status); if(pPacket) { if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 || pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD) { pClassifyData->pPacket = KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy((FWPS_STREAM_CALLOUT_IO_PACKET*)pPacket); HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pPacket, status); } #if(NTDDI_VERSION >= NTDDI_WIN7) /// LayerData at the FWPM_LAYER_ALE_{BIND/CONNECT}_REDIRECT_V{4/6} is obtained via KrnlHlprRedirectDataCreate() else if(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 || pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6) { pClassifyData->pPacket = 0; } #endif /// (NTDDI_VERSION >= NTDDI_WIN7) else { if(NET_BUFFER_LIST_NEXT_NBL((NET_BUFFER_LIST*)pPacket)) { pClassifyData->chainedNBL = TRUE; pClassifyData->numChainedNBLs = 1; } if(pClassifyData->chainedNBL && ( /// The IPPACKET and IPFORWARD Layers allow for Fragment Grouping if the option is enabled pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 #if(NTDDI_VERSION >= NTDDI_WIN8) /// The NDIS layers allow for batched NBLs provided the callout was registered with FWP_CALLOUT_FLAG_ALLOW_L2_BATCH_CLASSIFY set || pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET #endif /// (NTDDI_VERSION >= NTDDI_WIN8) )) { for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pPacket; pCurrentNBL; pClassifyData->numChainedNBLs++) { NET_BUFFER_LIST* pNextNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL); FwpsReferenceNetBufferList(pCurrentNBL, TRUE); pCurrentNBL = pNextNBL; #if DBG InterlockedIncrement64((LONG64*)&(g_OutstandingNBLReferences)); #endif /// DBG } pClassifyData->pPacket = pPacket; } else { /// Otherwise we expect to receive a single NBL NT_ASSERT(NET_BUFFER_LIST_NEXT_NBL((NET_BUFFER_LIST*)pPacket) == 0); FwpsReferenceNetBufferList((NET_BUFFER_LIST*)pPacket, TRUE); pClassifyData->pPacket = pPacket; #if DBG InterlockedIncrement64((LONG64*)&(g_OutstandingNBLReferences)); #endif /// DBG } } } #if(NTDDI_VERSION >= NTDDI_WIN7) if(pClassifyContext) { /// ClassifyHandle for these layers is obtained in REDIRECT_DATA if(pClassifyValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 && pClassifyValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 && pClassifyValues->layerId != FWPS_LAYER_ALE_BIND_REDIRECT_V4 && pClassifyValues->layerId != FWPS_LAYER_ALE_BIND_REDIRECT_V6) { status = FwpsAcquireClassifyHandle((VOID*)pClassifyContext, 0, &(pClassifyData->classifyContextHandle)); HLPR_BAIL_ON_FAILURE(status); } } #else UNREFERENCED_PARAMETER(pClassifyContext); #endif /// (NTDDI_VERSION >= NTDDI_WIN7) if(pFilter) { pClassifyData->pFilter = KrnlHlprFwpsFilterCreateLocalCopy(pFilter); HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pFilter, status); } pClassifyData->flowContext = flowContext; if(pClassifyOut) { pClassifyData->pClassifyOut = KrnlHlprFwpsClassifyOutCreateLocalCopy(pClassifyOut); HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pClassifyOut, status); } HLPR_BAIL_LABEL: if(status != STATUS_SUCCESS) KrnlHlprClassifyDataReleaseLocalCopy(pClassifyData); #if DBG DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_INFO_LEVEL, " <--- KrnlHlprClassifyDataAcquireLocalCopy() [status: %#x]\n", status); #endif /// DBG return status; }
VOID IndicateReceivePacket( __in PTAP_ADAPTER_CONTEXT Adapter, __in PUCHAR packetData, __in const unsigned int packetLength ) { PUCHAR injectBuffer; // // Handle miniport Pause // --------------------- // NDIS 6 miniports implement a temporary "Pause" state normally followed // by the Restart. While in the Pause state it is forbidden for the miniport // to indicate receive NBLs. // // That is: The device interface may be "up", but the NDIS miniport send/receive // interface may be temporarily "down". // // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas inject path // the code below will simply ignore inject packets passed to the driver while // the miniport is in the Paused state. // // The correct implementation is to go ahead and build the NBLs corresponding // to the inject packet - but queue them. When Restart is entered the // queued NBLs would be dequeued and indicated to the host. // if(tapAdapterSendAndReceiveReady(Adapter) != NDIS_STATUS_SUCCESS) { DEBUGP (("[%s] Lying send in IndicateReceivePacket while adapter paused\n", MINIPORT_INSTANCE_ID (Adapter))); return; } // Allocate flat buffer for packet data. injectBuffer = (PUCHAR )NdisAllocateMemoryWithTagPriority( Adapter->MiniportAdapterHandle, packetLength, TAP_RX_INJECT_BUFFER_TAG, NormalPoolPriority ); if( injectBuffer) { PMDL mdl; // Copy packet data to flat buffer. NdisMoveMemory (injectBuffer, packetData, packetLength); // Allocate MDL for flat buffer. mdl = NdisAllocateMdl( Adapter->MiniportAdapterHandle, injectBuffer, packetLength ); if( mdl ) { PNET_BUFFER_LIST netBufferList; mdl->Next = NULL; // No next MDL // Allocate the NBL and NB. Link MDL chain to NB. netBufferList = NdisAllocateNetBufferAndNetBufferList( Adapter->ReceiveNblPool, 0, // ContextSize 0, // ContextBackFill mdl, // MDL chain 0, packetLength ); if(netBufferList != NULL) { ULONG receiveFlags = 0; LONG nblCount; NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL if(KeGetCurrentIrql() == DISPATCH_LEVEL) { receiveFlags |= NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL; } // Set flag indicating that this is an injected packet TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED); netBufferList->MiniportReserved[0] = NULL; netBufferList->MiniportReserved[1] = NULL; // Increment in-flight receive NBL count. nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); ASSERT(nblCount > 0 ); netBufferList->SourceHandle = Adapter->MiniportAdapterHandle; // // Indicate the packet // ------------------- // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length // contains the complete packet including Ethernet header and payload. // NdisMIndicateReceiveNetBufferLists( Adapter->MiniportAdapterHandle, netBufferList, NDIS_DEFAULT_PORT_NUMBER, 1, // NumberOfNetBufferLists receiveFlags ); return; } else { DEBUGP (("[%s] NdisAllocateNetBufferAndNetBufferList failed in IndicateReceivePacket\n", MINIPORT_INSTANCE_ID (Adapter))); NOTE_ERROR (); NdisFreeMdl(mdl); NdisFreeMemory(injectBuffer,0,0); } } else { DEBUGP (("[%s] NdisAllocateMdl failed in IndicateReceivePacket\n", MINIPORT_INSTANCE_ID (Adapter))); NOTE_ERROR (); NdisFreeMemory(injectBuffer,0,0); } } else { DEBUGP (("[%s] NdisAllocateMemoryWithTagPriority failed in IndicateReceivePacket\n", MINIPORT_INSTANCE_ID (Adapter))); NOTE_ERROR (); } }