2019-03-08 22:33:15 +01:00
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright ( C ) 2018 - 2019 WireGuard LLC . All Rights Reserved .
*/
# define NDIS_MINIPORT_DRIVER
# define NDIS620_MINIPORT
# define NDIS_SUPPORT_NDIS620 1
# define NDIS_WDM 1
# include <stdio.h>
# include <string.h>
# include <wdm.h>
# include <wdmsec.h>
# include <ndis.h>
# include <bcrypt.h>
# include <ntstrsafe.h>
# pragma warning(disable : 4100) // unreferenced formal parameter
# pragma warning(disable : 4200) // nonstandard extension used: zero-sized array in struct/union
# pragma warning(disable : 4204) // nonstandard extension used: non-constant aggregate initializer
# pragma warning(disable : 4221) // nonstandard extension used: <member>: cannot be initialized using address of automatic variable <variable>
# define TUN_DEVICE_NAME L"WINTUN%u"
# define TUN_VENDOR_NAME "Wintun Tunnel"
# define TUN_VENDOR_ID 0xFFFFFF00
# define TUN_LINK_SPEED 100000000000ULL // 100gbps
# define TUN_EXCH_MAX_PACKETS 256 // Maximum number of exchange packets that can be exchanged in a single read/write
# define TUN_EXCH_MAX_PACKET_SIZE 0xF000 // Maximum exchange packet size - empirically determined by net buffer list (pool) limitations
# define TUN_EXCH_ALIGNMENT 16 // Memory alignment in exchange buffers
# define TUN_EXCH_MAX_IP_PACKET_SIZE (TUN_EXCH_MAX_PACKET_SIZE - sizeof(TUN_PACKET)) // Maximum IP packet size (headers + payload)
# define TUN_EXCH_MAX_BUFFER_SIZE (TUN_EXCH_MAX_PACKETS * TUN_EXCH_MAX_PACKET_SIZE) // Maximum size of read/write exchange buffer
2019-03-27 09:00:19 +01:00
# define TUN_QUEUE_MAX_NBLS 1000
2019-03-08 22:33:15 +01:00
typedef struct _TUN_PACKET {
ULONG Size ; // Size of packet data (TUN_EXCH_MAX_IP_PACKET_SIZE max)
_Field_size_bytes_ ( Size )
__declspec ( align ( TUN_EXCH_ALIGNMENT ) )
UCHAR Data [ ] ; // Packet data
} TUN_PACKET ;
typedef enum _TUN_STATE {
TUN_STATE_HALTED = 0 , // The Halted state is the initial state of all adapters. When an adapter is in the Halted state, NDIS can call the driver's MiniportInitializeEx function to initialize the adapter.
TUN_STATE_SHUTDOWN , // In the Shutdown state, a system shutdown and restart must occur before the system can use the adapter again
TUN_STATE_INITIALIZING , // In the Initializing state, a miniport driver completes any operations that are required to initialize an adapter.
TUN_STATE_PAUSED , // In the Paused state, the adapter does not indicate received network data or accept send requests.
TUN_STATE_RESTARTING , // In the Restarting state, a miniport driver completes any operations that are required to restart send and receive operations for an adapter.
TUN_STATE_RUNNING , // In the Running state, a miniport driver performs send and receive processing for an adapter.
TUN_STATE_PAUSING , // In the Pausing state, a miniport driver completes any operations that are required to stop send and receive operations for an adapter.
} TUN_STATE ;
typedef struct _TUN_CTX {
volatile TUN_STATE State ;
volatile NDIS_DEVICE_POWER_STATE PowerState ;
NDIS_HANDLE MiniportAdapterHandle ;
NDIS_STATISTICS_INFO Statistics ;
2019-03-22 13:20:28 +01:00
volatile LONG64 ActiveTransactionCount ;
2019-03-08 22:33:15 +01:00
struct {
NDIS_HANDLE Handle ;
DEVICE_OBJECT * Object ;
volatile LONG64 RefCount ;
2019-03-27 09:00:19 +01:00
struct {
KSPIN_LOCK Lock ;
IO_CSQ Csq ;
LIST_ENTRY List ;
} ReadQueue ;
2019-03-08 22:33:15 +01:00
} Device ;
struct {
2019-03-27 09:00:19 +01:00
KSPIN_LOCK Lock ;
NET_BUFFER_LIST * FirstNbl , * LastNbl ;
NET_BUFFER * NextNb ;
LONG NumNbl ;
2019-03-08 22:33:15 +01:00
} PacketQueue ;
2019-03-27 09:00:19 +01:00
NDIS_HANDLE NBLPool ;
2019-03-08 22:33:15 +01:00
} TUN_CTX ;
static NDIS_HANDLE NdisMiniportDriverHandle = NULL ;
# if REG_DWORD == REG_DWORD_BIG_ENDIAN
# define TUN_MEMORY_TAG 'wtun'
# define TunHtons(x) ((USHORT)(x))
# define TunHtonl(x) ((ULONG)(x))
# elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN
# define TUN_MEMORY_TAG 'nutw'
# define TunHtons(x) RtlUshortByteSwap(x)
# define TunHtonl(x) RtlUlongByteSwap(x)
# else
# error "Unable to determine endianess"
# endif
2019-03-27 09:00:19 +01:00
# define TUN_CSQ_INSERT_HEAD ((PVOID)TRUE)
# define TUN_CSQ_INSERT_TAIL ((PVOID)FALSE)
2019-03-08 22:33:15 +01:00
# define InterlockedGet(val) (InterlockedAdd((val), 0))
# define InterlockedGet64(val) (InterlockedAdd64((val), 0))
2019-03-27 17:16:35 +01:00
# define InterlockedGetPointer(val) (InterlockedCompareExchangePointer((val), NULL, NULL))
2019-03-27 09:00:19 +01:00
# define InterlockedSubtract(val, n) (InterlockedAdd((val), -(LONG)(n)))
2019-03-08 22:33:15 +01:00
# define InterlockedSubtract64(val, n) (InterlockedAdd64((val), -(LONG64)(n)))
# 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; }
_IRQL_requires_max_ ( DISPATCH_LEVEL )
_IRQL_requires_same_
static void TunIndicateStatus ( _In_ TUN_CTX * ctx )
{
NDIS_LINK_STATE state = {
. Header = {
. Type = NDIS_OBJECT_TYPE_DEFAULT ,
. Revision = NDIS_LINK_STATE_REVISION_1 ,
. Size = NDIS_SIZEOF_LINK_STATE_REVISION_1
} ,
. MediaConnectState = ctx - > Device . RefCount > 0 ? MediaConnectStateConnected : MediaConnectStateDisconnected ,
. MediaDuplexState = MediaDuplexStateFull ,
. XmitLinkSpeed = TUN_LINK_SPEED ,
. RcvLinkSpeed = TUN_LINK_SPEED ,
. PauseFunctions = NdisPauseFunctionsUnsupported
} ;
NDIS_STATUS_INDICATION t = {
. Header = {
. Type = NDIS_OBJECT_TYPE_STATUS_INDICATION ,
. Revision = NDIS_STATUS_INDICATION_REVISION_1 ,
. Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1
} ,
. SourceHandle = ctx - > MiniportAdapterHandle ,
. StatusCode = NDIS_STATUS_LINK_STATE ,
. StatusBuffer = & state ,
. StatusBufferSize = sizeof ( state )
} ;
NdisMIndicateStatusEx ( ctx - > MiniportAdapterHandle , & t ) ;
}
2019-03-27 17:16:35 +01:00
_IRQL_requires_max_ ( HIGH_LEVEL )
2019-03-28 11:21:51 +01:00
_Must_inspect_result_
static _Return_type_success_ ( return ! = NULL ) TUN_CTX * volatile * TunGetContextPointer ( _In_ DEVICE_OBJECT * DeviceObject )
2019-03-27 17:16:35 +01:00
{
return ( TUN_CTX * volatile * ) NdisGetDeviceReservedExtension ( DeviceObject ) ;
}
2019-03-08 22:33:15 +01:00
_IRQL_requires_max_ ( HIGH_LEVEL )
2019-03-28 11:21:51 +01:00
_Must_inspect_result_
static _Return_type_success_ ( return ! = NULL ) TUN_CTX * TunGetContext ( _In_ DEVICE_OBJECT * DeviceObject )
2019-03-08 22:33:15 +01:00
{
2019-03-27 17:16:35 +01:00
TUN_CTX * volatile * ctx = TunGetContextPointer ( DeviceObject ) ;
return ctx ? InterlockedGetPointer ( ctx ) : NULL ;
2019-03-08 22:33:15 +01:00
}
_IRQL_requires_max_ ( DISPATCH_LEVEL )
static void TunCompleteRequest ( _Inout_ IRP * Irp , _In_ ULONG_PTR Information , _In_ NTSTATUS Status )
{
Irp - > IoStatus . Information = Information ;
Irp - > IoStatus . Status = Status ;
IoCompleteRequest ( Irp , IO_NO_INCREMENT ) ;
}
2019-03-22 13:27:35 +01:00
_IRQL_requires_same_
2019-03-28 11:21:51 +01:00
_Must_inspect_result_
2019-03-22 13:27:35 +01:00
static NTSTATUS TunCheckForPause ( _Inout_ TUN_CTX * ctx , _In_ LONG64 increment )
{
2019-03-22 13:38:35 +01:00
ASSERT ( InterlockedGet64 ( & ctx - > ActiveTransactionCount ) < = MAXLONG64 - increment ) ;
2019-03-22 13:27:35 +01:00
InterlockedAdd64 ( & ctx - > ActiveTransactionCount , increment ) ;
return
InterlockedGet ( ( LONG * ) & ctx - > State ) ! = TUN_STATE_RUNNING ? STATUS_NDIS_PAUSED :
ctx - > PowerState > = NdisDeviceStateD1 ? STATUS_NDIS_LOW_POWER_STATE :
2019-03-27 09:00:19 +01:00
STATUS_SUCCESS ;
2019-03-22 13:27:35 +01:00
}
2019-03-08 22:33:15 +01:00
_IRQL_requires_max_ ( DISPATCH_LEVEL )
2019-03-27 13:44:55 +01:00
static NDIS_STATUS TunCompletePause ( _Inout_ TUN_CTX * ctx , _In_ LONG64 decrement )
2019-03-08 22:33:15 +01:00
{
2019-03-22 13:38:35 +01:00
ASSERT ( decrement < = InterlockedGet64 ( & ctx - > ActiveTransactionCount ) ) ;
2019-03-22 13:20:28 +01:00
if ( ! InterlockedSubtract64 ( & ctx - > ActiveTransactionCount , decrement ) & &
2019-03-27 13:44:55 +01:00
InterlockedCompareExchange ( ( LONG * ) & ctx - > State , TUN_STATE_PAUSED , TUN_STATE_PAUSING ) = = TUN_STATE_PAUSING ) {
InterlockedExchange64 ( & ctx - > Device . RefCount , 0 ) ;
TunIndicateStatus ( ctx ) ;
2019-03-08 22:33:15 +01:00
NdisMPauseComplete ( ctx - > MiniportAdapterHandle ) ;
2019-03-27 13:44:55 +01: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_
static NTSTATUS TunCsqInsertIrpEx ( IO_CSQ * Csq , IRP * Irp , PVOID InsertContext )
{
TUN_CTX * ctx = CONTAINING_RECORD ( Csq , TUN_CTX , Device . ReadQueue . Csq ) ;
( InsertContext = = TUN_CSQ_INSERT_HEAD ? InsertHeadList : InsertTailList ) ( & ctx - > Device . ReadQueue . List , & Irp - > Tail . Overlay . ListEntry ) ;
return STATUS_SUCCESS ;
}
static IO_CSQ_REMOVE_IRP TunCsqRemoveIrp ;
_Use_decl_annotations_
static VOID TunCsqRemoveIrp ( IO_CSQ * Csq , IRP * Irp )
{
RemoveEntryList ( & Irp - > Tail . Overlay . ListEntry ) ;
}
static IO_CSQ_PEEK_NEXT_IRP TunCsqPeekNextIrp ;
_Use_decl_annotations_
static IRP * TunCsqPeekNextIrp ( IO_CSQ * Csq , IRP * Irp , _In_ PVOID PeekContext )
{
TUN_CTX * ctx = CONTAINING_RECORD ( Csq , TUN_CTX , Device . ReadQueue . Csq ) ;
/* 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 . */
for ( LIST_ENTRY
2019-03-27 15:41:21 +01:00
* head = & ctx - > Device . ReadQueue . List ,
* next = Irp ? Irp - > Tail . Overlay . ListEntry . Flink : head - > Flink ;
next ! = head ;
next = next - > Flink )
2019-03-27 09:00:19 +01:00
{
2019-03-27 15:41:21 +01:00
IRP * irp_next = CONTAINING_RECORD ( next , IRP , Tail . Overlay . ListEntry ) ;
2019-03-27 09:00:19 +01:00
if ( ! PeekContext )
2019-03-27 15:41:21 +01:00
return irp_next ;
2019-03-27 09:00:19 +01:00
2019-03-27 15:41:21 +01:00
IO_STACK_LOCATION * stack = IoGetCurrentIrpStackLocation ( irp_next ) ;
2019-03-27 09:00:19 +01:00
if ( stack - > FileObject = = ( FILE_OBJECT * ) PeekContext )
2019-03-27 15:41:21 +01:00
return irp_next ;
2019-03-27 09:00:19 +01:00
}
return NULL ;
}
_IRQL_raises_ ( DISPATCH_LEVEL )
_IRQL_requires_max_ ( DISPATCH_LEVEL )
_Acquires_lock_ ( CONTAINING_RECORD ( Csq , TUN_CTX , Device . ReadQueue . Csq ) - > Device . ReadQueue . Lock )
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 ) ;
KeAcquireSpinLock ( & ctx - > Device . ReadQueue . Lock , Irql ) ;
}
_IRQL_requires_ ( DISPATCH_LEVEL )
_Releases_lock_ ( CONTAINING_RECORD ( Csq , TUN_CTX , Device . ReadQueue . Csq ) - > Device . ReadQueue . Lock )
static VOID TunCsqReleaseLock ( _In_ IO_CSQ * Csq , _In_ _IRQL_restores_ KIRQL Irql )
{
TUN_CTX * ctx = CONTAINING_RECORD ( Csq , TUN_CTX , Device . ReadQueue . Csq ) ;
KeReleaseSpinLock ( & ctx - > Device . ReadQueue . Lock , Irql ) ;
}
static IO_CSQ_COMPLETE_CANCELED_IRP TunCsqCompleteCanceledIrp ;
_Use_decl_annotations_
static VOID TunCsqCompleteCanceledIrp ( IO_CSQ * Csq , IRP * Irp )
{
TUN_CTX * ctx = CONTAINING_RECORD ( Csq , TUN_CTX , Device . ReadQueue . Csq ) ;
TunCompleteRequest ( Irp , 0 , STATUS_CANCELLED ) ;
TunCompletePause ( ctx , 1 ) ;
}
2019-03-08 22:33:15 +01:00
_IRQL_requires_same_
2019-03-27 09:00:19 +01:00
static void TunSetNBLStatus ( _Inout_opt_ NET_BUFFER_LIST * nbl , _In_ NDIS_STATUS status )
2019-03-08 22:33:15 +01:00
{
2019-03-27 09:00:19 +01:00
for ( ; nbl ; nbl = NET_BUFFER_LIST_NEXT_NBL ( nbl ) )
2019-03-08 22:33:15 +01:00
NET_BUFFER_LIST_STATUS ( nbl ) = status ;
}
_IRQL_requires_max_ ( DISPATCH_LEVEL )
2019-03-28 11:11:43 +01:00
_Must_inspect_result_
static NTSTATUS TunGetIrpBuffer ( _In_ IRP * Irp , _Out_ UCHAR * * buffer , _Out_ ULONG * size )
2019-03-08 22:33:15 +01:00
{
/* Get and validate request parameters. */
ULONG priority ;
IO_STACK_LOCATION * stack = IoGetCurrentIrpStackLocation ( Irp ) ;
switch ( stack - > MajorFunction ) {
case IRP_MJ_READ :
* size = stack - > Parameters . Read . Length ;
priority = NormalPagePriority ;
break ;
case IRP_MJ_WRITE :
* size = stack - > Parameters . Write . Length ;
priority = NormalPagePriority | MdlMappingNoWrite ;
break ;
default :
return STATUS_INVALID_PARAMETER ;
}
/* Get buffer size and address. */
if ( ! Irp - > MdlAddress )
return STATUS_INVALID_PARAMETER ;
2019-03-27 09:00:19 +01:00
ULONG size_mdl ;
NdisQueryMdl ( Irp - > MdlAddress , buffer , & size_mdl , priority ) ;
2019-03-08 22:33:15 +01:00
if ( ! buffer )
return STATUS_INSUFFICIENT_RESOURCES ;
2019-03-27 09:00:19 +01:00
if ( size_mdl < * size )
* size = size_mdl ;
2019-03-08 22:33:15 +01:00
2019-03-28 11:47:30 +01:00
if ( * size > TUN_EXCH_MAX_BUFFER_SIZE )
return STATUS_INVALID_USER_BUFFER ;
2019-03-08 22:33:15 +01:00
return STATUS_SUCCESS ;
}
2019-03-28 08:30:13 +01:00
_IRQL_requires_max_ ( DISPATCH_LEVEL )
2019-03-28 11:11:43 +01:00
_Must_inspect_result_
static _Return_type_success_ ( return ! = NULL ) IRP * TunRemoveNextIrp ( _Inout_ TUN_CTX * ctx , _Out_ UCHAR * * buffer , _Out_ ULONG * size )
2019-03-28 08:30:13 +01:00
{
2019-03-28 11:11:43 +01:00
IRP * irp ;
retry :
irp = IoCsqRemoveNextIrp ( & ctx - > Device . ReadQueue . Csq , NULL ) ;
if ( ! irp )
return NULL ;
NTSTATUS status = TunGetIrpBuffer ( irp , buffer , size ) ;
if ( ! NT_SUCCESS ( status ) ) {
irp - > IoStatus . Status = status ;
IoCompleteRequest ( irp , IO_NETWORK_INCREMENT ) ;
TunCompletePause ( ctx , 1 ) ;
goto retry ;
}
ASSERT ( irp - > IoStatus . Information < = ( ULONG_PTR ) * size ) ;
return irp ;
2019-03-28 08:30:13 +01:00
}
2019-03-28 11:45:57 +01:00
_IRQL_requires_same_
static BOOLEAN TunCanFitIntoIrp ( _In_ IRP * Irp , _In_ ULONG size , _In_ NET_BUFFER * nb )
{
return ( ULONG_PTR ) size < Irp - > IoStatus . Information + TunPacketAlign ( sizeof ( TUN_PACKET ) + NET_BUFFER_DATA_LENGTH ( nb ) ) ;
}
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-03-28 11:11:43 +01:00
static NTSTATUS TunWriteIntoIrp ( _Inout_ IRP * Irp , _Inout_ UCHAR * buffer , _In_ NET_BUFFER * nb )
2019-03-08 22:33:15 +01:00
{
2019-03-27 09:00:19 +01:00
ULONG p_size = NET_BUFFER_DATA_LENGTH ( nb ) ;
2019-03-28 11:11:43 +01:00
TUN_PACKET * p = ( TUN_PACKET * ) ( buffer + Irp - > IoStatus . Information ) ;
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
p - > Size = p_size ;
void * ptr = NdisGetDataBuffer ( nb , p_size , p - > Data , 1 , 0 ) ;
if ( ! ptr )
return NDIS_STATUS_RESOURCES ;
if ( ptr ! = p - > Data )
NdisMoveMemory ( p - > Data , ptr , p_size ) ;
2019-03-08 22:33:15 +01:00
2019-03-28 11:11:43 +01:00
Irp - > IoStatus . Information + = TunPacketAlign ( sizeof ( TUN_PACKET ) + p_size ) ;
2019-03-27 09:00:19 +01:00
return STATUS_SUCCESS ;
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
# define NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl))
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
_IRQL_requires_same_
static void TunNBLRefInit ( _Inout_ TUN_CTX * ctx , _Inout_ NET_BUFFER_LIST * nbl )
{
InterlockedAdd64 ( & ctx - > ActiveTransactionCount , 1 ) ;
InterlockedAdd ( & ctx - > PacketQueue . NumNbl , 1 ) ;
InterlockedExchange64 ( NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT ( nbl ) , 1 ) ;
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
_IRQL_requires_same_
static void TunNBLRefInc ( _Inout_ NET_BUFFER_LIST * nbl )
{
ASSERT ( InterlockedGet64 ( NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT ( nbl ) ) ) ;
InterlockedAdd64 ( NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT ( nbl ) , 1 ) ;
}
2019-03-27 09:09:58 +01:00
_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 ) )
static BOOLEAN TunNBLRefDec ( _Inout_ TUN_CTX * ctx , _Inout_ NET_BUFFER_LIST * nbl , _In_ ULONG SendCompleteFlags )
2019-03-27 09:00:19 +01:00
{
ASSERT ( InterlockedGet64 ( NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT ( nbl ) ) ) ;
if ( ! InterlockedSubtract64 ( NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT ( nbl ) , 1 ) ) {
NET_BUFFER_LIST_NEXT_NBL ( nbl ) = NULL ;
2019-03-27 09:09:58 +01:00
NdisMSendNetBufferListsComplete ( ctx - > MiniportAdapterHandle , nbl , SendCompleteFlags ) ;
2019-03-27 09:00:19 +01:00
InterlockedSubtract ( & ctx - > PacketQueue . NumNbl , 1 ) ;
TunCompletePause ( ctx , 1 ) ;
return TRUE ;
2019-03-08 22:33:15 +01:00
}
2019-03-27 09:00:19 +01:00
return FALSE ;
}
_IRQL_requires_same_
static void TunAppendNBL ( _Inout_ NET_BUFFER_LIST * * head , _Inout_ NET_BUFFER_LIST * * tail , __drv_aliasesMem _In_ NET_BUFFER_LIST * nbl )
{
* ( * tail ? & NET_BUFFER_LIST_NEXT_NBL ( * tail ) : head ) = nbl ;
* tail = nbl ;
NET_BUFFER_LIST_NEXT_NBL ( nbl ) = NULL ;
}
_Requires_lock_not_held_ ( ctx - > PacketQueue . Lock )
_IRQL_requires_max_ ( DISPATCH_LEVEL )
static void TunQueueAppend ( _Inout_ TUN_CTX * ctx , _In_ NET_BUFFER_LIST * nbl , _In_ UINT max_nbls )
{
for ( NET_BUFFER_LIST * nbl_next ; nbl ; nbl = nbl_next ) {
nbl_next = NET_BUFFER_LIST_NEXT_NBL ( nbl ) ;
if ( ! NET_BUFFER_LIST_FIRST_NB ( nbl ) ) {
NET_BUFFER_LIST_NEXT_NBL ( nbl ) = NULL ;
NdisMSendNetBufferListsComplete ( ctx - > MiniportAdapterHandle , nbl , 0 ) ;
continue ;
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
KLOCK_QUEUE_HANDLE lqh ;
KeAcquireInStackQueuedSpinLock ( & ctx - > PacketQueue . Lock , & lqh ) ;
TunNBLRefInit ( ctx , nbl ) ;
TunAppendNBL ( & ctx - > PacketQueue . FirstNbl , & ctx - > PacketQueue . LastNbl , nbl ) ;
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
while ( ( UINT ) InterlockedGet ( & ctx - > PacketQueue . NumNbl ) > max_nbls & & ctx - > PacketQueue . FirstNbl ) {
NET_BUFFER_LIST * nbl_second = NET_BUFFER_LIST_NEXT_NBL ( ctx - > PacketQueue . FirstNbl ) ;
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
NET_BUFFER_LIST_STATUS ( ctx - > PacketQueue . FirstNbl ) = NDIS_STATUS_SEND_ABORTED ;
2019-03-27 09:09:58 +01:00
TunNBLRefDec ( ctx , ctx - > PacketQueue . FirstNbl , NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL ) ;
2019-03-27 09:00:19 +01:00
ctx - > PacketQueue . NextNb = NULL ;
ctx - > PacketQueue . FirstNbl = nbl_second ;
if ( ! ctx - > PacketQueue . FirstNbl )
ctx - > PacketQueue . LastNbl = NULL ;
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
KeReleaseInStackQueuedSpinLock ( & lqh ) ;
2019-03-08 22:33:15 +01:00
}
}
2019-03-27 09:00:19 +01:00
_Requires_lock_held_ ( ctx - > PacketQueue . Lock )
_IRQL_requires_ ( DISPATCH_LEVEL )
2019-03-28 11:21:51 +01:00
_Must_inspect_result_
static _Return_type_success_ ( return ! = NULL ) NET_BUFFER * TunQueueRemove ( _Inout_ TUN_CTX * ctx , _Out_ NET_BUFFER_LIST * * nbl )
2019-03-08 22:33:15 +01:00
{
2019-03-27 09:00:19 +01:00
NET_BUFFER_LIST * nbl_top ;
NET_BUFFER * ret ;
2019-03-28 09:05:29 +01:00
retry :
2019-03-27 09:00:19 +01:00
nbl_top = ctx - > PacketQueue . FirstNbl ;
* nbl = nbl_top ;
if ( ! nbl_top )
return NULL ;
if ( ! ctx - > PacketQueue . NextNb )
ctx - > PacketQueue . NextNb = NET_BUFFER_LIST_FIRST_NB ( nbl_top ) ;
ret = ctx - > PacketQueue . NextNb ;
ctx - > PacketQueue . NextNb = NET_BUFFER_NEXT_NB ( ret ) ;
if ( ! ctx - > PacketQueue . NextNb ) {
ctx - > PacketQueue . FirstNbl = NET_BUFFER_LIST_NEXT_NBL ( nbl_top ) ;
if ( ! ctx - > PacketQueue . FirstNbl )
ctx - > PacketQueue . LastNbl = NULL ;
NET_BUFFER_LIST_NEXT_NBL ( nbl_top ) = NULL ;
} else
TunNBLRefInc ( nbl_top ) ;
2019-03-28 09:05:29 +01:00
if ( ret & & NET_BUFFER_DATA_LENGTH ( ret ) > TUN_EXCH_MAX_IP_PACKET_SIZE ) {
NET_BUFFER_LIST_STATUS ( nbl_top ) = NDIS_STATUS_INVALID_LENGTH ;
TunNBLRefDec ( ctx , nbl_top , NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL ) ;
goto retry ; /* A for (;;) and a break would be fine, but this is clearer actually. */
}
2019-03-27 09:00:19 +01:00
return ret ;
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
/* Note: Must be called immediately after TunQueueRemove without dropping ctx->PacketQueue.Lock. */
_Requires_lock_held_ ( ctx - > PacketQueue . Lock )
_IRQL_requires_ ( DISPATCH_LEVEL )
2019-03-28 08:30:13 +01:00
static void TunQueuePrepend ( _Inout_ TUN_CTX * ctx , _In_ NET_BUFFER * nb , _In_ NET_BUFFER_LIST * nbl )
2019-03-27 09:00:19 +01:00
{
ctx - > PacketQueue . NextNb = nb ;
2019-03-28 08:30:13 +01:00
if ( ! nbl | | nbl = = ctx - > PacketQueue . FirstNbl )
2019-03-08 22:33:15 +01:00
return ;
2019-03-27 09:00:19 +01:00
2019-03-28 08:30:13 +01:00
TunNBLRefInc ( nbl ) ;
2019-03-27 09:00:19 +01:00
if ( ! ctx - > PacketQueue . FirstNbl )
2019-03-28 08:30:13 +01:00
ctx - > PacketQueue . FirstNbl = ctx - > PacketQueue . LastNbl = nbl ;
2019-03-27 09:00:19 +01:00
else {
2019-03-28 08:30:13 +01:00
NET_BUFFER_LIST_NEXT_NBL ( nbl ) = ctx - > PacketQueue . FirstNbl ;
ctx - > PacketQueue . FirstNbl = nbl ;
2019-03-08 22:33:15 +01:00
}
2019-03-27 09:00:19 +01:00
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
_Requires_lock_not_held_ ( ctx - > PacketQueue . Lock )
_IRQL_requires_max_ ( DISPATCH_LEVEL )
static void TunQueueProcess ( _Inout_ TUN_CTX * ctx )
{
2019-03-27 15:41:21 +01:00
IRP * irp = NULL ;
2019-03-28 11:11:43 +01:00
UCHAR * buffer = NULL ;
ULONG size = 0 ;
2019-03-27 09:00:19 +01:00
NET_BUFFER * nb ;
KLOCK_QUEUE_HANDLE lqh ;
for ( ; ; ) {
NET_BUFFER_LIST * nbl ;
KeAcquireInStackQueuedSpinLock ( & ctx - > PacketQueue . Lock , & lqh ) ;
2019-03-28 11:11:43 +01:00
/* Get head NB (and IRP). */
2019-03-27 15:41:21 +01:00
if ( ! irp ) {
2019-03-27 09:00:19 +01:00
nb = TunQueueRemove ( ctx , & nbl ) ;
if ( ! nb ) {
KeReleaseInStackQueuedSpinLock ( & lqh ) ;
return ;
}
2019-03-28 11:11:43 +01:00
irp = TunRemoveNextIrp ( ctx , & buffer , & size ) ;
2019-03-27 15:41:21 +01:00
if ( ! irp ) {
2019-03-28 08:30:13 +01:00
TunQueuePrepend ( ctx , nb , nbl ) ;
2019-03-27 09:00:19 +01:00
KeReleaseInStackQueuedSpinLock ( & lqh ) ;
2019-03-28 08:30:13 +01:00
if ( nbl )
TunNBLRefDec ( ctx , nbl , 0 ) ;
2019-03-27 09:00:19 +01:00
return ;
}
2019-03-28 07:35:23 +01:00
} else
2019-03-27 09:00:19 +01:00
nb = TunQueueRemove ( ctx , & nbl ) ;
2019-03-28 08:30:13 +01:00
2019-03-28 11:11:43 +01:00
/* If the NB won't fit in the IRP, return it. */
2019-03-28 11:45:57 +01:00
if ( nb & & TunCanFitIntoIrp ( irp , size , nb ) ) {
2019-03-28 08:30:13 +01:00
TunQueuePrepend ( ctx , nb , nbl ) ;
if ( nbl )
TunNBLRefDec ( ctx , nbl , NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL ) ;
nbl = NULL ;
nb = NULL ;
}
2019-03-27 09:00:19 +01:00
KeReleaseInStackQueuedSpinLock ( & lqh ) ;
2019-03-28 11:11:43 +01:00
/* Process NB and IRP. */
if ( nb ) {
NTSTATUS status = TunWriteIntoIrp ( irp , buffer , nb ) ;
if ( ! NT_SUCCESS ( status ) ) {
if ( nbl )
NET_BUFFER_LIST_STATUS ( nbl ) = status ;
IoCsqInsertIrpEx ( & ctx - > Device . ReadQueue . Csq , irp , NULL , TUN_CSQ_INSERT_HEAD ) ;
2019-03-28 11:45:57 +01:00
irp = NULL ;
2019-03-28 11:11:43 +01:00
}
} else {
2019-03-27 15:41:21 +01:00
irp - > IoStatus . Status = STATUS_SUCCESS ;
IoCompleteRequest ( irp , IO_NETWORK_INCREMENT ) ;
2019-03-27 09:00:19 +01:00
TunCompletePause ( ctx , 1 ) ;
2019-03-28 11:45:57 +01:00
irp = NULL ;
2019-03-27 09:00:19 +01:00
}
2019-03-28 11:11:43 +01:00
2019-03-27 09:00:19 +01:00
if ( nbl )
2019-03-27 09:09:58 +01:00
TunNBLRefDec ( ctx , nbl , 0 ) ;
2019-03-27 09:00:19 +01:00
}
2019-03-08 22:33:15 +01:00
}
static DRIVER_DISPATCH TunDispatchCreate ;
_Use_decl_annotations_
static NTSTATUS TunDispatchCreate ( DEVICE_OBJECT * DeviceObject , IRP * Irp )
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
TUN_CTX * ctx = TunGetContext ( DeviceObject ) ;
if ( ! ctx ) {
status = STATUS_INVALID_HANDLE ;
goto cleanup_complete_req ;
}
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( status = TunCheckForPause ( ctx , 1 ) ) )
2019-03-22 13:47:17 +01:00
goto cleanup_TunCompletePause ;
2019-03-22 13:38:35 +01:00
ASSERT ( InterlockedGet64 ( & ctx - > Device . RefCount ) < MAXLONG64 ) ;
2019-03-08 22:33:15 +01:00
InterlockedIncrement64 ( & ctx - > Device . RefCount ) ;
TunIndicateStatus ( ctx ) ;
2019-03-22 13:47:17 +01:00
cleanup_TunCompletePause :
2019-03-27 09:00:19 +01:00
TunCompletePause ( ctx , 1 ) ;
2019-03-08 22:33:15 +01:00
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_UNSUCCESSFUL ;
TUN_CTX * ctx = TunGetContext ( DeviceObject ) ;
if ( ! ctx ) {
status = STATUS_INVALID_HANDLE ;
goto cleanup_complete_req ;
}
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( status = TunCheckForPause ( ctx , 1 ) ) )
2019-03-22 13:47:17 +01:00
goto cleanup_TunCompletePause ;
2019-03-22 13:38:35 +01:00
ASSERT ( InterlockedGet64 ( & ctx - > Device . RefCount ) > 0 ) ;
2019-03-08 22:33:15 +01:00
InterlockedDecrement64 ( & ctx - > Device . RefCount ) ;
TunIndicateStatus ( ctx ) ;
2019-03-22 13:47:17 +01:00
cleanup_TunCompletePause :
2019-03-27 09:00:19 +01:00
TunCompletePause ( ctx , 1 ) ;
2019-03-08 22:33:15 +01:00
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_UNSUCCESSFUL ;
TUN_CTX * ctx = TunGetContext ( DeviceObject ) ;
if ( ! ctx ) {
status = STATUS_INVALID_HANDLE ;
goto cleanup_complete_req ;
}
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( status = TunCheckForPause ( ctx , 1 ) ) )
2019-03-22 13:47:17 +01:00
goto cleanup_TunCompletePause ;
2019-03-27 09:00:19 +01:00
Irp - > IoStatus . Information = 0 ;
InterlockedIncrement64 ( & ctx - > ActiveTransactionCount ) ;
status = IoCsqInsertIrpEx ( & ctx - > Device . ReadQueue . Csq , Irp , NULL , TUN_CSQ_INSERT_TAIL ) ;
if ( ! NT_SUCCESS ( status ) ) {
2019-03-27 13:44:55 +01:00
InterlockedDecrement64 ( & ctx - > ActiveTransactionCount ) ;
2019-03-27 09:00:19 +01:00
goto cleanup_TunCompletePause ;
}
TunQueueProcess ( ctx ) ;
TunCompletePause ( ctx , 1 ) ;
2019-03-08 22:33:15 +01:00
return STATUS_PENDING ;
2019-03-22 13:47:17 +01:00
cleanup_TunCompletePause :
2019-03-27 09:00:19 +01:00
TunCompletePause ( ctx , 1 ) ;
2019-03-08 22:33:15 +01:00
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_UNSUCCESSFUL ;
ULONG_PTR information = 0 ;
TUN_CTX * ctx = TunGetContext ( DeviceObject ) ;
if ( ! ctx ) {
status = STATUS_INVALID_HANDLE ;
goto cleanup_complete_req ;
}
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( status = TunCheckForPause ( ctx , 1 ) ) )
2019-03-22 13:47:17 +01:00
goto cleanup_TunCompletePause ;
2019-03-28 11:11:43 +01:00
UCHAR * buffer ;
ULONG size ;
status = TunGetIrpBuffer ( Irp , & buffer , & size ) ;
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( status ) )
2019-03-22 13:47:17 +01:00
goto cleanup_TunCompletePause ;
2019-03-08 22:33:15 +01:00
const UCHAR * b = buffer , * b_end = buffer + size ;
ULONG nbl_count = 0 ;
NET_BUFFER_LIST * nbl_head = NULL , * nbl_tail = NULL ;
2019-03-21 13:30:03 +01:00
LONG64 stat_size = 0 , stat_p_ok = 0 , stat_p_err = 0 ;
2019-03-08 22:33:15 +01:00
while ( b < b_end ) {
TUN_PACKET * p = ( TUN_PACKET * ) b ;
if ( p - > Size > TUN_EXCH_MAX_IP_PACKET_SIZE )
break ;
UINT p_size = TunPacketAlign ( sizeof ( TUN_PACKET ) + p - > Size ) ;
if ( b + p_size > b_end )
break ;
ULONG nbl_flags ;
USHORT nbl_proto ;
if ( p - > Size > = 20 & & p - > Data [ 0 ] > > 4 = = 4 ) {
nbl_flags = NDIS_NBL_FLAGS_IS_IPV4 ;
nbl_proto = NDIS_ETH_TYPE_IPV4 ;
} else if ( p - > Size > = 40 & & p - > Data [ 0 ] > > 4 = = 6 ) {
nbl_flags = NDIS_NBL_FLAGS_IS_IPV6 ;
nbl_proto = NDIS_ETH_TYPE_IPV6 ;
} else {
goto skip_packet ;
}
MDL * mdl = NdisAllocateMdl ( ctx - > MiniportAdapterHandle , p - > Data , p - > Size ) ;
if ( ! mdl )
goto skip_packet ;
NET_BUFFER_LIST * nbl = NdisAllocateNetBufferAndNetBufferList ( ctx - > NBLPool , 0 , 0 , mdl , 0 , p - > Size ) ;
if ( ! nbl )
goto cleanup_NdisFreeMdl ;
nbl - > SourceHandle = ctx - > MiniportAdapterHandle ;
NdisSetNblFlag ( nbl , nbl_flags ) ;
NET_BUFFER_LIST_INFO ( nbl , NetBufferListFrameType ) = ( PVOID ) TunHtons ( nbl_proto ) ;
NET_BUFFER_LIST_STATUS ( nbl ) = NDIS_STATUS_SUCCESS ;
TunAppendNBL ( & nbl_head , & nbl_tail , nbl ) ;
nbl_count + + ;
goto next_packet ;
cleanup_NdisFreeMdl :
NdisFreeMdl ( mdl ) ;
skip_packet :
stat_p_err + + ;
next_packet :
b + = p_size ;
}
2019-03-21 13:30:03 +01:00
/* 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 reqiures 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 .
*
* Choice :
* Both ( 1 ) and ( 2 ) have pros and cons . Making ( 1 ) work is clearly the better long term goal . But we lack the knowledge
* to make it work correctly . ( 2 ) seems like an acceptable stopgap solution until we ' re smart enough to reason about
* ( 1 ) . So , let ' s implement ( 2 ) now , and we ' ll let more knowledgeable people advise us on ( 1 ) later .
*/
2019-03-22 13:47:17 +01:00
if ( nbl_head )
NdisMIndicateReceiveNetBufferLists ( ctx - > MiniportAdapterHandle , nbl_head , NDIS_DEFAULT_PORT_NUMBER , nbl_count , NDIS_RECEIVE_FLAGS_RESOURCES ) ;
2019-03-08 22:33:15 +01:00
2019-03-21 13:30:03 +01:00
for ( NET_BUFFER_LIST * nbl = nbl_head , * nbl_next ; nbl ; nbl = nbl_next ) {
nbl_next = NET_BUFFER_LIST_NEXT_NBL ( nbl ) ;
NET_BUFFER_LIST_NEXT_NBL ( nbl ) = NULL ;
2019-03-08 22:33:15 +01:00
2019-03-21 13:30:03 +01:00
MDL * mdl = NET_BUFFER_FIRST_MDL ( NET_BUFFER_LIST_FIRST_NB ( nbl ) ) ;
2019-03-27 09:00:19 +01:00
if ( NT_SUCCESS ( NET_BUFFER_LIST_STATUS ( nbl ) ) ) {
2019-03-22 13:47:17 +01:00
ULONG p_size = MmGetMdlByteCount ( mdl ) ;
stat_size + = p_size ;
stat_p_ok + + ;
} else
stat_p_err + + ;
2019-03-21 13:30:03 +01:00
NdisFreeMdl ( mdl ) ;
NdisFreeNetBufferList ( nbl ) ;
}
InterlockedAdd64 ( ( LONG64 * ) & ctx - > Statistics . ifHCInOctets , stat_size ) ;
InterlockedAdd64 ( ( LONG64 * ) & ctx - > Statistics . ifHCInUcastOctets , stat_size ) ;
InterlockedAdd64 ( ( LONG64 * ) & ctx - > Statistics . ifHCInUcastPkts , stat_p_ok ) ;
InterlockedAdd64 ( ( LONG64 * ) & ctx - > Statistics . ifInErrors , stat_p_err ) ;
2019-03-08 22:33:15 +01:00
2019-03-22 13:47:17 +01:00
information = b - buffer ;
cleanup_TunCompletePause :
2019-03-27 09:00:19 +01:00
TunCompletePause ( ctx , 1 ) ;
2019-03-08 22:33:15 +01:00
cleanup_complete_req :
TunCompleteRequest ( Irp , information , status ) ;
return status ;
}
2019-03-27 09:00:19 +01:00
static DRIVER_DISPATCH TunDispatchCleanup ;
_Use_decl_annotations_
static NTSTATUS TunDispatchCleanup ( DEVICE_OBJECT * DeviceObject , IRP * Irp )
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
TUN_CTX * ctx = TunGetContext ( DeviceObject ) ;
if ( ! ctx ) {
status = STATUS_INVALID_HANDLE ;
goto cleanup_complete_req ;
}
2019-03-27 13:44:55 +01:00
LONG64 count = 1 ;
if ( ! NT_SUCCESS ( status = TunCheckForPause ( ctx , count ) ) )
2019-03-27 09:00:19 +01:00
goto cleanup_TunCompletePause ;
IO_STACK_LOCATION * stack = IoGetCurrentIrpStackLocation ( Irp ) ;
IRP * pending_irp ;
while ( ( pending_irp = IoCsqRemoveNextIrp ( & ctx - > Device . ReadQueue . Csq , stack - > FileObject ) ) ! = NULL ) {
count + + ;
TunCompleteRequest ( pending_irp , 0 , STATUS_CANCELLED ) ;
}
cleanup_TunCompletePause :
2019-03-27 13:44:55 +01:00
TunCompletePause ( ctx , count ) ;
2019-03-27 09:00:19 +01:00
cleanup_complete_req :
TunCompleteRequest ( Irp , 0 , status ) ;
return status ;
}
2019-03-08 22:33:15 +01:00
static MINIPORT_SET_OPTIONS TunSetOptions ;
_Use_decl_annotations_
static NDIS_STATUS TunSetOptions ( NDIS_HANDLE NdisDriverHandle , NDIS_HANDLE DriverContext )
{
/* TODO: This handler is optional. See if it can be removed. */
return NDIS_STATUS_SUCCESS ;
}
static MINIPORT_PAUSE TunPause ;
_Use_decl_annotations_
static NDIS_STATUS TunPause ( NDIS_HANDLE MiniportAdapterContext , PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters )
{
TUN_CTX * ctx = ( TUN_CTX * ) MiniportAdapterContext ;
2019-03-22 13:47:17 +01:00
2019-03-27 09:00:19 +01:00
LONG64 count = 1 ;
2019-03-27 13:44:55 +01:00
InterlockedAdd64 ( & ctx - > ActiveTransactionCount , count ) ;
if ( InterlockedCompareExchange ( ( LONG * ) & ctx - > State , TUN_STATE_PAUSING , TUN_STATE_RUNNING ) ! = TUN_STATE_RUNNING ) {
InterlockedDecrement64 ( & ctx - > ActiveTransactionCount ) ;
return NDIS_STATUS_FAILURE ;
}
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
KLOCK_QUEUE_HANDLE lqh ;
KeAcquireInStackQueuedSpinLock ( & ctx - > PacketQueue . Lock , & lqh ) ;
for ( NET_BUFFER_LIST * nbl = ctx - > PacketQueue . FirstNbl , * nbl_next ; nbl ; nbl = nbl_next ) {
nbl_next = NET_BUFFER_LIST_NEXT_NBL ( nbl ) ;
2019-03-27 10:10:08 +01:00
NET_BUFFER_LIST_STATUS ( nbl ) = STATUS_NDIS_PAUSED ;
2019-03-27 09:09:58 +01:00
TunNBLRefDec ( ctx , nbl , NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL ) ;
2019-03-27 09:00:19 +01:00
}
ctx - > PacketQueue . FirstNbl = NULL ;
ctx - > PacketQueue . LastNbl = NULL ;
ctx - > PacketQueue . NextNb = NULL ;
InterlockedExchange ( & ctx - > PacketQueue . NumNbl , 0 ) ;
KeReleaseInStackQueuedSpinLock ( & lqh ) ;
/* Cancel pending IRPs to unblock waiting clients. */
IRP * pending_irp ;
while ( ( pending_irp = IoCsqRemoveNextIrp ( & ctx - > Device . ReadQueue . Csq , NULL ) ) ! = NULL ) {
count + + ;
TunCompleteRequest ( pending_irp , 0 , STATUS_CANCELLED ) ;
}
2019-03-22 13:47:17 +01:00
2019-03-27 13:44:55 +01:00
return TunCompletePause ( ctx , count ) ;
2019-03-08 22:33:15 +01:00
}
static MINIPORT_RESTART TunRestart ;
_Use_decl_annotations_
static NDIS_STATUS TunRestart ( NDIS_HANDLE MiniportAdapterContext , PNDIS_MINIPORT_RESTART_PARAMETERS MiniportRestartParameters )
{
TUN_CTX * ctx = ( TUN_CTX * ) MiniportAdapterContext ;
if ( InterlockedCompareExchange ( ( LONG * ) & ctx - > State , TUN_STATE_RESTARTING , TUN_STATE_PAUSED ) ! = TUN_STATE_PAUSED )
return NDIS_STATUS_FAILURE ;
2019-03-22 13:47:17 +01:00
ASSERT ( ! InterlockedGet64 ( & ctx - > Device . RefCount ) ) ;
2019-03-08 22:33:15 +01:00
TunIndicateStatus ( ctx ) ;
InterlockedExchange ( ( LONG * ) & ctx - > State , TUN_STATE_RUNNING ) ;
return NDIS_STATUS_SUCCESS ;
}
static MINIPORT_RETURN_NET_BUFFER_LISTS TunReturnNetBufferLists ;
_Use_decl_annotations_
static void TunReturnNetBufferLists ( NDIS_HANDLE MiniportAdapterContext , PNET_BUFFER_LIST NetBufferLists , ULONG ReturnFlags )
{
2019-03-22 13:38:35 +01:00
ASSERTMSG ( " TunReturnNetBufferLists() should not be called as NBLs are delivered using NDIS_RECEIVE_FLAGS_RESOURCES flag in NdisMIndicateReceiveNetBufferLists(). " , 0 ) ;
2019-03-08 22:33:15 +01:00
}
static MINIPORT_CANCEL_SEND TunCancelSend ;
_Use_decl_annotations_
static void TunCancelSend ( NDIS_HANDLE MiniportAdapterContext , PVOID CancelId )
{
TUN_CTX * ctx = ( TUN_CTX * ) MiniportAdapterContext ;
2019-03-27 09:00:19 +01:00
KLOCK_QUEUE_HANDLE lqh ;
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
KeAcquireInStackQueuedSpinLock ( & ctx - > PacketQueue . Lock , & lqh ) ;
2019-03-08 22:33:15 +01:00
2019-03-27 10:10:08 +01:00
NET_BUFFER_LIST * nbl_last = NULL , * * nbl_last_link = & ctx - > PacketQueue . FirstNbl ;
2019-03-27 09:00:19 +01:00
for ( NET_BUFFER_LIST * nbl = ctx - > PacketQueue . FirstNbl , * nbl_next ; nbl ; nbl = nbl_next ) {
2019-03-08 22:33:15 +01:00
nbl_next = NET_BUFFER_LIST_NEXT_NBL ( nbl ) ;
if ( NDIS_GET_NET_BUFFER_LIST_CANCEL_ID ( nbl ) = = CancelId ) {
2019-03-27 10:10:08 +01:00
* nbl_last_link = nbl_next ;
2019-03-27 09:09:58 +01:00
TunNBLRefDec ( ctx , nbl , NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL ) ;
2019-03-27 09:00:19 +01:00
} else {
2019-03-27 10:10:08 +01:00
nbl_last = nbl ;
nbl_last_link = & NET_BUFFER_LIST_NEXT_NBL ( nbl ) ;
2019-03-27 09:00:19 +01:00
}
2019-03-08 22:33:15 +01:00
}
2019-03-27 10:10:08 +01:00
ctx - > PacketQueue . LastNbl = nbl_last ;
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
KeReleaseInStackQueuedSpinLock ( & lqh ) ;
2019-03-08 22:33:15 +01:00
}
static MINIPORT_DEVICE_PNP_EVENT_NOTIFY TunDevicePnPEventNotify ;
_Use_decl_annotations_
static void TunDevicePnPEventNotify ( NDIS_HANDLE MiniportAdapterContext , PNET_DEVICE_PNP_EVENT NetDevicePnPEvent )
{
}
static MINIPORT_SHUTDOWN TunShutdownEx ;
_Use_decl_annotations_
static void TunShutdownEx ( NDIS_HANDLE MiniportAdapterContext , NDIS_SHUTDOWN_ACTION ShutdownAction )
{
TUN_CTX * ctx = ( TUN_CTX * ) MiniportAdapterContext ;
if ( ShutdownAction = = NdisShutdownBugCheck )
return ;
InterlockedExchange ( ( LONG * ) & ctx - > State , TUN_STATE_SHUTDOWN ) ;
}
static MINIPORT_CANCEL_DIRECT_OID_REQUEST TunCancelDirectOidRequest ;
_Use_decl_annotations_
static void TunCancelDirectOidRequest ( NDIS_HANDLE MiniportAdapterContext , PVOID RequestId )
{
}
static MINIPORT_CANCEL_OID_REQUEST TunCancelOidRequest ;
_Use_decl_annotations_
static void TunCancelOidRequest ( NDIS_HANDLE MiniportAdapterContext , PVOID RequestId )
{
}
static MINIPORT_INITIALIZE TunInitializeEx ;
_Use_decl_annotations_
static NDIS_STATUS TunInitializeEx ( NDIS_HANDLE MiniportAdapterHandle , NDIS_HANDLE MiniportDriverContext , PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters )
{
NDIS_STATUS status ;
if ( ! MiniportAdapterHandle )
return NDIS_STATUS_FAILURE ;
TUN_CTX * ctx ;
# pragma warning(suppress: 6014) /* Leaking memory 'ctx'. Note: 'ctx' is aliased in attr.MiniportAdapterContext; or freed on failure. */
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( NdisAllocateMemoryWithTag ( & ctx , sizeof ( TUN_CTX ) , TUN_MEMORY_TAG ) ) )
2019-03-08 22:33:15 +01:00
return NDIS_STATUS_FAILURE ;
NdisZeroMemory ( ctx , sizeof ( * ctx ) ) ;
ctx - > State = TUN_STATE_INITIALIZING ;
ctx - > PowerState = NdisDeviceStateD0 ;
ctx - > MiniportAdapterHandle = MiniportAdapterHandle ;
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 =
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 ;
NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES attr = {
. Header = {
. Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES ,
. Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1 ,
. Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1
} ,
. AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND ,
. InterfaceType = NdisInterfaceInternal ,
. MiniportAdapterContext = ctx
} ;
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( NdisMSetMiniportAttributes ( MiniportAdapterHandle , ( PNDIS_MINIPORT_ADAPTER_ATTRIBUTES ) & attr ) ) ) {
2019-03-08 22:33:15 +01:00
status = NDIS_STATUS_FAILURE ;
goto cleanup_ctx ;
}
NDIS_PM_CAPABILITIES pmcap = {
. Header = {
. Type = NDIS_OBJECT_TYPE_DEFAULT ,
. Revision = NDIS_PM_CAPABILITIES_REVISION_1 ,
. Size = NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1
} ,
. MinMagicPacketWakeUp = NdisDeviceStateUnspecified ,
. MinPatternWakeUp = NdisDeviceStateUnspecified ,
. MinLinkChangeWakeUp = NdisDeviceStateUnspecified
} ;
static NDIS_OID suported_oids [ ] = {
OID_GEN_MAXIMUM_TOTAL_SIZE ,
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
} ;
NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES gen = {
. 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
} ,
. 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 ,
. SupportedStatistics = ctx - > Statistics . SupportedStatistics ,
. 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 ,
. SupportedOidList = suported_oids ,
. SupportedOidListLength = sizeof ( suported_oids ) ,
. PowerManagementCapabilitiesEx = & pmcap
} ;
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( NdisMSetMiniportAttributes ( MiniportAdapterHandle , ( PNDIS_MINIPORT_ADAPTER_ATTRIBUTES ) & gen ) ) ) {
2019-03-08 22:33:15 +01:00
status = NDIS_STATUS_FAILURE ;
goto cleanup_ctx ;
}
2019-03-27 09:00:19 +01:00
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 ) ;
2019-03-08 22:33:15 +01:00
/* A miniport driver can call NdisMIndicateStatusEx after setting its
* registration attributes even if the driver is still in the context
* of the MiniportInitializeEx function .
*/
TunIndicateStatus ( ctx ) ;
NET_BUFFER_LIST_POOL_PARAMETERS nbl_pool_param = {
. Header = {
. Type = NDIS_OBJECT_TYPE_DEFAULT ,
. Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1 ,
. Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1
} ,
. ProtocolId = NDIS_PROTOCOL_ID_DEFAULT ,
. fAllocateNetBuffer = TRUE ,
. PoolTag = TUN_MEMORY_TAG
} ;
# pragma warning(suppress: 6014) /* Leaking memory 'ctx->NBLPool'. Note: 'ctx->NBLPool' is freed in TunHaltEx; or freed on failure. */
ctx - > NBLPool = NdisAllocateNetBufferListPool ( MiniportAdapterHandle , & nbl_pool_param ) ;
if ( ! ctx - > NBLPool ) {
status = NDIS_STATUS_FAILURE ;
2019-03-27 09:00:19 +01:00
goto cleanup_ctx ;
2019-03-08 22:33:15 +01:00
}
WCHAR device_name [ ( sizeof ( L " \\ Device \\ " TUN_DEVICE_NAME ) + 10 /*MAXULONG as string*/ ) / sizeof ( WCHAR ) ] ;
UNICODE_STRING unicode_device_name ;
TunInitUnicodeString ( & unicode_device_name , device_name ) ;
RtlUnicodeStringPrintf ( & unicode_device_name , L " \\ Device \\ " TUN_DEVICE_NAME , ( ULONG ) MiniportInitParameters - > NetLuid . Info . NetLuidIndex ) ;
WCHAR symbolic_name [ ( sizeof ( L " \\ DosDevices \\ " TUN_DEVICE_NAME ) + 10 /*MAXULONG as string*/ ) / sizeof ( WCHAR ) ] ;
UNICODE_STRING unicode_symbolic_name ;
TunInitUnicodeString ( & unicode_symbolic_name , symbolic_name ) ;
RtlUnicodeStringPrintf ( & unicode_symbolic_name , L " \\ DosDevices \\ " TUN_DEVICE_NAME , ( ULONG ) MiniportInitParameters - > NetLuid . Info . NetLuidIndex ) ;
static PDRIVER_DISPATCH dispatch_table [ IRP_MJ_MAXIMUM_FUNCTION + 1 ] = {
2019-03-27 09:00:19 +01:00
TunDispatchCreate , /* IRP_MJ_CREATE */
NULL , /* IRP_MJ_CREATE_NAMED_PIPE */
TunDispatchClose , /* IRP_MJ_CLOSE */
TunDispatchRead , /* IRP_MJ_READ */
TunDispatchWrite , /* 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 */
TunDispatchCleanup , /* IRP_MJ_CLEANUP */
2019-03-08 22:33:15 +01:00
} ;
NDIS_DEVICE_OBJECT_ATTRIBUTES t = {
. Header = {
. Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES ,
. Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1 ,
. Size = NDIS_SIZEOF_DEVICE_OBJECT_ATTRIBUTES_REVISION_1
} ,
. DeviceName = & unicode_device_name ,
. SymbolicName = & unicode_symbolic_name ,
. MajorFunctions = dispatch_table ,
. ExtensionSize = sizeof ( TUN_CTX * ) ,
. DefaultSDDLString = & SDDL_DEVOBJ_SYS_ALL /* Kernel, and SYSTEM: full control. Others: none */
} ;
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( NdisRegisterDeviceEx ( NdisMiniportDriverHandle , & t , & ctx - > Device . Object , & ctx - > Device . Handle ) ) ) {
2019-03-08 22:33:15 +01:00
status = NDIS_STATUS_FAILURE ;
goto cleanup_NdisFreeNetBufferListPool ;
}
ctx - > Device . Object - > Flags & = ~ DO_BUFFERED_IO ;
ctx - > Device . Object - > Flags | = DO_DIRECT_IO ;
2019-03-27 17:16:35 +01:00
TUN_CTX * volatile * control_device_extension = TunGetContextPointer ( ctx - > Device . Object ) ;
2019-03-27 13:44:55 +01:00
if ( ! control_device_extension ) {
status = NDIS_STATUS_FAILURE ;
goto cleanup_NdisDeregisterDeviceEx ;
}
2019-03-27 17:16:35 +01:00
InterlockedExchangePointer ( control_device_extension , ctx ) ;
2019-03-27 13:44:55 +01:00
2019-03-08 22:33:15 +01:00
ctx - > State = TUN_STATE_PAUSED ;
return NDIS_STATUS_SUCCESS ;
2019-03-27 13:44:55 +01:00
cleanup_NdisDeregisterDeviceEx :
NdisDeregisterDeviceEx ( ctx - > Device . Handle ) ;
2019-03-08 22:33:15 +01:00
cleanup_NdisFreeNetBufferListPool :
NdisFreeNetBufferListPool ( ctx - > NBLPool ) ;
cleanup_ctx :
NdisFreeMemory ( ctx , 0 , 0 ) ;
return status ;
}
static MINIPORT_UNLOAD TunDriverUnload ;
_Use_decl_annotations_
static VOID TunDriverUnload ( PDRIVER_OBJECT DriverObject )
{
NdisMDeregisterMiniportDriver ( NdisMiniportDriverHandle ) ;
}
static MINIPORT_HALT TunHaltEx ;
_Use_decl_annotations_
static void TunHaltEx ( NDIS_HANDLE MiniportAdapterContext , NDIS_HALT_ACTION HaltAction )
{
TUN_CTX * ctx = ( TUN_CTX * ) MiniportAdapterContext ;
if ( InterlockedGet ( ( LONG * ) & ctx - > State ) ! = TUN_STATE_PAUSED )
return ;
2019-03-22 13:38:35 +01:00
ASSERT ( ! InterlockedGet64 ( & ctx - > ActiveTransactionCount ) ) ;
2019-03-22 13:47:17 +01:00
ASSERT ( ! InterlockedGet64 ( & ctx - > Device . RefCount ) ) ;
2019-03-08 22:33:15 +01:00
2019-03-27 13:44:55 +01:00
/* Reset adapter context in device object, as Windows keeps calling dispatch handlers even after NdisDeregisterDeviceEx(). */
2019-03-27 17:16:35 +01:00
TUN_CTX * volatile * control_device_extension = TunGetContextPointer ( ctx - > Device . Object ) ;
2019-03-27 13:44:55 +01:00
if ( control_device_extension )
2019-03-27 17:16:35 +01:00
InterlockedExchangePointer ( control_device_extension , NULL ) ;
2019-03-27 13:44:55 +01:00
2019-03-08 22:33:15 +01:00
/* Release resources. */
NdisDeregisterDeviceEx ( ctx - > Device . Handle ) ;
NdisFreeNetBufferListPool ( ctx - > NBLPool ) ;
NdisFreeMemory ( ctx , 0 , 0 ) ;
}
_IRQL_requires_max_ ( PASSIVE_LEVEL )
static NDIS_STATUS TunOidSet ( _Inout_ TUN_CTX * ctx , _In_ NDIS_OID Oid , _In_bytecount_ ( InformationBufferLength ) const void * InformationBuffer , _In_ UINT InformationBufferLength , _Out_ UINT * BytesRead , _Out_ UINT * BytesNeeded )
{
* BytesRead = 0 ;
* BytesNeeded = 0 ;
switch ( Oid ) {
case OID_GEN_CURRENT_PACKET_FILTER :
case OID_GEN_CURRENT_LOOKAHEAD :
if ( InformationBufferLength ! = 4 ) {
* BytesNeeded = 4 ;
return NDIS_STATUS_INVALID_LENGTH ;
}
* BytesRead = 4 ;
return NDIS_STATUS_SUCCESS ;
case OID_GEN_LINK_PARAMETERS :
* BytesRead = InformationBufferLength ;
return NDIS_STATUS_SUCCESS ;
case OID_GEN_INTERRUPT_MODERATION :
return NDIS_STATUS_INVALID_DATA ;
case OID_PNP_SET_POWER :
if ( InformationBufferLength ! = sizeof ( NDIS_DEVICE_POWER_STATE ) ) {
* BytesNeeded = sizeof ( NDIS_DEVICE_POWER_STATE ) ;
return NDIS_STATUS_INVALID_LENGTH ;
}
* BytesRead = sizeof ( NDIS_DEVICE_POWER_STATE ) ;
ctx - > PowerState = * ( ( NDIS_DEVICE_POWER_STATE * ) InformationBuffer ) ;
return NDIS_STATUS_SUCCESS ;
}
return NDIS_STATUS_INVALID_OID ;
}
_IRQL_requires_max_ ( APC_LEVEL )
2019-03-28 11:21:51 +01:00
_Must_inspect_result_
2019-03-08 22:33:15 +01:00
static NDIS_STATUS TunOidQuery ( _Inout_ TUN_CTX * ctx , _In_ NDIS_OID Oid , _Out_bytecap_post_bytecount_ ( InformationBufferLength , * BytesWritten ) void * InformationBuffer , _In_ UINT InformationBufferLength , _Out_ UINT * BytesWritten , _Out_ UINT * BytesNeeded )
{
UINT value32 ;
UINT size = sizeof ( value32 ) ;
const void * buf = & value32 ;
switch ( Oid ) {
case OID_GEN_MAXIMUM_TOTAL_SIZE :
case OID_GEN_TRANSMIT_BLOCK_SIZE :
case OID_GEN_RECEIVE_BLOCK_SIZE :
value32 = TUN_EXCH_MAX_IP_PACKET_SIZE ;
break ;
case OID_GEN_TRANSMIT_BUFFER_SPACE :
case OID_GEN_RECEIVE_BUFFER_SPACE :
value32 = TUN_EXCH_MAX_IP_PACKET_SIZE * TUN_EXCH_MAX_PACKETS ;
break ;
case OID_GEN_VENDOR_ID :
value32 = TunHtonl ( TUN_VENDOR_ID ) ;
break ;
case OID_GEN_VENDOR_DESCRIPTION :
size = ( UINT ) sizeof ( TUN_VENDOR_NAME ) ;
buf = TUN_VENDOR_NAME ;
break ;
case OID_GEN_VENDOR_DRIVER_VERSION :
value32 = ( WINTUN_VERSION_MAJ < < 16 ) | WINTUN_VERSION_MIN ;
break ;
case OID_GEN_XMIT_OK :
value32 = ( UINT ) (
InterlockedGet64 ( ( LONG64 * ) & ctx - > Statistics . ifHCOutUcastPkts ) +
InterlockedGet64 ( ( LONG64 * ) & ctx - > Statistics . ifHCOutMulticastPkts ) +
InterlockedGet64 ( ( LONG64 * ) & ctx - > Statistics . ifHCOutBroadcastPkts ) ) ;
break ;
case OID_GEN_RCV_OK :
value32 = ( UINT ) (
InterlockedGet64 ( ( LONG64 * ) & ctx - > Statistics . ifHCInUcastPkts ) +
InterlockedGet64 ( ( LONG64 * ) & ctx - > Statistics . ifHCInMulticastPkts ) +
InterlockedGet64 ( ( LONG64 * ) & ctx - > Statistics . ifHCInBroadcastPkts ) ) ;
break ;
case OID_GEN_STATISTICS :
size = ( UINT ) sizeof ( ctx - > Statistics ) ;
buf = & ctx - > Statistics ;
break ;
case OID_GEN_INTERRUPT_MODERATION : {
static const NDIS_INTERRUPT_MODERATION_PARAMETERS intp = {
. Header = {
. Type = NDIS_OBJECT_TYPE_DEFAULT ,
. Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1 ,
. Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1
} ,
. InterruptModeration = NdisInterruptModerationNotSupported
} ;
size = ( UINT ) sizeof ( intp ) ;
buf = & intp ;
break ;
}
case OID_PNP_QUERY_POWER :
size = 0 ;
break ;
default :
* BytesWritten = 0 ;
return NDIS_STATUS_INVALID_OID ;
}
if ( size > InformationBufferLength ) {
* BytesNeeded = size ;
* BytesWritten = 0 ;
return NDIS_STATUS_INVALID_LENGTH ;
}
NdisMoveMemory ( InformationBuffer , buf , size ) ;
* BytesNeeded = * BytesWritten = size ;
return NDIS_STATUS_SUCCESS ;
}
static MINIPORT_OID_REQUEST TunOidRequest ;
_Use_decl_annotations_
static NDIS_STATUS TunOidRequest ( NDIS_HANDLE MiniportAdapterContext , PNDIS_OID_REQUEST OidRequest )
{
switch ( OidRequest - > RequestType ) {
case NdisRequestQueryInformation :
case NdisRequestQueryStatistics :
return TunOidQuery ( MiniportAdapterContext , OidRequest - > DATA . QUERY_INFORMATION . Oid , OidRequest - > DATA . QUERY_INFORMATION . InformationBuffer , OidRequest - > DATA . QUERY_INFORMATION . InformationBufferLength , & OidRequest - > DATA . QUERY_INFORMATION . BytesWritten , & OidRequest - > DATA . QUERY_INFORMATION . BytesNeeded ) ;
case NdisRequestSetInformation :
return TunOidSet ( MiniportAdapterContext , OidRequest - > DATA . SET_INFORMATION . Oid , OidRequest - > DATA . SET_INFORMATION . InformationBuffer , OidRequest - > DATA . SET_INFORMATION . InformationBufferLength , & OidRequest - > DATA . SET_INFORMATION . BytesRead , & OidRequest - > DATA . SET_INFORMATION . BytesNeeded ) ;
default :
return NDIS_STATUS_NOT_SUPPORTED ;
}
}
static MINIPORT_DIRECT_OID_REQUEST TunDirectOidRequest ;
_Use_decl_annotations_
static NDIS_STATUS TunDirectOidRequest ( NDIS_HANDLE MiniportAdapterContext , PNDIS_OID_REQUEST OidRequest )
{
switch ( OidRequest - > RequestType ) {
case NdisRequestQueryInformation :
case NdisRequestQueryStatistics :
case NdisRequestSetInformation :
return NDIS_STATUS_INVALID_OID ;
default :
return NDIS_STATUS_NOT_SUPPORTED ;
}
}
static MINIPORT_SEND_NET_BUFFER_LISTS TunSendNetBufferLists ;
_Use_decl_annotations_
static void TunSendNetBufferLists ( NDIS_HANDLE MiniportAdapterContext , NET_BUFFER_LIST * NetBufferLists , NDIS_PORT_NUMBER PortNumber , ULONG SendFlags )
{
TUN_CTX * ctx = ( TUN_CTX * ) MiniportAdapterContext ;
NDIS_STATUS status ;
2019-03-27 09:00:19 +01:00
if ( ! NT_SUCCESS ( status = TunCheckForPause ( ctx , 1 ) ) ) {
2019-03-08 22:33:15 +01:00
TunSetNBLStatus ( NetBufferLists , status ) ;
2019-03-27 09:00:19 +01:00
NdisMSendNetBufferListsComplete ( ctx - > MiniportAdapterHandle , NetBufferLists , SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0 ) ;
goto cleanup_TunCompletePause ;
2019-03-08 22:33:15 +01:00
}
2019-03-27 09:00:19 +01:00
TunQueueAppend ( ctx , NetBufferLists , TUN_QUEUE_MAX_NBLS ) ;
TunQueueProcess ( ctx ) ;
2019-03-08 22:33:15 +01:00
2019-03-27 09:00:19 +01:00
cleanup_TunCompletePause :
TunCompletePause ( ctx , 1 ) ;
2019-03-08 22:33:15 +01:00
}
DRIVER_INITIALIZE DriverEntry ;
_Use_decl_annotations_
NTSTATUS DriverEntry ( DRIVER_OBJECT * DriverObject , UNICODE_STRING * RegistryPath )
{
NDIS_MINIPORT_DRIVER_CHARACTERISTICS miniport = {
. Header = {
. Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS ,
. Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2 ,
. Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2
} ,
. MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION ,
. MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION ,
. MajorDriverVersion = WINTUN_VERSION_MAJ ,
. MinorDriverVersion = WINTUN_VERSION_MIN ,
. SetOptionsHandler = TunSetOptions ,
. InitializeHandlerEx = TunInitializeEx ,
. HaltHandlerEx = TunHaltEx ,
. UnloadHandler = TunDriverUnload ,
. PauseHandler = TunPause ,
. RestartHandler = TunRestart ,
. OidRequestHandler = TunOidRequest ,
. SendNetBufferListsHandler = TunSendNetBufferLists ,
. ReturnNetBufferListsHandler = TunReturnNetBufferLists ,
. CancelSendHandler = TunCancelSend ,
. DevicePnPEventNotifyHandler = TunDevicePnPEventNotify ,
. ShutdownHandlerEx = TunShutdownEx ,
. CancelOidRequestHandler = TunCancelOidRequest ,
. DirectOidRequestHandler = TunDirectOidRequest ,
. CancelDirectOidRequestHandler = TunCancelDirectOidRequest
} ;
2019-03-27 09:00:19 +01:00
return NdisMRegisterMiniportDriver ( DriverObject , RegistryPath , NULL , & miniport , & NdisMiniportDriverHandle ) ;
2019-03-08 22:33:15 +01:00
}