func getDynamicIP(t Netlink, link netlink.Link, dc client.Client) (*dhcp.Packet, error) { var ack *dhcp.Packet var err error // use dhcp to acquire address id, err := client.NewID(link.Attrs().Index, link.Attrs().HardwareAddr) if err != nil { return nil, err } ack, err = dc.Request(id) if err != nil { log.Errorf("error sending dhcp request: %s", err) return nil, err } if ack.YourIP() == nil || ack.SubnetMask() == nil { err = fmt.Errorf("dhcp assigned nil ip or subnet mask") log.Error(err) return nil, err } log.Infof("DHCP response: IP=%s, SubnetMask=%s, Gateway=%s, DNS=%s, Lease Time=%s", ack.YourIP(), ack.SubnetMask(), ack.Gateway(), ack.DNS(), ack.LeaseTime()) defer func() { if err != nil && ack != nil { dc.Release(ack) } }() return ack, nil }
func (t *BaseOperations) dhcpLoop(stop chan struct{}, e *NetworkEndpoint, dc client.Client) { exp := time.After(dc.LastAck().LeaseTime() / 2) for { select { case <-stop: // release the ip log.Infof("releasing IP address for network %s", e.Name) dc.Release() return case <-exp: log.Infof("renewing IP address for network %s", e.Name) err := dc.Renew() if err != nil { log.Errorf("failed to renew ip address for network %s: %s", e.Name, err) continue } ack := dc.LastAck() log.Infof("successfully renewed ip address: IP=%s, SubnetMask=%s, Gateway=%s, DNS=%s, Lease Time=%s", ack.YourIP(), ack.SubnetMask(), ack.Gateway(), ack.DNS(), ack.LeaseTime()) e.DHCP = &DHCPInfo{ Assigned: net.IPNet{IP: ack.YourIP(), Mask: ack.SubnetMask()}, Gateway: net.IPNet{IP: ack.Gateway(), Mask: ack.SubnetMask()}, Nameservers: ack.DNS(), } t.Apply(e) if err = t.config.UpdateNetworkEndpoint(e); err != nil { log.Error(err) } // update any endpoints that share this NIC for _, d := range t.dynEndpoints[e.ID] { if e == d { continue } d.DHCP = e.DHCP t.Apply(d) if err = t.config.UpdateNetworkEndpoint(d); err != nil { log.Error(err) } } t.config.Flush() exp = time.After(ack.LeaseTime() / 2) } } }
func apply(nl Netlink, t *BaseOperations, endpoint *NetworkEndpoint) error { if endpoint.applied { log.Infof("skipping applying config for network %s as it has been applied already", endpoint.Network.Name) return nil // already applied } // Locate interface slot, err := strconv.Atoi(endpoint.ID) if err != nil { return fmt.Errorf("endpoint ID must be a base10 numeric pci slot identifier: %s", err) } defer func() { if err == nil { log.Infof("successfully applied config for network %s", endpoint.Network.Name) endpoint.applied = true } }() var link netlink.Link link, err = nl.LinkBySlot(int32(slot)) if err != nil { return fmt.Errorf("unable to acquire reference to link %s: %s", endpoint.ID, err) } // rename the link if needed link, err = renameLink(nl, link, int32(slot), endpoint) if err != nil { return fmt.Errorf("unable to reacquire link %s after rename pass: %s", endpoint.ID, err) } var dc client.Client defer func() { if err != nil && dc != nil { dc.Release() } }() var newIP *net.IPNet if endpoint.IsDynamic() && endpoint.DHCP == nil { if e, ok := t.dynEndpoints[endpoint.ID]; ok { // endpoint shares NIC, copy over DHCP endpoint.DHCP = e[0].DHCP } } log.Debugf("%+v", endpoint) if endpoint.IsDynamic() { if endpoint.DHCP == nil { dc, err = getDynamicIP(nl, link, endpoint) if err != nil { return err } ack := dc.LastAck() endpoint.DHCP = &DHCPInfo{ Assigned: net.IPNet{IP: ack.YourIP(), Mask: ack.SubnetMask()}, Nameservers: ack.DNS(), Gateway: net.IPNet{IP: ack.Gateway(), Mask: ack.SubnetMask()}, } } newIP = &endpoint.DHCP.Assigned } else { newIP = endpoint.IP if newIP.IP.Equal(net.IPv4zero) { // managed externally return nil } } var old *net.IPNet if !ip.IsUnspecifiedIP(endpoint.Assigned.IP) { old = &endpoint.Assigned } if err = linkAddrUpdate(old, newIP, nl, link); err != nil { return err } updateEndpoint(newIP, endpoint) if err = updateDefaultRoute(nl, link, endpoint); err != nil { return err } if err = t.updateHosts(endpoint); err != nil { return err } Sys.ResolvConf.RemoveNameservers(endpoint.Network.Nameservers...) if err = t.updateNameservers(endpoint); err != nil { return err } if endpoint.IsDynamic() { eps := t.dynEndpoints[endpoint.ID] found := false for _, e := range eps { if e == endpoint { found = true break } } if !found { eps = append(eps, endpoint) t.dynEndpoints[endpoint.ID] = eps } } // add renew/release loop if necessary if dc != nil { if _, ok := t.dhcpLoops[endpoint.ID]; !ok { stop := make(chan struct{}) if err != nil { log.Errorf("could not make DHCP client id for link %s: %s", link.Attrs().Name, err) } else { t.dhcpLoops[endpoint.ID] = stop go t.dhcpLoop(stop, endpoint, dc) } } } return nil }