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  }