Re: [PATCH v20 12/25] net/pcap: support VLAN strip and insert offloads

Bruce Richardson
2026-03-16T14:05:18Z

On Tue, Mar 10, 2026 at 09:09:50AM -0700, Stephen Hemminger wrote:
> Add VLAN tag handling to the pcap PMD, consistent with how virtio
> and af_packet drivers implement it. This also gets used for
> capture of VLAN tagged packets when legacy pdump is used.
>
> RX strip: when RTE_ETH_RX_OFFLOAD_VLAN_STRIP is enabled, the driver
> calls rte_vlan_strip() on received packets in both normal and
> infinite_rx modes. For infinite_rx, offloads are deferred to
> packet delivery rather than applied during ring fill, so the
> stored template packets remain unmodified.
>
> TX insert: when RTE_MBUF_F_TX_VLAN is set on an mbuf, the driver
> inserts the VLAN tag via rte_vlan_insert() before writing to pcap
> or sending to the interface. Indirect or shared mbufs get a new
> header mbuf to avoid modifying the original.
>
> Runtime reconfiguration is supported through vlan_offload_set,
> which propagates the strip setting to all active RX queues.
>
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> doc/guides/nics/features/pcap.ini | 1 +
> doc/guides/nics/pcap.rst | 11 +++
> doc/guides/rel_notes/release_26_03.rst | 5 ++
> drivers/net/pcap/pcap_ethdev.c | 96 ++++++++++++++++++++++++--
> 4 files changed, 109 insertions(+), 4 deletions(-)
>
> diff --git a/doc/guides/nics/features/pcap.ini b/doc/guides/nics/features/pcap.ini
> index c4f0360a06..ec7c91c650 100644
> --- a/doc/guides/nics/features/pcap.ini
> +++ b/doc/guides/nics/features/pcap.ini
> @@ -9,6 +9,7 @@ Queue start/stop = Y
> Timestamp offload = Y
> Basic stats = Y
> Stats per queue = Y
> +VLAN offload = Y
> Multiprocess aware = Y
> FreeBSD = Y
> Linux = Y
> diff --git a/doc/guides/nics/pcap.rst b/doc/guides/nics/pcap.rst
> index fbfe854bb1..bed5006a42 100644
> --- a/doc/guides/nics/pcap.rst
> +++ b/doc/guides/nics/pcap.rst
> @@ -247,3 +247,14 @@ will be discarded by the Rx flushing operation.
> The network interface provided to the PMD should be up.
> The PMD will return an error if the interface is down,
> and the PMD itself won't change the status of the external network interface.
> +
> +Features and Limitations
> +~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +* The PMD will re-insert the VLAN tag transparently to the packet if the kernel
> + strips it, as long as the ``RTE_ETH_RX_OFFLOAD_VLAN_STRIP`` is not enabled by the
> + application.
> +
> +* The PMD will transparently insert a VLAN tag to transmitted packets if
> + ``RTE_ETH_TX_OFFLOAD_VLAN_INSERT`` is enabled and the mbuf has ``RTE_MBUF_F_TX_VLAN``
> + set.
> diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
> index d121951f5c..6aef623bf8 100644
> --- a/doc/guides/rel_notes/release_26_03.rst
> +++ b/doc/guides/rel_notes/release_26_03.rst
> @@ -118,6 +118,11 @@ New Features
> Added handling of the key combination Control+L
> to clear the screen before redisplaying the prompt.
>
> +* **Updated PCAP ethernet driver.**
> +
> + * Added support for VLAN insertion and stripping.
> +
> +
> Removed Items
> -------------
>
> diff --git a/drivers/net/pcap/pcap_ethdev.c b/drivers/net/pcap/pcap_ethdev.c
> index c3270a676c..c49ca7fa2b 100644
> --- a/drivers/net/pcap/pcap_ethdev.c
> +++ b/drivers/net/pcap/pcap_ethdev.c
> @@ -76,6 +76,7 @@ struct queue_missed_stat {
> struct pcap_rx_queue {
> uint16_t port_id;
> uint16_t queue_id;
> + bool vlan_strip;
> struct rte_mempool *mb_pool;
> struct queue_stat rx_stat;
> struct queue_missed_stat missed_stat;
> @@ -106,6 +107,7 @@ struct pmd_internals {
> bool single_iface;
> bool phy_mac;
> bool infinite_rx;
> + bool vlan_strip;
> };
>
> struct pmd_process_private {
> @@ -270,7 +272,11 @@ eth_pcap_rx_infinite(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
> bufs[i]->data_len = pcap_buf->data_len;
> bufs[i]->pkt_len = pcap_buf->pkt_len;
> bufs[i]->port = pcap_q->port_id;
> - rx_bytes += pcap_buf->data_len;
> +
> + if (pcap_q->vlan_strip)
> + rte_vlan_strip(bufs[i]);
> +
> + rx_bytes += bufs[i]->data_len;
>
> /* Enqueue packet back on ring to allow infinite rx. */
> rte_ring_enqueue(pcap_q->pkts, pcap_buf);
> @@ -336,6 +342,10 @@ eth_pcap_rx(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
> }
>
> mbuf->pkt_len = len;
> +
> + if (pcap_q->vlan_strip)
> + rte_vlan_strip(mbuf);
> +
> uint64_t us = (uint64_t)header->ts.tv_sec * US_PER_S + header->ts.tv_usec;
>
> *RTE_MBUF_DYNFIELD(mbuf, timestamp_dynfield_offset, rte_mbuf_timestamp_t *) = us;
> @@ -382,6 +392,39 @@ calculate_timestamp(struct timeval *ts) {
> }
> }
>
> +static uint16_t
> +eth_pcap_tx_prepare(void *queue __rte_unused, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
> +{
> + uint16_t nb_tx;
> + int error;
> +
> + for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
> + struct rte_mbuf *m = tx_pkts[nb_tx];
> +
> +#ifdef RTE_LIBRTE_ETHDEV_DEBUG
> + error = rte_validate_tx_offload(m);
> + if (unlikely(error)) {
> + rte_errno = -error;
> + break;
> + }
> +#endif
> + /* Do VLAN tag insertion */
> + if (unlikely(m->ol_flags & RTE_MBUF_F_TX_VLAN)) {
> + error = rte_vlan_insert(&m);
> +
> + /* rte_vlan_insert() could change pointer (currently does not) */
> + tx_pkts[nb_tx] = m;
> +
> + if (unlikely(error != 0)) {
> + PMD_TX_LOG(ERR, "rte_vlan_insert failed: %s", strerror(-error));
> + rte_errno = -error;
> + break;
> + }
> + }
> + }
> + return nb_tx;
> +}
> +

Are we sure this is the correct way to do this? To behave like a normal NIC
the Tx VLAN tag should be inserted in the Tx function, rather than tx
prepare should it not? There is no guarantee apps will call tx_prepare
before Tx.

/Bruce