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 : if (rte_eal_init(eal_argc, eal_argv) < 0)
632 : 0 : rte_exit(EXIT_FAILURE, "EAL init failed: is primary process running?\n");
633 : 0 : }
634 : :
635 : : /* Create packet ring shared between callbacks and process */
636 : 0 : static struct rte_ring *create_ring(void)
637 : : {
638 : : struct rte_ring *ring;
639 : : char ring_name[RTE_RING_NAMESIZE];
640 : : size_t size, log2;
641 : :
642 : : /* Find next power of 2 >= size. */
643 : 0 : size = ring_size;
644 : 0 : log2 = sizeof(size) * 8 - __builtin_clzl(size - 1);
645 : 0 : size = 1u << log2;
646 : :
647 : 0 : if (size != ring_size) {
648 : 0 : fprintf(stderr, "Ring size %u rounded up to %zu\n",
649 : : ring_size, size);
650 : 0 : ring_size = size;
651 : : }
652 : :
653 : : /* Want one ring per invocation of program */
654 : 0 : snprintf(ring_name, sizeof(ring_name),
655 : : "dumpcap-%d", getpid());
656 : :
657 : 0 : ring = rte_ring_create(ring_name, ring_size,
658 : 0 : rte_socket_id(), 0);
659 : 0 : if (ring == NULL)
660 : 0 : rte_exit(EXIT_FAILURE, "Could not create ring :%s\n",
661 : : rte_strerror(rte_errno));
662 : :
663 : 0 : return ring;
664 : : }
665 : :
666 : 0 : static struct rte_mempool *create_mempool(void)
667 : : {
668 : : const struct interface *intf;
669 : : char pool_name[RTE_MEMPOOL_NAMESIZE];
670 : 0 : size_t num_mbufs = 2 * ring_size;
671 : : struct rte_mempool *mp;
672 : : uint32_t data_size = 128;
673 : :
674 : 0 : snprintf(pool_name, sizeof(pool_name), "capture_%d", getpid());
675 : :
676 : : /* Common pool so size mbuf for biggest snap length */
677 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
678 : 0 : uint32_t mbuf_size = rte_pcapng_mbuf_size(intf->opts.snap_len);
679 : :
680 : : if (mbuf_size > data_size)
681 : : data_size = mbuf_size;
682 : : }
683 : :
684 : 0 : mp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs,
685 : : MBUF_POOL_CACHE_SIZE, 0,
686 : : data_size,
687 : 0 : rte_socket_id(), "ring_mp_mc");
688 : 0 : if (mp == NULL)
689 : 0 : rte_exit(EXIT_FAILURE,
690 : : "Mempool (%s) creation failed: %s\n", pool_name,
691 : : rte_strerror(rte_errno));
692 : :
693 : 0 : return mp;
694 : : }
695 : :
696 : : /*
697 : : * Get Operating System information.
698 : : * Returns an string allocated via malloc().
699 : : */
700 : 0 : static char *get_os_info(void)
701 : : {
702 : : struct utsname uts;
703 : 0 : char *osname = NULL;
704 : :
705 : 0 : if (uname(&uts) < 0)
706 : : return NULL;
707 : :
708 : 0 : if (asprintf(&osname, "%s %s",
709 : : uts.sysname, uts.release) == -1)
710 : : return NULL;
711 : :
712 : 0 : return osname;
713 : : }
714 : :
715 : 0 : static dumpcap_out_t create_output(void)
716 : : {
717 : : dumpcap_out_t ret;
718 : : static char tmp_path[PATH_MAX];
719 : : int fd;
720 : :
721 : : /* If no filename specified make a tempfile name */
722 : 0 : if (output_name == NULL) {
723 : : struct interface *intf;
724 : : struct tm *tm;
725 : : time_t now;
726 : : char ts[32];
727 : :
728 : 0 : intf = TAILQ_FIRST(&interfaces);
729 : 0 : now = time(NULL);
730 : 0 : tm = localtime(&now);
731 : 0 : if (!tm)
732 : 0 : rte_panic("localtime failed\n");
733 : :
734 : 0 : strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", tm);
735 : :
736 : 0 : snprintf(tmp_path, sizeof(tmp_path),
737 : : "%s/%s_%u_%s_%s.%s", tmp_dir,
738 : 0 : progname, intf->port, intf->name, ts,
739 : 0 : use_pcapng ? "pcapng" : "pcap");
740 : 0 : output_name = tmp_path;
741 : : }
742 : :
743 : 0 : if (strcmp(output_name, "-") == 0)
744 : : fd = STDOUT_FILENO;
745 : : else {
746 : 0 : mode_t mode = group_read ? 0640 : 0600;
747 : :
748 : 0 : fprintf(stderr, "File: %s\n", output_name);
749 : 0 : fd = open(output_name, O_WRONLY | O_CREAT, mode);
750 : 0 : if (fd < 0)
751 : 0 : rte_exit(EXIT_FAILURE, "Can not open \"%s\": %s\n",
752 : 0 : output_name, strerror(errno));
753 : : }
754 : :
755 : 0 : if (use_pcapng) {
756 : : struct interface *intf;
757 : 0 : char *os = get_os_info();
758 : :
759 : 0 : ret.pcapng = rte_pcapng_fdopen(fd, os, NULL,
760 : : version(), capture_comment);
761 : 0 : if (ret.pcapng == NULL)
762 : 0 : rte_exit(EXIT_FAILURE, "pcapng_fdopen failed: %s\n",
763 : : strerror(rte_errno));
764 : 0 : free(os);
765 : :
766 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
767 : 0 : rte_pcapng_add_interface(ret.pcapng, intf->port,
768 : : intf->ifname, intf->ifdescr,
769 : : intf->opts.filter);
770 : : }
771 : : } else {
772 : : pcap_t *pcap;
773 : :
774 : 0 : pcap = pcap_open_dead_with_tstamp_precision(DLT_EN10MB,
775 : 0 : capture.snap_len,
776 : : PCAP_TSTAMP_PRECISION_NANO);
777 : 0 : if (pcap == NULL)
778 : 0 : rte_exit(EXIT_FAILURE, "pcap_open_dead failed\n");
779 : :
780 : 0 : ret.dumper = pcap_dump_fopen(pcap, fdopen(fd, "w"));
781 : 0 : if (ret.dumper == NULL)
782 : 0 : rte_exit(EXIT_FAILURE, "pcap_dump_fopen failed: %s\n",
783 : : pcap_geterr(pcap));
784 : : }
785 : :
786 : 0 : return ret;
787 : : }
788 : :
789 : 0 : static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp)
790 : : {
791 : : struct interface *intf;
792 : : unsigned int count = 0;
793 : : uint32_t flags;
794 : : int ret;
795 : :
796 : : flags = RTE_PDUMP_FLAG_RXTX;
797 : 0 : if (use_pcapng)
798 : : flags |= RTE_PDUMP_FLAG_PCAPNG;
799 : :
800 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
801 : 0 : ret = rte_pdump_enable_bpf(intf->port, RTE_PDUMP_ALL_QUEUES,
802 : : flags, intf->opts.snap_len,
803 : 0 : r, mp, intf->bpf_prm);
804 : 0 : if (ret < 0) {
805 : : const struct interface *intf2;
806 : :
807 : : /* unwind any previous enables */
808 : 0 : TAILQ_FOREACH(intf2, &interfaces, next) {
809 : 0 : if (intf == intf2)
810 : : break;
811 : 0 : rte_pdump_disable(intf2->port,
812 : : RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);
813 : 0 : if (intf2->opts.promisc_mode)
814 : 0 : rte_eth_promiscuous_disable(intf2->port);
815 : : }
816 : 0 : rte_exit(EXIT_FAILURE,
817 : : "Packet dump enable on %u:%s failed %s\n",
818 : 0 : intf->port, intf->name,
819 : : rte_strerror(rte_errno));
820 : : }
821 : :
822 : 0 : if (intf->opts.promisc_mode) {
823 : 0 : if (rte_eth_promiscuous_get(intf->port) == 1) {
824 : : /* promiscuous already enabled */
825 : 0 : intf->opts.promisc_mode = false;
826 : : } else {
827 : 0 : ret = rte_eth_promiscuous_enable(intf->port);
828 : 0 : if (ret != 0)
829 : 0 : fprintf(stderr,
830 : : "port %u set promiscuous enable failed: %d\n",
831 : 0 : intf->port, ret);
832 : 0 : intf->opts.promisc_mode = false;
833 : : }
834 : : }
835 : 0 : ++count;
836 : : }
837 : :
838 : 0 : fputs("Capturing on ", stdout);
839 : 0 : TAILQ_FOREACH(intf, &interfaces, next) {
840 : 0 : if (intf != TAILQ_FIRST(&interfaces)) {
841 : 0 : if (count > 2)
842 : : putchar(',');
843 : : putchar(' ');
844 : 0 : if (TAILQ_NEXT(intf, next) == NULL)
845 : 0 : fputs("and ", stdout);
846 : : }
847 : 0 : printf("'%s'", intf->name);
848 : : }
849 : : putchar('\n');
850 : 0 : }
851 : :
852 : : /*
853 : : * Show current count of captured packets
854 : : * with backspaces to overwrite last value.
855 : : */
856 : 0 : static void show_count(uint64_t count)
857 : : {
858 : : unsigned int i;
859 : : static unsigned int bt;
860 : :
861 : 0 : for (i = 0; i < bt; i++)
862 : 0 : fputc('\b', stderr);
863 : :
864 : 0 : bt = fprintf(stderr, "%"PRIu64" ", count);
865 : 0 : }
866 : :
867 : : /* Write multiple packets in older pcap format */
868 : : static ssize_t
869 : 0 : pcap_write_packets(pcap_dumper_t *dumper,
870 : : struct rte_mbuf *pkts[], uint16_t n)
871 : : {
872 : : uint8_t temp_data[RTE_MBUF_DEFAULT_BUF_SIZE];
873 : : struct pcap_pkthdr header;
874 : : uint16_t i;
875 : : size_t total = 0;
876 : :
877 : 0 : gettimeofday(&header.ts, NULL);
878 : :
879 : 0 : for (i = 0; i < n; i++) {
880 : 0 : struct rte_mbuf *m = pkts[i];
881 : :
882 : 0 : header.len = rte_pktmbuf_pkt_len(m);
883 : 0 : header.caplen = RTE_MIN(header.len, sizeof(temp_data));
884 : :
885 : 0 : pcap_dump((u_char *)dumper, &header,
886 : : rte_pktmbuf_read(m, 0, header.caplen, temp_data));
887 : :
888 : 0 : total += sizeof(header) + header.len;
889 : : }
890 : :
891 : 0 : return total;
892 : : }
893 : :
894 : : /* Process all packets in ring and dump to capture file */
895 : 0 : static int process_ring(dumpcap_out_t out, struct rte_ring *r)
896 : : {
897 : : struct rte_mbuf *pkts[BURST_SIZE];
898 : : unsigned int avail, n;
899 : : static unsigned int empty_count;
900 : : ssize_t written;
901 : :
902 : : n = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE,
903 : : &avail);
904 : 0 : if (n == 0) {
905 : : /* don't consume endless amounts of cpu if idle */
906 : 0 : if (empty_count < SLEEP_THRESHOLD)
907 : 0 : ++empty_count;
908 : : else
909 : 0 : usleep(10);
910 : 0 : return 0;
911 : : }
912 : :
913 : 0 : empty_count = (avail == 0);
914 : :
915 : 0 : if (use_pcapng)
916 : 0 : written = rte_pcapng_write_packets(out.pcapng, pkts, n);
917 : : else
918 : 0 : written = pcap_write_packets(out.dumper, pkts, n);
919 : :
920 : 0 : rte_pktmbuf_free_bulk(pkts, n);
921 : :
922 : 0 : if (written < 0)
923 : : return -1;
924 : :
925 : 0 : file_size += written;
926 : 0 : packets_received += n;
927 : 0 : if (!quiet)
928 : 0 : show_count(packets_received);
929 : :
930 : : return 0;
931 : : }
932 : :
933 : 0 : int main(int argc, char **argv)
934 : : {
935 : : struct rte_ring *r;
936 : : struct rte_mempool *mp;
937 : : dumpcap_out_t out;
938 : : char *p;
939 : :
940 : 0 : p = strrchr(argv[0], '/');
941 : 0 : if (p == NULL)
942 : 0 : progname = argv[0];
943 : : else
944 : 0 : progname = p + 1;
945 : :
946 : 0 : parse_opts(argc, argv);
947 : 0 : dpdk_init();
948 : :
949 : 0 : if (show_interfaces)
950 : 0 : dump_interfaces();
951 : :
952 : 0 : if (rte_eth_dev_count_avail() == 0)
953 : 0 : rte_exit(EXIT_FAILURE, "No Ethernet ports found\n");
954 : :
955 : 0 : if (TAILQ_EMPTY(&interfaces))
956 : 0 : set_default_interface();
957 : : else
958 : 0 : find_interfaces();
959 : :
960 : 0 : compile_filters();
961 : :
962 : 0 : signal(SIGINT, signal_handler);
963 : 0 : signal(SIGPIPE, SIG_IGN);
964 : :
965 : 0 : enable_primary_monitor();
966 : :
967 : 0 : if (print_stats) {
968 : 0 : statistics_loop();
969 : 0 : exit(0);
970 : : }
971 : :
972 : 0 : r = create_ring();
973 : 0 : mp = create_mempool();
974 : 0 : out = create_output();
975 : :
976 : 0 : start_time = time(NULL);
977 : 0 : enable_pdump(r, mp);
978 : :
979 : 0 : if (!quiet) {
980 : 0 : fprintf(stderr, "Packets captured: ");
981 : 0 : show_count(0);
982 : : }
983 : :
984 : 0 : while (!__atomic_load_n(&quit_signal, __ATOMIC_RELAXED)) {
985 : 0 : if (process_ring(out, r) < 0) {
986 : 0 : fprintf(stderr, "pcapng file write failed; %s\n",
987 : 0 : strerror(errno));
988 : : break;
989 : : }
990 : :
991 : 0 : if (stop.size && file_size >= stop.size)
992 : : break;
993 : :
994 : 0 : if (stop.packets && packets_received >= stop.packets)
995 : : break;
996 : :
997 : 0 : if (stop.duration != 0 &&
998 : 0 : time(NULL) - start_time > stop.duration)
999 : : break;
1000 : : }
1001 : :
1002 : 0 : disable_primary_monitor();
1003 : :
1004 : 0 : if (rte_eal_primary_proc_alive(NULL))
1005 : 0 : report_packet_stats(out);
1006 : :
1007 : 0 : if (use_pcapng)
1008 : 0 : rte_pcapng_close(out.pcapng);
1009 : : else
1010 : 0 : pcap_dump_close(out.dumper);
1011 : :
1012 : 0 : cleanup_pdump_resources();
1013 : :
1014 : 0 : rte_ring_free(r);
1015 : 0 : rte_mempool_free(mp);
1016 : :
1017 : 0 : return rte_eal_cleanup() ? EXIT_FAILURE : 0;
1018 : : }
|