DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Frank Dressler <frank@dressler.pro>
To: dev@dpdk.org
Cc: Thomas Monjalon <thomas@monjalon.net>,
	Stephen Hemminger <stephen@networkplumber.org>
Subject: [PATCH v2] net/af_packet: add option to ignore outgoing packets
Date: Mon,  5 Oct 2026 01:28:09 +0100	[thread overview]
Message-ID: <20261005002809.1676723-1-frank@dressler.pro> (raw)
In-Reply-To: <20261004042255.1517316-1-frank@dressler.pro>

By default, AF_PACKET delivers both incoming and outgoing packets.
This might surprise applications that expect rte_eth_rx_burst() to
return only incoming traffic.

This patch adds the option ignore_outgoing=<0|1> to enable
PACKET_IGNORE_OUTGOING (Linux 4.20+). When set, the kernel drops
outgoing frames. The default is 0 to keep the existing behavior.

Signed-off-by: Frank Dressler <frank@dressler.pro>
---
v2:
- use PACKET_IGNORE_OUTGOING / ignore_outgoing= instead of capture_dir

 .mailmap                                  |  1 +
 app/test/test_pmd_af_packet.c             | 97 +++++++++++++++++++++++
 doc/guides/nics/af_packet.rst             |  3 +
 doc/guides/rel_notes/release_26_11.rst    |  6 ++
 drivers/net/af_packet/rte_eth_af_packet.c | 27 ++++++-
 5 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/.mailmap b/.mailmap
index 2e348c3bce..a04fc553d4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -505,6 +505,7 @@ Francis Kelly <fkelly@nvidia.com> <fkelly@mellanox.com>
 Francis Racicot <francis.racicot@intel.com>
 Franck Lenormand <franck.lenormand@nxp.com>
 François-Frédéric Ozog <ff@ozog.com>
+Frank Dressler <frank@dressler.pro>
 Frank Du <frank.du@intel.com>
 Frank Zhao <frank.zhao@starfivetech.com>
 Frederico Cadete <frederico.cadete-ext@oneaccess-net.com>
diff --git a/app/test/test_pmd_af_packet.c b/app/test/test_pmd_af_packet.c
index b668ca5b81..ee4a54d86a 100644
--- a/app/test/test_pmd_af_packet.c
+++ b/app/test/test_pmd_af_packet.c
@@ -913,6 +913,102 @@ test_af_packet_qdisc_bypass(void)
 	return TEST_SUCCESS;
 }
 
+/*
+ * Test: Ignore outgoing packets configuration
+ * TX on the TAP with qdisc_bypass=0 produces PACKET_OUTGOING frames.
+ * A peer with ignore_outgoing=0 should see them; ignore_outgoing=1 must not.
+ */
+static int
+test_af_packet_ignore_outgoing(void)
+{
+	struct rte_mbuf *bufs[BURST_SIZE];
+	uint16_t tx_port, rx_port_ign_out_off, rx_port_ign_out_on, nb_tx;
+	unsigned int rx_off = 0, rx_on = 0, allocated;
+	uint64_t elapsed = 0;
+	const char *err = NULL;
+	int ret;
+
+	if (!tap_created) {
+		printf("SKIPPED: TAP interface not available (need root)\n");
+		return TEST_SKIPPED;
+	}
+
+	ret = create_af_packet_port("net_af_packet_ign_on",
+			"iface=" TAP_DEV_NAME ",ignore_outgoing=1",
+			&rx_port_ign_out_on);
+	if (ret != 0) {
+		printf("SKIPPED: ignore_outgoing may not be supported\n");
+		return TEST_SKIPPED;
+	}
+
+	ret = create_af_packet_port("net_af_packet_ign_off",
+			"iface=" TAP_DEV_NAME ",ignore_outgoing=0",
+			&rx_port_ign_out_off);
+	if (ret != 0) {
+		err = "Failed to create ignore_outgoing=0 port";
+		goto out_rx_port_ign_out_on;
+	}
+
+	/* qdisc_bypass=0 so the kernel TX tap sees the TX packets */
+	ret = create_af_packet_port("net_af_packet_ign_tx",
+			"iface=" TAP_DEV_NAME ",qdisc_bypass=0", &tx_port);
+	if (ret != 0) {
+		err = "Failed to create TX af_packet port";
+		goto out_rx_port_ign_out_off;
+	}
+
+	if (configure_af_packet_port(rx_port_ign_out_on, 1, 1) != 0 ||
+	    configure_af_packet_port(rx_port_ign_out_off, 1, 1) != 0 ||
+	    configure_af_packet_port(tx_port, 1, 1) != 0) {
+		err = "Failed to configure ports";
+		goto out;
+	}
+
+	while (do_rx_burst(rx_port_ign_out_off, 0, bufs, BURST_SIZE) > 0)
+		;
+	while (do_rx_burst(rx_port_ign_out_on, 0, bufs, BURST_SIZE) > 0)
+		;
+
+	allocated = alloc_tx_mbufs(bufs, 4);
+	nb_tx = do_tx_burst(tx_port, 0, bufs, allocated);
+	if (allocated == 0 || nb_tx == 0) {
+		err = "TX setup failed";
+		goto out;
+	}
+
+	while (elapsed < LOOPBACK_TIMEOUT_US) {
+		rx_off += do_rx_burst(rx_port_ign_out_off, 0, bufs, BURST_SIZE);
+		rx_on += do_rx_burst(rx_port_ign_out_on, 0, bufs, BURST_SIZE);
+		if (rx_off >= nb_tx)
+			break;
+		rte_delay_us_block(STATS_POLL_INTERVAL_US);
+		elapsed += STATS_POLL_INTERVAL_US;
+	}
+
+out:
+	rte_eth_dev_stop(tx_port);
+	rte_eth_dev_close(tx_port);
+	rte_vdev_uninit("net_af_packet_ign_tx");
+out_rx_port_ign_out_off:
+	rte_eth_dev_stop(rx_port_ign_out_off);
+	rte_eth_dev_close(rx_port_ign_out_off);
+	rte_vdev_uninit("net_af_packet_ign_off");
+out_rx_port_ign_out_on:
+	rte_eth_dev_stop(rx_port_ign_out_on);
+	rte_eth_dev_close(rx_port_ign_out_on);
+	rte_vdev_uninit("net_af_packet_ign_on");
+
+	TEST_ASSERT(err == NULL, "%s", err);
+	TEST_ASSERT(rx_off > 0, "Expected packets with ignore_outgoing=0");
+	TEST_ASSERT(rx_on == 0, "Expected no packets with ignore_outgoing=1");
+
+	ret = rte_vdev_init("net_af_packet_ign_bad",
+			    "iface=" TAP_DEV_NAME ",ignore_outgoing=2");
+	TEST_ASSERT(ret != 0, "Expected failure with ignore_outgoing=2");
+
+	return TEST_SUCCESS;
+}
+
 /*
  * Test: Multiple queue pairs
  */
