github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/dhclient/dhcp6.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
     6  
     7  import (
     8  	"fmt"
     9  	"net"
    10  	"net/url"
    11  	"os"
    12  
    13  	"github.com/insomniacslk/dhcp/dhcpv6"
    14  	"github.com/vishvananda/netlink"
    15  	"golang.org/x/sys/unix"
    16  )
    17  
    18  // Packet6 implements Packet for IPv6 DHCP.
    19  type Packet6 struct {
    20  	p     *dhcpv6.Message
    21  	iface netlink.Link
    22  }
    23  
    24  // NewPacket6 wraps a DHCPv6 packet with some convenience methods.
    25  func NewPacket6(iface netlink.Link, p *dhcpv6.Message) *Packet6 {
    26  	return &Packet6{
    27  		p:     p,
    28  		iface: iface,
    29  	}
    30  }
    31  
    32  // Link returns the interface this packet was received for.
    33  func (p *Packet6) Link() netlink.Link {
    34  	return p.iface
    35  }
    36  
    37  // Configure6 adds IPv6 addresses, routes, and DNS servers to the system.
    38  func Configure6(iface netlink.Link, packet *dhcpv6.Message) error {
    39  	p := NewPacket6(iface, packet)
    40  	return p.Configure()
    41  }
    42  
    43  // Configure configures interface using this packet.
    44  func (p *Packet6) Configure() error {
    45  	l := p.Lease()
    46  	if l == nil {
    47  		return fmt.Errorf("no lease returned")
    48  	}
    49  
    50  	// Add the address to the iface.
    51  	dst := &netlink.Addr{
    52  		IPNet: &net.IPNet{
    53  			IP:   l.IPv6Addr,
    54  			Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::")),
    55  		},
    56  		PreferedLft: int(l.PreferredLifetime),
    57  		ValidLft:    int(l.ValidLifetime),
    58  		// Optimistic DAD (Duplicate Address Detection) means we can
    59  		// use the address before DAD is complete. The DHCP server's
    60  		// job was to give us a unique IP so there is little risk of a
    61  		// collision.
    62  		Flags: unix.IFA_F_OPTIMISTIC,
    63  	}
    64  	if err := netlink.AddrReplace(p.iface, dst); err != nil {
    65  		if os.IsExist(err) {
    66  			return fmt.Errorf("add/replace %s to %v: %v", dst, p.iface, err)
    67  		}
    68  	}
    69  
    70  	if ips := p.DNS(); ips != nil {
    71  		if err := WriteDNSSettings(ips, nil, ""); err != nil {
    72  			return err
    73  		}
    74  	}
    75  	return nil
    76  }
    77  
    78  func (p *Packet6) String() string {
    79  	return fmt.Sprintf("IPv6 DHCP Lease IP %s", p.Lease().IPv6Addr)
    80  }
    81  
    82  // Lease returns lease information assigned.
    83  func (p *Packet6) Lease() *dhcpv6.OptIAAddress {
    84  	// TODO(chrisko): Reform dhcpv6 option handling to be like dhcpv4.
    85  	ianaOpt := p.p.GetOneOption(dhcpv6.OptionIANA)
    86  	iana, ok := ianaOpt.(*dhcpv6.OptIANA)
    87  	if !ok {
    88  		return nil
    89  	}
    90  
    91  	iaAddrOpt := iana.Options.GetOne(dhcpv6.OptionIAAddr)
    92  	iaAddr, ok := iaAddrOpt.(*dhcpv6.OptIAAddress)
    93  	if !ok {
    94  		return nil
    95  	}
    96  	return iaAddr
    97  }
    98  
    99  // DNS returns DNS servers assigned.
   100  func (p *Packet6) DNS() []net.IP {
   101  	// TODO: Would the IANA contain this, or the packet?
   102  	dnsOpt := p.p.GetOneOption(dhcpv6.OptionDNSRecursiveNameServer)
   103  	dns, ok := dnsOpt.(*dhcpv6.OptDNSRecursiveNameServer)
   104  	if !ok {
   105  		return nil
   106  	}
   107  	return dns.NameServers
   108  }
   109  
   110  // Boot returns the boot file URL and parameters assigned.
   111  //
   112  // TODO: RFC 5970 is helpfully avoidant of where these options are used. Are
   113  // they added to the packet? Are they added to an IANA?  It *seems* like it's
   114  // in the packet.
   115  func (p *Packet6) Boot() (*url.URL, error) {
   116  	uriOpt := p.p.GetOneOption(dhcpv6.OptionBootfileURL)
   117  	uri, ok := uriOpt.(*dhcpv6.OptBootFileURL)
   118  	if !ok {
   119  		return nil, fmt.Errorf("packet does not contain boot file URL")
   120  	}
   121  	// Srsly, a []byte?
   122  	return url.Parse(string(uri.ToBytes()))
   123  }
   124  
   125  // ISCSIBoot returns the target address and volume name to boot from if
   126  // they were part of the DHCP message.
   127  //
   128  // Parses the DHCPv6 Boot File for iSCSI target and volume as specified by RFC
   129  // 4173 and RFC 5970.
   130  func (p *Packet6) ISCSIBoot() (*net.TCPAddr, string, error) {
   131  	uriOpt := p.p.GetOneOption(dhcpv6.OptionBootfileURL)
   132  	uri, ok := uriOpt.(*dhcpv6.OptBootFileURL)
   133  	if !ok {
   134  		return nil, "", fmt.Errorf("packet does not contain boot file URL")
   135  	}
   136  	return parseISCSIURI(string(uri.ToBytes()))
   137  }