mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-22 04:55:33 +03:00
Pull request 1861: 4923-gopacket-dhcp vol.1
Merge in DNS/adguard-home from 4923-gopacket-dhcp to master Updates #4923. Squashed commit of the following: commit edf36ce8b1873272c3daebe8cc8f8132793aac44 Merge: a17513d3e123ca8738
Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Jun 22 14:14:39 2023 +0300 Merge branch 'master' into 4923-gopacket-dhcp commit a17513d3e0a9e596d56444dfa46478eee15631de Merge: f04727c29994906fbd
Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jun 21 17:49:09 2023 +0300 Merge branch 'master' into 4923-gopacket-dhcp commit f04727c29eaf22f9eb53f3aa33d42d00e177b224 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Jun 20 15:42:31 2023 +0300 home: revert clients container commit c58284ac6b5b2274da5eed2e853847d757709e5b Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Jun 19 21:10:36 2023 +0300 all: imp code, names, docs commit 4c4613c939e1325d11655822d9dbc3f05a6d203c Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Jun 13 18:51:12 2023 +0300 all: imp code commit 0b4a6e0dd561d9b7bb78dea21dcc947bcd0bd583 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jun 7 18:40:15 2023 +0300 all: imp api commit 0425edea03d6ca0859657df683bef6ec45bfc399 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Mon Jun 5 15:57:23 2023 +0300 dhcpsvc: introduce package commit 5628ebe6cccf91e2c48778966730bcbbe9e1d9f2 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Jun 1 17:49:12 2023 +0300 WIP
This commit is contained in:
parent
123ca87388
commit
f40ef76c79
10 changed files with 324 additions and 104 deletions
|
@ -25,11 +25,8 @@ func (s *bitSet) isSet(n uint64) (ok bool) {
|
||||||
|
|
||||||
var word uint64
|
var word uint64
|
||||||
word, ok = s.words[wordIdx]
|
word, ok = s.words[wordIdx]
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return word&(1<<bitIdx) != 0
|
return ok && word&(1<<bitIdx) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// set sets or unsets a bit.
|
// set sets or unsets a bit.
|
||||||
|
|
|
@ -249,31 +249,30 @@ func (c *dhcpConn) buildEtherPkt(payload []byte, peer *dhcpUnicastAddr) (pkt []b
|
||||||
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
|
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
|
||||||
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
|
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
|
||||||
case giaddr != nil && !giaddr.IsUnspecified():
|
case giaddr != nil && !giaddr.IsUnspecified():
|
||||||
// Send any return messages to the server port on the BOOTP
|
// Send any return messages to the server port on the BOOTP relay agent
|
||||||
// relay agent whose address appears in giaddr.
|
// whose address appears in giaddr.
|
||||||
peer = &net.UDPAddr{
|
peer = &net.UDPAddr{
|
||||||
IP: giaddr,
|
IP: giaddr,
|
||||||
Port: dhcpv4.ServerPort,
|
Port: dhcpv4.ServerPort,
|
||||||
}
|
}
|
||||||
if mtype == dhcpv4.MessageTypeNak {
|
if mtype == dhcpv4.MessageTypeNak {
|
||||||
// Set the broadcast bit in the DHCPNAK, so that the relay agent
|
// Set the broadcast bit in the DHCPNAK, so that the relay agent
|
||||||
// broadcasts it to the client, because the client may not have
|
// broadcasts it to the client, because the client may not have a
|
||||||
// a correct network address or subnet mask, and the client may not
|
// correct network address or subnet mask, and the client may not be
|
||||||
// be answering ARP requests.
|
// answering ARP requests.
|
||||||
resp.SetBroadcast()
|
resp.SetBroadcast()
|
||||||
}
|
}
|
||||||
case mtype == dhcpv4.MessageTypeNak:
|
case mtype == dhcpv4.MessageTypeNak:
|
||||||
// Broadcast any DHCPNAK messages to 0xffffffff.
|
// Broadcast any DHCPNAK messages to 0xffffffff.
|
||||||
case ciaddr != nil && !ciaddr.IsUnspecified():
|
case ciaddr != nil && !ciaddr.IsUnspecified():
|
||||||
// Unicast DHCPOFFER and DHCPACK messages to the address in
|
// Unicast DHCPOFFER and DHCPACK messages to the address in ciaddr.
|
||||||
// ciaddr.
|
|
||||||
peer = &net.UDPAddr{
|
peer = &net.UDPAddr{
|
||||||
IP: ciaddr,
|
IP: ciaddr,
|
||||||
Port: dhcpv4.ClientPort,
|
Port: dhcpv4.ClientPort,
|
||||||
}
|
}
|
||||||
case !req.IsBroadcast() && req.ClientHWAddr != nil:
|
case !req.IsBroadcast() && req.ClientHWAddr != nil:
|
||||||
// Unicast DHCPOFFER and DHCPACK messages to the client's
|
// Unicast DHCPOFFER and DHCPACK messages to the client's hardware
|
||||||
// hardware address and yiaddr.
|
// address and yiaddr.
|
||||||
peer = &dhcpUnicastAddr{
|
peer = &dhcpUnicastAddr{
|
||||||
Addr: raw.Addr{HardwareAddr: req.ClientHWAddr},
|
Addr: raw.Addr{HardwareAddr: req.ClientHWAddr},
|
||||||
yiaddr: resp.YourIPAddr,
|
yiaddr: resp.YourIPAddr,
|
||||||
|
|
|
@ -247,31 +247,30 @@ func (c *dhcpConn) buildEtherPkt(payload []byte, peer *dhcpUnicastAddr) (pkt []b
|
||||||
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
|
func (s *v4Server) send(peer net.Addr, conn net.PacketConn, req, resp *dhcpv4.DHCPv4) {
|
||||||
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
|
switch giaddr, ciaddr, mtype := req.GatewayIPAddr, req.ClientIPAddr, resp.MessageType(); {
|
||||||
case giaddr != nil && !giaddr.IsUnspecified():
|
case giaddr != nil && !giaddr.IsUnspecified():
|
||||||
// Send any return messages to the server port on the BOOTP
|
// Send any return messages to the server port on the BOOTP relay agent
|
||||||
// relay agent whose address appears in giaddr.
|
// whose address appears in giaddr.
|
||||||
peer = &net.UDPAddr{
|
peer = &net.UDPAddr{
|
||||||
IP: giaddr,
|
IP: giaddr,
|
||||||
Port: dhcpv4.ServerPort,
|
Port: dhcpv4.ServerPort,
|
||||||
}
|
}
|
||||||
if mtype == dhcpv4.MessageTypeNak {
|
if mtype == dhcpv4.MessageTypeNak {
|
||||||
// Set the broadcast bit in the DHCPNAK, so that the relay agent
|
// Set the broadcast bit in the DHCPNAK, so that the relay agent
|
||||||
// broadcasts it to the client, because the client may not have
|
// broadcasts it to the client, because the client may not have a
|
||||||
// a correct network address or subnet mask, and the client may not
|
// correct network address or subnet mask, and the client may not be
|
||||||
// be answering ARP requests.
|
// answering ARP requests.
|
||||||
resp.SetBroadcast()
|
resp.SetBroadcast()
|
||||||
}
|
}
|
||||||
case mtype == dhcpv4.MessageTypeNak:
|
case mtype == dhcpv4.MessageTypeNak:
|
||||||
// Broadcast any DHCPNAK messages to 0xffffffff.
|
// Broadcast any DHCPNAK messages to 0xffffffff.
|
||||||
case ciaddr != nil && !ciaddr.IsUnspecified():
|
case ciaddr != nil && !ciaddr.IsUnspecified():
|
||||||
// Unicast DHCPOFFER and DHCPACK messages to the address in
|
// Unicast DHCPOFFER and DHCPACK messages to the address in ciaddr.
|
||||||
// ciaddr.
|
|
||||||
peer = &net.UDPAddr{
|
peer = &net.UDPAddr{
|
||||||
IP: ciaddr,
|
IP: ciaddr,
|
||||||
Port: dhcpv4.ClientPort,
|
Port: dhcpv4.ClientPort,
|
||||||
}
|
}
|
||||||
case !req.IsBroadcast() && req.ClientHWAddr != nil:
|
case !req.IsBroadcast() && req.ClientHWAddr != nil:
|
||||||
// Unicast DHCPOFFER and DHCPACK messages to the client's
|
// Unicast DHCPOFFER and DHCPACK messages to the client's hardware
|
||||||
// hardware address and yiaddr.
|
// address and yiaddr.
|
||||||
peer = &dhcpUnicastAddr{
|
peer = &dhcpUnicastAddr{
|
||||||
Addr: packet.Addr{HardwareAddr: req.ClientHWAddr},
|
Addr: packet.Addr{HardwareAddr: req.ClientHWAddr},
|
||||||
yiaddr: resp.YourIPAddr,
|
yiaddr: resp.YourIPAddr,
|
||||||
|
|
|
@ -28,8 +28,9 @@ const (
|
||||||
defaultBackoff time.Duration = 500 * time.Millisecond
|
defaultBackoff time.Duration = 500 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lease contains the necessary information about a DHCP lease. It's used in
|
// Lease contains the necessary information about a DHCP lease. It's used as is
|
||||||
// various places. So don't change it without good reason.
|
// in the database, so don't change it until it's absolutely necessary, see
|
||||||
|
// [dataVersion].
|
||||||
type Lease struct {
|
type Lease struct {
|
||||||
// Expiry is the expiration time of the lease.
|
// Expiry is the expiration time of the lease.
|
||||||
Expiry time.Time `json:"expires"`
|
Expiry time.Time `json:"expires"`
|
||||||
|
@ -41,8 +42,6 @@ type Lease struct {
|
||||||
HWAddr net.HardwareAddr `json:"mac"`
|
HWAddr net.HardwareAddr `json:"mac"`
|
||||||
|
|
||||||
// IP is the IP address leased to the client.
|
// IP is the IP address leased to the client.
|
||||||
//
|
|
||||||
// TODO(a.garipov): Migrate leases.db.
|
|
||||||
IP netip.Addr `json:"ip"`
|
IP netip.Addr `json:"ip"`
|
||||||
|
|
||||||
// IsStatic defines if the lease is static.
|
// IsStatic defines if the lease is static.
|
||||||
|
|
|
@ -200,7 +200,7 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
|
||||||
func (ra *raCtx) Init() (err error) {
|
func (ra *raCtx) Init() (err error) {
|
||||||
ra.stop.Store(0)
|
ra.stop.Store(0)
|
||||||
ra.conn = nil
|
ra.conn = nil
|
||||||
if !(ra.raAllowSLAAC || ra.raSLAACOnly) {
|
if !ra.raAllowSLAAC && !ra.raSLAACOnly {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
86
internal/dhcpsvc/config.go
Normal file
86
internal/dhcpsvc/config.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package dhcpsvc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is the configuration for the DHCP service.
|
||||||
|
type Config struct {
|
||||||
|
// Interfaces stores configurations of DHCP server specific for the network
|
||||||
|
// interface identified by its name.
|
||||||
|
Interfaces map[string]*InterfaceConfig
|
||||||
|
|
||||||
|
// LocalDomainName is the top-level domain name to use for resolving DHCP
|
||||||
|
// clients' hostnames.
|
||||||
|
LocalDomainName string
|
||||||
|
|
||||||
|
// ICMPTimeout is the timeout for checking another DHCP server's presence.
|
||||||
|
ICMPTimeout time.Duration
|
||||||
|
|
||||||
|
// Enabled is the state of the service, whether it is enabled or not.
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceConfig is the configuration of a single DHCP interface.
|
||||||
|
type InterfaceConfig struct {
|
||||||
|
// IPv4 is the configuration of DHCP protocol for IPv4.
|
||||||
|
IPv4 *IPv4Config
|
||||||
|
|
||||||
|
// IPv6 is the configuration of DHCP protocol for IPv6.
|
||||||
|
IPv6 *IPv6Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4Config is the interface-specific configuration for DHCPv4.
|
||||||
|
type IPv4Config struct {
|
||||||
|
// GatewayIP is the IPv4 address of the network's gateway. It is used as
|
||||||
|
// the default gateway for DHCP clients and also used in calculating the
|
||||||
|
// network-specific broadcast address.
|
||||||
|
GatewayIP netip.Addr
|
||||||
|
|
||||||
|
// SubnetMask is the IPv4 subnet mask of the network. It should be a valid
|
||||||
|
// IPv4 subnet mask (i.e. all 1s followed by all 0s).
|
||||||
|
SubnetMask netip.Addr
|
||||||
|
|
||||||
|
// RangeStart is the first address in the range to assign to DHCP clients.
|
||||||
|
RangeStart netip.Addr
|
||||||
|
|
||||||
|
// RangeEnd is the last address in the range to assign to DHCP clients.
|
||||||
|
RangeEnd netip.Addr
|
||||||
|
|
||||||
|
// Options is the list of DHCP options to send to DHCP clients.
|
||||||
|
Options layers.DHCPOptions
|
||||||
|
|
||||||
|
// LeaseDuration is the TTL of a DHCP lease.
|
||||||
|
LeaseDuration time.Duration
|
||||||
|
|
||||||
|
// Enabled is the state of the DHCPv4 service, whether it is enabled or not
|
||||||
|
// on the specific interface.
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6Config is the interface-specific configuration for DHCPv6.
|
||||||
|
type IPv6Config struct {
|
||||||
|
// RangeStart is the first address in the range to assign to DHCP clients.
|
||||||
|
RangeStart netip.Addr
|
||||||
|
|
||||||
|
// Options is the list of DHCP options to send to DHCP clients.
|
||||||
|
Options layers.DHCPOptions
|
||||||
|
|
||||||
|
// LeaseDuration is the TTL of a DHCP lease.
|
||||||
|
LeaseDuration time.Duration
|
||||||
|
|
||||||
|
// RASlaacOnly defines whether the DHCP clients should only use SLAAC for
|
||||||
|
// address assignment.
|
||||||
|
RASLAACOnly bool
|
||||||
|
|
||||||
|
// RAAllowSlaac defines whether the DHCP clients may use SLAAC for address
|
||||||
|
// assignment.
|
||||||
|
RAAllowSLAAC bool
|
||||||
|
|
||||||
|
// Enabled is the state of the DHCPv6 service, whether it is enabled or not
|
||||||
|
// on the specific interface.
|
||||||
|
Enabled bool
|
||||||
|
}
|
120
internal/dhcpsvc/dhcpsvc.go
Normal file
120
internal/dhcpsvc/dhcpsvc.go
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// Package dhcpsvc contains the AdGuard Home DHCP service.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Add tests.
|
||||||
|
package dhcpsvc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lease is a DHCP lease.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Consider it to [agh], since it also may be needed in
|
||||||
|
// [websvc]. Also think of implementing iterating methods with appropriate
|
||||||
|
// signatures.
|
||||||
|
type Lease struct {
|
||||||
|
// IP is the IP address leased to the client.
|
||||||
|
IP netip.Addr
|
||||||
|
|
||||||
|
// Expiry is the expiration time of the lease.
|
||||||
|
Expiry time.Time
|
||||||
|
|
||||||
|
// Hostname of the client.
|
||||||
|
Hostname string
|
||||||
|
|
||||||
|
// HWAddr is the physical hardware address (MAC address).
|
||||||
|
HWAddr net.HardwareAddr
|
||||||
|
|
||||||
|
// IsStatic defines if the lease is static.
|
||||||
|
IsStatic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
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 by the client or assigned automatically.
|
||||||
|
IPByHost(host string) (ip netip.Addr)
|
||||||
|
|
||||||
|
// Leases returns all the DHCP leases.
|
||||||
|
Leases() (leases []*Lease)
|
||||||
|
|
||||||
|
// AddLease adds a new DHCP lease. It returns an error if the lease is
|
||||||
|
// invalid or already exists.
|
||||||
|
AddLease(l *Lease) (err error)
|
||||||
|
|
||||||
|
// EditLease changes an existing DHCP lease. It returns an error if there
|
||||||
|
// is no lease equal to old or if new is invalid or already exists.
|
||||||
|
EditLease(old, new *Lease) (err error)
|
||||||
|
|
||||||
|
// RemoveLease removes an existing DHCP lease. It returns an error if there
|
||||||
|
// is no lease equal to l.
|
||||||
|
RemoveLease(l *Lease) (err error)
|
||||||
|
|
||||||
|
// Reset removes all the DHCP leases.
|
||||||
|
Reset() (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty is an [Interface] implementation that does nothing.
|
||||||
|
type Empty struct{}
|
||||||
|
|
||||||
|
// type check
|
||||||
|
var _ Interface = Empty{}
|
||||||
|
|
||||||
|
// Start implements the [Service] interface for Empty.
|
||||||
|
func (Empty) Start() (err error) { return nil }
|
||||||
|
|
||||||
|
// Shutdown implements the [Service] interface for Empty.
|
||||||
|
func (Empty) Shutdown(_ context.Context) (err error) { return nil }
|
||||||
|
|
||||||
|
var _ agh.ServiceWithConfig[*Config] = Empty{}
|
||||||
|
|
||||||
|
// Config implements the [ServiceWithConfig] interface for Empty.
|
||||||
|
func (Empty) Config() (conf *Config) { return nil }
|
||||||
|
|
||||||
|
// 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(_ *Lease) (err error) { return nil }
|
||||||
|
|
||||||
|
// EditLease implements the [Interface] interface for Empty.
|
||||||
|
func (Empty) EditLease(_, _ *Lease) (err error) { return nil }
|
||||||
|
|
||||||
|
// RemoveLease implements the [Interface] interface for Empty.
|
||||||
|
func (Empty) RemoveLease(_ *Lease) (err error) { return nil }
|
||||||
|
|
||||||
|
// Reset implements the [Interface] interface for Empty.
|
||||||
|
func (Empty) Reset() (err error) { return nil }
|
|
@ -48,12 +48,33 @@ var webRegistered bool
|
||||||
|
|
||||||
// hostToIPTable is a convenient type alias for tables of host names to an IP
|
// hostToIPTable is a convenient type alias for tables of host names to an IP
|
||||||
// address.
|
// address.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Use the [DHCP] interface instead.
|
||||||
type hostToIPTable = map[string]netip.Addr
|
type hostToIPTable = map[string]netip.Addr
|
||||||
|
|
||||||
// ipToHostTable is a convenient type alias for tables of IP addresses to their
|
// ipToHostTable is a convenient type alias for tables of IP addresses to their
|
||||||
// host names. For example, for use with PTR queries.
|
// host names. For example, for use with PTR queries.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Use the [DHCP] interface instead.
|
||||||
type ipToHostTable = map[netip.Addr]string
|
type ipToHostTable = map[netip.Addr]string
|
||||||
|
|
||||||
|
// DHCP is an interface for accesing DHCP lease data needed in this package.
|
||||||
|
type DHCP interface {
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// 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 by the client or assigned automatically.
|
||||||
|
IPByHost(host string) (ip netip.Addr)
|
||||||
|
|
||||||
|
// Enabled returns true if DHCP provides information about clients.
|
||||||
|
Enabled() (ok bool)
|
||||||
|
}
|
||||||
|
|
||||||
// Server is the main way to start a DNS server.
|
// Server is the main way to start a DNS server.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||||
|
@ -24,6 +25,23 @@ import (
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DHCP is an interface for accesing DHCP lease data the [clientsContainer]
|
||||||
|
// needs.
|
||||||
|
type DHCP interface {
|
||||||
|
// Leases returns all the DHCP leases.
|
||||||
|
Leases() (leases []*dhcpsvc.Lease)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
MACByIP(ip netip.Addr) (mac net.HardwareAddr)
|
||||||
|
}
|
||||||
|
|
||||||
// clientsContainer is the storage of all runtime and persistent clients.
|
// clientsContainer is the storage of all runtime and persistent clients.
|
||||||
type clientsContainer struct {
|
type clientsContainer struct {
|
||||||
// TODO(a.garipov): Perhaps use a number of separate indices for different
|
// TODO(a.garipov): Perhaps use a number of separate indices for different
|
||||||
|
@ -101,9 +119,9 @@ func (clients *clientsContainer) Init(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clients.updateFromDHCP(true)
|
|
||||||
if clients.dhcpServer != nil {
|
if clients.dhcpServer != nil {
|
||||||
clients.dhcpServer.SetOnLeaseChanged(clients.onDHCPLeaseChanged)
|
clients.dhcpServer.SetOnLeaseChanged(clients.onDHCPLeaseChanged)
|
||||||
|
clients.onDHCPLeaseChanged(dhcpd.LeaseChangedAdded)
|
||||||
}
|
}
|
||||||
|
|
||||||
if clients.etcHosts != nil {
|
if clients.etcHosts != nil {
|
||||||
|
@ -277,15 +295,38 @@ func (clients *clientsContainer) periodicUpdate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onDHCPLeaseChanged is a callback for the DHCP server. It updates the list of
|
||||||
|
// runtime clients using the DHCP server's leases.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Remove when switched to dhcpsvc.
|
||||||
func (clients *clientsContainer) onDHCPLeaseChanged(flags int) {
|
func (clients *clientsContainer) onDHCPLeaseChanged(flags int) {
|
||||||
switch flags {
|
if clients.dhcpServer == nil || !config.Clients.Sources.DHCP {
|
||||||
case dhcpd.LeaseChangedAdded,
|
return
|
||||||
dhcpd.LeaseChangedAddedStatic,
|
|
||||||
dhcpd.LeaseChangedRemovedStatic:
|
|
||||||
clients.updateFromDHCP(true)
|
|
||||||
case dhcpd.LeaseChangedRemovedAll:
|
|
||||||
clients.updateFromDHCP(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clients.lock.Lock()
|
||||||
|
defer clients.lock.Unlock()
|
||||||
|
|
||||||
|
clients.rmHostsBySrc(ClientSourceDHCP)
|
||||||
|
|
||||||
|
if flags == dhcpd.LeaseChangedRemovedAll {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leases := clients.dhcpServer.Leases(dhcpd.LeasesAll)
|
||||||
|
n := 0
|
||||||
|
for _, l := range leases {
|
||||||
|
if l.Hostname == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := clients.addHostLocked(l.IP, l.Hostname, ClientSourceDHCP)
|
||||||
|
if ok {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("clients: added %d client aliases from dhcp", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clientSource checks if client with this IP address already exists and returns
|
// clientSource checks if client with this IP address already exists and returns
|
||||||
|
@ -301,11 +342,11 @@ func (clients *clientsContainer) clientSource(ip netip.Addr) (src clientSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
rc, ok := clients.ipToRC[ip]
|
rc, ok := clients.ipToRC[ip]
|
||||||
if !ok {
|
if ok {
|
||||||
return ClientSourceNone
|
return rc.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc.Source
|
return ClientSourceNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// findMultiple is a wrapper around Find to make it a valid client finder for
|
// findMultiple is a wrapper around Find to make it a valid client finder for
|
||||||
|
@ -466,11 +507,11 @@ func (clients *clientsContainer) findLocked(id string) (c *Client, ok bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if clients.dhcpServer == nil {
|
if clients.dhcpServer != nil {
|
||||||
return nil, false
|
return clients.findDHCP(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
return clients.findDHCP(ip)
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// findDHCP searches for a client by its MAC, if the DHCP server is active and
|
// findDHCP searches for a client by its MAC, if the DHCP server is active and
|
||||||
|
@ -697,28 +738,27 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||||
_, ok := clients.findLocked(ip.String())
|
_, ok := clients.findLocked(ip.String())
|
||||||
if ok {
|
if ok {
|
||||||
log.Debug("clients: client for %s is already created, ignore whois info", ip)
|
log.Debug("clients: client for %s is already created, ignore whois info", ip)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(e.burkov): Consider storing WHOIS information separately and
|
||||||
|
// potentially get rid of [RuntimeClient].
|
||||||
rc, ok := clients.ipToRC[ip]
|
rc, ok := clients.ipToRC[ip]
|
||||||
if ok {
|
if !ok {
|
||||||
rc.WHOIS = wi
|
|
||||||
log.Debug("clients: set whois info for runtime client %s: %+v", rc.Host, wi)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a RuntimeClient implicitly so that we don't do this check
|
// Create a RuntimeClient implicitly so that we don't do this check
|
||||||
// again.
|
// again.
|
||||||
rc = &RuntimeClient{
|
rc = &RuntimeClient{
|
||||||
Source: ClientSourceWHOIS,
|
Source: ClientSourceWHOIS,
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.WHOIS = wi
|
|
||||||
|
|
||||||
clients.ipToRC[ip] = rc
|
clients.ipToRC[ip] = rc
|
||||||
|
|
||||||
log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi)
|
log.Debug("clients: set whois info for runtime client with ip %s: %+v", ip, wi)
|
||||||
|
} else {
|
||||||
|
log.Debug("clients: set whois info for runtime client %s: %+v", rc.Host, wi)
|
||||||
|
}
|
||||||
|
|
||||||
|
rc.WHOIS = wi
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHost adds a new IP-hostname pairing. The priorities of the sources are
|
// AddHost adds a new IP-hostname pairing. The priorities of the sources are
|
||||||
|
@ -742,22 +782,18 @@ func (clients *clientsContainer) addHostLocked(
|
||||||
src clientSource,
|
src clientSource,
|
||||||
) (ok bool) {
|
) (ok bool) {
|
||||||
rc, ok := clients.ipToRC[ip]
|
rc, ok := clients.ipToRC[ip]
|
||||||
if ok {
|
if !ok {
|
||||||
if rc.Source > src {
|
rc = &RuntimeClient{
|
||||||
|
WHOIS: &whois.Info{},
|
||||||
|
}
|
||||||
|
|
||||||
|
clients.ipToRC[ip] = rc
|
||||||
|
} else if src < rc.Source {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.Host = host
|
rc.Host = host
|
||||||
rc.Source = src
|
rc.Source = src
|
||||||
} else {
|
|
||||||
rc = &RuntimeClient{
|
|
||||||
Host: host,
|
|
||||||
Source: src,
|
|
||||||
WHOIS: &whois.Info{},
|
|
||||||
}
|
|
||||||
|
|
||||||
clients.ipToRC[ip] = rc
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("clients: added %s -> %q [%d]", ip, host, len(clients.ipToRC))
|
log.Debug("clients: added %s -> %q [%d]", ip, host, len(clients.ipToRC))
|
||||||
|
|
||||||
|
@ -827,38 +863,6 @@ func (clients *clientsContainer) addFromSystemARP() {
|
||||||
log.Debug("clients: added %d client aliases from arp neighborhood", added)
|
log.Debug("clients: added %d client aliases from arp neighborhood", added)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateFromDHCP adds the clients that have a non-empty hostname from the DHCP
|
|
||||||
// server.
|
|
||||||
func (clients *clientsContainer) updateFromDHCP(add bool) {
|
|
||||||
if clients.dhcpServer == nil || !config.Clients.Sources.DHCP {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
clients.lock.Lock()
|
|
||||||
defer clients.lock.Unlock()
|
|
||||||
|
|
||||||
clients.rmHostsBySrc(ClientSourceDHCP)
|
|
||||||
|
|
||||||
if !add {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leases := clients.dhcpServer.Leases(dhcpd.LeasesAll)
|
|
||||||
n := 0
|
|
||||||
for _, l := range leases {
|
|
||||||
if l.Hostname == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := clients.addHostLocked(l.IP, l.Hostname, ClientSourceDHCP)
|
|
||||||
if ok {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("clients: added %d client aliases from dhcp", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// close gracefully closes all the client-specific upstream configurations of
|
// close gracefully closes all the client-specific upstream configurations of
|
||||||
// the persistent clients.
|
// the persistent clients.
|
||||||
func (clients *clientsContainer) close() (err error) {
|
func (clients *clientsContainer) close() (err error) {
|
||||||
|
|
|
@ -228,12 +228,7 @@ func TestRDNS_WorkerLoop(t *testing.T) {
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
w.Reset()
|
w.Reset()
|
||||||
|
|
||||||
cc := &clientsContainer{
|
cc := newClientsContainer()
|
||||||
list: map[string]*Client{},
|
|
||||||
idIndex: map[string]*Client{},
|
|
||||||
ipToRC: map[netip.Addr]*RuntimeClient{},
|
|
||||||
allTags: stringutil.NewSet(),
|
|
||||||
}
|
|
||||||
ch := make(chan netip.Addr)
|
ch := make(chan netip.Addr)
|
||||||
rdns := &RDNS{
|
rdns := &RDNS{
|
||||||
exchanger: &rDNSExchanger{
|
exchanger: &rDNSExchanger{
|
||||||
|
|
Loading…
Reference in a new issue