route_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. package overlay
  2. import (
  3. "fmt"
  4. "net/netip"
  5. "testing"
  6. "github.com/slackhq/nebula/config"
  7. "github.com/slackhq/nebula/header"
  8. "github.com/slackhq/nebula/routing"
  9. "github.com/slackhq/nebula/test"
  10. "github.com/slackhq/nebula/udp"
  11. "github.com/stretchr/testify/assert"
  12. "github.com/stretchr/testify/require"
  13. )
  14. func Test_parseRoutes(t *testing.T) {
  15. l := test.NewLogger()
  16. c := config.NewC(l)
  17. n, err := netip.ParsePrefix("10.0.0.0/24")
  18. require.NoError(t, err)
  19. // test no routes config
  20. routes, err := parseRoutes(c, []netip.Prefix{n})
  21. require.NoError(t, err)
  22. assert.Empty(t, routes)
  23. // not an array
  24. c.Settings["tun"] = map[string]any{"routes": "hi"}
  25. routes, err = parseRoutes(c, []netip.Prefix{n})
  26. assert.Nil(t, routes)
  27. require.EqualError(t, err, "tun.routes is not an array")
  28. // no routes
  29. c.Settings["tun"] = map[string]any{"routes": []any{}}
  30. routes, err = parseRoutes(c, []netip.Prefix{n})
  31. require.NoError(t, err)
  32. assert.Empty(t, routes)
  33. // weird route
  34. c.Settings["tun"] = map[string]any{"routes": []any{"asdf"}}
  35. routes, err = parseRoutes(c, []netip.Prefix{n})
  36. assert.Nil(t, routes)
  37. require.EqualError(t, err, "entry 1 in tun.routes is invalid")
  38. // no mtu
  39. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{}}}
  40. routes, err = parseRoutes(c, []netip.Prefix{n})
  41. assert.Nil(t, routes)
  42. require.EqualError(t, err, "entry 1.mtu in tun.routes is not present")
  43. // bad mtu
  44. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "nope"}}}
  45. routes, err = parseRoutes(c, []netip.Prefix{n})
  46. assert.Nil(t, routes)
  47. require.EqualError(t, err, "entry 1.mtu in tun.routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
  48. // low mtu
  49. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "499"}}}
  50. routes, err = parseRoutes(c, []netip.Prefix{n})
  51. assert.Nil(t, routes)
  52. require.EqualError(t, err, "entry 1.mtu in tun.routes is below 500: 499")
  53. // missing route
  54. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500"}}}
  55. routes, err = parseRoutes(c, []netip.Prefix{n})
  56. assert.Nil(t, routes)
  57. require.EqualError(t, err, "entry 1.route in tun.routes is not present")
  58. // unparsable route
  59. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "nope"}}}
  60. routes, err = parseRoutes(c, []netip.Prefix{n})
  61. assert.Nil(t, routes)
  62. require.EqualError(t, err, "entry 1.route in tun.routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
  63. // below network range
  64. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "1.0.0.0/8"}}}
  65. routes, err = parseRoutes(c, []netip.Prefix{n})
  66. assert.Nil(t, routes)
  67. require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 1.0.0.0/8, networks: [10.0.0.0/24]")
  68. // above network range
  69. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "10.0.1.0/24"}}}
  70. routes, err = parseRoutes(c, []netip.Prefix{n})
  71. assert.Nil(t, routes)
  72. require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 10.0.1.0/24, networks: [10.0.0.0/24]")
  73. // Not in multiple ranges
  74. c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "192.0.0.0/24"}}}
  75. routes, err = parseRoutes(c, []netip.Prefix{n, netip.MustParsePrefix("192.1.0.0/24")})
  76. assert.Nil(t, routes)
  77. require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 192.0.0.0/24, networks: [10.0.0.0/24 192.1.0.0/24]")
  78. // happy case
  79. c.Settings["tun"] = map[string]any{"routes": []any{
  80. map[string]any{"mtu": "9000", "route": "10.0.0.0/29"},
  81. map[string]any{"mtu": "8000", "route": "10.0.0.1/32"},
  82. }}
  83. routes, err = parseRoutes(c, []netip.Prefix{n})
  84. require.NoError(t, err)
  85. assert.Len(t, routes, 2)
  86. tested := 0
  87. for _, r := range routes {
  88. assert.True(t, r.Install)
  89. if r.MTU == 8000 {
  90. assert.Equal(t, "10.0.0.1/32", r.Cidr.String())
  91. tested++
  92. } else {
  93. assert.Equal(t, 9000, r.MTU)
  94. assert.Equal(t, "10.0.0.0/29", r.Cidr.String())
  95. tested++
  96. }
  97. }
  98. if tested != 2 {
  99. t.Fatal("Did not see both routes")
  100. }
  101. }
  102. func Test_parseUnsafeRoutes(t *testing.T) {
  103. l := test.NewLogger()
  104. c := config.NewC(l)
  105. n, err := netip.ParsePrefix("10.0.0.0/24")
  106. require.NoError(t, err)
  107. // test no routes config
  108. routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
  109. require.NoError(t, err)
  110. assert.Empty(t, routes)
  111. // not an array
  112. c.Settings["tun"] = map[string]any{"unsafe_routes": "hi"}
  113. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  114. assert.Nil(t, routes)
  115. require.EqualError(t, err, "tun.unsafe_routes is not an array")
  116. // no routes
  117. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{}}
  118. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  119. require.NoError(t, err)
  120. assert.Empty(t, routes)
  121. // weird route
  122. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{"asdf"}}
  123. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  124. assert.Nil(t, routes)
  125. require.EqualError(t, err, "entry 1 in tun.unsafe_routes is invalid")
  126. // no via
  127. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{}}}
  128. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  129. assert.Nil(t, routes)
  130. require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not present")
  131. // invalid via
  132. for _, invalidValue := range []any{
  133. 127, false, nil, 1.0, []string{"1", "2"},
  134. } {
  135. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": invalidValue}}}
  136. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  137. assert.Nil(t, routes)
  138. require.EqualError(t, err, fmt.Sprintf("entry 1.via in tun.unsafe_routes is not a string or list of gateways: found %T", invalidValue))
  139. }
  140. // Unparsable list of via
  141. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": []string{"1", "2"}}}}
  142. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  143. assert.Nil(t, routes)
  144. require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not a string or list of gateways: found []string")
  145. // unparsable via
  146. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": "nope"}}}
  147. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  148. assert.Nil(t, routes)
  149. require.EqualError(t, err, "entry 1.via in tun.unsafe_routes failed to parse address: ParseAddr(\"nope\"): unable to parse IP")
  150. // unparsable gateway
  151. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"gateway": "1"}}}}}
  152. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  153. assert.Nil(t, routes)
  154. require.EqualError(t, err, "entry .gateway in tun.unsafe_routes[1].via[1] failed to parse address: ParseAddr(\"1\"): unable to parse IP")
  155. // missing gateway element
  156. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"weight": "1"}}}}}
  157. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  158. assert.Nil(t, routes)
  159. require.EqualError(t, err, "entry .gateway in tun.unsafe_routes[1].via[1] is not present")
  160. // unparsable weight element
  161. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"gateway": "10.0.0.1", "weight": "a"}}}}}
  162. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  163. assert.Nil(t, routes)
  164. require.EqualError(t, err, "entry .weight in tun.unsafe_routes[1].via[1] is not an integer")
  165. // missing route
  166. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "500"}}}
  167. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  168. assert.Nil(t, routes)
  169. require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is not present")
  170. // unparsable route
  171. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "500", "route": "nope"}}}
  172. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  173. assert.Nil(t, routes)
  174. require.EqualError(t, err, "entry 1.route in tun.unsafe_routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
  175. // within network range
  176. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "10.0.0.0/24"}}}
  177. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  178. assert.Nil(t, routes)
  179. require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is contained within the configured vpn networks; route: 10.0.0.0/24, network: 10.0.0.0/24")
  180. // below network range
  181. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
  182. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  183. assert.Len(t, routes, 1)
  184. require.NoError(t, err)
  185. // above network range
  186. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "10.0.1.0/24"}}}
  187. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  188. assert.Len(t, routes, 1)
  189. require.NoError(t, err)
  190. // no mtu
  191. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
  192. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  193. assert.Len(t, routes, 1)
  194. assert.Equal(t, 0, routes[0].MTU)
  195. // bad mtu
  196. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "nope"}}}
  197. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  198. assert.Nil(t, routes)
  199. require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
  200. // low mtu
  201. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "499"}}}
  202. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  203. assert.Nil(t, routes)
  204. require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is below 500: 499")
  205. // bad install
  206. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "nope"}}}
  207. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  208. assert.Nil(t, routes)
  209. require.EqualError(t, err, "entry 1.install in tun.unsafe_routes is not a boolean: strconv.ParseBool: parsing \"nope\": invalid syntax")
  210. // happy case
  211. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
  212. map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "t"},
  213. map[string]any{"via": "127.0.0.1", "mtu": "8000", "route": "1.0.0.1/32", "install": 0},
  214. map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32", "install": 1},
  215. map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32"},
  216. }}
  217. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  218. require.NoError(t, err)
  219. assert.Len(t, routes, 4)
  220. tested := 0
  221. for _, r := range routes {
  222. if r.MTU == 8000 {
  223. assert.Equal(t, "1.0.0.1/32", r.Cidr.String())
  224. assert.False(t, r.Install)
  225. tested++
  226. } else if r.MTU == 9000 {
  227. assert.Equal(t, 9000, r.MTU)
  228. assert.Equal(t, "1.0.0.0/29", r.Cidr.String())
  229. assert.True(t, r.Install)
  230. tested++
  231. } else {
  232. assert.Equal(t, 1500, r.MTU)
  233. assert.Equal(t, 1234, r.Metric)
  234. assert.Equal(t, "1.0.0.2/32", r.Cidr.String())
  235. assert.True(t, r.Install)
  236. tested++
  237. }
  238. }
  239. if tested != 4 {
  240. t.Fatal("Did not see all unsafe_routes")
  241. }
  242. }
  243. func Test_makeRouteTree(t *testing.T) {
  244. l := test.NewLogger()
  245. c := config.NewC(l)
  246. n, err := netip.ParsePrefix("10.0.0.0/24")
  247. require.NoError(t, err)
  248. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
  249. map[string]any{"via": "192.168.0.1", "route": "1.0.0.0/28"},
  250. map[string]any{"via": "192.168.0.2", "route": "1.0.0.1/32"},
  251. }}
  252. routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
  253. require.NoError(t, err)
  254. assert.Len(t, routes, 2)
  255. routeTree, err := makeRouteTree(test.NewLogger(), routes, true)
  256. require.NoError(t, err)
  257. ip, err := netip.ParseAddr("1.0.0.2")
  258. require.NoError(t, err)
  259. r, ok := routeTree.Lookup(ip)
  260. assert.True(t, ok)
  261. nip, err := netip.ParseAddr("192.168.0.1")
  262. require.NoError(t, err)
  263. assert.Equal(t, nip, r[0].Addr())
  264. ip, err = netip.ParseAddr("1.0.0.1")
  265. require.NoError(t, err)
  266. r, ok = routeTree.Lookup(ip)
  267. assert.True(t, ok)
  268. nip, err = netip.ParseAddr("192.168.0.2")
  269. require.NoError(t, err)
  270. assert.Equal(t, nip, r[0].Addr())
  271. ip, err = netip.ParseAddr("1.1.0.1")
  272. require.NoError(t, err)
  273. r, ok = routeTree.Lookup(ip)
  274. assert.False(t, ok)
  275. }
  276. func Test_makeMultipathUnsafeRouteTree(t *testing.T) {
  277. l := test.NewLogger()
  278. c := config.NewC(l)
  279. n, err := netip.ParsePrefix("10.0.0.0/24")
  280. require.NoError(t, err)
  281. c.Settings["tun"] = map[string]any{
  282. "unsafe_routes": []any{
  283. map[string]any{
  284. "route": "192.168.86.0/24",
  285. "via": "192.168.100.10",
  286. },
  287. map[string]any{
  288. "route": "192.168.87.0/24",
  289. "via": []any{
  290. map[string]any{
  291. "gateway": "10.0.0.1",
  292. },
  293. map[string]any{
  294. "gateway": "10.0.0.2",
  295. },
  296. map[string]any{
  297. "gateway": "10.0.0.3",
  298. },
  299. },
  300. },
  301. map[string]any{
  302. "route": "192.168.89.0/24",
  303. "via": []any{
  304. map[string]any{
  305. "gateway": "10.0.0.1",
  306. "weight": 10,
  307. },
  308. map[string]any{
  309. "gateway": "10.0.0.2",
  310. "weight": 5,
  311. },
  312. },
  313. },
  314. },
  315. }
  316. routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
  317. require.NoError(t, err)
  318. assert.Len(t, routes, 3)
  319. routeTree, err := makeRouteTree(test.NewLogger(), routes, true)
  320. require.NoError(t, err)
  321. ip, err := netip.ParseAddr("192.168.86.1")
  322. require.NoError(t, err)
  323. r, ok := routeTree.Lookup(ip)
  324. assert.True(t, ok)
  325. nip, err := netip.ParseAddr("192.168.100.10")
  326. require.NoError(t, err)
  327. assert.Equal(t, nip, r[0].Addr())
  328. ip, err = netip.ParseAddr("192.168.87.1")
  329. require.NoError(t, err)
  330. r, ok = routeTree.Lookup(ip)
  331. assert.True(t, ok)
  332. expectedGateways := routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 1),
  333. routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 1),
  334. routing.NewGateway(netip.MustParseAddr("10.0.0.3"), 1)}
  335. routing.CalculateBucketsForGateways(expectedGateways)
  336. assert.ElementsMatch(t, expectedGateways, r)
  337. ip, err = netip.ParseAddr("192.168.89.1")
  338. require.NoError(t, err)
  339. r, ok = routeTree.Lookup(ip)
  340. assert.True(t, ok)
  341. expectedGateways = routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 10),
  342. routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 5)}
  343. routing.CalculateBucketsForGateways(expectedGateways)
  344. assert.ElementsMatch(t, expectedGateways, r)
  345. }
  346. func Test_getMTU(t *testing.T) {
  347. l := test.NewLogger()
  348. c := config.NewC(l)
  349. // unset
  350. assert.Equal(t, DefaultMTU, getMTU(l, c))
  351. // the largest a relayed packet can carry in one underlay buffer
  352. require.Equal(t, udp.MTU, MaxMTU+header.MaxOverhead)
  353. c.Settings["tun"] = map[string]any{"mtu": MaxMTU}
  354. assert.Equal(t, MaxMTU, getMTU(l, c))
  355. // too big is capped
  356. c.Settings["tun"] = map[string]any{"mtu": 9000}
  357. assert.Equal(t, MaxMTU, getMTU(l, c))
  358. }
  359. func Test_getAllRoutesFromConfigCapsMTU(t *testing.T) {
  360. l := test.NewLogger()
  361. c := config.NewC(l)
  362. c.Settings["tun"] = map[string]any{
  363. "routes": []any{
  364. map[string]any{"mtu": "9000", "route": "10.0.0.0/29"},
  365. map[string]any{"mtu": "1300", "route": "10.0.0.8/29"},
  366. },
  367. "unsafe_routes": []any{
  368. map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29"},
  369. },
  370. }
  371. changed, routes, err := getAllRoutesFromConfig(l, c, []netip.Prefix{netip.MustParsePrefix("10.0.0.0/24")}, true)
  372. require.NoError(t, err)
  373. assert.True(t, changed)
  374. mtus := map[string]int{}
  375. for _, r := range routes {
  376. mtus[r.Cidr.String()] = r.MTU
  377. }
  378. assert.Equal(t, map[string]int{"10.0.0.0/29": MaxMTU, "10.0.0.8/29": 1300, "1.0.0.0/29": MaxMTU}, mtus)
  379. }