remote_list_test.go 14 KB

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