mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-27 18:11:29 +02:00
* [client] Skip IPv6 route tests when the default nexthop is unusable
ensureIPv6DefaultRoute treated a successful netlink RouteAdd as proof that
a usable IPv6 nexthop exists. Installing ::/0 via loopback can succeed while
the kernel still rejects that nexthop for a concrete prefix, which surfaced
on ubuntu22/20260810.260 runners as:
add route to table: netlink add route: invalid argument
Probe the resolved nexthop by installing and removing a discard-prefix route
through the same code path the tests use, and skip when it fails. EEXIST
means the nexthop already carries a route, so it counts as usable.
* [client] Probe the IPv6 nexthop through raw netlink
addRoute swallows EAFNOSUPPORT and EOPNOTSUPP via isOpErr, so a nil return
did not prove the probe route was installed. Call netlink directly so an
unsupported operation skips the test instead of passing as usable.
76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
//go:build linux && !android && privileged
|
|
|
|
package systemops
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"net/netip"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
// ensureIPv6DefaultRoute installs a low-preference IPv6 default route via the
|
|
// loopback interface so route lookups for global IPv6 prefixes resolve in
|
|
// environments without v6 connectivity. Any pre-existing default route wins
|
|
// because of its lower metric.
|
|
func ensureIPv6DefaultRoute(t *testing.T) {
|
|
t.Helper()
|
|
|
|
lo, err := netlink.LinkByName("lo")
|
|
require.NoError(t, err, "find loopback interface")
|
|
|
|
route := &netlink.Route{
|
|
Dst: &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)},
|
|
LinkIndex: lo.Attrs().Index,
|
|
Priority: 1 << 20,
|
|
}
|
|
if err := netlink.RouteAdd(route); err != nil {
|
|
if errors.Is(err, syscall.EEXIST) {
|
|
requireUsableIPv6Nexthop(t)
|
|
return
|
|
}
|
|
t.Skipf("install IPv6 fallback default route: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := netlink.RouteDel(route); err != nil && !errors.Is(err, syscall.ESRCH) {
|
|
t.Logf("delete IPv6 fallback default route: %v", err)
|
|
}
|
|
})
|
|
|
|
requireUsableIPv6Nexthop(t)
|
|
}
|
|
|
|
// requireUsableIPv6Nexthop skips the test unless the resolved IPv6 default
|
|
// nexthop can actually carry a route. Installing the default route succeeding
|
|
// does not imply the kernel accepts it as a nexthop for a concrete prefix.
|
|
func requireUsableIPv6Nexthop(t *testing.T) {
|
|
t.Helper()
|
|
|
|
nexthop, err := GetNextHop(netip.IPv6Unspecified())
|
|
if err != nil {
|
|
t.Skipf("resolve IPv6 default nexthop: %v", err)
|
|
}
|
|
|
|
probe := &netlink.Route{
|
|
Scope: netlink.SCOPE_UNIVERSE,
|
|
Table: syscall.RT_TABLE_MAIN,
|
|
Family: netlink.FAMILY_V6,
|
|
Dst: &net.IPNet{IP: net.ParseIP("100::64"), Mask: net.CIDRMask(128, 128)},
|
|
}
|
|
require.NoError(t, addNextHop(nexthop, probe), "build IPv6 probe route")
|
|
|
|
switch err := netlink.RouteAdd(probe); {
|
|
case err == nil:
|
|
if err := netlink.RouteDel(probe); err != nil && !errors.Is(err, syscall.ESRCH) {
|
|
t.Logf("delete IPv6 probe route: %v", err)
|
|
}
|
|
case errors.Is(err, syscall.EEXIST):
|
|
default:
|
|
t.Skipf("IPv6 nexthop %s unusable for route installation: %v", nexthop, err)
|
|
}
|
|
}
|