Skip to content

Commit e6cee80

Browse files
committed
samples: net: pkt_filter: Add a sample to demo packet filtering
Add a network packet filtering sample to show how the packet filtering can be used. Signed-off-by: Jukka Rissanen <[email protected]>
1 parent 3c5e655 commit e6cee80

File tree

7 files changed

+365
-0
lines changed

7 files changed

+365
-0
lines changed

samples/net/pkt_filter/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(pkt_filter)
7+
8+
target_sources(app PRIVATE src/main.c)
9+
10+
include(${ZEPHYR_BASE}/samples/net/common/common.cmake)

samples/net/pkt_filter/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Private config options for pkt_filter sample app
2+
3+
# Copyright (c) 2025 Nordic Semiconductor ASA
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
source "samples/net/common/Kconfig"
7+
source "Kconfig.zephyr"

samples/net/pkt_filter/README.rst

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
.. zephyr:code-sample:: net-pkt-filter
2+
:name: Network packet filter
3+
:relevant-api: net_pkt_filter
4+
5+
Install network packet filter hooks.
6+
7+
Overview
8+
********
9+
10+
This sample shows how to set network packet filters from a user application.
11+
12+
The source code for this sample application can be found at:
13+
:zephyr_file:`samples/net/pkt_filter`.
14+
15+
Requirements
16+
************
17+
18+
- :ref:`networking_with_host`
19+
20+
Building and Running
21+
********************
22+
23+
A good way to run this sample application is with QEMU or native_sim board
24+
as described in :ref:`networking_with_host`.
25+
26+
For demo purposes, the VLAN support needs to be enabled in host side like this.
27+
Execute these commands in a terminal window:
28+
29+
.. code-block:: console
30+
31+
$ cd tools/net-tools
32+
$ ./net-setup.sh -c zeth-vlan.conf
33+
34+
Then follow these steps to build the network packet filter sample application for
35+
either ``qemu_x86`` or ``native_sim`` boards:
36+
37+
.. zephyr-app-commands::
38+
:zephyr-app: samples/net/pkt_filter
39+
:board: <board to use>
40+
:conf: "prj.conf overlay-vlan.conf"
41+
:goals: build
42+
:compact:
43+
44+
In this example, we enable VLAN support with these settings:
45+
46+
The VLAN overlay configuration file :zephyr_file:`samples/net/pkt_filter/overlay-vlan.conf`
47+
creates two virtual LAN networks with these settings:
48+
49+
- VLAN tag 100: IPv4 198.51.100.1 and IPv6 2001:db8:100::1
50+
- VLAN tag 200: IPv4 203.0.113.1 and IPv6 2001:db8:200::1
51+
52+
In network shell, you can monitor the network packet filters:
53+
54+
.. code-block:: console
55+
56+
uart:~$ net filter
57+
Rule Type Verdict Tests
58+
[ 1] recv OK 3 eth vlan type[0x0800],size max[200],iface[2]
59+
[ 2] recv OK 3 eth vlan type[0x0800],size min[100],iface[3]
60+
[ 3] recv OK 1 iface[1]
61+
[ 4] recv OK 2 eth vlan type[0x0806],iface[2]
62+
[ 5] recv OK 2 eth vlan type[0x0806],iface[3]
63+
[ 6] recv DROP 0
64+
65+
The above sample application network packet filter rules can be interpreted
66+
like this:
67+
68+
* Rule 1: Allow IPv4 (Ethernet type 0x0800) packets with max size 200 bytes
69+
to network interface 2 which is the first VLAN interface.
70+
71+
* Rule 2: Allow IPv4 packets with min size 100 bytes to network interface 3
72+
which is the second VLAN interface.
73+
74+
* Rule 3: Allow all incoming traffic to Ethernet interface 1
75+
76+
* Rule 4: Allow ARP packets (Ethernet type 0x0806) to VLAN interface 2
77+
78+
* Rule 5: Allow ARP packets (Ethernet type 0x0806) to VLAN interface 3
79+
80+
* Rule 6: Drop all other packets. This also means that IPv6 packets are
81+
dropped.
82+
83+
The network statistics can be used to see that the packets are dropped.
84+
Use ``net stats`` command to monitor statistics.
85+
86+
You can verify the rules from network shell:
87+
88+
.. code-block:: console
89+
90+
uart:~$ net ping 2001:db8:100::2 -c 2
91+
PING 2001:db8:100::2
92+
Ping timeout
93+
uart:~$ net stats 2
94+
Interface 0x8089c6c (Virtual) [2]
95+
==================================
96+
IPv6 recv 0 sent 3 drop 0 forwarded 0
97+
IPv6 ND recv 0 sent 7 drop 1
98+
IPv6 MLD recv 0 sent 0 drop 0
99+
ICMP recv 0 sent 3 drop 0
100+
...
101+
Filter drop rx 10 tx 0
102+
Bytes received 320
103+
Bytes sent 660
104+
Processing err 10
105+
106+
uart:~$ net ping 198.51.100.2 -c 1
107+
PING 198.51.100.2
108+
28 bytes from 198.51.100.2 to 198.51.100.1: icmp_seq=1 ttl=64 time=100 ms
109+
110+
uart:~$ net ping 198.51.100.2 -c 1 -s 201
111+
PING 198.51.100.2
112+
Ping timeout
113+
114+
uart:~$ net ping 203.0.113.2 -c 1
115+
PING 203.0.113.2
116+
Ping timeout
117+
118+
uart:~$ net ping 203.0.113.2 -c 1 -s 101
119+
PING 203.0.113.2
120+
125 bytes from 203.0.113.2 to 203.0.113.1: icmp_seq=1 ttl=64 time=20 ms
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CONFIG_NET_VLAN=y
2+
3+
# Allow useful interface assigned to VLAN interface
4+
CONFIG_NET_INTERFACE_NAME_LEN=15
5+
6+
# We have one non-vlan interface and two VLAN interfaces
7+
CONFIG_NET_VLAN_COUNT=2
8+
9+
# There will be three network interfaces so allocate enough IPv4 and IPv6 configs.
10+
CONFIG_NET_IF_MAX_IPV4_COUNT=3
11+
CONFIG_NET_IF_MAX_IPV6_COUNT=3
12+
13+
# First ethernet interface will use these settings
14+
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
15+
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
16+
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
17+
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
18+
19+
# First VLAN interface will have these settings
20+
CONFIG_NET_SAMPLE_COMMON_VLAN_SETUP_1="100;2001:db8:100::1/64,198.51.100.1/24"
21+
22+
# Second VLAN interface will have these settings
23+
CONFIG_NET_SAMPLE_COMMON_VLAN_SETUP_2="200;2001:db8:200::1/64,203.0.113.1/24"

