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 <jukka.rissanen@nordicsemi.no>
This commit is contained in:
parent
8d0c16ef9c
commit
3064c7b11d
7 changed files with 365 additions and 0 deletions
10
samples/net/pkt_filter/CMakeLists.txt
Normal file
10
samples/net/pkt_filter/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(pkt_filter)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
||||||
|
|
||||||
|
include(${ZEPHYR_BASE}/samples/net/common/common.cmake)
|
7
samples/net/pkt_filter/Kconfig
Normal file
7
samples/net/pkt_filter/Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Private config options for pkt_filter sample app
|
||||||
|
|
||||||
|
# Copyright (c) 2025 Nordic Semiconductor ASA
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
source "samples/net/common/Kconfig"
|
||||||
|
source "Kconfig.zephyr"
|
120
samples/net/pkt_filter/README.rst
Normal file
120
samples/net/pkt_filter/README.rst
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
.. zephyr:code-sample:: net-pkt-filter
|
||||||
|
:name: Network packet filter
|
||||||
|
:relevant-api: net_pkt_filter
|
||||||
|
|
||||||
|
Install network packet filter hooks.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
This sample shows how to set network packet filters from a user application.
|
||||||
|
|
||||||
|
The source code for this sample application can be found at:
|
||||||
|
:zephyr_file:`samples/net/pkt_filter`.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
************
|
||||||
|
|
||||||
|
- :ref:`networking_with_host`
|
||||||
|
|
||||||
|
Building and Running
|
||||||
|
********************
|
||||||
|
|
||||||
|
A good way to run this sample application is with QEMU or native_sim board
|
||||||
|
as described in :ref:`networking_with_host`.
|
||||||
|
|
||||||
|
For demo purposes, the VLAN support needs to be enabled in host side like this.
|
||||||
|
Execute these commands in a terminal window:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ cd tools/net-tools
|
||||||
|
$ ./net-setup.sh -c zeth-vlan.conf
|
||||||
|
|
||||||
|
Then follow these steps to build the network packet filter sample application for
|
||||||
|
either ``qemu_x86`` or ``native_sim`` boards:
|
||||||
|
|
||||||
|
.. zephyr-app-commands::
|
||||||
|
:zephyr-app: samples/net/pkt_filter
|
||||||
|
:board: <board to use>
|
||||||
|
:conf: "prj.conf overlay-vlan.conf"
|
||||||
|
:goals: build
|
||||||
|
:compact:
|
||||||
|
|
||||||
|
In this example, we enable VLAN support with these settings:
|
||||||
|
|
||||||
|
The VLAN overlay configuration file :zephyr_file:`samples/net/pkt_filter/overlay-vlan.conf`
|
||||||
|
creates two virtual LAN networks with these settings:
|
||||||
|
|
||||||
|
- VLAN tag 100: IPv4 198.51.100.1 and IPv6 2001:db8:100::1
|
||||||
|
- VLAN tag 200: IPv4 203.0.113.1 and IPv6 2001:db8:200::1
|
||||||
|
|
||||||
|
In network shell, you can monitor the network packet filters:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
uart:~$ net filter
|
||||||
|
Rule Type Verdict Tests
|
||||||
|
[ 1] recv OK 3 eth vlan type[0x0800],size max[200],iface[2]
|
||||||
|
[ 2] recv OK 3 eth vlan type[0x0800],size min[100],iface[3]
|
||||||
|
[ 3] recv OK 1 iface[1]
|
||||||
|
[ 4] recv OK 2 eth vlan type[0x0806],iface[2]
|
||||||
|
[ 5] recv OK 2 eth vlan type[0x0806],iface[3]
|
||||||
|
[ 6] recv DROP 0
|
||||||
|
|
||||||
|
The above sample application network packet filter rules can be interpreted
|
||||||
|
like this:
|
||||||
|
|
||||||
|
* Rule 1: Allow IPv4 (Ethernet type 0x0800) packets with max size 200 bytes
|
||||||
|
to network interface 2 which is the first VLAN interface.
|
||||||
|
|
||||||
|
* Rule 2: Allow IPv4 packets with min size 100 bytes to network interface 3
|
||||||
|
which is the second VLAN interface.
|
||||||
|
|
||||||
|
* Rule 3: Allow all incoming traffic to Ethernet interface 1
|
||||||
|
|
||||||
|
* Rule 4: Allow ARP packets (Ethernet type 0x0806) to VLAN interface 2
|
||||||
|
|
||||||
|
* Rule 5: Allow ARP packets (Ethernet type 0x0806) to VLAN interface 3
|
||||||
|
|
||||||
|
* Rule 6: Drop all other packets. This also means that IPv6 packets are
|
||||||
|
dropped.
|
||||||
|
|
||||||
|
The network statistics can be used to see that the packets are dropped.
|
||||||
|
Use ``net stats`` command to monitor statistics.
|
||||||
|
|
||||||
|
You can verify the rules from network shell:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
uart:~$ net ping 2001:db8:100::2 -c 2
|
||||||
|
PING 2001:db8:100::2
|
||||||
|
Ping timeout
|
||||||
|
uart:~$ net stats 2
|
||||||
|
Interface 0x8089c6c (Virtual) [2]
|
||||||
|
==================================
|
||||||
|
IPv6 recv 0 sent 3 drop 0 forwarded 0
|
||||||
|
IPv6 ND recv 0 sent 7 drop 1
|
||||||
|
IPv6 MLD recv 0 sent 0 drop 0
|
||||||
|
ICMP recv 0 sent 3 drop 0
|
||||||
|
...
|
||||||
|
Filter drop rx 10 tx 0
|
||||||
|
Bytes received 320
|
||||||
|
Bytes sent 660
|
||||||
|
Processing err 10
|
||||||
|
|
||||||
|
uart:~$ net ping 198.51.100.2 -c 1
|
||||||
|
PING 198.51.100.2
|
||||||
|
28 bytes from 198.51.100.2 to 198.51.100.1: icmp_seq=1 ttl=64 time=100 ms
|
||||||
|
|
||||||
|
uart:~$ net ping 198.51.100.2 -c 1 -s 201
|
||||||
|
PING 198.51.100.2
|
||||||
|
Ping timeout
|
||||||
|
|
||||||
|
uart:~$ net ping 203.0.113.2 -c 1
|
||||||
|
PING 203.0.113.2
|
||||||
|
Ping timeout
|
||||||
|
|
||||||
|
uart:~$ net ping 203.0.113.2 -c 1 -s 101
|
||||||
|
PING 203.0.113.2
|
||||||
|
125 bytes from 203.0.113.2 to 203.0.113.1: icmp_seq=1 ttl=64 time=20 ms
|
23
samples/net/pkt_filter/overlay-vlan.conf
Normal file
23
samples/net/pkt_filter/overlay-vlan.conf
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
CONFIG_NET_VLAN=y
|
||||||
|
|
||||||
|
# Allow useful interface assigned to VLAN interface
|
||||||
|
CONFIG_NET_INTERFACE_NAME_LEN=15
|
||||||
|
|
||||||
|
# We have one non-vlan interface and two VLAN interfaces
|
||||||
|
CONFIG_NET_VLAN_COUNT=2
|
||||||
|
|
||||||
|
# There will be three network interfaces so allocate enough IPv4 and IPv6 configs.
|
||||||
|
CONFIG_NET_IF_MAX_IPV4_COUNT=3
|
||||||
|
CONFIG_NET_IF_MAX_IPV6_COUNT=3
|
||||||
|
|
||||||
|
# First ethernet interface will use these settings
|
||||||
|
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
||||||
|
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
||||||
|
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
||||||
|
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
|
||||||
|
|
||||||
|
# First VLAN interface will have these settings
|
||||||
|
CONFIG_NET_SAMPLE_COMMON_VLAN_SETUP_1="100;2001:db8:100::1/64,198.51.100.1/24"
|
||||||
|
|
||||||
|
# Second VLAN interface will have these settings
|
||||||
|
CONFIG_NET_SAMPLE_COMMON_VLAN_SETUP_2="200;2001:db8:200::1/64,203.0.113.1/24"
|
53
samples/net/pkt_filter/prj.conf
Normal file
53
samples/net/pkt_filter/prj.conf
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# Generic network options
|
||||||
|
CONFIG_NETWORKING=y
|
||||||
|
CONFIG_NET_LOG=y
|
||||||
|
CONFIG_NET_IPV6=y
|
||||||
|
CONFIG_NET_IPV4=y
|
||||||
|
CONFIG_NET_DHCPV4=n
|
||||||
|
CONFIG_NET_UDP=y
|
||||||
|
CONFIG_NET_TCP=y
|
||||||
|
|
||||||
|
# Enable packet filtering
|
||||||
|
CONFIG_NET_PKT_FILTER=y
|
||||||
|
CONFIG_NET_PKT_FILTER_IPV4_HOOK=y
|
||||||
|
CONFIG_NET_PKT_FILTER_IPV6_HOOK=y
|
||||||
|
CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK=y
|
||||||
|
|
||||||
|
# Network statistics options
|
||||||
|
CONFIG_NET_STATISTICS=y
|
||||||
|
CONFIG_NET_STATISTICS_USER_API=y
|
||||||
|
CONFIG_NET_STATISTICS_PER_INTERFACE=y
|
||||||
|
CONFIG_NET_STATISTICS_ETHERNET=y
|
||||||
|
|
||||||
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
|
|
||||||
|
# Network packet configuration
|
||||||
|
CONFIG_NET_PKT_RX_COUNT=32
|
||||||
|
CONFIG_NET_PKT_TX_COUNT=32
|
||||||
|
CONFIG_NET_BUF_RX_COUNT=32
|
||||||
|
CONFIG_NET_BUF_TX_COUNT=32
|
||||||
|
|
||||||
|
# IP address configuration
|
||||||
|
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=5
|
||||||
|
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5
|
||||||
|
CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT=1
|
||||||
|
CONFIG_NET_MAX_CONTEXTS=10
|
||||||
|
|
||||||
|
CONFIG_INIT_STACKS=y
|
||||||
|
CONFIG_PRINTK=y
|
||||||
|
CONFIG_NET_SHELL=y
|
||||||
|
|
||||||
|
# Application configuration
|
||||||
|
CONFIG_NET_CONFIG_NEED_IPV6=y
|
||||||
|
CONFIG_NET_CONFIG_NEED_IPV4=y
|
||||||
|
CONFIG_NET_CONFIG_SETTINGS=y
|
||||||
|
|
||||||
|
# IP address configuration
|
||||||
|
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
||||||
|
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
||||||
|
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
|
||||||
|
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
CONFIG_LOG=y
|
||||||
|
CONFIG_LOG_BUFFER_SIZE=4096
|
14
samples/net/pkt_filter/sample.yaml
Normal file
14
samples/net/pkt_filter/sample.yaml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
sample:
|
||||||
|
description: Network packet filtering functionality
|
||||||
|
name: Network packet filter sample app
|
||||||
|
tests:
|
||||||
|
sample.net.pkt_filter:
|
||||||
|
harness: net
|
||||||
|
min_ram: 64
|
||||||
|
tags:
|
||||||
|
- net
|
||||||
|
- statistics
|
||||||
|
- net_pkt_filter
|
||||||
|
depends_on: netif
|
||||||
|
integration_platforms:
|
||||||
|
- native_sim
|
138
samples/net/pkt_filter/src/main.c
Normal file
138
samples/net/pkt_filter/src/main.c
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_stats_sample, LOG_LEVEL_DBG);
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <zephyr/net/net_core.h>
|
||||||
|
#include <zephyr/net/net_if.h>
|
||||||
|
#include <zephyr/net/ethernet.h>
|
||||||
|
#include <zephyr/net/net_pkt_filter.h>
|
||||||
|
|
||||||
|
#include "net_sample_common.h"
|
||||||
|
|
||||||
|
#define MAX_INTERFACES 3
|
||||||
|
|
||||||
|
static struct net_if *interfaces[MAX_INTERFACES];
|
||||||
|
|
||||||
|
/* Interface match rule. The interface value is set at runtime in init_app() */
|
||||||
|
static NPF_IFACE_MATCH(match_iface_vlan1, NULL);
|
||||||
|
static NPF_IFACE_MATCH(match_iface_vlan2, NULL);
|
||||||
|
static NPF_IFACE_MATCH(match_iface_eth, NULL);
|
||||||
|
|
||||||
|
/* Allow all traffic to Ethernet interface, drop VLAN traffic except
|
||||||
|
* couple of exceptions for IPv4 and IPv6
|
||||||
|
*/
|
||||||
|
static NPF_RULE(eth_iface_rule, NET_OK, match_iface_eth);
|
||||||
|
|
||||||
|
/* Match max size rule */
|
||||||
|
static NPF_SIZE_MAX(maxsize_200, 200);
|
||||||
|
static NPF_ETH_VLAN_TYPE_MATCH(ipv4_packet1, NET_ETH_PTYPE_IP);
|
||||||
|
static NPF_RULE(small_ipv4_pkt, NET_OK, ipv4_packet1, maxsize_200,
|
||||||
|
match_iface_vlan1);
|
||||||
|
|
||||||
|
/* Match min size rule */
|
||||||
|
static NPF_SIZE_MIN(minsize_100, 100);
|
||||||
|
static NPF_ETH_VLAN_TYPE_MATCH(ipv4_packet2, NET_ETH_PTYPE_IP);
|
||||||
|
static NPF_RULE(large_ipv4_pkt, NET_OK, ipv4_packet2, minsize_100,
|
||||||
|
match_iface_vlan2);
|
||||||
|
|
||||||
|
/* Allow ARP for VLAN interface */
|
||||||
|
static NPF_ETH_VLAN_TYPE_MATCH(arp_packet, NET_ETH_PTYPE_ARP);
|
||||||
|
static NPF_RULE(arp_pkt_vlan1, NET_OK, arp_packet, match_iface_vlan1);
|
||||||
|
static NPF_RULE(arp_pkt_vlan2, NET_OK, arp_packet, match_iface_vlan2);
|
||||||
|
|
||||||
|
static void iface_cb(struct net_if *iface, void *user_data)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
ARG_UNUSED(user_data);
|
||||||
|
|
||||||
|
ARRAY_FOR_EACH(interfaces, i) {
|
||||||
|
if (interfaces[i] == NULL) {
|
||||||
|
interfaces[i] = iface;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERR("Too many interfaces %d (max is %d)", count, MAX_INTERFACES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_app(void)
|
||||||
|
{
|
||||||
|
struct net_if *iface, *eth = NULL, *vlan1 = NULL, *vlan2 = NULL;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
ARRAY_FOR_EACH(interfaces, i) {
|
||||||
|
if (interfaces[i] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface = interfaces[i];
|
||||||
|
|
||||||
|
if (net_eth_is_vlan_interface(iface)) {
|
||||||
|
if (vlan1 == NULL) {
|
||||||
|
vlan1 = iface;
|
||||||
|
} else if (vlan2 == NULL) {
|
||||||
|
vlan2 = iface;
|
||||||
|
}
|
||||||
|
} else if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
|
||||||
|
eth = iface;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == 0) {
|
||||||
|
LOG_ERR("No interfaces found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match_iface_vlan1.iface = vlan1;
|
||||||
|
match_iface_vlan2.iface = vlan2;
|
||||||
|
match_iface_eth.iface = eth;
|
||||||
|
|
||||||
|
/* The sample will setup the Ethernet interface and two VLAN
|
||||||
|
* optional interfaces (if VLAN is enabled).
|
||||||
|
* We allow all traffic to the Ethernet interface, but have
|
||||||
|
* filters for the VLAN interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* We allow small IPv4 packets to the VLAN interface 1 */
|
||||||
|
npf_append_recv_rule(&small_ipv4_pkt);
|
||||||
|
|
||||||
|
/* We allow large IPv4 packets to the VLAN interface 2 */
|
||||||
|
npf_append_recv_rule(&large_ipv4_pkt);
|
||||||
|
|
||||||
|
/* We allow all traffic to the Ethernet interface */
|
||||||
|
npf_append_recv_rule(ð_iface_rule);
|
||||||
|
|
||||||
|
/* We allow ARP traffic to the VLAN 1 interface */
|
||||||
|
npf_append_recv_rule(&arp_pkt_vlan1);
|
||||||
|
|
||||||
|
/* We allow ARP traffic to the VLAN 2 interface */
|
||||||
|
npf_append_recv_rule(&arp_pkt_vlan2);
|
||||||
|
|
||||||
|
/* The remaining packets that do not match are dropped */
|
||||||
|
npf_append_recv_rule(&npf_default_drop);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
net_if_foreach(iface_cb, interfaces);
|
||||||
|
|
||||||
|
init_vlan();
|
||||||
|
init_app();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue