From 2b6ab5b2bd30cd4d15a7160b3ef178c2ac692dca Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 12 Apr 2020 10:33:11 +0200 Subject: [PATCH] dhcp4d: make hostname override settable from status page --- cmd/dhcp4d/dhcp4d.go | 27 ++++++++++++++++++++- internal/dhcp4d/dhcp4d.go | 49 ++++++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/cmd/dhcp4d/dhcp4d.go b/cmd/dhcp4d/dhcp4d.go index 48f0422..7d4b79d 100644 --- a/cmd/dhcp4d/dhcp4d.go +++ b/cmd/dhcp4d/dhcp4d.go @@ -137,6 +137,9 @@ span.hostname-override { tr:nth-child(even) { background: #eee; } +form { + display: inline; +} @@ -152,7 +155,10 @@ tr:nth-child(even) { {{$l.Addr}} -{{$l.Hostname}} +
+ + +
{{ if (ne $l.HostnameOverride "") }} ! {{ end }} @@ -258,6 +264,25 @@ func newSrv(permDir string) (*srv, error) { return nil, err } + http.HandleFunc("/sethostname", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "want POST", http.StatusMethodNotAllowed) + return + } + hwaddr := r.FormValue("hardwareaddr") + if hwaddr == "" { + http.Error(w, "missing hardwareaddr parameter", http.StatusBadRequest) + return + } + hostname := r.FormValue("hostname") + if hostname == "" { + http.Error(w, "missing hostname parameter", http.StatusBadRequest) + return + } + handler.SetHostname(hwaddr, hostname) + http.Redirect(w, r, "/", http.StatusFound) + }) + http.HandleFunc("/lease/", func(w http.ResponseWriter, r *http.Request) { hostname := strings.TrimPrefix(r.URL.Path, "/lease/") if hostname == "" { diff --git a/internal/dhcp4d/dhcp4d.go b/internal/dhcp4d/dhcp4d.go index a84b8d0..a7d3a00 100644 --- a/internal/dhcp4d/dhcp4d.go +++ b/internal/dhcp4d/dhcp4d.go @@ -19,6 +19,7 @@ import ( "log" "math/rand" "net" + "sync" "syscall" "time" @@ -49,8 +50,6 @@ type Handler struct { leaseRange int // number of IP addresses to hand out LeasePeriod time.Duration options dhcp4.Options - leasesHW map[string]int // points into leasesIP - leasesIP map[int]*Lease rawConn net.PacketConn iface *net.Interface @@ -58,6 +57,10 @@ type Handler struct { // Leases is called whenever a new lease is handed out Leases func([]*Lease, *Lease) + + leasesMu sync.Mutex + leasesHW map[string]int // points into leasesIP + leasesIP map[int]*Lease } func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.PacketConn) (*Handler, error) { @@ -105,6 +108,8 @@ func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.Pac // loaded from persistent storage. There is no locking, so SetLeases must be // called before Serve. func (h *Handler) SetLeases(leases []*Lease) { + h.leasesMu.Lock() + defer h.leasesMu.Unlock() h.leasesHW = make(map[string]int) h.leasesIP = make(map[int]*Lease) for _, l := range leases { @@ -113,7 +118,30 @@ func (h *Handler) SetLeases(leases []*Lease) { } } +func (h *Handler) callLeasesLocked(lease *Lease) { + if h.Leases == nil { + return + } + var leases []*Lease + for _, l := range h.leasesIP { + leases = append(leases, l) + } + h.Leases(leases, lease) +} + +func (h *Handler) SetHostname(hwaddr, hostname string) { + h.leasesMu.Lock() + defer h.leasesMu.Unlock() + leaseNum := h.leasesHW[hwaddr] + lease := h.leasesIP[leaseNum] + lease.Hostname = hostname + lease.HostnameOverride = hostname + h.callLeasesLocked(lease) +} + func (h *Handler) findLease() int { + h.leasesMu.Lock() + defer h.leasesMu.Unlock() now := h.timeNow() if len(h.leasesIP) < h.leaseRange { // TODO: hash the hwaddr like dnsmasq @@ -140,6 +168,8 @@ func (h *Handler) canLease(reqIP net.IP, hwaddr string) int { return -1 } + h.leasesMu.Lock() + defer h.leasesMu.Unlock() l, ok := h.leasesIP[leaseNum] if !ok { return leaseNum // lease available @@ -156,6 +186,7 @@ func (h *Handler) canLease(reqIP net.IP, hwaddr string) int { return -1 // lease unavailable } +// ServeDHCP is always called from the same goroutine, so no locking is required. func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) dhcp4.Packet { reply := h.serveDHCP(p, msgType, options) if reply == nil { @@ -205,6 +236,8 @@ func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options d } func (h *Handler) leaseHW(hwAddr string) (*Lease, bool) { + h.leasesMu.Lock() + defer h.leasesMu.Unlock() num, ok := h.leasesHW[hwAddr] if !ok { return nil, false @@ -284,18 +317,16 @@ func (h *Handler) serveDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options d } // Release any old leases for this client + h.leasesMu.Lock() delete(h.leasesIP, l.Num) + h.leasesMu.Unlock() } + h.leasesMu.Lock() + defer h.leasesMu.Unlock() h.leasesIP[leaseNum] = lease h.leasesHW[lease.HardwareAddr] = leaseNum - if h.Leases != nil { - var leases []*Lease - for _, l := range h.leasesIP { - leases = append(leases, l) - } - h.Leases(leases, lease) - } + h.callLeasesLocked(lease) return dhcp4.ReplyPacket(p, dhcp4.ACK, h.serverIP, reqIP, h.LeasePeriod, h.options.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList])) }