mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-23 13:59:09 +02:00
92 lines
3.4 KiB
Go
92 lines
3.4 KiB
Go
//go:build !windows
|
|
|
|
package routemanager
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/netbirdio/netbird/client/iface/device"
|
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
|
"github.com/netbirdio/netbird/client/iface/wgbackend"
|
|
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
|
)
|
|
|
|
// reconcileWGMock is a minimal iface.WGIface that records AddAllowedIP calls and reports the
|
|
// configured address; every other method is an inert stub because the tests exercise none of them.
|
|
type reconcileWGMock struct {
|
|
mu sync.Mutex
|
|
adds map[string][]netip.Prefix
|
|
addr wgaddr.Address
|
|
}
|
|
|
|
func (m *reconcileWGMock) AddAllowedIP(peerKey string, allowedIP netip.Prefix) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.adds == nil {
|
|
m.adds = map[string][]netip.Prefix{}
|
|
}
|
|
m.adds[peerKey] = append(m.adds[peerKey], allowedIP)
|
|
return nil
|
|
}
|
|
|
|
func (m *reconcileWGMock) added(peerKey string) []netip.Prefix {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
return m.adds[peerKey]
|
|
}
|
|
|
|
func (m *reconcileWGMock) RemoveAllowedIP(string, netip.Prefix) error { return nil }
|
|
func (m *reconcileWGMock) Name() string { return "utun-test" }
|
|
func (m *reconcileWGMock) Address() wgaddr.Address { return m.addr }
|
|
func (m *reconcileWGMock) ToInterface() *net.Interface { return nil }
|
|
func (m *reconcileWGMock) IsUserspaceBind() bool { return false }
|
|
func (m *reconcileWGMock) GetFilter() device.PacketFilter { return nil }
|
|
func (m *reconcileWGMock) GetDevice() *device.FilteredDevice { return nil }
|
|
func (m *reconcileWGMock) GetNet() *wgbackend.NetstackNet { return nil }
|
|
|
|
// TestReconcilePeerAllowedIPs verifies the declarative reconcile re-applies every routed prefix
|
|
// tracked for the peer (self-heal, independent of refcount level) and stays scoped to that peer.
|
|
func TestReconcilePeerAllowedIPs(t *testing.T) {
|
|
wg := &reconcileWGMock{}
|
|
m := &DefaultManager{wgInterface: wg}
|
|
m.allowedIPsRefCounter = refcounter.NewAllowedIPs(
|
|
func(_ netip.Prefix, peerKey string) (string, error) { return peerKey, nil },
|
|
func(netip.Prefix, string) error { return nil },
|
|
)
|
|
|
|
peerA1 := netip.MustParsePrefix("10.0.0.0/24")
|
|
peerA2 := netip.MustParsePrefix("10.1.0.0/24")
|
|
peerB1 := netip.MustParsePrefix("10.2.0.0/24")
|
|
|
|
for prefix, peer := range map[netip.Prefix]string{peerA1: "peerA", peerA2: "peerA", peerB1: "peerB"} {
|
|
_, err := m.allowedIPsRefCounter.Increment(prefix, peer)
|
|
require.NoError(t, err)
|
|
}
|
|
// Extra reference: reconcile must still re-apply the prefix even though its refcount never
|
|
// hit 0 again (the exact case the plain incremental path skips).
|
|
_, err := m.allowedIPsRefCounter.Increment(peerA1, "peerA")
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, m.ReconcilePeerAllowedIPs("peerA"))
|
|
|
|
assert.ElementsMatch(t, []netip.Prefix{peerA1, peerA2}, wg.added("peerA"),
|
|
"reconcile must re-apply all routed prefixes of the peer")
|
|
assert.Empty(t, wg.added("peerB"), "reconcile must not touch another peer's prefixes")
|
|
}
|
|
|
|
// TestReconcilePeerAllowedIPsNoCounter verifies reconcile is a safe no-op before the refcounter is
|
|
// set up.
|
|
func TestReconcilePeerAllowedIPsNoCounter(t *testing.T) {
|
|
wg := &reconcileWGMock{}
|
|
m := &DefaultManager{wgInterface: wg}
|
|
|
|
require.NoError(t, m.ReconcilePeerAllowedIPs("peerA"))
|
|
assert.Empty(t, wg.added("peerA"))
|
|
}
|