2019-03-08 22:33:15 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2019-05-30 21:39:33 +02:00
|
|
|
#include <ntifs.h>
|
2019-03-08 22:33:15 +01:00
|
|
|
#include <wdm.h>
|
|
|
|
#include <wdmsec.h>
|
|
|
|
#include <ndis.h>
|
|
|
|
#include <bcrypt.h>
|
|
|
|
#include <ntstrsafe.h>
|
2019-05-30 21:39:33 +02:00
|
|
|
#include "undocumented.h"
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-27 15:08:29 +02:00
|
|
|
#pragma warning(disable : 4100) /* unreferenced formal parameter */
|
|
|
|
#pragma warning(disable : 4200) /* nonstandard: zero-sized array in struct/union */
|
|
|
|
#pragma warning(disable : 4204) /* nonstandard: non-constant aggregate initializer */
|
|
|
|
#pragma warning(disable : 4221) /* nonstandard: cannot be initialized using address of automatic variable */
|
|
|
|
#pragma warning(disable : 6320) /* exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER */
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
#define NDIS_MINIPORT_VERSION_MIN ((NDIS_MINIPORT_MINIMUM_MAJOR_VERSION << 16) | NDIS_MINIPORT_MINIMUM_MINOR_VERSION)
|
|
|
|
#define NDIS_MINIPORT_VERSION_MAX ((NDIS_MINIPORT_MAJOR_VERSION << 16) | NDIS_MINIPORT_MINOR_VERSION)
|
|
|
|
|
|
|
|
#define TUN_DEVICE_NAME L"WINTUN%u"
|
|
|
|
|
|
|
|
#define TUN_VENDOR_NAME "Wintun Tunnel"
|
|
|
|
#define TUN_VENDOR_ID 0xFFFFFF00
|
2019-06-27 15:08:29 +02:00
|
|
|
#define TUN_LINK_SPEED 100000000000ULL /* 100gbps */
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Maximum number of full-sized exchange packets that can be exchanged in a single read/write. */
|
2019-06-26 19:37:29 +02:00
|
|
|
#define TUN_EXCH_MAX_PACKETS 256
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Maximum exchange packet size - empirically determined by net buffer list (pool) limitations */
|
2019-06-26 19:37:29 +02:00
|
|
|
#define TUN_EXCH_MAX_PACKET_SIZE 0xF000
|
2019-07-04 23:06:17 +02:00
|
|
|
#define TUN_EXCH_ALIGNMENT sizeof(ULONG) /* Memory alignment in exchange buffers */
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Maximum IP packet size (headers + payload) */
|
2019-06-26 19:37:29 +02:00
|
|
|
#define TUN_EXCH_MAX_IP_PACKET_SIZE (TUN_EXCH_MAX_PACKET_SIZE - sizeof(TUN_PACKET))
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Maximum size of read/write exchange buffer */
|
2019-06-26 19:37:29 +02:00
|
|
|
#define TUN_EXCH_MAX_BUFFER_SIZE (TUN_EXCH_MAX_PACKETS * TUN_EXCH_MAX_PACKET_SIZE)
|
2019-06-27 15:08:29 +02:00
|
|
|
#define TUN_EXCH_MIN_BUFFER_SIZE_READ TUN_EXCH_MAX_PACKET_SIZE /* Minimum size of read exchange buffer */
|
|
|
|
#define TUN_EXCH_MIN_BUFFER_SIZE_WRITE (sizeof(TUN_PACKET)) /* Minimum size of write exchange buffer */
|
2019-06-26 14:52:38 +02:00
|
|
|
#define TUN_QUEUE_MAX_NBLS 1000
|
|
|
|
#define TUN_CSQ_INSERT_HEAD ((PVOID)TRUE)
|
|
|
|
#define TUN_CSQ_INSERT_TAIL ((PVOID)FALSE)
|
2019-06-07 12:23:44 +02:00
|
|
|
|
|
|
|
#if REG_DWORD == REG_DWORD_BIG_ENDIAN
|
2019-06-26 14:52:38 +02:00
|
|
|
# define TUN_HTONS(x) ((USHORT)(x))
|
|
|
|
# define TUN_HTONL(x) ((ULONG)(x))
|
2019-06-07 12:23:44 +02:00
|
|
|
#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN
|
2019-06-26 19:37:29 +02:00
|
|
|
# define TUN_HTONS(x) ((((USHORT)(x)&0x00ff) << 8) | (((USHORT)(x)&0xff00) >> 8))
|
2019-06-26 14:52:38 +02:00
|
|
|
# define TUN_HTONL(x) \
|
2019-06-26 19:37:29 +02:00
|
|
|
((((ULONG)(x)&0x000000ff) << 24) | (((ULONG)(x)&0x0000ff00) << 8) | (((ULONG)(x)&0x00ff0000) >> 8) | \
|
|
|
|
(((ULONG)(x)&0xff000000) >> 24))
|
2019-06-07 12:23:44 +02:00
|
|
|
#else
|
2019-06-26 14:52:38 +02:00
|
|
|
# error "Unable to determine endianess"
|
2019-06-07 12:23:44 +02:00
|
|
|
#endif
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-07-03 12:43:55 +02:00
|
|
|
#define TUN_MEMORY_TAG TUN_HTONL('wtun')
|
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
typedef struct _TUN_PACKET
|
|
|
|
{
|
2019-06-27 15:08:29 +02:00
|
|
|
ULONG Size; /* Size of packet data (TUN_EXCH_MAX_IP_PACKET_SIZE max) */
|
2019-07-04 23:06:17 +02:00
|
|
|
_Field_size_bytes_(Size)
|
|
|
|
UCHAR Data[]; /* Packet data */
|
2019-03-08 22:33:15 +01:00
|
|
|
} TUN_PACKET;
|
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
typedef enum _TUN_FLAGS
|
|
|
|
{
|
2019-06-27 15:08:29 +02:00
|
|
|
TUN_FLAGS_RUNNING = 1 << 0, /* Toggles between paused and running state */
|
|
|
|
TUN_FLAGS_PRESENT = 1 << 1, /* Toggles between removal pending and being present */
|
2019-06-10 15:02:10 +02:00
|
|
|
} TUN_FLAGS;
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
typedef struct _TUN_CTX
|
|
|
|
{
|
|
|
|
volatile LONG Flags;
|
|
|
|
|
|
|
|
/* Used like RCU. When we're making use of queues, we take a shared lock. When we want to
|
|
|
|
* drain the queues and toggle the state, we take an exclusive lock before toggling the
|
|
|
|
* atomic and then releasing. It's similar to setting the atomic and then calling rcu_barrier(). */
|
|
|
|
EX_SPIN_LOCK TransitionLock;
|
|
|
|
|
2019-06-27 15:08:29 +02:00
|
|
|
NDIS_HANDLE MiniportAdapterHandle; /* This is actually a pointer to NDIS_MINIPORT_BLOCK struct. */
|
2019-06-26 14:52:38 +02:00
|
|
|
NDIS_STATISTICS_INFO Statistics;
|
|
|
|
|
|
|
|
volatile LONG64 ActiveNBLCount;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
NDIS_HANDLE Handle;
|
|
|
|
volatile LONG64 RefCount;
|
|
|
|
IO_REMOVE_LOCK RemoveLock;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
KSPIN_LOCK Lock;
|
|
|
|
IO_CSQ Csq;
|
|
|
|
LIST_ENTRY List;
|
|
|
|
} ReadQueue;
|
|
|
|
|
|
|
|
DEVICE_OBJECT *Object;
|
|
|
|
} Device;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
KSPIN_LOCK Lock;
|
|
|
|
NET_BUFFER_LIST *FirstNbl, *LastNbl;
|
|
|
|
NET_BUFFER *NextNb;
|
|
|
|
LONG NumNbl;
|
|
|
|
} PacketQueue;
|
|
|
|
|
|
|
|
NDIS_HANDLE NBLPool;
|
2019-03-08 22:33:15 +01:00
|
|
|
} TUN_CTX;
|
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
typedef struct _TUN_MAPPED_UBUFFER
|
|
|
|
{
|
2019-06-27 11:08:57 +02:00
|
|
|
VOID *volatile UserAddress;
|
|
|
|
VOID *KernelAddress;
|
2019-06-26 14:52:38 +02:00
|
|
|
MDL *Mdl;
|
|
|
|
ULONG Size;
|
2019-06-27 11:08:57 +02:00
|
|
|
FAST_MUTEX InitializationComplete;
|
2019-06-27 15:08:29 +02:00
|
|
|
/* TODO: ThreadID for checking */
|
2019-06-26 12:30:52 +02:00
|
|
|
} TUN_MAPPED_UBUFFER;
|
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
typedef struct _TUN_FILE_CTX
|
|
|
|
{
|
|
|
|
TUN_MAPPED_UBUFFER ReadBuffer;
|
2019-06-26 12:30:52 +02:00
|
|
|
} TUN_FILE_CTX;
|
|
|
|
|
2019-04-03 03:01:28 +02:00
|
|
|
static UINT NdisVersion;
|
2019-05-31 00:50:07 +02:00
|
|
|
static NDIS_HANDLE NdisMiniportDriverHandle;
|
2019-06-20 09:46:52 +02:00
|
|
|
static DRIVER_DISPATCH *NdisDispatchPnP;
|
2019-06-20 13:42:35 +02:00
|
|
|
static volatile LONG64 TunAdapterCount;
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
#define InterlockedGet(val) (InterlockedAdd((val), 0))
|
|
|
|
#define InterlockedGet64(val) (InterlockedAdd64((val), 0))
|
|
|
|
#define InterlockedGetPointer(val) (InterlockedCompareExchangePointer((val), NULL, NULL))
|
|
|
|
#define TunPacketAlign(size) (((UINT)(size) + (UINT)(TUN_EXCH_ALIGNMENT - 1)) & ~(UINT)(TUN_EXCH_ALIGNMENT - 1))
|
|
|
|
#define TunInitUnicodeString(str, buf) \
|
|
|
|
{ \
|
|
|
|
(str)->Length = 0; \
|
|
|
|
(str)->MaximumLength = sizeof(buf); \
|
|
|
|
(str)->Buffer = buf; \
|
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-07-03 12:50:01 +02:00
|
|
|
_IRQL_requires_same_
|
|
|
|
static void
|
2019-06-26 14:52:38 +02:00
|
|
|
TunIndicateStatus(_In_ NDIS_HANDLE MiniportAdapterHandle, _In_ NDIS_MEDIA_CONNECT_STATE MediaConnectState)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_LINK_STATE State = { .Header = { .Type = NDIS_OBJECT_TYPE_DEFAULT,
|
2019-06-26 18:59:02 +02:00
|
|
|
.Revision = NDIS_LINK_STATE_REVISION_1,
|
|
|
|
.Size = NDIS_SIZEOF_LINK_STATE_REVISION_1 },
|
|
|
|
.MediaConnectState = MediaConnectState,
|
|
|
|
.MediaDuplexState = MediaDuplexStateFull,
|
|
|
|
.XmitLinkSpeed = TUN_LINK_SPEED,
|
|
|
|
.RcvLinkSpeed = TUN_LINK_SPEED,
|
|
|
|
.PauseFunctions = NdisPauseFunctionsUnsupported };
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_STATUS_INDICATION Indication = { .Header = { .Type = NDIS_OBJECT_TYPE_STATUS_INDICATION,
|
|
|
|
.Revision = NDIS_STATUS_INDICATION_REVISION_1,
|
|
|
|
.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1 },
|
|
|
|
.SourceHandle = MiniportAdapterHandle,
|
|
|
|
.StatusCode = NDIS_STATUS_LINK_STATE,
|
|
|
|
.StatusBuffer = &State,
|
|
|
|
.StatusBufferSize = sizeof(State) };
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NdisMIndicateStatusEx(MiniportAdapterHandle, &Indication);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunCompleteRequest(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp, _In_ NTSTATUS Status, _In_ CCHAR PriorityBoost)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
IoCompleteRequest(Irp, PriorityBoost);
|
|
|
|
IoReleaseRemoveLock(&Ctx->Device.RemoveLock, Irp);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunCompletePause(_Inout_ TUN_CTX *Ctx, _In_ BOOLEAN AsyncCompletion)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
ASSERT(InterlockedGet64(&Ctx->ActiveNBLCount) > 0);
|
|
|
|
if (InterlockedDecrement64(&Ctx->ActiveNBLCount) <= 0)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
if (AsyncCompletion)
|
|
|
|
NdisMPauseComplete(Ctx->MiniportAdapterHandle);
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NDIS_STATUS_PENDING;
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-03-27 09:00:19 +01:00
|
|
|
static IO_CSQ_INSERT_IRP_EX TunCsqInsertIrpEx;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
|
|
|
TunCsqInsertIrpEx(IO_CSQ *Csq, IRP *Irp, PVOID InsertContext)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
|
|
|
if (InsertContext == TUN_CSQ_INSERT_HEAD)
|
|
|
|
InsertHeadList(&Ctx->Device.ReadQueue.List, &Irp->Tail.Overlay.ListEntry);
|
|
|
|
else
|
|
|
|
InsertTailList(&Ctx->Device.ReadQueue.List, &Irp->Tail.Overlay.ListEntry);
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_SUCCESS;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static IO_CSQ_REMOVE_IRP TunCsqRemoveIrp;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static VOID
|
|
|
|
TunCsqRemoveIrp(IO_CSQ *Csq, IRP *Irp)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static IO_CSQ_PEEK_NEXT_IRP TunCsqPeekNextIrp;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static IRP *
|
|
|
|
TunCsqPeekNextIrp(IO_CSQ *Csq, IRP *Irp, _In_ PVOID PeekContext)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
/* If the IRP is non-NULL, we will start peeking from that IRP onwards, else
|
|
|
|
* we will start from the listhead. This is done under the assumption that
|
|
|
|
* new IRPs are always inserted at the tail. */
|
2019-06-29 13:34:15 +02:00
|
|
|
for (LIST_ENTRY *Head = &Ctx->Device.ReadQueue.List, *Next = Irp ? Irp->Tail.Overlay.ListEntry.Flink : Head->Flink;
|
|
|
|
Next != Head;
|
|
|
|
Next = Next->Flink)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
IRP *NextIrp = CONTAINING_RECORD(Next, IRP, Tail.Overlay.ListEntry);
|
2019-06-26 14:52:38 +02:00
|
|
|
if (!PeekContext)
|
2019-06-29 13:34:15 +02:00
|
|
|
return NextIrp;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(NextIrp);
|
|
|
|
if (Stack->FileObject == (FILE_OBJECT *)PeekContext)
|
|
|
|
return NextIrp;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_raises_(DISPATCH_LEVEL)
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-05-31 08:42:36 +02:00
|
|
|
_Requires_lock_not_held_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
|
2019-06-26 19:37:29 +02:00
|
|
|
_Acquires_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
|
2019-06-26 14:52:38 +02:00
|
|
|
static VOID
|
|
|
|
TunCsqAcquireLock(_In_ IO_CSQ *Csq, _Out_ _At_(*Irql, _Post_ _IRQL_saves_) KIRQL *Irql)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
KeAcquireSpinLock(&CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock, Irql);
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_(DISPATCH_LEVEL)
|
2019-05-31 08:42:36 +02:00
|
|
|
_Requires_lock_held_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
|
2019-04-10 13:42:16 +02:00
|
|
|
_Releases_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
|
2019-06-26 14:52:38 +02:00
|
|
|
static VOID
|
|
|
|
TunCsqReleaseLock(_In_ IO_CSQ *Csq, _In_ _IRQL_restores_ KIRQL Irql)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
KeReleaseSpinLock(&CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock, Irql);
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static IO_CSQ_COMPLETE_CANCELED_IRP TunCsqCompleteCanceledIrp;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static VOID
|
|
|
|
TunCsqCompleteCanceledIrp(IO_CSQ *Csq, IRP *Irp)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
|
|
|
TunCompleteRequest(Ctx, Irp, STATUS_CANCELLED, IO_NO_INCREMENT);
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
2019-06-26 12:30:52 +02:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
|
|
|
TunMapUbuffer(_Inout_ TUN_MAPPED_UBUFFER *MappedBuffer, _In_ VOID *UserAddress, _In_ ULONG Size)
|
2019-06-26 12:30:52 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
VOID *CurrentUserAddress = InterlockedGetPointer(&MappedBuffer->UserAddress);
|
|
|
|
if (CurrentUserAddress)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
if (UserAddress != CurrentUserAddress || Size > MappedBuffer->Size) /* TODO: Check ThreadID */
|
2019-06-27 11:41:43 +02:00
|
|
|
return STATUS_ALREADY_INITIALIZED;
|
|
|
|
return STATUS_SUCCESS;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-06-26 21:25:03 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
2019-06-27 11:08:57 +02:00
|
|
|
ExAcquireFastMutex(&MappedBuffer->InitializationComplete);
|
|
|
|
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Recheck the same thing as above, but locked this time. */
|
2019-06-29 13:34:15 +02:00
|
|
|
CurrentUserAddress = InterlockedGetPointer(&MappedBuffer->UserAddress);
|
|
|
|
if (CurrentUserAddress)
|
2019-06-27 11:08:57 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
if (UserAddress != CurrentUserAddress || Size > MappedBuffer->Size) /* TODO: Check ThreadID */
|
|
|
|
Status = STATUS_ALREADY_INITIALIZED;
|
2019-06-27 11:08:57 +02:00
|
|
|
goto err_releasemutex;
|
|
|
|
}
|
|
|
|
|
2019-06-26 21:25:03 +02:00
|
|
|
MappedBuffer->Mdl = IoAllocateMdl(UserAddress, Size, FALSE, FALSE, NULL);
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
2019-06-26 21:25:03 +02:00
|
|
|
if (!MappedBuffer->Mdl)
|
2019-06-27 11:08:57 +02:00
|
|
|
goto err_releasemutex;
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
2019-06-26 19:37:29 +02:00
|
|
|
try
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-26 21:25:03 +02:00
|
|
|
MmProbeAndLockPages(MappedBuffer->Mdl, UserMode, IoWriteAccess);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-06-27 11:08:57 +02:00
|
|
|
except(EXCEPTION_EXECUTE_HANDLER) { goto err_freemdl; }
|
|
|
|
|
2019-06-26 21:25:03 +02:00
|
|
|
MappedBuffer->KernelAddress =
|
|
|
|
MmGetSystemAddressForMdlSafe(MappedBuffer->Mdl, NormalPagePriority | MdlMappingNoExecute);
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
2019-06-26 21:25:03 +02:00
|
|
|
if (!MappedBuffer->KernelAddress)
|
2019-06-27 11:08:57 +02:00
|
|
|
goto err_unlockmdl;
|
2019-06-26 21:25:03 +02:00
|
|
|
MappedBuffer->Size = Size;
|
2019-06-27 11:08:57 +02:00
|
|
|
InterlockedExchangePointer(&MappedBuffer->UserAddress, UserAddress);
|
|
|
|
ExReleaseFastMutex(&MappedBuffer->InitializationComplete);
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_SUCCESS;
|
2019-06-27 11:08:57 +02:00
|
|
|
|
|
|
|
err_unlockmdl:
|
|
|
|
MmUnlockPages(MappedBuffer->Mdl);
|
|
|
|
err_freemdl:
|
|
|
|
IoFreeMdl(MappedBuffer->Mdl);
|
|
|
|
MappedBuffer->Mdl = NULL;
|
|
|
|
err_releasemutex:
|
|
|
|
ExReleaseFastMutex(&MappedBuffer->InitializationComplete);
|
2019-06-29 13:34:15 +02:00
|
|
|
return Status;
|
2019-06-26 12:30:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunUnmapUbuffer(_Inout_ TUN_MAPPED_UBUFFER *MappedBuffer)
|
2019-06-26 12:30:52 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
if (MappedBuffer->Mdl)
|
|
|
|
{
|
|
|
|
MmUnlockPages(MappedBuffer->Mdl);
|
|
|
|
IoFreeMdl(MappedBuffer->Mdl);
|
|
|
|
MappedBuffer->UserAddress = MappedBuffer->KernelAddress = MappedBuffer->Mdl = NULL;
|
|
|
|
}
|
2019-06-26 12:30:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
|
|
|
TunMapIrp(_In_ IRP *Irp)
|
2019-06-26 12:30:52 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
ULONG Size;
|
|
|
|
TUN_MAPPED_UBUFFER *UserBuffer;
|
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
TUN_FILE_CTX *FileCtx = (TUN_FILE_CTX *)Stack->FileObject->FsContext;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
switch (Stack->MajorFunction)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
|
|
|
case IRP_MJ_READ:
|
2019-06-29 13:34:15 +02:00
|
|
|
Size = Stack->Parameters.Read.Length;
|
|
|
|
if (Size < TUN_EXCH_MIN_BUFFER_SIZE_READ)
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_INVALID_USER_BUFFER;
|
2019-06-29 13:34:15 +02:00
|
|
|
UserBuffer = &FileCtx->ReadBuffer;
|
2019-06-26 14:52:38 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
if (Size > TUN_EXCH_MAX_BUFFER_SIZE)
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_INVALID_USER_BUFFER;
|
2019-06-29 13:34:15 +02:00
|
|
|
return TunMapUbuffer(UserBuffer, Irp->UserBuffer, Size);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-03-28 08:30:13 +01:00
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-03-28 11:11:43 +01:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static _Return_type_success_(
|
2019-06-26 19:37:29 +02:00
|
|
|
return != NULL) IRP *TunRemoveNextIrp(_Inout_ TUN_CTX *Ctx, _Out_ UCHAR **Buffer, _Out_ ULONG *Size)
|
2019-03-28 08:30:13 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
IRP *Irp = IoCsqRemoveNextIrp(&Ctx->Device.ReadQueue.Csq, NULL);
|
|
|
|
if (!Irp)
|
2019-06-26 14:52:38 +02:00
|
|
|
return NULL;
|
2019-06-29 13:34:15 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
*Size = Stack->Parameters.Read.Length;
|
|
|
|
ASSERT(Irp->IoStatus.Information <= (ULONG_PTR)*Size);
|
|
|
|
*Buffer = ((TUN_FILE_CTX *)Stack->FileObject->FsContext)->ReadBuffer.KernelAddress;
|
|
|
|
return Irp;
|
2019-03-28 08:30:13 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 12:50:01 +02:00
|
|
|
_IRQL_requires_same_
|
|
|
|
static BOOLEAN
|
2019-06-26 19:37:29 +02:00
|
|
|
TunWontFitIntoIrp(_In_ IRP *Irp, _In_ ULONG Size, _In_ NET_BUFFER *Nb)
|
2019-03-28 11:45:57 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
return (ULONG_PTR)Size <
|
|
|
|
Irp->IoStatus.Information + TunPacketAlign(sizeof(TUN_PACKET) + NET_BUFFER_DATA_LENGTH(Nb));
|
2019-03-28 11:45:57 +01:00
|
|
|
}
|
|
|
|
|
2019-03-08 22:33:15 +01:00
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-03-28 11:21:51 +01:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunWriteIntoIrp(_Inout_ IRP *Irp, _Inout_ UCHAR *Buffer, _In_ NET_BUFFER *Nb, _Inout_ NDIS_STATISTICS_INFO *Statistics)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
ULONG PacketSize = NET_BUFFER_DATA_LENGTH(Nb);
|
|
|
|
TUN_PACKET *Packet = (TUN_PACKET *)(Buffer + Irp->IoStatus.Information);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:40:26 +02:00
|
|
|
Packet->Size = PacketSize; /* We shouldn't trust Packet->Size directly for reading, because the user controls it. */
|
2019-06-29 13:34:15 +02:00
|
|
|
void *NbData = NdisGetDataBuffer(Nb, PacketSize, Packet->Data, 1, 0);
|
|
|
|
if (!NbData)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
if (Statistics)
|
|
|
|
InterlockedIncrement64((LONG64 *)&Statistics->ifOutErrors);
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_RESOURCES;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
if (NbData != Packet->Data)
|
|
|
|
NdisMoveMemory(Packet->Data, NbData, PacketSize);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
Irp->IoStatus.Information += TunPacketAlign(sizeof(TUN_PACKET) + PacketSize);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedAdd64((LONG64 *)&Statistics->ifHCOutOctets, PacketSize);
|
|
|
|
InterlockedAdd64((LONG64 *)&Statistics->ifHCOutUcastOctets, PacketSize);
|
2019-06-26 19:37:29 +02:00
|
|
|
InterlockedIncrement64((LONG64 *)&Statistics->ifHCOutUcastPkts);
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_SUCCESS;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
|
Switch to pending writes
Commentary from Jason:
Problem statement:
We call IoCompleteRequest(Irp) immediately after
NdisMIndicateReceiveNetBufferLists, which frees Irp->MdlAddress.
Since we've just given the same memory to
NdisMIndicateReceiveNetBufferLists (in a different MDL), we wind up
freeing the memory before NDIS finishes processing them.
Fix possibility 1:
Move IoCompleteRequest(Irp) to TunReturnNetBufferLists. This requires
reference counting how many NBLs are currently in flight that are
using an IRP. When that drops to zero, we can call IoCompleteRequest
(Irp).
Problem:
This means we have to block future wireguard-go Writes until *all*
NBLs have completed processing in the networking stack. Is that safe
to do? Will that introduce latency? Can userspace processes sabotage
it by refusing to read from a TCP socket buffer? We don't know enough
about how NdisMIndicateReceiveNetBufferLists works to assess its
characteristics here.
Fix possibility 2:
Use NDIS_RECEIVE_FLAGS_RESOURCES, so that
NdisMIndicateReceiveNetBufferLists makes a copy, and then we'll simply
free everything immediately after. This is slow, and it could
potentially lead to wireguard-go making the kernel allocate lots of
memory in the case that NdisAllocateNetBufferAndNetBufferList doesn't
ratelimit its creation in the same way Linux's skb_alloc does.
However, it does make the lifetime of Irps shorter, which is easier to
analyze, and it might lead to better latency, since we don't need to
wait until userspace sends its next packets, so long as Ndis'
ingestion queue doesn't become too large.
This commit switches from (2) to (1).
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-30 21:12:55 +02:00
|
|
|
#define NET_BUFFER_LIST_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl))
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-07-03 12:50:01 +02:00
|
|
|
_IRQL_requires_same_
|
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunNBLRefInit(_Inout_ TUN_CTX *Ctx, _Inout_ NET_BUFFER_LIST *Nbl)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
InterlockedIncrement64(&Ctx->ActiveNBLCount);
|
|
|
|
InterlockedIncrement(&Ctx->PacketQueue.NumNbl);
|
|
|
|
InterlockedExchange64(NET_BUFFER_LIST_REFCOUNT(Nbl), 1);
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-07-03 12:50:01 +02:00
|
|
|
_IRQL_requires_same_
|
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunNBLRefInc(_Inout_ NET_BUFFER_LIST *Nbl)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
ASSERT(InterlockedGet64(NET_BUFFER_LIST_REFCOUNT(Nbl)));
|
|
|
|
InterlockedIncrement64(NET_BUFFER_LIST_REFCOUNT(Nbl));
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
_When_((SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_(DISPATCH_LEVEL))
|
2019-03-27 09:09:58 +01:00
|
|
|
_When_(!(SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_max_(DISPATCH_LEVEL))
|
2019-06-26 14:52:38 +02:00
|
|
|
static BOOLEAN
|
2019-06-26 19:37:29 +02:00
|
|
|
TunNBLRefDec(_Inout_ TUN_CTX *Ctx, _Inout_ NET_BUFFER_LIST *Nbl, _In_ ULONG SendCompleteFlags)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
ASSERT(InterlockedGet64(NET_BUFFER_LIST_REFCOUNT(Nbl)) > 0);
|
|
|
|
if (InterlockedDecrement64(NET_BUFFER_LIST_REFCOUNT(Nbl)) <= 0)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
|
|
|
|
NdisMSendNetBufferListsComplete(Ctx->MiniportAdapterHandle, Nbl, SendCompleteFlags);
|
|
|
|
ASSERT(InterlockedGet(&Ctx->PacketQueue.NumNbl) > 0);
|
|
|
|
InterlockedDecrement(&Ctx->PacketQueue.NumNbl);
|
|
|
|
TunCompletePause(Ctx, TRUE);
|
2019-06-26 14:52:38 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 12:50:01 +02:00
|
|
|
_IRQL_requires_same_
|
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunAppendNBL(_Inout_ NET_BUFFER_LIST **Head, _Inout_ NET_BUFFER_LIST **Tail, __drv_aliasesMem _In_ NET_BUFFER_LIST *Nbl)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
*(*Tail ? &NET_BUFFER_LIST_NEXT_NBL(*Tail) : Head) = Nbl;
|
|
|
|
*Tail = Nbl;
|
|
|
|
NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
_Requires_lock_not_held_(Ctx->PacketQueue.Lock)
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunQueueAppend(_Inout_ TUN_CTX *Ctx, _In_ NET_BUFFER_LIST *Nbl, _In_ UINT MaxNbls)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
for (NET_BUFFER_LIST *NextNbl; Nbl; Nbl = NextNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
2019-06-26 19:37:29 +02:00
|
|
|
if (!NET_BUFFER_LIST_FIRST_NB(Nbl))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
|
|
|
|
NdisMSendNetBufferListsComplete(Ctx->MiniportAdapterHandle, Nbl, 0);
|
2019-06-26 14:52:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
|
|
KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &LockHandle);
|
2019-06-26 19:37:29 +02:00
|
|
|
TunNBLRefInit(Ctx, Nbl);
|
|
|
|
TunAppendNBL(&Ctx->PacketQueue.FirstNbl, &Ctx->PacketQueue.LastNbl, Nbl);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
while ((UINT)InterlockedGet(&Ctx->PacketQueue.NumNbl) > MaxNbls && Ctx->PacketQueue.FirstNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST *SecondNbl = NET_BUFFER_LIST_NEXT_NBL(Ctx->PacketQueue.FirstNbl);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
NET_BUFFER_LIST_STATUS(Ctx->PacketQueue.FirstNbl) = NDIS_STATUS_SEND_ABORTED;
|
|
|
|
TunNBLRefDec(Ctx, Ctx->PacketQueue.FirstNbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
Ctx->PacketQueue.NextNb = NULL;
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->PacketQueue.FirstNbl = SecondNbl;
|
2019-06-26 19:37:29 +02:00
|
|
|
if (!Ctx->PacketQueue.FirstNbl)
|
|
|
|
Ctx->PacketQueue.LastNbl = NULL;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
_Requires_lock_held_(Ctx->PacketQueue.Lock)
|
2019-03-27 09:00:19 +01:00
|
|
|
_IRQL_requires_(DISPATCH_LEVEL)
|
2019-03-28 11:21:51 +01:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static _Return_type_success_(return !=
|
2019-06-26 19:37:29 +02:00
|
|
|
NULL) NET_BUFFER *TunQueueRemove(_Inout_ TUN_CTX *Ctx, _Out_ NET_BUFFER_LIST **Nbl)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST *TopNbl;
|
|
|
|
NET_BUFFER *RetNbl;
|
2019-03-27 09:00:19 +01:00
|
|
|
|
2019-03-28 09:05:29 +01:00
|
|
|
retry:
|
2019-06-29 13:34:15 +02:00
|
|
|
TopNbl = Ctx->PacketQueue.FirstNbl;
|
|
|
|
*Nbl = TopNbl;
|
|
|
|
if (!TopNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
return NULL;
|
2019-06-26 19:37:29 +02:00
|
|
|
if (!Ctx->PacketQueue.NextNb)
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->PacketQueue.NextNb = NET_BUFFER_LIST_FIRST_NB(TopNbl);
|
|
|
|
RetNbl = Ctx->PacketQueue.NextNb;
|
|
|
|
Ctx->PacketQueue.NextNb = NET_BUFFER_NEXT_NB(RetNbl);
|
2019-06-26 19:37:29 +02:00
|
|
|
if (!Ctx->PacketQueue.NextNb)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->PacketQueue.FirstNbl = NET_BUFFER_LIST_NEXT_NBL(TopNbl);
|
2019-06-26 19:37:29 +02:00
|
|
|
if (!Ctx->PacketQueue.FirstNbl)
|
|
|
|
Ctx->PacketQueue.LastNbl = NULL;
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST_NEXT_NBL(TopNbl) = NULL;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
else
|
2019-06-29 13:34:15 +02:00
|
|
|
TunNBLRefInc(TopNbl);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
if (RetNbl && NET_BUFFER_DATA_LENGTH(RetNbl) > TUN_EXCH_MAX_IP_PACKET_SIZE)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST_STATUS(TopNbl) = NDIS_STATUS_INVALID_LENGTH;
|
|
|
|
TunNBLRefDec(Ctx, TopNbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
2019-06-26 19:37:29 +02:00
|
|
|
InterlockedIncrement64((LONG64 *)&Ctx->Statistics.ifOutDiscards);
|
|
|
|
goto retry;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
return RetNbl;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Note: Must be called immediately after TunQueueRemove without dropping ctx->PacketQueue.Lock. */
|
2019-06-26 19:37:29 +02:00
|
|
|
_Requires_lock_held_(Ctx->PacketQueue.Lock)
|
2019-03-27 09:00:19 +01:00
|
|
|
_IRQL_requires_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunQueuePrepend(_Inout_ TUN_CTX *Ctx, _In_ NET_BUFFER *Nb, _In_ NET_BUFFER_LIST *Nbl)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
Ctx->PacketQueue.NextNb = Nb;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
if (!Nbl || Nbl == Ctx->PacketQueue.FirstNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
return;
|
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
TunNBLRefInc(Nbl);
|
|
|
|
if (!Ctx->PacketQueue.FirstNbl)
|
|
|
|
Ctx->PacketQueue.FirstNbl = Ctx->PacketQueue.LastNbl = Nbl;
|
2019-06-26 14:52:38 +02:00
|
|
|
else
|
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
NET_BUFFER_LIST_NEXT_NBL(Nbl) = Ctx->PacketQueue.FirstNbl;
|
|
|
|
Ctx->PacketQueue.FirstNbl = Nbl;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
_Requires_lock_not_held_(Ctx->PacketQueue.Lock)
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunQueueClear(_Inout_ TUN_CTX *Ctx, _In_ NDIS_STATUS Status)
|
2019-04-02 13:41:02 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
|
|
KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &LockHandle);
|
|
|
|
for (NET_BUFFER_LIST *Nbl = Ctx->PacketQueue.FirstNbl, *NextNbl; Nbl; Nbl = NextNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
|
|
|
NET_BUFFER_LIST_STATUS(Nbl) = Status;
|
|
|
|
TunNBLRefDec(Ctx, Nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-06-26 19:37:29 +02:00
|
|
|
Ctx->PacketQueue.FirstNbl = NULL;
|
|
|
|
Ctx->PacketQueue.LastNbl = NULL;
|
|
|
|
Ctx->PacketQueue.NextNb = NULL;
|
|
|
|
InterlockedExchange(&Ctx->PacketQueue.NumNbl, 0);
|
2019-06-29 13:34:15 +02:00
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
2019-04-02 13:41:02 +02:00
|
|
|
}
|
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
_Requires_lock_not_held_(Ctx->PacketQueue.Lock)
|
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunQueueProcess(_Inout_ TUN_CTX *Ctx)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
IRP *Irp = NULL;
|
|
|
|
UCHAR *Buffer = NULL;
|
|
|
|
ULONG Size = 0;
|
|
|
|
NET_BUFFER *Nb;
|
|
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST *Nbl;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &LockHandle);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
/* Get head NB (and IRP). */
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!Irp)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Nb = TunQueueRemove(Ctx, &Nbl);
|
|
|
|
if (!Nb)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
2019-06-26 14:52:38 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
Irp = TunRemoveNextIrp(Ctx, &Buffer, &Size);
|
|
|
|
if (!Irp)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TunQueuePrepend(Ctx, Nb, Nbl);
|
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
|
|
if (Nbl)
|
|
|
|
TunNBLRefDec(Ctx, Nbl, 0);
|
2019-06-26 14:52:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
_Analysis_assume_(Buffer);
|
|
|
|
_Analysis_assume_(Irp->IoStatus.Information <= Size);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
else
|
2019-06-29 13:34:15 +02:00
|
|
|
Nb = TunQueueRemove(Ctx, &Nbl);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
/* If the NB won't fit in the IRP, return it. */
|
2019-06-29 13:34:15 +02:00
|
|
|
if (Nb && TunWontFitIntoIrp(Irp, Size, Nb))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TunQueuePrepend(Ctx, Nb, Nbl);
|
|
|
|
if (Nbl)
|
|
|
|
TunNBLRefDec(Ctx, Nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
|
|
|
Nbl = NULL;
|
|
|
|
Nb = NULL;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
/* Process NB and IRP. */
|
2019-06-29 13:34:15 +02:00
|
|
|
if (Nb)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS status = TunWriteIntoIrp(Irp, Buffer, Nb, &Ctx->Statistics);
|
2019-06-26 14:52:38 +02:00
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
if (Nbl)
|
|
|
|
NET_BUFFER_LIST_STATUS(Nbl) = status;
|
|
|
|
IoCsqInsertIrpEx(&Ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_HEAD);
|
|
|
|
Irp = NULL;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TunCompleteRequest(Ctx, Irp, STATUS_SUCCESS, IO_NETWORK_INCREMENT);
|
|
|
|
Irp = NULL;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
if (Nbl)
|
|
|
|
TunNBLRefDec(Ctx, Nbl, 0);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 12:50:01 +02:00
|
|
|
_IRQL_requires_same_
|
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunSetNBLStatus(_Inout_opt_ NET_BUFFER_LIST *Nbl, _In_ NDIS_STATUS Status)
|
2019-06-07 12:23:44 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
for (; Nbl; Nbl = NET_BUFFER_LIST_NEXT_NBL(Nbl))
|
|
|
|
NET_BUFFER_LIST_STATUS(Nbl) = Status;
|
2019-06-07 12:23:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static MINIPORT_SEND_NET_BUFFER_LISTS TunSendNetBufferLists;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunSendNetBufferLists(
|
|
|
|
NDIS_HANDLE MiniportAdapterContext,
|
|
|
|
NET_BUFFER_LIST *NetBufferLists,
|
|
|
|
NDIS_PORT_NUMBER PortNumber,
|
|
|
|
ULONG SendFlags)
|
2019-06-07 12:23:44 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
2019-06-07 12:23:44 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedIncrement64(&Ctx->ActiveNBLCount);
|
2019-06-07 12:23:44 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KIRQL Irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
|
|
|
|
LONG Flags = InterlockedGet(&Ctx->Flags);
|
|
|
|
NDIS_STATUS Status;
|
|
|
|
if ((Status = NDIS_STATUS_ADAPTER_REMOVED, !(Flags & TUN_FLAGS_PRESENT)) ||
|
|
|
|
(Status = NDIS_STATUS_PAUSED, !(Flags & TUN_FLAGS_RUNNING)) ||
|
|
|
|
(Status = NDIS_STATUS_MEDIA_DISCONNECTED, InterlockedGet64(&Ctx->Device.RefCount) <= 0))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TunSetNBLStatus(NetBufferLists, Status);
|
2019-06-26 14:52:38 +02:00
|
|
|
NdisMSendNetBufferListsComplete(
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->MiniportAdapterHandle, NetBufferLists, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_ExReleaseSpinLockShared;
|
|
|
|
}
|
2019-06-07 12:23:44 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
TunQueueAppend(Ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS);
|
2019-06-07 12:23:44 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
TunQueueProcess(Ctx);
|
2019-06-07 12:23:44 +02:00
|
|
|
|
2019-06-12 20:00:36 +02:00
|
|
|
cleanup_ExReleaseSpinLockShared:
|
2019-06-29 13:34:15 +02:00
|
|
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
|
|
|
TunCompletePause(Ctx, TRUE);
|
2019-06-07 12:23:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static MINIPORT_CANCEL_SEND TunCancelSend;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
|
2019-06-07 12:23:44 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
|
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &LockHandle);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST *LastNbl = NULL, **LastNblLink = &Ctx->PacketQueue.FirstNbl;
|
|
|
|
for (NET_BUFFER_LIST *Nbl = Ctx->PacketQueue.FirstNbl, *NextNbl; Nbl; Nbl = NextNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
|
|
|
if (NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(Nbl) == CancelId)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_SEND_ABORTED;
|
|
|
|
*LastNblLink = NextNbl;
|
|
|
|
TunNBLRefDec(Ctx, Nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
LastNbl = Nbl;
|
|
|
|
LastNblLink = &NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->PacketQueue.LastNbl = LastNbl;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
2019-06-07 12:23:44 +02:00
|
|
|
}
|
|
|
|
|
2019-06-26 12:30:52 +02:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
2019-06-12 20:00:36 +02:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunDispatchRead(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
|
2019-06-12 20:00:36 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status = TunMapIrp(Irp);
|
|
|
|
if (!NT_SUCCESS(Status))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_CompleteRequest;
|
2019-06-12 20:00:36 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KIRQL Irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
|
|
|
|
LONG Flags = InterlockedGet(&Ctx->Flags);
|
|
|
|
if ((Status = STATUS_FILE_FORCED_CLOSED, !(Flags & TUN_FLAGS_PRESENT)) ||
|
|
|
|
!NT_SUCCESS(Status = IoCsqInsertIrpEx(&Ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL)))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_ExReleaseSpinLockShared;
|
2019-06-12 20:00:36 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
TunQueueProcess(Ctx);
|
2019-06-29 13:34:15 +02:00
|
|
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_PENDING;
|
2019-06-12 20:00:36 +02:00
|
|
|
|
|
|
|
cleanup_ExReleaseSpinLockShared:
|
2019-06-29 13:34:15 +02:00
|
|
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
2019-06-26 12:30:52 +02:00
|
|
|
cleanup_CompleteRequest:
|
2019-06-29 13:34:15 +02:00
|
|
|
TunCompleteRequest(Ctx, Irp, Status, IO_NO_INCREMENT);
|
|
|
|
return Status;
|
2019-06-12 20:00:36 +02:00
|
|
|
}
|
|
|
|
|
2019-07-03 14:21:32 +02:00
|
|
|
#define NET_BUFFER_LIST_MDL_REFCOUNT(Nbl) (NET_BUFFER_LIST_MINIPORT_RESERVED(Nbl)[0])
|
Switch to pending writes
Commentary from Jason:
Problem statement:
We call IoCompleteRequest(Irp) immediately after
NdisMIndicateReceiveNetBufferLists, which frees Irp->MdlAddress.
Since we've just given the same memory to
NdisMIndicateReceiveNetBufferLists (in a different MDL), we wind up
freeing the memory before NDIS finishes processing them.
Fix possibility 1:
Move IoCompleteRequest(Irp) to TunReturnNetBufferLists. This requires
reference counting how many NBLs are currently in flight that are
using an IRP. When that drops to zero, we can call IoCompleteRequest
(Irp).
Problem:
This means we have to block future wireguard-go Writes until *all*
NBLs have completed processing in the networking stack. Is that safe
to do? Will that introduce latency? Can userspace processes sabotage
it by refusing to read from a TCP socket buffer? We don't know enough
about how NdisMIndicateReceiveNetBufferLists works to assess its
characteristics here.
Fix possibility 2:
Use NDIS_RECEIVE_FLAGS_RESOURCES, so that
NdisMIndicateReceiveNetBufferLists makes a copy, and then we'll simply
free everything immediately after. This is slow, and it could
potentially lead to wireguard-go making the kernel allocate lots of
memory in the case that NdisAllocateNetBufferAndNetBufferList doesn't
ratelimit its creation in the same way Linux's skb_alloc does.
However, it does make the lifetime of Irps shorter, which is easier to
analyze, and it might lead to better latency, since we don't need to
wait until userspace sends its next packets, so long as Ndis'
ingestion queue doesn't become too large.
This commit switches from (2) to (1).
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-30 21:12:55 +02:00
|
|
|
|
2019-07-03 10:44:17 +02:00
|
|
|
static MINIPORT_RETURN_NET_BUFFER_LISTS TunReturnNetBufferLists;
|
|
|
|
_Use_decl_annotations_
|
|
|
|
static void
|
|
|
|
TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST NetBufferLists, ULONG ReturnFlags)
|
|
|
|
{
|
|
|
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
|
|
|
|
|
|
|
LONG64 StatSize = 0, StatPacketsOk = 0, StatPacketsError = 0;
|
|
|
|
for (NET_BUFFER_LIST *Nbl = NetBufferLists, *NextNbl; Nbl; Nbl = NextNbl)
|
|
|
|
{
|
|
|
|
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
|
|
|
NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
|
|
|
|
|
|
|
|
if (NT_SUCCESS(NET_BUFFER_LIST_STATUS(Nbl)))
|
|
|
|
{
|
|
|
|
StatSize += NET_BUFFER_LIST_FIRST_NB(Nbl)->DataLength;
|
|
|
|
StatPacketsOk++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
StatPacketsError++;
|
|
|
|
|
|
|
|
TunCompletePause(Ctx, TRUE);
|
|
|
|
|
2019-07-03 14:21:32 +02:00
|
|
|
volatile LONG *MdlRefcount = NET_BUFFER_LIST_MDL_REFCOUNT(Nbl);
|
|
|
|
ASSERT(InterlockedGet(MdlRefcount) > 0);
|
|
|
|
if (InterlockedDecrement(MdlRefcount) <= 0)
|
|
|
|
{
|
|
|
|
/* MdlRefcount is also the first pointer in the allocation. */
|
|
|
|
ExFreePoolWithTag((PVOID)MdlRefcount, TUN_MEMORY_TAG);
|
|
|
|
NdisFreeMdl(NET_BUFFER_LIST_FIRST_NB(Nbl)->MdlChain);
|
|
|
|
}
|
|
|
|
NdisFreeNetBufferList(Nbl);
|
2019-07-03 10:44:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInOctets, StatSize);
|
|
|
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInUcastOctets, StatSize);
|
|
|
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInUcastPkts, StatPacketsOk);
|
|
|
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInErrors, StatPacketsError);
|
|
|
|
}
|
|
|
|
|
2019-06-26 12:30:52 +02:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
2019-04-09 13:16:40 +02:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunDispatchWrite(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
InterlockedIncrement64(&Ctx->ActiveNBLCount);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-07-03 14:21:32 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ULONG Size = Stack->Parameters.Write.Length;
|
|
|
|
if (Status = STATUS_INVALID_USER_BUFFER, (Size < TUN_EXCH_MIN_BUFFER_SIZE_WRITE || Size > TUN_EXCH_MAX_BUFFER_SIZE))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_CompleteRequest;
|
2019-07-03 14:21:32 +02:00
|
|
|
UCHAR *BufferStart = ExAllocatePoolWithTag(NonPagedPoolNx, Size, TUN_MEMORY_TAG);
|
|
|
|
if (Status = STATUS_INSUFFICIENT_RESOURCES, !BufferStart)
|
|
|
|
goto cleanup_CompleteRequest;
|
|
|
|
/* We don't write to this until we're totally finished using Packet->Size. */
|
|
|
|
LONG *MdlRefcount = (LONG *)BufferStart;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ProbeForRead(Irp->UserBuffer, Size, 1);
|
|
|
|
NdisMoveMemory(BufferStart, Irp->UserBuffer, Size);
|
|
|
|
}
|
|
|
|
except(EXCEPTION_EXECUTE_HANDLER) { goto cleanup_ExFreePoolWithTag; }
|
|
|
|
|
|
|
|
MDL *Mdl = NdisAllocateMdl(Ctx->MiniportAdapterHandle, BufferStart, Size);
|
|
|
|
if (Status = STATUS_INSUFFICIENT_RESOURCES, !Mdl)
|
|
|
|
goto cleanup_ExFreePoolWithTag;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KIRQL Irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
|
|
|
|
LONG Flags = InterlockedGet(&Ctx->Flags);
|
|
|
|
if (Status = STATUS_FILE_FORCED_CLOSED, !(Flags & TUN_FLAGS_PRESENT))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_ExReleaseSpinLockShared;
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
const UCHAR *BufferPos = BufferStart, *BufferEnd = BufferStart + Size;
|
|
|
|
typedef enum
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
EtherTypeIndexIPv4 = 0,
|
|
|
|
EtherTypeIndexStart = 0,
|
|
|
|
EtherTypeIndexIPv6,
|
|
|
|
EtherTypeIndexEnd
|
|
|
|
} EtherTypeIndex;
|
2019-06-26 14:52:38 +02:00
|
|
|
static const struct
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
ULONG NblFlags;
|
|
|
|
USHORT NblProto;
|
|
|
|
} EtherTypeConstants[EtherTypeIndexEnd] = {
|
2019-06-26 18:59:02 +02:00
|
|
|
{ NDIS_NBL_FLAGS_IS_IPV4, TUN_HTONS(NDIS_ETH_TYPE_IPV4) },
|
|
|
|
{ NDIS_NBL_FLAGS_IS_IPV6, TUN_HTONS(NDIS_ETH_TYPE_IPV6) },
|
2019-06-26 14:52:38 +02:00
|
|
|
};
|
|
|
|
struct
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST *Head, *Tail;
|
|
|
|
LONG Count;
|
|
|
|
} NblQueue[EtherTypeIndexEnd] = { { NULL, NULL, 0 }, { NULL, NULL, 0 } };
|
|
|
|
LONG NblCount = 0;
|
|
|
|
while (BufferEnd - BufferPos >= sizeof(TUN_PACKET))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
if (NblCount >= MAXLONG)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_PACKET *Packet = (TUN_PACKET *)BufferPos;
|
2019-07-03 14:21:32 +02:00
|
|
|
if (Packet->Size > TUN_EXCH_MAX_IP_PACKET_SIZE)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
2019-07-03 14:21:32 +02:00
|
|
|
ULONG AlignedPacketSize = TunPacketAlign(sizeof(TUN_PACKET) + Packet->Size);
|
2019-06-29 13:40:26 +02:00
|
|
|
if (BufferEnd - BufferPos < (ptrdiff_t)AlignedPacketSize)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
EtherTypeIndex Index;
|
2019-07-03 14:21:32 +02:00
|
|
|
if (Packet->Size >= 20 && Packet->Data[0] >> 4 == 4)
|
2019-06-29 13:34:15 +02:00
|
|
|
Index = EtherTypeIndexIPv4;
|
2019-07-03 14:21:32 +02:00
|
|
|
else if (Packet->Size >= 40 && Packet->Data[0] >> 4 == 6)
|
2019-06-29 13:34:15 +02:00
|
|
|
Index = EtherTypeIndexIPv6;
|
2019-06-26 14:52:38 +02:00
|
|
|
else
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST *Nbl = NdisAllocateNetBufferAndNetBufferList(
|
2019-07-03 14:21:32 +02:00
|
|
|
Ctx->NBLPool, 0, 0, Mdl, (ULONG)(Packet->Data - BufferStart), Packet->Size);
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!Nbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
Nbl->SourceHandle = Ctx->MiniportAdapterHandle;
|
|
|
|
NdisSetNblFlag(Nbl, EtherTypeConstants[Index].NblFlags);
|
|
|
|
NET_BUFFER_LIST_INFO(Nbl, NetBufferListFrameType) = (PVOID)EtherTypeConstants[Index].NblProto;
|
|
|
|
NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_SUCCESS;
|
2019-07-03 14:21:32 +02:00
|
|
|
NET_BUFFER_LIST_MDL_REFCOUNT(Nbl) = MdlRefcount;
|
2019-06-29 13:34:15 +02:00
|
|
|
TunAppendNBL(&NblQueue[Index].Head, &NblQueue[Index].Tail, Nbl);
|
|
|
|
NblQueue[Index].Count++;
|
|
|
|
NblCount++;
|
2019-06-29 13:40:26 +02:00
|
|
|
BufferPos += AlignedPacketSize;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
if ((ULONG)(BufferPos - BufferStart) != Size)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
Irp->IoStatus.Information = Size;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!NblCount)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_SUCCESS;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_ExReleaseSpinLockShared;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!(Flags & TUN_FLAGS_RUNNING))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInDiscards, NblCount);
|
|
|
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInErrors, NblCount);
|
|
|
|
Status = STATUS_SUCCESS;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_nbl_queues;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedAdd64(&Ctx->ActiveNBLCount, NblCount);
|
2019-07-03 14:21:32 +02:00
|
|
|
*MdlRefcount = NblCount;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-07-02 17:54:35 +02:00
|
|
|
for (EtherTypeIndex Index = EtherTypeIndexStart; Index < EtherTypeIndexEnd; Index++)
|
|
|
|
{
|
|
|
|
if (!NblQueue[Index].Head)
|
|
|
|
continue;
|
2019-06-26 14:52:38 +02:00
|
|
|
NdisMIndicateReceiveNetBufferLists(
|
2019-06-26 19:37:29 +02:00
|
|
|
Ctx->MiniportAdapterHandle,
|
2019-07-02 17:54:35 +02:00
|
|
|
NblQueue[Index].Head,
|
2019-06-26 14:52:38 +02:00
|
|
|
NDIS_DEFAULT_PORT_NUMBER,
|
2019-07-02 17:54:35 +02:00
|
|
|
NblQueue[Index].Count,
|
2019-07-03 14:21:32 +02:00
|
|
|
NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE | NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL);
|
2019-07-02 17:54:35 +02:00
|
|
|
}
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
2019-07-03 14:21:32 +02:00
|
|
|
TunCompleteRequest(Ctx, Irp, STATUS_SUCCESS, IO_NETWORK_INCREMENT);
|
2019-06-26 19:37:29 +02:00
|
|
|
TunCompletePause(Ctx, TRUE);
|
2019-07-03 10:41:37 +02:00
|
|
|
return STATUS_SUCCESS;
|
2019-03-22 13:47:17 +01:00
|
|
|
|
2019-06-06 08:11:30 +02:00
|
|
|
cleanup_nbl_queues:
|
2019-06-29 13:34:15 +02:00
|
|
|
for (EtherTypeIndex Index = EtherTypeIndexStart; Index < EtherTypeIndexEnd; Index++)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
for (NET_BUFFER_LIST *Nbl = NblQueue[Index].Head, *NextNbl; Nbl; Nbl = NextNbl)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
|
|
|
NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
|
|
|
|
NdisFreeNetBufferList(Nbl);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 20:00:36 +02:00
|
|
|
cleanup_ExReleaseSpinLockShared:
|
2019-06-29 13:34:15 +02:00
|
|
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
2019-07-03 14:21:32 +02:00
|
|
|
NdisFreeMdl(Mdl);
|
|
|
|
cleanup_ExFreePoolWithTag:
|
|
|
|
ExFreePoolWithTag(BufferStart, TUN_MEMORY_TAG);
|
2019-06-26 12:30:52 +02:00
|
|
|
cleanup_CompleteRequest:
|
2019-06-29 13:34:15 +02:00
|
|
|
TunCompleteRequest(Ctx, Irp, Status, IO_NO_INCREMENT);
|
2019-06-26 19:37:29 +02:00
|
|
|
TunCompletePause(Ctx, TRUE);
|
2019-06-29 13:34:15 +02:00
|
|
|
return Status;
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-26 20:34:08 +02:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
2019-06-12 20:00:36 +02:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunDispatchCreate(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
|
2019-06-12 20:00:36 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status;
|
2019-07-03 12:43:55 +02:00
|
|
|
TUN_FILE_CTX *FileCtx = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(*FileCtx), TUN_MEMORY_TAG);
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!FileCtx)
|
2019-06-26 14:52:38 +02:00
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
2019-06-29 13:34:15 +02:00
|
|
|
RtlZeroMemory(FileCtx, sizeof(*FileCtx));
|
|
|
|
ExInitializeFastMutex(&FileCtx->ReadBuffer.InitializationComplete);
|
2019-06-12 20:00:36 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KIRQL Irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
|
|
|
|
LONG Flags = InterlockedGet(&Ctx->Flags);
|
|
|
|
if ((Status = STATUS_DELETE_PENDING, !(Flags & TUN_FLAGS_PRESENT)))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_ExReleaseSpinLockShared;
|
2019-06-12 20:00:36 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (!NT_SUCCESS(Status = IoAcquireRemoveLock(&Ctx->Device.RemoveLock, Stack->FileObject)))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_ExReleaseSpinLockShared;
|
2019-06-29 13:34:15 +02:00
|
|
|
Stack->FileObject->FsContext = FileCtx;
|
2019-06-12 20:00:36 +02:00
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
if (InterlockedIncrement64(&Ctx->Device.RefCount) == 1)
|
|
|
|
TunIndicateStatus(Ctx->MiniportAdapterHandle, MediaConnectStateConnected);
|
2019-06-12 20:00:36 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_SUCCESS;
|
2019-06-12 20:00:36 +02:00
|
|
|
|
|
|
|
cleanup_ExReleaseSpinLockShared:
|
2019-06-29 13:34:15 +02:00
|
|
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
|
|
|
TunCompleteRequest(Ctx, Irp, Status, IO_NO_INCREMENT);
|
|
|
|
if (!NT_SUCCESS(Status))
|
2019-07-03 12:43:55 +02:00
|
|
|
ExFreePoolWithTag(FileCtx, TUN_MEMORY_TAG);
|
2019-06-29 13:34:15 +02:00
|
|
|
return Status;
|
2019-06-12 20:00:36 +02:00
|
|
|
}
|
|
|
|
|
2019-06-26 20:34:08 +02:00
|
|
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
|
|
|
static void
|
|
|
|
TunDispatchClose(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
|
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
2019-06-26 20:34:08 +02:00
|
|
|
ASSERT(InterlockedGet64(&Ctx->Device.RefCount) > 0);
|
2019-06-29 13:34:15 +02:00
|
|
|
BOOLEAN IsLastFileHandle = InterlockedDecrement64(&Ctx->Device.RefCount) <= 0;
|
2019-06-27 11:59:22 +02:00
|
|
|
ExReleaseSpinLockExclusive(
|
|
|
|
&Ctx->TransitionLock,
|
2019-06-27 15:08:29 +02:00
|
|
|
ExAcquireSpinLockExclusive(&Ctx->TransitionLock)); /* Ensure above change is visible to all readers. */
|
2019-06-29 13:34:15 +02:00
|
|
|
if (IsLastFileHandle)
|
2019-06-26 20:34:08 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_HANDLE AdapterHandle = InterlockedGetPointer(&Ctx->MiniportAdapterHandle);
|
|
|
|
if (AdapterHandle)
|
|
|
|
TunIndicateStatus(AdapterHandle, MediaConnectStateDisconnected);
|
2019-06-26 20:34:08 +02:00
|
|
|
TunQueueClear(Ctx, NDIS_STATUS_MEDIA_DISCONNECTED);
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_FILE_CTX *FileCtx = (TUN_FILE_CTX *)Stack->FileObject->FsContext;
|
|
|
|
TunUnmapUbuffer(&FileCtx->ReadBuffer);
|
2019-07-03 12:43:55 +02:00
|
|
|
ExFreePoolWithTag(FileCtx, TUN_MEMORY_TAG);
|
2019-06-29 13:34:15 +02:00
|
|
|
IoReleaseRemoveLock(&Ctx->Device.RemoveLock, Stack->FileObject);
|
2019-06-26 20:34:08 +02:00
|
|
|
}
|
|
|
|
|
2019-04-09 13:16:40 +02:00
|
|
|
static DRIVER_DISPATCH TunDispatch;
|
2019-03-27 09:00:19 +01:00
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
|
|
|
TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
2019-03-27 09:00:19 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
Irp->IoStatus.Information = 0;
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = NdisGetDeviceReservedExtension(DeviceObject);
|
|
|
|
if (!Ctx)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_HANDLE;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_complete_req;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (Stack->MajorFunction)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
|
|
|
case IRP_MJ_READ:
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!NT_SUCCESS(Status = IoAcquireRemoveLock(&Ctx->Device.RemoveLock, Irp)))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_complete_req;
|
2019-06-29 13:34:15 +02:00
|
|
|
return TunDispatchRead(Ctx, Irp);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
case IRP_MJ_WRITE:
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!NT_SUCCESS(Status = IoAcquireRemoveLock(&Ctx->Device.RemoveLock, Irp)))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_complete_req;
|
2019-06-29 13:34:15 +02:00
|
|
|
return TunDispatchWrite(Ctx, Irp);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
case IRP_MJ_CREATE:
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!NT_SUCCESS(Status = IoAcquireRemoveLock(&Ctx->Device.RemoveLock, Irp)))
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_complete_req;
|
2019-06-29 13:34:15 +02:00
|
|
|
return TunDispatchCreate(Ctx, Irp);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
case IRP_MJ_CLOSE:
|
2019-06-29 13:34:15 +02:00
|
|
|
TunDispatchClose(Ctx, Irp);
|
2019-06-26 20:34:08 +02:00
|
|
|
break;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
case IRP_MJ_CLEANUP:
|
2019-06-29 13:34:15 +02:00
|
|
|
for (IRP *PendingIrp; (PendingIrp = IoCsqRemoveNextIrp(&Ctx->Device.ReadQueue.Csq, Stack->FileObject)) != NULL;)
|
|
|
|
TunCompleteRequest(Ctx, PendingIrp, STATUS_CANCELLED, IO_NO_INCREMENT);
|
2019-06-26 20:34:08 +02:00
|
|
|
break;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
default:
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
2019-06-26 20:34:08 +02:00
|
|
|
break;
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-03-27 09:00:19 +01:00
|
|
|
|
|
|
|
cleanup_complete_req:
|
2019-06-29 13:34:15 +02:00
|
|
|
Irp->IoStatus.Status = Status;
|
2019-06-26 14:52:38 +02:00
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
2019-06-29 13:34:15 +02:00
|
|
|
return Status;
|
2019-03-27 09:00:19 +01:00
|
|
|
}
|
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
_Dispatch_type_(IRP_MJ_PNP) static DRIVER_DISPATCH TunDispatchPnP;
|
2019-06-20 09:46:52 +02:00
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
|
|
|
TunDispatchPnP(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
2019-06-20 09:46:52 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
IO_STACK_LOCATION *Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if (Stack->MajorFunction == IRP_MJ_PNP)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
|
|
|
#pragma warning(suppress : 28175)
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = DeviceObject->Reserved;
|
|
|
|
if (!Ctx)
|
2019-06-27 13:57:14 +02:00
|
|
|
return NdisDispatchPnP(DeviceObject, Irp);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
switch (Stack->MinorFunction)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
|
|
|
case IRP_MN_QUERY_REMOVE_DEVICE:
|
2019-07-03 12:15:48 +02:00
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedAnd(&Ctx->Flags, ~TUN_FLAGS_PRESENT);
|
2019-06-27 11:59:22 +02:00
|
|
|
ExReleaseSpinLockExclusive(
|
2019-06-29 13:34:15 +02:00
|
|
|
&Ctx->TransitionLock,
|
|
|
|
ExAcquireSpinLockExclusive(&Ctx->TransitionLock)); /* Ensure above change is visible to all readers. */
|
|
|
|
TunQueueClear(Ctx, NDIS_STATUS_ADAPTER_REMOVED);
|
2019-06-26 14:52:38 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedOr(&Ctx->Flags, TUN_FLAGS_PRESENT);
|
2019-06-26 14:52:38 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NdisDispatchPnP(DeviceObject, Irp);
|
2019-06-20 09:46:52 +02:00
|
|
|
}
|
|
|
|
|
2019-03-08 22:33:15 +01:00
|
|
|
static MINIPORT_RESTART TunRestart;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunRestart(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_RESTART_PARAMETERS MiniportRestartParameters)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedExchange64(&Ctx->ActiveNBLCount, 1);
|
|
|
|
InterlockedOr(&Ctx->Flags, TUN_FLAGS_RUNNING);
|
2019-05-24 14:18:18 +02:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
static MINIPORT_PAUSE TunPause;
|
2019-03-08 22:33:15 +01:00
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedAnd(&Ctx->Flags, ~TUN_FLAGS_RUNNING);
|
2019-06-27 11:59:22 +02:00
|
|
|
ExReleaseSpinLockExclusive(
|
2019-06-29 13:34:15 +02:00
|
|
|
&Ctx->TransitionLock,
|
|
|
|
ExAcquireSpinLockExclusive(&Ctx->TransitionLock)); /* Ensure above change is visible to all readers. */
|
|
|
|
TunQueueClear(Ctx, NDIS_STATUS_PAUSED);
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
return TunCompletePause(Ctx, FALSE);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static MINIPORT_DEVICE_PNP_EVENT_NOTIFY TunDevicePnPEventNotify;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunDevicePnPEventNotify(NDIS_HANDLE MiniportAdapterContext, PNET_DEVICE_PNP_EVENT NetDevicePnPEvent)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static MINIPORT_INITIALIZE TunInitializeEx;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunInitializeEx(
|
|
|
|
NDIS_HANDLE MiniportAdapterHandle,
|
|
|
|
NDIS_HANDLE MiniportDriverContext,
|
|
|
|
PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_STATUS Status;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
if (!MiniportAdapterHandle)
|
|
|
|
return NDIS_STATUS_FAILURE;
|
|
|
|
|
2019-06-27 15:08:29 +02:00
|
|
|
/* Register device first. Having only one device per adapter allows us to store
|
|
|
|
* adapter context inside device extension. */
|
2019-07-03 12:15:48 +02:00
|
|
|
WCHAR DeviceName[sizeof(L"\\Device\\" TUN_DEVICE_NAME L"4294967295") / sizeof(WCHAR) + 1] = { 0 };
|
2019-06-29 13:34:15 +02:00
|
|
|
UNICODE_STRING UnicodeDeviceName;
|
|
|
|
TunInitUnicodeString(&UnicodeDeviceName, DeviceName);
|
2019-06-26 14:52:38 +02:00
|
|
|
RtlUnicodeStringPrintf(
|
2019-06-29 13:34:15 +02:00
|
|
|
&UnicodeDeviceName, L"\\Device\\" TUN_DEVICE_NAME, (ULONG)MiniportInitParameters->NetLuid.Info.NetLuidIndex);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-07-03 12:15:48 +02:00
|
|
|
WCHAR SymbolicName[sizeof(L"\\DosDevices\\" TUN_DEVICE_NAME L"4294967295") / sizeof(WCHAR) + 1] = { 0 };
|
2019-06-29 13:34:15 +02:00
|
|
|
UNICODE_STRING UnicodeSymbolicName;
|
|
|
|
TunInitUnicodeString(&UnicodeSymbolicName, SymbolicName);
|
2019-06-26 14:52:38 +02:00
|
|
|
RtlUnicodeStringPrintf(
|
2019-06-29 13:34:15 +02:00
|
|
|
&UnicodeSymbolicName,
|
2019-06-26 14:52:38 +02:00
|
|
|
L"\\DosDevices\\" TUN_DEVICE_NAME,
|
|
|
|
(ULONG)MiniportInitParameters->NetLuid.Info.NetLuidIndex);
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
static PDRIVER_DISPATCH DispatchTable[IRP_MJ_MAXIMUM_FUNCTION + 1] = {
|
2019-06-26 14:52:38 +02:00
|
|
|
TunDispatch, /* IRP_MJ_CREATE */
|
|
|
|
NULL, /* IRP_MJ_CREATE_NAMED_PIPE */
|
|
|
|
TunDispatch, /* IRP_MJ_CLOSE */
|
|
|
|
TunDispatch, /* IRP_MJ_READ */
|
|
|
|
TunDispatch, /* IRP_MJ_WRITE */
|
|
|
|
NULL, /* IRP_MJ_QUERY_INFORMATION */
|
|
|
|
NULL, /* IRP_MJ_SET_INFORMATION */
|
|
|
|
NULL, /* IRP_MJ_QUERY_EA */
|
|
|
|
NULL, /* IRP_MJ_SET_EA */
|
|
|
|
NULL, /* IRP_MJ_FLUSH_BUFFERS */
|
|
|
|
NULL, /* IRP_MJ_QUERY_VOLUME_INFORMATION */
|
|
|
|
NULL, /* IRP_MJ_SET_VOLUME_INFORMATION */
|
|
|
|
NULL, /* IRP_MJ_DIRECTORY_CONTROL */
|
|
|
|
NULL, /* IRP_MJ_FILE_SYSTEM_CONTROL */
|
|
|
|
NULL, /* IRP_MJ_DEVICE_CONTROL */
|
|
|
|
NULL, /* IRP_MJ_INTERNAL_DEVICE_CONTROL */
|
|
|
|
NULL, /* IRP_MJ_SHUTDOWN */
|
|
|
|
NULL, /* IRP_MJ_LOCK_CONTROL */
|
|
|
|
TunDispatch, /* IRP_MJ_CLEANUP */
|
|
|
|
};
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_DEVICE_OBJECT_ATTRIBUTES DeviceObjectAttributes = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES,
|
|
|
|
.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1,
|
|
|
|
.Size = NDIS_SIZEOF_DEVICE_OBJECT_ATTRIBUTES_REVISION_1 },
|
2019-06-29 13:34:15 +02:00
|
|
|
.DeviceName = &UnicodeDeviceName,
|
|
|
|
.SymbolicName = &UnicodeSymbolicName,
|
|
|
|
.MajorFunctions = DispatchTable,
|
2019-06-26 14:52:38 +02:00
|
|
|
.ExtensionSize = sizeof(TUN_CTX),
|
|
|
|
.DefaultSDDLString = &SDDL_DEVOBJ_SYS_ALL /* Kernel, and SYSTEM: full control. Others: none */
|
|
|
|
};
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_HANDLE DeviceObjectHandle;
|
|
|
|
DEVICE_OBJECT *DeviceObject;
|
|
|
|
if (!NT_SUCCESS(
|
|
|
|
Status = NdisRegisterDeviceEx(
|
|
|
|
NdisMiniportDriverHandle, &DeviceObjectAttributes, &DeviceObject, &DeviceObjectHandle)))
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_FAILURE;
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
DeviceObject->Flags &= ~(DO_BUFFERED_IO | DO_DIRECT_IO);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = NdisGetDeviceReservedExtension(DeviceObject);
|
|
|
|
if (!Ctx)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = NDIS_STATUS_FAILURE;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_NdisDeregisterDeviceEx;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
DEVICE_OBJECT *FunctionalDeviceObject;
|
|
|
|
NdisMGetDeviceProperty(MiniportAdapterHandle, NULL, &FunctionalDeviceObject, NULL, NULL, NULL);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-27 11:46:33 +02:00
|
|
|
/* Reverse engineering indicates that we'd be better off calling
|
|
|
|
* NdisWdfGetAdapterContextFromAdapterHandle(functional_device),
|
|
|
|
* which points to our TUN_CTX object directly, but this isn't
|
|
|
|
* available before Windows 10, so for now we just stick it into
|
|
|
|
* this reserved field. Revisit this when we drop support for old
|
|
|
|
* Windows versions. */
|
2019-06-26 14:52:38 +02:00
|
|
|
#pragma warning(suppress : 28175)
|
2019-06-29 13:34:15 +02:00
|
|
|
ASSERT(!FunctionalDeviceObject->Reserved);
|
2019-06-26 14:52:38 +02:00
|
|
|
#pragma warning(suppress : 28175)
|
2019-06-29 13:34:15 +02:00
|
|
|
FunctionalDeviceObject->Reserved = Ctx;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NdisZeroMemory(Ctx, sizeof(*Ctx));
|
|
|
|
Ctx->MiniportAdapterHandle = MiniportAdapterHandle;
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->Statistics.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
|
|
|
|
Ctx->Statistics.Header.Revision = NDIS_STATISTICS_INFO_REVISION_1;
|
|
|
|
Ctx->Statistics.Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1;
|
|
|
|
Ctx->Statistics.SupportedStatistics =
|
2019-06-26 14:52:38 +02:00
|
|
|
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV | NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS | NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT | NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR | NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV | NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV | NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT |
|
|
|
|
NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT | NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT;
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->Device.Handle = DeviceObjectHandle;
|
|
|
|
Ctx->Device.Object = DeviceObject;
|
2019-07-03 12:43:55 +02:00
|
|
|
IoInitializeRemoveLock(&Ctx->Device.RemoveLock, TUN_MEMORY_TAG, 0, 0);
|
2019-06-29 13:34:15 +02:00
|
|
|
KeInitializeSpinLock(&Ctx->Device.ReadQueue.Lock);
|
2019-06-26 14:52:38 +02:00
|
|
|
IoCsqInitializeEx(
|
2019-06-29 13:34:15 +02:00
|
|
|
&Ctx->Device.ReadQueue.Csq,
|
2019-06-26 14:52:38 +02:00
|
|
|
TunCsqInsertIrpEx,
|
|
|
|
TunCsqRemoveIrp,
|
|
|
|
TunCsqPeekNextIrp,
|
|
|
|
TunCsqAcquireLock,
|
|
|
|
TunCsqReleaseLock,
|
|
|
|
TunCsqCompleteCanceledIrp);
|
2019-06-29 13:34:15 +02:00
|
|
|
InitializeListHead(&Ctx->Device.ReadQueue.List);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
KeInitializeSpinLock(&Ctx->PacketQueue.Lock);
|
2019-06-26 14:52:38 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NET_BUFFER_LIST_POOL_PARAMETERS NblPoolParameters = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_DEFAULT,
|
|
|
|
.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1,
|
|
|
|
.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1 },
|
2019-06-26 14:52:38 +02:00
|
|
|
.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT,
|
|
|
|
.fAllocateNetBuffer = TRUE,
|
2019-07-03 12:43:55 +02:00
|
|
|
.PoolTag = TUN_MEMORY_TAG
|
2019-06-26 18:59:02 +02:00
|
|
|
};
|
2019-06-26 14:52:38 +02:00
|
|
|
#pragma warning( \
|
|
|
|
suppress : 6014) /* Leaking memory 'ctx->NBLPool'. Note: 'ctx->NBLPool' is freed in TunHaltEx; or on failure. */
|
2019-06-29 13:34:15 +02:00
|
|
|
Ctx->NBLPool = NdisAllocateNetBufferListPool(MiniportAdapterHandle, &NblPoolParameters);
|
|
|
|
if (!Ctx->NBLPool)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = NDIS_STATUS_FAILURE;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_NdisDeregisterDeviceEx;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES AdapterRegistrationAttributes = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES,
|
|
|
|
.Revision = NdisVersion < NDIS_RUNTIME_VERSION_630
|
|
|
|
? NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1
|
|
|
|
: NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2,
|
|
|
|
.Size = NdisVersion < NDIS_RUNTIME_VERSION_630
|
|
|
|
? NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1
|
|
|
|
: NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2 },
|
2019-06-26 14:52:38 +02:00
|
|
|
.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND | NDIS_MINIPORT_ATTRIBUTES_SURPRISE_REMOVE_OK,
|
|
|
|
.InterfaceType = NdisInterfaceInternal,
|
2019-06-29 13:34:15 +02:00
|
|
|
.MiniportAdapterContext = Ctx
|
2019-06-26 18:59:02 +02:00
|
|
|
};
|
2019-06-26 14:52:38 +02:00
|
|
|
if (!NT_SUCCESS(
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = NdisMSetMiniportAttributes(
|
|
|
|
MiniportAdapterHandle, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterRegistrationAttributes)))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = NDIS_STATUS_FAILURE;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_NdisFreeNetBufferListPool;
|
|
|
|
}
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_PM_CAPABILITIES PmCapabilities = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_DEFAULT,
|
|
|
|
.Revision = NdisVersion < NDIS_RUNTIME_VERSION_630 ? NDIS_PM_CAPABILITIES_REVISION_1
|
|
|
|
: NDIS_PM_CAPABILITIES_REVISION_2,
|
|
|
|
.Size = NdisVersion < NDIS_RUNTIME_VERSION_630 ? NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1
|
|
|
|
: NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_2 },
|
2019-06-26 14:52:38 +02:00
|
|
|
.MinMagicPacketWakeUp = NdisDeviceStateUnspecified,
|
|
|
|
.MinPatternWakeUp = NdisDeviceStateUnspecified,
|
2019-06-26 18:59:02 +02:00
|
|
|
.MinLinkChangeWakeUp = NdisDeviceStateUnspecified
|
|
|
|
};
|
2019-06-29 13:34:15 +02:00
|
|
|
static NDIS_OID SupportedOids[] = { OID_GEN_MAXIMUM_TOTAL_SIZE,
|
2019-06-26 18:59:02 +02:00
|
|
|
OID_GEN_CURRENT_LOOKAHEAD,
|
|
|
|
OID_GEN_TRANSMIT_BUFFER_SPACE,
|
|
|
|
OID_GEN_RECEIVE_BUFFER_SPACE,
|
|
|
|
OID_GEN_TRANSMIT_BLOCK_SIZE,
|
|
|
|
OID_GEN_RECEIVE_BLOCK_SIZE,
|
|
|
|
OID_GEN_VENDOR_DESCRIPTION,
|
|
|
|
OID_GEN_VENDOR_ID,
|
|
|
|
OID_GEN_VENDOR_DRIVER_VERSION,
|
|
|
|
OID_GEN_XMIT_OK,
|
|
|
|
OID_GEN_RCV_OK,
|
|
|
|
OID_GEN_CURRENT_PACKET_FILTER,
|
|
|
|
OID_GEN_STATISTICS,
|
|
|
|
OID_GEN_INTERRUPT_MODERATION,
|
|
|
|
OID_GEN_LINK_PARAMETERS,
|
|
|
|
OID_PNP_SET_POWER,
|
|
|
|
OID_PNP_QUERY_POWER };
|
2019-06-29 13:34:15 +02:00
|
|
|
NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES AdapterGeneralAttributes = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES,
|
|
|
|
.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2,
|
|
|
|
.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2 },
|
2019-06-26 14:52:38 +02:00
|
|
|
.MediaType = NdisMediumIP,
|
|
|
|
.PhysicalMediumType = NdisPhysicalMediumUnspecified,
|
|
|
|
.MtuSize = TUN_EXCH_MAX_IP_PACKET_SIZE,
|
|
|
|
.MaxXmitLinkSpeed = TUN_LINK_SPEED,
|
|
|
|
.MaxRcvLinkSpeed = TUN_LINK_SPEED,
|
|
|
|
.RcvLinkSpeed = TUN_LINK_SPEED,
|
|
|
|
.XmitLinkSpeed = TUN_LINK_SPEED,
|
|
|
|
.MediaConnectState = MediaConnectStateDisconnected,
|
|
|
|
.LookaheadSize = TUN_EXCH_MAX_IP_PACKET_SIZE,
|
|
|
|
.MacOptions =
|
|
|
|
NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_NO_LOOPBACK,
|
|
|
|
.SupportedPacketFilters = NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_MULTICAST |
|
|
|
|
NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_ALL_LOCAL |
|
|
|
|
NDIS_PACKET_TYPE_ALL_FUNCTIONAL,
|
|
|
|
.AccessType = NET_IF_ACCESS_BROADCAST,
|
|
|
|
.DirectionType = NET_IF_DIRECTION_SENDRECEIVE,
|
|
|
|
.ConnectionType = NET_IF_CONNECTION_DEDICATED,
|
|
|
|
.IfType = IF_TYPE_PROP_VIRTUAL,
|
|
|
|
.IfConnectorPresent = FALSE,
|
2019-06-29 13:34:15 +02:00
|
|
|
.SupportedStatistics = Ctx->Statistics.SupportedStatistics,
|
2019-06-26 14:52:38 +02:00
|
|
|
.SupportedPauseFunctions = NdisPauseFunctionsUnsupported,
|
|
|
|
.AutoNegotiationFlags =
|
|
|
|
NDIS_LINK_STATE_XMIT_LINK_SPEED_AUTO_NEGOTIATED | NDIS_LINK_STATE_RCV_LINK_SPEED_AUTO_NEGOTIATED |
|
|
|
|
NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED | NDIS_LINK_STATE_PAUSE_FUNCTIONS_AUTO_NEGOTIATED,
|
2019-06-29 13:34:15 +02:00
|
|
|
.SupportedOidList = SupportedOids,
|
|
|
|
.SupportedOidListLength = sizeof(SupportedOids),
|
|
|
|
.PowerManagementCapabilitiesEx = &PmCapabilities
|
2019-06-26 18:59:02 +02:00
|
|
|
};
|
2019-06-26 14:52:38 +02:00
|
|
|
if (!NT_SUCCESS(
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = NdisMSetMiniportAttributes(
|
|
|
|
MiniportAdapterHandle, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterGeneralAttributes)))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = NDIS_STATUS_FAILURE;
|
2019-06-26 14:52:38 +02:00
|
|
|
goto cleanup_NdisFreeNetBufferListPool;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A miniport driver can call NdisMIndicateStatusEx after setting its
|
|
|
|
* registration attributes even if the driver is still in the context
|
2019-06-26 19:37:29 +02:00
|
|
|
* of the MiniportInitializeEx function. */
|
2019-06-26 14:52:38 +02:00
|
|
|
TunIndicateStatus(MiniportAdapterHandle, MediaConnectStateDisconnected);
|
|
|
|
InterlockedIncrement64(&TunAdapterCount);
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedOr(&Ctx->Flags, TUN_FLAGS_PRESENT);
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
2019-03-08 22:33:15 +01:00
|
|
|
|
|
|
|
cleanup_NdisFreeNetBufferListPool:
|
2019-06-29 13:34:15 +02:00
|
|
|
NdisFreeNetBufferListPool(Ctx->NBLPool);
|
2019-04-10 13:42:16 +02:00
|
|
|
cleanup_NdisDeregisterDeviceEx:
|
2019-06-29 13:34:15 +02:00
|
|
|
NdisDeregisterDeviceEx(DeviceObjectHandle);
|
|
|
|
return Status;
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-17 04:05:45 +02:00
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static NTSTATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunDeviceSetDenyAllDacl(_In_ DEVICE_OBJECT *DeviceObject)
|
2019-06-17 04:05:45 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status;
|
|
|
|
SECURITY_DESCRIPTOR Sd;
|
|
|
|
ACL Acl;
|
|
|
|
HANDLE DeviceObjectHandle;
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(Status = RtlCreateSecurityDescriptor(&Sd, SECURITY_DESCRIPTOR_REVISION)))
|
|
|
|
return Status;
|
|
|
|
if (!NT_SUCCESS(Status = RtlCreateAcl(&Acl, sizeof(ACL), ACL_REVISION)))
|
|
|
|
return Status;
|
|
|
|
if (!NT_SUCCESS(Status = RtlSetDaclSecurityDescriptor(&Sd, TRUE, &Acl, FALSE)))
|
|
|
|
return Status;
|
|
|
|
Status = ObOpenObjectByPointer(
|
|
|
|
DeviceObject, OBJ_KERNEL_HANDLE, NULL, WRITE_DAC, *IoDeviceObjectType, KernelMode, &DeviceObjectHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
return Status;
|
|
|
|
|
|
|
|
Status = ZwSetSecurityObject(DeviceObjectHandle, DACL_SECURITY_INFORMATION, &Sd);
|
|
|
|
|
|
|
|
ZwClose(DeviceObjectHandle);
|
|
|
|
return Status;
|
2019-06-17 04:05:45 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-26 19:37:29 +02:00
|
|
|
TunForceHandlesClosed(_Inout_ TUN_CTX *Ctx)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
NTSTATUS Status;
|
|
|
|
PEPROCESS Process;
|
|
|
|
KAPC_STATE ApcState;
|
|
|
|
PVOID Object = NULL;
|
|
|
|
ULONG VerifierFlags = 0;
|
|
|
|
OBJECT_HANDLE_INFORMATION HandleInfo;
|
|
|
|
SYSTEM_HANDLE_INFORMATION_EX *HandleTable = NULL;
|
|
|
|
|
|
|
|
MmIsVerifierEnabled(&VerifierFlags);
|
|
|
|
|
|
|
|
for (ULONG Size = 0, RequestedSize;
|
|
|
|
(Status = ZwQuerySystemInformation(SystemExtendedHandleInformation, HandleTable, Size, &RequestedSize)) ==
|
|
|
|
STATUS_INFO_LENGTH_MISMATCH;
|
|
|
|
Size = RequestedSize)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
if (HandleTable)
|
2019-07-03 12:43:55 +02:00
|
|
|
ExFreePoolWithTag(HandleTable, TUN_MEMORY_TAG);
|
|
|
|
HandleTable = ExAllocatePoolWithTag(PagedPool, RequestedSize, TUN_MEMORY_TAG);
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!HandleTable)
|
2019-06-26 14:52:38 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
if (!NT_SUCCESS(Status) || !HandleTable)
|
2019-06-26 14:52:38 +02:00
|
|
|
goto out;
|
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
for (ULONG_PTR Index = 0; Index < HandleTable->NumberOfHandles; ++Index)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-27 15:08:29 +02:00
|
|
|
/* XXX: We should perhaps first look at table->Handles[i].ObjectTypeIndex, but
|
|
|
|
* the value changes lots between NT versions, and it should be implicit anyway. */
|
2019-06-29 13:34:15 +02:00
|
|
|
FILE_OBJECT *FileObject = HandleTable->Handles[Index].Object;
|
|
|
|
if (!FileObject || FileObject->Type != 5 || FileObject->DeviceObject != Ctx->Device.Object)
|
2019-06-26 14:52:38 +02:00
|
|
|
continue;
|
2019-06-29 13:34:15 +02:00
|
|
|
Status = PsLookupProcessByProcessId(HandleTable->Handles[Index].UniqueProcessId, &Process);
|
|
|
|
if (!NT_SUCCESS(Status))
|
2019-06-26 14:52:38 +02:00
|
|
|
continue;
|
2019-06-29 13:34:15 +02:00
|
|
|
KeStackAttachProcess(Process, &ApcState);
|
|
|
|
if (!VerifierFlags)
|
|
|
|
Status = ObReferenceObjectByHandle(
|
|
|
|
HandleTable->Handles[Index].HandleValue, 0, NULL, UserMode, &Object, &HandleInfo);
|
|
|
|
if (NT_SUCCESS(Status))
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
if (VerifierFlags || Object == FileObject)
|
|
|
|
ObCloseHandle(HandleTable->Handles[Index].HandleValue, UserMode);
|
|
|
|
if (!VerifierFlags)
|
|
|
|
ObfDereferenceObject(Object);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-06-29 13:34:15 +02:00
|
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
ObfDereferenceObject(Process);
|
2019-06-26 14:52:38 +02:00
|
|
|
}
|
2019-06-07 12:23:44 +02:00
|
|
|
out:
|
2019-06-29 13:34:15 +02:00
|
|
|
if (HandleTable)
|
2019-07-03 12:43:55 +02:00
|
|
|
ExFreePoolWithTag(HandleTable, TUN_MEMORY_TAG);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-05-31 14:40:49 +02:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
2019-06-29 13:34:15 +02:00
|
|
|
TunWaitForReferencesToDropToZero(_In_ DEVICE_OBJECT *DeviceObject)
|
2019-05-31 14:40:49 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
/* The sleep loop isn't pretty, but we don't have a choice. This is an NDIS bug we're working around. */
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
SleepTime = 50,
|
|
|
|
TotalTime = 2 * 60 * 1000,
|
|
|
|
MaxTries = TotalTime / SleepTime
|
|
|
|
};
|
|
|
|
#pragma warning(suppress : 28175)
|
2019-06-29 13:34:15 +02:00
|
|
|
for (int Try = 0; Try < MaxTries && DeviceObject->ReferenceCount; ++Try)
|
2019-06-26 14:52:38 +02:00
|
|
|
NdisMSleep(SleepTime);
|
2019-05-31 14:40:49 +02:00
|
|
|
}
|
|
|
|
|
2019-03-08 22:33:15 +01:00
|
|
|
static MINIPORT_HALT TunHaltEx;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltAction)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-29 13:34:15 +02:00
|
|
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
ASSERT(!InterlockedGet64(&Ctx->ActiveNBLCount)); /* Adapter should not be halted if there are (potential)
|
2019-06-27 15:08:29 +02:00
|
|
|
* active NBLs present. */
|
Switch to pending writes
Commentary from Jason:
Problem statement:
We call IoCompleteRequest(Irp) immediately after
NdisMIndicateReceiveNetBufferLists, which frees Irp->MdlAddress.
Since we've just given the same memory to
NdisMIndicateReceiveNetBufferLists (in a different MDL), we wind up
freeing the memory before NDIS finishes processing them.
Fix possibility 1:
Move IoCompleteRequest(Irp) to TunReturnNetBufferLists. This requires
reference counting how many NBLs are currently in flight that are
using an IRP. When that drops to zero, we can call IoCompleteRequest
(Irp).
Problem:
This means we have to block future wireguard-go Writes until *all*
NBLs have completed processing in the networking stack. Is that safe
to do? Will that introduce latency? Can userspace processes sabotage
it by refusing to read from a TCP socket buffer? We don't know enough
about how NdisMIndicateReceiveNetBufferLists works to assess its
characteristics here.
Fix possibility 2:
Use NDIS_RECEIVE_FLAGS_RESOURCES, so that
NdisMIndicateReceiveNetBufferLists makes a copy, and then we'll simply
free everything immediately after. This is slow, and it could
potentially lead to wireguard-go making the kernel allocate lots of
memory in the case that NdisAllocateNetBufferAndNetBufferList doesn't
ratelimit its creation in the same way Linux's skb_alloc does.
However, it does make the lifetime of Irps shorter, which is easier to
analyze, and it might lead to better latency, since we don't need to
wait until userspace sends its next packets, so long as Ndis'
ingestion queue doesn't become too large.
This commit switches from (2) to (1).
Signed-off-by: Simon Rozman <simon@rozman.si>
2019-05-30 21:12:55 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedAnd(&Ctx->Flags, ~TUN_FLAGS_PRESENT);
|
2019-06-27 11:59:22 +02:00
|
|
|
ExReleaseSpinLockExclusive(
|
2019-06-29 13:34:15 +02:00
|
|
|
&Ctx->TransitionLock,
|
|
|
|
ExAcquireSpinLockExclusive(&Ctx->TransitionLock)); /* Ensure above change is visible to all readers. */
|
2019-04-05 20:11:08 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
for (IRP *PendingIrp; (PendingIrp = IoCsqRemoveNextIrp(&Ctx->Device.ReadQueue.Csq, NULL)) != NULL;)
|
|
|
|
TunCompleteRequest(Ctx, PendingIrp, STATUS_FILE_FORCED_CLOSED, IO_NO_INCREMENT);
|
2019-06-03 14:08:06 +02:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
/* Setting a deny-all DACL we prevent userspace to open the device by symlink after TunForceHandlesClosed(). */
|
2019-06-29 13:34:15 +02:00
|
|
|
TunDeviceSetDenyAllDacl(Ctx->Device.Object);
|
2019-06-03 14:08:06 +02:00
|
|
|
|
2019-06-29 13:34:15 +02:00
|
|
|
if (InterlockedGet64(&Ctx->Device.RefCount))
|
|
|
|
TunForceHandlesClosed(Ctx);
|
2019-04-10 11:21:51 +02:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
/* Wait for processing IRP(s) to complete. */
|
2019-06-29 13:34:15 +02:00
|
|
|
IoAcquireRemoveLock(&Ctx->Device.RemoveLock, NULL);
|
|
|
|
IoReleaseRemoveLockAndWait(&Ctx->Device.RemoveLock, NULL);
|
|
|
|
NdisFreeNetBufferListPool(Ctx->NBLPool);
|
2019-04-10 13:42:16 +02:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
/* MiniportAdapterHandle must not be used in TunDispatch(). After TunHaltEx() returns it is invalidated. */
|
2019-06-29 13:34:15 +02:00
|
|
|
InterlockedExchangePointer(&Ctx->MiniportAdapterHandle, NULL);
|
2019-04-10 13:42:16 +02:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
ASSERT(InterlockedGet64(&TunAdapterCount) > 0);
|
|
|
|
if (InterlockedDecrement64(&TunAdapterCount) <= 0)
|
2019-06-29 13:34:15 +02:00
|
|
|
TunWaitForReferencesToDropToZero(Ctx->Device.Object);
|
2019-05-31 14:40:49 +02:00
|
|
|
|
2019-06-26 14:52:38 +02:00
|
|
|
/* Deregister device _after_ we are done using ctx not to risk an UaF. The ctx is hosted by device extension. */
|
2019-06-29 13:34:15 +02:00
|
|
|
NdisDeregisterDeviceEx(Ctx->Device.Handle);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
static MINIPORT_SHUTDOWN TunShutdownEx;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunShutdownEx(NDIS_HANDLE MiniportAdapterContext, NDIS_SHUTDOWN_ACTION ShutdownAction)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-04-05 11:04:13 +02:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunOidQueryWrite(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG Value)
|
2019-04-05 11:04:13 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG))
|
|
|
|
{
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG);
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
|
|
|
|
return NDIS_STATUS_BUFFER_TOO_SHORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
|
2019-06-26 19:37:29 +02:00
|
|
|
*(ULONG *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = Value;
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
2019-04-05 11:04:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunOidQueryWrite32or64(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG64 Value)
|
2019-04-05 11:04:13 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG))
|
|
|
|
{
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG64);
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
|
|
|
|
return NDIS_STATUS_BUFFER_TOO_SHORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG64))
|
|
|
|
{
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG64);
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
|
2019-06-26 19:37:29 +02:00
|
|
|
*(ULONG *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (ULONG)(Value & 0xffffffff);
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG64);
|
2019-06-26 19:37:29 +02:00
|
|
|
*(ULONG64 *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = Value;
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
2019-04-05 11:04:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
2019-06-26 19:37:29 +02:00
|
|
|
TunOidQueryWriteBuf(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_bytecount_(Size) const void *Buf, _In_ UINT Size)
|
2019-04-05 11:04:13 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < Size)
|
2019-06-26 14:52:38 +02:00
|
|
|
{
|
2019-06-26 19:37:29 +02:00
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = Size;
|
2019-06-26 14:52:38 +02:00
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
|
|
|
|
return NDIS_STATUS_BUFFER_TOO_SHORT;
|
|
|
|
}
|
|
|
|
|
2019-06-26 19:37:29 +02:00
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = Size;
|
|
|
|
NdisMoveMemory(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, Buf, Size);
|
2019-06-26 14:52:38 +02:00
|
|
|
return NDIS_STATUS_SUCCESS;
|
2019-04-05 11:04:13 +02:00
|
|
|
}
|
|
|
|
|
2019-03-08 22:33:15 +01:00
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
2019-03-28 11:21:51 +01:00
|
|
|
_Must_inspect_result_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunOidQuery(_Inout_ TUN_CTX *ctx, _Inout_ NDIS_OID_REQUEST *OidRequest)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
ASSERT(
|
|
|
|
OidRequest->RequestType == NdisRequestQueryInformation ||
|
|
|
|
OidRequest->RequestType == NdisRequestQueryStatistics);
|
|
|
|
|
|
|
|
switch (OidRequest->DATA.QUERY_INFORMATION.Oid)
|
|
|
|
{
|
|
|
|
case OID_GEN_MAXIMUM_TOTAL_SIZE:
|
|
|
|
case OID_GEN_TRANSMIT_BLOCK_SIZE:
|
|
|
|
case OID_GEN_RECEIVE_BLOCK_SIZE:
|
|
|
|
return TunOidQueryWrite(OidRequest, TUN_EXCH_MAX_IP_PACKET_SIZE);
|
|
|
|
|
|
|
|
case OID_GEN_TRANSMIT_BUFFER_SPACE:
|
|
|
|
return TunOidQueryWrite(OidRequest, TUN_EXCH_MAX_IP_PACKET_SIZE * TUN_QUEUE_MAX_NBLS);
|
|
|
|
|
|
|
|
case OID_GEN_RECEIVE_BUFFER_SPACE:
|
|
|
|
return TunOidQueryWrite(OidRequest, TUN_EXCH_MAX_IP_PACKET_SIZE * TUN_EXCH_MAX_PACKETS);
|
|
|
|
|
|
|
|
case OID_GEN_VENDOR_ID:
|
|
|
|
return TunOidQueryWrite(OidRequest, TUN_HTONL(TUN_VENDOR_ID));
|
|
|
|
|
|
|
|
case OID_GEN_VENDOR_DESCRIPTION:
|
|
|
|
return TunOidQueryWriteBuf(OidRequest, TUN_VENDOR_NAME, (UINT)sizeof(TUN_VENDOR_NAME));
|
|
|
|
|
|
|
|
case OID_GEN_VENDOR_DRIVER_VERSION:
|
|
|
|
return TunOidQueryWrite(OidRequest, (WINTUN_VERSION_MAJ << 16) | WINTUN_VERSION_MIN);
|
|
|
|
|
|
|
|
case OID_GEN_XMIT_OK:
|
|
|
|
return TunOidQueryWrite32or64(
|
|
|
|
OidRequest,
|
|
|
|
InterlockedGet64((LONG64 *)&ctx->Statistics.ifHCOutUcastPkts) +
|
|
|
|
InterlockedGet64((LONG64 *)&ctx->Statistics.ifHCOutMulticastPkts) +
|
|
|
|
InterlockedGet64((LONG64 *)&ctx->Statistics.ifHCOutBroadcastPkts));
|
|
|
|
|
|
|
|
case OID_GEN_RCV_OK:
|
|
|
|
return TunOidQueryWrite32or64(
|
|
|
|
OidRequest,
|
|
|
|
InterlockedGet64((LONG64 *)&ctx->Statistics.ifHCInUcastPkts) +
|
|
|
|
InterlockedGet64((LONG64 *)&ctx->Statistics.ifHCInMulticastPkts) +
|
|
|
|
InterlockedGet64((LONG64 *)&ctx->Statistics.ifHCInBroadcastPkts));
|
|
|
|
|
|
|
|
case OID_GEN_STATISTICS:
|
|
|
|
return TunOidQueryWriteBuf(OidRequest, &ctx->Statistics, (UINT)sizeof(ctx->Statistics));
|
|
|
|
|
|
|
|
case OID_GEN_INTERRUPT_MODERATION: {
|
|
|
|
static const NDIS_INTERRUPT_MODERATION_PARAMETERS intp = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_DEFAULT,
|
|
|
|
.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1,
|
|
|
|
.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1 },
|
|
|
|
.InterruptModeration = NdisInterruptModerationNotSupported
|
|
|
|
};
|
2019-06-26 14:52:38 +02:00
|
|
|
return TunOidQueryWriteBuf(OidRequest, &intp, (UINT)sizeof(intp));
|
|
|
|
}
|
|
|
|
|
|
|
|
case OID_PNP_QUERY_POWER:
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
|
|
|
|
return NDIS_STATUS_NOT_SUPPORTED;
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunOidSet(_Inout_ TUN_CTX *ctx, _Inout_ NDIS_OID_REQUEST *OidRequest)
|
2019-06-07 12:23:44 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
ASSERT(OidRequest->RequestType == NdisRequestSetInformation);
|
|
|
|
|
|
|
|
OidRequest->DATA.SET_INFORMATION.BytesNeeded = OidRequest->DATA.SET_INFORMATION.BytesRead = 0;
|
|
|
|
|
|
|
|
switch (OidRequest->DATA.SET_INFORMATION.Oid)
|
|
|
|
{
|
|
|
|
case OID_GEN_CURRENT_PACKET_FILTER:
|
|
|
|
case OID_GEN_CURRENT_LOOKAHEAD:
|
|
|
|
if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength != 4)
|
|
|
|
{
|
|
|
|
OidRequest->DATA.SET_INFORMATION.BytesNeeded = 4;
|
|
|
|
return NDIS_STATUS_INVALID_LENGTH;
|
|
|
|
}
|
|
|
|
OidRequest->DATA.SET_INFORMATION.BytesRead = 4;
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
case OID_GEN_LINK_PARAMETERS:
|
|
|
|
OidRequest->DATA.SET_INFORMATION.BytesRead = OidRequest->DATA.SET_INFORMATION.InformationBufferLength;
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
case OID_GEN_INTERRUPT_MODERATION:
|
|
|
|
return NDIS_STATUS_INVALID_DATA;
|
|
|
|
|
|
|
|
case OID_PNP_SET_POWER:
|
|
|
|
if (OidRequest->DATA.SET_INFORMATION.InformationBufferLength != sizeof(NDIS_DEVICE_POWER_STATE))
|
|
|
|
{
|
|
|
|
OidRequest->DATA.SET_INFORMATION.BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE);
|
|
|
|
return NDIS_STATUS_INVALID_LENGTH;
|
|
|
|
}
|
|
|
|
OidRequest->DATA.SET_INFORMATION.BytesRead = sizeof(NDIS_DEVICE_POWER_STATE);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NDIS_STATUS_NOT_SUPPORTED;
|
2019-06-07 12:23:44 +02:00
|
|
|
}
|
|
|
|
|
2019-03-08 22:33:15 +01:00
|
|
|
static MINIPORT_OID_REQUEST TunOidRequest;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunOidRequest(NDIS_HANDLE MiniportAdapterContext, PNDIS_OID_REQUEST OidRequest)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
switch (OidRequest->RequestType)
|
|
|
|
{
|
|
|
|
case NdisRequestQueryInformation:
|
|
|
|
case NdisRequestQueryStatistics:
|
|
|
|
return TunOidQuery(MiniportAdapterContext, OidRequest);
|
|
|
|
|
|
|
|
case NdisRequestSetInformation:
|
|
|
|
return TunOidSet(MiniportAdapterContext, OidRequest);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NDIS_STATUS_INVALID_OID;
|
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
static MINIPORT_CANCEL_OID_REQUEST TunCancelOidRequest;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunCancelOidRequest(NDIS_HANDLE MiniportAdapterContext, PVOID RequestId)
|
2019-06-07 12:23:44 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-03-08 22:33:15 +01:00
|
|
|
static MINIPORT_DIRECT_OID_REQUEST TunDirectOidRequest;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunDirectOidRequest(NDIS_HANDLE MiniportAdapterContext, PNDIS_OID_REQUEST OidRequest)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
switch (OidRequest->RequestType)
|
|
|
|
{
|
|
|
|
case NdisRequestQueryInformation:
|
|
|
|
case NdisRequestQueryStatistics:
|
|
|
|
case NdisRequestSetInformation:
|
|
|
|
return NDIS_STATUS_NOT_SUPPORTED;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NDIS_STATUS_INVALID_OID;
|
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
static MINIPORT_CANCEL_DIRECT_OID_REQUEST TunCancelDirectOidRequest;
|
2019-03-08 22:33:15 +01:00
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static void
|
|
|
|
TunCancelDirectOidRequest(NDIS_HANDLE MiniportAdapterContext, PVOID RequestId)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-07 12:23:44 +02:00
|
|
|
}
|
2019-03-08 22:33:15 +01:00
|
|
|
|
2019-06-21 11:43:17 +02:00
|
|
|
static MINIPORT_SYNCHRONOUS_OID_REQUEST TunSynchronousOidRequest;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static NDIS_STATUS
|
|
|
|
TunSynchronousOidRequest(NDIS_HANDLE MiniportAdapterContext, PNDIS_OID_REQUEST OidRequest)
|
2019-06-21 11:43:17 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
switch (OidRequest->RequestType)
|
|
|
|
{
|
|
|
|
case NdisRequestQueryInformation:
|
|
|
|
case NdisRequestQueryStatistics:
|
|
|
|
case NdisRequestSetInformation:
|
|
|
|
return NDIS_STATUS_NOT_SUPPORTED;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NDIS_STATUS_INVALID_OID;
|
|
|
|
}
|
2019-06-21 11:43:17 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 12:23:44 +02:00
|
|
|
static MINIPORT_UNLOAD TunUnload;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
static VOID
|
|
|
|
TunUnload(PDRIVER_OBJECT DriverObject)
|
2019-06-07 12:23:44 +02:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
NdisMDeregisterMiniportDriver(NdisMiniportDriverHandle);
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DRIVER_INITIALIZE DriverEntry;
|
|
|
|
_Use_decl_annotations_
|
2019-06-26 14:52:38 +02:00
|
|
|
NTSTATUS
|
|
|
|
DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
|
2019-03-08 22:33:15 +01:00
|
|
|
{
|
2019-06-26 14:52:38 +02:00
|
|
|
NTSTATUS status;
|
|
|
|
|
|
|
|
NdisVersion = NdisGetVersion();
|
|
|
|
if (NdisVersion < NDIS_MINIPORT_VERSION_MIN)
|
|
|
|
return NDIS_STATUS_UNSUPPORTED_REVISION;
|
|
|
|
if (NdisVersion > NDIS_MINIPORT_VERSION_MAX)
|
|
|
|
NdisVersion = NDIS_MINIPORT_VERSION_MAX;
|
|
|
|
|
|
|
|
NDIS_MINIPORT_DRIVER_CHARACTERISTICS miniport = {
|
2019-06-26 18:59:02 +02:00
|
|
|
.Header = { .Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS,
|
|
|
|
.Revision = NdisVersion < NDIS_RUNTIME_VERSION_680
|
|
|
|
? NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2
|
|
|
|
: NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_3,
|
|
|
|
.Size = NdisVersion < NDIS_RUNTIME_VERSION_680
|
|
|
|
? NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2
|
|
|
|
: NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_3 },
|
2019-06-26 14:52:38 +02:00
|
|
|
|
|
|
|
.MajorNdisVersion = (UCHAR)((NdisVersion & 0x00ff0000) >> 16),
|
|
|
|
.MinorNdisVersion = (UCHAR)(NdisVersion & 0x000000ff),
|
|
|
|
|
|
|
|
.MajorDriverVersion = WINTUN_VERSION_MAJ,
|
|
|
|
.MinorDriverVersion = WINTUN_VERSION_MIN,
|
|
|
|
|
|
|
|
.InitializeHandlerEx = TunInitializeEx,
|
|
|
|
.HaltHandlerEx = TunHaltEx,
|
|
|
|
.UnloadHandler = TunUnload,
|
|
|
|
.PauseHandler = TunPause,
|
|
|
|
.RestartHandler = TunRestart,
|
|
|
|
.OidRequestHandler = TunOidRequest,
|
|
|
|
.SendNetBufferListsHandler = TunSendNetBufferLists,
|
|
|
|
.ReturnNetBufferListsHandler = TunReturnNetBufferLists,
|
|
|
|
.CancelSendHandler = TunCancelSend,
|
|
|
|
.DevicePnPEventNotifyHandler = TunDevicePnPEventNotify,
|
|
|
|
.ShutdownHandlerEx = TunShutdownEx,
|
|
|
|
.CancelOidRequestHandler = TunCancelOidRequest,
|
|
|
|
.DirectOidRequestHandler = TunDirectOidRequest,
|
|
|
|
.CancelDirectOidRequestHandler = TunCancelDirectOidRequest,
|
2019-06-26 18:59:02 +02:00
|
|
|
.SynchronousOidRequestHandler = TunSynchronousOidRequest
|
|
|
|
};
|
2019-06-26 19:37:29 +02:00
|
|
|
status = NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle);
|
|
|
|
if (!NT_SUCCESS(status))
|
2019-06-26 14:52:38 +02:00
|
|
|
return status;
|
|
|
|
|
|
|
|
NdisDispatchPnP = DriverObject->MajorFunction[IRP_MJ_PNP];
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = TunDispatchPnP;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
2019-03-08 22:33:15 +01:00
|
|
|
}
|