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 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; 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); }