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