mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-28 09:58:52 +03:00
a91a257b15
Merge in DNS/adguard-home from AG-20352-imp-leases-db to master Squashed commit of the following: commit 2235fb4671bb3f80c933847362cd35b5704dd18d Merge: 0c4d76d4f76a74b271
Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Apr 18 15:09:34 2023 +0300 Merge branch 'master' into AG-20352-imp-leases-db commit 0c4d76d4f6222ae06c568864d366df866dc55a54 Merge: e586b82c74afd39b22
Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Apr 18 11:07:27 2023 +0300 Merge branch 'master' into AG-20352-imp-leases-db commit e586b82c700c4d432e34f36400519eb08b2653ad Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Tue Apr 18 11:06:40 2023 +0300 dhcpd: imp docs commit 411d4e6f6e36051bf6a66c709380ed268c161c41 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Apr 17 16:56:56 2023 +0300 dhcpd: imp code commit e457dc2c385ab62b36df7f96c949e6b90ed2034a Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Mon Apr 17 14:29:29 2023 +0300 all: imp code more commit c2df20d0125d368d0155af0808af979921763e1f Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Fri Apr 14 15:07:53 2023 +0300 all: imp code commit a4e9ffb9ae769c828c22d62ddf231f7bcfea14db Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Apr 12 19:19:35 2023 +0300 dhcpd: fix test more commit 138d89414f1a89558b23962acb7174dce28346d9 Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Apr 12 18:08:29 2023 +0300 dhcpd: fix test commit e07e7a23e7c913951c8ecb38c12a3345ebe473be Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Apr 12 17:22:27 2023 +0300 all: upd chlog commit 1b6a76e79cf4beed9ca980766ce97930b375bfde Author: Stanislav Chzhen <s.chzhen@adguard.com> Date: Wed Apr 12 13:24:11 2023 +0300 all: migrate leases db
232 lines
7.1 KiB
Go
232 lines
7.1 KiB
Go
package dhcpd
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
|
"github.com/AdguardTeam/golibs/errors"
|
|
)
|
|
|
|
// ServerConfig is the configuration for the DHCP server. The order of YAML
|
|
// fields is important, since the YAML configuration file follows it.
|
|
type ServerConfig struct {
|
|
// Called when the configuration is changed by HTTP request
|
|
ConfigModified func() `yaml:"-"`
|
|
|
|
// Register an HTTP handler
|
|
HTTPRegister aghhttp.RegisterFunc `yaml:"-"`
|
|
|
|
Enabled bool `yaml:"enabled"`
|
|
InterfaceName string `yaml:"interface_name"`
|
|
|
|
// LocalDomainName is the domain name used for DHCP hosts. For example,
|
|
// a DHCP client with the hostname "myhost" can be addressed as "myhost.lan"
|
|
// when LocalDomainName is "lan".
|
|
LocalDomainName string `yaml:"local_domain_name"`
|
|
|
|
Conf4 V4ServerConf `yaml:"dhcpv4"`
|
|
Conf6 V6ServerConf `yaml:"dhcpv6"`
|
|
|
|
// WorkDir is used to store DHCP leases.
|
|
//
|
|
// Deprecated: Remove it when migration of DHCP leases will not be needed.
|
|
WorkDir string `yaml:"-"`
|
|
|
|
// DataDir is used to store DHCP leases.
|
|
DataDir string `yaml:"-"`
|
|
|
|
// dbFilePath is the path to the file with stored DHCP leases.
|
|
dbFilePath string `yaml:"-"`
|
|
}
|
|
|
|
// DHCPServer - DHCP server interface
|
|
type DHCPServer interface {
|
|
// ResetLeases resets leases.
|
|
ResetLeases(leases []*Lease) (err error)
|
|
// GetLeases returns deep clones of the current leases.
|
|
GetLeases(flags GetLeasesFlags) (leases []*Lease)
|
|
// AddStaticLease - add a static lease
|
|
AddStaticLease(l *Lease) (err error)
|
|
// RemoveStaticLease - remove a static lease
|
|
RemoveStaticLease(l *Lease) (err error)
|
|
|
|
// FindMACbyIP returns a MAC address by the IP address of its lease, if
|
|
// there is one.
|
|
FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr)
|
|
|
|
// WriteDiskConfig4 - copy disk configuration
|
|
WriteDiskConfig4(c *V4ServerConf)
|
|
// WriteDiskConfig6 - copy disk configuration
|
|
WriteDiskConfig6(c *V6ServerConf)
|
|
|
|
// Start - start server
|
|
Start() (err error)
|
|
// Stop - stop server
|
|
Stop() (err error)
|
|
getLeasesRef() []*Lease
|
|
}
|
|
|
|
// V4ServerConf - server configuration
|
|
type V4ServerConf struct {
|
|
Enabled bool `yaml:"-" json:"-"`
|
|
InterfaceName string `yaml:"-" json:"-"`
|
|
|
|
GatewayIP netip.Addr `yaml:"gateway_ip" json:"gateway_ip"`
|
|
SubnetMask netip.Addr `yaml:"subnet_mask" json:"subnet_mask"`
|
|
// broadcastIP is the broadcasting address pre-calculated from the
|
|
// configured gateway IP and subnet mask.
|
|
broadcastIP netip.Addr
|
|
|
|
// The first & the last IP address for dynamic leases
|
|
// Bytes [0..2] of the last allowed IP address must match the first IP
|
|
RangeStart netip.Addr `yaml:"range_start" json:"range_start"`
|
|
RangeEnd netip.Addr `yaml:"range_end" json:"range_end"`
|
|
|
|
LeaseDuration uint32 `yaml:"lease_duration" json:"lease_duration"` // in seconds
|
|
|
|
// IP conflict detector: time (ms) to wait for ICMP reply
|
|
// 0: disable
|
|
ICMPTimeout uint32 `yaml:"icmp_timeout_msec" json:"-"`
|
|
|
|
// Custom Options.
|
|
//
|
|
// Option with arbitrary hexadecimal data:
|
|
// DEC_CODE hex HEX_DATA
|
|
// where DEC_CODE is a decimal DHCPv4 option code in range [1..255]
|
|
//
|
|
// Option with IP data (only 1 IP is supported):
|
|
// DEC_CODE ip IP_ADDR
|
|
Options []string `yaml:"options" json:"-"`
|
|
|
|
ipRange *ipRange
|
|
|
|
leaseTime time.Duration // the time during which a dynamic lease is considered valid
|
|
dnsIPAddrs []netip.Addr // IPv4 addresses to return to DHCP clients as DNS server addresses
|
|
|
|
// subnet contains the DHCP server's subnet. The IP is the IP of the
|
|
// gateway.
|
|
subnet netip.Prefix
|
|
|
|
// notify is a way to signal to other components that leases have been
|
|
// changed. notify must be called outside of locked sections, since the
|
|
// clients might want to get the new data.
|
|
//
|
|
// TODO(a.garipov): This is utter madness and must be refactored. It just
|
|
// begs for deadlock bugs and other nastiness.
|
|
notify func(uint32)
|
|
}
|
|
|
|
// errNilConfig is an error returned by validation method if the config is nil.
|
|
const errNilConfig errors.Error = "nil config"
|
|
|
|
// ensureV4 returns an unmapped version of ip. An error is returned if the
|
|
// passed ip is not an IPv4.
|
|
func ensureV4(ip netip.Addr, kind string) (ip4 netip.Addr, err error) {
|
|
ip4 = ip.Unmap()
|
|
if !ip4.IsValid() || !ip4.Is4() {
|
|
return netip.Addr{}, fmt.Errorf("%v is not an IPv4 %s", ip, kind)
|
|
}
|
|
|
|
return ip4, nil
|
|
}
|
|
|
|
// Validate returns an error if c is not a valid configuration.
|
|
//
|
|
// TODO(e.burkov): Don't set the config fields when the server itself will stop
|
|
// containing the config.
|
|
func (c *V4ServerConf) Validate() (err error) {
|
|
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
|
|
|
if c == nil {
|
|
return errNilConfig
|
|
}
|
|
|
|
gatewayIP, err := ensureV4(c.GatewayIP, "address")
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is and there is
|
|
// an annotation deferred already.
|
|
return err
|
|
}
|
|
|
|
subnetMask, err := ensureV4(c.SubnetMask, "subnet mask")
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is and there is
|
|
// an annotation deferred already.
|
|
return err
|
|
}
|
|
maskLen, _ := net.IPMask(subnetMask.AsSlice()).Size()
|
|
|
|
c.subnet = netip.PrefixFrom(gatewayIP, maskLen)
|
|
c.broadcastIP = aghnet.BroadcastFromPref(c.subnet)
|
|
|
|
rangeStart, err := ensureV4(c.RangeStart, "address")
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is and there is
|
|
// an annotation deferred already.
|
|
return err
|
|
}
|
|
|
|
rangeEnd, err := ensureV4(c.RangeEnd, "address")
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is and there is
|
|
// an annotation deferred already.
|
|
return err
|
|
}
|
|
|
|
c.ipRange, err = newIPRange(rangeStart.AsSlice(), rangeEnd.AsSlice())
|
|
if err != nil {
|
|
// Don't wrap the error since it's informative enough as is and there is
|
|
// an annotation deferred already.
|
|
return err
|
|
}
|
|
|
|
if c.ipRange.contains(gatewayIP.AsSlice()) {
|
|
return fmt.Errorf("gateway ip %v in the ip range: %v-%v",
|
|
gatewayIP,
|
|
c.RangeStart,
|
|
c.RangeEnd,
|
|
)
|
|
}
|
|
|
|
if !c.subnet.Contains(rangeStart) {
|
|
return fmt.Errorf("range start %v is outside network %v",
|
|
c.RangeStart,
|
|
c.subnet,
|
|
)
|
|
}
|
|
|
|
if !c.subnet.Contains(rangeEnd) {
|
|
return fmt.Errorf("range end %v is outside network %v",
|
|
c.RangeEnd,
|
|
c.subnet,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// V6ServerConf - server configuration
|
|
type V6ServerConf struct {
|
|
Enabled bool `yaml:"-" json:"-"`
|
|
InterfaceName string `yaml:"-" json:"-"`
|
|
|
|
// The first IP address for dynamic leases
|
|
// The last allowed IP address ends with 0xff byte
|
|
RangeStart net.IP `yaml:"range_start" json:"range_start"`
|
|
|
|
LeaseDuration uint32 `yaml:"lease_duration" json:"lease_duration"` // in seconds
|
|
|
|
RASLAACOnly bool `yaml:"ra_slaac_only" json:"-"` // send ICMPv6.RA packets without MO flags
|
|
RAAllowSLAAC bool `yaml:"ra_allow_slaac" json:"-"` // send ICMPv6.RA packets with MO flags
|
|
|
|
ipStart net.IP // starting IP address for dynamic leases
|
|
leaseTime time.Duration // the time during which a dynamic lease is considered valid
|
|
dnsIPAddrs []net.IP // IPv6 addresses to return to DHCP clients as DNS server addresses
|
|
|
|
// Server calls this function when leases data changes
|
|
notify func(uint32)
|
|
}
|