1
0

route_test.go 18 KB

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