Nazeem.Me

A blog about technology, football and all the other random stuff in my life

The kill switch held. IPv6 didn’t.

Written by

in

·

My router reboots itself every Sunday at 05:00. I read the boot log line by line to see whether my torrent client ever reached the internet without the VPN.

The setup

My router is an ASUS RT-BE88U running Asuswrt-Merlin. It runs a WireGuard client to hide.me, and Merlin’s VPN Director decides which LAN hosts go through it. Thirteen devices do. One of them is a small Proxmox container running qBittorrent.

The router also has a scheduled reboot, early every Sunday. That raised an obvious question: for the few minutes the router is coming back up, where does the torrent client’s traffic go?

The wrong assumption

My first framing was “check that the VPN came up before qBittorrent started.” That’s the wrong question. qBittorrent doesn’t run on the router. It lives on a different machine and had been running for 12 hours when the router went down. It never stopped.

So the client doesn’t wait for the tunnel. It keeps trying to talk to peers the whole time the router is rebooting. Whatever protects it has to be on the router, and it has to be in place before the WAN link is.

Reading the boot

Merlin’s syslog has a trap. Until NTP syncs, the router has no idea what time it is, so every early line is stamped 1 January. I (Claude actually)mapped those lines to real time using the router’s uptime counter. Here’s the boot, in real time:

  • 05:00:09 Scheduled reboot. The tunnel goes down with everything else.
  • 05:03:27 openvpn-routing: Applying all killswitches
  • 05:03:30 WAN was restored
  • 05:03:31 NTP sets the clock.
  • 05:03:32 VPN Director installs its 13 routing rules and the WireGuard client starts.
  • 05:04:00 My IPv6 hook runs: no IPv6 prefix from the ISP yet, holding off.
  • 05:04:39 The prefix arrives, 66 seconds after the hook started waiting. IPv6 rules are installed.
  • 05:04:43 qBittorrent binds its new IPv6 address.
  • 05:05:11 qBittorrent reports its external IPv6 address. It’s the VPN’s.

The order matters more than the timings. The kill switch loaded three seconds before the WAN came back. The tunnel was up two seconds after that. For IPv4, the torrent box was never connected to the internet without a block between it and the WAN.

What a kill switch actually is

There’s nothing clever about it. On Linux, the router decides where a packet goes by working down a list of policy rules. For each VPN Director host, Merlin installs two:

11210:  from TORRENT_BOX lookup wgc1
12215:  from TORRENT_BOX prohibit

The first sends the host’s traffic to the WireGuard routing table. When the tunnel is up, that table has a default route and the packet leaves through the VPN. When the tunnel is down, the table is empty, the lookup finds nothing, and the packet falls through to the next rule. That rule is prohibit, so the packet is refused. It never reaches the main table, which is where the normal WAN route lives.

VPN killswitch toggle in Asus Router

That’s the whole mechanism: a refusal placed just behind the VPN route. It works during a reboot because Merlin installs it at boot, before the WAN is up.

Where it doesn’t reach

IPv6

VPN Director is IPv4-only. Every rule it writes is an IPv4 rule. It doesn’t create a single IPv6 rule, VPN route or kill switch. Out of the box, any device with an IPv6 address goes straight out through the ISP with your real prefix attached. And modern software prefers IPv6.

I found that in August and closed it with my own scripts: three ip -6 rules that send the LAN’s IPv6 traffic into the WireGuard table, plus a blackhole rule behind them so IPv6 fails closed if the tunnel drops. My first version didn’t survive a reboot. It ran before the ISP had handed out a prefix, saw nothing to protect, and gave up. The current version waits for the prefix and rechecks every minute.

It still wasn’t enough. qBittorrent logs every external address it discovers, and the logs show my ISP’s real IPv6 prefix three times: after the reboots on 20 and 27 September, and again later on the 27th. I haven’t confirmed the cause. My best guess is the window between the ISP issuing a new prefix and my rules catching up, because the rules are tied to the specific prefix. That window was a minute or less, and a torrent client needs no longer than that to tell a tracker who it is.

This morning’s reboot was clean. The rules were in place four seconds before qBittorrent bound its new address. That was the order of events this time. Nothing guarantees it next time.

A tunnel that’s switched off

The second gap is less obvious. Merlin’s kill switch protects against a tunnel that fails. It doesn’t protect against one that’s switched off. When you disable the client, Merlin removes its own prohibit rules and leaves the routing rules behind. Traffic falls through to the main table and out the WAN. I measured this in August: a desktop in VPN Director, with the kill switch enabled, returned my real home IP after I turned the client off.

That makes sense if you think of the kill switch as a safety net for the tunnel rather than a policy for the device. It’s still the opposite of what the setting’s name suggests.

How to check your own

  • Use the client as the witness. Router logs show what the router intended. qBittorrent’s log shows what peers actually saw. Search it for Detected external IP and look at every address that appears.
  • Test both stacks. A bare curl ifconfig.me will often use IPv6, which may not go through your VPN at all. Run curl -4 and curl -6 separately.
  • Check the ASN, not the address. I once mislabelled my own WAN IP as a VPN exit because it looked like the right sort of address. An ASN lookup shows whether it belongs to your VPN provider or your ISP.
  • Test the reboot itself. Every test I ran by hand happened on a router that had been up for hours and already had its IPv6 prefix. The failure only showed up on a real boot.

What I’d take away

  • A kill switch is a routing rule. Learn which rules exist, and you’ll know exactly what it covers.
  • “IPv4 only” is a leak, not a footnote. If the VPN client doesn’t handle IPv6, nothing behind it is protected over IPv6.
  • Read the logs from the client’s side. Mine said the router was fine in September. qBittorrent knew otherwise.