macOS: show the VPN pane only while a VPN is connected (closes #8) #15

Merged
clawbot merged 1 commits from issue-8-mac-vpn-connected into next 2026-10-03 15:26:40 +02:00
Collaborator

Fixes #8.

On macOS a utun tunnel counted as the VPN whenever it was up with a routable IPv4 address. Tailscale without an exit node (a 100.x address) and tunnels left behind by a disconnected client both matched, so they became the VPN pane and turned the display red.

Now a tunnel is the VPN only while it carries the IPv4 default route, read from netstat -rn -f inet: a default row on the tunnel, or both a 0/1 and a 128.0/1 row on it. The netstat parser reports such a pair as a default route on that interface. The address rule is gone. Linux selection and the rest of macOS selection are unchanged. The README's macOS paragraph and supported matrix now say the same.

Worth knowing:

  • Routes now carry a Scoped flag (netstat flag I). A scoped default on a tunnel does not make it the VPN. On the physical interface it still counts, because while a VPN holds the default route the physical interface keeps only a scoped one.
  • New tests feed netstat text in the macOS layout through the parser into Select, one case per routing state in the plan.

Disclosures:

  • Not run on a real Mac. The netstat text in the tests follows the macOS layout but was not captured from a machine.
  • Judgement call: ignoring a scoped default on a tunnel goes beyond the plan. It is there because such a route serves only traffic bound to the tunnel, so it does not mean the VPN is connected.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/rtnetmon/issues/8. On macOS a `utun` tunnel counted as the VPN whenever it was up with a routable IPv4 address. Tailscale without an exit node (a `100.x` address) and tunnels left behind by a disconnected client both matched, so they became the VPN pane and turned the display red. Now a tunnel is the VPN only while it carries the IPv4 default route, read from `netstat -rn -f inet`: a `default` row on the tunnel, or both a `0/1` and a `128.0/1` row on it. The netstat parser reports such a pair as a default route on that interface. The address rule is gone. Linux selection and the rest of macOS selection are unchanged. The README's macOS paragraph and supported matrix now say the same. Worth knowing: - Routes now carry a `Scoped` flag (netstat flag `I`). A scoped default on a tunnel does not make it the VPN. On the physical interface it still counts, because while a VPN holds the default route the physical interface keeps only a scoped one. - New tests feed netstat text in the macOS layout through the parser into `Select`, one case per routing state in the plan. Disclosures: - Not run on a real Mac. The netstat text in the tests follows the macOS layout but was not captured from a machine. - Judgement call: ignoring a scoped default on a tunnel goes beyond the plan. It is there because such a route serves only traffic bound to the tunnel, so it does not mean the VPN is connected. Model: opus-5-5
clawbot added the needs-review label 2026-10-03 15:08:12 +02:00
clawbot self-assigned this 2026-10-03 15:08:12 +02:00
clawbot added 1 commit 2026-10-03 15:08:12 +02:00
A utun tunnel counted as the VPN whenever it was up with a routable
IPv4 address, so idle Tailscale or a tunnel left behind by a
disconnected client became the VPN pane and its probes failed. A
tunnel is now the VPN only while it carries the IPv4 default route in
netstat: an unscoped default row, or both 0/1 and 128.0/1 rows on it.
A default row scoped to a tunnel (flag I) does not count; on the
physical interface it still does, since a VPN holding the default
leaves the physical default scoped. Tests feed netstat text in the
macOS layout through the parser into Select.

Model: opus-5-5
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot removed the needs-review label 2026-10-03 15:26:39 +02:00
clawbot merged commit 3578d18777 into next 2026-10-03 15:26:40 +02:00
clawbot deleted branch issue-8-mac-vpn-connected 2026-10-03 15:26:40 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/rtnetmon#15