VOID ndisprotServiceReads( IN PNDISPROT_OPEN_CONTEXT pOpenContext ) /*++ Routine Description: Utility routine to copy received data into user buffers and complete READ IRPs. Arguments: pOpenContext - pointer to open context Return Value: None --*/ { PIRP pIrp = NULL; PLIST_ENTRY pIrpEntry; PNET_BUFFER_LIST pRcvNetBufList; PLIST_ENTRY pRcvNetBufListEntry; PUCHAR pSrc, pDst; ULONG BytesRemaining; // at pDst PMDL pMdl; ULONG BytesAvailable; BOOLEAN FoundPendingIrp = FALSE; ULONG SrcTotalLength = 0; // Source NetBuffer DataLenght ULONG Offset = 0; // CurrentMdlOffset ULONG BytesToCopy = 0; DEBUGP(DL_VERY_LOUD, ("ServiceReads: open %p/%x\n", pOpenContext, pOpenContext->Flags)); NPROT_REF_OPEN(pOpenContext); // temp ref - service reads NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); while (!NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads) && !NPROT_IS_LIST_EMPTY(&pOpenContext->RecvNetBufListQueue)) { FoundPendingIrp = FALSE; // // Get the first pended Read IRP // pIrpEntry = pOpenContext->PendedReads.Flink; while (pIrpEntry != &pOpenContext->PendedReads) { pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry); // // Check to see if it is being cancelled. // if (IoSetCancelRoutine(pIrp, NULL)) { // // It isn't being cancelled, and can't be cancelled henceforth. // NPROT_REMOVE_ENTRY_LIST(pIrpEntry); FoundPendingIrp = TRUE; break; // // NOTE: we decrement PendedReadCount way below in the // while loop, to avoid letting through a thread trying // to unbind. // } else { // // The IRP is being cancelled; let the cancel routine handle it. // DEBUGP(DL_INFO, ("ServiceReads: open %p, skipping cancelled IRP %p\n", pOpenContext, pIrp)); pIrpEntry = pIrpEntry->Flink; } } if (FoundPendingIrp == FALSE) { break; } // // Get the first queued receive packet // pRcvNetBufListEntry = pOpenContext->RecvNetBufListQueue.Flink; NPROT_REMOVE_ENTRY_LIST(pRcvNetBufListEntry); pOpenContext->RecvNetBufListCount --; NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); NPROT_DEREF_OPEN(pOpenContext); // Service: dequeue rcv packet pRcvNetBufList = NPROT_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry); NPROT_ASSERT(pRcvNetBufList != NULL); _Analysis_assume_(pRcvNetBufList != NULL); NPROT_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry) = NULL; // // Copy as much data as possible from the receive packet to // the IRP MDL. // pDst = NULL; NdisQueryMdl(pIrp->MdlAddress, &pDst, &BytesRemaining, NormalPagePriority); NPROT_ASSERT(pDst != NULL); // since it was already mapped _Analysis_assume_(pDst != NULL); pMdl = NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(pRcvNetBufList)); // // Copy the data in the received packet into the buffer provided by the client. // If the length of the receive packet is greater than length of the given buffer, // we just copy as many bytes as we can. Once the buffer is full, we just discard // the rest of the data, and complete the IRP sucessfully even we only did a partial copy. // SrcTotalLength = NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(pRcvNetBufList)); Offset = NET_BUFFER_CURRENT_MDL_OFFSET(NET_BUFFER_LIST_FIRST_NB(pRcvNetBufList)); while (BytesRemaining && (pMdl != NULL) && SrcTotalLength) { pSrc = NULL; NdisQueryMdl(pMdl, &pSrc, &BytesAvailable, NormalPagePriority); if (pSrc == NULL) { DEBUGP(DL_FATAL, ("ServiceReads: Open %p, NdisQueryMdl failed for MDL %p\n", pOpenContext, pMdl)); break; } NPROT_ASSERT(BytesAvailable > Offset); BytesToCopy = MIN(BytesAvailable - Offset, BytesRemaining); BytesToCopy = MIN(BytesToCopy, SrcTotalLength); NPROT_COPY_MEM(pDst, pSrc + Offset, BytesToCopy); BytesRemaining -= BytesToCopy; pDst += BytesToCopy; SrcTotalLength -= BytesToCopy; // // CurrentMdlOffset is used only for the first Mdl processed. For the remaining Mdls, it is 0. // Offset = 0; NdisGetNextMdl(pMdl, &pMdl); } // // Complete the IRP. // pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining; DEBUGP(DL_INFO, ("ServiceReads: Open %p, IRP %p completed with %d bytes\n", pOpenContext, pIrp, (ULONG)pIrp->IoStatus.Information)); IoCompleteRequest(pIrp, IO_NO_INCREMENT); ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList,FALSE); NPROT_DEREF_OPEN(pOpenContext); // took out pended Read NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); pOpenContext->PendedReadCount--; } NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); NPROT_DEREF_OPEN(pOpenContext); // temp ref - service reads }
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); } }
VOID ndisprotServiceReads( IN PNDISPROT_OPEN_CONTEXT pOpenContext ) /*++ Routine Description: Utility routine to copy received data into user buffers and complete READ IRPs. Arguments: pOpenContext - pointer to open context Return Value: None --*/ { PIRP pIrp = NULL; PLIST_ENTRY pIrpEntry; PNDIS_PACKET pRcvPacket; PLIST_ENTRY pRcvPacketEntry; PUCHAR pSrc, pDst; ULONG BytesRemaining; // at pDst PNDIS_BUFFER pNdisBuffer; ULONG BytesAvailable; BOOLEAN FoundPendingIrp; DEBUGP(DL_VERY_LOUD, ("ServiceReads: open %p/%x\n", pOpenContext, pOpenContext->Flags)); NPROT_REF_OPEN(pOpenContext); // temp ref - service reads NPROT_ACQUIRE_LOCK(&pOpenContext->Lock); while (!NPROT_IS_LIST_EMPTY(&pOpenContext->PendedReads) && !NPROT_IS_LIST_EMPTY(&pOpenContext->RecvPktQueue)) { FoundPendingIrp = FALSE; // // Get the first pended Read IRP // pIrpEntry = pOpenContext->PendedReads.Flink; while (pIrpEntry != &pOpenContext->PendedReads) { pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry); // // Check to see if it is being cancelled. // if (IoSetCancelRoutine(pIrp, NULL)) { // // It isn't being cancelled, and can't be cancelled henceforth. // NPROT_REMOVE_ENTRY_LIST(pIrpEntry); FoundPendingIrp = TRUE; break; // // NOTE: we decrement PendedReadCount way below in the // while loop, to avoid letting through a thread trying // to unbind. // } else { // // The IRP is being cancelled; let the cancel routine handle it. // DEBUGP(DL_INFO, ("ServiceReads: open %p, skipping cancelled IRP %p\n", pOpenContext, pIrp)); pIrpEntry = pIrpEntry->Flink; } } // // If no pending IRP // if (FoundPendingIrp == FALSE) { break; } // // Get the first queued receive packet // pRcvPacketEntry = pOpenContext->RecvPktQueue.Flink; NPROT_REMOVE_ENTRY_LIST(pRcvPacketEntry); pOpenContext->RecvPktCount --; NPROT_RELEASE_LOCK(&pOpenContext->Lock); NPROT_DEREF_OPEN(pOpenContext); // Service: dequeue rcv packet pRcvPacket = NPROT_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry); // // Copy as much data as possible from the receive packet to // the IRP MDL. // pDst = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority); __analysis_assume(pDst); NPROT_ASSERT(pDst != NULL); // since it was already mapped BytesRemaining = MmGetMdlByteCount(pIrp->MdlAddress); pNdisBuffer = NDIS_PACKET_FIRST_NDIS_BUFFER(pRcvPacket); // // Copy the data in the received packet into the buffer provided by the client. // If the length of the receive packet is greater than length of the given buffer, // we just copy as many bytes as we can. Once the buffer is full, we just discard // the rest of the data, and complete the IRP sucessfully even we only did a partial copy. // while (BytesRemaining && (pNdisBuffer != NULL)) { #ifndef WIN9X NdisQueryBufferSafe(pNdisBuffer, &pSrc, &BytesAvailable, NormalPagePriority); if (pSrc == NULL) { DEBUGP(DL_FATAL, ("ServiceReads: Open %p, QueryBuffer failed for buffer %p\n", pOpenContext, pNdisBuffer)); break; } #else NdisQueryBuffer(pNdisBuffer, &pSrc, &BytesAvailable); #endif if (BytesAvailable) { ULONG BytesToCopy = MIN(BytesAvailable, BytesRemaining); NPROT_COPY_MEM(pDst, pSrc, BytesToCopy); BytesRemaining -= BytesToCopy; pDst += BytesToCopy; } NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer); } // // Complete the IRP. // pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining; DEBUGP(DL_INFO, ("ServiceReads: Open %p, IRP %p completed with %d bytes\n", pOpenContext, pIrp, pIrp->IoStatus.Information)); IoCompleteRequest(pIrp, IO_NO_INCREMENT); // // Free up the receive packet - back to the miniport if it // belongs to it, else reclaim it (local copy). // if (NdisGetPoolFromPacket(pRcvPacket) != pOpenContext->RecvPacketPool) { NdisReturnPackets(&pRcvPacket, 1); } else { ndisprotFreeReceivePacket(pOpenContext, pRcvPacket); } NPROT_DEREF_OPEN(pOpenContext); // took out pended Read NPROT_ACQUIRE_LOCK(&pOpenContext->Lock); pOpenContext->PendedReadCount--; } NPROT_RELEASE_LOCK(&pOpenContext->Lock); NPROT_DEREF_OPEN(pOpenContext); // temp ref - service reads }