table_test.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package art
  4. import (
  5. crand "crypto/rand"
  6. "fmt"
  7. "math/rand"
  8. "net/netip"
  9. "runtime"
  10. "strconv"
  11. "testing"
  12. "time"
  13. )
  14. func TestRegression(t *testing.T) {
  15. // These tests are specific triggers for subtle correctness issues
  16. // that came up during initial implementation. Even if they seem
  17. // arbitrary, please do not clean them up. They are checking edge
  18. // cases that are very easy to get wrong, and quite difficult for
  19. // the other statistical tests to trigger promptly.
  20. t.Run("prefixes_aligned_on_stride_boundary", func(t *testing.T) {
  21. // Regression test for computePrefixSplit called with equal
  22. // arguments.
  23. tbl := &Table[int]{}
  24. slow := slowPrefixTable[int]{}
  25. p := netip.MustParsePrefix
  26. tbl.Insert(p("226.205.197.0/24"), 1)
  27. slow.insert(p("226.205.197.0/24"), 1)
  28. tbl.Insert(p("226.205.0.0/16"), 2)
  29. slow.insert(p("226.205.0.0/16"), 2)
  30. probe := netip.MustParseAddr("226.205.121.152")
  31. got, gotOK := tbl.Get(probe)
  32. want, wantOK := slow.get(probe)
  33. if !getsEqual(got, gotOK, want, wantOK) {
  34. t.Fatalf("got (%v, %v), want (%v, %v)", got, gotOK, want, wantOK)
  35. }
  36. })
  37. t.Run("parent_prefix_inserted_in_different_orders", func(t *testing.T) {
  38. // Regression test for the off-by-one correction applied
  39. // within computePrefixSplit.
  40. t1, t2 := &Table[int]{}, &Table[int]{}
  41. p := netip.MustParsePrefix
  42. t1.Insert(p("136.20.0.0/16"), 1)
  43. t1.Insert(p("136.20.201.62/32"), 2)
  44. t2.Insert(p("136.20.201.62/32"), 2)
  45. t2.Insert(p("136.20.0.0/16"), 1)
  46. a := netip.MustParseAddr("136.20.54.139")
  47. got1, ok1 := t1.Get(a)
  48. got2, ok2 := t2.Get(a)
  49. if !getsEqual(got1, ok1, got2, ok2) {
  50. t.Errorf("Get(%q) is insertion order dependent: t1=(%v, %v), t2=(%v, %v)", a, got1, ok1, got2, ok2)
  51. }
  52. })
  53. }
  54. func TestComputePrefixSplit(t *testing.T) {
  55. // These tests are partially redundant with other tests. Please
  56. // keep them anyway. computePrefixSplit's behavior is remarkably
  57. // subtle, and all the test cases listed below come from
  58. // hard-earned debugging of malformed route tables.
  59. var tests = []struct {
  60. // prefixA can be a /8, /16 or /24 (v4).
  61. // prefixB can be anything /9 or more specific.
  62. prefixA, prefixB string
  63. lastCommon string
  64. aStride, bStride uint8
  65. }{
  66. {"192.168.1.0/24", "192.168.5.5/32", "192.168.0.0/16", 1, 5},
  67. {"192.168.129.0/24", "192.168.128.0/17", "192.168.0.0/16", 129, 128},
  68. {"192.168.5.0/24", "192.168.0.0/16", "192.0.0.0/8", 168, 168},
  69. {"192.168.0.0/16", "192.168.0.0/16", "192.0.0.0/8", 168, 168},
  70. {"ff:aaaa:aaaa::1/128", "ff:aaaa::/120", "ff:aaaa::/32", 170, 0},
  71. }
  72. for _, test := range tests {
  73. a, b := netip.MustParsePrefix(test.prefixA), netip.MustParsePrefix(test.prefixB)
  74. gotLastCommon, gotAStride, gotBStride := computePrefixSplit(a, b)
  75. if want := netip.MustParsePrefix(test.lastCommon); gotLastCommon != want || gotAStride != test.aStride || gotBStride != test.bStride {
  76. t.Errorf("computePrefixSplit(%q, %q) = %s, %d, %d; want %s, %d, %d", a, b, gotLastCommon, gotAStride, gotBStride, want, test.aStride, test.bStride)
  77. }
  78. }
  79. }
  80. func TestInsert(t *testing.T) {
  81. tbl := &Table[int]{}
  82. p := netip.MustParsePrefix
  83. // Create a new leaf strideTable, with compressed path
  84. tbl.Insert(p("192.168.0.1/32"), 1)
  85. checkRoutes(t, tbl, []tableTest{
  86. {"192.168.0.1", 1},
  87. {"192.168.0.2", -1},
  88. {"192.168.0.3", -1},
  89. {"192.168.0.255", -1},
  90. {"192.168.1.1", -1},
  91. {"192.170.1.1", -1},
  92. {"192.180.0.1", -1},
  93. {"192.180.3.5", -1},
  94. {"10.0.0.5", -1},
  95. {"10.0.0.15", -1},
  96. })
  97. // Insert into previous leaf, no tree changes
  98. tbl.Insert(p("192.168.0.2/32"), 2)
  99. checkRoutes(t, tbl, []tableTest{
  100. {"192.168.0.1", 1},
  101. {"192.168.0.2", 2},
  102. {"192.168.0.3", -1},
  103. {"192.168.0.255", -1},
  104. {"192.168.1.1", -1},
  105. {"192.170.1.1", -1},
  106. {"192.180.0.1", -1},
  107. {"192.180.3.5", -1},
  108. {"10.0.0.5", -1},
  109. {"10.0.0.15", -1},
  110. })
  111. // Insert into previous leaf, unaligned prefix covering the /32s
  112. tbl.Insert(p("192.168.0.0/26"), 7)
  113. checkRoutes(t, tbl, []tableTest{
  114. {"192.168.0.1", 1},
  115. {"192.168.0.2", 2},
  116. {"192.168.0.3", 7},
  117. {"192.168.0.255", -1},
  118. {"192.168.1.1", -1},
  119. {"192.170.1.1", -1},
  120. {"192.180.0.1", -1},
  121. {"192.180.3.5", -1},
  122. {"10.0.0.5", -1},
  123. {"10.0.0.15", -1},
  124. })
  125. // Create a different leaf elsewhere
  126. tbl.Insert(p("10.0.0.0/27"), 3)
  127. checkRoutes(t, tbl, []tableTest{
  128. {"192.168.0.1", 1},
  129. {"192.168.0.2", 2},
  130. {"192.168.0.3", 7},
  131. {"192.168.0.255", -1},
  132. {"192.168.1.1", -1},
  133. {"192.170.1.1", -1},
  134. {"192.180.0.1", -1},
  135. {"192.180.3.5", -1},
  136. {"10.0.0.5", 3},
  137. {"10.0.0.15", 3},
  138. })
  139. // Insert that creates a new intermediate table and a new child
  140. tbl.Insert(p("192.168.1.1/32"), 4)
  141. checkRoutes(t, tbl, []tableTest{
  142. {"192.168.0.1", 1},
  143. {"192.168.0.2", 2},
  144. {"192.168.0.3", 7},
  145. {"192.168.0.255", -1},
  146. {"192.168.1.1", 4},
  147. {"192.170.1.1", -1},
  148. {"192.180.0.1", -1},
  149. {"192.180.3.5", -1},
  150. {"10.0.0.5", 3},
  151. {"10.0.0.15", 3},
  152. })
  153. // Insert that creates a new intermediate table but no new child
  154. tbl.Insert(p("192.170.0.0/16"), 5)
  155. checkRoutes(t, tbl, []tableTest{
  156. {"192.168.0.1", 1},
  157. {"192.168.0.2", 2},
  158. {"192.168.0.3", 7},
  159. {"192.168.0.255", -1},
  160. {"192.168.1.1", 4},
  161. {"192.170.1.1", 5},
  162. {"192.180.0.1", -1},
  163. {"192.180.3.5", -1},
  164. {"10.0.0.5", 3},
  165. {"10.0.0.15", 3},
  166. })
  167. // New leaf in a different subtree, so the next insert can test a
  168. // variant of decompression.
  169. tbl.Insert(p("192.180.0.1/32"), 8)
  170. checkRoutes(t, tbl, []tableTest{
  171. {"192.168.0.1", 1},
  172. {"192.168.0.2", 2},
  173. {"192.168.0.3", 7},
  174. {"192.168.0.255", -1},
  175. {"192.168.1.1", 4},
  176. {"192.170.1.1", 5},
  177. {"192.180.0.1", 8},
  178. {"192.180.3.5", -1},
  179. {"10.0.0.5", 3},
  180. {"10.0.0.15", 3},
  181. })
  182. // Insert that creates a new intermediate table but no new child,
  183. // with an unaligned intermediate
  184. tbl.Insert(p("192.180.0.0/21"), 9)
  185. checkRoutes(t, tbl, []tableTest{
  186. {"192.168.0.1", 1},
  187. {"192.168.0.2", 2},
  188. {"192.168.0.3", 7},
  189. {"192.168.0.255", -1},
  190. {"192.168.1.1", 4},
  191. {"192.170.1.1", 5},
  192. {"192.180.0.1", 8},
  193. {"192.180.3.5", 9},
  194. {"10.0.0.5", 3},
  195. {"10.0.0.15", 3},
  196. })
  197. // Insert a default route, those have their own codepath.
  198. tbl.Insert(p("0.0.0.0/0"), 6)
  199. checkRoutes(t, tbl, []tableTest{
  200. {"192.168.0.1", 1},
  201. {"192.168.0.2", 2},
  202. {"192.168.0.3", 7},
  203. {"192.168.0.255", 6},
  204. {"192.168.1.1", 4},
  205. {"192.170.1.1", 5},
  206. {"192.180.0.1", 8},
  207. {"192.180.3.5", 9},
  208. {"10.0.0.5", 3},
  209. {"10.0.0.15", 3},
  210. })
  211. // Now all of the above again, but for IPv6.
  212. // Create a new leaf strideTable, with compressed path
  213. tbl.Insert(p("ff:aaaa::1/128"), 1)
  214. checkRoutes(t, tbl, []tableTest{
  215. {"ff:aaaa::1", 1},
  216. {"ff:aaaa::2", -1},
  217. {"ff:aaaa::3", -1},
  218. {"ff:aaaa::255", -1},
  219. {"ff:aaaa:aaaa::1", -1},
  220. {"ff:aaaa:aaaa:bbbb::1", -1},
  221. {"ff:cccc::1", -1},
  222. {"ff:cccc::ff", -1},
  223. {"ffff:bbbb::5", -1},
  224. {"ffff:bbbb::15", -1},
  225. })
  226. // Insert into previous leaf, no tree changes
  227. tbl.Insert(p("ff:aaaa::2/128"), 2)
  228. checkRoutes(t, tbl, []tableTest{
  229. {"ff:aaaa::1", 1},
  230. {"ff:aaaa::2", 2},
  231. {"ff:aaaa::3", -1},
  232. {"ff:aaaa::255", -1},
  233. {"ff:aaaa:aaaa::1", -1},
  234. {"ff:aaaa:aaaa:bbbb::1", -1},
  235. {"ff:cccc::1", -1},
  236. {"ff:cccc::ff", -1},
  237. {"ffff:bbbb::5", -1},
  238. {"ffff:bbbb::15", -1},
  239. })
  240. // Insert into previous leaf, unaligned prefix covering the /128s
  241. tbl.Insert(p("ff:aaaa::/125"), 7)
  242. checkRoutes(t, tbl, []tableTest{
  243. {"ff:aaaa::1", 1},
  244. {"ff:aaaa::2", 2},
  245. {"ff:aaaa::3", 7},
  246. {"ff:aaaa::255", -1},
  247. {"ff:aaaa:aaaa::1", -1},
  248. {"ff:aaaa:aaaa:bbbb::1", -1},
  249. {"ff:cccc::1", -1},
  250. {"ff:cccc::ff", -1},
  251. {"ffff:bbbb::5", -1},
  252. {"ffff:bbbb::15", -1},
  253. })
  254. // Create a different leaf elsewhere
  255. tbl.Insert(p("ffff:bbbb::/120"), 3)
  256. checkRoutes(t, tbl, []tableTest{
  257. {"ff:aaaa::1", 1},
  258. {"ff:aaaa::2", 2},
  259. {"ff:aaaa::3", 7},
  260. {"ff:aaaa::255", -1},
  261. {"ff:aaaa:aaaa::1", -1},
  262. {"ff:aaaa:aaaa:bbbb::1", -1},
  263. {"ff:cccc::1", -1},
  264. {"ff:cccc::ff", -1},
  265. {"ffff:bbbb::5", 3},
  266. {"ffff:bbbb::15", 3},
  267. })
  268. // Insert that creates a new intermediate table and a new child
  269. tbl.Insert(p("ff:aaaa:aaaa::1/128"), 4)
  270. checkRoutes(t, tbl, []tableTest{
  271. {"ff:aaaa::1", 1},
  272. {"ff:aaaa::2", 2},
  273. {"ff:aaaa::3", 7},
  274. {"ff:aaaa::255", -1},
  275. {"ff:aaaa:aaaa::1", 4},
  276. {"ff:aaaa:aaaa:bbbb::1", -1},
  277. {"ff:cccc::1", -1},
  278. {"ff:cccc::ff", -1},
  279. {"ffff:bbbb::5", 3},
  280. {"ffff:bbbb::15", 3},
  281. })
  282. // Insert that creates a new intermediate table but no new child
  283. tbl.Insert(p("ff:aaaa:aaaa:bb00::/56"), 5)
  284. checkRoutes(t, tbl, []tableTest{
  285. {"ff:aaaa::1", 1},
  286. {"ff:aaaa::2", 2},
  287. {"ff:aaaa::3", 7},
  288. {"ff:aaaa::255", -1},
  289. {"ff:aaaa:aaaa::1", 4},
  290. {"ff:aaaa:aaaa:bbbb::1", 5},
  291. {"ff:cccc::1", -1},
  292. {"ff:cccc::ff", -1},
  293. {"ffff:bbbb::5", 3},
  294. {"ffff:bbbb::15", 3},
  295. })
  296. // New leaf in a different subtree, so the next insert can test a
  297. // variant of decompression.
  298. tbl.Insert(p("ff:cccc::1/128"), 8)
  299. checkRoutes(t, tbl, []tableTest{
  300. {"ff:aaaa::1", 1},
  301. {"ff:aaaa::2", 2},
  302. {"ff:aaaa::3", 7},
  303. {"ff:aaaa::255", -1},
  304. {"ff:aaaa:aaaa::1", 4},
  305. {"ff:aaaa:aaaa:bbbb::1", 5},
  306. {"ff:cccc::1", 8},
  307. {"ff:cccc::ff", -1},
  308. {"ffff:bbbb::5", 3},
  309. {"ffff:bbbb::15", 3},
  310. })
  311. // Insert that creates a new intermediate table but no new child,
  312. // with an unaligned intermediate
  313. tbl.Insert(p("ff:cccc::/37"), 9)
  314. checkRoutes(t, tbl, []tableTest{
  315. {"ff:aaaa::1", 1},
  316. {"ff:aaaa::2", 2},
  317. {"ff:aaaa::3", 7},
  318. {"ff:aaaa::255", -1},
  319. {"ff:aaaa:aaaa::1", 4},
  320. {"ff:aaaa:aaaa:bbbb::1", 5},
  321. {"ff:cccc::1", 8},
  322. {"ff:cccc::ff", 9},
  323. {"ffff:bbbb::5", 3},
  324. {"ffff:bbbb::15", 3},
  325. })
  326. // Insert a default route, those have their own codepath.
  327. tbl.Insert(p("::/0"), 6)
  328. checkRoutes(t, tbl, []tableTest{
  329. {"ff:aaaa::1", 1},
  330. {"ff:aaaa::2", 2},
  331. {"ff:aaaa::3", 7},
  332. {"ff:aaaa::255", 6},
  333. {"ff:aaaa:aaaa::1", 4},
  334. {"ff:aaaa:aaaa:bbbb::1", 5},
  335. {"ff:cccc::1", 8},
  336. {"ff:cccc::ff", 9},
  337. {"ffff:bbbb::5", 3},
  338. {"ffff:bbbb::15", 3},
  339. })
  340. }
  341. func TestDelete(t *testing.T) {
  342. t.Parallel()
  343. p := netip.MustParsePrefix
  344. t.Run("prefix_in_root", func(t *testing.T) {
  345. // Add/remove prefix from root table.
  346. tbl := &Table[int]{}
  347. checkSize(t, tbl, 2)
  348. tbl.Insert(p("10.0.0.0/8"), 1)
  349. checkRoutes(t, tbl, []tableTest{
  350. {"10.0.0.1", 1},
  351. {"255.255.255.255", -1},
  352. })
  353. checkSize(t, tbl, 2)
  354. tbl.Delete(p("10.0.0.0/8"))
  355. checkRoutes(t, tbl, []tableTest{
  356. {"10.0.0.1", -1},
  357. {"255.255.255.255", -1},
  358. })
  359. checkSize(t, tbl, 2)
  360. })
  361. t.Run("prefix_in_leaf", func(t *testing.T) {
  362. // Create, then delete a single leaf table.
  363. tbl := &Table[int]{}
  364. checkSize(t, tbl, 2)
  365. tbl.Insert(p("192.168.0.1/32"), 1)
  366. checkRoutes(t, tbl, []tableTest{
  367. {"192.168.0.1", 1},
  368. {"255.255.255.255", -1},
  369. })
  370. checkSize(t, tbl, 3)
  371. tbl.Delete(p("192.168.0.1/32"))
  372. checkRoutes(t, tbl, []tableTest{
  373. {"192.168.0.1", -1},
  374. {"255.255.255.255", -1},
  375. })
  376. checkSize(t, tbl, 2)
  377. })
  378. t.Run("intermediate_no_routes", func(t *testing.T) {
  379. // Create an intermediate with 2 children, then delete one leaf.
  380. tbl := &Table[int]{}
  381. checkSize(t, tbl, 2)
  382. tbl.Insert(p("192.168.0.1/32"), 1)
  383. tbl.Insert(p("192.180.0.1/32"), 2)
  384. checkRoutes(t, tbl, []tableTest{
  385. {"192.168.0.1", 1},
  386. {"192.180.0.1", 2},
  387. {"192.40.0.1", -1},
  388. })
  389. checkSize(t, tbl, 5) // 2 roots, 1 intermediate, 2 leaves
  390. tbl.Delete(p("192.180.0.1/32"))
  391. checkRoutes(t, tbl, []tableTest{
  392. {"192.168.0.1", 1},
  393. {"192.180.0.1", -1},
  394. {"192.40.0.1", -1},
  395. })
  396. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  397. })
  398. t.Run("intermediate_with_route", func(t *testing.T) {
  399. // Same, but the intermediate carries a route as well.
  400. tbl := &Table[int]{}
  401. checkSize(t, tbl, 2)
  402. tbl.Insert(p("192.168.0.1/32"), 1)
  403. tbl.Insert(p("192.180.0.1/32"), 2)
  404. tbl.Insert(p("192.0.0.0/10"), 3)
  405. checkRoutes(t, tbl, []tableTest{
  406. {"192.168.0.1", 1},
  407. {"192.180.0.1", 2},
  408. {"192.40.0.1", 3},
  409. {"192.255.0.1", -1},
  410. })
  411. checkSize(t, tbl, 5) // 2 roots, 1 intermediate, 2 leaves
  412. tbl.Delete(p("192.180.0.1/32"))
  413. checkRoutes(t, tbl, []tableTest{
  414. {"192.168.0.1", 1},
  415. {"192.180.0.1", -1},
  416. {"192.40.0.1", 3},
  417. {"192.255.0.1", -1},
  418. })
  419. checkSize(t, tbl, 4) // 2 roots, 1 intermediate w/route, 1 leaf
  420. })
  421. t.Run("intermediate_many_leaves", func(t *testing.T) {
  422. // Intermediate with 3 leaves, then delete one leaf.
  423. tbl := &Table[int]{}
  424. checkSize(t, tbl, 2)
  425. tbl.Insert(p("192.168.0.1/32"), 1)
  426. tbl.Insert(p("192.180.0.1/32"), 2)
  427. tbl.Insert(p("192.200.0.1/32"), 3)
  428. checkRoutes(t, tbl, []tableTest{
  429. {"192.168.0.1", 1},
  430. {"192.180.0.1", 2},
  431. {"192.200.0.1", 3},
  432. {"192.255.0.1", -1},
  433. })
  434. checkSize(t, tbl, 6) // 2 roots, 1 intermediate, 3 leaves
  435. tbl.Delete(p("192.180.0.1/32"))
  436. checkRoutes(t, tbl, []tableTest{
  437. {"192.168.0.1", 1},
  438. {"192.180.0.1", -1},
  439. {"192.200.0.1", 3},
  440. {"192.255.0.1", -1},
  441. })
  442. checkSize(t, tbl, 5) // 2 roots, 1 intermediate, 2 leaves
  443. })
  444. t.Run("nosuchprefix_missing_child", func(t *testing.T) {
  445. // Delete non-existent prefix, missing strideTable path.
  446. tbl := &Table[int]{}
  447. checkSize(t, tbl, 2)
  448. tbl.Insert(p("192.168.0.1/32"), 1)
  449. checkRoutes(t, tbl, []tableTest{
  450. {"192.168.0.1", 1},
  451. {"192.255.0.1", -1},
  452. })
  453. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  454. tbl.Delete(p("200.0.0.0/32")) // lookup miss in root
  455. checkRoutes(t, tbl, []tableTest{
  456. {"192.168.0.1", 1},
  457. {"192.255.0.1", -1},
  458. })
  459. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  460. })
  461. t.Run("nosuchprefix_wrong_turn", func(t *testing.T) {
  462. // Delete non-existent prefix, strideTable path exists but
  463. // with a wrong turn.
  464. tbl := &Table[int]{}
  465. checkSize(t, tbl, 2)
  466. tbl.Insert(p("192.168.0.1/32"), 1)
  467. checkRoutes(t, tbl, []tableTest{
  468. {"192.168.0.1", 1},
  469. {"192.255.0.1", -1},
  470. })
  471. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  472. tbl.Delete(p("192.40.0.0/32")) // finds wrong child
  473. checkRoutes(t, tbl, []tableTest{
  474. {"192.168.0.1", 1},
  475. {"192.255.0.1", -1},
  476. })
  477. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  478. })
  479. t.Run("nosuchprefix_not_in_leaf", func(t *testing.T) {
  480. // Delete non-existent prefix, strideTable path exists but
  481. // leaf doesn't contain route.
  482. tbl := &Table[int]{}
  483. checkSize(t, tbl, 2)
  484. tbl.Insert(p("192.168.0.1/32"), 1)
  485. checkRoutes(t, tbl, []tableTest{
  486. {"192.168.0.1", 1},
  487. {"192.255.0.1", -1},
  488. })
  489. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  490. tbl.Delete(p("192.168.0.5/32")) // right leaf, no route
  491. checkRoutes(t, tbl, []tableTest{
  492. {"192.168.0.1", 1},
  493. {"192.255.0.1", -1},
  494. })
  495. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  496. })
  497. t.Run("intermediate_with_deleted_route", func(t *testing.T) {
  498. // Intermediate table loses its last route and becomes
  499. // compactable.
  500. tbl := &Table[int]{}
  501. checkSize(t, tbl, 2)
  502. tbl.Insert(p("192.168.0.1/32"), 1)
  503. tbl.Insert(p("192.168.0.0/22"), 2)
  504. checkRoutes(t, tbl, []tableTest{
  505. {"192.168.0.1", 1},
  506. {"192.168.0.2", 2},
  507. {"192.255.0.1", -1},
  508. })
  509. checkSize(t, tbl, 4) // 2 roots, 1 intermediate w/route, 1 leaf
  510. tbl.Delete(p("192.168.0.0/22"))
  511. checkRoutes(t, tbl, []tableTest{
  512. {"192.168.0.1", 1},
  513. {"192.168.0.2", -1},
  514. {"192.255.0.1", -1},
  515. })
  516. checkSize(t, tbl, 3) // 2 roots, 1 leaf
  517. })
  518. t.Run("default_route", func(t *testing.T) {
  519. // Default routes have a special case in the code.
  520. tbl := &Table[int]{}
  521. tbl.Insert(p("0.0.0.0/0"), 1)
  522. tbl.Delete(p("0.0.0.0/0"))
  523. checkRoutes(t, tbl, []tableTest{
  524. {"1.2.3.4", -1},
  525. })
  526. checkSize(t, tbl, 2) // 2 roots
  527. })
  528. }
  529. func TestInsertCompare(t *testing.T) {
  530. // Create large route tables repeatedly, and compare Table's
  531. // behavior to a naive and slow but correct implementation.
  532. t.Parallel()
  533. pfxs := randomPrefixes(10_000)
  534. slow := slowPrefixTable[int]{pfxs}
  535. fast := Table[int]{}
  536. for _, pfx := range pfxs {
  537. fast.Insert(pfx.pfx, pfx.val)
  538. }
  539. if debugInsert {
  540. t.Log(fast.debugSummary())
  541. }
  542. seenVals4 := map[int]bool{}
  543. seenVals6 := map[int]bool{}
  544. for range 10_000 {
  545. a := randomAddr()
  546. slowVal, slowOK := slow.get(a)
  547. fastVal, fastOK := fast.Get(a)
  548. if !getsEqual(slowVal, slowOK, fastVal, fastOK) {
  549. t.Fatalf("get(%q) = (%v, %v), want (%v, %v)", a, fastVal, fastOK, slowVal, slowOK)
  550. }
  551. if a.Is6() {
  552. seenVals6[fastVal] = true
  553. } else {
  554. seenVals4[fastVal] = true
  555. }
  556. }
  557. // Empirically, 10k probes into 5k v4 prefixes and 5k v6 prefixes results in
  558. // ~1k distinct values for v4 and ~300 for v6. distinct routes. This sanity
  559. // check that we didn't just return a single route for everything should be
  560. // very generous indeed.
  561. if cnt := len(seenVals4); cnt < 10 {
  562. t.Fatalf("saw %d distinct v4 route results, statistically expected ~1000", cnt)
  563. }
  564. if cnt := len(seenVals6); cnt < 10 {
  565. t.Fatalf("saw %d distinct v6 route results, statistically expected ~300", cnt)
  566. }
  567. }
  568. func TestInsertShuffled(t *testing.T) {
  569. // The order in which you insert prefixes into a route table
  570. // should not matter, as long as you're inserting the same set of
  571. // routes. Verify that this is true, because ART does execute
  572. // vastly different code depending on the order of insertion, even
  573. // if the end result is identical.
  574. //
  575. // If you're here because this package's tests are slow and you
  576. // want to make them faster, please do not delete this test (or
  577. // any test, really). It may seem excessive to test this, but
  578. // these shuffle tests found a lot of very nasty edge cases during
  579. // development, and you _really_ don't want to be debugging a
  580. // faulty route table in production.
  581. t.Parallel()
  582. pfxs := randomPrefixes(1000)
  583. var pfxs2 []slowPrefixEntry[int]
  584. defer func() {
  585. if t.Failed() {
  586. t.Logf("pre-shuffle: %#v", pfxs)
  587. t.Logf("post-shuffle: %#v", pfxs2)
  588. }
  589. }()
  590. for range 10 {
  591. pfxs2 := append([]slowPrefixEntry[int](nil), pfxs...)
  592. rand.Shuffle(len(pfxs2), func(i, j int) { pfxs2[i], pfxs2[j] = pfxs2[j], pfxs2[i] })
  593. addrs := make([]netip.Addr, 0, 10_000)
  594. for range 10_000 {
  595. addrs = append(addrs, randomAddr())
  596. }
  597. rt := Table[int]{}
  598. rt2 := Table[int]{}
  599. for _, pfx := range pfxs {
  600. rt.Insert(pfx.pfx, pfx.val)
  601. }
  602. for _, pfx := range pfxs2 {
  603. rt2.Insert(pfx.pfx, pfx.val)
  604. }
  605. for _, a := range addrs {
  606. val1, ok1 := rt.Get(a)
  607. val2, ok2 := rt2.Get(a)
  608. if !getsEqual(val1, ok1, val2, ok2) {
  609. t.Fatalf("get(%q) = (%v, %v), want (%v, %v)", a, val2, ok2, val1, ok1)
  610. }
  611. }
  612. }
  613. }
  614. func TestDeleteCompare(t *testing.T) {
  615. // Create large route tables repeatedly, delete half of their
  616. // prefixes, and compare Table's behavior to a naive and slow but
  617. // correct implementation.
  618. t.Parallel()
  619. const (
  620. numPrefixes = 10_000 // total prefixes to insert (test deletes 50% of them)
  621. numPerFamily = numPrefixes / 2
  622. deleteCut = numPerFamily / 2
  623. numProbes = 10_000 // random addr lookups to do
  624. )
  625. // We have to do this little dance instead of just using allPrefixes,
  626. // because we want pfxs and toDelete to be non-overlapping sets.
  627. all4, all6 := randomPrefixes4(numPerFamily), randomPrefixes6(numPerFamily)
  628. pfxs := append([]slowPrefixEntry[int](nil), all4[:deleteCut]...)
  629. pfxs = append(pfxs, all6[:deleteCut]...)
  630. toDelete := append([]slowPrefixEntry[int](nil), all4[deleteCut:]...)
  631. toDelete = append(toDelete, all6[deleteCut:]...)
  632. defer func() {
  633. if t.Failed() {
  634. for _, pfx := range pfxs {
  635. fmt.Printf("%q, ", pfx.pfx)
  636. }
  637. fmt.Println("")
  638. for _, pfx := range toDelete {
  639. fmt.Printf("%q, ", pfx.pfx)
  640. }
  641. fmt.Println("")
  642. }
  643. }()
  644. slow := slowPrefixTable[int]{pfxs}
  645. fast := Table[int]{}
  646. for _, pfx := range pfxs {
  647. fast.Insert(pfx.pfx, pfx.val)
  648. }
  649. for _, pfx := range toDelete {
  650. fast.Insert(pfx.pfx, pfx.val)
  651. }
  652. for _, pfx := range toDelete {
  653. fast.Delete(pfx.pfx)
  654. }
  655. seenVals4 := map[int]bool{}
  656. seenVals6 := map[int]bool{}
  657. for range numProbes {
  658. a := randomAddr()
  659. slowVal, slowOK := slow.get(a)
  660. fastVal, fastOK := fast.Get(a)
  661. if !getsEqual(slowVal, slowOK, fastVal, fastOK) {
  662. t.Fatalf("get(%q) = (%v, %v), want (%v, %v)", a, fastVal, fastOK, slowVal, slowOK)
  663. }
  664. if a.Is6() {
  665. seenVals6[fastVal] = true
  666. } else {
  667. seenVals4[fastVal] = true
  668. }
  669. }
  670. // Empirically, 10k probes into 5k v4 prefixes and 5k v6 prefixes results in
  671. // ~1k distinct values for v4 and ~300 for v6. distinct routes. This sanity
  672. // check that we didn't just return a single route for everything should be
  673. // very generous indeed.
  674. if cnt := len(seenVals4); cnt < 10 {
  675. t.Fatalf("saw %d distinct v4 route results, statistically expected ~1000", cnt)
  676. }
  677. if cnt := len(seenVals6); cnt < 10 {
  678. t.Fatalf("saw %d distinct v6 route results, statistically expected ~300", cnt)
  679. }
  680. }
  681. func TestDeleteShuffled(t *testing.T) {
  682. // The order in which you delete prefixes from a route table
  683. // should not matter, as long as you're deleting the same set of
  684. // routes. Verify that this is true, because ART does execute
  685. // vastly different code depending on the order of deletions, even
  686. // if the end result is identical.
  687. //
  688. // If you're here because this package's tests are slow and you
  689. // want to make them faster, please do not delete this test (or
  690. // any test, really). It may seem excessive to test this, but
  691. // these shuffle tests found a lot of very nasty edge cases during
  692. // development, and you _really_ don't want to be debugging a
  693. // faulty route table in production.
  694. t.Parallel()
  695. const (
  696. numPrefixes = 10_000 // prefixes to insert (test deletes 50% of them)
  697. numPerFamily = numPrefixes / 2
  698. deleteCut = numPerFamily / 2
  699. numProbes = 10_000 // random addr lookups to do
  700. )
  701. // We have to do this little dance instead of just using allPrefixes,
  702. // because we want pfxs and toDelete to be non-overlapping sets.
  703. all4, all6 := randomPrefixes4(numPerFamily), randomPrefixes6(numPerFamily)
  704. pfxs := append([]slowPrefixEntry[int](nil), all4[:deleteCut]...)
  705. pfxs = append(pfxs, all6[:deleteCut]...)
  706. toDelete := append([]slowPrefixEntry[int](nil), all4[deleteCut:]...)
  707. toDelete = append(toDelete, all6[deleteCut:]...)
  708. rt := Table[int]{}
  709. for _, pfx := range pfxs {
  710. rt.Insert(pfx.pfx, pfx.val)
  711. }
  712. for _, pfx := range toDelete {
  713. rt.Insert(pfx.pfx, pfx.val)
  714. }
  715. for _, pfx := range toDelete {
  716. rt.Delete(pfx.pfx)
  717. }
  718. for range 10 {
  719. pfxs2 := append([]slowPrefixEntry[int](nil), pfxs...)
  720. toDelete2 := append([]slowPrefixEntry[int](nil), toDelete...)
  721. rand.Shuffle(len(toDelete2), func(i, j int) { toDelete2[i], toDelete2[j] = toDelete2[j], toDelete2[i] })
  722. rt2 := Table[int]{}
  723. for _, pfx := range pfxs2 {
  724. rt2.Insert(pfx.pfx, pfx.val)
  725. }
  726. for _, pfx := range toDelete2 {
  727. rt2.Insert(pfx.pfx, pfx.val)
  728. }
  729. for _, pfx := range toDelete2 {
  730. rt2.Delete(pfx.pfx)
  731. }
  732. // Diffing a deep tree of tables gives cmp.Diff a nervous breakdown, so
  733. // test for equivalence statistically with random probes instead.
  734. for range numProbes {
  735. a := randomAddr()
  736. val1, ok1 := rt.Get(a)
  737. val2, ok2 := rt2.Get(a)
  738. if !getsEqual(val1, ok1, val2, ok2) {
  739. t.Errorf("get(%q) = (%v, %v), want (%v, %v)", a, val2, ok2, val1, ok1)
  740. }
  741. }
  742. }
  743. }
  744. func TestDeleteIsReverseOfInsert(t *testing.T) {
  745. // Insert N prefixes, then delete those same prefixes in reverse
  746. // order. Each deletion should exactly undo the internal structure
  747. // changes that each insert did.
  748. const N = 100
  749. var tab Table[int]
  750. prefixes := randomPrefixes(N)
  751. defer func() {
  752. if t.Failed() {
  753. fmt.Printf("the prefixes that fail the test: %v\n", prefixes)
  754. }
  755. }()
  756. want := make([]string, 0, len(prefixes))
  757. for _, p := range prefixes {
  758. want = append(want, tab.debugSummary())
  759. tab.Insert(p.pfx, p.val)
  760. }
  761. for i := len(prefixes) - 1; i >= 0; i-- {
  762. tab.Delete(prefixes[i].pfx)
  763. if got := tab.debugSummary(); got != want[i] {
  764. t.Fatalf("after delete %d, mismatch:\n\n got: %s\n\nwant: %s", i, got, want[i])
  765. }
  766. }
  767. }
  768. type tableTest struct {
  769. // addr is an IP address string to look up in a route table.
  770. addr string
  771. // want is the expected >=0 value associated with the route, or -1
  772. // if we expect a lookup miss.
  773. want int
  774. }
  775. // checkRoutes verifies that the route lookups in tt return the
  776. // expected results on tbl.
  777. func checkRoutes(t *testing.T, tbl *Table[int], tt []tableTest) {
  778. t.Helper()
  779. for _, tc := range tt {
  780. v, ok := tbl.Get(netip.MustParseAddr(tc.addr))
  781. if !ok && tc.want != -1 {
  782. t.Errorf("lookup %q got (%v, %v), want (_, false)", tc.addr, v, ok)
  783. }
  784. if ok && v != tc.want {
  785. t.Errorf("lookup %q got (%v, %v), want (%v, true)", tc.addr, v, ok, tc.want)
  786. }
  787. }
  788. }
  789. // 100k routes for IPv6, at the current size of strideTable and strideEntry, is
  790. // in the ballpark of 4GiB if you assume worst-case prefix distribution. Future
  791. // optimizations will knock down the memory consumption by over an order of
  792. // magnitude, so for now just skip the 100k benchmarks to stay well away of
  793. // OOMs.
  794. //
  795. // TODO(go/bug/7781): reenable larger table tests once memory utilization is
  796. // optimized.
  797. var benchRouteCount = []int{10, 100, 1000, 10_000} //, 100_000}
  798. // forFamilyAndCount runs the benchmark fn with different sets of
  799. // routes.
  800. //
  801. // fn is called once for each combination of {addr_family, num_routes},
  802. // where addr_family is ipv4 or ipv6, num_routes is the values in
  803. // benchRouteCount.
  804. func forFamilyAndCount(b *testing.B, fn func(b *testing.B, routes []slowPrefixEntry[int])) {
  805. for _, fam := range []string{"ipv4", "ipv6"} {
  806. rng := randomPrefixes4
  807. if fam == "ipv6" {
  808. rng = randomPrefixes6
  809. }
  810. b.Run(fam, func(b *testing.B) {
  811. for _, nroutes := range benchRouteCount {
  812. routes := rng(nroutes)
  813. b.Run(fmt.Sprint(nroutes), func(b *testing.B) {
  814. fn(b, routes)
  815. })
  816. }
  817. })
  818. }
  819. }
  820. func BenchmarkTableInsertion(b *testing.B) {
  821. forFamilyAndCount(b, func(b *testing.B, routes []slowPrefixEntry[int]) {
  822. b.StopTimer()
  823. b.ResetTimer()
  824. var startMem, endMem runtime.MemStats
  825. runtime.ReadMemStats(&startMem)
  826. b.StartTimer()
  827. for range b.N {
  828. var rt Table[int]
  829. for _, route := range routes {
  830. rt.Insert(route.pfx, route.val)
  831. }
  832. }
  833. b.StopTimer()
  834. runtime.ReadMemStats(&endMem)
  835. inserts := float64(b.N) * float64(len(routes))
  836. allocs := float64(endMem.Mallocs - startMem.Mallocs)
  837. bytes := float64(endMem.TotalAlloc - startMem.TotalAlloc)
  838. elapsed := float64(b.Elapsed().Nanoseconds())
  839. elapsedSec := b.Elapsed().Seconds()
  840. b.ReportMetric(elapsed/inserts, "ns/op")
  841. b.ReportMetric(inserts/elapsedSec, "routes/s")
  842. b.ReportMetric(roundFloat64(allocs/inserts), "avg-allocs/op")
  843. b.ReportMetric(roundFloat64(bytes/inserts), "avg-B/op")
  844. })
  845. }
  846. func BenchmarkTableDelete(b *testing.B) {
  847. forFamilyAndCount(b, func(b *testing.B, routes []slowPrefixEntry[int]) {
  848. // Collect memstats for one round of insertions, so we can remove it
  849. // from the total at the end and get only the deletion alloc count.
  850. insertAllocs, insertBytes := getMemCost(func() {
  851. var rt Table[int]
  852. for _, route := range routes {
  853. rt.Insert(route.pfx, route.val)
  854. }
  855. })
  856. insertAllocs *= float64(b.N)
  857. insertBytes *= float64(b.N)
  858. var t runningTimer
  859. allocs, bytes := getMemCost(func() {
  860. for range b.N {
  861. var rt Table[int]
  862. for _, route := range routes {
  863. rt.Insert(route.pfx, route.val)
  864. }
  865. t.Start()
  866. for _, route := range routes {
  867. rt.Delete(route.pfx)
  868. }
  869. t.Stop()
  870. }
  871. })
  872. inserts := float64(b.N) * float64(len(routes))
  873. allocs -= insertAllocs
  874. bytes -= insertBytes
  875. elapsed := float64(t.Elapsed().Nanoseconds())
  876. elapsedSec := t.Elapsed().Seconds()
  877. b.ReportMetric(elapsed/inserts, "ns/op")
  878. b.ReportMetric(inserts/elapsedSec, "routes/s")
  879. b.ReportMetric(roundFloat64(allocs/inserts), "avg-allocs/op")
  880. b.ReportMetric(roundFloat64(bytes/inserts), "avg-B/op")
  881. })
  882. }
  883. func BenchmarkTableGet(b *testing.B) {
  884. forFamilyAndCount(b, func(b *testing.B, routes []slowPrefixEntry[int]) {
  885. genAddr := randomAddr4
  886. if routes[0].pfx.Addr().Is6() {
  887. genAddr = randomAddr6
  888. }
  889. var rt Table[int]
  890. for _, route := range routes {
  891. rt.Insert(route.pfx, route.val)
  892. }
  893. addrAllocs, addrBytes := getMemCost(func() {
  894. // Have to run genAddr more than once, otherwise the reported
  895. // cost is 16 bytes - presumably due to some amortized costs in
  896. // the memory allocator? Either way, empirically 100 iterations
  897. // reliably reports the correct cost.
  898. for range 100 {
  899. _ = genAddr()
  900. }
  901. })
  902. addrAllocs /= 100
  903. addrBytes /= 100
  904. var t runningTimer
  905. allocs, bytes := getMemCost(func() {
  906. for range b.N {
  907. addr := genAddr()
  908. t.Start()
  909. writeSink, _ = rt.Get(addr)
  910. t.Stop()
  911. }
  912. })
  913. b.ReportAllocs() // Enables the output, but we report manually below
  914. allocs -= (addrAllocs * float64(b.N))
  915. bytes -= (addrBytes * float64(b.N))
  916. lookups := float64(b.N)
  917. elapsed := float64(t.Elapsed().Nanoseconds())
  918. elapsedSec := float64(t.Elapsed().Seconds())
  919. b.ReportMetric(elapsed/lookups, "ns/op")
  920. b.ReportMetric(lookups/elapsedSec, "addrs/s")
  921. b.ReportMetric(allocs/lookups, "allocs/op")
  922. b.ReportMetric(bytes/lookups, "B/op")
  923. })
  924. }
  925. // getMemCost runs fn 100 times and returns the number of allocations and bytes
  926. // allocated by each call to fn.
  927. //
  928. // Note that if your fn allocates very little memory (less than ~16 bytes), you
  929. // should make fn run its workload ~100 times and divide the results of
  930. // getMemCost yourself. Otherwise, the byte count you get will be rounded up due
  931. // to the memory allocator's bucketing granularity.
  932. func getMemCost(fn func()) (allocs, bytes float64) {
  933. var start, end runtime.MemStats
  934. runtime.ReadMemStats(&start)
  935. fn()
  936. runtime.ReadMemStats(&end)
  937. return float64(end.Mallocs - start.Mallocs), float64(end.TotalAlloc - start.TotalAlloc)
  938. }
  939. // runningTimer is a timer that keeps track of the cumulative time it's spent
  940. // running since creation. A newly created runningTimer is stopped.
  941. //
  942. // This timer exists because some of our benchmarks have to interleave costly
  943. // ancillary logic in each benchmark iteration, rather than being able to
  944. // front-load all the work before a single b.ResetTimer().
  945. //
  946. // As it turns out, b.StartTimer() and b.StopTimer() are expensive function
  947. // calls, because they do costly memory allocation accounting on every call.
  948. // Starting and stopping the benchmark timer in every b.N loop iteration slows
  949. // the benchmarks down by orders of magnitude.
  950. //
  951. // So, rather than rely on testing.B's timing facility, we use this very
  952. // lightweight timer combined with getMemCost to do our own accounting more
  953. // efficiently.
  954. type runningTimer struct {
  955. cumulative time.Duration
  956. start time.Time
  957. }
  958. func (t *runningTimer) Start() {
  959. t.Stop()
  960. t.start = time.Now()
  961. }
  962. func (t *runningTimer) Stop() {
  963. if t.start.IsZero() {
  964. return
  965. }
  966. t.cumulative += time.Since(t.start)
  967. t.start = time.Time{}
  968. }
  969. func (t *runningTimer) Elapsed() time.Duration {
  970. return t.cumulative
  971. }
  972. func checkSize(t *testing.T, tbl *Table[int], want int) {
  973. t.Helper()
  974. if got := tbl.numStrides(); got != want {
  975. t.Errorf("wrong table size, got %d strides want %d", got, want)
  976. }
  977. }
  978. func (t *Table[T]) numStrides() int {
  979. seen := map[*strideTable[T]]bool{}
  980. return t.numStridesRec(seen, &t.v4) + t.numStridesRec(seen, &t.v6)
  981. }
  982. func (t *Table[T]) numStridesRec(seen map[*strideTable[T]]bool, st *strideTable[T]) int {
  983. ret := 1
  984. if st.childRefs == 0 {
  985. return ret
  986. }
  987. for _, c := range st.children {
  988. if c == nil || seen[c] {
  989. continue
  990. }
  991. seen[c] = true
  992. ret += t.numStridesRec(seen, c)
  993. }
  994. return ret
  995. }
  996. // slowPrefixTable is a routing table implemented as a set of prefixes that are
  997. // explicitly scanned in full for every route lookup. It is very slow, but also
  998. // reasonably easy to verify by inspection, and so a good correctness reference
  999. // for Table.
  1000. type slowPrefixTable[T any] struct {
  1001. prefixes []slowPrefixEntry[T]
  1002. }
  1003. type slowPrefixEntry[T any] struct {
  1004. pfx netip.Prefix
  1005. val T
  1006. }
  1007. func (t *slowPrefixTable[T]) insert(pfx netip.Prefix, val T) {
  1008. pfx = pfx.Masked()
  1009. for i, ent := range t.prefixes {
  1010. if ent.pfx == pfx {
  1011. t.prefixes[i].val = val
  1012. return
  1013. }
  1014. }
  1015. t.prefixes = append(t.prefixes, slowPrefixEntry[T]{pfx, val})
  1016. }
  1017. func (t *slowPrefixTable[T]) get(addr netip.Addr) (ret T, ok bool) {
  1018. bestLen := -1
  1019. for _, pfx := range t.prefixes {
  1020. if pfx.pfx.Contains(addr) && pfx.pfx.Bits() > bestLen {
  1021. ret = pfx.val
  1022. bestLen = pfx.pfx.Bits()
  1023. }
  1024. }
  1025. return ret, bestLen != -1
  1026. }
  1027. // randomPrefixes returns n randomly generated prefixes and associated values,
  1028. // distributed equally between IPv4 and IPv6.
  1029. func randomPrefixes(n int) []slowPrefixEntry[int] {
  1030. pfxs := randomPrefixes4(n / 2)
  1031. pfxs = append(pfxs, randomPrefixes6(n-len(pfxs))...)
  1032. return pfxs
  1033. }
  1034. // randomPrefixes4 returns n randomly generated IPv4 prefixes and associated values.
  1035. func randomPrefixes4(n int) []slowPrefixEntry[int] {
  1036. pfxs := map[netip.Prefix]bool{}
  1037. for len(pfxs) < n {
  1038. len := rand.Intn(33)
  1039. pfx, err := randomAddr4().Prefix(len)
  1040. if err != nil {
  1041. panic(err)
  1042. }
  1043. pfxs[pfx] = true
  1044. }
  1045. ret := make([]slowPrefixEntry[int], 0, len(pfxs))
  1046. for pfx := range pfxs {
  1047. ret = append(ret, slowPrefixEntry[int]{pfx, rand.Int()})
  1048. }
  1049. return ret
  1050. }
  1051. // randomPrefixes6 returns n randomly generated IPv4 prefixes and associated values.
  1052. func randomPrefixes6(n int) []slowPrefixEntry[int] {
  1053. pfxs := map[netip.Prefix]bool{}
  1054. for len(pfxs) < n {
  1055. len := rand.Intn(129)
  1056. pfx, err := randomAddr6().Prefix(len)
  1057. if err != nil {
  1058. panic(err)
  1059. }
  1060. pfxs[pfx] = true
  1061. }
  1062. ret := make([]slowPrefixEntry[int], 0, len(pfxs))
  1063. for pfx := range pfxs {
  1064. ret = append(ret, slowPrefixEntry[int]{pfx, rand.Int()})
  1065. }
  1066. return ret
  1067. }
  1068. // randomAddr returns a randomly generated IP address.
  1069. func randomAddr() netip.Addr {
  1070. if rand.Intn(2) == 1 {
  1071. return randomAddr6()
  1072. } else {
  1073. return randomAddr4()
  1074. }
  1075. }
  1076. // randomAddr4 returns a randomly generated IPv4 address.
  1077. func randomAddr4() netip.Addr {
  1078. var b [4]byte
  1079. if _, err := crand.Read(b[:]); err != nil {
  1080. panic(err)
  1081. }
  1082. return netip.AddrFrom4(b)
  1083. }
  1084. // randomAddr6 returns a randomly generated IPv6 address.
  1085. func randomAddr6() netip.Addr {
  1086. var b [16]byte
  1087. if _, err := crand.Read(b[:]); err != nil {
  1088. panic(err)
  1089. }
  1090. return netip.AddrFrom16(b)
  1091. }
  1092. // roundFloat64 rounds f to 2 decimal places, for display.
  1093. //
  1094. // It round-trips through a float->string->float conversion, so should not be
  1095. // used in a performance critical setting.
  1096. func roundFloat64(f float64) float64 {
  1097. s := fmt.Sprintf("%.2f", f)
  1098. ret, err := strconv.ParseFloat(s, 64)
  1099. if err != nil {
  1100. panic(err)
  1101. }
  1102. return ret
  1103. }