Allow set txpkts to accept packet lengths as small as one byte, enabling generation of runt frames and ultra-small packets for testing NIC padding logic and undersized frame handling.
The txonly engine handles three ranges: normal (>= 42 bytes) with full Ethernet/IPv4/UDP headers, runt (14-41 bytes) with truncated headers, and ultra-small (< 14 bytes) filled with a repeating pattern. Checksum offloads are disabled when headers are incomplete. Minimum length validation in set_tx_pkt_segments() is relaxed from 42 to 1 byte. Random split and multi-flow still require the full header stack in the first segment, as enforced by tx_only_begin(). Signed-off-by: Xingui Yang <[email protected]> Suggested-by: Stephen Hemminger <[email protected]> --- app/test-pmd/config.c | 18 +++-- app/test-pmd/txonly.c | 79 ++++++++++++++++++--- doc/guides/rel_notes/release_26_07.rst | 5 +- doc/guides/testpmd_app_ug/testpmd_funcs.rst | 21 ++++++ 4 files changed, 104 insertions(+), 19 deletions(-) diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 3df7412ef6..31b33d3956 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -6327,9 +6327,9 @@ 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 may be as small as one byte, this allows + * generating runt frames and ultra-small packets for testing NIC + * padding logic and undersized frame handling. */ tx_pkt_len = 0; for (i = 0; i < nb_segs; i++) { @@ -6341,12 +6341,16 @@ 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 == 0) { + fprintf(stderr, "total packet length is zero - give up\n"); return; } + if (tx_pkt_len < (sizeof(struct rte_ether_hdr) + 20 + 8)) + fprintf(stderr, + "Warning: total packet length=%u < %d (full Ether/IP/UDP), " + "generating runt or ultra-small frames\n", + (unsigned int) tx_pkt_len, + (int)(sizeof(struct rte_ether_hdr) + 20 + 8)); for (i = 0; i < nb_segs; i++) tx_pkt_seg_lengths[i] = (uint16_t) seg_lengths[i]; diff --git a/app/test-pmd/txonly.c b/app/test-pmd/txonly.c index a4acb85d29..6e102e2447 100644 --- a/app/test-pmd/txonly.c +++ b/app/test-pmd/txonly.c @@ -64,6 +64,9 @@ static int32_t timestamp_off; /**< Timestamp dynamic field offset */ static bool timestamp_enable; /**< Timestamp enable */ static uint64_t timestamp_initial[RTE_MAX_ETHPORTS]; +/* Fill pattern for ultra-small packets too small to hold Ethernet header. */ +static const char pad_pattern[] = "0123456789abcdef"; + static void copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset) @@ -76,6 +79,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 +93,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; } @@ -192,9 +203,6 @@ pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp, pkt->ol_flags |= ol_flags; 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; for (i = 1; i < nb_segs; i++) { @@ -204,15 +212,60 @@ 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. */ + /* * Copy headers in first packet segment(s). + * For runt frames (pkt_len < full header stack), headers are + * truncated to fit, copy_buf_to_pkt_segs stops at the last + * segment so no out-of-bounds access occurs. For ultra-small + * packets (pkt_len < Ethernet header), a fill pattern is used + * instead of headers. */ - copy_buf_to_pkt(eth_hdr, sizeof(*eth_hdr), pkt, 0); - copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt, - sizeof(struct rte_ether_hdr)); - copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt, - sizeof(struct rte_ether_hdr) + - sizeof(struct rte_ipv4_hdr)); + if (pkt_len >= sizeof(struct rte_ether_hdr)) { + /* + * 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(rte_ether_hdr), so the subtraction below + * cannot underflow. + */ + pkt->l2_len = sizeof(struct rte_ether_hdr); + 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_buf_to_pkt(eth_hdr, sizeof(*eth_hdr), pkt, 0); + copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt, + sizeof(struct rte_ether_hdr)); + copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt, + sizeof(struct rte_ether_hdr) + + sizeof(struct rte_ipv4_hdr)); + } else { + struct rte_mbuf *seg; + uint32_t j; + + pkt->l2_len = 0; + pkt->l3_len = 0; + pkt->ol_flags &= ~(RTE_MBUF_F_TX_IP_CKSUM | + RTE_MBUF_F_TX_L4_MASK); + seg = pkt; + while (seg != NULL) { + char *data = rte_pktmbuf_mtod(seg, char *); + + for (j = 0; j < seg->data_len; j++) + data[j] = pad_pattern[j % + (sizeof(pad_pattern) - 1)]; + seg = seg->next; + } + } + if (txonly_multi_flow) { uint16_t src_var = RTE_PER_LCORE(_src_port_var); struct rte_udp_hdr *udp_hdr; @@ -405,7 +458,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 or ultra-small 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_07.rst b/doc/guides/rel_notes/release_26_07.rst index 8b1bdada1a..3fca990f78 100644 --- a/doc/guides/rel_notes/release_26_07.rst +++ b/doc/guides/rel_notes/release_26_07.rst @@ -242,8 +242,9 @@ New Features * **Updated testpmd application.** - Added support for setting VLAN priority and CFI/DEI bits - in ``tx_vlan set``/``tx_qinq set`` commands and ``vlan_tci`` parameter. + * Added support for setting VLAN priority and CFI/DEI bits + in ``tx_vlan set``/``tx_qinq set`` commands and ``vlan_tci`` parameter. + * Added support for runt and ultra-small frames in the txonly mode. * **Added AI review helpers.** diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst index 364d348372..b69924c5f9 100644 --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst @@ -876,6 +876,27 @@ 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 one byte, which is below +the size of an empty IPv4/UDP packet. This generates runt frames or +ultra-small packets, which is useful for testing how a driver handles +undersized frames and NIC padding logic. + +Normal packets (>= 42 bytes) + Full Ethernet/IPv4/UDP headers with checksum offloads. + +Runt frames (14-41 bytes) + Ethernet header with truncated or absent IPv4/UDP header. + Checksum offloads are automatically disabled. + +Ultra-small packets (< 14 bytes) + No standard headers; filled with a repeating pattern. + Checksum offloads are automatically disabled. + +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 +or ultra-small lengths. + set txtimes ~~~~~~~~~~~ -- 2.43.0

