github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/pkg/dhclient/dhclient.go (about)

     1  // Copyright 2017-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package dhclient provides a unified interface for interfacing with both
     6  // DHCPv4 and DHCPv6 clients.
     7  package dhclient
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"net"
    14  	"os"
    15  	"time"
    16  
    17  	"github.com/mdlayher/dhcp6"
    18  	"github.com/mdlayher/dhcp6/dhcp6opts"
    19  	"github.com/u-root/dhcp4"
    20  	"github.com/vishvananda/netlink"
    21  )
    22  
    23  const linkUpAttempt = 30 * time.Second
    24  
    25  // IfUp ensures the given network interface is up and returns the link object.
    26  func IfUp(ifname string) (netlink.Link, error) {
    27  	start := time.Now()
    28  	for time.Since(start) < linkUpAttempt {
    29  		// Note that it may seem odd to keep trying the LinkByName
    30  		// operation, but consider that a hotplug device such as USB
    31  		// ethernet can just vanish.
    32  		iface, err := netlink.LinkByName(ifname)
    33  		if err != nil {
    34  			return nil, fmt.Errorf("cannot get interface %q by name: %v", ifname, err)
    35  		}
    36  
    37  		if iface.Attrs().OperState == netlink.OperUp {
    38  			return iface, nil
    39  		}
    40  
    41  		if err := netlink.LinkSetUp(iface); err != nil {
    42  			return nil, fmt.Errorf("interface %q: %v can't make it up: %v", ifname, iface, err)
    43  		}
    44  		time.Sleep(100 * time.Millisecond)
    45  	}
    46  
    47  	return nil, fmt.Errorf("link %q still down after %d seconds", ifname, linkUpAttempt)
    48  }
    49  
    50  // Configure4 adds IP addresses, routes, and DNS servers to the system.
    51  func Configure4(iface netlink.Link, packet *dhcp4.Packet) error {
    52  	p := NewPacket4(packet)
    53  
    54  	l := p.Lease()
    55  	if l == nil {
    56  		return fmt.Errorf("no lease returned")
    57  	}
    58  
    59  	// Add the address to the iface.
    60  	dst := &netlink.Addr{
    61  		IPNet: l,
    62  	}
    63  	if err := netlink.AddrReplace(iface, dst); err != nil {
    64  		if os.IsExist(err) {
    65  			return fmt.Errorf("add/replace %s to %v: %v", dst, iface, err)
    66  		}
    67  	}
    68  
    69  	if gw := p.Gateway(); gw != nil {
    70  		r := &netlink.Route{
    71  			LinkIndex: iface.Attrs().Index,
    72  			Gw:        gw,
    73  		}
    74  
    75  		if err := netlink.RouteReplace(r); err != nil {
    76  			return fmt.Errorf("%s: add %s: %v", iface.Attrs().Name, r, err)
    77  		}
    78  	}
    79  
    80  	if ips := p.DNS(); ips != nil {
    81  		if err := WriteDNSSettings(ips); err != nil {
    82  			return err
    83  		}
    84  	}
    85  	return nil
    86  }
    87  
    88  // Configure6 adds IPv6 addresses, routes, and DNS servers to the system.
    89  func Configure6(iface netlink.Link, packet *dhcp6.Packet, iana *dhcp6opts.IANA) error {
    90  	p := NewPacket6(packet, iana)
    91  
    92  	l := p.Lease()
    93  	if l == nil {
    94  		return fmt.Errorf("no lease returned")
    95  	}
    96  
    97  	// Add the address to the iface.
    98  	dst := &netlink.Addr{
    99  		IPNet: &net.IPNet{
   100  			IP:   l.IP,
   101  			Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::")),
   102  		},
   103  		PreferedLft: int(l.PreferredLifetime.Seconds()),
   104  		ValidLft:    int(l.ValidLifetime.Seconds()),
   105  	}
   106  	if err := netlink.AddrReplace(iface, dst); err != nil {
   107  		if os.IsExist(err) {
   108  			return fmt.Errorf("add/replace %s to %v: %v", dst, iface, err)
   109  		}
   110  	}
   111  
   112  	if ips := p.DNS(); ips != nil {
   113  		if err := WriteDNSSettings(ips); err != nil {
   114  			return err
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  // WriteDNSSettings writes the given IPs as nameservers to resolv.conf.
   121  func WriteDNSSettings(ips []net.IP) error {
   122  	rc := &bytes.Buffer{}
   123  	for _, ip := range ips {
   124  		rc.WriteString(fmt.Sprintf("nameserver %s\n", ip))
   125  	}
   126  	return ioutil.WriteFile("/etc/resolv.conf", rc.Bytes(), 0644)
   127  }