mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-14 01:08:46 +02:00
[client] Use platform-native routing APIs for freeBSD, macOS and Windows
This commit is contained in:
268
client/internal/routemanager/systemops/systemops_test.go
Normal file
268
client/internal/routemanager/systemops/systemops_test.go
Normal file
@ -0,0 +1,268 @@
|
||||
package systemops
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager/notifier"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager/vars"
|
||||
)
|
||||
|
||||
type mockWGIface struct {
|
||||
address wgaddr.Address
|
||||
name string
|
||||
}
|
||||
|
||||
func (m *mockWGIface) Address() wgaddr.Address {
|
||||
return m.address
|
||||
}
|
||||
|
||||
func (m *mockWGIface) Name() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
func TestSysOps_validateRoute(t *testing.T) {
|
||||
wgNetwork := netip.MustParsePrefix("10.0.0.0/24")
|
||||
mockWG := &mockWGIface{
|
||||
address: wgaddr.Address{
|
||||
IP: wgNetwork.Addr(),
|
||||
Network: wgNetwork,
|
||||
},
|
||||
name: "wg0",
|
||||
}
|
||||
|
||||
sysOps := &SysOps{
|
||||
wgInterface: mockWG,
|
||||
notifier: ¬ifier.Notifier{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string
|
||||
expectError bool
|
||||
}{
|
||||
// Valid routes
|
||||
{
|
||||
name: "valid IPv4 route",
|
||||
prefix: "192.168.1.0/24",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid IPv6 route",
|
||||
prefix: "2001:db8::/32",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid single IPv4 host",
|
||||
prefix: "8.8.8.8/32",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid single IPv6 host",
|
||||
prefix: "2001:4860:4860::8888/128",
|
||||
expectError: false,
|
||||
},
|
||||
|
||||
// Invalid routes - loopback
|
||||
{
|
||||
name: "IPv4 loopback",
|
||||
prefix: "127.0.0.1/32",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "IPv6 loopback",
|
||||
prefix: "::1/128",
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Invalid routes - link-local unicast
|
||||
{
|
||||
name: "IPv4 link-local unicast",
|
||||
prefix: "169.254.1.1/32",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "IPv6 link-local unicast",
|
||||
prefix: "fe80::1/128",
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Invalid routes - multicast
|
||||
{
|
||||
name: "IPv4 multicast",
|
||||
prefix: "224.0.0.1/32",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "IPv6 multicast",
|
||||
prefix: "ff02::1/128",
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Invalid routes - link-local multicast
|
||||
{
|
||||
name: "IPv4 link-local multicast",
|
||||
prefix: "224.0.0.0/24",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "IPv6 link-local multicast",
|
||||
prefix: "ff02::/16",
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Invalid routes - interface-local multicast (IPv6 only)
|
||||
{
|
||||
name: "IPv6 interface-local multicast",
|
||||
prefix: "ff01::1/128",
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Invalid routes - overlaps with WG interface network
|
||||
{
|
||||
name: "overlaps with WG network - exact match",
|
||||
prefix: "10.0.0.0/24",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "overlaps with WG network - subset",
|
||||
prefix: "10.0.0.1/32",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "overlaps with WG network - host in range",
|
||||
prefix: "10.0.0.100/32",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prefix, err := netip.ParsePrefix(tt.prefix)
|
||||
require.NoError(t, err, "Failed to parse test prefix %s", tt.prefix)
|
||||
|
||||
err = sysOps.validateRoute(prefix)
|
||||
|
||||
if tt.expectError {
|
||||
require.Error(t, err, "validateRoute() expected error for %s", tt.prefix)
|
||||
assert.Equal(t, vars.ErrRouteNotAllowed, err, "validateRoute() expected ErrRouteNotAllowed for %s", tt.prefix)
|
||||
} else {
|
||||
assert.NoError(t, err, "validateRoute() expected no error for %s", tt.prefix)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysOps_validateRoute_SubnetOverlap(t *testing.T) {
|
||||
wgNetwork := netip.MustParsePrefix("192.168.100.0/24")
|
||||
mockWG := &mockWGIface{
|
||||
address: wgaddr.Address{
|
||||
IP: wgNetwork.Addr(),
|
||||
Network: wgNetwork,
|
||||
},
|
||||
name: "wg0",
|
||||
}
|
||||
|
||||
sysOps := &SysOps{
|
||||
wgInterface: mockWG,
|
||||
notifier: ¬ifier.Notifier{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string
|
||||
expectError bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "identical subnet",
|
||||
prefix: "192.168.100.0/24",
|
||||
expectError: true,
|
||||
description: "exact same network as WG interface",
|
||||
},
|
||||
{
|
||||
name: "broader subnet containing WG network",
|
||||
prefix: "192.168.0.0/16",
|
||||
expectError: false,
|
||||
description: "broader network that contains WG network should be allowed",
|
||||
},
|
||||
{
|
||||
name: "host within WG network",
|
||||
prefix: "192.168.100.50/32",
|
||||
expectError: true,
|
||||
description: "specific host within WG network",
|
||||
},
|
||||
{
|
||||
name: "subnet within WG network",
|
||||
prefix: "192.168.100.128/25",
|
||||
expectError: true,
|
||||
description: "smaller subnet within WG network",
|
||||
},
|
||||
{
|
||||
name: "adjacent subnet - same /23",
|
||||
prefix: "192.168.101.0/24",
|
||||
expectError: false,
|
||||
description: "adjacent subnet, no overlap",
|
||||
},
|
||||
{
|
||||
name: "adjacent subnet - different /16",
|
||||
prefix: "192.167.100.0/24",
|
||||
expectError: false,
|
||||
description: "different network, no overlap",
|
||||
},
|
||||
{
|
||||
name: "WG network broadcast address",
|
||||
prefix: "192.168.100.255/32",
|
||||
expectError: true,
|
||||
description: "broadcast address of WG network",
|
||||
},
|
||||
{
|
||||
name: "WG network first usable",
|
||||
prefix: "192.168.100.1/32",
|
||||
expectError: true,
|
||||
description: "first usable address in WG network",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
prefix, err := netip.ParsePrefix(tt.prefix)
|
||||
require.NoError(t, err, "Failed to parse test prefix %s", tt.prefix)
|
||||
|
||||
err = sysOps.validateRoute(prefix)
|
||||
|
||||
if tt.expectError {
|
||||
require.Error(t, err, "validateRoute() expected error for %s (%s)", tt.prefix, tt.description)
|
||||
assert.Equal(t, vars.ErrRouteNotAllowed, err, "validateRoute() expected ErrRouteNotAllowed for %s (%s)", tt.prefix, tt.description)
|
||||
} else {
|
||||
assert.NoError(t, err, "validateRoute() expected no error for %s (%s)", tt.prefix, tt.description)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysOps_validateRoute_InvalidPrefix(t *testing.T) {
|
||||
wgNetwork := netip.MustParsePrefix("10.0.0.0/24")
|
||||
mockWG := &mockWGIface{
|
||||
address: wgaddr.Address{
|
||||
IP: wgNetwork.Addr(),
|
||||
Network: wgNetwork,
|
||||
},
|
||||
name: "wt0",
|
||||
}
|
||||
|
||||
sysOps := &SysOps{
|
||||
wgInterface: mockWG,
|
||||
notifier: ¬ifier.Notifier{},
|
||||
}
|
||||
|
||||
var invalidPrefix netip.Prefix
|
||||
err := sysOps.validateRoute(invalidPrefix)
|
||||
|
||||
require.Error(t, err, "validateRoute() expected error for invalid prefix")
|
||||
assert.Equal(t, vars.ErrRouteNotAllowed, err, "validateRoute() expected ErrRouteNotAllowed for invalid prefix")
|
||||
}
|
Reference in New Issue
Block a user