aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/adapter.h7
-rw-r--r--api/api.vcxproj2
-rw-r--r--api/api.vcxproj.filters6
-rw-r--r--api/atomic.h26
-rw-r--r--api/exports.def6
-rw-r--r--api/pch.h1
-rw-r--r--api/session.c213
-rw-r--r--api/wintun.h123
8 files changed, 383 insertions, 1 deletions
diff --git a/api/adapter.h b/api/adapter.h
index bacaf8b..7284002 100644
--- a/api/adapter.h
+++ b/api/adapter.h
@@ -97,6 +97,12 @@ void WINAPI
WintunFreeAdapter(_In_ WINTUN_ADAPTER *Adapter);
/**
+ * @copydoc WINTUN_GET_ADAPTER_DEVICE_OBJECT_FUNC
+ */
+WINTUN_STATUS WINAPI
+WintunGetAdapterDeviceObject(_In_ const WINTUN_ADAPTER *Adapter, _Out_ HANDLE *Handle);
+
+/**
* @copydoc WINTUN_CREATE_ADAPTER_FUNC
*/
WINTUN_STATUS WINAPI
@@ -112,4 +118,3 @@ WintunCreateAdapter(
*/
WINTUN_STATUS WINAPI
WintunDeleteAdapter(_In_ const WINTUN_ADAPTER *Adapter, _Inout_ BOOL *RebootRequired);
-
diff --git a/api/api.vcxproj b/api/api.vcxproj
index 17756e8..ed0dea6 100644
--- a/api/api.vcxproj
+++ b/api/api.vcxproj
@@ -195,6 +195,7 @@
<ItemGroup>
<ClInclude Include="api.h" />
<ClInclude Include="adapter.h" />
+ <ClInclude Include="atomic.h" />
<ClInclude Include="driver.h" />
<ClInclude Include="logger.h" />
<ClInclude Include="namespace.h" />
@@ -216,6 +217,7 @@
</ClCompile>
<ClCompile Include="registry.c" />
<ClCompile Include="resource.c" />
+ <ClCompile Include="session.c" />
<ClCompile Include="rundll32.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/api/api.vcxproj.filters b/api/api.vcxproj.filters
index 682b11f..cda6b16 100644
--- a/api/api.vcxproj.filters
+++ b/api/api.vcxproj.filters
@@ -55,6 +55,9 @@
<ClInclude Include="wintun.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="atomic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="api.c">
@@ -87,5 +90,8 @@
<ClCompile Include="adapter.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="session.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/api/atomic.h b/api/atomic.h
new file mode 100644
index 0000000..011e53b
--- /dev/null
+++ b/api/atomic.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <Windows.h>
+
+static __forceinline VOID
+InterlockedSetU(_Inout_ _Interlocked_operand_ ULONG volatile *Target, _In_ ULONG Value)
+{
+ *Target = Value;
+}
+
+static __forceinline LONG
+InterlockedGet(_In_ _Interlocked_operand_ LONG volatile *Value)
+{
+ return *Value;
+}
+
+static __forceinline ULONG
+InterlockedGetU(_In_ _Interlocked_operand_ ULONG volatile *Value)
+{
+ return *Value;
+}
diff --git a/api/exports.def b/api/exports.def
index 4305f47..85e4453 100644
--- a/api/exports.def
+++ b/api/exports.def
@@ -1,6 +1,7 @@
EXPORTS
WintunCreateAdapter
WintunDeleteAdapter
+ WintunEndSession
WintunEnumAdapters
WintunFreeAdapter
WintunGetAdapter
@@ -9,5 +10,10 @@ EXPORTS
WintunGetAdapterLUID
WintunGetAdapterName
WintunGetVersion
+ WintunIsPacketAvailable
+ WintunReceivePackets
+ WintunSendPackets
WintunSetAdapterName
WintunSetLogger
+ WintunStartSession
+ WintunWaitForPacket
diff --git a/api/pch.h b/api/pch.h
index 2c89a90..a4320e4 100644
--- a/api/pch.h
+++ b/api/pch.h
@@ -6,6 +6,7 @@
#pragma once
#include "adapter.h"
+#include "atomic.h"
#include "api.h"
#include "driver.h"
#include "logger.h"
diff --git a/api/session.c b/api/session.c
new file mode 100644
index 0000000..5822cb6
--- /dev/null
+++ b/api/session.c
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "pch.h"
+
+#pragma warning(disable : 4200) /* nonstandard: zero-sized array in 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_RING_WRAP(Value, Capacity) ((Value) & (Capacity - 1))
+
+typedef struct _TUN_PACKET
+{
+ ULONG Size;
+ UCHAR _Field_size_bytes_(Size)
+ Data[];
+} TUN_PACKET;
+
+typedef struct _TUN_RING
+{
+ volatile ULONG Head;
+ volatile ULONG Tail;
+ volatile LONG Alertable;
+ UCHAR Data[];
+} TUN_RING;
+
+#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+
+typedef struct _TUN_REGISTER_RINGS
+{
+ struct
+ {
+ ULONG RingSize;
+ TUN_RING *Ring;
+ HANDLE TailMoved;
+ } Send, Receive;
+} TUN_REGISTER_RINGS;
+
+typedef struct _TUN_SESSION
+{
+ ULONG Capacity;
+ TUN_REGISTER_RINGS Descriptor;
+ HANDLE Handle;
+} TUN_SESSION;
+
+WINTUN_STATUS WINAPI
+WintunStartSession(_In_ const WINTUN_ADAPTER *Adapter, _In_ DWORD Capacity, _Out_ TUN_SESSION **Session)
+{
+ HANDLE Heap = GetProcessHeap();
+ *Session = HeapAlloc(Heap, 0, sizeof(TUN_SESSION));
+ if (!*Session)
+ return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
+ const ULONG RingSize = TUN_RING_SIZE(Capacity);
+ DWORD Result;
+ BYTE *AllocatedRegion = VirtualAlloc(0, (size_t)RingSize * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (!AllocatedRegion)
+ {
+ Result = LOG_LAST_ERROR(L"Failed to allocate ring memory");
+ goto cleanupRings;
+ }
+ (*Session)->Descriptor.Send.RingSize = RingSize;
+ (*Session)->Descriptor.Send.Ring = (TUN_RING *)AllocatedRegion;
+ (*Session)->Descriptor.Send.TailMoved = CreateEventW(SecurityAttributes, FALSE, FALSE, NULL);
+ if (!(*Session)->Descriptor.Send.TailMoved)
+ {
+ Result = LOG_LAST_ERROR(L"Failed to create send event");
+ goto cleanupAllocatedRegion;
+ }
+
+ (*Session)->Descriptor.Receive.RingSize = RingSize;
+ (*Session)->Descriptor.Receive.Ring = (TUN_RING *)(AllocatedRegion + RingSize);
+ (*Session)->Descriptor.Receive.TailMoved = CreateEvent(SecurityAttributes, FALSE, FALSE, NULL);
+ if (!(*Session)->Descriptor.Receive.TailMoved)
+ {
+ Result = LOG_LAST_ERROR(L"Failed to create receive event");
+ goto cleanupSendTailMoved;
+ }
+
+ Result = WintunGetAdapterDeviceObject(Adapter, &(*Session)->Handle);
+ if (Result != ERROR_SUCCESS)
+ {
+ LOG(WINTUN_LOG_ERR, L"Failed to open adapter device object");
+ goto cleanupReceiveTailMoved;
+ }
+ DWORD BytesReturned;
+ if (!DeviceIoControl(
+ (*Session)->Handle,
+ TUN_IOCTL_REGISTER_RINGS,
+ &(*Session)->Descriptor,
+ sizeof(TUN_REGISTER_RINGS),
+ NULL,
+ 0,
+ &BytesReturned,
+ NULL))
+ {
+ Result = LOG_LAST_ERROR(L"Failed to perform ioctl");
+ goto cleanupHandle;
+ }
+ (*Session)->Capacity = Capacity;
+ return ERROR_SUCCESS;
+cleanupHandle:
+ CloseHandle((*Session)->Handle);
+cleanupReceiveTailMoved:
+ CloseHandle((*Session)->Descriptor.Receive.TailMoved);
+cleanupSendTailMoved:
+ CloseHandle((*Session)->Descriptor.Send.TailMoved);
+cleanupAllocatedRegion:
+ VirtualFree(AllocatedRegion, 0, MEM_RELEASE);
+cleanupRings:
+ HeapFree(Heap, 0, *Session);
+ *Session = NULL;
+ return Result;
+}
+
+void WINAPI
+WintunEndSession(_In_ TUN_SESSION *Session)
+{
+ SetEvent(Session->Descriptor.Send.TailMoved); // wake the reader if it's sleeping
+ CloseHandle(Session->Handle);
+ CloseHandle(Session->Descriptor.Send.TailMoved);
+ CloseHandle(Session->Descriptor.Receive.TailMoved);
+ VirtualFree(Session->Descriptor.Send.Ring, 0, MEM_RELEASE);
+ HeapFree(GetProcessHeap(), 0, Session);
+}
+
+BOOL WINAPI
+WintunIsPacketAvailable(_In_ TUN_SESSION *Session)
+{
+ return InterlockedGetU(&Session->Descriptor.Send.Ring->Head) !=
+ InterlockedGetU(&Session->Descriptor.Send.Ring->Tail);
+}
+
+WINTUN_STATUS WINAPI
+WintunWaitForPacket(_In_ TUN_SESSION *Session, _In_ DWORD Milliseconds)
+{
+ return WaitForSingleObject(Session->Descriptor.Send.TailMoved, Milliseconds);
+}
+
+WINTUN_STATUS WINAPI
+WintunReceivePackets(_In_ TUN_SESSION *Session, _Inout_ WINTUN_PACKET *Queue)
+{
+ ULONG BuffHead = InterlockedGetU(&Session->Descriptor.Send.Ring->Head);
+ if (BuffHead >= Session->Capacity)
+ return ERROR_HANDLE_EOF;
+
+ for (; Queue; Queue = Queue->Next)
+ {
+ const ULONG BuffTail = InterlockedGetU(&Session->Descriptor.Send.Ring->Tail);
+ if (BuffTail >= Session->Capacity)
+ return ERROR_HANDLE_EOF;
+
+ if (BuffHead == BuffTail)
+ return ERROR_NO_MORE_ITEMS;
+
+ const ULONG BuffContent = TUN_RING_WRAP(BuffTail - BuffHead, Session->Capacity);
+ if (BuffContent < sizeof(TUN_PACKET))
+ return ERROR_INVALID_DATA;
+
+ const TUN_PACKET *Packet = (TUN_PACKET *)&Session->Descriptor.Send.Ring->Data[BuffHead];
+ if (Packet->Size > WINTUN_MAX_IP_PACKET_SIZE)
+ return ERROR_INVALID_DATA;
+
+ const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + Packet->Size);
+ if (AlignedPacketSize > BuffContent)
+ return ERROR_INVALID_DATA;
+
+ Queue->Size = Packet->Size;
+ memcpy(Queue->Data, Packet->Data, Packet->Size);
+ BuffHead = TUN_RING_WRAP(BuffHead + AlignedPacketSize, Session->Capacity);
+ InterlockedSetU(&Session->Descriptor.Send.Ring->Head, BuffHead);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+WINTUN_STATUS WINAPI
+WintunSendPackets(_In_ TUN_SESSION *Session, _In_ const WINTUN_PACKET *Queue)
+{
+ ULONG BuffTail = InterlockedGetU(&Session->Descriptor.Receive.Ring->Tail);
+ if (BuffTail >= Session->Capacity)
+ return ERROR_HANDLE_EOF;
+
+ for (; Queue; Queue = Queue->Next)
+ {
+ const ULONG PacketSize = Queue->Size;
+ const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize);
+
+ const ULONG BuffHead = InterlockedGetU(&Session->Descriptor.Receive.Ring->Head);
+ if (BuffHead >= Session->Capacity)
+ return ERROR_HANDLE_EOF;
+
+ const ULONG BuffSpace = TUN_RING_WRAP(BuffHead - BuffTail - TUN_ALIGNMENT, Session->Capacity);
+ if (AlignedPacketSize > BuffSpace)
+ return ERROR_BUFFER_OVERFLOW; /* Dropping when ring is full. */
+
+ TUN_PACKET *Packet = (TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[BuffTail];
+ Packet->Size = PacketSize;
+ memcpy(Packet->Data, Queue->Data, PacketSize);
+ BuffTail = TUN_RING_WRAP(BuffTail + AlignedPacketSize, Session->Capacity);
+ InterlockedSetU(&Session->Descriptor.Receive.Ring->Tail, BuffTail);
+
+ if (InterlockedGet(&Session->Descriptor.Receive.Ring->Alertable))
+ SetEvent(Session->Descriptor.Receive.TailMoved);
+ }
+
+ return ERROR_SUCCESS;
+}
diff --git a/api/wintun.h b/api/wintun.h
index dadac10..1ee089d 100644
--- a/api/wintun.h
+++ b/api/wintun.h
@@ -211,3 +211,126 @@ typedef BOOL(CALLBACK *WINTUN_LOGGER_FUNC)(_In_ WINTUN_LOGGER_LEVEL Level, _In_z
* NewLogger.
*/
typedef void(WINAPI *WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_FUNC NewLogger);
+
+/**
+ * Minimum ring capacity.
+ */
+#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
+
+/**
+ * Maximum ring capacity.
+ */
+#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
+
+/**
+ * A handle representing Wintun session
+ */
+typedef void *WINTUN_SESSION_HANDLE;
+
+/**
+ * Starts Wintun session.
+ *
+ * @param Adapter Adapter handle obtained with WintunGetAdapter 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 Session Pointer to a variable to receive Wintun session handle
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+typedef WINTUN_STATUS(WINAPI *WINTUN_START_SESSION_FUNC)(
+ _In_ WINTUN_ADAPTER_HANDLE Adapter,
+ _In_ DWORD Capacity,
+ _Out_ WINTUN_SESSION_HANDLE *Session);
+
+/**
+ * Ends Wintun session.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ */
+typedef void(WINAPI *WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
+
+/**
+ * Maximum IP packet size
+ */
+#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
+
+/**
+ * Packet with data
+ */
+typedef struct _WINTUN_PACKET
+{
+ /**
+ * Pointer to next packet in queue
+ */
+ struct _WINTUN_PACKET *Next;
+
+ /**
+ * Size of packet (max WINTUN_MAX_IP_PACKET_SIZE).
+ */
+ DWORD Size;
+
+ /**
+ * Pointer to layer 3 IPv4 or IPv6 packet
+ */
+ BYTE *Data;
+} WINTUN_PACKET;
+
+/**
+ * Peeks if there is a packet available for reading.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @return Non-zero if there is a packet available; zero otherwise.
+ */
+BOOL(WINAPI *WINTUN_IS_PACKET_AVAILABLE_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
+
+/**
+ * Waits for a packet to become available for reading.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Milliseconds The time-out interval, in milliseconds. If a nonzero value is specified, the function waits
+ * until a packet is available or the interval elapses. If Milliseconds is zero, the function
+ * does not enter a wait state if a packet is not available; it always returns immediately.
+ * If Milliseconds is INFINITE, the function will return only when a packet is available.
+ *
+ * @return See WaitForSingleObject() for return values.
+ */
+WINTUN_STATUS(WINAPI *WINTUN_WAIT_FOR_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD Milliseconds);
+
+/**
+ * Reads one or more packets.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Queue A linked list of nodes to fill with packets read. The list must be NULL terminated. May
+ * contain only one node to read one packet at a time. All nodes should be big enough to
+ * accommodate WINTUN_MAX_IP_PACKET_SIZE packet. The Size field of every node should be
+ * initialized to something bigger than WINTUN_MAX_IP_PACKET_SIZE allowing post-festum
+ * detection which nodes were actually filled with packets.
+ *
+ * @return
+ * ERROR_HANDLE_EOF Wintun adapter is terminating.
+ * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted.
+ * ERROR_INVALID_DATA Wintun buffer is corrupt.
+ * ERROR_SUCCESS Requested amount of packets was read successfully.
+ * Regardless, if the error was returned, some packets might have been read nevertheless.
+ */
+WINTUN_STATUS(WINAPI *WINTUN_RECEIVE_PACKETS_FUNC)
+(_In_ WINTUN_SESSION_HANDLE Session, _Inout_ WINTUN_PACKET *Queue);
+
+/**
+ * Sends packets.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Queue Linked list of packets to send. The list must be NULL terminated.
+ *
+ * @return
+ * ERROR_HANDLE_EOF Wintun adapter is terminating.
+ * ERROR_BUFFER_OVERFLOW Wintun buffer is full. One or more packets were dropped.
+ * ERROR_SUCCESS All packets were sent successfully.
+ */
+WINTUN_STATUS(WINAPI *WINTUN_SEND_PACKETS_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const WINTUN_PACKET *Queue);