| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- package overlay
- import (
- "bytes"
- "fmt"
- "net/netip"
- "strings"
- "testing"
- "github.com/slackhq/nebula/config"
- "github.com/slackhq/nebula/header"
- "github.com/slackhq/nebula/routing"
- "github.com/slackhq/nebula/test"
- "github.com/slackhq/nebula/udp"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- func Test_parseRoutes(t *testing.T) {
- l := test.NewLogger()
- c := config.NewC(l)
- n, err := netip.ParsePrefix("10.0.0.0/24")
- require.NoError(t, err)
- // test no routes config
- routes, err := parseRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Empty(t, routes)
- // not an array
- c.Settings["tun"] = map[string]any{"routes": "hi"}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "tun.routes is not an array")
- // no routes
- c.Settings["tun"] = map[string]any{"routes": []any{}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Empty(t, routes)
- // weird route
- c.Settings["tun"] = map[string]any{"routes": []any{"asdf"}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1 in tun.routes is invalid")
- // no mtu
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.mtu in tun.routes is not present")
- // bad mtu
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "nope"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.mtu in tun.routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
- // low mtu
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "499"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.mtu in tun.routes is below 500: 499")
- // missing route
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.route in tun.routes is not present")
- // unparsable route
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "nope"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.route in tun.routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
- // below network range
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "1.0.0.0/8"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- 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]")
- // above network range
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "10.0.1.0/24"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- 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]")
- // Not in multiple ranges
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "192.0.0.0/24"}}}
- routes, err = parseRoutes(c, []netip.Prefix{n, netip.MustParsePrefix("192.1.0.0/24")})
- assert.Nil(t, routes)
- 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]")
- // happy case
- c.Settings["tun"] = map[string]any{"routes": []any{
- map[string]any{"mtu": "9000", "route": "10.0.0.0/29"},
- map[string]any{"mtu": "8000", "route": "10.0.0.1/32"},
- }}
- routes, err = parseRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Len(t, routes, 2)
- tested := 0
- for _, r := range routes {
- assert.True(t, r.Install)
- if r.MTU == 8000 {
- assert.Equal(t, "10.0.0.1/32", r.Cidr.String())
- tested++
- } else {
- assert.Equal(t, 9000, r.MTU)
- assert.Equal(t, "10.0.0.0/29", r.Cidr.String())
- tested++
- }
- }
- if tested != 2 {
- t.Fatal("Did not see both routes")
- }
- }
- func Test_parseUnsafeRoutes(t *testing.T) {
- l := test.NewLogger()
- c := config.NewC(l)
- n, err := netip.ParsePrefix("10.0.0.0/24")
- require.NoError(t, err)
- // test no routes config
- routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Empty(t, routes)
- // not an array
- c.Settings["tun"] = map[string]any{"unsafe_routes": "hi"}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "tun.unsafe_routes is not an array")
- // no routes
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Empty(t, routes)
- // weird route
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{"asdf"}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1 in tun.unsafe_routes is invalid")
- // no via
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not present")
- // invalid via
- for _, invalidValue := range []any{
- 127, false, nil, 1.0, []string{"1", "2"},
- } {
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": invalidValue}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, fmt.Sprintf("entry 1.via in tun.unsafe_routes is not a string or list of gateways: found %T", invalidValue))
- }
- // Unparsable list of via
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": []string{"1", "2"}}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not a string or list of gateways: found []string")
- // unparsable via
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": "nope"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.via in tun.unsafe_routes failed to parse address: ParseAddr(\"nope\"): unable to parse IP")
- // unparsable gateway
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"gateway": "1"}}}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry .gateway in tun.unsafe_routes[1].via[1] failed to parse address: ParseAddr(\"1\"): unable to parse IP")
- // missing gateway element
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"weight": "1"}}}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry .gateway in tun.unsafe_routes[1].via[1] is not present")
- // unparsable weight element
- 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"}}}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry .weight in tun.unsafe_routes[1].via[1] is not an integer")
- // missing route
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "500"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is not present")
- // unparsable route
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "500", "route": "nope"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.route in tun.unsafe_routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
- // within network range
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "10.0.0.0/24"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- 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")
- // below network range
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Len(t, routes, 1)
- require.NoError(t, err)
- // above network range
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "10.0.1.0/24"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Len(t, routes, 1)
- require.NoError(t, err)
- // no mtu
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Len(t, routes, 1)
- assert.Equal(t, 0, routes[0].MTU)
- // bad mtu
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "nope"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
- // low mtu
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "499"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is below 500: 499")
- // bad install
- 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"}}}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- assert.Nil(t, routes)
- require.EqualError(t, err, "entry 1.install in tun.unsafe_routes is not a boolean: strconv.ParseBool: parsing \"nope\": invalid syntax")
- // happy case
- 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": "t"},
- map[string]any{"via": "127.0.0.1", "mtu": "8000", "route": "1.0.0.1/32", "install": 0},
- map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32", "install": 1},
- map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32"},
- }}
- routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Len(t, routes, 4)
- tested := 0
- for _, r := range routes {
- if r.MTU == 8000 {
- assert.Equal(t, "1.0.0.1/32", r.Cidr.String())
- assert.False(t, r.Install)
- tested++
- } else if r.MTU == 9000 {
- assert.Equal(t, 9000, r.MTU)
- assert.Equal(t, "1.0.0.0/29", r.Cidr.String())
- assert.True(t, r.Install)
- tested++
- } else {
- assert.Equal(t, 1500, r.MTU)
- assert.Equal(t, 1234, r.Metric)
- assert.Equal(t, "1.0.0.2/32", r.Cidr.String())
- assert.True(t, r.Install)
- tested++
- }
- }
- if tested != 4 {
- t.Fatal("Did not see all unsafe_routes")
- }
- }
- func Test_makeRouteTree(t *testing.T) {
- l := test.NewLogger()
- c := config.NewC(l)
- n, err := netip.ParsePrefix("10.0.0.0/24")
- require.NoError(t, err)
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
- map[string]any{"via": "192.168.0.1", "route": "1.0.0.0/28"},
- map[string]any{"via": "192.168.0.2", "route": "1.0.0.1/32"},
- }}
- routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Len(t, routes, 2)
- routeTree, err := makeRouteTree(test.NewLogger(), routes, true)
- require.NoError(t, err)
- ip, err := netip.ParseAddr("1.0.0.2")
- require.NoError(t, err)
- r, ok := routeTree.Lookup(ip)
- assert.True(t, ok)
- nip, err := netip.ParseAddr("192.168.0.1")
- require.NoError(t, err)
- assert.Equal(t, nip, r[0].Addr())
- ip, err = netip.ParseAddr("1.0.0.1")
- require.NoError(t, err)
- r, ok = routeTree.Lookup(ip)
- assert.True(t, ok)
- nip, err = netip.ParseAddr("192.168.0.2")
- require.NoError(t, err)
- assert.Equal(t, nip, r[0].Addr())
- ip, err = netip.ParseAddr("1.1.0.1")
- require.NoError(t, err)
- r, ok = routeTree.Lookup(ip)
- assert.False(t, ok)
- }
- func Test_makeMultipathUnsafeRouteTree(t *testing.T) {
- l := test.NewLogger()
- c := config.NewC(l)
- n, err := netip.ParsePrefix("10.0.0.0/24")
- require.NoError(t, err)
- c.Settings["tun"] = map[string]any{
- "unsafe_routes": []any{
- map[string]any{
- "route": "192.168.86.0/24",
- "via": "192.168.100.10",
- },
- map[string]any{
- "route": "192.168.87.0/24",
- "via": []any{
- map[string]any{
- "gateway": "10.0.0.1",
- },
- map[string]any{
- "gateway": "10.0.0.2",
- },
- map[string]any{
- "gateway": "10.0.0.3",
- },
- },
- },
- map[string]any{
- "route": "192.168.89.0/24",
- "via": []any{
- map[string]any{
- "gateway": "10.0.0.1",
- "weight": 10,
- },
- map[string]any{
- "gateway": "10.0.0.2",
- "weight": 5,
- },
- },
- },
- },
- }
- routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- assert.Len(t, routes, 3)
- routeTree, err := makeRouteTree(test.NewLogger(), routes, true)
- require.NoError(t, err)
- ip, err := netip.ParseAddr("192.168.86.1")
- require.NoError(t, err)
- r, ok := routeTree.Lookup(ip)
- assert.True(t, ok)
- nip, err := netip.ParseAddr("192.168.100.10")
- require.NoError(t, err)
- assert.Equal(t, nip, r[0].Addr())
- ip, err = netip.ParseAddr("192.168.87.1")
- require.NoError(t, err)
- r, ok = routeTree.Lookup(ip)
- assert.True(t, ok)
- expectedGateways := routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 1),
- routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 1),
- routing.NewGateway(netip.MustParseAddr("10.0.0.3"), 1)}
- routing.CalculateBucketsForGateways(expectedGateways)
- assert.ElementsMatch(t, expectedGateways, r)
- ip, err = netip.ParseAddr("192.168.89.1")
- require.NoError(t, err)
- r, ok = routeTree.Lookup(ip)
- assert.True(t, ok)
- expectedGateways = routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 10),
- routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 5)}
- routing.CalculateBucketsForGateways(expectedGateways)
- assert.ElementsMatch(t, expectedGateways, r)
- }
- func Test_getMTU(t *testing.T) {
- l := test.NewLogger()
- c := config.NewC(l)
- // unset
- assert.Equal(t, DefaultMTU, getMTU(l, c))
- // the largest a relayed packet can carry in one underlay buffer
- require.Equal(t, udp.MTU, MaxMTU+header.MaxOverhead)
- c.Settings["tun"] = map[string]any{"mtu": MaxMTU}
- assert.Equal(t, MaxMTU, getMTU(l, c))
- // too big is capped
- c.Settings["tun"] = map[string]any{"mtu": 9000}
- assert.Equal(t, MaxMTU, getMTU(l, c))
- }
- func Test_getAllRoutesFromConfigCapsMTU(t *testing.T) {
- l := test.NewLogger()
- c := config.NewC(l)
- c.Settings["tun"] = map[string]any{
- "routes": []any{
- map[string]any{"mtu": "9000", "route": "10.0.0.0/29"},
- map[string]any{"mtu": "1300", "route": "10.0.0.8/29"},
- },
- "unsafe_routes": []any{
- map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29"},
- },
- }
- changed, routes, err := getAllRoutesFromConfig(l, c, []netip.Prefix{netip.MustParsePrefix("10.0.0.0/24")}, true)
- require.NoError(t, err)
- assert.True(t, changed)
- mtus := map[string]int{}
- for _, r := range routes {
- mtus[r.Cidr.String()] = r.MTU
- }
- 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)
- }
- // The route tree keeps one entry per CIDR, so a CIDR listed twice only routes by the last one. Say so instead of
- // letting the earlier via, or on Windows the earlier metric, quietly lose
- func Test_makeRouteTree_WarnsOnDuplicate(t *testing.T) {
- buf := &bytes.Buffer{}
- l := test.NewLoggerWithOutput(buf)
- via := func(s string) routing.Gateways {
- return routing.Gateways{routing.NewGateway(netip.MustParseAddr(s), 1)}
- }
- cidr := netip.MustParsePrefix("10.251.1.0/24")
- _, err := makeRouteTree(l, []Route{
- {Cidr: cidr, Via: via("10.250.0.2"), Install: true, Metric: 100},
- {Cidr: netip.MustParsePrefix("10.251.2.0/24"), Via: via("10.250.0.2"), Install: true},
- }, false)
- require.NoError(t, err)
- assert.Empty(t, buf.String())
- tree, err := makeRouteTree(l, []Route{
- {Cidr: cidr, Via: via("10.250.0.2"), Install: true, Metric: 100},
- {Cidr: cidr, Via: via("10.250.0.3"), Install: true, Metric: 200},
- }, false)
- require.NoError(t, err)
- assert.Equal(t, 1, strings.Count(buf.String(), "route is listed more than once, only the last entry's via is used"))
- gw, ok := tree.Lookup(netip.MustParseAddr("10.251.1.1"))
- require.True(t, ok)
- assert.Equal(t, netip.MustParseAddr("10.250.0.3"), gw[0].Addr(), "the last entry wins")
- }
- // A quoted metric or weight is a number written as a string, it means that number. Anything else is an error, not a
- // panic
- func Test_parseUnsafeRoutes_QuotedNumbers(t *testing.T) {
- c := config.NewC(test.NewLogger())
- n := netip.MustParsePrefix("10.0.0.1/24")
- route := func(metric, weight any) {
- c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{
- "route": "1.0.0.0/8",
- "metric": metric,
- "via": []any{map[string]any{"gateway": "10.0.0.2", "weight": weight}},
- }}}
- }
- route("100", "5")
- routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
- require.NoError(t, err)
- require.Len(t, routes, 1)
- assert.Equal(t, 100, routes[0].Metric)
- assert.Equal(t, "{addr: 10.0.0.2, weight: 5}", routes[0].Via[0].String())
- route(1.5, 1)
- _, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- require.EqualError(t, err, "entry 1.metric in tun.unsafe_routes is not an integer: 1.5")
- route(1, true)
- _, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- require.EqualError(t, err, "entry .weight in tun.unsafe_routes[1].via[1] is not an integer")
- }
- // An mtu that is neither a number nor a quoted one is an error, not a panic
- func Test_parseRoutes_MTUNotANumber(t *testing.T) {
- c := config.NewC(test.NewLogger())
- n := netip.MustParsePrefix("10.0.0.1/24")
- c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"route": "1.0.0.0/8", "mtu": 1500.5}}}
- _, err := parseRoutes(c, []netip.Prefix{n})
- require.EqualError(t, err, "entry 1.mtu in tun.routes is not an integer: 1500.5")
- 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}}}
- _, err = parseUnsafeRoutes(c, []netip.Prefix{n})
- require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is not an integer: 1500.5")
- }
|