diff options
Diffstat (limited to 'netboot/netconf.go')
-rw-r--r-- | netboot/netconf.go | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/netboot/netconf.go b/netboot/netconf.go new file mode 100644 index 0000000..4740b0f --- /dev/null +++ b/netboot/netconf.go @@ -0,0 +1,131 @@ +package netboot + +import ( + "errors" + "fmt" + "io/ioutil" + "net" + "os" + "strings" + "time" + + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/vishvananda/netlink" +) + +// AddrConf holds a single IP address configuration for a NIC +type AddrConf struct { + IPNet net.IPNet + PreferredLifetime int + ValidLifetime int +} + +// NetConf holds multiple IP configuration for a NIC, and DNS configuration +type NetConf struct { + Addresses []AddrConf + DNSServers []net.IP + DNSSearchList []string +} + +// GetNetConfFromPacketv6 extracts network configuration information from a DHCPv6 +// Reply packet and returns a populated NetConf structure +func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) { + opt := d.GetOneOption(dhcpv6.OPTION_IA_NA) + if opt == nil { + return nil, errors.New("No option IA NA found") + } + netconf := NetConf{} + // get IP configuration + oiana := opt.(*dhcpv6.OptIANA) + iaaddrs := make([]*dhcpv6.OptIAAddress, 0) + for _, o := range oiana.Options() { + if o.Code() == dhcpv6.OPTION_IAADDR { + iaaddrs = append(iaaddrs, o.(*dhcpv6.OptIAAddress)) + } + } + netmask := net.IPMask(net.ParseIP("ffff:ffff:ffff:ffff::")) + for _, iaaddr := range iaaddrs { + netconf.Addresses = append(netconf.Addresses, AddrConf{ + IPNet: net.IPNet{ + IP: iaaddr.IPv6Addr(), + Mask: netmask, + }, + PreferredLifetime: int(iaaddr.PreferredLifetime()), + ValidLifetime: int(iaaddr.ValidLifetime()), + }) + } + // get DNS configuration + opt = d.GetOneOption(dhcpv6.DNS_RECURSIVE_NAME_SERVER) + if opt == nil { + return nil, errors.New("No option DNS Recursive Name Servers found ") + } + odnsserv := opt.(*dhcpv6.OptDNSRecursiveNameServer) + // TODO should this be copied? + netconf.DNSServers = odnsserv.NameServers() + + opt = d.GetOneOption(dhcpv6.DOMAIN_SEARCH_LIST) + if opt == nil { + return nil, errors.New("No option DNS Domain Search List found") + } + odomains := opt.(*dhcpv6.OptDomainSearchList) + // TODO should this be copied? + netconf.DNSSearchList = odomains.DomainSearchList() + + return &netconf, nil +} + +// IfUp brings up an interface by name, and waits for it to come up until a timeout expires +func IfUp(ifname string, timeout time.Duration) (netlink.Link, error) { + start := time.Now() + for time.Since(start) < timeout { + iface, err := netlink.LinkByName(ifname) + if err != nil { + return nil, fmt.Errorf("cannot get interface %q by name: %v", ifname, err) + } + + // if the interface is up, return + if iface.Attrs().OperState == netlink.OperUp { + // XXX despite the OperUp state, upon the first attempt I + // consistently get a "cannot assign requested address" error. This + // may be a bug in the netlink library. Need to investigate more. + time.Sleep(time.Second) + return iface, nil + } + // otherwise try to bring it up + if err := netlink.LinkSetUp(iface); err != nil { + return nil, fmt.Errorf("interface %q: %v can't make it up: %v", ifname, iface, err) + } + } + + return nil, fmt.Errorf("timed out while waiting for %s to come up", ifname) + +} + +// ConfigureInterface configures a network interface with the configuration held by a +// NetConf structure +func ConfigureInterface(ifname string, netconf *NetConf) error { + iface, err := netlink.LinkByName(ifname) + if err != nil { + return fmt.Errorf("error getting interface information for %s: %v", ifname, err) + } + // configure interfaces + for _, addr := range netconf.Addresses { + dest := &netlink.Addr{ + IPNet: &addr.IPNet, + PreferedLft: addr.PreferredLifetime, + ValidLft: addr.ValidLifetime, + } + if err := netlink.AddrReplace(iface, dest); err != nil { + if os.IsExist(err) { + return fmt.Errorf("cannot configure %s on %s,%d,%d: %v", ifname, addr.IPNet, addr.PreferredLifetime, addr.ValidLifetime, err) + } + } + } + // configure /etc/resolv.conf + resolvconf := "" + for _, ns := range netconf.DNSServers { + resolvconf += fmt.Sprintf("nameserver %s\n", ns) + } + resolvconf += fmt.Sprintf("search %s\n", strings.Join(netconf.DNSSearchList, " ")) + return ioutil.WriteFile("/etc/resolv.conf", []byte(resolvconf), 0644) +} |