Skip to content
 
 

Repository files navigation

WinTapNetAdapterCx

WinTapNetAdapterCx is a Windows software network adapter project built with the NetAdapterCx framework. Its goal is to implement the core concept of the Linux TUN/TAP adapter on Windows: providing a software device that allows user-mode applications to exchange Ethernet frames with the Windows networking stack.

SPDX headers

All governed source, script, metadata, workflow, specification, and Markdown files use the MIT SPDX header policy. See SPDX.md for comment forms, preamble rules, explicit exclusions, hook setup, and CI enforcement.

Project goals

  • Create a virtual Ethernet adapter using NetAdapterCx.
  • Provide a driver-managed data path for transmitting and receiving Ethernet frames.
  • Expose a practical user-mode interface for applications that need TAP-style packet access.
  • Follow Windows driver development, signing, installation, and security practices.

Status

The approved REQ-008/REQ-009 privileged integration change and the CHG-001 through CHG-014 maintenance patch set are applied, and the approved CHG-015 through CHG-020, CHG-022, and CHG-032 alignment corrections are applied. CHG-021 was superseded by the consolidated D0 change. CHG-032 keeps stack-RX injection and stack-TX capture in separate frame queues so a successful TAP write cannot complete a TAP read. CHG-033 restarts purged manual control queues before a recovered owner or D0 lifecycle re-enters the open state. The approved dynamic topology replaces static runtime adapters: a ROOT\WinTapBus KMDF parent hosts GUID-keyed WinTapChild PDOs, and the administrator-only versioned manager is \\.\Global\WinTapBusMgr. ROOT\WinTapRust and ROOT\WinTapRust2 are legacy migration inputs only. Use .\scripts\migrate-wintap-legacy.ps1 to report stale legacy instances; only -CleanupLegacy performs their explicit removal. Hosted validation covers repository artifacts, Rust WDK tool provisioning, CMake configure/build/package, and package shape for x64 and ARM64. The hosted workflow runs the existing ICMP TAP and routed dual-adapter harnesses, uploading diagnostics for each. A runner that cannot satisfy test-signing or privileged device operations fails explicitly; it does not claim packet-path coverage as passed. See specs/current-status.md for deferred evidence and WDK findings.

Privileged integration harness

The existing entry point preserves the basic overlapped-I/O checks and adds an opt-in Ethernet/ICMP round trip:

.\tests\run-wintap-harness.ps1 -Integration `
  -InstallDriver -RequireTestSigning `
  -PackageDirectory .\out\cmake\x64\package\x64\Release `
  -DiagnosticsPath .\artifacts\wintap-harness

The integration path creates a disposable GUID child through the manager, waits for its returned TAP interface, assigns only 192.0.2.1/30, services ARP for 192.0.2.2, validates and replies to ICMP Echo, verifies the Windows Ping result, and removes only the address/child state it created. Run it elevated in a Hyper-V VM with test signing enabled.

This repository's windows-latest and windows-2022 hosted runners are already test-signed. If that runner configuration regresses, the harness fails explicitly rather than reporting a capability-only pass.

Routed dual-adapter relay harness

The DuoNIC-style integration harness installs one bus, creates two disposable GUID-keyed WinTap children through its manager, installs reciprocal endpoint host routes, and relays Ethernet frames between their independently exclusive TAP handles. It tests unbound IPv4 ICMP and IPv6 ICMPv6 traffic through the adapter datapaths rather than loopback, detects byte-identical reflected injections, and validates/counts then suppresses ARP and IPv6 Neighbor Discovery/DAD control frames:

.\tests\run-wintap-dual-adapter-harness.ps1 `
  -PackageDirectory .\out\cmake\x64\package\x64\Release `
  -Architecture x64 `
  -DiagnosticsPath .\artifacts\wintap-dual-adapter-harness

