Migrate device-specific data to device extension

This allows to keep all device pipe specific information to persist as
long as device is in use.

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2019-04-09 13:16:40 +02:00
parent 2fa408a92f
commit 04f2cc318e

427
wintun.c
View File

@ -65,24 +65,30 @@ typedef struct _TUN_CTX {
struct { struct {
NDIS_HANDLE Handle; NDIS_HANDLE Handle;
DEVICE_OBJECT *Object; DEVICE_OBJECT *Object;
volatile LONG64 RefCount; } Device;
struct { NDIS_HANDLE NBLPool;
} TUN_CTX;
typedef struct _TUN_IRP_QUEUE {
KSPIN_LOCK Lock; KSPIN_LOCK Lock;
IO_CSQ Csq; IO_CSQ Csq;
LIST_ENTRY List; LIST_ENTRY List;
} ReadQueue; } TUN_IRP_QUEUE;
} Device;
struct { typedef struct _TUN_PACKET_QUEUE {
KSPIN_LOCK Lock; KSPIN_LOCK Lock;
NET_BUFFER_LIST *FirstNbl, *LastNbl; NET_BUFFER_LIST *FirstNbl, *LastNbl;
NET_BUFFER *NextNb; NET_BUFFER *NextNb;
LONG NumNbl; LONG NumNbl;
} PacketQueue; } TUN_PACKET_QUEUE;
NDIS_HANDLE NBLPool; typedef struct _TUN_DEVEXT {
} TUN_CTX; TUN_CTX * volatile MiniportAdapterContext;
volatile LONG64 RefCount;
TUN_IRP_QUEUE ReadQueue;
TUN_PACKET_QUEUE PacketQueue;
} TUN_DEVEXT;
static UINT NdisVersion; static UINT NdisVersion;
static NDIS_HANDLE NdisMiniportDriverHandle = NULL; static NDIS_HANDLE NdisMiniportDriverHandle = NULL;
@ -112,7 +118,7 @@ static NDIS_HANDLE NdisMiniportDriverHandle = NULL;
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_same_ _IRQL_requires_same_
static void TunIndicateStatus(_In_ TUN_CTX *ctx) static void TunIndicateStatus(_In_ NDIS_HANDLE MiniportAdapterHandle, _In_ NDIS_MEDIA_CONNECT_STATE MediaConnectState)
{ {
NDIS_LINK_STATE state = { NDIS_LINK_STATE state = {
.Header = { .Header = {
@ -120,7 +126,7 @@ static void TunIndicateStatus(_In_ TUN_CTX *ctx)
.Revision = NDIS_LINK_STATE_REVISION_1, .Revision = NDIS_LINK_STATE_REVISION_1,
.Size = NDIS_SIZEOF_LINK_STATE_REVISION_1 .Size = NDIS_SIZEOF_LINK_STATE_REVISION_1
}, },
.MediaConnectState = ctx->Device.RefCount > 0 ? MediaConnectStateConnected : MediaConnectStateDisconnected, .MediaConnectState = MediaConnectState,
.MediaDuplexState = MediaDuplexStateFull, .MediaDuplexState = MediaDuplexStateFull,
.XmitLinkSpeed = TUN_LINK_SPEED, .XmitLinkSpeed = TUN_LINK_SPEED,
.RcvLinkSpeed = TUN_LINK_SPEED, .RcvLinkSpeed = TUN_LINK_SPEED,
@ -133,29 +139,13 @@ static void TunIndicateStatus(_In_ TUN_CTX *ctx)
.Revision = NDIS_STATUS_INDICATION_REVISION_1, .Revision = NDIS_STATUS_INDICATION_REVISION_1,
.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1 .Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1
}, },
.SourceHandle = ctx->MiniportAdapterHandle, .SourceHandle = MiniportAdapterHandle,
.StatusCode = NDIS_STATUS_LINK_STATE, .StatusCode = NDIS_STATUS_LINK_STATE,
.StatusBuffer = &state, .StatusBuffer = &state,
.StatusBufferSize = sizeof(state) .StatusBufferSize = sizeof(state)
}; };
NdisMIndicateStatusEx(ctx->MiniportAdapterHandle, &t); NdisMIndicateStatusEx(MiniportAdapterHandle, &t);
}
_IRQL_requires_max_(HIGH_LEVEL)
_Must_inspect_result_
static _Return_type_success_(return != NULL) TUN_CTX * volatile *TunGetContextPointer(_In_ DEVICE_OBJECT *DeviceObject)
{
return (TUN_CTX * volatile *)NdisGetDeviceReservedExtension(DeviceObject);
}
_IRQL_requires_max_(HIGH_LEVEL)
_Must_inspect_result_
static _Return_type_success_(return != NULL) TUN_CTX *TunGetContext(_In_ DEVICE_OBJECT *DeviceObject)
{
TUN_CTX * volatile * ctx = TunGetContextPointer(DeviceObject);
return ctx ? InterlockedGetPointer(ctx) : NULL;
} }
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
@ -196,8 +186,8 @@ static IO_CSQ_INSERT_IRP_EX TunCsqInsertIrpEx;
_Use_decl_annotations_ _Use_decl_annotations_
static NTSTATUS TunCsqInsertIrpEx(IO_CSQ *Csq, IRP *Irp, PVOID InsertContext) static NTSTATUS TunCsqInsertIrpEx(IO_CSQ *Csq, IRP *Irp, PVOID InsertContext)
{ {
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq); TUN_IRP_QUEUE *queue = CONTAINING_RECORD(Csq, TUN_IRP_QUEUE, Csq);
(InsertContext == TUN_CSQ_INSERT_HEAD ? InsertHeadList : InsertTailList)(&ctx->Device.ReadQueue.List, &Irp->Tail.Overlay.ListEntry); (InsertContext == TUN_CSQ_INSERT_HEAD ? InsertHeadList : InsertTailList)(&queue->List, &Irp->Tail.Overlay.ListEntry);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -212,13 +202,13 @@ static IO_CSQ_PEEK_NEXT_IRP TunCsqPeekNextIrp;
_Use_decl_annotations_ _Use_decl_annotations_
static IRP *TunCsqPeekNextIrp(IO_CSQ *Csq, IRP *Irp, _In_ PVOID PeekContext) static IRP *TunCsqPeekNextIrp(IO_CSQ *Csq, IRP *Irp, _In_ PVOID PeekContext)
{ {
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq); TUN_IRP_QUEUE *queue = CONTAINING_RECORD(Csq, TUN_IRP_QUEUE, Csq);
/* If the IRP is non-NULL, we will start peeking from that IRP onwards, else /* 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 * we will start from the listhead. This is done under the assumption that
* new IRPs are always inserted at the tail. */ * new IRPs are always inserted at the tail. */
for (LIST_ENTRY for (LIST_ENTRY
*head = &ctx->Device.ReadQueue.List, *head = &queue->List,
*next = Irp ? Irp->Tail.Overlay.ListEntry.Flink : head->Flink; *next = Irp ? Irp->Tail.Overlay.ListEntry.Flink : head->Flink;
next != head; next != head;
next = next->Flink) next = next->Flink)
@ -237,19 +227,19 @@ static IRP *TunCsqPeekNextIrp(IO_CSQ *Csq, IRP *Irp, _In_ PVOID PeekContext)
_IRQL_raises_(DISPATCH_LEVEL) _IRQL_raises_(DISPATCH_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Acquires_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock) _Acquires_lock_(CONTAINING_RECORD(Csq, TUN_IRP_QUEUE, Csq)->Lock)
static VOID TunCsqAcquireLock(_In_ IO_CSQ *Csq, _Out_ _At_(*Irql, _Post_ _IRQL_saves_) KIRQL *Irql) static VOID TunCsqAcquireLock(_In_ IO_CSQ *Csq, _Out_ _At_(*Irql, _Post_ _IRQL_saves_) KIRQL *Irql)
{ {
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq); TUN_IRP_QUEUE *queue = CONTAINING_RECORD(Csq, TUN_IRP_QUEUE, Csq);
KeAcquireSpinLock(&ctx->Device.ReadQueue.Lock, Irql); KeAcquireSpinLock(&queue->Lock, Irql);
} }
_IRQL_requires_(DISPATCH_LEVEL) _IRQL_requires_(DISPATCH_LEVEL)
_Releases_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock) _Releases_lock_(CONTAINING_RECORD(Csq, TUN_IRP_QUEUE, Csq)->Lock)
static VOID TunCsqReleaseLock(_In_ IO_CSQ *Csq, _In_ _IRQL_restores_ KIRQL Irql) static VOID TunCsqReleaseLock(_In_ IO_CSQ *Csq, _In_ _IRQL_restores_ KIRQL Irql)
{ {
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq); TUN_IRP_QUEUE *queue = CONTAINING_RECORD(Csq, TUN_IRP_QUEUE, Csq);
KeReleaseSpinLock(&ctx->Device.ReadQueue.Lock, Irql); KeReleaseSpinLock(&queue->Lock, Irql);
} }
static IO_CSQ_COMPLETE_CANCELED_IRP TunCsqCompleteCanceledIrp; static IO_CSQ_COMPLETE_CANCELED_IRP TunCsqCompleteCanceledIrp;
@ -318,12 +308,12 @@ static NTSTATUS TunGetIrpBuffer(_In_ IRP *Irp, _Out_ UCHAR **buffer, _Out_ ULONG
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static _Return_type_success_(return != NULL) IRP *TunRemoveNextIrp(_Inout_ TUN_CTX *ctx, _Out_ UCHAR ** buffer, _Out_ ULONG *size) static _Return_type_success_(return != NULL) IRP *TunRemoveNextIrp(_Inout_ TUN_DEVEXT *devext, _Out_ UCHAR **buffer, _Out_ ULONG *size)
{ {
IRP *irp; IRP *irp;
retry: retry:
irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, NULL); irp = IoCsqRemoveNextIrp(&devext->ReadQueue.Csq, NULL);
if (!irp) if (!irp)
return NULL; return NULL;
@ -347,7 +337,7 @@ static BOOLEAN TunCanFitIntoIrp(_In_ IRP *Irp, _In_ ULONG size, _In_ NET_BUFFER
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NTSTATUS TunWriteIntoIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp, _Inout_ UCHAR *buffer, _In_ NET_BUFFER *nb) static NTSTATUS TunWriteIntoIrp(_Inout_ IRP *Irp, _Inout_ UCHAR *buffer, _In_ NET_BUFFER *nb, _Inout_ NDIS_STATISTICS_INFO *statistics)
{ {
ULONG p_size = NET_BUFFER_DATA_LENGTH(nb); ULONG p_size = NET_BUFFER_DATA_LENGTH(nb);
TUN_PACKET *p = (TUN_PACKET *)(buffer + Irp->IoStatus.Information); TUN_PACKET *p = (TUN_PACKET *)(buffer + Irp->IoStatus.Information);
@ -355,7 +345,8 @@ static NTSTATUS TunWriteIntoIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp, _Inout_
p->Size = p_size; p->Size = p_size;
void *ptr = NdisGetDataBuffer(nb, p_size, p->Data, 1, 0); void *ptr = NdisGetDataBuffer(nb, p_size, p->Data, 1, 0);
if (!ptr) { if (!ptr) {
InterlockedIncrement64((LONG64 *)&ctx->Statistics.ifOutErrors); if (statistics)
InterlockedIncrement64((LONG64 *)&statistics->ifOutErrors);
return NDIS_STATUS_RESOURCES; return NDIS_STATUS_RESOURCES;
} }
if (ptr != p->Data) if (ptr != p->Data)
@ -363,19 +354,19 @@ static NTSTATUS TunWriteIntoIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp, _Inout_
Irp->IoStatus.Information += TunPacketAlign(sizeof(TUN_PACKET) + p_size); Irp->IoStatus.Information += TunPacketAlign(sizeof(TUN_PACKET) + p_size);
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCOutOctets, p_size); InterlockedAdd64((LONG64 *)&statistics->ifHCOutOctets, p_size);
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCOutUcastOctets, p_size); InterlockedAdd64((LONG64 *)&statistics->ifHCOutUcastOctets, p_size);
InterlockedIncrement64((LONG64 *)&ctx->Statistics.ifHCOutUcastPkts); InterlockedIncrement64((LONG64 *)&statistics->ifHCOutUcastPkts);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
#define NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl)) #define NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl))
_IRQL_requires_same_ _IRQL_requires_same_
static void TunNBLRefInit(_Inout_ TUN_CTX *ctx, _Inout_ NET_BUFFER_LIST *nbl) static void TunNBLRefInit(_Inout_ TUN_DEVEXT *devext, _Inout_ NET_BUFFER_LIST *nbl)
{ {
InterlockedAdd64(&ctx->ActiveTransactionCount, 1); InterlockedAdd64(&devext->MiniportAdapterContext->ActiveTransactionCount, 1);
InterlockedAdd(&ctx->PacketQueue.NumNbl, 1); InterlockedAdd(&devext->PacketQueue.NumNbl, 1);
InterlockedExchange64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1); InterlockedExchange64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1);
} }
@ -388,14 +379,14 @@ static void TunNBLRefInc(_Inout_ NET_BUFFER_LIST *nbl)
_When_( (SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_ (DISPATCH_LEVEL)) _When_( (SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_ (DISPATCH_LEVEL))
_When_(!(SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_max_(DISPATCH_LEVEL)) _When_(!(SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_max_(DISPATCH_LEVEL))
static BOOLEAN TunNBLRefDec(_Inout_ TUN_CTX *ctx, _Inout_ NET_BUFFER_LIST *nbl, _In_ ULONG SendCompleteFlags) static BOOLEAN TunNBLRefDec(_Inout_ TUN_DEVEXT *devext, _Inout_ NET_BUFFER_LIST *nbl, _In_ ULONG SendCompleteFlags)
{ {
ASSERT(InterlockedGet64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl))); ASSERT(InterlockedGet64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl)));
if (!InterlockedSubtract64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1)) { if (!InterlockedSubtract64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1)) {
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, SendCompleteFlags); NdisMSendNetBufferListsComplete(devext->MiniportAdapterContext->MiniportAdapterHandle, nbl, SendCompleteFlags);
InterlockedSubtract(&ctx->PacketQueue.NumNbl, 1); InterlockedSubtract(&devext->PacketQueue.NumNbl, 1);
TunCompletePause(ctx, TRUE); TunCompletePause(devext->MiniportAdapterContext, TRUE);
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
@ -409,114 +400,114 @@ static void TunAppendNBL(_Inout_ NET_BUFFER_LIST **head, _Inout_ NET_BUFFER_LIST
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
} }
_Requires_lock_not_held_(ctx->PacketQueue.Lock) _Requires_lock_not_held_(devext->PacketQueue.Lock)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
static void TunQueueAppend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER_LIST *nbl, _In_ UINT max_nbls) static void TunQueueAppend(_Inout_ TUN_DEVEXT *devext, _In_ NET_BUFFER_LIST *nbl, _In_ UINT max_nbls)
{ {
for (NET_BUFFER_LIST *nbl_next; nbl; nbl = nbl_next) { for (NET_BUFFER_LIST *nbl_next; nbl; nbl = nbl_next) {
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl); nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
if (!NET_BUFFER_LIST_FIRST_NB(nbl)) { if (!NET_BUFFER_LIST_FIRST_NB(nbl)) {
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, 0); NdisMSendNetBufferListsComplete(devext->MiniportAdapterContext->MiniportAdapterHandle, nbl, 0);
continue; continue;
} }
KLOCK_QUEUE_HANDLE lqh; KLOCK_QUEUE_HANDLE lqh;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); KeAcquireInStackQueuedSpinLock(&devext->PacketQueue.Lock, &lqh);
TunNBLRefInit(ctx, nbl); TunNBLRefInit(devext, nbl);
TunAppendNBL(&ctx->PacketQueue.FirstNbl, &ctx->PacketQueue.LastNbl, nbl); TunAppendNBL(&devext->PacketQueue.FirstNbl, &devext->PacketQueue.LastNbl, nbl);
while ((UINT)InterlockedGet(&ctx->PacketQueue.NumNbl) > max_nbls && ctx->PacketQueue.FirstNbl) { while ((UINT)InterlockedGet(&devext->PacketQueue.NumNbl) > max_nbls && devext->PacketQueue.FirstNbl) {
NET_BUFFER_LIST *nbl_second = NET_BUFFER_LIST_NEXT_NBL(ctx->PacketQueue.FirstNbl); NET_BUFFER_LIST *nbl_second = NET_BUFFER_LIST_NEXT_NBL(devext->PacketQueue.FirstNbl);
NET_BUFFER_LIST_STATUS(ctx->PacketQueue.FirstNbl) = NDIS_STATUS_SEND_ABORTED; NET_BUFFER_LIST_STATUS(devext->PacketQueue.FirstNbl) = NDIS_STATUS_SEND_ABORTED;
TunNBLRefDec(ctx, ctx->PacketQueue.FirstNbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(devext, devext->PacketQueue.FirstNbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
ctx->PacketQueue.NextNb = NULL; devext->PacketQueue.NextNb = NULL;
ctx->PacketQueue.FirstNbl = nbl_second; devext->PacketQueue.FirstNbl = nbl_second;
if (!ctx->PacketQueue.FirstNbl) if (!devext->PacketQueue.FirstNbl)
ctx->PacketQueue.LastNbl = NULL; devext->PacketQueue.LastNbl = NULL;
} }
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
} }
} }
_Requires_lock_held_(ctx->PacketQueue.Lock) _Requires_lock_held_(devext->PacketQueue.Lock)
_IRQL_requires_(DISPATCH_LEVEL) _IRQL_requires_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static _Return_type_success_(return != NULL) NET_BUFFER *TunQueueRemove(_Inout_ TUN_CTX *ctx, _Out_ NET_BUFFER_LIST **nbl) static _Return_type_success_(return != NULL) NET_BUFFER *TunQueueRemove(_Inout_ TUN_DEVEXT *devext, _Out_ NET_BUFFER_LIST **nbl)
{ {
NET_BUFFER_LIST *nbl_top; NET_BUFFER_LIST *nbl_top;
NET_BUFFER *ret; NET_BUFFER *ret;
retry: retry:
nbl_top = ctx->PacketQueue.FirstNbl; nbl_top = devext->PacketQueue.FirstNbl;
*nbl = nbl_top; *nbl = nbl_top;
if (!nbl_top) if (!nbl_top)
return NULL; return NULL;
if (!ctx->PacketQueue.NextNb) if (!devext->PacketQueue.NextNb)
ctx->PacketQueue.NextNb = NET_BUFFER_LIST_FIRST_NB(nbl_top); devext->PacketQueue.NextNb = NET_BUFFER_LIST_FIRST_NB(nbl_top);
ret = ctx->PacketQueue.NextNb; ret = devext->PacketQueue.NextNb;
ctx->PacketQueue.NextNb = NET_BUFFER_NEXT_NB(ret); devext->PacketQueue.NextNb = NET_BUFFER_NEXT_NB(ret);
if (!ctx->PacketQueue.NextNb) { if (!devext->PacketQueue.NextNb) {
ctx->PacketQueue.FirstNbl = NET_BUFFER_LIST_NEXT_NBL(nbl_top); devext->PacketQueue.FirstNbl = NET_BUFFER_LIST_NEXT_NBL(nbl_top);
if (!ctx->PacketQueue.FirstNbl) if (!devext->PacketQueue.FirstNbl)
ctx->PacketQueue.LastNbl = NULL; devext->PacketQueue.LastNbl = NULL;
NET_BUFFER_LIST_NEXT_NBL(nbl_top) = NULL; NET_BUFFER_LIST_NEXT_NBL(nbl_top) = NULL;
} else } else
TunNBLRefInc(nbl_top); TunNBLRefInc(nbl_top);
if (ret && NET_BUFFER_DATA_LENGTH(ret) > TUN_EXCH_MAX_IP_PACKET_SIZE) { if (ret && NET_BUFFER_DATA_LENGTH(ret) > TUN_EXCH_MAX_IP_PACKET_SIZE) {
NET_BUFFER_LIST_STATUS(nbl_top) = NDIS_STATUS_INVALID_LENGTH; NET_BUFFER_LIST_STATUS(nbl_top) = NDIS_STATUS_INVALID_LENGTH;
TunNBLRefDec(ctx, nbl_top, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(devext, nbl_top, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
InterlockedIncrement64((LONG64 *)&ctx->Statistics.ifOutDiscards); InterlockedIncrement64((LONG64 *)&devext->MiniportAdapterContext->Statistics.ifOutDiscards);
goto retry; /* A for (;;) and a break would be fine, but this is clearer actually. */ goto retry; /* A for (;;) and a break would be fine, but this is clearer actually. */
} }
return ret; return ret;
} }
/* Note: Must be called immediately after TunQueueRemove without dropping ctx->PacketQueue.Lock. */ /* Note: Must be called immediately after TunQueueRemove without dropping devext->PacketQueue.Lock. */
_Requires_lock_held_(ctx->PacketQueue.Lock) _Requires_lock_held_(devext->PacketQueue.Lock)
_IRQL_requires_(DISPATCH_LEVEL) _IRQL_requires_(DISPATCH_LEVEL)
static void TunQueuePrepend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER *nb, _In_ NET_BUFFER_LIST *nbl) static void TunQueuePrepend(_Inout_ TUN_DEVEXT *devext, _In_ NET_BUFFER *nb, _In_ NET_BUFFER_LIST *nbl)
{ {
ctx->PacketQueue.NextNb = nb; devext->PacketQueue.NextNb = nb;
if (!nbl || nbl == ctx->PacketQueue.FirstNbl) if (!nbl || nbl == devext->PacketQueue.FirstNbl)
return; return;
TunNBLRefInc(nbl); TunNBLRefInc(nbl);
if (!ctx->PacketQueue.FirstNbl) if (!devext->PacketQueue.FirstNbl)
ctx->PacketQueue.FirstNbl = ctx->PacketQueue.LastNbl = nbl; devext->PacketQueue.FirstNbl = devext->PacketQueue.LastNbl = nbl;
else { else {
NET_BUFFER_LIST_NEXT_NBL(nbl) = ctx->PacketQueue.FirstNbl; NET_BUFFER_LIST_NEXT_NBL(nbl) = devext->PacketQueue.FirstNbl;
ctx->PacketQueue.FirstNbl = nbl; devext->PacketQueue.FirstNbl = nbl;
} }
} }
_Requires_lock_not_held_(ctx->PacketQueue.Lock) _Requires_lock_not_held_(devext->PacketQueue.Lock)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
static void TunQueueClear(_Inout_ TUN_CTX *ctx) static void TunQueueClear(_Inout_ TUN_DEVEXT *devext)
{ {
KLOCK_QUEUE_HANDLE lqh; KLOCK_QUEUE_HANDLE lqh;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); KeAcquireInStackQueuedSpinLock(&devext->PacketQueue.Lock, &lqh);
for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) { for (NET_BUFFER_LIST *nbl = devext->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) {
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl); nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
NET_BUFFER_LIST_STATUS(nbl) = STATUS_NDIS_PAUSED; NET_BUFFER_LIST_STATUS(nbl) = STATUS_NDIS_PAUSED;
TunNBLRefDec(ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(devext, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
} }
ctx->PacketQueue.FirstNbl = NULL; devext->PacketQueue.FirstNbl = NULL;
ctx->PacketQueue.LastNbl = NULL; devext->PacketQueue.LastNbl = NULL;
ctx->PacketQueue.NextNb = NULL; devext->PacketQueue.NextNb = NULL;
InterlockedExchange(&ctx->PacketQueue.NumNbl, 0); InterlockedExchange(&devext->PacketQueue.NumNbl, 0);
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
} }
_Requires_lock_not_held_(ctx->PacketQueue.Lock) _Requires_lock_not_held_(devext->PacketQueue.Lock)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
static void TunQueueProcess(_Inout_ TUN_CTX *ctx) static void TunQueueProcess(_Inout_ TUN_DEVEXT *devext)
{ {
IRP *irp = NULL; IRP *irp = NULL;
UCHAR *buffer = NULL; UCHAR *buffer = NULL;
@ -527,31 +518,31 @@ static void TunQueueProcess(_Inout_ TUN_CTX *ctx)
for (;;) { for (;;) {
NET_BUFFER_LIST *nbl; NET_BUFFER_LIST *nbl;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); KeAcquireInStackQueuedSpinLock(&devext->PacketQueue.Lock, &lqh);
/* Get head NB (and IRP). */ /* Get head NB (and IRP). */
if (!irp) { if (!irp) {
nb = TunQueueRemove(ctx, &nbl); nb = TunQueueRemove(devext, &nbl);
if (!nb) { if (!nb) {
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
return; return;
} }
irp = TunRemoveNextIrp(ctx, &buffer, &size); irp = TunRemoveNextIrp(devext, &buffer, &size);
if (!irp) { if (!irp) {
TunQueuePrepend(ctx, nb, nbl); TunQueuePrepend(devext, nb, nbl);
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
if (nbl) if (nbl)
TunNBLRefDec(ctx, nbl, 0); TunNBLRefDec(devext, nbl, 0);
return; return;
} }
} else } else
nb = TunQueueRemove(ctx, &nbl); nb = TunQueueRemove(devext, &nbl);
/* If the NB won't fit in the IRP, return it. */ /* If the NB won't fit in the IRP, return it. */
if (nb && TunCanFitIntoIrp(irp, size, nb)) { if (nb && TunCanFitIntoIrp(irp, size, nb)) {
TunQueuePrepend(ctx, nb, nbl); TunQueuePrepend(devext, nb, nbl);
if (nbl) if (nbl)
TunNBLRefDec(ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(devext, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
nbl = NULL; nbl = NULL;
nb = NULL; nb = NULL;
} }
@ -560,11 +551,11 @@ static void TunQueueProcess(_Inout_ TUN_CTX *ctx)
/* Process NB and IRP. */ /* Process NB and IRP. */
if (nb) { if (nb) {
NTSTATUS status = TunWriteIntoIrp(ctx, irp, buffer, nb); NTSTATUS status = TunWriteIntoIrp(irp, buffer, nb, &devext->MiniportAdapterContext->Statistics);
if (!NT_SUCCESS(status)) { if (!NT_SUCCESS(status)) {
if (nbl) if (nbl)
NET_BUFFER_LIST_STATUS(nbl) = status; NET_BUFFER_LIST_STATUS(nbl) = status;
IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, irp, NULL, TUN_CSQ_INSERT_HEAD); IoCsqInsertIrpEx(&devext->ReadQueue.Csq, irp, NULL, TUN_CSQ_INSERT_HEAD);
irp = NULL; irp = NULL;
} }
} else { } else {
@ -574,90 +565,16 @@ static void TunQueueProcess(_Inout_ TUN_CTX *ctx)
} }
if (nbl) if (nbl)
TunNBLRefDec(ctx, nbl, 0); TunNBLRefDec(devext, nbl, 0);
} }
} }
static DRIVER_DISPATCH TunDispatchCreate; _IRQL_requires_max_(DISPATCH_LEVEL)
_Use_decl_annotations_ _Must_inspect_result_
static NTSTATUS TunDispatchCreate(DEVICE_OBJECT *DeviceObject, IRP *Irp) static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
{ {
NTSTATUS status = STATUS_SUCCESS; NTSTATUS status = STATUS_SUCCESS;
TUN_CTX *ctx = TunGetContext(DeviceObject);
if (!ctx) {
status = STATUS_INVALID_HANDLE;
goto cleanup_complete_req;
}
ASSERT(InterlockedGet64(&ctx->Device.RefCount) < MAXLONG64);
InterlockedIncrement64(&ctx->Device.RefCount);
TunIndicateStatus(ctx);
cleanup_complete_req:
TunCompleteRequest(Irp, 0, status);
return status;
}
static DRIVER_DISPATCH TunDispatchClose;
_Use_decl_annotations_
static NTSTATUS TunDispatchClose(DEVICE_OBJECT *DeviceObject, IRP *Irp)
{
NTSTATUS status = STATUS_SUCCESS;
TUN_CTX *ctx = TunGetContext(DeviceObject);
if (!ctx) {
status = STATUS_INVALID_HANDLE;
goto cleanup_complete_req;
}
ASSERT(InterlockedGet64(&ctx->Device.RefCount) > 0);
InterlockedDecrement64(&ctx->Device.RefCount);
TunIndicateStatus(ctx);
cleanup_complete_req:
TunCompleteRequest(Irp, 0, status);
return status;
}
static DRIVER_DISPATCH TunDispatchRead;
_Use_decl_annotations_
static NTSTATUS TunDispatchRead(DEVICE_OBJECT *DeviceObject, IRP *Irp)
{
NTSTATUS status = STATUS_SUCCESS;
TUN_CTX *ctx = TunGetContext(DeviceObject);
if (!ctx) {
status = STATUS_INVALID_HANDLE;
goto cleanup_complete_req;
}
Irp->IoStatus.Information = 0;
status = IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL);
if (!NT_SUCCESS(status))
goto cleanup_complete_req;
TunQueueProcess(ctx);
return STATUS_PENDING;
cleanup_complete_req:
TunCompleteRequest(Irp, 0, status);
return status;
}
static DRIVER_DISPATCH TunDispatchWrite;
_Use_decl_annotations_
static NTSTATUS TunDispatchWrite(DEVICE_OBJECT *DeviceObject, IRP *Irp)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG_PTR information = 0;
TUN_CTX *ctx = TunGetContext(DeviceObject);
if (!ctx) {
status = STATUS_INVALID_HANDLE;
goto cleanup_complete_req;
}
if (!NT_SUCCESS(status = TunCheckForPause(ctx))) if (!NT_SUCCESS(status = TunCheckForPause(ctx)))
goto cleanup_TunCompletePause; goto cleanup_TunCompletePause;
@ -767,34 +684,66 @@ static NTSTATUS TunDispatchWrite(DEVICE_OBJECT *DeviceObject, IRP *Irp)
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCInUcastPkts, stat_p_ok); InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCInUcastPkts, stat_p_ok);
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInErrors, stat_p_err); InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInErrors, stat_p_err);
information = b - buffer; Irp->IoStatus.Information = b - buffer;
cleanup_TunCompletePause: cleanup_TunCompletePause:
TunCompletePause(ctx, TRUE); TunCompletePause(ctx, TRUE);
cleanup_complete_req:
TunCompleteRequest(Irp, information, status);
return status; return status;
} }
static DRIVER_DISPATCH TunDispatchCleanup; static DRIVER_DISPATCH TunDispatch;
_Use_decl_annotations_ _Use_decl_annotations_
static NTSTATUS TunDispatchCleanup(DEVICE_OBJECT *DeviceObject, IRP *Irp) static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
{ {
NTSTATUS status = STATUS_SUCCESS; NTSTATUS status = STATUS_SUCCESS;
TUN_CTX *ctx = TunGetContext(DeviceObject); Irp->IoStatus.Information = 0;
if (!ctx) {
TUN_DEVEXT *devext = NdisGetDeviceReservedExtension(DeviceObject);
if (!devext) {
status = STATUS_INVALID_HANDLE; status = STATUS_INVALID_HANDLE;
goto cleanup_complete_req; goto cleanup_complete_req;
} }
TUN_CTX *ctx = InterlockedGetPointer(&devext->MiniportAdapterContext);
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp); IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
IRP *pending_irp; switch (stack->MajorFunction) {
while ((pending_irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, stack->FileObject)) != NULL) case IRP_MJ_READ:
status = IoCsqInsertIrpEx(&devext->ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL);
if (!NT_SUCCESS(status))
break;
TunQueueProcess(devext);
return STATUS_PENDING;
case IRP_MJ_WRITE:
status = ctx ? TunWriteFromIrp(ctx, Irp) : NDIS_STATUS_ADAPTER_REMOVED;
break;
case IRP_MJ_CREATE:
ASSERT(InterlockedGet64(&devext->RefCount) < MAXLONG64);
if (InterlockedIncrement64(&devext->RefCount) > 0 && ctx)
TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateConnected);
break;
case IRP_MJ_CLOSE:
ASSERT(InterlockedGet64(&devext->RefCount) > 0);
if (InterlockedDecrement64(&devext->RefCount) <= 0 && ctx)
TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateDisconnected);
break;
case IRP_MJ_CLEANUP:
for (IRP *pending_irp; (pending_irp = IoCsqRemoveNextIrp(&devext->ReadQueue.Csq, stack->FileObject)) != NULL; )
TunCompleteRequest(pending_irp, 0, STATUS_CANCELLED); TunCompleteRequest(pending_irp, 0, STATUS_CANCELLED);
break;
default:
status = STATUS_INVALID_PARAMETER;
}
cleanup_complete_req: cleanup_complete_req:
TunCompleteRequest(Irp, 0, status); Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status; return status;
} }
@ -818,7 +767,9 @@ static NDIS_STATUS TunPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_P
return NDIS_STATUS_FAILURE; return NDIS_STATUS_FAILURE;
} }
TunQueueClear(ctx); TUN_DEVEXT *devext = NdisGetDeviceReservedExtension(ctx->Device.Object);
if (devext)
TunQueueClear(devext);
return TunCompletePause(ctx, FALSE); return TunCompletePause(ctx, FALSE);
} }
@ -849,21 +800,25 @@ static void TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext; TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
KLOCK_QUEUE_HANDLE lqh; KLOCK_QUEUE_HANDLE lqh;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); TUN_DEVEXT *devext = NdisGetDeviceReservedExtension(ctx->Device.Object);
if (!devext)
return;
NET_BUFFER_LIST *nbl_last = NULL, **nbl_last_link = &ctx->PacketQueue.FirstNbl; KeAcquireInStackQueuedSpinLock(&devext->PacketQueue.Lock, &lqh);
for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) {
NET_BUFFER_LIST *nbl_last = NULL, **nbl_last_link = &devext->PacketQueue.FirstNbl;
for (NET_BUFFER_LIST *nbl = devext->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) {
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl); nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
if (NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(nbl) == CancelId) { if (NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(nbl) == CancelId) {
NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SEND_ABORTED; NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SEND_ABORTED;
*nbl_last_link = nbl_next; *nbl_last_link = nbl_next;
TunNBLRefDec(ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(devext, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
} else { } else {
nbl_last = nbl; nbl_last = nbl;
nbl_last_link = &NET_BUFFER_LIST_NEXT_NBL(nbl); nbl_last_link = &NET_BUFFER_LIST_NEXT_NBL(nbl);
} }
} }
ctx->PacketQueue.LastNbl = nbl_last; devext->PacketQueue.LastNbl = nbl_last;
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
} }
@ -1030,23 +985,11 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
goto cleanup_ctx; goto cleanup_ctx;
} }
KeInitializeSpinLock(&ctx->PacketQueue.Lock);
KeInitializeSpinLock(&ctx->Device.ReadQueue.Lock);
InitializeListHead(&ctx->Device.ReadQueue.List);
IoCsqInitializeEx(&ctx->Device.ReadQueue.Csq,
TunCsqInsertIrpEx,
TunCsqRemoveIrp,
TunCsqPeekNextIrp,
TunCsqAcquireLock,
TunCsqReleaseLock,
TunCsqCompleteCanceledIrp);
/* A miniport driver can call NdisMIndicateStatusEx after setting its /* A miniport driver can call NdisMIndicateStatusEx after setting its
* registration attributes even if the driver is still in the context * registration attributes even if the driver is still in the context
* of the MiniportInitializeEx function. * of the MiniportInitializeEx function.
*/ */
TunIndicateStatus(ctx); TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateDisconnected);
NET_BUFFER_LIST_POOL_PARAMETERS nbl_pool_param = { NET_BUFFER_LIST_POOL_PARAMETERS nbl_pool_param = {
.Header = { .Header = {
@ -1076,11 +1019,11 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
RtlUnicodeStringPrintf(&unicode_symbolic_name, L"\\DosDevices\\" TUN_DEVICE_NAME, (ULONG)MiniportInitParameters->NetLuid.Info.NetLuidIndex); RtlUnicodeStringPrintf(&unicode_symbolic_name, L"\\DosDevices\\" TUN_DEVICE_NAME, (ULONG)MiniportInitParameters->NetLuid.Info.NetLuidIndex);
static PDRIVER_DISPATCH dispatch_table[IRP_MJ_MAXIMUM_FUNCTION + 1] = { static PDRIVER_DISPATCH dispatch_table[IRP_MJ_MAXIMUM_FUNCTION + 1] = {
TunDispatchCreate, /* IRP_MJ_CREATE */ TunDispatch, /* IRP_MJ_CREATE */
NULL, /* IRP_MJ_CREATE_NAMED_PIPE */ NULL, /* IRP_MJ_CREATE_NAMED_PIPE */
TunDispatchClose, /* IRP_MJ_CLOSE */ TunDispatch, /* IRP_MJ_CLOSE */
TunDispatchRead, /* IRP_MJ_READ */ TunDispatch, /* IRP_MJ_READ */
TunDispatchWrite, /* IRP_MJ_WRITE */ TunDispatch, /* IRP_MJ_WRITE */
NULL, /* IRP_MJ_QUERY_INFORMATION */ NULL, /* IRP_MJ_QUERY_INFORMATION */
NULL, /* IRP_MJ_SET_INFORMATION */ NULL, /* IRP_MJ_SET_INFORMATION */
NULL, /* IRP_MJ_QUERY_EA */ NULL, /* IRP_MJ_QUERY_EA */
@ -1094,7 +1037,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
NULL, /* IRP_MJ_INTERNAL_DEVICE_CONTROL */ NULL, /* IRP_MJ_INTERNAL_DEVICE_CONTROL */
NULL, /* IRP_MJ_SHUTDOWN */ NULL, /* IRP_MJ_SHUTDOWN */
NULL, /* IRP_MJ_LOCK_CONTROL */ NULL, /* IRP_MJ_LOCK_CONTROL */
TunDispatchCleanup, /* IRP_MJ_CLEANUP */ TunDispatch, /* IRP_MJ_CLEANUP */
}; };
NDIS_DEVICE_OBJECT_ATTRIBUTES t = { NDIS_DEVICE_OBJECT_ATTRIBUTES t = {
.Header = { .Header = {
@ -1105,7 +1048,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
.DeviceName = &unicode_device_name, .DeviceName = &unicode_device_name,
.SymbolicName = &unicode_symbolic_name, .SymbolicName = &unicode_symbolic_name,
.MajorFunctions = dispatch_table, .MajorFunctions = dispatch_table,
.ExtensionSize = sizeof(TUN_CTX *), .ExtensionSize = sizeof(TUN_DEVEXT),
.DefaultSDDLString = &SDDL_DEVOBJ_SYS_ALL /* Kernel, and SYSTEM: full control. Others: none */ .DefaultSDDLString = &SDDL_DEVOBJ_SYS_ALL /* Kernel, and SYSTEM: full control. Others: none */
}; };
if (!NT_SUCCESS(NdisRegisterDeviceEx(NdisMiniportDriverHandle, &t, &ctx->Device.Object, &ctx->Device.Handle))) { if (!NT_SUCCESS(NdisRegisterDeviceEx(NdisMiniportDriverHandle, &t, &ctx->Device.Object, &ctx->Device.Handle))) {
@ -1116,12 +1059,26 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
ctx->Device.Object->Flags &= ~DO_BUFFERED_IO; ctx->Device.Object->Flags &= ~DO_BUFFERED_IO;
ctx->Device.Object->Flags |= DO_DIRECT_IO; ctx->Device.Object->Flags |= DO_DIRECT_IO;
TUN_CTX * volatile * control_device_extension = TunGetContextPointer(ctx->Device.Object); TUN_DEVEXT *devext = NdisGetDeviceReservedExtension(ctx->Device.Object);
if (!control_device_extension) { if (!devext) {
status = NDIS_STATUS_FAILURE; status = NDIS_STATUS_FAILURE;
goto cleanup_NdisDeregisterDeviceEx; goto cleanup_NdisDeregisterDeviceEx;
} }
InterlockedExchangePointer(control_device_extension, ctx); NdisZeroMemory(devext, sizeof(*devext));
InterlockedExchangePointer(&devext->MiniportAdapterContext, ctx);
KeInitializeSpinLock(&devext->ReadQueue.Lock);
InitializeListHead(&devext->ReadQueue.List);
IoCsqInitializeEx(&devext->ReadQueue.Csq,
TunCsqInsertIrpEx,
TunCsqRemoveIrp,
TunCsqPeekNextIrp,
TunCsqAcquireLock,
TunCsqReleaseLock,
TunCsqCompleteCanceledIrp);
KeInitializeSpinLock(&devext->PacketQueue.Lock);
ctx->State = TUN_STATE_PAUSED; ctx->State = TUN_STATE_PAUSED;
return NDIS_STATUS_SUCCESS; return NDIS_STATUS_SUCCESS;
@ -1152,15 +1109,11 @@ static void TunHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltA
ASSERT(!InterlockedGet64(&ctx->ActiveTransactionCount)); ASSERT(!InterlockedGet64(&ctx->ActiveTransactionCount));
/* Cancel pending IRPs to unblock waiting clients. */ TUN_DEVEXT *devext = NdisGetDeviceReservedExtension(ctx->Device.Object);
IRP *pending_irp; if (devext) {
while ((pending_irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, NULL)) != NULL)
TunCompleteRequest(pending_irp, 0, STATUS_CANCELLED);
/* Reset adapter context in device object, as Windows keeps calling dispatch handlers even after NdisDeregisterDeviceEx(). */ /* Reset adapter context in device object, as Windows keeps calling dispatch handlers even after NdisDeregisterDeviceEx(). */
TUN_CTX * volatile * control_device_extension = TunGetContextPointer(ctx->Device.Object); InterlockedExchangePointer(&devext->MiniportAdapterContext, NULL);
if (control_device_extension) }
InterlockedExchangePointer(control_device_extension, NULL);
/* Release resources. */ /* Release resources. */
NdisDeregisterDeviceEx(ctx->Device.Handle); NdisDeregisterDeviceEx(ctx->Device.Handle);
@ -1362,16 +1315,18 @@ _Use_decl_annotations_
static void TunSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER_LIST *NetBufferLists, NDIS_PORT_NUMBER PortNumber, ULONG SendFlags) static void TunSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER_LIST *NetBufferLists, NDIS_PORT_NUMBER PortNumber, ULONG SendFlags)
{ {
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext; TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
TUN_DEVEXT *devext;
NDIS_STATUS status; NDIS_STATUS status;
if (!NT_SUCCESS(status = TunCheckForPause(ctx))) { if (!NT_SUCCESS(status = TunCheckForPause(ctx)) ||
(status = NDIS_STATUS_DEVICE_FAILED, (devext = NdisGetDeviceReservedExtension(ctx->Device.Object)) == NULL)) {
TunSetNBLStatus(NetBufferLists, status); TunSetNBLStatus(NetBufferLists, status);
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, NetBufferLists, SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, NetBufferLists, SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
goto cleanup_TunCompletePause; goto cleanup_TunCompletePause;
} }
TunQueueAppend(ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS); TunQueueAppend(devext, NetBufferLists, TUN_QUEUE_MAX_NBLS);
TunQueueProcess(ctx); TunQueueProcess(devext);
cleanup_TunCompletePause: cleanup_TunCompletePause:
TunCompletePause(ctx, TRUE); TunCompletePause(ctx, TRUE);