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 }