NTSTATUS NdisProtClose( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling IRP_MJ_CLOSE. We simply succeed this. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { NTSTATUS NtStatus; PIO_STACK_LOCATION pIrpSp; PNDISPROT_OPEN_CONTEXT pOpenContext; PAGED_CODE(); UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; DEBUGP(DL_INFO, ("Close: FileObject %p\n", IoGetCurrentIrpStackLocation(pIrp)->FileObject)); if (pOpenContext != NULL) { NPROT_STRUCT_ASSERT(pOpenContext, oc); // // Deref the endpoint // NPROT_DEREF_OPEN(pOpenContext); // Close } pIrpSp->FileObject->FsContext = NULL; NtStatus = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return NtStatus; }
VOID NdisprotCancelRead( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Cancel a pending read IRP. We unlink the IRP from the open context queue and complete it. Arguments: pDeviceObject - pointer to our device object pIrp - IRP to be cancelled Return Value: None --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; UNREFERENCED_PARAMETER(pDeviceObject); IoReleaseCancelSpinLock(pIrp->CancelIrql); pOpenContext = (PNDISPROT_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0]; NPROT_STRUCT_ASSERT(pOpenContext, oc); NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry); pOpenContext->PendedReadCount--; NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); DEBUGP(DL_INFO, ("CancelRead: Open %p, IRP %p\n", pOpenContext, pIrp)); pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); NPROT_DEREF_OPEN(pOpenContext); // Cancel removed pended Read }
VOID NdisProtEvtFileClose( IN WDFFILEOBJECT FileObject ) /*++ Routine Description: EvtFileClose is called when all the handles represented by the FileObject is closed and all the references to FileObject is removed. This callback may get called in an arbitrary thread context instead of the thread that called CloseHandle. If you want to delete any per FileObject context that must be done in the context of the user thread that made the Create call, you should do that in the EvtDeviceCleanp callback. Arguments: FileObject - Pointer to fileobject that represents the open handle. Return Value: VOID --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; PFILE_OBJECT_CONTEXT fileContext; fileContext = GetFileObjectContext(FileObject); pOpenContext = fileContext->OpenContext; DEBUGP(DL_INFO, ("Close: FileObject %p\n", FileObject)); if (pOpenContext != NULL) { NPROT_STRUCT_ASSERT(pOpenContext, oc); // // Deref the endpoint // NPROT_DEREF_OPEN(pOpenContext); // Close } fileContext->OpenContext = NULL; return; }
VOID NdisProtSendComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET pNdisPacket, IN NDIS_STATUS Status ) /*++ 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. NDIS 5.1: Arguments: ProtocolBindingContext - pointer to open context pNdisPacket - packet that completed send Status - status of send Return Value: None --*/ { PIRP pIrp; PIO_STACK_LOCATION pIrpSp; PNDISPROT_OPEN_CONTEXT pOpenContext; pOpenContext = (PNDISPROT_OPEN_CONTEXT)ProtocolBindingContext; NPROT_STRUCT_ASSERT(pOpenContext, oc); pIrp = NPROT_IRP_FROM_SEND_PKT(pNdisPacket); if (pOpenContext->bRunningOnWin9x) { // // We would have attached our own NDIS_BUFFER. Take it out // and free it. // #ifndef NDIS51 PNDIS_BUFFER pNdisBuffer; PVOID VirtualAddr; UINT BufferLength; UINT TotalLength; #endif #ifdef NDIS51 NPROT_ASSERT(FALSE); // NDIS 5.1 not on Win9X! #else NdisGetFirstBufferFromPacket( pNdisPacket, &pNdisBuffer, &VirtualAddr, &BufferLength, &TotalLength); NPROT_ASSERT(pNdisBuffer != NULL); NdisFreeBuffer(pNdisBuffer); #endif } #ifdef NDIS51 IoSetCancelRoutine(pIrp, NULL); NPROT_ACQUIRE_LOCK(&pOpenContext->Lock); NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry); NPROT_RELEASE_LOCK(&pOpenContext->Lock); #endif // // We are done with the NDIS_PACKET: // NPROT_DEREF_SEND_PKT(pNdisPacket); // // Complete the Write IRP with the right status. // pIrpSp = IoGetCurrentIrpStackLocation(pIrp); if (Status == NDIS_STATUS_SUCCESS) { pIrp->IoStatus.Information = pIrpSp->Parameters.Write.Length; pIrp->IoStatus.Status = STATUS_SUCCESS; } else { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; } DEBUGP(DL_INFO, ("SendComplete: packet %p/IRP %p/Length %d " "completed with status %x\n", pNdisPacket, pIrp, pIrp->IoStatus.Information, pIrp->IoStatus.Status)); IoCompleteRequest(pIrp, IO_NO_INCREMENT); NdisInterlockedDecrement((PLONG)&pOpenContext->PendedSendCount); NPROT_DEREF_OPEN(pOpenContext); // send complete - dequeued send IRP }
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); }
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; PNDIS_PACKET pNdisPacket; UNREFERENCED_PARAMETER(pDeviceObject); IoReleaseCancelSpinLock(pIrp->CancelIrql); // // The NDIS packet representing this Write IRP. // pNdisPacket = NULL; pOpenContext = (PNDISPROT_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0]; 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); for (pIrpEntry = pOpenContext->PendedWrites.Flink; pIrpEntry != &pOpenContext->PendedWrites; pIrpEntry = pIrpEntry->Flink) { if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry)) { pNdisPacket = (PNDIS_PACKET) pIrp->Tail.Overlay.DriverContext[1]; // // Place a reference on this packet so that it won't get // freed/reused until we are done with it. // NPROT_REF_SEND_PKT(pNdisPacket); break; } } NPROT_RELEASE_LOCK(&pOpenContext->Lock); if (pNdisPacket != NULL) { // // Either the send completion routine hasn't run, or we got a peak // at the IRP/packet 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 packet 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 pkt %p on Open %p\n", pNdisPacket, pOpenContext)); NdisCancelSendPackets( pOpenContext->BindingHandle, NDIS_GET_PACKET_CANCEL_ID(pNdisPacket) ); // // It is now safe to remove the reference we had placed on the packet. // NPROT_DEREF_SEND_PKT(pNdisPacket); } // // else the send completion routine has already picked up this IRP. // }
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); } }
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 NdisProtEvtFileCleanup( IN WDFFILEOBJECT FileObject ) /*++ Routine Description: EvtFileCleanup is called when the handle represented by the FileObject is closed. This callback is invoked in the context of the thread that closed the handle. Arguments: FileObject - Pointer to fileobject that represents the open handle. Return Value: VOID --*/ { NTSTATUS NtStatus; NDIS_STATUS NdisStatus; PNDISPROT_OPEN_CONTEXT pOpenContext; ULONG PacketFilter; ULONG BytesProcessed; PFILE_OBJECT_CONTEXT fileContext; fileContext = GetFileObjectContext(FileObject); pOpenContext = fileContext->OpenContext; DEBUGP(DL_VERY_LOUD, ("Cleanup: FileObject %p, Open %p\n", FileObject, pOpenContext)); if (pOpenContext != NULL) { NPROT_STRUCT_ASSERT(pOpenContext, oc); // // Mark this endpoint. // NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); NPROT_SET_FLAGS(pOpenContext->Flags, NPROTO_OPEN_FLAGS, NPROTO_OPEN_IDLE); pOpenContext->pFileObject = NULL; NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); // // Set the packet filter to 0, telling NDIS that we aren't // interested in any more receives. // PacketFilter = 0; NdisStatus = ndisprotValidateOpenAndDoRequest( pOpenContext, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &BytesProcessed, FALSE // Don't wait for device to be powered on ); if (NdisStatus != NDIS_STATUS_SUCCESS) { DEBUGP(DL_INFO, ("Cleanup: Open %p, set packet filter (%x) failed: %x\n", pOpenContext, PacketFilter, NdisStatus)); // // Ignore the result. If this failed, we may continue // to get indicated receives, which will be handled // appropriately. // NdisStatus = NDIS_STATUS_SUCCESS; } NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)){ NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); // // Cancel any pending reads. // WdfIoQueuePurgeSynchronously(pOpenContext->ReadQueue); // // Cancel pending control request for status indication. // WdfIoQueuePurgeSynchronously(pOpenContext->StatusIndicationQueue); } else { NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); } // // Clean up the receive packet queue // ndisprotFlushReceiveQueue(pOpenContext); } NtStatus = STATUS_SUCCESS; DEBUGP(DL_INFO, ("Cleanup: OpenContext %p\n", pOpenContext)); return; }
NTSTATUS NdisProtCleanup( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling IRP_MJ_CLEANUP. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus; NDIS_STATUS NdisStatus; PNDISPROT_OPEN_CONTEXT pOpenContext; ULONG PacketFilter; ULONG BytesProcessed; UNREFERENCED_PARAMETER(pDeviceObject); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; DEBUGP(DL_VERY_LOUD, ("Cleanup: FileObject %p, Open %p\n", pIrpSp->FileObject, pOpenContext)); if (pOpenContext != NULL) { NPROT_STRUCT_ASSERT(pOpenContext, oc); // // Mark this endpoint. // NPROT_ACQUIRE_LOCK(&pOpenContext->Lock); NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_OPEN_FLAGS, NUIOO_OPEN_IDLE); pOpenContext->pFileObject = NULL; NPROT_RELEASE_LOCK(&pOpenContext->Lock); // // Set the packet filter to 0, telling NDIS that we aren't // interested in any more receives. // PacketFilter = 0; NdisStatus = ndisprotValidateOpenAndDoRequest( pOpenContext, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &PacketFilter, sizeof(PacketFilter), &BytesProcessed, FALSE // Don't wait for device to be powered on ); if (NdisStatus != NDIS_STATUS_SUCCESS) { DEBUGP(DL_INFO, ("Cleanup: Open %p, set packet filter (%x) failed: %x\n", pOpenContext, PacketFilter, NdisStatus)); // // Ignore the result. If this failed, we may continue // to get indicated receives, which will be handled // appropriately. // NdisStatus = NDIS_STATUS_SUCCESS; } // // Cancel any pending reads. // ndisprotCancelPendingReads(pOpenContext); // // Clean up the receive packet queue // ndisprotFlushReceiveQueue(pOpenContext); } NtStatus = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); DEBUGP(DL_INFO, ("Cleanup: OpenContext %p\n", pOpenContext)); return (NtStatus); }
VOID NdisProtCancelRead( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Cancel a pending read IRP. We unlink the IRP from the open context queue and complete it. Arguments: pDeviceObject - pointer to our device object pIrp - IRP to be cancelled Return Value: None --*/ { PNDISPROT_OPEN_CONTEXT pOpenContext; PLIST_ENTRY pIrpEntry; BOOLEAN Found; UNREFERENCED_PARAMETER(pDeviceObject); IoReleaseCancelSpinLock(pIrp->CancelIrql); Found = FALSE; pOpenContext = (PNDISPROT_OPEN_CONTEXT) pIrp->Tail.Overlay.DriverContext[0]; NPROT_STRUCT_ASSERT(pOpenContext, oc); NPROT_ACQUIRE_LOCK(&pOpenContext->Lock); // // Locate the IRP in the pended read queue and remove it if found. // for (pIrpEntry = pOpenContext->PendedReads.Flink; pIrpEntry != &pOpenContext->PendedReads; pIrpEntry = pIrpEntry->Flink) { if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry)) { NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry); pOpenContext->PendedReadCount--; Found = TRUE; break; } } NPROT_RELEASE_LOCK(&pOpenContext->Lock); if (Found) { DEBUGP(DL_INFO, ("CancelRead: Open %p, IRP %p\n", pOpenContext, pIrp)); pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); NPROT_DEREF_OPEN(pOpenContext); // Cancel removed pended Read } }
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 --*/ { PIRP pIrp; PIO_STACK_LOCATION pIrpSp; PNDISPROT_OPEN_CONTEXT pOpenContext; PNET_BUFFER_LIST CurrNetBufferList = NULL; PNET_BUFFER_LIST NextNetBufferList; NDIS_STATUS CompletionStatus; BOOLEAN DispatchLevel; 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); pIrp = NPROT_IRP_FROM_SEND_NBL(CurrNetBufferList); IoAcquireCancelSpinLock(&pIrp->CancelIrql); IoSetCancelRoutine(pIrp, NULL); pIrp->Tail.Overlay.DriverContext[0] = NULL; pIrp->Tail.Overlay.DriverContext[1] = NULL; IoReleaseCancelSpinLock(pIrp->CancelIrql); NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); NPROT_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry); NPROT_RELEASE_LOCK(&pOpenContext->Lock, DispatchLevel); CompletionStatus = NET_BUFFER_LIST_STATUS(CurrNetBufferList); // // We are done with the NDIS_PACKET: // NPROT_DEREF_SEND_NBL(CurrNetBufferList, DispatchLevel); // // Complete the Write IRP with the right status. // pIrpSp = IoGetCurrentIrpStackLocation(pIrp); if (CompletionStatus == NDIS_STATUS_SUCCESS) { pIrp->IoStatus.Information = pIrpSp->Parameters.Write.Length; pIrp->IoStatus.Status = STATUS_SUCCESS; } else { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; } DEBUGP(DL_INFO, ("SendComplete: NetBufferList %p/IRP %p/Length %d " "completed with status %x\n", CurrNetBufferList, pIrp, pIrp->IoStatus.Information, pIrp->IoStatus.Status)); IoCompleteRequest(pIrp, IO_NO_INCREMENT); NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); pOpenContext->PendedSendCount--; if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) && (pOpenContext->PendedSendCount == 0)) { 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 } }
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); }
VOID NdisProtEvtIoDeviceControl( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode ) /*++ Routine Description: This event is called when the framework receives IRP_MJ_DEVICE_CONTROL requests from the system. Arguments: Queue - Handle to the framework queue object that is associated with the I/O request. Request - Handle to a framework request object. OutputBufferLength - length of the request's output buffer, if an output buffer is available. InputBufferLength - length of the request's input buffer, if an input buffer is available. IoControlCode - the driver-defined or system-defined I/O control code (IOCTL) that is associated with the request. Return Value: VOID --*/ { NTSTATUS NtStatus; NDIS_STATUS Status; PNDISPROT_OPEN_CONTEXT pOpenContext; ULONG BytesReturned; PVOID sysBuffer; WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); size_t bufSize; UNREFERENCED_PARAMETER(Queue); UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); DEBUGP(DL_LOUD, ("IoControl: Irp %p\n", Request)); pOpenContext = GetFileObjectContext(fileObject)->OpenContext; BytesReturned = 0; switch (IoControlCode) { case IOCTL_NDISPROT_BIND_WAIT: // // Block until we have seen a NetEventBindsComplete event, // meaning that we have finished binding to all running // adapters that we are supposed to bind to. // // If we don't get this event in 5 seconds, time out. // NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); if (NPROT_WAIT_EVENT(&Globals.BindsComplete, 5000)) { NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_TIMEOUT; } DEBUGP(DL_INFO, ("IoControl: BindWait returning %x\n", NtStatus)); break; case IOCTL_NDISPROT_QUERY_BINDING: NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); NtStatus = WdfRequestRetrieveOutputBuffer(Request, sizeof(NDISPROT_QUERY_BINDING), &sysBuffer, &bufSize); if( !NT_SUCCESS(NtStatus) ) { DEBUGP(DL_FATAL, ("WdfRequestRetrieveOutputBuffer failed %x\n", NtStatus)); break; } Status = ndisprotQueryBinding( sysBuffer, (ULONG) bufSize, (ULONG) bufSize, &BytesReturned ); NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); DEBUGP(DL_LOUD, ("IoControl: QueryBinding returning %x\n", NtStatus)); break; case IOCTL_NDISPROT_OPEN_DEVICE: NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { NPROT_STRUCT_ASSERT(pOpenContext, oc); DEBUGP(DL_WARN, ("IoControl: OPEN_DEVICE: FileObj %p already" " associated with open %p\n", fileObject, pOpenContext)); NtStatus = STATUS_DEVICE_BUSY; break; } NtStatus = WdfRequestRetrieveInputBuffer(Request, 0, &sysBuffer, &bufSize); if( !NT_SUCCESS(NtStatus) ) { DEBUGP(DL_FATAL, ("WdfRequestRetrieveInputBuffer failed %x\n", NtStatus)); break; } NtStatus = ndisprotOpenDevice( sysBuffer, (ULONG)bufSize, fileObject, &pOpenContext ); if (NT_SUCCESS(NtStatus)) { DEBUGP(DL_VERY_LOUD, ("IoControl OPEN_DEVICE: Open %p <-> FileObject %p\n", pOpenContext, fileObject)); } break; case IOCTL_NDISPROT_QUERY_OID_VALUE: NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); NtStatus = WdfRequestRetrieveOutputBuffer(Request, sizeof(NDISPROT_QUERY_OID), &sysBuffer, &bufSize); if( !NT_SUCCESS(NtStatus) ) { DEBUGP(DL_FATAL, ("WdfRequestRetrieveOutputBuffer failed %x\n", NtStatus)); break; } if (pOpenContext != NULL) { Status = ndisprotQueryOidValue( pOpenContext, sysBuffer, (ULONG)bufSize, &BytesReturned ); NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; case IOCTL_NDISPROT_SET_OID_VALUE: NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); NtStatus = WdfRequestRetrieveInputBuffer(Request, sizeof(NDISPROT_SET_OID), &sysBuffer, &bufSize); if( !NT_SUCCESS(NtStatus) ) { DEBUGP(DL_FATAL, ("WdfRequestRetrieveInputBuffer failed %x\n", NtStatus)); break; } if (pOpenContext != NULL) { Status = ndisprotSetOidValue( pOpenContext, sysBuffer, (ULONG)bufSize ); BytesReturned = 0; NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; case IOCTL_NDISPROT_INDICATE_STATUS: NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { NtStatus = WdfRequestForwardToIoQueue(Request, pOpenContext->StatusIndicationQueue ); if(NT_SUCCESS(NtStatus)) { NtStatus = STATUS_PENDING; } } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; default: NtStatus = STATUS_NOT_SUPPORTED; break; } if (NtStatus != STATUS_PENDING) { WdfRequestCompleteWithInformation(Request, NtStatus, BytesReturned); } return; }
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 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 } }
NTSTATUS NdisProtIoControl( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: This is the dispatch routine for handling device ioctl requests. Arguments: pDeviceObject - Pointer to the device object. pIrp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION pIrpSp; ULONG FunctionCode; NTSTATUS NtStatus; NDIS_STATUS Status; PNDISPROT_OPEN_CONTEXT pOpenContext; ULONG BytesReturned; USHORT EthType; #if !DBG UNREFERENCED_PARAMETER(pDeviceObject); #endif DEBUGP(DL_LOUD, ("IoControl: DevObj %p, Irp %p\n", pDeviceObject, pIrp)); pIrpSp = IoGetCurrentIrpStackLocation(pIrp); FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; pOpenContext = (PNDISPROT_OPEN_CONTEXT)pIrpSp->FileObject->FsContext; BytesReturned = 0; switch (FunctionCode) { case IOCTL_NDISPROT_BIND_WAIT: // // Block until we have seen a NetEventBindsComplete event, // meaning that we have finished binding to all running // adapters that we are supposed to bind to. // // If we don't get this event in 5 seconds, time out. // NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (NPROT_WAIT_EVENT(&Globals.BindsComplete, 5000)) { NtStatus = STATUS_SUCCESS; } else { NtStatus = STATUS_TIMEOUT; } DEBUGP(DL_INFO, ("IoControl: BindWait returning %x\n", NtStatus)); break; case IOCTL_NDISPROT_QUERY_BINDING: NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); Status = ndisprotQueryBinding( pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, &BytesReturned ); NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); DEBUGP(DL_LOUD, ("IoControl: QueryBinding returning %x\n", NtStatus)); break; case IOCTL_NDISPROT_OPEN_DEVICE: NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { NPROT_STRUCT_ASSERT(pOpenContext, oc); DEBUGP(DL_WARN, ("IoControl: OPEN_DEVICE: FileObj %p already" " associated with open %p\n", pIrpSp->FileObject, pOpenContext)); NtStatus = STATUS_DEVICE_BUSY; break; } NtStatus = ndisprotOpenDevice( pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength, pIrpSp->FileObject, &pOpenContext ); if (NT_SUCCESS(NtStatus)) { DEBUGP(DL_VERY_LOUD, ("IoControl OPEN_DEVICE: Open %p <-> FileObject %p\n", pOpenContext, pIrpSp->FileObject)); } break; case IOCTL_NDISPROT_QUERY_OID_VALUE: NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { Status = ndisprotQueryOidValue( pOpenContext, pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.OutputBufferLength, &BytesReturned ); NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; case IOCTL_NDISPROT_SET_OID_VALUE: NPROT_ASSERT((FunctionCode & 0x3) == METHOD_BUFFERED); if (pOpenContext != NULL) { Status = ndisprotSetOidValue( pOpenContext, pIrp->AssociatedIrp.SystemBuffer, pIrpSp->Parameters.DeviceIoControl.InputBufferLength ); BytesReturned = 0; NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); } else { NtStatus = STATUS_DEVICE_NOT_CONNECTED; } break; default: NtStatus = STATUS_NOT_SUPPORTED; break; } if (NtStatus != STATUS_PENDING) { pIrp->IoStatus.Information = BytesReturned; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } return NtStatus; }