route_test.go 14 KB

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