Run it elevated in a clean, test-signed Hyper-V/WinDbg VM. It refuses to touch pre-existing WinTap adapters, resolves devcon.exe from the pinned WDK, uses 198.51.100.1/30/198.51.100.2/30 and 2001:db8:515:1::1/64/2001:db8:515:1::2/64, and removes the two devices, network state, and only a driver-store package added by that run. It runs 257 IPv4/IPv6 relay iterations by default; use -RelayIterations to select a bounded alternative.

Two-TAP switch core

The wintap-switch-core crate implements the first-release switch contract above two selected manager-returned GUID/interface pairs. It provides collection-oriented endpoint identities, MAC/VLAN learning with a bounded 4,096-entry FDB, peer-only forwarding decisions, and generation-protected bounded buffer-slot state. Arbitrary-N forwarding remains deferred.

Deploying to a test VM

The following procedure deploys the x64 Rust package and the wintap-switch executable to a clean, test-signed Windows VM over SSH. The example VM is alanjo-ssp; replace the VM name, address, and user as needed. Run the local PowerShell commands from an elevated 64-bit PowerShell session.

1. Build the driver package and switch

Restore the pinned WDK/SDK packages and build the x64 Release package:

cmake --preset vs18-x64-debug
cmake --build .\out\build\vs2022-x64-debug --config Release --target wintap_package
cargo build -p wintap-switch --release

The driver package is normally under out\cmake\x64\package\x64\Release. The switch executable is under target\release\wintap-switch.exe unless the WDK build configuration redirects Cargo output to out\rust-target.

Resolve the pinned x64 DevCon tool:

$DevCon = (Get-ChildItem .\out\packages -Recurse -Filter devcon.exe |
    Where-Object FullName -Match 'WDK\.x64.*\\x64\\devcon\.exe' |
    Select-Object -First 1 -ExpandProperty FullName)
if (-not $DevCon) { throw "Pinned x64 devcon.exe was not found." }

2. Resolve the VM address and verify SSH

For a local Hyper-V VM:

$VmName = 'alanjo-ssp'
$VmIp = Get-VMNetworkAdapter -VMName $VmName |
    Select-Object -ExpandProperty IPAddresses |
    Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' } |
    Select-Object -First 1
if (-not $VmIp) { throw "No IPv4 address was reported for $VmName." }

$VmUser = 'administrator'
ssh -o StrictHostKeyChecking=accept-new "$VmUser@$VmIp" hostname

SSH must authenticate as an administrator. The VM must be test-signed and configured to permit driver installation. Do not use a production machine for this procedure.

3. Copy the package, DevCon, and switch

$RemoteRoot = 'C:/Temp/WinTapSwitch'
ssh "$VmUser@$VmIp" "cmd /c if not exist C:\Temp\WinTapSwitch\package mkdir C:\Temp\WinTapSwitch\package"

scp -r .\out\cmake\x64\package\x64\Release\* `
    "$VmUser@${VmIp}:$RemoteRoot/package/"
scp $DevCon "$VmUser@${VmIp}:$RemoteRoot/devcon.exe"

$SwitchExe = '.\target\release\wintap-switch.exe'
if (-not (Test-Path $SwitchExe)) {
    $SwitchExe = '.\out\rust-target\release\wintap-switch.exe'
}
if (-not (Test-Path $SwitchExe)) { throw "wintap-switch.exe was not found." }
scp $SwitchExe "$VmUser@${VmIp}:$RemoteRoot/wintap-switch.exe"

Verify the transfer before running it:

Get-FileHash $SwitchExe -Algorithm SHA256
ssh "$VmUser@$VmIp" `
    "powershell -NoProfile -Command Get-FileHash -LiteralPath C:\Temp\WinTapSwitch\wintap-switch.exe -Algorithm SHA256"

4. Provision two GUID-keyed children

Copy tests\wintap-bus-manager.psm1 with the harness. Stage the child INF, install one ROOT\WinTapBus parent, then create two GUIDs through its administrator-only manager. The returned Guid and InterfacePath values are the only valid TAP selection inputs; do not infer device order or use fixed DOS paths. The routed harness performs these steps and records the mapping in manager-created-children.json.

