LCOV - code coverage report
Current view: top level - app/test - test_pmd_pcap.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 923 1067 86.5 %
Date: 2026-06-01 18:36:17 Functions: 54 56 96.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 517 996 51.9 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2026 Stephen Hemminger
       3                 :            :  */
       4                 :            : 
       5                 :            : #include "test.h"
       6                 :            : 
       7                 :            : #include "packet_burst_generator.h"
       8                 :            : 
       9                 :            : #include <stdio.h>
      10                 :            : #include <stdlib.h>
      11                 :            : #include <string.h>
      12                 :            : #include <stdint.h>
      13                 :            : 
      14                 :            : #include <unistd.h>
      15                 :            : 
      16                 :            : #ifdef RTE_EXEC_ENV_WINDOWS
      17                 :            : #include <windows.h>
      18                 :            : #else
      19                 :            : #include <sys/ioctl.h>
      20                 :            : #include <sys/socket.h>
      21                 :            : #include <net/if.h>
      22                 :            : #endif
      23                 :            : 
      24                 :            : #include <pcap/pcap.h>
      25                 :            : 
      26                 :            : #include <rte_ethdev.h>
      27                 :            : #include <rte_bus_vdev.h>
      28                 :            : #include <rte_mbuf.h>
      29                 :            : #include <rte_mbuf_dyn.h>
      30                 :            : #include <rte_mempool.h>
      31                 :            : #include <rte_ether.h>
      32                 :            : #include <rte_string_fns.h>
      33                 :            : #include <rte_cycles.h>
      34                 :            : #include <rte_ip.h>
      35                 :            : #include <rte_udp.h>
      36                 :            : 
      37                 :            : #define SOCKET0 0
      38                 :            : #define RING_SIZE 256
      39                 :            : #define NB_MBUF 1024
      40                 :            : #define NUM_PACKETS 64
      41                 :            : #define MAX_PKT_BURST 32
      42                 :            : #define PCAP_SNAPLEN 65535
      43                 :            : 
      44                 :            : /* Packet sizes to test */
      45                 :            : #define PKT_SIZE_MIN 60
      46                 :            : #define PKT_SIZE_SMALL 128
      47                 :            : #define PKT_SIZE_MEDIUM 512
      48                 :            : #define PKT_SIZE_LARGE 1024
      49                 :            : #define PKT_SIZE_MTU 1500
      50                 :            : #define PKT_SIZE_JUMBO 9000
      51                 :            : 
      52                 :            : static struct rte_mempool *mp;
      53                 :            : 
      54                 :            : /* Timestamp dynamic field access */
      55                 :            : static int timestamp_dynfield_offset = -1;
      56                 :            : static uint64_t timestamp_rx_dynflag;
      57                 :            : 
      58                 :            : /* Temporary file paths shared between tests */
      59                 :            : static char tx_pcap_path[PATH_MAX];     /* test_tx_to_file -> test_rx_from_file */
      60                 :            : 
      61                 :            : /* Constants for multi-queue tests */
      62                 :            : #define MULTI_QUEUE_NUM_QUEUES   4U
      63                 :            : #define MULTI_QUEUE_NUM_PACKETS  100U
      64                 :            : #define MULTI_QUEUE_BURST_SIZE   32U
      65                 :            : 
      66                 :            : /* MAC addresses for packet generation */
      67                 :            : static struct rte_ether_addr src_mac;
      68                 :            : static struct rte_ether_addr dst_mac = {
      69                 :            :         .addr_bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
      70                 :            : };
      71                 :            : 
      72                 :            : /* Sample Ethernet/IPv4/UDP packet for testing */
      73                 :            : static const uint8_t test_packet[] = {
      74                 :            :         /* Ethernet header */
      75                 :            :         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,  /* dst MAC (broadcast) */
      76                 :            :         0x00, 0x11, 0x22, 0x33, 0x44, 0x55,  /* src MAC */
      77                 :            :         0x08, 0x00,                          /* EtherType: IPv4 */
      78                 :            :         /* IPv4 header */
      79                 :            :         0x45, 0x00, 0x00, 0x2e,              /* ver, ihl, tos, len */
      80                 :            :         0x00, 0x01, 0x00, 0x00,              /* id, flags, frag */
      81                 :            :         0x40, 0x11, 0x00, 0x00,              /* ttl, proto(UDP), csum */
      82                 :            :         0x0a, 0x00, 0x00, 0x01,              /* src: 10.0.0.1 */
      83                 :            :         0x0a, 0x00, 0x00, 0x02,              /* dst: 10.0.0.2 */
      84                 :            :         /* UDP header */
      85                 :            :         0x04, 0xd2, 0x04, 0xd2,              /* sport, dport (1234) */
      86                 :            :         0x00, 0x1a, 0x00, 0x00,              /* len, csum */
      87                 :            :         /* Payload: "Test packet!" */
      88                 :            :         0x54, 0x65, 0x73, 0x74, 0x20, 0x70,
      89                 :            :         0x61, 0x63, 0x6b, 0x65, 0x74, 0x21
      90                 :            : };
      91                 :            : 
      92                 :            : /* Helper: Get timestamp from mbuf using dynamic field */
      93                 :            : static inline rte_mbuf_timestamp_t
      94                 :            : mbuf_timestamp_get(const struct rte_mbuf *mbuf)
      95                 :            : {
      96                 :        192 :         return *RTE_MBUF_DYNFIELD(mbuf, timestamp_dynfield_offset, rte_mbuf_timestamp_t *);
      97                 :            : }
      98                 :            : 
      99                 :            : /* Helper: Check if mbuf has valid timestamp */
     100                 :            : static inline int
     101                 :            : mbuf_has_timestamp(const struct rte_mbuf *mbuf)
     102                 :            : {
     103                 :        193 :         return (mbuf->ol_flags & timestamp_rx_dynflag) != 0;
     104                 :            : }
     105                 :            : 
     106                 :            : /* Helper: Initialize timestamp dynamic field access */
     107                 :            : static int
     108                 :          1 : timestamp_init(void)
     109                 :            : {
     110                 :            :         int offset;
     111                 :            : 
     112                 :          1 :         offset = rte_mbuf_dynfield_lookup(RTE_MBUF_DYNFIELD_TIMESTAMP_NAME, NULL);
     113         [ -  + ]:          1 :         if (offset < 0) {
     114                 :            :                 printf("Timestamp dynfield not registered\n");
     115                 :          0 :                 return -1;
     116                 :            :         }
     117                 :          1 :         timestamp_dynfield_offset = offset;
     118                 :            : 
     119                 :          1 :         offset = rte_mbuf_dynflag_lookup(RTE_MBUF_DYNFLAG_RX_TIMESTAMP_NAME, NULL);
     120         [ -  + ]:          1 :         if (offset < 0) {
     121                 :            :                 printf("Timestamp dynflag not registered\n");
     122                 :          0 :                 return -1;
     123                 :            :         }
     124                 :          1 :         timestamp_rx_dynflag = RTE_BIT64(offset);
     125                 :          1 :         return 0;
     126                 :            : }
     127                 :            : 
     128                 :            : #ifdef RTE_EXEC_ENV_WINDOWS
     129                 :            : 
     130                 :            : /*
     131                 :            :  * Helper: Create a unique temporary file path (Windows version)
     132                 :            :  */
     133                 :            : static int
     134                 :            : create_temp_path(char *buf, size_t buflen, const char *prefix)
     135                 :            : {
     136                 :            :         char temp_dir[MAX_PATH];
     137                 :            :         char temp_file[MAX_PATH];
     138                 :            :         DWORD ret;
     139                 :            : 
     140                 :            :         ret = GetTempPathA(sizeof(temp_dir), temp_dir);
     141                 :            :         if (ret == 0 || ret > sizeof(temp_dir))
     142                 :            :                 return -1;
     143                 :            : 
     144                 :            :         if (GetTempFileNameA(temp_dir, prefix, 0, temp_file) == 0)
     145                 :            :                 return -1;
     146                 :            : 
     147                 :            :         ret = snprintf(buf, buflen, "%s.pcap", temp_file);
     148                 :            :         if (ret >= buflen) {
     149                 :            :                 DeleteFileA(temp_file);
     150                 :            :                 return -1;
     151                 :            :         }
     152                 :            : 
     153                 :            :         if (MoveFileA(temp_file, buf) == 0) {
     154                 :            :                 DeleteFileA(temp_file);
     155                 :            :                 return -1;
     156                 :            :         }
     157                 :            : 
     158                 :            :         return 0;
     159                 :            : }
     160                 :            : 
     161                 :            : /*
     162                 :            :  * Helper: Remove temporary file (Windows version)
     163                 :            :  */
     164                 :            : static inline void
     165                 :            : remove_temp_file(const char *path)
     166                 :            : {
     167                 :            :         if (path[0] != '\0')
     168                 :            :                 DeleteFileA(path);
     169                 :            : }
     170                 :            : 
     171                 :            : #else /* POSIX */
     172                 :            : 
     173                 :            : /*
     174                 :            :  * Helper: Create a unique temporary file path (POSIX version)
     175                 :            :  */
     176                 :            : static int
     177                 :         32 : create_temp_path(char *buf, size_t buflen, const char *prefix)
     178                 :            : {
     179                 :            :         int fd;
     180                 :            : 
     181                 :            :         snprintf(buf, buflen, "/tmp/%s_XXXXXX.pcap", prefix);
     182                 :         32 :         fd = mkstemps(buf, 5);  /* 5 = strlen(".pcap") */
     183         [ +  - ]:         32 :         if (fd < 0)
     184                 :            :                 return -1;
     185                 :         32 :         close(fd);
     186                 :         32 :         return 0;
     187                 :            : }
     188                 :            : 
     189                 :            : /*
     190                 :            :  * Helper: Remove temporary file (POSIX version)
     191                 :            :  */
     192                 :            : static inline void
     193                 :            : remove_temp_file(const char *path)
     194                 :            : {
     195   [ +  -  +  -  :         22 :         if (path[0] != '\0')
          +  -  +  -  +  
          -  -  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  -  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
             +  -  +  - ]
     196                 :         32 :                 unlink(path);
     197                 :            : }
     198                 :            : 
     199                 :            : #endif /* RTE_EXEC_ENV_WINDOWS */
     200                 :            : 
     201                 :            : /*
     202                 :            :  * Helper: Create a pcap file with test packets using libpcap
     203                 :            :  */
     204                 :            : static int
     205                 :         13 : create_test_pcap(const char *path, unsigned int num_pkts)
     206                 :            : {
     207                 :            :         pcap_t *pd;
     208                 :            :         pcap_dumper_t *dumper;
     209                 :            :         struct pcap_pkthdr hdr;
     210                 :            :         unsigned int i;
     211                 :            : 
     212                 :         13 :         pd = pcap_open_dead(DLT_EN10MB, PCAP_SNAPLEN);
     213         [ -  + ]:         13 :         if (pd == NULL) {
     214                 :            :                 printf("pcap_open_dead failed\n");
     215                 :          0 :                 return -1;
     216                 :            :         }
     217                 :            : 
     218                 :         13 :         dumper = pcap_dump_open(pd, path);
     219         [ -  + ]:         13 :         if (dumper == NULL) {
     220                 :          0 :                 printf("pcap_dump_open failed: %s\n", pcap_geterr(pd));
     221                 :          0 :                 pcap_close(pd);
     222                 :          0 :                 return -1;
     223                 :            :         }
     224                 :            : 
     225                 :            :         memset(&hdr, 0, sizeof(hdr));
     226                 :         13 :         hdr.caplen = sizeof(test_packet);
     227                 :         13 :         hdr.len = sizeof(test_packet);
     228                 :            : 
     229         [ +  + ]:        503 :         for (i = 0; i < num_pkts; i++) {
     230                 :        490 :                 hdr.ts.tv_sec = i;
     231                 :        490 :                 hdr.ts.tv_usec = 0;
     232                 :        490 :                 pcap_dump((u_char *)dumper, &hdr, test_packet);
     233                 :            :         }
     234                 :            : 
     235                 :         13 :         pcap_dump_close(dumper);
     236                 :         13 :         pcap_close(pd);
     237                 :         13 :         return 0;
     238                 :            : }
     239                 :            : 
     240                 :            : /*
     241                 :            :  * Helper: Create pcap file with packets of specified size
     242                 :            :  */
     243                 :            : static int
     244                 :          3 : create_sized_pcap(const char *path, unsigned int num_pkts, uint16_t pkt_size)
     245                 :            : {
     246                 :            :         pcap_t *pd;
     247                 :            :         pcap_dumper_t *dumper;
     248                 :            :         struct pcap_pkthdr hdr;
     249                 :            :         uint8_t *pkt_data;
     250                 :            :         unsigned int i;
     251                 :            : 
     252                 :            :         /* Minimum valid ethernet frame */
     253                 :            :         if (pkt_size < 60)
     254                 :            :                 pkt_size = 60;
     255                 :            : 
     256                 :          3 :         pkt_data = calloc(1, pkt_size);
     257         [ +  - ]:          3 :         if (pkt_data == NULL)
     258                 :            :                 return -1;
     259                 :            : 
     260                 :            :         /* Build ethernet header */
     261                 :            :         struct rte_ether_hdr *eth_hdr = (struct rte_ether_hdr *)pkt_data;
     262                 :            :         rte_ether_addr_copy(&src_mac, &eth_hdr->src_addr);
     263                 :            :         rte_ether_addr_copy(&dst_mac, &eth_hdr->dst_addr);
     264                 :          3 :         eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
     265                 :            : 
     266                 :            :         /* Build IP header */
     267                 :          3 :         struct rte_ipv4_hdr *ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
     268                 :          3 :         uint16_t ip_len = pkt_size - sizeof(struct rte_ether_hdr);
     269                 :          3 :         ip_hdr->version_ihl = RTE_IPV4_VHL_DEF;
     270         [ +  - ]:          3 :         ip_hdr->total_length = rte_cpu_to_be_16(ip_len);
     271                 :          3 :         ip_hdr->time_to_live = 64;
     272                 :          3 :         ip_hdr->next_proto_id = IPPROTO_UDP;
     273                 :          3 :         ip_hdr->src_addr = rte_cpu_to_be_32(IPV4_ADDR(10, 0, 0, 1));
     274                 :          3 :         ip_hdr->dst_addr = rte_cpu_to_be_32(IPV4_ADDR(10, 0, 0, 2));
     275         [ +  - ]:          3 :         ip_hdr->hdr_checksum = 0;
     276                 :          3 :         ip_hdr->hdr_checksum = rte_ipv4_cksum(ip_hdr);
     277                 :            : 
     278                 :            :         /* Build UDP header */
     279                 :            :         struct rte_udp_hdr *udp_hdr = (struct rte_udp_hdr *)(ip_hdr + 1);
     280                 :          3 :         uint16_t udp_len = ip_len - sizeof(struct rte_ipv4_hdr);
     281                 :          3 :         udp_hdr->src_port = rte_cpu_to_be_16(1234);
     282                 :          3 :         udp_hdr->dst_port = rte_cpu_to_be_16(1234);
     283         [ +  - ]:          3 :         udp_hdr->dgram_len = rte_cpu_to_be_16(udp_len);
     284                 :          3 :         udp_hdr->dgram_cksum = 0;
     285                 :            : 
     286                 :            :         /* Fill payload with pattern */
     287                 :          3 :         uint8_t *payload = (uint8_t *)(udp_hdr + 1);
     288                 :          3 :         uint16_t payload_len = udp_len - sizeof(struct rte_udp_hdr);
     289         [ +  + ]:      26877 :         for (uint16_t j = 0; j < payload_len; j++)
     290                 :      26874 :                 payload[j] = (uint8_t)(j & 0xFF);
     291                 :            : 
     292                 :          3 :         pd = pcap_open_dead(DLT_EN10MB, PCAP_SNAPLEN);
     293         [ -  + ]:          3 :         if (pd == NULL) {
     294                 :          0 :                 free(pkt_data);
     295                 :          0 :                 return -1;
     296                 :            :         }
     297                 :            : 
     298                 :          3 :         dumper = pcap_dump_open(pd, path);
     299         [ -  + ]:          3 :         if (dumper == NULL) {
     300                 :          0 :                 pcap_close(pd);
     301                 :          0 :                 free(pkt_data);
     302                 :          0 :                 return -1;
     303                 :            :         }
     304                 :            : 
     305                 :            :         memset(&hdr, 0, sizeof(hdr));
     306                 :          3 :         hdr.caplen = pkt_size;
     307                 :          3 :         hdr.len = pkt_size;
     308                 :            : 
     309         [ +  + ]:         51 :         for (i = 0; i < num_pkts; i++) {
     310                 :         48 :                 hdr.ts.tv_sec = i;
     311                 :         48 :                 hdr.ts.tv_usec = 0;
     312                 :            :                 /* Vary sequence byte in payload */
     313                 :         48 :                 payload[0] = (uint8_t)(i & 0xFF);
     314                 :         48 :                 pcap_dump((u_char *)dumper, &hdr, pkt_data);
     315                 :            :         }
     316                 :            : 
     317                 :          3 :         pcap_dump_close(dumper);
     318                 :          3 :         pcap_close(pd);
     319                 :          3 :         free(pkt_data);
     320                 :          3 :         return 0;
     321                 :            : }
     322                 :            : 
     323                 :            : /*
     324                 :            :  * Helper: Create pcap file with varied packet sizes
     325                 :            :  */
     326                 :            : static int
     327                 :          1 : create_varied_pcap(const char *path, unsigned int num_pkts)
     328                 :            : {
     329                 :            :         static const uint16_t sizes[] = {
     330                 :            :                 PKT_SIZE_MIN, PKT_SIZE_SMALL, PKT_SIZE_MEDIUM,
     331                 :            :                 PKT_SIZE_LARGE, PKT_SIZE_MTU
     332                 :            :         };
     333                 :            :         pcap_t *pd;
     334                 :            :         pcap_dumper_t *dumper;
     335                 :            :         struct pcap_pkthdr hdr;
     336                 :            :         uint8_t *pkt_data;
     337                 :            :         unsigned int i;
     338                 :            : 
     339                 :          1 :         pkt_data = calloc(1, PKT_SIZE_MTU);
     340         [ +  - ]:          1 :         if (pkt_data == NULL)
     341                 :            :                 return -1;
     342                 :            : 
     343                 :          1 :         pd = pcap_open_dead(DLT_EN10MB, PCAP_SNAPLEN);
     344         [ -  + ]:          1 :         if (pd == NULL) {
     345                 :          0 :                 free(pkt_data);
     346                 :          0 :                 return -1;
     347                 :            :         }
     348                 :            : 
     349                 :          1 :         dumper = pcap_dump_open(pd, path);
     350         [ -  + ]:          1 :         if (dumper == NULL) {
     351                 :          0 :                 pcap_close(pd);
     352                 :          0 :                 free(pkt_data);
     353                 :          0 :                 return -1;
     354                 :            :         }
     355                 :            : 
     356         [ +  + ]:         65 :         for (i = 0; i < num_pkts; i++) {
     357                 :         64 :                 uint16_t pkt_size = sizes[i % RTE_DIM(sizes)];
     358                 :            : 
     359         [ -  + ]:         64 :                 memset(pkt_data, 0, pkt_size);
     360                 :            : 
     361                 :            :                 /* Build ethernet header */
     362                 :            :                 struct rte_ether_hdr *eth_hdr = (struct rte_ether_hdr *)pkt_data;
     363                 :            :                 rte_ether_addr_copy(&src_mac, &eth_hdr->src_addr);
     364                 :            :                 rte_ether_addr_copy(&dst_mac, &eth_hdr->dst_addr);
     365                 :         64 :                 eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
     366                 :            : 
     367                 :            :                 /* Build IP header */
     368                 :         64 :                 struct rte_ipv4_hdr *ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
     369                 :         64 :                 uint16_t ip_len = pkt_size - sizeof(struct rte_ether_hdr);
     370                 :         64 :                 ip_hdr->version_ihl = RTE_IPV4_VHL_DEF;
     371         [ -  + ]:         64 :                 ip_hdr->total_length = rte_cpu_to_be_16(ip_len);
     372                 :         64 :                 ip_hdr->time_to_live = 64;
     373                 :         64 :                 ip_hdr->next_proto_id = IPPROTO_UDP;
     374                 :         64 :                 ip_hdr->src_addr = rte_cpu_to_be_32(IPV4_ADDR(10, 0, 0, 1));
     375                 :         64 :                 ip_hdr->dst_addr = rte_cpu_to_be_32(IPV4_ADDR(10, 0, 0, 2));
     376         [ -  + ]:         64 :                 ip_hdr->hdr_checksum = 0;
     377                 :         64 :                 ip_hdr->hdr_checksum = rte_ipv4_cksum(ip_hdr);
     378                 :            : 
     379                 :            :                 /* Build UDP header */
     380                 :            :                 struct rte_udp_hdr *udp_hdr = (struct rte_udp_hdr *)(ip_hdr + 1);
     381                 :         64 :                 uint16_t udp_len = ip_len - sizeof(struct rte_ipv4_hdr);
     382                 :         64 :                 udp_hdr->src_port = rte_cpu_to_be_16(1234);
     383                 :         64 :                 udp_hdr->dst_port = rte_cpu_to_be_16(1234);
     384         [ -  + ]:        128 :                 udp_hdr->dgram_len = rte_cpu_to_be_16(udp_len);
     385                 :            : 
     386                 :            :                 memset(&hdr, 0, sizeof(hdr));
     387                 :         64 :                 hdr.ts.tv_sec = i;
     388                 :         64 :                 hdr.caplen = pkt_size;
     389                 :         64 :                 hdr.len = pkt_size;
     390                 :            : 
     391                 :         64 :                 pcap_dump((u_char *)dumper, &hdr, pkt_data);
     392                 :            :         }
     393                 :            : 
     394                 :          1 :         pcap_dump_close(dumper);
     395                 :          1 :         pcap_close(pd);
     396                 :          1 :         free(pkt_data);
     397                 :          1 :         return 0;
     398                 :            : }
     399                 :            : 
     400                 :            : /*
     401                 :            :  * Helper: Create pcap file with specific timestamps for testing
     402                 :            :  */
     403                 :            : static int
     404                 :          1 : create_timestamped_pcap(const char *path, unsigned int num_pkts,
     405                 :            :                         uint32_t base_sec, uint32_t usec_increment)
     406                 :            : {
     407                 :            :         pcap_t *pd;
     408                 :            :         pcap_dumper_t *dumper;
     409                 :            :         struct pcap_pkthdr hdr;
     410                 :            :         unsigned int i;
     411                 :            : 
     412                 :          1 :         pd = pcap_open_dead_with_tstamp_precision(DLT_EN10MB, PCAP_SNAPLEN,
     413                 :            :                                                   PCAP_TSTAMP_PRECISION_MICRO);
     414         [ +  - ]:          1 :         if (pd == NULL)
     415                 :            :                 return -1;
     416                 :            : 
     417                 :          1 :         dumper = pcap_dump_open(pd, path);
     418         [ -  + ]:          1 :         if (dumper == NULL) {
     419                 :          0 :                 pcap_close(pd);
     420                 :          0 :                 return -1;
     421                 :            :         }
     422                 :            : 
     423                 :            :         memset(&hdr, 0, sizeof(hdr));
     424                 :          1 :         hdr.caplen = sizeof(test_packet);
     425                 :          1 :         hdr.len = sizeof(test_packet);
     426                 :            : 
     427         [ +  + ]:         65 :         for (i = 0; i < num_pkts; i++) {
     428                 :         64 :                 uint64_t total_usec = (uint64_t)i * usec_increment;
     429                 :         64 :                 hdr.ts.tv_sec = base_sec + total_usec / 1000000;
     430                 :         64 :                 hdr.ts.tv_usec = total_usec % 1000000;
     431                 :         64 :                 pcap_dump((u_char *)dumper, &hdr, test_packet);
     432                 :            :         }
     433                 :            : 
     434                 :          1 :         pcap_dump_close(dumper);
     435                 :          1 :         pcap_close(pd);
     436                 :          1 :         return 0;
     437                 :            : }
     438                 :            : 
     439                 :            : /*
     440                 :            :  * Helper: Count packets in a pcap file using libpcap
     441                 :            :  */
     442                 :            : static int
     443                 :          5 : count_pcap_packets(const char *path)
     444                 :            : {
     445                 :            :         pcap_t *pd;
     446                 :            :         char errbuf[PCAP_ERRBUF_SIZE];
     447                 :            :         struct pcap_pkthdr *hdr;
     448                 :            :         const u_char *data;
     449                 :            :         int count = 0;
     450                 :            : 
     451                 :          5 :         pd = pcap_open_offline(path, errbuf);
     452         [ +  - ]:          5 :         if (pd == NULL)
     453                 :            :                 return -1;
     454                 :            : 
     455         [ +  + ]:        169 :         while (pcap_next_ex(pd, &hdr, &data) == 1)
     456                 :        164 :                 count++;
     457                 :            : 
     458                 :          5 :         pcap_close(pd);
     459                 :          5 :         return count;
     460                 :            : }
     461                 :            : 
     462                 :            : /*
     463                 :            :  * Helper: Get packet sizes from pcap file
     464                 :            :  */
     465                 :            : static int
     466                 :          2 : get_pcap_packet_sizes(const char *path, uint16_t *sizes, unsigned int max_pkts)
     467                 :            : {
     468                 :            :         pcap_t *pd;
     469                 :            :         char errbuf[PCAP_ERRBUF_SIZE];
     470                 :            :         struct pcap_pkthdr *hdr;
     471                 :            :         const u_char *data;
     472                 :            :         unsigned int count = 0;
     473                 :            : 
     474                 :          2 :         pd = pcap_open_offline(path, errbuf);
     475         [ +  - ]:          2 :         if (pd == NULL)
     476                 :            :                 return -1;
     477                 :            : 
     478   [ +  +  +  - ]:        106 :         while (pcap_next_ex(pd, &hdr, &data) == 1 && count < max_pkts) {
     479                 :        104 :                 sizes[count] = hdr->caplen;
     480                 :        104 :                 count++;
     481                 :            :         }
     482                 :            : 
     483                 :          2 :         pcap_close(pd);
     484                 :          2 :         return count;
     485                 :            : }
     486                 :            : 
     487                 :            : /*
     488                 :            :  * Helper: Verify packets in pcap file are truncated correctly
     489                 :            :  * Returns 0 if all packets have caplen == expected_caplen and len == expected_len
     490                 :            :  */
     491                 :            : static int
     492                 :          2 : verify_pcap_truncation(const char *path, uint32_t expected_caplen,
     493                 :            :                        uint32_t expected_len, unsigned int *pkt_count)
     494                 :            : {
     495                 :            :         pcap_t *pd;
     496                 :            :         char errbuf[PCAP_ERRBUF_SIZE];
     497                 :            :         struct pcap_pkthdr *hdr;
     498                 :            :         const u_char *data;
     499                 :            :         unsigned int count = 0;
     500                 :            : 
     501                 :          2 :         pd = pcap_open_offline(path, errbuf);
     502         [ +  - ]:          2 :         if (pd == NULL)
     503                 :            :                 return -1;
     504                 :            : 
     505         [ +  + ]:         74 :         while (pcap_next_ex(pd, &hdr, &data) == 1) {
     506   [ +  -  -  + ]:         72 :                 if (hdr->caplen != expected_caplen || hdr->len != expected_len) {
     507                 :          0 :                         printf("Packet %u: caplen=%u (expected %u), len=%u (expected %u)\n",
     508                 :            :                                count, hdr->caplen, expected_caplen,
     509                 :            :                                hdr->len, expected_len);
     510                 :          0 :                         pcap_close(pd);
     511                 :          0 :                         return -1;
     512                 :            :                 }
     513                 :         72 :                 count++;
     514                 :            :         }
     515                 :            : 
     516                 :          2 :         pcap_close(pd);
     517         [ +  - ]:          2 :         if (pkt_count)
     518                 :          2 :                 *pkt_count = count;
     519                 :            :         return 0;
     520                 :            : }
     521                 :            : 
     522                 :            : /*
     523                 :            :  * Helper: Configure and start a pcap ethdev port with custom config
     524                 :            :  */
     525                 :            : static int
     526                 :         20 : setup_pcap_port_conf(uint16_t port, const struct rte_eth_conf *conf)
     527                 :            : {
     528                 :            :         int ret;
     529                 :            : 
     530                 :         20 :         ret = rte_eth_dev_configure(port, 1, 1, conf);
     531         [ -  + ]:         20 :         TEST_ASSERT(ret == 0, "Failed to configure port %u: %s",
     532                 :            :                     port, rte_strerror(-ret));
     533                 :            : 
     534                 :         20 :         ret = rte_eth_rx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL, mp);
     535         [ -  + ]:         20 :         TEST_ASSERT(ret == 0, "Failed to setup RX queue on port %u: %s",
     536                 :            :                     port, rte_strerror(-ret));
     537                 :            : 
     538                 :         20 :         ret = rte_eth_tx_queue_setup(port, 0, RING_SIZE, SOCKET0, NULL);
     539         [ -  + ]:         20 :         TEST_ASSERT(ret == 0, "Failed to setup TX queue on port %u: %s",
     540                 :            :                     port, rte_strerror(-ret));
     541                 :            : 
     542                 :         20 :         ret = rte_eth_dev_start(port);
     543         [ -  + ]:         20 :         TEST_ASSERT(ret == 0, "Failed to start port %u: %s",
     544                 :            :                     port, rte_strerror(-ret));
     545                 :            : 
     546                 :            :         return 0;
     547                 :            : }
     548                 :            : 
     549                 :            : /*
     550                 :            :  * Helper: Configure and start a pcap ethdev port (default: timestamp offload)
     551                 :            :  */
     552                 :            : static int
     553                 :            : setup_pcap_port(uint16_t port)
     554                 :            : {
     555                 :         17 :         struct rte_eth_conf port_conf = {
     556                 :            :                 .rxmode.offloads = RTE_ETH_RX_OFFLOAD_TIMESTAMP,
     557                 :            :         };
     558                 :            : 
     559                 :         17 :         return setup_pcap_port_conf(port, &port_conf);
     560                 :            : }
     561                 :            : 
     562                 :            : /*
     563                 :            :  * Helper: Create a pcap vdev and return its port ID
     564                 :            :  */
     565                 :            : static int
     566                 :         14 : create_pcap_vdev(const char *name, const char *devargs, uint16_t *port_id)
     567                 :            : {
     568                 :            :         int ret;
     569                 :            : 
     570                 :         14 :         ret = rte_vdev_init(name, devargs);
     571         [ -  + ]:         14 :         TEST_ASSERT(ret == 0, "Failed to create vdev %s: %s",
     572                 :            :                     name, rte_strerror(-ret));
     573                 :            : 
     574                 :         14 :         ret = rte_eth_dev_get_port_by_name(name, port_id);
     575         [ -  + ]:         14 :         TEST_ASSERT(ret == 0, "Failed to get port ID for %s", name);
     576                 :            : 
     577                 :            :         return 0;
     578                 :            : }
     579                 :            : 
     580                 :            : /*
     581                 :            :  * Helper: Cleanup a pcap vdev
     582                 :            :  */
     583                 :            : static void
     584                 :            : cleanup_pcap_vdev(const char *name, uint16_t port_id)
     585                 :            : {
     586                 :         20 :         rte_eth_dev_stop(port_id);
     587                 :         20 :         rte_vdev_uninit(name);
     588                 :            : }
     589                 :            : 
     590                 :            : /*
     591                 :            :  * Helper: Generate test packets using packet_burst_generator
     592                 :            :  */
     593                 :            : static int
     594                 :          7 : generate_test_packets(struct rte_mempool *pool, struct rte_mbuf **mbufs,
     595                 :            :                       unsigned int count, uint16_t pkt_len)
     596                 :            : {
     597                 :            :         struct rte_ether_hdr eth_hdr;
     598                 :            :         struct rte_ipv4_hdr ip_hdr;
     599                 :            :         struct rte_udp_hdr udp_hdr;
     600                 :            :         uint16_t ip_pkt_data_len;
     601                 :            :         int nb_pkt;
     602                 :            : 
     603                 :            :         /* Initialize ethernet header */
     604                 :          7 :         initialize_eth_header(&eth_hdr, &src_mac, &dst_mac,
     605                 :            :                               RTE_ETHER_TYPE_IPV4, 0, 0);
     606                 :            : 
     607                 :            :         /* Calculate IP payload length (total - eth - ip headers) */
     608                 :          7 :         ip_pkt_data_len = pkt_len - sizeof(struct rte_ether_hdr) -
     609                 :            :                           sizeof(struct rte_ipv4_hdr);
     610                 :            : 
     611                 :            :         /* Initialize UDP header */
     612                 :          7 :         initialize_udp_header(&udp_hdr, 1234, 1234,
     613                 :          7 :                               ip_pkt_data_len - sizeof(struct rte_udp_hdr));
     614                 :            : 
     615                 :            :         /* Initialize IPv4 header */
     616                 :          7 :         initialize_ipv4_header(&ip_hdr, IPV4_ADDR(10, 0, 0, 1),
     617                 :            :                                IPV4_ADDR(10, 0, 0, 2), ip_pkt_data_len);
     618                 :            : 
     619                 :            :         /* Generate packet burst */
     620                 :          7 :         nb_pkt = generate_packet_burst(pool, mbufs, &eth_hdr, 0,
     621                 :            :                                        &ip_hdr, 1, &udp_hdr,
     622                 :            :                                        count, pkt_len, 1);
     623                 :            : 
     624                 :          7 :         return nb_pkt;
     625                 :            : }
     626                 :            : 
     627                 :            : /*
     628                 :            :  * Helper: Allocate mbufs and fill with test packet data (legacy method)
     629                 :            :  */
     630                 :            : static int
     631                 :          3 : alloc_test_mbufs(struct rte_mbuf **mbufs, unsigned int count)
     632                 :            : {
     633                 :            :         unsigned int i;
     634                 :            :         int ret;
     635                 :            : 
     636                 :          3 :         ret = rte_pktmbuf_alloc_bulk(mp, mbufs, count);
     637         [ +  - ]:          3 :         if (ret != 0)
     638                 :            :                 return -1;
     639                 :            : 
     640         [ +  + ]:        132 :         for (i = 0; i < count; i++) {
     641                 :        129 :                 memcpy(rte_pktmbuf_mtod(mbufs[i], void *),
     642                 :            :                        test_packet, sizeof(test_packet));
     643                 :        129 :                 mbufs[i]->data_len = sizeof(test_packet);
     644                 :        129 :                 mbufs[i]->pkt_len = sizeof(test_packet);
     645                 :            :         }
     646                 :            :         return 0;
     647                 :            : }
     648                 :            : 
     649                 :            : /*
     650                 :            :  * Helper: Allocate a multi-segment mbuf for jumbo frames
     651                 :            :  * Returns the head mbuf with chained segments, or NULL on failure
     652                 :            :  */
     653                 :            : static struct rte_mbuf *
     654                 :          8 : alloc_jumbo_mbuf(uint32_t pkt_len, uint8_t fill_byte)
     655                 :            : {
     656                 :          8 :         struct rte_mbuf *head = NULL;
     657                 :            :         struct rte_mbuf **prev = &head;
     658                 :            :         uint32_t remaining = pkt_len;
     659                 :            :         uint16_t nb_segs = 0;
     660                 :            : 
     661         [ +  + ]:         48 :         while (remaining > 0) {
     662                 :         40 :                 struct rte_mbuf *seg = rte_pktmbuf_alloc(mp);
     663                 :            :                 uint16_t seg_size;
     664                 :            : 
     665         [ -  + ]:         40 :                 if (seg == NULL) {
     666                 :          0 :                         rte_pktmbuf_free(head);
     667                 :          0 :                         return NULL;
     668                 :            :                 }
     669                 :            : 
     670                 :         40 :                 seg_size = RTE_MIN(remaining, rte_pktmbuf_tailroom(seg));
     671                 :         40 :                 seg->data_len = seg_size;
     672                 :            : 
     673                 :            :                 /* Fill segment with pattern */
     674                 :         40 :                 memset(rte_pktmbuf_mtod(seg, void *), fill_byte, seg_size);
     675                 :            : 
     676                 :         40 :                 *prev = seg;
     677                 :         40 :                 prev = &seg->next;
     678                 :         40 :                 remaining -= seg_size;
     679                 :         40 :                 nb_segs++;
     680                 :            :         }
     681                 :            : 
     682         [ +  - ]:          8 :         if (head != NULL) {
     683                 :          8 :                 head->pkt_len = pkt_len;
     684                 :          8 :                 head->nb_segs = nb_segs;
     685                 :            :         }
     686                 :            : 
     687                 :            :         return head;
     688                 :            : }
     689                 :            : 
     690                 :            : /*
     691                 :            :  * Helper: Allocate a multi-segment mbuf with controlled segment size.
     692                 :            :  *
     693                 :            :  * Unlike alloc_jumbo_mbuf which fills segments to tailroom capacity,
     694                 :            :  * this limits each segment to seg_size bytes, guaranteeing that the
     695                 :            :  * resulting mbuf chain has multiple segments even for moderate pkt_len.
     696                 :            :  */
     697                 :            : static struct rte_mbuf *
     698                 :          8 : alloc_multiseg_mbuf(uint32_t pkt_len, uint16_t seg_size, uint8_t fill_byte)
     699                 :            : {
     700                 :          8 :         struct rte_mbuf *head = NULL;
     701                 :            :         struct rte_mbuf **prev = &head;
     702                 :            :         uint32_t remaining = pkt_len;
     703                 :            :         uint16_t nb_segs = 0;
     704                 :            : 
     705         [ +  + ]:         48 :         while (remaining > 0) {
     706                 :         40 :                 struct rte_mbuf *seg = rte_pktmbuf_alloc(mp);
     707                 :            :                 uint16_t this_len;
     708                 :            : 
     709         [ -  + ]:         40 :                 if (seg == NULL) {
     710                 :          0 :                         rte_pktmbuf_free(head);
     711                 :          0 :                         return NULL;
     712                 :            :                 }
     713                 :            : 
     714                 :         40 :                 this_len = RTE_MIN(remaining, seg_size);
     715                 :         40 :                 this_len = RTE_MIN(this_len, rte_pktmbuf_tailroom(seg));
     716                 :         40 :                 seg->data_len = this_len;
     717                 :            : 
     718                 :         40 :                 memset(rte_pktmbuf_mtod(seg, void *), fill_byte, this_len);
     719                 :            : 
     720                 :         40 :                 *prev = seg;
     721                 :         40 :                 prev = &seg->next;
     722                 :         40 :                 remaining -= this_len;
     723                 :         40 :                 nb_segs++;
     724                 :            :         }
     725                 :            : 
     726         [ +  - ]:          8 :         if (head != NULL) {
     727                 :          8 :                 head->pkt_len = pkt_len;
     728                 :          8 :                 head->nb_segs = nb_segs;
     729                 :            :         }
     730                 :            : 
     731                 :            :         return head;
     732                 :            : }
     733                 :            : 
     734                 :            : /*
     735                 :            :  * Helper: Receive packets from port (no retry needed for file-based RX)
     736                 :            :  */
     737                 :            : static int
     738                 :          8 : receive_packets(uint16_t port, struct rte_mbuf **mbufs,
     739                 :            :                 unsigned int max_pkts, unsigned int *received)
     740                 :            : {
     741                 :            :         unsigned int total = 0;
     742                 :            : 
     743         [ +  + ]:         15 :         while (total < max_pkts) {
     744                 :          8 :                 uint16_t nb_rx = rte_eth_rx_burst(port, 0, &mbufs[total], max_pkts - total);
     745         [ +  + ]:          8 :                 if (nb_rx == 0)
     746                 :            :                         break;
     747                 :          7 :                 total += nb_rx;
     748                 :            :         }
     749                 :          8 :         *received = total;
     750                 :          8 :         return 0;
     751                 :            : }
     752                 :            : 
     753                 :            : /*
     754                 :            :  * Helper: Verify mbuf contains expected test packet
     755                 :            :  */
     756                 :            : static int
     757                 :         64 : verify_packet(struct rte_mbuf *mbuf)
     758                 :            : {
     759         [ -  + ]:         64 :         TEST_ASSERT_EQUAL(rte_pktmbuf_data_len(mbuf), sizeof(test_packet),
     760                 :            :                           "Packet length mismatch");
     761         [ -  + ]:         64 :         TEST_ASSERT_BUFFERS_ARE_EQUAL(rte_pktmbuf_mtod(mbuf, void *),
     762                 :            :                                       test_packet, sizeof(test_packet),
     763                 :            :                                       "Packet data mismatch");
     764                 :            :         return 0;
     765                 :            : }
     766                 :            : 
     767                 :            : /*
     768                 :            :  * Helper: Check if interface supports Ethernet (DLT_EN10MB)
     769                 :            :  *
     770                 :            :  * The pcap PMD only works with Ethernet interfaces. On FreeBSD/macOS,
     771                 :            :  * the loopback interface uses DLT_NULL which is incompatible.
     772                 :            :  */
     773                 :            : static int
     774                 :         42 : iface_is_ethernet(const char *name)
     775                 :            : {
     776                 :            :         char errbuf[PCAP_ERRBUF_SIZE];
     777                 :            :         pcap_t *pcap;
     778                 :            :         int datalink;
     779                 :            : 
     780                 :         42 :         pcap = pcap_open_live(name, 256, 0, 0, errbuf);
     781         [ +  + ]:         42 :         if (pcap == NULL)
     782                 :            :                 return 0;
     783                 :            : 
     784                 :         25 :         datalink = pcap_datalink(pcap);
     785                 :         25 :         pcap_close(pcap);
     786                 :            : 
     787                 :         25 :         return datalink == DLT_EN10MB;
     788                 :            : }
     789                 :            : 
     790                 :            : /*
     791                 :            :  * Helper: Find a usable test interface using pcap_findalldevs
     792                 :            :  *
     793                 :            :  * Uses libpcap's portable interface enumeration which works on
     794                 :            :  * Linux, FreeBSD, macOS, and Windows.
     795                 :            :  *
     796                 :            :  * Only selects interfaces that support Ethernet link type (DLT_EN10MB).
     797                 :            :  * This excludes loopback on FreeBSD/macOS which uses DLT_NULL.
     798                 :            :  *
     799                 :            :  * Preference order:
     800                 :            :  * 1. Loopback interface (if Ethernet - Linux only)
     801                 :            :  * 2. Any interface that is UP and RUNNING
     802                 :            :  * 3. Any available Ethernet interface
     803                 :            :  *
     804                 :            :  * Returns static buffer with interface name, or NULL if none found.
     805                 :            :  */
     806                 :            : static const char *
     807                 :          5 : find_test_iface(void)
     808                 :            : {
     809                 :            :         static char iface_name[256];
     810                 :            :         pcap_if_t *alldevs, *dev;
     811                 :            :         char errbuf[PCAP_ERRBUF_SIZE];
     812                 :            :         const char *loopback = NULL;
     813                 :            :         const char *any_up = NULL;
     814                 :            :         const char *any_ether = NULL;
     815                 :            : 
     816         [ -  + ]:          5 :         if (pcap_findalldevs(&alldevs, errbuf) != 0) {
     817                 :            :                 printf("pcap_findalldevs failed: %s\n", errbuf);
     818                 :          0 :                 return NULL;
     819                 :            :         }
     820                 :            : 
     821         [ -  + ]:          5 :         if (alldevs == NULL) {
     822                 :            :                 printf("No interfaces found\n");
     823                 :          0 :                 return NULL;
     824                 :            :         }
     825                 :            : 
     826         [ +  + ]:         45 :         for (dev = alldevs; dev != NULL; dev = dev->next) {
     827         [ -  + ]:         40 :                 if (dev->name == NULL)
     828                 :          0 :                         continue;
     829                 :            : 
     830                 :            :                 /* Only consider Ethernet interfaces */
     831         [ +  + ]:         40 :                 if (!iface_is_ethernet(dev->name))
     832                 :         30 :                         continue;
     833                 :            : 
     834         [ +  + ]:         10 :                 if (any_ether == NULL)
     835                 :          5 :                         any_ether = dev->name;
     836                 :            : 
     837                 :            :                 /* Prefer loopback for safety (Linux lo supports DLT_EN10MB) */
     838   [ +  +  +  - ]:         10 :                 if ((dev->flags & PCAP_IF_LOOPBACK) && loopback == NULL) {
     839                 :          5 :                         loopback = dev->name;
     840                 :          5 :                         continue;
     841                 :            :                 }
     842                 :            : 
     843                 :            : #ifdef PCAP_IF_UP
     844         [ +  - ]:          5 :                 if ((dev->flags & PCAP_IF_UP) &&
     845         [ +  - ]:          5 :                     (dev->flags & PCAP_IF_RUNNING) &&
     846                 :            :                     any_up == NULL)
     847                 :          5 :                         any_up = dev->name;
     848                 :            : #else
     849                 :            :                 if (any_up == NULL)
     850                 :            :                         any_up = dev->name;
     851                 :            : #endif
     852                 :            :         }
     853                 :            : 
     854                 :            :         /* Select best available interface */
     855                 :            :         const char *selected = NULL;
     856         [ -  + ]:          5 :         if (loopback != NULL)
     857                 :            :                 selected = loopback;
     858         [ #  # ]:          0 :         else if (any_up != NULL)
     859                 :            :                 selected = any_up;
     860         [ #  # ]:          0 :         else if (any_ether != NULL)
     861                 :            :                 selected = any_ether;
     862                 :            : 
     863                 :            :         if (selected != NULL)
     864                 :            :                 strlcpy(iface_name, selected, sizeof(iface_name));
     865                 :            : 
     866                 :          5 :         pcap_freealldevs(alldevs);
     867         [ -  + ]:          5 :         return selected ? iface_name : NULL;
     868                 :            : }
     869                 :            : 
     870                 :            : /*
     871                 :            :  * Test: Transmit packets to pcap file
     872                 :            :  */
     873                 :            : static int
     874                 :          1 : test_tx_to_file(void)
     875                 :            : {
     876                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
     877                 :            :         char devargs[256];
     878                 :            :         uint16_t port_id;
     879                 :            :         int nb_tx, pkt_count;
     880                 :            :         int ret;
     881                 :            : 
     882                 :            :         printf("Testing TX to pcap file\n");
     883                 :            : 
     884         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_pcap_path, sizeof(tx_pcap_path),
     885                 :            :                                      "pcap_tx") == 0,
     886                 :            :                     "Failed to create temp file path");
     887                 :            : 
     888                 :            :         ret = snprintf(devargs, sizeof(devargs), "tx_pcap=%s", tx_pcap_path);
     889         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
     890                 :            :                     "devargs string truncated");
     891         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_tx", devargs, &port_id) == 0,
     892                 :            :                     "Failed to create TX vdev");
     893         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
     894                 :            :                     "Failed to setup TX port");
     895         [ -  + ]:          1 :         TEST_ASSERT(alloc_test_mbufs(mbufs, NUM_PACKETS) == 0,
     896                 :            :                     "Failed to allocate mbufs");
     897                 :            : 
     898                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, NUM_PACKETS);
     899         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(nb_tx, NUM_PACKETS,
     900                 :            :                           "TX burst failed: sent %d/%d", nb_tx, NUM_PACKETS);
     901                 :            : 
     902                 :          1 :         cleanup_pcap_vdev("net_pcap_tx", port_id);
     903                 :            : 
     904                 :          1 :         pkt_count = count_pcap_packets(tx_pcap_path);
     905         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(pkt_count, NUM_PACKETS,
     906                 :            :                           "Pcap file has %d packets, expected %d",
     907                 :            :                           pkt_count, NUM_PACKETS);
     908                 :            : 
     909                 :            :         printf("TX to file PASSED: %d packets written\n", NUM_PACKETS);
     910                 :          1 :         return TEST_SUCCESS;
     911                 :            : }
     912                 :            : 
     913                 :            : /*
     914                 :            :  * Test: Receive packets from pcap file
     915                 :            :  * Uses output from TX test as input
     916                 :            :  */
     917                 :            : static int
     918                 :          1 : test_rx_from_file(void)
     919                 :            : {
     920                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
     921                 :            :         char devargs[256];
     922                 :            :         uint16_t port_id;
     923                 :            :         unsigned int received, i;
     924                 :            :         int ret;
     925                 :            : 
     926                 :            :         printf("Testing RX from pcap file\n");
     927                 :            : 
     928                 :            :         /* Create input file if TX test didn't run */
     929         [ -  + ]:          1 :         if (access(tx_pcap_path, F_OK) != 0) {
     930         [ #  # ]:          0 :                 TEST_ASSERT(create_temp_path(tx_pcap_path, sizeof(tx_pcap_path),
     931                 :            :                                              "pcap_rx_input") == 0,
     932                 :            :                             "Failed to create temp path");
     933         [ #  # ]:          0 :                 TEST_ASSERT(create_test_pcap(tx_pcap_path, NUM_PACKETS) == 0,
     934                 :            :                             "Failed to create input pcap");
     935                 :            :         }
     936                 :            : 
     937                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", tx_pcap_path);
     938         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
     939                 :            :                     "devargs string truncated");
     940         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_rx", devargs, &port_id) == 0,
     941                 :            :                     "Failed to create RX vdev");
     942         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
     943                 :            :                     "Failed to setup RX port");
     944                 :            : 
     945                 :          1 :         receive_packets(port_id, mbufs, NUM_PACKETS, &received);
     946         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(received, NUM_PACKETS,
     947                 :            :                           "Received %u packets, expected %d",
     948                 :            :                           received, NUM_PACKETS);
     949                 :            : 
     950         [ +  + ]:         65 :         for (i = 0; i < received; i++) {
     951         [ -  + ]:         64 :                 TEST_ASSERT(verify_packet(mbufs[i]) == 0,
     952                 :            :                             "Packet %u verification failed", i);
     953                 :            :         }
     954                 :          1 :         rte_pktmbuf_free_bulk(mbufs, received);
     955                 :            : 
     956                 :          1 :         cleanup_pcap_vdev("net_pcap_rx", port_id);
     957                 :            : 
     958                 :            :         printf("RX from file PASSED: %u packets verified\n", received);
     959                 :          1 :         return TEST_SUCCESS;
     960                 :            : }
     961                 :            : 
     962                 :            : /*
     963                 :            :  * Test: TX with varied packet sizes using packet_burst_generator
     964                 :            :  */
     965                 :            : static int
     966                 :          1 : test_tx_varied_sizes(void)
     967                 :            : {
     968                 :            :         static const uint16_t test_sizes[] = {
     969                 :            :                 PKT_SIZE_MIN, PKT_SIZE_SMALL, 200
     970                 :            :         };
     971                 :            :         char tx_path[PATH_MAX];
     972                 :            :         char devargs[256];
     973                 :            :         uint16_t port_id;
     974                 :            :         unsigned int i;
     975                 :            :         int ret;
     976                 :            : 
     977                 :            :         printf("Testing TX with varied packet sizes\n");
     978                 :            : 
     979         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_path, sizeof(tx_path),
     980                 :            :                                      "pcap_tx_varied") == 0,
     981                 :            :                     "Failed to create temp file path");
     982                 :            : 
     983                 :            :         ret = snprintf(devargs, sizeof(devargs), "tx_pcap=%s", tx_path);
     984         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
     985                 :            :                     "devargs string truncated");
     986         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_tx_var", devargs, &port_id) == 0,
     987                 :            :                     "Failed to create TX vdev");
     988         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
     989                 :            :                     "Failed to setup TX port");
     990                 :            : 
     991                 :            :         unsigned int total_tx = 0;
     992                 :            : 
     993         [ +  + ]:          4 :         for (i = 0; i < RTE_DIM(test_sizes); i++) {
     994                 :            :                 struct rte_mbuf *mbufs[MAX_PKT_BURST];
     995                 :            :                 int nb_pkt, nb_tx;
     996                 :            : 
     997                 :          3 :                 nb_pkt = generate_test_packets(mp, mbufs, MAX_PKT_BURST,
     998                 :          3 :                                                test_sizes[i]);
     999         [ -  + ]:          3 :                 TEST_ASSERT(nb_pkt > 0,
    1000                 :            :                             "Failed to generate packets of size %u",
    1001                 :            :                             test_sizes[i]);
    1002                 :            : 
    1003                 :          3 :                 nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, nb_pkt);
    1004         [ -  + ]:          3 :                 if (nb_tx < nb_pkt)
    1005                 :          0 :                         rte_pktmbuf_free_bulk(&mbufs[nb_tx], nb_pkt - nb_tx);
    1006                 :            : 
    1007                 :            :                 printf("  Size %u: generated %d, transmitted %d\n",
    1008                 :            :                        test_sizes[i], nb_pkt, nb_tx);
    1009         [ -  + ]:          3 :                 TEST_ASSERT(nb_tx > 0, "Failed to TX packets of size %u",
    1010                 :            :                             test_sizes[i]);
    1011                 :          3 :                 total_tx += nb_tx;
    1012                 :            :         }
    1013                 :            : 
    1014                 :          1 :         cleanup_pcap_vdev("net_pcap_tx_var", port_id);
    1015                 :            : 
    1016                 :            :         /* Read back pcap file and verify packet sizes match what was sent */
    1017                 :            :         {
    1018                 :            :                 uint16_t sizes[MAX_PKT_BURST * RTE_DIM(test_sizes)];
    1019                 :            :                 int pkt_count;
    1020                 :            :                 unsigned int idx = 0;
    1021                 :            : 
    1022                 :          1 :                 pkt_count = get_pcap_packet_sizes(tx_path, sizes,
    1023                 :            :                                                   RTE_DIM(sizes));
    1024         [ -  + ]:          1 :                 TEST_ASSERT_EQUAL((unsigned int)pkt_count, total_tx,
    1025                 :            :                                   "Pcap has %d packets, expected %u",
    1026                 :            :                                   pkt_count, total_tx);
    1027                 :            : 
    1028         [ +  + ]:          4 :                 for (i = 0; i < RTE_DIM(test_sizes); i++) {
    1029                 :            :                         unsigned int j;
    1030                 :            :                         /* Each size produced MAX_PKT_BURST (or fewer) packets */
    1031         [ +  + ]:         99 :                         for (j = 0; j < MAX_PKT_BURST && idx < (unsigned int)pkt_count; j++, idx++) {
    1032         [ -  + ]:         96 :                                 TEST_ASSERT_EQUAL(sizes[idx], test_sizes[i],
    1033                 :            :                                                   "Packet %u: size %u, expected %u",
    1034                 :            :                                                   idx, sizes[idx], test_sizes[i]);
    1035                 :            :                         }
    1036                 :            :                 }
    1037                 :            :         }
    1038                 :            : 
    1039                 :            :         remove_temp_file(tx_path);
    1040                 :            : 
    1041                 :            :         printf("TX varied sizes PASSED: %u packets verified\n", total_tx);
    1042                 :          1 :         return TEST_SUCCESS;
    1043                 :            : }
    1044                 :            : 
    1045                 :            : /*
    1046                 :            :  * Test: RX with varied packet sizes
    1047                 :            :  */
    1048                 :            : static int
    1049                 :          1 : test_rx_varied_sizes(void)
    1050                 :            : {
    1051                 :            :         static const uint16_t expected_sizes[] = {
    1052                 :            :                 PKT_SIZE_MIN, PKT_SIZE_SMALL, PKT_SIZE_MEDIUM,
    1053                 :            :                 PKT_SIZE_LARGE, PKT_SIZE_MTU
    1054                 :            :         };
    1055                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    1056                 :            :         uint16_t rx_sizes[NUM_PACKETS];
    1057                 :            :         char varied_pcap_path[PATH_MAX];
    1058                 :            :         char devargs[256];
    1059                 :            :         uint16_t port_id;
    1060                 :            :         unsigned int received, i;
    1061                 :            :         int ret;
    1062                 :            : 
    1063                 :            :         printf("Testing RX with varied packet sizes\n");
    1064                 :            : 
    1065         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(varied_pcap_path, sizeof(varied_pcap_path),
    1066                 :            :                                      "pcap_varied") == 0,
    1067                 :            :                     "Failed to create temp path");
    1068         [ -  + ]:          1 :         TEST_ASSERT(create_varied_pcap(varied_pcap_path, NUM_PACKETS) == 0,
    1069                 :            :                     "Failed to create varied pcap");
    1070                 :            : 
    1071                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", varied_pcap_path);
    1072         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1073                 :            :                     "devargs string truncated");
    1074         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_var", devargs, &port_id) == 0,
    1075                 :            :                     "Failed to create varied RX vdev");
    1076         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    1077                 :            :                     "Failed to setup varied RX port");
    1078                 :            : 
    1079                 :          1 :         receive_packets(port_id, mbufs, NUM_PACKETS, &received);
    1080         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(received, NUM_PACKETS,
    1081                 :            :                           "Received %u packets, expected %d",
    1082                 :            :                           received, NUM_PACKETS);
    1083                 :            : 
    1084                 :            :         /* Verify packet sizes match expected pattern */
    1085         [ +  + ]:         65 :         for (i = 0; i < received; i++) {
    1086                 :         64 :                 uint16_t expected = expected_sizes[i % RTE_DIM(expected_sizes)];
    1087                 :         64 :                 rx_sizes[i] = rte_pktmbuf_pkt_len(mbufs[i]);
    1088         [ -  + ]:         64 :                 TEST_ASSERT_EQUAL(rx_sizes[i], expected,
    1089                 :            :                                   "Packet %u: size %u, expected %u",
    1090                 :            :                                   i, rx_sizes[i], expected);
    1091                 :            :         }
    1092                 :            : 
    1093                 :          1 :         rte_pktmbuf_free_bulk(mbufs, received);
    1094                 :          1 :         cleanup_pcap_vdev("net_pcap_var", port_id);
    1095                 :            :         remove_temp_file(varied_pcap_path);
    1096                 :            : 
    1097                 :            :         printf("RX varied sizes PASSED: %u packets with correct sizes\n", received);
    1098                 :          1 :         return TEST_SUCCESS;
    1099                 :            : }
    1100                 :            : 
    1101                 :            : /*
    1102                 :            :  * Test: Infinite RX mode - loops through pcap file continuously
    1103                 :            :  */
    1104                 :            : static int
    1105                 :          1 : test_infinite_rx(void)
    1106                 :            : {
    1107                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    1108                 :            :         char infinite_pcap_path[PATH_MAX];
    1109                 :            :         char devargs[256];
    1110                 :            :         uint16_t port_id;
    1111                 :            :         unsigned int total_rx = 0;
    1112                 :            :         int iter, attempts;
    1113                 :            :         int ret;
    1114                 :            : 
    1115                 :            :         printf("Testing infinite RX mode\n");
    1116                 :            : 
    1117         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(infinite_pcap_path, sizeof(infinite_pcap_path),
    1118                 :            :                                      "pcap_inf") == 0,
    1119                 :            :                     "Failed to create temp path");
    1120         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(infinite_pcap_path, NUM_PACKETS) == 0,
    1121                 :            :                     "Failed to create input pcap");
    1122                 :            : 
    1123                 :            :         ret = snprintf(devargs, sizeof(devargs),
    1124                 :            :                        "rx_pcap=%s,infinite_rx=1", infinite_pcap_path);
    1125         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1126                 :            :                     "devargs string truncated");
    1127         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_inf", devargs, &port_id) == 0,
    1128                 :            :                     "Failed to create infinite RX vdev");
    1129         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    1130                 :            :                     "Failed to setup infinite RX port");
    1131                 :            : 
    1132                 :            :         /* Read more packets than file contains to verify looping */
    1133         [ +  + ]:          2 :         for (iter = 0; iter < 3 && total_rx < NUM_PACKETS * 2; iter++) {
    1134         [ +  + ]:          5 :                 for (attempts = 0; attempts < 100 && total_rx < NUM_PACKETS * 2;
    1135                 :          4 :                      attempts++) {
    1136                 :          4 :                         uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, mbufs,
    1137                 :            :                                                           MAX_PKT_BURST);
    1138         [ +  - ]:          4 :                         if (nb_rx > 0)
    1139                 :          4 :                                 rte_pktmbuf_free_bulk(mbufs, nb_rx);
    1140                 :          4 :                         total_rx += nb_rx;
    1141         [ -  + ]:          4 :                         if (nb_rx == 0)
    1142                 :          0 :                                 rte_delay_us_sleep(100);
    1143                 :            :                 }
    1144                 :            :         }
    1145                 :            : 
    1146                 :          1 :         cleanup_pcap_vdev("net_pcap_inf", port_id);
    1147                 :            :         remove_temp_file(infinite_pcap_path);
    1148                 :            : 
    1149         [ -  + ]:          1 :         TEST_ASSERT(total_rx >= NUM_PACKETS * 2,
    1150                 :            :                     "Infinite RX: got %u packets, need >= %d",
    1151                 :            :                     total_rx, NUM_PACKETS * 2);
    1152                 :            : 
    1153                 :            :         printf("Infinite RX PASSED: %u packets (file has %d)\n",
    1154                 :            :                total_rx, NUM_PACKETS);
    1155                 :          1 :         return TEST_SUCCESS;
    1156                 :            : }
    1157                 :            : 
    1158                 :            : /*
    1159                 :            :  * Test: TX drop mode - packets dropped when no tx_pcap specified
    1160                 :            :  */
    1161                 :            : static int
    1162                 :          1 : test_tx_drop(void)
    1163                 :            : {
    1164                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    1165                 :            :         struct rte_eth_stats stats;
    1166                 :            :         char rx_pcap_path[PATH_MAX];
    1167                 :            :         char devargs[256];
    1168                 :            :         uint16_t port_id;
    1169                 :            :         int nb_tx;
    1170                 :            :         int ret;
    1171                 :            : 
    1172                 :            :         printf("Testing TX drop mode\n");
    1173                 :            : 
    1174         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_pcap_path, sizeof(rx_pcap_path), "pcap_drop") == 0,
    1175                 :            :                     "Failed to create temp path");
    1176         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(rx_pcap_path, NUM_PACKETS) == 0,
    1177                 :            :                     "Failed to create input pcap");
    1178                 :            : 
    1179                 :            :         /* Only rx_pcap - TX should silently drop */
    1180                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", rx_pcap_path);
    1181         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1182                 :            :                     "devargs string truncated");
    1183         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_drop", devargs, &port_id) == 0,
    1184                 :            :                     "Failed to create drop vdev");
    1185         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    1186                 :            :                     "Failed to setup drop port");
    1187         [ -  + ]:          1 :         TEST_ASSERT(alloc_test_mbufs(mbufs, NUM_PACKETS) == 0,
    1188                 :            :                     "Failed to allocate mbufs");
    1189                 :            : 
    1190         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_reset(port_id) == 0,
    1191                 :            :                     "Failed to reset stats");
    1192                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, NUM_PACKETS);
    1193                 :            : 
    1194                 :            :         /* Packets should be accepted even in drop mode */
    1195         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(nb_tx, NUM_PACKETS,
    1196                 :            :                           "Drop mode TX: %d/%u accepted",
    1197                 :            :                           nb_tx, NUM_PACKETS);
    1198                 :            : 
    1199         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_get(port_id, &stats) == 0,
    1200                 :            :                     "Failed to get stats");
    1201                 :          1 :         cleanup_pcap_vdev("net_pcap_drop", port_id);
    1202                 :            :         remove_temp_file(rx_pcap_path);
    1203                 :            : 
    1204                 :          1 :         printf("TX drop PASSED: %d packets dropped, opackets=%" PRIu64"\n",
    1205                 :            :                nb_tx, stats.opackets);
    1206                 :          1 :         return TEST_SUCCESS;
    1207                 :            : }
    1208                 :            : 
    1209                 :            : /*
    1210                 :            :  * Test: Statistics accuracy and reset
    1211                 :            :  */
    1212                 :            : static int
    1213                 :          1 : test_stats(void)
    1214                 :            : {
    1215                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    1216                 :            :         struct rte_eth_stats stats;
    1217                 :            :         char rx_pcap_path[PATH_MAX];
    1218                 :            :         char devargs[256];
    1219                 :            :         char stats_tx_path[PATH_MAX];
    1220                 :            :         uint16_t port_id;
    1221                 :            :         unsigned int received;
    1222                 :            :         int nb_tx;
    1223                 :            :         int ret;
    1224                 :            : 
    1225                 :            :         printf("Testing statistics accuracy\n");
    1226                 :            : 
    1227         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_pcap_path, sizeof(rx_pcap_path), "pcap_stats_rx") == 0,
    1228                 :            :                     "Failed to create RX temp path");
    1229         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(stats_tx_path, sizeof(stats_tx_path), "pcap_stats_tx") == 0,
    1230                 :            :                     "Failed to create TX temp path");
    1231         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(rx_pcap_path, NUM_PACKETS) == 0,
    1232                 :            :                     "Failed to create input pcap");
    1233                 :            : 
    1234                 :            :         ret = snprintf(devargs, sizeof(devargs),
    1235                 :            :                        "rx_pcap=%s,tx_pcap=%s", rx_pcap_path, stats_tx_path);
    1236         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1237                 :            :                     "devargs string truncated");
    1238         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_stats", devargs, &port_id) == 0,
    1239                 :            :                     "Failed to create stats vdev");
    1240         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    1241                 :            :                     "Failed to setup stats port");
    1242                 :            : 
    1243                 :            :         /* Verify stats start at zero */
    1244         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_reset(port_id) == 0,
    1245                 :            :                     "Failed to reset stats");
    1246         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_get(port_id, &stats) == 0,
    1247                 :            :                     "Failed to get stats");
    1248   [ +  -  +  -  :          1 :         TEST_ASSERT(stats.ipackets == 0 && stats.opackets == 0 &&
             +  -  -  + ]
    1249                 :            :                     stats.ibytes == 0 && stats.obytes == 0,
    1250                 :            :                     "Initial stats not zero");
    1251                 :            : 
    1252                 :            :         /* RX and verify stats */
    1253                 :          1 :         receive_packets(port_id, mbufs, NUM_PACKETS, &received);
    1254         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_get(port_id, &stats) == 0,
    1255                 :            :                     "Failed to get stats after RX");
    1256         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(stats.ipackets, received,
    1257                 :            :                           "RX stats: ipackets=%"PRIu64", received=%u",
    1258                 :            :                           stats.ipackets, received);
    1259         [ -  + ]:          1 :         TEST_ASSERT(stats.ibytes > 0,
    1260                 :            :                     "RX stats: ibytes should be > 0");
    1261         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(stats.ibytes, (uint64_t)received * sizeof(test_packet),
    1262                 :            :                           "RX stats: ibytes=%"PRIu64", expected=%"PRIu64,
    1263                 :            :                           stats.ibytes,
    1264                 :            :                           (uint64_t)received * sizeof(test_packet));
    1265                 :            : 
    1266                 :            :         /* TX and verify stats */
    1267                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, received);
    1268         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_get(port_id, &stats) == 0,
    1269                 :            :                     "Failed to get stats after TX");
    1270         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(stats.opackets, (uint64_t)nb_tx,
    1271                 :            :                           "TX stats: opackets=%"PRIu64", sent=%u",
    1272                 :            :                           stats.opackets, nb_tx);
    1273         [ -  + ]:          1 :         TEST_ASSERT(stats.obytes > 0,
    1274                 :            :                     "TX stats: obytes should be > 0");
    1275         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(stats.obytes, (uint64_t)nb_tx * sizeof(test_packet),
    1276                 :            :                           "TX stats: obytes=%"PRIu64", expected=%"PRIu64,
    1277                 :            :                           stats.obytes,
    1278                 :            :                           (uint64_t)nb_tx * sizeof(test_packet));
    1279                 :            : 
    1280                 :            :         /* Verify stats reset */
    1281         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_reset(port_id) == 0,
    1282                 :            :                     "Failed to reset stats");
    1283         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_stats_get(port_id, &stats) == 0,
    1284                 :            :                     "Failed to get stats after reset");
    1285   [ +  -  -  + ]:          1 :         TEST_ASSERT(stats.ipackets == 0 && stats.opackets == 0,
    1286                 :            :                     "Stats not reset to zero");
    1287                 :            : 
    1288                 :          1 :         cleanup_pcap_vdev("net_pcap_stats", port_id);
    1289                 :            :         remove_temp_file(rx_pcap_path);
    1290                 :            :         remove_temp_file(stats_tx_path);
    1291                 :            : 
    1292                 :            :         printf("Statistics PASSED: RX=%u, TX=%d\n", received, nb_tx);
    1293                 :          1 :         return TEST_SUCCESS;
    1294                 :            : }
    1295                 :            : 
    1296                 :            : /*
    1297                 :            :  * Test: Jumbo frame RX (multi-segment mbufs)
    1298                 :            :  */
    1299                 :            : static int
    1300                 :          1 : test_jumbo_rx(void)
    1301                 :            : {
    1302                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    1303                 :            :         char jumbo_pcap_path[PATH_MAX];
    1304                 :            :         char devargs[256];
    1305                 :            :         uint16_t port_id;
    1306                 :            :         unsigned int received, i;
    1307                 :            :         int ret;
    1308                 :            :         const unsigned int num_jumbo = 16;
    1309                 :            : 
    1310                 :            :         printf("Testing jumbo frame RX (%u byte packets, multi-segment)\n",
    1311                 :            :                PKT_SIZE_JUMBO);
    1312                 :            : 
    1313         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(jumbo_pcap_path, sizeof(jumbo_pcap_path), "pcap_jumbo") == 0,
    1314                 :            :                     "Failed to create temp path");
    1315         [ -  + ]:          1 :         TEST_ASSERT(create_sized_pcap(jumbo_pcap_path, num_jumbo,
    1316                 :            :                                       PKT_SIZE_JUMBO) == 0,
    1317                 :            :                     "Failed to create jumbo pcap");
    1318                 :            : 
    1319                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", jumbo_pcap_path);
    1320         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1321                 :            :                     "devargs string truncated");
    1322         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_jumbo", devargs, &port_id) == 0,
    1323                 :            :                     "Failed to create jumbo RX vdev");
    1324                 :            : 
    1325                 :            :         /* Jumbo frames require scatter to receive into multi-segment mbufs */
    1326                 :          1 :         struct rte_eth_conf jumbo_conf = {
    1327                 :            :                 .rxmode.offloads = RTE_ETH_RX_OFFLOAD_SCATTER |
    1328                 :            :                                    RTE_ETH_RX_OFFLOAD_TIMESTAMP,
    1329                 :            :         };
    1330         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port_conf(port_id, &jumbo_conf) == 0,
    1331                 :            :                     "Failed to setup jumbo RX port");
    1332                 :            : 
    1333                 :          1 :         receive_packets(port_id, mbufs, num_jumbo, &received);
    1334         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(received, num_jumbo,
    1335                 :            :                           "Received %u packets, expected %u", received, num_jumbo);
    1336                 :            : 
    1337                 :            :         /* Verify all packets are jumbo size (may be multi-segment) */
    1338         [ +  + ]:         17 :         for (i = 0; i < received; i++) {
    1339                 :         16 :                 uint32_t pkt_len = rte_pktmbuf_pkt_len(mbufs[i]);
    1340                 :         16 :                 uint16_t nb_segs = mbufs[i]->nb_segs;
    1341                 :            : 
    1342         [ -  + ]:         16 :                 TEST_ASSERT_EQUAL(pkt_len, PKT_SIZE_JUMBO,
    1343                 :            :                                   "Packet %u: size %u, expected %u",
    1344                 :            :                                   i, pkt_len, PKT_SIZE_JUMBO);
    1345                 :            : 
    1346                 :            :                 /* Jumbo frames should use multiple segments */
    1347         [ +  - ]:         16 :                 if (nb_segs > 1)
    1348                 :         16 :                         printf("  Packet %u: %u segments\n", i, nb_segs);
    1349                 :            :         }
    1350                 :            : 
    1351                 :          1 :         rte_pktmbuf_free_bulk(mbufs, received);
    1352                 :          1 :         cleanup_pcap_vdev("net_pcap_jumbo", port_id);
    1353                 :            :         remove_temp_file(jumbo_pcap_path);
    1354                 :            : 
    1355                 :            :         printf("Jumbo RX PASSED: %u jumbo packets received\n", received);
    1356                 :          1 :         return TEST_SUCCESS;
    1357                 :            : }
    1358                 :            : 
    1359                 :            : /*
    1360                 :            :  * Test: Jumbo frame TX (multi-segment mbufs)
    1361                 :            :  */
    1362                 :            : static int
    1363                 :          1 : test_jumbo_tx(void)
    1364                 :            : {
    1365                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    1366                 :            :         char tx_path[PATH_MAX];
    1367                 :            :         char devargs[256];
    1368                 :            :         uint16_t port_id;
    1369                 :            :         uint16_t sizes[MAX_PKT_BURST];
    1370                 :            :         int nb_tx, pkt_count;
    1371                 :            :         unsigned int i;
    1372                 :            :         int ret;
    1373                 :            :         const unsigned int num_jumbo = 8;
    1374                 :            : 
    1375                 :            :         printf("Testing jumbo frame TX (multi-segment mbufs)\n");
    1376                 :            : 
    1377         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_path, sizeof(tx_path),
    1378                 :            :                                      "pcap_jumbo_tx") == 0,
    1379                 :            :                     "Failed to create temp file path");
    1380                 :            : 
    1381                 :            :         ret = snprintf(devargs, sizeof(devargs), "tx_pcap=%s", tx_path);
    1382         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1383                 :            :                     "devargs string truncated");
    1384         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_jumbo_tx", devargs, &port_id) == 0,
    1385                 :            :                     "Failed to create TX vdev");
    1386         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    1387                 :            :                     "Failed to setup TX port");
    1388                 :            : 
    1389                 :            :         /* Allocate multi-segment mbufs for jumbo frames */
    1390         [ +  + ]:          9 :         for (i = 0; i < num_jumbo; i++) {
    1391                 :          8 :                 mbufs[i] = alloc_jumbo_mbuf(PKT_SIZE_JUMBO, (uint8_t)(i & 0xFF));
    1392         [ -  + ]:          8 :                 if (mbufs[i] == NULL) {
    1393                 :            :                         /* Free already allocated mbufs */
    1394         [ #  # ]:          0 :                         while (i > 0)
    1395                 :          0 :                                 rte_pktmbuf_free(mbufs[--i]);
    1396                 :          0 :                         cleanup_pcap_vdev("net_pcap_jumbo_tx", port_id);
    1397                 :            :                         remove_temp_file(tx_path);
    1398                 :          0 :                         return TEST_FAILED;
    1399                 :            :                 }
    1400                 :          8 :                 printf("  Packet %u: %u segments for %u bytes\n",
    1401                 :          8 :                        i, mbufs[i]->nb_segs, PKT_SIZE_JUMBO);
    1402                 :            :         }
    1403                 :            : 
    1404                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, num_jumbo);
    1405                 :            :         /* Free any unsent mbufs */
    1406         [ -  + ]:          1 :         for (i = nb_tx; i < num_jumbo; i++)
    1407                 :          0 :                 rte_pktmbuf_free(mbufs[i]);
    1408                 :            : 
    1409         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(nb_tx, (int)num_jumbo,
    1410                 :            :                           "TX burst failed: sent %d/%u", nb_tx, num_jumbo);
    1411                 :            : 
    1412                 :          1 :         cleanup_pcap_vdev("net_pcap_jumbo_tx", port_id);
    1413                 :            : 
    1414                 :            :         /* Verify pcap file has correct packet count and sizes */
    1415                 :          1 :         pkt_count = get_pcap_packet_sizes(tx_path, sizes, MAX_PKT_BURST);
    1416         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(pkt_count, (int)num_jumbo,
    1417                 :            :                           "Pcap file has %d packets, expected %u",
    1418                 :            :                           pkt_count, num_jumbo);
    1419                 :            : 
    1420         [ +  + ]:          9 :         for (i = 0; i < (unsigned int)pkt_count; i++) {
    1421         [ -  + ]:          8 :                 TEST_ASSERT_EQUAL(sizes[i], PKT_SIZE_JUMBO,
    1422                 :            :                                   "Packet %u: size %u, expected %u",
    1423                 :            :                                   i, sizes[i], PKT_SIZE_JUMBO);
    1424                 :            :         }
    1425                 :            : 
    1426                 :            :         remove_temp_file(tx_path);
    1427                 :            : 
    1428                 :            :         printf("Jumbo TX PASSED: %d jumbo packets written\n", nb_tx);
    1429                 :          1 :         return TEST_SUCCESS;
    1430                 :            : }
    1431                 :            : 
    1432                 :            : /*
    1433                 :            :  * Test: Layering on Linux network interface
    1434                 :            :  */
    1435                 :            : static int
    1436                 :          1 : test_iface(void)
    1437                 :            : {
    1438                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    1439                 :            :         struct rte_eth_dev_info dev_info;
    1440                 :            :         char devargs[256];
    1441                 :            :         uint16_t port_id;
    1442                 :            :         const char *iface;
    1443                 :            :         int ret, nb_tx, nb_pkt;
    1444                 :            : 
    1445                 :            :         printf("Testing pcap on network interface\n");
    1446                 :            : 
    1447                 :          1 :         iface = find_test_iface();
    1448         [ -  + ]:          1 :         if (iface == NULL) {
    1449                 :            :                 printf("No suitable interface, skipping\n");
    1450                 :          0 :                 return TEST_SKIPPED;
    1451                 :            :         }
    1452                 :            :         printf("Using interface: %s\n", iface);
    1453                 :            : 
    1454                 :            :         ret = snprintf(devargs, sizeof(devargs), "iface=%s", iface);
    1455         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1456                 :            :                     "devargs string truncated");
    1457         [ -  + ]:          1 :         if (rte_vdev_init("net_pcap_iface", devargs) < 0) {
    1458                 :            :                 printf("Cannot create iface vdev (needs root?), skipping\n");
    1459                 :          0 :                 return TEST_SKIPPED;
    1460                 :            :         }
    1461                 :            : 
    1462         [ -  + ]:          1 :         TEST_ASSERT(rte_eth_dev_get_port_by_name("net_pcap_iface",
    1463                 :            :                                                  &port_id) == 0,
    1464                 :            :                     "Failed to get iface port ID");
    1465         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    1466                 :            :                     "Failed to setup iface port");
    1467                 :            : 
    1468                 :          1 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
    1469         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get dev info: %s", rte_strerror(-ret));
    1470                 :            : 
    1471                 :          1 :         printf("Driver: %s, max_rx_queues=%u, max_tx_queues=%u\n",
    1472                 :          1 :                dev_info.driver_name, dev_info.max_rx_queues,
    1473                 :          1 :                dev_info.max_tx_queues);
    1474                 :            : 
    1475                 :            :         /* Use packet_burst_generator for interface test */
    1476                 :          1 :         nb_pkt = generate_test_packets(mp, mbufs, MAX_PKT_BURST,
    1477                 :            :                                        PACKET_BURST_GEN_PKT_LEN);
    1478         [ -  + ]:          1 :         TEST_ASSERT(nb_pkt > 0, "Failed to generate packets");
    1479                 :            : 
    1480                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, nb_pkt);
    1481         [ -  + ]:          1 :         if (nb_tx < nb_pkt)
    1482                 :          0 :                 rte_pktmbuf_free_bulk(&mbufs[nb_tx], nb_pkt - nb_tx);
    1483                 :            : 
    1484                 :          1 :         cleanup_pcap_vdev("net_pcap_iface", port_id);
    1485                 :            : 
    1486                 :            :         printf("Interface test PASSED: sent %d packets\n", nb_tx);
    1487                 :          1 :         return TEST_SUCCESS;
    1488                 :            : }
    1489                 :            : 
    1490                 :            : /*
    1491                 :            :  * Test: Link status and speed reporting
    1492                 :            :  *
    1493                 :            :  * This test verifies that:
    1494                 :            :  * 1. In interface (pass-through) mode, link state reflects the real interface
    1495                 :            :  * 2. In file mode, link status follows device started/stopped state
    1496                 :            :  * 3. Link speed values are properly reported
    1497                 :            :  */
    1498                 :            : static int
    1499                 :          1 : test_link_status(void)
    1500                 :            : {
    1501                 :            :         struct rte_eth_link link;
    1502                 :            :         char rx_pcap_path[PATH_MAX];
    1503                 :            :         char devargs[256];
    1504                 :            :         uint16_t port_id;
    1505                 :            :         const char *iface;
    1506                 :            :         int ret;
    1507                 :            : 
    1508                 :            :         printf("Testing link status reporting\n");
    1509                 :            : 
    1510                 :            :         /*
    1511                 :            :          * Test 1: Interface (pass-through) mode
    1512                 :            :          * Link state should reflect the underlying interface
    1513                 :            :          */
    1514                 :          1 :         iface = find_test_iface();
    1515         [ +  - ]:          1 :         if (iface != NULL) {
    1516                 :            :                 printf("  Testing interface mode with: %s\n", iface);
    1517                 :            : 
    1518                 :            :                 ret = snprintf(devargs, sizeof(devargs), "iface=%s", iface);
    1519         [ -  + ]:          1 :                 TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1520                 :            :                             "devargs string truncated");
    1521         [ +  - ]:          1 :                 if (rte_vdev_init("net_pcap_link_iface", devargs) == 0) {
    1522                 :          1 :                         ret = rte_eth_dev_get_port_by_name("net_pcap_link_iface", &port_id);
    1523         [ -  + ]:          1 :                         TEST_ASSERT(ret == 0, "Failed to get port ID");
    1524                 :            : 
    1525                 :          1 :                         ret = setup_pcap_port(port_id);
    1526         [ -  + ]:          1 :                         TEST_ASSERT(ret == 0, "Failed to setup port");
    1527                 :            : 
    1528                 :            :                         /* Get link status */
    1529                 :          1 :                         ret = rte_eth_link_get_nowait(port_id, &link);
    1530         [ -  + ]:          1 :                         TEST_ASSERT(ret == 0, "Failed to get link: %s", rte_strerror(-ret));
    1531                 :            : 
    1532                 :          1 :                         printf("    Link status: %s\n",
    1533         [ -  + ]:          1 :                                link.link_status ? "UP" : "DOWN");
    1534                 :          1 :                         printf("    Link speed: %u Mbps\n", link.link_speed);
    1535                 :          1 :                         printf("    Link duplex: %s\n",
    1536         [ -  + ]:          1 :                                link.link_duplex ? "full" : "half");
    1537                 :          1 :                         printf("    Link autoneg: %s\n",
    1538         [ +  - ]:          1 :                                link.link_autoneg ? "enabled" : "disabled");
    1539                 :            : 
    1540                 :            :                         /*
    1541                 :            :                          * For loopback interface, link should be up.
    1542                 :            :                          * Speed may be 0 or undefined for virtual interfaces.
    1543                 :            :                          */
    1544         [ +  - ]:          1 :                         if (strcmp(iface, "lo") == 0) {
    1545         [ -  + ]:          1 :                                 TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
    1546                 :            :                                             "Loopback should report link UP");
    1547                 :            :                         }
    1548                 :            : 
    1549                 :            :                         /*
    1550                 :            :                          * Verify link_get returns consistent results
    1551                 :            :                          */
    1552                 :            :                         struct rte_eth_link link2;
    1553                 :          1 :                         ret = rte_eth_link_get(port_id, &link2);
    1554         [ -  + ]:          1 :                         TEST_ASSERT(ret == 0, "Second link_get failed");
    1555         [ -  + ]:          1 :                         TEST_ASSERT(link.link_status == link2.link_status,
    1556                 :            :                                     "Link status inconsistent between calls");
    1557                 :            : 
    1558                 :          1 :                         cleanup_pcap_vdev("net_pcap_link_iface", port_id);
    1559                 :            :                         printf("  Interface mode link test PASSED\n");
    1560                 :            :                 } else {
    1561                 :            :                         printf("  Cannot create iface vdev (needs root?), skipping iface test\n");
    1562                 :            :                 }
    1563                 :            :         } else {
    1564                 :            :                 printf("  No suitable interface found, skipping iface test\n");
    1565                 :            :         }
    1566                 :            : 
    1567                 :            :         /*
    1568                 :            :          * Test 2: File mode
    1569                 :            :          * Link status should be DOWN before start, UP after start
    1570                 :            :          */
    1571                 :            :         printf("  Testing file mode link status\n");
    1572                 :            : 
    1573                 :            :         /* Create a simple pcap file for testing */
    1574         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_pcap_path, sizeof(rx_pcap_path), "pcap_link") == 0,
    1575                 :            :                     "Failed to create temp path");
    1576         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(rx_pcap_path, 1) == 0,
    1577                 :            :                     "Failed to create test pcap");
    1578                 :            : 
    1579                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", rx_pcap_path);
    1580         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1581                 :            :                     "devargs string truncated");
    1582         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_link_file", devargs, &port_id) == 0,
    1583                 :            :                     "Failed to create file vdev");
    1584                 :            : 
    1585                 :            :         /* Before starting: configure but don't start */
    1586                 :          1 :         struct rte_eth_conf port_conf = { 0 };
    1587                 :          1 :         ret = rte_eth_dev_configure(port_id, 1, 1, &port_conf);
    1588         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to configure port");
    1589                 :            : 
    1590                 :          1 :         ret = rte_eth_rx_queue_setup(port_id, 0, RING_SIZE, SOCKET0, NULL, mp);
    1591         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to setup RX queue");
    1592                 :            : 
    1593                 :          1 :         ret = rte_eth_tx_queue_setup(port_id, 0, RING_SIZE, SOCKET0, NULL);
    1594         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to setup TX queue");
    1595                 :            : 
    1596                 :            :         /* Check link before start - should be DOWN */
    1597                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1598         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link before start");
    1599                 :          1 :         printf("    Before start: link %s, speed %u Mbps\n",
    1600         [ +  - ]:          1 :                link.link_status ? "UP" : "DOWN", link.link_speed);
    1601         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_DOWN,
    1602                 :            :                     "Link should be DOWN before start");
    1603                 :            : 
    1604                 :            :         /* Start the port */
    1605                 :          1 :         ret = rte_eth_dev_start(port_id);
    1606         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to start port");
    1607                 :            : 
    1608                 :            :         /* Check link after start - should be UP */
    1609                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1610         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link after start");
    1611                 :          1 :         printf("    After start: link %s, speed %u Mbps\n",
    1612         [ -  + ]:          1 :                link.link_status ? "UP" : "DOWN", link.link_speed);
    1613         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
    1614                 :            :                     "Link should be UP after start");
    1615                 :            : 
    1616                 :            :         /* Stop the port */
    1617                 :          1 :         ret = rte_eth_dev_stop(port_id);
    1618         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to stop port");
    1619                 :            : 
    1620                 :            :         /* Check link after stop - should be DOWN again */
    1621                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1622         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link after stop");
    1623                 :          1 :         printf("    After stop: link %s\n",
    1624         [ +  - ]:          1 :                link.link_status ? "UP" : "DOWN");
    1625         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_DOWN,
    1626                 :            :                     "Link should be DOWN after stop");
    1627                 :            : 
    1628                 :          1 :         rte_vdev_uninit("net_pcap_link_file");
    1629                 :            :         remove_temp_file(rx_pcap_path);
    1630                 :            : 
    1631                 :            :         printf("Link status test PASSED\n");
    1632                 :          1 :         return TEST_SUCCESS;
    1633                 :            : }
    1634                 :            : 
    1635                 :            : #ifdef RTE_EXEC_ENV_WINDOWS
    1636                 :            : static int
    1637                 :            : test_lsc_iface(void)
    1638                 :            : {
    1639                 :            :         printf("  Link toggle test not supported on Windows, skipping\n");
    1640                 :            :         return TEST_SKIPPED;
    1641                 :            : }
    1642                 :            : #else
    1643                 :            : /*
    1644                 :            :  * Test: Link Status Change (LSC) interrupt support
    1645                 :            :  *
    1646                 :            :  * Verifies that:
    1647                 :            :  * 1. LSC capability is NOT advertised for file mode
    1648                 :            :  * 2. LSC capability IS advertised for iface mode
    1649                 :            :  * 3. LSC callback fires when the underlying interface goes down/up
    1650                 :            :  *
    1651                 :            :  * Requires a toggleable Ethernet interface created before running:
    1652                 :            :  *   Linux:   ip link add dummy0 type dummy && ip link set dummy0 up
    1653                 :            :  *   FreeBSD: ifconfig disc0 create && ifconfig disc0 up
    1654                 :            :  *
    1655                 :            :  * Skipped if no suitable interface is found or on Windows.
    1656                 :            :  */
    1657                 :            : 
    1658                 :            : /* Callback counter for LSC test */
    1659                 :            : static volatile int lsc_callback_count;
    1660                 :            : 
    1661                 :            : static int
    1662                 :          0 : test_lsc_callback(uint16_t port_id __rte_unused,
    1663                 :            :                   enum rte_eth_event_type event __rte_unused,
    1664                 :            :                   void *cb_arg __rte_unused, void *ret_param __rte_unused)
    1665                 :            : {
    1666                 :          0 :         lsc_callback_count++;
    1667                 :          0 :         return 0;
    1668                 :            : }
    1669                 :            : 
    1670                 :            : /*
    1671                 :            :  * Helper: Set interface link up or down via ioctl.
    1672                 :            :  * Returns 0 on success, -errno on failure.
    1673                 :            :  */
    1674                 :            : static int
    1675                 :          0 : set_iface_up_down(const char *ifname, int up)
    1676                 :            : {
    1677                 :            :         struct ifreq ifr;
    1678                 :            :         int fd, ret;
    1679                 :            : 
    1680                 :          0 :         fd = socket(AF_INET, SOCK_DGRAM, 0);
    1681         [ #  # ]:          0 :         if (fd < 0)
    1682                 :          0 :                 return -errno;
    1683                 :            : 
    1684                 :            :         memset(&ifr, 0, sizeof(ifr));
    1685                 :            :         strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
    1686                 :            : 
    1687                 :          0 :         ret = ioctl(fd, SIOCGIFFLAGS, &ifr);
    1688         [ #  # ]:          0 :         if (ret < 0) {
    1689                 :          0 :                 ret = -errno;
    1690                 :          0 :                 close(fd);
    1691                 :          0 :                 return ret;
    1692                 :            :         }
    1693                 :            : 
    1694         [ #  # ]:          0 :         if (up)
    1695                 :          0 :                 ifr.ifr_flags |= IFF_UP;
    1696                 :            :         else
    1697                 :          0 :                 ifr.ifr_flags &= ~IFF_UP;
    1698                 :            : 
    1699                 :          0 :         ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
    1700         [ #  # ]:          0 :         if (ret < 0)
    1701                 :          0 :                 ret = -errno;
    1702                 :            :         else
    1703                 :            :                 ret = 0;
    1704                 :            : 
    1705                 :          0 :         close(fd);
    1706                 :          0 :         return ret;
    1707                 :            : }
    1708                 :            : 
    1709                 :            : /*
    1710                 :            :  * Helper: Find a toggleable test interface for LSC testing.
    1711                 :            :  *
    1712                 :            :  * Looks for well-known interfaces that are safe to bring up/down:
    1713                 :            :  *   Linux:   dummy0 (ip link add dummy0 type dummy)
    1714                 :            :  *   FreeBSD: disc0 (ifconfig disc0 create)
    1715                 :            :  *
    1716                 :            :  * Returns interface name or NULL if none found.
    1717                 :            :  */
    1718                 :            : static const char *
    1719                 :          1 : find_lsc_test_iface(void)
    1720                 :            : {
    1721                 :            :         static const char * const candidates[] = { "dummy0", "disc0" };
    1722                 :            :         unsigned int i;
    1723                 :            : 
    1724         [ +  + ]:          3 :         for (i = 0; i < RTE_DIM(candidates); i++) {
    1725         [ -  + ]:          2 :                 if (iface_is_ethernet(candidates[i]))
    1726                 :          0 :                         return candidates[i];
    1727                 :            :         }
    1728                 :            :         return NULL;
    1729                 :            : }
    1730                 :            : 
    1731                 :            : static int
    1732                 :          1 : test_lsc_iface(void)
    1733                 :            : {
    1734                 :            :         struct rte_eth_dev_info dev_info;
    1735                 :            :         char devargs[256];
    1736                 :            :         int ret;
    1737                 :            : 
    1738                 :            :         printf("Testing Link Status Change (LSC) support\n");
    1739                 :            : 
    1740                 :            :         /*
    1741                 :            :          * Test 1: Verify LSC is NOT advertised for file mode
    1742                 :            :          */
    1743                 :            :         printf("  Testing file mode does not advertise LSC\n");
    1744                 :            :         {
    1745                 :            :                 char lsc_pcap_path[PATH_MAX];
    1746                 :            :                 uint16_t file_port_id;
    1747                 :            : 
    1748         [ -  + ]:          1 :                 TEST_ASSERT(create_temp_path(lsc_pcap_path, sizeof(lsc_pcap_path),
    1749                 :            :                                              "pcap_lsc") == 0,
    1750                 :            :                             "Failed to create temp path");
    1751         [ -  + ]:          1 :                 TEST_ASSERT(create_test_pcap(lsc_pcap_path, 1) == 0,
    1752                 :            :                             "Failed to create test pcap");
    1753                 :            : 
    1754                 :            :                 ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", lsc_pcap_path);
    1755         [ -  + ]:          1 :                 TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1756                 :            :                             "devargs string truncated");
    1757         [ -  + ]:          1 :                 TEST_ASSERT(create_pcap_vdev("net_pcap_lsc_file", devargs,
    1758                 :            :                                              &file_port_id) == 0,
    1759                 :            :                             "Failed to create file vdev");
    1760                 :            : 
    1761                 :          1 :                 ret = rte_eth_dev_info_get(file_port_id, &dev_info);
    1762         [ -  + ]:          1 :                 TEST_ASSERT(ret == 0, "Failed to get dev info");
    1763                 :            : 
    1764         [ -  + ]:          1 :                 TEST_ASSERT((*dev_info.dev_flags & RTE_ETH_DEV_INTR_LSC) == 0,
    1765                 :            :                             "File mode should NOT advertise LSC capability");
    1766                 :            : 
    1767                 :          1 :                 rte_vdev_uninit("net_pcap_lsc_file");
    1768                 :            :                 remove_temp_file(lsc_pcap_path);
    1769                 :            :                 printf("  File mode LSC check PASSED\n");
    1770                 :            :         }
    1771                 :            : 
    1772                 :            :         struct rte_eth_link link;
    1773                 :          1 :         struct rte_eth_conf port_conf = {
    1774                 :            :                 .intr_conf.lsc = 1,
    1775                 :            :         };
    1776                 :            :         uint16_t port_id;
    1777                 :            : 
    1778                 :            :         /*
    1779                 :            :          * Test 2: Use a toggleable interface to test link change events.
    1780                 :            :          * Skip if not present.
    1781                 :            :          */
    1782                 :          1 :         const char *lsc_iface = find_lsc_test_iface();
    1783         [ +  - ]:          1 :         if (lsc_iface == NULL) {
    1784                 :            :                 printf("  No toggleable interface found, skipping link change test\n");
    1785                 :            :                 printf("  Linux:   ip link add dummy0 type dummy && ip link set dummy0 up\n");
    1786                 :            :                 printf("  FreeBSD: ifconfig disc0 create && ifconfig disc0 up\n");
    1787                 :          1 :                 return TEST_SKIPPED;
    1788                 :            :         }
    1789                 :            : 
    1790                 :            :         printf("  Testing iface mode LSC with: %s\n", lsc_iface);
    1791                 :            : 
    1792                 :            :         /* Ensure interface is up before we start */
    1793                 :          0 :         ret = set_iface_up_down(lsc_iface, 1);
    1794         [ #  # ]:          0 :         if (ret != 0) {
    1795                 :          0 :                 printf("  Cannot set %s up (%s), skipping\n",
    1796                 :            :                        lsc_iface, strerror(-ret));
    1797                 :          0 :                 return TEST_SKIPPED;
    1798                 :            :         }
    1799                 :            : 
    1800                 :            :         ret = snprintf(devargs, sizeof(devargs), "iface=%s", lsc_iface);
    1801         [ #  # ]:          0 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1802                 :            :                     "devargs string truncated");
    1803                 :          0 :         ret = rte_vdev_init("net_pcap_lsc", devargs);
    1804         [ #  # ]:          0 :         if (ret < 0) {
    1805                 :            :                 printf("  Cannot create iface vdev for %s, skipping\n", lsc_iface);
    1806                 :          0 :                 return TEST_SKIPPED;
    1807                 :            :         }
    1808                 :            : 
    1809                 :          0 :         ret = rte_eth_dev_get_port_by_name("net_pcap_lsc", &port_id);
    1810         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    1811                 :            : 
    1812                 :            :         /* Verify LSC capability is advertised */
    1813                 :          0 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
    1814         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to get dev info");
    1815         [ #  # ]:          0 :         TEST_ASSERT(*dev_info.dev_flags & RTE_ETH_DEV_INTR_LSC,
    1816                 :            :                     "Iface mode should advertise LSC capability");
    1817                 :            :         printf("    LSC capability advertised: yes\n");
    1818                 :            : 
    1819                 :            :         /* Register LSC callback */
    1820                 :          0 :         lsc_callback_count = 0;
    1821                 :          0 :         ret = rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_INTR_LSC,
    1822                 :            :                                             test_lsc_callback, NULL);
    1823         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to register LSC callback");
    1824                 :            : 
    1825                 :            :         /* Configure with LSC enabled and start */
    1826         [ #  # ]:          0 :         TEST_ASSERT(setup_pcap_port_conf(port_id, &port_conf) == 0,
    1827                 :            :                     "Failed to setup port with LSC");
    1828                 :            : 
    1829                 :            :         /* Verify link is up initially */
    1830                 :          0 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1831         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to get link status");
    1832         [ #  # ]:          0 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
    1833                 :            :                     "Link should be UP after start");
    1834                 :            :         printf("    Link after start: UP\n");
    1835                 :            : 
    1836                 :            :         /* Bring interface down - should trigger LSC */
    1837                 :          0 :         lsc_callback_count = 0;
    1838                 :          0 :         ret = set_iface_up_down(lsc_iface, 0);
    1839         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to set %s down: %s",
    1840                 :            :                     lsc_iface, strerror(-ret));
    1841                 :            : 
    1842                 :            :         /* Poll for link state change (1 second poll interval in driver) */
    1843                 :            :         {
    1844                 :            :                 int poll;
    1845         [ #  # ]:          0 :                 for (poll = 0; poll < 30; poll++) {
    1846                 :          0 :                         ret = rte_eth_link_get_nowait(port_id, &link);
    1847   [ #  #  #  # ]:          0 :                         if (ret == 0 && link.link_status == RTE_ETH_LINK_DOWN &&
    1848         [ #  # ]:          0 :                             lsc_callback_count >= 1)
    1849                 :            :                                 break;
    1850                 :          0 :                         rte_delay_us_sleep(100 * 1000);
    1851                 :            :                 }
    1852                 :            :         }
    1853                 :            : 
    1854                 :          0 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1855         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to get link after down");
    1856         [ #  # ]:          0 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_DOWN,
    1857                 :            :                     "Link should be DOWN after interface down");
    1858         [ #  # ]:          0 :         TEST_ASSERT(lsc_callback_count >= 1,
    1859                 :            :                     "LSC callback should have fired, count=%d",
    1860                 :            :                     lsc_callback_count);
    1861                 :          0 :         printf("    Interface down: link DOWN, callbacks=%d\n",
    1862                 :            :                lsc_callback_count);
    1863                 :            : 
    1864                 :            :         /* Bring it back up - should trigger another LSC */
    1865                 :          0 :         lsc_callback_count = 0;
    1866                 :          0 :         ret = set_iface_up_down(lsc_iface, 1);
    1867         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to set %s up: %s",
    1868                 :            :                     lsc_iface, strerror(-ret));
    1869                 :            : 
    1870                 :            :         /* Poll for link state change back to UP */
    1871                 :            :         {
    1872                 :            :                 int poll;
    1873         [ #  # ]:          0 :                 for (poll = 0; poll < 30; poll++) {
    1874                 :          0 :                         ret = rte_eth_link_get_nowait(port_id, &link);
    1875   [ #  #  #  # ]:          0 :                         if (ret == 0 && link.link_status == RTE_ETH_LINK_UP &&
    1876         [ #  # ]:          0 :                             lsc_callback_count >= 1)
    1877                 :            :                                 break;
    1878                 :          0 :                         rte_delay_us_sleep(100 * 1000);
    1879                 :            :                 }
    1880                 :            :         }
    1881                 :            : 
    1882                 :          0 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1883         [ #  # ]:          0 :         TEST_ASSERT(ret == 0, "Failed to get link after up");
    1884         [ #  # ]:          0 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
    1885                 :            :                     "Link should be UP after interface up");
    1886         [ #  # ]:          0 :         TEST_ASSERT(lsc_callback_count >= 1,
    1887                 :            :                     "LSC callback should have fired on link restore, count=%d",
    1888                 :            :                     lsc_callback_count);
    1889                 :          0 :         printf("    Interface up: link UP, callbacks=%d\n",
    1890                 :            :                lsc_callback_count);
    1891                 :            : 
    1892                 :            :         /* Cleanup */
    1893                 :          0 :         rte_eth_dev_stop(port_id);
    1894                 :          0 :         rte_eth_dev_callback_unregister(port_id, RTE_ETH_EVENT_INTR_LSC,
    1895                 :            :                                         test_lsc_callback, NULL);
    1896                 :          0 :         rte_vdev_uninit("net_pcap_lsc");
    1897                 :            : 
    1898                 :            :         printf("LSC test PASSED\n");
    1899                 :          0 :         return TEST_SUCCESS;
    1900                 :            : }
    1901                 :            : #endif /* RTE_EXEC_ENV_WINDOWS */
    1902                 :            : 
    1903                 :            : /*
    1904                 :            :  * Test: EOF notification via link status change
    1905                 :            :  *
    1906                 :            :  * Verifies that:
    1907                 :            :  * 1. The eof devarg causes link down + LSC event at end of pcap file
    1908                 :            :  * 2. link_get reports DOWN after EOF
    1909                 :            :  * 3. Stop/start resets the EOF state and replays the file
    1910                 :            :  * 4. The eof and infinite_rx options are mutually exclusive
    1911                 :            :  */
    1912                 :            : 
    1913                 :            : static volatile int eof_callback_count;
    1914                 :            : 
    1915                 :            : static int
    1916                 :          2 : test_eof_callback(uint16_t port_id __rte_unused,
    1917                 :            :                   enum rte_eth_event_type event __rte_unused,
    1918                 :            :                   void *cb_arg __rte_unused, void *ret_param __rte_unused)
    1919                 :            : {
    1920                 :          2 :         eof_callback_count++;
    1921                 :          2 :         return 0;
    1922                 :            : }
    1923                 :            : 
    1924                 :            : static int
    1925                 :          1 : test_eof_rx(void)
    1926                 :            : {
    1927                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    1928                 :          1 :         struct rte_eth_conf port_conf = {
    1929                 :            :                 .intr_conf.lsc = 1,
    1930                 :            :         };
    1931                 :            :         struct rte_eth_link link;
    1932                 :            :         char eof_pcap_path[PATH_MAX];
    1933                 :            :         char devargs[256];
    1934                 :            :         uint16_t port_id;
    1935                 :            :         unsigned int total_rx;
    1936                 :            :         int ret;
    1937                 :            : 
    1938                 :            :         printf("Testing EOF notification via link status change\n");
    1939                 :            : 
    1940                 :            :         /* Create pcap file with known number of packets */
    1941         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(eof_pcap_path, sizeof(eof_pcap_path),
    1942                 :            :                                      "pcap_eof") == 0,
    1943                 :            :                     "Failed to create temp file path");
    1944         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(eof_pcap_path, NUM_PACKETS) == 0,
    1945                 :            :                     "Failed to create test pcap file");
    1946                 :            : 
    1947                 :            :         /*
    1948                 :            :          * Test 1: EOF triggers link down and LSC callback
    1949                 :            :          */
    1950                 :            :         printf("  Testing EOF triggers link down and LSC event\n");
    1951                 :            : 
    1952                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s,eof=1",
    1953                 :            :                        eof_pcap_path);
    1954         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    1955                 :            :                     "devargs string truncated");
    1956                 :          1 :         ret = rte_vdev_init("net_pcap_eof", devargs);
    1957         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to create eof vdev: %s",
    1958                 :            :                     rte_strerror(-ret));
    1959                 :            : 
    1960                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_eof", &port_id);
    1961         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    1962                 :            : 
    1963                 :            :         /* Verify LSC capability is advertised */
    1964                 :            :         struct rte_eth_dev_info dev_info;
    1965                 :          1 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
    1966         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get dev info");
    1967         [ -  + ]:          1 :         TEST_ASSERT(*dev_info.dev_flags & RTE_ETH_DEV_INTR_LSC,
    1968                 :            :                     "EOF mode should advertise LSC capability");
    1969                 :            : 
    1970                 :            :         /* Register LSC callback */
    1971                 :          1 :         eof_callback_count = 0;
    1972                 :          1 :         ret = rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_INTR_LSC,
    1973                 :            :                                             test_eof_callback, NULL);
    1974         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to register LSC callback");
    1975                 :            : 
    1976                 :            :         /* Configure with LSC enabled and start */
    1977         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port_conf(port_id, &port_conf) == 0,
    1978                 :            :                     "Failed to setup port with LSC");
    1979                 :            : 
    1980                 :            :         /* Verify link is up initially */
    1981                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
    1982         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link");
    1983         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
    1984                 :            :                     "Link should be UP after start");
    1985                 :            : 
    1986                 :            :         /* Drain all packets from the pcap file */
    1987                 :            :         total_rx = 0;
    1988         [ +  - ]:          3 :         for (int attempts = 0; attempts < 200; attempts++) {
    1989                 :          3 :                 uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, mbufs,
    1990                 :            :                                                   MAX_PKT_BURST);
    1991         [ +  + ]:          3 :                 if (nb_rx > 0) {
    1992                 :          2 :                         rte_pktmbuf_free_bulk(mbufs, nb_rx);
    1993                 :          2 :                         total_rx += nb_rx;
    1994         [ -  + ]:          1 :                 } else if (total_rx >= NUM_PACKETS) {
    1995                 :            :                         /* Got all packets and rx returned 0 — EOF hit */
    1996                 :            :                         break;
    1997                 :            :                 }
    1998                 :            :         }
    1999                 :            : 
    2000                 :            :         printf("    Received %u packets (expected %d)\n", total_rx, NUM_PACKETS);
    2001         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(total_rx, NUM_PACKETS,
    2002                 :            :                           "Should receive exactly %d packets", NUM_PACKETS);
    2003                 :            : 
    2004                 :            :         /* Verify link went down */
    2005                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
    2006         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link after EOF");
    2007         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_DOWN,
    2008                 :            :                     "Link should be DOWN after EOF");
    2009                 :            : 
    2010                 :            :         /* Poll for the deferred EOF alarm to fire on the interrupt thread */
    2011                 :            :         {
    2012                 :            :                 int poll;
    2013         [ +  - ]:          2 :                 for (poll = 0; poll < 20; poll++) {
    2014         [ +  + ]:          2 :                         if (eof_callback_count >= 1)
    2015                 :            :                                 break;
    2016                 :          1 :                         rte_delay_us_sleep(100 * 1000);
    2017                 :            :                 }
    2018                 :            :         }
    2019                 :            : 
    2020                 :            :         /* Verify callback fired exactly once */
    2021         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(eof_callback_count, 1,
    2022                 :            :                           "LSC callback should fire once, fired %d times",
    2023                 :            :                           eof_callback_count);
    2024                 :            :         printf("    EOF signaled: link DOWN, callback fired\n");
    2025                 :            : 
    2026                 :            :         /*
    2027                 :            :          * Test 2: Stop/start resets EOF and replays the file
    2028                 :            :          */
    2029                 :            :         printf("  Testing restart replays pcap file\n");
    2030                 :            : 
    2031                 :          1 :         ret = rte_eth_dev_stop(port_id);
    2032         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to stop port");
    2033                 :            : 
    2034                 :          1 :         eof_callback_count = 0;
    2035                 :            : 
    2036                 :          1 :         ret = rte_eth_dev_start(port_id);
    2037         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to restart port");
    2038                 :            : 
    2039                 :            :         /* Verify link is up again */
    2040                 :          1 :         ret = rte_eth_link_get_nowait(port_id, &link);
    2041         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get link after restart");
    2042         [ -  + ]:          1 :         TEST_ASSERT(link.link_status == RTE_ETH_LINK_UP,
    2043                 :            :                     "Link should be UP after restart");
    2044                 :            : 
    2045                 :            :         /* Read packets again */
    2046                 :            :         total_rx = 0;
    2047         [ +  - ]:          3 :         for (int attempts = 0; attempts < 200; attempts++) {
    2048                 :          3 :                 uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, mbufs,
    2049                 :            :                                                   MAX_PKT_BURST);
    2050         [ +  + ]:          3 :                 if (nb_rx > 0) {
    2051                 :          2 :                         rte_pktmbuf_free_bulk(mbufs, nb_rx);
    2052                 :          2 :                         total_rx += nb_rx;
    2053         [ -  + ]:          1 :                 } else if (total_rx >= NUM_PACKETS) {
    2054                 :            :                         break;
    2055                 :            :                 }
    2056                 :            :         }
    2057                 :            : 
    2058         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(total_rx, NUM_PACKETS,
    2059                 :            :                           "Restart: should receive %d packets, got %u",
    2060                 :            :                           NUM_PACKETS, total_rx);
    2061                 :            : 
    2062                 :            :         /* Poll for the deferred EOF alarm to fire on the interrupt thread */
    2063                 :            :         {
    2064                 :            :                 int poll;
    2065         [ +  - ]:          2 :                 for (poll = 0; poll < 20; poll++) {
    2066         [ +  + ]:          2 :                         if (eof_callback_count >= 1)
    2067                 :            :                                 break;
    2068                 :          1 :                         rte_delay_us_sleep(100 * 1000);
    2069                 :            :                 }
    2070                 :            :         }
    2071                 :            : 
    2072         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(eof_callback_count, 1,
    2073                 :            :                           "Restart: callback should fire once, fired %d times",
    2074                 :            :                           eof_callback_count);
    2075                 :            :         printf("    Restart replay: %u packets, EOF signaled again\n", total_rx);
    2076                 :            : 
    2077                 :            :         /* Cleanup */
    2078                 :          1 :         rte_eth_dev_callback_unregister(port_id, RTE_ETH_EVENT_INTR_LSC,
    2079                 :            :                                         test_eof_callback, NULL);
    2080                 :          1 :         rte_eth_dev_stop(port_id);
    2081                 :          1 :         rte_vdev_uninit("net_pcap_eof");
    2082                 :            : 
    2083                 :            :         /*
    2084                 :            :          * Test 3: eof + infinite_rx is rejected
    2085                 :            :          */
    2086                 :            :         printf("  Testing eof + infinite_rx mutual exclusion\n");
    2087                 :            : 
    2088                 :            :         ret = snprintf(devargs, sizeof(devargs),
    2089                 :            :                        "rx_pcap=%s,eof=1,infinite_rx=1", eof_pcap_path);
    2090         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2091                 :            :                     "devargs string truncated");
    2092                 :          1 :         ret = rte_vdev_init("net_pcap_eof_bad", devargs);
    2093         [ -  + ]:          1 :         TEST_ASSERT(ret != 0, "eof + infinite_rx should be rejected");
    2094                 :            :         printf("    Mutual exclusion check PASSED\n");
    2095                 :            : 
    2096                 :            :         remove_temp_file(eof_pcap_path);
    2097                 :            : 
    2098                 :            :         printf("EOF test PASSED\n");
    2099                 :          1 :         return TEST_SUCCESS;
    2100                 :            : }
    2101                 :            : 
    2102                 :            : /*
    2103                 :            :  * Test: Verify receive timestamps from pcap file
    2104                 :            :  */
    2105                 :            : static int
    2106                 :          1 : test_rx_timestamp(void)
    2107                 :            : {
    2108                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    2109                 :            :         char timestamp_pcap_path[PATH_MAX];
    2110                 :            :         char devargs[256];
    2111                 :            :         uint16_t port_id;
    2112                 :            :         unsigned int received, i;
    2113                 :            :         int ret;
    2114                 :            :         const uint32_t base_sec = 1000;
    2115                 :            :         const uint32_t usec_increment = 10000; /* 10ms between packets */
    2116                 :            :         rte_mbuf_timestamp_t prev_ts = 0;
    2117                 :            : 
    2118                 :            :         printf("Testing RX timestamp accuracy\n");
    2119                 :            : 
    2120         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(timestamp_pcap_path, sizeof(timestamp_pcap_path),
    2121                 :            :                                      "pcap_ts") == 0,
    2122                 :            :                     "Failed to create temp path");
    2123         [ -  + ]:          1 :         TEST_ASSERT(create_timestamped_pcap(timestamp_pcap_path, NUM_PACKETS,
    2124                 :            :                                             base_sec, usec_increment) == 0,
    2125                 :            :                     "Failed to create timestamped pcap");
    2126                 :            : 
    2127                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", timestamp_pcap_path);
    2128         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2129                 :            :                     "devargs string truncated");
    2130         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_ts", devargs, &port_id) == 0,
    2131                 :            :                     "Failed to create timestamp vdev");
    2132         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    2133                 :            :                     "Failed to setup timestamp port");
    2134                 :            : 
    2135                 :            :         /* Try to initialize timestamp dynamic field access */
    2136         [ -  + ]:          1 :         TEST_ASSERT(timestamp_init() == 0, "Timestamp dynfield not available");
    2137                 :            : 
    2138                 :          1 :         receive_packets(port_id, mbufs, NUM_PACKETS, &received);
    2139         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(received, NUM_PACKETS,
    2140                 :            :                           "Received %u packets, expected %d", received, NUM_PACKETS);
    2141                 :            : 
    2142                 :            :         /* Check if first packet has timestamp flag set */
    2143         [ -  + ]:          1 :         if (!mbuf_has_timestamp(mbufs[0])) {
    2144                 :            :                 printf("Timestamps not enabled in mbufs, skipping validation\n");
    2145                 :          0 :                 rte_pktmbuf_free_bulk(mbufs, received);
    2146                 :          0 :                 cleanup_pcap_vdev("net_pcap_ts", port_id);
    2147                 :          0 :                 return TEST_SKIPPED;
    2148                 :            :         }
    2149                 :            : 
    2150         [ +  + ]:         65 :         for (i = 0; i < received; i++) {
    2151                 :         64 :                 struct rte_mbuf *m = mbufs[i];
    2152                 :            : 
    2153         [ -  + ]:         64 :                 TEST_ASSERT(mbuf_has_timestamp(m),
    2154                 :            :                             "Packet %u missing timestamp flag", i);
    2155                 :            : 
    2156                 :            :                 /* PCAP PMD stores timestamp in nanoseconds */
    2157                 :            :                 rte_mbuf_timestamp_t ts = mbuf_timestamp_get(mbufs[i]);
    2158                 :         64 :                 uint64_t expected = (uint64_t)base_sec * NS_PER_S
    2159                 :         64 :                         + (uint64_t)i * usec_increment * 1000;
    2160                 :            : 
    2161         [ -  + ]:         64 :                 TEST_ASSERT_EQUAL(ts, expected,
    2162                 :            :                                   "Packet %u: timestamp mismatch, expected=%"PRIu64
    2163                 :            :                                   " actual=%"PRIu64, i, expected, ts);
    2164                 :            : 
    2165                 :            :                 /* Verify monotonically increasing timestamps */
    2166         [ +  + ]:         64 :                 if (i > 0) {
    2167         [ -  + ]:         63 :                         TEST_ASSERT(ts >= prev_ts,
    2168                 :            :                                     "Packet %u: timestamp not monotonic %"PRIu64" > %"PRIu64,
    2169                 :            :                                     i, prev_ts, ts);
    2170                 :            :                 }
    2171                 :            :                 prev_ts = ts;
    2172                 :            :         }
    2173                 :            : 
    2174                 :          1 :         rte_pktmbuf_free_bulk(mbufs, received);
    2175                 :          1 :         cleanup_pcap_vdev("net_pcap_ts", port_id);
    2176                 :            :         remove_temp_file(timestamp_pcap_path);
    2177                 :            : 
    2178                 :            :         printf("RX timestamp PASSED: %u packets with valid timestamps\n", received);
    2179                 :          1 :         return TEST_SUCCESS;
    2180                 :            : }
    2181                 :            : 
    2182                 :            : /* Helper: Generate packets for multi-queue tests */
    2183                 :            : static int
    2184                 :          4 : generate_mq_test_packets(struct rte_mbuf **pkts, unsigned int nb_pkts, uint16_t queue_id)
    2185                 :            : {
    2186                 :            :         struct rte_ether_hdr eth_hdr;
    2187                 :            :         struct rte_ipv4_hdr ip_hdr;
    2188                 :            :         struct rte_udp_hdr udp_hdr;
    2189                 :            :         uint16_t pkt_data_len;
    2190                 :            :         unsigned int i;
    2191                 :            : 
    2192                 :          4 :         initialize_eth_header(&eth_hdr, &src_mac, &dst_mac, RTE_ETHER_TYPE_IPV4, 0, 0);
    2193                 :            :         pkt_data_len = sizeof(struct rte_udp_hdr);
    2194                 :          4 :         initialize_udp_header(&udp_hdr, 1234, 1234, pkt_data_len);
    2195                 :          4 :         initialize_ipv4_header(&ip_hdr, IPV4_ADDR(192, 168, 1, 1), IPV4_ADDR(192, 168, 1, 2),
    2196                 :            :                                pkt_data_len + sizeof(struct rte_udp_hdr));
    2197                 :            : 
    2198         [ +  + ]:        104 :         for (i = 0; i < nb_pkts; i++) {
    2199                 :        100 :                 pkts[i] = rte_pktmbuf_alloc(mp);
    2200         [ -  + ]:        100 :                 if (pkts[i] == NULL) {
    2201                 :            :                         printf("Failed to allocate mbuf\n");
    2202         [ #  # ]:          0 :                         while (i > 0)
    2203                 :          0 :                                 rte_pktmbuf_free(pkts[--i]);
    2204                 :            :                         return -1;
    2205                 :            :                 }
    2206                 :            : 
    2207                 :            :                 char *pkt_data = rte_pktmbuf_append(pkts[i], PACKET_BURST_GEN_PKT_LEN);
    2208         [ -  + ]:        100 :                 if (pkt_data == NULL) {
    2209                 :            :                         printf("Failed to append data to mbuf\n");
    2210                 :          0 :                         rte_pktmbuf_free(pkts[i]);
    2211         [ #  # ]:          0 :                         while (i > 0)
    2212                 :          0 :                                 rte_pktmbuf_free(pkts[--i]);
    2213                 :            :                         return -1;
    2214                 :            :                 }
    2215                 :            : 
    2216                 :            :                 size_t offset = 0;
    2217                 :            :                 memcpy(pkt_data + offset, &eth_hdr, sizeof(eth_hdr));
    2218                 :            :                 offset += sizeof(eth_hdr);
    2219                 :            : 
    2220                 :            :                 /* Mark packet with queue ID in IP packet_id field for tracing */
    2221         [ -  + ]:        100 :                 ip_hdr.packet_id = rte_cpu_to_be_16((queue_id << 8) | (i & 0xFF));
    2222                 :        100 :                 ip_hdr.hdr_checksum = 0;
    2223                 :        100 :                 ip_hdr.hdr_checksum = rte_ipv4_cksum(&ip_hdr);
    2224                 :            : 
    2225                 :        100 :                 memcpy(pkt_data + offset, &ip_hdr, sizeof(ip_hdr));
    2226                 :            :                 offset += sizeof(ip_hdr);
    2227                 :        100 :                 memcpy(pkt_data + offset, &udp_hdr, sizeof(udp_hdr));
    2228                 :            :         }
    2229                 :          4 :         return (int)nb_pkts;
    2230                 :            : }
    2231                 :            : 
    2232                 :            : /* Helper: Validate pcap file structure using libpcap */
    2233                 :            : static int
    2234                 :          4 : validate_pcap_file(const char *filename)
    2235                 :            : {
    2236                 :            :         pcap_t *pcap;
    2237                 :            :         char errbuf[PCAP_ERRBUF_SIZE];
    2238                 :            : 
    2239                 :          4 :         pcap = pcap_open_offline(filename, errbuf);
    2240         [ -  + ]:          4 :         if (pcap == NULL) {
    2241                 :            :                 printf("Failed to validate pcap file %s: %s\n", filename, errbuf);
    2242                 :          0 :                 return -1;
    2243                 :            :         }
    2244         [ -  + ]:          4 :         if (pcap_datalink(pcap) != DLT_EN10MB) {
    2245                 :          0 :                 printf("Unexpected datalink type: %d\n", pcap_datalink(pcap));
    2246                 :          0 :                 pcap_close(pcap);
    2247                 :          0 :                 return -1;
    2248                 :            :         }
    2249                 :          4 :         pcap_close(pcap);
    2250                 :          4 :         return 0;
    2251                 :            : }
    2252                 :            : 
    2253                 :            : /*
    2254                 :            :  * Test: Multiple TX queues writing to separate pcap files
    2255                 :            :  *
    2256                 :            :  * This test creates a pcap PMD with multiple TX queues, each configured
    2257                 :            :  * to write to its own output file. We verify that:
    2258                 :            :  * 1. All packets from all queues are written
    2259                 :            :  * 2. Each resulting pcap file is valid
    2260                 :            :  * 3. Each file has the expected packet count
    2261                 :            :  */
    2262                 :            : static int
    2263                 :          1 : test_multi_tx_queue(void)
    2264                 :            : {
    2265                 :            :         char multi_tx_pcap_paths[MULTI_QUEUE_NUM_QUEUES][PATH_MAX];
    2266                 :            :         char devargs[512];
    2267                 :            :         uint16_t port_id;
    2268                 :            :         struct rte_eth_conf port_conf;
    2269                 :            :         struct rte_eth_txconf tx_conf;
    2270                 :            :         struct rte_mbuf *pkts[MULTI_QUEUE_BURST_SIZE];
    2271                 :            :         uint16_t q;
    2272                 :            :         int ret;
    2273                 :            :         unsigned int total_tx = 0;
    2274                 :          1 :         unsigned int tx_per_queue[MULTI_QUEUE_NUM_QUEUES] = {0};
    2275                 :            : 
    2276                 :            :         printf("Testing multiple TX queues to separate files\n");
    2277                 :            : 
    2278                 :            :         /* Create temp paths for each TX queue */
    2279         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2280                 :            :                 char prefix[32];
    2281                 :          4 :                 snprintf(prefix, sizeof(prefix), "pcap_multi_tx%u", q);
    2282         [ -  + ]:          4 :                 TEST_ASSERT(create_temp_path(multi_tx_pcap_paths[q],
    2283                 :            :                                              sizeof(multi_tx_pcap_paths[q]), prefix) == 0,
    2284                 :            :                             "Failed to create temp path for queue %u", q);
    2285                 :            :         }
    2286                 :            : 
    2287                 :            :         /* Create the pcap PMD with multiple TX queues to separate files */
    2288                 :            :         ret = snprintf(devargs, sizeof(devargs), "tx_pcap=%s,tx_pcap=%s,tx_pcap=%s,tx_pcap=%s",
    2289                 :            :                  multi_tx_pcap_paths[0], multi_tx_pcap_paths[1],
    2290                 :            :                  multi_tx_pcap_paths[2], multi_tx_pcap_paths[3]);
    2291         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2292                 :            :                     "devargs string truncated");
    2293                 :            : 
    2294                 :          1 :         ret = rte_vdev_init("net_pcap_multi_tx", devargs);
    2295         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to create pcap PMD: %s", rte_strerror(-ret));
    2296                 :            : 
    2297                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_multi_tx", &port_id);
    2298         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Cannot find added pcap device");
    2299                 :            : 
    2300                 :            :         memset(&port_conf, 0, sizeof(port_conf));
    2301                 :          1 :         ret = rte_eth_dev_configure(port_id, 0, MULTI_QUEUE_NUM_QUEUES, &port_conf);
    2302         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to configure device: %s", rte_strerror(-ret));
    2303                 :            : 
    2304                 :            :         memset(&tx_conf, 0, sizeof(tx_conf));
    2305         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2306                 :          4 :                 ret = rte_eth_tx_queue_setup(port_id, q, RING_SIZE,
    2307                 :          4 :                                              rte_eth_dev_socket_id(port_id), &tx_conf);
    2308         [ -  + ]:          4 :                 TEST_ASSERT_SUCCESS(ret, "Failed to setup TX queue %u: %s", q, rte_strerror(-ret));
    2309                 :            :         }
    2310                 :            : 
    2311                 :          1 :         ret = rte_eth_dev_start(port_id);
    2312         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to start device: %s", rte_strerror(-ret));
    2313                 :            : 
    2314                 :            :         /* Transmit packets from each queue */
    2315         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2316                 :            :                 unsigned int pkts_to_send = MULTI_QUEUE_NUM_PACKETS / MULTI_QUEUE_NUM_QUEUES;
    2317                 :            : 
    2318         [ +  + ]:          8 :                 while (tx_per_queue[q] < pkts_to_send) {
    2319                 :          4 :                         unsigned int burst = RTE_MIN(MULTI_QUEUE_BURST_SIZE,
    2320                 :            :                                                      pkts_to_send - tx_per_queue[q]);
    2321                 :            : 
    2322                 :          4 :                         ret = generate_mq_test_packets(pkts, burst, q);
    2323         [ -  + ]:          4 :                         TEST_ASSERT(ret >= 0, "Failed to generate packets for queue %u", q);
    2324                 :            : 
    2325                 :          4 :                         uint16_t nb_tx = rte_eth_tx_burst(port_id, q, pkts, burst);
    2326         [ -  + ]:          4 :                         for (unsigned int i = nb_tx; i < burst; i++)
    2327                 :          0 :                                 rte_pktmbuf_free(pkts[i]);
    2328                 :            : 
    2329                 :          4 :                         tx_per_queue[q] += nb_tx;
    2330                 :          4 :                         total_tx += nb_tx;
    2331                 :            : 
    2332         [ -  + ]:          4 :                         if (nb_tx == 0) {
    2333                 :            :                                 printf("TX stall on queue %u\n", q);
    2334                 :            :                                 break;
    2335                 :            :                         }
    2336                 :            :                 }
    2337                 :          4 :                 printf("  Queue %u: transmitted %u packets\n", q, tx_per_queue[q]);
    2338                 :            :         }
    2339                 :            : 
    2340                 :          1 :         rte_eth_dev_stop(port_id);
    2341                 :          1 :         rte_vdev_uninit("net_pcap_multi_tx");
    2342                 :            :         rte_delay_ms(100);
    2343                 :            : 
    2344                 :            :         /* Validate each pcap file */
    2345                 :            :         unsigned int total_in_files = 0;
    2346         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2347                 :          4 :                 ret = validate_pcap_file(multi_tx_pcap_paths[q]);
    2348         [ -  + ]:          4 :                 TEST_ASSERT_SUCCESS(ret, "pcap file for queue %u is invalid", q);
    2349                 :            : 
    2350                 :          4 :                 int pkt_count = count_pcap_packets(multi_tx_pcap_paths[q]);
    2351         [ -  + ]:          4 :                 TEST_ASSERT(pkt_count >= 0, "Could not count packets in pcap file for queue %u", q);
    2352                 :            : 
    2353                 :            :                 printf("  Queue %u file: %d packets\n", q, pkt_count);
    2354         [ -  + ]:          4 :                 TEST_ASSERT_EQUAL((unsigned int)pkt_count, tx_per_queue[q],
    2355                 :            :                                   "Queue %u: file has %d packets, expected %u",
    2356                 :            :                                   q, pkt_count, tx_per_queue[q]);
    2357                 :          4 :                 total_in_files += pkt_count;
    2358                 :            :         }
    2359                 :            : 
    2360                 :            :         printf("  Total packets transmitted: %u\n", total_tx);
    2361                 :            :         printf("  Total packets in all files: %u\n", total_in_files);
    2362                 :            : 
    2363         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(total_in_files, total_tx,
    2364                 :            :                           "Total packet count mismatch: expected %u, got %u",
    2365                 :            :                           total_tx, total_in_files);
    2366                 :            : 
    2367         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++)
    2368         [ +  - ]:          4 :                 remove_temp_file(multi_tx_pcap_paths[q]);
    2369                 :            : 
    2370                 :            :         printf("Multi-TX queue PASSED\n");
    2371                 :          1 :         return TEST_SUCCESS;
    2372                 :            : }
    2373                 :            : 
    2374                 :            : /*
    2375                 :            :  * Test: Multiple RX queues reading from the same pcap file
    2376                 :            :  *
    2377                 :            :  * This test creates a pcap PMD with multiple RX queues all configured
    2378                 :            :  * to read from the same input file. We verify that:
    2379                 :            :  * 1. Each queue can read packets
    2380                 :            :  * 2. The total packets read equals the file content (or expected behavior)
    2381                 :            :  */
    2382                 :            : static int
    2383                 :          1 : test_multi_rx_queue_same_file(void)
    2384                 :            : {
    2385                 :            :         char multi_rx_pcap_path[PATH_MAX];
    2386                 :            :         char devargs[512];
    2387                 :            :         uint16_t port_id;
    2388                 :            :         struct rte_eth_conf port_conf;
    2389                 :            :         struct rte_eth_rxconf rx_conf;
    2390                 :            :         struct rte_mbuf *pkts[MULTI_QUEUE_BURST_SIZE];
    2391                 :            :         uint16_t q;
    2392                 :            :         int ret;
    2393                 :            :         unsigned int total_rx = 0;
    2394                 :          1 :         unsigned int rx_per_queue[MULTI_QUEUE_NUM_QUEUES] = {0};
    2395                 :            :         unsigned int seed_packets = MULTI_QUEUE_NUM_PACKETS;
    2396                 :            :         unsigned int expected_total;
    2397                 :            : 
    2398                 :            :         printf("Testing multiple RX queues from same file\n");
    2399                 :            : 
    2400         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(multi_rx_pcap_path, sizeof(multi_rx_pcap_path),
    2401                 :            :                                      "pcap_multi_rx") == 0,
    2402                 :            :                     "Failed to create temp path");
    2403                 :            : 
    2404                 :          1 :         ret = create_test_pcap(multi_rx_pcap_path, seed_packets);
    2405         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to create seed pcap file");
    2406                 :            :         printf("  Created seed pcap file with %u packets\n", seed_packets);
    2407                 :            : 
    2408                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s,rx_pcap=%s,rx_pcap=%s,rx_pcap=%s",
    2409                 :            :                  multi_rx_pcap_path, multi_rx_pcap_path, multi_rx_pcap_path, multi_rx_pcap_path);
    2410         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2411                 :            :                     "devargs string truncated");
    2412                 :            : 
    2413                 :          1 :         ret = rte_vdev_init("net_pcap_multi_rx", devargs);
    2414         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to create pcap PMD: %s", rte_strerror(-ret));
    2415                 :            : 
    2416                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_multi_rx", &port_id);
    2417         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Cannot find added pcap device");
    2418                 :            : 
    2419                 :            :         memset(&port_conf, 0, sizeof(port_conf));
    2420                 :          1 :         ret = rte_eth_dev_configure(port_id, MULTI_QUEUE_NUM_QUEUES, 0, &port_conf);
    2421         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to configure device: %s", rte_strerror(-ret));
    2422                 :            : 
    2423                 :            :         memset(&rx_conf, 0, sizeof(rx_conf));
    2424         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2425                 :          4 :                 ret = rte_eth_rx_queue_setup(port_id, q, RING_SIZE,
    2426                 :          4 :                                              rte_eth_dev_socket_id(port_id), &rx_conf, mp);
    2427         [ -  + ]:          4 :                 TEST_ASSERT_SUCCESS(ret, "Failed to setup RX queue %u: %s", q, rte_strerror(-ret));
    2428                 :            :         }
    2429                 :            : 
    2430                 :          1 :         ret = rte_eth_dev_start(port_id);
    2431         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to start device: %s", rte_strerror(-ret));
    2432                 :            : 
    2433                 :            :         /* Receive packets from all queues. Each queue has its own file handle. */
    2434                 :            :         int empty_rounds = 0;
    2435         [ +  + ]:         11 :         while (empty_rounds < 10) {
    2436                 :            :                 int received_this_round = 0;
    2437         [ +  + ]:        126 :                 for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2438                 :         56 :                         uint16_t nb_rx = rte_eth_rx_burst(port_id, q, pkts, MULTI_QUEUE_BURST_SIZE);
    2439         [ +  + ]:         56 :                         if (nb_rx > 0) {
    2440                 :         16 :                                 rx_per_queue[q] += nb_rx;
    2441                 :         16 :                                 total_rx += nb_rx;
    2442                 :         16 :                                 received_this_round += nb_rx;
    2443                 :         16 :                                 rte_pktmbuf_free_bulk(pkts, nb_rx);
    2444                 :            :                         }
    2445                 :            :                 }
    2446         [ +  + ]:         14 :                 if (received_this_round == 0)
    2447                 :         10 :                         empty_rounds++;
    2448                 :            :                 else
    2449                 :            :                         empty_rounds = 0;
    2450                 :            :         }
    2451                 :            : 
    2452                 :            :         printf("  RX Results:\n");
    2453         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++)
    2454                 :          4 :                 printf("    Queue %u: received %u packets\n", q, rx_per_queue[q]);
    2455                 :            :         printf("    Total received: %u packets\n", total_rx);
    2456                 :            : 
    2457                 :            :         /* Each RX queue opens its own file handle, so each reads all packets */
    2458                 :            :         expected_total = seed_packets * MULTI_QUEUE_NUM_QUEUES;
    2459                 :            :         printf("    Expected total (each queue reads all): %u packets\n", expected_total);
    2460                 :            : 
    2461                 :          1 :         rte_eth_dev_stop(port_id);
    2462                 :          1 :         rte_vdev_uninit("net_pcap_multi_rx");
    2463                 :            : 
    2464         [ -  + ]:          1 :         TEST_ASSERT(total_rx > 0, "No packets received at all");
    2465         [ +  + ]:          5 :         for (q = 0; q < MULTI_QUEUE_NUM_QUEUES; q++) {
    2466         [ -  + ]:          4 :                 TEST_ASSERT(rx_per_queue[q] > 0, "Queue %u received no packets", q);
    2467         [ -  + ]:          4 :                 TEST_ASSERT_EQUAL(rx_per_queue[q], seed_packets,
    2468                 :            :                                   "Queue %u received %u packets, expected %u",
    2469                 :            :                                   q, rx_per_queue[q], seed_packets);
    2470                 :            :         }
    2471         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(total_rx, expected_total,
    2472                 :            :                           "Total RX mismatch: expected %u, got %u", expected_total, total_rx);
    2473                 :            : 
    2474                 :            :         remove_temp_file(multi_rx_pcap_path);
    2475                 :            : 
    2476                 :            :         printf("Multi-RX queue PASSED\n");
    2477                 :          1 :         return TEST_SUCCESS;
    2478                 :            : }
    2479                 :            : 
    2480                 :            : /*
    2481                 :            :  * Test: Device info reports correct queue counts and MTU limits
    2482                 :            :  *
    2483                 :            :  * This test verifies that rte_eth_dev_info_get() returns correct values:
    2484                 :            :  * 1. max_rx_queues matches the number of rx_pcap files passed
    2485                 :            :  * 2. max_tx_queues matches the number of tx_pcap files passed
    2486                 :            :  * 3. max_rx_pktlen and max_mtu are based on default snapshot length
    2487                 :            :  */
    2488                 :            : static int
    2489                 :          1 : test_dev_info(void)
    2490                 :            : {
    2491                 :            :         struct rte_eth_dev_info dev_info;
    2492                 :            :         char devargs[512];
    2493                 :            :         char rx_paths[3][PATH_MAX];
    2494                 :            :         char tx_paths[2][PATH_MAX];
    2495                 :            :         uint16_t port_id;
    2496                 :            :         int ret;
    2497                 :            :         unsigned int i;
    2498                 :            :         /* Default snapshot length is 65535 */
    2499                 :            :         const uint32_t default_snaplen = 65535;
    2500                 :            :         const uint32_t expected_max_mtu = default_snaplen - RTE_ETHER_HDR_LEN;
    2501                 :            : 
    2502                 :            :         printf("Testing device info reporting\n");
    2503                 :            : 
    2504                 :            :         /* Create temp RX pcap files (3 queues) */
    2505         [ +  + ]:          4 :         for (i = 0; i < 3; i++) {
    2506                 :            :                 char prefix[32];
    2507                 :            :                 snprintf(prefix, sizeof(prefix), "pcap_devinfo_rx%u", i);
    2508         [ -  + ]:          3 :                 TEST_ASSERT(create_temp_path(rx_paths[i], sizeof(rx_paths[i]), prefix) == 0,
    2509                 :            :                             "Failed to create RX temp path %u", i);
    2510         [ -  + ]:          3 :                 TEST_ASSERT(create_test_pcap(rx_paths[i], 1) == 0,
    2511                 :            :                             "Failed to create RX pcap %u", i);
    2512                 :            :         }
    2513                 :            : 
    2514                 :            :         /* Create temp TX pcap files (2 queues) */
    2515         [ +  + ]:          3 :         for (i = 0; i < 2; i++) {
    2516                 :            :                 char prefix[32];
    2517                 :            :                 snprintf(prefix, sizeof(prefix), "pcap_devinfo_tx%u", i);
    2518         [ -  + ]:          2 :                 TEST_ASSERT(create_temp_path(tx_paths[i], sizeof(tx_paths[i]), prefix) == 0,
    2519                 :            :                             "Failed to create TX temp path %u", i);
    2520                 :            :         }
    2521                 :            : 
    2522                 :            :         /* Create device with 3 RX queues and 2 TX queues */
    2523                 :            :         ret = snprintf(devargs, sizeof(devargs),
    2524                 :            :                        "rx_pcap=%s,rx_pcap=%s,rx_pcap=%s,tx_pcap=%s,tx_pcap=%s",
    2525                 :            :                        rx_paths[0], rx_paths[1], rx_paths[2], tx_paths[0], tx_paths[1]);
    2526         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2527                 :            :                     "devargs string truncated");
    2528                 :            : 
    2529                 :          1 :         ret = rte_vdev_init("net_pcap_devinfo", devargs);
    2530         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to create pcap PMD: %s", rte_strerror(-ret));
    2531                 :            : 
    2532                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_devinfo", &port_id);
    2533         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Cannot find added pcap device");
    2534                 :            : 
    2535                 :          1 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
    2536         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to get device info: %s", rte_strerror(-ret));
    2537                 :            : 
    2538                 :            :         printf("  Device info:\n");
    2539                 :          1 :         printf("    driver_name: %s\n", dev_info.driver_name);
    2540                 :          1 :         printf("    max_rx_queues: %u (expected: 3)\n", dev_info.max_rx_queues);
    2541                 :          1 :         printf("    max_tx_queues: %u (expected: 2)\n", dev_info.max_tx_queues);
    2542                 :          1 :         printf("    max_rx_pktlen: %u (expected: %u)\n", dev_info.max_rx_pktlen, default_snaplen);
    2543                 :          1 :         printf("    max_mtu: %u (expected: %u)\n", dev_info.max_mtu, expected_max_mtu);
    2544                 :            : 
    2545                 :            :         /* Verify queue counts match number of pcap files */
    2546         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(dev_info.max_rx_queues, 3U,
    2547                 :            :                           "max_rx_queues mismatch: expected 3, got %u", dev_info.max_rx_queues);
    2548         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(dev_info.max_tx_queues, 2U,
    2549                 :            :                           "max_tx_queues mismatch: expected 2, got %u", dev_info.max_tx_queues);
    2550                 :            : 
    2551                 :            :         /* Verify max_rx_pktlen equals default snapshot length */
    2552         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(dev_info.max_rx_pktlen, default_snaplen,
    2553                 :            :                           "max_rx_pktlen mismatch: expected %u, got %u",
    2554                 :            :                           default_snaplen, dev_info.max_rx_pktlen);
    2555                 :            : 
    2556                 :            :         /* Verify max_mtu is snapshot_len minus ethernet header */
    2557         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(dev_info.max_mtu, expected_max_mtu,
    2558                 :            :                           "max_mtu mismatch: expected %u, got %u",
    2559                 :            :                           expected_max_mtu, dev_info.max_mtu);
    2560                 :            : 
    2561                 :          1 :         rte_vdev_uninit("net_pcap_devinfo");
    2562                 :            : 
    2563                 :            :         /* Cleanup temp files */
    2564         [ +  + ]:          4 :         for (i = 0; i < 3; i++)
    2565         [ +  - ]:          3 :                 remove_temp_file(rx_paths[i]);
    2566         [ +  + ]:          3 :         for (i = 0; i < 2; i++)
    2567         [ +  - ]:          2 :                 remove_temp_file(tx_paths[i]);
    2568                 :            : 
    2569                 :            :         printf("Device info PASSED\n");
    2570                 :          1 :         return TEST_SUCCESS;
    2571                 :            : }
    2572                 :            : 
    2573                 :            : /*
    2574                 :            :  * Test: Custom snapshot length (snaplen) parameter
    2575                 :            :  *
    2576                 :            :  * This test verifies that the snaplen devarg works correctly:
    2577                 :            :  * 1. max_rx_pktlen reflects the custom snapshot length
    2578                 :            :  * 2. max_mtu is calculated as snaplen - ethernet header
    2579                 :            :  */
    2580                 :            : static int
    2581                 :          1 : test_snaplen(void)
    2582                 :            : {
    2583                 :            :         struct rte_eth_dev_info dev_info;
    2584                 :            :         char devargs[512];
    2585                 :            :         char rx_path[PATH_MAX];
    2586                 :            :         char tx_path[PATH_MAX];
    2587                 :            :         uint16_t port_id;
    2588                 :            :         int ret;
    2589                 :            :         const uint32_t custom_snaplen = 9000;
    2590                 :            :         const uint32_t expected_max_mtu = custom_snaplen - RTE_ETHER_HDR_LEN;
    2591                 :            : 
    2592                 :            :         printf("Testing custom snapshot length parameter\n");
    2593                 :            : 
    2594                 :            :         /* Create temp files */
    2595         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_path, sizeof(rx_path), "pcap_snaplen_rx") == 0,
    2596                 :            :                     "Failed to create RX temp path");
    2597         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(rx_path, 1) == 0,
    2598                 :            :                     "Failed to create RX pcap");
    2599         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_path, sizeof(tx_path), "pcap_snaplen_tx") == 0,
    2600                 :            :                     "Failed to create TX temp path");
    2601                 :            : 
    2602                 :            :         /* Create device with custom snaplen */
    2603                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s,tx_pcap=%s,snaplen=%u",
    2604                 :            :                  rx_path, tx_path, custom_snaplen);
    2605         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2606                 :            :                     "devargs string truncated");
    2607                 :            : 
    2608                 :          1 :         ret = rte_vdev_init("net_pcap_snaplen", devargs);
    2609         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to create pcap PMD: %s", rte_strerror(-ret));
    2610                 :            : 
    2611                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_snaplen", &port_id);
    2612         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Cannot find added pcap device");
    2613                 :            : 
    2614                 :          1 :         ret = rte_eth_dev_info_get(port_id, &dev_info);
    2615         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to get device info: %s", rte_strerror(-ret));
    2616                 :            : 
    2617                 :            :         printf("  Custom snaplen: %u\n", custom_snaplen);
    2618                 :          1 :         printf("  max_rx_pktlen: %u (expected: %u)\n", dev_info.max_rx_pktlen, custom_snaplen);
    2619                 :          1 :         printf("  max_mtu: %u (expected: %u)\n", dev_info.max_mtu, expected_max_mtu);
    2620                 :            : 
    2621                 :            :         /* Verify max_rx_pktlen equals custom snapshot length */
    2622         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(dev_info.max_rx_pktlen, custom_snaplen,
    2623                 :            :                           "max_rx_pktlen mismatch: expected %u, got %u",
    2624                 :            :                           custom_snaplen, dev_info.max_rx_pktlen);
    2625                 :            : 
    2626                 :            :         /* Verify max_mtu is snaplen minus ethernet header */
    2627         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(dev_info.max_mtu, expected_max_mtu,
    2628                 :            :                           "max_mtu mismatch: expected %u, got %u",
    2629                 :            :                           expected_max_mtu, dev_info.max_mtu);
    2630                 :            : 
    2631                 :          1 :         rte_vdev_uninit("net_pcap_snaplen");
    2632                 :            : 
    2633                 :            :         /* Cleanup temp files */
    2634                 :            :         remove_temp_file(rx_path);
    2635                 :            :         remove_temp_file(tx_path);
    2636                 :            : 
    2637                 :            :         printf("Snapshot length test PASSED\n");
    2638                 :          1 :         return TEST_SUCCESS;
    2639                 :            : }
    2640                 :            : 
    2641                 :            : /*
    2642                 :            :  * Test: Snapshot length truncation behavior
    2643                 :            :  *
    2644                 :            :  * This test verifies that packets larger than snaplen are properly truncated
    2645                 :            :  * when written to pcap files:
    2646                 :            :  * 1. caplen in pcap header is limited to snaplen
    2647                 :            :  * 2. len in pcap header preserves original packet length
    2648                 :            :  * 3. Only snaplen bytes of data are written
    2649                 :            :  */
    2650                 :            : static int
    2651                 :          1 : test_snaplen_truncation(void)
    2652                 :            : {
    2653                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    2654                 :            :         char devargs[512];
    2655                 :            :         char tx_path[PATH_MAX];
    2656                 :            :         uint16_t port_id;
    2657                 :            :         int ret, nb_tx, nb_gen;
    2658                 :            :         unsigned int pkt_count;
    2659                 :            :         const uint32_t test_snaplen = 100;
    2660                 :            :         const uint8_t pkt_size = 200;
    2661                 :            : 
    2662                 :            :         printf("Testing snaplen truncation behavior\n");
    2663                 :            : 
    2664                 :            :         /* Create temp TX file */
    2665         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_path, sizeof(tx_path), "pcap_trunc_tx") == 0,
    2666                 :            :                     "Failed to create TX temp path");
    2667                 :            : 
    2668                 :            :         /* Create device with small snaplen */
    2669                 :            :         ret = snprintf(devargs, sizeof(devargs), "tx_pcap=%s,snaplen=%u",
    2670                 :            :                  tx_path, test_snaplen);
    2671         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2672                 :            :                     "devargs string truncated");
    2673                 :            : 
    2674                 :          1 :         ret = rte_vdev_init("net_pcap_trunc", devargs);
    2675         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Failed to create pcap PMD: %s", rte_strerror(-ret));
    2676                 :            : 
    2677                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_trunc", &port_id);
    2678         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Cannot find added pcap device");
    2679                 :            : 
    2680         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0, "Failed to setup port");
    2681                 :            : 
    2682                 :            :         /* Generate packets larger than snaplen */
    2683                 :          1 :         nb_gen = generate_test_packets(mp, mbufs, NUM_PACKETS, pkt_size);
    2684         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(nb_gen, NUM_PACKETS,
    2685                 :            :                           "Failed to generate packets: got %d, expected %d", nb_gen, NUM_PACKETS);
    2686                 :            : 
    2687                 :            :         printf("  Sending %d packets of size %u with snaplen=%u\n",
    2688                 :            :                NUM_PACKETS, pkt_size, test_snaplen);
    2689                 :            : 
    2690                 :            :         /* Transmit packets */
    2691                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, NUM_PACKETS);
    2692         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(nb_tx, NUM_PACKETS,
    2693                 :            :                           "TX burst failed: sent %u/%d", nb_tx, NUM_PACKETS);
    2694                 :            : 
    2695                 :          1 :         cleanup_pcap_vdev("net_pcap_trunc", port_id);
    2696                 :            : 
    2697                 :            :         /* Verify truncation in output file */
    2698                 :          1 :         ret = verify_pcap_truncation(tx_path, test_snaplen, pkt_size, &pkt_count);
    2699         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Truncation verification failed");
    2700         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(pkt_count, (unsigned int)NUM_PACKETS,
    2701                 :            :                           "Packet count mismatch: got %u, expected %d",
    2702                 :            :                           pkt_count, NUM_PACKETS);
    2703                 :            : 
    2704                 :            :         printf("  Verified %u packets: caplen=%u, len=%u\n",
    2705                 :            :                pkt_count, test_snaplen, pkt_size);
    2706                 :            : 
    2707                 :            :         /* Cleanup */
    2708                 :            :         remove_temp_file(tx_path);
    2709                 :            : 
    2710                 :            :         printf("Snaplen truncation test PASSED\n");
    2711                 :          1 :         return TEST_SUCCESS;
    2712                 :            : }
    2713                 :            : 
    2714                 :            : /*
    2715                 :            :  * Test: Snapshot length truncation with multi-segment mbufs
    2716                 :            :  *
    2717                 :            :  * This test verifies that the dumper path correctly truncates
    2718                 :            :  * non-contiguous (multi-segment) mbufs when the total packet length
    2719                 :            :  * exceeds the configured snaplen.  It exercises the RTE_MIN(len, snaplen)
    2720                 :            :  * cap in the TX dumper by ensuring:
    2721                 :            :  *
    2722                 :            :  * 1. caplen in the pcap header equals snaplen (not pkt_len)
    2723                 :            :  * 2. len in the pcap header preserves the original packet length
    2724                 :            :  * 3. Truncation works when the snaplen boundary falls mid-chain
    2725                 :            :  */
    2726                 :            : static int
    2727                 :          1 : test_snaplen_truncation_multiseg(void)
    2728                 :            : {
    2729                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    2730                 :            :         char devargs[512];
    2731                 :            :         char tx_path[PATH_MAX];
    2732                 :            :         uint16_t port_id;
    2733                 :            :         int ret, nb_tx;
    2734                 :            :         unsigned int i, pkt_count;
    2735                 :            :         const uint32_t test_snaplen = 100;
    2736                 :            :         const uint32_t pkt_size = 300;
    2737                 :            :         const uint16_t seg_size = 64;
    2738                 :            :         const unsigned int num_pkts = 8;
    2739                 :            : 
    2740                 :            :         printf("Testing snaplen truncation with multi-segment mbufs\n");
    2741                 :            : 
    2742                 :            :         /* Create temp TX file */
    2743         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_path, sizeof(tx_path),
    2744                 :            :                                      "pcap_trunc_ms") == 0,
    2745                 :            :                     "Failed to create TX temp path");
    2746                 :            : 
    2747                 :            :         /* Create device with small snaplen */
    2748                 :            :         ret = snprintf(devargs, sizeof(devargs), "tx_pcap=%s,snaplen=%u",
    2749                 :            :                        tx_path, test_snaplen);
    2750         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2751                 :            :                     "devargs string truncated");
    2752                 :            : 
    2753         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_trunc_ms", devargs,
    2754                 :            :                                      &port_id) == 0,
    2755                 :            :                     "Failed to create TX vdev");
    2756         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0, "Failed to setup port");
    2757                 :            : 
    2758                 :            :         /*
    2759                 :            :          * Allocate multi-segment mbufs.  With seg_size=64 and pkt_size=300,
    2760                 :            :          * each mbuf will have 5 segments (4×64 + 1×44).  The snaplen of 100
    2761                 :            :          * falls partway through the second segment, forcing the dumper to
    2762                 :            :          * stop writing in the middle of the chain.
    2763                 :            :          */
    2764         [ +  + ]:          9 :         for (i = 0; i < num_pkts; i++) {
    2765                 :         16 :                 mbufs[i] = alloc_multiseg_mbuf(pkt_size, seg_size,
    2766                 :          8 :                                                (uint8_t)(0xA0 + i));
    2767         [ -  + ]:          8 :                 if (mbufs[i] == NULL) {
    2768         [ #  # ]:          0 :                         while (i > 0)
    2769                 :          0 :                                 rte_pktmbuf_free(mbufs[--i]);
    2770                 :          0 :                         cleanup_pcap_vdev("net_pcap_trunc_ms", port_id);
    2771                 :            :                         remove_temp_file(tx_path);
    2772                 :          0 :                         return TEST_FAILED;
    2773                 :            :                 }
    2774                 :            :         }
    2775                 :            : 
    2776                 :          1 :         printf("  Sending %u packets: pkt_len=%u, seg_size=%u (%u segs), snaplen=%u\n",
    2777                 :          1 :                num_pkts, pkt_size, seg_size, mbufs[0]->nb_segs, test_snaplen);
    2778                 :            : 
    2779                 :            :         /* Verify mbufs are actually multi-segment */
    2780         [ -  + ]:          1 :         TEST_ASSERT(mbufs[0]->nb_segs > 1,
    2781                 :            :                     "Expected multi-segment mbufs, got %u segment(s)",
    2782                 :            :                     mbufs[0]->nb_segs);
    2783                 :            : 
    2784                 :            :         /* Transmit packets */
    2785                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, num_pkts);
    2786                 :            : 
    2787                 :            :         /* Free any unsent mbufs */
    2788         [ -  + ]:          1 :         for (i = nb_tx; i < num_pkts; i++)
    2789                 :          0 :                 rte_pktmbuf_free(mbufs[i]);
    2790                 :            : 
    2791         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(nb_tx, (int)num_pkts,
    2792                 :            :                           "TX burst failed: sent %d/%u", nb_tx, num_pkts);
    2793                 :            : 
    2794                 :          1 :         cleanup_pcap_vdev("net_pcap_trunc_ms", port_id);
    2795                 :            : 
    2796                 :            :         /* Verify truncation in output file */
    2797                 :          1 :         ret = verify_pcap_truncation(tx_path, test_snaplen, pkt_size,
    2798                 :            :                                      &pkt_count);
    2799         [ -  + ]:          1 :         TEST_ASSERT_SUCCESS(ret, "Truncation verification failed");
    2800         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(pkt_count, num_pkts,
    2801                 :            :                           "Packet count mismatch: got %u, expected %u",
    2802                 :            :                           pkt_count, num_pkts);
    2803                 :            : 
    2804                 :            :         printf("  Verified %u packets: caplen=%u, len=%u\n",
    2805                 :            :                pkt_count, test_snaplen, pkt_size);
    2806                 :            : 
    2807                 :            :         remove_temp_file(tx_path);
    2808                 :            : 
    2809                 :            :         printf("Snaplen truncation multi-segment test PASSED\n");
    2810                 :          1 :         return TEST_SUCCESS;
    2811                 :            : }
    2812                 :            : 
    2813                 :            : 
    2814                 :            : /*
    2815                 :            :  * Test: Timestamps in infinite RX mode
    2816                 :            :  *
    2817                 :            :  * This test verifies that timestamp offload works correctly when combined
    2818                 :            :  * with infinite_rx mode. Since infinite_rx generates packets on-the-fly,
    2819                 :            :  * timestamps should reflect the current time rather than pcap file timestamps.
    2820                 :            :  */
    2821                 :            : static int
    2822                 :          1 : test_timestamp_infinite_rx(void)
    2823                 :            : {
    2824                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    2825                 :          1 :         struct rte_eth_conf port_conf = {
    2826                 :            :                 .rxmode.offloads = RTE_ETH_RX_OFFLOAD_TIMESTAMP,
    2827                 :            :         };
    2828                 :            :         char ts_inf_pcap_path[PATH_MAX];
    2829                 :            :         char devargs[256];
    2830                 :            :         uint16_t port_id;
    2831                 :            :         unsigned int total_rx = 0;
    2832                 :            :         unsigned int ts_count = 0;
    2833                 :            :         int iter, attempts, ret;
    2834                 :            :         rte_mbuf_timestamp_t first_ts = 0;
    2835                 :            :         rte_mbuf_timestamp_t last_ts = 0;
    2836                 :            : 
    2837                 :            :         printf("Testing timestamps with infinite RX mode\n");
    2838                 :            : 
    2839                 :            :         /* Initialize timestamp dynamic field access */
    2840         [ -  + ]:          1 :         if (timestamp_dynfield_offset < 0) {
    2841                 :          0 :                 ret = timestamp_init();
    2842         [ #  # ]:          0 :                 if (ret != 0) {
    2843                 :            :                         printf("Timestamp dynfield not available, skipping\n");
    2844                 :          0 :                         return TEST_SKIPPED;
    2845                 :            :                 }
    2846                 :            :         }
    2847                 :            : 
    2848                 :            :         /* Create simple pcap file */
    2849         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(ts_inf_pcap_path, sizeof(ts_inf_pcap_path),
    2850                 :            :                                      "pcap_ts_inf") == 0,
    2851                 :            :                     "Failed to create temp file path");
    2852                 :            : 
    2853         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(ts_inf_pcap_path, NUM_PACKETS) == 0,
    2854                 :            :                     "Failed to create test pcap file");
    2855                 :            : 
    2856                 :            :         /* Create vdev with infinite_rx enabled */
    2857                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s,infinite_rx=1", ts_inf_pcap_path);
    2858         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2859                 :            :                     "devargs string truncated");
    2860                 :          1 :         ret = rte_vdev_init("net_pcap_ts_inf", devargs);
    2861         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to create infinite RX vdev: %s", rte_strerror(-ret));
    2862                 :            : 
    2863                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_ts_inf", &port_id);
    2864         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    2865                 :            : 
    2866                 :            :         /* Configure with timestamp offload enabled and start */
    2867         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port_conf(port_id, &port_conf) == 0,
    2868                 :            :                     "Failed to setup port with timestamps");
    2869                 :            : 
    2870                 :            :         /* Read packets */
    2871         [ +  + ]:          2 :         for (iter = 0; iter < 3 && total_rx < NUM_PACKETS * 2; iter++) {
    2872         [ +  + ]:          5 :                 for (attempts = 0; attempts < 100 && total_rx < NUM_PACKETS * 2; attempts++) {
    2873                 :          4 :                         uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, mbufs, MAX_PKT_BURST);
    2874                 :            : 
    2875         [ +  + ]:        132 :                         for (uint16_t i = 0; i < nb_rx; i++) {
    2876         [ +  - ]:        128 :                                 if (mbuf_has_timestamp(mbufs[i])) {
    2877                 :            :                                         rte_mbuf_timestamp_t ts = mbuf_timestamp_get(mbufs[i]);
    2878                 :            : 
    2879         [ +  + ]:        128 :                                         if (ts_count == 0)
    2880                 :            :                                                 first_ts = ts;
    2881                 :            :                                         last_ts = ts;
    2882                 :        128 :                                         ts_count++;
    2883                 :            :                                 }
    2884                 :            :                         }
    2885                 :            : 
    2886         [ +  - ]:          4 :                         if (nb_rx > 0)
    2887                 :          4 :                                 rte_pktmbuf_free_bulk(mbufs, nb_rx);
    2888                 :          4 :                         total_rx += nb_rx;
    2889                 :            : 
    2890         [ -  + ]:          4 :                         if (nb_rx == 0)
    2891                 :          0 :                                 rte_delay_us_sleep(100);
    2892                 :            :                 }
    2893                 :            :         }
    2894                 :            : 
    2895                 :          1 :         rte_eth_dev_stop(port_id);
    2896                 :          1 :         rte_vdev_uninit("net_pcap_ts_inf");
    2897                 :            :         remove_temp_file(ts_inf_pcap_path);
    2898                 :            : 
    2899         [ -  + ]:          1 :         TEST_ASSERT(total_rx >= NUM_PACKETS * 2,
    2900                 :            :                     "Infinite RX: got %u packets, need >= %d", total_rx, NUM_PACKETS * 2);
    2901                 :            : 
    2902         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(ts_count, total_rx,
    2903                 :            :                           "Timestamp missing: only %u/%u packets have timestamps",
    2904                 :            :                           ts_count, total_rx);
    2905                 :            : 
    2906                 :            :         /* Timestamps should be monotonically increasing (current time) */
    2907         [ -  + ]:          1 :         TEST_ASSERT(last_ts >= first_ts,
    2908                 :            :                     "Timestamps not monotonic: first=%" PRIu64 " last=%" PRIu64,
    2909                 :            :                     first_ts, last_ts);
    2910                 :            : 
    2911                 :            :         printf("Timestamp infinite RX PASSED: %u packets with valid timestamps\n", total_rx);
    2912                 :          1 :         return TEST_SUCCESS;
    2913                 :            : }
    2914                 :            : 
    2915                 :            : /*
    2916                 :            :  * Test suite setup
    2917                 :            :  */
    2918                 :            : static int
    2919                 :          1 : test_setup(void)
    2920                 :            : {
    2921                 :            :         /* Generate random source MAC address */
    2922                 :          1 :         rte_eth_random_addr(src_mac.addr_bytes);
    2923                 :            : 
    2924                 :          1 :         mp = rte_pktmbuf_pool_create("pcap_test_pool", NB_MBUF, 32, 0,
    2925                 :            :                                      RTE_MBUF_DEFAULT_BUF_SIZE,
    2926                 :          1 :                                      rte_socket_id());
    2927         [ -  + ]:          1 :         TEST_ASSERT_NOT_NULL(mp, "Failed to create mempool");
    2928                 :            : 
    2929                 :            :         return 0;
    2930                 :            : }
    2931                 :            : 
    2932                 :            : 
    2933                 :            : /*
    2934                 :            :  * Test: Oversized packets are dropped when scatter is disabled
    2935                 :            :  *
    2936                 :            :  * Use the default mempool (buf_size ~2048) without scatter enabled.
    2937                 :            :  * Read a pcap file containing packets larger than the mbuf data room.
    2938                 :            :  * Verify that oversized packets are dropped and counted as errors.
    2939                 :            :  */
    2940                 :            : static int
    2941                 :          1 : test_scatter_drop_oversized(void)
    2942                 :            : {
    2943                 :            :         struct rte_eth_conf port_conf;
    2944                 :            :         struct rte_eth_stats stats;
    2945                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    2946                 :            :         char rx_pcap_path[PATH_MAX];
    2947                 :            :         char devargs[256];
    2948                 :            :         uint16_t port_id;
    2949                 :            :         unsigned int received;
    2950                 :            :         int ret;
    2951                 :            :         const unsigned int num_pkts = 16;
    2952                 :            : 
    2953                 :            :         printf("Testing scatter: oversized packets dropped without scatter\n");
    2954                 :            : 
    2955         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_pcap_path, sizeof(rx_pcap_path),
    2956                 :            :                                      "pcap_scat_drop") == 0,
    2957                 :            :                     "Failed to create temp file path");
    2958                 :            : 
    2959                 :            :         /*
    2960                 :            :          * Create pcap with jumbo packets (9000 bytes) that exceed the
    2961                 :            :          * default mbuf data room (~2048 bytes). Without scatter enabled,
    2962                 :            :          * these should be dropped at receive time.
    2963                 :            :          */
    2964         [ -  + ]:          1 :         TEST_ASSERT(create_sized_pcap(rx_pcap_path, num_pkts,
    2965                 :            :                                       PKT_SIZE_JUMBO) == 0,
    2966                 :            :                     "Failed to create jumbo pcap file");
    2967                 :            : 
    2968                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", rx_pcap_path);
    2969         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    2970                 :            :                     "devargs string truncated");
    2971                 :          1 :         ret = rte_vdev_init("net_pcap_scat_drop", devargs);
    2972         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to create vdev: %s", rte_strerror(-ret));
    2973                 :            : 
    2974                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_scat_drop", &port_id);
    2975         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    2976                 :            : 
    2977                 :            :         /* Configure without scatter - MTU check passes (1514 < 2048) */
    2978                 :            :         memset(&port_conf, 0, sizeof(port_conf));
    2979                 :          1 :         ret = rte_eth_dev_configure(port_id, 1, 1, &port_conf);
    2980         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to configure port: %s", rte_strerror(-ret));
    2981                 :            : 
    2982                 :          1 :         ret = rte_eth_rx_queue_setup(port_id, 0, RING_SIZE, SOCKET0, NULL, mp);
    2983         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "rx_queue_setup failed: %s", rte_strerror(-ret));
    2984                 :            : 
    2985                 :          1 :         ret = rte_eth_tx_queue_setup(port_id, 0, RING_SIZE, SOCKET0, NULL);
    2986         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "tx_queue_setup failed: %s", rte_strerror(-ret));
    2987                 :            : 
    2988                 :          1 :         ret = rte_eth_dev_start(port_id);
    2989         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to start port: %s", rte_strerror(-ret));
    2990                 :            : 
    2991                 :          1 :         ret = rte_eth_stats_reset(port_id);
    2992         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to reset stats");
    2993                 :            : 
    2994                 :            :         /*
    2995                 :            :          * Read all packets. The pcap file has 9000-byte packets but
    2996                 :            :          * scatter is not enabled. They should all be dropped.
    2997                 :            :          */
    2998                 :          1 :         receive_packets(port_id, mbufs, num_pkts, &received);
    2999                 :          1 :         rte_pktmbuf_free_bulk(mbufs, received);
    3000                 :            : 
    3001                 :          1 :         ret = rte_eth_stats_get(port_id, &stats);
    3002         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get stats");
    3003                 :            : 
    3004                 :          1 :         printf("  Received %u packets, errors=%" PRIu64 "\n",
    3005                 :            :                received, stats.ierrors);
    3006                 :            : 
    3007         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(received, 0U,
    3008                 :            :                           "Expected 0 received packets without scatter, got %u",
    3009                 :            :                           received);
    3010         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(stats.ierrors, (uint64_t)num_pkts,
    3011                 :            :                           "Expected %u errors for oversized packets, got %" PRIu64,
    3012                 :            :                           num_pkts, stats.ierrors);
    3013                 :            : 
    3014                 :          1 :         cleanup_pcap_vdev("net_pcap_scat_drop", port_id);
    3015                 :            :         remove_temp_file(rx_pcap_path);
    3016                 :            : 
    3017                 :          1 :         printf("Scatter drop oversized PASSED: %" PRIu64 " packets dropped\n",
    3018                 :            :                stats.ierrors);
    3019                 :          1 :         return TEST_SUCCESS;
    3020                 :            : }
    3021                 :            : 
    3022                 :            : /*
    3023                 :            :  * Test: Jumbo packets are scattered when scatter is enabled
    3024                 :            :  *
    3025                 :            :  * With scatter enabled and a normal mempool, read jumbo-sized packets
    3026                 :            :  * from a pcap file. Verify they arrive as multi-segment mbufs with
    3027                 :            :  * correct total length.
    3028                 :            :  */
    3029                 :            : static int
    3030                 :          1 : test_scatter_jumbo_rx(void)
    3031                 :            : {
    3032                 :            :         struct rte_mbuf *mbufs[NUM_PACKETS];
    3033                 :            :         struct rte_eth_conf port_conf;
    3034                 :            :         char rx_pcap_path[PATH_MAX];
    3035                 :            :         char devargs[256];
    3036                 :            :         uint16_t port_id;
    3037                 :            :         unsigned int received, i;
    3038                 :            :         unsigned int multiseg_count = 0;
    3039                 :            :         int ret;
    3040                 :            :         const unsigned int num_pkts = 16;
    3041                 :            : 
    3042                 :            :         printf("Testing scatter: jumbo RX with scatter enabled\n");
    3043                 :            : 
    3044         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_pcap_path, sizeof(rx_pcap_path),
    3045                 :            :                                      "pcap_scat_jumbo") == 0,
    3046                 :            :                     "Failed to create temp file path");
    3047         [ -  + ]:          1 :         TEST_ASSERT(create_sized_pcap(rx_pcap_path, num_pkts,
    3048                 :            :                                       PKT_SIZE_JUMBO) == 0,
    3049                 :            :                     "Failed to create jumbo pcap file");
    3050                 :            : 
    3051                 :            :         ret = snprintf(devargs, sizeof(devargs), "rx_pcap=%s", rx_pcap_path);
    3052         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    3053                 :            :                     "devargs string truncated");
    3054                 :          1 :         ret = rte_vdev_init("net_pcap_scat_jumbo", devargs);
    3055         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to create vdev: %s", rte_strerror(-ret));
    3056                 :            : 
    3057                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_scat_jumbo", &port_id);
    3058         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    3059                 :            : 
    3060                 :            :         /* Configure WITH scatter enabled */
    3061                 :            :         memset(&port_conf, 0, sizeof(port_conf));
    3062                 :          1 :         port_conf.rxmode.offloads = RTE_ETH_RX_OFFLOAD_SCATTER;
    3063                 :          1 :         ret = rte_eth_dev_configure(port_id, 1, 1, &port_conf);
    3064         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to configure port: %s", rte_strerror(-ret));
    3065                 :            : 
    3066                 :          1 :         ret = rte_eth_rx_queue_setup(port_id, 0, RING_SIZE, SOCKET0, NULL, mp);
    3067         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "rx_queue_setup failed: %s", rte_strerror(-ret));
    3068                 :            : 
    3069                 :          1 :         ret = rte_eth_tx_queue_setup(port_id, 0, RING_SIZE, SOCKET0, NULL);
    3070         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "tx_queue_setup failed: %s", rte_strerror(-ret));
    3071                 :            : 
    3072                 :          1 :         ret = rte_eth_dev_start(port_id);
    3073         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to start port: %s", rte_strerror(-ret));
    3074                 :            : 
    3075                 :          1 :         receive_packets(port_id, mbufs, num_pkts, &received);
    3076         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(received, num_pkts,
    3077                 :            :                           "Received %u packets, expected %u", received, num_pkts);
    3078                 :            : 
    3079         [ +  + ]:         17 :         for (i = 0; i < received; i++) {
    3080                 :         16 :                 uint32_t pkt_len = rte_pktmbuf_pkt_len(mbufs[i]);
    3081                 :         16 :                 uint16_t nb_segs = mbufs[i]->nb_segs;
    3082                 :            : 
    3083         [ -  + ]:         16 :                 TEST_ASSERT_EQUAL(pkt_len, PKT_SIZE_JUMBO,
    3084                 :            :                                   "Packet %u: size %u, expected %u",
    3085                 :            :                                   i, pkt_len, PKT_SIZE_JUMBO);
    3086                 :            : 
    3087         [ +  - ]:         16 :                 if (nb_segs > 1)
    3088                 :         16 :                         multiseg_count++;
    3089                 :            :         }
    3090                 :            : 
    3091                 :          1 :         rte_pktmbuf_free_bulk(mbufs, received);
    3092                 :          1 :         cleanup_pcap_vdev("net_pcap_scat_jumbo", port_id);
    3093                 :            :         remove_temp_file(rx_pcap_path);
    3094                 :            : 
    3095                 :            :         /* Jumbo packets should require multiple segments */
    3096         [ -  + ]:          1 :         TEST_ASSERT(multiseg_count > 0,
    3097                 :            :                     "Expected multi-segment mbufs for jumbo packets");
    3098                 :            : 
    3099                 :            :         printf("Scatter jumbo RX PASSED: %u/%u packets were multi-segment\n",
    3100                 :            :                multiseg_count, received);
    3101                 :          1 :         return TEST_SUCCESS;
    3102                 :            : }
    3103                 :            : 
    3104                 :            : /*
    3105                 :            :  * Test: Asymmetric rx_iface/tx_iface mode
    3106                 :            :  *
    3107                 :            :  * Verifies that the rx_iface= and tx_iface= devargs work when
    3108                 :            :  * specified separately, which exercises a distinct code path from
    3109                 :            :  * the symmetric iface= mode.
    3110                 :            :  */
    3111                 :            : static int
    3112                 :          1 : test_rx_tx_iface(void)
    3113                 :            : {
    3114                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    3115                 :            :         char devargs[256];
    3116                 :            :         uint16_t port_id;
    3117                 :            :         const char *iface;
    3118                 :            :         int ret, nb_tx, nb_pkt;
    3119                 :            : 
    3120                 :            :         printf("Testing asymmetric rx_iface/tx_iface mode\n");
    3121                 :            : 
    3122                 :          1 :         iface = find_test_iface();
    3123         [ -  + ]:          1 :         if (iface == NULL) {
    3124                 :            :                 printf("No suitable interface, skipping\n");
    3125                 :          0 :                 return TEST_SKIPPED;
    3126                 :            :         }
    3127                 :            :         printf("Using interface: %s\n", iface);
    3128                 :            : 
    3129                 :            :         ret = snprintf(devargs, sizeof(devargs),
    3130                 :            :                        "rx_iface=%s,tx_iface=%s", iface, iface);
    3131         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    3132                 :            :                     "devargs string truncated");
    3133         [ -  + ]:          1 :         if (rte_vdev_init("net_pcap_rxtx_iface", devargs) < 0) {
    3134                 :            :                 printf("Cannot create rx_iface/tx_iface vdev (needs root?), skipping\n");
    3135                 :          0 :                 return TEST_SKIPPED;
    3136                 :            :         }
    3137                 :            : 
    3138                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_rxtx_iface", &port_id);
    3139         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    3140         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    3141                 :            :                     "Failed to setup port");
    3142                 :            : 
    3143                 :            :         /* Transmit some packets to verify TX path works */
    3144                 :          1 :         nb_pkt = generate_test_packets(mp, mbufs, MAX_PKT_BURST,
    3145                 :            :                                        PACKET_BURST_GEN_PKT_LEN);
    3146         [ -  + ]:          1 :         TEST_ASSERT(nb_pkt > 0, "Failed to generate packets");
    3147                 :            : 
    3148                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, nb_pkt);
    3149         [ -  + ]:          1 :         if (nb_tx < nb_pkt)
    3150                 :          0 :                 rte_pktmbuf_free_bulk(&mbufs[nb_tx], nb_pkt - nb_tx);
    3151                 :            : 
    3152                 :            :         /* RX burst to exercise the receive path (may or may not get packets) */
    3153                 :            :         {
    3154                 :          1 :                 uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, mbufs, MAX_PKT_BURST);
    3155         [ +  - ]:          1 :                 if (nb_rx > 0)
    3156                 :          1 :                         rte_pktmbuf_free_bulk(mbufs, nb_rx);
    3157                 :            :         }
    3158                 :            : 
    3159                 :          1 :         cleanup_pcap_vdev("net_pcap_rxtx_iface", port_id);
    3160                 :            : 
    3161                 :            :         printf("rx_iface/tx_iface PASSED: sent %d packets\n", nb_tx);
    3162                 :          1 :         return TEST_SUCCESS;
    3163                 :            : }
    3164                 :            : 
    3165                 :            : /*
    3166                 :            :  * Test: rx_iface_in direction filtering
    3167                 :            :  *
    3168                 :            :  * Verifies that rx_iface_in= sets pcap direction to PCAP_D_IN,
    3169                 :            :  * which filters out outgoing packets. This exercises the
    3170                 :            :  * set_iface_direction() code path and PCAP_D_IN filtering.
    3171                 :            :  */
    3172                 :            : static int
    3173                 :          1 : test_rx_iface_in(void)
    3174                 :            : {
    3175                 :            :         struct rte_mbuf *mbufs[MAX_PKT_BURST];
    3176                 :            :         char devargs[256];
    3177                 :            :         uint16_t port_id;
    3178                 :            :         const char *iface;
    3179                 :            :         int ret, nb_pkt, nb_tx;
    3180                 :            : 
    3181                 :            :         printf("Testing rx_iface_in direction filtering\n");
    3182                 :            : 
    3183                 :          1 :         iface = find_test_iface();
    3184         [ -  + ]:          1 :         if (iface == NULL) {
    3185                 :            :                 printf("No suitable interface, skipping\n");
    3186                 :          0 :                 return TEST_SKIPPED;
    3187                 :            :         }
    3188                 :            :         printf("Using interface: %s\n", iface);
    3189                 :            : 
    3190                 :            :         ret = snprintf(devargs, sizeof(devargs),
    3191                 :            :                        "rx_iface_in=%s,tx_iface=%s", iface, iface);
    3192         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    3193                 :            :                     "devargs string truncated");
    3194         [ -  + ]:          1 :         if (rte_vdev_init("net_pcap_iface_in", devargs) < 0) {
    3195                 :            :                 printf("Cannot create rx_iface_in vdev (needs root?), skipping\n");
    3196                 :          0 :                 return TEST_SKIPPED;
    3197                 :            :         }
    3198                 :            : 
    3199                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_iface_in", &port_id);
    3200         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    3201         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    3202                 :            :                     "Failed to setup port");
    3203                 :            : 
    3204                 :            :         /*
    3205                 :            :          * Send packets on the TX side. With rx_iface_in (PCAP_D_IN),
    3206                 :            :          * our own transmitted packets should NOT appear on the RX side
    3207                 :            :          * because they are outgoing, not incoming.
    3208                 :            :          */
    3209                 :          1 :         nb_pkt = generate_test_packets(mp, mbufs, MAX_PKT_BURST,
    3210                 :            :                                        PACKET_BURST_GEN_PKT_LEN);
    3211         [ -  + ]:          1 :         TEST_ASSERT(nb_pkt > 0, "Failed to generate packets");
    3212                 :            : 
    3213                 :          1 :         nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, nb_pkt);
    3214         [ -  + ]:          1 :         if (nb_tx < nb_pkt)
    3215                 :          0 :                 rte_pktmbuf_free_bulk(&mbufs[nb_tx], nb_pkt - nb_tx);
    3216                 :            : 
    3217                 :            :         /* Small delay then try to receive - should get 0 (our own TX filtered) */
    3218                 :          1 :         rte_delay_us_sleep(50 * 1000);
    3219                 :            :         {
    3220                 :          1 :                 uint16_t nb_rx = rte_eth_rx_burst(port_id, 0, mbufs, MAX_PKT_BURST);
    3221         [ +  - ]:          1 :                 if (nb_rx > 0) {
    3222                 :          1 :                         printf("  Note: received %u packets (external traffic present)\n",
    3223                 :            :                                nb_rx);
    3224                 :          1 :                         rte_pktmbuf_free_bulk(mbufs, nb_rx);
    3225                 :            :                 } else {
    3226                 :            :                         printf("  No packets received (TX correctly filtered by PCAP_D_IN)\n");
    3227                 :            :                 }
    3228                 :            :         }
    3229                 :            : 
    3230                 :          1 :         cleanup_pcap_vdev("net_pcap_iface_in", port_id);
    3231                 :            : 
    3232                 :            :         printf("rx_iface_in PASSED: direction filtering exercised\n");
    3233                 :          1 :         return TEST_SUCCESS;
    3234                 :            : }
    3235                 :            : 
    3236                 :            : /*
    3237                 :            :  * Test: Per-queue start and stop
    3238                 :            :  *
    3239                 :            :  * Verifies that rx_queue_start/stop and tx_queue_start/stop API calls
    3240                 :            :  * succeed and return correct status. Note: the pcap PMD burst functions
    3241                 :            :  * do not check queue state in the fast path, so this test validates
    3242                 :            :  * the API plumbing rather than burst-level gating.
    3243                 :            :  */
    3244                 :            : static int
    3245                 :          1 : test_queue_start_stop(void)
    3246                 :            : {
    3247                 :            :         char rx_pcap_path[PATH_MAX];
    3248                 :            :         char tx_pcap_path_local[PATH_MAX];
    3249                 :            :         char devargs[256];
    3250                 :            :         uint16_t port_id;
    3251                 :            :         int ret;
    3252                 :            : 
    3253                 :            :         printf("Testing per-queue start/stop\n");
    3254                 :            : 
    3255         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(rx_pcap_path, sizeof(rx_pcap_path),
    3256                 :            :                                      "pcap_qss_rx") == 0,
    3257                 :            :                     "Failed to create RX temp path");
    3258         [ -  + ]:          1 :         TEST_ASSERT(create_temp_path(tx_pcap_path_local, sizeof(tx_pcap_path_local),
    3259                 :            :                                      "pcap_qss_tx") == 0,
    3260                 :            :                     "Failed to create TX temp path");
    3261         [ -  + ]:          1 :         TEST_ASSERT(create_test_pcap(rx_pcap_path, NUM_PACKETS) == 0,
    3262                 :            :                     "Failed to create test pcap");
    3263                 :            : 
    3264                 :            :         ret = snprintf(devargs, sizeof(devargs),
    3265                 :            :                        "rx_pcap=%s,tx_pcap=%s", rx_pcap_path, tx_pcap_path_local);
    3266         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    3267                 :            :                     "devargs string truncated");
    3268         [ -  + ]:          1 :         TEST_ASSERT(create_pcap_vdev("net_pcap_qss", devargs, &port_id) == 0,
    3269                 :            :                     "Failed to create vdev");
    3270         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    3271                 :            :                     "Failed to setup port");
    3272                 :            : 
    3273                 :            :         /* Stop RX queue */
    3274                 :          1 :         ret = rte_eth_dev_rx_queue_stop(port_id, 0);
    3275         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to stop RX queue: %s", rte_strerror(-ret));
    3276                 :            : 
    3277                 :            :         /* Restart RX queue */
    3278                 :          1 :         ret = rte_eth_dev_rx_queue_start(port_id, 0);
    3279         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to start RX queue: %s", rte_strerror(-ret));
    3280                 :            : 
    3281                 :            :         /* Stop TX queue */
    3282                 :          1 :         ret = rte_eth_dev_tx_queue_stop(port_id, 0);
    3283         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to stop TX queue: %s", rte_strerror(-ret));
    3284                 :            : 
    3285                 :            :         /* Restart TX queue */
    3286                 :          1 :         ret = rte_eth_dev_tx_queue_start(port_id, 0);
    3287         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to start TX queue: %s", rte_strerror(-ret));
    3288                 :            : 
    3289                 :            :         /* Verify burst still works after stop/start cycle */
    3290                 :            :         {
    3291                 :            :                 struct rte_mbuf *mbufs[NUM_PACKETS];
    3292                 :            :                 unsigned int received;
    3293                 :            : 
    3294                 :          1 :                 receive_packets(port_id, mbufs, NUM_PACKETS, &received);
    3295         [ -  + ]:          1 :                 TEST_ASSERT(received > 0,
    3296                 :            :                             "Expected packets after queue stop/start cycle, got 0");
    3297                 :          1 :                 rte_pktmbuf_free_bulk(mbufs, received);
    3298                 :            :         }
    3299                 :            : 
    3300                 :            :         {
    3301                 :            :                 struct rte_mbuf *mbufs[1];
    3302                 :            : 
    3303         [ -  + ]:          1 :                 TEST_ASSERT(alloc_test_mbufs(mbufs, 1) == 0,
    3304                 :            :                             "Failed to allocate mbufs");
    3305                 :          1 :                 int nb_tx = rte_eth_tx_burst(port_id, 0, mbufs, 1);
    3306         [ -  + ]:          1 :                 TEST_ASSERT_EQUAL(nb_tx, 1,
    3307                 :            :                                   "TX after queue stop/start cycle failed: sent %d/1",
    3308                 :            :                                   nb_tx);
    3309                 :            :         }
    3310                 :            : 
    3311                 :          1 :         cleanup_pcap_vdev("net_pcap_qss", port_id);
    3312                 :            :         remove_temp_file(rx_pcap_path);
    3313                 :            :         remove_temp_file(tx_pcap_path_local);
    3314                 :            : 
    3315                 :            :         printf("Per-queue start/stop PASSED\n");
    3316                 :          1 :         return TEST_SUCCESS;
    3317                 :            : }
    3318                 :            : 
    3319                 :            : /*
    3320                 :            :  * Test: imissed statistic in interface mode
    3321                 :            :  *
    3322                 :            :  * Verifies that the imissed counter (based on pcap_stats kernel drops)
    3323                 :            :  * is accessible and starts at zero after stats reset. Kernel-level
    3324                 :            :  * drops are hard to trigger deterministically, so this test validates
    3325                 :            :  * the counter plumbing rather than forcing drops.
    3326                 :            :  */
    3327                 :            : static int
    3328                 :          1 : test_imissed_stat(void)
    3329                 :            : {
    3330                 :            :         struct rte_eth_stats stats;
    3331                 :            :         char devargs[256];
    3332                 :            :         uint16_t port_id;
    3333                 :            :         const char *iface;
    3334                 :            :         int ret;
    3335                 :            : 
    3336                 :            :         printf("Testing imissed statistic\n");
    3337                 :            : 
    3338                 :          1 :         iface = find_test_iface();
    3339         [ -  + ]:          1 :         if (iface == NULL) {
    3340                 :            :                 printf("No suitable interface, skipping\n");
    3341                 :          0 :                 return TEST_SKIPPED;
    3342                 :            :         }
    3343                 :            :         printf("Using interface: %s\n", iface);
    3344                 :            : 
    3345                 :            :         ret = snprintf(devargs, sizeof(devargs), "iface=%s", iface);
    3346         [ -  + ]:          1 :         TEST_ASSERT(ret > 0 && ret < (int)sizeof(devargs),
    3347                 :            :                     "devargs string truncated");
    3348         [ -  + ]:          1 :         if (rte_vdev_init("net_pcap_imissed", devargs) < 0) {
    3349                 :            :                 printf("Cannot create iface vdev (needs root?), skipping\n");
    3350                 :          0 :                 return TEST_SKIPPED;
    3351                 :            :         }
    3352                 :            : 
    3353                 :          1 :         ret = rte_eth_dev_get_port_by_name("net_pcap_imissed", &port_id);
    3354         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get port ID");
    3355         [ -  + ]:          1 :         TEST_ASSERT(setup_pcap_port(port_id) == 0,
    3356                 :            :                     "Failed to setup port");
    3357                 :            : 
    3358                 :            :         /* Reset stats and verify imissed starts at zero */
    3359                 :          1 :         ret = rte_eth_stats_reset(port_id);
    3360         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to reset stats");
    3361                 :            : 
    3362                 :          1 :         ret = rte_eth_stats_get(port_id, &stats);
    3363         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get stats");
    3364                 :            : 
    3365         [ -  + ]:          1 :         TEST_ASSERT_EQUAL(stats.imissed, 0U,
    3366                 :            :                           "imissed should be 0 after reset, got %"PRIu64,
    3367                 :            :                           stats.imissed);
    3368                 :            : 
    3369                 :            :         /* Do some RX bursts to exercise the pcap_stats path */
    3370                 :            :         {
    3371                 :            :                 struct rte_mbuf *mbufs[MAX_PKT_BURST];
    3372                 :            :                 int attempts;
    3373                 :            : 
    3374         [ +  + ]:          6 :                 for (attempts = 0; attempts < 5; attempts++) {
    3375                 :          5 :                         uint16_t nb_rx = rte_eth_rx_burst(port_id, 0,
    3376                 :            :                                                           mbufs, MAX_PKT_BURST);
    3377         [ -  + ]:          5 :                         if (nb_rx > 0)
    3378                 :          0 :                                 rte_pktmbuf_free_bulk(mbufs, nb_rx);
    3379                 :            :                 }
    3380                 :            :         }
    3381                 :            : 
    3382                 :            :         /* Query stats again - imissed should still be queryable */
    3383                 :          1 :         ret = rte_eth_stats_get(port_id, &stats);
    3384         [ -  + ]:          1 :         TEST_ASSERT(ret == 0, "Failed to get stats after RX");
    3385                 :          1 :         printf("  imissed=%"PRIu64" (kernel drops via pcap_stats)\n",
    3386                 :            :                stats.imissed);
    3387                 :            : 
    3388                 :          1 :         cleanup_pcap_vdev("net_pcap_imissed", port_id);
    3389                 :            : 
    3390                 :            :         printf("imissed stat PASSED\n");
    3391                 :          1 :         return TEST_SUCCESS;
    3392                 :            : }
    3393                 :            : 
    3394                 :            : /*
    3395                 :            :  * Test suite teardown
    3396                 :            :  */
    3397                 :            : static void
    3398         [ +  - ]:          1 : test_teardown(void)
    3399                 :            : {
    3400                 :            :         /* Cleanup shared temp files */
    3401                 :            :         remove_temp_file(tx_pcap_path);
    3402                 :            : 
    3403                 :          1 :         rte_mempool_free(mp);
    3404                 :          1 :         mp = NULL;
    3405                 :          1 : }
    3406                 :            : 
    3407                 :            : static struct unit_test_suite test_pmd_pcap_suite = {
    3408                 :            :         .setup = test_setup,
    3409                 :            :         .teardown = test_teardown,
    3410                 :            :         .suite_name = "PCAP PMD Unit Test Suite",
    3411                 :            :         .unit_test_cases = {
    3412                 :            :                 TEST_CASE(test_dev_info),
    3413                 :            :                 TEST_CASE(test_tx_to_file),
    3414                 :            :                 TEST_CASE(test_rx_from_file),
    3415                 :            :                 TEST_CASE(test_tx_varied_sizes),
    3416                 :            :                 TEST_CASE(test_rx_varied_sizes),
    3417                 :            :                 TEST_CASE(test_jumbo_rx),
    3418                 :            :                 TEST_CASE(test_jumbo_tx),
    3419                 :            :                 TEST_CASE(test_infinite_rx),
    3420                 :            :                 TEST_CASE(test_tx_drop),
    3421                 :            :                 TEST_CASE(test_stats),
    3422                 :            :                 TEST_CASE(test_iface),
    3423                 :            :                 TEST_CASE(test_link_status),
    3424                 :            :                 TEST_CASE(test_lsc_iface),
    3425                 :            :                 TEST_CASE(test_eof_rx),
    3426                 :            :                 TEST_CASE(test_rx_timestamp),
    3427                 :            :                 TEST_CASE(test_multi_tx_queue),
    3428                 :            :                 TEST_CASE(test_multi_rx_queue_same_file),
    3429                 :            :                 TEST_CASE(test_timestamp_infinite_rx),
    3430                 :            :                 TEST_CASE(test_snaplen),
    3431                 :            :                 TEST_CASE(test_snaplen_truncation),
    3432                 :            :                 TEST_CASE(test_snaplen_truncation_multiseg),
    3433                 :            :                 TEST_CASE(test_scatter_drop_oversized),
    3434                 :            :                 TEST_CASE(test_scatter_jumbo_rx),
    3435                 :            :                 TEST_CASE(test_rx_tx_iface),
    3436                 :            :                 TEST_CASE(test_rx_iface_in),
    3437                 :            :                 TEST_CASE(test_queue_start_stop),
    3438                 :            :                 TEST_CASE(test_imissed_stat),
    3439                 :            : 
    3440                 :            :                 TEST_CASES_END()
    3441                 :            :         }
    3442                 :            : };
    3443                 :            : 
    3444                 :            : static int
    3445                 :          1 : test_pmd_pcap(void)
    3446                 :            : {
    3447                 :          1 :         return unit_test_suite_runner(&test_pmd_pcap_suite);
    3448                 :            : }
    3449                 :            : 
    3450                 :        286 : REGISTER_FAST_TEST(pcap_pmd_autotest, NOHUGE_OK, ASAN_OK, test_pmd_pcap);

Generated by: LCOV version 1.14