|
|
@@ -0,0 +1,117 @@
|
|
|
+package nicfinder
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "net"
|
|
|
+ "net/netip"
|
|
|
+ "regexp"
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/slackhq/nebula/test"
|
|
|
+ "github.com/stretchr/testify/assert"
|
|
|
+)
|
|
|
+
|
|
|
+type allowAll struct{}
|
|
|
+
|
|
|
+func (allowAll) Allow(netip.Addr) bool { return true }
|
|
|
+func (allowAll) AllowName(string) bool { return true }
|
|
|
+
|
|
|
+type testAllowList struct {
|
|
|
+ bannedName *regexp.Regexp
|
|
|
+ bannedAddr netip.Addr
|
|
|
+}
|
|
|
+
|
|
|
+func (l *testAllowList) Allow(addr netip.Addr) bool {
|
|
|
+ return addr != l.bannedAddr
|
|
|
+}
|
|
|
+
|
|
|
+func (l *testAllowList) AllowName(n string) bool {
|
|
|
+ return !l.bannedName.MatchString(n)
|
|
|
+}
|
|
|
+
|
|
|
+func newTestAllowList(bannedName string) *testAllowList {
|
|
|
+ return &testAllowList{
|
|
|
+ bannedName: regexp.MustCompile(bannedName),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestAllowedAddr(t *testing.T) {
|
|
|
+ ctx, l := context.Background(), test.NewLogger()
|
|
|
+ allowed := func(filter Filter, addrs []net.Addr) []netip.Addr {
|
|
|
+ var out []netip.Addr
|
|
|
+ for _, rawAddr := range addrs {
|
|
|
+ if addr, ok := allowedAddr(ctx, l, filter, rawAddr); ok {
|
|
|
+ out = append(out, addr)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return out
|
|
|
+ }
|
|
|
+ addrs := []net.Addr{
|
|
|
+ &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)},
|
|
|
+ &net.IPNet{IP: net.ParseIP("10.0.0.5"), Mask: net.CIDRMask(24, 32)},
|
|
|
+ &net.IPNet{IP: net.ParseIP("169.254.10.10"), Mask: net.CIDRMask(16, 32)},
|
|
|
+ &net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)},
|
|
|
+ &net.IPAddr{IP: net.ParseIP("fd00::5")},
|
|
|
+ &net.IPNet{},
|
|
|
+ otherAddr{},
|
|
|
+ nil,
|
|
|
+ }
|
|
|
+
|
|
|
+ // Loopback, link local and anything without an IP are dropped, and IPv4 comes back unmapped
|
|
|
+ // even though net.ParseIP hands over a 16 byte slice.
|
|
|
+ assert.Equal(t, []netip.Addr{
|
|
|
+ netip.MustParseAddr("10.0.0.5"),
|
|
|
+ netip.MustParseAddr("fd00::5"),
|
|
|
+ }, allowed(allowAll{}, addrs))
|
|
|
+
|
|
|
+ // The filter sees the unmapped form and can drop a single address.
|
|
|
+ al := newTestAllowList("none")
|
|
|
+ al.bannedAddr = netip.MustParseAddr("10.0.0.5")
|
|
|
+ assert.Equal(t, []netip.Addr{netip.MustParseAddr("fd00::5")}, allowed(al, addrs))
|
|
|
+}
|
|
|
+
|
|
|
+// otherAddr is a net.Addr that is neither *net.IPNet nor *net.IPAddr. netlink.Addr is one such
|
|
|
+// type: it satisfies net.Addr through an embedded *net.IPNet, and passing it directly once
|
|
|
+// silently produced no addresses.
|
|
|
+type otherAddr struct{}
|
|
|
+
|
|
|
+func (otherAddr) Network() string { return "other" }
|
|
|
+func (otherAddr) String() string { return "other" }
|
|
|
+
|
|
|
+// hostFilter counts AllowName calls and can reject every name or every address.
|
|
|
+type hostFilter struct {
|
|
|
+ rejectNames bool
|
|
|
+ rejectAddrs bool
|
|
|
+ asked map[string]int
|
|
|
+}
|
|
|
+
|
|
|
+func (f *hostFilter) Allow(netip.Addr) bool { return !f.rejectAddrs }
|
|
|
+func (f *hostFilter) AllowName(name string) bool {
|
|
|
+ f.asked[name]++
|
|
|
+ return !f.rejectNames
|
|
|
+}
|
|
|
+
|
|
|
+// Runs this platform's localAddrs against the host and checks it honors the whole filter.
|
|
|
+func TestLocalAddrsAppliesFilter(t *testing.T) {
|
|
|
+ ctx, l := context.Background(), test.NewLogger()
|
|
|
+
|
|
|
+ f := &hostFilter{asked: map[string]int{}}
|
|
|
+ all, err := localAddrs(ctx, l, f)
|
|
|
+ if len(f.asked) == 0 {
|
|
|
+ t.Skipf("no interfaces to enumerate here: %v", err)
|
|
|
+ }
|
|
|
+ for name, n := range f.asked {
|
|
|
+ assert.Equal(t, 1, n, "AllowName asked %d times for %s", n, name)
|
|
|
+ }
|
|
|
+ // Loopback and link-local never come back, whatever the filter says.
|
|
|
+ for _, addr := range all {
|
|
|
+ assert.False(t, addr.IsLoopback() || addr.IsLinkLocalUnicast(), "%s", addr)
|
|
|
+ }
|
|
|
+
|
|
|
+ none, _ := localAddrs(ctx, l, &hostFilter{rejectNames: true, asked: map[string]int{}})
|
|
|
+ assert.Empty(t, none, "addresses got past a filter that rejects every interface name")
|
|
|
+
|
|
|
+ none, _ = localAddrs(ctx, l, &hostFilter{rejectAddrs: true, asked: map[string]int{}})
|
|
|
+ assert.Empty(t, none, "addresses got past a filter that rejects every address")
|
|
|
+}
|