github.com/rck/u-root@v0.0.0-20180106144920-7eb602e381bb/pkg/dhclient/dhclient.go (about)

     1  // Package dhclient provides a unified interface for interfacing with both
     2  // DHCPv4 and DHCPv6 clients.
     3  package dhclient
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"log"
    10  	"net"
    11  	"net/url"
    12  	"os"
    13  	"time"
    14  
    15  	"github.com/vishvananda/netlink"
    16  )
    17  
    18  const linkUpAttempt = 30 * time.Second
    19  
    20  // IfUp ensures the given network interface is up and returns the link object.
    21  func IfUp(ifname string) (netlink.Link, error) {
    22  	start := time.Now()
    23  	for time.Since(start) < linkUpAttempt {
    24  		// Note that it may seem odd to keep trying the LinkByName
    25  		// operation, but consider that a hotplug device such as USB
    26  		// ethernet can just vanish.
    27  		iface, err := netlink.LinkByName(ifname)
    28  		if err != nil {
    29  			return nil, fmt.Errorf("cannot get interface %q by name: %v", ifname, err)
    30  		}
    31  
    32  		if iface.Attrs().OperState == netlink.OperUp {
    33  			return iface, nil
    34  		}
    35  
    36  		if err := netlink.LinkSetUp(iface); err != nil {
    37  			return nil, fmt.Errorf("interface %q: %v can't make it up: %v", ifname, iface, err)
    38  		}
    39  		time.Sleep(100 * time.Millisecond)
    40  	}
    41  
    42  	return nil, fmt.Errorf("link %q still down after %d seconds", ifname, linkUpAttempt)
    43  }
    44  
    45  // Lease is a DHCP lease.
    46  type Lease struct {
    47  	IPNet             *net.IPNet
    48  	PreferredLifetime time.Duration
    49  	ValidLifetime     time.Duration
    50  }
    51  
    52  // Packet is a DHCP packet.
    53  type Packet interface {
    54  	IPs() []net.IP
    55  
    56  	// Leases are the leases returned by the DHCP server if specified,
    57  	// otherwise nil.
    58  	Leases() []Lease
    59  
    60  	// Gateway is the gateway server, if specified, otherwise nil.
    61  	Gateway() net.IP
    62  
    63  	// DNS are DNS server addresses, if specified, otherwise nil.
    64  	DNS() []net.IP
    65  
    66  	// Boot returns the boot URI and boot parameters if specified,
    67  	// otherwise an error.
    68  	Boot() (url.URL, string, error)
    69  }
    70  
    71  // Client is a DHCP client.
    72  type Client interface {
    73  	// Solicit solicits a new DHCP lease.
    74  	Solicit() (Packet, error)
    75  
    76  	// Renew renews an existing DHCP lease.
    77  	Renew(p Packet) (Packet, error)
    78  }
    79  
    80  // HandlePacket adds IP addresses, routes, and DNS servers to the system.
    81  func HandlePacket(iface netlink.Link, packet Packet) error {
    82  	l := packet.Leases()
    83  
    84  	// We currently only know how to handle one lease.
    85  	if len(l) > 1 {
    86  		log.Printf("interface %s: only handling one lease.", iface.Attrs().Name)
    87  	}
    88  
    89  	// Add the address to the iface.
    90  	dst := &netlink.Addr{
    91  		IPNet:       l[0].IPNet,
    92  		PreferedLft: int(l[0].PreferredLifetime.Seconds()),
    93  		ValidLft:    int(l[0].ValidLifetime.Seconds()),
    94  	}
    95  	if err := netlink.AddrReplace(iface, dst); err != nil {
    96  		if os.IsExist(err) {
    97  			return fmt.Errorf("add/replace %s to %v: %v", dst, iface, err)
    98  		}
    99  	}
   100  
   101  	if gw := packet.Gateway(); gw != nil {
   102  		r := &netlink.Route{
   103  			LinkIndex: iface.Attrs().Index,
   104  			Gw:        gw,
   105  		}
   106  
   107  		if err := netlink.RouteReplace(r); err != nil {
   108  			return fmt.Errorf("%s: add %s: %v", iface.Attrs().Name, r, err)
   109  		}
   110  	}
   111  
   112  	if ips := packet.DNS(); ips != nil {
   113  		rc := &bytes.Buffer{}
   114  		for _, ip := range ips {
   115  			rc.WriteString(fmt.Sprintf("nameserver %s\n", ip))
   116  		}
   117  		if err := ioutil.WriteFile("/etc/resolv.conf", rc.Bytes(), 0644); err != nil {
   118  			return err
   119  		}
   120  	}
   121  	return nil
   122  }