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);
+}