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:
parent
8162297c15
commit
60e103cad3
142
wintun.c
142
wintun.c
@ -56,7 +56,7 @@ typedef struct _TUN_PACKET {
|
|||||||
} TUN_PACKET;
|
} TUN_PACKET;
|
||||||
|
|
||||||
typedef enum _TUN_FLAGS {
|
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_PRESENT = 1 << 1, // Toggles between removal pending and being present
|
||||||
} TUN_FLAGS;
|
} TUN_FLAGS;
|
||||||
|
|
||||||
@ -152,22 +152,6 @@ static void TunCompleteRequest(_Inout_ TUN_CTX *ctx, _Inout_ IRP *irp, _In_ NTST
|
|||||||
IoReleaseRemoveLock(&ctx->Device.RemoveLock, irp);
|
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)
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||||
static NDIS_STATUS TunCompletePause(_Inout_ TUN_CTX *ctx, _In_ BOOLEAN async_completion)
|
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;
|
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;
|
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);
|
TunSetNBLStatus(NetBufferLists, status);
|
||||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, NetBufferLists, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, NetBufferLists, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
|
||||||
goto cleanup_TunCompletePause;
|
goto cleanup_ExReleaseSpinLockShared;
|
||||||
}
|
}
|
||||||
|
|
||||||
TunQueueAppend(ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS);
|
TunQueueAppend(ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS);
|
||||||
|
|
||||||
TunQueueProcess(ctx);
|
TunQueueProcess(ctx);
|
||||||
|
|
||||||
cleanup_TunCompletePause:
|
cleanup_ExReleaseSpinLockShared:
|
||||||
TunCompletePause(ctx, TRUE);
|
|
||||||
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
||||||
|
TunCompletePause(ctx, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MINIPORT_CANCEL_SEND TunCancelSend;
|
static MINIPORT_CANCEL_SEND TunCancelSend;
|
||||||
@ -628,26 +618,50 @@ static void TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
|
|||||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
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 IRP_REFCOUNT(irp) ((volatile LONG *)&(irp)->Tail.Overlay.DriverContext[0])
|
||||||
#define NET_BUFFER_LIST_IRP(nbl) (NET_BUFFER_LIST_MINIPORT_RESERVED(nbl)[0])
|
#define NET_BUFFER_LIST_IRP(nbl) (NET_BUFFER_LIST_MINIPORT_RESERVED(nbl)[0])
|
||||||
|
|
||||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||||
_Must_inspect_result_
|
_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;
|
NTSTATUS status;
|
||||||
|
|
||||||
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
|
ASSERT(InterlockedGet64(&ctx->ActiveTransactionCount) < MAXLONG64);
|
||||||
|
InterlockedIncrement64(&ctx->ActiveTransactionCount);
|
||||||
|
|
||||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx))) {
|
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
|
||||||
status = status == STATUS_NDIS_ADAPTER_REMOVED ? STATUS_FILE_FORCED_CLOSED : STATUS_CANCELLED;
|
LONG flags = InterlockedGet(&ctx->Flags);
|
||||||
goto cleanup_TunCompletePause;
|
if ((status = STATUS_FILE_FORCED_CLOSED, !(flags & TUN_FLAGS_PRESENT)) ||
|
||||||
}
|
(status = STATUS_CANCELLED , !(flags & TUN_FLAGS_RUNNING)))
|
||||||
|
goto cleanup_ExReleaseSpinLockShared;
|
||||||
|
|
||||||
UCHAR *buffer;
|
UCHAR *buffer;
|
||||||
ULONG size;
|
ULONG size;
|
||||||
if (!NT_SUCCESS(status = TunGetIrpBuffer(Irp, &buffer, &size)))
|
if (!NT_SUCCESS(status = TunGetIrpBuffer(Irp, &buffer, &size)))
|
||||||
goto cleanup_TunCompletePause;
|
goto cleanup_ExReleaseSpinLockShared;
|
||||||
|
|
||||||
const UCHAR *b = buffer, *b_end = buffer + size;
|
const UCHAR *b = buffer, *b_end = buffer + size;
|
||||||
typedef enum _ethtypeidx_t {
|
typedef enum _ethtypeidx_t {
|
||||||
@ -729,7 +743,7 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
|
|||||||
|
|
||||||
if (!nbl_count) {
|
if (!nbl_count) {
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
goto cleanup_TunCompletePause;
|
goto cleanup_ExReleaseSpinLockShared;
|
||||||
}
|
}
|
||||||
|
|
||||||
InterlockedAdd64(&ctx->ActiveTransactionCount, nbl_count);
|
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)
|
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);
|
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);
|
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
||||||
|
TunCompletePause(ctx, TRUE);
|
||||||
return STATUS_PENDING;
|
return STATUS_PENDING;
|
||||||
|
|
||||||
cleanup_nbl_queues:
|
cleanup_nbl_queues:
|
||||||
@ -754,9 +768,9 @@ cleanup_nbl_queues:
|
|||||||
NdisFreeNetBufferList(nbl);
|
NdisFreeNetBufferList(nbl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanup_TunCompletePause:
|
cleanup_ExReleaseSpinLockShared:
|
||||||
TunCompletePause(ctx, TRUE);
|
|
||||||
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
||||||
|
TunCompletePause(ctx, TRUE);
|
||||||
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
|
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -796,6 +810,33 @@ static void TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUF
|
|||||||
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInErrors, stat_p_err);
|
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;
|
static DRIVER_DISPATCH TunDispatch;
|
||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
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);
|
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
|
||||||
switch (stack->MajorFunction) {
|
switch (stack->MajorFunction) {
|
||||||
case IRP_MJ_READ:
|
case IRP_MJ_READ:
|
||||||
if ((status = STATUS_FILE_FORCED_CLOSED, !(InterlockedGet(&ctx->Flags) & TUN_FLAGS_PRESENT)) ||
|
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
|
||||||
!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
|
|
||||||
goto cleanup_complete_req;
|
goto cleanup_complete_req;
|
||||||
|
return TunDispatchRead(ctx, Irp);
|
||||||
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;
|
|
||||||
|
|
||||||
case IRP_MJ_WRITE:
|
case IRP_MJ_WRITE:
|
||||||
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
|
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
|
||||||
goto cleanup_complete_req;
|
goto cleanup_complete_req;
|
||||||
|
return TunDispatchWrite(ctx, Irp);
|
||||||
return TunWriteFromIrp(ctx, Irp);
|
|
||||||
|
|
||||||
case IRP_MJ_CREATE:
|
case IRP_MJ_CREATE:
|
||||||
if ((status = STATUS_DELETE_PENDING, !(InterlockedGet(&ctx->Flags) & TUN_FLAGS_PRESENT)) ||
|
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
|
||||||
!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp)))
|
|
||||||
goto cleanup_complete_req;
|
goto cleanup_complete_req;
|
||||||
|
return TunDispatchCreate(ctx, Irp);
|
||||||
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;
|
|
||||||
|
|
||||||
case IRP_MJ_CLOSE:
|
case IRP_MJ_CLOSE:
|
||||||
irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
|
irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
|
||||||
@ -872,10 +896,6 @@ static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
|||||||
goto cleanup_complete_req;
|
goto cleanup_complete_req;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup_complete_req_and_release_remove_lock:
|
|
||||||
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT);
|
|
||||||
return status;
|
|
||||||
|
|
||||||
cleanup_complete_req:
|
cleanup_complete_req:
|
||||||
Irp->IoStatus.Status = status;
|
Irp->IoStatus.Status = status;
|
||||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||||
@ -889,7 +909,7 @@ static NDIS_STATUS TunRestart(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT
|
|||||||
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
|
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
|
||||||
|
|
||||||
InterlockedExchange64(&ctx->ActiveTransactionCount, 1);
|
InterlockedExchange64(&ctx->ActiveTransactionCount, 1);
|
||||||
InterlockedOr(&ctx->Flags, TUN_FLAGS_ENABLED);
|
InterlockedOr(&ctx->Flags, TUN_FLAGS_RUNNING);
|
||||||
|
|
||||||
return NDIS_STATUS_SUCCESS;
|
return NDIS_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -901,7 +921,7 @@ static NDIS_STATUS TunPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_P
|
|||||||
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
|
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
|
||||||
|
|
||||||
KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
|
KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
|
||||||
InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_ENABLED);
|
InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_RUNNING);
|
||||||
ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql);
|
ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql);
|
||||||
TunQueueClear(ctx, STATUS_NDIS_PAUSED);
|
TunQueueClear(ctx, STATUS_NDIS_PAUSED);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user