tailscale / tailscale/tailscale

macOS: IPv6 routing permanently broken after disconnecting from IPv6 exit node until network interface is re-initialized

Open
#19,036 5 comments 1 reaction 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.