From b358bbab802434eabcc536bf636199f4676e054b Mon Sep 17 00:00:00 2001 From: Donatas Abraitis Date: Thu, 4 Nov 2021 04:53:28 +0200 Subject: [PATCH] Add an ability to specify metric for unsafe routes (#474) --- CHANGELOG.md | 5 ++++- examples/config.yml | 5 ++++- tun_common.go | 30 +++++++++++++++++++++++++----- tun_linux.go | 1 + tun_test.go | 12 +++++++++--- tun_windows.go | 2 +- 6 files changed, 44 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6603019..5b3e86f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New config option `pki.disconnect_invalid` that will tear down tunnels when they become invalid (through expiry or removal of root trust). Default is `false`. Note, this will not currently recognize if a remote has changed certificates since the last handshake. (#370) - + +- New config option `unsafe_routes..metric` will set a metric for a specific unsafe route. It's useful if you have + more than one identical route and want to prefer one against the other. + ### Changed - Build against go 1.17. (#553) diff --git a/examples/config.yml b/examples/config.yml index dce8ef9..87b7954 100644 --- a/examples/config.yml +++ b/examples/config.yml @@ -165,10 +165,13 @@ tun: # Unsafe routes allows you to route traffic over nebula to non-nebula nodes # Unsafe routes should be avoided unless you have hosts/services that cannot run nebula # NOTE: The nebula certificate of the "via" node *MUST* have the "route" defined as a subnet in its certificate + # `mtu` will default to tun mtu if this option is not specified + # `metric` will default to 0 if this option is not specified unsafe_routes: #- route: 172.16.1.0/24 # via: 192.168.100.99 - # mtu: 1300 #mtu will default to tun mtu if this option is not sepcified + # mtu: 1300 + # metric: 100 # TODO diff --git a/tun_common.go b/tun_common.go index 0a81eab..a6c7c1e 100644 --- a/tun_common.go +++ b/tun_common.go @@ -2,6 +2,7 @@ package nebula import ( "fmt" + "math" "net" "strconv" @@ -11,9 +12,10 @@ import ( const DEFAULT_MTU = 1300 type route struct { - mtu int - route *net.IPNet - via *net.IP + mtu int + metric int + route *net.IPNet + via *net.IP } func parseRoutes(c *config.C, network *net.IPNet) ([]route, error) { @@ -127,6 +129,23 @@ func parseUnsafeRoutes(c *config.C, network *net.IPNet) ([]route, error) { return nil, fmt.Errorf("entry %v.mtu in tun.unsafe_routes is below 500: %v", i+1, mtu) } + rMetric, ok := m["metric"] + if !ok { + rMetric = 0 + } + + metric, ok := rMetric.(int) + if !ok { + _, err = strconv.ParseInt(rMetric.(string), 10, 32) + if err != nil { + return nil, fmt.Errorf("entry %v.metric in tun.unsafe_routes is not an integer: %v", i+1, err) + } + } + + if metric < 0 || metric > math.MaxInt32 { + return nil, fmt.Errorf("entry %v.metric in tun.unsafe_routes is not in range (0-%d) : %v", i+1, math.MaxInt32, metric) + } + rVia, ok := m["via"] if !ok { return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not present", i+1) @@ -148,8 +167,9 @@ func parseUnsafeRoutes(c *config.C, network *net.IPNet) ([]route, error) { } r := route{ - via: &nVia, - mtu: mtu, + via: &nVia, + mtu: mtu, + metric: metric, } _, r.route, err = net.ParseCIDR(fmt.Sprintf("%v", rRoute)) diff --git a/tun_linux.go b/tun_linux.go index e982be9..406a6c5 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -300,6 +300,7 @@ func (c Tun) Activate() error { LinkIndex: link.Attrs().Index, Dst: r.route, MTU: r.mtu, + Priority: r.metric, AdvMSS: c.advMSS(r), Scope: unix.RT_SCOPE_LINK, } diff --git a/tun_test.go b/tun_test.go index 6043eb5..01c904a 100644 --- a/tun_test.go +++ b/tun_test.go @@ -208,24 +208,30 @@ func Test_parseUnsafeRoutes(t *testing.T) { c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{ map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29"}, map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "8000", "route": "1.0.0.1/32"}, + map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32"}, }} routes, err = parseUnsafeRoutes(c, n) assert.Nil(t, err) - assert.Len(t, routes, 2) + assert.Len(t, routes, 3) tested := 0 for _, r := range routes { if r.mtu == 8000 { assert.Equal(t, "1.0.0.1/32", r.route.String()) tested++ - } else { + } else if r.mtu == 9000 { assert.Equal(t, 9000, r.mtu) assert.Equal(t, "1.0.0.0/29", r.route.String()) tested++ + } else { + assert.Equal(t, 1500, r.mtu) + assert.Equal(t, 1234, r.metric) + assert.Equal(t, "1.0.0.2/32", r.route.String()) + tested++ } } - if tested != 2 { + if tested != 3 { t.Fatal("Did not see both unsafe_routes") } } diff --git a/tun_windows.go b/tun_windows.go index 17bbe4e..a06070d 100644 --- a/tun_windows.go +++ b/tun_windows.go @@ -92,7 +92,7 @@ func (c *Tun) Activate() error { for _, r := range c.UnsafeRoutes { err = exec.Command( - "C:\\Windows\\System32\\route.exe", "add", r.route.String(), r.via.String(), "IF", strconv.Itoa(iface.Index), + "C:\\Windows\\System32\\route.exe", "add", r.route.String(), r.via.String(), "IF", strconv.Itoa(iface.Index), "METRIC", strconv.Itoa(r.metric), ).Run() if err != nil { return fmt.Errorf("failed to add the unsafe_route %s: %v", r.route.String(), err)