samples/net/pkt_filter/prj.conf

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Generic network options
2+
CONFIG_NETWORKING=y
3+
CONFIG_NET_LOG=y
4+
CONFIG_NET_IPV6=y
5+
CONFIG_NET_IPV4=y
6+
CONFIG_NET_DHCPV4=n
7+
CONFIG_NET_UDP=y
8+
CONFIG_NET_TCP=y
9+
10+
# Enable packet filtering
11+
CONFIG_NET_PKT_FILTER=y
12+
CONFIG_NET_PKT_FILTER_IPV4_HOOK=y
13+
CONFIG_NET_PKT_FILTER_IPV6_HOOK=y
14+
CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK=y
15+
16+
# Network statistics options
17+
CONFIG_NET_STATISTICS=y
18+
CONFIG_NET_STATISTICS_USER_API=y
19+
CONFIG_NET_STATISTICS_PER_INTERFACE=y
20+
CONFIG_NET_STATISTICS_ETHERNET=y
21+
22+
CONFIG_TEST_RANDOM_GENERATOR=y
23+
24+
# Network packet configuration
25+
CONFIG_NET_PKT_RX_COUNT=32
26+
CONFIG_NET_PKT_TX_COUNT=32
27+
CONFIG_NET_BUF_RX_COUNT=32
28+
CONFIG_NET_BUF_TX_COUNT=32
29+
30+
# IP address configuration
31+
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=5
32+
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5
33+
CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT=1
34+
CONFIG_NET_MAX_CONTEXTS=10
35+
36+
CONFIG_INIT_STACKS=y
37+
CONFIG_PRINTK=y
38+
CONFIG_NET_SHELL=y
39+
40+
# Application configuration
41+
CONFIG_NET_CONFIG_NEED_IPV6=y
42+
CONFIG_NET_CONFIG_NEED_IPV4=y
43+
CONFIG_NET_CONFIG_SETTINGS=y
44+
45+
# IP address configuration
46+
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
47+
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
48+
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
49+
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
50+
51+
# Logging
52+
CONFIG_LOG=y
53+
CONFIG_LOG_BUFFER_SIZE=4096

samples/net/pkt_filter/sample.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
sample:
2+
description: Network packet filtering functionality
3+
name: Network packet filter sample app
4+
tests:
5+
sample.net.pkt_filter:
6+
harness: net
7+
min_ram: 64
8+
tags:
9+
- net
10+
- statistics
11+
- net_pkt_filter
12+
depends_on: netif
13+
integration_platforms:
14+
- native_sim

