// Assumes RWLock is held func (subnet *Subnet) getFreeIP() (*net.IP, bool) { bit, success := firstClearBit(subnet.ActiveBits) if success { subnet.ActiveBits.Set(bit) ip := dhcp.IPAdd(subnet.ActiveStart, int(bit)) return &ip, true } // Free invalid or expired leases save_me := false now := time.Now() for k, lease := range subnet.Leases { if now.After(lease.ExpireTime) { if dhcp.IPInRange(subnet.ActiveStart, subnet.ActiveEnd, lease.Ip) { subnet.ActiveBits.Clear(uint(dhcp.IPRange(lease.Ip, subnet.ActiveStart) - 1)) } delete(subnet.Leases, k) save_me = true } } bit, success = firstClearBit(subnet.ActiveBits) if success { subnet.ActiveBits.Set(bit) ip := dhcp.IPAdd(subnet.ActiveStart, int(bit)) return &ip, true } // We got nothin' return nil, save_me }
func (subnet *Subnet) free_lease(dt *DataTracker, nic string) { subnet.lock.Lock() lease := subnet.Leases[nic] if lease != nil { if dhcp.IPInRange(subnet.ActiveStart, subnet.ActiveEnd, lease.Ip) { subnet.ActiveBits.Clear(uint(dhcp.IPRange(lease.Ip, subnet.ActiveStart) - 1)) } delete(subnet.Leases, nic) subnet.lock.Unlock() dt.save_data() } else { subnet.lock.Unlock() } }
func (dt *DataTracker) AddBinding(subnetName string, binding Binding) (error, int) { lsubnet := dt.Subnets[subnetName] if lsubnet == nil { return errors.New("Not Found"), http.StatusNotFound } // If existing, clear the reservation for IP b := lsubnet.Bindings[binding.Mac] if b != nil { if dhcp.IPInRange(lsubnet.ActiveStart, lsubnet.ActiveEnd, b.Ip) { lsubnet.ActiveBits.Clear(uint(dhcp.IPRange(lsubnet.ActiveStart, b.Ip) - 1)) } } // Reserve the IP if in Active range if dhcp.IPInRange(lsubnet.ActiveStart, lsubnet.ActiveEnd, binding.Ip) { lsubnet.ActiveBits.Set(uint(dhcp.IPRange(lsubnet.ActiveStart, binding.Ip) - 1)) } lsubnet.Bindings[binding.Mac] = &binding dt.save_data() return nil, http.StatusOK }
func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) { switch msgType { case dhcp.Discover: free, nic := -1, p.CHAddr().String() for i, v := range h.leases { // Find previous lease if v.nic == nic { free = i goto reply } } if free = h.freeLease(); free == -1 { return } reply: return dhcp.ReplyPacket(p, dhcp.Offer, h.ip, dhcp.IPAdd(h.start, free), h.leaseDuration, h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) case dhcp.Request: if server, ok := options[dhcp.OptionServerIdentifier]; ok && !net.IP(server).Equal(h.ip) { return nil // Message not for this dhcp server } reqIP := net.IP(options[dhcp.OptionRequestedIPAddress]) if reqIP == nil { reqIP = net.IP(p.CIAddr()) } if len(reqIP) == 4 && !reqIP.Equal(net.IPv4zero) { if leaseNum := dhcp.IPRange(h.start, reqIP) - 1; leaseNum >= 0 && leaseNum < h.leaseRange { if l, exists := h.leases[leaseNum]; !exists || l.nic == p.CHAddr().String() { h.leases[leaseNum] = lease{nic: p.CHAddr().String(), expiry: time.Now().Add(h.leaseDuration)} return dhcp.ReplyPacket(p, dhcp.ACK, h.ip, net.IP(options[dhcp.OptionRequestedIPAddress]), h.leaseDuration, h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) } } } return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil) case dhcp.Release, dhcp.Decline: nic := p.CHAddr().String() for i, v := range h.leases { if v.nic == nic { delete(h.leases, i) break } } } return nil }
func (dt *DataTracker) DeleteBinding(subnetName, mac string) (error, int) { lsubnet := dt.Subnets[subnetName] if lsubnet == nil { return errors.New("Subnet Not Found"), http.StatusNotFound } b := lsubnet.Bindings[mac] if b == nil { return errors.New("Binding Not Found"), http.StatusNotFound } if dhcp.IPInRange(lsubnet.ActiveStart, lsubnet.ActiveEnd, b.Ip) { lsubnet.ActiveBits.Clear(uint(dhcp.IPRange(lsubnet.ActiveStart, b.Ip) - 1)) } delete(lsubnet.Bindings, mac) dt.save_data() return nil, http.StatusOK }
func convertApiSubnetToSubnet(as *ApiSubnet, subnet *Subnet) (*Subnet, error) { if subnet == nil { subnet = NewSubnet() } subnet.Name = as.Name _, netdata, err := net.ParseCIDR(as.Subnet) if err != nil { return nil, err } subnet.Subnet = &MyIPNet{netdata} subnet.ActiveStart = net.ParseIP(as.ActiveStart).To4() subnet.ActiveEnd = net.ParseIP(as.ActiveEnd).To4() subnet.ActiveLeaseTime = time.Duration(as.ActiveLeaseTime) * time.Second subnet.ReservedLeaseTime = time.Duration(as.ReservedLeaseTime) * time.Second subnet.ActiveBits = bitset.New(uint(dhcp.IPRange(subnet.ActiveStart, subnet.ActiveEnd))) if as.NextServer != nil { ip := net.ParseIP(*as.NextServer).To4() subnet.NextServer = &ip } if subnet.ActiveLeaseTime == 0 { subnet.ActiveLeaseTime = 30 * time.Second } if subnet.ReservedLeaseTime == 0 { subnet.ReservedLeaseTime = 2 * time.Hour } for _, v := range as.Leases { subnet.Leases[v.Mac] = v if dhcp.IPInRange(subnet.ActiveStart, subnet.ActiveEnd, v.Ip) { subnet.ActiveBits.Set(uint(dhcp.IPRange(subnet.ActiveStart, v.Ip) - 1)) } } for _, v := range as.Bindings { subnet.Bindings[v.Mac] = v if dhcp.IPInRange(subnet.ActiveStart, subnet.ActiveEnd, v.Ip) { subnet.ActiveBits.Set(uint(dhcp.IPRange(subnet.ActiveStart, v.Ip) - 1)) } } // Seed initial options subnet.Options[dhcp.OptionSubnetMask] = []byte(net.IP(netdata.Mask).To4()) m := binary.BigEndian.Uint32(subnet.Options[dhcp.OptionSubnetMask]) n := binary.BigEndian.Uint32(netdata.IP) b := n | ^m result := make([]byte, 4) binary.BigEndian.PutUint32(result, b) subnet.Options[dhcp.OptionBroadcastAddress] = result for _, o := range as.Options { subnet.Options[o.Code], err = convertOptionValueToByte(o.Code, o.Value) if err != nil { return nil, err } } if !netdata.Contains(subnet.ActiveStart) { return nil, errors.New("ActiveStart not in Subnet") } if !netdata.Contains(subnet.ActiveEnd) { return nil, errors.New("ActiveEnd not in Subnet") } if dhcp.IPLess(subnet.ActiveEnd, subnet.ActiveStart) { return nil, errors.New("ActiveEnd less than ActiveStart") } return subnet, nil }