Allow setting transmit size to be a small value which has Ethernet
header but no IP or UDP header, since control level protocols can be
very short.

Checksum offloads are disabled when headers are incomplete.
copy_buf_to_pkt_segs stops at the last segment to prevent OOB access.

Suggested-by: Stephen Hemminger <[email protected]>
Signed-off-by: Xingui Yang <[email protected]>
---
Changes in v4:
- Removed ultra-small frame support (< 14 bytes) per Stephen's review.
---
 app/test-pmd/config.c                       | 13 ++++----
 app/test-pmd/txonly.c                       | 36 +++++++++++++++++++--
 doc/guides/rel_notes/release_26_11.rst      |  7 ++++
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 11 +++++++
 4 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index aa03eb99cc..86c794b923 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -6333,9 +6333,8 @@ set_tx_pkt_segments(unsigned int *seg_lengths, unsigned 
int nb_segs)
        /*
         * Check that each segment length is greater or equal than
         * the mbuf data size.
-        * Check also that the total packet length is greater or equal than the
-        * size of an empty UDP/IP packet (sizeof(struct rte_ether_hdr) +
-        * 20 + 8).
+        * The total packet length must be at least the size of an
+        * Ethernet header.
         */
        tx_pkt_len = 0;
        for (i = 0; i < nb_segs; i++) {
@@ -6347,10 +6346,10 @@ set_tx_pkt_segments(unsigned int *seg_lengths, unsigned 
int nb_segs)
                }
                tx_pkt_len = (uint16_t)(tx_pkt_len + seg_lengths[i]);
        }
-       if (tx_pkt_len < (sizeof(struct rte_ether_hdr) + 20 + 8)) {
-               fprintf(stderr, "total packet length=%u < %d - give up\n",
-                               (unsigned) tx_pkt_len,
-                               (int)(sizeof(struct rte_ether_hdr) + 20 + 8));
+       if (tx_pkt_len < sizeof(struct rte_ether_hdr)) {
+               fprintf(stderr, "total packet length=%u < %zu - give up\n",
+                               (unsigned int) tx_pkt_len,
+                               sizeof(struct rte_ether_hdr));
                return;
        }
 
diff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c
index a4acb85d29..e90e28a6e6 100644
--- a/app/test-pmd/txonly.c
+++ b/app/test-pmd/txonly.c
@@ -76,6 +76,12 @@ copy_buf_to_pkt_segs(void* buf, unsigned len, struct 
rte_mbuf *pkt,
        while (offset >= seg->data_len) {
                offset -= seg->data_len;
                seg = seg->next;
+               /*
+                * The packet may be shorter than the header stack when
+                * generating runt frames, stop once it runs out of segments.
+                */
+               if (seg == NULL)
+                       return;
        }
        copy_len = seg->data_len - offset;
        seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);
@@ -84,6 +90,8 @@ copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf 
*pkt,
                len -= copy_len;
                buf = ((char*) buf + copy_len);
                seg = seg->next;
+               if (seg == NULL)
+                       return;
                seg_buf = rte_pktmbuf_mtod(seg, char *);
                copy_len = seg->data_len;
        }
@@ -193,7 +201,6 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool 
*mbp,
        pkt->vlan_tci = vlan_tci;
        pkt->vlan_tci_outer = vlan_tci_outer;
        pkt->l2_len = sizeof(struct rte_ether_hdr);
-       pkt->l3_len = sizeof(struct rte_ipv4_hdr);
 
        pkt_len = pkt->data_len;
        pkt_seg = pkt;
@@ -204,6 +211,25 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool 
*mbp,
                pkt_len += pkt_seg->data_len;
        }
        pkt_seg->next = NULL; /* Last segment of packet. */
+
+       /*
+        * A runt frame may be too short to carry a full IPv4/UDP header.
+        * Clamp l3_len and drop any checksum offload whose header is not
+        * fully present, so the PMD is never asked to checksum bytes that
+        * are not in the frame. pkt_len is at least sizeof(struct 
rte_ether_hdr),
+        * so the subtraction below cannot underflow.
+        */
+       pkt->l3_len = RTE_MIN(sizeof(struct rte_ipv4_hdr),
+                       pkt_len - sizeof(struct rte_ether_hdr));
+       if (pkt_len < sizeof(struct rte_ether_hdr) +
+                       sizeof(struct rte_ipv4_hdr))
+               pkt->ol_flags &= ~(RTE_MBUF_F_TX_IP_CKSUM |
+                               RTE_MBUF_F_TX_L4_MASK);
+       else if (pkt_len < sizeof(struct rte_ether_hdr) +
+                       sizeof(struct rte_ipv4_hdr) +
+                       sizeof(struct rte_udp_hdr))
+               pkt->ol_flags &= ~RTE_MBUF_F_TX_L4_MASK;
+
        /*
         * Copy headers in first packet segment(s).
         */
@@ -405,7 +431,13 @@ tx_only_begin(portid_t pi)
        pkt_hdr_len = (uint16_t)(sizeof(struct rte_ether_hdr) +
                                 sizeof(struct rte_ipv4_hdr) +
                                 sizeof(struct rte_udp_hdr));
-       pkt_data_len = tx_pkt_length - pkt_hdr_len;
+       /*
+        * tx_pkt_length may be smaller than the full header stack when
+        * generating runt frames, clamp the payload length to zero in
+        * that case so the IP/UDP length fields stay sane.
+        */
+       pkt_data_len = tx_pkt_length > pkt_hdr_len ?
+                       tx_pkt_length - pkt_hdr_len : 0;
 
        if ((tx_pkt_split == TX_PKT_SPLIT_RND || txonly_multi_flow) &&
            tx_pkt_seg_lengths[0] < pkt_hdr_len) {
diff --git a/doc/guides/rel_notes/release_26_11.rst 
b/doc/guides/rel_notes/release_26_11.rst
index c8cc86295d..577b892d57 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,13 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Updated testpmd application.**
+
+  Added support for runt frames in txonly mode. The minimum packet
+  length for ``set txpkts`` is relaxed to the Ethernet header size,
+  since control level protocols can be very short. Checksum offloads
+  are automatically disabled when headers are incomplete.
+
 
 Removed Items
 -------------
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst 
b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index e65376df54..0edcdd8444 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -874,6 +874,17 @@ Set the length of each segment of the TX-ONLY packets or 
length of packet for FL
 
 Where x[,y]* represents a CSV list of values, without white space.
 
+The total packet length may be set as small as the Ethernet header
+(``sizeof(struct rte_ether_hdr)``), since control level protocols can
+be very short. This generates runt frames with truncated IPv4/UDP
+headers. Checksum offloads are automatically disabled when the
+corresponding header is not fully present.
+
+Note that random split (``set txsplit rand``) and multi-flow
+(``set txonly-flows``) still require the first segment to hold the full
+Ethernet/IPv4/UDP header stack, so they cannot be combined with runt
+lengths.
+
 set txtimes
 ~~~~~~~~~~~
 
-- 
2.43.0

Reply via email to