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 struct npf_test { 46 npf_test_fn_t *fn; /**< packet condition test function */ 47 }; 48 49 /** @brief filter rule structure */ 50 struct npf_rule { 51 sys_snode_t node; /**< Slist rule list node */ 52 enum net_verdict result; /**< result if all tests pass */ 53 uint32_t nb_tests; /**< number of tests for this rule */ 54 struct npf_test *tests[]; /**< pointers to @ref npf_test instances */ 55 }; 56 57 /** @brief Default rule list termination for accepting a packet */ 58 extern struct npf_rule npf_default_ok; 59 /** @brief Default rule list termination for rejecting a packet */ 60 extern struct npf_rule npf_default_drop; 61 62 /** @brief rule set for a given test location */ 63 struct npf_rule_list { 64 sys_slist_t rule_head; /**< List head */ 65 struct k_spinlock lock; /**< Lock protecting the list access */ 66 }; 67 68 /** @brief rule list applied to outgoing packets */ 69 extern struct npf_rule_list npf_send_rules; 70 /** @brief rule list applied to incoming packets */ 71 extern struct npf_rule_list npf_recv_rules; 72 /** @brief rule list applied for local incoming packets */ 73 extern struct npf_rule_list npf_local_in_recv_rules; 74 /** @brief rule list applied for IPv4 incoming packets */ 75 extern struct npf_rule_list npf_ipv4_recv_rules; 76 /** @brief rule list applied for IPv6 incoming packets */ 77 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 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 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 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 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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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_ */ 561