diff options
author | Simon Rozman <simon@rozman.si> | 2020-10-25 00:23:33 +0200 |
---|---|---|
committer | Simon Rozman <simon@rozman.si> | 2020-10-31 06:55:58 +0100 |
commit | 202f1dc9b88e2d962573e694a9abf38472575f9d (patch) | |
tree | eab98197015c2760df25f5aa698383530c060d6f /README.md | |
parent | api: allow wintun.h use in C++ (diff) | |
download | wintun-202f1dc9b88e2d962573e694a9abf38472575f9d.tar.xz wintun-202f1dc9b88e2d962573e694a9abf38472575f9d.zip |
api: update README.md
Signed-off-by: Simon Rozman <simon@rozman.si>
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 195 |
1 files changed, 80 insertions, 115 deletions
@@ -5,158 +5,123 @@ This is a layer 3 TUN driver for Windows 7, 8, 8.1, and 10. Originally created f ## Installation -The following snippet may be used inside of a WiX `<Product>` element for including Wintun inside of a Windows Installer. Note that **MSI is the only supported method of installing Wintun**; if you're using a different install system (such as NSIS) and cannot bother to switch to MSI/WiX, then simply bundle an embedded MSI that you can execute with `msiexec.exe`. - -```xml -<DirectoryRef Id="INSTALLFOLDER"> - <Merge Id="WintunMergeModule" Language="0" DiskId="1" SourceFile="path\to\wintun-x.y-amd64.msm" /> -</DirectoryRef> -<Feature Id="WintunFeature" Title="Wintun" Level="1"> - <MergeRef Id="WintunMergeModule" /> -</Feature> -``` - -It is advisable to use the prebuilt and Microsoft-signed MSM files from [Wintun.net](https://www.wintun.net/). +Wintun is deployed as a platform-specific `wintun.dll` file. Install the `wintun.dll` file side-by-side with your application. ## Usage -After loading the driver and creating a network interface the typical way using [SetupAPI](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/setupapi), open the NDIS device object associated with the PnPInstanceId, enabling all forms of file sharing: - -```C -TCHAR *InterfaceList = NULL; -for (;;) { - free(InterfaceList); - DWORD RequiredBytes; - if (CM_Get_Device_Interface_List_Size(&RequiredBytes, (LPGUID)&GUID_DEVINTERFACE_NET, - InstanceId, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS) - return FALSE; - InterfaceList = calloc(sizeof(*InterfaceList), RequiredBytes); - if (!InterfaceList) - return FALSE; - CONFIGRET Ret = CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_NET, InstanceId, - InterfaceList, RequiredBytes, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); - if (Ret == CR_SUCCESS) - break; - if (Ret != CR_BUFFER_SMALL) { - free(InterfaceList); - return FALSE; - } -} -HANDLE WintunHandle = CreateFile(InterfaceList, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, 0, NULL); -free(InterfaceList); -... -``` +Include `wintun.h` file in your project and dynamically load the `wintun.dll` using [`LoadLibraryEx()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa) and [`GetProcAddress()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress). -### Ring layout - -You must allocate two ring structs, one for receiving and one for sending: +Each function has its function typedef in `wintun.h` with additional usage documentation. ```C -typedef struct _TUN_RING { - volatile ULONG Head; - volatile ULONG Tail; - volatile LONG Alertable; - UCHAR Data[]; -} TUN_RING; +#include "wintun.h" +⋮ +HMODULE Wintun = LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); +if (!Wintun) + return GetLastError(); +WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter = (WINTUN_CREATE_ADAPTER_FUNC)GetProcAddress(Wintun, "WintunCreateAdapter"); +WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter = (WINTUN_DELETE_ADAPTER_FUNC)GetProcAddress(Wintun, "WintunDeleteAdapter"); ``` -- `Head`: Byte offset of the first packet in the ring. Its value must be a multiple of 4 and less than ring capacity. - -- `Tail`: Byte offset of the start of free space in the ring. Its value must be multiple of 4 and less than ring capacity. - -- `Alertable`: Zero when the consumer is processing packets, non-zero when the consumer has processed all packets and is waiting for `TailMoved` event. - -- `Data`: The ring data. +### Adapter management -In order to determine the size of the `Data` array: +Adapters are grouped together in pools to allow various clients on the same machine. Each client vendor should pick own unique pool name. -1. Pick a ring capacity ranging from 128kiB to 64MiB bytes. This capacity must be a power of two (e.g. 1MiB). The ring can hold up to this much data. -2. Add 0x10000 trailing bytes to the capacity, in order to allow for always-contigious packet segments. +Manage the network adapters using following functions: -The total ring size memory is then `sizeof(TUN_RING) + capacity + 0x10000`. +- `WintunCreateAdapter()` creates a new adapter. +- `WintunDeleteAdapter()` deletes the adapter. +- `WintunEnumAdapters()` enumerates all existing adapters. +- `WintunGetAdapter()` gets existing adapter handle. +- `WintunFreeAdapter()` frees adapter handle. +- `WintunGetAdapterDeviceObject()` opens adapter device object. +- `WintunGetAdapterGUID()` gets adapter GUID. +- `WintunGetAdapterLUID()` gets adapter LUID. +- `WintunGetAdapterName()` gets adapter name. +- `WintunSetAdapterName()` sets adapter name. -Each packet is stored in the ring aligned to `sizeof(ULONG)` as: +Example: ```C -typedef struct _TUN_PACKET { - ULONG Size; - UCHAR Data[]; -} TUN_PACKET; +DWORD Result; +WINTUN_ADAPTER_HANDLE Adapter; +BOOL RebootRequired; +Result = WintunCreateAdapter(L"com.contoso.myapp", "My VPN adapter", NULL, &Adapter, &RebootRequired); +if (Result != ERROR_SUCCESS) + return Result; ``` -- `Size`: Size of packet (max 0xFFFF). +### Session management -- `Data`: Layer 3 IPv4 or IPv6 packet. +Once adapter is created, use the following functions to start a session and transfer packets: -### Registering rings +- `WintunStartSession()` starts a session. One adapter may have only one session. +- `WintunEndSession()` ends and frees the session. +- `WintunIsPacketAvailable()` checks if there is a receive packet available. +- `WintunWaitForPacket()` waits for a receive packet to become available. +- `WintunReceivePackets()` receives one or more packets. +- `WintunSendPackets()` sends one or more packets. -In order to register the two `TUN_RING`s, prepare a registration struct as: +#### Writing to and from rings + +Reading packets from the adapter may be done as: ```C -typedef struct _TUN_REGISTER_RINGS { - struct { - ULONG RingSize; - TUN_RING *Ring; - HANDLE TailMoved; - } Send, Receive; -} TUN_REGISTER_RINGS; +// TODO: Preallocate WINTUN_PACKET *Queue linked list with WINTUN_MAX_IP_PACKET_SIZE packets. +for (;;) { + if (!WintunIsPacketAvailable(Session)) + WintunWaitForPacket(Session, INFINITE); + for (WINTUN_PACKET *p = Queue; p && p->Size <= WINTUN_MAX_IP_PACKET_SIZE; p = p->Next) + p->Size = DWORD_MAX; + DWORD Result = WintunReceivePackets(Session, Queue); + if (Result != ERROR_SUCCESS && Result != ERROR_NO_MORE_ITEMS) + return Result; + for (WINTUN_PACKET *p = Queue; p && p->Size <= WINTUN_MAX_IP_PACKET_SIZE; p = p->Next) { + // TODO: Process packet p. + } +} ``` -- `Send.RingSize`, `Receive.RingSize`: Sizes of the rings (`sizeof(TUN_RING) + capacity + 0x10000`, as above). - -- `Send.Ring`, `Receive.Ring`: Pointers to the rings. - -- `Send.TailMoved`: A handle to an [`auto-reset event`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa) created by the client that Wintun signals after it moves the `Tail` member of the send ring. - -- `Receive.TailMoved`: A handle to an [`auto-reset event`](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa) created by the client that the client will signal when it changes `Receive.Ring->Tail` and `Receive.Ring->Alertable` is non-zero. - -With events created, send and receive rings allocated, and registration struct populated, [`DeviceIoControl`](https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol)(`TUN_IOCTL_REGISTER_RINGS`: 0xca6ce5c0) with pointer and size of descriptor struct specified as `lpInBuffer` and `nInBufferSize` parameters. You may call `TUN_IOCTL_REGISTER_RINGS` on one handle only. - - -### Writing to and from rings +It may be desirable to spin on `WintunIsPacketAvailable()` for some time under heavy use before waiting with `WintunWaitForPacket()`, in order to reduce latency. -Reading packets from the send ring may be done as: +Writing packets to the adapter may be done as: ```C -for (;;) { - TUN_PACKET *Next = PopFromRing(Rings->Send.Ring); - if (!Next) { - Rings->Send.Ring->Alertable = TRUE; - Next = PopFromRing(Rings->Send.Ring); - if (!Next) { - WaitForSingleObject(Rings->Send.TailMoved, INFINITE); - Rings->Send.Ring->Alertable = FALSE; - continue; - } - Rings->Send.Ring->Alertable = FALSE; - ResetEvent(Rings->Send.TailMoved); - } - SendToClientProgram(Next); -} +// TODO: Prepare WINTUN_PACKET *Queue linked list with packets. +DWORD Result = WintunSendPackets(Session, Queue); ``` -It may be desirable to spin for some time under heavy use before waiting on the `TailMoved` event, in order to reduce latency. +### Misc functions + +Other `wintun.dll` functions are: -When closing the handle, Wintun will set the `Tail` to 0xFFFFFFFF and set the `TailMoved` event to unblock the waiting user process. +- `WintunGetVersion()` returns driver and NDIS major and minor versions. +- `WintunSetLogger()` sets global logging callback function. -Writing packets to the receive ring may be done as: +Example: ```C -for (;;) { - TUN_PACKET *Next = ReceiveFromClientProgram(); - WriteToRing(Rings->Receive.Ring, Next); - if (Rings->Receive.Ring->Alertable) - SetEvent(Rings->Recieve.TailMoved); +static BOOL CALLBACK +ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ const WCHAR *LogLine) +{ + const WCHAR *Template; + switch (Level) + { + case WINTUN_LOG_INFO: Template = L"[+] %s\n"; break; + case WINTUN_LOG_WARN: Template = L"[-] %s\n"; break; + case WINTUN_LOG_ERR: Template = L"[!] %s\n"; break; + default: return FALSE; + } + fwprintf(stderr, Template, LogLine); + return TRUE; } +⋮ +WintunSetLogger(ConsoleLogger); ``` -Wintun will abort reading the receive ring on invalid `Head` or `Tail` or on a bogus packet. In this case, Wintun will set the `Head` to 0xFFFFFFFF. In order to restart it, reopen the handle and call `TUN_IOCTL_REGISTER_RINGS` again. However, it should be entirely possible to avoid feeding Wintun bogus packets and invalid offsets. - ## Building -**Do not distribute drivers named "Wintun", as they will most certainly clash with official deployments. Instead distribute [the signed MSMs from Wintun.net](https://www.wintun.net/).** If you are unable to use MSMs, [consult the MSI creation instructions](msi-example/README.md). +**Do not distribute drivers named "Wintun", as they will most certainly clash with official deployments. Instead distribute `wintun.dll`.** General requirements: |