From 725aeeb910eeb69fc7463cd2492eaf97e4863868 Mon Sep 17 00:00:00 2001 From: Simon Zolin Date: Tue, 14 May 2019 13:02:04 +0300 Subject: [PATCH] + dhcp: /dhcp/add_static_lease, /dhcp/remove_static_lease: control static lease table --- control.go | 2 ++ dhcp.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ dhcpd/dhcpd.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/control.go b/control.go index 2be9b8ba..77970de2 100644 --- a/control.go +++ b/control.go @@ -993,6 +993,8 @@ func registerControlHandlers() { http.HandleFunc("/control/dhcp/interfaces", postInstall(optionalAuth(ensureGET(handleDHCPInterfaces)))) http.HandleFunc("/control/dhcp/set_config", postInstall(optionalAuth(ensurePOST(handleDHCPSetConfig)))) http.HandleFunc("/control/dhcp/find_active_dhcp", postInstall(optionalAuth(ensurePOST(handleDHCPFindActiveServer)))) + http.HandleFunc("/control/dhcp/add_static_lease", postInstall(optionalAuth(ensurePOST(handleDHCPAddStaticLease)))) + http.HandleFunc("/control/dhcp/remove_static_lease", postInstall(optionalAuth(ensurePOST(handleDHCPRemoveStaticLease)))) RegisterTLSHandlers() RegisterClientsHandlers() diff --git a/dhcp.go b/dhcp.go index e2938fa8..36c8fa39 100644 --- a/dhcp.go +++ b/dhcp.go @@ -47,8 +47,15 @@ func handleDHCPStatus(w http.ResponseWriter, r *http.Request) { } } +type leaseJSON struct { + HWAddr string `json:"mac"` + IP string `json:"ip"` + Hostname string `json:"hostname"` +} + type dhcpServerConfigJSON struct { dhcpd.ServerConfig `json:",inline"` + StaticLeases []leaseJSON `json:"static_leases"` } func handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { @@ -349,6 +356,68 @@ func setStaticIP(ifaceName string) error { return nil } +func handleDHCPAddStaticLease(w http.ResponseWriter, r *http.Request) { + log.Tracef("%s %v", r.Method, r.URL) + + lj := leaseJSON{} + err := json.NewDecoder(r.Body).Decode(&lj) + if err != nil { + httpError(w, http.StatusBadRequest, "json.Decode: %s", err) + return + } + + ip := parseIPv4(lj.IP) + if ip == nil { + httpError(w, http.StatusBadRequest, "invalid IP") + return + } + + mac, _ := net.ParseMAC(lj.HWAddr) + + lease := dhcpd.Lease{ + IP: ip, + HWAddr: mac, + Hostname: lj.Hostname, + } + err = dhcpServer.AddStaticLease(lease) + if err != nil { + httpError(w, http.StatusBadRequest, "%s", err) + return + } + returnOK(w) +} + +func handleDHCPRemoveStaticLease(w http.ResponseWriter, r *http.Request) { + log.Tracef("%s %v", r.Method, r.URL) + + lj := leaseJSON{} + err := json.NewDecoder(r.Body).Decode(&lj) + if err != nil { + httpError(w, http.StatusBadRequest, "json.Decode: %s", err) + return + } + + ip := parseIPv4(lj.IP) + if ip == nil { + httpError(w, http.StatusBadRequest, "invalid IP") + return + } + + mac, _ := net.ParseMAC(lj.HWAddr) + + lease := dhcpd.Lease{ + IP: ip, + HWAddr: mac, + Hostname: lj.Hostname, + } + err = dhcpServer.RemoveStaticLease(lease) + if err != nil { + httpError(w, http.StatusBadRequest, "%s", err) + return + } + returnOK(w) +} + func startDHCPServer() error { if !config.DHCP.Enabled { // not enabled, don't do anything diff --git a/dhcpd/dhcpd.go b/dhcpd/dhcpd.go index 292cb244..20ab4d24 100644 --- a/dhcpd/dhcpd.go +++ b/dhcpd/dhcpd.go @@ -14,6 +14,7 @@ import ( ) const defaultDiscoverTime = time.Second * 3 +const leaseExpireStatic = 1 // Lease contains the necessary information about a DHCP lease // field ordering is important -- yaml fields will mirror ordering from here @@ -21,7 +22,10 @@ type Lease struct { HWAddr net.HardwareAddr `json:"mac" yaml:"hwaddr"` IP net.IP `json:"ip"` Hostname string `json:"hostname"` - Expiry time.Time `json:"expires"` + + // Lease expiration time + // 1: static lease + Expiry time.Time `json:"expires"` } // ServerConfig - DHCP server configuration @@ -274,7 +278,7 @@ func (s *Server) findLease(p dhcp4.Packet) *Lease { func (s *Server) findExpiredLease() int { now := time.Now().Unix() for i, lease := range s.leases { - if lease.Expiry.Unix() <= now { + if lease.Expiry.Unix() <= now && lease.Expiry.Unix() != leaseExpireStatic { return i } } @@ -529,6 +533,69 @@ func (s *Server) handleDecline(p dhcp4.Packet, options dhcp4.Options) dhcp4.Pack return nil } +// AddStaticLease adds a static lease (thread-safe) +func (s *Server) AddStaticLease(l Lease) error { + if s.IPpool == nil { + return fmt.Errorf("DHCP server isn't started") + } + + if len(l.IP) != 4 { + return fmt.Errorf("Invalid IP") + } + if len(l.HWAddr) != 6 { + return fmt.Errorf("Invalid MAC") + } + l.Expiry = time.Unix(leaseExpireStatic, 0) + + s.leasesLock.Lock() + defer s.leasesLock.Unlock() + + if s.findReservedHWaddr(l.IP) != nil { + return fmt.Errorf("IP is already used") + } + s.leases = append(s.leases, &l) + s.reserveIP(l.IP, l.HWAddr) + s.dbStore() + return nil +} + +// RemoveStaticLease removes a static lease (thread-safe) +func (s *Server) RemoveStaticLease(l Lease) error { + if s.IPpool == nil { + return fmt.Errorf("DHCP server isn't started") + } + + if len(l.IP) != 4 { + return fmt.Errorf("Invalid IP") + } + if len(l.HWAddr) != 6 { + return fmt.Errorf("Invalid MAC") + } + + s.leasesLock.Lock() + defer s.leasesLock.Unlock() + + if s.findReservedHWaddr(l.IP) == nil { + return fmt.Errorf("Lease not found") + } + + var newLeases []*Lease + for _, lease := range s.leases { + if bytes.Equal(lease.IP.To4(), l.IP) { + if !bytes.Equal(lease.HWAddr, l.HWAddr) || + lease.Hostname != l.Hostname { + return fmt.Errorf("Lease not found") + } + continue + } + newLeases = append(newLeases, lease) + } + s.leases = newLeases + s.unreserveIP(l.IP) + s.dbStore() + return nil +} + // Leases returns the list of current DHCP leases (thread-safe) func (s *Server) Leases() []Lease { var result []Lease