From 5d4d179cd8ae9867bbe4572686b489ba7d52334e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A1n=20C=20McCord?= Date: Mon, 16 Nov 2020 16:24:58 -0800 Subject: [PATCH] feat: support ipv6 routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While IPv6 were mostly supported already, there was a single segment in the interface setup which forced everything into an IPv4 route. This limitation has been removed. In so doing, route metrics have been cleaned up a small amount. This change allows the specification of the route metric from the config. Fixes #2772 Signed-off-by: Seán C McCord --- internal/app/networkd/pkg/address/address.go | 19 ++++++++-- internal/app/networkd/pkg/address/dhcp.go | 37 ++++++++++++++++--- internal/app/networkd/pkg/address/static.go | 23 ++++++++++-- internal/app/networkd/pkg/nic/nic.go | 24 +++--------- pkg/machinery/config/provider.go | 1 + .../types/v1alpha1/v1alpha1_provider.go | 5 +++ .../config/types/v1alpha1/v1alpha1_types.go | 3 ++ .../types/v1alpha1/v1alpha1_types_doc.go | 7 +++- .../docs/v0.7/Reference/configuration.md | 17 +++++++++ 9 files changed, 105 insertions(+), 31 deletions(-) diff --git a/internal/app/networkd/pkg/address/address.go b/internal/app/networkd/pkg/address/address.go index 7c96f7561a..d5d8e09422 100644 --- a/internal/app/networkd/pkg/address/address.go +++ b/internal/app/networkd/pkg/address/address.go @@ -8,8 +8,6 @@ import ( "context" "net" "time" - - "github.com/insomniacslk/dhcp/dhcpv4" ) // Addressing provides an interface for abstracting the underlying network @@ -32,4 +30,19 @@ type Addressing interface { } // Route is a representation of a network route. -type Route = dhcpv4.Route +type Route struct { + // Destination is the destination network this route provides. + Destination *net.IPNet + + // Gateway is the router through which the destination may be reached. + // This option is exclusive of Interface + Gateway net.IP + + // Interface indicates the route is an interface route, and traffic destinted for the Gateway should be sent through the given network interface. + // This option is exclusive of Gateway. + Interface string + + // Metric indicates the "distance" to the destination through this route. + // This is an integer which allows the control of priority in the case of multiple routes to the same destination. + Metric uint32 +} diff --git a/internal/app/networkd/pkg/address/dhcp.go b/internal/app/networkd/pkg/address/dhcp.go index 0bf0f774f5..78eb1d8645 100644 --- a/internal/app/networkd/pkg/address/dhcp.go +++ b/internal/app/networkd/pkg/address/dhcp.go @@ -21,6 +21,8 @@ import ( "github.com/talos-systems/talos/pkg/machinery/constants" ) +const dhcpReceivedRouteMetric uint32 = 1024 + // DHCP implements the Addressing interface. type DHCP struct { Ack *dhcpv4.DHCPv4 @@ -116,26 +118,51 @@ func (d *DHCP) Valid() bool { // If the DHCP server returns both a Classless Static Routes option and // a Router option, the DHCP client MUST ignore the Router option. func (d *DHCP) Routes() (routes []*Route) { + metric := dhcpReceivedRouteMetric + + if d.DHCPOptions != nil && d.DHCPOptions.RouteMetric() != 0 { + metric = d.DHCPOptions.RouteMetric() + } + defRoute := &net.IPNet{ IP: net.IPv4zero, Mask: net.IPv4Mask(0, 0, 0, 0), } for _, router := range d.Ack.Router() { - routes = append(routes, &Route{Router: router, Dest: defRoute}) + routes = append(routes, &Route{ + Destination: defRoute, + Gateway: router, + Metric: metric, + }) } // overwrite router option if classless routes were provided. if len(d.Ack.ClasslessStaticRoute()) > 0 { - routes = d.Ack.ClasslessStaticRoute() + routes = []*Route{} + + for _, dhcpRoute := range d.Ack.ClasslessStaticRoute() { + routes = append(routes, &Route{ + Destination: dhcpRoute.Dest, + Gateway: dhcpRoute.Router, + Metric: metric, + }) + } } // append any routes that were provided in config for _, route := range d.RouteList { - // nolint: errcheck - _, ipnet, _ := net.ParseCIDR(route.Network()) + _, ipnet, err := net.ParseCIDR(route.Network()) + if err != nil { + // TODO: we should at least log this failure + continue + } - routes = append(routes, &Route{Dest: ipnet, Router: net.ParseIP(route.Gateway())}) + routes = append(routes, &Route{ + Destination: ipnet, + Gateway: net.ParseIP(route.Gateway()), + Metric: staticRouteDefaultMetric, + }) } return routes diff --git a/internal/app/networkd/pkg/address/static.go b/internal/app/networkd/pkg/address/static.go index 57c0b0b9f3..b1a41d6402 100644 --- a/internal/app/networkd/pkg/address/static.go +++ b/internal/app/networkd/pkg/address/static.go @@ -14,6 +14,8 @@ import ( "github.com/talos-systems/talos/pkg/machinery/config" ) +const staticRouteDefaultMetric uint32 = 10 + // Static implements the Addressing interface. type Static struct { CIDR string @@ -98,10 +100,23 @@ func (s *Static) Scope() uint8 { // TODO: do we need to be explicit on route vs gateway? func (s *Static) Routes() (routes []*Route) { for _, route := range s.RouteList { - // nolint: errcheck - _, ipnet, _ := net.ParseCIDR(route.Network()) - - routes = append(routes, &Route{Dest: ipnet, Router: net.ParseIP(route.Gateway())}) + _, ipnet, err := net.ParseCIDR(route.Network()) + if err != nil { + // TODO: we should at least log the error + continue + } + + metric := staticRouteDefaultMetric + + if route.Metric() != 0 { + metric = route.Metric() + } + + routes = append(routes, &Route{ + Destination: ipnet, + Gateway: net.ParseIP(route.Gateway()), + Metric: metric, + }) } return routes diff --git a/internal/app/networkd/pkg/nic/nic.go b/internal/app/networkd/pkg/nic/nic.go index 3bfdbe070b..f689ad150f 100644 --- a/internal/app/networkd/pkg/nic/nic.go +++ b/internal/app/networkd/pkg/nic/nic.go @@ -378,38 +378,26 @@ func (n *NetworkInterface) configureInterface(method address.Addressing, link *n // Add any routes for _, r := range method.Routes() { // If gateway/router is 0.0.0.0 we'll set to nil so route scope decision will be correct - gw := r.Router - if net.IPv4zero.Equal(gw) { + gw := r.Gateway + if net.IPv4zero.Equal(gw) || net.IPv6zero.Equal(gw) { gw = nil } src := method.Address() - // if destination is the ipv6 route,and gateway is LL do not pass a src address to set the default geteway - if net.IPv6zero.Equal(r.Dest.IP) && gw.IsLinkLocalUnicast() { + // if destination is the ipv6 default route,and gateway is LL do not pass a src address to set the default geteway + if net.IPv6zero.Equal(r.Destination.IP) && gw.IsLinkLocalUnicast() { src = nil } attr := rtnetlink.RouteAttributes{ - Dst: r.Dest.IP, - OutIface: uint32(method.Link().Index), + Priority: r.Metric, } if gw != nil { attr.Gateway = gw } - // Set DHCP specific options - if dhcpObj, ok := method.(*address.DHCP); ok { - if dhcpObj.DHCPOptions != nil { - attr.Priority = dhcpObj.DHCPOptions.RouteMetric() - } - - if attr.Priority == uint32(0) { - attr.Priority = uint32(1024) - } - } - - err = n.rtnlConn.RouteAdd(method.Link(), *r.Dest, gw, rtnl.WithRouteSrc(src), rtnl.WithRouteAttrs(attr)) + err = n.rtnlConn.RouteAdd(method.Link(), *r.Destination, gw, rtnl.WithRouteSrc(src), rtnl.WithRouteAttrs(attr)) if err != nil { return err } diff --git a/pkg/machinery/config/provider.go b/pkg/machinery/config/provider.go index 6f6576dd4f..91899ceae7 100644 --- a/pkg/machinery/config/provider.go +++ b/pkg/machinery/config/provider.go @@ -166,6 +166,7 @@ type Vlan interface { type Route interface { Network() string Gateway() string + Metric() uint32 } // Time defines the requirements for a config that pertains to time related diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go index b99cb3a4d4..e7e9647bfa 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go @@ -771,6 +771,11 @@ func (r *Route) Gateway() string { return r.RouteGateway } +// Metric implements the MachineNetwork interface. +func (r *Route) Metric() uint32 { + return r.RouteMetric +} + // Interfaces implements the MachineNetwork interface. func (b *Bond) Interfaces() []string { if b == nil { diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go index 7942fd973c..b1ee36daa4 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go @@ -134,6 +134,7 @@ var ( { RouteNetwork: "0.0.0.0/0", RouteGateway: "192.168.2.1", + RouteMetric: 1024, }, }, }, @@ -1214,6 +1215,8 @@ type Route struct { RouteNetwork string `yaml:"network"` // description: The route's gateway. RouteGateway string `yaml:"gateway"` + // description: The optional metric for the route. + RouteMetric uint32 `yaml:"metric,omitempty"` } // RegistryMirrorConfig represents mirror configuration for a registry. diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go index eaf7342523..30f762d61d 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go @@ -1223,7 +1223,7 @@ func init() { FieldName: "routes", }, } - RouteDoc.Fields = make([]encoder.Doc, 2) + RouteDoc.Fields = make([]encoder.Doc, 3) RouteDoc.Fields[0].Name = "network" RouteDoc.Fields[0].Type = "string" RouteDoc.Fields[0].Note = "" @@ -1234,6 +1234,11 @@ func init() { RouteDoc.Fields[1].Note = "" RouteDoc.Fields[1].Description = "The route's gateway." RouteDoc.Fields[1].Comments[encoder.LineComment] = "The route's gateway." + RouteDoc.Fields[2].Name = "metric" + RouteDoc.Fields[2].Type = "uint32" + RouteDoc.Fields[2].Note = "" + RouteDoc.Fields[2].Description = "The optional metric for the route." + RouteDoc.Fields[2].Comments[encoder.LineComment] = "The optional metric for the route." RegistryMirrorConfigDoc.Type = "RegistryMirrorConfig" RegistryMirrorConfigDoc.Comments[encoder.LineComment] = "RegistryMirrorConfig represents mirror configuration for a registry." diff --git a/website/content/docs/v0.7/Reference/configuration.md b/website/content/docs/v0.7/Reference/configuration.md index 46bfd3d520..1fecd06912 100644 --- a/website/content/docs/v0.7/Reference/configuration.md +++ b/website/content/docs/v0.7/Reference/configuration.md @@ -330,6 +330,7 @@ network: routes: - network: 0.0.0.0/0 # The route's network. gateway: 192.168.2.1 # The route's gateway. + metric: 1024 # The optional metric for the route. mtu: 1500 # The interface's MTU. # # Bond specific options. @@ -1228,6 +1229,7 @@ interfaces: routes: - network: 0.0.0.0/0 # The route's network. gateway: 192.168.2.1 # The route's gateway. + metric: 1024 # The optional metric for the route. mtu: 1500 # The interface's MTU. # # Bond specific options. @@ -1298,6 +1300,7 @@ interfaces: routes: - network: 0.0.0.0/0 # The route's network. gateway: 192.168.2.1 # The route's gateway. + metric: 1024 # The optional metric for the route. mtu: 1500 # The interface's MTU. # # Bond specific options. @@ -2633,6 +2636,7 @@ Appears in: routes: - network: 0.0.0.0/0 # The route's network. gateway: 192.168.2.1 # The route's gateway. + metric: 1024 # The optional metric for the route. mtu: 1500 # The interface's MTU. # # Bond specific options. @@ -3425,6 +3429,19 @@ The route's gateway.
+
+ +metric uint32 + +
+
+ +The optional metric for the route. + +
+ +
+