xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: npf_rule_test.c,v 1.14 2017/01/29 04:12:52 christos Exp $	*/
2 
3 /*
4  * NPF ruleset test.
5  *
6  * Public Domain.
7  */
8 
9 #ifdef _KERNEL
10 #include <sys/types.h>
11 #endif
12 
13 #include "npf_impl.h"
14 #include "npf_test.h"
15 
16 #define	RESULT_PASS	0
17 #define	RESULT_BLOCK	ENETUNREACH
18 
19 static const struct test_case {
20 	const char *	src;
21 	const char *	dst;
22 	const char *	ifname;
23 	int		di;
24 	int		stateful_ret;
25 	int		ret;
26 } test_cases[] = {
27 
28 	/* Stateful pass. */
29 	{
30 		.src = "10.1.1.1",		.dst = "10.1.1.2",
31 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
32 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
33 	},
34 	{
35 		.src = "10.1.1.2",		.dst = "10.1.1.1",
36 		.ifname = IFNAME_INT,		.di = PFIL_IN,
37 		.stateful_ret = RESULT_PASS,	.ret = RESULT_BLOCK
38 	},
39 
40 	/* Pass forwards stream only. */
41 	{
42 		.src = "10.1.1.1",		.dst = "10.1.1.3",
43 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
44 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
45 	},
46 	{
47 		.src = "10.1.1.3",		.dst = "10.1.1.1",
48 		.ifname = IFNAME_INT,		.di = PFIL_IN,
49 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
50 	},
51 
52 	/* Block. */
53 	{	.src = "10.1.1.1",		.dst = "10.1.1.4",
54 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
55 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
56 	},
57 
58 };
59 
60 static struct mbuf *
61 fill_packet(const struct test_case *t)
62 {
63 	struct mbuf *m;
64 	struct ip *ip;
65 	struct udphdr *uh;
66 
67 	m = mbuf_construct(IPPROTO_UDP);
68 	uh = mbuf_return_hdrs(m, false, &ip);
69 	ip->ip_src.s_addr = inet_addr(t->src);
70 	ip->ip_dst.s_addr = inet_addr(t->dst);
71 	uh->uh_sport = htons(9000);
72 	uh->uh_dport = htons(9000);
73 	return m;
74 }
75 
76 static int
77 npf_rule_raw_test(bool verbose, struct mbuf *m, ifnet_t *ifp, int di)
78 {
79 	npf_t *npf = npf_getkernctx();
80 	npf_cache_t npc = { .npc_info = 0, .npc_ctx = npf };
81 	nbuf_t nbuf;
82 	npf_rule_t *rl;
83 	npf_match_info_t mi;
84 	int error;
85 
86 	nbuf_init(npf, &nbuf, m, ifp);
87 	npc.npc_nbuf = &nbuf;
88 	npf_cache_all(&npc);
89 
90 	int slock = npf_config_read_enter();
91 	rl = npf_ruleset_inspect(&npc, npf_config_ruleset(npf),
92 	    di, NPF_LAYER_3);
93 	if (rl) {
94 		error = npf_rule_conclude(rl, &mi);
95 	} else {
96 		error = ENOENT;
97 	}
98 	npf_config_read_exit(slock);
99 	return error;
100 }
101 
102 static int
103 npf_test_case(u_int i, bool verbose)
104 {
105 	const struct test_case *t = &test_cases[i];
106 	ifnet_t *ifp = npf_test_getif(t->ifname);
107 	int error;
108 
109 	struct mbuf *m = fill_packet(t);
110 	error = npf_rule_raw_test(verbose, m, ifp, t->di);
111 	m_freem(m);
112 	return error;
113 }
114 
115 static npf_rule_t *
116 npf_blockall_rule(void)
117 {
118 	npf_t *npf = npf_getkernctx();
119 	prop_dictionary_t rldict;
120 
121 	rldict = prop_dictionary_create();
122 	prop_dictionary_set_uint32(rldict, "attr",
123 	    NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC);
124 	return npf_rule_alloc(npf, rldict);
125 }
126 
127 bool
128 npf_rule_test(bool verbose)
129 {
130 	npf_t *npf = npf_getkernctx();
131 	npf_ruleset_t *rlset;
132 	npf_rule_t *rl;
133 	bool fail = false;
134 	uint64_t id;
135 	int error;
136 
137 	for (unsigned i = 0; i < __arraycount(test_cases); i++) {
138 		const struct test_case *t = &test_cases[i];
139 		ifnet_t *ifp = npf_test_getif(t->ifname);
140 		int serror;
141 
142 		if (ifp == NULL) {
143 			printf("Interface %s is not configured.\n", t->ifname);
144 			return false;
145 		}
146 
147 		struct mbuf *m = fill_packet(t);
148 		error = npf_rule_raw_test(verbose, m, ifp, t->di);
149 		serror = npf_packet_handler(npf, &m, ifp, t->di);
150 
151 		if (m) {
152 			m_freem(m);
153 		}
154 
155 		if (verbose) {
156 			printf("Rule test %d, expected %d (stateful) and %d \n"
157 			    "-> returned %d and %d.\n",
158 			    i + 1, t->stateful_ret, t->ret, serror, error);
159 		}
160 		fail |= (serror != t->stateful_ret || error != t->ret);
161 	}
162 
163 	/*
164 	 * Test dynamic NPF rules.
165 	 */
166 
167 	error = npf_test_case(0, verbose);
168 	assert(error == RESULT_PASS);
169 
170 	npf_config_enter(npf);
171 	rlset = npf_config_ruleset(npf);
172 
173 	rl = npf_blockall_rule();
174 	error = npf_ruleset_add(rlset, "test-rules", rl);
175 	fail |= error != 0;
176 
177 	error = npf_test_case(0, verbose);
178 	fail |= (error != RESULT_BLOCK);
179 
180 	id = npf_rule_getid(rl);
181 	error = npf_ruleset_remove(rlset, "test-rules", id);
182 	fail |= error != 0;
183 
184 	npf_config_exit(npf);
185 
186 	error = npf_test_case(0, verbose);
187 	fail |= (error != RESULT_PASS);
188 
189 	return !fail;
190 }
191