xref: /freebsd/sbin/pfctl/tests/pfctl_test.c (revision d80b9f8d51c71909abb829811f6351ab5c006b4e)
144ec023cSAlex Richardson /*-
244ec023cSAlex Richardson  * SPDX-License-Identifier: BSD-2-Clause
344ec023cSAlex Richardson  *
444ec023cSAlex Richardson  * Copyright 2020 Alex Richardson <arichardson@FreeBSD.org>
544ec023cSAlex Richardson  *
644ec023cSAlex Richardson  * This software was developed by SRI International and the University of
744ec023cSAlex Richardson  * Cambridge Computer Laboratory (Department of Computer Science and
844ec023cSAlex Richardson  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
944ec023cSAlex Richardson  * DARPA SSITH research programme.
1044ec023cSAlex Richardson  *
1144ec023cSAlex Richardson  * This work was supported by Innovate UK project 105694, "Digital Security by
1244ec023cSAlex Richardson  * Design (DSbD) Technology Platform Prototype".
1344ec023cSAlex Richardson  *
1444ec023cSAlex Richardson  * Redistribution and use in source and binary forms, with or without
1544ec023cSAlex Richardson  * modification, are permitted provided that the following conditions are met:
1644ec023cSAlex Richardson  * 1. Redistributions of source code must retain the above copyright notice,
1744ec023cSAlex Richardson  *    this list of conditions and the following disclaimer.
1844ec023cSAlex Richardson  * 2. Redistributions in binary form must reproduce the above copyright notice,
1944ec023cSAlex Richardson  *    this list of conditions and the following disclaimer in the documentation
2044ec023cSAlex Richardson  *    and/or other materials provided with the distribution.
2144ec023cSAlex Richardson  *
2244ec023cSAlex Richardson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
2344ec023cSAlex Richardson  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2444ec023cSAlex Richardson  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2544ec023cSAlex Richardson  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
2644ec023cSAlex Richardson  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2744ec023cSAlex Richardson  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2844ec023cSAlex Richardson  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2944ec023cSAlex Richardson  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3044ec023cSAlex Richardson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3144ec023cSAlex Richardson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3244ec023cSAlex Richardson  */
3344ec023cSAlex Richardson #include <sys/cdefs.h>
3444ec023cSAlex Richardson __FBSDID("$FreeBSD$");
3544ec023cSAlex Richardson 
3644ec023cSAlex Richardson #include <sys/types.h>
3744ec023cSAlex Richardson #include <sys/param.h>
3844ec023cSAlex Richardson #include <err.h>
3944ec023cSAlex Richardson #include <errno.h>
4044ec023cSAlex Richardson #include <fcntl.h>
4144ec023cSAlex Richardson #include <stdbool.h>
4244ec023cSAlex Richardson #include <stdio.h>
4344ec023cSAlex Richardson #include <stdlib.h>
4444ec023cSAlex Richardson #include <spawn.h>
4544ec023cSAlex Richardson #include <sys/module.h>
4644ec023cSAlex Richardson #include <sys/sbuf.h>
4744ec023cSAlex Richardson #include <sys/stat.h>
4844ec023cSAlex Richardson #include <sys/wait.h>
4944ec023cSAlex Richardson 
5044ec023cSAlex Richardson #include <atf-c.h>
5144ec023cSAlex Richardson 
5244ec023cSAlex Richardson /*
5344ec023cSAlex Richardson  * Tests 0001-0999 are copied from OpenBSD's regress/sbin/pfctl.
5444ec023cSAlex Richardson  * Tests 1001-1999 are ours (FreeBSD's own).
5544ec023cSAlex Richardson  *
5644ec023cSAlex Richardson  * pf: Run pfctl -nv on pfNNNN.in and check that the output matches pfNNNN.ok.
5744ec023cSAlex Richardson  *     Copied from OpenBSD.  Main differences are some things not working
5844ec023cSAlex Richardson  *     in FreeBSD:
5944ec023cSAlex Richardson  *         * The action 'match'
6044ec023cSAlex Richardson  *         * The command 'set reassemble'
6144ec023cSAlex Richardson  *         * The 'from'/'to' options together with 'route-to'
6244ec023cSAlex Richardson  *         * The option 'scrub' (it is an action in FreeBSD)
6344ec023cSAlex Richardson  *         * Accepting undefined routing tables in actions (??: see pf0093.in)
6444ec023cSAlex Richardson  *         * The 'route' option
6544ec023cSAlex Richardson  *         * The 'set queue def' option
6644ec023cSAlex Richardson  * selfpf: Feed pfctl output through pfctl again and verify it stays the same.
6744ec023cSAlex Richardson  *         Copied from OpenBSD.
6844ec023cSAlex Richardson  */
6944ec023cSAlex Richardson 
7044ec023cSAlex Richardson static bool
71*d80b9f8dSAdrian Chadd check_pf_module_available(void)
7244ec023cSAlex Richardson {
7344ec023cSAlex Richardson 	int modid;
7444ec023cSAlex Richardson 	struct module_stat stat;
7544ec023cSAlex Richardson 
7644ec023cSAlex Richardson 	if ((modid = modfind("pf")) < 0) {
7744ec023cSAlex Richardson 		warn("pf module not found");
7844ec023cSAlex Richardson 		return false;
7944ec023cSAlex Richardson 	}
8044ec023cSAlex Richardson 	stat.version = sizeof(struct module_stat);
8144ec023cSAlex Richardson 	if (modstat(modid, &stat) < 0) {
8244ec023cSAlex Richardson 		warn("can't stat pf module id %d", modid);
8344ec023cSAlex Richardson 		return false;
8444ec023cSAlex Richardson 	}
8544ec023cSAlex Richardson 	return (true);
8644ec023cSAlex Richardson }
8744ec023cSAlex Richardson 
8844ec023cSAlex Richardson extern char **environ;
8944ec023cSAlex Richardson 
9044ec023cSAlex Richardson static struct sbuf *
9144ec023cSAlex Richardson read_fd(int fd, size_t sizehint)
9244ec023cSAlex Richardson {
9344ec023cSAlex Richardson 	struct sbuf *sb;
9444ec023cSAlex Richardson 	ssize_t count;
9544ec023cSAlex Richardson 	char buffer[MAXBSIZE];
9644ec023cSAlex Richardson 
9744ec023cSAlex Richardson 	sb = sbuf_new(NULL, NULL, sizehint, SBUF_AUTOEXTEND);
9844ec023cSAlex Richardson 	errno = 0;
9944ec023cSAlex Richardson 	while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
10044ec023cSAlex Richardson 		sbuf_bcat(sb, buffer, count);
10144ec023cSAlex Richardson 	}
10244ec023cSAlex Richardson 	ATF_REQUIRE_ERRNO(0, count == 0 && "Should have reached EOF");
10344ec023cSAlex Richardson 	sbuf_finish(sb); /* Ensure NULL-termination */
10444ec023cSAlex Richardson 	return (sb);
10544ec023cSAlex Richardson }
10644ec023cSAlex Richardson 
10744ec023cSAlex Richardson static struct sbuf *
10844ec023cSAlex Richardson read_file(const char *filename)
10944ec023cSAlex Richardson {
11044ec023cSAlex Richardson 	struct stat s;
11144ec023cSAlex Richardson 	struct sbuf *result;
11244ec023cSAlex Richardson 	int fd;
11344ec023cSAlex Richardson 
11444ec023cSAlex Richardson 	errno = 0;
11544ec023cSAlex Richardson 	ATF_REQUIRE_EQ_MSG(stat(filename, &s), 0, "cannot stat %s", filename);
11644ec023cSAlex Richardson 	fd = open(filename, O_RDONLY);
11744ec023cSAlex Richardson 	ATF_REQUIRE_ERRNO(0, fd > 0);
11844ec023cSAlex Richardson 	result = read_fd(fd, s.st_size);
11944ec023cSAlex Richardson 	ATF_REQUIRE_ERRNO(0, close(fd) == 0);
12044ec023cSAlex Richardson 	return (result);
12144ec023cSAlex Richardson }
12244ec023cSAlex Richardson 
12344ec023cSAlex Richardson static void
12444ec023cSAlex Richardson run_pfctl_test(const char *input_path, const char *expected_path,
12544ec023cSAlex Richardson     const atf_tc_t *tc)
12644ec023cSAlex Richardson {
12744ec023cSAlex Richardson 	int status;
12844ec023cSAlex Richardson 	pid_t pid;
12944ec023cSAlex Richardson 	int pipefds[2];
13044ec023cSAlex Richardson 	char input_files_path[PATH_MAX];
13144ec023cSAlex Richardson 	struct sbuf *expected_output;
13244ec023cSAlex Richardson 	struct sbuf *real_output;
13344ec023cSAlex Richardson 	posix_spawn_file_actions_t action;
13444ec023cSAlex Richardson 
13544ec023cSAlex Richardson 	if (!check_pf_module_available())
13644ec023cSAlex Richardson 		atf_tc_skip("pf(4) is not loaded");
13744ec023cSAlex Richardson 
13844ec023cSAlex Richardson 	/* The test inputs need to be able to use relative includes. */
13944ec023cSAlex Richardson 	snprintf(input_files_path, sizeof(input_files_path), "%s/files",
14044ec023cSAlex Richardson 	    atf_tc_get_config_var(tc, "srcdir"));
14144ec023cSAlex Richardson 	ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0);
14244ec023cSAlex Richardson 
14344ec023cSAlex Richardson 	ATF_REQUIRE_ERRNO(0, pipe(pipefds) == 0);
14444ec023cSAlex Richardson 	expected_output = read_file(expected_path);
14544ec023cSAlex Richardson 
14644ec023cSAlex Richardson 	posix_spawn_file_actions_init(&action);
14744ec023cSAlex Richardson 	posix_spawn_file_actions_addclose(&action, STDIN_FILENO);
14844ec023cSAlex Richardson 	posix_spawn_file_actions_addclose(&action, pipefds[1]);
14944ec023cSAlex Richardson 	posix_spawn_file_actions_adddup2(&action, pipefds[0], STDOUT_FILENO);
15044ec023cSAlex Richardson 	posix_spawn_file_actions_adddup2(&action, pipefds[0], STDERR_FILENO);
15144ec023cSAlex Richardson 
15244ec023cSAlex Richardson 	const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path,
15344ec023cSAlex Richardson 		NULL };
15444ec023cSAlex Richardson 	printf("Running %s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3],
15544ec023cSAlex Richardson 	    argv[4]);
15644ec023cSAlex Richardson 	status = posix_spawnp(
15744ec023cSAlex Richardson 	    &pid, "pfctl", &action, NULL, __DECONST(char **, argv), environ);
15844ec023cSAlex Richardson 	ATF_REQUIRE_EQ_MSG(
15944ec023cSAlex Richardson 	    status, 0, "posix_spawn failed: %s", strerror(errno));
16044ec023cSAlex Richardson 	posix_spawn_file_actions_destroy(&action);
16144ec023cSAlex Richardson 	close(pipefds[0]);
16244ec023cSAlex Richardson 
16344ec023cSAlex Richardson 	real_output = read_fd(pipefds[1], 0);
16444ec023cSAlex Richardson 	printf("---\n%s---\n", sbuf_data(real_output));
16544ec023cSAlex Richardson 	ATF_REQUIRE_EQ(waitpid(pid, &status, 0), pid);
16644ec023cSAlex Richardson 	ATF_REQUIRE_MSG(WIFEXITED(status),
16744ec023cSAlex Richardson 	    "pfctl returned non-zero! Output:\n %s", sbuf_data(real_output));
16844ec023cSAlex Richardson 
16944ec023cSAlex Richardson 	ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output));
17044ec023cSAlex Richardson 	sbuf_delete(expected_output);
17144ec023cSAlex Richardson 	sbuf_delete(real_output);
17244ec023cSAlex Richardson 	close(pipefds[1]);
17344ec023cSAlex Richardson }
17444ec023cSAlex Richardson 
17544ec023cSAlex Richardson static void
17644ec023cSAlex Richardson do_pf_test(const char *number, const atf_tc_t *tc)
17744ec023cSAlex Richardson {
17844ec023cSAlex Richardson 	char *input_path;
17944ec023cSAlex Richardson 	char *expected_path;
18044ec023cSAlex Richardson 	asprintf(&input_path, "%s/files/pf%s.in",
18144ec023cSAlex Richardson 	    atf_tc_get_config_var(tc, "srcdir"), number);
18244ec023cSAlex Richardson 	asprintf(&expected_path, "%s/files/pf%s.ok",
18344ec023cSAlex Richardson 	    atf_tc_get_config_var(tc, "srcdir"), number);
18444ec023cSAlex Richardson 	run_pfctl_test(input_path, expected_path, tc);
18544ec023cSAlex Richardson 	free(input_path);
18644ec023cSAlex Richardson 	free(expected_path);
18744ec023cSAlex Richardson }
18844ec023cSAlex Richardson 
18944ec023cSAlex Richardson static void
19044ec023cSAlex Richardson do_selfpf_test(const char *number, const atf_tc_t *tc)
19144ec023cSAlex Richardson {
19244ec023cSAlex Richardson 	char *expected_path;
19344ec023cSAlex Richardson 	asprintf(&expected_path, "%s/files/pf%s.ok",
19444ec023cSAlex Richardson 	    atf_tc_get_config_var(tc, "srcdir"), number);
19544ec023cSAlex Richardson 	run_pfctl_test(expected_path, expected_path, tc);
19644ec023cSAlex Richardson 	free(expected_path);
19744ec023cSAlex Richardson }
19844ec023cSAlex Richardson 
19944ec023cSAlex Richardson #define PFCTL_TEST(number, descr)				\
20044ec023cSAlex Richardson 	ATF_TC(pf##number);					\
20144ec023cSAlex Richardson 	ATF_TC_HEAD(pf##number, tc)				\
20244ec023cSAlex Richardson 	{							\
20344ec023cSAlex Richardson 		atf_tc_set_md_var(tc, "descr", descr);		\
20444ec023cSAlex Richardson 	}							\
20544ec023cSAlex Richardson 	ATF_TC_BODY(pf##number, tc)				\
20644ec023cSAlex Richardson 	{							\
20744ec023cSAlex Richardson 		do_pf_test(#number, tc);			\
20844ec023cSAlex Richardson 	}							\
20944ec023cSAlex Richardson 	ATF_TC(selfpf##number);					\
21044ec023cSAlex Richardson 	ATF_TC_HEAD(selfpf##number, tc)				\
21144ec023cSAlex Richardson 	{							\
21244ec023cSAlex Richardson 		atf_tc_set_md_var(tc, "descr", "Self " descr);	\
21344ec023cSAlex Richardson 	}							\
21444ec023cSAlex Richardson 	ATF_TC_BODY(selfpf##number, tc)				\
21544ec023cSAlex Richardson 	{							\
21644ec023cSAlex Richardson 		do_selfpf_test(#number, tc);			\
21744ec023cSAlex Richardson 	}
21844ec023cSAlex Richardson #include "pfctl_test_list.inc"
21944ec023cSAlex Richardson #undef PFCTL_TEST
22044ec023cSAlex Richardson 
22144ec023cSAlex Richardson ATF_TP_ADD_TCS(tp)
22244ec023cSAlex Richardson {
22344ec023cSAlex Richardson #define PFCTL_TEST(number, descr)		\
22444ec023cSAlex Richardson 	ATF_TP_ADD_TC(tp, pf##number);		\
22544ec023cSAlex Richardson 	ATF_TP_ADD_TC(tp, selfpf##number);
22644ec023cSAlex Richardson #include "pfctl_test_list.inc"
22744ec023cSAlex Richardson #undef PFCTL_TEST
22844ec023cSAlex Richardson 
22944ec023cSAlex Richardson 	return atf_no_error();
23044ec023cSAlex Richardson }
231