dhcp4d: display active devices based on LastACK

This has the advantage that it also works for static DHCP leases,
provided the device obtains a DHCP lease at all (and isn’t configured with a
static IP address, like the shelly motion sensors for example).
This commit is contained in:
Michael Stapelberg 2022-03-12 17:38:16 +01:00
parent 593cd8c12d
commit 2014da4ca3
2 changed files with 44 additions and 23 deletions

View File

@ -92,6 +92,9 @@ var (
}
return dur.Truncate(1 * time.Second).String()
},
"zero": func(t time.Time) bool {
return t.IsZero()
},
}).Parse(`<!DOCTYPE html>
<head>
<meta charset="utf-8">
@ -150,7 +153,7 @@ form {
<th>Hostname</th>
<th>MAC address</th>
<th>Vendor</th>
<th>Expiry</th>
<th>Last ACK</th>
</tr>
{{ range $idx, $l := . }}
<tr>
@ -166,27 +169,31 @@ form {
</td>
<td class="hwaddr">{{$l.HardwareAddr}}</td>
<td>{{$l.Vendor}}</td>
<td title="{{ timefmt $l.Expiry }}">
{{ if $l.Expired }}
{{ since $l.Expiry }}
<span class="expired">expired</span>
{{ else }}
{{ if $l.Static }}
<span class="static">static</span>
{{ else }}
{{ timefmt $l.Expiry }}
<td>
{{ if (not (zero $l.LastACK)) }}
{{ timefmt $l.LastACK }}
{{ if $l.Active }}
<span class="active">active</span>
{{ end }}
{{ if $l.Expired }}
<span class="expired">expired</span>
{{ end }}
{{ end }}
</td>
</tr>
{{ end }}
{{ end }}
<h1>Static Leases</h1>
<table cellpadding="0" cellspacing="0">
{{ template "table" .StaticLeases }}
</table>
<h1>Dynamic Leases</h1>
<table cellpadding="0" cellspacing="0">
{{ template "table" .DynamicLeases }}
</table>
</body>
</html>
`))
@ -341,17 +348,20 @@ func newSrv(permDir string) (*srv, error) {
Vendor string
Expired bool
Static bool
Active bool
}
leasesMu.Lock()
defer leasesMu.Unlock()
static := make([]tmplLease, 0, len(leases))
dynamic := make([]tmplLease, 0, len(leases))
now := time.Now()
tl := func(l *dhcp4d.Lease) tmplLease {
return tmplLease{
Lease: *l,
Vendor: ouiDB.Lookup(l.HardwareAddr[:8]),
Expired: l.Expired(time.Now()),
Expired: l.Expired(now),
Active: l.Active(now),
Static: l.Expiry.IsZero(),
}
}

View File

@ -43,12 +43,17 @@ type Lease struct {
Hostname string `json:"hostname"`
HostnameOverride string `json:"hostname_override"`
Expiry time.Time `json:"expiry"`
LastACK time.Time `json:"last_ack"`
}
func (l *Lease) Expired(at time.Time) bool {
return !l.Expiry.IsZero() && at.After(l.Expiry)
}
func (l *Lease) Active(at time.Time) bool {
return !l.LastACK.IsZero() && at.Before(l.LastACK.Add(leasePeriod))
}
type Handler struct {
serverIP net.IP
start net.IP // first IP address to hand out
@ -90,18 +95,14 @@ func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.Pac
copy(start, serverIP)
start[len(start)-1] += 1
return &Handler{
rawConn: conn,
iface: iface,
leasesHW: make(map[string]int),
leasesIP: make(map[int]*Lease),
serverIP: serverIP,
start: start,
leaseRange: 230,
// Apple recommends a DHCP lease time of 1 hour in
// https://support.apple.com/de-ch/HT202068,
// so if 20 minutes ever causes any trouble,
// we should try increasing it to 1 hour.
LeasePeriod: 20 * time.Minute,
rawConn: conn,
iface: iface,
leasesHW: make(map[string]int),
leasesIP: make(map[int]*Lease),
serverIP: serverIP,
start: start,
leaseRange: 230,
LeasePeriod: leasePeriod,
options: dhcp4.Options{
dhcp4.OptionSubnetMask: []byte{255, 255, 255, 0},
dhcp4.OptionRouter: []byte(serverIP),
@ -113,6 +114,12 @@ func NewHandler(dir string, iface *net.Interface, ifaceName string, conn net.Pac
}, nil
}
// Apple recommends a DHCP lease time of 1 hour in
// https://support.apple.com/de-ch/HT202068,
// so if 20 minutes ever causes any trouble,
// we should try increasing it to 1 hour.
const leasePeriod = 20 * time.Minute
// SetLeases overwrites the leases database with the specified leases, typically
// loaded from persistent storage. There is no locking, so SetLeases must be
// called before Serve.
@ -122,6 +129,9 @@ func (h *Handler) SetLeases(leases []*Lease) {
h.leasesHW = make(map[string]int)
h.leasesIP = make(map[int]*Lease)
for _, l := range leases {
if l.LastACK.IsZero() {
l.LastACK = l.Expiry
}
h.leasesHW[l.HardwareAddr] = l.Num
h.leasesIP[l.Num] = l
}
@ -334,6 +344,7 @@ func (h *Handler) serveDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options d
HardwareAddr: hwAddr,
Expiry: h.timeNow().Add(h.leasePeriodForDevice(hwAddr)),
Hostname: string(options[dhcp4.OptionHostName]),
LastACK: h.timeNow(),
}
copy(lease.Addr, reqIP.To4())