diff options
author | Simon Rozman <simon@rozman.si> | 2021-04-10 15:33:01 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-04-13 15:56:04 -0600 |
commit | 564a883e23fff00140c10271f08b37dc649d57c8 (patch) | |
tree | 7b7ce433ed469541e68489cd9429b00f04e370b0 /api | |
parent | version: bump (diff) | |
download | wintun-564a883e23fff00140c10271f08b37dc649d57c8.tar.xz wintun-564a883e23fff00140c10271f08b37dc649d57c8.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>
Diffstat (limited to 'api')
-rw-r--r-- | api/exports.def | 1 | ||||
-rw-r--r-- | api/session.c | 72 | ||||
-rw-r--r-- | api/wintun.h | 28 |
3 files changed, 82 insertions, 19 deletions
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); |