VOID IndicateReceivePacket( __in PTAP_ADAPTER_CONTEXT Adapter, __in PUCHAR packetData, __in const unsigned int packetLength ) { PUCHAR injectBuffer; // // Handle miniport Pause // --------------------- // NDIS 6 miniports implement a temporary "Pause" state normally followed // by the Restart. While in the Pause state it is forbidden for the miniport // to indicate receive NBLs. // // That is: The device interface may be "up", but the NDIS miniport send/receive // interface may be temporarily "down". // // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas inject path // the code below will simply ignore inject packets passed to the driver while // the miniport is in the Paused state. // // The correct implementation is to go ahead and build the NBLs corresponding // to the inject packet - but queue them. When Restart is entered the // queued NBLs would be dequeued and indicated to the host. // if(tapAdapterSendAndReceiveReady(Adapter) != NDIS_STATUS_SUCCESS) { DEBUGP (("[%s] Lying send in IndicateReceivePacket while adapter paused\n", MINIPORT_INSTANCE_ID (Adapter))); return; } // Allocate flat buffer for packet data. injectBuffer = (PUCHAR )NdisAllocateMemoryWithTagPriority( Adapter->MiniportAdapterHandle, packetLength, TAP_RX_INJECT_BUFFER_TAG, NormalPoolPriority ); if( injectBuffer) { PMDL mdl; // Copy packet data to flat buffer. NdisMoveMemory (injectBuffer, packetData, packetLength); // Allocate MDL for flat buffer. mdl = NdisAllocateMdl( Adapter->MiniportAdapterHandle, injectBuffer, packetLength ); if( mdl ) { PNET_BUFFER_LIST netBufferList; mdl->Next = NULL; // No next MDL // Allocate the NBL and NB. Link MDL chain to NB. netBufferList = NdisAllocateNetBufferAndNetBufferList( Adapter->ReceiveNblPool, 0, // ContextSize 0, // ContextBackFill mdl, // MDL chain 0, packetLength ); if(netBufferList != NULL) { ULONG receiveFlags = 0; LONG nblCount; NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL if(KeGetCurrentIrql() == DISPATCH_LEVEL) { receiveFlags |= NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL; } // Set flag indicating that this is an injected packet TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED); netBufferList->MiniportReserved[0] = NULL; netBufferList->MiniportReserved[1] = NULL; // Increment in-flight receive NBL count. nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); ASSERT(nblCount > 0 ); netBufferList->SourceHandle = Adapter->MiniportAdapterHandle; // // Indicate the packet // ------------------- // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length // contains the complete packet including Ethernet header and payload. // NdisMIndicateReceiveNetBufferLists( Adapter->MiniportAdapterHandle, netBufferList, NDIS_DEFAULT_PORT_NUMBER, 1, // NumberOfNetBufferLists receiveFlags ); return; } else { DEBUGP (("[%s] NdisAllocateNetBufferAndNetBufferList failed in IndicateReceivePacket\n", MINIPORT_INSTANCE_ID (Adapter))); NOTE_ERROR (); NdisFreeMdl(mdl); NdisFreeMemory(injectBuffer,0,0); } } else { DEBUGP (("[%s] NdisAllocateMdl failed in IndicateReceivePacket\n", MINIPORT_INSTANCE_ID (Adapter))); NOTE_ERROR (); NdisFreeMemory(injectBuffer,0,0); } } else { DEBUGP (("[%s] NdisAllocateMemoryWithTagPriority failed in IndicateReceivePacket\n", MINIPORT_INSTANCE_ID (Adapter))); NOTE_ERROR (); } }
// IRP_MJ_CREATE NTSTATUS TapDeviceCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) /*++ Routine Description: This routine is called by the I/O system when the device is opened. No action is performed other than completing the request successfully. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: NT status code --*/ { NDIS_STATUS status; PIO_STACK_LOCATION irpSp; // Pointer to current stack location PTAP_ADAPTER_CONTEXT adapter = NULL; PFILE_OBJECT originalFileObject; PAGED_CODE(); DEBUGP(("[TAP] --> TapDeviceCreate\n")); irpSp = IoGetCurrentIrpStackLocation(Irp); // // Invalidate file context // irpSp->FileObject->FsContext = NULL; irpSp->FileObject->FsContext2 = NULL; // // Find adapter context for this device. // ------------------------------------- // Returns with added reference on adapter context. // adapter = tapAdapterContextFromDeviceObject(DeviceObject); // Insure that adapter exists. ASSERT(adapter); if (adapter == NULL) { DEBUGP(("[TAP] release [%d.%d] open request; adapter not found\n", TAP_DRIVER_MAJOR_VERSION, TAP_DRIVER_MINOR_VERSION)); Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_DEVICE_DOES_NOT_EXIST; } DEBUGP(("[%s] [TAP] release [%d.%d] open request (TapFileIsOpen=%d)\n", MINIPORT_INSTANCE_ID(adapter), TAP_DRIVER_MAJOR_VERSION, TAP_DRIVER_MINOR_VERSION, adapter->TapFileIsOpen)); // Enforce exclusive access originalFileObject = InterlockedCompareExchangePointer(&adapter->TapFileObject, irpSp->FileObject, NULL); if (originalFileObject == NULL) { irpSp->FileObject->FsContext = adapter; // Quick reference status = STATUS_SUCCESS; } else { status = STATUS_UNSUCCESSFUL; } // Release the lock. // tapAdapterReleaseLock(adapter,FALSE); if (status == STATUS_SUCCESS) { // Reset adapter state on successful open. tapResetAdapterState(adapter); adapter->TapFileIsOpen = 1; // Legacy... // NOTE!!! Reference added by tapAdapterContextFromDeviceObject // will be removed when file is closed. } else { DEBUGP(("[%s] TAP is presently unavailable (TapFileIsOpen=%d)\n", MINIPORT_INSTANCE_ID(adapter), adapter->TapFileIsOpen)); NOTE_ERROR(); // Remove reference added by tapAdapterContextFromDeviceObject. tapAdapterContextDereference(adapter); } // Complete the IRP. Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); DEBUGP(("[TAP] <-- TapDeviceCreate; status = %8.8X\n", status)); return status; }
// IRP_MJ_WRITE callback. NTSTATUS TapDeviceWrite( PDEVICE_OBJECT DeviceObject, PIRP Irp ) { NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success PIO_STACK_LOCATION irpSp;// Pointer to current stack location PTAP_ADAPTER_CONTEXT adapter = NULL; ULONG dataLength; PAGED_CODE(); irpSp = IoGetCurrentIrpStackLocation( Irp ); // // Fetch adapter context for this device. // -------------------------------------- // Adapter pointer was stashed in FsContext when handle was opened. // adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext; ASSERT(adapter); // // Sanity checks on state variables // if (!tapAdapterReadAndWriteReady(adapter)) { //DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n", // MINIPORT_INSTANCE_ID (adapter))); //NOTE_ERROR(); Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); return ntStatus; } // Save IRP-accessible copy of buffer length Irp->IoStatus.Information = irpSp->Parameters.Write.Length; if (Irp->MdlAddress == NULL) { DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n", MINIPORT_INSTANCE_ID (adapter))); NOTE_ERROR(); Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); return ntStatus; } // // Try to get a virtual address for the MDL. // NdisQueryMdl( Irp->MdlAddress, &Irp->AssociatedIrp.SystemBuffer, &dataLength, NormalPagePriority ); if (Irp->AssociatedIrp.SystemBuffer == NULL) { DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n", MINIPORT_INSTANCE_ID (adapter))); NOTE_ERROR(); Irp->IoStatus.Status = ntStatus = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); return ntStatus; } ASSERT(dataLength == irpSp->Parameters.Write.Length); Irp->IoStatus.Information = irpSp->Parameters.Write.Length; // // Handle miniport Pause // --------------------- // NDIS 6 miniports implement a temporary "Pause" state normally followed // by the Restart. While in the Pause state it is forbidden for the miniport // to indicate receive NBLs. // // That is: The device interface may be "up", but the NDIS miniport send/receive // interface may be temporarily "down". // // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas receive path // the code below will perform a "lying send" for write IRPs passed to the // driver while the miniport is in the Paused state. // // The correct implementation is to go ahead and build the NBLs corresponding // to the user-mode write - but queue them. When Restart is entered the // queued NBLs would be dequeued and indicated to the host. // if(tapAdapterSendAndReceiveReady(adapter) == NDIS_STATUS_SUCCESS) { if (!adapter->m_tun && ((irpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE)) { PNET_BUFFER_LIST netBufferList; DUMP_PACKET ("IRP_MJ_WRITE ETH", (unsigned char *) Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.Write.Length); //===================================================== // If IPv4 packet, check whether or not packet // was truncated. //===================================================== #if PACKET_TRUNCATION_CHECK IPv4PacketSizeVerify ( (unsigned char *) Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.Write.Length, FALSE, "RX", &adapter->m_RxTrunc ); #endif (Irp->MdlAddress)->Next = NULL; // No next MDL // Allocate the NBL and NB. Link MDL chain to NB. netBufferList = NdisAllocateNetBufferAndNetBufferList( adapter->ReceiveNblPool, 0, // ContextSize 0, // ContextBackFill Irp->MdlAddress, // MDL chain 0, dataLength ); if(netBufferList != NULL) { LONG nblCount; NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL // Stash IRP pointer in NBL MiniportReserved[0] field. netBufferList->MiniportReserved[0] = Irp; netBufferList->MiniportReserved[1] = NULL; // BUGBUG!!! Setup for IRP cancel!!! TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); // Increment in-flight receive NBL count. nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount); ASSERT(nblCount > 0 ); // // Indicate the packet // ------------------- // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length // contains the complete packet including Ethernet header and payload. // NdisMIndicateReceiveNetBufferLists( adapter->MiniportAdapterHandle, netBufferList, NDIS_DEFAULT_PORT_NUMBER, 1, // NumberOfNetBufferLists 0 // ReceiveFlags ); ntStatus = STATUS_PENDING; } else { DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n", MINIPORT_INSTANCE_ID (adapter))); NOTE_ERROR (); // Fail the IRP Irp->IoStatus.Information = 0; ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } else if (adapter->m_tun && ((irpSp->Parameters.Write.Length) >= IP_HEADER_SIZE)) { PETH_HEADER p_UserToTap = &adapter->m_UserToTap; PMDL mdl; // Head of MDL chain. // For IPv6, need to use Ethernet header with IPv6 proto if ( IPH_GET_VER( ((IPHDR*) Irp->AssociatedIrp.SystemBuffer)->version_len) == 6 ) { p_UserToTap = &adapter->m_UserToTap_IPv6; } DUMP_PACKET2 ("IRP_MJ_WRITE P2P", p_UserToTap, (unsigned char *) Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.Write.Length); //===================================================== // If IPv4 packet, check whether or not packet // was truncated. //===================================================== #if PACKET_TRUNCATION_CHECK IPv4PacketSizeVerify ( (unsigned char *) Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.Write.Length, TRUE, "RX", &adapter->m_RxTrunc ); #endif // // Allocate MDL for Ethernet header // -------------------------------- // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length // contains the only the Ethernet payload. Prepend the user-mode provided // payload with the Ethernet header pointed to by p_UserToTap. // mdl = NdisAllocateMdl( adapter->MiniportAdapterHandle, p_UserToTap, sizeof(ETH_HEADER) ); if(mdl != NULL) { PNET_BUFFER_LIST netBufferList; // Chain user's Ethernet payload behind Ethernet header. mdl->Next = Irp->MdlAddress; (Irp->MdlAddress)->Next = NULL; // No next MDL // Allocate the NBL and NB. Link MDL chain to NB. netBufferList = NdisAllocateNetBufferAndNetBufferList( adapter->ReceiveNblPool, 0, // ContextSize 0, // ContextBackFill mdl, // MDL chain 0, sizeof(ETH_HEADER) + dataLength ); if(netBufferList != NULL) { LONG nblCount; NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL // This IRP is pended. IoMarkIrpPending(Irp); // This IRP cannot be cancelled while in-flight. IoSetCancelRoutine(Irp,NULL); // Stash IRP pointer in NBL MiniportReserved[0] field. netBufferList->MiniportReserved[0] = Irp; netBufferList->MiniportReserved[1] = NULL; // Set flag indicating that this is P2P packet TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList); TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_P2P); // Increment in-flight receive NBL count. nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount); ASSERT(nblCount > 0 ); // // Indicate the packet // NdisMIndicateReceiveNetBufferLists( adapter->MiniportAdapterHandle, netBufferList, NDIS_DEFAULT_PORT_NUMBER, 1, // NumberOfNetBufferLists 0 // ReceiveFlags ); ntStatus = STATUS_PENDING; } else { mdl->Next = NULL; NdisFreeMdl(mdl); DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n", MINIPORT_INSTANCE_ID (adapter))); NOTE_ERROR (); // Fail the IRP Irp->IoStatus.Information = 0; ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } else { DEBUGP (("[%s] NdisAllocateMdl failed in IRP_MJ_WRITE\n", MINIPORT_INSTANCE_ID (adapter))); NOTE_ERROR (); // Fail the IRP Irp->IoStatus.Information = 0; ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } else { DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n", MINIPORT_INSTANCE_ID (adapter), irpSp->Parameters.Write.Length)); NOTE_ERROR (); Irp->IoStatus.Information = 0; // ETHERNET_HEADER_SIZE; Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL; } } else { DEBUGP (("[%s] Lying send in IRP_MJ_WRITE while adapter paused\n", MINIPORT_INSTANCE_ID (adapter))); ntStatus = STATUS_SUCCESS; } if (ntStatus != STATUS_PENDING) { Irp->IoStatus.Status = ntStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return ntStatus; }
// IRP_MJ_DEVICE_CONTROL callback. NTSTATUS TapDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) /*++ Routine Description: This routine is called by the I/O system to perform a device I/O control function. Arguments: DeviceObject - a pointer to the object that represents the device that I/O is to be done on. Irp - a pointer to the I/O Request Packet for this request. Return Value: NT status code --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; // Assume success PIO_STACK_LOCATION irpSp; // Pointer to current stack location PTAP_ADAPTER_CONTEXT adapter = NULL; ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length PCHAR inBuf, outBuf; // pointer to Input and output buffer PMDL mdl = NULL; PCHAR buffer = NULL; PAGED_CODE(); irpSp = IoGetCurrentIrpStackLocation(Irp); // // Fetch adapter context for this device. // -------------------------------------- // Adapter pointer was stashed in FsContext when handle was opened. // adapter = (PTAP_ADAPTER_CONTEXT)(irpSp->FileObject)->FsContext; ASSERT(adapter); inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; if (!inBufLength || !outBufLength) { ntStatus = STATUS_INVALID_PARAMETER; goto End; } // // Determine which I/O control code was specified. // switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case TAP_WIN_IOCTL_GET_MAC: { if (outBufLength >= MACADDR_SIZE) { ETH_COPY_NETWORK_ADDRESS(Irp->AssociatedIrp.SystemBuffer, adapter->CurrentAddress); Irp->IoStatus.Information = MACADDR_SIZE; } else { NOTE_ERROR(); Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL; } } break; case TAP_WIN_IOCTL_GET_VERSION: { const ULONG size = sizeof(ULONG) * 3; if (outBufLength >= size) { ((PULONG)(Irp->AssociatedIrp.SystemBuffer))[0] = TAP_DRIVER_MAJOR_VERSION; ((PULONG)(Irp->AssociatedIrp.SystemBuffer))[1] = TAP_DRIVER_MINOR_VERSION; ((PULONG)(Irp->AssociatedIrp.SystemBuffer))[2] #if DBG = 1; #else = 0; #endif Irp->IoStatus.Information = size; } else {