Go to file
Jason A. Donenfeld 366aaf2820 Rearrange README and add additional MSM info
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2019-08-02 11:57:30 +02:00
installer Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
.clang-format Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
.editorconfig Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
.gitignore Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
atomic.h Separate out atomic helpers 2019-08-02 09:43:32 +00:00
COPYING Initial commit 2019-03-22 16:52:31 -06:00
README.md Rearrange README and add additional MSM info 2019-08-02 11:57:30 +02:00
undocumented.h Add handle closing ioctl 2019-07-31 13:59:54 +00:00
wintun.c Use reference counter and KEVENT instead of remove locks 2019-08-02 09:43:32 +00:00
wintun.inf Annotate service control constants 2019-06-07 12:28:07 +02:00
wintun.proj Do not rebuild driver on installer library build 2019-08-02 09:43:32 +00:00
wintun.props Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
wintun.rc Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
wintun.sln Rewrite installer logic in C 2019-08-02 09:43:32 +00:00
wintun.vcxproj Separate out atomic helpers 2019-08-02 09:43:32 +00:00
wintun.vcxproj.filters Separate out atomic helpers 2019-08-02 09:43:32 +00:00

Wintun Network Adapter

TUN Device Driver for Windows

This is a layer 3 TUN driver for Windows 7, 8, 8.1, and 10. Originally created for WireGuard, it is intended to be useful to a wide variety of projects that require layer 3 tunneling devices with implementations primarily in userspace.

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.

<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.

Usage

After loading the driver and creating a network interface the typical way using SetupAPI, open the NDIS device object associated with the PnPInstanceId, enabling all forms of file sharing.

Ring layout

You must allocate two ring structs, one for receiving and one for sending:

typedef struct _TUN_RING {
    volatile ULONG Head;
    volatile ULONG Tail;
    volatile LONG Alertable;
    UCHAR Data[];
} TUN_RING;
  • 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.

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:

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_RINGs, prepare a registration struct as:

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 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 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(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:

for (;;) {
    TUN_PACKET *next = PopFromRing(r->Send.Ring);
    if (!next) {
        r->Send.Ring->Alertable = TRUE;
        next = PopFromRing(r->Send.Ring);
        if (!next) {
            WaitForSingleObject(r->Send.TailMoved, INFINITE);
            r->Send.Ring->Alertable = FALSE;
            continue;
        }
        r->Send.Ring->Alertable = FALSE;
        ResetEvent(r->Send.TailMoved);
    }
    SendToClientProgram(next);
}

It may be desirable to spin for ~50ms 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:

for (;;) {
    TUN_PACKET *next = ReceiveFromClientProgram();
    WriteToRing(r->Receive.Ring, next);
    if (r->Receive.Ring->Alertable)
        SetEvent(r->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

General requirements:

wintun.sln may be opened in Visual Studio for development and building. Or the below instructions can be followed for command line builds.

Building from Command Line

Open Developer Command Prompt for VS 2019 and use the msbuild command:

msbuild wintun.proj [/t:<target>]

Targets

  • Build: Builds the driver release configurations of all supported platforms. This is the default target.

  • Clean: Deletes all intermediate and output files.

  • Rebuild: Alias for Clean followed by Build.

  • SDV: Runs Static Driver Verifier, which includes a clean driver build, only for AMD64 release configuration.

  • SDVView: Views the results of the Static Driver Verifier.

  • DVL: Runs the SDV, and creates a Driver Verification Log, only for AMD64 release configurations.

  • MSM: Builds Microsoft Installer Merge Modules in <output folder>\wintun-<platform>-<version>.msm. Requires WHQL signed driver.

The driver output folders are:

Platform and Configuration Folder
x86 Debug x86\Debug\wintun
x86 Release x86\Release\wintun
AMD64 Debug amd64\Debug\wintun
AMD64 Release amd64\Release\wintun
ARM64 Debug arm64\Debug\wintun
ARM64 Release arm64\Release\wintun

Do note that since the Build target builds for all supported platforms, you will need to have the toolchains installed for those platforms.

Building Microsoft Installer Merge Modules

  1. msbuild wintun.proj /t:DVL;Build.
  2. Perform Windows Hardware Lab Kit tests.
  3. Submit submission package to Microsoft.
  4. Copy WHQL-signed driver to x86\Release\whql\ and amd64\Release\whql\ subfolders.
  5. msbuild wintun.proj /t:MSM
  6. MSM files are placed in dist subfolder.

Note: due to the use of SHA256 signatures throughout, Windows 7 users who would like a prompt-less installation generally need to have the KB2921916 hotfix installed, which can be obtained from these mirrors: amd64 and x86.

Digital Signing

By default, the driver will be test-signed using a certificate that the WDK should automatically generate. To subsequently load the driver, you will need to put your computer into test mode by executing as Administrator bcdedit /set testsigning on.

If you possess an EV certificate for kernel mode code signing you should switch TUN driver digital signing from test-signing to production-signing by authoring your wintun.vcxproj.user file to look something like this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <SignMode>ProductionSign</SignMode>
    <CrossCertificateFile>$(WDKContentRoot)CrossCertificates\DigiCert_High_Assurance_EV_Root_CA.crt</CrossCertificateFile>
    <ProductionCertificate>DF98E075A012ED8C86FBCF14854B8F9555CB3D45</ProductionCertificate>
    <TimestampServer>http://timestamp.digicert.com</TimestampServer>
  </PropertyGroup>
</Project>

Modify the <CrossCertificateFile> to contain the full path to the cross-signing certificate of CA that issued your certificate. You should be able to find its .crt file in C:\Program Files (x86)\Windows Kits\10\CrossCertificates. Note that the $(WDKContentRoot) expands to C:\Program Files (x86)\Windows Kits\10\.

If you already have wintun.vcxproj.user file, just add the <PropertyGroup> section.