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