all: sync with master; upd chlog

This commit is contained in:
Ainar Garipov
2023-09-07 17:13:48 +03:00
parent 3be7676970
commit 7b93f5d7cf
306 changed files with 19770 additions and 4916 deletions

View File

@@ -16,17 +16,17 @@ To set up a test environment for DHCP server you will need:
### Configure Virtual Box
1. Install Virtual Box and run the following command to create a Host-Only
1. Install Virtual Box and run the following command to create a Host-Only
network:
```sh
$ VBoxManage hostonlyif create
```
You can check its status by `ip a` command.
You can also set up Host-Only network using Virtual Box menu:
You can check its status by `ip a` command.
You can also set up Host-Only network using Virtual Box menu:
```
File -> Host Network Manager...
```

View File

@@ -23,9 +23,12 @@ type ServerConfig struct {
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"
// 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".
//
// TODO(e.burkov): Probably, remove this field. See the TODO on
// [Interface.Enabled].
LocalDomainName string `yaml:"local_domain_name"`
Conf4 V4ServerConf `yaml:"dhcpv4"`
@@ -58,6 +61,14 @@ type DHCPServer interface {
// there is one.
FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr)
// HostByIP returns a hostname by the IP address of its lease, if there is
// one.
HostByIP(ip netip.Addr) (host string)
// IPByHost returns an IP address by the hostname of its lease, if there is
// one.
IPByHost(host string) (ip netip.Addr)
// WriteDiskConfig4 - copy disk configuration
WriteDiskConfig4(c *V4ServerConf)
// WriteDiskConfig6 - copy disk configuration

View File

@@ -81,21 +81,6 @@ func (s *v4Server) newDHCPConn(iface *net.Interface) (c net.PacketConn, err erro
}, nil
}
// wrapErrs is a helper to wrap the errors from two independent underlying
// connections.
func (*dhcpConn) wrapErrs(action string, udpConnErr, rawConnErr error) (err error) {
switch {
case udpConnErr != nil && rawConnErr != nil:
return errors.List(fmt.Sprintf("%s both connections", action), udpConnErr, rawConnErr)
case udpConnErr != nil:
return fmt.Errorf("%s udp connection: %w", action, udpConnErr)
case rawConnErr != nil:
return fmt.Errorf("%s raw connection: %w", action, rawConnErr)
default:
return nil
}
}
// WriteTo implements net.PacketConn for *dhcpConn. It selects the underlying
// connection to write to based on the type of addr.
func (c *dhcpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
@@ -117,7 +102,7 @@ func (c *dhcpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
Port: dhcpv4.ClientPort,
})
return n, c.wrapErrs("writing to", uerr, rerr)
return n, wrapErrs("writing to", uerr, rerr)
case *net.UDPAddr:
if addr.IP.Equal(net.IPv4bcast) {
// Broadcast the message for the client which supports
@@ -157,7 +142,7 @@ func (c *dhcpConn) Close() (err error) {
rerr = nil
}
return c.wrapErrs("closing", c.udpConn.Close(), rerr)
return wrapErrs("closing", c.udpConn.Close(), rerr)
}
// LocalAddr implements net.PacketConn for *dhcpConn.
@@ -167,12 +152,12 @@ func (c *dhcpConn) LocalAddr() (a net.Addr) {
// SetDeadline implements net.PacketConn for *dhcpConn.
func (c *dhcpConn) SetDeadline(t time.Time) (err error) {
return c.wrapErrs("setting deadline on", c.udpConn.SetDeadline(t), c.rawConn.SetDeadline(t))
return wrapErrs("setting deadline on", c.udpConn.SetDeadline(t), c.rawConn.SetDeadline(t))
}
// SetReadDeadline implements net.PacketConn for *dhcpConn.
func (c *dhcpConn) SetReadDeadline(t time.Time) error {
return c.wrapErrs(
return wrapErrs(
"setting reading deadline on",
c.udpConn.SetReadDeadline(t),
c.rawConn.SetReadDeadline(t),
@@ -181,7 +166,7 @@ func (c *dhcpConn) SetReadDeadline(t time.Time) error {
// SetWriteDeadline implements net.PacketConn for *dhcpConn.
func (c *dhcpConn) SetWriteDeadline(t time.Time) error {
return c.wrapErrs(
return wrapErrs(
"setting writing deadline on",
c.udpConn.SetWriteDeadline(t),
c.rawConn.SetWriteDeadline(t),

View File

@@ -79,21 +79,6 @@ func (s *v4Server) newDHCPConn(iface *net.Interface) (c net.PacketConn, err erro
}, nil
}
// wrapErrs is a helper to wrap the errors from two independent underlying
// connections.
func (*dhcpConn) wrapErrs(action string, udpConnErr, rawConnErr error) (err error) {
switch {
case udpConnErr != nil && rawConnErr != nil:
return errors.List(fmt.Sprintf("%s both connections", action), udpConnErr, rawConnErr)
case udpConnErr != nil:
return fmt.Errorf("%s udp connection: %w", action, udpConnErr)
case rawConnErr != nil:
return fmt.Errorf("%s raw connection: %w", action, rawConnErr)
default:
return nil
}
}
// WriteTo implements net.PacketConn for *dhcpConn. It selects the underlying
// connection to write to based on the type of addr.
func (c *dhcpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
@@ -115,7 +100,7 @@ func (c *dhcpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
Port: dhcpv4.ClientPort,
})
return n, c.wrapErrs("writing to", uerr, rerr)
return n, wrapErrs("writing to", uerr, rerr)
case *net.UDPAddr:
if addr.IP.Equal(net.IPv4bcast) {
// Broadcast the message for the client which supports
@@ -155,7 +140,7 @@ func (c *dhcpConn) Close() (err error) {
rerr = nil
}
return c.wrapErrs("closing", c.udpConn.Close(), rerr)
return wrapErrs("closing", c.udpConn.Close(), rerr)
}
// LocalAddr implements net.PacketConn for *dhcpConn.
@@ -165,12 +150,12 @@ func (c *dhcpConn) LocalAddr() (a net.Addr) {
// SetDeadline implements net.PacketConn for *dhcpConn.
func (c *dhcpConn) SetDeadline(t time.Time) (err error) {
return c.wrapErrs("setting deadline on", c.udpConn.SetDeadline(t), c.rawConn.SetDeadline(t))
return wrapErrs("setting deadline on", c.udpConn.SetDeadline(t), c.rawConn.SetDeadline(t))
}
// SetReadDeadline implements net.PacketConn for *dhcpConn.
func (c *dhcpConn) SetReadDeadline(t time.Time) error {
return c.wrapErrs(
return wrapErrs(
"setting reading deadline on",
c.udpConn.SetReadDeadline(t),
c.rawConn.SetReadDeadline(t),
@@ -179,7 +164,7 @@ func (c *dhcpConn) SetReadDeadline(t time.Time) error {
// SetWriteDeadline implements net.PacketConn for *dhcpConn.
func (c *dhcpConn) SetWriteDeadline(t time.Time) error {
return c.wrapErrs(
return wrapErrs(
"setting writing deadline on",
c.udpConn.SetWriteDeadline(t),
c.rawConn.SetWriteDeadline(t),

View File

@@ -0,0 +1,24 @@
//go:build darwin || freebsd || linux || openbsd
package dhcpd
import (
"fmt"
"github.com/AdguardTeam/golibs/errors"
)
// wrapErrs is a helper to wrap the errors from two independent underlying
// connections.
func wrapErrs(action string, udpConnErr, rawConnErr error) (err error) {
switch {
case udpConnErr != nil && rawConnErr != nil:
return fmt.Errorf("%s both connections: %s", action, errors.Join(udpConnErr, rawConnErr))
case udpConnErr != nil:
return fmt.Errorf("%s udp connection: %w", action, udpConnErr)
case rawConnErr != nil:
return fmt.Errorf("%s raw connection: %w", action, rawConnErr)
default:
return nil
}
}

View File

@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
@@ -99,8 +100,8 @@ func (s *server) dbStore() (err error) {
func writeDB(path string, leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "writing db: %w") }()
slices.SortFunc(leases, func(a, b *Lease) bool {
return a.Hostname < b.Hostname
slices.SortFunc(leases, func(a, b *Lease) (res int) {
return strings.Compare(a.Hostname, b.Hostname)
})
dl := &dataLeases{

View File

@@ -9,6 +9,7 @@ import (
"path/filepath"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
"golang.org/x/exp/slices"
@@ -31,6 +32,8 @@ const (
// Lease contains the necessary information about a DHCP lease. It's used as is
// in the database, so don't change it until it's absolutely necessary, see
// [dataVersion].
//
// TODO(e.burkov): Unexport it and use [dhcpsvc.Lease].
type Lease struct {
// Expiry is the expiration time of the lease.
Expiry time.Time `json:"expires"`
@@ -153,53 +156,37 @@ const (
type Interface interface {
Start() (err error)
Stop() (err error)
// Enabled returns true if the DHCP server is running.
//
// TODO(e.burkov): Currently, we need this method to determine whether the
// local domain suffix should be considered while resolving A/AAAA requests.
// This is because other parts of the code aren't aware of the DNS suffixes
// in DHCP clients names and caller is responsible for trimming it. This
// behavior should be changed in the future.
Enabled() (ok bool)
Leases(flags GetLeasesFlags) (leases []*Lease)
SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT)
FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr)
// Leases returns all the leases in the database.
Leases() (leases []*dhcpsvc.Lease)
// MacByIP returns the MAC address of a client with ip. It returns nil if
// there is no such client, due to an assumption that a DHCP client must
// always have a HardwareAddr.
MACByIP(ip netip.Addr) (mac net.HardwareAddr)
// 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 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.
IPByHost(host string) (ip netip.Addr)
WriteDiskConfig(c *ServerConfig)
}
// MockInterface is a mock Interface implementation.
//
// TODO(e.burkov): Move to aghtest when the API stabilized.
type MockInterface struct {
OnStart func() (err error)
OnStop func() (err error)
OnEnabled func() (ok bool)
OnLeases func(flags GetLeasesFlags) (leases []*Lease)
OnSetOnLeaseChanged func(f OnLeaseChangedT)
OnFindMACbyIP func(ip netip.Addr) (mac net.HardwareAddr)
OnWriteDiskConfig func(c *ServerConfig)
}
var _ Interface = (*MockInterface)(nil)
// Start implements the Interface for *MockInterface.
func (s *MockInterface) Start() (err error) { return s.OnStart() }
// Stop implements the Interface for *MockInterface.
func (s *MockInterface) Stop() (err error) { return s.OnStop() }
// Enabled implements the Interface for *MockInterface.
func (s *MockInterface) Enabled() (ok bool) { return s.OnEnabled() }
// Leases implements the Interface for *MockInterface.
func (s *MockInterface) Leases(flags GetLeasesFlags) (ls []*Lease) { return s.OnLeases(flags) }
// SetOnLeaseChanged implements the Interface for *MockInterface.
func (s *MockInterface) SetOnLeaseChanged(f OnLeaseChangedT) { s.OnSetOnLeaseChanged(f) }
// FindMACbyIP implements the [Interface] for *MockInterface.
func (s *MockInterface) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) {
return s.OnFindMACbyIP(ip)
}
// WriteDiskConfig implements the Interface for *MockInterface.
func (s *MockInterface) WriteDiskConfig(c *ServerConfig) { s.OnWriteDiskConfig(c) }
// server is the DHCP service that handles DHCPv4, DHCPv6, and HTTP API.
type server struct {
srv4 DHCPServer
@@ -269,7 +256,8 @@ func Create(conf *ServerConfig) (s *server, err error) {
}
// setServers updates DHCPv4 and DHCPv6 servers created from the provided
// configuration conf.
// configuration conf. It returns the status of both the DHCPv4 and the DHCPv6
// servers, which is always false for corresponding server on any error.
func (s *server) setServers(conf *ServerConfig) (v4Enabled, v6Enabled bool, err error) {
v4conf := conf.Conf4
v4conf.InterfaceName = s.conf.InterfaceName
@@ -279,7 +267,7 @@ func (s *server) setServers(conf *ServerConfig) (v4Enabled, v6Enabled bool, err
s.srv4, err = v4Create(&v4conf)
if err != nil {
if v4conf.Enabled {
return true, false, fmt.Errorf("creating dhcpv4 srv: %w", err)
return false, false, fmt.Errorf("creating dhcpv4 srv: %w", err)
}
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
@@ -288,14 +276,11 @@ func (s *server) setServers(conf *ServerConfig) (v4Enabled, v6Enabled bool, err
v6conf := conf.Conf6
v6conf.InterfaceName = s.conf.InterfaceName
v6conf.notify = s.onNotify
v6conf.Enabled = s.conf.Enabled
if len(v6conf.RangeStart) == 0 {
v6conf.Enabled = false
}
v6conf.Enabled = s.conf.Enabled && len(v6conf.RangeStart) != 0
s.srv6, err = v6Create(v6conf)
if err != nil {
return v4conf.Enabled, v6conf.Enabled, fmt.Errorf("creating dhcpv6 srv: %w", err)
return v4conf.Enabled, false, fmt.Errorf("creating dhcpv6 srv: %w", err)
}
return v4conf.Enabled, v6conf.Enabled, nil
@@ -337,11 +322,6 @@ func (s *server) onNotify(flags uint32) {
s.notify(int(flags))
}
// SetOnLeaseChanged - set callback
func (s *server) SetOnLeaseChanged(onLeaseChanged OnLeaseChangedT) {
s.onLeaseChanged = append(s.onLeaseChanged, onLeaseChanged)
}
func (s *server) notify(flags int) {
for _, f := range s.onLeaseChanged {
f(flags)
@@ -388,15 +368,26 @@ func (s *server) Stop() (err error) {
return nil
}
// Leases returns the list of active IPv4 and IPv6 DHCP leases. It's safe for
// concurrent use.
func (s *server) Leases(flags GetLeasesFlags) (leases []*Lease) {
return append(s.srv4.GetLeases(flags), s.srv6.GetLeases(flags)...)
// Leases returns the list of active DHCP leases.
func (s *server) Leases() (leases []*dhcpsvc.Lease) {
ls := append(s.srv4.GetLeases(LeasesAll), s.srv6.GetLeases(LeasesAll)...)
leases = make([]*dhcpsvc.Lease, len(ls))
for i, l := range ls {
leases[i] = &dhcpsvc.Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: l.HWAddr,
IP: l.IP,
IsStatic: l.IsStatic,
}
}
return leases
}
// FindMACbyIP returns a MAC address by the IP address of its lease, if there is
// MACByIP returns a MAC address by the IP address of its lease, if there is
// one.
func (s *server) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) {
func (s *server) MACByIP(ip netip.Addr) (mac net.HardwareAddr) {
if ip.Is4() {
return s.srv4.FindMACbyIP(ip)
}
@@ -404,6 +395,24 @@ func (s *server) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) {
return s.srv6.FindMACbyIP(ip)
}
// HostByIP implements the [Interface] interface for *server.
//
// TODO(e.burkov): Implement this method for DHCPv6.
func (s *server) HostByIP(ip netip.Addr) (host string) {
if ip.Is4() {
return s.srv4.HostByIP(ip)
}
return ""
}
// IPByHost implements the [Interface] interface for *server.
//
// TODO(e.burkov): Implement this method for DHCPv6.
func (s *server) IPByHost(host string) (ip netip.Addr) {
return s.srv4.IPByHost(host)
}
// AddStaticLease - add static v4 lease
func (s *server) AddStaticLease(l *Lease) error {
return s.srv4.AddStaticLease(l)

View File

@@ -14,9 +14,11 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/exp/slices"
)
type v4ServerConfJSON struct {
@@ -75,7 +77,7 @@ type leaseStatic struct {
}
// leasesToStatic converts list of leases to their JSON form.
func leasesToStatic(leases []*Lease) (static []*leaseStatic) {
func leasesToStatic(leases []*dhcpsvc.Lease) (static []*leaseStatic) {
static = make([]*leaseStatic, len(leases))
for i, l := range leases {
@@ -113,7 +115,7 @@ type leaseDynamic struct {
}
// leasesToDynamic converts list of leases to their JSON form.
func leasesToDynamic(leases []*Lease) (dynamic []*leaseDynamic) {
func leasesToDynamic(leases []*dhcpsvc.Lease) (dynamic []*leaseDynamic) {
dynamic = make([]*leaseDynamic, len(leases))
for i, l := range leases {
@@ -143,10 +145,29 @@ func (s *server) handleDHCPStatus(w http.ResponseWriter, r *http.Request) {
s.srv4.WriteDiskConfig4(&status.V4)
s.srv6.WriteDiskConfig6(&status.V6)
status.Leases = leasesToDynamic(s.Leases(LeasesDynamic))
status.StaticLeases = leasesToStatic(s.Leases(LeasesStatic))
leases := s.Leases()
slices.SortFunc(leases, func(a, b *dhcpsvc.Lease) (res int) {
if a.IsStatic == b.IsStatic {
return 0
} else if a.IsStatic {
return -1
} else {
return 1
}
})
_ = aghhttp.WriteJSONResponse(w, r, status)
dynamicIdx := slices.IndexFunc(leases, func(l *dhcpsvc.Lease) (ok bool) {
return !l.IsStatic
})
if dynamicIdx == -1 {
dynamicIdx = len(leases)
}
status.Leases = leasesToDynamic(leases[dynamicIdx:])
status.StaticLeases = leasesToStatic(leases[:dynamicIdx])
aghhttp.WriteJSONResponseOK(w, r, status)
}
func (s *server) enableDHCP(ifaceName string) (code int, err error) {
@@ -395,7 +416,7 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
}
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
aghhttp.WriteJSONResponseOK(w, r, resp)
}
// newNetInterfaceJSON creates a JSON object from a [net.Interface] iface.
@@ -547,7 +568,7 @@ func (s *server) handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Reque
setOtherDHCPResult(ifaceName, result)
_ = aghhttp.WriteJSONResponse(w, r, result)
aghhttp.WriteJSONResponseOK(w, r, result)
}
// setOtherDHCPResult sets the results of the check for another DHCP server in

View File

@@ -24,7 +24,7 @@ type jsonError struct {
// TODO(a.garipov): Either take the logger from the server after we've
// refactored logging or make this not a method of *Server.
func (s *server) notImplemented(w http.ResponseWriter, r *http.Request) {
_ = aghhttp.WriteJSONResponseCode(w, r, http.StatusNotImplemented, &jsonError{
aghhttp.WriteJSONResponse(w, r, http.StatusNotImplemented, &jsonError{
Message: aghos.Unsupported("dhcp").Error(),
})
}

View File

@@ -24,6 +24,8 @@ func (winServer) WriteDiskConfig4(_ *V4ServerConf) {}
func (winServer) WriteDiskConfig6(_ *V6ServerConf) {}
func (winServer) Start() (err error) { return nil }
func (winServer) Stop() (err error) { return nil }
func (winServer) HostByIP(_ netip.Addr) (host string) { return "" }
func (winServer) IPByHost(_ string) (ip netip.Addr) { return netip.Addr{} }
func v4Create(_ *V4ServerConf) (s DHCPServer, err error) { return winServer{}, nil }
func v6Create(_ V6ServerConf) (s DHCPServer, err error) { return winServer{}, nil }

View File

@@ -15,7 +15,6 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/timeutil"
"github.com/go-ping/ping"
"github.com/insomniacslk/dhcp/dhcpv4"
@@ -46,11 +45,14 @@ type v4Server struct {
// leased.
leasedOffsets *bitSet
// leaseHosts is the set of all hostnames of all known DHCP clients.
leaseHosts *stringutil.Set
// leases contains all dynamic and static leases.
leases []*Lease
// hostsIndex is the set of all hostnames of all known DHCP clients.
hostsIndex map[string]*Lease
// ipIndex is an index of leases by their IP addresses.
ipIndex map[netip.Addr]*Lease
}
func (s *v4Server) enabled() (ok bool) {
@@ -114,6 +116,30 @@ func (s *v4Server) validHostnameForClient(cliHostname string, ip netip.Addr) (ho
return hostname
}
// HostByIP implements the [Interface] interface for *v4Server.
func (s *v4Server) HostByIP(ip netip.Addr) (host string) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
if l, ok := s.ipIndex[ip]; ok {
return l.Hostname
}
return ""
}
// IPByHost implements the [Interface] interface for *v4Server.
func (s *v4Server) IPByHost(host string) (ip netip.Addr) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
if l, ok := s.hostsIndex[host]; ok {
return l.IP
}
return netip.Addr{}
}
// ResetLeases resets leases.
func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
@@ -123,7 +149,8 @@ func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
}
s.leasedOffsets = newBitSet()
s.leaseHosts = stringutil.NewSet()
s.hostsIndex = make(map[string]*Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*Lease, len(leases))
s.leases = nil
for _, l := range leases {
@@ -199,20 +226,18 @@ func (s *v4Server) GetLeases(flags GetLeasesFlags) (leases []*Lease) {
// FindMACbyIP implements the [Interface] for *v4Server.
func (s *v4Server) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) {
if !ip.Is4() {
return nil
}
now := time.Now()
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
if !ip.Is4() {
return nil
}
for _, l := range s.leases {
if l.IP == ip {
if l.IsStatic || l.Expiry.After(now) {
return l.HWAddr
}
if l, ok := s.ipIndex[ip]; ok {
if l.IsStatic || l.Expiry.After(now) {
return l.HWAddr
}
}
@@ -249,7 +274,8 @@ func (s *v4Server) rmLeaseByIndex(i int) {
s.leasedOffsets.set(offset, false)
}
s.leaseHosts.Del(l.Hostname)
delete(s.hostsIndex, l.Hostname)
delete(s.ipIndex, l.IP)
log.Debug("dhcpv4: removed lease %s (%s)", l.IP, l.HWAddr)
}
@@ -303,13 +329,15 @@ func (s *v4Server) addLease(l *Lease) (err error) {
return fmt.Errorf("lease %s (%s) out of range, not adding", l.IP, l.HWAddr)
}
// TODO(e.burkov): l must have a valid hostname here, investigate.
if l.Hostname != "" {
if s.leaseHosts.Has(l.Hostname) {
if _, ok := s.hostsIndex[l.Hostname]; ok {
return ErrDupHostname
}
s.leaseHosts.Add(l.Hostname)
s.hostsIndex[l.Hostname] = l
}
s.ipIndex[l.IP] = l
s.leases = append(s.leases, l)
s.leasedOffsets.set(offset, true)
@@ -574,7 +602,7 @@ func (s *v4Server) commitLease(l *Lease, hostname string) {
prev := l.Hostname
hostname = s.validHostnameForClient(hostname, l.IP)
if s.leaseHosts.Has(hostname) {
if _, ok := s.hostsIndex[hostname]; ok {
log.Info("dhcpv4: hostname %q already exists", hostname)
if prev == "" {
@@ -590,11 +618,12 @@ func (s *v4Server) commitLease(l *Lease, hostname string) {
l.Expiry = time.Now().Add(s.conf.leaseTime)
if prev != "" && prev != l.Hostname {
s.leaseHosts.Del(prev)
delete(s.hostsIndex, prev)
}
if l.Hostname != "" {
s.leaseHosts.Add(l.Hostname)
s.hostsIndex[l.Hostname] = l
}
s.ipIndex[l.IP] = l
}
// allocateLease allocates a new lease for the MAC address. If there are no IP
@@ -765,7 +794,7 @@ func (s *v4Server) handleInitReboot(req *dhcpv4.DHCPv4, reqIP net.IP) (l *Lease,
return nil, false
}
if !s.conf.subnet.Contains(netip.AddrFrom4(*(*[4]byte)(ip4))) {
if !s.conf.subnet.Contains(netip.AddrFrom4([4]byte(ip4))) {
// If the DHCP server detects that the client is on the wrong net then
// the server SHOULD send a DHCPNAK message to the client.
log.Debug("dhcpv4: wrong subnet in init-reboot req msg for %s: %s", mac, reqIP)
@@ -1292,7 +1321,8 @@ func (s *v4Server) Stop() (err error) {
// Create DHCPv4 server
func v4Create(conf *V4ServerConf) (srv *v4Server, err error) {
s := &v4Server{
leaseHosts: stringutil.NewSet(),
hostsIndex: map[string]*Lease{},
ipIndex: map[netip.Addr]*Lease{},
}
err = conf.Validate()

View File

@@ -791,6 +791,14 @@ func TestV4Server_FindMACbyIP(t *testing.T) {
IP: anotherIP,
}},
}
s.ipIndex = map[netip.Addr]*Lease{
staticIP: s.leases[0],
anotherIP: s.leases[1],
}
s.hostsIndex = map[string]*Lease{
staticName: s.leases[0],
anotherName: s.leases[1],
}
testCases := []struct {
want net.HardwareAddr
@@ -867,7 +875,7 @@ func TestV4Server_handleDecline(t *testing.T) {
func TestV4Server_handleRelease(t *testing.T) {
const (
dynamicName = "dymamic-client"
dynamicName = "dynamic-client"
anotherName = "another-client"
)

View File

@@ -26,15 +26,14 @@ const valueIAID = "ADGH" // value for IANA.ID
//
// TODO(a.garipov): Think about unifying this and v4Server.
type v6Server struct {
srv *server6.Server
leasesLock sync.Mutex
leases []*Lease
ipAddrs [256]byte
sid dhcpv6.DUID
ra raCtx // RA module
ra raCtx
conf V6ServerConf
sid dhcpv6.DUID
srv *server6.Server
leases []*Lease
leasesLock sync.Mutex
ipAddrs [256]byte
}
// WriteDiskConfig4 - write configuration
@@ -59,6 +58,34 @@ func ip6InRange(start, ip net.IP) bool {
return start[15] <= ip[15]
}
// HostByIP implements the [Interface] interface for *v6Server.
func (s *v6Server) HostByIP(ip netip.Addr) (host string) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
for _, l := range s.leases {
if l.IP == ip {
return l.Hostname
}
}
return ""
}
// IPByHost implements the [Interface] interface for *v6Server.
func (s *v6Server) IPByHost(host string) (ip netip.Addr) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
for _, l := range s.leases {
if l.Hostname == host {
return l.IP
}
}
return netip.Addr{}
}
// ResetLeases resets leases.
func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()