Compare commits

...

2 Commits

Author SHA1 Message Date
Zoltán Papp
8f6e916517 [client] Improve error reporting for route socket operations 2026-04-09 17:30:58 +02:00
Zoltán Papp
7a1c2f08b8 [client] Handle EEXIST when adding routes on macOS/BSD
On macOS, routes from a previous session can survive across sleep/wake
cycles even though cleanup reports success. When the new engine instance
tries to add the same route, it fails with "file exists" (EEXIST),
leaving the route untracked by the route manager while the OS still
has it pointing at a stale interface.

Handle this by detecting EEXIST on RTM_ADD, removing the stale route,
and retrying. This matches how Linux silently handles existing routes
via netlink.
2026-04-09 17:19:29 +02:00

View File

@@ -105,7 +105,20 @@ func (r *SysOps) FlushMarkedRoutes() error {
}
func (r *SysOps) addToRouteTable(prefix netip.Prefix, nexthop Nexthop) error {
return r.routeSocket(unix.RTM_ADD, prefix, nexthop)
if err := r.routeSocket(unix.RTM_ADD, prefix, nexthop); err != nil {
if !errors.Is(err, unix.EEXIST) {
return err
}
// Route already exists from a previous session that wasn't cleaned up properly
// (e.g. macOS sleep/wake). Remove the stale route and retry.
log.Infof("Route for %s already exists, replacing with new route", prefix)
if err := r.routeSocket(unix.RTM_DELETE, prefix, nexthop); err != nil {
log.Warnf("Failed to remove stale route for %s: %v", prefix, err)
}
return r.routeSocket(unix.RTM_ADD, prefix, nexthop)
}
return nil
}
func (r *SysOps) removeFromRouteTable(prefix netip.Prefix, nexthop Nexthop) error {
@@ -228,7 +241,7 @@ func (r *SysOps) parseRouteResponse(buf []byte) error {
rtMsg := (*unix.RtMsghdr)(unsafe.Pointer(&buf[0]))
if rtMsg.Errno != 0 {
return fmt.Errorf("parse: %d", rtMsg.Errno)
return fmt.Errorf("route socket: %w", syscall.Errno(rtMsg.Errno))
}
return nil