From ad599a87edb8966fd15591816f0dfad74c5fbcc2 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Thu, 9 Apr 2026 06:40:29 +0000 Subject: [PATCH] fix(net/netmon): skip RTM_MISS route messages on Darwin On Darwin, the AF_ROUTE socket delivers RTM_MISS on every failed route lookup. When a STUN probe targets an IPv6 address with no route (common on machines without global v6 connectivity), each probe generates an RTM_MISS that netmon treats as a LinkChange. The LinkChange triggers ReSTUN, which fires another probe, creating a self-sustaining loop (~1.3 events/s vs the normal ~1/30s cadence). RTM_MISS is emitted from the route lookup path, not the table mutation path. Route withdrawals always emit RTM_DELETE before any subsequent lookup can miss, so RTM_MISS is never the leading signal for a real topology change. Every mature BSD route-socket consumer (bird, dhcpcd, frr) ignores it for table-change purposes. --- net/netmon/netmon_darwin.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/netmon/netmon_darwin.go b/net/netmon/netmon_darwin.go index cc630112523fa..6338c4b9b5d54 100644 --- a/net/netmon/netmon_darwin.go +++ b/net/netmon/netmon_darwin.go @@ -132,6 +132,18 @@ func (m *darwinRouteMon) skipInterfaceAddrMessage(msg *route.InterfaceAddrMessag } func (m *darwinRouteMon) skipRouteMessage(msg *route.RouteMessage) bool { + // RTM_MISS fires on every failed route lookup (no matching + // entry in the routing table). It scales with traffic volume, + // not network-state changes, and is never the leading signal + // for a topology change — route withdrawals emit RTM_DELETE + // synchronously before any subsequent lookup can miss. + // Letting these through causes netmon to report spurious + // LinkChange events, which trigger a ReSTUN/netcheck loop + // when the destination is unreachable (e.g. STUN probes to + // an IPv6 address with no route). + if msg.Type == unix.RTM_MISS { + return true + } if ip := ipOfAddr(addrType(msg.Addrs, unix.RTAX_DST)); ip.IsLinkLocalUnicast() { // Skip those like: // dst = fe80::b476:66ff:fe30:c8f6%15