Explorar o código

Drop hostQueries to our overlay addresses (#1866)

* Drop hostQueries to our overlay addresses

Don't bother responding to hostQuery's for our overlay addresses.
Matt Richardson hai 1 mes
pai
achega
e50f8128f4
Modificáronse 2 ficheiros con 94 adicións e 54 borrados
  1. 15 1
      lighthouse.go
  2. 79 53
      lighthouse_test.go

+ 15 - 1
lighthouse.go

@@ -34,7 +34,9 @@ type LightHouse struct {
 
 	myVpnNetworks      []netip.Prefix
 	myVpnNetworksTable *bart.Lite
-	punchy             *Punchy
+	// myVpnAddrsTable contains our overlay host addrs, as opposed to the overlay networks
+	myVpnAddrsTable *bart.Lite
+	punchy          *Punchy
 
 	// localAddrsFn enumerates the underlay addresses we advertise. It is a field so tests can supply simulated
 	// addresses rather than whatever this machine's NICs happen to be. Set it before Start.
@@ -104,6 +106,7 @@ func NewLightHouseFromConfig(ctx context.Context, l *slog.Logger, c *config.C, c
 		amLighthouse:       amLighthouse,
 		myVpnNetworks:      cs.myVpnNetworks,
 		myVpnNetworksTable: cs.myVpnNetworksTable,
+		myVpnAddrsTable:    cs.myVpnAddrsTable,
 		addrMap:            make(map[netip.Addr]*RemoteList),
 		nebulaPort:         nebulaPort,
 		punchy:             p,
@@ -1158,6 +1161,17 @@ func (lhh *LightHouseHandler) handleHostQuery(n *NebulaMeta, fromVpnAddrs []neti
 		return
 	}
 
+	// Don't respond to requests for us.
+	if lhh.lh.myVpnAddrsTable.Contains(queryVpnAddr) {
+		if lhh.l.Enabled(context.Background(), slog.LevelDebug) {
+			lhh.l.Debug("Ignoring HostQuery for one of my own addresses",
+				"fromVpnAddrs", fromVpnAddrs,
+				"queryVpnAddr", queryVpnAddr,
+			)
+		}
+		return
+	}
+
 	found, ln, err := lhh.lh.queryAndPrepMessage(queryVpnAddr, func(c *cache) (int, error) {
 		n = lhh.resetMeta()
 		n.Type = NebulaMeta_HostQueryReply

+ 79 - 53
lighthouse_test.go

@@ -27,15 +27,27 @@ func TestOldIPv4Only(t *testing.T) {
 	assert.Equal(t, binary.BigEndian.Uint32(bp[:]), m.GetAddr())
 }
 
+func testCertState(networks ...netip.Prefix) *CertState {
+	cs := &CertState{
+		myVpnNetworks:      networks,
+		myVpnNetworksTable: new(bart.Lite),
+		myVpnAddrs:         make([]netip.Addr, 0, len(networks)),
+		myVpnAddrsTable:    new(bart.Lite),
+	}
+
+	for _, n := range networks {
+		cs.myVpnNetworksTable.Insert(n)
+		cs.myVpnAddrs = append(cs.myVpnAddrs, n.Addr())
+		cs.myVpnAddrsTable.Insert(netip.PrefixFrom(n.Addr(), n.Addr().BitLen()))
+	}
+
+	return cs
+}
+
 func Test_lhStaticMapping(t *testing.T) {
 	l := test.NewLogger()
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/16")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 	lh1 := "10.128.0.2"
 
 	c := config.NewC(l)
@@ -55,12 +67,7 @@ func Test_lhStaticMapping(t *testing.T) {
 func TestReloadLighthouseInterval(t *testing.T) {
 	l := test.NewLogger()
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/16")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 	lh1 := "10.128.0.2"
 
 	c := config.NewC(l)
@@ -90,12 +97,7 @@ func TestReloadLighthouseInterval(t *testing.T) {
 func BenchmarkLighthouseHandleRequest(b *testing.B) {
 	l := test.NewLogger()
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/0")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 
 	c := config.NewC(l)
 	lh, err := NewLightHouseFromConfig(b.Context(), l, c, cs, nil, nil)
@@ -195,12 +197,7 @@ func TestLighthouse_Memory(t *testing.T) {
 	c.Settings["listen"] = map[string]any{"port": 4242}
 
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 	lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
 	lh.ifce = &mockEncWriter{}
 	require.NoError(t, err)
@@ -280,12 +277,7 @@ func TestLighthouse_reload(t *testing.T) {
 	c.Settings["listen"] = map[string]any{"port": 4242}
 
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 
 	lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
 	require.NoError(t, err)
@@ -315,12 +307,7 @@ func TestLighthouse_reloadStaticHostMap(t *testing.T) {
 	}
 
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 
 	lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
 	require.NoError(t, err)
@@ -429,7 +416,9 @@ func TestLighthouse_reloadStaticHostMap(t *testing.T) {
 	assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("3.3.3.3:4242")}, rl.CopyAddrs([]netip.Prefix{}))
 }
 
-func newLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler) testLhReply {
+// sendLHHostRequest delivers a HostQuery to lhh and hands back the writer that
+// captured what it emitted. Pass a nil filter to see every message.
+func sendLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler, filter *NebulaMeta_MessageType) *testEncWriter {
 	req := &NebulaMeta{
 		Type:    NebulaMeta_HostQuery,
 		Details: &NebulaMetaDetails{},
@@ -447,12 +436,59 @@ func newLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, l
 		panic(err)
 	}
 
+	w := &testEncWriter{metaFilter: filter}
+	lhh.HandleRequest(fromAddr, []netip.Addr{myVpnIp}, b, w)
+	return w
+}
+
+func newLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler) testLhReply {
 	filter := NebulaMeta_HostQueryReply
-	w := &testEncWriter{
-		metaFilter: &filter,
+	return sendLHHostRequest(fromAddr, myVpnIp, queryVpnIp, lhh, &filter).lastReply
+}
+
+func TestLighthouse_IgnoresHostQueryForItself(t *testing.T) {
+	// Validate that we don't answer host queries for our own address.
+	l := test.NewLogger()
+
+	myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
+	myVpnIp := myVpnNet.Addr()
+
+	c := config.NewC(l)
+	c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
+	c.Settings["listen"] = map[string]any{"port": 4242}
+	// Add a static_host_map entry for ourselves, so our address
+	// is in the addrMap.
+	c.Settings["static_host_map"] = map[string]any{
+		myVpnIp.String(): []any{"192.168.100.1:4242"},
 	}
-	lhh.HandleRequest(fromAddr, []netip.Addr{myVpnIp}, b, w)
-	return w.lastReply
+
+	lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(myVpnNet), nil, nil)
+	require.NoError(t, err)
+	lh.ifce = &mockEncWriter{}
+	lhh := lh.NewRequestHandler()
+
+	peerVpnIp := netip.MustParseAddr("10.128.0.2")
+	peerUdpAddr := netip.MustParseAddrPort("10.0.0.2:4242")
+	otherVpnIp := netip.MustParseAddr("10.128.0.3")
+	otherUdpAddr := netip.MustParseAddrPort("10.0.0.3:4242")
+
+	newLHHostUpdate(peerUdpAddr, peerVpnIp, []netip.AddrPort{peerUdpAddr}, lhh)
+	newLHHostUpdate(otherUdpAddr, otherVpnIp, []netip.AddrPort{otherUdpAddr}, lhh)
+
+	// Control: a query about a real peer is still answered, and still ends with
+	// the punch notification aimed at the host that was asked about.
+	w := sendLHHostRequest(peerUdpAddr, peerVpnIp, otherVpnIp, lhh, nil)
+	require.NotNil(t, w.lastReply.msg)
+	assert.Equal(t, NebulaMeta_HostPunchNotification, w.lastReply.msg.Type)
+	assert.Equal(t, otherVpnIp, w.lastReply.vpnIp)
+
+	// Now validate that we don't send to ourselves.
+	found, _, err := lh.queryAndPrepMessage(myVpnIp, func(*cache) (int, error) { return 0, nil })
+	require.NoError(t, err)
+	require.True(t, found, "the lighthouse should hold a cache entry for its own address")
+
+	w = sendLHHostRequest(peerUdpAddr, peerVpnIp, myVpnIp, lhh, nil)
+	assert.Nil(t, w.lastReply.msg, "a query about our own address must produce no reply and no punch notification")
 }
 
 func newLHHostUpdate(fromAddr netip.AddrPort, vpnIp netip.Addr, addrs []netip.AddrPort, lhh *LightHouseHandler) {
@@ -642,12 +678,7 @@ func TestLighthouse_Dont_Delete_Static_Hosts(t *testing.T) {
 	}
 
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 	lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
 	require.NoError(t, err)
 	lh.ifce = &mockEncWriter{}
@@ -708,12 +739,7 @@ func TestLighthouse_DeletesWork(t *testing.T) {
 	}
 
 	myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
-	nt := new(bart.Lite)
-	nt.Insert(myVpnNet)
-	cs := &CertState{
-		myVpnNetworks:      []netip.Prefix{myVpnNet},
-		myVpnNetworksTable: nt,
-	}
+	cs := testCertState(myVpnNet)
 	lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
 	require.NoError(t, err)
 	lh.ifce = &mockEncWriter{}