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