remote_list_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. package nebula
  2. import (
  3. "encoding/binary"
  4. "net/netip"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/stretchr/testify/require"
  8. )
  9. // trackedHostnameResults builds a *hostnamesResults with a known cancel function and a
  10. // pre-populated ips map so tests can assert cancellation and verify previously-resolved
  11. // IPs survive a cancel without spinning up a real DNS resolver.
  12. func trackedHostnameResults(cancelFn func(), addrs ...string) *hostnamesResults {
  13. hr := &hostnamesResults{cancelFn: cancelFn}
  14. ips := map[netip.AddrPort]struct{}{}
  15. for _, a := range addrs {
  16. ips[netip.MustParseAddrPort(a)] = struct{}{}
  17. }
  18. hr.ips.Store(&ips)
  19. return hr
  20. }
  21. func TestRemoteList_Rebuild(t *testing.T) {
  22. rl := NewRemoteList([]netip.Addr{netip.MustParseAddr("0.0.0.0")}, nil)
  23. rl.unlockedSetV4(
  24. netip.MustParseAddr("0.0.0.0"),
  25. netip.MustParseAddr("0.0.0.0"),
  26. []*V4AddrPort{
  27. newIp4AndPortFromString("70.199.182.92:1475"), // this is duped
  28. newIp4AndPortFromString("172.17.0.182:10101"),
  29. newIp4AndPortFromString("172.17.1.1:10101"), // this is duped
  30. newIp4AndPortFromString("172.18.0.1:10101"), // this is duped
  31. newIp4AndPortFromString("172.18.0.1:10101"), // this is a dupe
  32. newIp4AndPortFromString("172.19.0.1:10101"),
  33. newIp4AndPortFromString("172.31.0.1:10101"),
  34. newIp4AndPortFromString("172.17.1.1:10101"), // this is a dupe
  35. newIp4AndPortFromString("70.199.182.92:1476"), // almost dupe of 0 with a diff port
  36. newIp4AndPortFromString("70.199.182.92:1475"), // this is a dupe
  37. },
  38. func(netip.Addr, *V4AddrPort) bool { return true },
  39. )
  40. rl.unlockedSetV6(
  41. netip.MustParseAddr("0.0.0.1"),
  42. netip.MustParseAddr("0.0.0.1"),
  43. []*V6AddrPort{
  44. newIp6AndPortFromString("[1::1]:1"), // this is duped
  45. newIp6AndPortFromString("[1::1]:2"), // almost dupe of 0 with a diff port, also gets duped
  46. newIp6AndPortFromString("[1:100::1]:1"),
  47. newIp6AndPortFromString("[1::1]:1"), // this is a dupe
  48. newIp6AndPortFromString("[1::1]:2"), // this is a dupe
  49. },
  50. func(netip.Addr, *V6AddrPort) bool { return true },
  51. )
  52. rl.unlockedSetRelay(
  53. netip.MustParseAddr("0.0.0.1"),
  54. []netip.Addr{
  55. netip.MustParseAddr("1::1"),
  56. netip.MustParseAddr("1.2.3.4"),
  57. netip.MustParseAddr("1.2.3.4"),
  58. netip.MustParseAddr("1::1"),
  59. },
  60. )
  61. rl.Rebuild([]netip.Prefix{})
  62. assert.Len(t, rl.addrs, 10, "addrs contains too many entries")
  63. // ipv6 first, sorted lexically within
  64. assert.Equal(t, "[1::1]:1", rl.addrs[0].String())
  65. assert.Equal(t, "[1::1]:2", rl.addrs[1].String())
  66. assert.Equal(t, "[1:100::1]:1", rl.addrs[2].String())
  67. // ipv4 last, sorted by public first, then private, lexically within them
  68. assert.Equal(t, "70.199.182.92:1475", rl.addrs[3].String())
  69. assert.Equal(t, "70.199.182.92:1476", rl.addrs[4].String())
  70. assert.Equal(t, "172.17.0.182:10101", rl.addrs[5].String())
  71. assert.Equal(t, "172.17.1.1:10101", rl.addrs[6].String())
  72. assert.Equal(t, "172.18.0.1:10101", rl.addrs[7].String())
  73. assert.Equal(t, "172.19.0.1:10101", rl.addrs[8].String())
  74. assert.Equal(t, "172.31.0.1:10101", rl.addrs[9].String())
  75. // Now ensure we can hoist ipv4 up
  76. rl.Rebuild([]netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")})
  77. assert.Len(t, rl.addrs, 10, "addrs contains too many entries")
  78. // ipv4 first, public then private, lexically within them
  79. assert.Equal(t, "70.199.182.92:1475", rl.addrs[0].String())
  80. assert.Equal(t, "70.199.182.92:1476", rl.addrs[1].String())
  81. assert.Equal(t, "172.17.0.182:10101", rl.addrs[2].String())
  82. assert.Equal(t, "172.17.1.1:10101", rl.addrs[3].String())
  83. assert.Equal(t, "172.18.0.1:10101", rl.addrs[4].String())
  84. assert.Equal(t, "172.19.0.1:10101", rl.addrs[5].String())
  85. assert.Equal(t, "172.31.0.1:10101", rl.addrs[6].String())
  86. // ipv6 last, sorted by public first, then private, lexically within them
  87. assert.Equal(t, "[1::1]:1", rl.addrs[7].String())
  88. assert.Equal(t, "[1::1]:2", rl.addrs[8].String())
  89. assert.Equal(t, "[1:100::1]:1", rl.addrs[9].String())
  90. // assert relay deduplicated
  91. assert.Len(t, rl.relays, 2)
  92. assert.Equal(t, "1.2.3.4", rl.relays[0].String())
  93. assert.Equal(t, "1::1", rl.relays[1].String())
  94. // Ensure we can hoist a specific ipv4 range over anything else
  95. rl.Rebuild([]netip.Prefix{netip.MustParsePrefix("172.17.0.0/16")})
  96. assert.Len(t, rl.addrs, 10, "addrs contains too many entries")
  97. // Preferred ipv4 first
  98. assert.Equal(t, "172.17.0.182:10101", rl.addrs[0].String())
  99. assert.Equal(t, "172.17.1.1:10101", rl.addrs[1].String())
  100. // ipv6 next
  101. assert.Equal(t, "[1::1]:1", rl.addrs[2].String())
  102. assert.Equal(t, "[1::1]:2", rl.addrs[3].String())
  103. assert.Equal(t, "[1:100::1]:1", rl.addrs[4].String())
  104. // the remaining ipv4 last
  105. assert.Equal(t, "70.199.182.92:1475", rl.addrs[5].String())
  106. assert.Equal(t, "70.199.182.92:1476", rl.addrs[6].String())
  107. assert.Equal(t, "172.18.0.1:10101", rl.addrs[7].String())
  108. assert.Equal(t, "172.19.0.1:10101", rl.addrs[8].String())
  109. assert.Equal(t, "172.31.0.1:10101", rl.addrs[9].String())
  110. }
  111. func TestRemoteList_ResetForOwner(t *testing.T) {
  112. ourselves := netip.MustParseAddr("10.0.0.1")
  113. otherOwner := netip.MustParseAddr("10.0.0.2")
  114. vpnAddr := netip.MustParseAddr("10.0.0.99")
  115. rl := NewRemoteList([]netip.Addr{vpnAddr}, nil)
  116. rl.unlockedSetV4(ourselves, vpnAddr,
  117. []*V4AddrPort{newIp4AndPortFromString("1.1.1.1:4242")},
  118. func(netip.Addr, *V4AddrPort) bool { return true },
  119. )
  120. rl.unlockedSetV6(ourselves, vpnAddr,
  121. []*V6AddrPort{newIp6AndPortFromString("[1::1]:4242")},
  122. func(netip.Addr, *V6AddrPort) bool { return true },
  123. )
  124. rl.unlockedSetV4(otherOwner, vpnAddr,
  125. []*V4AddrPort{newIp4AndPortFromString("2.2.2.2:4242")},
  126. func(netip.Addr, *V4AddrPort) bool { return true },
  127. )
  128. canceled := 0
  129. hr := trackedHostnameResults(func() { canceled++ }, "3.3.3.3:4242")
  130. rl.Lock()
  131. rl.unlockedSetHostnamesResults(hr)
  132. rl.Unlock()
  133. rl.ResetForOwner(ourselves)
  134. rl.RLock()
  135. defer rl.RUnlock()
  136. assert.Empty(t, rl.cache[ourselves].v4.reported, "our v4 reported should be cleared")
  137. assert.Empty(t, rl.cache[ourselves].v6.reported, "our v6 reported should be cleared")
  138. assert.Len(t, rl.cache[otherOwner].v4.reported, 1, "other owner's contribution must be preserved")
  139. assert.Equal(t, "2.2.2.2:4242", protoV4AddrPortToNetAddrPort(rl.cache[otherOwner].v4.reported[0]).String())
  140. assert.Equal(t, 1, canceled, "DNS resolution goroutine should be canceled")
  141. assert.Same(t, hr, rl.hr, "hostnamesResults must be preserved so DNS-resolved IPs keep feeding addrs until replaced")
  142. assert.NotEmpty(t, rl.hr.GetAddrs(), "previously-resolved IPs should still be readable after cancel")
  143. assert.True(t, rl.shouldRebuild, "shouldRebuild must be set so the next Rebuild recomputes addrs")
  144. }
  145. func TestRemoteList_ResetForOwner_NoEntry(t *testing.T) {
  146. // An owner with no cache entry must not panic; shouldRebuild is still set and any
  147. // existing hostnamesResults is canceled.
  148. rl := NewRemoteList([]netip.Addr{netip.MustParseAddr("10.0.0.99")}, nil)
  149. canceled := 0
  150. rl.Lock()
  151. rl.unlockedSetHostnamesResults(trackedHostnameResults(func() { canceled++ }, "3.3.3.3:4242"))
  152. rl.Unlock()
  153. rl.ResetForOwner(netip.MustParseAddr("10.0.0.1"))
  154. rl.RLock()
  155. defer rl.RUnlock()
  156. assert.Equal(t, 1, canceled)
  157. assert.True(t, rl.shouldRebuild)
  158. }
  159. func TestRemoteList_ClearHostnameResults(t *testing.T) {
  160. rl := NewRemoteList([]netip.Addr{netip.MustParseAddr("10.0.0.99")}, nil)
  161. canceled := 0
  162. hr := trackedHostnameResults(func() { canceled++ }, "3.3.3.3:4242")
  163. rl.Lock()
  164. rl.unlockedSetHostnamesResults(hr)
  165. rl.Unlock()
  166. require.NotEmpty(t, hr.GetAddrs(), "hostnamesResults should have its fastrack IPs populated")
  167. rl.ClearHostnameResults()
  168. rl.RLock()
  169. defer rl.RUnlock()
  170. assert.Equal(t, 1, canceled, "DNS resolution goroutine should be canceled")
  171. assert.Nil(t, rl.hr, "hostnamesResults should be dropped")
  172. assert.True(t, rl.shouldRebuild)
  173. }
  174. func BenchmarkFullRebuild(b *testing.B) {
  175. rl := NewRemoteList([]netip.Addr{netip.MustParseAddr("0.0.0.0")}, nil)
  176. rl.unlockedSetV4(
  177. netip.MustParseAddr("0.0.0.0"),
  178. netip.MustParseAddr("0.0.0.0"),
  179. []*V4AddrPort{
  180. newIp4AndPortFromString("70.199.182.92:1475"),
  181. newIp4AndPortFromString("172.17.0.182:10101"),
  182. newIp4AndPortFromString("172.17.1.1:10101"),
  183. newIp4AndPortFromString("172.18.0.1:10101"),
  184. newIp4AndPortFromString("172.19.0.1:10101"),
  185. newIp4AndPortFromString("172.31.0.1:10101"),
  186. newIp4AndPortFromString("172.17.1.1:10101"), // this is a dupe
  187. newIp4AndPortFromString("70.199.182.92:1476"), // dupe of 0 with a diff port
  188. },
  189. func(netip.Addr, *V4AddrPort) bool { return true },
  190. )
  191. rl.unlockedSetV6(
  192. netip.MustParseAddr("0.0.0.0"),
  193. netip.MustParseAddr("0.0.0.0"),
  194. []*V6AddrPort{
  195. newIp6AndPortFromString("[1::1]:1"),
  196. newIp6AndPortFromString("[1::1]:2"), // dupe of 0 with a diff port
  197. newIp6AndPortFromString("[1:100::1]:1"),
  198. newIp6AndPortFromString("[1::1]:1"), // this is a dupe
  199. },
  200. func(netip.Addr, *V6AddrPort) bool { return true },
  201. )
  202. b.Run("no preferred", func(b *testing.B) {
  203. for i := 0; i < b.N; i++ {
  204. rl.shouldRebuild = true
  205. rl.Rebuild([]netip.Prefix{})
  206. }
  207. })
  208. ipNet1 := netip.MustParsePrefix("172.17.0.0/16")
  209. b.Run("1 preferred", func(b *testing.B) {
  210. for i := 0; i < b.N; i++ {
  211. rl.shouldRebuild = true
  212. rl.Rebuild([]netip.Prefix{ipNet1})
  213. }
  214. })
  215. ipNet2 := netip.MustParsePrefix("70.0.0.0/8")
  216. b.Run("2 preferred", func(b *testing.B) {
  217. for i := 0; i < b.N; i++ {
  218. rl.shouldRebuild = true
  219. rl.Rebuild([]netip.Prefix{ipNet2})
  220. }
  221. })
  222. ipNet3 := netip.MustParsePrefix("0.0.0.0/0")
  223. b.Run("3 preferred", func(b *testing.B) {
  224. for i := 0; i < b.N; i++ {
  225. rl.shouldRebuild = true
  226. rl.Rebuild([]netip.Prefix{ipNet1, ipNet2, ipNet3})
  227. }
  228. })
  229. }
  230. func BenchmarkSortRebuild(b *testing.B) {
  231. rl := NewRemoteList([]netip.Addr{netip.MustParseAddr("0.0.0.0")}, nil)
  232. rl.unlockedSetV4(
  233. netip.MustParseAddr("0.0.0.0"),
  234. netip.MustParseAddr("0.0.0.0"),
  235. []*V4AddrPort{
  236. newIp4AndPortFromString("70.199.182.92:1475"),
  237. newIp4AndPortFromString("172.17.0.182:10101"),
  238. newIp4AndPortFromString("172.17.1.1:10101"),
  239. newIp4AndPortFromString("172.18.0.1:10101"),
  240. newIp4AndPortFromString("172.19.0.1:10101"),
  241. newIp4AndPortFromString("172.31.0.1:10101"),
  242. newIp4AndPortFromString("172.17.1.1:10101"), // this is a dupe
  243. newIp4AndPortFromString("70.199.182.92:1476"), // dupe of 0 with a diff port
  244. },
  245. func(netip.Addr, *V4AddrPort) bool { return true },
  246. )
  247. rl.unlockedSetV6(
  248. netip.MustParseAddr("0.0.0.0"),
  249. netip.MustParseAddr("0.0.0.0"),
  250. []*V6AddrPort{
  251. newIp6AndPortFromString("[1::1]:1"),
  252. newIp6AndPortFromString("[1::1]:2"), // dupe of 0 with a diff port
  253. newIp6AndPortFromString("[1:100::1]:1"),
  254. newIp6AndPortFromString("[1::1]:1"), // this is a dupe
  255. },
  256. func(netip.Addr, *V6AddrPort) bool { return true },
  257. )
  258. b.Run("no preferred", func(b *testing.B) {
  259. for i := 0; i < b.N; i++ {
  260. rl.shouldRebuild = true
  261. rl.Rebuild([]netip.Prefix{})
  262. }
  263. })
  264. ipNet1 := netip.MustParsePrefix("172.17.0.0/16")
  265. rl.Rebuild([]netip.Prefix{ipNet1})
  266. b.Run("1 preferred", func(b *testing.B) {
  267. for i := 0; i < b.N; i++ {
  268. rl.Rebuild([]netip.Prefix{ipNet1})
  269. }
  270. })
  271. ipNet2 := netip.MustParsePrefix("70.0.0.0/8")
  272. rl.Rebuild([]netip.Prefix{ipNet1, ipNet2})
  273. b.Run("2 preferred", func(b *testing.B) {
  274. for i := 0; i < b.N; i++ {
  275. rl.Rebuild([]netip.Prefix{ipNet1, ipNet2})
  276. }
  277. })
  278. ipNet3 := netip.MustParsePrefix("0.0.0.0/0")
  279. rl.Rebuild([]netip.Prefix{ipNet1, ipNet2, ipNet3})
  280. b.Run("3 preferred", func(b *testing.B) {
  281. for i := 0; i < b.N; i++ {
  282. rl.Rebuild([]netip.Prefix{ipNet1, ipNet2, ipNet3})
  283. }
  284. })
  285. }
  286. func newIp4AndPortFromString(s string) *V4AddrPort {
  287. a := netip.MustParseAddrPort(s)
  288. v4Addr := a.Addr().As4()
  289. return &V4AddrPort{
  290. Addr: binary.BigEndian.Uint32(v4Addr[:]),
  291. Port: uint32(a.Port()),
  292. }
  293. }
  294. func newIp6AndPortFromString(s string) *V6AddrPort {
  295. a := netip.MustParseAddrPort(s)
  296. v6Addr := a.Addr().As16()
  297. return &V6AddrPort{
  298. Hi: binary.BigEndian.Uint64(v6Addr[:8]),
  299. Lo: binary.BigEndian.Uint64(v6Addr[8:]),
  300. Port: uint32(a.Port()),
  301. }
  302. }