VOID NdisProtEvtIoWrite( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ) /*++ Routine Description: Dispatch routine to handle Request_MJ_WRITE. Arguments: Queue - Default queue handle Request - Handle to the read/write request Lenght - Length of the data buffer associated with the request. The default property of the queue is to not dispatch zero lenght read & write requests to the driver and complete is with status success. So we will never get a zero length request. Return Value: VOID --*/ { ULONG DataLength; NTSTATUS NtStatus; PNDISPROT_OPEN_CONTEXT pOpenContext; PNET_BUFFER_LIST pNetBufferList; NDISPROT_ETH_HEADER UNALIGNED *pEthHeader; PVOID CancelId; ULONG SendFlags = 0; PMDL pMdl = NULL; WDFFILEOBJECT fileObject; PREQUEST_CONTEXT reqContext; WDF_OBJECT_ATTRIBUTES attributes; UNREFERENCED_PARAMETER(Queue); fileObject = WdfRequestGetFileObject(Request); pOpenContext = GetFileObjectContext(fileObject)->OpenContext; do { // // Create a context to track the length of transfer and NDIS packet // associated with this request. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT); NtStatus = WdfObjectAllocateContext(Request, &attributes, &reqContext); if(!NT_SUCCESS(NtStatus)){ DEBUGP(DL_WARN, ("Write: WdfObjectAllocateContext failed: %x\n", NtStatus)); NtStatus = STATUS_INVALID_HANDLE; break; } reqContext->Length = (ULONG) Length; if (pOpenContext == NULL) { DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n", fileObject)); NtStatus = STATUS_INVALID_HANDLE; break; } NPROT_STRUCT_ASSERT(pOpenContext, oc); NtStatus = WdfRequestRetrieveInputWdmMdl(Request, &pMdl); if (!NT_SUCCESS(NtStatus)) { DEBUGP(DL_FATAL, ("Write: WdfRequestRetrieveInputWdmMdl failed %x\n", NtStatus)); break; } // // Try to get a virtual address for the MDL. // pEthHeader = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority | MdlMappingNoExecute); if (pEthHeader == NULL) { DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for" " Request %p, MDL %p\n", Request, pMdl)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Sanity-check the length. // DataLength = MmGetMdlByteCount(pMdl); if (DataLength < sizeof(NDISPROT_ETH_HEADER)) { DEBUGP(DL_WARN, ("Write: too small to be a valid packet (%d bytes)\n", DataLength)); NtStatus = STATUS_BUFFER_TOO_SMALL; break; } if (DataLength > (pOpenContext->MaxFrameSize + sizeof(NDISPROT_ETH_HEADER))) { DEBUGP(DL_WARN, ("Write: Open %p: data length (%d)" " larger than max frame size (%d)\n", pOpenContext, DataLength, pOpenContext->MaxFrameSize)); NtStatus = STATUS_INVALID_BUFFER_SIZE; break; } // // To prevent applications from sending packets with spoofed // mac address, we will do the following check to make sure the source // address in the packet is same as the current MAC address of the NIC. // if ((WdfRequestGetRequestorMode(Request) == UserMode) && !NPROT_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NPROT_MAC_ADDR_LEN)) { DEBUGP(DL_WARN, ("Write: Failing with invalid Source address")); NtStatus = STATUS_INVALID_PARAMETER; break; } NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_FATAL, ("Write: Open %p is not bound" " or in low power state\n", pOpenContext)); NtStatus = STATUS_INVALID_HANDLE; break; } if ((pOpenContext->State == NdisprotPaused) || (pOpenContext->State == NdisprotPausing)) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_INFO, ("Device is paused.\n")); NtStatus = STATUS_UNSUCCESSFUL; break; } NPROT_ASSERT(pOpenContext->SendNetBufferListPool != NULL); pNetBufferList = NdisAllocateNetBufferAndNetBufferList( pOpenContext->SendNetBufferListPool, sizeof(NPROT_SEND_NETBUFLIST_RSVD), //Request control offset delta 0, // back fill size pMdl, 0, // Data offset DataLength); if (pNetBufferList == NULL) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send net buffer list\n", pOpenContext)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } pOpenContext->PendedSendCount++; NPROT_REF_OPEN(pOpenContext); // pended send // // Initialize the NetBufferList ref count. This NetBufferList will be freed // when this count goes to zero. // NPROT_SEND_NBL_RSVD(pNetBufferList)->RefCount = 1; // // We set up a cancel ID on each send NetBufferList (which maps to a Write IRP), // and save the NetBufferList pointer in the IRP. If the IRP gets cancelled, we use // NdisCancelSendNetBufferLists() to cancel the NetBufferList. // // Note that this sample code does not implement the cancellation logic. An actual // driver may find value in implementing this. // CancelId = NPROT_GET_NEXT_CANCEL_ID(); NDIS_SET_NET_BUFFER_LIST_CANCEL_ID(pNetBufferList, CancelId); reqContext->NetBufferList = (PVOID)pNetBufferList; NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); // // Set a back pointer from the packet to the IRP. // NPROT_REQUEST_FROM_SEND_NBL(pNetBufferList) = Request; NtStatus = STATUS_PENDING; pNetBufferList->SourceHandle = pOpenContext->BindingHandle; NPROT_ASSERT (pMdl->Next == NULL); SendFlags |= NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK; NdisSendNetBufferLists( pOpenContext->BindingHandle, pNetBufferList, NDIS_DEFAULT_PORT_NUMBER, SendFlags); } while (FALSE); if (NtStatus != STATUS_PENDING) { WdfRequestComplete(Request, NtStatus); } return; }
VOID ndisprotQueueReceiveNetBufferList( IN PNDISPROT_OPEN_CONTEXT pOpenContext, IN PNET_BUFFER_LIST pRcvNetBufList, IN BOOLEAN DispatchLevel ) /*++ Routine Description: Queue up a received net buffer list on the open context structure. If the queue size goes beyond a water mark, discard a Net Buffer list at the head of the queue. Finally, run the queue service routine. Arguments: pOpenContext - pointer to open context pRcvPacket - the received packet DipatchLevel - the irql level Return Value: None --*/ { PLIST_ENTRY pEnt; PLIST_ENTRY pDiscardEnt; PNET_BUFFER_LIST pDiscardNetBufList; do { NPROT_REF_OPEN(pOpenContext); // queued rcv net buffer list NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); if ((pOpenContext->State == NdisprotPaused) || (pOpenContext->State == NdisprotPausing)) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList, DispatchLevel); break; } // // Check if the binding is in the proper state to receive // this net buffer list. // if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE) && (pOpenContext->PowerState == NetDeviceStateD0)) { // // Queue the net buffer list // pEnt = NPROT_RCV_NBL_TO_LIST_ENTRY(pRcvNetBufList); NPROT_INSERT_TAIL_LIST(&pOpenContext->RecvNetBufListQueue, pEnt); NPROT_RCV_NBL_FROM_LIST_ENTRY(pEnt) = pRcvNetBufList; pOpenContext->RecvNetBufListCount++; DEBUGP(DL_VERY_LOUD, ("QueueReceiveNetBufferList: open %p," " queued nbl %p, queue size %d\n", pOpenContext, pRcvNetBufList, pOpenContext->RecvNetBufListCount)); } else { // // Received this net buffer list when the binding is going away. // Drop this. // NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList, DispatchLevel); NPROT_DEREF_OPEN(pOpenContext); // dropped rcv packet - bad state break; } // // Trim the queue if it has grown too big. // if (pOpenContext->RecvNetBufListCount > MAX_RECV_QUEUE_SIZE) { // // Remove the head of the queue. // pDiscardEnt = pOpenContext->RecvNetBufListQueue.Flink; NPROT_REMOVE_ENTRY_LIST(pDiscardEnt); pOpenContext->RecvNetBufListCount --; NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); pDiscardNetBufList = NPROT_RCV_NBL_FROM_LIST_ENTRY(pDiscardEnt); NPROT_RCV_NBL_FROM_LIST_ENTRY(pDiscardEnt) = NULL; ndisprotFreeReceiveNetBufferList(pOpenContext, pDiscardNetBufList, DispatchLevel); NPROT_DEREF_OPEN(pOpenContext); // dropped rcv packet - queue too long DEBUGP(DL_INFO, ("QueueReceiveNetBufferList: open %p queue" " too long, discarded %p\n", pOpenContext, pDiscardNetBufList)); } else { NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); } // // Run the receive queue service routine now. // ndisprotServiceReads(pOpenContext); } while (FALSE); }
NTSTATUS NdisProtWrite( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Dispatch routine to handle IRP_MJ_WRITE. Arguments: pDeviceObject - pointer to our device object pIrp - Pointer to request packet Return Value: NT status code. --*/ { PIO_STACK_LOCATION pIrpSp; ULONG DataLength; NTSTATUS NtStatus; NDIS_STATUS Status; PNDISPROT_OPEN_CONTEXT pOpenContext; PNDIS_PACKET pNdisPacket; PNDIS_BUFFER pNdisBuffer; NDISPROT_ETH_HEADER UNALIGNED *pEthHeader; #ifdef NDIS51 PVOID CancelId; #endif UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; pNdisPacket = NULL; do { if (pOpenContext == NULL) { DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n", pIrpSp->FileObject)); NtStatus = STATUS_INVALID_HANDLE; break; } NPROT_STRUCT_ASSERT(pOpenContext, oc); if (pIrp->MdlAddress == NULL) { DEBUGP(DL_FATAL, ("Write: NULL MDL address on IRP %p\n", pIrp)); NtStatus = STATUS_INVALID_PARAMETER; break; } // // Try to get a virtual address for the MDL. // pEthHeader = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority); if (pEthHeader == NULL) { DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for" " IRP %p, MDL %p\n", pIrp, pIrp->MdlAddress)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Sanity-check the length. // DataLength = MmGetMdlByteCount(pIrp->MdlAddress); if (DataLength < sizeof(NDISPROT_ETH_HEADER)) { DEBUGP(DL_WARN, ("Write: too small to be a valid packet (%d bytes)\n", DataLength)); NtStatus = STATUS_BUFFER_TOO_SMALL; break; } if (DataLength > (pOpenContext->MaxFrameSize + sizeof(NDISPROT_ETH_HEADER))) { DEBUGP(DL_WARN, ("Write: Open %p: data length (%d)" " larger than max frame size (%d)\n", pOpenContext, DataLength, pOpenContext->MaxFrameSize)); NtStatus = STATUS_INVALID_BUFFER_SIZE; break; } // // To prevent applications from sending packets with spoofed // mac address, we will do the following check to make sure the source // address in the packet is same as the current MAC address of the NIC. // if ((pIrp->RequestorMode == UserMode) && !NPROT_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NPROT_MAC_ADDR_LEN)) { DEBUGP(DL_WARN, ("Write: Failing with invalid Source address")); NtStatus = STATUS_INVALID_PARAMETER; break; } NPROT_ACQUIRE_LOCK(&pOpenContext->Lock); if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_ACTIVE)) { NPROT_RELEASE_LOCK(&pOpenContext->Lock); DEBUGP(DL_FATAL, ("Write: Open %p is not bound" " or in low power state\n", pOpenContext)); NtStatus = STATUS_INVALID_HANDLE; break; } // // Allocate a send packet. // NPROT_ASSERT(pOpenContext->SendPacketPool != NULL); NdisAllocatePacket( &Status, &pNdisPacket, pOpenContext->SendPacketPool); if (Status != NDIS_STATUS_SUCCESS) { NPROT_RELEASE_LOCK(&pOpenContext->Lock); DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send pkt\n", pOpenContext)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Allocate a send buffer if necessary. // if (pOpenContext->bRunningOnWin9x) { NdisAllocateBuffer( &Status, &pNdisBuffer, pOpenContext->SendBufferPool, pEthHeader, DataLength); if (Status != NDIS_STATUS_SUCCESS) { NPROT_RELEASE_LOCK(&pOpenContext->Lock); NdisFreePacket(pNdisPacket); DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send buf\n", pOpenContext)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } } else { pNdisBuffer = pIrp->MdlAddress; } NdisInterlockedIncrement((PLONG)&pOpenContext->PendedSendCount); NPROT_REF_OPEN(pOpenContext); // pended send IoMarkIrpPending(pIrp); // // Initialize the packet ref count. This packet will be freed // when this count goes to zero. // NPROT_SEND_PKT_RSVD(pNdisPacket)->RefCount = 1; #ifdef NDIS51 // // NDIS 5.1 supports cancelling sends. We set up a cancel ID on // each send packet (which maps to a Write IRP), and save the // packet pointer in the IRP. If the IRP gets cancelled, we use // NdisCancelSendPackets() to cancel the packet. // CancelId = NPROT_GET_NEXT_CANCEL_ID(); NDIS_SET_PACKET_CANCEL_ID(pNdisPacket, CancelId); pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext; pIrp->Tail.Overlay.DriverContext[1] = (PVOID)pNdisPacket; NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedWrites, &pIrp->Tail.Overlay.ListEntry); IoSetCancelRoutine(pIrp, NdisProtCancelWrite); #endif // NDIS51 NPROT_RELEASE_LOCK(&pOpenContext->Lock); // // Set a back pointer from the packet to the IRP. // NPROT_IRP_FROM_SEND_PKT(pNdisPacket) = pIrp; NtStatus = STATUS_PENDING; pNdisBuffer->Next = NULL; NdisChainBufferAtFront(pNdisPacket, pNdisBuffer); #if SEND_DBG { PUCHAR pData; pData = MmGetSystemAddressForMdlSafe(pNdisBuffer, NormalPagePriority); NPROT_ASSERT(pEthHeader == pData); DEBUGP(DL_VERY_LOUD, ("Write: MDL %p, MdlFlags %x, SystemAddr %p, %d bytes\n", pIrp->MdlAddress, pIrp->MdlAddress->MdlFlags, pData, DataLength)); DEBUGPDUMP(DL_VERY_LOUD, pData, MIN(DataLength, 48)); } #endif // SEND_DBG NdisSendPackets(pOpenContext->BindingHandle, &pNdisPacket, 1); } while (FALSE); if (NtStatus != STATUS_PENDING) { pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } return (NtStatus); }
NTSTATUS NdisprotRead( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Dispatch routine to handle IRP_MJ_READ. Arguments: pDeviceObject - pointer to our device object pIrp - Pointer to request packet Return Value: NT status code. --*/ { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus; PNDISPROT_OPEN_CONTEXT pOpenContext; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; do { // // Validate! // if (pOpenContext == NULL) { DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n", pIrpSp->FileObject)); NtStatus = STATUS_INVALID_HANDLE; break; } NPROT_STRUCT_ASSERT(pOpenContext, oc); if (pIrp->MdlAddress == NULL) { DEBUGP(DL_FATAL, ("Read: NULL MDL address on IRP %p\n", pIrp)); NtStatus = STATUS_INVALID_PARAMETER; break; } // // Try to get a virtual address for the MDL. // if (MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority) == NULL) { DEBUGP(DL_FATAL, ("Read: MmGetSystemAddr failed for IRP %p, MDL %p\n", pIrp, pIrp->MdlAddress)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); NtStatus = STATUS_INVALID_HANDLE; break; } IoSetCancelRoutine(pIrp, NdisprotCancelRead); if (pIrp->Cancel && IoSetCancelRoutine(pIrp, NULL)) { // // IRP has been canceled but the I/O manager did not manage to call our cancel routine. This // code is safe referencing the Irp->Cancel field without locks because of the memory barriers // in the interlocked exchange sequences used by IoSetCancelRoutine. // NtStatus = STATUS_CANCELLED; // IRP should be completed after releasing the lock } else { // // Add this IRP to the list of pended Read IRPs // NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedReads, &pIrp->Tail.Overlay.ListEntry); pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext; NPROT_REF_OPEN(pOpenContext); // pended read IRP pOpenContext->PendedReadCount++; IoMarkIrpPending(pIrp); NtStatus = STATUS_PENDING; } NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); // // Run the service routine for reads. // ndisprotServiceReads(pOpenContext); } while (FALSE); if (NtStatus != STATUS_PENDING) { NPROT_ASSERT(NtStatus != STATUS_SUCCESS); pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } return (NtStatus); }
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 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 }
NTSTATUS NdisprotWrite( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Dispatch routine to handle IRP_MJ_WRITE. Arguments: pDeviceObject - pointer to our device object pIrp - Pointer to request packet Return Value: NT status code. --*/ { PIO_STACK_LOCATION pIrpSp; ULONG DataLength; NTSTATUS NtStatus; PNDISPROT_OPEN_CONTEXT pOpenContext; PNET_BUFFER_LIST pNetBufferList; PMDL pMdl; NDISPROT_ETH_HEADER UNALIGNED *pEthHeader; PVOID CancelId; ULONG SendFlags = 0; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; do { if (pOpenContext == NULL) { DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n", pIrpSp->FileObject)); NtStatus = STATUS_INVALID_HANDLE; break; } NPROT_STRUCT_ASSERT(pOpenContext, oc); if (pIrp->MdlAddress == NULL) { DEBUGP(DL_FATAL, ("Write: NULL MDL address on IRP %p\n", pIrp)); NtStatus = STATUS_INVALID_PARAMETER; break; } // // Try to get a virtual address for the MDL. // pEthHeader = NULL; NdisQueryMdl(pIrp->MdlAddress, &pEthHeader, &DataLength, NormalPagePriority); if (pEthHeader == NULL) { DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for" " IRP %p, MDL %p\n", pIrp, pIrp->MdlAddress)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Sanity-check the length. // if (DataLength < sizeof(NDISPROT_ETH_HEADER)) { DEBUGP(DL_WARN, ("Write: too small to be a valid packet (%d bytes)\n", DataLength)); NtStatus = STATUS_BUFFER_TOO_SMALL; break; } if (DataLength > (pOpenContext->MaxFrameSize + sizeof(NDISPROT_ETH_HEADER))) { DEBUGP(DL_WARN, ("Write: Open %p: data length (%d)" " larger than max frame size (%d)\n", pOpenContext, DataLength, pOpenContext->MaxFrameSize)); NtStatus = STATUS_INVALID_BUFFER_SIZE; break; } if (pEthHeader->EthType != Globals.EthType) { DEBUGP(DL_WARN, ("Write: Failing send with EthType %x\n", pEthHeader->EthType)); NtStatus = STATUS_INVALID_PARAMETER; break; } if (!NPROT_MEM_CMP(pEthHeader->SrcAddr, pOpenContext->CurrentAddress, NPROT_MAC_ADDR_LEN)) { DEBUGP(DL_WARN, ("Write: Failing with invalid Source address")); NtStatus = STATUS_INVALID_PARAMETER; break; } NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_FATAL, ("Write: Open %p is not bound" " or in low power state\n", pOpenContext)); NtStatus = STATUS_INVALID_HANDLE; break; } if (pOpenContext->State != NdisprotRunning || pOpenContext->PowerState != NetDeviceStateD0) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_INFO, ("Device is not ready.\n")); NtStatus = STATUS_UNSUCCESSFUL; break; } pMdl = pIrp->MdlAddress; NPROT_ASSERT(pOpenContext->SendNetBufferListPool != NULL); pNetBufferList = NdisAllocateNetBufferAndNetBufferList( pOpenContext->SendNetBufferListPool, sizeof(NPROT_SEND_NETBUFLIST_RSVD), //Request control offset delta 0, // back fill size pMdl, 0, // Data offset DataLength); if (pNetBufferList == NULL) { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_FATAL, ("Write: open %p, failed to alloc send net buffer list\n", pOpenContext)); NtStatus = STATUS_INSUFFICIENT_RESOURCES; break; } pOpenContext->PendedSendCount++; NPROT_REF_OPEN(pOpenContext); // pended send IoMarkIrpPending(pIrp); // // Initialize the NetBufferList ref count. This NetBufferList will be freed // when this count goes to zero. // NPROT_SEND_NBL_RSVD(pNetBufferList)->RefCount = 1; // // We set up a cancel ID on each send NetBufferList (which maps to a Write IRP), // and save the NetBufferList pointer in the IRP. If the IRP gets cancelled, we use // NdisCancelSendNetBufferLists() to cancel the NetBufferList. // CancelId = NPROT_GET_NEXT_CANCEL_ID(); NDIS_SET_NET_BUFFER_LIST_CANCEL_ID(pNetBufferList, CancelId); pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pOpenContext; pIrp->Tail.Overlay.DriverContext[1] = (PVOID)pNetBufferList; pIrp->Tail.Overlay.DriverContext[2] = CancelId; NPROT_INSERT_TAIL_LIST(&pOpenContext->PendedWrites, &pIrp->Tail.Overlay.ListEntry); IoSetCancelRoutine(pIrp, NdisprotCancelWrite); NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); // // Set a back pointer from the packet to the IRP. // NPROT_IRP_FROM_SEND_NBL(pNetBufferList) = pIrp; NtStatus = STATUS_PENDING; #if SEND_DBG { PUCHAR pData; pData = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority); NPROT_ASSERT(pEthHeader == pData); DEBUGP(DL_VERY_LOUD, ("Write: MDL %p, MdlFlags %x, SystemAddr %p, %d bytes\n", pIrp->MdlAddress, pIrp->MdlAddress->MdlFlags, pData, DataLength)); DEBUGPDUMP(DL_VERY_LOUD, pData, MIN(DataLength, 48)); } #endif // SEND_DBG pNetBufferList->SourceHandle = pOpenContext->BindingHandle; ASSERT (NDIS_MDL_LINKAGE(pMdl) == NULL); SendFlags |= NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK; NdisSendNetBufferLists( pOpenContext->BindingHandle, pNetBufferList, NDIS_DEFAULT_PORT_NUMBER, SendFlags); } while (FALSE); if (NtStatus != STATUS_PENDING) { pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } return (NtStatus); }
VOID NdisprotCancelWrite( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Cancel a pending write IRP. This routine attempt to cancel the NDIS send. Arguments: pDeviceObject - pointer to our device object pIrp - IRP to be cancelled Return Value: None --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; PLIST_ENTRY pIrpEntry; BOOLEAN FoundIrp = FALSE; UNREFERENCED_PARAMETER(pDeviceObject); pOpenContext = (PNDISPROT_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0]; if (pOpenContext == NULL) { // // The IRP is going to be completed from send complete function // IoReleaseCancelSpinLock(pIrp->CancelIrql); return; } NPROT_REF_OPEN(pOpenContext); IoReleaseCancelSpinLock(pIrp->CancelIrql); NPROT_STRUCT_ASSERT(pOpenContext, oc); // // Try to locate the IRP in the pended write queue. The send completion // routine may be running and might have removed it from there. // NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); for (pIrpEntry = pOpenContext->PendedWrites.Flink; pIrpEntry != &pOpenContext->PendedWrites; pIrpEntry = pIrpEntry->Flink) { if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry)) { FoundIrp = TRUE; break; } } NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); if (FoundIrp) { PVOID CancelId; CancelId = pIrp->Tail.Overlay.DriverContext[2]; // // Either the send completion routine hasn't run, or we got a peak // at the IRP/netbufferlist before it had a chance to take it out of the // pending IRP queue. // // We do not complete the IRP here - note that we didn't dequeue it // above. This is because we always want the send complete routine to // complete the IRP. And this in turn is because the NetBufferlist that was // prepared from the IRP has a buffer chain pointing to data associated // with this IRP. Therefore we cannot complete the IRP before the driver // below us is done with the data it pointed to. // // // Request NDIS to cancel this send. The result of this call is that // our SendComplete handler will be called (if not already called). // DEBUGP(DL_INFO, ("CancelWrite: cancelling nbl %p on Open %p\n", pIrp->Tail.Overlay.DriverContext[1], pOpenContext)); NdisCancelSendNetBufferLists( pOpenContext->BindingHandle, CancelId); } // // else the send completion routine has already picked up this IRP. // NPROT_DEREF_OPEN(pOpenContext); }