api: update README.md

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2020-10-25 00:23:33 +02:00
parent fbb9098393
commit 202f1dc9b8
4 changed files with 91 additions and 290 deletions

217
README.md
View File

@ -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:
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).
Each function has its function typedef in `wintun.h` with additional usage documentation.
```C
TCHAR *InterfaceList = NULL;
#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");
```
### Adapter management
Adapters are grouped together in pools to allow various clients on the same machine. Each client vendor should pick own unique pool name.
Manage the network adapters using following functions:
- `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.
Example:
```C
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;
```
### Session management
Once adapter is created, use the following functions to start a session and transfer packets:
- `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.
#### Writing to and from rings
Reading packets from the adapter may be done as:
```C
// TODO: Preallocate WINTUN_PACKET *Queue linked list with WINTUN_MAX_IP_PACKET_SIZE packets.
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;
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.
}
}
HANDLE WintunHandle = CreateFile(InterfaceList, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, NULL);
free(InterfaceList);
...
```
### Ring layout
It may be desirable to spin on `WintunIsPacketAvailable()` for some time under heavy use before waiting with `WintunWaitForPacket()`, in order to reduce latency.
You must allocate two ring structs, one for receiving and one for sending:
Writing packets to the adapter may be done as:
```C
typedef struct _TUN_RING {
volatile ULONG Head;
volatile ULONG Tail;
volatile LONG Alertable;
UCHAR Data[];
} TUN_RING;
// TODO: Prepare WINTUN_PACKET *Queue linked list with packets.
DWORD Result = WintunSendPackets(Session, Queue);
```
- `Head`: Byte offset of the first packet in the ring. Its value must be a multiple of 4 and less than ring capacity.
### Misc functions
- `Tail`: Byte offset of the start of free space in the ring. Its value must be multiple of 4 and less than ring capacity.
Other `wintun.dll` functions are:
- `Alertable`: Zero when the consumer is processing packets, non-zero when the consumer has processed all packets and is waiting for `TailMoved` event.
- `WintunGetVersion()` returns driver and NDIS major and minor versions.
- `WintunSetLogger()` sets global logging callback function.
- `Data`: The ring data.
In order to determine the size of the `Data` array:
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.
The total ring size memory is then `sizeof(TUN_RING) + capacity + 0x10000`.
Each packet is stored in the ring aligned to `sizeof(ULONG)` as:
Example:
```C
typedef struct _TUN_PACKET {
ULONG Size;
UCHAR Data[];
} TUN_PACKET;
```
- `Size`: Size of packet (max 0xFFFF).
- `Data`: Layer 3 IPv4 or IPv6 packet.
### Registering rings
In order to register the two `TUN_RING`s, prepare a registration struct as:
```C
typedef struct _TUN_REGISTER_RINGS {
struct {
ULONG RingSize;
TUN_RING *Ring;
HANDLE TailMoved;
} Send, Receive;
} TUN_REGISTER_RINGS;
```
- `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
Reading packets from the send ring 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);
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;
}
SendToClientProgram(Next);
fwprintf(stderr, Template, LogLine);
return TRUE;
}
WintunSetLogger(ConsoleLogger);
```
It may be desirable to spin for some time under heavy use before waiting on the `TailMoved` event, in order to reduce latency.
When closing the handle, Wintun will set the `Tail` to 0xFFFFFFFF and set the `TailMoved` event to unblock the waiting user process.
Writing packets to the receive ring may be done as:
```C
for (;;) {
TUN_PACKET *Next = ReceiveFromClientProgram();
WriteToRing(Rings->Receive.Ring, Next);
if (Rings->Receive.Ring->Alertable)
SetEvent(Rings->Recieve.TailMoved);
}
```
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:

View File

