Skip to content

Optic00/IPv6Monitor

Repository files navigation

IPv6Monitor

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.

Rationale

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.
  • SystemConfiguration still reported the correct IPv6 router for the active interface.
  • ndp -r still 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 --nwi still reported IPv6 on the interface as reachable.
  • But route -n get -inet6 default returned not in table, and netstat -rn -f inet6 showed 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:

Installation

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.

Prerequisites

  • A Mac running macOS 13 or later.
  • Xcode (available for free from the Mac App Store).

Building the App

  1. Download or clone this repository.
  2. Open IPv6Monitor.xcodeproj in Xcode.
  3. In the top-left corner, ensure the IPv6Monitor target is selected and your Mac is chosen as the destination.
  4. Go to Product > Archive (or simply Run to test it).
  5. If Archiving: Once the archive is complete, click Distribute App, select Custom, then Copy App. This will give you the runnable .app file.
  6. Move the IPv6Monitor.app to your /Applications folder.

Run on Startup

To ensure the monitor runs automatically:

  1. Open System Settings.
  2. Go to General > Login Items & Extensions.
  3. Click the + button under "Open at Login".
  4. Select IPv6Monitor.app.

Sudoers Configuration

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.

Configuration

  1. 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
  2. The installer prints the exact sudoers line to add. Add it via:
    sudo visudo -f /etc/sudoers.d/ipv6monitor
    The line (with your_username replaced 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 *
    
  3. Save and exit (in nano: Ctrl+O, Enter, Ctrl+X).

Never grant pfctl or /sbin/route broadly - only this wrapper with these fixed subcommands.

Upgrading from an earlier version

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.sh

This 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.

Verification

To verify the setup works (it should not ask for a password):

  1. Open a new Terminal window.
  2. Run:
    sudo -n /Library/PrivilegedHelperTools/ipv6monitor-pf status
    If this returns a JSON status line without a password prompt, the route-add/route-delete subcommands (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.

How it works

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:

  1. It attempts to discover the router using ndp (Neighbor Discovery Protocol) if not found via API.
  2. 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 STATIC flag), it removes that stale route first via the wrapper's route-delete subcommand. It never touches a foreign default route that isn't STATIC, 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.
  3. It executes the wrapper's route-add subcommand (sudo -n /Library/PrivilegedHelperTools/ipv6monitor-pf route-add <iface> <gateway>) to restore the route on the monitored interface.

Diagnostics

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, recent configd/networkd logs, …) and logs a compact, greppable line capturing the lifetime of each RA sender at the moment of loss:
    RA@loss total=8 high=[29m50s] medium=[1h59m55s,…] low=[]
    
    Collected over many losses, this shows whether losses correlate with the 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 IPv6 badge 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).

Note on device identification

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.

About

Tool to monitor the IPv6 Default GW on MacOS and reacquires it as needed

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors