Revise adapter state checking

TunCheckForPause() was inlined and adjusted: it had two calls with two
potential new ones, but each call would have a slightly different
optimal variant: some with ActiveTransactionCount increment, others
without; some with Device.RefCount check, others without... No two
particular use cases were completely identical:

1. TunSendNetBufferLists:
   - requires ActiveTransactionCount increment
   - requires Device.RefCount > 0 check
   - reports NDIS_STATUS in case of rejection
2. IRP_MJ_READ:
   - no ActiveTransactionCount increment
   - no Device.RefCount > 0 check, as IRP_MJ_READ implies it
   - reports NTSTATUS in case of rejection
3. IRP_MJ_WRITE:
   - requires ActiveTransactionCount increment
   - no Device.RefCount > 0 check, as IRP_MJ_WRITE implies it
   - reports NTSTATUS in case of rejection
4. IRP_MJ_CREATE:
   - no ActiveTransactionCount increment
   - no Device.RefCount > 0 check to allow initial client connection
   - reports NTSTATUS in case of rejection, with slightly different
     status codes than IRP_MJ_READ and IRP_MJ_WRITE.

TUN_FLAGS_ENABLED was renamed to TUN_FLAGS_RUNNING: enabled/disabled
adapter means initialized&running/paused&halted in Windows world.

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2019-06-12 20:00:36 +02:00
parent 8162297c15
commit 60e103cad3

142
wintun.c
View File

