From da53c9827fe3542257247264d0eeacc07b1f63e2 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Tue, 19 May 2026 14:43:00 +0200 Subject: [PATCH] Filter scoped/cloned default routes from BSD network monitor RTM_ADD --- .../networkmonitor/check_change_common.go | 20 ++++++++++++------ .../systemops/routeflags_addfilter_bsd.go | 9 ++++++++ .../systemops/routeflags_addfilter_darwin.go | 21 +++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 client/internal/routemanager/systemops/routeflags_addfilter_bsd.go create mode 100644 client/internal/routemanager/systemops/routeflags_addfilter_darwin.go diff --git a/client/internal/networkmonitor/check_change_common.go b/client/internal/networkmonitor/check_change_common.go index a4a4f76ac..f693081a6 100644 --- a/client/internal/networkmonitor/check_change_common.go +++ b/client/internal/networkmonitor/check_change_common.go @@ -50,7 +50,7 @@ func routeCheck(ctx context.Context, fd int, nexthopv4, nexthopv6 systemops.Next switch msg.Type { // handle route changes case unix.RTM_ADD, syscall.RTM_DELETE: - route, err := parseRouteMessage(buf[:n]) + route, flags, err := parseRouteMessage(buf[:n]) if err != nil { log.Debugf("Network monitor: error parsing routing message: %v", err) continue @@ -66,6 +66,10 @@ func routeCheck(ctx context.Context, fd int, nexthopv4, nexthopv6 systemops.Next } switch msg.Type { case unix.RTM_ADD: + if systemops.IgnoreAddedDefaultRoute(flags) { + log.Debugf("Network monitor: ignoring added default route via %s, interface %s, flags %#x", route.Gw, intf, flags) + continue + } log.Infof("Network monitor: default route changed: via %s, interface %s", route.Gw, intf) return nil case unix.RTM_DELETE: @@ -78,22 +82,26 @@ func routeCheck(ctx context.Context, fd int, nexthopv4, nexthopv6 systemops.Next } } -func parseRouteMessage(buf []byte) (*systemops.Route, error) { +func parseRouteMessage(buf []byte) (*systemops.Route, int, error) { msgs, err := route.ParseRIB(route.RIBTypeRoute, buf) if err != nil { - return nil, fmt.Errorf("parse RIB: %v", err) + return nil, 0, fmt.Errorf("parse RIB: %v", err) } if len(msgs) != 1 { - return nil, fmt.Errorf("unexpected RIB message msgs: %v", msgs) + return nil, 0, fmt.Errorf("unexpected RIB message msgs: %v", msgs) } msg, ok := msgs[0].(*route.RouteMessage) if !ok { - return nil, fmt.Errorf("unexpected RIB message type: %T", msgs[0]) + return nil, 0, fmt.Errorf("unexpected RIB message type: %T", msgs[0]) } - return systemops.MsgToRoute(msg) + r, err := systemops.MsgToRoute(msg) + if err != nil { + return nil, 0, err + } + return r, msg.Flags, nil } // waitReadable blocks until fd has data to read, or ctx is cancelled. diff --git a/client/internal/routemanager/systemops/routeflags_addfilter_bsd.go b/client/internal/routemanager/systemops/routeflags_addfilter_bsd.go new file mode 100644 index 000000000..45a1bfceb --- /dev/null +++ b/client/internal/routemanager/systemops/routeflags_addfilter_bsd.go @@ -0,0 +1,9 @@ +//go:build dragonfly || freebsd || netbsd || openbsd + +package systemops + +// IgnoreAddedDefaultRoute reports whether an RTM_ADD default route with the +// given flags should be ignored by the network monitor. +func IgnoreAddedDefaultRoute(flags int) bool { + return filterRoutesByFlags(flags) +} diff --git a/client/internal/routemanager/systemops/routeflags_addfilter_darwin.go b/client/internal/routemanager/systemops/routeflags_addfilter_darwin.go new file mode 100644 index 000000000..e8f655387 --- /dev/null +++ b/client/internal/routemanager/systemops/routeflags_addfilter_darwin.go @@ -0,0 +1,21 @@ +//go:build darwin + +package systemops + +import "golang.org/x/sys/unix" + +// IgnoreAddedDefaultRoute reports whether an RTM_ADD default route with the +// given flags should be ignored by the network monitor. Scoped routes +// (RTF_IFSCOPE) are tied to a specific interface index and cannot replace the +// unscoped default the kernel uses for general egress, so flapping ones (e.g. +// Wi-Fi calling IMS tunnels on ipsec0, Docker bridges, scoped utun defaults) +// must not trigger an engine restart. +func IgnoreAddedDefaultRoute(flags int) bool { + if filterRoutesByFlags(flags) { + return true + } + if flags&unix.RTF_IFSCOPE != 0 { + return true + } + return false +}