diff options
Diffstat (limited to 'sbin/dhclient/tests')
-rw-r--r-- | sbin/dhclient/tests/Makefile | 17 | ||||
-rw-r--r-- | sbin/dhclient/tests/Makefile.depend | 18 | ||||
-rw-r--r-- | sbin/dhclient/tests/fake.c | 77 | ||||
-rw-r--r-- | sbin/dhclient/tests/option-domain-search.c | 374 | ||||
-rw-r--r-- | sbin/dhclient/tests/pcp.sh | 196 |
5 files changed, 682 insertions, 0 deletions
diff --git a/sbin/dhclient/tests/Makefile b/sbin/dhclient/tests/Makefile new file mode 100644 index 000000000000..2cca8635fa13 --- /dev/null +++ b/sbin/dhclient/tests/Makefile @@ -0,0 +1,17 @@ +.PATH: ${.CURDIR:H} + +ATF_TESTS_SH= pcp + +# Tests assign a common IP address. +TEST_METADATA.pcp+= is_exclusive=true + +PLAIN_TESTS_C= option-domain-search_test +SRCS.option-domain-search_test= alloc.c convert.c hash.c options.c \ + tables.c parse.c conflex.c tree.c fake.c \ + option-domain-search.c +CFLAGS.option-domain-search_test+= -I${.CURDIR:H} +LIBADD.option-domain-search_test= util + +WARNS?= 2 + +.include <bsd.test.mk> diff --git a/sbin/dhclient/tests/Makefile.depend b/sbin/dhclient/tests/Makefile.depend new file mode 100644 index 000000000000..28acae7b0d55 --- /dev/null +++ b/sbin/dhclient/tests/Makefile.depend @@ -0,0 +1,18 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/arpa \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libutil \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sbin/dhclient/tests/fake.c b/sbin/dhclient/tests/fake.c new file mode 100644 index 000000000000..15ed04dde077 --- /dev/null +++ b/sbin/dhclient/tests/fake.c @@ -0,0 +1,77 @@ + +#include <setjmp.h> +#include <stdarg.h> +#include <stdio.h> + +#include "dhcpd.h" + +extern jmp_buf env; +int warnings_occurred; + +void +error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + longjmp(env, 1); +} + +int +warning(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + /* + * The original warning() would return "ret" here. We do this to + * check warnings explicitly. + */ + longjmp(env, 1); +} + +int +note(const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + return ret; +} + +int +parse_warn(const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + return ret; +} + +void +bootp(struct packet *packet) +{ +} + +void +dhcp(struct packet *packet) +{ +} diff --git a/sbin/dhclient/tests/option-domain-search.c b/sbin/dhclient/tests/option-domain-search.c new file mode 100644 index 000000000000..6a7f92c8775d --- /dev/null +++ b/sbin/dhclient/tests/option-domain-search.c @@ -0,0 +1,374 @@ + +#include <setjmp.h> +#include <stdlib.h> + +#include "dhcpd.h" + +jmp_buf env; + +void expand_domain_search(struct packet *packet); + +void +no_option_present() +{ + int ret; + struct option_data option; + struct packet p; + + option.data = NULL; + option.len = 0; + p.options[DHO_DOMAIN_SEARCH] = option; + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (p.options[DHO_DOMAIN_SEARCH].len != 0 || + p.options[DHO_DOMAIN_SEARCH].data != NULL) + abort(); +} + +void +one_domain_valid() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = "\007example\003org\0"; + char *expected = "example.org."; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 13; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (option->len != strlen(expected) || + strcmp(option->data, expected) != 0) + abort(); + + free(option->data); +} + +void +one_domain_truncated1() +{ + int ret; + struct option_data *option; + struct packet p; + + char *data = "\007example\003org"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 12; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +one_domain_truncated2() +{ + int ret; + struct option_data *option; + struct packet p; + + char *data = "\007ex"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 3; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +two_domains_valid() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = "\007example\003org\0\007example\003com\0"; + char *expected = "example.org. example.com."; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 26; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (option->len != strlen(expected) || + strcmp(option->data, expected) != 0) + abort(); + + free(option->data); +} + +void +two_domains_truncated1() +{ + int ret; + struct option_data *option; + struct packet p; + + char *data = "\007example\003org\0\007example\003com"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 25; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +two_domains_truncated2() +{ + int ret; + struct option_data *option; + struct packet p; + + char *data = "\007example\003org\0\007ex"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 16; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +two_domains_compressed() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = "\007example\003org\0\006foobar\xc0\x08"; + char *expected = "example.org. foobar.org."; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 22; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (option->len != strlen(expected) || + strcmp(option->data, expected) != 0) + abort(); + + free(option->data); +} + +void +two_domains_infloop() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = "\007example\003org\0\006foobar\xc0\x0d"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 22; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +two_domains_forwardptr() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = "\007example\003org\xc0\x0d\006foobar\0"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 22; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +two_domains_truncatedptr() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = "\007example\003org\0\006foobar\xc0"; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 21; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (ret != 1) + abort(); + + free(option->data); +} + +void +multiple_domains_valid() +{ + int ret; + struct packet p; + struct option_data *option; + + char *data = + "\007example\003org\0\002cl\006foobar\003com\0\002fr\xc0\x10"; + + char *expected = "example.org. cl.foobar.com. fr.foobar.com."; + + option = &p.options[DHO_DOMAIN_SEARCH]; + option->len = 33; + option->data = malloc(option->len); + memcpy(option->data, data, option->len); + + ret = setjmp(env); + if (ret == 0) + expand_domain_search(&p); + + if (option->len != strlen(expected) || + strcmp(option->data, expected) != 0) + abort(); + + free(option->data); +} + +static +void +parse_date_helper(const char *string, time_t timestamp) +{ + int ret = 0; + FILE *file = NULL; + time_t ts; + + file = fopen("/tmp/dhclient.test", "w"); + if (!file) + abort(); + + ret = fwrite(string, strlen(string), 1, file); + if (ret <= 0) + abort(); + + fclose(file); + + file = fopen("/tmp/dhclient.test", "r"); + if (!file) + abort(); + + new_parse("test"); + ts = parse_date(file); + if (ts != timestamp) + abort(); + + fclose(file); +} + +void +parse_date_valid(void) +{ + int ret; + + ret = setjmp(env); + if (ret != 0) + abort(); + + parse_date_helper(" 2 2024/7/2 20:25:50;\n", 1719951950); +#ifndef __i386__ + parse_date_helper(" 1 2091/7/2 20:25:50;\n", 3834246350); +#endif +} + +int +main(int argc, char *argv[]) +{ + + no_option_present(); + + one_domain_valid(); + one_domain_truncated1(); + one_domain_truncated2(); + + two_domains_valid(); + two_domains_truncated1(); + two_domains_truncated2(); + + two_domains_compressed(); + two_domains_infloop(); + two_domains_forwardptr(); + two_domains_truncatedptr(); + + multiple_domains_valid(); + + parse_date_valid(); + + return (0); +} diff --git a/sbin/dhclient/tests/pcp.sh b/sbin/dhclient/tests/pcp.sh new file mode 100644 index 000000000000..4875a10a10ce --- /dev/null +++ b/sbin/dhclient/tests/pcp.sh @@ -0,0 +1,196 @@ +#!/usr/bin/env atf-sh +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright 2022 John-Mark Gurney +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# Run the tests: +# make WITH_TESTS=yes -j 4 all install && kyua test -k /usr/tests/Kyuafile sbin/dhclient/pcp +# +# Output last run: +# kyua report --verbose -r $(ls -tr ~/.kyua/store/results.*.db | tail -n 1) + +. $(atf_get_srcdir)/../../sys/common/vnet.subr + +generic_dhcp_cleanup() +{ + + # clean up programs + kill $(cat dhclient.test.pid) $(cat dhcpd.pid) + + # clean up files + rm -f dhclient.dhcpd.conf lease.dhclient.test dhclient.test.pid + + vnet_cleanup +} + +atf_test_case normal cleanup +normal_head() +{ + atf_set descr 'test dhclient against a server' + atf_set require.user root +} + +normal_body() +{ + dhcpd=$(which dhcpd) + + if ! [ -x "$dhcpd" ]; then + atf_skip "ISC dhcp server (isc-dhcp44-server) not installed" + fi + + vnet_init + + epair=$(vnet_mkepair) + + vnet_mkjail dhclient_normal_test ${epair}b + + # Set IP on server iface + ifconfig ${epair}a 192.0.2.2/24 up + + # Create dhcp server config + cat > dhclient.dhcpd.conf << EOF +default-lease-time 36000; +max-lease-time 86400; +authoritative; +subnet 192.0.2.0 netmask 255.255.255.0 { + range 192.0.2.10 192.0.2.10; + option routers 192.0.2.2; + option domain-name-servers 192.0.2.2; +} +EOF + + # Start dhcp server + touch dhcpd.leases.conf + atf_check -e ignore ${dhcpd} -cf ./dhclient.dhcpd.conf -lf ./dhcpd.leases.conf -pf ./dhcpd.pid ${epair}a + + # Expect that we get an IP assigned + atf_check -e match:'DHCPACK from 192.0.2.2' jexec dhclient_normal_test dhclient -c /dev/null -l ./lease.dhclient.test -p ./dhclient.test.pid ${epair}b + + # And it's the correct one + atf_check -o match:'inet 192.0.2.10' jexec dhclient_normal_test ifconfig ${epair}b + +} + +normal_cleanup() +{ + + generic_dhcp_cleanup +} + +atf_test_case pcp cleanup +pcp_head() +{ + atf_set descr 'test dhclient on pcp interface' + atf_set require.user root +} + +pcp_body() +{ + dhcpd=$(which dhcpd) + + if ! [ -x "$dhcpd" ]; then + atf_skip "ISC dhcp server (isc-dhcp44-server) not installed" + fi + + vnet_init + + epair=$(vnet_mkepair) + + # Server side needs to be up to pass packets + ifconfig ${epair}a up + + # Make sure necessary netgraph modules are loaded + kldstat -q -n ng_ether || kldload ng_ether + kldstat -q -n ng_iface || kldload ng_iface + kldstat -q -n ng_vlan || kldload ng_vlan + + # create vlan, and attach epair to it (has incoming/outgoing vlan + # 0 tagged frames) + ngctl mkpeer ${epair}a: vlan lower downstream + + # create new interface on other side of vlan (untagged/pcp) + ngctl mkpeer ${epair}a:lower. eiface vlan0 ether + + # get the interface created + ngiface=$(ngctl show ${epair}a:lower.vlan0 | head -n 1 | awk '{ print $2}') + + # schedule it for clean up + echo ${ngiface} >> ngctl.shutdown + + # set the filter on it + ngctl msg ${epair}a:lower. 'addfilter { vlan=0 hook="vlan0" }' + + vnet_mkjail dhclient_pcp_test ${epair}b + + # Set IP on server iface + ifconfig ${ngiface} up 192.0.2.2/24 + + # Set pcp in jail + jexec dhclient_pcp_test ifconfig ${epair}b pcp 0 up + + # Create dhcp server config + cat > dhclient.dhcpd.conf << EOF +default-lease-time 36000; +max-lease-time 86400; +authoritative; +subnet 192.0.2.0 netmask 255.255.255.0 { + range 192.0.2.10 192.0.2.10; + option routers 192.0.2.2; + option domain-name-servers 192.0.2.2; +} +EOF + + # Start dhcp server + touch dhcpd.leases.conf + atf_check -e ignore ${dhcpd} -cf ./dhclient.dhcpd.conf -lf ./dhcpd.leases.conf -pf ./dhcpd.pid ${ngiface} + + # Expect that we get an IP assigned + atf_check -e match:'DHCPACK from 192.0.2.2' jexec dhclient_pcp_test dhclient -c /dev/null -l ./lease.dhclient.test -p ./dhclient.test.pid ${epair}b + + # And it's the correct one + atf_check -o match:'inet 192.0.2.10' jexec dhclient_pcp_test ifconfig ${epair}b +} + +pcp_cleanup() +{ + + generic_dhcp_cleanup + + # Clean up netgraph nodes + for i in $(cat ngctl.shutdown); do + ngctl shutdown ${i}: + done + rm -f ngctl.shutdown +} + +atf_init_test_cases() +{ + atf_add_test_case normal + atf_add_test_case pcp +} + |