mirror of
https://github.com/netbirdio/netbird.git
synced 2025-07-22 08:40:36 +02:00
269 lines
6.3 KiB
Go
269 lines
6.3 KiB
Go
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")
|
|
}
|