established_remotes_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package nebula
  2. import (
  3. "net/netip"
  4. "sync"
  5. "testing"
  6. "github.com/slackhq/nebula/test"
  7. "github.com/slackhq/nebula/udp"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. )
  11. // establishedPeerConn records which destinations the hostmap has told it are established peers, and fails the test
  12. // on a call that doesn't change anything, which would mean a reference count went wrong.
  13. type establishedPeerConn struct {
  14. udp.NoopConn
  15. t *testing.T
  16. mu sync.Mutex
  17. peers map[netip.AddrPort]bool
  18. }
  19. func (c *establishedPeerConn) SetEstablishedPeer(dst netip.AddrPort, established bool) {
  20. c.mu.Lock()
  21. defer c.mu.Unlock()
  22. assert.NotEqual(c.t, established, c.peers[dst], "SetEstablishedPeer(%v, %v) repeated", dst, established)
  23. if established {
  24. c.peers[dst] = true
  25. } else {
  26. delete(c.peers, dst)
  27. }
  28. }
  29. func (c *establishedPeerConn) established() []netip.AddrPort {
  30. c.mu.Lock()
  31. defer c.mu.Unlock()
  32. var out []netip.AddrPort
  33. for dst := range c.peers {
  34. out = append(out, dst)
  35. }
  36. return out
  37. }
  38. // newEstablishedTestHostMap returns a hostmap whose conns are a recording conn and one that doesn't track peers.
  39. func newEstablishedTestHostMap(t *testing.T) (*HostMap, *establishedPeerConn) {
  40. hm := newHostMap(test.NewLogger())
  41. hm.preferredRanges.Store(&[]netip.Prefix{netip.MustParsePrefix("192.168.0.0/16")})
  42. c := &establishedPeerConn{t: t, peers: map[netip.AddrPort]bool{}}
  43. hm.established = newEstablishedRemotes([]udp.Conn{c, udp.NoopConn{}})
  44. require.NotNil(t, hm.established)
  45. return hm, c
  46. }
  47. func newEstablishedTestHostInfo(vpnAddr netip.Addr, index uint32) *HostInfo {
  48. return &HostInfo{
  49. vpnAddrs: []netip.Addr{vpnAddr},
  50. localIndexId: index,
  51. remotes: NewRemoteList([]netip.Addr{vpnAddr}, nil),
  52. }
  53. }
  54. func TestNewEstablishedRemotesWithoutTrackingConns(t *testing.T) {
  55. assert.Nil(t, newEstablishedRemotes([]udp.Conn{udp.NoopConn{}}))
  56. }
  57. // A conn hears about a remote while at least one hostinfo in the main hostmap has it as its direct remote: from
  58. // the add, through roaming and SetRemoteIfPreferred, until the last such hostinfo is deleted or retired by
  59. // MaxHostInfosPerVpnIp. Pending and relayed hostinfos never count.
  60. func TestHostMap_EstablishedRemotes(t *testing.T) {
  61. hm, c := newEstablishedTestHostMap(t)
  62. f := &Interface{}
  63. a := netip.MustParseAddr("10.0.0.1")
  64. r1 := netip.MustParseAddrPort("203.0.113.1:4242")
  65. r2 := netip.MustParseAddrPort("203.0.113.2:4242")
  66. r3 := netip.MustParseAddrPort("192.168.1.3:4242")
  67. // A pending hostinfo, as a handshake sets its remote, isn't established.
  68. h1 := newEstablishedTestHostInfo(a, 1)
  69. h1.SetRemote(r1)
  70. assert.Empty(t, c.established())
  71. // Handshake completion adds it to the main hostmap.
  72. hm.unlockedAddHostInfo(h1, f)
  73. assert.ElementsMatch(t, []netip.AddrPort{r1}, c.established())
  74. // A relayed tunnel has no direct remote.
  75. relayed := newEstablishedTestHostInfo(netip.MustParseAddr("10.0.0.2"), 2)
  76. hm.unlockedAddHostInfo(relayed, f)
  77. assert.ElementsMatch(t, []netip.AddrPort{r1}, c.established())
  78. // A rehandshake's new hostinfo shares the remote, which stays established until both are gone.
  79. h3 := newEstablishedTestHostInfo(a, 3)
  80. h3.SetRemote(r1)
  81. hm.unlockedAddHostInfo(h3, f)
  82. hm.DeleteHostInfo(h1)
  83. assert.ElementsMatch(t, []netip.AddrPort{r1}, c.established())
  84. // Roaming moves the reference.
  85. h3.SetRemote(r2)
  86. assert.ElementsMatch(t, []netip.AddrPort{r2}, c.established())
  87. require.True(t, h3.SetRemoteIfPreferred(hm, ViaSender{UdpAddr: r3}))
  88. assert.ElementsMatch(t, []netip.AddrPort{r3}, c.established())
  89. // A relayed tunnel that hears from its peer directly roams onto a direct remote.
  90. relayed.SetRemote(r1)
  91. assert.ElementsMatch(t, []netip.AddrPort{r1, r3}, c.established())
  92. hm.DeleteHostInfo(relayed)
  93. // A deleted hostinfo lets go, and a SetRemote after that changes nothing.
  94. hm.DeleteHostInfo(h3)
  95. assert.Empty(t, c.established())
  96. h3.SetRemote(r2)
  97. assert.Empty(t, c.established())
  98. hm.DeleteHostInfo(h3)
  99. // Adding past MaxHostInfosPerVpnIp retires the oldest hostinfo and its remote.
  100. var hs []*HostInfo
  101. for i := range MaxHostInfosPerVpnIp + 1 {
  102. h := newEstablishedTestHostInfo(a, uint32(10+i))
  103. h.SetRemote(netip.AddrPortFrom(netip.MustParseAddr("198.51.100.1"), uint16(1000+i)))
  104. hm.unlockedAddHostInfo(h, f)
  105. hs = append(hs, h)
  106. }
  107. assert.Len(t, c.established(), MaxHostInfosPerVpnIp)
  108. assert.NotContains(t, c.established(), hs[0].GetRemote())
  109. }
  110. // Roaming on the receive path races tunnel teardown on the connection manager; whatever the order, a deleted
  111. // hostinfo holds no reference.
  112. func TestHostMap_EstablishedRemotesRoamRacesDelete(t *testing.T) {
  113. hm, c := newEstablishedTestHostMap(t)
  114. f := &Interface{}
  115. a := netip.MustParseAddr("10.0.0.1")
  116. for i := range 200 {
  117. h := newEstablishedTestHostInfo(a, uint32(i+1))
  118. h.SetRemote(netip.MustParseAddrPort("203.0.113.1:4242"))
  119. hm.Lock()
  120. hm.unlockedAddHostInfo(h, f)
  121. hm.Unlock()
  122. var wg sync.WaitGroup
  123. wg.Go(func() { h.SetRemote(netip.AddrPortFrom(netip.MustParseAddr("203.0.113.2"), uint16(i+1))) })
  124. wg.Go(func() { hm.DeleteHostInfo(h) })
  125. wg.Wait()
  126. require.Empty(t, c.established(), "round %d", i)
  127. }
  128. }
  129. // A hostinfo holds its reference in the first tracking hostmap it joins; another's add and delete of it change
  130. // nothing, so neither hostmap's counts go wrong.
  131. func TestHostMap_EstablishedRemotesSingleOwner(t *testing.T) {
  132. hm1, c1 := newEstablishedTestHostMap(t)
  133. hm2, c2 := newEstablishedTestHostMap(t)
  134. f := &Interface{}
  135. r := netip.MustParseAddrPort("203.0.113.1:4242")
  136. h := newEstablishedTestHostInfo(netip.MustParseAddr("10.0.0.1"), 1)
  137. h.SetRemote(r)
  138. hm1.unlockedAddHostInfo(h, f)
  139. hm2.unlockedAddHostInfo(h, f)
  140. assert.ElementsMatch(t, []netip.AddrPort{r}, c1.established())
  141. assert.Empty(t, c2.established())
  142. hm2.DeleteHostInfo(h)
  143. assert.Equal(t, map[netip.AddrPort]int{r: 1}, hm1.established.refs)
  144. assert.Empty(t, hm2.established.refs)
  145. assert.ElementsMatch(t, []netip.AddrPort{r}, c1.established())
  146. hm1.DeleteHostInfo(h)
  147. assert.Empty(t, hm1.established.refs)
  148. assert.Empty(t, hm2.established.refs)
  149. assert.Empty(t, c1.established())
  150. assert.Empty(t, c2.established())
  151. }