Seán C McCord 845cd92e5d fix: increase retries for DHCP
Increased retry count to 6 for DHCP.  In my testing, this worked
reliably in my setup, where the default (3) did not.

Ultimately, this should probably be configurable from the userdata.
Instead, this just makes it work for me.

Fixes #1099

Signed-off-by: Seán C McCord <ulexus@gmail.com>
2019-09-02 19:02:53 -07:00

173 lines
4.3 KiB
Go

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package address
import (
"context"
"log"
"net"
"strings"
"time"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/talos-systems/talos/internal/pkg/kernel"
"github.com/talos-systems/talos/pkg/constants"
"golang.org/x/sys/unix"
)
// DHCP implements the Addressing interface
type DHCP struct {
Ack *dhcpv4.DHCPv4
NetIf *net.Interface
}
// Name returns back the name of the address method.
func (d *DHCP) Name() string {
return "dhcp"
}
// Link returns the underlying net.Interface that this address
// method is configured for
func (d *DHCP) Link() *net.Interface {
return d.NetIf
}
// Discover handles the DHCP client exchange stores the DHCP Ack.
func (d *DHCP) Discover(ctx context.Context) error {
// TODO do something with context
ack, err := d.discover()
d.Ack = ack
return err
}
// Address returns back the IP address from the received DHCP offer.
func (d *DHCP) Address() *net.IPNet {
return &net.IPNet{
IP: d.Ack.YourIPAddr,
Mask: d.Mask(),
}
}
// Mask returns the netmask from the DHCP offer.
func (d *DHCP) Mask() net.IPMask {
return d.Ack.SubnetMask()
}
// MTU returs the MTU size from the DHCP offer.
func (d *DHCP) MTU() uint32 {
// TODO do we need to implement dhcpv4.GetUint32 upstream?
mtu, err := dhcpv4.GetUint16(dhcpv4.OptionInterfaceMTU, d.Ack.Options)
if err != nil {
return uint32(d.NetIf.MTU)
}
return uint32(mtu)
}
// TTL denotes how long a DHCP offer is valid for.
func (d *DHCP) TTL() time.Duration {
if d.Ack == nil {
return 0
}
return d.Ack.IPAddressLeaseTime(time.Minute * 30)
}
// Family qualifies the address as ipv4 or ipv6
func (d *DHCP) Family() int {
if d.Ack.YourIPAddr.To4() != nil {
return unix.AF_INET
}
return unix.AF_INET6
}
// Scope sets the address scope
func (d *DHCP) Scope() uint8 {
return unix.RT_SCOPE_UNIVERSE
}
// Valid denotes if this address method should be used.
func (d *DHCP) Valid() bool {
return d.Ack != nil
}
// Routes aggregates all Routers and ClasslessStaticRoutes retrieved from
// the DHCP offer.
// rfc3442:
// 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) {
if len(d.Ack.ClasslessStaticRoute()) > 0 {
return d.Ack.ClasslessStaticRoute()
}
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})
}
return routes
}
// Resolvers returns the DNS resolvers from the DHCP offer.
func (d *DHCP) Resolvers() []net.IP {
return d.Ack.DNS()
}
// Hostname returns the hostname from the DHCP offer.
func (d *DHCP) Hostname() string {
// Truncate the returned hostname to only return
// the actual host entry
return strings.Split(d.Ack.HostName(), ".")[0]
}
// discover handles the actual DHCP conversation.
func (d *DHCP) discover() (*dhcpv4.DHCPv4, error) {
opts := []dhcpv4.OptionCode{
dhcpv4.OptionClasslessStaticRoute,
dhcpv4.OptionDomainNameServer,
dhcpv4.OptionDNSDomainSearchList,
dhcpv4.OptionHostName,
// TODO: handle these options
dhcpv4.OptionNTPServers,
dhcpv4.OptionDomainName,
}
// <3 azure
// When including dhcp.OptionInterfaceMTU we don't get a dhcp offer back on azure.
// So we'll need to explicitly exclude adding this option for azure.
if p := kernel.ProcCmdline().Get(constants.KernelParamPlatform).First(); p != nil {
if *p != "azure" {
opts = append(opts, dhcpv4.OptionInterfaceMTU)
}
}
mods := []dhcpv4.Modifier{dhcpv4.WithRequestedOptions(opts...)}
// TODO expose this ( nclient4.WithDebugLogger() ) with some
// debug logging option
cli, err := nclient4.New(d.NetIf.Name,
nclient4.WithTimeout(2*time.Second),
nclient4.WithRetry(5),
)
if err != nil {
return nil, err
}
// nolint: errcheck
defer cli.Close()
_, ack, err := cli.Request(context.Background(), mods...)
if err != nil {
// TODO: Make this a well defined error so we can make it not fatal
log.Println("failed dhcp request")
return nil, err
}
return ack, err
}