feat: support metric values for DHCP

This PR adds a "DHCPOptions" field to the config. This field contains a
single subfield currently, "RouteMetric". Setting this well ensure that
any routes provided from the DHCP server are given this metric upon
injection into the routing table.

Signed-off-by: Spencer Smith <robertspencersmith@gmail.com>
This commit is contained in:
Spencer Smith 2020-10-15 15:38:16 -04:00 committed by talos-bot
parent a12eb76734
commit 7bc3fcf77d
7 changed files with 147 additions and 18 deletions

View File

@ -1320,6 +1320,22 @@ Type: `bool`
Indicates if the interface is a dummy interface.
Type: `bool`
#### dhcpOptions
DHCP specific options.
DHCP *must* be set to true for these to take effect.
Type: `DHCPOptions`
---
### DHCPOptions
#### routeMetric
The priority of all routes received via DHCP
Type: `uint32`
---
### Bond

View File

@ -18,6 +18,7 @@ import (
"github.com/talos-systems/go-procfs/procfs"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
@ -25,6 +26,7 @@ import (
type DHCP struct {
Ack *dhcpv4.DHCPv4
NetIf *net.Interface
DHCPOptions config.DHCPOptions
}
// Name returns back the name of the address method.

View File

@ -46,7 +46,7 @@ func buildOptions(device config.Device, hostname string) (name string, opts []ni
opts = append(opts, nic.WithAddressing(s))
case device.DHCP():
d := &address.DHCP{}
d := &address.DHCP{DHCPOptions: device.DHCPOptions()}
opts = append(opts, nic.WithAddressing(d))
default:
// Allow master interface without any addressing if VLANs exist

View File

@ -17,6 +17,7 @@ import (
"time"
"github.com/hashicorp/go-multierror"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/jsimonetti/rtnetlink"
"github.com/jsimonetti/rtnetlink/rtnl"
"github.com/mdlayher/netlink"
@ -338,7 +339,9 @@ func (n *NetworkInterface) configureInterface(method address.Addressing, link *n
if method.Address() != nil {
// Check to see if we need to configure the address
addrs, err := n.rtnlConn.Addrs(method.Link(), method.Family())
var addrs []*net.IPNet
addrs, err = n.rtnlConn.Addrs(method.Link(), method.Family())
if err != nil {
return err
}
@ -368,20 +371,9 @@ 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 = 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() {
src = nil
}
if err := n.rtnlConn.RouteAddSrc(method.Link(), *r.Dest, src, gw); err != nil {
log.Println("failed to configure route: " + err.Error())
err = n.addRoute(method, r)
if err != nil {
return err
}
}
@ -414,3 +406,89 @@ func (n *NetworkInterface) Reset() {
// nolint: errcheck
n.rtnlConn.LinkDown(link)
}
// addRoute is a function loosely copied from https://github.com/jsimonetti/rtnetlink/blob/154ecd417600f79d7847278a1e984056dc647acc/rtnl/route.go
// and merged with our logic on determining gw, src, dst, etc.
// we need this b/c we need to craft the route message ourselves to add attributes.
// nolint: gocyclo
func (n *NetworkInterface) addRoute(method address.Addressing, r *dhcpv4.Route) error {
dst := *r.Dest
// 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 = 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() {
src = nil
}
// determine if this is ipv4 or 6
var af int
if dst.IP.To4() != nil {
af = unix.AF_INET
} else if len(dst.IP) == net.IPv6len {
af = unix.AF_INET6
}
ifc := method.Link()
// Determine scope
var scope uint8
switch {
case gw != nil:
scope = unix.RT_SCOPE_UNIVERSE
case len(dst.IP) == net.IPv6len && dst.IP.To4() == nil:
scope = unix.RT_SCOPE_UNIVERSE
default:
// Set default scope to LINK
scope = unix.RT_SCOPE_LINK
}
attr := rtnetlink.RouteAttributes{
Dst: dst.IP,
OutIface: uint32(ifc.Index),
}
// 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)
}
}
if gw != nil {
attr.Gateway = gw
}
var srclen int
if src != nil {
srclen, _ = src.Mask.Size()
attr.Src = src.IP
}
dstlen, _ := dst.Mask.Size()
tx := &rtnetlink.RouteMessage{
Family: uint8(af),
Table: unix.RT_TABLE_MAIN,
Protocol: unix.RTPROT_BOOT,
Type: unix.RTN_UNICAST,
Scope: scope,
DstLength: uint8(dstlen),
SrcLength: uint8(srclen),
Attributes: attr,
}
return n.rtnlConn.Conn.Route.Add(tx)
}

View File

@ -115,6 +115,12 @@ type Device interface {
DHCP() bool
Ignore() bool
Dummy() bool
DHCPOptions() DHCPOptions
}
// DHCPOptions represents a set of DHCP options.
type DHCPOptions interface {
RouteMetric() uint32
}
// Bond contains the various options for configuring a

View File

@ -749,6 +749,23 @@ func (d *Device) Dummy() bool {
return d.DeviceDummy
}
// DHCPOptions implements the MachineNetwork interface.
func (d *Device) DHCPOptions() config.DHCPOptions {
// Default route metric on systemd is 1024. This sets the same.
if d.DeviceDHCPOptions == nil {
return &DHCPOptions{
DHCPRouteMetric: uint32(0),
}
}
return d.DeviceDHCPOptions
}
// RouteMetric implements the MachineNetwork interface.
func (d *DHCPOptions) RouteMetric() uint32 {
return d.DHCPRouteMetric
}
// Network implements the MachineNetwork interface.
func (r *Route) Network() string {
return r.RouteNetwork

View File

@ -832,6 +832,16 @@ type Device struct {
DeviceIgnore bool `yaml:"ignore"`
// description: Indicates if the interface is a dummy interface.
DeviceDummy bool `yaml:"dummy"`
// description: |
// DHCP specific options.
// DHCP *must* be set to true for these to take effect.
DeviceDHCPOptions *DHCPOptions `yaml:"dhcpOptions"`
}
// DHCPOptions contains options for configuring the DHCP settings for a given interface.
type DHCPOptions struct {
// description: The priority of all routes received via DHCP
DHCPRouteMetric uint32 `yaml:"routeMetric"`
}
// Bond contains the various options for configuring a