route_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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. assert.Len(t, routes, 1)
  192. assert.Equal(t, 0, routes[0].MTU)
  193. // bad mtu
  194. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "nope"}}}
  195. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  196. assert.Nil(t, routes)
  197. require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
  198. // low mtu
  199. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "499"}}}
  200. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  201. assert.Nil(t, routes)
  202. require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is below 500: 499")
  203. // bad install
  204. 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"}}}
  205. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  206. assert.Nil(t, routes)
  207. require.EqualError(t, err, "entry 1.install in tun.unsafe_routes is not a boolean: strconv.ParseBool: parsing \"nope\": invalid syntax")
  208. // happy case
  209. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
  210. map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "t"},
  211. map[string]any{"via": "127.0.0.1", "mtu": "8000", "route": "1.0.0.1/32", "install": 0},
  212. map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32", "install": 1},
  213. map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32"},
  214. }}
  215. routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
  216. require.NoError(t, err)
  217. assert.Len(t, routes, 4)
  218. tested := 0
  219. for _, r := range routes {
  220. if r.MTU == 8000 {
  221. assert.Equal(t, "1.0.0.1/32", r.Cidr.String())
  222. assert.False(t, r.Install)
  223. tested++
  224. } else if r.MTU == 9000 {
  225. assert.Equal(t, 9000, r.MTU)
  226. assert.Equal(t, "1.0.0.0/29", r.Cidr.String())
  227. assert.True(t, r.Install)
  228. tested++
  229. } else {
  230. assert.Equal(t, 1500, r.MTU)
  231. assert.Equal(t, 1234, r.Metric)
  232. assert.Equal(t, "1.0.0.2/32", r.Cidr.String())
  233. assert.True(t, r.Install)
  234. tested++
  235. }
  236. }
  237. if tested != 4 {
  238. t.Fatal("Did not see all unsafe_routes")
  239. }
  240. }
  241. func Test_makeRouteTree(t *testing.T) {
  242. l := test.NewLogger()
  243. c := config.NewC(l)
  244. n, err := netip.ParsePrefix("10.0.0.0/24")
  245. require.NoError(t, err)
  246. c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
  247. map[string]any{"via": "192.168.0.1", "route": "1.0.0.0/28"},
  248. map[string]any{"via": "192.168.0.2", "route": "1.0.0.1/32"},
  249. }}
  250. routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
  251. require.NoError(t, err)
  252. assert.Len(t, routes, 2)
  253. routeTree, err := makeRouteTree(l, routes, true)
  254. require.NoError(t, err)
  255. ip, err := netip.ParseAddr("1.0.0.2")
  256. require.NoError(t, err)
  257. r, ok := routeTree.Lookup(ip)
  258. assert.True(t, ok)
  259. nip, err := netip.ParseAddr("192.168.0.1")
  260. require.NoError(t, err)
  261. assert.Equal(t, nip, r[0].Addr())
  262. ip, err = netip.ParseAddr("1.0.0.1")
  263. require.NoError(t, err)
  264. r, ok = routeTree.Lookup(ip)
  265. assert.True(t, ok)
  266. nip, err = netip.ParseAddr("192.168.0.2")
  267. require.NoError(t, err)
  268. assert.Equal(t, nip, r[0].Addr())
  269. ip, err = netip.ParseAddr("1.1.0.1")
  270. require.NoError(t, err)
  271. r, ok = routeTree.Lookup(ip)
  272. assert.False(t, ok)
  273. }
  274. func Test_makeMultipathUnsafeRouteTree(t *testing.T) {
  275. l := test.NewLogger()
  276. c := config.NewC(l)
  277. n, err := netip.ParsePrefix("10.0.0.0/24")
  278. require.NoError(t, err)
  279. c.Settings["tun"] = map[string]any{
  280. "unsafe_routes": []any{
  281. map[string]any{
  282. "route": "192.168.86.0/24",
  283. "via": "192.168.100.10",
  284. },
  285. map[string]any{
  286. "route": "192.168.87.0/24",
  287. "via": []any{
  288. map[string]any{
  289. "gateway": "10.0.0.1",
  290. },
  291. map[string]any{
  292. "gateway": "10.0.0.2",
  293. },
  294. map[string]any{
  295. "gateway": "10.0.0.3",
  296. },
  297. },
  298. },
  299. map[string]any{
  300. "route": "192.168.89.0/24",
  301. "via": []any{
  302. map[string]any{
  303. "gateway": "10.0.0.1",
  304. "weight": 10,
  305. },
  306. map[string]any{
  307. "gateway": "10.0.0.2",
  308. "weight": 5,
  309. },
  310. },
  311. },
  312. },
  313. }
  314. routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
  315. require.NoError(t, err)
  316. assert.Len(t, routes, 3)
  317. routeTree, err := makeRouteTree(l, routes, true)
  318. require.NoError(t, err)
  319. ip, err := netip.ParseAddr("192.168.86.1")
  320. require.NoError(t, err)
  321. r, ok := routeTree.Lookup(ip)
  322. assert.True(t, ok)
  323. nip, err := netip.ParseAddr("192.168.100.10")
  324. require.NoError(t, err)
  325. assert.Equal(t, nip, r[0].Addr())
  326. ip, err = netip.ParseAddr("192.168.87.1")
  327. require.NoError(t, err)
  328. r, ok = routeTree.Lookup(ip)
  329. assert.True(t, ok)
  330. expectedGateways := routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 1),
  331. routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 1),
  332. routing.NewGateway(netip.MustParseAddr("10.0.0.3"), 1)}
  333. routing.CalculateBucketsForGateways(expectedGateways)
  334. assert.ElementsMatch(t, expectedGateways, r)
  335. ip, err = netip.ParseAddr("192.168.89.1")
  336. require.NoError(t, err)
  337. r, ok = routeTree.Lookup(ip)
  338. assert.True(t, ok)
  339. expectedGateways = routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 10),
  340. routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 5)}
  341. routing.CalculateBucketsForGateways(expectedGateways)
  342. assert.ElementsMatch(t, expectedGateways, r)
  343. }