/* * TAP-Windows -- A kernel driver to provide virtual tap * device functionality on Windows. * * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson. * * This source code is Copyright (C) 2002-2014 OpenVPN Technologies, Inc., * and is released under the GPL version 2 (see below). * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //------------------ // Memory Management //------------------ #include "tap.h" PVOID MemAlloc( __in ULONG p_Size, __in BOOLEAN zero ) { PVOID l_Return = NULL; if (p_Size) { __try { if (NdisAllocateMemoryWithTag (&l_Return, p_Size, 'APAT') == NDIS_STATUS_SUCCESS) { if (zero) { NdisZeroMemory (l_Return, p_Size); } } else { l_Return = NULL; } } __except (EXCEPTION_EXECUTE_HANDLER) { l_Return = NULL; } } return l_Return; } VOID MemFree( __in PVOID p_Addr, __in ULONG p_Size ) { if (p_Addr && p_Size) { __try { #if DBG NdisZeroMemory (p_Addr, p_Size); #endif NdisFreeMemory (p_Addr, p_Size, 0); } __except (EXCEPTION_EXECUTE_HANDLER) { } } } //====================================================================== // TAP Packet Queue Support //====================================================================== VOID tapPacketQueueInsertTail( __in PTAP_PACKET_QUEUE TapPacketQueue, __in PTAP_PACKET TapPacket ) { KIRQL irql; KeAcquireSpinLock(&TapPacketQueue->QueueLock,&irql); InsertTailList(&TapPacketQueue->Queue,&TapPacket->QueueLink); // BUGBUG!!! Enforce PACKET_QUEUE_SIZE queue count limit??? // For NDIS 6 there is no per-packet status, so this will need to // be handled on per-NBL basis in AdapterSendNetBufferLists... // Update counts ++TapPacketQueue->Count; if(TapPacketQueue->Count > TapPacketQueue->MaxCount) { TapPacketQueue->MaxCount = TapPacketQueue->Count; DEBUGP (("[TAP] tapPacketQueueInsertTail: New MAX queued packet count = %d\n", TapPacketQueue->MaxCount)); } KeReleaseSpinLock(&TapPacketQueue->QueueLock,irql); } // Call with QueueLock held PTAP_PACKET tapPacketRemoveHeadLocked( __in PTAP_PACKET_QUEUE TapPacketQueue ) { PTAP_PACKET tapPacket = NULL; PLIST_ENTRY listEntry; listEntry = RemoveHeadList(&TapPacketQueue->Queue); if(listEntry != &TapPacketQueue->Queue) { tapPacket = CONTAINING_RECORD(listEntry, TAP_PACKET, QueueLink); // Update counts --TapPacketQueue->Count; } return tapPacket; } PTAP_PACKET tapPacketRemoveHead( __in PTAP_PACKET_QUEUE TapPacketQueue ) { PTAP_PACKET tapPacket = NULL; KIRQL irql; KeAcquireSpinLock(&TapPacketQueue->QueueLock,&irql); tapPacket = tapPacketRemoveHeadLocked(TapPacketQueue); KeReleaseSpinLock(&TapPacketQueue->QueueLock,irql); return tapPacket; } VOID tapPacketQueueInitialize( __in PTAP_PACKET_QUEUE TapPacketQueue ) { KeInitializeSpinLock(&TapPacketQueue->QueueLock); NdisInitializeListHead(&TapPacketQueue->Queue); } //====================================================================== // TAP Cancel-Safe Queue Support //====================================================================== VOID tapIrpCsqInsert ( __in struct _IO_CSQ *Csq, __in PIRP Irp ) { PTAP_IRP_CSQ tapIrpCsq; tapIrpCsq = (PTAP_IRP_CSQ )Csq; InsertTailList( &tapIrpCsq->Queue, &Irp->Tail.Overlay.ListEntry ); // Update counts ++tapIrpCsq->Count; if(tapIrpCsq->Count > tapIrpCsq->MaxCount) { tapIrpCsq->MaxCount = tapIrpCsq->Count; DEBUGP (("[TAP] tapIrpCsqInsert: New MAX queued IRP count = %d\n", tapIrpCsq->MaxCount)); } } VOID tapIrpCsqRemoveIrp( __in PIO_CSQ Csq, __in PIRP Irp ) { PTAP_IRP_CSQ tapIrpCsq; tapIrpCsq = (PTAP_IRP_CSQ )Csq; // Update counts --tapIrpCsq->Count; RemoveEntryList(&Irp->Tail.Overlay.ListEntry); } PIRP tapIrpCsqPeekNextIrp( __in PIO_CSQ Csq, __in PIRP Irp, __in PVOID PeekContext ) { PTAP_IRP_CSQ tapIrpCsq; PIRP nextIrp = NULL; PLIST_ENTRY nextEntry; PLIST_ENTRY listHead; PIO_STACK_LOCATION irpStack; tapIrpCsq = (PTAP_IRP_CSQ )Csq; listHead = &tapIrpCsq->Queue; // // If the IRP is NULL, we will start peeking from the listhead, else // we will start from that IRP onwards. This is done under the // assumption that new IRPs are always inserted at the tail. // if (Irp == NULL) { nextEntry = listHead->Flink; } else { nextEntry = Irp->Tail.Overlay.ListEntry.Flink; } while(nextEntry != listHead) { nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry); irpStack = IoGetCurrentIrpStackLocation(nextIrp); // // If context is present, continue until you find a matching one. // Else you break out as you got next one. // if (PeekContext) { if (irpStack->FileObject == (PFILE_OBJECT) PeekContext) { break; } } else { break; } nextIrp = NULL; nextEntry = nextEntry->Flink; } return nextIrp; } // // tapIrpCsqAcquireQueueLock modifies the execution level of the current processor. // // KeAcquireSpinLock raises the execution level to Dispatch Level and stores // the current execution level in the Irql parameter to be restored at a later // time. KeAcqurieSpinLock also requires us to be running at no higher than // Dispatch level when it is called. // // The annotations reflect these changes and requirments. // __drv_raisesIRQL(DISPATCH_LEVEL) __drv_maxIRQL(DISPATCH_LEVEL) VOID tapIrpCsqAcquireQueueLock( __in PIO_CSQ Csq, __out PKIRQL Irql ) { PTAP_IRP_CSQ tapIrpCsq; tapIrpCsq = (PTAP_IRP_CSQ )Csq; // // Suppressing because the address below csq is valid since it's // part of TAP_ADAPTER_CONTEXT structure. // #pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'adapter->PendingReadCsqQueueLock'") KeAcquireSpinLock(&tapIrpCsq->QueueLock, Irql); } // // tapIrpCsqReleaseQueueLock modifies the execution level of the current processor. // // KeReleaseSpinLock assumes we already hold the spin lock and are therefore // running at Dispatch level. It will use the Irql parameter saved in a // previous call to KeAcquireSpinLock to return the thread back to it's original // execution level. // // The annotations reflect these changes and requirments. // __drv_requiresIRQL(DISPATCH_LEVEL) VOID tapIrpCsqReleaseQueueLock( __in PIO_CSQ Csq, __in KIRQL Irql ) { PTAP_IRP_CSQ tapIrpCsq; tapIrpCsq = (PTAP_IRP_CSQ )Csq; // // Suppressing because the address below csq is valid since it's // part of TAP_ADAPTER_CONTEXT structure. // #pragma prefast(suppress: __WARNING_BUFFER_UNDERFLOW, "Underflow using expression 'adapter->PendingReadCsqQueueLock'") KeReleaseSpinLock(&tapIrpCsq->QueueLock, Irql); } VOID tapIrpCsqCompleteCanceledIrp( __in PIO_CSQ pCsq, __in PIRP Irp ) { UNREFERENCED_PARAMETER(pCsq); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } VOID tapIrpCsqInitialize( __in PTAP_IRP_CSQ TapIrpCsq ) { KeInitializeSpinLock(&TapIrpCsq->QueueLock); NdisInitializeListHead(&TapIrpCsq->Queue); IoCsqInitialize( &TapIrpCsq->CsqQueue, tapIrpCsqInsert, tapIrpCsqRemoveIrp, tapIrpCsqPeekNextIrp, tapIrpCsqAcquireQueueLock, tapIrpCsqReleaseQueueLock, tapIrpCsqCompleteCanceledIrp ); } VOID tapIrpCsqFlush( __in PTAP_IRP_CSQ TapIrpCsq ) { PIRP pendingIrp; // // Flush the pending read IRP queue. // pendingIrp = IoCsqRemoveNextIrp( &TapIrpCsq->CsqQueue, NULL ); while(pendingIrp) { // Cancel the IRP pendingIrp->IoStatus.Information = 0; pendingIrp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(pendingIrp, IO_NO_INCREMENT); pendingIrp = IoCsqRemoveNextIrp( &TapIrpCsq->CsqQueue, NULL ); } ASSERT(IsListEmpty(&TapIrpCsq->Queue)); }