npf_rule_test.c revision 1.14 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