diff --git a/include/zephyr/net/net_pkt.h b/include/zephyr/net/net_pkt.h index 0ffd066981..d540a91d9f 100644 --- a/include/zephyr/net/net_pkt.h +++ b/include/zephyr/net/net_pkt.h @@ -1275,6 +1275,37 @@ static inline bool net_pkt_filter_recv_ok(struct net_pkt *pkt) #endif /* CONFIG_NET_PKT_FILTER */ +#if defined(CONFIG_NET_PKT_FILTER) && \ + (defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK)) + +bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt); + +#else + +static inline bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return true; +} + +#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */ + +#if defined(CONFIG_NET_PKT_FILTER) && defined(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK) + +bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt); + +#else + +static inline bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt) +{ + ARG_UNUSED(pkt); + + return true; +} + +#endif /* CONFIG_NET_PKT_FILTER && CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */ + /* @endcond */ /** diff --git a/include/zephyr/net/net_pkt_filter.h b/include/zephyr/net/net_pkt_filter.h index 7353522530..aa1ad89b0e 100644 --- a/include/zephyr/net/net_pkt_filter.h +++ b/include/zephyr/net/net_pkt_filter.h @@ -67,6 +67,12 @@ struct npf_rule_list { extern struct npf_rule_list npf_send_rules; /** @brief rule list applied to incoming packets */ extern struct npf_rule_list npf_recv_rules; +/** @brief rule list applied for local incoming packets */ +extern struct npf_rule_list npf_local_in_recv_rules; +/** @brief rule list applied for IPv4 incoming packets */ +extern struct npf_rule_list npf_ipv4_recv_rules; +/** @brief rule list applied for IPv6 incoming packets */ +extern struct npf_rule_list npf_ipv6_recv_rules; /** * @brief Insert a rule at the front of given rule list @@ -111,6 +117,27 @@ bool npf_remove_all_rules(struct npf_rule_list *rules); #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) +#ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK +#define npf_insert_local_in_recv_rule(rule) npf_insert_rule(&npf_local_in_recv_rules, rule) +#define npf_append_local_in_recv_rule(rule) npf_append_rule(&npf_local_in_recv_rules, rule) +#define npf_remove_local_in_recv_rule(rule) npf_remove_rule(&npf_local_in_recv_rules, rule) +#define npf_remove_all_local_in_recv_rules() npf_remove_all_rules(&npf_local_in_recv_rules) +#endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */ + +#ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK +#define npf_insert_ipv4_recv_rule(rule) npf_insert_rule(&npf_ipv4_recv_rules, rule) +#define npf_append_ipv4_recv_rule(rule) npf_append_rule(&npf_ipv4_recv_rules, rule) +#define npf_remove_ipv4_recv_rule(rule) npf_remove_rule(&npf_ipv4_recv_rules, rule) +#define npf_remove_all_ipv4_recv_rules() npf_remove_all_rules(&npf_ipv4_recv_rules) +#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */ + +#ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK +#define npf_insert_ipv6_recv_rule(rule) npf_insert_rule(&npf_ipv6_recv_rules, rule) +#define npf_append_ipv6_recv_rule(rule) npf_append_rule(&npf_ipv6_recv_rules, rule) +#define npf_remove_ipv6_recv_rule(rule) npf_remove_rule(&npf_ipv6_recv_rules, rule) +#define npf_remove_all_ipv6_recv_rules() npf_remove_all_rules(&npf_ipv6_recv_rules) +#endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */ + /** * @brief Statically define one packet filter rule * @@ -296,6 +323,60 @@ extern npf_test_fn_t npf_size_inbounds; .test.fn = npf_size_inbounds, \ } +/** @cond INTERNAL_HIDDEN */ + +struct npf_test_ip { + struct npf_test test; + uint8_t addr_family; + void *ipaddr; + uint32_t ipaddr_num; +}; + +extern npf_test_fn_t npf_ip_src_addr_match; +extern npf_test_fn_t npf_ip_src_addr_unmatch; + +/** @endcond */ + +/** + * @brief Statically define a "ip address allowlist" packet filter condition + * + * This tests if the packet source ip address matches any of the ip + * addresses contained in the provided set. + * + * @param _name Name of the condition + * @param _ip_addr_array Array of struct in_addr or struct in6_addr items to test + *against + * @param _ip_addr_num number of IP addresses in the array + * @param _af Addresses family type (AF_INET / AF_INET6) in the array + */ +#define NPF_IP_SRC_ADDR_ALLOWLIST(_name, _ip_addr_array, _ip_addr_num, _af) \ + struct npf_test_ip _name = { \ + .addr_family = _af, \ + .ipaddr = (_ip_addr_array), \ + .ipaddr_num = _ip_addr_num, \ + .test.fn = npf_ip_src_addr_match, \ + } + +/** + * @brief Statically define a "ip address blocklist" packet filter condition + * + * This tests if the packet source ip address matches any of the ip + * addresses contained in the provided set. + * + * @param _name Name of the condition + * @param _ip_addr_array Array of struct in_addr or struct in6_addr items to test + *against + * @param _ip_addr_num number of IP addresses in the array + * @param _af Addresses family type (AF_INET / AF_INET6) in the array + */ +#define NPF_IP_SRC_ADDR_BLOCKLIST(_name, _ip_addr_array, _ip_addr_num, _af) \ + struct npf_test_ip _name = { \ + .addr_family = _af, \ + .ipaddr = (_ip_addr_array), \ + .ipaddr_num = _ip_addr_num, \ + .test.fn = npf_ip_src_addr_unmatch, \ + } + /** @} */ /** diff --git a/subsys/net/ip/connection.c b/subsys/net/ip/connection.c index b370bc1835..225a3fdbaa 100644 --- a/subsys/net/ip/connection.c +++ b/subsys/net/ip/connection.c @@ -576,6 +576,11 @@ enum net_verdict net_conn_input(struct net_pkt *pkt, uint8_t pkt_family = net_pkt_family(pkt); uint16_t src_port = 0U, dst_port = 0U; + if (!net_pkt_filter_local_in_recv_ok(pkt)) { + /* drop the packet */ + return NET_DROP; + } + if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6)) { if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) { src_port = proto_hdr->udp->src_port; diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index 0392e75cec..83827303f6 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -302,6 +302,15 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt) goto drop; } + net_pkt_set_ipv4_ttl(pkt, hdr->ttl); + + net_pkt_set_family(pkt, PF_INET); + + if (!net_pkt_filter_ip_recv_ok(pkt)) { + /* drop the packet */ + return NET_DROP; + } + if ((!net_ipv4_is_my_addr((struct in_addr *)hdr->dst) && !net_ipv4_is_addr_mcast((struct in_addr *)hdr->dst) && !(hdr->proto == IPPROTO_UDP && @@ -326,10 +335,6 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt) } } - net_pkt_set_ipv4_ttl(pkt, hdr->ttl); - - net_pkt_set_family(pkt, PF_INET); - if (IS_ENABLED(CONFIG_NET_IPV4_FRAGMENT)) { /* Check if this is a fragmented packet, and if so, handle reassembly */ if ((ntohs(*((uint16_t *)&hdr->offset[0])) & diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index 4816509975..7ef7583140 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -520,6 +520,11 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_HDR(pkt)->hop_limit); net_pkt_set_family(pkt, PF_INET6); + if (!net_pkt_filter_ip_recv_ok(pkt)) { + /* drop the packet */ + return NET_DROP; + } + if (IS_ENABLED(CONFIG_NET_ROUTE_MCAST) && net_ipv6_is_addr_mcast((struct in6_addr *)hdr->dst)) { /* If the packet is a multicast packet and multicast routing diff --git a/subsys/net/pkt_filter/Kconfig b/subsys/net/pkt_filter/Kconfig index ef3a02ad45..8b95a67caa 100644 --- a/subsys/net/pkt_filter/Kconfig +++ b/subsys/net/pkt_filter/Kconfig @@ -12,6 +12,28 @@ config NET_PKT_FILTER transmission and reception. if NET_PKT_FILTER + +config NET_PKT_FILTER_IPV4_HOOK + bool "Additional network packet filtering hook inside IPv4 stack" + depends on NET_IPV4 + help + This additional hook provides infrastructure to construct custom + rules on the IP packet. + +config NET_PKT_FILTER_IPV6_HOOK + bool "Additional network packet filtering hook inside IPv6 stack" + depends on NET_IPV6 + help + This additional hook provides infrastructure to construct custom + rules on the IP packet. + +config NET_PKT_FILTER_LOCAL_IN_HOOK + bool "Additional network packet filtering hook for connection input" + depends on NET_IP + help + This additional hook provides infrastructure to construct custom + rules for e.g. TCP/UDP packets. + module = NET_PKT_FILTER module-dep = NET_LOG module-str = Log level for packet filtering diff --git a/subsys/net/pkt_filter/base.c b/subsys/net/pkt_filter/base.c index e33091e7af..bb2eb9f085 100644 --- a/subsys/net/pkt_filter/base.c +++ b/subsys/net/pkt_filter/base.c @@ -25,6 +25,50 @@ struct npf_rule_list npf_recv_rules = { .lock = { }, }; +#ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK +struct npf_rule_list npf_local_in_recv_rules = { + .rule_head = SYS_SLIST_STATIC_INIT(&local_in_recv_rules.rule_head), + .lock = { }, +}; +#endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */ + +#ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK +struct npf_rule_list npf_ipv4_recv_rules = { + .rule_head = SYS_SLIST_STATIC_INIT(&ipv4_recv_rules.rule_head), + .lock = { }, +}; +#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */ + +#ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK +struct npf_rule_list npf_ipv6_recv_rules = { + .rule_head = SYS_SLIST_STATIC_INIT(&ipv6_recv_rules.rule_head), + .lock = { }, +}; +#endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */ + +/* + * Helper function + */ +static struct npf_rule_list *get_ip_rules(uint8_t pf) +{ + switch (pf) { + case PF_INET: +#ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK + return &npf_ipv4_recv_rules; +#endif + break; + case PF_INET6: +#ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK + return &npf_ipv6_recv_rules; +#endif + break; + default: + return NULL; + } + + return NULL; +} + /* * Rule application */ @@ -98,6 +142,31 @@ bool net_pkt_filter_recv_ok(struct net_pkt *pkt) return result == NET_OK; } +#ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK +bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt) +{ + enum net_verdict result = lock_evaluate(&npf_local_in_recv_rules, pkt); + + return result == NET_OK; +} +#endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */ + +#if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK) +bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt) +{ + struct npf_rule_list *rules = get_ip_rules(net_pkt_family(pkt)); + + if (!rules) { + NET_DBG("no rules"); + return true; + } + + enum net_verdict result = lock_evaluate(rules, pkt); + + return result == NET_OK; +} +#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */ + /* * Rule management */ @@ -199,3 +268,32 @@ bool npf_size_inbounds(struct npf_test *test, struct net_pkt *pkt) return pkt_size >= bounds->min && pkt_size <= bounds->max; } + +bool npf_ip_src_addr_match(struct npf_test *test, struct net_pkt *pkt) +{ + struct npf_test_ip *test_ip = + CONTAINER_OF(test, struct npf_test_ip, test); + uint8_t pkt_family = net_pkt_family(pkt); + + for (uint32_t ip_it = 0; ip_it < test_ip->ipaddr_num; ip_it++) { + if (IS_ENABLED(CONFIG_NET_IPV4) && pkt_family == AF_INET) { + struct in_addr *addr = (struct in_addr *)NET_IPV4_HDR(pkt)->src; + + if (net_ipv4_addr_cmp(addr, &((struct in_addr *)test_ip->ipaddr)[ip_it])) { + return true; + } + } else if (IS_ENABLED(CONFIG_NET_IPV6) && pkt_family == AF_INET6) { + struct in6_addr *addr = (struct in6_addr *)NET_IPV6_HDR(pkt)->src; + + if (net_ipv6_addr_cmp(addr, &((struct in6_addr *)test_ip->ipaddr)[ip_it])) { + return true; + } + } + } + return false; +} + +bool npf_ip_src_addr_unmatch(struct npf_test *test, struct net_pkt *pkt) +{ + return !npf_ip_src_addr_match(test, pkt); +}