samples/net/pkt_filter/src/main.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/logging/log.h>
8+
LOG_MODULE_REGISTER(net_stats_sample, LOG_LEVEL_DBG);
9+
10+
#include <zephyr/kernel.h>
11+
#include <errno.h>
12+
13+
#include <zephyr/net/net_core.h>
14+
#include <zephyr/net/net_if.h>
15+
#include <zephyr/net/ethernet.h>
16+
#include <zephyr/net/net_pkt_filter.h>
17+
18+
#include "net_sample_common.h"
19+
20+
#define MAX_INTERFACES 3
21+
22+
static struct net_if *interfaces[MAX_INTERFACES];
23+
24+
/* Interface match rule. The interface value is set at runtime in init_app() */
25+
static NPF_IFACE_MATCH(match_iface_vlan1, NULL);
26+
static NPF_IFACE_MATCH(match_iface_vlan2, NULL);
27+
static NPF_IFACE_MATCH(match_iface_eth, NULL);
28+
29+
/* Allow all traffic to Ethernet interface, drop VLAN traffic except
30+
* couple of exceptions for IPv4 and IPv6
31+
*/
32+
static NPF_RULE(eth_iface_rule, NET_OK, match_iface_eth);
33+
34+
/* Match max size rule */
35+
static NPF_SIZE_MAX(maxsize_200, 200);
36+
static NPF_ETH_VLAN_TYPE_MATCH(ipv4_packet1, NET_ETH_PTYPE_IP);
37+
static NPF_RULE(small_ipv4_pkt, NET_OK, ipv4_packet1, maxsize_200,
38+
match_iface_vlan1);
39+
40+
/* Match min size rule */
41+
static NPF_SIZE_MIN(minsize_100, 100);
42+
static NPF_ETH_VLAN_TYPE_MATCH(ipv4_packet2, NET_ETH_PTYPE_IP);
43+
static NPF_RULE(large_ipv4_pkt, NET_OK, ipv4_packet2, minsize_100,
44+
match_iface_vlan2);
45+
46+
/* Allow ARP for VLAN interface */
47+
static NPF_ETH_VLAN_TYPE_MATCH(arp_packet, NET_ETH_PTYPE_ARP);
48+
static NPF_RULE(arp_pkt_vlan1, NET_OK, arp_packet, match_iface_vlan1);
49+
static NPF_RULE(arp_pkt_vlan2, NET_OK, arp_packet, match_iface_vlan2);
50+
51+
static void iface_cb(struct net_if *iface, void *user_data)
52+
{
53+
int count = 0;
54+
55+
ARG_UNUSED(user_data);
56+
57+
ARRAY_FOR_EACH(interfaces, i) {
58+
if (interfaces[i] == NULL) {
59+
interfaces[i] = iface;
60+
return;
61+
}
62+
63+
count++;
64+
}
65+
66+
LOG_ERR("Too many interfaces %d (max is %d)", count, MAX_INTERFACES);
67+
}
68+
69+
static void init_app(void)
70+
{
71+
struct net_if *iface, *eth = NULL, *vlan1 = NULL, *vlan2 = NULL;
72+
int found = 0;
73+
74+
ARRAY_FOR_EACH(interfaces, i) {
75+
if (interfaces[i] == NULL) {
76+
continue;
77+
}
78+
79+
iface = interfaces[i];
80+
81+
if (net_eth_is_vlan_interface(iface)) {
82+
if (vlan1 == NULL) {
83+
vlan1 = iface;
84+
} else if (vlan2 == NULL) {
85+
vlan2 = iface;
86+
}
87+
} else if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
88+
eth = iface;
89+
} else {
90+
continue;
91+
}
92+
93+
found++;
94+
}
95+
96+
if (found == 0) {
97+
LOG_ERR("No interfaces found");
98+
return;
99+
}
100+
101+
match_iface_vlan1.iface = vlan1;
102+
match_iface_vlan2.iface = vlan2;
103+
match_iface_eth.iface = eth;
104+
105+
/* The sample will setup the Ethernet interface and two VLAN
106+
* optional interfaces (if VLAN is enabled).
107+
* We allow all traffic to the Ethernet interface, but have
108+
* filters for the VLAN interfaces.
109+
*/
110+
111+
/* We allow small IPv4 packets to the VLAN interface 1 */
112+
npf_append_recv_rule(&small_ipv4_pkt);
113+
114+
/* We allow large IPv4 packets to the VLAN interface 2 */
115+
npf_append_recv_rule(&large_ipv4_pkt);
116+
117+
/* We allow all traffic to the Ethernet interface */
118+
npf_append_recv_rule(&eth_iface_rule);
119+
120+
/* We allow ARP traffic to the VLAN 1 interface */
121+
npf_append_recv_rule(&arp_pkt_vlan1);
122+
123+
/* We allow ARP traffic to the VLAN 2 interface */
124+
npf_append_recv_rule(&arp_pkt_vlan2);
125+
126+
/* The remaining packets that do not match are dropped */
127+
npf_append_recv_rule(&npf_default_drop);
128+
}
129+
130+
int main(void)
131+
{
132+
net_if_foreach(iface_cb, interfaces);
133+
134+
init_vlan();
135+
init_app();
136+
137+
return 0;
138+
}

0 commit comments

Comments
 (0)