5. Run the switch startup and shutdown smoke test

The switch opens both TAP endpoints exclusively, probes the required I/O-ring read/write operations, registers its handles and buffers, and remains in its completion loop until Ctrl+C or console close. Start it in the VM:

ssh "$VmUser@$VmIp" `
  "C:\Temp\WinTapSwitch\wintap-switch.exe --endpoint <GUID-A>=<InterfacePath-A> --endpoint <GUID-B>=<InterfacePath-B> --read-depth 128"

--read-depth controls the shared total number of pending read/write buffers. It must be a positive even value; buffers are divided equally between the two TAP endpoints. The effective maximum is determined by checked size arithmetic, available memory, and I/O-ring/API resource limits. The default is 128, so use --read-depth 64 or a larger even value to compare configurations.

Leave it running long enough to confirm it remains alive, then press Ctrl+C. A missing endpoint, unavailable I/O-ring capability, registration failure, or stale completion is reported as a nonzero startup/termination error. The switch currently uses the contiguous v3 path; v4 scatter/gather remains disabled until its dedicated validation is complete.

For repeatable switch experiments, copy tests\run-wintap-switch-experiment.ps1 and tests\wintap-bus-manager.psm1 to the VM and run the script there. It stages the child driver, installs the bus, creates two GUID-keyed TAPs, assigns 198.51.100.1/32 and 198.51.100.2/32 with reciprocal host routes, starts the switch, and removes all resources it created when the bounded run ends:

.\run-wintap-switch-experiment.ps1 `
  -PackageDirectory C:\Temp\WinTapSwitch\package `
  -SwitchPath C:\Temp\WinTapSwitch\wintap-switch.exe `
  -DevConPath C:\Temp\WinTapSwitch\devcon.exe `
  -DurationSeconds 300 -Stats

6. Run the routed driver relay validation

The existing harness validates the provisioned driver, TAP ownership, routed IPv4/IPv6 traffic, cleanup, and Driver Verifier-facing ownership assertions. It owns the TAP handles itself, so stop wintap-switch.exe before running it:

scp .\tests\run-wintap-dual-adapter-harness.ps1 `
    "$VmUser@$VmIp:$RemoteRoot/run-wintap-dual-adapter-harness.ps1"

ssh "$VmUser@$VmIp" `
    "powershell -NoProfile -ExecutionPolicy Bypass -File C:\Temp\WinTapSwitch\run-wintap-dual-adapter-harness.ps1 -PackageDirectory C:\Temp\WinTapSwitch\package -Architecture x64 -DevConPath C:\Temp\WinTapSwitch\devcon.exe -DiagnosticsPath C:\Temp\WinTapSwitch\diagnostics -RelayIterations 257"

The harness uses documentation-only IPv4/IPv6 addresses, exact host routes, static neighbors, and run-scoped firewall rules. It refuses to modify pre-existing matching adapters and removes only state created by that run. Inspect C:\Temp\WinTapSwitch\diagnostics after a failure and copy it back with scp -r before cleaning the VM.

7. Clean up the VM

If the harness was not used, call Remove-WinTapBusChild for each recorded GUID and wait for Absent before removing the recorded bus parent. The harness performs this cleanup automatically, including addresses, routes, neighbors, firewall rules, child PnP state, and any driver-store package added by that invocation. Do not remove a pre-existing Driver Store package by hand.

Intended platform

  • Windows 10 and later
  • Windows Driver Kit (WDK) with NetAdapterCx support
  • Rust 1.85.0, cargo-wdk, LLVM/libclang, and NuGet

The project targets Windows 10 version 2004 and later on x64 and ARM64. Build dependencies use the pinned WDK/SDK NuGet version 10.0.28000.2526.

Related technologies

License

This project is licensed under the MIT License.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages