Line data Source code
1 1 : /** @file
2 : * @brief Network packet filtering public header file
3 : *
4 : * The network packet filtering provides a mechanism for deciding the fate
5 : * of an incoming or outgoing packet based on a set of basic rules.
6 : */
7 :
8 : /*
9 : * Copyright (c) 2021 BayLibre SAS
10 : *
11 : * SPDX-License-Identifier: Apache-2.0
12 : */
13 :
14 : #ifndef ZEPHYR_INCLUDE_NET_PKT_FILTER_H_
15 : #define ZEPHYR_INCLUDE_NET_PKT_FILTER_H_
16 :
17 : #include <limits.h>
18 : #include <stdbool.h>
19 : #include <zephyr/sys/slist.h>
20 : #include <zephyr/net/net_core.h>
21 : #include <zephyr/net/ethernet.h>
22 :
23 : #ifdef __cplusplus
24 : extern "C" {
25 : #endif
26 :
27 : /**
28 : * @brief Network Packet Filter API
29 : * @defgroup net_pkt_filter Network Packet Filter API
30 : * @since 3.0
31 : * @version 0.8.0
32 : * @ingroup networking
33 : * @{
34 : */
35 :
36 : /** @cond INTERNAL_HIDDEN */
37 :
38 : struct npf_test;
39 :
40 : typedef bool (npf_test_fn_t)(struct npf_test *test, struct net_pkt *pkt);
41 :
42 : /** @endcond */
43 :
44 : /** @brief common filter test structure to be embedded into larger structures */
45 1 : struct npf_test {
46 1 : npf_test_fn_t *fn; /**< packet condition test function */
47 : };
48 :
49 : /** @brief filter rule structure */
50 1 : struct npf_rule {
51 1 : sys_snode_t node; /**< Slist rule list node */
52 1 : enum net_verdict result; /**< result if all tests pass */
53 1 : uint32_t nb_tests; /**< number of tests for this rule */
54 1 : struct npf_test *tests[]; /**< pointers to @ref npf_test instances */
55 : };
56 :
57 : /** @brief Default rule list termination for accepting a packet */
58 1 : extern struct npf_rule npf_default_ok;
59 : /** @brief Default rule list termination for rejecting a packet */
60 1 : extern struct npf_rule npf_default_drop;
61 :
62 : /** @brief rule set for a given test location */
63 1 : struct npf_rule_list {
64 1 : sys_slist_t rule_head; /**< List head */
65 1 : struct k_spinlock lock; /**< Lock protecting the list access */
66 : };
67 :
68 : /** @brief rule list applied to outgoing packets */
69 1 : extern struct npf_rule_list npf_send_rules;
70 : /** @brief rule list applied to incoming packets */
71 1 : extern struct npf_rule_list npf_recv_rules;
72 : /** @brief rule list applied for local incoming packets */
73 1 : extern struct npf_rule_list npf_local_in_recv_rules;
74 : /** @brief rule list applied for IPv4 incoming packets */
75 1 : extern struct npf_rule_list npf_ipv4_recv_rules;
76 : /** @brief rule list applied for IPv6 incoming packets */
77 1 : extern struct npf_rule_list npf_ipv6_recv_rules;
78 :
79 : /**
80 : * @brief Insert a rule at the front of given rule list
81 : *
82 : * @param rules the affected rule list
83 : * @param rule the rule to be inserted
84 : */
85 1 : void npf_insert_rule(struct npf_rule_list *rules, struct npf_rule *rule);
86 :
87 : /**
88 : * @brief Append a rule at the end of given rule list
89 : *
90 : * @param rules the affected rule list
91 : * @param rule the rule to be appended
92 : */
93 1 : void npf_append_rule(struct npf_rule_list *rules, struct npf_rule *rule);
94 :
95 : /**
96 : * @brief Remove a rule from the given rule list
97 : *
98 : * @param rules the affected rule list
99 : * @param rule the rule to be removed
100 : * @retval true if given rule was found in the rule list and removed
101 : */
102 1 : bool npf_remove_rule(struct npf_rule_list *rules, struct npf_rule *rule);
103 :
104 : /**
105 : * @brief Remove all rules from the given rule list
106 : *
107 : * @param rules the affected rule list
108 : * @retval true if at least one rule was removed from the rule list
109 : */
110 1 : bool npf_remove_all_rules(struct npf_rule_list *rules);
111 :
112 : /** @cond INTERNAL_HIDDEN */
113 :
114 : /* convenience shortcuts */
115 : #define npf_insert_send_rule(rule) npf_insert_rule(&npf_send_rules, rule)
116 : #define npf_insert_recv_rule(rule) npf_insert_rule(&npf_recv_rules, rule)
117 : #define npf_append_send_rule(rule) npf_append_rule(&npf_send_rules, rule)
118 : #define npf_append_recv_rule(rule) npf_append_rule(&npf_recv_rules, rule)
119 : #define npf_remove_send_rule(rule) npf_remove_rule(&npf_send_rules, rule)
120 : #define npf_remove_recv_rule(rule) npf_remove_rule(&npf_recv_rules, rule)
121 : #define npf_remove_all_send_rules() npf_remove_all_rules(&npf_send_rules)
122 : #define npf_remove_all_recv_rules() npf_remove_all_rules(&npf_recv_rules)
123 :
124 : #ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
125 : #define npf_insert_local_in_recv_rule(rule) npf_insert_rule(&npf_local_in_recv_rules, rule)
126 : #define npf_append_local_in_recv_rule(rule) npf_append_rule(&npf_local_in_recv_rules, rule)
127 : #define npf_remove_local_in_recv_rule(rule) npf_remove_rule(&npf_local_in_recv_rules, rule)
128 : #define npf_remove_all_local_in_recv_rules() npf_remove_all_rules(&npf_local_in_recv_rules)
129 : #endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
130 :
131 : #ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
132 : #define npf_insert_ipv4_recv_rule(rule) npf_insert_rule(&npf_ipv4_recv_rules, rule)
133 : #define npf_append_ipv4_recv_rule(rule) npf_append_rule(&npf_ipv4_recv_rules, rule)
134 : #define npf_remove_ipv4_recv_rule(rule) npf_remove_rule(&npf_ipv4_recv_rules, rule)
135 : #define npf_remove_all_ipv4_recv_rules() npf_remove_all_rules(&npf_ipv4_recv_rules)
136 : #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */
137 :
138 : #ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
139 : #define npf_insert_ipv6_recv_rule(rule) npf_insert_rule(&npf_ipv6_recv_rules, rule)
140 : #define npf_append_ipv6_recv_rule(rule) npf_append_rule(&npf_ipv6_recv_rules, rule)
141 : #define npf_remove_ipv6_recv_rule(rule) npf_remove_rule(&npf_ipv6_recv_rules, rule)
142 : #define npf_remove_all_ipv6_recv_rules() npf_remove_all_rules(&npf_ipv6_recv_rules)
143 : #endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */
144 :
145 : /** @endcond */
146 :
147 : /**
148 : * @brief Statically define one packet filter rule
149 : *
150 : * This creates a rule from a variable amount of filter conditions.
151 : * This rule can then be inserted or appended to the rule list for a given
152 : * network packet path.
153 : *
154 : * Example:
155 : *
156 : * @code{.c}
157 : *
158 : * static NPF_SIZE_MAX(maxsize_200, 200);
159 : * static NPF_ETH_TYPE_MATCH(ip_packet, NET_ETH_PTYPE_IP);
160 : *
161 : * static NPF_RULE(small_ip_pkt, NET_OK, ip_packet, maxsize_200);
162 : *
163 : * void install_my_filter(void)
164 : * {
165 : * npf_insert_recv_rule(&npf_default_drop);
166 : * npf_insert_recv_rule(&small_ip_pkt);
167 : * }
168 : *
169 : * @endcode
170 : *
171 : * The above would accept IP packets that are 200 bytes or smaller, and drop
172 : * all other packets.
173 : *
174 : * Another (less efficient) way to create the same result could be:
175 : *
176 : * @code{.c}
177 : *
178 : * static NPF_SIZE_MIN(minsize_201, 201);
179 : * static NPF_ETH_TYPE_UNMATCH(not_ip_packet, NET_ETH_PTYPE_IP);
180 : *
181 : * static NPF_RULE(reject_big_pkts, NET_DROP, minsize_201);
182 : * static NPF_RULE(reject_non_ip, NET_DROP, not_ip_packet);
183 : *
184 : * void install_my_filter(void) {
185 : * npf_append_recv_rule(&reject_big_pkts);
186 : * npf_append_recv_rule(&reject_non_ip);
187 : * npf_append_recv_rule(&npf_default_ok);
188 : * }
189 : *
190 : * @endcode
191 : *
192 : * The first rule in the list for which all conditions are true determines
193 : * the fate of the packet. If one condition is false then the next rule in
194 : * the list is evaluated.
195 : *
196 : * @param _name Name for this rule.
197 : * @param _result Fate of the packet if all conditions are true, either
198 : * <tt>NET_OK</tt> or <tt>NET_DROP</tt>.
199 : * @param ... List of conditions for this rule.
200 : */
201 1 : #define NPF_RULE(_name, _result, ...) \
202 : struct npf_rule _name = { \
203 : .result = (_result), \
204 : .nb_tests = NUM_VA_ARGS_LESS_1(__VA_ARGS__) + 1, \
205 : .tests = { FOR_EACH(Z_NPF_TEST_ADDR, (,), __VA_ARGS__) }, \
206 : }
207 :
208 : #define Z_NPF_TEST_ADDR(arg) &arg.test
209 :
210 : /** @} */
211 :
212 : /**
213 : * @defgroup npf_basic_cond Basic Filter Conditions
214 : * @since 3.0
215 : * @version 0.8.0
216 : * @ingroup net_pkt_filter
217 : * @{
218 : */
219 :
220 : /** @cond INTERNAL_HIDDEN */
221 :
222 : struct npf_test_iface {
223 : struct npf_test test;
224 : struct net_if *iface;
225 : };
226 :
227 : extern npf_test_fn_t npf_iface_match;
228 : extern npf_test_fn_t npf_iface_unmatch;
229 : extern npf_test_fn_t npf_orig_iface_match;
230 : extern npf_test_fn_t npf_orig_iface_unmatch;
231 :
232 : /** @endcond */
233 :
234 : /**
235 : * @brief Statically define an "interface match" packet filter condition
236 : *
237 : * @param _name Name of the condition
238 : * @param _iface Interface to match
239 : */
240 1 : #define NPF_IFACE_MATCH(_name, _iface) \
241 : struct npf_test_iface _name = { \
242 : .iface = (_iface), \
243 : .test.fn = npf_iface_match, \
244 : }
245 :
246 : /**
247 : * @brief Statically define an "interface unmatch" packet filter condition
248 : *
249 : * @param _name Name of the condition
250 : * @param _iface Interface to exclude
251 : */
252 1 : #define NPF_IFACE_UNMATCH(_name, _iface) \
253 : struct npf_test_iface _name = { \
254 : .iface = (_iface), \
255 : .test.fn = npf_iface_unmatch, \
256 : }
257 :
258 : /**
259 : * @brief Statically define an "orig interface match" packet filter condition
260 : *
261 : * @param _name Name of the condition
262 : * @param _iface Interface to match
263 : */
264 1 : #define NPF_ORIG_IFACE_MATCH(_name, _iface) \
265 : struct npf_test_iface _name = { \
266 : .iface = (_iface), \
267 : .test.fn = npf_orig_iface_match, \
268 : }
269 :
270 : /**
271 : * @brief Statically define an "orig interface unmatch" packet filter condition
272 : *
273 : * @param _name Name of the condition
274 : * @param _iface Interface to exclude
275 : */
276 1 : #define NPF_ORIG_IFACE_UNMATCH(_name, _iface) \
277 : struct npf_test_iface _name = { \
278 : .iface = (_iface), \
279 : .test.fn = npf_orig_iface_unmatch, \
280 : }
281 :
282 : /** @cond INTERNAL_HIDDEN */
283 :
284 : struct npf_test_size_bounds {
285 : struct npf_test test;
286 : size_t min;
287 : size_t max;
288 : };
289 :
290 : extern npf_test_fn_t npf_size_inbounds;
291 :
292 : /** @endcond */
293 :
294 : /**
295 : * @brief Statically define a "data minimum size" packet filter condition
296 : *
297 : * @param _name Name of the condition
298 : * @param _size Lower bound of the packet's data size
299 : */
300 1 : #define NPF_SIZE_MIN(_name, _size) \
301 : struct npf_test_size_bounds _name = { \
302 : .min = (_size), \
303 : .max = SIZE_MAX, \
304 : .test.fn = npf_size_inbounds, \
305 : }
306 :
307 : /**
308 : * @brief Statically define a "data maximum size" packet filter condition
309 : *
310 : * @param _name Name of the condition
311 : * @param _size Higher bound of the packet's data size
312 : */
313 1 : #define NPF_SIZE_MAX(_name, _size) \
314 : struct npf_test_size_bounds _name = { \
315 : .min = 0, \
316 : .max = (_size), \
317 : .test.fn = npf_size_inbounds, \
318 : }
319 :
320 : /**
321 : * @brief Statically define a "data bounded size" packet filter condition
322 : *
323 : * @param _name Name of the condition
324 : * @param _min_size Lower bound of the packet's data size
325 : * @param _max_size Higher bound of the packet's data size
326 : */
327 1 : #define NPF_SIZE_BOUNDS(_name, _min_size, _max_size) \
328 : struct npf_test_size_bounds _name = { \
329 : .min = (_min_size), \
330 : .max = (_max_size), \
331 : .test.fn = npf_size_inbounds, \
332 : }
333 :
334 : /** @cond INTERNAL_HIDDEN */
335 :
336 : struct npf_test_ip {
337 : struct npf_test test;
338 : uint8_t addr_family;
339 : void *ipaddr;
340 : uint32_t ipaddr_num;
341 : };
342 :
343 : extern npf_test_fn_t npf_ip_src_addr_match;
344 : extern npf_test_fn_t npf_ip_src_addr_unmatch;
345 :
346 : /** @endcond */
347 :
348 : /**
349 : * @brief Statically define a "ip address allowlist" packet filter condition
350 : *
351 : * This tests if the packet source ip address matches any of the ip
352 : * addresses contained in the provided set.
353 : *
354 : * @param _name Name of the condition
355 : * @param _ip_addr_array Array of <tt>struct in_addr</tt> or <tt>struct in6_addr</tt> items to test
356 : *against
357 : * @param _ip_addr_num number of IP addresses in the array
358 : * @param _af Addresses family type (AF_INET / AF_INET6) in the array
359 : */
360 1 : #define NPF_IP_SRC_ADDR_ALLOWLIST(_name, _ip_addr_array, _ip_addr_num, _af) \
361 : struct npf_test_ip _name = { \
362 : .addr_family = _af, \
363 : .ipaddr = (_ip_addr_array), \
364 : .ipaddr_num = _ip_addr_num, \
365 : .test.fn = npf_ip_src_addr_match, \
366 : }
367 :
368 : /**
369 : * @brief Statically define a "ip address blocklist" packet filter condition
370 : *
371 : * This tests if the packet source ip address matches any of the ip
372 : * addresses contained in the provided set.
373 : *
374 : * @param _name Name of the condition
375 : * @param _ip_addr_array Array of <tt>struct in_addr</tt> or <tt>struct in6_addr</tt> items to test
376 : *against
377 : * @param _ip_addr_num number of IP addresses in the array
378 : * @param _af Addresses family type (AF_INET / AF_INET6) in the array
379 : */
380 1 : #define NPF_IP_SRC_ADDR_BLOCKLIST(_name, _ip_addr_array, _ip_addr_num, _af) \
381 : struct npf_test_ip _name = { \
382 : .addr_family = _af, \
383 : .ipaddr = (_ip_addr_array), \
384 : .ipaddr_num = _ip_addr_num, \
385 : .test.fn = npf_ip_src_addr_unmatch, \
386 : }
387 :
388 : /** @} */
389 :
390 : /**
391 : * @defgroup npf_eth_cond Ethernet Filter Conditions
392 : * @ingroup net_pkt_filter
393 : * @since 3.0
394 : * @version 0.8.0
395 : * @{
396 : */
397 :
398 : /** @cond INTERNAL_HIDDEN */
399 :
400 : struct npf_test_eth_addr {
401 : struct npf_test test;
402 : unsigned int nb_addresses;
403 : struct net_eth_addr *addresses;
404 : struct net_eth_addr mask;
405 : };
406 :
407 : extern npf_test_fn_t npf_eth_src_addr_match;
408 : extern npf_test_fn_t npf_eth_src_addr_unmatch;
409 : extern npf_test_fn_t npf_eth_dst_addr_match;
410 : extern npf_test_fn_t npf_eth_dst_addr_unmatch;
411 :
412 : /** @endcond */
413 :
414 : /**
415 : * @brief Statically define a "source address match" packet filter condition
416 : *
417 : * This tests if the packet source address matches any of the Ethernet
418 : * addresses contained in the provided set.
419 : *
420 : * @param _name Name of the condition
421 : * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against
422 : */
423 1 : #define NPF_ETH_SRC_ADDR_MATCH(_name, _addr_array) \
424 : struct npf_test_eth_addr _name = { \
425 : .addresses = (_addr_array), \
426 : .nb_addresses = ARRAY_SIZE(_addr_array), \
427 : .test.fn = npf_eth_src_addr_match, \
428 : .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \
429 : }
430 :
431 : /**
432 : * @brief Statically define a "source address unmatch" packet filter condition
433 : *
434 : * This tests if the packet source address matches none of the Ethernet
435 : * addresses contained in the provided set.
436 : *
437 : * @param _name Name of the condition
438 : * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against
439 : */
440 1 : #define NPF_ETH_SRC_ADDR_UNMATCH(_name, _addr_array) \
441 : struct npf_test_eth_addr _name = { \
442 : .addresses = (_addr_array), \
443 : .nb_addresses = ARRAY_SIZE(_addr_array), \
444 : .test.fn = npf_eth_src_addr_unmatch, \
445 : .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \
446 : }
447 :
448 : /**
449 : * @brief Statically define a "destination address match" packet filter condition
450 : *
451 : * This tests if the packet destination address matches any of the Ethernet
452 : * addresses contained in the provided set.
453 : *
454 : * @param _name Name of the condition
455 : * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against
456 : */
457 1 : #define NPF_ETH_DST_ADDR_MATCH(_name, _addr_array) \
458 : struct npf_test_eth_addr _name = { \
459 : .addresses = (_addr_array), \
460 : .nb_addresses = ARRAY_SIZE(_addr_array), \
461 : .test.fn = npf_eth_dst_addr_match, \
462 : .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \
463 : }
464 :
465 : /**
466 : * @brief Statically define a "destination address unmatch" packet filter condition
467 : *
468 : * This tests if the packet destination address matches none of the Ethernet
469 : * addresses contained in the provided set.
470 : *
471 : * @param _name Name of the condition
472 : * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against
473 : */
474 1 : #define NPF_ETH_DST_ADDR_UNMATCH(_name, _addr_array) \
475 : struct npf_test_eth_addr _name = { \
476 : .addresses = (_addr_array), \
477 : .nb_addresses = ARRAY_SIZE(_addr_array), \
478 : .test.fn = npf_eth_dst_addr_unmatch, \
479 : .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \
480 : }
481 :
482 : /**
483 : * @brief Statically define a "source address match with mask" packet filter condition
484 : *
485 : * This tests if the packet source address matches any of the Ethernet
486 : * addresses contained in the provided set after applying specified mask.
487 : *
488 : * @param _name Name of the condition
489 : * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against
490 : * @param ... up to 6 mask bytes
491 : */
492 1 : #define NPF_ETH_SRC_ADDR_MASK_MATCH(_name, _addr_array, ...) \
493 : struct npf_test_eth_addr _name = { \
494 : .addresses = (_addr_array), \
495 : .nb_addresses = ARRAY_SIZE(_addr_array), \
496 : .mask.addr = { __VA_ARGS__ }, \
497 : .test.fn = npf_eth_src_addr_match, \
498 : }
499 :
500 : /**
501 : * @brief Statically define a "destination address match with mask" packet filter condition
502 : *
503 : * This tests if the packet destination address matches any of the Ethernet
504 : * addresses contained in the provided set after applying specified mask.
505 : *
506 : * @param _name Name of the condition
507 : * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against
508 : * @param ... up to 6 mask bytes
509 : */
510 1 : #define NPF_ETH_DST_ADDR_MASK_MATCH(_name, _addr_array, ...) \
511 : struct npf_test_eth_addr _name = { \
512 : .addresses = (_addr_array), \
513 : .nb_addresses = ARRAY_SIZE(_addr_array), \
514 : .mask.addr = { __VA_ARGS__ }, \
515 : .test.fn = npf_eth_dst_addr_match, \
516 : }
517 :
518 : /** @cond INTERNAL_HIDDEN */
519 :
520 : struct npf_test_eth_type {
521 : struct npf_test test;
522 : uint16_t type; /* type in network order */
523 : };
524 :
525 : extern npf_test_fn_t npf_eth_type_match;
526 : extern npf_test_fn_t npf_eth_type_unmatch;
527 :
528 : /** @endcond */
529 :
530 : /**
531 : * @brief Statically define an "Ethernet type match" packet filter condition
532 : *
533 : * @param _name Name of the condition
534 : * @param _type Ethernet type to match
535 : */
536 1 : #define NPF_ETH_TYPE_MATCH(_name, _type) \
537 : struct npf_test_eth_type _name = { \
538 : .type = htons(_type), \
539 : .test.fn = npf_eth_type_match, \
540 : }
541 :
542 : /**
543 : * @brief Statically define an "Ethernet type unmatch" packet filter condition
544 : *
545 : * @param _name Name of the condition
546 : * @param _type Ethernet type to exclude
547 : */
548 1 : #define NPF_ETH_TYPE_UNMATCH(_name, _type) \
549 : struct npf_test_eth_type _name = { \
550 : .type = htons(_type), \
551 : .test.fn = npf_eth_type_unmatch, \
552 : }
553 :
554 : /** @} */
555 :
556 : #ifdef __cplusplus
557 : }
558 : #endif
559 :
560 : #endif /* ZEPHYR_INCLUDE_NET_PKT_FILTER_H_ */
|