summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/README.md23
-rw-r--r--tests/kernel-module-install/test.sh31
-rw-r--r--tests/unit/dhcpv6_safeguards_test.c69
-rw-r--r--tests/unit/run_safeguards.sh6
4 files changed, 117 insertions, 12 deletions
diff --git a/tests/README.md b/tests/README.md
index 870cf8c8..44bae6a4 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -2,6 +2,11 @@
These tests are done for Ubuntu and Debian distros. Please use latest stable Debian or Ubuntu to run the tests.
+The commands in this guide require CMake/CTest 3.20 or newer, including
+`ctest --test-dir`. The standalone kernel-module installation script requires
+CMake 3.15 or newer for `cmake --install`. These command requirements are
+separate from the project's minimum CMake version of 3.10.
+
## Focused unit tests
The standalone unit tests compile production callbacks with small test fixtures
@@ -16,18 +21,14 @@ These tests do not require installed kernel modules or root privileges. The
sanitizer runtime needs an environment that permits its process inspection.
The runner has six suites: `dhcpv6`, `radius`, `vrf`, `lcp`, `mempool`, and
-`log_line`. With no names it attempts all six. The DHCPv6 fixture currently
-refers to the removed `conf_dns_count` variable, and the runner does not link
-the `ipv6_dns.c` implementation now used by the reply builder. That fixture
-and its build command must be updated before a full run can pass.
-
-The other five suites can be run explicitly while that regression is being fixed:
+`log_line`. With no names it runs all six:
```bash
-sh tests/unit/run_safeguards.sh build radius vrf lcp mempool log_line
+sh tests/unit/run_safeguards.sh build
```
-A successful subset run does not validate the DHCPv6 safeguards. The
+The DHCPv6 suite checks bounded DNS replies and failure propagation when
+non-truncatable options exhaust reply space. The
[integration validation record](../docs/ng40-safeguards-review.md#validation)
describes results from the revision at which those safeguards were introduced.
@@ -37,6 +38,9 @@ The kernel module installation regression test is rootless and does not need
kernel headers. It uses a fake kernel build tree to verify that `ipoe`,
`vlan_mon`, and `ppposeq` are installed from their actual build directories,
honor `DESTDIR`, and make installation fail when `modules_install` fails.
+It also verifies that Debian and CentOS packaging configurations include
+`ppposeq.ko` with the standalone driver package name, and omit it when the
+build flag is disabled.
Run it through CTest after configuring the project:
@@ -69,7 +73,8 @@ sudo apt install iproute2 ppp pppoe isc-dhcp-client
```
Install the build dependencies described in the project README, including
-kernel headers and Lua development files for the options below. Run these
+kernel headers and Lua 5.1 development files (`liblua5.1-0-dev` on Debian and
+Ubuntu) for `LUA=TRUE` below. Run these
commands from the repository root.
Install accel-pppd (make install or use distro package). Do not run accel-pppd using systemd or other supervisors
diff --git a/tests/kernel-module-install/test.sh b/tests/kernel-module-install/test.sh
index 1fbfd623..2f3ec579 100644
--- a/tests/kernel-module-install/test.sh
+++ b/tests/kernel-module-install/test.sh
@@ -36,3 +36,34 @@ if FAIL_MODULES_INSTALL=1 DESTDIR="${work_dir}/failed-stage" \
fi
grep -q 'Failed to install kernel module' "${work_dir}/failed-install.log"
+
+# Packaging configurations bypass modules_install and must install the .ko
+# explicitly, including when PPPoSEQ is built as a standalone driver package.
+for package_type in Debian12 Centos9; do
+ package_build="${work_dir}/package-${package_type}"
+ package_stage="${work_dir}/stage-${package_type}"
+ "${cmake_command}" \
+ -S "${source_dir}" \
+ -B "${package_build}" \
+ -DBUILD_TESTING=OFF \
+ -DBUILD_DRIVER_ONLY=TRUE \
+ -DBUILD_PPPOSEQ_DRIVER=TRUE \
+ -DIGNORE_GIT=TRUE \
+ -DCPACK_TYPE="${package_type}" \
+ -DMODULES_KDIR=test-kernel \
+ -DCENTOS_KDIR=test-kernel \
+ -DKDIR="${script_dir}"
+ "${cmake_command}" --build "${package_build}"
+ DESTDIR="${package_stage}" \
+ "${cmake_command}" --install "${package_build}"
+ test -f "${package_stage}/lib/modules/test-kernel/extra/ppposeq.ko"
+ grep -q 'set(CPACK_PACKAGE_NAME "accel-ppp-ppposeq-kmod")' \
+ "${package_build}/CPackConfig.cmake"
+
+ # A stale build artifact must not be installed after the flag is disabled.
+ "${cmake_command}" -S "${source_dir}" -B "${package_build}" \
+ -DBUILD_PPPOSEQ_DRIVER=FALSE
+ DESTDIR="${package_stage}-disabled" \
+ "${cmake_command}" --install "${package_build}"
+ test ! -e "${package_stage}-disabled/lib/modules/test-kernel/extra/ppposeq.ko"
+done
diff --git a/tests/unit/dhcpv6_safeguards_test.c b/tests/unit/dhcpv6_safeguards_test.c
index 63692ee3..0938047c 100644
--- a/tests/unit/dhcpv6_safeguards_test.c
+++ b/tests/unit/dhcpv6_safeguards_test.c
@@ -14,6 +14,7 @@ static size_t response_len;
void log_warn(const char *fmt, ...) {}
void log_emerg(const char *fmt, ...) {}
void log_ppp_error(const char *fmt, ...) {}
+void log_ppp_warn(const char *fmt, ...) {}
void log_ppp_info2(const char *fmt, ...) {}
int ip6addr_add_peer(int i, struct in6_addr *a, struct in6_addr *p) { return 0; }
int ip6route_add(int i, const struct in6_addr *a, int p, const struct in6_addr *g, int m, uint32_t prio, const char *v) { return 0; }
@@ -60,10 +61,36 @@ static size_t relay(unsigned char *buf, size_t len)
return len + hdrlen + 4;
}
+static void check_dns_reply(unsigned int requested)
+{
+ struct dhcpv6_packet *pkt;
+ struct dhcpv6_option *opt;
+ unsigned int count = 0;
+ size_t offset, len;
+
+ assert(sent == 1);
+ assert(response_len <= sizeof(response));
+ pkt = dhcpv6_packet_parse(response, response_len);
+ assert(pkt);
+ list_for_each_entry(opt, &pkt->opt_list, entry) {
+ if (ntohs(opt->hdr->code) != D6_OPTION_DNS_SERVERS)
+ continue;
+ len = ntohs(opt->hdr->len);
+ assert(len && len % sizeof(struct in6_addr) == 0);
+ for (offset = 0; offset < len; offset += sizeof(struct in6_addr))
+ assert(!memcmp(opt->hdr->data + offset, &in6addr_loopback,
+ sizeof(in6addr_loopback)));
+ count += len / sizeof(struct in6_addr);
+ }
+ assert(count > 0 && count < requested);
+ dhcpv6_packet_free(pkt);
+}
+
int main(void)
{
unsigned char buf[16000], oro[4000], name[256];
const unsigned char valid_name[] = {2, 'g', 'w', 0};
+ unsigned char domain[] = {1, 'x', 0};
struct dhcpv6_packet *pkt, *reply;
struct dhcpv6_option *opt;
struct ap_session ses = {0};
@@ -132,8 +159,27 @@ int main(void)
}
assert(!dhcpv6_packet_parse(buf, len));
- /* An ORO can amplify a small request into more than 4096 response bytes. */
- conf_dns_count = 1;
+ /* A large DNS list is truncated to the reply buffer capacity. */
+ assert(ipv6_dns_reserve(&conf_dns, 300) == 0);
+ for (i = 0; i < 300; i++)
+ conf_dns.addr[i] = in6addr_loopback;
+ conf_dns.count = 300;
+ u_write_be16(oro, D6_OPTION_DNS_SERVERS);
+ len = request(buf);
+ len += option(buf + len, D6_OPTION_ORO, oro, 2);
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ pkt->ses = &ses;
+ sent = 0;
+ dhcpv6_send_reply(pkt, &pd, D6_REPLY);
+ check_dns_reply(conf_dns.count);
+ sent = 0;
+ dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
+ check_dns_reply(conf_dns.count);
+ dhcpv6_packet_free(pkt);
+
+ /* Repeated ORO entries leave no room for the final Preference option. */
+ conf_dns.count = 1;
for (i = 0; i < sizeof(oro); i += 2)
u_write_be16(oro + i, D6_OPTION_DNS_SERVERS);
len = request(buf);
@@ -147,6 +193,25 @@ int main(void)
dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
assert(!sent);
dhcpv6_packet_free(pkt);
+ free(conf_dns.addr);
+ memset(&conf_dns, 0, sizeof(conf_dns));
+
+ /* Domain-list options cannot be shortened; exhaustion drops the reply. */
+ conf_dnssl = domain;
+ conf_dnssl_size = sizeof(domain);
+ for (i = 0; i < sizeof(oro); i += 2)
+ u_write_be16(oro + i, D6_OPTION_DOMAIN_LIST);
+ len = request(buf);
+ len += option(buf + len, D6_OPTION_ORO, oro, sizeof(oro));
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ pkt->ses = &ses;
+ sent = 0;
+ dhcpv6_send_reply(pkt, &pd, D6_REPLY);
+ assert(!sent);
+ dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
+ assert(!sent);
+ dhcpv6_packet_free(pkt);
puts("DHCPv6 safeguards: PASS");
return 0;
}
diff --git a/tests/unit/run_safeguards.sh b/tests/unit/run_safeguards.sh
index 7f18c492..001cd19e 100644
--- a/tests/unit/run_safeguards.sh
+++ b/tests/unit/run_safeguards.sh
@@ -13,11 +13,15 @@ for name in $names; do
# LCP's global layer table retains the whole daemon under ASan; its test
# checks wire lengths with UBSan. The packet/VRF tests also use ASan.
if [ "$name" = lcp ]; then extra=-fno-sanitize=address; fi
+ set -- "tests/unit/${name}_safeguards_test.c"
+ if [ "$name" = dhcpv6 ]; then
+ set -- "$@" accel-pppd/ipv6_dns.c
+ fi
${CC:-cc} -O1 -g -Wall -Wno-unused-function -Wno-unused-result -D_GNU_SOURCE -DAP_SESSIONID_LEN=16 \
-DOPENSSL_API_COMPAT=0x10100000L -fno-strict-aliasing \
-ffunction-sections -fdata-sections -Wl,--gc-sections \
-fsanitize=address,undefined -fno-sanitize-recover=all $extra \
-I "$build" -I accel-pppd -I accel-pppd/include -I accel-pppd/triton \
- -o "$out/$name" "tests/unit/${name}_safeguards_test.c" -lcrypto -lpthread
+ -o "$out/$name" "$@" -lcrypto -lpthread
"$out/$name"
done