ZeroTierOne/windows/TapDriver6/mem.c

402 lines
9.4 KiB
C

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