Tool to monitor the IPv6 default route on macOS and reacquire it as needed. It runs as a status item in the menu bar and does not show an icon in the Dock.
This tool was created to address a persistent issue where macOS sporadically loses its IPv6 default route, resulting in "No route to host" errors for IPv6 traffic outside the local network.
The issue appears intermittently and is difficult to reproduce on demand. It has been observed on multiple Apple Silicon Macs and across different network interfaces, including Wi-Fi, built-in Ethernet, and external USB-C Ethernet adapters. Sometimes the network remains stable for several days; at other times the route disappears repeatedly within minutes after a reboot.
In captured failure states, macOS still had valid IPv6 state everywhere except in the kernel routing table:
- The active interface still had valid global IPv6 addresses.
SystemConfigurationstill reported the correct IPv6 router for the active interface.ndp -rstill listed the same router as a default router with remaining lifetime.- Packet captures still showed valid Router Advertisements from the main gateway, including a non-zero router lifetime and high router preference.
scutil --nwistill reported IPv6 on the interface as reachable.- But
route -n get -inet6 defaultreturnednot in table, andnetstat -rn -f inet6showed no IPv6 default route via the active LAN/Wi-Fi interface.
This has been seen in networks where a normal IPv6 gateway advertises the internet default route while Apple Thread Border Routers, such as Apple TV and HomePod mini devices, also send Router Advertisements with Route Information Options for ULA prefixes. Those Thread Border Router advertisements use router lifetime 0, meaning they should not become default routers themselves. The suspected bug is that macOS sometimes ends up with an inconsistent IPv6 state: Neighbor Discovery and SystemConfiguration still know the default router, but the kernel default route is missing.
When this happens, IPv6 connectivity is broken until the route is manually restored or the interface is cycled. This tool automates the restoration process by detecting the missing default route and re-adding it.
References:
- Reddit: macOS losing IPv6 default route
- Reddit: Since switching to a UniFi Cloud Gateway, macOS loses IPv6 default route
This application is open-source and intended to be built from source. You do not need a paid Apple Developer Program membership to run it.
- A Mac running macOS 13 or later.
- Xcode (available for free from the Mac App Store).
- Download or clone this repository.
- Open
IPv6Monitor.xcodeprojin Xcode. - In the top-left corner, ensure the
IPv6Monitortarget is selected and your Mac is chosen as the destination. - Go to Product > Archive (or simply Run to test it).
- If Archiving: Once the archive is complete, click Distribute App, select Custom, then Copy App. This will give you the runnable
.appfile. - Move the
IPv6Monitor.appto your/Applicationsfolder.
To ensure the monitor runs automatically:
- Open System Settings.
- Go to General > Login Items & Extensions.
- Click the
+button under "Open at Login". - Select
IPv6Monitor.app.
This tool is designed to work automatically in the background without user intervention. Since modifying the system routing table (and, optionally, pf) requires root privileges, the app needs a way to run those actions as root.
To avoid the extreme complexity of a signed privileged helper (which usually requires a paid Apple Developer account), this tool uses sudo -n targeting a single root-owned wrapper (ipv6monitor-pf). All privileged actions - route add/delete and the optional RA-protection pf commands - go through that one wrapper, which validates every argument (interface regex, IPv6 gateway) in root-context code before building the route/pfctl argv. There is intentionally no raw NOPASSWD: /sbin/route rule: that would hand any user-context process silent, unrestricted control of the routing table.
- Install the wrapper (copies it to
/Library/PrivilegedHelperTools/ipv6monitor-pf, root:wheel 0755, and verifies it is not writable by any non-root user):sudo Scripts/install-ipv6monitor-pf.sh
- The installer prints the exact sudoers line to add. Add it via:
The line (with
sudo visudo -f /etc/sudoers.d/ipv6monitor
your_usernamereplaced by the user running the app) is:your_username ALL=(root) NOPASSWD: /Library/PrivilegedHelperTools/ipv6monitor-pf detect *, /Library/PrivilegedHelperTools/ipv6monitor-pf on *, /Library/PrivilegedHelperTools/ipv6monitor-pf off, /Library/PrivilegedHelperTools/ipv6monitor-pf status, /Library/PrivilegedHelperTools/ipv6monitor-pf route-add *, /Library/PrivilegedHelperTools/ipv6monitor-pf route-delete * - Save and exit (in nano:
Ctrl+O,Enter,Ctrl+X).
Never grant pfctl or /sbin/route broadly - only this wrapper with these fixed subcommands.
Versions before the wrapper-based route-add/route-delete subcommands used a raw NOPASSWD: /sbin/route sudoers rule. That old rule does nothing for the current app (route repair now goes through the wrapper) and is a needlessly broad grant, so replace it:
sudo Scripts/apply-sudoers-update.shThis reinstalls the wrapper, backs up your existing /etc/sudoers.d/ipv6monitor, writes the wrapper-only rule above (validated with visudo -c before it's installed), and verifies it works without a password prompt.
To verify the setup works (it should not ask for a password):
- Open a new Terminal window.
- Run:
If this returns a JSON status line without a password prompt, the
sudo -n /Library/PrivilegedHelperTools/ipv6monitor-pf status
route-add/route-deletesubcommands (covered by the same sudoers rule) will work too.
The wrapper also powers an optional, separately-armed RA-filtering feature - see Scripts/README-ra-protection.md.
The app monitors the routing table using the SystemConfiguration API and by verifying the kernel routing table directly. If the default IPv6 route is missing:
- It attempts to discover the router using
ndp(Neighbor Discovery Protocol) if not found via API. - If the kernel's current default route points at a different, dead interface (link-down and without a real global IPv6 address) via a route this app previously installed itself (identified by the
STATICflag), it removes that stale route first via the wrapper'sroute-deletesubcommand. It never touches a foreign default route that isn'tSTATIC, or one on an interface that is active or still has a real global IPv6 address (so a live VPN's default route is left alone) - macOS's own routing decisions on another interface are left alone. - It executes the wrapper's
route-addsubcommand (sudo -n /Library/PrivilegedHelperTools/ipv6monitor-pf route-add <iface> <gateway>) to restore the route on the monitored interface.
The primary goal of the diagnostics is to gather evidence about why the route is lost (e.g. to support an Apple Feedback report), since the trigger is multiple Router Advertisement (RA) senders on the LAN.
- RA sender panel (Connectivity Check window): lists every IPv6 default router the kernel learned via RAs on the monitored interface, with its preference (
high/medium/low) and remaining lifetime (expire). More than one router is highlighted as a warning, since that is the documented bug trigger. - Loss forensics: on every route loss the app writes a timestamped diagnostic snapshot to
~/Library/Logs/IPv6Monitor/(ndp,netstat,ifconfig,scutil, recentconfigd/networkdlogs, …) and logs a compact, greppable line capturing the lifetime of each RA sender at the moment of loss:Collected over many losses, this shows whether losses correlate with theRA@loss total=8 high=[29m50s] medium=[1h59m55s,…] low=[]high-preference gateway's RA lifetime lapsing. - Interface picker: the interface that carries the default route and has a global-unicast IPv6 address is marked Recommended and sorted to the top. The
IPv6badge now reflects a real global address (not merely a link-local one). - Log window: full-text search, filtering by type, an "events only" toggle that hides the recurring route-OK heartbeat, and date separators.
The log directory can be opened any time via the menu (Export Diagnostics… / ⌘E).
Identifying the RA senders by MAC/vendor is intentionally not included: macOS masks neighbor-cache link-layer addresses (ndp -an returns 2:0:0:0:0:0) for the hardened GUI app, and the relevant Thread Border Routers use rotating RFC 7217 privacy addresses, so reliable attribution is not possible without elevated privileges.