Class LinuxVpnKillSwitch
Represents the kill switch's block on Linux: an nftables table of ours for both IP versions (inet), replaced
whole by one nft -f, a transaction with no gap.
public sealed class LinuxVpnKillSwitch : IVpnKillSwitch
- Inheritance
-
LinuxVpnKillSwitch
- Implements
- Inherited Members
Remarks
nft, not iptables, for one table over both IP versions, one transaction, and sets for the ranges. It sits beside ufw's, firewalld's and Docker's tables, and a drop in any table wins, so ours only adds drops. The output chain lets out the tunnel's interface by name (first, so a tunnel packet costs one compare, and a name matches a tun made later), loopback, the service by its cgroup, DHCP and NDP, and an out-of-tunnel plan's resolvers; then it blocks DNS, a LAN router's too; then it lets out the device-level split and blocks the rest. The kernel runs the table for every outbound packet while the switch is on, a cost accepted for the switch: eBPF hooks at connect would be more work and miss unconnected UDP.
A blocked TCP connect is reset, so it fails at once: of a dropped SYN the kernel only sends another, and the app hangs till its own timeout (seen on Ubuntu 26.04); a dropped UDP send fails at once (EPERM). The forward chain does the same for forwarded traffic (Docker, libvirt), which would otherwise go around it; its DNS rules too, or a container's lookups reach the LAN's router (seen with Docker on Ubuntu 26.04).
The kernel keeps the table through a crash, not a reboot, so a block also goes to a file that a boot unit of its own loads before the network comes up, where systemd runs; without it, the block does not outlast a reboot. That copy leaves the service's rule out: nft resolves a cgroup when it loads the rule, and the service's is not there yet; the service is let out again at its next block. The names are the SDK's, so every app built on VpnHood shares the one block; the table's comment names the app whose block it is. It needs root: the service.
Constructors
LinuxVpnKillSwitch(string)
Represents the kill switch's block on Linux: an nftables table of ours for both IP versions (inet), replaced
whole by one nft -f, a transaction with no gap.
public LinuxVpnKillSwitch(string owner)
Parameters
ownerstringThe app's storage folder, which names the app whose block it is and an update keeps.
Remarks
nft, not iptables, for one table over both IP versions, one transaction, and sets for the ranges. It sits beside ufw's, firewalld's and Docker's tables, and a drop in any table wins, so ours only adds drops. The output chain lets out the tunnel's interface by name (first, so a tunnel packet costs one compare, and a name matches a tun made later), loopback, the service by its cgroup, DHCP and NDP, and an out-of-tunnel plan's resolvers; then it blocks DNS, a LAN router's too; then it lets out the device-level split and blocks the rest. The kernel runs the table for every outbound packet while the switch is on, a cost accepted for the switch: eBPF hooks at connect would be more work and miss unconnected UDP.
A blocked TCP connect is reset, so it fails at once: of a dropped SYN the kernel only sends another, and the app hangs till its own timeout (seen on Ubuntu 26.04); a dropped UDP send fails at once (EPERM). The forward chain does the same for forwarded traffic (Docker, libvirt), which would otherwise go around it; its DNS rules too, or a container's lookups reach the LAN's router (seen with Docker on Ubuntu 26.04).
The kernel keeps the table through a crash, not a reboot, so a block also goes to a file that a boot unit of its own loads before the network comes up, where systemd runs; without it, the block does not outlast a reboot. That copy leaves the service's rule out: nft resolves a cgroup when it loads the rule, and the service's is not there yet; the service is let out again at its next block. The names are the SDK's, so every app built on VpnHood shares the one block; the table's comment names the app whose block it is. It needs root: the service.
Methods
Block(VpnKillSwitchRules, CancellationToken)
Puts this app's block up, or re-points it, in one change with no gap, in place of any app's.
public Task Block(VpnKillSwitchRules rules, CancellationToken cancellationToken)
Parameters
rulesVpnKillSwitchRulesWhat the block lets pass.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that completes once the block is up.
Remarks
It fails where it cannot, and then nothing may connect: a connect never goes on unprotected.
Exceptions
- NotSupportedException
Linux has no nftables, or the service is in no cgroup v2 group of its own.
- ExternalException
The OS refused the block: Linux's
nft, or Windows' filtering engine, as without an administrator.- OperationCanceledException
cancellationTokenwas canceled.- NotSupportedException
nftables (
nft) is not installed, or the service is in no cgroup v2 group of its own.- ExternalException
nftrefused the table, orsystemctlthe boot unit.
IsBlocking(CancellationToken)
Determines whether this app's block is up.
public Task<bool> IsBlocking(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
Remarks
Without root, it cannot read the table and answers false. Where the table is gone but its boot copy is this app's, as after a reboot before the boot unit has loaded it, it answers true though no filtering runs.
Exceptions
- ExternalException
The OS's rules could not be read: Linux's
nft, or Windows' filtering engine.- OperationCanceledException
cancellationTokenwas canceled.
Unblock(bool, CancellationToken)
Removes this app's block, or, with anyApp, any app's: the tunnel's holder's call.
public Task Unblock(bool anyApp, CancellationToken cancellationToken)
Parameters
anyAppbooltrue to remove any app's block; false to remove this app's only.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that completes once the block is gone.
Remarks
It removes the table and its boot copy. Without root, it does nothing: a process of a person's could not have put a block up.
Exceptions
- ExternalException
The OS refused the change: Linux's
nft, or Windows' filtering engine.- OperationCanceledException
cancellationTokenwas canceled.