mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 12:27:03 +02:00
Advertise underlay addresses on Android
On Android 11+ the app sandbox denies bind() on netlink_route_socket, so the stdlib's net.Interfaces fails with EACCES. localAddrs discarded that error and returned an empty slice, so the node advertised no underlay addresses and peers could only ever reach it at the address a lighthouse observed. A device on the same LAN as a peer was unreachable at its LAN address. Split interface enumeration behind a build-tagged seam and use github.com/wlynxg/anet on Android, which reads RTM_GETADDR from an unbound socket. Interface addresses have to come from anet as well, since net.Interface.Addrs goes back through the same denied path. Every other platform keeps the net package implementation. Stop discarding the enumeration errors, which are exceptional now that the sandbox case is handled. anet needs -ldflags=-checklinkname=0 on Go 1.23+. Nebula ships no Android binaries, so build-test-mobile is unaffected, but consumers linking Android artifacts will need the flag.
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package nebula
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"testing"
|
||||
@@ -401,3 +404,85 @@ func TestHostMap_RelayState(t *testing.T) {
|
||||
assert.Equal(t, []netip.Addr{}, h1.relayState.relays)
|
||||
|
||||
}
|
||||
|
||||
func TestCollectLocalAddrs(t *testing.T) {
|
||||
ifaces := []net.Interface{
|
||||
{Index: 1, Name: "lo"},
|
||||
{Index: 2, Name: "eth0"},
|
||||
{Index: 3, Name: "docker0"},
|
||||
}
|
||||
addrs := map[string][]net.Addr{
|
||||
"lo": {
|
||||
&net.IPNet{IP: net.ParseIP("127.0.0.1"), Mask: net.CIDRMask(8, 32)},
|
||||
&net.IPNet{IP: net.ParseIP("::1"), Mask: net.CIDRMask(128, 128)},
|
||||
},
|
||||
"eth0": {
|
||||
&net.IPNet{IP: net.ParseIP("10.0.0.5"), Mask: net.CIDRMask(24, 32)},
|
||||
&net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)},
|
||||
&net.IPAddr{IP: net.ParseIP("fd00::5")},
|
||||
},
|
||||
"docker0": {
|
||||
&net.IPNet{IP: net.ParseIP("172.17.0.1"), Mask: net.CIDRMask(16, 32)},
|
||||
},
|
||||
}
|
||||
|
||||
enumerate := func() ([]net.Interface, error) { return ifaces, nil }
|
||||
addrsFor := func(i *net.Interface) ([]net.Addr, error) { return addrs[i.Name], nil }
|
||||
|
||||
// Loopback and link local are dropped, everything else on every interface is kept.
|
||||
out := collectLocalAddrs(test.NewLogger(), nil, enumerate, addrsFor)
|
||||
assert.Equal(t, []netip.Addr{
|
||||
netip.MustParseAddr("10.0.0.5"),
|
||||
netip.MustParseAddr("fd00::5"),
|
||||
netip.MustParseAddr("172.17.0.1"),
|
||||
}, out)
|
||||
|
||||
// An interface the allow list rejects by name is never asked for its addresses.
|
||||
c := config.NewC(test.NewLogger())
|
||||
c.Settings["allowlist"] = map[string]any{
|
||||
"interfaces": map[string]any{`docker.*`: false},
|
||||
}
|
||||
al, err := NewLocalAllowListFromConfig(c, "allowlist")
|
||||
require.NoError(t, err)
|
||||
|
||||
asked := make(map[string]struct{})
|
||||
countingAddrsFor := func(i *net.Interface) ([]net.Addr, error) {
|
||||
asked[i.Name] = struct{}{}
|
||||
return addrs[i.Name], nil
|
||||
}
|
||||
out = collectLocalAddrs(test.NewLogger(), al, enumerate, countingAddrsFor)
|
||||
assert.Equal(t, []netip.Addr{
|
||||
netip.MustParseAddr("10.0.0.5"),
|
||||
netip.MustParseAddr("fd00::5"),
|
||||
}, out)
|
||||
assert.NotContains(t, asked, "docker0")
|
||||
|
||||
// A failure to enumerate interfaces at all is reported rather than silently advertising nothing.
|
||||
logOut := &bytes.Buffer{}
|
||||
out = collectLocalAddrs(
|
||||
test.NewLoggerWithOutput(logOut),
|
||||
nil,
|
||||
func() ([]net.Interface, error) { return nil, errors.New("netlinkrib: permission denied") },
|
||||
addrsFor,
|
||||
)
|
||||
assert.Nil(t, out)
|
||||
assert.Contains(t, logOut.String(), "Failed to enumerate local interfaces")
|
||||
assert.Contains(t, logOut.String(), "netlinkrib: permission denied")
|
||||
|
||||
// One interface failing is reported and skipped, the rest are still collected.
|
||||
logOut.Reset()
|
||||
out = collectLocalAddrs(
|
||||
test.NewLoggerWithOutput(logOut),
|
||||
nil,
|
||||
enumerate,
|
||||
func(i *net.Interface) ([]net.Addr, error) {
|
||||
if i.Name == "eth0" {
|
||||
return nil, errors.New("nope")
|
||||
}
|
||||
return addrs[i.Name], nil
|
||||
},
|
||||
)
|
||||
assert.Equal(t, []netip.Addr{netip.MustParseAddr("172.17.0.1")}, out)
|
||||
assert.Contains(t, logOut.String(), "Failed to get addresses for local interface")
|
||||
assert.Contains(t, logOut.String(), "eth0")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user