// Package dhcpsvc contains the AdGuard Home DHCP service. // // TODO(e.burkov): Add tests. package dhcpsvc import ( "context" "net" "net/netip" "github.com/AdguardTeam/AdGuardHome/internal/next/agh" ) const ( // keyInterface is the key for logging the network interface name. keyInterface = "iface" // keyFamily is the key for logging the handled address family. keyFamily = "family" ) // Interface is a DHCP service. // // TODO(e.burkov): Separate HostByIP, MACByIP, IPByHost into a separate // interface. This is also applicable to Enabled method. // // TODO(e.burkov): Reconsider the requirements for the leases validity. type Interface interface { agh.ServiceWithConfig[*Config] // Enabled returns true if DHCP provides information about clients. Enabled() (ok bool) // HostByIP returns the hostname of the DHCP client with the given IP // address. The address will be netip.Addr{} if there is no such client, // due to an assumption that a DHCP client must always have an IP address. HostByIP(ip netip.Addr) (host string) // MACByIP returns the MAC address for the given IP address leased. It // returns nil if there is no such client, due to an assumption that a DHCP // client must always have a MAC address. // // TODO(e.burkov): Think of a contract for the returned value. MACByIP(ip netip.Addr) (mac net.HardwareAddr) // IPByHost returns the IP address of the DHCP client with the given // hostname. The hostname will be an empty string if there is no such // client, due to an assumption that a DHCP client must always have a // hostname, either set or generated. IPByHost(host string) (ip netip.Addr) // Leases returns all the active DHCP leases. The returned slice should be // a clone. The order of leases is undefined. // // TODO(e.burkov): Consider implementing iterating methods with appropriate // signatures instead of cloning the whole list. Leases() (ls []*Lease) // AddLease adds a new DHCP lease. l must be valid. It returns an error if // l already exists. AddLease(ctx context.Context, l *Lease) (err error) // UpdateStaticLease replaces an existing static DHCP lease. l must be // valid. It returns an error if the lease with the given hardware address // doesn't exist or if other values match another existing lease. UpdateStaticLease(ctx context.Context, l *Lease) (err error) // RemoveLease removes an existing DHCP lease. l must be valid. It returns // an error if there is no lease equal to l. RemoveLease(ctx context.Context, l *Lease) (err error) // Reset removes all the DHCP leases. // // TODO(e.burkov): If it's really needed? Reset(ctx context.Context) (err error) } // Empty is an [Interface] implementation that does nothing. type Empty struct{} // type check var _ agh.ServiceWithConfig[*Config] = Empty{} // Start implements the [Service] interface for Empty. func (Empty) Start(_ context.Context) (err error) { return nil } // Shutdown implements the [Service] interface for Empty. func (Empty) Shutdown(_ context.Context) (err error) { return nil } // Config implements the [ServiceWithConfig] interface for Empty. func (Empty) Config() (conf *Config) { return nil } // type check var _ Interface = Empty{} // Enabled implements the [Interface] interface for Empty. func (Empty) Enabled() (ok bool) { return false } // HostByIP implements the [Interface] interface for Empty. func (Empty) HostByIP(_ netip.Addr) (host string) { return "" } // MACByIP implements the [Interface] interface for Empty. func (Empty) MACByIP(_ netip.Addr) (mac net.HardwareAddr) { return nil } // IPByHost implements the [Interface] interface for Empty. func (Empty) IPByHost(_ string) (ip netip.Addr) { return netip.Addr{} } // Leases implements the [Interface] interface for Empty. func (Empty) Leases() (leases []*Lease) { return nil } // AddLease implements the [Interface] interface for Empty. func (Empty) AddLease(_ context.Context, _ *Lease) (err error) { return nil } // UpdateStaticLease implements the [Interface] interface for Empty. func (Empty) UpdateStaticLease(_ context.Context, _ *Lease) (err error) { return nil } // RemoveLease implements the [Interface] interface for Empty. func (Empty) RemoveLease(_ context.Context, _ *Lease) (err error) { return nil } // Reset implements the [Interface] interface for Empty. func (Empty) Reset(_ context.Context) (err error) { return nil }