mirror of
https://github.com/slackhq/nebula.git
synced 2026-04-01 08:05:18 +02:00
180 lines
6.5 KiB
Go
180 lines
6.5 KiB
Go
package overlay
|
|
|
|
import (
|
|
"io"
|
|
"net/netip"
|
|
"testing"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/slackhq/nebula/config"
|
|
"github.com/slackhq/nebula/routing"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// mockDevice is a minimal Device implementation for testing prepareUnsafeOriginAddr.
|
|
type mockDevice struct {
|
|
networks []netip.Prefix
|
|
unsafeNetworks []netip.Prefix
|
|
snatAddr netip.Prefix
|
|
unsafeSnatAddr netip.Prefix
|
|
}
|
|
|
|
func (d *mockDevice) Read([]byte) (int, error) { return 0, nil }
|
|
func (d *mockDevice) Write([]byte) (int, error) { return 0, nil }
|
|
func (d *mockDevice) Close() error { return nil }
|
|
func (d *mockDevice) Activate() error { return nil }
|
|
func (d *mockDevice) Networks() []netip.Prefix { return d.networks }
|
|
func (d *mockDevice) UnsafeNetworks() []netip.Prefix { return d.unsafeNetworks }
|
|
func (d *mockDevice) SNATAddress() netip.Prefix { return d.snatAddr }
|
|
func (d *mockDevice) UnsafeIPv4OriginAddress() netip.Prefix { return d.unsafeSnatAddr }
|
|
func (d *mockDevice) Name() string { return "mock" }
|
|
func (d *mockDevice) RoutesFor(netip.Addr) routing.Gateways { return routing.Gateways{} }
|
|
func (d *mockDevice) SupportsMultiqueue() bool { return false }
|
|
func (d *mockDevice) NewMultiQueueReader() (io.ReadWriteCloser, error) { return nil, nil }
|
|
|
|
func TestPrepareSnatAddr_V4Primary_NoSnat(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
|
|
// If the device has an IPv4 primary address, no SNAT needed
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("10.0.0.1/24")},
|
|
}
|
|
result := prepareUnsafeOriginAddr(d, l, c, nil)
|
|
assert.Equal(t, netip.Prefix{}, result, "should not assign SNAT addr when device has IPv4 primary")
|
|
}
|
|
|
|
func TestPrepareSnatAddr_V6Primary_NoUnsafeOrRoutes(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
|
|
// IPv6 primary but no unsafe networks or IPv4 routes
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
}
|
|
result := prepareUnsafeOriginAddr(d, l, c, nil)
|
|
assert.Equal(t, netip.Prefix{}, result, "should not assign SNAT addr without IPv4 unsafe networks or routes")
|
|
}
|
|
|
|
func TestPrepareSnatAddr_V6Primary_WithV4Unsafe(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
|
|
// IPv6 primary with IPv4 unsafe network -> should get SNAT addr
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
|
|
}
|
|
result := prepareSnatAddr(d, l, c)
|
|
require.True(t, result.IsValid(), "should assign SNAT addr")
|
|
assert.True(t, result.Addr().Is4(), "SNAT addr should be IPv4")
|
|
assert.True(t, result.Addr().IsLinkLocalUnicast(), "SNAT addr should be link-local")
|
|
assert.Equal(t, 32, result.Bits(), "SNAT addr should be /32")
|
|
|
|
result = prepareUnsafeOriginAddr(d, l, c, nil)
|
|
require.False(t, result.IsValid(), "no routes = no origin addr needed")
|
|
}
|
|
|
|
func TestPrepareUnsafeOriginAddr_V6Primary_WithV4Route(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
|
|
// IPv6 primary with IPv4 route -> should get SNAT addr
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
}
|
|
routes := []Route{
|
|
{Cidr: netip.MustParsePrefix("10.0.0.0/8")},
|
|
}
|
|
result := prepareUnsafeOriginAddr(d, l, c, routes)
|
|
require.True(t, result.IsValid(), "should assign SNAT addr when IPv4 route exists")
|
|
assert.True(t, result.Addr().Is4())
|
|
assert.True(t, result.Addr().IsLinkLocalUnicast())
|
|
|
|
result = prepareSnatAddr(d, l, c)
|
|
require.False(t, result.IsValid(), "no UnsafeNetworks = no snat addr needed")
|
|
}
|
|
|
|
func TestPrepareSnatAddr_V6Primary_V6UnsafeOnly(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
|
|
// IPv6 primary with only IPv6 unsafe network -> no SNAT needed
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("fd01::/64")},
|
|
}
|
|
result := prepareUnsafeOriginAddr(d, l, c, nil)
|
|
assert.Equal(t, netip.Prefix{}, result, "should not assign SNAT addr for IPv6-only unsafe networks")
|
|
}
|
|
|
|
func TestPrepareSnatAddr_ManualAddress(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
c.Settings["tun"] = map[string]any{
|
|
"snat_address_for_4over6": "169.254.42.42",
|
|
}
|
|
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
|
|
}
|
|
result := prepareSnatAddr(d, l, c)
|
|
require.True(t, result.IsValid())
|
|
assert.Equal(t, netip.MustParseAddr("169.254.42.42"), result.Addr())
|
|
assert.Equal(t, 32, result.Bits())
|
|
}
|
|
|
|
func TestPrepareSnatAddr_InvalidManualAddress_Fallback(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
c.Settings["tun"] = map[string]any{
|
|
"snat_address_for_4over6": "not-an-ip",
|
|
}
|
|
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
|
|
}
|
|
result := prepareSnatAddr(d, l, c)
|
|
// Should fall back to auto-assignment
|
|
require.True(t, result.IsValid(), "should fall back to auto-assigned address")
|
|
assert.True(t, result.Addr().Is4())
|
|
assert.True(t, result.Addr().IsLinkLocalUnicast())
|
|
}
|
|
|
|
func TestPrepareSnatAddr_AutoGenerated_Range(t *testing.T) {
|
|
l := logrus.New()
|
|
l.SetLevel(logrus.PanicLevel)
|
|
c := config.NewC(l)
|
|
|
|
d := &mockDevice{
|
|
networks: []netip.Prefix{netip.MustParsePrefix("fd00::1/128")},
|
|
unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")},
|
|
}
|
|
|
|
// Generate several addresses and verify they're all in the expected range
|
|
for i := 0; i < 100; i++ {
|
|
result := prepareSnatAddr(d, l, c)
|
|
require.True(t, result.IsValid())
|
|
addr := result.Addr()
|
|
octets := addr.As4()
|
|
assert.Equal(t, byte(169), octets[0], "first octet should be 169")
|
|
assert.Equal(t, byte(254), octets[1], "second octet should be 254")
|
|
// Should not have .0 in the last octet
|
|
assert.NotEqual(t, byte(0), octets[3], "last octet should not be 0")
|
|
// Should not be 169.254.255.255 (broadcast)
|
|
if octets[2] == 255 {
|
|
assert.NotEqual(t, byte(255), octets[3], "should not be broadcast address")
|
|
}
|
|
}
|
|
}
|