fix: prevent endless loop with DHCP requests in networkd

There were two problems:

* `configureInterfaces` was always failing if interface is already set
up, as the routes already exist

* `renew` was halving the renew interval each time `configureInterface`
fails, which starts at (LeaseTime/2) and goes effectively to zero

This was leading to high networkd CPU usage, storm of DHCP requests on
the network.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
This commit is contained in:
Andrey Smirnov 2020-12-01 17:49:57 +03:00 committed by talos-bot
parent 71063c4a6e
commit 360d887967
4 changed files with 43 additions and 10 deletions

View File

@ -46,9 +46,11 @@ func (d *DHCP) Link() *net.Interface {
// Discover handles the DHCP client exchange stores the DHCP Ack. // Discover handles the DHCP client exchange stores the DHCP Ack.
func (d *DHCP) Discover(ctx context.Context, link *net.Interface) error { func (d *DHCP) Discover(ctx context.Context, link *net.Interface) error {
d.NetIf = link d.NetIf = link
// TODO do something with context ack, err := d.discover(ctx)
ack, err := d.discover()
d.Ack = ack if ack != nil {
d.Ack = ack
}
return err return err
} }
@ -191,7 +193,7 @@ func (d *DHCP) Hostname() (hostname string) {
} }
// discover handles the actual DHCP conversation. // discover handles the actual DHCP conversation.
func (d *DHCP) discover() (*dhcpv4.DHCPv4, error) { func (d *DHCP) discover(ctx context.Context) (*dhcpv4.DHCPv4, error) {
opts := []dhcpv4.OptionCode{ opts := []dhcpv4.OptionCode{
dhcpv4.OptionClasslessStaticRoute, dhcpv4.OptionClasslessStaticRoute,
dhcpv4.OptionDomainNameServer, dhcpv4.OptionDomainNameServer,
@ -223,7 +225,7 @@ func (d *DHCP) discover() (*dhcpv4.DHCPv4, error) {
// nolint: errcheck // nolint: errcheck
defer cli.Close() defer cli.Close()
lease, err := cli.Request(context.Background(), mods...) lease, err := cli.Request(ctx, mods...)
if err != nil { if err != nil {
// TODO: Make this a well defined error so we can make it not fatal // TODO: Make this a well defined error so we can make it not fatal
log.Printf("failed dhcp request for %q: %v", d.NetIf.Name, err) log.Printf("failed dhcp request for %q: %v", d.NetIf.Name, err)
@ -231,5 +233,21 @@ func (d *DHCP) discover() (*dhcpv4.DHCPv4, error) {
return nil, err return nil, err
} }
log.Printf("DHCP ACK on %q: %s", d.NetIf.Name, collapseSummary(lease.ACK.Summary()))
return lease.ACK, err return lease.ACK, err
} }
func collapseSummary(summary string) string {
lines := strings.Split(summary, "\n")[1:]
for i := range lines {
lines[i] = strings.TrimSpace(lines[i])
}
if len(lines) > 0 && lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
return strings.Join(lines, ", ")
}

View File

@ -48,6 +48,10 @@ func (n *NetworkInterface) setMTU(idx int, mtu uint32) error {
return err return err
} }
if msg.Attributes != nil && msg.Attributes.MTU == mtu {
return nil
}
err = n.rtConn.Link.Set(&rtnetlink.LinkMessage{ err = n.rtConn.Link.Set(&rtnetlink.LinkMessage{
Family: msg.Family, Family: msg.Family,
Type: msg.Type, Type: msg.Type,

View File

@ -312,18 +312,26 @@ func (n *NetworkInterface) Renew() {
// address TTL. If that fails, we'll continue to attempt to retry every // address TTL. If that fails, we'll continue to attempt to retry every
// halflife. // halflife.
func (n *NetworkInterface) renew(method address.Addressing) { func (n *NetworkInterface) renew(method address.Addressing) {
const minRenewDuration = 5 * time.Second // protect from renewing too often
renewDuration := method.TTL() / 2 renewDuration := method.TTL() / 2
var err error var err error
for { for {
<-time.After(renewDuration) time.Sleep(renewDuration)
if err = n.configureInterface(method, n.Link); err != nil { if err = n.configureInterface(method, n.Link); err != nil {
log.Printf("failure to renew address for %q: %s", n.Name, err)
renewDuration = (renewDuration / 2) renewDuration = (renewDuration / 2)
} else { } else {
renewDuration = method.TTL() / 2 renewDuration = method.TTL() / 2
} }
if renewDuration < minRenewDuration {
renewDuration = minRenewDuration
}
} }
} }
@ -337,7 +345,7 @@ func (n *NetworkInterface) configureInterface(method address.Addressing, link *n
// Set link MTU in any case // Set link MTU in any case
if err = n.setMTU(method.Link().Index, method.MTU()); err != nil { if err = n.setMTU(method.Link().Index, method.MTU()); err != nil {
return err return fmt.Errorf("error setting MTU %d on %q: %w", method.MTU(), n.Name, err)
} }
if discoverErr != nil { if discoverErr != nil {
@ -368,7 +376,7 @@ func (n *NetworkInterface) configureInterface(method address.Addressing, link *n
switch err := err.(type) { switch err := err.(type) {
case *netlink.OpError: case *netlink.OpError:
if !os.IsExist(err.Err) && err.Err != syscall.ESRCH { if !os.IsExist(err.Err) && err.Err != syscall.ESRCH {
return err return fmt.Errorf("error adding address %s on %q: %w", method.Address(), n.Name, err)
} }
default: default:
return fmt.Errorf("failed to add address (already exists) %+v to %s: %v", method.Address(), method.Link().Name, err) return fmt.Errorf("failed to add address (already exists) %+v to %s: %v", method.Address(), method.Link().Name, err)
@ -401,7 +409,10 @@ func (n *NetworkInterface) configureInterface(method address.Addressing, link *n
err = n.rtnlConn.RouteAdd(method.Link(), *r.Destination, 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 { if err != nil {
return err // ignore "EEXIST" errors for routes which are already present
if opErr, ok := err.(*netlink.OpError); !ok || !os.IsExist(opErr.Err) {
return fmt.Errorf("error adding route %s %s on %q: %s", *r.Destination, gw, n.Name, err)
}
} }
} }

View File

@ -52,7 +52,7 @@ func handler(serverIP net.IP, statePath string) server4.Handler {
dhcpv4.WithOption(dhcpv4.OptHostName(match.Hostname)), dhcpv4.WithOption(dhcpv4.OptHostName(match.Hostname)),
dhcpv4.WithOption(dhcpv4.OptDNS(match.Nameservers...)), dhcpv4.WithOption(dhcpv4.OptDNS(match.Nameservers...)),
dhcpv4.WithOption(dhcpv4.OptRouter(match.Gateway)), dhcpv4.WithOption(dhcpv4.OptRouter(match.Gateway)),
dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(time.Hour)), dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(5*time.Minute)),
dhcpv4.WithOption(dhcpv4.OptServerIdentifier(serverIP)), dhcpv4.WithOption(dhcpv4.OptServerIdentifier(serverIP)),
) )
if err != nil { if err != nil {