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, ð_hdr->src_addr);
263 : : rte_ether_addr_copy(&dst_mac, ð_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, ð_hdr->src_addr);
364 : : rte_ether_addr_copy(&dst_mac, ð_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(ð_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, ð_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(ð_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, ð_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);
|