Files
abrown38andClaude Opus 5.5 3c0808591f Steward 0.1.0 (in development): a DHCPv4 server with a web UI, API and CLI
Subnets served per interface, pools, reservations, options at three
levels, leases in SQLite saved before the ACK, ping and decline conflict
checks, validated changes with history and undo, users and tokens, an
audit log, a Kea importer, a sandboxed systemd unit and Debian packages.
Tested with real udhcpc and dhcpcd clients in Docker (scripts/e2e.sh).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:14:07 +01:00

67 lines
2.1 KiB
Go

package model
import (
"net/netip"
"time"
)
// Lease states.
const (
// LeaseActive: the client holds the address until End.
LeaseActive = "active"
// LeaseReleased: the client gave the address back. The row stays so
// the client gets the same address next time, if it is still free.
LeaseReleased = "released"
// LeaseExpired: the lease ran out (the state is computed: an active
// lease past its End).
LeaseExpired = "expired"
// LeaseDeclined: a client said the address is in use by someone else
// (DHCPDECLINE), or it answered a ping. It isn't handed out until End.
LeaseDeclined = "declined"
)
// Lease is an address handed out, or held back.
type Lease struct {
IP netip.Addr `json:"ip"`
SubnetID int64 `json:"subnet_id"`
MAC string `json:"mac"`
ClientID string `json:"client_id,omitempty"`
// Hostname is the name the client sent (option 12 or 81).
Hostname string `json:"hostname"`
State string `json:"state"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
// Interface is where the client was last seen.
Interface string `json:"interface"`
Updated time.Time `json:"updated"`
}
// StateAt is the lease's state at a time (an active lease past its end
// has expired).
func (l *Lease) StateAt(now time.Time) string {
if l.State == LeaseActive && !now.Before(l.End) {
return LeaseExpired
}
return l.State
}
// ActiveAt reports whether the lease holds its address at a time.
func (l *Lease) ActiveAt(now time.Time) bool {
return l.State == LeaseActive && now.Before(l.End)
}
// HeldAt reports whether the address can't be handed out to another
// client at a time: an active or declined lease that hasn't ended.
func (l *Lease) HeldAt(now time.Time) bool {
return (l.State == LeaseActive || l.State == LeaseDeclined) && now.Before(l.End)
}
// Owns reports whether the lease belongs to a client: by client
// identifier when both have one, else by MAC address.
func (l *Lease) Owns(mac, clientID string) bool {
if l.ClientID != "" && clientID != "" {
return l.ClientID == clientID
}
return l.MAC != "" && l.MAC == mac
}