@ -1,46 +0,0 @@
## Example Standalone MSI
The best way to include Wintun in your software is by including the MSMs in your final MSI,
as described by [the main README](../README.md). However, if you're stuck with an installation
system such as NSIS, which can not bundle MSM files, then you must build your own MSI, which
NSIS can then invoke. ***Do not use an MSI from elsewhere. You must build it yourself and
distribute only the MSI that you yourself build.*** Otherwise different projects will wind up
uninstalling each other by accident and disturbing the MSM reference counting. The steps in
this file should only be taken if you're not able to include an MSM into a MSI, something that
is easily possible using WiX or most commercial installation solutions.
This `msi-example` folder contains a WiX skeleton and a build script that handles all
dependencies. use it as follows below.
#### Steps:
1. Generate a UUID using uuidgen.exe and replace `{{{FIXED AMD64 UUID}}}` in exampletun.wxs
with that UUID. For the life time of your entire product, even across versions, do not change
that UUID.
2. Generate a UUID using uuidgen.exe and replace `{{{FIXED ARM64 UUID}}}` in exampletun.wxs
with that UUID. For the life time of your entire product, even across versions, do not change
that UUID.
3. Generate another UUID using uuidgen.exe and replace `{{{FIXED X86 UUID}}}` in
exampletun.wxs with that UUID. For the life time of your entire product, even across versions,
do not change that UUID.
4. Go to [Wintun.net](https://www.wintun.net/) and look at what the latest version is (`0.6`,
for example). Replace `{{{VERSION}}}` in build.bat with that version.
5. Download the amd64 MSM from [Wintun.net](https://www.wintun.net/) and compute its SHA2-256
sum in all lowercase hex digits using `CertUtil -hashfile "path/to/file" SHA256`, and replace
`{{{64BIT HASH}}}` in build.bat with that value.
6. Download the arm64 MSM from [Wintun.net](https://www.wintun.net/) and compute its SHA2-256
sum in all lowercase hex digits using `CertUtil -hashfile "path/to/file" SHA256`, and replace
`{{{64BIT HASH}}}` in build.bat with that value.
7. Download the x86 MSM from [Wintun.net](https://www.wintun.net/) and compute its SHA2-256
sum in all lowercase hex digits using `CertUtil -hashfile "path/to/file" SHA256`, and replace
`{{{32BIT HASH}}}` in build.bat with that value.
8. Run build.bat.
9. Distribute dist\exampletun-*.msi for your own software only.

View File

@ -1,62 +0,0 @@
@echo off
rem SPDX-License-Identifier: GPL-2.0
rem Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
setlocal
set PATHEXT=.exe
set BUILDDIR=%~dp0
cd /d %BUILDDIR% || exit /b 1
set WIX_CANDLE_FLAGS=-nologo
set WIX_LIGHT_FLAGS=-nologo -spdb -sice:ICE71 -sice:ICE61
if exist .deps\prepared goto :build
:installdeps
rmdir /s /q .deps 2> NUL
mkdir .deps || goto :error
cd .deps || goto :error
call :download wintun-x86.msm https://www.wintun.net/builds/wintun-x86-{{{VERSION}}}.msm {{{32BIT HASH}}} || goto :error
call :download wintun-amd64.msm https://www.wintun.net/builds/wintun-amd64-{{{VERSION}}}.msm {{{64BIT HASH}}} || goto :error
call :download wintun-arm64.msm https://www.wintun.net/builds/wintun-arm64-{{{VERSION}}}.msm {{{64BIT HASH}}} || goto :error
call :download wix-binaries.zip https://wixtoolset.org/downloads/v3.14.0.3910/wix314-binaries.zip 0904a88a4bcd9dd3c2274caabe73989cd72767ee90c8fa0bf813d004eec90d32 || goto :error
echo [+] Extracting wix-binaries.zip
mkdir wix\bin || goto :error
tar -xf wix-binaries.zip -C wix\bin || goto :error
echo [+] Cleaning up wix-binaries.zip
del wix-binaries.zip || goto :error
copy /y NUL prepared > NUL || goto :error
cd .. || goto :error
:build
set WIX=%BUILDDIR%.deps\wix\
call :msi x86 x86 || goto :error
call :msi amd64 x64 || goto :error
call :msi arm64 arm64 || goto :error
if exist ..\sign.bat call ..\sign.bat
if "%SigningCertificate%"=="" goto :success
if "%TimestampServer%"=="" goto :success
echo [+] Signing
signtool sign /sha1 "%SigningCertificate%" /fd sha256 /tr "%TimestampServer%" /td sha256 /d "ExampleTun Setup" "dist\exampletun-*.msi" || goto :error
:success
echo [+] Success.
exit /b 0
:download
echo [+] Downloading %1
curl -#fLo %1 %2 || exit /b 1
echo [+] Verifying %1
for /f %%a in ('CertUtil -hashfile %1 SHA256 ^| findstr /r "^[0-9a-f]*$"') do if not "%%a"=="%~3" exit /b 1
goto :eof
:msi
if not exist "%~1" mkdir "%~1"
echo [+] Compiling %1
"%WIX%bin\candle" %WIX_CANDLE_FLAGS% -dEXAMPLETUN_PLATFORM="%~1" -out "%~1\exampletun.wixobj" -arch %2 exampletun.wxs || exit /b %errorlevel%
echo [+] Linking %1
"%WIX%bin\light" %WIX_LIGHT_FLAGS% -out "dist\exampletun-%~1.msi" "%~1\exampletun.wixobj" || exit /b %errorlevel%
goto :eof
:error
echo [-] Failed with error #%errorlevel%.
cmd /c exit %errorlevel%

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-License-Identifier: GPL-2.0
Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
-->
<?if $(var.EXAMPLETUN_PLATFORM) = "amd64"?>
<?define UpgradeCode = "{{{FIXED AMD64 UUID}}}"?>
<?elseif $(var.EXAMPLETUN_PLATFORM) = "arm64"?>
<?define UpgradeCode = "{{{FIXED ARM64 UUID}}}"?>
<?elseif $(var.EXAMPLETUN_PLATFORM) = "x86"?>
<?define UpgradeCode = "{{{FIXED X86 UUID}}}"?>
<?else?>
<?error Unknown platform ?>
<?endif?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product
Id="*"
Name="ExampleTun"
Language="1033"
Version="1.0"
Manufacturer="Acme Widgets Corporation"
UpgradeCode="$(var.UpgradeCode)">
<Package
InstallerVersion="400"
Compressed="yes"
InstallScope="perMachine"
Description="ExampleTun: Acme Widget's Distribution of Wintun"
ReadOnly="yes" />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<Property Id="ARPNOMODIFY" Value="yes" />
<Property Id="ARPSYSTEMCOMPONENT" Value="1" />
<Property Id="DISABLEADVTSHORTCUTS" Value="yes" />
<Property Id="DISABLEROLLBACK" Value="yes" />
<Property Id="MSIDISABLERMRESTART" Value="1" />
<Property Id="MSIRMSHUTDOWN" Value="1" />
<MajorUpgrade
AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
Schedule="afterInstallExecute" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Merge Id="WintunMergeModule" Language="0" DiskId="1" SourceFile=".deps\wintun-$(var.EXAMPLETUN_PLATFORM).msm" />
</Directory>
<Feature Id="WintunFeature" Title="Wintun" Level="1">
<MergeRef Id="WintunMergeModule" />
</Feature>
</Product>
</Wix>