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 static void usage(void) 111 { 112 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 } 153 154 static const char *version(void) 155 { 156 static char str[128]; 157 158 snprintf(str, sizeof(str), 159 "%s 1.0 (%s)\n", progname, rte_version()); 160 return str; 161 } 162 163 /* Parse numeric argument from command line */ 164 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 u = strtoul(arg, &endp, 0); 171 if (*arg == '\0' || *endp != '\0') 172 rte_exit(EXIT_FAILURE, 173 "Specified %s \"%s\" is not a valid number\n", 174 name, arg); 175 if (limit && u > limit) 176 rte_exit(EXIT_FAILURE, 177 "Specified %s \"%s\" is too large (greater than %u)\n", 178 name, arg, limit); 179 180 return u; 181 } 182 183 /* Set auto stop values */ 184 static void auto_stop(char *opt) 185 { 186 char *value, *endp; 187 188 value = strchr(opt, ':'); 189 if (value == NULL) 190 rte_exit(EXIT_FAILURE, 191 "Missing colon in auto stop parameter\n"); 192 193 *value++ = '\0'; 194 if (strcmp(opt, "duration") == 0) { 195 double interval = strtod(value, &endp); 196 197 if (*value == '\0' || *endp != '\0' || interval <= 0) 198 rte_exit(EXIT_FAILURE, 199 "Invalid duration \"%s\"\n", value); 200 stop.duration = interval; 201 } else if (strcmp(opt, "filesize") == 0) { 202 stop.size = get_uint(value, "filesize", 0) * 1024; 203 } else if (strcmp(opt, "packets") == 0) { 204 stop.packets = get_uint(value, "packets", 0); 205 } else { 206 rte_exit(EXIT_FAILURE, 207 "Unknown autostop parameter \"%s\"\n", opt); 208 } 209 } 210 211 /* Add interface to list of interfaces to capture */ 212 static struct interface *add_interface(const char *name) 213 { 214 struct interface *intf; 215 216 if (strlen(name) >= RTE_ETH_NAME_MAX_LEN) 217 rte_exit(EXIT_FAILURE, "invalid name for interface: '%s'\n", name); 218 219 intf = malloc(sizeof(*intf)); 220 if (!intf) 221 rte_exit(EXIT_FAILURE, "no memory for interface\n"); 222 223 memset(intf, 0, sizeof(*intf)); 224 rte_strscpy(intf->name, name, sizeof(intf->name)); 225 intf->opts = capture; 226 intf->port = -1; /* port set later after EAL init */ 227 228 TAILQ_INSERT_TAIL(&interfaces, intf, next); 229 return intf; 230 } 231 232 /* Name has been set but need to lookup port after eal_init */ 233 static void find_interfaces(void) 234 { 235 struct interface *intf; 236 237 TAILQ_FOREACH(intf, &interfaces, next) { 238 /* if name is valid then just record port */ 239 if (rte_eth_dev_get_port_by_name(intf->name, &intf->port) == 0) 240 continue; 241 242 /* maybe got passed port number string as name */ 243 intf->port = get_uint(intf->name, "port_number", UINT16_MAX); 244 if (rte_eth_dev_get_name_by_port(intf->port, intf->name) < 0) 245 rte_exit(EXIT_FAILURE, "Invalid port number %u\n", 246 intf->port); 247 } 248 } 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 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 RTE_ETH_FOREACH_DEV(p) { 261 if (rte_eth_dev_get_name_by_port(p, name) < 0) 262 continue; 263 264 intf = add_interface(name); 265 intf->port = p; 266 return; 267 } 268 rte_exit(EXIT_FAILURE, "No usable interfaces found\n"); 269 } 270 271 /* Display list of possible interfaces that can be used. */ 272 static void dump_interfaces(void) 273 { 274 char name[RTE_ETH_NAME_MAX_LEN]; 275 uint16_t p; 276 277 RTE_ETH_FOREACH_DEV(p) { 278 if (rte_eth_dev_get_name_by_port(p, name) < 0) 279 continue; 280 printf("%u. %s\n", p, name); 281 } 282 283 exit(0); 284 } 285 286 static void compile_filters(void) 287 { 288 struct interface *intf; 289 290 TAILQ_FOREACH(intf, &interfaces, next) { 291 struct rte_bpf_prm *bpf_prm; 292 struct bpf_program bf; 293 pcap_t *pcap; 294 295 pcap = pcap_open_dead(DLT_EN10MB, intf->opts.snap_len); 296 if (!pcap) 297 rte_exit(EXIT_FAILURE, "can not open pcap\n"); 298 299 if (pcap_compile(pcap, &bf, intf->opts.filter, 300 1, PCAP_NETMASK_UNKNOWN) != 0) { 301 fprintf(stderr, 302 "Invalid capture filter \"%s\": for interface '%s'\n", 303 intf->opts.filter, intf->name); 304 rte_exit(EXIT_FAILURE, "\n%s\n", 305 pcap_geterr(pcap)); 306 } 307 308 bpf_prm = rte_bpf_convert(&bf); 309 if (bpf_prm == NULL) 310 rte_exit(EXIT_FAILURE, 311 "BPF convert interface '%s'\n%s(%d)\n", 312 intf->name, 313 rte_strerror(rte_errno), rte_errno); 314 315 if (dump_bpf) { 316 printf("cBPF program (%u insns)\n", bf.bf_len); 317 bpf_dump(&bf, 1); 318 printf("\neBPF program (%u insns)\n", 319 bpf_prm->nb_ins); 320 rte_bpf_dump(stdout, bpf_prm->ins, bpf_prm->nb_ins); 321 exit(0); 322 } 323 324 intf->bpf_prm = bpf_prm; 325 326 /* Don't care about original program any more */ 327 pcap_freecode(&bf); 328 pcap_close(pcap); 329 } 330 } 331 332 /* 333 * Parse command line options. 334 * These are chosen to be similar to dumpcap command. 335 */ 336 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 c = getopt_long(argc, argv, "a:b:c:dDf:ghi:nN:pPqSs:vw:", 361 long_options, &option_index); 362 if (c == -1) 363 break; 364 365 switch (c) { 366 case 0: { 367 const char *longopt 368 = long_options[option_index].name; 369 370 if (!strcmp(longopt, "capture-comment")) { 371 capture_comment = optarg; 372 } else if (!strcmp(longopt, "file-prefix")) { 373 file_prefix = optarg; 374 } else if (!strcmp(longopt, "temp-dir")) { 375 tmp_dir = optarg; 376 } else if (!strcmp(longopt, "ifdescr")) { 377 if (last_intf == NULL) 378 rte_exit(EXIT_FAILURE, 379 "--ifdescr must be specified after a -i option\n"); 380 last_intf->ifdescr = optarg; 381 } else if (!strcmp(longopt, "ifname")) { 382 if (last_intf == NULL) 383 rte_exit(EXIT_FAILURE, 384 "--ifname must be specified after a -i option\n"); 385 last_intf->ifname = optarg; 386 } else { 387 usage(); 388 exit(1); 389 } 390 break; 391 } 392 case 'a': 393 auto_stop(optarg); 394 break; 395 case 'b': 396 rte_exit(EXIT_FAILURE, 397 "multiple files not implemented\n"); 398 break; 399 case 'c': 400 stop.packets = get_uint(optarg, "packet_count", 0); 401 break; 402 case 'd': 403 dump_bpf = true; 404 break; 405 case 'D': 406 show_interfaces = true; 407 break; 408 case 'f': 409 if (last_intf == NULL) 410 capture.filter = optarg; 411 else 412 last_intf->opts.filter = optarg; 413 break; 414 case 'g': 415 group_read = true; 416 break; 417 case 'h': 418 printf("%s\n\n", version()); 419 usage(); 420 exit(0); 421 case 'i': 422 last_intf = add_interface(optarg); 423 break; 424 case 'n': 425 use_pcapng = true; 426 break; 427 case 'N': 428 ring_size = get_uint(optarg, "packet_limit", 0); 429 break; 430 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 if (last_intf == NULL) 440 capture.promisc_mode = false; 441 else 442 last_intf->opts.promisc_mode = false; 443 break; 444 case 'P': 445 use_pcapng = false; 446 break; 447 case 'q': 448 quiet = true; 449 break; 450 case 's': 451 len = get_uint(optarg, "snap_len", 0); 452 if (last_intf == NULL) 453 capture.snap_len = len; 454 else 455 last_intf->opts.snap_len = len; 456 break; 457 case 'S': 458 print_stats = true; 459 break; 460 case 'w': 461 output_name = optarg; 462 break; 463 case 'v': 464 printf("%s\n", version()); 465 exit(0); 466 default: 467 fprintf(stderr, "Invalid option: %s\n", 468 argv[optind - 1]); 469 usage(); 470 exit(1); 471 } 472 } 473 } 474 475 static void 476 signal_handler(int sig_num __rte_unused) 477 { 478 __atomic_store_n(&quit_signal, true, __ATOMIC_RELAXED); 479 } 480 481 482 /* Instead of capturing, it tracks interface statistics */ 483 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 while (!__atomic_load_n(&quit_signal, __ATOMIC_RELAXED)) { 494 RTE_ETH_FOREACH_DEV(p) { 495 if (rte_eth_dev_get_name_by_port(p, name) < 0) 496 continue; 497 498 r = rte_eth_stats_get(p, &stats); 499 if (r < 0) { 500 fprintf(stderr, 501 "stats_get for port %u failed: %d (%s)\n", 502 p, r, strerror(-r)); 503 return; 504 } 505 506 printf("%-15s %10"PRIu64" %10"PRIu64"\n", 507 name, stats.ipackets, 508 stats.imissed + stats.ierrors + stats.rx_nombuf); 509 } 510 sleep(1); 511 } 512 } 513 514 static void 515 cleanup_pdump_resources(void) 516 { 517 struct interface *intf; 518 519 TAILQ_FOREACH(intf, &interfaces, next) { 520 rte_pdump_disable(intf->port, 521 RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX); 522 if (intf->opts.promisc_mode) 523 rte_eth_promiscuous_disable(intf->port); 524 } 525 } 526 527 /* Alarm signal handler, used to check that primary process */ 528 static void 529 monitor_primary(void *arg __rte_unused) 530 { 531 if (__atomic_load_n(&quit_signal, __ATOMIC_RELAXED)) 532 return; 533 534 if (rte_eal_primary_proc_alive(NULL)) { 535 rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL); 536 } else { 537 fprintf(stderr, 538 "Primary process is no longer active, exiting...\n"); 539 __atomic_store_n(&quit_signal, true, __ATOMIC_RELAXED); 540 } 541 } 542 543 /* Setup handler to check when primary exits. */ 544 static void 545 enable_primary_monitor(void) 546 { 547 int ret; 548 549 /* Once primary exits, so will pdump. */ 550 ret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL); 551 if (ret < 0) 552 fprintf(stderr, "Fail to enable monitor:%d\n", ret); 553 } 554 555 static void 556 disable_primary_monitor(void) 557 { 558 int ret; 559 560 ret = rte_eal_alarm_cancel(monitor_primary, NULL); 561 if (ret < 0) 562 fprintf(stderr, "Fail to disable monitor:%d\n", ret); 563 } 564 565 static void 566 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 fputc('\n', stderr); 574 TAILQ_FOREACH(intf, &interfaces, next) { 575 if (rte_pdump_stats(intf->port, &pdump_stats) < 0) 576 continue; 577 578 /* do what Wiretap does */ 579 ifrecv = pdump_stats.accepted + pdump_stats.filtered; 580 ifdrop = pdump_stats.nombuf + pdump_stats.ringfull; 581 582 if (use_pcapng) 583 rte_pcapng_write_stats(out.pcapng, intf->port, 584 ifrecv, ifdrop, NULL); 585 586 if (ifrecv == 0) 587 percent = 0; 588 else 589 percent = 100. * ifrecv / (ifrecv + ifdrop); 590 591 fprintf(stderr, 592 "Packets received/dropped on interface '%s': " 593 "%"PRIu64 "/%" PRIu64 " (%.1f)\n", 594 intf->name, ifrecv, ifdrop, percent); 595 } 596 } 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 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 if (file_prefix != NULL) 615 eal_argc += 2; 616 617 /* DPDK API requires mutable versions of command line arguments. */ 618 eal_argv = calloc(eal_argc + 1, sizeof(char *)); 619 if (eal_argv == NULL) 620 rte_panic("No memory\n"); 621 622 eal_argv[0] = strdup(progname); 623 for (i = 1; i < RTE_DIM(args); i++) 624 eal_argv[i] = strdup(args[i]); 625 626 if (file_prefix != NULL) { 627 eal_argv[i++] = strdup("--file-prefix"); 628 eal_argv[i++] = strdup(file_prefix); 629 } 630 631 for (i = 0; i < (unsigned int)eal_argc; i++) { 632 if (eal_argv[i] == NULL) 633 rte_panic("No memory\n"); 634 } 635 636 if (rte_eal_init(eal_argc, eal_argv) < 0) 637 rte_exit(EXIT_FAILURE, "EAL init failed: is primary process running?\n"); 638 } 639 640 /* Create packet ring shared between callbacks and process */ 641 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 size = ring_size; 649 log2 = sizeof(size) * 8 - __builtin_clzl(size - 1); 650 size = 1u << log2; 651 652 if (size != ring_size) { 653 fprintf(stderr, "Ring size %u rounded up to %zu\n", 654 ring_size, size); 655 ring_size = size; 656 } 657 658 /* Want one ring per invocation of program */ 659 snprintf(ring_name, sizeof(ring_name), 660 "dumpcap-%d", getpid()); 661 662 ring = rte_ring_create(ring_name, ring_size, 663 rte_socket_id(), 0); 664 if (ring == NULL) 665 rte_exit(EXIT_FAILURE, "Could not create ring :%s\n", 666 rte_strerror(rte_errno)); 667 668 return ring; 669 } 670 671 static struct rte_mempool *create_mempool(void) 672 { 673 const struct interface *intf; 674 char pool_name[RTE_MEMPOOL_NAMESIZE]; 675 size_t num_mbufs = 2 * ring_size; 676 struct rte_mempool *mp; 677 uint32_t data_size = 128; 678 679 snprintf(pool_name, sizeof(pool_name), "capture_%d", getpid()); 680 681 /* Common pool so size mbuf for biggest snap length */ 682 TAILQ_FOREACH(intf, &interfaces, next) { 683 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 mp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs, 690 MBUF_POOL_CACHE_SIZE, 0, 691 data_size, 692 rte_socket_id(), "ring_mp_mc"); 693 if (mp == NULL) 694 rte_exit(EXIT_FAILURE, 695 "Mempool (%s) creation failed: %s\n", pool_name, 696 rte_strerror(rte_errno)); 697 698 return mp; 699 } 700 701 /* 702 * Get Operating System information. 703 * Returns an string allocated via malloc(). 704 */ 705 static char *get_os_info(void) 706 { 707 struct utsname uts; 708 char *osname = NULL; 709 710 if (uname(&uts) < 0) 711 return NULL; 712 713 if (asprintf(&osname, "%s %s", 714 uts.sysname, uts.release) == -1) 715 return NULL; 716 717 return osname; 718 } 719 720 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 if (output_name == NULL) { 728 struct interface *intf; 729 struct tm *tm; 730 time_t now; 731 char ts[32]; 732 733 intf = TAILQ_FIRST(&interfaces); 734 now = time(NULL); 735 tm = localtime(&now); 736 if (!tm) 737 rte_panic("localtime failed\n"); 738 739 strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", tm); 740 741 snprintf(tmp_path, sizeof(tmp_path), 742 "%s/%s_%u_%s_%s.%s", tmp_dir, 743 progname, intf->port, intf->name, ts, 744 use_pcapng ? "pcapng" : "pcap"); 745 output_name = tmp_path; 746 } 747 748 if (strcmp(output_name, "-") == 0) 749 fd = STDOUT_FILENO; 750 else { 751 mode_t mode = group_read ? 0640 : 0600; 752 753 fprintf(stderr, "File: %s\n", output_name); 754 fd = open(output_name, O_WRONLY | O_CREAT, mode); 755 if (fd < 0) 756 rte_exit(EXIT_FAILURE, "Can not open \"%s\": %s\n", 757 output_name, strerror(errno)); 758 } 759 760 if (use_pcapng) { 761 struct interface *intf; 762 char *os = get_os_info(); 763 764 ret.pcapng = rte_pcapng_fdopen(fd, os, NULL, 765 version(), capture_comment); 766 if (ret.pcapng == NULL) 767 rte_exit(EXIT_FAILURE, "pcapng_fdopen failed: %s\n", 768 strerror(rte_errno)); 769 free(os); 770 771 TAILQ_FOREACH(intf, &interfaces, next) { 772 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 pcap = pcap_open_dead_with_tstamp_precision(DLT_EN10MB, 780 capture.snap_len, 781 PCAP_TSTAMP_PRECISION_NANO); 782 if (pcap == NULL) 783 rte_exit(EXIT_FAILURE, "pcap_open_dead failed\n"); 784 785 ret.dumper = pcap_dump_fopen(pcap, fdopen(fd, "w")); 786 if (ret.dumper == NULL) 787 rte_exit(EXIT_FAILURE, "pcap_dump_fopen failed: %s\n", 788 pcap_geterr(pcap)); 789 } 790 791 return ret; 792 } 793 794 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 if (use_pcapng) 803 flags |= RTE_PDUMP_FLAG_PCAPNG; 804 805 TAILQ_FOREACH(intf, &interfaces, next) { 806 ret = rte_pdump_enable_bpf(intf->port, RTE_PDUMP_ALL_QUEUES, 807 flags, intf->opts.snap_len, 808 r, mp, intf->bpf_prm); 809 if (ret < 0) { 810 const struct interface *intf2; 811 812 /* unwind any previous enables */ 813 TAILQ_FOREACH(intf2, &interfaces, next) { 814 if (intf == intf2) 815 break; 816 rte_pdump_disable(intf2->port, 817 RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX); 818 if (intf2->opts.promisc_mode) 819 rte_eth_promiscuous_disable(intf2->port); 820 } 821 rte_exit(EXIT_FAILURE, 822 "Packet dump enable on %u:%s failed %s\n", 823 intf->port, intf->name, 824 rte_strerror(rte_errno)); 825 } 826 827 if (intf->opts.promisc_mode) { 828 if (rte_eth_promiscuous_get(intf->port) == 1) { 829 /* promiscuous already enabled */ 830 intf->opts.promisc_mode = false; 831 } else { 832 ret = rte_eth_promiscuous_enable(intf->port); 833 if (ret != 0) 834 fprintf(stderr, 835 "port %u set promiscuous enable failed: %d\n", 836 intf->port, ret); 837 intf->opts.promisc_mode = false; 838 } 839 } 840 ++count; 841 } 842 843 fputs("Capturing on ", stdout); 844 TAILQ_FOREACH(intf, &interfaces, next) { 845 if (intf != TAILQ_FIRST(&interfaces)) { 846 if (count > 2) 847 putchar(','); 848 putchar(' '); 849 if (TAILQ_NEXT(intf, next) == NULL) 850 fputs("and ", stdout); 851 } 852 printf("'%s'", intf->name); 853 } 854 putchar('\n'); 855 } 856 857 /* 858 * Show current count of captured packets 859 * with backspaces to overwrite last value. 860 */ 861 static void show_count(uint64_t count) 862 { 863 unsigned int i; 864 static unsigned int bt; 865 866 for (i = 0; i < bt; i++) 867 fputc('\b', stderr); 868 869 bt = fprintf(stderr, "%"PRIu64" ", count); 870 } 871 872 /* Write multiple packets in older pcap format */ 873 static ssize_t 874 pcap_write_packets(pcap_dumper_t *dumper, 875 struct rte_mbuf *pkts[], uint16_t n) 876 { 877 uint8_t temp_data[RTE_MBUF_DEFAULT_BUF_SIZE]; 878 struct pcap_pkthdr header; 879 uint16_t i; 880 size_t total = 0; 881 882 gettimeofday(&header.ts, NULL); 883 884 for (i = 0; i < n; i++) { 885 struct rte_mbuf *m = pkts[i]; 886 887 header.len = rte_pktmbuf_pkt_len(m); 888 header.caplen = RTE_MIN(header.len, sizeof(temp_data)); 889 890 pcap_dump((u_char *)dumper, &header, 891 rte_pktmbuf_read(m, 0, header.caplen, temp_data)); 892 893 total += sizeof(header) + header.len; 894 } 895 896 return total; 897 } 898 899 /* Process all packets in ring and dump to capture file */ 900 static int process_ring(dumpcap_out_t out, struct rte_ring *r) 901 { 902 struct rte_mbuf *pkts[BURST_SIZE]; 903 unsigned int avail, n; 904 static unsigned int empty_count; 905 ssize_t written; 906 907 n = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE, 908 &avail); 909 if (n == 0) { 910 /* don't consume endless amounts of cpu if idle */ 911 if (empty_count < SLEEP_THRESHOLD) 912 ++empty_count; 913 else 914 usleep(10); 915 return 0; 916 } 917 918 empty_count = (avail == 0); 919 920 if (use_pcapng) 921 written = rte_pcapng_write_packets(out.pcapng, pkts, n); 922 else 923 written = pcap_write_packets(out.dumper, pkts, n); 924 925 rte_pktmbuf_free_bulk(pkts, n); 926 927 if (written < 0) 928 return -1; 929 930 file_size += written; 931 packets_received += n; 932 if (!quiet) 933 show_count(packets_received); 934 935 return 0; 936 } 937 938 int main(int argc, char **argv) 939 { 940 struct rte_ring *r; 941 struct rte_mempool *mp; 942 dumpcap_out_t out; 943 char *p; 944 945 p = strrchr(argv[0], '/'); 946 if (p == NULL) 947 progname = argv[0]; 948 else 949 progname = p + 1; 950 951 parse_opts(argc, argv); 952 dpdk_init(); 953 954 if (show_interfaces) 955 dump_interfaces(); 956 957 if (rte_eth_dev_count_avail() == 0) 958 rte_exit(EXIT_FAILURE, "No Ethernet ports found\n"); 959 960 if (TAILQ_EMPTY(&interfaces)) 961 set_default_interface(); 962 else 963 find_interfaces(); 964 965 compile_filters(); 966 967 signal(SIGINT, signal_handler); 968 signal(SIGPIPE, SIG_IGN); 969 970 enable_primary_monitor(); 971 972 if (print_stats) { 973 statistics_loop(); 974 exit(0); 975 } 976 977 r = create_ring(); 978 mp = create_mempool(); 979 out = create_output(); 980 981 start_time = time(NULL); 982 enable_pdump(r, mp); 983 984 if (!quiet) { 985 fprintf(stderr, "Packets captured: "); 986 show_count(0); 987 } 988 989 while (!__atomic_load_n(&quit_signal, __ATOMIC_RELAXED)) { 990 if (process_ring(out, r) < 0) { 991 fprintf(stderr, "pcapng file write failed; %s\n", 992 strerror(errno)); 993 break; 994 } 995 996 if (stop.size && file_size >= stop.size) 997 break; 998 999 if (stop.packets && packets_received >= stop.packets) 1000 break; 1001 1002 if (stop.duration != 0 && 1003 time(NULL) - start_time > stop.duration) 1004 break; 1005 } 1006 1007 disable_primary_monitor(); 1008 1009 if (rte_eal_primary_proc_alive(NULL)) 1010 report_packet_stats(out); 1011 1012 if (use_pcapng) 1013 rte_pcapng_close(out.pcapng); 1014 else 1015 pcap_dump_close(out.dumper); 1016 1017 cleanup_pdump_resources(); 1018 1019 rte_ring_free(r); 1020 rte_mempool_free(mp); 1021 1022 return rte_eal_cleanup() ? EXIT_FAILURE : 0; 1023 } 1024