summaryrefslogtreecommitdiffhomepage
path: root/netboot/netconf.go
diff options
context:
space:
mode:
Diffstat (limited to 'netboot/netconf.go')
-rw-r--r--netboot/netconf.go120
1 files changed, 119 insertions, 1 deletions
diff --git a/netboot/netconf.go b/netboot/netconf.go
index 84fb263..50e339c 100644
--- a/netboot/netconf.go
+++ b/netboot/netconf.go
@@ -9,6 +9,7 @@ import (
"strings"
"time"
+ "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/vishvananda/netlink"
)
@@ -25,6 +26,7 @@ type NetConf struct {
Addresses []AddrConf
DNSServers []net.IP
DNSSearchList []string
+ Routers []net.IP
}
// GetNetConfFromPacketv6 extracts network configuration information from a DHCPv6
@@ -74,6 +76,89 @@ func GetNetConfFromPacketv6(d *dhcpv6.DHCPv6Message) (*NetConf, error) {
return &netconf, nil
}
+// GetNetConfFromPacketv4 extracts network configuration information from a DHCPv4
+// Reply packet and returns a populated NetConf structure
+func GetNetConfFromPacketv4(d *dhcpv4.DHCPv4) (*NetConf, error) {
+ // extract the address from the DHCPv4 address
+ ipAddr := d.YourIPAddr()
+ if ipAddr.Equal(net.IPv4zero) {
+ return nil, errors.New("ip address is null (0.0.0.0)")
+ }
+ netconf := NetConf{}
+
+ // get the subnet mask from OptionSubnetMask. If the netmask is not defined
+ // in the packet, an error is returned
+ netmaskOption := d.GetOneOption(dhcpv4.OptionSubnetMask)
+ if netmaskOption == nil {
+ return nil, errors.New("no netmask option in response packet")
+ }
+ netmask := netmaskOption.(*dhcpv4.OptSubnetMask).SubnetMask
+ ones, _ := netmask.Size()
+ if ones == 0 {
+ return nil, errors.New("netmask extracted from OptSubnetMask options is null")
+ }
+
+ // netconf struct requires a valid lifetime to be specified. ValidLifetime is a dhcpv6
+ // concept, the closest mapping in dhcpv4 world is "IP Address Lease Time". If the lease
+ // time option is nil, we set it to 0
+ leaseTimeOption := d.GetOneOption(dhcpv4.OptionIPAddressLeaseTime)
+ leaseTime := uint32(0)
+ if leaseTimeOption != nil {
+ leaseTime = leaseTimeOption.(*dhcpv4.OptIPAddressLeaseTime).LeaseTime
+ }
+
+ if int(leaseTime) < 0 {
+ return nil, fmt.Errorf("lease time overflow, Original lease time: %d", leaseTime)
+ }
+
+ netconf.Addresses = append(netconf.Addresses, AddrConf{
+ IPNet: net.IPNet{
+ IP: ipAddr,
+ Mask: netmask,
+ },
+ PreferredLifetime: 0,
+ ValidLifetime: int(leaseTime),
+ })
+
+ // get DNS configuration
+ dnsServersOption := d.GetOneOption(dhcpv4.OptionDomainNameServer)
+ if dnsServersOption == nil {
+ return nil, errors.New("name servers option is empty")
+ }
+ dnsServers := dnsServersOption.(*dhcpv4.OptDomainNameServer).NameServers
+ if len(dnsServers) == 0 {
+ return nil, errors.New("no dns servers options in response packet")
+ }
+ netconf.DNSServers = dnsServers
+
+ // get domain search list
+ dnsDomainSearchListOption := d.GetOneOption(dhcpv4.OptionDNSDomainSearchList)
+ if dnsDomainSearchListOption == nil {
+ return nil, errors.New("no domain search list option in response packet")
+
+ }
+ dnsSearchList := dnsDomainSearchListOption.(*dhcpv4.OptDomainSearch).DomainSearch
+ if len(dnsSearchList) == 0 {
+ return nil, errors.New("dns search list is empty")
+ }
+ netconf.DNSSearchList = dnsSearchList
+
+ // get default gateway
+ routerOption := d.GetOneOption(dhcpv4.OptionRouter)
+ if routerOption == nil {
+ return nil, errors.New("no router option specified in response packet")
+ }
+
+ routersList := routerOption.(*dhcpv4.OptRouter).Routers
+ if len(routersList) == 0 {
+ return nil, errors.New("no routers specified in the corresponding option")
+ }
+
+ netconf.Routers = routersList
+
+ 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()
@@ -127,5 +212,38 @@ func ConfigureInterface(ifname string, netconf *NetConf) error {
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)
+ if err = ioutil.WriteFile("/etc/resolv.conf", []byte(resolvconf), 0644); err != nil {
+ return fmt.Errorf("could not write resolv.conf file %v", err)
+ }
+
+ // add default route information for v4 space. only one default route is allowed
+ // so ignore the others if there are multiple ones
+ if len(netconf.Routers) > 0 {
+ iface, err = netlink.LinkByName(ifname)
+ if err != nil {
+ return fmt.Errorf("could not obtain interface when adding default route: %v", err)
+ }
+ // if there is a default v4 route, remove it, as we want to add the one we just got during
+ // the dhcp transaction. if the route is not present, which is the final state we want,
+ // an error is returned so ignore it
+ dst := &net.IPNet{
+ IP: net.IPv4(0, 0, 0, 0),
+ Mask: net.CIDRMask(0, 32),
+ }
+ // Remove a possible default route (dst 0.0.0.0) to the L2 domain (gw: 0.0.0.0), which is what
+ // a client would want to add before initiating the DHCP transaction in order not to fail with
+ // ENETUNREACH. If this default route has a specific metric assigned, it doesn't get removed.
+ // The code doesn't remove any other default route (i.e. gw != 0.0.0.0).
+ route := netlink.Route{LinkIndex: iface.Attrs().Index, Dst: dst, Src: net.IPv4(0, 0, 0, 0)}
+ netlink.RouteDel(&route)
+
+ src := netconf.Addresses[0].IPNet.IP
+ route = netlink.Route{LinkIndex: iface.Attrs().Index, Dst: dst, Src: src, Gw: netconf.Routers[0]}
+ err = netlink.RouteAdd(&route)
+ if err != nil {
+ return fmt.Errorf("could not add default route (%+v) to interface %s: %v", route, iface.Attrs().Name, err)
+ }
+ }
+
+ return nil
}