func (c *client) isCompletePacket(p *dhcp.Packet) bool { complete := !ip.IsUnspecifiedIP(p.YourIP()) && !ip.IsUnspecifiedIP(p.ServerIP()) if !complete { return false } for _, param := range c.params { switch dhcp4.OptionCode(param) { case dhcp4.OptionSubnetMask: ones, bits := p.SubnetMask().Size() if ones == 0 || bits == 0 { return false } case dhcp4.OptionRouter: if ip.IsUnspecifiedIP(p.Gateway()) { return false } case dhcp4.OptionDomainNameServer: if len(p.DNS()) == 0 { return false } } } if p.LeaseTime().Seconds() == 0 { return false } return true }
func (t *BaseOperations) dhcpLoop(stop chan bool, e *NetworkEndpoint, ack *dhcp.Packet, id client.ID) { exp := time.After(ack.LeaseTime() / 2) for { select { case <-stop: // release the ip log.Infof("releasing IP address for network %s", e.Name) t.dhcpClient.Release(ack) return case <-exp: log.Infof("renewing IP address for network %s", e.Name) newack, err := t.dhcpClient.Renew(id, ack) if err != nil { log.Errorf("failed to renew ip address for network %s: %s", e.Name, err) continue } ack = newack 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 getDynamicIP(t Netlink, link netlink.Link, endpoint *NetworkEndpoint) (client.Client, error) { var ack *dhcp.Packet var err error // use dhcp to acquire address dc, err := client.NewClient(link.Attrs().Index, link.Attrs().HardwareAddr) if err != nil { return nil, err } params := []byte{byte(dhcp4.OptionSubnetMask)} if ip.IsUnspecifiedIP(endpoint.Network.Gateway.IP) { params = append(params, byte(dhcp4.OptionRouter)) } if len(endpoint.Network.Nameservers) == 0 { params = append(params, byte(dhcp4.OptionDomainNameServer)) } dc.SetParameterRequestList(params...) err = dc.Request() if err != nil { log.Errorf("error sending dhcp request: %s", err) return nil, err } ack = dc.LastAck() 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() } }() return dc, nil }
func apply(nl Netlink, t *BaseOperations, endpoint *NetworkEndpoint) error { // Locate interface slot, err := strconv.Atoi(endpoint.ID) if err != nil { detail := fmt.Sprintf("endpoint ID must be a base10 numeric pci slot identifier: %s", err) return errors.New(detail) } link, err := nl.LinkBySlot(int32(slot)) if err != nil { detail := fmt.Sprintf("unable to acquire reference to link %s: %s", endpoint.ID, err) return errors.New(detail) } // rename the link if needed link, err = renameLink(nl, link, int32(slot), endpoint) if err != nil { detail := fmt.Sprintf("unable to reacquire link %s after rename pass: %s", endpoint.ID, err) return errors.New(detail) } var ack *dhcp.Packet defer func() { if err != nil && ack != nil { t.dhcpClient.Release(ack) } }() 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 { ack, err = getDynamicIP(nl, link, t.dhcpClient) if err != nil { return err } 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.Static if newIP.IP.IsUnspecified() { // 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 } t.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 ack != nil { if _, ok := t.dhcpLoops[endpoint.ID]; !ok { stop := make(chan bool) id, err := client.NewID(link.Attrs().Index, link.Attrs().HardwareAddr) if err != nil { log.Errorf("could not make DHCP client id for link %s: %s", link.Attrs().Name, err) } else { go t.dhcpLoop(stop, endpoint, ack, id) t.dhcpLoops[endpoint.ID] = stop } } } return nil }
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 }