github.hscsec.cn/u-root/u-root@v7.0.0+incompatible/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/dhcpv4"
    14  	"github.com/insomniacslk/dhcp/dhcpv6"
    15  	"github.com/vishvananda/netlink"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  // Packet6 implements Packet for IPv6 DHCP.
    20  type Packet6 struct {
    21  	p     *dhcpv6.Message
    22  	iface netlink.Link
    23  }
    24  
    25  // NewPacket6 wraps a DHCPv6 packet with some convenience methods.
    26  func NewPacket6(iface netlink.Link, p *dhcpv6.Message) *Packet6 {
    27  	return &Packet6{
    28  		p:     p,
    29  		iface: iface,
    30  	}
    31  }
    32  
    33  // Message returns the unwrapped DHCPv6 packet.
    34  func (p *Packet6) Message() (*dhcpv4.DHCPv4, *dhcpv6.Message) {
    35  	return nil, p.p
    36  }
    37  
    38  // Link returns the interface this packet was received for.
    39  func (p *Packet6) Link() netlink.Link {
    40  	return p.iface
    41  }
    42  
    43  // Configure6 adds IPv6 addresses, routes, and DNS servers to the system.
    44  func Configure6(iface netlink.Link, packet *dhcpv6.Message) error {
    45  	p := NewPacket6(iface, packet)
    46  	return p.Configure()
    47  }
    48  
    49  // Configure configures interface using this packet.
    50  func (p *Packet6) Configure() error {
    51  	l := p.Lease()
    52  	if l == nil {
    53  		return fmt.Errorf("no lease returned")
    54  	}
    55  
    56  	// Add the address to the iface.
    57  	dst := &netlink.Addr{
    58  		IPNet: &net.IPNet{
    59  			IP: l.IPv6Addr,
    60  
    61  			// This mask tells Linux which addresses we know to be
    62  			// "on-link" (i.e., reachable on this interface without
    63  			// having to talk to a router).
    64  			//
    65  			// Since DHCPv6 does not give us that information, we
    66  			// have to assume that no addresses are on-link. To do
    67  			// that, we use /128. (See also RFC 5942 Section 5,
    68  			// "Observed Incorrect Implementation Behavior".)
    69  			Mask: net.CIDRMask(128, 128),
    70  		},
    71  		PreferedLft: int(l.PreferredLifetime),
    72  		ValidLft:    int(l.ValidLifetime),
    73  		// Optimistic DAD (Duplicate Address Detection) means we can
    74  		// use the address before DAD is complete. The DHCP server's
    75  		// job was to give us a unique IP so there is little risk of a
    76  		// collision.
    77  		Flags: unix.IFA_F_OPTIMISTIC,
    78  	}
    79  	if err := netlink.AddrReplace(p.iface, dst); err != nil {
    80  		if os.IsExist(err) {
    81  			return fmt.Errorf("add/replace %s to %v: %v", dst, p.iface, err)
    82  		}
    83  	}
    84  
    85  	if ips := p.DNS(); ips != nil {
    86  		if err := WriteDNSSettings(ips, nil, ""); err != nil {
    87  			return err
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  func (p *Packet6) String() string {
    94  	return fmt.Sprintf("IPv6 DHCP Lease IP %s", p.Lease().IPv6Addr)
    95  }
    96  
    97  // Lease returns lease information assigned.
    98  func (p *Packet6) Lease() *dhcpv6.OptIAAddress {
    99  	iana := p.p.Options.OneIANA()
   100  	if iana == nil {
   101  		return nil
   102  	}
   103  	return iana.Options.OneAddress()
   104  }
   105  
   106  // DNS returns DNS servers assigned.
   107  func (p *Packet6) DNS() []net.IP {
   108  	return p.p.Options.DNS()
   109  }
   110  
   111  // Boot returns the boot file URL and parameters assigned.
   112  func (p *Packet6) Boot() (*url.URL, error) {
   113  	uri := p.p.Options.BootFileURL()
   114  	if len(uri) == 0 {
   115  		return nil, fmt.Errorf("packet does not contain boot file URL")
   116  	}
   117  	return url.Parse(uri)
   118  }
   119  
   120  // ISCSIBoot returns the target address and volume name to boot from if
   121  // they were part of the DHCP message.
   122  //
   123  // Parses the DHCPv6 Boot File for iSCSI target and volume as specified by RFC
   124  // 4173 and RFC 5970.
   125  func (p *Packet6) ISCSIBoot() (*net.TCPAddr, string, error) {
   126  	uri := p.p.Options.BootFileURL()
   127  	if len(uri) == 0 {
   128  		return nil, "", fmt.Errorf("packet does not contain boot file URL")
   129  	}
   130  	return ParseISCSIURI(uri)
   131  }