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