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 : 0 : }
532 : :
533 : : /* Alarm signal handler, used to check that primary process */
534 : : static void
535 : 0 : monitor_primary(void *arg __rte_unused)
536 : : {
537 : 0 : if (rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed))
538 : : return;
539 : :
540 : 0 : if (rte_eal_primary_proc_alive(NULL)) {
541 : 0 : rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);
542 : : } else {
543 : 0 : fprintf(stderr,
544 : : "Primary process is no longer active, exiting...\n");
545 : 0 : rte_atomic_store_explicit(&quit_signal, true, rte_memory_order_relaxed);
546 : : }
547 : : }
548 : :
549 : : /* Setup handler to check when primary exits. */
550 : : static void
551 : 0 : enable_primary_monitor(void)
552 : : {
553 : : int ret;
554 : :
555 : : /* Once primary exits, so will pdump. */
556 : 0 : ret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);
557 : 0 : if (ret < 0)
558 : 0 : fprintf(stderr, "Fail to enable monitor:%d\n", ret);
559 : 0 : }
560 : :
561 : : static void
562 : 0 : disable_primary_monitor(void)
563 : : {
564 : : int ret;
565 : :
566 : 0 : ret = rte_eal_alarm_cancel(monitor_primary, NULL);
567 : 0 : if (ret < 0)
568 : 0 : fprintf(stderr, "Fail to disable monitor:%d\n", ret);
569 : 0 : }
570 : :
571 : : static void
572 : 0 : report_packet_stats(dumpcap_out_t out)
573 : : {
574 : : struct rte_pdump_stats pdump_stats;
575 : : struct interface *intf;
576 : : uint64_t ifrecv, ifdrop;
577 : : double percent;
578 : :
579 : 0 : fputc('\n', stderr);
580 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
581 : 0 : if (rte_pdump_stats(intf->port, &pdump_stats) < 0)
582 : 0 : continue;
583 : :
584 : : /* do what Wiretap does */
585 : 0 : ifrecv = pdump_stats.accepted + pdump_stats.filtered;
586 : 0 : ifdrop = pdump_stats.nombuf + pdump_stats.ringfull;
587 : :
588 : 0 : if (use_pcapng)
589 : 0 : rte_pcapng_write_stats(out.pcapng, intf->port,
590 : : ifrecv, ifdrop, NULL);
591 : :
592 : 0 : if (ifrecv == 0)
593 : : percent = 0;
594 : : else
595 : 0 : percent = 100. * ifrecv / (ifrecv + ifdrop);
596 : :
597 : 0 : fprintf(stderr,
598 : : "Packets received/dropped on interface '%s': "
599 : : "%"PRIu64 "/%" PRIu64 " (%.1f)\n",
600 : 0 : intf->name, ifrecv, ifdrop, percent);
601 : : }
602 : 0 : }
603 : :
604 : : /*
605 : : * Start DPDK EAL with arguments.
606 : : * Unlike most DPDK programs, this application does not use the
607 : : * typical EAL command line arguments.
608 : : * We don't want to expose all the DPDK internals to the user.
609 : : */
610 : 0 : static void dpdk_init(void)
611 : : {
612 : : static const char * const args[] = {
613 : : "dumpcap", "--proc-type", "secondary",
614 : : "--log-level", "notice"
615 : : };
616 : : int eal_argc = RTE_DIM(args);
617 : 0 : rte_cpuset_t cpuset = { };
618 : : char **eal_argv;
619 : : unsigned int i;
620 : :
621 : 0 : if (file_prefix != NULL)
622 : : eal_argc += 2;
623 : :
624 : 0 : if (lcore_arg != NULL)
625 : 0 : eal_argc += 2;
626 : :
627 : : /* DPDK API requires mutable versions of command line arguments. */
628 : 0 : eal_argv = calloc(eal_argc + 1, sizeof(char *));
629 : 0 : if (eal_argv == NULL)
630 : 0 : rte_panic("No memory\n");
631 : :
632 : 0 : eal_argv[0] = strdup(progname);
633 : 0 : for (i = 1; i < RTE_DIM(args); i++)
634 : 0 : eal_argv[i] = strdup(args[i]);
635 : :
636 : 0 : if (lcore_arg != NULL) {
637 : 0 : eal_argv[i++] = strdup("--lcores");
638 : 0 : eal_argv[i++] = strdup(lcore_arg);
639 : : }
640 : :
641 : 0 : if (file_prefix != NULL) {
642 : 0 : eal_argv[i++] = strdup("--file-prefix");
643 : 0 : eal_argv[i++] = strdup(file_prefix);
644 : : }
645 : :
646 : 0 : for (i = 0; i < (unsigned int)eal_argc; i++) {
647 : 0 : if (eal_argv[i] == NULL)
648 : 0 : rte_panic("No memory\n");
649 : : }
650 : :
651 : : /*
652 : : * Need to get the original cpuset, before EAL init changes
653 : : * the affinity of this thread (main lcore).
654 : : */
655 : 0 : if (lcore_arg == NULL &&
656 : 0 : rte_thread_get_affinity_by_id(rte_thread_self(), &cpuset) != 0)
657 : 0 : rte_panic("rte_thread_getaffinity failed\n");
658 : :
659 : 0 : if (rte_eal_init(eal_argc, eal_argv) < 0)
660 : 0 : rte_exit(EXIT_FAILURE, "EAL init failed: is primary process running?\n");
661 : :
662 : : /*
663 : : * If no lcore argument was specified, then run this program as a normal process
664 : : * which can be scheduled on any non-isolated CPU.
665 : : */
666 : 0 : if (lcore_arg == NULL &&
667 : 0 : rte_thread_set_affinity_by_id(rte_thread_self(), &cpuset) != 0)
668 : 0 : rte_exit(EXIT_FAILURE, "Can not restore original CPU affinity\n");
669 : 0 : }
670 : :
671 : : /* Create packet ring shared between callbacks and process */
672 : 0 : static struct rte_ring *create_ring(void)
673 : : {
674 : : struct rte_ring *ring;
675 : : char ring_name[RTE_RING_NAMESIZE];
676 : : size_t size, log2;
677 : :
678 : : /* Find next power of 2 >= size. */
679 : 0 : size = ring_size;
680 : 0 : log2 = sizeof(size) * 8 - rte_clz64(size - 1);
681 : 0 : size = 1u << log2;
682 : :
683 : 0 : if (size != ring_size) {
684 : 0 : fprintf(stderr, "Ring size %u rounded up to %zu\n",
685 : : ring_size, size);
686 : 0 : ring_size = size;
687 : : }
688 : :
689 : : /* Want one ring per invocation of program */
690 : 0 : snprintf(ring_name, sizeof(ring_name),
691 : : "dumpcap-%d", getpid());
692 : :
693 : 0 : ring = rte_ring_create(ring_name, ring_size,
694 : 0 : rte_socket_id(), 0);
695 : 0 : if (ring == NULL)
696 : 0 : rte_exit(EXIT_FAILURE, "Could not create ring :%s\n",
697 : : rte_strerror(rte_errno));
698 : :
699 : 0 : return ring;
700 : : }
701 : :
702 : 0 : static struct rte_mempool *create_mempool(void)
703 : : {
704 : : const struct interface *intf;
705 : : char pool_name[RTE_MEMPOOL_NAMESIZE];
706 : 0 : size_t num_mbufs = 2 * ring_size;
707 : : struct rte_mempool *mp;
708 : : uint32_t data_size = 128;
709 : :
710 : 0 : snprintf(pool_name, sizeof(pool_name), "capture_%d", getpid());
711 : :
712 : : /* Common pool so size mbuf for biggest snap length */
713 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
714 : 0 : uint32_t mbuf_size = rte_pcapng_mbuf_size(intf->opts.snap_len);
715 : :
716 : : if (mbuf_size > data_size)
717 : : data_size = mbuf_size;
718 : : }
719 : :
720 : 0 : mp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs,
721 : : MBUF_POOL_CACHE_SIZE, 0,
722 : : data_size,
723 : 0 : rte_socket_id(), "ring_mp_mc");
724 : 0 : if (mp == NULL)
725 : 0 : rte_exit(EXIT_FAILURE,
726 : : "Mempool (%s) creation failed: %s\n", pool_name,
727 : : rte_strerror(rte_errno));
728 : :
729 : 0 : return mp;
730 : : }
731 : :
732 : : /*
733 : : * Get Operating System information.
734 : : * Returns an string allocated via malloc().
735 : : */
736 : 0 : static char *get_os_info(void)
737 : : {
738 : : struct utsname uts;
739 : 0 : char *osname = NULL;
740 : :
741 : 0 : if (uname(&uts) < 0)
742 : : return NULL;
743 : :
744 : 0 : if (asprintf(&osname, "%s %s",
745 : : uts.sysname, uts.release) == -1)
746 : : return NULL;
747 : :
748 : 0 : return osname;
749 : : }
750 : :
751 : 0 : static dumpcap_out_t create_output(void)
752 : : {
753 : : dumpcap_out_t ret;
754 : : static char tmp_path[PATH_MAX];
755 : : int fd;
756 : :
757 : : /* If no filename specified make a tempfile name */
758 : 0 : if (output_name == NULL) {
759 : : struct interface *intf;
760 : : struct tm *tm;
761 : : time_t now;
762 : : char ts[32];
763 : :
764 : 0 : intf = TAILQ_FIRST(&interfaces);
765 : 0 : now = time(NULL);
766 : 0 : tm = localtime(&now);
767 : 0 : if (!tm)
768 : 0 : rte_panic("localtime failed\n");
769 : :
770 : 0 : strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", tm);
771 : :
772 : 0 : snprintf(tmp_path, sizeof(tmp_path),
773 : : "%s/%s_%u_%s_%s.%s", tmp_dir,
774 : 0 : progname, intf->port, intf->name, ts,
775 : 0 : use_pcapng ? "pcapng" : "pcap");
776 : 0 : output_name = tmp_path;
777 : : }
778 : :
779 : 0 : if (strcmp(output_name, "-") == 0)
780 : : fd = STDOUT_FILENO;
781 : : else {
782 : 0 : mode_t mode = group_read ? 0640 : 0600;
783 : :
784 : 0 : fprintf(stderr, "File: %s\n", output_name);
785 : 0 : fd = open(output_name, O_WRONLY | O_CREAT, mode);
786 : 0 : if (fd < 0)
787 : 0 : rte_exit(EXIT_FAILURE, "Can not open \"%s\": %s\n",
788 : 0 : output_name, strerror(errno));
789 : : }
790 : :
791 : 0 : if (use_pcapng) {
792 : : struct interface *intf;
793 : 0 : char *os = get_os_info();
794 : :
795 : 0 : ret.pcapng = rte_pcapng_fdopen(fd, os, NULL,
796 : : version(), capture_comment);
797 : 0 : if (ret.pcapng == NULL)
798 : 0 : rte_exit(EXIT_FAILURE, "pcapng_fdopen failed: %s\n",
799 : : strerror(rte_errno));
800 : 0 : free(os);
801 : :
802 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
803 : 0 : if (rte_pcapng_add_interface(ret.pcapng, intf->port, intf->ifname,
804 : : intf->ifdescr, intf->opts.filter) < 0)
805 : 0 : rte_exit(EXIT_FAILURE, "rte_pcapng_add_interface %u failed\n",
806 : 0 : intf->port);
807 : : }
808 : : } else {
809 : : pcap_t *pcap;
810 : :
811 : 0 : pcap = pcap_open_dead_with_tstamp_precision(DLT_EN10MB,
812 : 0 : capture.snap_len,
813 : : PCAP_TSTAMP_PRECISION_NANO);
814 : 0 : if (pcap == NULL)
815 : 0 : rte_exit(EXIT_FAILURE, "pcap_open_dead failed\n");
816 : :
817 : 0 : ret.dumper = pcap_dump_fopen(pcap, fdopen(fd, "w"));
818 : 0 : if (ret.dumper == NULL)
819 : 0 : rte_exit(EXIT_FAILURE, "pcap_dump_fopen failed: %s\n",
820 : : pcap_geterr(pcap));
821 : : }
822 : :
823 : 0 : return ret;
824 : : }
825 : :
826 : 0 : static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp)
827 : : {
828 : : struct interface *intf;
829 : : unsigned int count = 0;
830 : : uint32_t flags;
831 : : int ret;
832 : :
833 : : flags = RTE_PDUMP_FLAG_RXTX;
834 : 0 : if (use_pcapng)
835 : : flags |= RTE_PDUMP_FLAG_PCAPNG;
836 : :
837 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
838 : 0 : ret = rte_pdump_enable_bpf(intf->port, RTE_PDUMP_ALL_QUEUES,
839 : : flags, intf->opts.snap_len,
840 : 0 : r, mp, intf->bpf_prm);
841 : 0 : if (ret < 0) {
842 : : const struct interface *intf2;
843 : :
844 : : /* unwind any previous enables */
845 : 0 : TAILQ_FOREACH(intf2, &interfaces, next) {
846 : 0 : if (intf == intf2)
847 : : break;
848 : 0 : rte_pdump_disable(intf2->port,
849 : : RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);
850 : 0 : if (intf2->opts.promisc_mode)
851 : 0 : rte_eth_promiscuous_disable(intf2->port);
852 : : }
853 : 0 : rte_exit(EXIT_FAILURE,
854 : : "Packet dump enable on %u:%s failed %s\n",
855 : 0 : intf->port, intf->name,
856 : : rte_strerror(rte_errno));
857 : : }
858 : :
859 : 0 : if (intf->opts.promisc_mode) {
860 : 0 : if (rte_eth_promiscuous_get(intf->port) == 1) {
861 : : /* promiscuous already enabled */
862 : 0 : intf->opts.promisc_mode = false;
863 : 0 : } else if (rte_eth_promiscuous_enable(intf->port) < 0) {
864 : 0 : fprintf(stderr, "port %u:%s set promiscuous failed\n",
865 : 0 : intf->port, intf->name);
866 : 0 : intf->opts.promisc_mode = false;
867 : : }
868 : : }
869 : 0 : ++count;
870 : : }
871 : :
872 : 0 : fputs("Capturing on ", stdout);
873 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
874 : 0 : if (intf != TAILQ_FIRST(&interfaces)) {
875 : 0 : if (count > 2)
876 : : putchar(',');
877 : : putchar(' ');
878 : 0 : if (TAILQ_NEXT(intf, next) == NULL)
879 : 0 : fputs("and ", stdout);
880 : : }
881 : 0 : printf("'%s'", intf->name);
882 : : }
883 : : putchar('\n');
884 : 0 : }
885 : :
886 : : /*
887 : : * Show current count of captured packets
888 : : * with backspaces to overwrite last value.
889 : : */
890 : 0 : static void show_count(uint64_t count)
891 : : {
892 : : unsigned int i;
893 : : static unsigned int bt;
894 : :
895 : 0 : for (i = 0; i < bt; i++)
896 : 0 : fputc('\b', stderr);
897 : :
898 : 0 : bt = fprintf(stderr, "%"PRIu64" ", count);
899 : 0 : }
900 : :
901 : : /* Write multiple packets in older pcap format */
902 : : static ssize_t
903 : 0 : pcap_write_packets(pcap_dumper_t *dumper,
904 : : struct rte_mbuf *pkts[], uint16_t n)
905 : : {
906 : : uint8_t temp_data[RTE_ETHER_MAX_JUMBO_FRAME_LEN];
907 : : struct pcap_pkthdr header;
908 : : uint16_t i;
909 : : size_t total = 0;
910 : :
911 : 0 : gettimeofday(&header.ts, NULL);
912 : :
913 : 0 : for (i = 0; i < n; i++) {
914 : 0 : struct rte_mbuf *m = pkts[i];
915 : : size_t len, caplen;
916 : :
917 : 0 : len = caplen = rte_pktmbuf_pkt_len(m);
918 : 0 : if (unlikely(!rte_pktmbuf_is_contiguous(m) && len > sizeof(temp_data)))
919 : : caplen = sizeof(temp_data);
920 : :
921 : 0 : header.len = len;
922 : 0 : header.caplen = caplen;
923 : :
924 : 0 : pcap_dump((u_char *)dumper, &header,
925 : : rte_pktmbuf_read(m, 0, caplen, temp_data));
926 : :
927 : 0 : total += sizeof(header) + caplen;
928 : : }
929 : :
930 : 0 : return total;
931 : : }
932 : :
933 : : /* Process all packets in ring and dump to capture file */
934 : 0 : static int process_ring(dumpcap_out_t out, struct rte_ring *r)
935 : : {
936 : : struct rte_mbuf *pkts[BURST_SIZE];
937 : : unsigned int avail, n;
938 : : static unsigned int empty_count;
939 : : ssize_t written;
940 : :
941 : : n = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE,
942 : : &avail);
943 : : if (n == 0) {
944 : : /* don't consume endless amounts of cpu if idle */
945 : 0 : if (empty_count < SLEEP_THRESHOLD)
946 : 0 : ++empty_count;
947 : : else
948 : 0 : usleep(10);
949 : 0 : return 0;
950 : : }
951 : :
952 : 0 : empty_count = (avail == 0);
953 : :
954 : 0 : if (use_pcapng)
955 : 0 : written = rte_pcapng_write_packets(out.pcapng, pkts, n);
956 : : else
957 : 0 : written = pcap_write_packets(out.dumper, pkts, n);
958 : :
959 : 0 : rte_pktmbuf_free_bulk(pkts, n);
960 : :
961 : 0 : if (written < 0)
962 : : return -1;
963 : :
964 : 0 : file_size += written;
965 : 0 : packets_received += n;
966 : 0 : if (!quiet)
967 : 0 : show_count(packets_received);
968 : :
969 : : return 0;
970 : : }
971 : :
972 : 0 : int main(int argc, char **argv)
973 : : {
974 : : struct rte_ring *r;
975 : : struct rte_mempool *mp;
976 : 0 : struct sigaction action = {
977 : : .sa_flags = SA_RESTART,
978 : : .sa_handler = signal_handler,
979 : : };
980 : : struct sigaction origaction;
981 : : dumpcap_out_t out;
982 : : char *p;
983 : :
984 : 0 : p = strrchr(argv[0], '/');
985 : 0 : if (p == NULL)
986 : 0 : progname = argv[0];
987 : : else
988 : 0 : progname = p + 1;
989 : :
990 : 0 : parse_opts(argc, argv);
991 : 0 : dpdk_init();
992 : :
993 : 0 : if (show_interfaces)
994 : 0 : dump_interfaces();
995 : :
996 : 0 : if (rte_eth_dev_count_avail() == 0)
997 : 0 : rte_exit(EXIT_FAILURE, "No Ethernet ports found\n");
998 : :
999 : 0 : if (TAILQ_EMPTY(&interfaces))
1000 : 0 : set_default_interface();
1001 : : else
1002 : 0 : find_interfaces();
1003 : :
1004 : 0 : compile_filters();
1005 : :
1006 : 0 : sigemptyset(&action.sa_mask);
1007 : 0 : sigaction(SIGTERM, &action, NULL);
1008 : 0 : sigaction(SIGINT, &action, NULL);
1009 : 0 : sigaction(SIGPIPE, &action, NULL);
1010 : 0 : sigaction(SIGHUP, NULL, &origaction);
1011 : 0 : if (origaction.sa_handler == SIG_DFL)
1012 : 0 : sigaction(SIGHUP, &action, NULL);
1013 : :
1014 : 0 : enable_primary_monitor();
1015 : :
1016 : 0 : if (print_stats) {
1017 : 0 : statistics_loop();
1018 : 0 : exit(0);
1019 : : }
1020 : :
1021 : 0 : r = create_ring();
1022 : 0 : mp = create_mempool();
1023 : 0 : out = create_output();
1024 : :
1025 : 0 : start_time = time(NULL);
1026 : 0 : enable_pdump(r, mp);
1027 : :
1028 : 0 : if (!quiet) {
1029 : 0 : fprintf(stderr, "Packets captured: ");
1030 : 0 : show_count(0);
1031 : : }
1032 : :
1033 : 0 : while (!rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed)) {
1034 : 0 : if (process_ring(out, r) < 0) {
1035 : 0 : fprintf(stderr, "pcapng file write failed; %s\n",
1036 : 0 : strerror(errno));
1037 : : break;
1038 : : }
1039 : :
1040 : 0 : if (stop.size && file_size >= stop.size)
1041 : : break;
1042 : :
1043 : 0 : if (stop.packets && packets_received >= stop.packets)
1044 : : break;
1045 : :
1046 : 0 : if (stop.duration != 0 &&
1047 : 0 : time(NULL) - start_time > stop.duration)
1048 : : break;
1049 : : }
1050 : :
1051 : 0 : disable_primary_monitor();
1052 : :
1053 : 0 : if (rte_eal_primary_proc_alive(NULL))
1054 : 0 : report_packet_stats(out);
1055 : :
1056 : 0 : if (use_pcapng)
1057 : 0 : rte_pcapng_close(out.pcapng);
1058 : : else
1059 : 0 : pcap_dump_close(out.dumper);
1060 : :
1061 : 0 : cleanup_pdump_resources();
1062 : :
1063 : 0 : rte_ring_free(r);
1064 : 0 : rte_mempool_free(mp);
1065 : :
1066 : 0 : return rte_eal_cleanup() ? EXIT_FAILURE : 0;
1067 : : }
|