mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-22 14:22:23 +00:00
402 lines
9.4 KiB
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));
|
|
}
|