@@ -1107,6 +1203,7 @@ static struct unit_test_suite af_packet_test_suite = {
 		TEST_CASE(test_af_packet_invalid_qpairs),
 		TEST_CASE(test_af_packet_frame_config),
 		TEST_CASE(test_af_packet_qdisc_bypass),
+		TEST_CASE(test_af_packet_ignore_outgoing),
 		TEST_CASE(test_af_packet_multi_queue),
 
 		TEST_CASES_END() /**< NULL terminate unit test array */
diff --git a/doc/guides/nics/af_packet.rst b/doc/guides/nics/af_packet.rst
index 1505b98ff7..d1c04908dd 100644
--- a/doc/guides/nics/af_packet.rst
+++ b/doc/guides/nics/af_packet.rst
@@ -25,6 +25,9 @@ Some of these, in turn, will be used to configure the PACKET_MMAP settings.
     disabled by default);
 *   ``fanout_mode`` - set fanout algorithm.
     Possible choices: hash, lb, cpu, rollover, rnd, qm (optional, default hash);
+*   ``ignore_outgoing`` - set PACKET_IGNORE_OUTGOING so the socket does not
+    receive packets transmitted by the host on the same interface (optional,
+    default 0; requires Linux kernel >= 4.20);
 *   ``blocksz`` - PACKET_MMAP block size (optional, default 4096);
 *   ``framesz`` - PACKET_MMAP frame size (optional, default 2048B; Note: multiple
     of 16B);
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 030bd84cea..a7421cf9d4 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -64,6 +64,12 @@ New Features
 
   Added ``rte_vlan_insert_tpid()`` to the net library.
 
+* **Updated AF_PACKET driver.**
+
+  Added ``ignore_outgoing`` vdev argument to enable ``PACKET_IGNORE_OUTGOING``,
+  so the PMD does not receive packets transmitted by the host on the same
+  interface. Requires Linux kernel >= 4.20.
+
 * **Updated AF_XDP driver.**
 
   * Changed the default device plugin endpoint path used when
diff --git a/drivers/net/af_packet/rte_eth_af_packet.c b/drivers/net/af_packet/rte_eth_af_packet.c
index a93df97023..efb32ad8a6 100644
--- a/drivers/net/af_packet/rte_eth_af_packet.c
+++ b/drivers/net/af_packet/rte_eth_af_packet.c
@@ -39,6 +39,7 @@
 #define ETH_AF_PACKET_FRAMECOUNT_ARG	"framecnt"
 #define ETH_AF_PACKET_QDISC_BYPASS_ARG	"qdisc_bypass"
 #define ETH_AF_PACKET_FANOUT_MODE_ARG	"fanout_mode"
