Skip to content

Commit

Permalink
Pull request 2014: 4923 gopacket dhcp vol.3
Browse files Browse the repository at this point in the history
Merge in DNS/adguard-home from 4923-gopacket-dhcp-vol.3 to master

Updates #4923.

Squashed commit of the following:

commit 1a09c43
Merge: 95bcf07 c3f141a
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Sep 28 19:38:57 2023 +0300

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

commit 95bcf07
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Sep 28 13:19:42 2023 +0300

    dhcpsvc: fix interface to match legacy version

commit 5da513c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Sep 28 12:32:21 2023 +0300

    dhcpsvc: make it build on 32bit

commit 37a9355
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 27 19:39:35 2023 +0300

    dhcpd: fix v6 as well

commit 03b5454
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 27 19:34:17 2023 +0300

    dhcpsvc: imp code, docs

commit 91a0e45
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Sep 22 15:25:58 2023 +0300

    dhcpsvc: imp filing

commit 57c91e1
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Sep 22 15:23:02 2023 +0300

    dhcpsvc: imp code

commit d86be56
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Sep 14 12:24:39 2023 +0300

    dhcpsvc: imp code, docs

commit c9ef290
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Wed Sep 13 17:53:55 2023 +0300

    dhcpsvc: add constructor, validations, tests

commit f2533ed
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Sep 12 23:05:42 2023 +0500

    WIP
  • Loading branch information
EugeneOne1 committed Oct 2, 2023
1 parent c3f141a commit 39aeaf8
Show file tree
Hide file tree
Showing 12 changed files with 863 additions and 61 deletions.
10 changes: 5 additions & 5 deletions internal/dhcpd/v4_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ func (s *v4Server) ResetLeases(leases []*Lease) (err error) {
return nil
}

s.leasesLock.Lock()
defer s.leasesLock.Unlock()

s.leasedOffsets = newBitSet()
s.hostsIndex = make(map[string]*Lease, len(leases))
s.ipIndex = make(map[netip.Addr]*Lease, len(leases))
Expand Down Expand Up @@ -182,16 +185,13 @@ func (s *v4Server) isBlocklisted(l *Lease) (ok bool) {
return false
}

ok = true
for _, b := range l.HWAddr {
if b != 0 {
ok = false

break
return false
}
}

return ok
return true
}

