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 devarg ignore_outgoing=<0|1> to enable
PACKET_IGNORE_OUTGOING. When set, the kernel drops outgoing frames.
The default is 0 to keep the existing behavior.

Signed-off-by: Frank Dressler <[email protected]>
---
v3:
- drop #ifdef PACKET_IGNORE_OUTGOING (uapi since 4.20; DPDK requires kernel >= 
5.4)
- fail the test if ignore_outgoing=1 cannot create a port
- drop kernel >= 4.20 notes from docs and release notes
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 | 25 +++++-
 5 files changed, 131 insertions(+), 1 deletion(-)

diff --git a/.mailmap b/.mailmap
index 2e348c3bce..a04fc553d4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -505,6 +505,7 @@ Francis Kelly <[email protected]> <[email protected]>
 Francis Racicot <[email protected]>
 Franck Lenormand <[email protected]>
 François-Frédéric Ozog <[email protected]>
+Frank Dressler <[email protected]>
 Frank Du <[email protected]>
 Frank Zhao <[email protected]>
 Frederico Cadete <[email protected]>
diff --git a/app/test/test_pmd_af_packet.c b/app/test/test_pmd_af_packet.c
index b668ca5b81..1ed1de8c02 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) {
+               err = "Failed to create ignore_outgoing=1 port";
+               goto fail_rx_ign_on;
+       }
+
+       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 fail_rx_ign_off;
+       }
+
+       /* 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 fail_tx;
+       }
+
+       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 fail_setup;
+       }
+
+       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 fail_setup;
+       }
+
+       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;
+       }
+
+fail_setup:
+       rte_eth_dev_stop(tx_port);
+       rte_eth_dev_close(tx_port);
+       rte_vdev_uninit("net_af_packet_ign_tx");
+fail_tx:
+       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");
+fail_rx_ign_off:
+       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");
+fail_rx_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..11c9def583 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);
 *   ``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..13598e1272 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.
+
 * **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..66400073a7 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,17 @@ rte_pmd_init_internals(struct rte_vdev_device *dev,
 #endif
                }
 
+               if (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;
+                       }
+               }
+
                rc = setsockopt(qsockfd, SOL_PACKET, PACKET_RX_RING, req, 
sizeof(*req));
                if (rc == -1) {
                        PMD_LOG_ERRNO(ERR,
@@ -1206,6 +1220,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 +1287,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 +1318,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 +1423,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

Reply via email to