Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2019-2020 Microsoft Corporation
3 : : *
4 : : * DPDK application to dump network traffic
5 : : * This is designed to look and act like the Wireshark
6 : : * dumpcap program.
7 : : */
8 : :
9 : : #include <errno.h>
10 : : #include <fcntl.h>
11 : : #include <getopt.h>
12 : : #include <inttypes.h>
13 : : #include <limits.h>
14 : : #include <signal.h>
15 : : #include <stdbool.h>
16 : : #include <stdint.h>
17 : : #include <stdio.h>
18 : : #include <stdlib.h>
19 : : #include <string.h>
20 : : #include <sys/queue.h>
21 : : #include <sys/types.h>
22 : : #include <sys/utsname.h>
23 : : #include <time.h>
24 : : #include <unistd.h>
25 : :
26 : : #include <rte_alarm.h>
27 : : #include <rte_bitops.h>
28 : : #include <rte_bpf.h>
29 : : #include <rte_config.h>
30 : : #include <rte_debug.h>
31 : : #include <rte_eal.h>
32 : : #include <rte_errno.h>
33 : : #include <rte_ethdev.h>
34 : : #include <rte_lcore.h>
35 : : #include <rte_malloc.h>
36 : : #include <rte_mbuf.h>
37 : : #include <rte_mempool.h>
38 : : #include <rte_pcapng.h>
39 : : #include <rte_pdump.h>
40 : : #include <rte_ring.h>
41 : : #include <rte_string_fns.h>
42 : : #include <rte_thread.h>
43 : : #include <rte_time.h>
44 : : #include <rte_version.h>
45 : :
46 : : #include <pcap/pcap.h>
47 : : #include <pcap/bpf.h>
48 : :
49 : : #define MONITOR_INTERVAL (500 * 1000)
50 : : #define MBUF_POOL_CACHE_SIZE 32
51 : : #define BURST_SIZE 32
52 : : #define SLEEP_THRESHOLD 1000
53 : :
54 : : /* command line flags */
55 : : static const char *progname;
56 : : static RTE_ATOMIC(bool) quit_signal;
57 : : static bool group_read;
58 : : static bool quiet;
59 : : static bool use_pcapng = true;
60 : : static char *output_name;
61 : : static const char *tmp_dir = "/tmp";
62 : : static unsigned int ring_size = 2048;
63 : : static const char *capture_comment;
64 : : static const char *file_prefix;
65 : : static const char *lcore_arg;
66 : : static bool dump_bpf;
67 : : static bool show_interfaces;
68 : : static bool print_stats;
69 : :
70 : : /* capture limit options */
71 : : static struct {
72 : : time_t duration; /* seconds */
73 : : unsigned long packets; /* number of packets in file */
74 : : size_t size; /* file size (bytes) */
75 : : } stop;
76 : :
77 : : /* Running state */
78 : : static time_t start_time;
79 : : static uint64_t packets_received;
80 : : static size_t file_size;
81 : :
82 : : /* capture options */
83 : : struct capture_options {
84 : : const char *filter;
85 : : uint32_t snap_len;
86 : : bool promisc_mode;
87 : : } capture = {
88 : : .snap_len = RTE_MBUF_DEFAULT_BUF_SIZE,
89 : : .promisc_mode = true,
90 : : };
91 : :
92 : : struct interface {
93 : : TAILQ_ENTRY(interface) next;
94 : : uint16_t port;
95 : : struct capture_options opts;
96 : : struct rte_bpf_prm *bpf_prm;
97 : : char name[RTE_ETH_NAME_MAX_LEN];
98 : :
99 : : const char *ifname;
100 : : const char *ifdescr;
101 : : };
102 : :
103 : : TAILQ_HEAD(interface_list, interface);
104 : : static struct interface_list interfaces = TAILQ_HEAD_INITIALIZER(interfaces);
105 : :
106 : : /* Can do either pcap or pcapng format output */
107 : : typedef union {
108 : : rte_pcapng_t *pcapng;
109 : : pcap_dumper_t *dumper;
110 : : } dumpcap_out_t;
111 : :
112 : 0 : static void usage(void)
113 : : {
114 : 0 : printf("Usage: %s [options] ...\n\n", progname);
115 : : printf("Capture Interface:\n"
116 : : " -i <interface>, --interface <interface>\n"
117 : : " name or port index of interface\n"
118 : : " -f <capture filter> packet filter in libpcap filter syntax\n");
119 : : printf(" --ifname <name> name to use in the capture file\n");
120 : : printf(" --ifdescr <description>\n");
121 : : printf(" description to use in the capture file\n");
122 : : printf(" -s <snaplen>, --snapshot-length <snaplen>\n"
123 : : " packet snapshot length (def: %u)\n",
124 : : RTE_MBUF_DEFAULT_BUF_SIZE);
125 : : printf(" -p, --no-promiscuous-mode\n"
126 : : " don't capture in promiscuous mode\n"
127 : : " -D, --list-interfaces print list of interfaces and exit\n"
128 : : " -d print generated BPF code for capture filter\n"
129 : : " -S print statistics for each interface once per second\n"
130 : : "\n"
131 : : "Stop conditions:\n"
132 : : " -c <packet count> stop after n packets (def: infinite)\n"
133 : : " -a <autostop cond.> ..., --autostop <autostop cond.> ...\n"
134 : : " duration:NUM - stop after NUM seconds\n"
135 : : " filesize:NUM - stop this file after NUM kB\n"
136 : : " packets:NUM - stop after NUM packets\n"
137 : : "Output (files):\n"
138 : : " -w <filename> name of file to save (def: tempfile)\n"
139 : : " -g enable group read access on the output file(s)\n"
140 : : " -n use pcapng format instead of pcap (default)\n"
141 : : " -P use libpcap format instead of pcapng\n"
142 : : " --capture-comment <comment>\n"
143 : : " add a capture comment to the output file\n"
144 : : " --temp-dir <directory> write temporary files to this directory\n"
145 : : " (default: /tmp)\n"
146 : : "\n"
147 : : "Miscellaneous:\n"
148 : : " --lcore=<core> CPU core to run on (default: any)\n"
149 : : " --file-prefix=<prefix> prefix to use for multi-process\n"
150 : : " -q don't report packet capture counts\n"
151 : : " -v, --version print version information and exit\n"
152 : : " -h, --help display this help and exit\n"
153 : : "\n"
154 : : "Use Ctrl-C to stop capturing at any time.\n");
155 : 0 : }
156 : :
157 : 0 : static const char *version(void)
158 : : {
159 : : static char str[128];
160 : :
161 : 0 : snprintf(str, sizeof(str),
162 : : "%s 1.0 (%s)\n", progname, rte_version());
163 : 0 : return str;
164 : : }
165 : :
166 : : /* Parse numeric argument from command line */
167 : 0 : static unsigned long get_uint(const char *arg, const char *name,
168 : : unsigned int limit)
169 : : {
170 : : unsigned long u;
171 : : char *endp;
172 : :
173 : 0 : u = strtoul(arg, &endp, 0);
174 : 0 : if (*arg == '\0' || *endp != '\0')
175 : 0 : rte_exit(EXIT_FAILURE,
176 : : "Specified %s \"%s\" is not a valid number\n",
177 : : name, arg);
178 : 0 : if (limit && u > limit)
179 : 0 : rte_exit(EXIT_FAILURE,
180 : : "Specified %s \"%s\" is too large (greater than %u)\n",
181 : : name, arg, limit);
182 : :
183 : 0 : return u;
184 : : }
185 : :
186 : : /* Set auto stop values */
187 : 0 : static void auto_stop(char *opt)
188 : : {
189 : : char *value, *endp;
190 : :
191 : 0 : value = strchr(opt, ':');
192 : 0 : if (value == NULL)
193 : 0 : rte_exit(EXIT_FAILURE,
194 : : "Missing colon in auto stop parameter\n");
195 : :
196 : 0 : *value++ = '\0';
197 : 0 : if (strcmp(opt, "duration") == 0) {
198 : 0 : double interval = strtod(value, &endp);
199 : :
200 : 0 : if (*value == '\0' || *endp != '\0' || interval <= 0)
201 : 0 : rte_exit(EXIT_FAILURE,
202 : : "Invalid duration \"%s\"\n", value);
203 : 0 : stop.duration = interval;
204 : 0 : } else if (strcmp(opt, "filesize") == 0) {
205 : 0 : stop.size = get_uint(value, "filesize", 0) * 1024;
206 : 0 : } else if (strcmp(opt, "packets") == 0) {
207 : 0 : stop.packets = get_uint(value, "packets", 0);
208 : : } else {
209 : 0 : rte_exit(EXIT_FAILURE,
210 : : "Unknown autostop parameter \"%s\"\n", opt);
211 : : }
212 : 0 : }
213 : :
214 : : /* Add interface to list of interfaces to capture */
215 : 0 : static struct interface *add_interface(const char *name)
216 : : {
217 : : struct interface *intf;
218 : :
219 : 0 : if (strlen(name) >= RTE_ETH_NAME_MAX_LEN)
220 : 0 : rte_exit(EXIT_FAILURE, "invalid name for interface: '%s'\n", name);
221 : :
222 : 0 : intf = malloc(sizeof(*intf));
223 : 0 : if (!intf)
224 : 0 : rte_exit(EXIT_FAILURE, "no memory for interface\n");
225 : :
226 : : memset(intf, 0, sizeof(*intf));
227 : 0 : rte_strscpy(intf->name, name, sizeof(intf->name));
228 : 0 : intf->opts = capture;
229 : 0 : intf->port = -1; /* port set later after EAL init */
230 : :
231 : 0 : TAILQ_INSERT_TAIL(&interfaces, intf, next);
232 : 0 : return intf;
233 : : }
234 : :
235 : : /* Name has been set but need to lookup port after eal_init */
236 : 0 : static void find_interfaces(void)
237 : : {
238 : : struct interface *intf;
239 : :
240 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
241 : : /* if name is valid then just record port */
242 : 0 : if (rte_eth_dev_get_port_by_name(intf->name, &intf->port) == 0)
243 : 0 : continue;
244 : :
245 : : /* maybe got passed port number string as name */
246 : 0 : intf->port = get_uint(intf->name, "port_number", UINT16_MAX);
247 : 0 : if (rte_eth_dev_get_name_by_port(intf->port, intf->name) < 0)
248 : 0 : rte_exit(EXIT_FAILURE, "Invalid port number %u\n",
249 : 0 : intf->port);
250 : : }
251 : 0 : }
252 : :
253 : : /*
254 : : * Choose interface to capture if no -i option given.
255 : : * Select the first DPDK port, this matches what dumpcap does.
256 : : */
257 : 0 : static void set_default_interface(void)
258 : : {
259 : : struct interface *intf;
260 : : char name[RTE_ETH_NAME_MAX_LEN];
261 : : uint16_t p;
262 : :
263 : 0 : RTE_ETH_FOREACH_DEV(p) {
264 : 0 : if (rte_eth_dev_get_name_by_port(p, name) < 0)
265 : : continue;
266 : :
267 : 0 : intf = add_interface(name);
268 : 0 : intf->port = p;
269 : 0 : return;
270 : : }
271 : 0 : rte_exit(EXIT_FAILURE, "No usable interfaces found\n");
272 : : }
273 : :
274 : : /* Display list of possible interfaces that can be used. */
275 : 0 : static void dump_interfaces(void)
276 : : {
277 : : char name[RTE_ETH_NAME_MAX_LEN];
278 : : uint16_t p;
279 : :
280 : 0 : RTE_ETH_FOREACH_DEV(p) {
281 : 0 : if (rte_eth_dev_get_name_by_port(p, name) < 0)
282 : 0 : continue;
283 : : printf("%u. %s\n", p, name);
284 : : }
285 : :
286 : 0 : exit(0);
287 : : }
288 : :
289 : 0 : static void compile_filters(void)
290 : : {
291 : : struct interface *intf;
292 : :
293 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
294 : : struct rte_bpf_prm *bpf_prm;
295 : : struct bpf_program bf;
296 : : pcap_t *pcap;
297 : :
298 : 0 : pcap = pcap_open_dead(DLT_EN10MB, intf->opts.snap_len);
299 : 0 : if (!pcap)
300 : 0 : rte_exit(EXIT_FAILURE, "can not open pcap\n");
301 : :
302 : 0 : if (pcap_compile(pcap, &bf, intf->opts.filter,
303 : : 1, PCAP_NETMASK_UNKNOWN) != 0) {
304 : 0 : fprintf(stderr,
305 : : "Invalid capture filter \"%s\": for interface '%s'\n",
306 : 0 : intf->opts.filter, intf->name);
307 : 0 : rte_exit(EXIT_FAILURE, "\n%s\n",
308 : : pcap_geterr(pcap));
309 : : }
310 : :
311 : 0 : bpf_prm = rte_bpf_convert(&bf);
312 : 0 : if (bpf_prm == NULL)
313 : 0 : rte_exit(EXIT_FAILURE,
314 : : "BPF convert interface '%s'\n%s(%d)\n",
315 : 0 : intf->name,
316 : : rte_strerror(rte_errno), rte_errno);
317 : :
318 : 0 : if (dump_bpf) {
319 : 0 : printf("cBPF program (%u insns)\n", bf.bf_len);
320 : 0 : bpf_dump(&bf, 1);
321 : 0 : printf("\neBPF program (%u insns)\n",
322 : : bpf_prm->nb_ins);
323 : 0 : rte_bpf_dump(stdout, bpf_prm->ins, bpf_prm->nb_ins);
324 : 0 : exit(0);
325 : : }
326 : :
327 : 0 : intf->bpf_prm = bpf_prm;
328 : :
329 : : /* Don't care about original program any more */
330 : 0 : pcap_freecode(&bf);
331 : 0 : pcap_close(pcap);
332 : : }
333 : 0 : }
334 : :
335 : : /*
336 : : * Parse command line options.
337 : : * These are chosen to be similar to dumpcap command.
338 : : */
339 : 0 : static void parse_opts(int argc, char **argv)
340 : : {
341 : : static const struct option long_options[] = {
342 : : { "autostop", required_argument, NULL, 'a' },
343 : : { "capture-comment", required_argument, NULL, 0 },
344 : : { "file-prefix", required_argument, NULL, 0 },
345 : : { "help", no_argument, NULL, 'h' },
346 : : { "ifdescr", required_argument, NULL, 0 },
347 : : { "ifname", required_argument, NULL, 0 },
348 : : { "interface", required_argument, NULL, 'i' },
349 : : { "lcore", required_argument, NULL, 0 },
350 : : { "list-interfaces", no_argument, NULL, 'D' },
351 : : { "no-promiscuous-mode", no_argument, NULL, 'p' },
352 : : { "output-file", required_argument, NULL, 'w' },
353 : : { "ring-buffer", required_argument, NULL, 'b' },
354 : : { "snapshot-length", required_argument, NULL, 's' },
355 : : { "temp-dir", required_argument, NULL, 0 },
356 : : { "version", no_argument, NULL, 'v' },
357 : : { NULL },
358 : : };
359 : : int option_index, c;
360 : : struct interface *last_intf = NULL;
361 : : uint32_t len;
362 : :
363 : : for (;;) {
364 : 0 : c = getopt_long(argc, argv, "a:b:c:dDf:ghi:nN:pPqSs:vw:",
365 : : long_options, &option_index);
366 : 0 : if (c == -1)
367 : : break;
368 : :
369 : 0 : switch (c) {
370 : 0 : case 0: {
371 : 0 : const char *longopt
372 : 0 : = long_options[option_index].name;
373 : :
374 : 0 : if (!strcmp(longopt, "capture-comment")) {
375 : 0 : capture_comment = optarg;
376 : 0 : } else if (!strcmp(longopt, "lcore")) {
377 : 0 : lcore_arg = optarg;
378 : 0 : } else if (!strcmp(longopt, "file-prefix")) {
379 : 0 : file_prefix = optarg;
380 : 0 : } else if (!strcmp(longopt, "temp-dir")) {
381 : 0 : tmp_dir = optarg;
382 : 0 : } else if (!strcmp(longopt, "ifdescr")) {
383 : 0 : if (last_intf == NULL)
384 : 0 : rte_exit(EXIT_FAILURE,
385 : : "--ifdescr must be specified after a -i option\n");
386 : 0 : last_intf->ifdescr = optarg;
387 : 0 : } else if (!strcmp(longopt, "ifname")) {
388 : 0 : if (last_intf == NULL)
389 : 0 : rte_exit(EXIT_FAILURE,
390 : : "--ifname must be specified after a -i option\n");
391 : 0 : last_intf->ifname = optarg;
392 : : } else {
393 : 0 : usage();
394 : 0 : exit(1);
395 : : }
396 : : break;
397 : : }
398 : 0 : case 'a':
399 : 0 : auto_stop(optarg);
400 : 0 : break;
401 : 0 : case 'b':
402 : 0 : rte_exit(EXIT_FAILURE,
403 : : "multiple files not implemented\n");
404 : : break;
405 : 0 : case 'c':
406 : 0 : stop.packets = get_uint(optarg, "packet_count", 0);
407 : 0 : break;
408 : 0 : case 'd':
409 : 0 : dump_bpf = true;
410 : 0 : break;
411 : 0 : case 'D':
412 : 0 : show_interfaces = true;
413 : 0 : break;
414 : 0 : case 'f':
415 : 0 : if (last_intf == NULL)
416 : 0 : capture.filter = optarg;
417 : : else
418 : 0 : last_intf->opts.filter = optarg;
419 : : break;
420 : 0 : case 'g':
421 : 0 : group_read = true;
422 : 0 : break;
423 : 0 : case 'h':
424 : 0 : printf("%s\n\n", version());
425 : 0 : usage();
426 : 0 : exit(0);
427 : 0 : case 'i':
428 : 0 : last_intf = add_interface(optarg);
429 : 0 : break;
430 : 0 : case 'n':
431 : 0 : use_pcapng = true;
432 : 0 : break;
433 : 0 : case 'N':
434 : 0 : ring_size = get_uint(optarg, "packet_limit", 0);
435 : 0 : break;
436 : 0 : case 'p':
437 : : /* Like dumpcap this option can occur multiple times.
438 : : *
439 : : * If used before the first occurrence of the -i option,
440 : : * no interface will be put into the promiscuous mode.
441 : : * If used after an -i option, the interface specified
442 : : * by the last -i option occurring before this option
443 : : * will not be put into the promiscuous mode.
444 : : */
445 : 0 : if (last_intf == NULL)
446 : 0 : capture.promisc_mode = false;
447 : : else
448 : 0 : last_intf->opts.promisc_mode = false;
449 : : break;
450 : 0 : case 'P':
451 : 0 : use_pcapng = false;
452 : 0 : break;
453 : 0 : case 'q':
454 : 0 : quiet = true;
455 : 0 : break;
456 : 0 : case 's':
457 : 0 : len = get_uint(optarg, "snap_len", 0);
458 : 0 : if (last_intf == NULL)
459 : 0 : capture.snap_len = len;
460 : : else
461 : 0 : last_intf->opts.snap_len = len;
462 : : break;
463 : 0 : case 'S':
464 : 0 : print_stats = true;
465 : 0 : break;
466 : 0 : case 'w':
467 : 0 : output_name = optarg;
468 : 0 : break;
469 : 0 : case 'v':
470 : 0 : printf("%s\n", version());
471 : 0 : exit(0);
472 : 0 : default:
473 : 0 : fprintf(stderr, "Invalid option: %s\n",
474 : 0 : argv[optind - 1]);
475 : 0 : usage();
476 : 0 : exit(1);
477 : : }
478 : : }
479 : 0 : }
480 : :
481 : : static void
482 : 0 : signal_handler(int sig_num __rte_unused)
483 : : {
484 : 0 : rte_atomic_store_explicit(&quit_signal, true, rte_memory_order_relaxed);
485 : 0 : }
486 : :
487 : :
488 : : /* Instead of capturing, it tracks interface statistics */
489 : 0 : static void statistics_loop(void)
490 : : {
491 : : struct rte_eth_stats stats;
492 : : char name[RTE_ETH_NAME_MAX_LEN];
493 : : uint16_t p;
494 : : int r;
495 : :
496 : : printf("%-15s %10s %10s\n",
497 : : "Interface", "Received", "Dropped");
498 : :
499 : 0 : while (!rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed)) {
500 : 0 : RTE_ETH_FOREACH_DEV(p) {
501 : 0 : if (rte_eth_dev_get_name_by_port(p, name) < 0)
502 : 0 : continue;
503 : :
504 : 0 : r = rte_eth_stats_get(p, &stats);
505 : 0 : if (r < 0) {
506 : 0 : fprintf(stderr,
507 : : "stats_get for port %u failed: %d (%s)\n",
508 : : p, r, strerror(-r));
509 : 0 : return;
510 : : }
511 : :
512 : 0 : printf("%-15s %10"PRIu64" %10"PRIu64"\n",
513 : : name, stats.ipackets,
514 : 0 : stats.imissed + stats.ierrors + stats.rx_nombuf);
515 : : }
516 : 0 : sleep(1);
517 : : }
518 : : }
519 : :
520 : : static void
521 : 0 : cleanup_pdump_resources(void)
522 : : {
523 : : struct interface *intf;
524 : :
525 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
526 : 0 : rte_pdump_disable(intf->port,
527 : : RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);
528 : 0 : if (intf->opts.promisc_mode)
529 : 0 : rte_eth_promiscuous_disable(intf->port);
530 : : }
531 : :
532 : 0 : rte_pdump_uninit();
533 : 0 : }
534 : :
535 : : /* Alarm signal handler, used to check that primary process */
536 : : static void
537 : 0 : monitor_primary(void *arg __rte_unused)
538 : : {
539 : 0 : if (rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed))
540 : : return;
541 : :
542 : 0 : if (rte_eal_primary_proc_alive(NULL)) {
543 : 0 : rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);
544 : : } else {
545 : 0 : fprintf(stderr,
546 : : "Primary process is no longer active, exiting...\n");
547 : 0 : rte_atomic_store_explicit(&quit_signal, true, rte_memory_order_relaxed);
548 : : }
549 : : }
550 : :
551 : : /* Setup handler to check when primary exits. */
552 : : static void
553 : 0 : enable_primary_monitor(void)
554 : : {
555 : : int ret;
556 : :
557 : : /* Once primary exits, so will pdump. */
558 : 0 : ret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);
559 : 0 : if (ret < 0)
560 : 0 : fprintf(stderr, "Fail to enable monitor:%d\n", ret);
561 : 0 : }
562 : :
563 : : static void
564 : 0 : disable_primary_monitor(void)
565 : : {
566 : : int ret;
567 : :
568 : 0 : ret = rte_eal_alarm_cancel(monitor_primary, NULL);
569 : 0 : if (ret < 0)
570 : 0 : fprintf(stderr, "Fail to disable monitor:%d\n", ret);
571 : 0 : }
572 : :
573 : : static void
574 : 0 : report_packet_stats(dumpcap_out_t out)
575 : : {
576 : : struct rte_pdump_stats pdump_stats;
577 : : struct interface *intf;
578 : : uint64_t ifrecv, ifdrop;
579 : : double percent;
580 : :
581 : 0 : fputc('\n', stderr);
582 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
583 : 0 : if (rte_pdump_stats(intf->port, &pdump_stats) < 0)
584 : 0 : continue;
585 : :
586 : : /* do what Wiretap does */
587 : 0 : ifrecv = pdump_stats.accepted + pdump_stats.filtered;
588 : 0 : ifdrop = pdump_stats.nombuf + pdump_stats.ringfull;
589 : :
590 : 0 : if (use_pcapng)
591 : 0 : rte_pcapng_write_stats(out.pcapng, intf->port,
592 : : ifrecv, ifdrop, NULL);
593 : :
594 : 0 : if (ifrecv == 0)
595 : : percent = 0;
596 : : else
597 : 0 : percent = 100. * ifrecv / (ifrecv + ifdrop);
598 : :
599 : 0 : fprintf(stderr,
600 : : "Packets received/dropped on interface '%s': "
601 : : "%"PRIu64 "/%" PRIu64 " (%.1f)\n",
602 : 0 : intf->name, ifrecv, ifdrop, percent);
603 : : }
604 : 0 : }
605 : :
606 : : /*
607 : : * Start DPDK EAL with arguments.
608 : : * Unlike most DPDK programs, this application does not use the
609 : : * typical EAL command line arguments.
610 : : * We don't want to expose all the DPDK internals to the user.
611 : : */
612 : 0 : static void dpdk_init(void)
613 : : {
614 : : static const char * const args[] = {
615 : : "dumpcap", "--proc-type", "secondary",
616 : : "--log-level", "notice"
617 : : };
618 : : int eal_argc = RTE_DIM(args);
619 : 0 : rte_cpuset_t cpuset = { };
620 : : char **eal_argv;
621 : : unsigned int i;
622 : :
623 : 0 : if (file_prefix != NULL)
624 : : eal_argc += 2;
625 : :
626 : 0 : if (lcore_arg != NULL)
627 : 0 : eal_argc += 2;
628 : :
629 : : /* DPDK API requires mutable versions of command line arguments. */
630 : 0 : eal_argv = calloc(eal_argc + 1, sizeof(char *));
631 : 0 : if (eal_argv == NULL)
632 : 0 : rte_panic("No memory\n");
633 : :
634 : 0 : eal_argv[0] = strdup(progname);
635 : 0 : for (i = 1; i < RTE_DIM(args); i++)
636 : 0 : eal_argv[i] = strdup(args[i]);
637 : :
638 : 0 : if (lcore_arg != NULL) {
639 : 0 : eal_argv[i++] = strdup("--lcores");
640 : 0 : eal_argv[i++] = strdup(lcore_arg);
641 : : }
642 : :
643 : 0 : if (file_prefix != NULL) {
644 : 0 : eal_argv[i++] = strdup("--file-prefix");
645 : 0 : eal_argv[i++] = strdup(file_prefix);
646 : : }
647 : :
648 : 0 : for (i = 0; i < (unsigned int)eal_argc; i++) {
649 : 0 : if (eal_argv[i] == NULL)
650 : 0 : rte_panic("No memory\n");
651 : : }
652 : :
653 : : /*
654 : : * Need to get the original cpuset, before EAL init changes
655 : : * the affinity of this thread (main lcore).
656 : : */
657 : 0 : if (lcore_arg == NULL &&
658 : 0 : rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset) != 0)
659 : 0 : rte_panic("rte_thread_getaffinity failed\n");
660 : :
661 : 0 : if (rte_eal_init(eal_argc, eal_argv) < 0)
662 : 0 : rte_exit(EXIT_FAILURE, "EAL init failed: is primary process running?\n");
663 : :
664 : : /*
665 : : * Register pdump callback handler.
666 : : * Primary will notify all secondary processes of change.
667 : : * No impact for this application, but need to reply.
668 : : */
669 : 0 : if (rte_pdump_init() < 0)
670 : 0 : rte_exit(EXIT_FAILURE, "EAL pdump init failed\n");
671 : :
672 : : /*
673 : : * If no lcore argument was specified, then run this program as a normal process
674 : : * which can be scheduled on any non-isolated CPU.
675 : : */
676 : 0 : if (lcore_arg == NULL &&
677 : 0 : rte_thread_set_affinity_by_id(rte_thread_self(), &cpuset) != 0)
678 : 0 : rte_exit(EXIT_FAILURE, "Can not restore original CPU affinity\n");
679 : 0 : }
680 : :
681 : : /* Create packet ring shared between callbacks and process */
682 : 0 : static struct rte_ring *create_ring(void)
683 : : {
684 : : struct rte_ring *ring;
685 : : char ring_name[RTE_RING_NAMESIZE];
686 : : size_t size, log2;
687 : :
688 : : /* Find next power of 2 >= size. */
689 : 0 : size = ring_size;
690 : 0 : log2 = sizeof(size) * 8 - rte_clz64(size - 1);
691 : 0 : size = 1u << log2;
692 : :
693 : 0 : if (size != ring_size) {
694 : 0 : fprintf(stderr, "Ring size %u rounded up to %zu\n",
695 : : ring_size, size);
696 : 0 : ring_size = size;
697 : : }
698 : :
699 : : /* Want one ring per invocation of program */
700 : 0 : snprintf(ring_name, sizeof(ring_name),
701 : : "dumpcap-%d", getpid());
702 : :
703 : 0 : ring = rte_ring_create(ring_name, ring_size,
704 : 0 : rte_socket_id(), 0);
705 : 0 : if (ring == NULL)
706 : 0 : rte_exit(EXIT_FAILURE, "Could not create ring :%s\n",
707 : : rte_strerror(rte_errno));
708 : :
709 : 0 : return ring;
710 : : }
711 : :
712 : 0 : static struct rte_mempool *create_mempool(void)
713 : : {
714 : : const struct interface *intf;
715 : : char pool_name[RTE_MEMPOOL_NAMESIZE];
716 : 0 : size_t num_mbufs = 2 * ring_size;
717 : : struct rte_mempool *mp;
718 : : uint32_t data_size = 128;
719 : :
720 : 0 : snprintf(pool_name, sizeof(pool_name), "capture_%d", getpid());
721 : :
722 : : /* Common pool so size mbuf for biggest snap length */
723 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
724 : 0 : uint32_t mbuf_size = rte_pcapng_mbuf_size(intf->opts.snap_len);
725 : :
726 : : if (mbuf_size > data_size)
727 : : data_size = mbuf_size;
728 : : }
729 : :
730 : 0 : mp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs,
731 : : MBUF_POOL_CACHE_SIZE, 0,
732 : : data_size,
733 : 0 : rte_socket_id(), "ring_mp_mc");
734 : 0 : if (mp == NULL)
735 : 0 : rte_exit(EXIT_FAILURE,
736 : : "Mempool (%s) creation failed: %s\n", pool_name,
737 : : rte_strerror(rte_errno));
738 : :
739 : 0 : return mp;
740 : : }
741 : :
742 : : /*
743 : : * Get Operating System information.
744 : : * Returns an string allocated via malloc().
745 : : */
746 : 0 : static char *get_os_info(void)
747 : : {
748 : : struct utsname uts;
749 : 0 : char *osname = NULL;
750 : :
751 : 0 : if (uname(&uts) < 0)
752 : : return NULL;
753 : :
754 : 0 : if (asprintf(&osname, "%s %s",
755 : : uts.sysname, uts.release) == -1)
756 : : return NULL;
757 : :
758 : 0 : return osname;
759 : : }
760 : :
761 : 0 : static dumpcap_out_t create_output(void)
762 : : {
763 : : dumpcap_out_t ret;
764 : : static char tmp_path[PATH_MAX];
765 : : int fd;
766 : :
767 : : /* If no filename specified make a tempfile name */
768 : 0 : if (output_name == NULL) {
769 : : struct interface *intf;
770 : : struct tm *tm;
771 : : time_t now;
772 : : char ts[32];
773 : :
774 : 0 : intf = TAILQ_FIRST(&interfaces);
775 : 0 : now = time(NULL);
776 : 0 : tm = localtime(&now);
777 : 0 : if (!tm)
778 : 0 : rte_panic("localtime failed\n");
779 : :
780 : 0 : strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", tm);
781 : :
782 : 0 : snprintf(tmp_path, sizeof(tmp_path),
783 : : "%s/%s_%u_%s_%s.%s", tmp_dir,
784 : 0 : progname, intf->port, intf->name, ts,
785 : 0 : use_pcapng ? "pcapng" : "pcap");
786 : 0 : output_name = tmp_path;
787 : : }
788 : :
789 : 0 : if (strcmp(output_name, "-") == 0)
790 : : fd = STDOUT_FILENO;
791 : : else {
792 : 0 : mode_t mode = group_read ? 0640 : 0600;
793 : :
794 : 0 : fprintf(stderr, "File: %s\n", output_name);
795 : 0 : fd = open(output_name, O_WRONLY | O_CREAT, mode);
796 : 0 : if (fd < 0)
797 : 0 : rte_exit(EXIT_FAILURE, "Can not open \"%s\": %s\n",
798 : 0 : output_name, strerror(errno));
799 : : }
800 : :
801 : 0 : if (use_pcapng) {
802 : : struct interface *intf;
803 : 0 : char *os = get_os_info();
804 : :
805 : 0 : ret.pcapng = rte_pcapng_fdopen(fd, os, NULL,
806 : : version(), capture_comment);
807 : 0 : if (ret.pcapng == NULL)
808 : 0 : rte_exit(EXIT_FAILURE, "pcapng_fdopen failed: %s\n",
809 : : strerror(rte_errno));
810 : 0 : free(os);
811 : :
812 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
813 : 0 : if (rte_pcapng_add_interface(ret.pcapng, intf->port, DLT_EN10MB,
814 : : intf->ifname, intf->ifdescr,
815 : : intf->opts.filter) < 0)
816 : 0 : rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
817 : 0 : intf->port);
818 : : }
819 : : } else {
820 : : pcap_t *pcap;
821 : :
822 : 0 : pcap = pcap_open_dead_with_tstamp_precision(DLT_EN10MB,
823 : 0 : capture.snap_len,
824 : : PCAP_TSTAMP_PRECISION_NANO);
825 : 0 : if (pcap == NULL)
826 : 0 : rte_exit(EXIT_FAILURE, "pcap_open_dead failed\n");
827 : :
828 : 0 : ret.dumper = pcap_dump_fopen(pcap, fdopen(fd, "w"));
829 : 0 : if (ret.dumper == NULL)
830 : 0 : rte_exit(EXIT_FAILURE, "pcap_dump_fopen failed: %s\n",
831 : : pcap_geterr(pcap));
832 : : }
833 : :
834 : 0 : return ret;
835 : : }
836 : :
837 : 0 : static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp)
838 : : {
839 : : struct interface *intf;
840 : : unsigned int count = 0;
841 : : uint32_t flags;
842 : : int ret;
843 : :
844 : : flags = RTE_PDUMP_FLAG_RXTX;
845 : 0 : if (use_pcapng)
846 : : flags |= RTE_PDUMP_FLAG_PCAPNG;
847 : :
848 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
849 : 0 : ret = rte_pdump_enable_bpf(intf->port, RTE_PDUMP_ALL_QUEUES,
850 : : flags, intf->opts.snap_len,
851 : 0 : r, mp, intf->bpf_prm);
852 : 0 : if (ret < 0) {
853 : : const struct interface *intf2;
854 : :
855 : : /* unwind any previous enables */
856 : 0 : TAILQ_FOREACH(intf2, &interfaces, next) {
857 : 0 : if (intf == intf2)
858 : : break;
859 : 0 : rte_pdump_disable(intf2->port,
860 : : RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);
861 : 0 : if (intf2->opts.promisc_mode)
862 : 0 : rte_eth_promiscuous_disable(intf2->port);
863 : : }
864 : 0 : rte_exit(EXIT_FAILURE,
865 : : "Packet dump enable on %u:%s failed %s\n",
866 : 0 : intf->port, intf->name,
867 : : rte_strerror(rte_errno));
868 : : }
869 : :
870 : 0 : if (intf->opts.promisc_mode) {
871 : 0 : if (rte_eth_promiscuous_get(intf->port) == 1) {
872 : : /* promiscuous already enabled */
873 : 0 : intf->opts.promisc_mode = false;
874 : 0 : } else if (rte_eth_promiscuous_enable(intf->port) < 0) {
875 : 0 : fprintf(stderr, "port %u:%s set promiscuous failed\n",
876 : 0 : intf->port, intf->name);
877 : 0 : intf->opts.promisc_mode = false;
878 : : }
879 : : }
880 : 0 : ++count;
881 : : }
882 : :
883 : 0 : fputs("Capturing on ", stdout);
884 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
885 : 0 : if (intf != TAILQ_FIRST(&interfaces)) {
886 : 0 : if (count > 2)
887 : : putchar(',');
888 : : putchar(' ');
889 : 0 : if (TAILQ_NEXT(intf, next) == NULL)
890 : 0 : fputs("and ", stdout);
891 : : }
892 : 0 : printf("'%s'", intf->name);
893 : : }
894 : : putchar('\n');
895 : 0 : }
896 : :
897 : : /*
898 : : * Show current count of captured packets
899 : : * with backspaces to overwrite last value.
900 : : */
901 : 0 : static void show_count(uint64_t count)
902 : : {
903 : : unsigned int i;
904 : : static unsigned int bt;
905 : :
906 : 0 : for (i = 0; i < bt; i++)
907 : 0 : fputc('\b', stderr);
908 : :
909 : 0 : bt = fprintf(stderr, "%"PRIu64" ", count);
910 : 0 : }
911 : :
912 : : /* Write multiple packets in older pcap format */
913 : : static ssize_t
914 : 0 : pcap_write_packets(pcap_dumper_t *dumper,
915 : : struct rte_mbuf *pkts[], uint16_t n)
916 : : {
917 : : uint8_t temp_data[RTE_ETHER_MAX_JUMBO_FRAME_LEN];
918 : : struct pcap_pkthdr header;
919 : : uint16_t i;
920 : : size_t total = 0;
921 : :
922 : 0 : gettimeofday(&header.ts, NULL);
923 : :
924 : 0 : for (i = 0; i < n; i++) {
925 : 0 : struct rte_mbuf *m = pkts[i];
926 : : size_t len, caplen;
927 : :
928 : 0 : len = caplen = rte_pktmbuf_pkt_len(m);
929 : 0 : if (unlikely(!rte_pktmbuf_is_contiguous(m) && len > sizeof(temp_data)))
930 : : caplen = sizeof(temp_data);
931 : :
932 : 0 : header.len = len;
933 : 0 : header.caplen = caplen;
934 : :
935 : 0 : pcap_dump((u_char *)dumper, &header,
936 : : rte_pktmbuf_read(m, 0, caplen, temp_data));
937 : :
938 : 0 : total += sizeof(header) + caplen;
939 : : }
940 : :
941 : 0 : return total;
942 : : }
943 : :
944 : : /* Process all packets in ring and dump to capture file */
945 : 0 : static int process_ring(dumpcap_out_t out, struct rte_ring *r)
946 : : {
947 : : struct rte_mbuf *pkts[BURST_SIZE];
948 : : unsigned int avail, n;
949 : : static unsigned int empty_count;
950 : : ssize_t written;
951 : :
952 : : n = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE,
953 : : &avail);
954 : : if (n == 0) {
955 : : /* don't consume endless amounts of cpu if idle */
956 : 0 : if (empty_count < SLEEP_THRESHOLD)
957 : 0 : ++empty_count;
958 : : else
959 : 0 : usleep(10);
960 : 0 : return 0;
961 : : }
962 : :
963 : 0 : empty_count = (avail == 0);
964 : :
965 : 0 : if (use_pcapng)
966 : 0 : written = rte_pcapng_write_packets(out.pcapng, pkts, n);
967 : : else
968 : 0 : written = pcap_write_packets(out.dumper, pkts, n);
969 : :
970 : 0 : rte_pktmbuf_free_bulk(pkts, n);
971 : :
972 : 0 : if (written < 0)
973 : : return -1;
974 : :
975 : 0 : file_size += written;
976 : 0 : packets_received += n;
977 : 0 : if (!quiet)
978 : 0 : show_count(packets_received);
979 : :
980 : : return 0;
981 : : }
982 : :
983 : 0 : int main(int argc, char **argv)
984 : : {
985 : : struct rte_ring *r;
986 : : struct rte_mempool *mp;
987 : 0 : struct sigaction action = {
988 : : .sa_flags = SA_RESTART,
989 : : .sa_handler = signal_handler,
990 : : };
991 : : struct sigaction origaction;
992 : : dumpcap_out_t out;
993 : : char *p;
994 : :
995 : 0 : p = strrchr(argv[0], '/');
996 : 0 : if (p == NULL)
997 : 0 : progname = argv[0];
998 : : else
999 : 0 : progname = p + 1;
1000 : :
1001 : 0 : parse_opts(argc, argv);
1002 : 0 : dpdk_init();
1003 : :
1004 : 0 : if (show_interfaces)
1005 : 0 : dump_interfaces();
1006 : :
1007 : 0 : if (rte_eth_dev_count_avail() == 0)
1008 : 0 : rte_exit(EXIT_FAILURE, "No Ethernet ports found\n");
1009 : :
1010 : 0 : if (TAILQ_EMPTY(&interfaces))
1011 : 0 : set_default_interface();
1012 : : else
1013 : 0 : find_interfaces();
1014 : :
1015 : 0 : compile_filters();
1016 : :
1017 : 0 : sigemptyset(&action.sa_mask);
1018 : 0 : sigaction(SIGTERM, &action, NULL);
1019 : 0 : sigaction(SIGINT, &action, NULL);
1020 : 0 : sigaction(SIGPIPE, &action, NULL);
1021 : 0 : sigaction(SIGHUP, NULL, &origaction);
1022 : 0 : if (origaction.sa_handler == SIG_DFL)
1023 : 0 : sigaction(SIGHUP, &action, NULL);
1024 : :
1025 : 0 : enable_primary_monitor();
1026 : :
1027 : 0 : if (print_stats) {
1028 : 0 : statistics_loop();
1029 : 0 : exit(0);
1030 : : }
1031 : :
1032 : 0 : r = create_ring();
1033 : 0 : mp = create_mempool();
1034 : 0 : out = create_output();
1035 : :
1036 : 0 : start_time = time(NULL);
1037 : 0 : enable_pdump(r, mp);
1038 : :
1039 : 0 : if (!quiet) {
1040 : 0 : fprintf(stderr, "Packets captured: ");
1041 : 0 : show_count(0);
1042 : : }
1043 : :
1044 : 0 : while (!rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed)) {
1045 : 0 : if (process_ring(out, r) < 0) {
1046 : 0 : fprintf(stderr, "pcapng file write failed; %s\n",
1047 : 0 : strerror(errno));
1048 : : break;
1049 : : }
1050 : :
1051 : 0 : if (stop.size && file_size >= stop.size)
1052 : : break;
1053 : :
1054 : 0 : if (stop.packets && packets_received >= stop.packets)
1055 : : break;
1056 : :
1057 : 0 : if (stop.duration != 0 &&
1058 : 0 : time(NULL) - start_time > stop.duration)
1059 : : break;
1060 : : }
1061 : :
1062 : 0 : disable_primary_monitor();
1063 : :
1064 : 0 : if (rte_eal_primary_proc_alive(NULL))
1065 : 0 : report_packet_stats(out);
1066 : :
1067 : 0 : if (use_pcapng)
1068 : 0 : rte_pcapng_close(out.pcapng);
1069 : : else
1070 : 0 : pcap_dump_close(out.dumper);
1071 : :
1072 : : /* If primary has exited, do not try and communicate with it */
1073 : 0 : if (!rte_eal_primary_proc_alive(NULL))
1074 : : return 0;
1075 : :
1076 : 0 : cleanup_pdump_resources();
1077 : :
1078 : 0 : rte_ring_free(r);
1079 : 0 : rte_mempool_free(mp);
1080 : :
1081 : 0 : return rte_eal_cleanup() ? EXIT_FAILURE : 0;
1082 : : }
|