+#define ETH_AF_PACKET_IGNORE_OUTGOING_ARG	"ignore_outgoing"
 
 #define DFLT_FRAME_SIZE		(1 << 11)
 #define DFLT_FRAME_COUNT	(1 << 9)
@@ -103,6 +104,7 @@ static const char *valid_arguments[] = {
 	ETH_AF_PACKET_FRAMECOUNT_ARG,
 	ETH_AF_PACKET_QDISC_BYPASS_ARG,
 	ETH_AF_PACKET_FANOUT_MODE_ARG,
+	ETH_AF_PACKET_IGNORE_OUTGOING_ARG,
 	NULL
 };
 
@@ -877,6 +879,7 @@ rte_pmd_init_internals(struct rte_vdev_device *dev,
 		       unsigned int framecnt,
 		       unsigned int qdisc_bypass,
 		       const char *fanout_mode,
+		       unsigned int ignore_outgoing,
 		       struct pmd_internals **internals,
 		       struct rte_eth_dev **eth_dev,
 		       struct rte_kvargs *kvlist)
@@ -1025,6 +1028,19 @@ rte_pmd_init_internals(struct rte_vdev_device *dev,
 #endif
 		}
 
+		if (ignore_outgoing) {
+#if defined(PACKET_IGNORE_OUTGOING)
+			rc = setsockopt(qsockfd, SOL_PACKET, PACKET_IGNORE_OUTGOING,
+					&ignore_outgoing, sizeof(ignore_outgoing));
+			if (rc == -1) {
+				PMD_LOG_ERRNO(ERR,
+					"%s: could not set PACKET_IGNORE_OUTGOING on AF_PACKET socket for %s",
+					name, pair->value);
+				goto error;
+			}
+#endif
+		}
+
 		rc = setsockopt(qsockfd, SOL_PACKET, PACKET_RX_RING, req, sizeof(*req));
 		if (rc == -1) {
 			PMD_LOG_ERRNO(ERR,
@@ -1206,6 +1222,7 @@ rte_eth_from_packet(struct rte_vdev_device *dev,
 	unsigned int qpairs = 1;
 	unsigned int qdisc_bypass = 1;
 	const char *fanout_mode = NULL;
+	unsigned int ignore_outgoing = 0;
 
 	/* do some parameter checking */
 	if (*sockfd < 0)
@@ -1272,6 +1289,11 @@ rte_eth_from_packet(struct rte_vdev_device *dev,
 			fanout_mode = pair->value;
 			continue;
 		}
+		if (strstr(pair->key, ETH_AF_PACKET_IGNORE_OUTGOING_ARG) != NULL) {
+			if (parse_uint(pair->key, pair->value, &ignore_outgoing, 1) < 0)
+				return -1;
+			continue;
+		}
 	}
 
 	if (framesize > blocksize) {
@@ -1298,12 +1320,14 @@ rte_eth_from_packet(struct rte_vdev_device *dev,
 		PMD_LOG(DEBUG, "%s:\tfanout mode %s", name, fanout_mode);
 	else
 		PMD_LOG(DEBUG, "%s:\tfanout mode %s", name, "default PACKET_FANOUT_HASH");
+	PMD_LOG(DEBUG, "%s:\tignore outgoing %d", name, ignore_outgoing);
 
 	if (rte_pmd_init_internals(dev, *sockfd, qpairs,
 				   blocksize, blockcount,
 				   framesize, framecount,
 				   qdisc_bypass,
 				   fanout_mode,
+				   ignore_outgoing,
 				   &internals, &eth_dev,
 				   kvlist) < 0)
 		return -1;
@@ -1401,4 +1425,5 @@ RTE_PMD_REGISTER_PARAM_STRING(net_af_packet,
 	"framesz=<int> "
 	"framecnt=<int> "
 	"qdisc_bypass=<0|1> "
-	"fanout_mode=<hash|lb|cpu|rollover|rnd|qm>");
+	"fanout_mode=<hash|lb|cpu|rollover|rnd|qm> "
+	"ignore_outgoing=<0|1>");
-- 
2.56.0


  parent reply	other threads:[~2026-10-05  0:28 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-04  4:22 [PATCH] net/af_packet: add capture direction option Frank Dressler
2026-10-04 16:10 ` Stephen Hemminger
2026-10-05  0:26   ` Frank Dressler
2026-10-05  0:28 ` Frank Dressler [this message]
2026-10-05 16:41   ` [PATCH v2] net/af_packet: add option to ignore outgoing packets Stephen Hemminger
2026-10-06  6:42   ` [PATCH v3] " Frank Dressler
2026-10-06 13:41     ` Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261005002809.1676723-1-frank@dressler.pro \
    --to=frank@dressler.pro \
    --cc=dev@dpdk.org \
    --cc=stephen@networkplumber.org \
    --cc=thomas@monjalon.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox