1 /*
2 * Copyright (c) 2021 BayLibre SAS
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(npf_base, CONFIG_NET_PKT_FILTER_LOG_LEVEL);
9
10 #include <zephyr/net/net_core.h>
11 #include <zephyr/net/net_pkt_filter.h>
12 #include <zephyr/spinlock.h>
13
14 /*
15 * Our actual rule lists for supported test points
16 */
17
18 struct npf_rule_list npf_send_rules = {
19 .rule_head = SYS_SLIST_STATIC_INIT(&send_rules.rule_head),
20 .lock = { },
21 };
22
23 struct npf_rule_list npf_recv_rules = {
24 .rule_head = SYS_SLIST_STATIC_INIT(&recv_rules.rule_head),
25 .lock = { },
26 };
27
28 #ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
29 struct npf_rule_list npf_local_in_recv_rules = {
30 .rule_head = SYS_SLIST_STATIC_INIT(&local_in_recv_rules.rule_head),
31 .lock = { },
32 };
33 #endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
34
35 #ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
36 struct npf_rule_list npf_ipv4_recv_rules = {
37 .rule_head = SYS_SLIST_STATIC_INIT(&ipv4_recv_rules.rule_head),
38 .lock = { },
39 };
40 #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */
41
42 #ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
43 struct npf_rule_list npf_ipv6_recv_rules = {
44 .rule_head = SYS_SLIST_STATIC_INIT(&ipv6_recv_rules.rule_head),
45 .lock = { },
46 };
47 #endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */
48
49 /*
50 * Helper function
51 */
52 #if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK)
get_ip_rules(uint8_t pf)53 static struct npf_rule_list *get_ip_rules(uint8_t pf)
54 {
55 switch (pf) {
56 case PF_INET:
57 #ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
58 return &npf_ipv4_recv_rules;
59 #endif
60 break;
61 case PF_INET6:
62 #ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
63 return &npf_ipv6_recv_rules;
64 #endif
65 break;
66 default:
67 return NULL;
68 }
69
70 return NULL;
71 }
72 #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */
73 /*
74 * Rule application
75 */
76
77 /*
78 * All tests must be true to return true.
79 * If no tests then it is true.
80 */
apply_tests(struct npf_rule * rule,struct net_pkt * pkt)81 static bool apply_tests(struct npf_rule *rule, struct net_pkt *pkt)
82 {
83 struct npf_test *test;
84 unsigned int i;
85 bool result;
86
87 for (i = 0; i < rule->nb_tests; i++) {
88 test = rule->tests[i];
89 result = test->fn(test, pkt);
90 NET_DBG("test %s (%p) result %d",
91 COND_CODE_1(NPF_TEST_ENABLE_NAME, (test->name), ("")),
92 test, result);
93 if (result == false) {
94 return false;
95 }
96 }
97
98 return true;
99 }
100
101 /*
102 * We return the specified result for the first rule whose tests are all true.
103 */
evaluate(sys_slist_t * rule_head,struct net_pkt * pkt)104 static enum net_verdict evaluate(sys_slist_t *rule_head, struct net_pkt *pkt)
105 {
106 struct npf_rule *rule;
107
108 NET_DBG("rule_head %p on pkt %p", rule_head, pkt);
109
110 if (sys_slist_is_empty(rule_head)) {
111 NET_DBG("no rules");
112 return NET_OK;
113 }
114
115 SYS_SLIST_FOR_EACH_CONTAINER(rule_head, rule, node) {
116 if (apply_tests(rule, pkt) == true) {
117 return rule->result;
118 }
119 }
120
121 NET_DBG("no matching rules from rule_head %p", rule_head);
122 return NET_DROP;
123 }
124
lock_evaluate(struct npf_rule_list * rules,struct net_pkt * pkt)125 static enum net_verdict lock_evaluate(struct npf_rule_list *rules, struct net_pkt *pkt)
126 {
127 k_spinlock_key_t key = k_spin_lock(&rules->lock);
128 enum net_verdict result = evaluate(&rules->rule_head, pkt);
129
130 k_spin_unlock(&rules->lock, key);
131 return result;
132 }
133
net_pkt_filter_send_ok(struct net_pkt * pkt)134 bool net_pkt_filter_send_ok(struct net_pkt *pkt)
135 {
136 enum net_verdict result = lock_evaluate(&npf_send_rules, pkt);
137
138 return result == NET_OK;
139 }
140
net_pkt_filter_recv_ok(struct net_pkt * pkt)141 bool net_pkt_filter_recv_ok(struct net_pkt *pkt)
142 {
143 enum net_verdict result = lock_evaluate(&npf_recv_rules, pkt);
144
145 return result == NET_OK;
146 }
147
148 #ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
net_pkt_filter_local_in_recv_ok(struct net_pkt * pkt)149 bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt)
150 {
151 enum net_verdict result = lock_evaluate(&npf_local_in_recv_rules, pkt);
152
153 return result == NET_OK;
154 }
155 #endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
156
157 #if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK)
net_pkt_filter_ip_recv_ok(struct net_pkt * pkt)158 bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt)
159 {
160 struct npf_rule_list *rules = get_ip_rules(net_pkt_family(pkt));
161
162 if (!rules) {
163 NET_DBG("no rules");
164 return true;
165 }
166
167 enum net_verdict result = lock_evaluate(rules, pkt);
168
169 return result == NET_OK;
170 }
171 #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */
172
173 /*
174 * Rule management
175 */
176
npf_insert_rule(struct npf_rule_list * rules,struct npf_rule * rule)177 void npf_insert_rule(struct npf_rule_list *rules, struct npf_rule *rule)
178 {
179 k_spinlock_key_t key = k_spin_lock(&rules->lock);
180
181 NET_DBG("inserting rule %p into %p", rule, rules);
182 sys_slist_prepend(&rules->rule_head, &rule->node);
183
184 k_spin_unlock(&rules->lock, key);
185 }
186
npf_append_rule(struct npf_rule_list * rules,struct npf_rule * rule)187 void npf_append_rule(struct npf_rule_list *rules, struct npf_rule *rule)
188 {
189 __ASSERT(sys_slist_peek_tail(&rules->rule_head) != &npf_default_ok.node, "");
190 __ASSERT(sys_slist_peek_tail(&rules->rule_head) != &npf_default_drop.node, "");
191
192 k_spinlock_key_t key = k_spin_lock(&rules->lock);
193
194 NET_DBG("appending rule %p into %p", rule, rules);
195 sys_slist_append(&rules->rule_head, &rule->node);
196
197 k_spin_unlock(&rules->lock, key);
198 }
199
npf_remove_rule(struct npf_rule_list * rules,struct npf_rule * rule)200 bool npf_remove_rule(struct npf_rule_list *rules, struct npf_rule *rule)
201 {
202 k_spinlock_key_t key = k_spin_lock(&rules->lock);
203 bool result = sys_slist_find_and_remove(&rules->rule_head, &rule->node);
204
205 k_spin_unlock(&rules->lock, key);
206 NET_DBG("removing rule %p from %p: %d", rule, rules, result);
207 return result;
208 }
209
npf_remove_all_rules(struct npf_rule_list * rules)210 bool npf_remove_all_rules(struct npf_rule_list *rules)
211 {
212 k_spinlock_key_t key = k_spin_lock(&rules->lock);
213 bool result = !sys_slist_is_empty(&rules->rule_head);
214
215 if (result) {
216 sys_slist_init(&rules->rule_head);
217 NET_DBG("removing all rules from %p", rules);
218 }
219
220 k_spin_unlock(&rules->lock, key);
221 return result;
222 }
223
224 /*
225 * Default rule list terminations.
226 */
227
228 struct npf_rule npf_default_ok = {
229 .result = NET_OK,
230 };
231
232 struct npf_rule npf_default_drop = {
233 .result = NET_DROP,
234 };
235
236 /*
237 * Some simple generic conditions
238 */
239
npf_iface_match(struct npf_test * test,struct net_pkt * pkt)240 bool npf_iface_match(struct npf_test *test, struct net_pkt *pkt)
241 {
242 struct npf_test_iface *test_iface =
243 CONTAINER_OF(test, struct npf_test_iface, test);
244
245 NET_DBG("iface %d pkt %d",
246 net_if_get_by_iface(test_iface->iface),
247 net_if_get_by_iface(net_pkt_iface(pkt)));
248
249 return test_iface->iface == net_pkt_iface(pkt);
250 }
251
npf_iface_unmatch(struct npf_test * test,struct net_pkt * pkt)252 bool npf_iface_unmatch(struct npf_test *test, struct net_pkt *pkt)
253 {
254 return !npf_iface_match(test, pkt);
255 }
256
npf_orig_iface_match(struct npf_test * test,struct net_pkt * pkt)257 bool npf_orig_iface_match(struct npf_test *test, struct net_pkt *pkt)
258 {
259 struct npf_test_iface *test_iface =
260 CONTAINER_OF(test, struct npf_test_iface, test);
261
262 NET_DBG("orig iface %d pkt %d",
263 net_if_get_by_iface(test_iface->iface),
264 net_if_get_by_iface(net_pkt_orig_iface(pkt)));
265
266 return test_iface->iface == net_pkt_orig_iface(pkt);
267 }
268
npf_orig_iface_unmatch(struct npf_test * test,struct net_pkt * pkt)269 bool npf_orig_iface_unmatch(struct npf_test *test, struct net_pkt *pkt)
270 {
271 return !npf_orig_iface_match(test, pkt);
272 }
273
npf_size_inbounds(struct npf_test * test,struct net_pkt * pkt)274 bool npf_size_inbounds(struct npf_test *test, struct net_pkt *pkt)
275 {
276 struct npf_test_size_bounds *bounds =
277 CONTAINER_OF(test, struct npf_test_size_bounds, test);
278 size_t pkt_size = net_pkt_get_len(pkt);
279
280 NET_DBG("pkt_size %zu min %zu max %zu",
281 pkt_size, bounds->min, bounds->max);
282
283 return pkt_size >= bounds->min && pkt_size <= bounds->max;
284 }
285
npf_ip_src_addr_match(struct npf_test * test,struct net_pkt * pkt)286 bool npf_ip_src_addr_match(struct npf_test *test, struct net_pkt *pkt)
287 {
288 struct npf_test_ip *test_ip =
289 CONTAINER_OF(test, struct npf_test_ip, test);
290 uint8_t pkt_family = net_pkt_family(pkt);
291
292 for (uint32_t ip_it = 0; ip_it < test_ip->ipaddr_num; ip_it++) {
293 if (IS_ENABLED(CONFIG_NET_IPV4) && pkt_family == AF_INET) {
294 struct in_addr *addr = (struct in_addr *)NET_IPV4_HDR(pkt)->src;
295
296 if (net_ipv4_addr_cmp(addr, &((struct in_addr *)test_ip->ipaddr)[ip_it])) {
297 return true;
298 }
299 } else if (IS_ENABLED(CONFIG_NET_IPV6) && pkt_family == AF_INET6) {
300 struct in6_addr *addr = (struct in6_addr *)NET_IPV6_HDR(pkt)->src;
301
302 if (net_ipv6_addr_cmp(addr, &((struct in6_addr *)test_ip->ipaddr)[ip_it])) {
303 return true;
304 }
305 }
306 }
307 return false;
308 }
309
npf_ip_src_addr_unmatch(struct npf_test * test,struct net_pkt * pkt)310 bool npf_ip_src_addr_unmatch(struct npf_test *test, struct net_pkt *pkt)
311 {
312 return !npf_ip_src_addr_match(test, pkt);
313 }
314
rules_cb(struct npf_rule_list * rules,enum npf_rule_type type,npf_rule_cb_t cb,void * user_data)315 static void rules_cb(struct npf_rule_list *rules, enum npf_rule_type type,
316 npf_rule_cb_t cb, void *user_data)
317 {
318 struct npf_rule *rule;
319
320 SYS_SLIST_FOR_EACH_CONTAINER(&rules->rule_head, rule, node) {
321 cb(rule, type, user_data);
322 }
323 }
324
npf_rules_foreach(npf_rule_cb_t cb,void * user_data)325 void npf_rules_foreach(npf_rule_cb_t cb, void *user_data)
326 {
327 rules_cb(&npf_send_rules, NPF_RULE_TYPE_SEND, cb, user_data);
328 rules_cb(&npf_recv_rules, NPF_RULE_TYPE_RECV, cb, user_data);
329
330 #ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
331 rules_cb(&npf_local_in_recv_rules, NPF_RULE_TYPE_LOCAL_IN_RECV, cb, user_data);
332 #endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
333
334 #ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
335 rules_cb(&npf_ipv4_recv_rules, NPF_RULE_TYPE_IPV4_RECV, cb, user_data);
336 #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */
337
338 #ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
339 rules_cb(&npf_ipv6_recv_rules, NPF_RULE_TYPE_IPV6_RECV, cb, user_data);
340 #endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */
341 }
342
343 #include "net_private.h" /* for net_sprint_addr() */
344
npf_test_get_str(struct npf_test * test,char * buf,size_t len)345 const char *npf_test_get_str(struct npf_test *test, char *buf, size_t len)
346 {
347 #if defined(CONFIG_NET_SHELL_PKT_FILTER_SUPPORTED)
348 if (test->type == NPF_TEST_TYPE_IFACE_MATCH ||
349 test->type == NPF_TEST_TYPE_IFACE_UNMATCH ||
350 test->type == NPF_TEST_TYPE_ORIG_IFACE_MATCH ||
351 test->type == NPF_TEST_TYPE_ORIG_IFACE_UNMATCH) {
352 struct npf_test_iface *test_iface =
353 CONTAINER_OF(test, struct npf_test_iface, test);
354
355 snprintk(buf, len, "[%d]", net_if_get_by_iface(test_iface->iface));
356
357 } else if (test->type == NPF_TEST_TYPE_SIZE_BOUNDS ||
358 test->type == NPF_TEST_TYPE_SIZE_MIN ||
359 test->type == NPF_TEST_TYPE_SIZE_MAX) {
360 struct npf_test_size_bounds *bounds =
361 CONTAINER_OF(test, struct npf_test_size_bounds, test);
362
363 if (test->type == NPF_TEST_TYPE_SIZE_MIN ||
364 test->type == NPF_TEST_TYPE_SIZE_MAX) {
365 snprintk(buf, len, "[%zu]",
366 test->type == NPF_TEST_TYPE_SIZE_MIN ?
367 bounds->min : bounds->max);
368 } else {
369 snprintk(buf, len, "[%zu-%zu]", bounds->min, bounds->max);
370 }
371
372 } else if (test->type == NPF_TEST_TYPE_IP_SRC_ADDR_ALLOWLIST ||
373 test->type == NPF_TEST_TYPE_IP_SRC_ADDR_BLOCKLIST) {
374 struct npf_test_ip *test_ip =
375 CONTAINER_OF(test, struct npf_test_ip, test);
376 int pos = 1;
377
378 if (len < 2) {
379 goto out;
380 }
381
382 if (test_ip->ipaddr_num == 0) {
383 snprintk(buf, len, "[]");
384 goto out;
385 }
386
387 buf[0] = '[';
388
389 for (uint32_t i = 0; i < test_ip->ipaddr_num; i++) {
390 if (IS_ENABLED(CONFIG_NET_IPV4) &&
391 test_ip->addr_family == AF_INET) {
392 struct in_addr *addr =
393 &((struct in_addr *)test_ip->ipaddr)[i];
394
395 pos += snprintk(buf + pos, len - pos,
396 "%s%s", pos > 1 ? "," : "",
397 net_sprint_ipv4_addr(addr));
398
399 } else if (IS_ENABLED(CONFIG_NET_IPV6) &&
400 test_ip->addr_family == AF_INET6) {
401 struct in6_addr *addr =
402 &((struct in6_addr *)test_ip->ipaddr)[i];
403
404 pos += snprintk(buf + pos, len - pos,
405 "%s%s", pos > 1 ? "," : "",
406 net_sprint_ipv6_addr(addr));
407 }
408 }
409
410 if (pos >= len) {
411 goto out;
412 }
413
414 buf[pos] = ']';
415
416 } else if (test->type == NPF_TEST_TYPE_ETH_SRC_ADDR_MATCH ||
417 test->type == NPF_TEST_TYPE_ETH_SRC_ADDR_UNMATCH ||
418 test->type == NPF_TEST_TYPE_ETH_DST_ADDR_MATCH ||
419 test->type == NPF_TEST_TYPE_ETH_DST_ADDR_UNMATCH ||
420 test->type == NPF_TEST_TYPE_ETH_SRC_ADDR_MASK_MATCH ||
421 test->type == NPF_TEST_TYPE_ETH_DST_ADDR_MASK_MATCH) {
422 struct npf_test_eth_addr *test_eth =
423 CONTAINER_OF(test, struct npf_test_eth_addr, test);
424 int pos = 1;
425
426 if (len < 2) {
427 goto out;
428 }
429
430 if (test_eth->nb_addresses == 0) {
431 snprintk(buf, len, "[]");
432 goto out;
433 }
434
435 buf[0] = '[';
436
437 for (uint32_t i = 0; i < test_eth->nb_addresses; i++) {
438 pos += snprintk(buf + pos, len - pos,
439 "%s%s", pos > 1 ? "," : "",
440 net_sprint_ll_addr(
441 (const uint8_t *)(&test_eth->addresses[i]),
442 NET_ETH_ADDR_LEN));
443 }
444
445 if (pos >= len) {
446 goto out;
447 }
448
449 buf[pos] = ']';
450
451 } else if (test->type == NPF_TEST_TYPE_ETH_TYPE_MATCH ||
452 test->type == NPF_TEST_TYPE_ETH_TYPE_UNMATCH ||
453 test->type == NPF_TEST_TYPE_ETH_VLAN_TYPE_MATCH ||
454 test->type == NPF_TEST_TYPE_ETH_VLAN_TYPE_UNMATCH) {
455 struct npf_test_eth_type *test_eth =
456 CONTAINER_OF(test, struct npf_test_eth_type, test);
457
458 snprintk(buf, len, "[0x%04x]", ntohs(test_eth->type));
459 }
460
461 out:
462 return test->name;
463 #else
464 return "<UNKNOWN>";
465 #endif
466 }
467