github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/pkg/dhclient/dhcp4.go (about) 1 package dhclient 2 3 import ( 4 "fmt" 5 "net" 6 "net/url" 7 "time" 8 9 "github.com/u-root/dhcp4" 10 dhcp4client "github.com/u-root/dhcp4/client" 11 dhcp4opts "github.com/u-root/dhcp4/opts" 12 "github.com/vishvananda/netlink" 13 ) 14 15 // Packet4 implements Packet for IPv4 DHCP. 16 type Packet4 struct { 17 p *dhcp4.Packet 18 } 19 20 var _ Packet = &Packet4{} 21 22 func newPacket4(p *dhcp4.Packet) *Packet4 { 23 return &Packet4{ 24 p: p, 25 } 26 } 27 28 // IPs implements Packet.IPs. 29 func (p *Packet4) IPs() []net.IP { 30 return []net.IP{p.p.YIAddr} 31 } 32 33 // Leases implements Packet.Leases. 34 // 35 // DHCPv4 only returns one lease at a time. 36 func (p *Packet4) Leases() []Lease { 37 netmask, err := dhcp4opts.GetSubnetMask(p.p.Options) 38 if err != nil { 39 // If they did not offer a subnet mask, we choose the most 40 // restrictive option. 41 netmask = []byte{255, 255, 255, 255} 42 } 43 44 return []Lease{ 45 { 46 IPNet: &net.IPNet{ 47 IP: p.p.YIAddr, 48 Mask: net.IPMask(netmask), 49 }, 50 }, 51 } 52 } 53 54 // Gateway implements Packet.Gateway. 55 // 56 // OptionRouter is used as opposed to GIAddr, which seems unused by most DHCP 57 // servers? 58 func (p *Packet4) Gateway() net.IP { 59 gw, err := dhcp4opts.GetRouters(p.p.Options) 60 if err != nil { 61 return nil 62 } 63 return gw[0] 64 } 65 66 // DNS implements Packet.DNS. 67 func (p *Packet4) DNS() []net.IP { 68 ips, err := dhcp4opts.GetDomainNameServers(p.p.Options) 69 if err != nil { 70 return nil 71 } 72 return []net.IP(ips) 73 } 74 75 // Boot implements Packet.Boot. 76 func (p *Packet4) Boot() (url.URL, string, error) { 77 // TODO: This is not 100% right -- if a certain option is set, this 78 // stuff is encoded in options instead of in the packet's BootFile and 79 // ServerName fields. 80 81 // While the default is tftp, servers may specify HTTP or FTP URIs. 82 u, err := url.Parse(p.p.BootFile) 83 if err != nil { 84 return url.URL{}, "", err 85 } 86 87 if len(u.Scheme) == 0 { 88 // Defaults to tftp is not specified. 89 u.Scheme = "tftp" 90 u.Path = p.p.BootFile 91 if len(p.p.ServerName) == 0 { 92 server, err := dhcp4opts.GetServerIdentifier(p.p.Options) 93 if err != nil { 94 return url.URL{}, "", err 95 } 96 u.Host = net.IP(server).String() 97 } else { 98 u.Host = p.p.ServerName 99 } 100 } 101 return *u, "", nil 102 } 103 104 // Client4 implements Client for DHCPv4. 105 type Client4 struct { 106 iface netlink.Link 107 client *dhcp4client.Client 108 } 109 110 var _ Client = &Client4{} 111 112 // NewV4 implements a new DHCPv4 client. 113 func NewV4(iface netlink.Link, timeout time.Duration, retry int) (*Client4, error) { 114 ifa, err := net.InterfaceByIndex(iface.Attrs().Index) 115 if err != nil { 116 return nil, err 117 } 118 119 client, err := dhcp4client.New(ifa /*, timeout, retry*/) 120 if err != nil { 121 return nil, err 122 } 123 124 return &Client4{ 125 iface: iface, 126 client: client, 127 }, nil 128 } 129 130 // Solicit implements Client.Solicit. 131 func (c *Client4) Solicit() (Packet, error) { 132 pkt, err := c.client.Request() 133 if err != nil { 134 return nil, err 135 } 136 return newPacket4(pkt), nil 137 } 138 139 // Renew implements Client.Renew. 140 func (c *Client4) Renew(p Packet) (Packet, error) { 141 pkt, ok := p.(*Packet4) 142 if !ok { 143 return nil, fmt.Errorf("passed non-DHCPv4 packet to RenewPacket4") 144 } 145 pp, err := c.client.Renew(pkt.p) 146 if err != nil { 147 return nil, err 148 } 149 return newPacket4(pp), nil 150 }