// GetLeases returns the list of current DHCP leases. It is safe for concurrent
Expand Down
3 changes: 3 additions & 0 deletions internal/dhcpd/v6_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ func (s *v6Server) IPByHost(host string) (ip netip.Addr) {
func (s *v6Server) ResetLeases(leases []*Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv6: %w") }()

s.leasesLock.Lock()
defer s.leasesLock.Unlock()

s.leases = nil
for _, l := range leases {
ip := net.IP(l.IP.AsSlice())
Expand Down
98 changes: 52 additions & 46 deletions internal/dhcpsvc/config.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package dhcpsvc

import (
"net/netip"
"fmt"
"time"

"github.com/google/gopacket/layers"
"github.com/AdguardTeam/golibs/netutil"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)

// Config is the configuration for the DHCP service.
Expand Down Expand Up @@ -33,54 +35,58 @@ type InterfaceConfig struct {
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
// Validate returns an error in conf if any.
func (conf *Config) Validate() (err error) {
switch {
case conf == nil:
return errNilConfig
case !conf.Enabled:
return nil
case conf.ICMPTimeout < 0:
return fmt.Errorf("icmp timeout %s must be non-negative", conf.ICMPTimeout)
}

err = netutil.ValidateDomainName(conf.LocalDomainName)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}

if len(conf.Interfaces) == 0 {
return errNoInterfaces
}

ifaces := maps.Keys(conf.Interfaces)
slices.Sort(ifaces)

for _, iface := range ifaces {
if err = conf.Interfaces[iface].validate(); err != nil {
return fmt.Errorf("interface %q: %w", iface, err)
}
}

return nil
}

// 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
// mustBeErr returns an error that indicates that valName must be as must
// describes.
func mustBeErr(valName, must string, val fmt.Stringer) (err error) {
return fmt.Errorf("%s %s must %s", valName, val, must)
}

// LeaseDuration is the TTL of a DHCP lease.
LeaseDuration time.Duration
// validate returns an error in ic, if any.
func (ic *InterfaceConfig) validate() (err error) {
if ic == nil {
return errNilConfig
}

// RASlaacOnly defines whether the DHCP clients should only use SLAAC for
// address assignment.
RASLAACOnly bool
if err = ic.IPv4.validate(); err != nil {
return fmt.Errorf("ipv4: %w", err)
}

// RAAllowSlaac defines whether the DHCP clients may use SLAAC for address
// assignment.
RAAllowSLAAC bool
if err = ic.IPv6.validate(); err != nil {
return fmt.Errorf("ipv6: %w", err)
}

// Enabled is the state of the DHCPv6 service, whether it is enabled or not
// on the specific interface.
Enabled bool
return nil
}
88 changes: 88 additions & 0 deletions internal/dhcpsvc/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package dhcpsvc_test

import (
"testing"

"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
"github.com/AdguardTeam/golibs/testutil"
)

func TestConfig_Validate(t *testing.T) {
testCases := []struct {
name string
conf *dhcpsvc.Config
wantErrMsg string
}{{
name: "nil_config",
conf: nil,
wantErrMsg: "config is nil",
}, {
name: "disabled",
conf: &dhcpsvc.Config{},
wantErrMsg: "",
}, {
name: "empty",
conf: &dhcpsvc.Config{
Enabled: true,
},
wantErrMsg: `bad domain name "": domain name is empty`,
}, {
conf: &dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: nil,
},
name: "no_interfaces",
wantErrMsg: "no interfaces specified",
}, {
conf: &dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: nil,
},
name: "no_interfaces",
wantErrMsg: "no interfaces specified",
}, {
conf: &dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": nil,
},
},
name: "nil_interface",
wantErrMsg: `interface "eth0": config is nil`,
}, {
conf: &dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
IPv4: nil,
IPv6: &dhcpsvc.IPv6Config{Enabled: false},
},
},
},
name: "nil_ipv4",
wantErrMsg: `interface "eth0": ipv4: config is nil`,
}, {
conf: &dhcpsvc.Config{
Enabled: true,
LocalDomainName: testLocalTLD,
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
"eth0": {
IPv4: &dhcpsvc.IPv4Config{Enabled: false},
IPv6: nil,
},
},
},
name: "nil_ipv6",
wantErrMsg: `interface "eth0": ipv6: config is nil`,
}}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
testutil.AssertErrorMsg(t, tc.wantErrMsg, tc.conf.Validate())
})
}
}
19 changes: 9 additions & 10 deletions internal/dhcpsvc/dhcpsvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,17 @@ type Interface interface {
// hostname, either set or generated.
IPByHost(host string) (ip netip.Addr)

// Leases returns all the DHCP leases.
Leases() (leases []*Lease)
// Leases returns all the active DHCP leases.
Leases() (ls []*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)
// UpdateStaticLease changes an existing DHCP lease. It returns an error if
// there is no lease with such hardware addressor if new values are invalid
// or already exist.
UpdateStaticLease(l *Lease) (err error)

// RemoveLease removes an existing DHCP lease. It returns an error if there
// is no lease equal to l.
Expand All @@ -79,16 +80,14 @@ type Interface interface {
type Empty struct{}

// type check
var _ Interface = Empty{}
var _ agh.ServiceWithConfig[*Config] = 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 }

Expand All @@ -113,8 +112,8 @@ 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 }
// UpdateStaticLease implements the [Interface] interface for Empty.
func (Empty) UpdateStaticLease(_ *Lease) (err error) { return nil }

// RemoveLease implements the [Interface] interface for Empty.
func (Empty) RemoveLease(_ *Lease) (err error) { return nil }
Expand Down
11 changes: 11 additions & 0 deletions internal/dhcpsvc/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dhcpsvc

import "github.com/AdguardTeam/golibs/errors"

const (
// errNilConfig is returned when a nil config met.
errNilConfig errors.Error = "config is nil"

// errNoInterfaces is returned when no interfaces found in configuration.
errNoInterfaces errors.Error = "no interfaces specified"
)
Loading

0 comments on commit 39aeaf8

Please sign in to comment.