@ -56,7 +56,7 @@ typedef struct _TUN_PACKET {
} TUN_PACKET;
typedef enum _TUN_FLAGS {
TUN_FLAGS_ENABLED = 1 << 0, // Toggles between paused and running state
TUN_FLAGS_RUNNING = 1 << 0, // Toggles between paused and running state
TUN_FLAGS_PRESENT = 1 << 1, // Toggles between removal pending and being present
} TUN_FLAGS;
@ -152,22 +152,6 @@ static void TunCompleteRequest(_Inout_ TUN_CTX *ctx, _Inout_ IRP *irp, _In_ NTST
IoReleaseRemoveLock(&ctx->Device.RemoveLock, irp);
}
_IRQL_requires_same_
_Must_inspect_result_
_Requires_lock_held_(ctx->TransitionLock)
static NTSTATUS TunCheckForPause(_Inout_ TUN_CTX *ctx)
{
ASSERT(InterlockedGet64(&ctx->ActiveTransactionCount) < MAXLONG64);
InterlockedIncrement64(&ctx->ActiveTransactionCount);
LONG flags = InterlockedGet(&ctx->Flags);
return
!(flags & TUN_FLAGS_PRESENT) ? STATUS_NDIS_ADAPTER_REMOVED :
!(flags & TUN_FLAGS_ENABLED) ? STATUS_NDIS_PAUSED :
InterlockedGet64(&ctx->Device.RefCount) <= 0 ? STATUS_NDIS_MEDIA_DISCONNECTED :
STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
static NDIS_STATUS TunCompletePause(_Inout_ TUN_CTX *ctx, _In_ BOOLEAN async_completion)
{
@ -584,22 +568,28 @@ static void TunSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER
{
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
ASSERT(InterlockedGet64(&ctx->ActiveTransactionCount) < MAXLONG64);
InterlockedIncrement64(&ctx->ActiveTransactionCount);
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
LONG flags = InterlockedGet(&ctx->Flags);
NDIS_STATUS status;
if (!NT_SUCCESS(status = TunCheckForPause(ctx))) {
if ((status = STATUS_NDIS_ADAPTER_REMOVED , !(flags & TUN_FLAGS_PRESENT)) ||
(status = STATUS_NDIS_PAUSED , !(flags & TUN_FLAGS_RUNNING)) ||
(status = STATUS_NDIS_MEDIA_DISCONNECTED, InterlockedGet64(&ctx->Device.RefCount) <= 0))
{
TunSetNBLStatus(NetBufferLists, status);
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, NetBufferLists, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
goto cleanup_TunCompletePause;
goto cleanup_ExReleaseSpinLockShared;
}
TunQueueAppend(ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS);
TunQueueProcess(ctx);
cleanup_TunCompletePause:
TunCompletePause(ctx, TRUE);
cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
TunCompletePause(ctx, TRUE);
}
static MINIPORT_CANCEL_SEND TunCancelSend;
@ -628,26 +618,50 @@ static void TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
KeReleaseInStackQueuedSpinLock(&lqh);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
static NTSTATUS TunDispatchRead(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
{
NTSTATUS status;
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)))
goto cleanup_ExReleaseSpinLockShared;
TunQueueProcess(ctx);
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
return STATUS_PENDING;
cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
return status;
}
#define IRP_REFCOUNT(irp) ((volatile LONG *)&(irp)->Tail.Overlay.DriverContext[0])
#define NET_BUFFER_LIST_IRP(nbl) (NET_BUFFER_LIST_MINIPORT_RESERVED(nbl)[0])
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
static NTSTATUS TunDispatchWrite(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
{
NTSTATUS status;
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
ASSERT(InterlockedGet64(&ctx->ActiveTransactionCount) < MAXLONG64);
InterlockedIncrement64(&ctx->ActiveTransactionCount);
if (!NT_SUCCESS(status = TunCheckForPause(ctx))) {
status = status == STATUS_NDIS_ADAPTER_REMOVED ? STATUS_FILE_FORCED_CLOSED : STATUS_CANCELLED;
goto cleanup_TunCompletePause;
}
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
LONG flags = InterlockedGet(&ctx->Flags);
if ((status = STATUS_FILE_FORCED_CLOSED, !(flags & TUN_FLAGS_PRESENT)) ||
(status = STATUS_CANCELLED , !(flags & TUN_FLAGS_RUNNING)))
goto cleanup_ExReleaseSpinLockShared;
UCHAR *buffer;
ULONG size;
if (!NT_SUCCESS(status = TunGetIrpBuffer(Irp, &buffer, &size)))
goto cleanup_TunCompletePause;
goto cleanup_ExReleaseSpinLockShared;
const UCHAR *b = buffer, *b_end = buffer + size;
typedef enum _ethtypeidx_t {
@ -729,7 +743,7 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
if (!nbl_count) {
status = STATUS_SUCCESS;
goto cleanup_TunCompletePause;
goto cleanup_ExReleaseSpinLockShared;
}
InterlockedAdd64(&ctx->ActiveTransactionCount, nbl_count);
@ -741,8 +755,8 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
if (nbl_queue[ethtypeidx_ipv6].head)
NdisMIndicateReceiveNetBufferLists(ctx->MiniportAdapterHandle, nbl_queue[ethtypeidx_ipv6].head, NDIS_DEFAULT_PORT_NUMBER, nbl_queue[ethtypeidx_ipv6].count, NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE);
TunCompletePause(ctx, TRUE);
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
TunCompletePause(ctx, TRUE);
return STATUS_PENDING;
cleanup_nbl_queues:
@ -754,9 +768,9 @@ cleanup_nbl_queues:
NdisFreeNetBufferList(nbl);
}
}
cleanup_TunCompletePause:
TunCompletePause(ctx, TRUE);
cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
TunCompletePause(ctx, TRUE);
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
return status;
}
@ -796,6 +810,33 @@ static void TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUF
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInErrors, stat_p_err);
}
_IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_
static NTSTATUS TunDispatchCreate(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
{
NTSTATUS status;
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
LONG flags = InterlockedGet(&ctx->Flags);
if ((status = STATUS_DELETE_PENDING, !(flags & TUN_FLAGS_PRESENT)))
goto cleanup_ExReleaseSpinLockShared;
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, stack->FileObject)))
goto cleanup_ExReleaseSpinLockShared;
ASSERT(InterlockedGet64(&ctx->Device.RefCount) < MAXLONG64);
if (InterlockedIncrement64(&ctx->Device.RefCount) > 0)
TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateConnected);
status = STATUS_SUCCESS;
cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
return status;
}
static DRIVER_DISPATCH TunDispatch;
_Use_decl_annotations_
static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
@ -814,36 +855,19 @@ static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
switch (stack->MajorFunction) {
case IRP_MJ_READ:
if ((status = STATUS_FILE_FORCED_CLOSED, !(InterlockedGet(&ctx->Flags) & TUN_FLAGS_PRESENT)) ||
!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
goto cleanup_complete_req;
if (!NT_SUCCESS(status = IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL)))
goto cleanup_complete_req_and_release_remove_lock;
TunQueueProcess(ctx);
return STATUS_PENDING;
return TunDispatchRead(ctx, Irp);
case IRP_MJ_WRITE:
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
goto cleanup_complete_req;
return TunWriteFromIrp(ctx, Irp);
return TunDispatchWrite(ctx, Irp);
case IRP_MJ_CREATE:
if ((status = STATUS_DELETE_PENDING, !(InterlockedGet(&ctx->Flags) & TUN_FLAGS_PRESENT)) ||
!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
goto cleanup_complete_req;
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, stack->FileObject)))
goto cleanup_complete_req_and_release_remove_lock;
ASSERT(InterlockedGet64(&ctx->Device.RefCount) < MAXLONG64);
if (InterlockedIncrement64(&ctx->Device.RefCount) > 0)
TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateConnected);
status = STATUS_SUCCESS;
goto cleanup_complete_req_and_release_remove_lock;
return TunDispatchCreate(ctx, Irp);
case IRP_MJ_CLOSE:
irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
@ -872,10 +896,6 @@ static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
goto cleanup_complete_req;
}
cleanup_complete_req_and_release_remove_lock:
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
return status;
cleanup_complete_req:
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
@ -889,7 +909,7 @@ static NDIS_STATUS TunRestart(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
InterlockedExchange64(&ctx->ActiveTransactionCount, 1);
InterlockedOr(&ctx->Flags, TUN_FLAGS_ENABLED);
InterlockedOr(&ctx->Flags, TUN_FLAGS_RUNNING);
return NDIS_STATUS_SUCCESS;
}
@ -901,7 +921,7 @@ static NDIS_STATUS TunPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_P
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_ENABLED);
InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_RUNNING);
ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql);
TunQueueClear(ctx, STATUS_NDIS_PAUSED);