Pull request 2070: 4923 gopacket DHCP vol.4

Merge in DNS/adguard-home from 4923-gopacket-dhcp-vol.4 to master

Updates #4923.

Squashed commit of the following:

commit 4b87258c70ac98b2abb1ac95f7e916e244b3cd08
Merge: 61458864f 9b91a8740
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 16 14:05:34 2023 +0300

    Merge branch 'master' into 4923-gopacket-dhcp-vol.4

commit 61458864f3df7a027e65060a5f0fb516cc7911a7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Nov 15 18:48:40 2023 +0300

    all: imp code

commit 506a0ab81e76beebb900f86580577563b471e4e2
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Nov 14 15:59:56 2023 +0300

    all: cleanup moving lease

commit 8d218b732662ac4308ed09d28c1bf9f65906d47c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Mon Nov 13 18:13:39 2023 +0300

    all: rm old leases type
This commit is contained in:
Eugene Burkov 2023-11-16 14:14:40 +03:00
parent 9b91a87406
commit 8bb1aad739
17 changed files with 332 additions and 311 deletions

View file

@ -8,6 +8,7 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
)
@ -49,16 +50,16 @@ type ServerConfig struct {
// DHCPServer - DHCP server interface
type DHCPServer interface {
// ResetLeases resets leases.
ResetLeases(leases []*Lease) (err error)
ResetLeases(leases []*dhcpsvc.Lease) (err error)
// GetLeases returns deep clones of the current leases.
GetLeases(flags GetLeasesFlags) (leases []*Lease)
GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease)
// AddStaticLease - add a static lease
AddStaticLease(l *Lease) (err error)
AddStaticLease(l *dhcpsvc.Lease) (err error)
// RemoveStaticLease - remove a static lease
RemoveStaticLease(l *Lease) (err error)
RemoveStaticLease(l *dhcpsvc.Lease) (err error)
// UpdateStaticLease updates IP, hostname of the lease.
UpdateStaticLease(l *Lease) (err error)
UpdateStaticLease(l *dhcpsvc.Lease) (err error)
// FindMACbyIP returns a MAC address by the IP address of its lease, if
// there is one.
@ -81,7 +82,7 @@ type DHCPServer interface {
Start() (err error)
// Stop - stop server
Stop() (err error)
getLeasesRef() []*Lease
getLeasesRef() []*dhcpsvc.Lease
}
// V4ServerConf - server configuration

View file

@ -5,9 +5,13 @@ package dhcpd
import (
"encoding/json"
"fmt"
"net"
"net/netip"
"os"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/google/renameio/v2/maybe"
@ -28,7 +32,60 @@ type dataLeases struct {
Version int `json:"version"`
// Leases is the list containing stored DHCP leases.
Leases []*Lease `json:"leases"`
Leases []*dbLease `json:"leases"`
}
// dbLease is the structure of stored lease.
type dbLease struct {
Expiry string `json:"expires"`
IP netip.Addr `json:"ip"`
Hostname string `json:"hostname"`
HWAddr string `json:"mac"`
IsStatic bool `json:"static"`
}
// fromLease converts *dhcpsvc.Lease to *dbLease.
func fromLease(l *dhcpsvc.Lease) (dl *dbLease) {
var expiryStr string
if !l.IsStatic {
// The front-end is waiting for RFC 3999 format of the time value. It
// also shouldn't got an Expiry field for static leases.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2692.
expiryStr = l.Expiry.Format(time.RFC3339)
}
return &dbLease{
Expiry: expiryStr,
Hostname: l.Hostname,
HWAddr: l.HWAddr.String(),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
// toLease converts *dbLease to *dhcpsvc.Lease.
func (dl *dbLease) toLease() (l *dhcpsvc.Lease, err error) {
mac, err := net.ParseMAC(dl.HWAddr)
if err != nil {
return nil, fmt.Errorf("parsing hardware address: %w", err)
}
expiry := time.Time{}
if !dl.IsStatic {
expiry, err = time.Parse(time.RFC3339, dl.Expiry)
if err != nil {
return nil, fmt.Errorf("parsing expiry time: %w", err)
}
}
return &dhcpsvc.Lease{
Expiry: expiry,
IP: dl.IP,
Hostname: dl.Hostname,
HWAddr: mac,
IsStatic: dl.IsStatic,
}, nil
}
// dbLoad loads stored leases.
@ -49,15 +106,22 @@ func (s *server) dbLoad() (err error) {
}
leases := dl.Leases
leases4 := []*Lease{}
leases6 := []*Lease{}
leases4 := []*dhcpsvc.Lease{}
leases6 := []*dhcpsvc.Lease{}
for _, l := range leases {
if l.IP.Is4() {
leases4 = append(leases4, l)
var lease *dhcpsvc.Lease
lease, err = l.toLease()
if err != nil {
log.Info("dhcp: invalid lease: %s", err)
continue
}
if lease.IP.Is4() {
leases4 = append(leases4, lease)
} else {
leases6 = append(leases6, l)
leases6 = append(leases6, lease)
}
}
@ -73,8 +137,12 @@ func (s *server) dbLoad() (err error) {
}
}
log.Info("dhcp: loaded leases v4:%d v6:%d total-read:%d from DB",
len(leases4), len(leases6), len(leases))
log.Info(
"dhcp: loaded leases v4:%d v6:%d total-read:%d from DB",
len(leases4),
len(leases6),
len(leases),
)
return nil
}
@ -83,24 +151,26 @@ func (s *server) dbLoad() (err error) {
func (s *server) dbStore() (err error) {
// Use an empty slice here as opposed to nil so that it doesn't write
// "null" into the database file if leases are empty.
leases := []*Lease{}
leases := []*dbLease{}
leases4 := s.srv4.getLeasesRef()
leases = append(leases, leases4...)
for _, l := range s.srv4.getLeasesRef() {
leases = append(leases, fromLease(l))
}
if s.srv6 != nil {
leases6 := s.srv6.getLeasesRef()
leases = append(leases, leases6...)
for _, l := range s.srv6.getLeasesRef() {
leases = append(leases, fromLease(l))
}
}
return writeDB(s.conf.dbFilePath, leases)
}
// writeDB writes leases to file at path.
func writeDB(path string, leases []*Lease) (err error) {
func writeDB(path string, leases []*dbLease) (err error) {
defer func() { err = errors.Annotate(err, "writing db: %w") }()
slices.SortFunc(leases, func(a, b *Lease) (res int) {
slices.SortFunc(leases, func(a, b *dbLease) (res int) {
return strings.Compare(a.Hostname, b.Hostname)
})

View file

@ -2,7 +2,6 @@
package dhcpd
import (
"encoding/json"
"fmt"
"net"
"net/netip"
@ -12,7 +11,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/timeutil"
"golang.org/x/exp/slices"
)
const (
@ -29,105 +27,6 @@ const (
defaultBackoff time.Duration = 500 * time.Millisecond
)
// 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"`
// Hostname of the client.
Hostname string `json:"hostname"`
// HWAddr is the physical hardware address (MAC address).
HWAddr net.HardwareAddr `json:"mac"`
// IP is the IP address leased to the client.
IP netip.Addr `json:"ip"`
// IsStatic defines if the lease is static.
IsStatic bool `json:"static"`
}
// Clone returns a deep copy of l.
func (l *Lease) Clone() (clone *Lease) {
if l == nil {
return nil
}
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: slices.Clone(l.HWAddr),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
// IsBlocklisted returns true if the lease is blocklisted.
//
// TODO(a.garipov): Just make it a boolean field.
func (l *Lease) IsBlocklisted() (ok bool) {
if len(l.HWAddr) == 0 {
return false
}
for _, b := range l.HWAddr {
if b != 0 {
return false
}
}
return true
}
// MarshalJSON implements the json.Marshaler interface for Lease.
func (l Lease) MarshalJSON() ([]byte, error) {
var expiryStr string
if !l.IsStatic {
// The front-end is waiting for RFC 3999 format of the time
// value. It also shouldn't got an Expiry field for static
// leases.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2692.
expiryStr = l.Expiry.Format(time.RFC3339)
}
type lease Lease
return json.Marshal(&struct {
HWAddr string `json:"mac"`
Expiry string `json:"expires,omitempty"`
lease
}{
HWAddr: l.HWAddr.String(),
Expiry: expiryStr,
lease: lease(l),
})
}
// UnmarshalJSON implements the json.Unmarshaler interface for *Lease.
func (l *Lease) UnmarshalJSON(data []byte) (err error) {
type lease Lease
aux := struct {
*lease
HWAddr string `json:"mac"`
}{
lease: (*lease)(l),
}
if err = json.Unmarshal(data, &aux); err != nil {
return err
}
l.HWAddr, err = net.ParseMAC(aux.HWAddr)
if err != nil {
return fmt.Errorf("couldn't parse MAC address: %w", err)
}
return nil
}
// OnLeaseChangedT is a callback for lease changes.
type OnLeaseChangedT func(flags int)
@ -370,19 +269,7 @@ func (s *server) Stop() (err error) {
// 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
return append(s.srv4.GetLeases(LeasesAll), s.srv6.GetLeases(LeasesAll)...)
}
// MACByIP returns a MAC address by the IP address of its lease, if there is
@ -414,6 +301,6 @@ func (s *server) IPByHost(host string) (ip netip.Addr) {
}
// AddStaticLease - add static v4 lease
func (s *server) AddStaticLease(l *Lease) error {
func (s *server) AddStaticLease(l *dhcpsvc.Lease) error {
return s.srv4.AddStaticLease(l)
}

View file

@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -44,7 +45,7 @@ func TestDB(t *testing.T) {
s.srv6, err = v6Create(V6ServerConf{})
require.NoError(t, err)
leases := []*Lease{{
leases := []*dhcpsvc.Lease{{
Expiry: time.Now().Add(time.Hour),
Hostname: "static-1.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},

View file

@ -93,13 +93,13 @@ func leasesToStatic(leases []*dhcpsvc.Lease) (static []*leaseStatic) {
}
// toLease converts leaseStatic to Lease or returns error.
func (l *leaseStatic) toLease() (lease *Lease, err error) {
func (l *leaseStatic) toLease() (lease *dhcpsvc.Lease, err error) {
addr, err := net.ParseMAC(l.HWAddr)
if err != nil {
return nil, fmt.Errorf("couldn't parse MAC address: %w", err)
}
return &Lease{
return &dhcpsvc.Lease{
HWAddr: addr,
IP: l.IP,
Hostname: l.Hostname,
@ -593,7 +593,7 @@ func setOtherDHCPResult(ifaceName string, result *dhcpSearchResult) {
// parseLease parses a lease from r. If there is no error returns DHCPServer
// and *Lease. r must be non-nil.
func (s *server) parseLease(r io.Reader) (srv DHCPServer, lease *Lease, err error) {
func (s *server) parseLease(r io.Reader) (srv DHCPServer, lease *dhcpsvc.Lease, err error) {
l := &leaseStatic{}
err = json.NewDecoder(r).Decode(l)
if err != nil {

View file

@ -2,6 +2,7 @@ package dhcpd
import (
"encoding/json"
"fmt"
"net"
"net/netip"
"os"
@ -25,9 +26,9 @@ const (
dbFilename = "leases.db"
)
// leaseJSON is the structure of stored lease.
// leaseJSON is the structure of stored lease in a legacy database.
//
// Deprecated: Use [Lease].
// Deprecated: Use [dbLease].
type leaseJSON struct {
HWAddr []byte `json:"mac"`
IP []byte `json:"ip"`
@ -35,13 +36,28 @@ type leaseJSON struct {
Expiry int64 `json:"exp"`
}
func normalizeIP(ip net.IP) net.IP {
ip4 := ip.To4()
if ip4 != nil {
return ip4
// readOldDB reads the old database from the given path.
func readOldDB(path string) (leases []*leaseJSON, err error) {
// #nosec G304 -- Trust this path, since it's taken from the old file name
// relative to the working directory and should generally be considered
// safe.
file, err := os.Open(path)
if errors.Is(err, os.ErrNotExist) {
// Nothing to migrate.
return nil, nil
} else if err != nil {
// Don't wrap the error since it's informative enough as is.
return nil, err
}
defer func() { err = errors.WithDeferred(err, file.Close()) }()
leases = []*leaseJSON{}
err = json.NewDecoder(file).Decode(&leases)
if err != nil {
return nil, fmt.Errorf("decoding old db: %w", err)
}
return ip
return leases, nil
}
// migrateDB migrates stored leases if necessary.
@ -51,59 +67,50 @@ func migrateDB(conf *ServerConfig) (err error) {
oldLeasesPath := filepath.Join(conf.WorkDir, dbFilename)
dataDirPath := filepath.Join(conf.DataDir, dataFilename)
// #nosec G304 -- Trust this path, since it's taken from the old file name
// relative to the working directory and should generally be considered
// safe.
file, err := os.Open(oldLeasesPath)
if errors.Is(err, os.ErrNotExist) {
oldLeases, err := readOldDB(oldLeasesPath)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
} else if oldLeases == nil {
// Nothing to migrate.
return nil
} else if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
ljs := []leaseJSON{}
err = json.NewDecoder(file).Decode(&ljs)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
err = file.Close()
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
leases := []*Lease{}
for _, lj := range ljs {
lj.IP = normalizeIP(lj.IP)
ip, ok := netip.AddrFromSlice(lj.IP)
leases := make([]*dbLease, 0, len(oldLeases))
for _, l := range oldLeases {
l.IP = normalizeIP(l.IP)
ip, ok := netip.AddrFromSlice(l.IP)
if !ok {
log.Info("dhcp: invalid IP: %s", lj.IP)
log.Info("dhcp: invalid IP: %s", l.IP)
continue
}
lease := &Lease{
Expiry: time.Unix(lj.Expiry, 0),
Hostname: lj.Hostname,
HWAddr: lj.HWAddr,
leases = append(leases, &dbLease{
Expiry: time.Unix(l.Expiry, 0).Format(time.RFC3339),
Hostname: l.Hostname,
HWAddr: net.HardwareAddr(l.HWAddr).String(),
IP: ip,
IsStatic: lj.Expiry == leaseExpireStatic,
}
leases = append(leases, lease)
IsStatic: l.Expiry == leaseExpireStatic,
})
}
err = writeDB(dataDirPath, leases)
if err != nil {
// Don't wrap the error since it's informative enough as is.
// Don't wrap the error since an annotation deferred already.
return err
}
return os.Remove(oldLeasesPath)
}
// normalizeIP converts the given IP address to IPv4 if it's IPv4-mapped IPv6,
// or leaves it as is otherwise.
func normalizeIP(ip net.IP) (normalized net.IP) {
normalized = ip.To4()
if normalized != nil {
return normalized
}
return ip
}

View file

@ -2,7 +2,6 @@ package dhcpd
import (
"encoding/json"
"net"
"net/netip"
"os"
"path/filepath"
@ -27,16 +26,16 @@ func TestMigrateDB(t *testing.T) {
err := os.WriteFile(oldLeasesPath, []byte(testData), 0o644)
require.NoError(t, err)
wantLeases := []*Lease{{
Expiry: time.Time{},
wantLeases := []*dbLease{{
Expiry: time.Unix(1, 0).Format(time.RFC3339),
Hostname: "test1",
HWAddr: net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
HWAddr: "11:22:33:44:55:66",
IP: netip.MustParseAddr("1.2.3.4"),
IsStatic: true,
}, {
Expiry: time.Unix(1231231231, 0),
Expiry: time.Unix(1231231231, 0).Format(time.RFC3339),
Hostname: "test2",
HWAddr: net.HardwareAddr{0x66, 0x55, 0x44, 0x33, 0x22, 0x11},
HWAddr: "66:55:44:33:22:11",
IP: netip.MustParseAddr("4.3.2.1"),
IsStatic: false,
}}
@ -62,12 +61,12 @@ func TestMigrateDB(t *testing.T) {
leases := dl.Leases
for i, wl := range wantLeases {
assert.Equal(t, wl.Hostname, leases[i].Hostname)
assert.Equal(t, wl.HWAddr, leases[i].HWAddr)
assert.Equal(t, wl.IP, leases[i].IP)
assert.Equal(t, wl.IsStatic, leases[i].IsStatic)
for i, wantLease := range wantLeases {
assert.Equal(t, wantLease.Hostname, leases[i].Hostname)
assert.Equal(t, wantLease.HWAddr, leases[i].HWAddr)
assert.Equal(t, wantLease.IP, leases[i].IP)
assert.Equal(t, wantLease.IsStatic, leases[i].IsStatic)
require.True(t, wl.Expiry.Equal(leases[i].Expiry))
require.Equal(t, wantLease.Expiry, leases[i].Expiry)
}
}

View file

@ -7,6 +7,8 @@ package dhcpd
import (
"net"
"net/netip"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
)
type winServer struct{}
@ -14,19 +16,19 @@ type winServer struct{}
// type check
var _ DHCPServer = winServer{}
func (winServer) ResetLeases(_ []*Lease) (err error) { return nil }
func (winServer) GetLeases(_ GetLeasesFlags) (leases []*Lease) { return nil }
func (winServer) getLeasesRef() []*Lease { return nil }
func (winServer) AddStaticLease(_ *Lease) (err error) { return nil }
func (winServer) RemoveStaticLease(_ *Lease) (err error) { return nil }
func (winServer) UpdateStaticLease(_ *Lease) (err error) { return nil }
func (winServer) FindMACbyIP(_ netip.Addr) (mac net.HardwareAddr) { return nil }
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 (winServer) ResetLeases(_ []*dhcpsvc.Lease) (err error) { return nil }
func (winServer) GetLeases(_ GetLeasesFlags) (leases []*dhcpsvc.Lease) { return nil }
func (winServer) getLeasesRef() []*dhcpsvc.Lease { return nil }
func (winServer) AddStaticLease(_ *dhcpsvc.Lease) (err error) { return nil }
func (winServer) RemoveStaticLease(_ *dhcpsvc.Lease) (err error) { return nil }
func (winServer) UpdateStaticLease(_ *dhcpsvc.Lease) (err error) { return nil }
func (winServer) FindMACbyIP(_ netip.Addr) (mac net.HardwareAddr) { return nil }
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

@ -12,6 +12,7 @@ import (
"time"
"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"
@ -38,7 +39,7 @@ type v4Server struct {
// have intersections with [implicitOpts].
explicitOpts dhcpv4.Options
// leasesLock protects leases, leaseHosts, and leasedOffsets.
// leasesLock protects leases, hostsIndex, ipIndex, and leasedOffsets.
leasesLock sync.Mutex
// leasedOffsets contains offsets from conf.ipRange.start that have been
@ -46,13 +47,13 @@ type v4Server struct {
leasedOffsets *bitSet
// leases contains all dynamic and static leases.
leases []*Lease
leases []*dhcpsvc.Lease
// hostsIndex is the set of all hostnames of all known DHCP clients.
hostsIndex map[string]*Lease
hostsIndex map[string]*dhcpsvc.Lease
// ipIndex is an index of leases by their IP addresses.
ipIndex map[netip.Addr]*Lease
ipIndex map[netip.Addr]*dhcpsvc.Lease
}
func (s *v4Server) enabled() (ok bool) {
@ -141,7 +142,7 @@ func (s *v4Server) IPByHost(host string) (ip netip.Addr) {
}
// ResetLeases resets leases.
func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
func (s *v4Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if s.conf == nil {
@ -152,8 +153,8 @@ func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
defer s.leasesLock.Unlock()
s.leasedOffsets = newBitSet()
s.hostsIndex = make(map[string]*Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*Lease, len(leases))
s.hostsIndex = make(map[string]*dhcpsvc.Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*dhcpsvc.Lease, len(leases))
s.leases = nil
for _, l := range leases {
@ -173,14 +174,14 @@ func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
}
// getLeasesRef returns the actual leases slice. For internal use only.
func (s *v4Server) getLeasesRef() []*Lease {
func (s *v4Server) getLeasesRef() []*dhcpsvc.Lease {
return s.leases
}
// isBlocklisted returns true if this lease holds a blocklisted IP.
//
// TODO(a.garipov): Make a method of *Lease?
func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
func (s *v4Server) isBlocklisted(l *dhcpsvc.Lease) (ok bool) {
if len(l.HWAddr) == 0 {
return false
}
@ -196,11 +197,11 @@ func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
// use.
func (s *v4Server) GetLeases(flags GetLeasesFlags) (leases []*Lease) {
func (s *v4Server) GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease) {
// The function shouldn't return nil, because zero-length slice behaves
// differently in cases like marshalling. Our front-end also requires
// a non-nil value in the response.
leases = []*Lease{}
leases = []*dhcpsvc.Lease{}
getDynamic := flags&LeasesDynamic != 0
getStatic := flags&LeasesStatic != 0
@ -248,7 +249,7 @@ func (s *v4Server) FindMACbyIP(ip netip.Addr) (mac net.HardwareAddr) {
const defaultHwAddrLen = 6
// Add the specified IP to the black list for a time period
func (s *v4Server) blocklistLease(l *Lease) {
func (s *v4Server) blocklistLease(l *dhcpsvc.Lease) {
l.HWAddr = make(net.HardwareAddr, defaultHwAddrLen)
l.Hostname = ""
l.Expiry = time.Now().Add(s.conf.leaseTime)
@ -284,7 +285,7 @@ func (s *v4Server) rmLeaseByIndex(i int) {
// Return error if a static lease is found
//
// TODO(s.chzhen): Refactor the code.
func (s *v4Server) rmDynamicLease(lease *Lease) (err error) {
func (s *v4Server) rmDynamicLease(lease *dhcpsvc.Lease) (err error) {
for i, l := range s.leases {
isStatic := l.IsStatic
@ -320,7 +321,7 @@ const (
)
// addLease adds a dynamic or static lease.
func (s *v4Server) addLease(l *Lease) (err error) {
func (s *v4Server) addLease(l *dhcpsvc.Lease) (err error) {
r := s.conf.ipRange
leaseIP := net.IP(l.IP.AsSlice())
offset, inOffset := r.offset(leaseIP)
@ -352,7 +353,7 @@ func (s *v4Server) addLease(l *Lease) (err error) {
}
// rmLease removes a lease with the same properties.
func (s *v4Server) rmLease(lease *Lease) (err error) {
func (s *v4Server) rmLease(lease *dhcpsvc.Lease) (err error) {
if len(s.leases) == 0 {
return nil
}
@ -378,7 +379,7 @@ const ErrUnconfigured errors.Error = "server is unconfigured"
// AddStaticLease implements the DHCPServer interface for *v4Server. It is
// safe for concurrent use.
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
func (s *v4Server) AddStaticLease(l *dhcpsvc.Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
if s.conf == nil {
@ -435,7 +436,7 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
}
// UpdateStaticLease updates IP, hostname of the static lease.
func (s *v4Server) UpdateStaticLease(l *Lease) (err error) {
func (s *v4Server) UpdateStaticLease(l *dhcpsvc.Lease) (err error) {
defer func() {
if err != nil {
err = errors.Annotate(err, "dhcpv4: updating static lease: %w")
@ -474,7 +475,7 @@ func (s *v4Server) UpdateStaticLease(l *Lease) (err error) {
}
// validateStaticLease returns an error if the static lease is invalid.
func (s *v4Server) validateStaticLease(l *Lease) (err error) {
func (s *v4Server) validateStaticLease(l *dhcpsvc.Lease) (err error) {
hostname, err := normalizeHostname(l.Hostname)
if err != nil {
// Don't wrap the error, because it's informative enough as is.
@ -511,7 +512,7 @@ func (s *v4Server) validateStaticLease(l *Lease) (err error) {
// updateStaticLease safe removes dynamic lease with the same properties and
// then adds a static lease l.
func (s *v4Server) updateStaticLease(l *Lease) (err error) {
func (s *v4Server) updateStaticLease(l *dhcpsvc.Lease) (err error) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
@ -529,7 +530,7 @@ func (s *v4Server) updateStaticLease(l *Lease) (err error) {
}
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
func (s *v4Server) RemoveStaticLease(l *dhcpsvc.Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
if s.conf == nil {
@ -606,7 +607,7 @@ func (s *v4Server) addrAvailable(target net.IP) (avail bool) {
}
// findLease finds a lease by its MAC-address.
func (s *v4Server) findLease(mac net.HardwareAddr) (l *Lease) {
func (s *v4Server) findLease(mac net.HardwareAddr) (l *dhcpsvc.Lease) {
for _, l = range s.leases {
if bytes.Equal(mac, l.HWAddr) {
return l
@ -646,8 +647,8 @@ func (s *v4Server) findExpiredLease() int {
// reserveLease reserves a lease for a client by its MAC-address. It returns
// nil if it couldn't allocate a new lease.
func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *Lease, err error) {
l = &Lease{HWAddr: slices.Clone(mac)}
func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *dhcpsvc.Lease, err error) {
l = &dhcpsvc.Lease{HWAddr: slices.Clone(mac)}
nextIP := s.nextIP()
if nextIP == nil {
@ -679,7 +680,7 @@ func (s *v4Server) reserveLease(mac net.HardwareAddr) (l *Lease, err error) {
// commitLease refreshes l's values. It takes the desired hostname into account
// when setting it into the lease, but generates a unique one if the provided
// can't be used.
func (s *v4Server) commitLease(l *Lease, hostname string) {
func (s *v4Server) commitLease(l *dhcpsvc.Lease, hostname string) {
prev := l.Hostname
hostname = s.validHostnameForClient(hostname, l.IP)
@ -709,7 +710,7 @@ func (s *v4Server) commitLease(l *Lease, hostname string) {
// allocateLease allocates a new lease for the MAC address. If there are no IP
// addresses left, both l and err are nil.
func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *Lease, err error) {
func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *dhcpsvc.Lease, err error) {
for {
l, err = s.reserveLease(mac)
if err != nil {
@ -728,7 +729,7 @@ func (s *v4Server) allocateLease(mac net.HardwareAddr) (l *Lease, err error) {
}
// handleDiscover is the handler for the DHCP Discover request.
func (s *v4Server) handleDiscover(req, resp *dhcpv4.DHCPv4) (l *Lease, err error) {
func (s *v4Server) handleDiscover(req, resp *dhcpv4.DHCPv4) (l *dhcpsvc.Lease, err error) {
mac := req.ClientHWAddr
defer s.conf.notify(LeaseChangedDBStore)
@ -787,7 +788,7 @@ func OptionFQDN(fqdn string) (opt dhcpv4.Option) {
// checkLease checks if the pair of mac and ip is already leased. The mismatch
// is true when the existing lease has the same hardware address but differs in
// its IP address.
func (s *v4Server) checkLease(mac net.HardwareAddr, ip net.IP) (lease *Lease, mismatch bool) {
func (s *v4Server) checkLease(mac net.HardwareAddr, ip net.IP) (l *dhcpsvc.Lease, mismatch bool) {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
@ -798,7 +799,7 @@ func (s *v4Server) checkLease(mac net.HardwareAddr, ip net.IP) (lease *Lease, mi
return nil, false
}
for _, l := range s.leases {
for _, l = range s.leases {
if !bytes.Equal(l.HWAddr, mac) {
continue
}
@ -823,7 +824,7 @@ func (s *v4Server) handleSelecting(
req *dhcpv4.DHCPv4,
reqIP net.IP,
sid net.IP,
) (l *Lease, needsReply bool) {
) (l *dhcpsvc.Lease, needsReply bool) {
// Client inserts the address of the selected server in server identifier,
// ciaddr MUST be zero.
mac := req.ClientHWAddr
@ -857,7 +858,10 @@ func (s *v4Server) handleSelecting(
}
// handleInitReboot handles the DHCPREQUEST generated during INIT-REBOOT state.
func (s *v4Server) handleInitReboot(req *dhcpv4.DHCPv4, reqIP net.IP) (l *Lease, needsReply bool) {
func (s *v4Server) handleInitReboot(
req *dhcpv4.DHCPv4,
reqIP net.IP,
) (l *dhcpsvc.Lease, needsReply bool) {
mac := req.ClientHWAddr
ip4 := reqIP.To4()
@ -899,7 +903,7 @@ func (s *v4Server) handleInitReboot(req *dhcpv4.DHCPv4, reqIP net.IP) (l *Lease,
// handleRenew handles the DHCPREQUEST generated during RENEWING or REBINDING
// state.
func (s *v4Server) handleRenew(req *dhcpv4.DHCPv4) (l *Lease, needsReply bool) {
func (s *v4Server) handleRenew(req *dhcpv4.DHCPv4) (l *dhcpsvc.Lease, needsReply bool) {
mac := req.ClientHWAddr
// ciaddr MUST be filled in with client's IP address.
@ -926,7 +930,7 @@ func (s *v4Server) handleRenew(req *dhcpv4.DHCPv4) (l *Lease, needsReply bool) {
// handleByRequestType handles the DHCPREQUEST according to the state during
// which it's generated by client.
func (s *v4Server) handleByRequestType(req *dhcpv4.DHCPv4) (lease *Lease, needsReply bool) {
func (s *v4Server) handleByRequestType(req *dhcpv4.DHCPv4) (lease *dhcpsvc.Lease, needsReply bool) {
reqIP, sid := req.RequestedIPAddress(), req.ServerIdentifier()
if sid != nil && !sid.IsUnspecified() {
@ -950,7 +954,7 @@ func (s *v4Server) handleByRequestType(req *dhcpv4.DHCPv4) (lease *Lease, needsR
// handleRequest is the handler for a DHCPREQUEST message.
//
// See https://datatracker.ietf.org/doc/html/rfc2131#section-4.3.2.
func (s *v4Server) handleRequest(req, resp *dhcpv4.DHCPv4) (lease *Lease, needsReply bool) {
func (s *v4Server) handleRequest(req, resp *dhcpv4.DHCPv4) (lease *dhcpsvc.Lease, needsReply bool) {
lease, needsReply = s.handleByRequestType(req)
if lease == nil {
return nil, needsReply
@ -1043,7 +1047,7 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
}
// findLeaseForIP returns a lease for provided ip and mac.
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *Lease) {
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *dhcpsvc.Lease) {
netIP, ok := netip.AddrFromSlice(ip)
if !ok {
log.Info("dhcpv4: invalid IP: %s", ip)
@ -1106,7 +1110,11 @@ func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
}
// messageHandler describes a DHCPv4 message handler function.
type messageHandler func(s *v4Server, req, resp *dhcpv4.DHCPv4) (rCode int, l *Lease, err error)
type messageHandler func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *dhcpsvc.Lease, err error)
// messageHandlers is a map of handlers for various messages with message types
// keys.
@ -1115,7 +1123,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
) (rCode int, l *dhcpsvc.Lease, err error) {
l, err = s.handleDiscover(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling discover: %s", err)
@ -1131,7 +1139,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
) (rCode int, l *dhcpsvc.Lease, err error) {
var toReply bool
l, toReply = s.handleRequest(req, resp)
if l == nil {
@ -1149,7 +1157,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
) (rCode int, l *dhcpsvc.Lease, err error) {
err = s.handleDecline(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling decline: %s", err)
@ -1161,7 +1169,7 @@ var messageHandlers = map[dhcpv4.MessageType]messageHandler{
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
) (rCode int, l *dhcpsvc.Lease, err error) {
err = s.handleRelease(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling release: %s", err)
@ -1402,8 +1410,8 @@ func (s *v4Server) Stop() (err error) {
// Create DHCPv4 server
func v4Create(conf *V4ServerConf) (srv *v4Server, err error) {
s := &v4Server{
hostsIndex: map[string]*Lease{},
ipIndex: map[netip.Addr]*Lease{},
hostsIndex: map[string]*dhcpsvc.Lease{},
ipIndex: map[netip.Addr]*dhcpsvc.Lease{},
}
err = conf.Validate()

View file

@ -11,6 +11,7 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/AdguardTeam/golibs/testutil"
@ -67,7 +68,7 @@ func TestV4Server_leasing(t *testing.T) {
s := defaultSrv(t)
t.Run("add_static", func(t *testing.T) {
err := s.AddStaticLease(&Lease{
err := s.AddStaticLease(&dhcpsvc.Lease{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,
@ -76,7 +77,7 @@ func TestV4Server_leasing(t *testing.T) {
require.NoError(t, err)
t.Run("same_name", func(t *testing.T) {
err = s.AddStaticLease(&Lease{
err = s.AddStaticLease(&dhcpsvc.Lease{
Hostname: staticName,
HWAddr: anotherMAC,
IP: anotherIP,
@ -90,7 +91,7 @@ func TestV4Server_leasing(t *testing.T) {
"dynamic leases for " + anotherIP.String() +
" (" + staticMAC.String() + "): static lease already exists"
err = s.AddStaticLease(&Lease{
err = s.AddStaticLease(&dhcpsvc.Lease{
Hostname: anotherName,
HWAddr: staticMAC,
IP: anotherIP,
@ -104,7 +105,7 @@ func TestV4Server_leasing(t *testing.T) {
"dynamic leases for " + staticIP.String() +
" (" + anotherMAC.String() + "): static lease already exists"
err = s.AddStaticLease(&Lease{
err = s.AddStaticLease(&dhcpsvc.Lease{
Hostname: anotherName,
HWAddr: anotherMAC,
IP: staticIP,
@ -208,11 +209,11 @@ func TestV4Server_AddRemove_static(t *testing.T) {
require.Empty(t, ls)
testCases := []struct {
lease *Lease
lease *dhcpsvc.Lease
name string
wantErrMsg string
}{{
lease: &Lease{
lease: &dhcpsvc.Lease{
Hostname: "success.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@ -220,7 +221,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
name: "success",
wantErrMsg: "",
}, {
lease: &Lease{
lease: &dhcpsvc.Lease{
Hostname: "probably-router.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: DefaultGatewayIP,
@ -229,7 +230,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
wantErrMsg: "dhcpv4: adding static lease: " +
`can't assign the gateway IP "192.168.10.1" to the lease`,
}, {
lease: &Lease{
lease: &dhcpsvc.Lease{
Hostname: "ip6.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("ffff::1"),
@ -238,7 +239,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
wantErrMsg: `dhcpv4: adding static lease: ` +
`invalid IP "ffff::1": only IPv4 is supported`,
}, {
lease: &Lease{
lease: &dhcpsvc.Lease{
Hostname: "bad-mac.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@ -247,7 +248,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
wantErrMsg: `dhcpv4: adding static lease: bad mac address "aa:aa": ` +
`bad mac address length 2, allowed: [6 8 20]`,
}, {
lease: &Lease{
lease: &dhcpsvc.Lease{
Hostname: "bad-lbl-.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@ -266,7 +267,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
return
}
err = s.RemoveStaticLease(&Lease{
err = s.RemoveStaticLease(&dhcpsvc.Lease{
IP: tc.lease.IP,
HWAddr: tc.lease.HWAddr,
})
@ -289,7 +290,7 @@ func TestV4_AddReplace(t *testing.T) {
s, ok := sIface.(*v4Server)
require.True(t, ok)
dynLeases := []Lease{{
dynLeases := []dhcpsvc.Lease{{
Hostname: "dynamic-1.local",
HWAddr: net.HardwareAddr{0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@ -304,7 +305,7 @@ func TestV4_AddReplace(t *testing.T) {
require.NoError(t, err)
}
stLeases := []*Lease{{
stLeases := []*dhcpsvc.Lease{{
Hostname: "static-1.local",
HWAddr: net.HardwareAddr{0x33, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@ -513,7 +514,7 @@ func TestV4StaticLease_Get(t *testing.T) {
s.conf.dnsIPAddrs = []netip.Addr{dnsAddr}
s.implicitOpts.Update(dhcpv4.OptDNS(dnsAddr.AsSlice()))
l := &Lease{
l := &dhcpsvc.Lease{
Hostname: "static-1.local",
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: netip.MustParseAddr("192.168.10.150"),
@ -779,7 +780,7 @@ func TestV4Server_FindMACbyIP(t *testing.T) {
anotherMAC := net.HardwareAddr{0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB}
s := &v4Server{
leases: []*Lease{{
leases: []*dhcpsvc.Lease{{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,
@ -791,11 +792,11 @@ func TestV4Server_FindMACbyIP(t *testing.T) {
IP: anotherIP,
}},
}
s.ipIndex = map[netip.Addr]*Lease{
s.ipIndex = map[netip.Addr]*dhcpsvc.Lease{
staticIP: s.leases[0],
anotherIP: s.leases[1],
}
s.hostsIndex = map[string]*Lease{
s.hostsIndex = map[string]*dhcpsvc.Lease{
staticName: s.leases[0],
anotherName: s.leases[1],
}
@ -845,7 +846,7 @@ func TestV4Server_handleDecline(t *testing.T) {
s4, ok := s.(*v4Server)
require.True(t, ok)
s4.leases = []*Lease{{
s4.leases = []*dhcpsvc.Lease{{
Hostname: dynamicName,
HWAddr: dynamicMAC,
IP: dynamicIP,
@ -887,7 +888,7 @@ func TestV4Server_handleRelease(t *testing.T) {
s4, ok := s.(*v4Server)
require.True(t, ok)
s4.leases = []*Lease{{
s4.leases = []*dhcpsvc.Lease{{
Hostname: dynamicName,
HWAddr: dynamicMAC,
IP: dynamicIP,

View file

@ -11,6 +11,7 @@ import (
"time"
"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"
@ -31,7 +32,7 @@ type v6Server struct {
sid dhcpv6.DUID
srv *server6.Server
leases []*Lease
leases []*dhcpsvc.Lease
leasesLock sync.Mutex
ipAddrs [256]byte
}
@ -87,7 +88,7 @@ func (s *v6Server) IPByHost(host string) (ip netip.Addr) {
}
// ResetLeases resets leases.
func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
func (s *v6Server) ResetLeases(leases []*dhcpsvc.Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
s.leasesLock.Lock()
@ -111,12 +112,14 @@ func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
// GetLeases returns the list of current DHCP leases. It is safe for concurrent
// use.
func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*Lease) {
func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*dhcpsvc.Lease) {
// The function shouldn't return nil value because zero-length slice
// behaves differently in cases like marshalling. Our front-end also
// requires non-nil value in the response.
leases = []*Lease{}
leases = []*dhcpsvc.Lease{}
s.leasesLock.Lock()
defer s.leasesLock.Unlock()
for _, l := range s.leases {
if l.IsStatic {
if (flags & LeasesStatic) != 0 {
@ -128,12 +131,12 @@ func (s *v6Server) GetLeases(flags GetLeasesFlags) (leases []*Lease) {
}
}
}
s.leasesLock.Unlock()
return leases
}
// getLeasesRef returns the actual leases slice. For internal use only.
func (s *v6Server) getLeasesRef() []*Lease {
func (s *v6Server) getLeasesRef() []*dhcpsvc.Lease {
return s.leases
}
@ -174,7 +177,7 @@ func (s *v6Server) leaseRemoveSwapByIndex(i int) {
// Remove a dynamic lease with the same properties
// Return error if a static lease is found
func (s *v6Server) rmDynamicLease(lease *Lease) (err error) {
func (s *v6Server) rmDynamicLease(lease *dhcpsvc.Lease) (err error) {
for i := 0; i < len(s.leases); i++ {
l := s.leases[i]
@ -204,7 +207,7 @@ func (s *v6Server) rmDynamicLease(lease *Lease) (err error) {
}
// AddStaticLease adds a static lease. It is safe for concurrent use.
func (s *v6Server) AddStaticLease(l *Lease) (err error) {
func (s *v6Server) AddStaticLease(l *dhcpsvc.Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
if !l.IP.Is6() {
@ -236,7 +239,7 @@ func (s *v6Server) AddStaticLease(l *Lease) (err error) {
}
// UpdateStaticLease updates IP, hostname of the static lease.
func (s *v6Server) UpdateStaticLease(l *Lease) (err error) {
func (s *v6Server) UpdateStaticLease(l *dhcpsvc.Lease) (err error) {
defer func() {
if err != nil {
err = errors.Annotate(err, "dhcpv6: updating static lease: %w")
@ -267,7 +270,7 @@ func (s *v6Server) UpdateStaticLease(l *Lease) (err error) {
}
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
func (s *v6Server) RemoveStaticLease(l *Lease) (err error) {
func (s *v6Server) RemoveStaticLease(l *dhcpsvc.Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()
if !l.IP.Is6() {
@ -292,7 +295,7 @@ func (s *v6Server) RemoveStaticLease(l *Lease) (err error) {
}
// Add a lease
func (s *v6Server) addLease(l *Lease) {
func (s *v6Server) addLease(l *dhcpsvc.Lease) {
s.leases = append(s.leases, l)
ip := l.IP.As16()
s.ipAddrs[ip[15]] = 1
@ -300,7 +303,7 @@ func (s *v6Server) addLease(l *Lease) {
}
// Remove a lease with the same properties
func (s *v6Server) rmLease(lease *Lease) (err error) {
func (s *v6Server) rmLease(lease *dhcpsvc.Lease) (err error) {
for i, l := range s.leases {
if l.IP == lease.IP {
if !bytes.Equal(l.HWAddr, lease.HWAddr) ||
@ -318,7 +321,7 @@ func (s *v6Server) rmLease(lease *Lease) (err error) {
}
// Find lease by MAC.
func (s *v6Server) findLease(mac net.HardwareAddr) (lease *Lease) {
func (s *v6Server) findLease(mac net.HardwareAddr) (lease *dhcpsvc.Lease) {
for i := range s.leases {
if bytes.Equal(mac, s.leases[i].HWAddr) {
return s.leases[i]
@ -356,8 +359,8 @@ func (s *v6Server) findFreeIP() net.IP {
}
// Reserve lease for MAC
func (s *v6Server) reserveLease(mac net.HardwareAddr) *Lease {
l := Lease{
func (s *v6Server) reserveLease(mac net.HardwareAddr) *dhcpsvc.Lease {
l := dhcpsvc.Lease{
HWAddr: make([]byte, len(mac)),
}
@ -390,7 +393,7 @@ func (s *v6Server) reserveLease(mac net.HardwareAddr) *Lease {
return &l
}
func (s *v6Server) commitDynamicLease(l *Lease) {
func (s *v6Server) commitDynamicLease(l *dhcpsvc.Lease) {
l.Expiry = time.Now().Add(s.conf.leaseTime)
s.leasesLock.Lock()
@ -438,7 +441,7 @@ func (s *v6Server) checkSID(msg *dhcpv6.Message) error {
}
// . IAAddress must be equal to the lease's IP
func (s *v6Server) checkIA(msg *dhcpv6.Message, lease *Lease) error {
func (s *v6Server) checkIA(msg *dhcpv6.Message, lease *dhcpsvc.Lease) error {
switch msg.Type() {
case dhcpv6.MessageTypeRequest,
dhcpv6.MessageTypeConfirm,
@ -464,7 +467,7 @@ func (s *v6Server) checkIA(msg *dhcpv6.Message, lease *Lease) error {
}
// Store lease in DB (if necessary) and return lease life time
func (s *v6Server) commitLease(msg *dhcpv6.Message, lease *Lease) time.Duration {
func (s *v6Server) commitLease(msg *dhcpv6.Message, lease *dhcpsvc.Lease) time.Duration {
lifetime := s.conf.leaseTime
switch msg.Type() {
@ -506,7 +509,7 @@ func (s *v6Server) process(msg *dhcpv6.Message, req, resp dhcpv6.DHCPv6) bool {
return false
}
var lease *Lease
var lease *dhcpsvc.Lease
func() {
s.leasesLock.Lock()
defer s.leasesLock.Unlock()

View file

@ -8,6 +8,7 @@ import (
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/iana"
"github.com/stretchr/testify/assert"
@ -28,7 +29,7 @@ func TestV6_AddRemove_static(t *testing.T) {
require.Empty(t, s.GetLeases(LeasesStatic))
// Add static lease.
l := &Lease{
l := &dhcpsvc.Lease{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
@ -47,7 +48,7 @@ func TestV6_AddRemove_static(t *testing.T) {
assert.True(t, ls[0].IsStatic)
// Try to remove non-existent static lease.
err = s.RemoveStaticLease(&Lease{
err = s.RemoveStaticLease(&dhcpsvc.Lease{
IP: netip.MustParseAddr("2001::2"),
HWAddr: l.HWAddr,
})
@ -72,7 +73,7 @@ func TestV6_AddReplace(t *testing.T) {
require.True(t, ok)
// Add dynamic leases.
dynLeases := []*Lease{{
dynLeases := []*dhcpsvc.Lease{{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0x11, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}, {
@ -84,7 +85,7 @@ func TestV6_AddReplace(t *testing.T) {
s.addLease(l)
}
stLeases := []*Lease{{
stLeases := []*dhcpsvc.Lease{{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0x33, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}, {
@ -126,7 +127,7 @@ func TestV6GetLease(t *testing.T) {
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
l := &Lease{
l := &dhcpsvc.Lease{
IP: netip.MustParseAddr("2001::1"),
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
}
@ -324,7 +325,7 @@ func TestV6_FindMACbyIP(t *testing.T) {
anotherMAC := net.HardwareAddr{0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB}
s := &v6Server{
leases: []*Lease{{
leases: []*dhcpsvc.Lease{{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,
@ -337,7 +338,7 @@ func TestV6_FindMACbyIP(t *testing.T) {
}},
}
s.leases = []*Lease{{
s.leases = []*dhcpsvc.Lease{{
Hostname: staticName,
HWAddr: staticMAC,
IP: staticIP,

View file

@ -43,7 +43,7 @@ func (conf *Config) Validate() (err error) {
case !conf.Enabled:
return nil
case conf.ICMPTimeout < 0:
return fmt.Errorf("icmp timeout %s must be non-negative", conf.ICMPTimeout)
return newMustErr("icmp timeout", "be non-negative", conf.ICMPTimeout)
}
err = netutil.ValidateDomainName(conf.LocalDomainName)
@ -68,9 +68,9 @@ func (conf *Config) Validate() (err error) {
return nil
}
// mustBeErr returns an error that indicates that valName must be as must
// newMustErr returns an error that indicates that valName must be as must
// describes.
func mustBeErr(valName, must string, val fmt.Stringer) (err error) {
func newMustErr(valName, must string, val fmt.Stringer) (err error) {
return fmt.Errorf("%s %s must %s", valName, val, must)
}

View file

@ -10,13 +10,13 @@ import (
"time"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"golang.org/x/exp/slices"
)
// 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.
// TODO(e.burkov): Consider moving it to [agh], since it also may be needed in
// [websvc].
type Lease struct {
// IP is the IP address leased to the client.
IP netip.Addr
@ -34,6 +34,21 @@ type Lease struct {
IsStatic bool
}
// Clone returns a deep copy of l.
func (l *Lease) Clone() (clone *Lease) {
if l == nil {
return nil
}
return &Lease{
Expiry: l.Expiry,
Hostname: l.Hostname,
HWAddr: slices.Clone(l.HWAddr),
IP: l.IP,
IsStatic: l.IsStatic,
}
}
type Interface interface {
agh.ServiceWithConfig[*Config]
@ -57,6 +72,9 @@ type Interface interface {
IPByHost(host string) (ip netip.Addr)
// Leases returns all the active DHCP leases.
//
// 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. It returns an error if the lease is
@ -91,6 +109,9 @@ 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 }
@ -103,9 +124,6 @@ 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{} }
// type check
var _ Interface = Empty{}
// Leases implements the [Interface] interface for Empty.
func (Empty) Leases() (leases []*Lease) { return nil }

View file

@ -25,6 +25,9 @@ type DHCPServer struct {
// interfaces6 is the set of IPv6 interfaces sorted by interface name.
interfaces6 []*iface6
// leases is the set of active DHCP leases.
leases []*Lease
// icmpTimeout is the timeout for checking another DHCP server's presence.
icmpTimeout time.Duration
}
@ -75,3 +78,23 @@ func New(conf *Config) (srv *DHCPServer, err error) {
icmpTimeout: conf.ICMPTimeout,
}, nil
}
// type check
//
// TODO(e.burkov): Uncomment when the [Interface] interface is implemented.
// var _ Interface = (*DHCPServer)(nil)
// Enabled implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Enabled() (ok bool) {
return srv.enabled.Load()
}
// Leases implements the [Interface] interface for *DHCPServer.
func (srv *DHCPServer) Leases() (leases []*Lease) {
leases = make([]*Lease, 0, len(srv.leases))
for _, lease := range srv.leases {
leases = append(leases, lease.Clone())
}
return leases
}

View file

@ -45,15 +45,15 @@ func (conf *IPv4Config) validate() (err error) {
case !conf.Enabled:
return nil
case !conf.GatewayIP.Is4():
return mustBeErr("gateway ip", "be a valid ipv4", conf.GatewayIP)
return newMustErr("gateway ip", "be a valid ipv4", conf.GatewayIP)
case !conf.SubnetMask.Is4():
return mustBeErr("subnet mask", "be a valid ipv4 cidr mask", conf.SubnetMask)
return newMustErr("subnet mask", "be a valid ipv4 cidr mask", conf.SubnetMask)
case !conf.RangeStart.Is4():
return mustBeErr("range start", "be a valid ipv4", conf.RangeStart)
return newMustErr("range start", "be a valid ipv4", conf.RangeStart)
case !conf.RangeEnd.Is4():
return mustBeErr("range end", "be a valid ipv4", conf.RangeEnd)
return newMustErr("range end", "be a valid ipv4", conf.RangeEnd)
case conf.LeaseDuration <= 0:
return mustBeErr("lease duration", "be less than %d", conf.LeaseDuration)
return newMustErr("lease duration", "be less than %d", conf.LeaseDuration)
default:
return nil
}

View file

@ -314,7 +314,7 @@ func TestClientsAddExisting(t *testing.T) {
clients.dhcp = dhcpServer
err = dhcpServer.AddStaticLease(&dhcpd.Lease{
err = dhcpServer.AddStaticLease(&dhcpsvc.Lease{
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
IP: ip,
Hostname: "testhost",