mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-14 10:49:07 +02:00
Android 16+ local network protection derives the blocked prefixes from the interface address prefix. A /16 address turns the whole overlay into a local network, so apps without ACCESS_LOCAL_NETWORK cannot reach any peer. Pass the address as /32 and /128 and add the overlay networks to the route list that the Android side turns into VPN routes, on both the initial create and the renew path.
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"
|
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
|
|
|
"github.com/netbirdio/netbird/client/iface/device"
|
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
|
"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() *netstack.Net { 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"))
|
|
}
|