api: update README.md
Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
fbb9098393
commit
202f1dc9b8
217
README.md
217
README.md
@ -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:
|
||||
|
||||
|
@ -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.
|
@ -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%
|
@ -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>
|
Loading…
Reference in New Issue
Block a user