Class WinTunVpnAdapter
- Namespace
- VpnHood.Net.VpnAdapters.WinTun
- Assembly
- VpnHood.Net.VpnAdapters.WinTun.dll
Provides the VPN adapter for Windows that runs on WinTun, a layer-3 TUN driver: it makes the interface and gives it its addresses, routes, DNS servers, MTU and metric, and, for a server, IPv4 NAT through WinNAT.
public class WinTunVpnAdapter : TunVpnAdapter, IVpnAdapter, IPacketTransport, IDisposable
- Inheritance
-
WinTunVpnAdapter
- Implements
- Inherited Members
Remarks
It runs as an administrator only. The first start in a process installs the wintun.dll it carries, for an x64 or arm64 process, into a protected folder of its own under Common Files, or takes the one installed there, and loads it for the process's life; WinTun installs its driver with the first interface. The interface's GUID comes from its name (BuildGuidFromName(string)).
Windows asks every adapter's DNS servers at once, whatever the routes, so a start that sets DNS servers also adds a Name Resolution Policy Table (NRPT) rule that makes the tunnel's servers the only ones; Group Policy's own NRPT rules override it, which the adapter logs. It has no app filter, and its NAT translates IPv4 only.
Constructors
WinTunVpnAdapter(WinVpnAdapterSettings)
Provides the VPN adapter for Windows that runs on WinTun, a layer-3 TUN driver: it makes the interface and gives it its addresses, routes, DNS servers, MTU and metric, and, for a server, IPv4 NAT through WinNAT.
public WinTunVpnAdapter(WinVpnAdapterSettings adapterSettings)
Parameters
adapterSettingsWinVpnAdapterSettingsThe adapter's settings.
Remarks
It runs as an administrator only. The first start in a process installs the wintun.dll it carries, for an x64 or arm64 process, into a protected folder of its own under Common Files, or takes the one installed there, and loads it for the process's life; WinTun installs its driver with the first interface. The interface's GUID comes from its name (BuildGuidFromName(string)).
Windows asks every adapter's DNS servers at once, whatever the routes, so a start that sets DNS servers also adds a Name Resolution Policy Table (NRPT) rule that makes the tunnel's servers the only ones; Group Policy's own NRPT rules override it, which the adapter logs. It has no app filter, and its NAT translates IPv4 only.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
Fields
MaxRingCapacity
The largest ring size WinTun takes, 64 MiB.
public const int MaxRingCapacity = 67108864
Field Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
MinRingCapacity
The smallest ring size WinTun takes, 128 KiB.
public const int MinRingCapacity = 131072
Field Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
Properties
AppPackageId
Gets the ID the app filter knows this app by, which it always lets into the tunnel, or null where there is no app filter.
protected override string? AppPackageId { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
CheckAdapterInterfaceAfterNetworkChanged
Gets a value indicating whether the adapter checks, at each change of the host's network, that its interface is still there, and stops when it is not. The check is IsAdapterInterfaceEnabled(), which by default looks for the interface by name or address only.
protected override bool CheckAdapterInterfaceAfterNetworkChanged { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
IsAppFilterSupported
Gets a value indicating whether the adapter can include or exclude apps from the tunnel (IncludeApps, ExcludeApps).
public override bool IsAppFilterSupported { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
IsSocketProtectedByBind
Gets a value indicating whether the adapter keeps a socket out of the tunnel by binding it to the primary address, rather than by an OS call of its own.
protected override bool IsSocketProtectedByBind { get; }
Property Value
Remarks
When true, the routes leave out the probe networks the adapter aims at to find the primary addresses, and that search runs on an unprotected socket after a network change.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
RestartAfterNetworkAddressChanged
Gets a value indicating whether the adapter restarts when the host moves to other networks. When false, it takes the new primary addresses and raises PrimaryNetworkChanged.
protected override bool RestartAfterNetworkAddressChanged { get; }
Property Value
- bool
true when the adapter uses NAT, as a server's does, which adds its NAT only with a primary address; false for a client's, which takes a new network without a restart, whose teardown would let the host's traffic pass outside the tunnel for its seconds.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
Methods
AdapterAdd(CancellationToken)
Makes the interface: the first step of a start.
protected override Task AdapterAdd(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
The first start loads wintun.dll, installing it first. An interface of the name whose process ended a moment ago may still be on its way out; the create is then tried once more.
Exceptions
- PlatformNotSupportedException
The OS is not Windows.
- NotSupportedException
The process is neither x64 nor arm64.
- UnauthorizedAccessException
The process is not an administrator's, or Common Files or the install folder is a link or one others than administrators may change, so wintun.dll cannot be installed.
- PInvokeException
WinTun could not create the interface, as when another process holds an interface of the name.
- InvalidOperationException
The package's wintun.dll is missing; the installed one is not the one the package carries, is a link, or is one others than administrators may change; or the new interface was not found by its name or has no IPv4 or IPv6 index.
AdapterClose()
Closes the interface for reading and writing: the first step of a stop, under the base's stop lock. It may run after a partial start.
protected override void AdapterClose()
Remarks
Ending the session frees its rings, so it first wakes the reader and waits up to a second for it to leave the session, and up to a second plus twice MaxPacketSendDelay for a batch being sent, then 100 ms more for each that is late; a reader or sender that stops the adapter itself is not waited for. When one does not leave in time, the session stays allocated until the process exits, which is logged as an error.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
AdapterOpen(CancellationToken)
Opens the interface for reading and writing packets: the last step of a start, before the reader starts.
protected override Task AdapterOpen(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It starts a WinTun session with rings of RingCapacity.
Exceptions
- InvalidOperationException
A stop removed the interface while the start was under way.
- Win32Exception
WinTun could not start the session, as for a ring size it does not take.
AdapterRemove()
Removes the interface and what the start added with it: the last step of a stop, after AdapterClose(). When the base calls it, it runs under the base's stop lock and must not wait for the reader or a sender to take that lock. It may run after a partial start, and after a close that failed.
protected override void AdapterRemove()
Remarks
It also removes the interface's NRPT rule and, with NAT, the NAT; a failure of either is logged, not thrown. A later start tries to replace or remove the NRPT rule, and one that adds NAT tries to remove a NAT left behind.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
AddAddress(IpNetwork, CancellationToken)
Gives the interface its address in a virtual network. A start calls it once per configured IP version.
protected override Task AddAddress(IpNetwork ipNetwork, CancellationToken cancellationToken)
Parameters
ipNetworkIpNetworkThe interface's address with its network.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It turns off duplicate address detection on the interface first: a tunnel shares its link with no one, and the detection would keep the address unusable for seconds after the interface comes up. The address lasts as long as the interface.
Exceptions
- Win32Exception
Windows refused the change.
AddNat(IpNetwork, CancellationToken)
Translates a virtual network to the host's primary address (NAT). A start calls it with UseNat, for each IP version the adapter's NAT translates and the host has a primary address of.
protected override Task AddNat(IpNetwork ipNetwork, CancellationToken cancellationToken)
Parameters
ipNetworkIpNetworkThe virtual network.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It runs PowerShell's New-NetNat, after trying to remove a NAT of the adapter's name that a killed run
left, as WinNAT keeps one after its process. A stop does not cut it off once begun, so a failed start's cleanup
comes after the command ends; after 60 seconds it stops waiting for PowerShell, which may go on and still make
the NAT.
Exceptions
- ExternalException
New-NetNatfailed, as WinNAT does for an IPv6 network.- Win32Exception
PowerShell could not be started.
- OperationCanceledException
cancellationTokenwas canceled before the command began, or the command ran 60 seconds.
AddRoute(IpNetwork, CancellationToken)
Routes a network through the interface. A start calls it once per network, for each IP version the interface carries.
protected override Task AddRoute(IpNetwork ipNetwork, CancellationToken cancellationToken)
Parameters
ipNetworkIpNetworkThe network.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- InvalidOperationException
The interface has not been made yet.
- Win32Exception
Windows refused the route.
BuildGuidFromName(string)
Builds the GUID of an adapter name's interface: the same for the name at every start, so a kill switch finds the interface by it.
public static Guid BuildGuidFromName(string adapterName)
Parameters
adapterNamestringThe adapter's name, AdapterName.
Returns
- Guid
A GUID in the version 5 layout, made from the SHA-1 hash of
VpnHood.and the name.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
DisposeUnmanaged()
Releases unmanaged resources, such as native handles. A derived class must catch its own errors.
protected override void DisposeUnmanaged()
Remarks
An interface still there is removed, and the removal of its NRPT rule and NAT tried, also when the finalizer calls it, which no stop came before: a NAT left pointing at a dead interface can break the host's network.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
~WinTunVpnAdapter()
Removes the interface of an adapter that was not disposed, as DisposeUnmanaged() does.
protected ~WinTunVpnAdapter()
Remarks
The adapter's handlers on the host's network-change events keep it reachable until it is disposed, so dispose it rather than count on this.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
IsNatSupported(IpVersion)
Determines whether the adapter's NAT translates an IP version. A server forwards a version the adapter does not translate by its own forwarders.
public override bool IsNatSupported(IpVersion ipVersion)
Parameters
ipVersionIpVersionThe IP version.
Returns
Remarks
WinNAT translates IPv4 only: New-NetNat answers "IPV6 is not supported."
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
ReadPacket(byte[])
Reads one packet from the interface into a buffer, without waiting for one.
protected override int ReadPacket(byte[] buffer)
Parameters
bufferbyte[]The buffer, at least the MTU long.
Returns
- int
The number of bytes read, from the buffer's start; 0 if no packet is ready.
Remarks
Only the bytes read are parsed, and a packet whose header claims more is refused: the buffer is rented, so past them it holds an earlier read's bytes.
Exceptions
- IOException
The session or the interface is closed.
- InvalidOperationException
WinTun found its ring's data invalid.
- PInvokeException
WinTun failed with another error.
SetAllowedApps(IEnumerable<string>, CancellationToken)
Lets only these apps' traffic into the tunnel. A start calls it with IncludeApps, where IsAppFilterSupported is true.
protected override Task SetAllowedApps(IEnumerable<string> packageIds, CancellationToken cancellationToken)
Parameters
packageIdsIEnumerable<string>The apps' IDs, this app's own included.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- NotSupportedException
Always: WinTun has no app filter.
SetDisallowedApps(IEnumerable<string>, CancellationToken)
Keeps these apps' traffic out of the tunnel. A start calls it with ExcludeApps, where IsAppFilterSupported is true.
protected override Task SetDisallowedApps(IEnumerable<string> packageIds, CancellationToken cancellationToken)
Parameters
packageIdsIEnumerable<string>The apps' IDs, never this app's own.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- NotSupportedException
Always: WinTun has no app filter.
SetDnsServers(IReadOnlyList<IPAddress>, CancellationToken)
Sets the DNS servers the interface gives the host. A start calls it every time.
protected override Task SetDnsServers(IReadOnlyList<IPAddress> dnsServers, CancellationToken cancellationToken)
Parameters
dnsServersIReadOnlyList<IPAddress>The servers, only of the IP versions the interface carries; may be empty.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
For each IP version that has servers, they replace the interface's own, through the IP Helper API from Windows 10 2004, through PowerShell before it. Then an NRPT rule makes them the only servers Windows asks, unless Group Policy sets NRPT rules of its own, which is logged: Windows asks every adapter's servers at once, each through its own adapter whatever the routes, often a router outside the tunnel. With no servers, the rule is removed, and the interface's settings are left as they are.
Exceptions
- Win32Exception
Windows refused the servers, or PowerShell could not be started.
- ExternalException
PowerShell's command failed, before Windows 10 2004.
- OperationCanceledException
cancellationTokenwas canceled before or while the PowerShell command ran.
SetMetric(int, bool, bool, CancellationToken)
Sets the interface's route metric. A start calls it when Metric is set.
protected override Task SetMetric(int metric, bool ipV4, bool ipV6, CancellationToken cancellationToken)
Parameters
metricintThe metric.
ipV4booltrue if the interface carries IPv4 in this run.
ipV6booltrue if the interface carries IPv6 in this run.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
The interface's metric then stops being automatic.
Exceptions
- Win32Exception
Windows refused the change.
SetMtu(int, bool, bool, CancellationToken)
Sets the interface's MTU. A start calls it when Mtu is set.
protected override Task SetMtu(int mtu, bool ipV4, bool ipV6, CancellationToken cancellationToken)
Parameters
mtuintThe MTU in bytes.
ipV4booltrue if the interface carries IPv4 in this run.
ipV6booltrue if the interface carries IPv6 in this run.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- Win32Exception
Windows refused the change.
SetSessionName(string, CancellationToken)
Names the session where the OS shows it. A start calls it when SessionName is not empty.
protected override Task SetSessionName(string sessionName, CancellationToken cancellationToken)
Parameters
sessionNamestringThe name.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It does nothing: WinTun shows no session name.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
WaitForTunRead()
Blocks until a packet may be ready, after a read found none. It runs on the reader's task.
protected override void WaitForTunRead()
Exceptions
- IOException
The session is closed.
- Win32Exception
The wait for WinTun's read event failed.
- PInvokeException
The wait for WinTun's read event ended in another way.
WaitForTunWrite()
Blocks until the interface may take a packet again, between the retries of a write it refused.
protected override void WaitForTunWrite()
Remarks
It sleeps a millisecond: WinTun has no event for room in its ring.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
WritePacket(IpPacket)
Writes one packet to the interface, without waiting for room.
protected override bool WritePacket(IpPacket ipPacket)
Parameters
ipPacketIpPacketThe packet.
Returns
- bool
true if the interface took it; false if it cannot take it now, which the adapter retries with doubling waits (see MaxPacketSendDelay).
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.