summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Markwalder <tmark@isc.org>2019-12-20 11:40:04 -0500
committerThomas Markwalder <tmark@isc.org>2019-12-20 11:40:04 -0500
commita61884c380ba84d584dbff72af89c40494e1f3c1 (patch)
treeaf3f07931620c0eafbb5ca213a241285d9c6518f
parentb68402e0bbb4943b84149cc21cbb3ec20cc07318 (diff)
downloadisc-dhcp-a61884c380ba84d584dbff72af89c40494e1f3c1.tar.gz
[#71] Add dhrelay unit tests files
new file: tests/Atffile new file: tests/Makefile.am new file: tests/relay_unittests.c
-rw-r--r--relay/tests/Atffile5
-rw-r--r--relay/tests/Makefile.am49
-rw-r--r--relay/tests/relay_unittests.c407
3 files changed, 461 insertions, 0 deletions
diff --git a/relay/tests/Atffile b/relay/tests/Atffile
new file mode 100644
index 00000000..0572f318
--- /dev/null
+++ b/relay/tests/Atffile
@@ -0,0 +1,5 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = dhcrelay
+
+tp-glob: *_unittests
diff --git a/relay/tests/Makefile.am b/relay/tests/Makefile.am
new file mode 100644
index 00000000..0da6e292
--- /dev/null
+++ b/relay/tests/Makefile.am
@@ -0,0 +1,49 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = $(ATF_CFLAGS) -DUNIT_TEST -I$(top_srcdir)/includes
+AM_CPPFLAGS += -I@BINDDIR@/include -I$(top_srcdir)
+AM_CPPFLAGS += -DLOCALSTATEDIR='"."'
+
+EXTRA_DIST = Atffile Kyuafile
+
+# for autotools debugging only
+info:
+ @echo "ATF_CFLAGS=$(ATF_CFLAGS)"
+ @echo "ATF_LDFLAGS=$(ATF_LDFLAGS)"
+ @echo "ATF_LIBS=$(ATF_LIBS)"
+
+DHCPSRC = ../dhcrelay.c
+
+DHCPLIBS = $(top_builddir)/common/libdhcp.@A@ \
+ $(top_builddir)/omapip/libomapi.@A@ \
+ @BINDLIBIRSDIR@/libirs.@A@ \
+ @BINDLIBDNSDIR@/libdns.@A@ \
+ @BINDLIBISCCFGDIR@/libisccfg.@A@ \
+ @BINDLIBISCDIR@/libisc.@A@
+
+ATF_TESTS =
+if HAVE_ATF
+
+ATF_TESTS += relay_unittests
+
+relay_unittests_SOURCES = $(DHCPSRC)
+relay_unittests_SOURCES += relay_unittests.c
+
+relay_unittests_LDADD = $(ATF_LDFLAGS)
+relay_unittests_LDADD += $(DHCPLIBS)
+
+check: $(ATF_TESTS)
+ @if test $(top_srcdir) != ${top_builddir}; then \
+ cp $(top_srcdir)/relay/tests/Atffile Atffile; \
+ cp $(top_srcdir)/relay/tests/Kyuafile Kyuafile; \
+ fi
+ sh ${top_builddir}/tests/unittest.sh
+
+distclean-local:
+ @if test $(top_srcdir) != ${top_builddir}; then \
+ rm -f Atffile Kyuafile;
+ fi
+
+endif
+
+check_PROGRAMS = $(ATF_TESTS)
diff --git a/relay/tests/relay_unittests.c b/relay/tests/relay_unittests.c
new file mode 100644
index 00000000..59320981
--- /dev/null
+++ b/relay/tests/relay_unittests.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2019 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "config.h"
+#include <atf-c.h>
+#include <omapip/omapip_p.h>
+#include "dhcpd.h"
+
+/* @brief Externs for dhcrelay.c functions under test */
+extern int add_agent_options;
+extern int add_relay_agent_options(struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ struct in_addr);
+
+extern int find_interface_by_agent_option(struct dhcp_packet *,
+ struct interface_info **,
+ u_int8_t *, int);
+
+extern int strip_relay_agent_options(struct interface_info *,
+ struct interface_info **,
+ struct dhcp_packet *, unsigned);
+
+/* @brief Add the given option data to a DHCPv4 packet
+*
+* It first fills the packet.options buffer with the given pad character.
+* Next it copies the DHCP magic cookie value into the beginning of the
+* options buffer. Finally it appends the given data after the cookie.
+*
+* @param packet pointer to the packet
+* @param data pointer to the option data to copy into the packet's options
+* buffer
+* @param len length of the option data to copy
+* @param pad byte value with which to initialize the packet's options buffer
+*
+* @return returns the new length of the packet
+*/
+unsigned set_packet_options(struct dhcp_packet *packet,
+ unsigned char* data, unsigned len,
+ unsigned char pad) {
+ unsigned new_len;
+ memset(packet->options, pad, DHCP_MAX_OPTION_LEN);
+
+ // Add the COOKIE
+ new_len = 4;
+ memcpy(packet->options, DHCP_OPTIONS_COOKIE, new_len);
+
+ new_len += len;
+ if (new_len > DHCP_MAX_OPTION_LEN) {
+ return(0);
+ }
+
+ memcpy(&packet->options[4], data, len);
+ return(new_len + DHCP_FIXED_NON_UDP);
+}
+
+/* @brief Checks two sets of option data for equalit
+*
+* It constructs the expected options content by creating an options buffer
+* filled with the pad value. Next it copies the DHCP magic cookie value
+* into the beginning of the buffer and then appends the expected data after
+* the cookie. It the compares this buffer to the actual buffer passed in
+* for equality and returns the result.
+*
+* @param actual_options pointer to the packet::options to be checked
+* @param expected_data pointer to the expected options data (everything after
+* the DHCP cookie)
+* @param data_len length of the expected options data
+* @param pad byte value with which to initialize the packet's options buffer
+*
+* @return zero it the sets of data match, non-zero otherwise
+*/
+int check_with_pad(unsigned char* actual_options,
+ unsigned char *expected_data,
+ unsigned data_len, unsigned char pad) {
+
+ unsigned char exp_options[DHCP_MAX_OPTION_LEN];
+ unsigned new_len;
+
+ memset(exp_options, pad, DHCP_MAX_OPTION_LEN);
+ new_len = 4;
+ memcpy(exp_options, DHCP_OPTIONS_COOKIE, new_len);
+
+ new_len += data_len;
+ if (new_len > DHCP_MAX_OPTION_LEN) {
+ return(-1);
+ }
+
+ memcpy(&exp_options[4], expected_data, data_len);
+ return (memcmp(actual_options, exp_options, DHCP_MAX_OPTION_LEN));
+}
+
+ATF_TC(strip_relay_agent_options_test);
+
+ATF_TC_HEAD(strip_relay_agent_options_test, tc) {
+ atf_tc_set_md_var(tc, "descr", "tests strip_relay-agent_options");
+}
+
+/* This Test exercises strip_relay_agent_options() function */
+ATF_TC_BODY(strip_relay_agent_options_test, tc) {
+
+ struct interface_info ifaces;
+ struct interface_info *matched;
+ struct dhcp_packet packet;
+ unsigned len;
+ int ret;
+
+ memset(&ifaces, 0x0, sizeof(ifaces));
+ matched = 0;
+ memset(&packet, 0x0, sizeof(packet));
+ len = 0;
+
+ /* Make sure an empty packet is harmless */
+ ret = strip_relay_agent_options(&ifaces, &matched, &packet, len);
+ if (ret != 0) {
+ atf_tc_fail("empty packet failed");
+ }
+
+ {
+ /*
+ * Uses valid input option data to verify that:
+ * - When add_agent_options is false, the output option data is the
+ * the same as the input option data (i.e. nothing removed)
+ * - When add_agent_options is true, the relay agent option has
+ * been removed from the output option data
+ * - When add_agent_options is true, 0 length relay agent option has
+ * been removed from the output option data
+ *
+ */
+
+ unsigned char input_data[] = {
+ 0x35, 0x00, /* DHCP_TYPE = DISCOVER */
+ 0x52, 0x00, /* Relay Agent Option, len = 0 */
+
+ 0x52, 0x08, 0x01, 0x06, 0x65, /* Relay Agent Option, len = 8 */
+ 0x6e, 0x70, 0x30, 0x73, 0x4f,
+
+ 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */
+ 0x43, 0x00 /* Opt 0x43, len = 0 */
+ };
+
+ unsigned char input_data2[] = {
+ 0x35, 0x00, /* DHCP_TYPE = DISCOVER */
+ 0x52, 0x00, /* Relay Agent Option, len = 0 */
+ 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */
+ 0x43, 0x00 /* Opt 0x43, len = 0 */
+ };
+
+ unsigned char output_data[] = {
+ 0x35, 0x00, /* DHCP_TYPE = DISCOVER */
+ 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */
+ 0x43, 0x00 /* Opt 0x43, len = 0 */
+ };
+
+ unsigned char pad = 0x0;
+ len = set_packet_options(&packet, input_data, sizeof(input_data), pad);
+ if (len == 0) {
+ atf_tc_fail("input_data: set_packet_options failed");
+ }
+
+ /* When add_agent_options = 0, no change should occur */
+ add_agent_options = 0;
+ ret = strip_relay_agent_options(&ifaces, &matched, &packet, len);
+ if (ret != len) {
+ atf_tc_fail("expected unchanged len %d, returned %d", len, ret);
+ }
+
+ if (check_with_pad(packet.options, input_data, sizeof(input_data),
+ pad) != 0) {
+ atf_tc_fail("expected unchanged data, does not match");
+ }
+
+ /* When add_agent_options = 1, it should remove the eight byte */
+ /* relay agent option. */
+ add_agent_options = 1;
+
+ /* Because the option data is less is small, the packet should be */
+ /* padded out to BOOTP_MIN_LEN */
+ ret = strip_relay_agent_options(&ifaces, &matched, &packet, len);
+ if (ret != BOOTP_MIN_LEN) {
+ atf_tc_fail("input_data: len of %d, returned %d",
+ BOOTP_MIN_LEN, ret);
+ }
+
+ if (check_with_pad(packet.options, output_data, sizeof(output_data),
+ pad) != 0) {
+ atf_tc_fail("input_data: expected data does not match");
+ }
+
+ // Now let's repeat it with a relay agent option 0 bytes in length.
+ len = set_packet_options(&packet, input_data2, sizeof(input_data2), pad);
+ if (len == 0) {
+ atf_tc_fail("input_data2 set_packet_options failed");
+ }
+
+ /* Because the option data is less is small, the packet should be */
+ /* padded out to BOOTP_MIN_LEN */
+ ret = strip_relay_agent_options(&ifaces, &matched, &packet, len);
+ if (ret != BOOTP_MIN_LEN) {
+ atf_tc_fail("input_data2: len of %d, returned %d",
+ BOOTP_MIN_LEN, ret);
+ }
+
+ if (check_with_pad(packet.options, output_data, sizeof(output_data),
+ pad) != 0) {
+ atf_tc_fail("input_data2: expected output does not match");
+ }
+ }
+
+ {
+ /* Verify that oversized pack filled with long options does not */
+ /* cause overrun */
+
+ /* We borrowed this union from discover.c:got_one() */
+ union {
+ unsigned char packbuf [4095]; /* Packet input buffer.
+ Must be as large as largest
+ possible MTU. */
+ struct dhcp_packet packet;
+ } u;
+
+ unsigned char input_data[] = {
+ 0x35, 0x00, // DHCP_TYPE = DISCOVER
+ 0x52, 0x00, // Relay Agent Option, len = 0
+ 0x42, 0x02, 0x00, 0x10, // Opt 0x42, len = 2
+ 0x43, 0x00 // Opt 0x43, len = 0
+ };
+
+ /* We'll pad it 0xFE, that way wherever we hit for "length" we'll */
+ /* have length of 254. Increasing the odds we'll push over the end. */
+ unsigned char pad = 0xFE;
+ memset(u.packbuf, pad, 4095);
+
+ len = set_packet_options(&u.packet, input_data, sizeof(input_data), pad);
+ if (len == 0) {
+ atf_tc_fail("overrun: set_packet_options failed");
+ }
+
+ // With add_agent_options = 1, it should remove the two byte
+ // relay agent option.
+ add_agent_options = 1;
+
+ ret = strip_relay_agent_options(&ifaces, &matched, &u.packet, 4095);
+ if (ret != 0) {
+ atf_tc_fail("overrun expected return len = 0, we got %d", ret);
+ }
+ }
+}
+
+ATF_TC(add_relay_agent_options_test);
+
+ATF_TC_HEAD(add_relay_agent_options_test, tc) {
+ atf_tc_set_md_var(tc, "descr", "tests agent_relay-agent_options");
+}
+
+/* This Test exercises add_relay_agent_options() function */
+ATF_TC_BODY(add_relay_agent_options_test, tc) {
+
+ struct interface_info ifc;
+ struct dhcp_packet packet;
+ unsigned len;
+ int ret;
+ struct in_addr giaddr;
+
+ giaddr.s_addr = inet_addr("192.0.1.1");
+
+ u_int8_t circuit_id[] = { 0x01,0x02,0x03,0x04,0x05,0x06 };
+ u_int8_t remote_id[] = { 0x11,0x22,0x33,0x44,0x55,0x66 };
+
+ memset(&ifc, 0x0, sizeof(ifc));
+ ifc.circuit_id = circuit_id;
+ ifc.circuit_id_len = sizeof(circuit_id);
+ ifc.remote_id = remote_id;
+ ifc.remote_id_len = sizeof(remote_id);
+
+ memset(&packet, 0x0, sizeof(packet));
+ len = 0;
+
+ /* Make sure an empty packet is harmless */
+ ret = add_relay_agent_options(&ifc, &packet, len, giaddr);
+ if (ret != 0) {
+ atf_tc_fail("empty packet failed");
+ }
+
+ {
+ /*
+ * Uses valid input option data to verify that:
+ * - When add_agent_options is false, the output option data is the
+ * the same as the input option data (i.e. nothing removed)
+ * - When add_agent_options is true, the relay agent option has
+ * been removed from the output option data
+ * - When add_agent_options is true, 0 length relay agent option has
+ * been removed from the output option data
+ *
+ */
+
+ unsigned char input_data[] = {
+ 0x35, 0x00, /* DHCP_TYPE = DISCOVER */
+ 0x52, 0x00, /* Relay Agent Option, len = 0 */
+
+ 0x52, 0x08, 0x01, 0x06, 0x65, /* Relay Agent Option, len = 8 */
+ 0x6e, 0x70, 0x30, 0x73, 0x4f,
+
+ 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */
+ 0x43, 0x00 /* Opt 0x43, len = 0 */
+ };
+
+ unsigned char input_data2[] = {
+ 0x35, 0x00, /* DHCP_TYPE = DISCOVER */
+ 0x52, 0x00, /* Relay Agent Option, len = 0 */
+ 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */
+ 0x43, 0x00 /* Opt 0x43, len = 0 */
+ };
+
+ unsigned char output_data[] = {
+ 0x35, 0x00, /* DHCP_TYPE = DISCOVER */
+ 0x42, 0x02, 0x00, 0x10, /* Opt 0x42, len = 2 */
+ 0x43, 0x00, /* Opt 0x43, len = 0 */
+ 0x52, 0x10, /* Relay Agent, len = 16 */
+ 0x1, 0x6, /* Circuit id, len = 6 */
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6,
+ 0x2, 0x6, /* Remete id, len = 6 */
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xff
+ };
+
+ unsigned char pad = 0x0;
+ len = set_packet_options(&packet, input_data, sizeof(input_data), pad);
+ if (len == 0) {
+ atf_tc_fail("input_data: set_packet_options failed");
+ }
+
+ /* When add_agent_options = 0, no change should occur */
+ add_agent_options = 0;
+ ret = add_relay_agent_options(&ifc, &packet, len, giaddr);
+ if (ret != len) {
+ atf_tc_fail("expected unchanged len %d, returned %d", len, ret);
+ }
+
+ if (check_with_pad(packet.options, input_data, sizeof(input_data),
+ pad) != 0) {
+ atf_tc_fail("expected unchanged data, does not match");
+ }
+
+ /* When add_agent_options = 1, it should remove the eight byte */
+ /* relay agent option. */
+ add_agent_options = 1;
+
+ /* Because the option data is less is small, the packet should be */
+ /* padded out to BOOTP_MIN_LEN */
+ ret = add_relay_agent_options(&ifc, &packet, len, giaddr);
+ if (ret != BOOTP_MIN_LEN) {
+ atf_tc_fail("input_data: len of %d, returned %d",
+ BOOTP_MIN_LEN, ret);
+ }
+
+ if (check_with_pad(packet.options, output_data, sizeof(output_data),
+ pad) != 0) {
+ atf_tc_fail("input_data: expected data does not match");
+ }
+
+ // Now let's repeat it with a relay agent option 0 bytes in length.
+ len = set_packet_options(&packet, input_data2, sizeof(input_data2),
+ pad);
+ if (len == 0) {
+ atf_tc_fail("input_data2 set_packet_options failed");
+ }
+
+ /* Because the option data is less is small, the packet should be */
+ /* padded out to BOOTP_MIN_LEN */
+ ret = add_relay_agent_options(&ifc, &packet, len, giaddr);
+ if (ret != BOOTP_MIN_LEN) {
+ atf_tc_fail("input_data2: len of %d, returned %d",
+ BOOTP_MIN_LEN, ret);
+ }
+
+ if (check_with_pad(packet.options, output_data, sizeof(output_data),
+ pad) != 0) {
+ atf_tc_fail("input_data2: expected output does not match");
+ }
+ }
+}
+
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, strip_relay_agent_options_test);
+ ATF_TP_ADD_TC(tp, add_relay_agent_options_test);
+
+ return (atf_no_error());
+}