From cde70232b9bbbb333c09a111167bbca44799543c Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 10 Apr 2025 17:03:38 +0300 Subject: [PATCH] net: pkt_filter: Add statistics support to packet filter As the network packet filter drops packets without any indication that the packet is dropped, it can be difficult to monitor what is going on in the system when receiving data. The user can now monitor the statistics and see if packets are being dropped because of packet filter activity. Signed-off-by: Jukka Rissanen --- include/zephyr/net/net_stats.h | 92 ++++++++++++++++++++++++++++++++++ subsys/net/ip/Kconfig.stats | 7 +++ subsys/net/ip/connection.c | 1 + subsys/net/ip/ipv4.c | 1 + subsys/net/ip/ipv6.c | 1 + subsys/net/ip/net_core.c | 5 +- subsys/net/ip/net_if.c | 5 +- subsys/net/ip/net_stats.h | 44 ++++++++++++++++ subsys/net/lib/shell/stats.c | 15 ++++++ 9 files changed, 169 insertions(+), 2 deletions(-) diff --git a/include/zephyr/net/net_stats.h b/include/zephyr/net/net_stats.h index 211f2dffd30..588121f5be1 100644 --- a/include/zephyr/net/net_stats.h +++ b/include/zephyr/net/net_stats.h @@ -371,6 +371,34 @@ struct net_stats_pm { uint32_t start_time; }; +/** + * @brief Network packet filter statistics + */ +struct net_stats_pkt_filter { + /** Network packet filter RX statistics */ + struct { + /** Network packets dropped at network interface level */ + net_stats_t drop; +#if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) + /** IPv4 packets dropped at network interface level */ + net_stats_t ipv4_drop; +#endif +#if defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK) + /** IPv6 packets dropped at network interface level */ + net_stats_t ipv6_drop; +#endif +#if defined(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK) + /** Packets dropped at connection input */ + net_stats_t local_drop; +#endif + } rx; + + /** Network packet filter TX statistics */ + struct { + /** Network packets dropped at network interface level */ + net_stats_t drop; + } tx; +}; /** * @brief All network statistics in one struct. @@ -388,6 +416,10 @@ struct net_stats { /** IP layer errors */ struct net_stats_ip_errors ip_errors; +#if defined(CONFIG_NET_STATISTICS_PKT_FILTER) + struct net_stats_pkt_filter pkt_filter; +#endif + #if defined(CONFIG_NET_STATISTICS_IPV6) /** IPv6 statistics */ struct net_stats_ip ipv6; @@ -709,6 +741,7 @@ struct net_stats_wifi { enum net_request_stats_cmd { NET_REQUEST_STATS_CMD_GET_ALL = 1, NET_REQUEST_STATS_CMD_GET_PROCESSING_ERROR, + NET_REQUEST_STATS_CMD_GET_PKT_FILTER_DROP, NET_REQUEST_STATS_CMD_GET_BYTES, NET_REQUEST_STATS_CMD_GET_IP_ERRORS, NET_REQUEST_STATS_CMD_GET_IPV4, @@ -737,6 +770,10 @@ enum net_request_stats_cmd { #define NET_REQUEST_STATS_GET_PROCESSING_ERROR \ (_NET_STATS_BASE | NET_REQUEST_STATS_CMD_GET_PROCESSING_ERROR) +/** Request all pkt_filter drop statistics */ +#define NET_REQUEST_STATS_GET_PKT_FILTER_DROP \ + (_NET_STATS_BASE | NET_REQUEST_STATS_CMD_GET_PKT_FILTER_DROP) + /** Request number of received and sent bytes */ #define NET_REQUEST_STATS_GET_BYTES \ (_NET_STATS_BASE | NET_REQUEST_STATS_CMD_GET_BYTES) @@ -752,6 +789,10 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_PROCESSING_ERROR); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_BYTES); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_IP_ERRORS); +#if defined(CONFIG_NET_STATISTICS_PKT_FILTER) +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_GET_PKT_FILTER_DROP); +#endif /* CONFIG_NET_STATISTICS_PKT_FILTER */ + /** @endcond */ #if defined(CONFIG_NET_STATISTICS_IPV4) @@ -1373,6 +1414,55 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_RESET_WIFI); #define NET_STATS_PROMETHEUS_RX_TIME(iface, dev_id, sfx) #endif +#define NET_STATS_PROMETHEUS_PKT_FILTER_IPV4(iface, dev_id, sfx) \ + NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ + "Packet filter RX IPv4 drop", \ + NET_STATS_GET_INSTANCE(dev_id, sfx, pkt_filter_rx_ipv4_drop), \ + "packet_count", \ + NET_STATS_GET_COLLECTOR_NAME(dev_id, sfx), \ + NET_STATS_GET_VAR(dev_id, sfx, pkt_filter_rx_ipv4_drop),\ + &(iface)->stats.pkt_filter.rx.ipv4_drop); + +#define NET_STATS_PROMETHEUS_PKT_FILTER_IPV6(iface, dev_id, sfx) \ + NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ + "Packet filter RX IPv6 drop", \ + NET_STATS_GET_INSTANCE(dev_id, sfx, pkt_filter_rx_ipv6_drop), \ + "packet_count", \ + NET_STATS_GET_COLLECTOR_NAME(dev_id, sfx), \ + NET_STATS_GET_VAR(dev_id, sfx, pkt_filter_rx_ipv6_drop),\ + &(iface)->stats.pkt_filter.rx.ipv6_drop); + +#define NET_STATS_PROMETHEUS_PKT_FILTER_LOCAL(iface, dev_id, sfx) \ + NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ + "Packet filter RX local drop", \ + NET_STATS_GET_INSTANCE(dev_id, sfx, pkt_filter_rx_local_drop), \ + "packet_count", \ + NET_STATS_GET_COLLECTOR_NAME(dev_id, sfx), \ + NET_STATS_GET_VAR(dev_id, sfx, pkt_filter_rx_local_drop),\ + &(iface)->stats.pkt_filter.rx.local_drop); + +#define NET_STATS_PROMETHEUS_PKT_FILTER(iface, dev_id, sfx) \ + NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ + "Packet filter RX drop", \ + NET_STATS_GET_INSTANCE(dev_id, sfx, pkt_filter_rx_drop),\ + "packet_count", \ + NET_STATS_GET_COLLECTOR_NAME(dev_id, sfx), \ + NET_STATS_GET_VAR(dev_id, sfx, pkt_filter_rx_drop), \ + &(iface)->stats.pkt_filter.rx.drop); \ + NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ + "Packet filter TX drop", \ + NET_STATS_GET_INSTANCE(dev_id, sfx, pkt_filter_tx_drop),\ + "packet_count", \ + NET_STATS_GET_COLLECTOR_NAME(dev_id, sfx), \ + NET_STATS_GET_VAR(dev_id, sfx, pkt_filter_tx_drop), \ + &(iface)->stats.pkt_filter.tx.drop); \ + IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV4_HOOK, \ + (NET_STATS_PROMETHEUS_PKT_FILTER_IPV4(iface, dev_id, sfx))) \ + IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV6_HOOK, \ + (NET_STATS_PROMETHEUS_PKT_FILTER_IPV6(iface, dev_id, sfx))) \ + IF_ENABLED(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK, \ + (NET_STATS_PROMETHEUS_PKT_FILTER_LOCAL(iface, dev_id, sfx))) + /* Per network interface statistics via Prometheus */ #define NET_STATS_PROMETHEUS(iface, dev_id, sfx) \ NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ @@ -1382,6 +1472,8 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_STATS_RESET_WIFI); NET_STATS_GET_COLLECTOR_NAME(dev_id, sfx), \ NET_STATS_GET_VAR(dev_id, sfx, processing_error), \ &(iface)->stats.processing_error); \ + IF_ENABLED(CONFIG_NET_STATISTICS_PKT_FILTER, \ + (NET_STATS_PROMETHEUS_PKT_FILTER(iface, dev_id, sfx))) \ /* IP layer error statistics */ \ NET_STATS_PROMETHEUS_COUNTER_DEFINE( \ "IP proto error", \ diff --git a/subsys/net/ip/Kconfig.stats b/subsys/net/ip/Kconfig.stats index 68f2c0a92d6..e7ed8dfaa28 100644 --- a/subsys/net/ip/Kconfig.stats +++ b/subsys/net/ip/Kconfig.stats @@ -114,6 +114,13 @@ config NET_STATISTICS_DNS help Keep track of DNS related statistics +config NET_STATISTICS_PKT_FILTER + bool "Network packet filter statistics" + depends on NET_PKT_FILTER + default y + help + Keep track of network packet filter related statistics + config NET_STATISTICS_PPP bool "Point-to-point (PPP) statistics" depends on NET_L2_PPP diff --git a/subsys/net/ip/connection.c b/subsys/net/ip/connection.c index c03b7f9b8ca..8dd431fea33 100644 --- a/subsys/net/ip/connection.c +++ b/subsys/net/ip/connection.c @@ -676,6 +676,7 @@ enum net_verdict net_conn_input(struct net_pkt *pkt, if (!net_pkt_filter_local_in_recv_ok(pkt)) { /* drop the packet */ + net_stats_update_filter_rx_local_drop(net_pkt_iface(pkt)); return NET_DROP; } diff --git a/subsys/net/ip/ipv4.c b/subsys/net/ip/ipv4.c index 64749cb64e9..0add73288bb 100644 --- a/subsys/net/ip/ipv4.c +++ b/subsys/net/ip/ipv4.c @@ -343,6 +343,7 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt, bool is_loopback) if (!net_pkt_filter_ip_recv_ok(pkt)) { /* drop the packet */ + net_stats_update_filter_rx_ipv4_drop(net_pkt_iface(pkt)); return NET_DROP; } diff --git a/subsys/net/ip/ipv6.c b/subsys/net/ip/ipv6.c index 24cc3d12871..7690ce38884 100644 --- a/subsys/net/ip/ipv6.c +++ b/subsys/net/ip/ipv6.c @@ -584,6 +584,7 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback) if (!net_pkt_filter_ip_recv_ok(pkt)) { /* drop the packet */ NET_DBG("DROP: pkt filter"); + net_stats_update_filter_rx_ipv6_drop(net_pkt_iface(pkt)); return NET_DROP; } diff --git a/subsys/net/ip/net_core.c b/subsys/net/ip/net_core.c index 0f3d5d7418a..97b6bd95f17 100644 --- a/subsys/net/ip/net_core.c +++ b/subsys/net/ip/net_core.c @@ -583,7 +583,10 @@ int net_recv_data(struct net_if *iface, struct net_pkt *pkt) net_pkt_set_iface(pkt, iface); if (!net_pkt_filter_recv_ok(pkt)) { - /* silently drop the packet */ + /* Silently drop the packet, but update the statistics in order + * to be able to monitor filter activity. + */ + net_stats_update_filter_rx_drop(net_pkt_iface(pkt)); net_pkt_unref(pkt); } else { net_queue_rx(iface, pkt); diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index e03d290044f..4c91f7b1721 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -340,7 +340,10 @@ void net_process_tx_packet(struct net_pkt *pkt) void net_if_try_queue_tx(struct net_if *iface, struct net_pkt *pkt, k_timeout_t timeout) { if (!net_pkt_filter_send_ok(pkt)) { - /* silently drop the packet */ + /* Silently drop the packet, but update the statistics in order + * to be able to monitor filter activity. + */ + net_stats_update_filter_tx_drop(net_pkt_iface(pkt)); net_pkt_unref(pkt); return; } diff --git a/subsys/net/ip/net_stats.h b/subsys/net/ip/net_stats.h index 6a7a3cdbd93..17b3f0d8c7f 100644 --- a/subsys/net/ip/net_stats.h +++ b/subsys/net/ip/net_stats.h @@ -59,12 +59,56 @@ static inline void net_stats_update_bytes_sent(struct net_if *iface, { UPDATE_STAT(iface, stats.bytes.sent += bytes); } + +#if defined(CONFIG_NET_STATISTICS_PKT_FILTER) +static inline void net_stats_update_filter_rx_drop(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.pkt_filter.rx.drop++); +} + +static inline void net_stats_update_filter_tx_drop(struct net_if *iface) +{ + UPDATE_STAT(iface, stats.pkt_filter.tx.drop++); +} + +static inline void net_stats_update_filter_rx_ipv4_drop(struct net_if *iface) +{ +#if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) + UPDATE_STAT(iface, stats.pkt_filter.rx.ipv4_drop++); +#endif +} + +static inline void net_stats_update_filter_rx_ipv6_drop(struct net_if *iface) +{ +#if defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK) + UPDATE_STAT(iface, stats.pkt_filter.rx.ipv6_drop++); +#endif +} + +static inline void net_stats_update_filter_rx_local_drop(struct net_if *iface) +{ +#if defined(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK) + UPDATE_STAT(iface, stats.pkt_filter.rx.local_drop++); +#endif +} +#else /* CONFIG_NET_STATISTICS_PKT_FILTER */ +#define net_stats_update_filter_rx_drop(iface) +#define net_stats_update_filter_tx_drop(iface) +#define net_stats_update_filter_rx_ipv4_drop(iface) +#define net_stats_update_filter_rx_ipv6_drop(iface) +#define net_stats_update_filter_rx_local_drop(iface) +#endif /* CONFIG_NET_STATISTICS_PKT_FILTER */ #else #define net_stats_update_processing_error(iface) #define net_stats_update_ip_errors_protoerr(iface) #define net_stats_update_ip_errors_vhlerr(iface) #define net_stats_update_bytes_recv(iface, bytes) #define net_stats_update_bytes_sent(iface, bytes) +#define net_stats_update_filter_rx_drop(iface) +#define net_stats_update_filter_tx_drop(iface) +#define net_stats_update_filter_rx_ipv4_drop(iface) +#define net_stats_update_filter_rx_ipv6_drop(iface) +#define net_stats_update_filter_rx_local_drop(iface) #endif /* CONFIG_NET_STATISTICS */ #if defined(CONFIG_NET_STATISTICS_IPV6) && defined(CONFIG_NET_NATIVE_IPV6) diff --git a/subsys/net/lib/shell/stats.c b/subsys/net/lib/shell/stats.c index cda7d9401e9..64256bc7473 100644 --- a/subsys/net/lib/shell/stats.c +++ b/subsys/net/lib/shell/stats.c @@ -559,6 +559,21 @@ static void net_shell_print_statistics(struct net_if *iface, void *user_data) GET_STAT(iface, dns.sent), GET_STAT(iface, dns.drop)); #endif /* CONFIG_NET_STATISTICS_DNS */ +#if defined(CONFIG_NET_STATISTICS_PKT_FILTER) + PR("Filter drop rx %d" + IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV4_HOOK, ("\tIPv4\t%d")) + IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV6_HOOK, ("\tIPv6\t%d")) + IF_ENABLED(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK, ("\tlocal\t%d")) + "\ttx\t%d\n", + GET_STAT(iface, pkt_filter.rx.drop), + IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV4_HOOK, + (GET_STAT(iface, pkt_filter.rx.ipv4_drop),)) + IF_ENABLED(CONFIG_NET_PKT_FILTER_IPV6_HOOK, + (GET_STAT(iface, pkt_filter.rx.ipv6_drop),)) + IF_ENABLED(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK, + (GET_STAT(iface, pkt_filter.rx.local_drop),)) + GET_STAT(iface, pkt_filter.tx.drop)); +#endif /* CONFIG_NET_STATISTICS_DNS */ PR("Bytes received %u\n", GET_STAT(iface, bytes.received)); PR("Bytes sent %u\n", GET_STAT(iface, bytes.sent));