tailscale / tailscale/tailscale
macOS: IPv6 routing permanently broken after disconnecting from IPv6 exit node until network interface is re-initialized
- Dominant language
- Go
- Stars
- 36.5k
- Forks
- 3.2k
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 132
Description
Hey team- running into a reproducible issue on macOS where disconnecting from a Tailscale exit node (with IPv6) permanently breaks local IPv6 routing until I manually re-initialize my network interface. Reboot also fixes it but that's obviously not great.
**Environment**
- macOS Tahoe (26.3.1)
- Tailscale standalone GUI version
- Local network: ULA IPv6 (fd00::/8), OPNsense router sending RAs, interface is a VLAN
- iPhone nearby (Continuity/Handoff active, CommCenter owns ipsec0)
**Steps to reproduce**
1. Connect to a Tailscale exit node that has IPv6
2. Disconnect from the exit node
3. Try `ping6 google.com`
**Expected behavior**
IPv6 routing restored to its pre-Tailscale state automatically.
**Actual behavior**
IPv6 is permanently broken until full reboot. `ping6 google.com` returns "No route to host" despite RAs still arriving on the interface. The weird part is the kernel routing table enters an inconsistent state - `netstat` shows a valid default route but the kernel's own lookup disagrees:
```
# netstat shows the route:
default fe80::be24:11ff:fe2d:42b9%vlan2 UGScIg vlan2
# but kernel lookup fails:
% route get -inet6 2607:f8b0:4012::200e
route: writing to routing socket: not in table
```
Additionally, `ipsec0` (owned by CommCenter for iPhone Continuity) ends up with a T-Mobile GUA injected and its default route wins over the local router. This doesn't happen before Tailscale exit node use.
**Workaround**
Re-initializing the IPv6 address via networksetup forces the interface to recover:
```zsh
sudo networksetup -setv6manual vlan11 fd00:11:11::ff 64 fd00:11:11::1
sleep 2
sudo networksetup -setv6manual vlan11 fd00:11:11::12 64 fd00:11:11::1
```
**What I think is happening**
Tailscale's Network Extension isn't fully cleaning up IPv6 routing state on exit node disconnect. It leaves the kernel routing table in a state where `netstat -rn -f inet6` shows routes that `route get -inet6` can't resolve - which persists until the interface re-initializes. The `ipsec0` behavior is a side effect of whatever state Tailscale leaves behind.
**Related:** #18923
Contributor guide
Research direction
No source files, tests, or entry points are named. Start by reproducing the exit-node disconnect on macOS and compare IPv6 route state before and after it, including the reported networksetup recovery. Done means identifying the cleanup path responsible and verifying that local IPv6 routing is restored without interface re-initialization.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos
- Domain
- networking, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100