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