aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rozman <simon@rozman.si>2021-04-10 15:33:01 +0200
committerSimon Rozman <simon@rozman.si>2021-04-10 15:36:55 +0200
commiteebd6aea4f75551f6e847a1d4fff857450bac6e9 (patch)
treec179e44330d4889fa390d08330d94168637bca2a
parentapi: fix fallback log line printf template (diff)
downloadwintun-eebd6aea4f75551f6e847a1d4fff857450bac6e9.tar.xz
wintun-eebd6aea4f75551f6e847a1d4fff857450bac6e9.zip
Allow optional padding before and after layer 3 packets
To avoid additional packet memory allocation and copy when sending or receiving packets, this commit introduces additional available space before and after layer 3 IPv4 or IPv6 packet in the Wintun rings. Wintun will ignore data in those areas. Requested-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Simon Rozman <simon@rozman.si>
-rw-r--r--README.md21
-rw-r--r--api/exports.def1
-rw-r--r--api/session.c72
-rw-r--r--api/wintun.h28
-rw-r--r--driver/wintun.c130
-rw-r--r--example/example.c2
6 files changed, 193 insertions, 61 deletions
diff --git a/README.md b/README.md
index cf6214c..af15235 100644
--- a/README.md
+++ b/README.md
@@ -304,7 +304,7 @@ Sets logger callback function.
`WINTUN_SESSION_HANDLE WintunStartSession (WINTUN_ADAPTER_HANDLE Adapter, DWORD Capacity)`
-Starts Wintun session.
+Starts Wintun session. Use `WintunStartSessionWithPadding()` when packet leading and/or trailing space is required.
**Parameters**
@@ -315,6 +315,23 @@ Starts Wintun session.
Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
+#### WintunStartSessionWithPadding()
+
+`WINTUN_SESSION_HANDLE WintunStartSessionWithPadding (WINTUN_ADAPTER_HANDLE Adapter, DWORD Capacity, DWORD LeadPadding, DWORD TrailPadding)`
+
+Starts Wintun session with specific packet padding.
+
+**Parameters**
+
+- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+- *Capacity*: Rings capacity. Must be between WINTUN\_MIN\_RING\_CAPACITY and WINTUN\_MAX\_RING\_CAPACITY (incl.) Must be a power of two.
+- *LeadPadding*: Amount of extra space before packet data
+- *TrailPadding*: Amount of extra space after packet data
+
+**Returns**
+
+Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is NULL. To get extended error information, call GetLastError.
+
#### WintunEndSession()
`void WintunEndSession (WINTUN_SESSION_HANDLE Session)`
@@ -353,6 +370,7 @@ Retrieves one or packet. After the packet content is consumed, call WintunReleas
**Returns**
Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_HANDLE\_EOF Wintun adapter is terminating; ERROR\_NO\_MORE\_ITEMS Wintun buffer is exhausted; ERROR\_INVALID\_DATA Wintun buffer is corrupt
+When non-zero padding is used, requested leading amount of space is available before this pointer and trailing amount of space after this pointer+`PacketSize`. The driver will ignore padding data.
#### WintunReleaseReceivePacket()
@@ -379,6 +397,7 @@ Allocates memory for a packet to send. After the memory is filled with packet da
**Returns**
Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_HANDLE\_EOF Wintun adapter is terminating; ERROR\_BUFFER\_OVERFLOW Wintun buffer is full;
+When non-zero padding is used, requested leading amount of space is available before this pointer and trailing amount of space after this pointer+`PacketSize`. The driver will ignore padding data.
#### WintunSendPacket()
diff --git a/api/exports.def b/api/exports.def
index 3539ad8..b69117f 100644
--- a/api/exports.def
+++ b/api/exports.def
@@ -18,3 +18,4 @@ EXPORTS
WintunSetAdapterName
WintunSetLogger
WintunStartSession
+ WintunStartSessionWithPadding
diff --git a/api/session.c b/api/session.c
index ef65214..797850a 100644
--- a/api/session.c
+++ b/api/session.c
@@ -11,13 +11,17 @@
#include <Windows.h>
#pragma warning(disable : 4200) /* nonstandard: zero-sized array in struct/union */
+#pragma warning(disable : 4201) /* nonstandard extension used: nameless struct/union */
#define TUN_ALIGNMENT sizeof(ULONG)
#define TUN_ALIGN(Size) (((ULONG)(Size) + ((ULONG)TUN_ALIGNMENT - 1)) & ~((ULONG)TUN_ALIGNMENT - 1))
#define TUN_IS_ALIGNED(Size) (!((ULONG)(Size) & ((ULONG)TUN_ALIGNMENT - 1)))
-#define TUN_MAX_PACKET_SIZE TUN_ALIGN(sizeof(TUN_PACKET) + WINTUN_MAX_IP_PACKET_SIZE)
-#define TUN_RING_CAPACITY(Size) ((Size) - sizeof(TUN_RING) - (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
-#define TUN_RING_SIZE(Capacity) (sizeof(TUN_RING) + (Capacity) + (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
+#define TUN_MAX_PACKET_SIZE(LeadPadding, TrailPadding) \
+ TUN_ALIGN(sizeof(TUN_PACKET) + (LeadPadding) + WINTUN_MAX_IP_PACKET_SIZE + (TrailPadding))
+#define TUN_RING_CAPACITY(Size, LeadPadding, TrailPadding) \
+ ((Size) - sizeof(TUN_RING) - (TUN_MAX_PACKET_SIZE(LeadPadding, TrailPadding) - TUN_ALIGNMENT))
+#define TUN_RING_SIZE(Capacity, LeadPadding, TrailPadding) \
+ (sizeof(TUN_RING) + (Capacity) + (TUN_MAX_PACKET_SIZE(LeadPadding, TrailPadding) - TUN_ALIGNMENT))
#define TUN_RING_WRAP(Value, Capacity) ((Value) & (Capacity - 1))
#define LOCK_SPIN_COUNT 0x10000
#define TUN_PACKET_RELEASE ((DWORD)0x80000000)
@@ -43,8 +47,18 @@ typedef struct _TUN_REGISTER_RINGS
struct
{
ULONG RingSize;
- TUN_RING *Ring;
- HANDLE TailMoved;
+ ULONG LeadPadding;
+ ULONG TrailPadding;
+ union
+ {
+ TUN_RING *Ring;
+ ULONG64 RingAddress;
+ };
+ union
+ {
+ HANDLE TailMoved;
+ ULONG64 TailMovedHandle;
+ };
} Send, Receive;
} TUN_REGISTER_RINGS;
@@ -69,8 +83,11 @@ typedef struct _TUN_SESSION
HANDLE Handle;
} TUN_SESSION;
-_Return_type_success_(return != NULL) TUN_SESSION *WINAPI
- WintunStartSession(_In_ const WINTUN_ADAPTER *Adapter, _In_ DWORD Capacity)
+_Return_type_success_(return != NULL) TUN_SESSION *WINAPI WintunStartSessionWithPadding(
+ _In_ const WINTUN_ADAPTER *Adapter,
+ _In_ DWORD Capacity,
+ _In_ DWORD LeadPadding,
+ _In_ DWORD TrailPadding)
{
DWORD LastError;
TUN_SESSION *Session = Zalloc(sizeof(TUN_SESSION));
@@ -79,7 +96,7 @@ _Return_type_success_(return != NULL) TUN_SESSION *WINAPI
LastError = GetLastError();
goto out;
}
- const ULONG RingSize = TUN_RING_SIZE(Capacity);
+ const ULONG RingSize = TUN_RING_SIZE(Capacity, LeadPadding, TrailPadding);
BYTE *AllocatedRegion = VirtualAlloc(0, (size_t)RingSize * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!AllocatedRegion)
{
@@ -92,6 +109,8 @@ _Return_type_success_(return != NULL) TUN_SESSION *WINAPI
goto cleanupAllocatedRegion;
}
Session->Descriptor.Send.RingSize = RingSize;
+ Session->Descriptor.Send.LeadPadding = LeadPadding;
+ Session->Descriptor.Send.TrailPadding = TrailPadding;
Session->Descriptor.Send.Ring = (TUN_RING *)AllocatedRegion;
Session->Descriptor.Send.TailMoved = CreateEventW(&SecurityAttributes, FALSE, FALSE, NULL);
if (!Session->Descriptor.Send.TailMoved)
@@ -101,6 +120,8 @@ _Return_type_success_(return != NULL) TUN_SESSION *WINAPI
}
Session->Descriptor.Receive.RingSize = RingSize;
+ Session->Descriptor.Receive.LeadPadding = LeadPadding;
+ Session->Descriptor.Receive.TrailPadding = TrailPadding;
Session->Descriptor.Receive.Ring = (TUN_RING *)(AllocatedRegion + RingSize);
Session->Descriptor.Receive.TailMoved = CreateEventW(&SecurityAttributes, FALSE, FALSE, NULL);
if (!Session->Descriptor.Receive.TailMoved)
@@ -151,6 +172,12 @@ out:
return NULL;
}
+_Return_type_success_(return != NULL) TUN_SESSION *WINAPI
+ WintunStartSession(_In_ const WINTUN_ADAPTER *Adapter, _In_ DWORD Capacity)
+{
+ return WintunStartSessionWithPadding(Adapter, Capacity, 0, 0);
+}
+
void WINAPI
WintunEndSession(_In_ TUN_SESSION *Session)
{
@@ -203,14 +230,16 @@ _Return_type_success_(return != NULL) _Ret_bytecount_(*PacketSize) BYTE *WINAPI
LastError = ERROR_INVALID_DATA;
goto cleanup;
}
- const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + BuffPacket->Size);
+ const ULONG AlignedPacketSize = TUN_ALIGN(
+ sizeof(TUN_PACKET) + Session->Descriptor.Send.LeadPadding + BuffPacket->Size +
+ Session->Descriptor.Send.TrailPadding);
if (AlignedPacketSize > BuffContent)
{
LastError = ERROR_INVALID_DATA;
goto cleanup;
}
*PacketSize = BuffPacket->Size;
- BYTE *Packet = BuffPacket->Data;
+ BYTE *Packet = BuffPacket->Data + Session->Descriptor.Send.LeadPadding;
Session->Send.Head = TUN_RING_WRAP(Session->Send.Head + AlignedPacketSize, Session->Capacity);
Session->Send.PacketsToRelease++;
LeaveCriticalSection(&Session->Send.Lock);
@@ -225,14 +254,17 @@ void WINAPI
WintunReleaseReceivePacket(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet)
{
EnterCriticalSection(&Session->Send.Lock);
- TUN_PACKET *ReleasedBuffPacket = (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data));
+ TUN_PACKET *ReleasedBuffPacket =
+ (TUN_PACKET *)(Packet - Session->Descriptor.Send.LeadPadding - offsetof(TUN_PACKET, Data));
ReleasedBuffPacket->Size |= TUN_PACKET_RELEASE;
while (Session->Send.PacketsToRelease)
{
const TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Send.Ring->Data[Session->Send.HeadRelease];
if ((BuffPacket->Size & TUN_PACKET_RELEASE) == 0)
break;
- const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + (BuffPacket->Size & ~TUN_PACKET_RELEASE));
+ const ULONG AlignedPacketSize = TUN_ALIGN(
+ sizeof(TUN_PACKET) + Session->Descriptor.Send.LeadPadding + (BuffPacket->Size & ~TUN_PACKET_RELEASE) +
+ Session->Descriptor.Send.TrailPadding);
Session->Send.HeadRelease = TUN_RING_WRAP(Session->Send.HeadRelease + AlignedPacketSize, Session->Capacity);
Session->Send.PacketsToRelease--;
}
@@ -250,7 +282,9 @@ _Return_type_success_(return != NULL) _Ret_bytecount_(PacketSize) BYTE *WINAPI
LastError = ERROR_HANDLE_EOF;
goto cleanup;
}
- const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize);
+ const ULONG AlignedPacketSize = TUN_ALIGN(
+ sizeof(TUN_PACKET) + Session->Descriptor.Receive.LeadPadding + PacketSize +
+ Session->Descriptor.Receive.TrailPadding);
const ULONG BuffHead = ReadULongAcquire(&Session->Descriptor.Receive.Ring->Head);
if (BuffHead >= Session->Capacity)
{
@@ -265,7 +299,7 @@ _Return_type_success_(return != NULL) _Ret_bytecount_(PacketSize) BYTE *WINAPI
}
TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[Session->Receive.Tail];
BuffPacket->Size = PacketSize | TUN_PACKET_RELEASE;
- BYTE *Packet = BuffPacket->Data;
+ BYTE *Packet = BuffPacket->Data + Session->Descriptor.Receive.LeadPadding;
Session->Receive.Tail = TUN_RING_WRAP(Session->Receive.Tail + AlignedPacketSize, Session->Capacity);
Session->Receive.PacketsToRelease++;
LeaveCriticalSection(&Session->Receive.Lock);
@@ -280,7 +314,8 @@ void WINAPI
WintunSendPacket(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet)
{
EnterCriticalSection(&Session->Receive.Lock);
- TUN_PACKET *ReleasedBuffPacket = (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data));
+ TUN_PACKET *ReleasedBuffPacket =
+ (TUN_PACKET *)(Packet - Session->Descriptor.Receive.LeadPadding - offsetof(TUN_PACKET, Data));
ReleasedBuffPacket->Size &= ~TUN_PACKET_RELEASE;
while (Session->Receive.PacketsToRelease)
{
@@ -288,12 +323,15 @@ WintunSendPacket(_In_ TUN_SESSION *Session, _In_ const BYTE *Packet)
(TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[Session->Receive.TailRelease];
if (BuffPacket->Size & TUN_PACKET_RELEASE)
break;
- const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + BuffPacket->Size);
+ const ULONG AlignedPacketSize = TUN_ALIGN(
+ sizeof(TUN_PACKET) + Session->Descriptor.Receive.LeadPadding + BuffPacket->Size +
+ Session->Descriptor.Receive.TrailPadding);
Session->Receive.TailRelease =
TUN_RING_WRAP(Session->Receive.TailRelease + AlignedPacketSize, Session->Capacity);
Session->Receive.PacketsToRelease--;
}
- if (Session->Descriptor.Receive.Ring->Tail != Session->Receive.TailRelease) {
+ if (Session->Descriptor.Receive.Ring->Tail != Session->Receive.TailRelease)
+ {
WriteULongRelease(&Session->Descriptor.Receive.Ring->Tail, Session->Receive.TailRelease);
if (ReadAcquire(&Session->Descriptor.Receive.Ring->Alertable))
SetEvent(Session->Descriptor.Receive.TailMoved);
diff --git a/api/wintun.h b/api/wintun.h
index 2b03d33..b90ba82 100644
--- a/api/wintun.h
+++ b/api/wintun.h
@@ -63,7 +63,6 @@ typedef _Return_type_success_(return != NULL) WINTUN_ADAPTER_HANDLE(WINAPI *WINT
typedef _Return_type_success_(return != NULL)
WINTUN_ADAPTER_HANDLE(WINAPI *WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ const WCHAR *Pool, _In_z_ const WCHAR *Name);
-
/**
* Deletes a Wintun adapter.
*
@@ -220,7 +219,7 @@ typedef void(WINAPI *WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogg
typedef void *WINTUN_SESSION_HANDLE;
/**
- * Starts Wintun session.
+ * Starts Wintun session. Use WintunStartSessionWithPadding() when packet leading and/or trailing space is required.
*
* @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
*
@@ -234,6 +233,27 @@ typedef _Return_type_success_(return != NULL)
WINTUN_SESSION_HANDLE(WINAPI *WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
/**
+ * Starts Wintun session with specific packet padding.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+ *
+ * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
+ * Must be a power of two.
+ *
+ * @param LeadPadding Amount of extra space before packet data
+ *
+ * @param TrailPadding Amount of extra space after packet data
+ *
+ * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
+ * NULL. To get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != NULL) WINTUN_SESSION_HANDLE(WINAPI *WINTUN_START_SESSION_WITH_PADDING_FUNC)(
+ _In_ WINTUN_ADAPTER_HANDLE Adapter,
+ _In_ DWORD Capacity,
+ _In_ DWORD LeadPadding,
+ _In_ DWORD TrailPadding);
+
+/**
* Ends Wintun session.
*
* @param Session Wintun session handle obtained with WintunStartSession
@@ -271,6 +291,8 @@ typedef HANDLE(WINAPI *WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HAND
* ERROR_HANDLE_EOF Wintun adapter is terminating;
* ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
* ERROR_INVALID_DATA Wintun buffer is corrupt
+ * When non-zero padding is used, requested leading amount of space is available before this pointer and
+ * trailing amount of space after this pointer+PacketSize. The driver will ignore padding data.
*/
typedef _Return_type_success_(return != NULL) _Ret_bytecount_(*PacketSize) BYTE *(
WINAPI *WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
@@ -298,6 +320,8 @@ typedef void(WINAPI *WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HAN
* following:
* ERROR_HANDLE_EOF Wintun adapter is terminating;
* ERROR_BUFFER_OVERFLOW Wintun buffer is full;
+ * When non-zero padding is used, requested leading amount of space is available before this pointer and
+ * trailing amount of space after this pointer+PacketSize. The driver will ignore padding data.
*/
typedef _Return_type_success_(return != NULL) _Ret_bytecount_(PacketSize) BYTE *(
WINAPI *WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
diff --git a/driver/wintun.c b/driver/wintun.c
index 931b7e8..4224642 100644
--- a/driver/wintun.c
+++ b/driver/wintun.c
@@ -12,6 +12,7 @@
#pragma warning(disable : 4100) /* unreferenced formal parameter */
#pragma warning(disable : 4200) /* nonstandard: zero-sized array in struct/union */
+#pragma warning(disable : 4201) /* nonstandard extension used: nameless struct/union */
#pragma warning(disable : 4204) /* nonstandard: non-constant aggregate initializer */
#pragma warning(disable : 4221) /* nonstandard: cannot be initialized using address of automatic variable */
#pragma warning(disable : 6320) /* exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER */
@@ -30,13 +31,15 @@
/* Maximum IP packet size */
#define TUN_MAX_IP_PACKET_SIZE 0xFFFF
/* Maximum packet size */
-#define TUN_MAX_PACKET_SIZE TUN_ALIGN(sizeof(TUN_PACKET) + TUN_MAX_IP_PACKET_SIZE)
+#define TUN_MAX_PACKET_SIZE(LeadPadding, TrailPadding) \
+ TUN_ALIGN(sizeof(TUN_PACKET) + (LeadPadding) + TUN_MAX_IP_PACKET_SIZE + (TrailPadding))
/* Minimum ring capacity. */
#define TUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
/* Maximum ring capacity. */
#define TUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
/* Calculates ring capacity */
-#define TUN_RING_CAPACITY(Size) ((Size) - sizeof(TUN_RING) - (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
+#define TUN_RING_CAPACITY(Size, LeadPadding, TrailPadding) \
+ ((Size) - sizeof(TUN_RING) - (TUN_MAX_PACKET_SIZE(LeadPadding, TrailPadding) - TUN_ALIGNMENT))
/* Calculates ring offset modulo capacity */
#define TUN_RING_WRAP(Value, Capacity) ((Value) & (Capacity - 1))
@@ -60,8 +63,7 @@ typedef struct _TUN_PACKET
ULONG Size;
/* Packet data */
- UCHAR _Field_size_bytes_(Size)
- Data[];
+ UCHAR Data[];
} TUN_PACKET;
typedef struct _TUN_RING
@@ -82,42 +84,64 @@ typedef struct _TUN_RING
UCHAR Data[];
} TUN_RING;
-typedef struct _TUN_REGISTER_RINGS
+/* Obsolete: See TUN_REGISTER_RINGS */
+typedef struct _TUN_REGISTER_RINGS_V1
{
struct
{
- /* Size of the ring */
ULONG RingSize;
-
- /* Pointer to client allocated ring */
TUN_RING *Ring;
-
- /* On send: An event created by the client the Wintun signals after it moves the Tail member of the send ring.
- * On receive: An event created by the client the client will signal when it moves the Tail member of
- * the receive ring if receive ring is alertable. */
HANDLE TailMoved;
} Send, Receive;
-} TUN_REGISTER_RINGS;
+} TUN_REGISTER_RINGS_V1;
#ifdef _WIN64
-typedef struct _TUN_REGISTER_RINGS_32
+/* Obsolete: See TUN_REGISTER_RINGS */
+typedef struct _TUN_REGISTER_RINGS_V1_32
{
struct
{
- /* Size of the ring */
ULONG RingSize;
-
- /* 32-bit addres of client allocated ring */
ULONG Ring;
-
- /* On send: An event created by the client the Wintun signals after it moves the Tail member of the send ring.
- * On receive: An event created by the client the client will signal when it moves the Tail member of
- * the receive ring if receive ring is alertable. */
ULONG TailMoved;
} Send, Receive;
-} TUN_REGISTER_RINGS_32;
+} TUN_REGISTER_RINGS_V1_32;
#endif
+typedef struct _TUN_REGISTER_RINGS_V2
+{
+ struct
+ {
+ /* Size of the ring */
+ ULONG RingSize;
+
+ /* Extra space before each packet data */
+ ULONG LeadPadding;
+
+ /* Extra space after each packet data */
+ ULONG TrailPadding;
+
+ union
+ {
+ /* Address of client allocated ring */
+ TUN_RING *Ring;
+ ULONG64 RingAddress;
+ };
+
+ union
+ {
+ /* On send: An event handle created by the client the Wintun signals after it moves the Tail member of the
+ * send ring.
+ * On receive: An event handle created by the client the client will signal when it moves the Tail member
+ * of the receive ring if receive ring is alertable. */
+ HANDLE TailMoved;
+ ULONG64 TailMovedHandle;
+ };
+ } Send, Receive;
+} TUN_REGISTER_RINGS_V2;
+
+#define TUN_REGISTER_RINGS TUN_REGISTER_RINGS_V2
+
/* Register rings hosted by the client.
* The lpInBuffer and nInBufferSize parameters of DeviceIoControl() must point to an TUN_REGISTER_RINGS struct.
* Client must wait for this IOCTL to finish before adding packets to the ring. */
@@ -151,6 +175,8 @@ typedef struct _TUN_CTX
MDL *Mdl;
TUN_RING *Ring;
ULONG Capacity;
+ ULONG LeadPadding;
+ ULONG TrailPadding;
KEVENT *TailMoved;
KSPIN_LOCK Lock;
ULONG RingTail;
@@ -165,6 +191,8 @@ typedef struct _TUN_CTX
MDL *Mdl;
TUN_RING *Ring;
ULONG Capacity;
+ ULONG LeadPadding;
+ ULONG TrailPadding;
KEVENT *TailMoved;
HANDLE Thread;
KSPIN_LOCK Lock;
@@ -251,6 +279,7 @@ TunSendNetBufferLists(
{
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
LONG64 SentPacketsCount = 0, SentPacketsSize = 0, ErrorPacketsCount = 0, DiscardedPacketsCount = 0;
+ const UINT Padding = Ctx->Device.Send.LeadPadding + Ctx->Device.Send.TrailPadding;
/* Measure NBLs. */
ULONG PacketsCount = 0, RequiredRingSpace = 0;
@@ -262,7 +291,7 @@ TunSendNetBufferLists(
UINT PacketSize = NET_BUFFER_DATA_LENGTH(Nb);
if (PacketSize > TUN_MAX_IP_PACKET_SIZE)
continue; /* The same condition holds down below, where we `goto skipPacket`. */
- RequiredRingSpace += TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize);
+ RequiredRingSpace += TUN_ALIGN(sizeof(TUN_PACKET) + Padding + PacketSize);
}
}
@@ -309,24 +338,25 @@ TunSendNetBufferLists(
TUN_PACKET *Packet = (TUN_PACKET *)(Ring->Data + RingTail);
Packet->Size = PacketSize;
- void *NbData = NdisGetDataBuffer(Nb, PacketSize, Packet->Data, 1, 0);
+ UCHAR *PacketData = Packet->Data + Ctx->Device.Send.LeadPadding;
+ void *NbData = NdisGetDataBuffer(Nb, PacketSize, PacketData, 1, 0);
if (!NbData)
{
/* The space for the packet has already been allocated in the ring. Write a zero-packet rather than
* fixing the gap in the ring. */
- NdisZeroMemory(Packet->Data, PacketSize);
+ NdisZeroMemory(PacketData, PacketSize);
DiscardedPacketsCount++;
NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_FAILURE;
}
else
{
- if (NbData != Packet->Data)
- NdisMoveMemory(Packet->Data, NbData, PacketSize);
+ if (NbData != PacketData)
+ NdisMoveMemory(PacketData, NbData, PacketSize);
SentPacketsCount++;
SentPacketsSize += PacketSize;
}
- RingTail = TUN_RING_WRAP(RingTail + TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize), RingCapacity);
+ RingTail = TUN_RING_WRAP(RingTail + TUN_ALIGN(sizeof(TUN_PACKET) + Padding + PacketSize), RingCapacity);
continue;
skipPacket:
@@ -441,6 +471,7 @@ TunProcessReceiveData(_Inout_ TUN_CTX *Ctx)
TUN_RING *Ring = Ctx->Device.Receive.Ring;
ULONG RingCapacity = Ctx->Device.Receive.Capacity;
+ const UINT Padding = Ctx->Device.Receive.LeadPadding + Ctx->Device.Receive.TrailPadding;
LARGE_INTEGER Frequency;
KeQueryPerformanceCounter(&Frequency);
ULONG64 SpinMax = Frequency.QuadPart / 1000 / 10; /* 1/10 ms */
@@ -497,18 +528,19 @@ TunProcessReceiveData(_Inout_ TUN_CTX *Ctx)
if (PacketSize > TUN_MAX_IP_PACKET_SIZE)
break;
- ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize);
+ ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + Padding + PacketSize);
if (AlignedPacketSize > RingContent)
break;
ULONG NblFlags;
USHORT NblProto;
- if (PacketSize >= 20 && Packet->Data[0] >> 4 == 4)
+ const UCHAR *PacketData = Packet->Data + Ctx->Device.Receive.LeadPadding;
+ if (PacketSize >= 20 && PacketData[0] >> 4 == 4)
{
NblFlags = NDIS_NBL_FLAGS_IS_IPV4;
NblProto = HTONS(NDIS_ETH_TYPE_IPV4);
}
- else if (PacketSize >= 40 && Packet->Data[0] >> 4 == 6)
+ else if (PacketSize >= 40 && PacketData[0] >> 4 == 6)
{
NblFlags = NDIS_NBL_FLAGS_IS_IPV6;
NblProto = HTONS(NDIS_ETH_TYPE_IPV6);
@@ -523,7 +555,7 @@ TunProcessReceiveData(_Inout_ TUN_CTX *Ctx)
IoBuildPartialMdl(
Ctx->Device.Receive.Mdl,
Mdl,
- (UCHAR *)MmGetMdlVirtualAddress(Ctx->Device.Receive.Mdl) + (ULONG)(Packet->Data - (UCHAR *)Ring),
+ (UCHAR *)MmGetMdlVirtualAddress(Ctx->Device.Receive.Mdl) + (ULONG)(PacketData - (UCHAR *)Ring),
PacketSize);
NET_BUFFER_LIST *Nbl = NdisAllocateNetBufferAndNetBufferList(Ctx->NblPool, 0, 0, Mdl, 0, PacketSize);
if (!Nbl)
@@ -595,14 +627,25 @@ TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
goto cleanupMutex;
Ctx->Device.OwningFileObject = Stack->FileObject;
- TUN_REGISTER_RINGS Rrb;
+ TUN_REGISTER_RINGS Rrb = { 0 };
if (Stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(Rrb))
NdisMoveMemory(&Rrb, Irp->AssociatedIrp.SystemBuffer, sizeof(Rrb));
+ else if (Stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(TUN_REGISTER_RINGS_V1))
+ {
+ TUN_REGISTER_RINGS_V1 *RrbV1 = Irp->AssociatedIrp.SystemBuffer;
+ Rrb.Send.RingSize = RrbV1->Send.RingSize;
+ Rrb.Send.Ring = RrbV1->Send.Ring;
+ Rrb.Send.TailMoved = RrbV1->Send.TailMoved;
+ Rrb.Receive.RingSize = RrbV1->Receive.RingSize;
+ Rrb.Receive.Ring = RrbV1->Receive.Ring;
+ Rrb.Receive.TailMoved = RrbV1->Receive.TailMoved;
+ }
#ifdef _WIN64
else if (
- IoIs32bitProcess(Irp) && Stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(TUN_REGISTER_RINGS_32))
+ IoIs32bitProcess(Irp) &&
+ Stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(TUN_REGISTER_RINGS_V1_32))
{
- TUN_REGISTER_RINGS_32 *Rrb32 = Irp->AssociatedIrp.SystemBuffer;
+ TUN_REGISTER_RINGS_V1_32 *Rrb32 = Irp->AssociatedIrp.SystemBuffer;
Rrb.Send.RingSize = Rrb32->Send.RingSize;
Rrb.Send.Ring = (TUN_RING *)Rrb32->Send.Ring;
Rrb.Send.TailMoved = (HANDLE)Rrb32->Send.TailMoved;
@@ -617,15 +660,17 @@ TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
goto cleanupResetOwner;
}
- Ctx->Device.Send.Capacity = TUN_RING_CAPACITY(Rrb.Send.RingSize);
+ Ctx->Device.Send.Capacity = TUN_RING_CAPACITY(Rrb.Send.RingSize, Rrb.Send.LeadPadding, Rrb.Send.TrailPadding);
if (Status = STATUS_INVALID_PARAMETER,
(Ctx->Device.Send.Capacity < TUN_MIN_RING_CAPACITY || Ctx->Device.Send.Capacity > TUN_MAX_RING_CAPACITY ||
!IS_POW2(Ctx->Device.Send.Capacity) || !Rrb.Send.TailMoved || !Rrb.Send.Ring))
goto cleanupResetOwner;
+ Ctx->Device.Send.LeadPadding = Rrb.Send.LeadPadding;
+ Ctx->Device.Send.TrailPadding = Rrb.Send.TrailPadding;
if (!NT_SUCCESS(
Status = ObReferenceObjectByHandle(
- Rrb.Send.TailMoved,
+ (HANDLE)Rrb.Send.TailMoved,
/* We will not wait on send ring tail moved event. */
EVENT_MODIFY_STATE,
*ExEventObjectType,
@@ -634,7 +679,7 @@ TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
NULL)))
goto cleanupResetOwner;
- Ctx->Device.Send.Mdl = IoAllocateMdl(Rrb.Send.Ring, Rrb.Send.RingSize, FALSE, FALSE, NULL);
+ Ctx->Device.Send.Mdl = IoAllocateMdl((PVOID)Rrb.Send.Ring, Rrb.Send.RingSize, FALSE, FALSE, NULL);
if (Status = STATUS_INSUFFICIENT_RESOURCES, !Ctx->Device.Send.Mdl)
goto cleanupSendTailMoved;
try
@@ -653,15 +698,18 @@ TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
if (Status = STATUS_INVALID_PARAMETER, Ctx->Device.Send.RingTail >= Ctx->Device.Send.Capacity)
goto cleanupSendUnlockPages;
- Ctx->Device.Receive.Capacity = TUN_RING_CAPACITY(Rrb.Receive.RingSize);
+ Ctx->Device.Receive.Capacity =
+ TUN_RING_CAPACITY(Rrb.Receive.RingSize, Rrb.Receive.LeadPadding, Rrb.Receive.TrailPadding);
if (Status = STATUS_INVALID_PARAMETER,
(Ctx->Device.Receive.Capacity < TUN_MIN_RING_CAPACITY || Ctx->Device.Receive.Capacity > TUN_MAX_RING_CAPACITY ||
!IS_POW2(Ctx->Device.Receive.Capacity) || !Rrb.Receive.TailMoved || !Rrb.Receive.Ring))
goto cleanupSendUnlockPages;
+ Ctx->Device.Receive.LeadPadding = Rrb.Receive.LeadPadding;
+ Ctx->Device.Receive.TrailPadding = Rrb.Receive.TrailPadding;
if (!NT_SUCCESS(
Status = ObReferenceObjectByHandle(
- Rrb.Receive.TailMoved,
+ (HANDLE)Rrb.Receive.TailMoved,
/* We need to clear receive ring TailMoved event on transition to non-alertable state. */
SYNCHRONIZE | EVENT_MODIFY_STATE,
*ExEventObjectType,
@@ -670,7 +718,7 @@ TunRegisterBuffers(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
NULL)))
goto cleanupSendUnlockPages;
- Ctx->Device.Receive.Mdl = IoAllocateMdl(Rrb.Receive.Ring, Rrb.Receive.RingSize, FALSE, FALSE, NULL);
+ Ctx->Device.Receive.Mdl = IoAllocateMdl((PVOID)Rrb.Receive.Ring, Rrb.Receive.RingSize, FALSE, FALSE, NULL);
if (Status = STATUS_INSUFFICIENT_RESOURCES, !Ctx->Device.Receive.Mdl)
goto cleanupReceiveTailMoved;
try
diff --git a/example/example.c b/example/example.c
index 9e5f2e8..004b19f 100644
--- a/example/example.c
+++ b/example/example.c
@@ -24,6 +24,7 @@ static WINTUN_SET_ADAPTER_NAME_FUNC WintunSetAdapterName;
static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
static WINTUN_SET_LOGGER_FUNC WintunSetLogger;
static WINTUN_START_SESSION_FUNC WintunStartSession;
+static WINTUN_START_SESSION_WITH_PADDING_FUNC WintunStartSessionWithPadding;
static WINTUN_END_SESSION_FUNC WintunEndSession;
static WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
static WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
@@ -47,6 +48,7 @@ InitializeWintun(void)
X(WintunSetAdapterName, WINTUN_SET_ADAPTER_NAME_FUNC) ||
X(WintunGetRunningDriverVersion, WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC) ||
X(WintunSetLogger, WINTUN_SET_LOGGER_FUNC) || X(WintunStartSession, WINTUN_START_SESSION_FUNC) ||
+ X(WintunStartSessionWithPadding, WINTUN_START_SESSION_WITH_PADDING_FUNC) ||
X(WintunEndSession, WINTUN_END_SESSION_FUNC) || X(WintunGetReadWaitEvent, WINTUN_GET_READ_WAIT_EVENT_FUNC) ||
X(WintunReceivePacket, WINTUN_RECEIVE_PACKET_FUNC) ||
X(WintunReleaseReceivePacket, WINTUN_RELEASE_RECEIVE_PACKET_FUNC) ||