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