package dhcpsvc_test import ( "net/netip" "path/filepath" "testing" "time" "github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc" "github.com/AdguardTeam/golibs/testutil" ) func TestConfig_Validate(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, } leasesPath := filepath.Join(t.TempDir(), "leases.json") testCases := []struct { name string conf *dhcpsvc.Config wantErrMsg string }{{ name: "nil_config", conf: nil, wantErrMsg: "no value", }, { name: "disabled", conf: &dhcpsvc.Config{}, wantErrMsg: "", }, { name: "empty", conf: &dhcpsvc.Config{ Enabled: true, Interfaces: testInterfaceConf, DBFilePath: leasesPath, }, wantErrMsg: `LocalDomainName: bad domain name "": domain name is empty`, }, { conf: &dhcpsvc.Config{ Enabled: true, LocalDomainName: testLocalTLD, Interfaces: nil, DBFilePath: leasesPath, }, name: "no_interfaces", wantErrMsg: "interfaces: empty value", }, { conf: &dhcpsvc.Config{ Enabled: true, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": nil, }, DBFilePath: leasesPath, }, name: "nil_interface", wantErrMsg: `eth0: no value`, }, { conf: &dhcpsvc.Config{ Enabled: true, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": { IPv4: nil, IPv6: &dhcpsvc.IPv6Config{Enabled: false}, }, }, DBFilePath: leasesPath, }, name: "nil_ipv4", wantErrMsg: `eth0: ipv4: no value`, }, { conf: &dhcpsvc.Config{ Enabled: true, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": { IPv4: &dhcpsvc.IPv4Config{Enabled: false}, IPv6: nil, }, }, DBFilePath: leasesPath, }, name: "nil_ipv6", wantErrMsg: `eth0: ipv6: no value`, }, { conf: &dhcpsvc.Config{ Enabled: true, Logger: discardLog, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": { IPv4: validIPv4Conf, IPv6: validIPv6Conf, }, }, DBFilePath: leasesPath, }, name: "valid", wantErrMsg: "", }, { conf: &dhcpsvc.Config{ Enabled: true, Logger: discardLog, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": { IPv4: &dhcpsvc.IPv4Config{Enabled: false}, IPv6: &dhcpsvc.IPv6Config{Enabled: false}, }, }, DBFilePath: leasesPath, }, name: "disabled_interfaces", wantErrMsg: "", }, { conf: &dhcpsvc.Config{ Enabled: true, Logger: discardLog, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": { IPv4: gwInRangeConf, IPv6: validIPv6Conf, }, }, DBFilePath: leasesPath, }, name: "gateway_within_range", wantErrMsg: "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, Logger: discardLog, LocalDomainName: testLocalTLD, Interfaces: map[string]*dhcpsvc.InterfaceConfig{ "eth0": { IPv4: badStartConf, IPv6: validIPv6Conf, }, }, DBFilePath: leasesPath, }, name: "bad_start", wantErrMsg: "eth0: ipv4: range start 127.0.0.1 is not within 192.168.0.1/24" + "\n" + "gateway ip 192.168.0.1 in the ip range 127.0.0.1-192.168.0.254", }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { testutil.AssertErrorMsg(t, tc.wantErrMsg, tc.conf.Validate()) }) } }