| /** @file |
| * @brief Network packet filtering public header file |
| * |
| * The network packet filtering provides a mechanism for deciding the fate |
| * of an incoming or outgoing packet based on a set of basic rules. |
| */ |
| |
| /* |
| * Copyright (c) 2021 BayLibre SAS |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_NET_PKT_FILTER_H_ |
| #define ZEPHYR_INCLUDE_NET_PKT_FILTER_H_ |
| |
| #include <limits.h> |
| #include <stdbool.h> |
| #include <sys/slist.h> |
| #include <net/net_core.h> |
| #include <net/ethernet.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** |
| * @brief Network Packet Filter API |
| * @defgroup net_pkt_filter Network Packet Filter API |
| * @ingroup networking |
| * @{ |
| */ |
| |
| /** @cond INTERNAL_HIDDEN */ |
| |
| struct npf_test; |
| |
| typedef bool (npf_test_fn_t)(struct npf_test *test, struct net_pkt *pkt); |
| |
| /** @endcond */ |
| |
| /** @brief common filter test structure to be embedded into larger structures */ |
| struct npf_test { |
| npf_test_fn_t *fn; /**< packet condition test function */ |
| }; |
| |
| /** @brief filter rule structure */ |
| struct npf_rule { |
| sys_snode_t node; |
| enum net_verdict result; /**< result if all tests pass */ |
| uint32_t nb_tests; /**< number of tests for this rule */ |
| struct npf_test *tests[]; /**< pointers to @ref npf_test instances */ |
| }; |
| |
| /** @brief Default rule list termination for accepting a packet */ |
| extern struct npf_rule npf_default_ok; |
| /** @brief Default rule list termination for rejecting a packet */ |
| extern struct npf_rule npf_default_drop; |
| |
| /** @brief rule set for a given test location */ |
| struct npf_rule_list { |
| sys_slist_t rule_head; |
| struct k_spinlock lock; |
| }; |
| |
| /** @brief rule list applied to outgoing packets */ |
| extern struct npf_rule_list npf_send_rules; |
| /** @brief rule list applied to incoming packets */ |
| extern struct npf_rule_list npf_recv_rules; |
| |
| /** |
| * @brief Insert a rule at the front of given rule list |
| * |
| * @param rules the affected rule list |
| * @param rule the rule to be inserted |
| */ |
| void npf_insert_rule(struct npf_rule_list *rules, struct npf_rule *rule); |
| |
| /** |
| * @brief Append a rule at the end of given rule list |
| * |
| * @param rules the affected rule list |
| * @param rule the rule to be appended |
| */ |
| void npf_append_rule(struct npf_rule_list *rules, struct npf_rule *rule); |
| |
| /** |
| * @brief Remove a rule from the given rule list |
| * |
| * @param rules the affected rule list |
| * @param rule the rule to be removed |
| * @retval true if given rule was found in the rule list and removed |
| */ |
| bool npf_remove_rule(struct npf_rule_list *rules, struct npf_rule *rule); |
| |
| /** |
| * @brief Remove all rules from the given rule list |
| * |
| * @param rules the affected rule list |
| * @retval true if at least one rule was removed from the rule list |
| */ |
| bool npf_remove_all_rules(struct npf_rule_list *rules); |
| |
| /* convenience shortcuts */ |
| #define npf_insert_send_rule(rule) npf_insert_rule(&npf_send_rules, rule) |
| #define npf_insert_recv_rule(rule) npf_insert_rule(&npf_recv_rules, rule) |
| #define npf_append_send_rule(rule) npf_append_rule(&npf_send_rules, rule) |
| #define npf_append_recv_rule(rule) npf_append_rule(&npf_recv_rules, rule) |
| #define npf_remove_send_rule(rule) npf_remove_rule(&npf_send_rules, rule) |
| #define npf_remove_recv_rule(rule) npf_remove_rule(&npf_recv_rules, rule) |
| #define npf_remove_all_send_rules() npf_remove_all_rules(&npf_send_rules) |
| #define npf_remove_all_recv_rules() npf_remove_all_rules(&npf_recv_rules) |
| |
| /** |
| * @brief Statically define one packet filter rule |
| * |
| * This creates a rule from a variable amount of filter conditions. |
| * This rule can then be inserted or appended to the rule list for a given |
| * network packet path. |
| * |
| * Example: |
| * |
| * @code{.c} |
| * |
| * static NPF_SIZE_MAX(maxsize_200, 200); |
| * static NPF_ETH_TYPE_MATCH(ip_packet, NET_ETH_PTYPE_IP); |
| * |
| * static NPF_RULE(small_ip_pkt, NET_OK, ip_packet, maxsize_200); |
| * |
| * void install_my_filter(void) |
| * { |
| * npf_insert_recv_rule(&npf_default_drop); |
| * npf_insert_recv_rule(&small_ip_pkt); |
| * } |
| * |
| * @endcode |
| * |
| * The above would accept IP packets that are 200 bytes or smaller, and drop |
| * all other packets. |
| * |
| * Another (less efficient) way to create the same result could be: |
| * |
| * @code{.c} |
| * |
| * static NPF_SIZE_MIN(minsize_201, 201); |
| * static NPF_ETH_TYPE_UNMATCH(not_ip_packet, NET_ETH_PTYPE_IP); |
| * |
| * static NPF_RULE(reject_big_pkts, NET_DROP, minsize_201); |
| * static NPF_RULE(reject_non_ip, NET_DROP, not_ip_packet); |
| * |
| * void install_my_filter(void) { |
| * npf_append_recv_rule(&reject_big_pkts); |
| * npf_append_recv_rule(&reject_non_ip); |
| * npf_append_recv_rule(&npf_default_ok); |
| * } |
| * |
| * @endcode |
| * |
| * The first rule in the list for which all conditions are true determines |
| * the fate of the packet. If one condition is false then the next rule in |
| * the list is evaluated. |
| * |
| * @param _name Name for this rule. |
| * @param _result Fate of the packet if all conditions are true, either |
| * <tt>NET_OK</tt> or <tt>NET_DROP</tt>. |
| * @param ... List of conditions for this rule. |
| */ |
| #define NPF_RULE(_name, _result, ...) \ |
| struct npf_rule _name = { \ |
| .result = (_result), \ |
| .nb_tests = NUM_VA_ARGS_LESS_1(__VA_ARGS__) + 1, \ |
| .tests = { FOR_EACH(Z_NPF_TEST_ADDR, (,), __VA_ARGS__) }, \ |
| } |
| |
| #define Z_NPF_TEST_ADDR(arg) &arg.test |
| |
| /** @} */ |
| |
| /** |
| * @defgroup npf_basic_cond Basic Filter Conditions |
| * @ingroup net_pkt_filter |
| * @{ |
| */ |
| |
| /** @cond INTERNAL_HIDDEN */ |
| |
| struct npf_test_iface { |
| struct npf_test test; |
| struct net_if *iface; |
| }; |
| |
| extern npf_test_fn_t npf_iface_match; |
| extern npf_test_fn_t npf_iface_unmatch; |
| extern npf_test_fn_t npf_orig_iface_match; |
| extern npf_test_fn_t npf_orig_iface_unmatch; |
| |
| /** @endcond */ |
| |
| /** |
| * @brief Statically define an "interface match" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _iface Interface to match |
| */ |
| #define NPF_IFACE_MATCH(_name, _iface) \ |
| struct npf_test_iface _name = { \ |
| .iface = (_iface), \ |
| .test.fn = npf_iface_match, \ |
| } |
| |
| /** |
| * @brief Statically define an "interface unmatch" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _iface Interface to exclude |
| */ |
| #define NPF_IFACE_UNMATCH(_name, _iface) \ |
| struct npf_test_iface _name = { \ |
| .iface = (_iface), \ |
| .test.fn = npf_iface_unmatch, \ |
| } |
| |
| /** |
| * @brief Statically define an "orig interface match" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _iface Interface to match |
| */ |
| #define NPF_ORIG_IFACE_MATCH(_name, _iface) \ |
| struct npf_test_iface _name = { \ |
| .iface = (_iface), \ |
| .test.fn = npf_orig_iface_match, \ |
| } |
| |
| /** |
| * @brief Statically define an "orig interface unmatch" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _iface Interface to exclude |
| */ |
| #define NPF_ORIG_IFACE_UNMATCH(_name, _iface) \ |
| struct npf_test_iface _name = { \ |
| .iface = (_iface), \ |
| .test.fn = npf_orig_iface_unmatch, \ |
| } |
| |
| /** @cond INTERNAL_HIDDEN */ |
| |
| struct npf_test_size_bounds { |
| struct npf_test test; |
| size_t min; |
| size_t max; |
| }; |
| |
| extern npf_test_fn_t npf_size_inbounds; |
| |
| /** @endcond */ |
| |
| /** |
| * @brief Statically define a "data minimum size" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _size Lower bound of the packet's data size |
| */ |
| #define NPF_SIZE_MIN(_name, _size) \ |
| struct npf_test_size_bounds _name = { \ |
| .min = (_size), \ |
| .max = SIZE_MAX, \ |
| .test.fn = npf_size_inbounds, \ |
| } |
| |
| /** |
| * @brief Statically define a "data maximum size" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _size Higher bound of the packet's data size |
| */ |
| #define NPF_SIZE_MAX(_name, _size) \ |
| struct npf_test_size_bounds _name = { \ |
| .min = 0, \ |
| .max = (_size), \ |
| .test.fn = npf_size_inbounds, \ |
| } |
| |
| /** |
| * @brief Statically define a "data bounded size" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _min_size Lower bound of the packet's data size |
| * @param _max_size Higher bound of the packet's data size |
| */ |
| #define NPF_SIZE_BOUNDS(_name, _min_size, _max_size) \ |
| struct npf_test_size_bounds _name = { \ |
| .min = (_min_size), \ |
| .max = (_max_size), \ |
| .test.fn = npf_size_inbounds, \ |
| } |
| |
| /** @} */ |
| |
| /** |
| * @defgroup npf_eth_cond Ethernet Filter Conditions |
| * @ingroup net_pkt_filter |
| * @{ |
| */ |
| |
| /** @cond INTERNAL_HIDDEN */ |
| |
| struct npf_test_eth_addr { |
| struct npf_test test; |
| unsigned int nb_addresses; |
| struct net_eth_addr *addresses; |
| struct net_eth_addr mask; |
| }; |
| |
| extern npf_test_fn_t npf_eth_src_addr_match; |
| extern npf_test_fn_t npf_eth_src_addr_unmatch; |
| extern npf_test_fn_t npf_eth_dst_addr_match; |
| extern npf_test_fn_t npf_eth_dst_addr_unmatch; |
| |
| /** @endcond */ |
| |
| /** |
| * @brief Statically define a "source address match" packet filter condition |
| * |
| * This tests if the packet source address matches any of the Ethernet |
| * addresses contained in the provided set. |
| * |
| * @param _name Name of the condition |
| * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against |
| */ |
| #define NPF_ETH_SRC_ADDR_MATCH(_name, _addr_array) \ |
| struct npf_test_eth_addr _name = { \ |
| .addresses = (_addr_array), \ |
| .nb_addresses = ARRAY_SIZE(_addr_array), \ |
| .test.fn = npf_eth_src_addr_match, \ |
| .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \ |
| } |
| |
| /** |
| * @brief Statically define a "source address unmatch" packet filter condition |
| * |
| * This tests if the packet source address matches none of the Ethernet |
| * addresses contained in the provided set. |
| * |
| * @param _name Name of the condition |
| * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against |
| */ |
| #define NPF_ETH_SRC_ADDR_UNMATCH(_name, _addr_array) \ |
| struct npf_test_eth_addr _name = { \ |
| .addresses = (_addr_array), \ |
| .nb_addresses = ARRAY_SIZE(_addr_array), \ |
| .test.fn = npf_eth_src_addr_unmatch, \ |
| .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \ |
| } |
| |
| /** |
| * @brief Statically define a "destination address match" packet filter condition |
| * |
| * This tests if the packet destination address matches any of the Ethernet |
| * addresses contained in the provided set. |
| * |
| * @param _name Name of the condition |
| * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against |
| */ |
| #define NPF_ETH_DST_ADDR_MATCH(_name, _addr_array) \ |
| struct npf_test_eth_addr _name = { \ |
| .addresses = (_addr_array), \ |
| .nb_addresses = ARRAY_SIZE(_addr_array), \ |
| .test.fn = npf_eth_dst_addr_match, \ |
| .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \ |
| } |
| |
| /** |
| * @brief Statically define a "destination address unmatch" packet filter condition |
| * |
| * This tests if the packet destination address matches none of the Ethernet |
| * addresses contained in the provided set. |
| * |
| * @param _name Name of the condition |
| * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against |
| */ |
| #define NPF_ETH_DST_ADDR_UNMATCH(_name, _addr_array) \ |
| struct npf_test_eth_addr _name = { \ |
| .addresses = (_addr_array), \ |
| .nb_addresses = ARRAY_SIZE(_addr_array), \ |
| .test.fn = npf_eth_dst_addr_unmatch, \ |
| .mask.addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, \ |
| } |
| |
| /** |
| * @brief Statically define a "source address match with mask" packet filter condition |
| * |
| * This tests if the packet source address matches any of the Ethernet |
| * addresses contained in the provided set after applying specified mask. |
| * |
| * @param _name Name of the condition |
| * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against |
| * @param ... up to 6 mask bytes |
| */ |
| #define NPF_ETH_SRC_ADDR_MASK_MATCH(_name, _addr_array, ...) \ |
| struct npf_test_eth_addr _name = { \ |
| .addresses = (_addr_array), \ |
| .nb_addresses = ARRAY_SIZE(_addr_array), \ |
| .mask.addr = { __VA_ARGS__ }, \ |
| .test.fn = npf_eth_src_addr_match, \ |
| } |
| |
| /** |
| * @brief Statically define a "destination address match with mask" packet filter condition |
| * |
| * This tests if the packet destination address matches any of the Ethernet |
| * addresses contained in the provided set after applying specified mask. |
| * |
| * @param _name Name of the condition |
| * @param _addr_array Array of <tt>struct net_eth_addr</tt> items to test against |
| * @param ... up to 6 mask bytes |
| */ |
| #define NPF_ETH_DST_ADDR_MASK_MATCH(_name, _addr_array, ...) \ |
| struct npf_test_eth_addr _name = { \ |
| .addresses = (_addr_array), \ |
| .nb_addresses = ARRAY_SIZE(_addr_array), \ |
| .mask.addr = { __VA_ARGS__ }, \ |
| .test.fn = npf_eth_dst_addr_match, \ |
| } |
| |
| /** @cond INTERNAL_HIDDEN */ |
| |
| struct npf_test_eth_type { |
| struct npf_test test; |
| uint16_t type; /* type in network order */ |
| }; |
| |
| extern npf_test_fn_t npf_eth_type_match; |
| extern npf_test_fn_t npf_eth_type_unmatch; |
| |
| /** @endcond */ |
| |
| /** |
| * @brief Statically define an "Ethernet type match" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _type Ethernet type to match |
| */ |
| #define NPF_ETH_TYPE_MATCH(_name, _type) \ |
| struct npf_test_eth_type _name = { \ |
| .type = htons(_type), \ |
| .test.fn = npf_eth_type_match, \ |
| } |
| |
| /** |
| * @brief Statically define an "Ethernet type unmatch" packet filter condition |
| * |
| * @param _name Name of the condition |
| * @param _type Ethernet type to exclude |
| */ |
| #define NPF_ETH_TYPE_UNMATCH(_name, _type) \ |
| struct npf_test_eth_type _name = { \ |
| .type = htons(_type), \ |
| .test.fn = npf_eth_type_unmatch, \ |
| } |
| |
| /** @} */ |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* ZEPHYR_INCLUDE_NET_PKT_FILTER_H_ */ |