Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (c) 2021 Microsoft Corporation
3 : : */
4 : :
5 : : #include <stdio.h>
6 : : #include <stdlib.h>
7 : : #include <string.h>
8 : : #include <time.h>
9 : :
10 : : #ifdef RTE_EXEC_ENV_WINDOWS
11 : : #include <winsock2.h>
12 : : #include <io.h>
13 : : #include <fcntl.h>
14 : : #include <windows.h>
15 : : #else
16 : : #include <unistd.h>
17 : : #endif
18 : :
19 : : #include <rte_bus_vdev.h>
20 : : #include <rte_ethdev.h>
21 : : #include <rte_ether.h>
22 : : #include <rte_ip.h>
23 : : #include <rte_mbuf.h>
24 : : #include <rte_mempool.h>
25 : : #include <rte_net.h>
26 : : #include <rte_pcapng.h>
27 : : #include <rte_random.h>
28 : : #include <rte_reciprocal.h>
29 : : #include <rte_time.h>
30 : : #include <rte_udp.h>
31 : :
32 : : #include <pcap/pcap.h>
33 : :
34 : : #include "test.h"
35 : :
36 : : #ifdef RTE_EXEC_ENV_WINDOWS
37 : : static uint64_t
38 : : current_timestamp(void)
39 : : {
40 : : FILETIME ft;
41 : : ULARGE_INTEGER ul;
42 : :
43 : : GetSystemTimeAsFileTime(&ft);
44 : : ul.LowPart = ft.dwLowDateTime;
45 : : ul.HighPart = ft.dwHighDateTime;
46 : : /* FILETIME is 100ns intervals since 1601-01-01, convert to ns since Unix epoch */
47 : : return (ul.QuadPart - 116444736000000000ULL) * 100;
48 : : }
49 : :
50 : : /*
51 : : * Create temporary file with suffix for Windows.
52 : : * Returns file descriptor or -1 on failure.
53 : : */
54 : : static int
55 : : mkstemps(char *tmpl, int suffixlen)
56 : : {
57 : : char temp_dir[MAX_PATH];
58 : : char temp_file[MAX_PATH];
59 : : DWORD ret;
60 : :
61 : : ret = GetTempPathA(sizeof(temp_dir), temp_dir);
62 : : if (ret == 0 || ret > sizeof(temp_dir))
63 : : return -1;
64 : :
65 : : if (GetTempFileNameA(temp_dir, "pcap", 0, temp_file) == 0)
66 : : return -1;
67 : :
68 : : /*
69 : : * GetTempFileNameA with uUnique=0 creates the file to reserve the name.
70 : : * Remove it since we open a different name with the original suffix appended.
71 : : */
72 : : DeleteFileA(temp_file);
73 : :
74 : : /* Append the original suffix (e.g. ".pcapng") to the temp file */
75 : : strlcat(temp_file, tmpl + strlen(tmpl) - suffixlen, sizeof(temp_file));
76 : : strlcpy(tmpl, temp_file, PATH_MAX);
77 : :
78 : : return _open(tmpl, _O_RDWR | _O_BINARY | _O_CREAT | _O_EXCL, 0666);
79 : : }
80 : : #endif /* RTE_EXEC_ENV_WINDOWS */
81 : :
82 : : #define PCAPNG_TEST_DEBUG 0
83 : :
84 : : /*
85 : : * Want to write enough packets to exercise timestamp logic.
86 : : * On fast CPU's TSC wraps around 32 bits in 4 seconds.
87 : : */
88 : : #define TOTAL_PACKETS 10000
89 : : #define MAX_BURST 32
90 : : #define NUM_BURSTS (TOTAL_PACKETS / MAX_BURST)
91 : : #define TEST_TIME_SEC 4
92 : : #define GAP_US ((TEST_TIME_SEC * US_PER_S) / NUM_BURSTS)
93 : :
94 : : #define DUMMY_MBUF_NUM 2
95 : :
96 : : static struct rte_mempool *mp;
97 : : static uint16_t port_id;
98 : : static const char null_dev[] = "net_null0";
99 : :
100 : : /* first mbuf in the packet, should always be at offset 0 */
101 : : struct dummy_mbuf {
102 : : struct rte_mbuf mb[DUMMY_MBUF_NUM];
103 : : uint8_t buf[DUMMY_MBUF_NUM][RTE_MBUF_DEFAULT_BUF_SIZE];
104 : : };
105 : :
106 : : #define MAX_DATA_SIZE (RTE_MBUF_DEFAULT_BUF_SIZE - RTE_PKTMBUF_HEADROOM)
107 : :
108 : : /* RFC 864 chargen pattern used for comment testing */
109 : : #define FILL_LINE_LENGTH 72
110 : : #define FILL_START 0x21 /* ! */
111 : : #define FILL_END 0x7e /* ~ */
112 : : #define FILL_RANGE (FILL_END - FILL_START)
113 : :
114 : : static void
115 : 2 : fill_mbuf(struct rte_mbuf *mb)
116 : : {
117 : 2 : unsigned int len = rte_pktmbuf_tailroom(mb);
118 : : char *buf = rte_pktmbuf_append(mb, len);
119 : : unsigned int n = 0;
120 : : unsigned int line = 0;
121 : :
122 [ + - ]: 2 : if (len == 0)
123 : : return;
124 : :
125 [ + + ]: 60 : while (n < len - 1) {
126 : 58 : char ch = FILL_START + (line % FILL_RANGE);
127 : : unsigned int i;
128 : :
129 [ + + + + ]: 4096 : for (i = 0; i < FILL_LINE_LENGTH && n < len - 1; i++) {
130 : 4038 : buf[n++] = ch;
131 [ + + ]: 4038 : if (++ch > FILL_END)
132 : : ch = FILL_START;
133 : : }
134 [ + + ]: 58 : if (n < len - 1)
135 : 56 : buf[n++] = '\n';
136 : 58 : line++;
137 : : }
138 : : }
139 : :
140 : : static void
141 : 4 : dummy_mbuf_prep(struct rte_mbuf *mb, uint8_t buf[], uint32_t buf_len)
142 : : {
143 : 4 : mb->buf_addr = buf;
144 : 4 : rte_mbuf_iova_set(mb, (uintptr_t)buf);
145 : 4 : mb->buf_len = buf_len;
146 : : rte_mbuf_refcnt_set(mb, 1);
147 : :
148 : : /* set pool pointer to dummy value, test doesn't use it */
149 : 4 : mb->pool = (void *)buf;
150 : :
151 : : rte_pktmbuf_reset(mb);
152 : 4 : }
153 : :
154 : : /* Make an IP packet consisting of chain of one packets */
155 : : static void
156 : 2 : mbuf1_prepare(struct dummy_mbuf *dm)
157 : : {
158 : : struct {
159 : : struct rte_ether_hdr eth;
160 : : struct rte_ipv4_hdr ip;
161 : : struct rte_udp_hdr udp;
162 : 2 : } pkt = {
163 : : .eth = {
164 : : .dst_addr.addr_bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
165 : : .ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4),
166 : : },
167 : : .ip = {
168 : : .version_ihl = RTE_IPV4_VHL_DEF,
169 : : .time_to_live = 1,
170 : : .next_proto_id = IPPROTO_UDP,
171 : : .src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK),
172 : : .dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST),
173 : : },
174 : : .udp = {
175 : : .src_port = rte_cpu_to_be_16(19), /* Chargen port */
176 : : .dst_port = rte_cpu_to_be_16(9), /* Discard port */
177 : : },
178 : : };
179 : :
180 : : memset(dm, 0, sizeof(*dm));
181 : 2 : dummy_mbuf_prep(&dm->mb[0], dm->buf[0], sizeof(dm->buf[0]));
182 : 2 : dummy_mbuf_prep(&dm->mb[1], dm->buf[1], sizeof(dm->buf[1]));
183 : :
184 : 2 : rte_eth_random_addr(pkt.eth.src_addr.addr_bytes);
185 : : memcpy(rte_pktmbuf_append(&dm->mb[0], sizeof(pkt)), &pkt, sizeof(pkt));
186 : :
187 : 2 : fill_mbuf(&dm->mb[1]);
188 : : rte_pktmbuf_chain(&dm->mb[0], &dm->mb[1]);
189 : :
190 : 2 : rte_mbuf_sanity_check(&dm->mb[0], 1);
191 : 2 : rte_mbuf_sanity_check(&dm->mb[1], 0);
192 : 2 : }
193 : :
194 : : static void
195 : 1785 : mbuf1_resize(struct dummy_mbuf *dm, uint16_t len)
196 : : {
197 : : struct {
198 : : struct rte_ether_hdr eth;
199 : : struct rte_ipv4_hdr ip;
200 : : struct rte_udp_hdr udp;
201 : 1785 : } *pkt = rte_pktmbuf_mtod(&dm->mb[0], void *);
202 : :
203 : 1785 : dm->mb[1].data_len = len;
204 : 1785 : dm->mb[0].pkt_len = dm->mb[0].data_len + dm->mb[1].data_len;
205 : :
206 : 1785 : len += sizeof(struct rte_udp_hdr);
207 [ - + ]: 1785 : pkt->udp.dgram_len = rte_cpu_to_be_16(len);
208 : :
209 : 1785 : len += sizeof(struct rte_ipv4_hdr);
210 [ - + ]: 1785 : pkt->ip.total_length = rte_cpu_to_be_16(len);
211 : 1785 : pkt->ip.hdr_checksum = 0;
212 : 1785 : pkt->ip.hdr_checksum = rte_ipv4_cksum(&pkt->ip);
213 : :
214 : 1785 : rte_mbuf_sanity_check(&dm->mb[0], 1);
215 : 1785 : rte_mbuf_sanity_check(&dm->mb[1], 0);
216 : 1785 : }
217 : :
218 : : static int
219 : 1 : test_setup(void)
220 : : {
221 : 1 : port_id = rte_eth_dev_count_avail();
222 : :
223 : : /* Make a dummy null device to snoop on */
224 [ - + ]: 1 : if (rte_vdev_init(null_dev, NULL) != 0) {
225 : : printf("Failed to create vdev '%s'\n", null_dev);
226 : 0 : goto fail;
227 : : }
228 : :
229 : : /* Make a pool for cloned packets */
230 : 1 : mp = rte_pktmbuf_pool_create_by_ops("pcapng_test_pool",
231 : : MAX_BURST * 32, 0, 0,
232 : 1 : rte_pcapng_mbuf_size(MAX_DATA_SIZE),
233 : : SOCKET_ID_ANY, "ring_mp_sc");
234 [ - + ]: 1 : if (mp == NULL) {
235 : : printf("Cannot create mempool\n");
236 : 0 : goto fail;
237 : : }
238 : :
239 : : return 0;
240 : :
241 : 0 : fail:
242 : 0 : rte_vdev_uninit(null_dev);
243 : 0 : rte_mempool_free(mp);
244 : 0 : return -1;
245 : : }
246 : :
247 : : static int
248 : 1 : fill_pcapng_file(rte_pcapng_t *pcapng)
249 : : {
250 : : struct dummy_mbuf mbfs;
251 : : struct rte_mbuf *orig;
252 : : unsigned int burst_size;
253 : : unsigned int count;
254 : : struct timespec start_time;
255 : : ssize_t len;
256 : : /*
257 : : * These are some silly comments to test various lengths and alignments sprinkle
258 : : * into the file. You can see these comments by using the dumpcap program on the file
259 : : */
260 : : static const char * const examples[] = {
261 : : "Lockless and fearless - that’s how we roll in userspace.",
262 : : "Memory pool deep / Mbufs swim in lockless rings / Zero copy dreams,",
263 : : "Poll mode driver waits / No interrupts disturb its zen / Busy loop finds peace,",
264 : : "Memory barriers / rte_atomic_thread_fence() / Guards our shared state",
265 : : "Hugepages so vast / Two megabytes of glory / TLB misses weep",
266 : : "Packets flow like streams / Through the graph node pipeline / Iterate in place",
267 : :
268 : : /* Long one to make sure we can do > 256 characters */
269 : : ("Dear future maintainer: I am sorry. This packet was captured at 3 AM while "
270 : : "debugging a priority flow control issue that turned out to be a loose cable. "
271 : : "The rte_eth_tx_burst() call you see here has been cargo-culted through four "
272 : : "generations of example code. The magic number 32 is not documented because "
273 : : "nobody remembers why. Trust the process."),
274 : : };
275 : :
276 : 1 : mbuf1_prepare(&mbfs);
277 : : orig = &mbfs.mb[0];
278 : :
279 : 1 : clock_gettime(CLOCK_MONOTONIC, &start_time);
280 : :
281 [ + - ]: 97 : for (count = 0; count < TOTAL_PACKETS; count += burst_size) {
282 : : struct rte_mbuf *clones[MAX_BURST];
283 : : struct timespec now;
284 : : unsigned int i;
285 : :
286 : : /* break off writing if test is taking too long */
287 : 97 : clock_gettime(CLOCK_MONOTONIC, &now);
288 [ + + ]: 97 : if (now.tv_sec >= start_time.tv_sec + TEST_TIME_SEC)
289 : : break;
290 : :
291 : : /* put 1 .. MAX_BURST packets in one write call */
292 : 96 : burst_size = rte_rand_max(MAX_BURST) + 1;
293 [ + + ]: 1880 : for (i = 0; i < burst_size; i++) {
294 : : struct rte_mbuf *mc;
295 : : const char *comment = NULL;
296 : :
297 : : /* Put randomized comment on every 100th packet (1%) */
298 [ + + ]: 1784 : if (count % 100 == 0)
299 : 28 : comment = examples[rte_rand_max(RTE_DIM(examples))];
300 : :
301 : : /* Vary the size of the packets, okay to allow 0 sized packet */
302 : 1784 : mbuf1_resize(&mbfs, rte_rand_max(MAX_DATA_SIZE));
303 : :
304 : 1784 : mc = rte_pcapng_copy(port_id, 0, orig, mp, rte_pktmbuf_pkt_len(orig),
305 : : RTE_PCAPNG_DIRECTION_IN, comment);
306 [ - + ]: 1784 : if (mc == NULL) {
307 : : printf("Cannot copy packet\n");
308 : 0 : return -1;
309 : : }
310 : 1784 : clones[i] = mc;
311 : : }
312 : :
313 : : /* write it to capture file */
314 : 96 : len = rte_pcapng_write_packets(pcapng, clones, burst_size);
315 : 96 : rte_pktmbuf_free_bulk(clones, burst_size);
316 : :
317 [ - + ]: 96 : if (len <= 0) {
318 : 0 : printf("Write of packets failed: %s\n",
319 : : rte_strerror(rte_errno));
320 : 0 : return -1;
321 : : }
322 : :
323 : 96 : rte_delay_us_block(rte_rand_max(2 * GAP_US));
324 : : }
325 : :
326 : 1 : return count;
327 : : }
328 : :
329 : : /* Convert time in nanoseconds since 1/1/1970 to UTC time string */
330 : : static char *
331 : 0 : fmt_time(char *buf, size_t size, uint64_t ts_ns)
332 : : {
333 : : time_t sec;
334 : : size_t len;
335 : :
336 : 0 : sec = ts_ns / NS_PER_S;
337 : :
338 : 0 : len = strftime(buf, size, "%T", gmtime(&sec));
339 : 0 : snprintf(buf + len, size - len, ".%09lu",
340 : 0 : (unsigned long)(ts_ns % NS_PER_S));
341 : :
342 : 0 : return buf;
343 : : }
344 : :
345 : : /* Context for the pcap_loop callback */
346 : : struct pkt_print_ctx {
347 : : pcap_t *pcap;
348 : : unsigned int count;
349 : : uint64_t start_ns;
350 : : uint64_t end_ns;
351 : : };
352 : :
353 : : static void
354 : 0 : print_packet(uint64_t ts_ns, const struct rte_ether_hdr *eh, size_t len)
355 : : {
356 : : char tbuf[128], src[64], dst[64];
357 : :
358 : 0 : fmt_time(tbuf, sizeof(tbuf), ts_ns);
359 : 0 : rte_ether_format_addr(dst, sizeof(dst), &eh->dst_addr);
360 : 0 : rte_ether_format_addr(src, sizeof(src), &eh->src_addr);
361 : 0 : printf("%s: %s -> %s type %x length %zu\n",
362 [ # # ]: 0 : tbuf, src, dst, rte_be_to_cpu_16(eh->ether_type), len);
363 : 0 : fflush(stdout);
364 : 0 : }
365 : :
366 : : /* Callback from pcap_loop used to validate packets in the file */
367 : : static void
368 : 1792 : parse_pcap_packet(u_char *user, const struct pcap_pkthdr *h,
369 : : const u_char *bytes)
370 : : {
371 : : struct pkt_print_ctx *ctx = (struct pkt_print_ctx *)user;
372 : : const struct rte_ether_hdr *eh;
373 : : const struct rte_ipv4_hdr *ip;
374 : : static unsigned int total_errors;
375 : : uint64_t ns;
376 : :
377 : : eh = (const struct rte_ether_hdr *)bytes;
378 : 1792 : ip = (const struct rte_ipv4_hdr *)(eh + 1);
379 : :
380 : 1792 : ctx->count += 1;
381 : :
382 : : /* The pcap library is misleading in reporting timestamp.
383 : : * packet header struct gives timestamp as a timeval (ie. usec);
384 : : * but the file is open in nanonsecond mode therefore
385 : : * the timestamp is really in timespec (ie. nanoseconds).
386 : : */
387 : 1792 : ns = (uint64_t)h->ts.tv_sec * NS_PER_S + h->ts.tv_usec;
388 [ + - - + ]: 1792 : if (ns < ctx->start_ns || ns > ctx->end_ns) {
389 : : char tstart[128], tend[128];
390 : :
391 : 0 : fmt_time(tstart, sizeof(tstart), ctx->start_ns);
392 : 0 : fmt_time(tend, sizeof(tend), ctx->end_ns);
393 : :
394 : : printf("Timestamp out of range [%s .. %s]\n",
395 : : tstart, tend);
396 : 0 : goto error;
397 : : }
398 : :
399 [ - + ]: 1792 : if (!rte_is_broadcast_ether_addr(&eh->dst_addr)) {
400 : : printf("Destination is not broadcast\n");
401 : 0 : goto error;
402 : : }
403 : :
404 [ - + ]: 1792 : if (rte_ipv4_cksum(ip) != 0) {
405 : : printf("Bad IPv4 checksum\n");
406 : 0 : goto error;
407 : : }
408 : :
409 : : return; /* packet is normal */
410 : :
411 : 0 : error:
412 : 0 : print_packet(ns, eh, h->len);
413 : :
414 : : /* Stop parsing at tenth error */
415 [ # # ]: 0 : if (++total_errors >= 10)
416 : 0 : pcap_breakloop(ctx->pcap);
417 : : }
418 : :
419 : : #ifndef RTE_EXEC_ENV_WINDOWS
420 : : static uint64_t
421 : : current_timestamp(void)
422 : : {
423 : : struct timespec ts;
424 : :
425 : 1 : clock_gettime(CLOCK_REALTIME, &ts);
426 : : return rte_timespec_to_ns(&ts);
427 : : }
428 : : #endif
429 : :
430 : : /*
431 : : * Open the resulting pcapng file with libpcap
432 : : * Would be better to use capinfos from wireshark
433 : : * but that creates an unwanted dependency.
434 : : */
435 : : static int
436 : 3 : valid_pcapng_file(const char *file_name, uint64_t started, unsigned int expected)
437 : : {
438 : : char errbuf[PCAP_ERRBUF_SIZE];
439 : 3 : struct pkt_print_ctx ctx = { };
440 : : int ret;
441 : :
442 : 3 : ctx.start_ns = started;
443 : 3 : ctx.end_ns = current_timestamp();
444 : :
445 : 3 : ctx.pcap = pcap_open_offline_with_tstamp_precision(file_name,
446 : : PCAP_TSTAMP_PRECISION_NANO,
447 : : errbuf);
448 [ - + ]: 3 : if (ctx.pcap == NULL) {
449 : : printf("pcap_open_offline('%s') failed: %s\n",
450 : : file_name, errbuf);
451 : 0 : return -1;
452 : : }
453 : :
454 : 3 : ret = pcap_loop(ctx.pcap, 0, parse_pcap_packet, (u_char *)&ctx);
455 [ - + ]: 3 : if (ret != 0) {
456 : 0 : printf("pcap_dispatch: failed: %s\n",
457 : : pcap_geterr(ctx.pcap));
458 [ - + ]: 3 : } else if (ctx.count != expected) {
459 : : printf("Only %u packets, expected %u\n",
460 : : ctx.count, expected);
461 : : ret = -1;
462 : : }
463 : :
464 : 3 : pcap_close(ctx.pcap);
465 : :
466 : 3 : return ret;
467 : : }
468 : :
469 : : static int
470 : 1 : test_add_interface(void)
471 : : {
472 : 1 : char file_name[PATH_MAX] = "/tmp/pcapng_test_XXXXXX.pcapng";
473 : : static rte_pcapng_t *pcapng;
474 : : int ret, tmp_fd;
475 : : uint64_t now = current_timestamp();
476 : :
477 : 1 : tmp_fd = mkstemps(file_name, strlen(".pcapng"));
478 [ - + ]: 1 : if (tmp_fd == -1) {
479 : 0 : perror("mkstemps() failure");
480 : 0 : goto fail;
481 : : }
482 : : printf("pcapng: output file %s\n", file_name);
483 : :
484 : : /* open a test capture file */
485 : 1 : pcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, "pcapng_addif", NULL);
486 [ - + ]: 1 : if (pcapng == NULL) {
487 : : printf("rte_pcapng_fdopen failed\n");
488 : 0 : close(tmp_fd);
489 : 0 : goto fail;
490 : : }
491 : :
492 : : /* Add interface to the file */
493 : 1 : ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
494 : : NULL, NULL, NULL);
495 [ - + ]: 1 : if (ret < 0) {
496 : 0 : printf("can not add port %u\n", port_id);
497 : 0 : goto fail;
498 : : }
499 : :
500 : : /* Add interface with ifname and ifdescr */
501 : 1 : ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
502 : : "myeth", "Some long description", NULL);
503 [ - + ]: 1 : if (ret < 0) {
504 : 0 : printf("can not add port %u with ifname\n", port_id);
505 : 0 : goto fail;
506 : : }
507 : :
508 : : /* Add interface with filter */
509 : 1 : ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
510 : : NULL, NULL, "tcp port 8080");
511 [ - + ]: 1 : if (ret < 0) {
512 : 0 : printf("can not add port %u with filter\n", port_id);
513 : 0 : goto fail;
514 : : }
515 : :
516 : 1 : rte_pcapng_close(pcapng);
517 : :
518 : 1 : ret = valid_pcapng_file(file_name, now, 0);
519 : : /* if test fails want to investigate the file */
520 [ + - ]: 1 : if (ret == 0)
521 : 1 : remove(file_name);
522 : :
523 : : return ret;
524 : :
525 : 0 : fail:
526 : 0 : rte_pcapng_close(pcapng);
527 : 0 : return -1;
528 : : }
529 : :
530 : : static int
531 : 1 : test_write_packets(void)
532 : : {
533 : 1 : char file_name[PATH_MAX] = "/tmp/pcapng_test_XXXXXX.pcapng";
534 : : rte_pcapng_t *pcapng = NULL;
535 : : int ret, tmp_fd, count;
536 : : uint64_t now = current_timestamp();
537 : :
538 : 1 : tmp_fd = mkstemps(file_name, strlen(".pcapng"));
539 [ - + ]: 1 : if (tmp_fd == -1) {
540 : 0 : perror("mkstemps() failure");
541 : 0 : goto fail;
542 : : }
543 : : printf("pcapng: output file %s\n", file_name);
544 : :
545 : : /* open a test capture file */
546 : 1 : pcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, "pcapng_test", NULL);
547 [ - + ]: 1 : if (pcapng == NULL) {
548 : : printf("rte_pcapng_fdopen failed\n");
549 : 0 : close(tmp_fd);
550 : 0 : goto fail;
551 : : }
552 : :
553 : : /* Add interface to the file */
554 : 1 : ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
555 : : NULL, NULL, NULL);
556 [ - + ]: 1 : if (ret < 0) {
557 : 0 : printf("can not add port %u\n", port_id);
558 : 0 : goto fail;
559 : : }
560 : :
561 : : /* write a statistics block */
562 : 1 : ret = rte_pcapng_write_stats(pcapng, port_id, 0, 0, NULL);
563 [ - + ]: 1 : if (ret <= 0) {
564 : : printf("Write of statistics failed\n");
565 : 0 : goto fail;
566 : : }
567 : :
568 : 1 : count = fill_pcapng_file(pcapng);
569 [ - + ]: 1 : if (count < 0)
570 : 0 : goto fail;
571 : :
572 : : /* write a statistics block */
573 : 1 : ret = rte_pcapng_write_stats(pcapng, port_id,
574 : : count, 0, "end of test");
575 [ - + ]: 1 : if (ret <= 0) {
576 : : printf("Write of statistics failed\n");
577 : 0 : goto fail;
578 : : }
579 : :
580 : 1 : rte_pcapng_close(pcapng);
581 : :
582 : 1 : ret = valid_pcapng_file(file_name, now, count);
583 : : /* if test fails want to investigate the file */
584 [ + - ]: 1 : if (ret == 0)
585 : 1 : remove(file_name);
586 : :
587 : : return ret;
588 : :
589 : 0 : fail:
590 : 0 : rte_pcapng_close(pcapng);
591 : 0 : return -1;
592 : : }
593 : :
594 : : static int
595 : 1 : test_write_before_open(void)
596 : : {
597 : 1 : char file_name[PATH_MAX] = "/tmp/pcapng_test_XXXXXX.pcapng";
598 : : struct dummy_mbuf mbfs;
599 : : struct rte_mbuf *clones[MAX_BURST];
600 : : rte_pcapng_t *pcapng = NULL;
601 : : int ret, tmp_fd, i;
602 : : unsigned int count = 8;
603 : : uint64_t now;
604 : : ssize_t len;
605 : :
606 : 1 : mbuf1_prepare(&mbfs);
607 : 1 : mbuf1_resize(&mbfs, rte_rand_max(MAX_DATA_SIZE));
608 : :
609 : : /* Copy packets BEFORE opening the pcapng file.
610 : : * This exercises the negative TSC delta path in tsc_to_ns_epoch().
611 : : */
612 [ + + ]: 9 : for (i = 0; i < (int)count; i++) {
613 : 8 : clones[i] = rte_pcapng_copy(port_id, 0, &mbfs.mb[0], mp,
614 : : rte_pktmbuf_pkt_len(&mbfs.mb[0]),
615 : : RTE_PCAPNG_DIRECTION_IN, NULL);
616 [ - + ]: 8 : if (clones[i] == NULL) {
617 : 0 : fprintf(stderr, "Cannot copy packet before open\n");
618 : 0 : rte_pktmbuf_free_bulk(clones, i);
619 : 0 : return -1;
620 : : }
621 : : }
622 : :
623 : : /* Small delay so fdopen's tsc_base is measurably after the copies */
624 : 1 : rte_delay_us_block(100);
625 : :
626 : : now = current_timestamp();
627 : :
628 : 1 : tmp_fd = mkstemps(file_name, strlen(".pcapng"));
629 [ - + ]: 1 : if (tmp_fd == -1) {
630 : 0 : perror("mkstemps() failure");
631 : 0 : rte_pktmbuf_free_bulk(clones, count);
632 : 0 : return -1;
633 : : }
634 : :
635 : 1 : pcapng = rte_pcapng_fdopen(tmp_fd, NULL, NULL, "pcapng_preopen", NULL);
636 [ - + ]: 1 : if (pcapng == NULL) {
637 : 0 : fprintf(stderr, "rte_pcapng_fdopen failed\n");
638 : 0 : close(tmp_fd);
639 : 0 : rte_pktmbuf_free_bulk(clones, count);
640 : 0 : return -1;
641 : : }
642 : :
643 : 1 : ret = rte_pcapng_add_interface(pcapng, port_id, DLT_EN10MB,
644 : : NULL, NULL, NULL);
645 [ - + ]: 1 : if (ret < 0) {
646 : 0 : fprintf(stderr, "can not add port %u\n", port_id);
647 : 0 : goto fail;
648 : : }
649 : :
650 : : /* Write the pre-captured packets — timestamps precede tsc_base */
651 : 1 : len = rte_pcapng_write_packets(pcapng, clones, count);
652 : 1 : rte_pktmbuf_free_bulk(clones, count);
653 [ - + ]: 1 : if (len <= 0) {
654 : 0 : fprintf(stderr, "Write of pre-open packets failed: %s\n",
655 : : rte_strerror(rte_errno));
656 : 0 : goto fail;
657 : : }
658 : :
659 : 1 : rte_pcapng_close(pcapng);
660 : :
661 : : /* Validate the file is parseable — timestamps should be
662 : : * slightly before 'now' but still reasonable.
663 : : */
664 : 1 : ret = valid_pcapng_file(file_name, now - NS_PER_S, count);
665 [ + - ]: 1 : if (ret == 0)
666 : 1 : remove(file_name);
667 : :
668 : : return ret;
669 : :
670 : 0 : fail:
671 : 0 : rte_pcapng_close(pcapng);
672 : 0 : return -1;
673 : : }
674 : :
675 : : static void
676 : 1 : test_cleanup(void)
677 : : {
678 : 1 : rte_mempool_free(mp);
679 : 1 : rte_vdev_uninit(null_dev);
680 : 1 : }
681 : :
682 : : static struct
683 : : unit_test_suite test_pcapng_suite = {
684 : : .setup = test_setup,
685 : : .teardown = test_cleanup,
686 : : .suite_name = "Test Pcapng Unit Test Suite",
687 : : .unit_test_cases = {
688 : : TEST_CASE(test_add_interface),
689 : : TEST_CASE(test_write_packets),
690 : : TEST_CASE(test_write_before_open),
691 : : TEST_CASES_END()
692 : : }
693 : : };
694 : :
695 : : static int
696 : 1 : test_pcapng(void)
697 : : {
698 : 1 : return unit_test_suite_runner(&test_pcapng_suite);
699 : : }
700 : :
701 : 276 : REGISTER_FAST_TEST(pcapng_autotest, NOHUGE_OK, ASAN_OK, test_pcapng);
|