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 }