package iptools import ( "fmt" "net/netip" "sort" "strings" "time" ) type LeaseInfo struct { HostName string `yaml:"hostname"` Addr netip.Addr `yaml:"addr"` HardwareAddress HardwareAddress `yaml:"mac_addr"` Expires time.Time `yaml:"expires"` } type sortableLease []LeaseInfo func (a sortableLease) Len() int { return len(a) } func (a sortableLease) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortableLease) Less(i, j int) bool { return a[i].Addr.Less(a[j].Addr) } func (li *LeaseInfo) ResetExpiry(t time.Time, dur time.Duration) { li.Expires = t.Add(dur) } func (li *LeaseInfo) IsExpired(t time.Time) bool { return t.After(li.Expires) } func (li *LeaseInfo) Expire() { li.Expires = time.Time{} } func SortLeases(leases []LeaseInfo) []LeaseInfo { sortable := sortableLease(leases) sort.Sort(sortable) return []LeaseInfo(sortable) } func (lease LeaseInfo) Reset() LeaseInfo { lease.Expires = time.Time{} lease.HardwareAddress = nil return lease } func (lease LeaseInfo) Compare(other LeaseInfo) error { susFields := []string{} if lease.Addr != other.Addr { susFields = append(susFields, fmt.Sprintf("address is %s, but is recorded as %s", lease.Addr, other.Addr)) } if !lease.HardwareAddress.Match(other.HardwareAddress) { susFields = append(susFields, fmt.Sprintf("hardware address is %s, but is recorded as %s", lease.HardwareAddress, other.HardwareAddress)) } if lease.HostName != other.HostName { susFields = append(susFields, fmt.Sprintf("hostname is %s, but is recorded as %s", lease.HostName, other.HostName)) } if len(susFields) > 0 { return fmt.Errorf("suspicious lease: %s", strings.Join(susFields, ";")) } return nil }