github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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 Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::")), 61 }, 62 PreferedLft: int(l.PreferredLifetime), 63 ValidLft: int(l.ValidLifetime), 64 // Optimistic DAD (Duplicate Address Detection) means we can 65 // use the address before DAD is complete. The DHCP server's 66 // job was to give us a unique IP so there is little risk of a 67 // collision. 68 Flags: unix.IFA_F_OPTIMISTIC, 69 } 70 if err := netlink.AddrReplace(p.iface, dst); err != nil { 71 if os.IsExist(err) { 72 return fmt.Errorf("add/replace %s to %v: %v", dst, p.iface, err) 73 } 74 } 75 76 if ips := p.DNS(); ips != nil { 77 if err := WriteDNSSettings(ips, nil, ""); err != nil { 78 return err 79 } 80 } 81 return nil 82 } 83 84 func (p *Packet6) String() string { 85 return fmt.Sprintf("IPv6 DHCP Lease IP %s", p.Lease().IPv6Addr) 86 } 87 88 // Lease returns lease information assigned. 89 func (p *Packet6) Lease() *dhcpv6.OptIAAddress { 90 // TODO(chrisko): Reform dhcpv6 option handling to be like dhcpv4. 91 ianaOpt := p.p.GetOneOption(dhcpv6.OptionIANA) 92 iana, ok := ianaOpt.(*dhcpv6.OptIANA) 93 if !ok { 94 return nil 95 } 96 97 iaAddrOpt := iana.Options.GetOne(dhcpv6.OptionIAAddr) 98 iaAddr, ok := iaAddrOpt.(*dhcpv6.OptIAAddress) 99 if !ok { 100 return nil 101 } 102 return iaAddr 103 } 104 105 // DNS returns DNS servers assigned. 106 func (p *Packet6) DNS() []net.IP { 107 // TODO: Would the IANA contain this, or the packet? 108 dnsOpt := p.p.GetOneOption(dhcpv6.OptionDNSRecursiveNameServer) 109 dns, ok := dnsOpt.(*dhcpv6.OptDNSRecursiveNameServer) 110 if !ok { 111 return nil 112 } 113 return dns.NameServers 114 } 115 116 // Boot returns the boot file URL and parameters assigned. 117 // 118 // TODO: RFC 5970 is helpfully avoidant of where these options are used. Are 119 // they added to the packet? Are they added to an IANA? It *seems* like it's 120 // in the packet. 121 func (p *Packet6) Boot() (*url.URL, error) { 122 uriOpt := p.p.GetOneOption(dhcpv6.OptionBootfileURL) 123 uri, ok := uriOpt.(dhcpv6.OptBootFileURL) 124 if !ok { 125 return nil, fmt.Errorf("packet does not contain boot file URL") 126 } 127 // Srsly, a []byte? 128 return url.Parse(string(uri.ToBytes())) 129 } 130 131 // ISCSIBoot returns the target address and volume name to boot from if 132 // they were part of the DHCP message. 133 // 134 // Parses the DHCPv6 Boot File for iSCSI target and volume as specified by RFC 135 // 4173 and RFC 5970. 136 func (p *Packet6) ISCSIBoot() (*net.TCPAddr, string, error) { 137 uriOpt := p.p.GetOneOption(dhcpv6.OptionBootfileURL) 138 uri, ok := uriOpt.(dhcpv6.OptBootFileURL) 139 if !ok { 140 return nil, "", fmt.Errorf("packet does not contain boot file URL") 141 } 142 return parseISCSIURI(string(uri.ToBytes())) 143 }