all: sync with master
This commit is contained in:
213
internal/dhcpd/config.go
Normal file
213
internal/dhcpd/config.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package dhcpd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
)
|
||||
|
||||
// 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 string `yaml:"-"`
|
||||
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 - find a MAC address by IP address in the currently active DHCP leases
|
||||
FindMACbyIP(ip net.IP) 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 net.IP `yaml:"gateway_ip" json:"gateway_ip"`
|
||||
SubnetMask net.IP `yaml:"subnet_mask" json:"subnet_mask"`
|
||||
// broadcastIP is the broadcasting address pre-calculated from the
|
||||
// configured gateway IP and subnet mask.
|
||||
broadcastIP net.IP
|
||||
|
||||
// The first & the last IP address for dynamic leases
|
||||
// Bytes [0..2] of the last allowed IP address must match the first IP
|
||||
RangeStart net.IP `yaml:"range_start" json:"range_start"`
|
||||
RangeEnd net.IP `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 []net.IP // 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 *net.IPNet
|
||||
|
||||
// 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 a 4-byte version of ip. An error is returned if the passed
|
||||
// ip is not an IPv4.
|
||||
func ensureV4(ip net.IP) (ip4 net.IP, err error) {
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("%v is not an IP address", ip)
|
||||
}
|
||||
|
||||
ip4 = ip.To4()
|
||||
if ip4 == nil {
|
||||
return nil, fmt.Errorf("%v is not an IPv4 address", ip)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var gatewayIP net.IP
|
||||
gatewayIP, err = ensureV4(c.GatewayIP)
|
||||
if err != nil {
|
||||
// Don't wrap an errors since it's inforative enough as is and there is
|
||||
// an annotation deferred already.
|
||||
return err
|
||||
}
|
||||
|
||||
if c.SubnetMask == nil {
|
||||
return fmt.Errorf("invalid subnet mask: %v", c.SubnetMask)
|
||||
}
|
||||
|
||||
subnetMask := net.IPMask(netutil.CloneIP(c.SubnetMask.To4()))
|
||||
c.subnet = &net.IPNet{
|
||||
IP: gatewayIP,
|
||||
Mask: subnetMask,
|
||||
}
|
||||
c.broadcastIP = aghnet.BroadcastFromIPNet(c.subnet)
|
||||
|
||||
c.ipRange, err = newIPRange(c.RangeStart, c.RangeEnd)
|
||||
if err != nil {
|
||||
// Don't wrap an errors since it's inforative enough as is and there is
|
||||
// an annotation deferred already.
|
||||
return err
|
||||
}
|
||||
|
||||
if c.ipRange.contains(gatewayIP) {
|
||||
return fmt.Errorf("gateway ip %v in the ip range: %v-%v",
|
||||
gatewayIP,
|
||||
c.RangeStart,
|
||||
c.RangeEnd,
|
||||
)
|
||||
}
|
||||
|
||||
if !c.subnet.Contains(c.RangeStart) {
|
||||
return fmt.Errorf("range start %v is outside network %v",
|
||||
c.RangeStart,
|
||||
c.subnet,
|
||||
)
|
||||
}
|
||||
|
||||
if !c.subnet.Contains(c.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)
|
||||
}
|
||||
Reference in New Issue
Block a user