596 lines
14 KiB
Go
596 lines
14 KiB
Go
package dhcpsvc_test
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
|
"github.com/AdguardTeam/golibs/testutil"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// testLocalTLD is a common local TLD for tests.
|
|
const testLocalTLD = "local"
|
|
|
|
// testInterfaceConf is a common set of interface configurations for tests.
|
|
var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{
|
|
"eth0": {
|
|
IPv4: &dhcpsvc.IPv4Config{
|
|
Enabled: true,
|
|
GatewayIP: netip.MustParseAddr("192.168.0.1"),
|
|
SubnetMask: netip.MustParseAddr("255.255.255.0"),
|
|
RangeStart: netip.MustParseAddr("192.168.0.2"),
|
|
RangeEnd: netip.MustParseAddr("192.168.0.254"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
},
|
|
IPv6: &dhcpsvc.IPv6Config{
|
|
Enabled: true,
|
|
RangeStart: netip.MustParseAddr("2001:db8::1"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
RAAllowSLAAC: true,
|
|
RASLAACOnly: true,
|
|
},
|
|
},
|
|
"eth1": {
|
|
IPv4: &dhcpsvc.IPv4Config{
|
|
Enabled: true,
|
|
GatewayIP: netip.MustParseAddr("172.16.0.1"),
|
|
SubnetMask: netip.MustParseAddr("255.255.255.0"),
|
|
RangeStart: netip.MustParseAddr("172.16.0.2"),
|
|
RangeEnd: netip.MustParseAddr("172.16.0.255"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
},
|
|
IPv6: &dhcpsvc.IPv6Config{
|
|
Enabled: true,
|
|
RangeStart: netip.MustParseAddr("2001:db9::1"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
RAAllowSLAAC: true,
|
|
RASLAACOnly: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
// mustParseMAC parses a hardware address from s and requires no errors.
|
|
func mustParseMAC(t require.TestingT, s string) (mac net.HardwareAddr) {
|
|
mac, err := net.ParseMAC(s)
|
|
require.NoError(t, err)
|
|
|
|
return mac
|
|
}
|
|
|
|
func TestNew(t *testing.T) {
|
|
validIPv4Conf := &dhcpsvc.IPv4Config{
|
|
Enabled: true,
|
|
GatewayIP: netip.MustParseAddr("192.168.0.1"),
|
|
SubnetMask: netip.MustParseAddr("255.255.255.0"),
|
|
RangeStart: netip.MustParseAddr("192.168.0.2"),
|
|
RangeEnd: netip.MustParseAddr("192.168.0.254"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
}
|
|
gwInRangeConf := &dhcpsvc.IPv4Config{
|
|
Enabled: true,
|
|
GatewayIP: netip.MustParseAddr("192.168.0.100"),
|
|
SubnetMask: netip.MustParseAddr("255.255.255.0"),
|
|
RangeStart: netip.MustParseAddr("192.168.0.1"),
|
|
RangeEnd: netip.MustParseAddr("192.168.0.254"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
}
|
|
badStartConf := &dhcpsvc.IPv4Config{
|
|
Enabled: true,
|
|
GatewayIP: netip.MustParseAddr("192.168.0.1"),
|
|
SubnetMask: netip.MustParseAddr("255.255.255.0"),
|
|
RangeStart: netip.MustParseAddr("127.0.0.1"),
|
|
RangeEnd: netip.MustParseAddr("192.168.0.254"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
}
|
|
|
|
validIPv6Conf := &dhcpsvc.IPv6Config{
|
|
Enabled: true,
|
|
RangeStart: netip.MustParseAddr("2001:db8::1"),
|
|
LeaseDuration: 1 * time.Hour,
|
|
RAAllowSLAAC: true,
|
|
RASLAACOnly: true,
|
|
}
|
|
|
|
testCases := []struct {
|
|
conf *dhcpsvc.Config
|
|
name string
|
|
wantErrMsg string
|
|
}{{
|
|
conf: &dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
|
"eth0": {
|
|
IPv4: validIPv4Conf,
|
|
IPv6: validIPv6Conf,
|
|
},
|
|
},
|
|
},
|
|
name: "valid",
|
|
wantErrMsg: "",
|
|
}, {
|
|
conf: &dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
|
"eth0": {
|
|
IPv4: &dhcpsvc.IPv4Config{Enabled: false},
|
|
IPv6: &dhcpsvc.IPv6Config{Enabled: false},
|
|
},
|
|
},
|
|
},
|
|
name: "disabled_interfaces",
|
|
wantErrMsg: "",
|
|
}, {
|
|
conf: &dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
|
"eth0": {
|
|
IPv4: gwInRangeConf,
|
|
IPv6: validIPv6Conf,
|
|
},
|
|
},
|
|
},
|
|
name: "gateway_within_range",
|
|
wantErrMsg: `interface "eth0": ipv4: ` +
|
|
`gateway ip 192.168.0.100 in the ip range 192.168.0.1-192.168.0.254`,
|
|
}, {
|
|
conf: &dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
|
"eth0": {
|
|
IPv4: badStartConf,
|
|
IPv6: validIPv6Conf,
|
|
},
|
|
},
|
|
},
|
|
name: "bad_start",
|
|
wantErrMsg: `interface "eth0": ipv4: ` +
|
|
`range start 127.0.0.1 is not within 192.168.0.1/24`,
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := dhcpsvc.New(tc.conf)
|
|
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDHCPServer_AddLease(t *testing.T) {
|
|
srv, err := dhcpsvc.New(&dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: testInterfaceConf,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
const (
|
|
host1 = "host1"
|
|
host2 = "host2"
|
|
host3 = "host3"
|
|
)
|
|
|
|
ip1 := netip.MustParseAddr("192.168.0.2")
|
|
ip2 := netip.MustParseAddr("192.168.0.3")
|
|
ip3 := netip.MustParseAddr("2001:db8::2")
|
|
|
|
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
|
|
mac2 := mustParseMAC(t, "06:05:04:03:02:01")
|
|
mac3 := mustParseMAC(t, "02:03:04:05:06:07")
|
|
|
|
require.NoError(t, srv.AddLease(&dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
IsStatic: true,
|
|
}))
|
|
|
|
testCases := []struct {
|
|
name string
|
|
lease *dhcpsvc.Lease
|
|
wantErrMsg string
|
|
}{{
|
|
name: "outside_range",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host2,
|
|
IP: netip.MustParseAddr("1.2.3.4"),
|
|
HWAddr: mac2,
|
|
},
|
|
wantErrMsg: "adding lease: no interface for ip 1.2.3.4",
|
|
}, {
|
|
name: "duplicate_ip",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host2,
|
|
IP: ip1,
|
|
HWAddr: mac2,
|
|
},
|
|
wantErrMsg: "adding lease: lease for ip " + ip1.String() +
|
|
" already exists",
|
|
}, {
|
|
name: "duplicate_hostname",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: ip2,
|
|
HWAddr: mac2,
|
|
},
|
|
wantErrMsg: "adding lease: lease for hostname " + host1 +
|
|
" already exists",
|
|
}, {
|
|
name: "duplicate_hostname_case",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: strings.ToUpper(host1),
|
|
IP: ip2,
|
|
HWAddr: mac2,
|
|
},
|
|
wantErrMsg: "adding lease: lease for hostname " +
|
|
strings.ToUpper(host1) + " already exists",
|
|
}, {
|
|
name: "duplicate_mac",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host2,
|
|
IP: ip2,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "adding lease: lease for mac " + mac1.String() +
|
|
" already exists",
|
|
}, {
|
|
name: "valid",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host2,
|
|
IP: ip2,
|
|
HWAddr: mac2,
|
|
},
|
|
wantErrMsg: "",
|
|
}, {
|
|
name: "valid_v6",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host3,
|
|
IP: ip3,
|
|
HWAddr: mac3,
|
|
},
|
|
wantErrMsg: "",
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.AddLease(tc.lease))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDHCPServer_index(t *testing.T) {
|
|
srv, err := dhcpsvc.New(&dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: testInterfaceConf,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
const (
|
|
host1 = "host1"
|
|
host2 = "host2"
|
|
host3 = "host3"
|
|
host4 = "host4"
|
|
host5 = "host5"
|
|
)
|
|
|
|
ip1 := netip.MustParseAddr("192.168.0.2")
|
|
ip2 := netip.MustParseAddr("192.168.0.3")
|
|
ip3 := netip.MustParseAddr("172.16.0.3")
|
|
ip4 := netip.MustParseAddr("172.16.0.4")
|
|
|
|
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
|
|
mac2 := mustParseMAC(t, "06:05:04:03:02:01")
|
|
mac3 := mustParseMAC(t, "02:03:04:05:06:07")
|
|
|
|
leases := []*dhcpsvc.Lease{{
|
|
Hostname: host1,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: host2,
|
|
IP: ip2,
|
|
HWAddr: mac2,
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: host3,
|
|
IP: ip3,
|
|
HWAddr: mac3,
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: host4,
|
|
IP: ip4,
|
|
HWAddr: mac1,
|
|
IsStatic: true,
|
|
}}
|
|
for _, l := range leases {
|
|
require.NoError(t, srv.AddLease(l))
|
|
}
|
|
|
|
t.Run("ip_idx", func(t *testing.T) {
|
|
assert.Equal(t, ip1, srv.IPByHost(host1))
|
|
assert.Equal(t, ip2, srv.IPByHost(host2))
|
|
assert.Equal(t, ip3, srv.IPByHost(host3))
|
|
assert.Equal(t, ip4, srv.IPByHost(host4))
|
|
assert.Equal(t, netip.Addr{}, srv.IPByHost(host5))
|
|
})
|
|
|
|
t.Run("name_idx", func(t *testing.T) {
|
|
assert.Equal(t, host1, srv.HostByIP(ip1))
|
|
assert.Equal(t, host2, srv.HostByIP(ip2))
|
|
assert.Equal(t, host3, srv.HostByIP(ip3))
|
|
assert.Equal(t, host4, srv.HostByIP(ip4))
|
|
assert.Equal(t, "", srv.HostByIP(netip.Addr{}))
|
|
})
|
|
|
|
t.Run("mac_idx", func(t *testing.T) {
|
|
assert.Equal(t, mac1, srv.MACByIP(ip1))
|
|
assert.Equal(t, mac2, srv.MACByIP(ip2))
|
|
assert.Equal(t, mac3, srv.MACByIP(ip3))
|
|
assert.Equal(t, mac1, srv.MACByIP(ip4))
|
|
assert.Nil(t, srv.MACByIP(netip.Addr{}))
|
|
})
|
|
}
|
|
|
|
func TestDHCPServer_UpdateStaticLease(t *testing.T) {
|
|
srv, err := dhcpsvc.New(&dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: testInterfaceConf,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
const (
|
|
host1 = "host1"
|
|
host2 = "host2"
|
|
host3 = "host3"
|
|
host4 = "host4"
|
|
host5 = "host5"
|
|
host6 = "host6"
|
|
)
|
|
|
|
ip1 := netip.MustParseAddr("192.168.0.2")
|
|
ip2 := netip.MustParseAddr("192.168.0.3")
|
|
ip3 := netip.MustParseAddr("192.168.0.4")
|
|
ip4 := netip.MustParseAddr("2001:db8::2")
|
|
ip5 := netip.MustParseAddr("2001:db8::3")
|
|
|
|
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
|
|
mac2 := mustParseMAC(t, "01:02:03:04:05:07")
|
|
mac3 := mustParseMAC(t, "06:05:04:03:02:01")
|
|
mac4 := mustParseMAC(t, "06:05:04:03:02:02")
|
|
|
|
leases := []*dhcpsvc.Lease{{
|
|
Hostname: host1,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: host2,
|
|
IP: ip2,
|
|
HWAddr: mac2,
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: host4,
|
|
IP: ip4,
|
|
HWAddr: mac4,
|
|
IsStatic: true,
|
|
}}
|
|
for _, l := range leases {
|
|
require.NoError(t, srv.AddLease(l))
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
lease *dhcpsvc.Lease
|
|
wantErrMsg string
|
|
}{{
|
|
name: "outside_range",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: netip.MustParseAddr("1.2.3.4"),
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "updating static lease: no interface for ip 1.2.3.4",
|
|
}, {
|
|
name: "not_found",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host3,
|
|
IP: ip3,
|
|
HWAddr: mac3,
|
|
},
|
|
wantErrMsg: "updating static lease: no lease for mac " + mac3.String(),
|
|
}, {
|
|
name: "duplicate_ip",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: ip2,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "updating static lease: lease for ip " + ip2.String() +
|
|
" already exists",
|
|
}, {
|
|
name: "duplicate_hostname",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host2,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "updating static lease: lease for hostname " + host2 +
|
|
" already exists",
|
|
}, {
|
|
name: "duplicate_hostname_case",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: strings.ToUpper(host2),
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "updating static lease: lease for hostname " +
|
|
strings.ToUpper(host2) + " already exists",
|
|
}, {
|
|
name: "valid",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host3,
|
|
IP: ip3,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "",
|
|
}, {
|
|
name: "valid_v6",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host6,
|
|
IP: ip5,
|
|
HWAddr: mac4,
|
|
},
|
|
wantErrMsg: "",
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.UpdateStaticLease(tc.lease))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDHCPServer_RemoveLease(t *testing.T) {
|
|
srv, err := dhcpsvc.New(&dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: testInterfaceConf,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
const (
|
|
host1 = "host1"
|
|
host2 = "host2"
|
|
host3 = "host3"
|
|
)
|
|
|
|
ip1 := netip.MustParseAddr("192.168.0.2")
|
|
ip2 := netip.MustParseAddr("192.168.0.3")
|
|
ip3 := netip.MustParseAddr("2001:db8::2")
|
|
|
|
mac1 := mustParseMAC(t, "01:02:03:04:05:06")
|
|
mac2 := mustParseMAC(t, "02:03:04:05:06:07")
|
|
mac3 := mustParseMAC(t, "06:05:04:03:02:01")
|
|
|
|
leases := []*dhcpsvc.Lease{{
|
|
Hostname: host1,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: host3,
|
|
IP: ip3,
|
|
HWAddr: mac3,
|
|
IsStatic: true,
|
|
}}
|
|
for _, l := range leases {
|
|
require.NoError(t, srv.AddLease(l))
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
lease *dhcpsvc.Lease
|
|
wantErrMsg string
|
|
}{{
|
|
name: "not_found_mac",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: ip1,
|
|
HWAddr: mac2,
|
|
},
|
|
wantErrMsg: "removing lease: no lease for mac " + mac2.String(),
|
|
}, {
|
|
name: "not_found_ip",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: ip2,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "removing lease: no lease for ip " + ip2.String(),
|
|
}, {
|
|
name: "not_found_host",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host2,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "removing lease: no lease for hostname " + host2,
|
|
}, {
|
|
name: "valid",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host1,
|
|
IP: ip1,
|
|
HWAddr: mac1,
|
|
},
|
|
wantErrMsg: "",
|
|
}, {
|
|
name: "valid_v6",
|
|
lease: &dhcpsvc.Lease{
|
|
Hostname: host3,
|
|
IP: ip3,
|
|
HWAddr: mac3,
|
|
},
|
|
wantErrMsg: "",
|
|
}}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.RemoveLease(tc.lease))
|
|
})
|
|
}
|
|
|
|
assert.Empty(t, srv.Leases())
|
|
}
|
|
|
|
func TestDHCPServer_Reset(t *testing.T) {
|
|
srv, err := dhcpsvc.New(&dhcpsvc.Config{
|
|
Enabled: true,
|
|
LocalDomainName: testLocalTLD,
|
|
Interfaces: testInterfaceConf,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
leases := []*dhcpsvc.Lease{{
|
|
Hostname: "host1",
|
|
IP: netip.MustParseAddr("192.168.0.2"),
|
|
HWAddr: mustParseMAC(t, "01:02:03:04:05:06"),
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: "host2",
|
|
IP: netip.MustParseAddr("192.168.0.3"),
|
|
HWAddr: mustParseMAC(t, "06:05:04:03:02:01"),
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: "host3",
|
|
IP: netip.MustParseAddr("2001:db8::2"),
|
|
HWAddr: mustParseMAC(t, "02:03:04:05:06:07"),
|
|
IsStatic: true,
|
|
}, {
|
|
Hostname: "host4",
|
|
IP: netip.MustParseAddr("2001:db8::3"),
|
|
HWAddr: mustParseMAC(t, "06:05:04:03:02:02"),
|
|
IsStatic: true,
|
|
}}
|
|
|
|
for _, l := range leases {
|
|
require.NoError(t, srv.AddLease(l))
|
|
}
|
|
|
|
require.Len(t, srv.Leases(), len(leases))
|
|
|
|
require.NoError(t, srv.Reset())
|
|
|
|
assert.Empty(t, srv.Leases())
|
|
}
|