From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 50DB9A2EEB for ; Mon, 7 Oct 2019 18:53:59 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id CE0941D41B; Mon, 7 Oct 2019 18:53:00 +0200 (CEST) Received: from mail-pf1-f196.google.com (mail-pf1-f196.google.com [209.85.210.196]) by dpdk.org (Postfix) with ESMTP id 0561C1D159 for ; Mon, 7 Oct 2019 18:52:52 +0200 (CEST) Received: by mail-pf1-f196.google.com with SMTP id b128so9054155pfa.1 for ; Mon, 07 Oct 2019 09:52:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=azyukN55pVDW6mlaa8A9WYpGM6aRxYm5VnHp3tcG+D8=; b=Gc0U+F3JYVQvkEYlKdrWqIH3HpEdgIVOGVB6VjwPyE+2byO7cOLnj+/6PMkC28VynK /UvuVtqvwzN/YWB6uOxzM2yc/tYKe+kMsnWHrVEQPGgGKk7HETj47gUnT7oj3byMplLN 1g3GpStNjUFwGVKyosd8KtejhwpaNCQ1hZ68OCsE4nFU/0jxm4Tfb6EUu4xirrKTG3fb NfpNPxTM/NG2qP3b4xngjxf54C/befjNMLQYvgK3wpdvkO6ZO5qCaR55b0jHvM+eikzr X7fhkKSCXAbTVSg2658k172Smiu3SUcIE5LyjQy2ce0/okjPdBQixtOvEVtDqLeq2Adn DoPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=azyukN55pVDW6mlaa8A9WYpGM6aRxYm5VnHp3tcG+D8=; b=Nlc5toKPtIeq7uKGs7czW9/oTZ8esS9qk6CofhU7kZPDlfh0fj0Lz0wVVsoLEw4zia mdo3egL3PAlWzm46UfwwwGDr4GhKMg887mhqjI9qDB9zKDtnlK/Zjp653RZKoaHHQKZu /xGhrTJWlTx/pLn14Tx8nzHWIALB41KiRdx309jr/n2r+CT+YDjkiDYGQ9KrLF/aYsdf /cxwFYgZgJJCZ2evrKj478ep8dYS8QWgXhIAwa11tHNflxiU8GEsSpnFLM0NThaViYOd Nd47POBEOBh+bViC+Iv1XoiOFZBLToQJTeUahqbrNQsL9LPdvkzBRcKEcDMBXUIMTh/o 54WA== X-Gm-Message-State: APjAAAUlazBAewzjtc3KrHWOYKcb9IVv2NWkfcenq7fARUdQUmIkEoq8 FSwgYrFmEug7fw1aer14zifodsPcD4CtIw== X-Google-Smtp-Source: APXvYqy3emsWbFW+J8yLMMTd8GBoGPoB4/PFSuUamBaF2XAFOxGByyHbeYO0I8yaf5q5KlvnitfcuA== X-Received: by 2002:a63:5d07:: with SMTP id r7mr6016641pgb.318.1570467169462; Mon, 07 Oct 2019 09:52:49 -0700 (PDT) Received: from hermes.lan (204-195-22-127.wavecable.com. [204.195.22.127]) by smtp.gmail.com with ESMTPSA id w5sm15920979pfn.96.2019.10.07.09.52.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Oct 2019 09:52:48 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Date: Mon, 7 Oct 2019 09:52:32 -0700 Message-Id: <20191007165232.14535-9-stephen@networkplumber.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20191007165232.14535-1-stephen@networkplumber.org> References: <20191007165232.14535-1-stephen@networkplumber.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [dpdk-dev] [RFC 8/8] app/capture: add packet capture using pcapng X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" New application (dpdk-capture) with syntax analogous to tshark's dumpcap command. It runs as a secondary process and produces capture output in pcapng format. It does not use DPDK style EAL arguments; instead the flags are meant to be the same as dumpcap. The program depends on libpcap since it uses the pcap_compile() function to compile a string into a BPF program. Signed-off-by: Stephen Hemminger --- app/Makefile | 1 + app/capture/Makefile | 19 ++ app/capture/main.c | 675 ++++++++++++++++++++++++++++++++++++++++ app/capture/meson.build | 22 ++ app/meson.build | 1 + config/common_base | 5 + 6 files changed, 723 insertions(+) create mode 100644 app/capture/Makefile create mode 100644 app/capture/main.c create mode 100644 app/capture/meson.build diff --git a/app/Makefile b/app/Makefile index 28acbceca904..509cd7f4de13 100644 --- a/app/Makefile +++ b/app/Makefile @@ -7,6 +7,7 @@ DIRS-$(CONFIG_RTE_APP_TEST) += test DIRS-$(CONFIG_RTE_TEST_PMD) += test-pmd DIRS-$(CONFIG_RTE_PROC_INFO) += proc-info DIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += pdump +DIRS-$(CONFIG_RTE_APP_CAPTURE) += capture DIRS-$(CONFIG_RTE_LIBRTE_ACL) += test-acl DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += test-cmdline DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += test-pipeline diff --git a/app/capture/Makefile b/app/capture/Makefile new file mode 100644 index 000000000000..78ff7d2e97bf --- /dev/null +++ b/app/capture/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2019 Microsoft Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +ifeq ($(CONFIG_RTE_LIBRTE_PCAPNG),y) + +APP = dpdk-capture + +CFLAGS += -DALLOW_EXPERIMENTAL_API +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) +LDLIBS += -lpcap + +SRCS-y := main.c + +include $(RTE_SDK)/mk/rte.app.mk + +endif diff --git a/app/capture/main.c b/app/capture/main.c new file mode 100644 index 000000000000..394c1edcc01b --- /dev/null +++ b/app/capture/main.c @@ -0,0 +1,675 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2019 Microsoft Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RING_NAME "capture-ring" +#define MONITOR_INTERVAL (500 * 1000) +#define MBUF_POOL_CACHE_SIZE 32 +#define BURST_SIZE 32 +#define SLEEP_THRESHOLD 1000 + +static const char *prog; +static volatile bool quit_signal; +static bool group_read; +static bool quiet; +static bool promiscuous_mode = true; +static bool use_pcapng = true; +static char *output_name; +static const char *filter_str; +static unsigned int ring_size = 2048; +static uint64_t packet_count, packets_received; +static const char *capture_comment; +static uint16_t snaplen = UINT16_MAX; +static bool dump_bpf; + +struct interface { + uint64_t received; + uint64_t dropped; + uint16_t port; + char name[RTE_ETH_NAME_MAX_LEN]; + + struct rte_rxtx_callback *rx_cb[RTE_MAX_QUEUES_PER_PORT]; + + TAILQ_ENTRY(interface) next; +}; + +TAILQ_HEAD(interface_list, interface); +struct interface_list interfaces = TAILQ_HEAD_INITIALIZER(interfaces); + +static struct interface *port2intf[RTE_MAX_ETHPORTS]; + +static void usage(void) +{ + printf("Usage: %s [options] ...\n\n", prog); + printf("Interface:\n" + " -i name or port index of interface\n" + " -f packet filter in libpcap filter syntax\n" + " -s packet snapshot length (default: infinite)\n" + " -p don't put interface in promiscuous mode\n" + " -D print list of interfaces and exit\n" + " -d print generated BPF code for capture filter\n" + " -S print statistics for each interface\n\n"); + printf("Stop condition:\n" + " -c stop after N packets (default: infinite)\n\n"); + printf("Output file:\n" + " -w name of file to save (default: tempfile)\n" + " -g enable group read access of output file\n" + " -n use pcapng format instead of pcap (default)\n" + " -P use libpcap format instead of pcapng\n" + " --capture-comment \n" + " add capture comment to output file\n"); + printf("Miscellaneous\n" + " -N maximum number of packets buffered (default: %u)\n", + ring_size); + printf(" -q don't report packet capture counts\n" + " -v print version information and exit\n" + " -h display this help and exit\n"); +} + +static void version(void) +{ + printf("%s 1.0 (DPDK %s)\n", prog, rte_version()); +} + +/* Parse numeric argument from command line */ +static unsigned int get_uint(const char *arg, const char *name, + unsigned int limit) +{ + unsigned long u; + char *endp; + + u = strtoul(arg, &endp, 0); + if (*arg == '\0' || *endp != '\0') + rte_exit(EXIT_FAILURE, + "Specified %s \"%s\" is not a valid number\n", + name, arg); + if (limit && u > limit) + rte_exit(EXIT_FAILURE, + "Specified %s \"%s\" is too large (greater than %u)\n", + name, arg, limit); + + return u; +} + +/* Add interface to list of interfaces to capture */ +static void add_interface(uint16_t port, const char *name) +{ + struct interface *intf; + + intf = malloc(sizeof(*intf)); + if (!intf) + rte_exit(EXIT_FAILURE, "no memory for interface\n"); + + memset(intf, 0, sizeof(*intf)); + strlcpy(intf->name, name, sizeof(intf->name)); + + printf("Capturing on '%s'\n", name); + + port2intf[port] = intf; + TAILQ_INSERT_TAIL(&interfaces, intf, next); +} + +/* Select all valid DPDK interfaces */ +static void select_all_interfaces(void) +{ + char name[RTE_ETH_NAME_MAX_LEN]; + uint16_t p; + + RTE_ETH_FOREACH_DEV(p) { + if (rte_eth_dev_get_name_by_port(p, name) < 0) + continue; + add_interface(p, name); + } +} + +/* + * Choose interface to capture if no -i option given. + * Select the first DPDK port, this matches what dumpcap does. + */ +static void set_default_interface(void) +{ + char name[RTE_ETH_NAME_MAX_LEN]; + uint16_t p; + + RTE_ETH_FOREACH_DEV(p) { + if (rte_eth_dev_get_name_by_port(p, name) < 0) + continue; + add_interface(p, name); + return; + } + rte_exit(EXIT_FAILURE, "No usable interfaces found\n"); +} + +/* Lookup interface by name or port and add it to the list */ +static void select_interface(const char *arg) +{ + uint16_t port; + + if (strcmp(arg, "*")) + select_all_interfaces(); + else if (rte_eth_dev_get_port_by_name(arg, &port) == 0) + add_interface(port, arg); + else { + char name[RTE_ETH_NAME_MAX_LEN]; + + port = get_uint(arg, "port_number", UINT16_MAX); + if (rte_eth_dev_get_name_by_port(port, name) < 0) + rte_exit(EXIT_FAILURE, "Invalid port number %u\n", + port); + add_interface(port, name); + } +} + +/* Display list of possible interfaces that can be used. */ +static void show_interfaces(void) +{ + char name[RTE_ETH_NAME_MAX_LEN]; + uint16_t p; + + RTE_ETH_FOREACH_DEV(p) { + if (rte_eth_dev_get_name_by_port(p, name) < 0) + continue; + printf("%u. %s\n", p, name); + } +} + +static struct bpf_insn *compile_filter(uint32_t *len) +{ + struct bpf_program fcode; + pcap_t *pcap; + void *fmem; + size_t sz; + + pcap = pcap_open_dead(DLT_EN10MB, snaplen); + if (!pcap) + rte_exit(EXIT_FAILURE, "can not open pcap\n"); + + if (pcap_compile(pcap, &fcode, filter_str, + 1, PCAP_NETMASK_UNKNOWN) != 0) + rte_exit(EXIT_FAILURE, "pcap filter string not valid (%s)\n", + pcap_geterr(pcap)); + + /* + * Need to put filter in shared memory where it can + * be read by primary process. + */ + *len = fcode.bf_len; + sz = fcode.bf_len * sizeof(struct bpf_insn); + fmem = rte_malloc("pcap_filter", sz, 0); + if (!fmem) + rte_exit(EXIT_FAILURE, "rte_malloc for filter failed\n"); + + rte_memcpy(fmem, fcode.bf_insns, sz); + pcap_freecode(&fcode); + pcap_close(pcap); + + return fmem; +} + +static void dump_filter(const struct bpf_insn *insn, uint32_t len) +{ + unsigned int i; + + if (insn == NULL) + rte_exit(EXIT_FAILURE, "no filter specified\n"); + + for (i = 0; i < len; insn++, i++) + printf("%s\n", bpf_image(insn, i)); + + exit(0); +} + +/* + * Parse command line options. + * These are chosen to be similar to dumpcap command. + */ +static void parse_opts(int argc, char **argv) +{ + static const struct option long_options[] = { + { "capture-comment", required_argument, NULL, 0 }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { NULL }, + }; + int option_index, c; + + for (;;) { + c = getopt_long(argc, argv, "i:f:ds:c:w:gN:pqvhDnP", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 0: + switch (option_index) { + case 0: + capture_comment = optarg; + break; + default: + usage(); + exit(1); + } + break; + case 'i': + select_interface(optarg); + break; + case 'f': + filter_str = optarg; + break; + case 'd': + dump_bpf = true; + break; + case 's': + snaplen = get_uint(optarg, "snap_len", 0); + break; + case 'D': + show_interfaces(); + exit(0); + case 'c': + packet_count = get_uint(optarg, "packet_count", 0); + break; + case 'w': + output_name = optarg; + break; + case 'g': + group_read = true; + break; + case 'N': + ring_size = get_uint(optarg, "packet_limit", 0); + break; + case 'p': + promiscuous_mode = false; + break; + case 'q': + quiet = true; + break; + case 'n': + use_pcapng = true; + break; + case 'P': + use_pcapng = false; + break; + case 'v': + version(); + exit(0); + case 'h': + usage(); + exit(0); + default: + fprintf(stderr, "Invalid option: %s", argv[optind - 1]); + usage(); + exit(1); + } + } +} + +static void +signal_handler(int sig_num __rte_unused) +{ + quit_signal = 1; +} + +static void +cleanup_pdump_resources(void) +{ + struct interface *intf; + + TAILQ_FOREACH(intf, &interfaces, next) { + rte_pdump_disable(intf->port, + RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX); + if (promiscuous_mode) + rte_eth_promiscuous_disable(intf->port); + } +} + +/* Alarm signal handler, used to check that primary process */ +static void +monitor_primary(void *arg __rte_unused) +{ + if (quit_signal) + return; + + if (rte_eal_primary_proc_alive(NULL)) { + rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL); + return; + } + + fprintf(stderr, "Primary process is no longer active, exiting...\n"); + quit_signal = 1; +} + +/* Setup handler to check when primary exits. */ +static void +enable_primary_monitor(void) +{ + int ret; + + /* Once primary exits, so will pdump. */ + ret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL); + if (ret < 0) + fprintf(stderr, "Fail to enable monitor:%d\n", ret); +} + +static void +disable_primary_monitor(void) +{ + int ret; + + ret = rte_eal_alarm_cancel(monitor_primary, NULL); + if (ret < 0) + fprintf(stderr, "Fail to disable monitor:%d\n", ret); +} + +static void +print_pdump_stats(void) +{ + struct interface *intf; + + fputc('\n', stderr); + TAILQ_FOREACH(intf, &interfaces, next) { + fprintf(stderr, "Packets received/dropped on interface '%s': " + "%"PRIu64 "/%" PRIu64 "\n", intf->name, + intf->received, intf->dropped); + } +} + +/* + * Start DPDK EAL with arguments. + * Unlike most DPDK programs, for usabilty, + * the arguments to EAL do not come from user command line. + */ +static void dpdk_init(void) +{ + const char *args[] = { + prog, + "--log-level", "error", + "--proc-type", "secondary", + }; + int eal_argc = RTE_DIM(args); + char **eal_argv; + size_t i; + + /* Make a mutable copy of args because... */ + eal_argv = calloc(sizeof(char *), RTE_DIM(args) + 1); + if (!eal_argv) + rte_exit(EXIT_FAILURE, "EAL arg alloc failed\n"); + + for (i = 0; i < RTE_DIM(args); i++) + eal_argv[i] = strdup(args[i]); + + if (rte_eal_init(eal_argc, eal_argv) < 0) + rte_panic("EAL init failed\n"); + + if (rte_eth_dev_count_avail() == 0) + rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); +} + +/* Create packet ring shared between callbacks and process */ +static struct rte_ring *create_ring(void) +{ + struct rte_ring *pring; + size_t size, log2; + + /* Find next power of 2 >= size. */ + size = ring_size; + log2 = sizeof(size) * 8 - __builtin_clzl(size - 1); + size = 1u << log2; + + if (size != ring_size) { + fprintf(stderr, "Ring size %u rounded up to %zu\n", + ring_size, size); + ring_size = size; + } + + pring = rte_ring_lookup(RING_NAME); + if (pring == NULL) { + pring = rte_ring_create(RING_NAME, ring_size, + rte_socket_id(), 0); + if (pring == NULL) + rte_exit(EXIT_FAILURE, "Could not create ring :%s\n", + rte_strerror(rte_errno)); + } + return pring; +} + +static struct rte_mempool *create_mempool(void) +{ + static const char pool_name[] = "capture_mbufs"; + size_t num_mbufs = 2 * ring_size; + struct rte_mempool *mp; + + mp = rte_mempool_lookup(pool_name); + if (mp) + return mp; + + mp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs, + MBUF_POOL_CACHE_SIZE, 0, + RTE_MIN(snaplen, + RTE_MBUF_DEFAULT_BUF_SIZE), + rte_socket_id(), "ring_mp_sc"); + if (mp == NULL) + rte_exit(EXIT_FAILURE, + "Mempool (%s) creation failed: %s\n", pool_name, + rte_strerror(rte_errno)); + + return mp; +} + +static rte_pcapng_t *create_output(void) +{ + int fd; + + /* If no filename specified make a tempfile name */ + if (output_name == NULL) { + struct interface *intf; + struct tm *tm; + time_t now; + char ts[32]; + + intf = TAILQ_FIRST(&interfaces); + now = time(NULL); + tm = localtime(&now); + if (!tm) + rte_panic("localtime failed\n"); + + strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", tm); + if (asprintf(&output_name, "/tmp/%s_%u_%s_%s.pcapng", + prog, intf->port, intf->name, ts) < 0) + rte_panic("asprintf failed\n"); + } + + if (strcmp(output_name, "-") == 0) + fd = STDOUT_FILENO; + else { + mode_t mode = group_read ? 0640 : 0600; + + fd = open(output_name, O_WRONLY | O_APPEND | O_CREAT, mode); + if (fd < 0) + rte_exit(EXIT_FAILURE, "Can not open \"%s\": %s\n", + output_name, strerror(errno)); + } + + return rte_pcapng_fdopen(fd, NULL, NULL, prog, capture_comment, 0); +} + +/* + * Take list of interfaces (from command line) + * and put records for them at start of capture file. + */ +static void dump_interfaces(rte_pcapng_t *out) +{ + struct interface *intf; + + TAILQ_FOREACH(intf, &interfaces, next) + rte_pcapng_add_interface(out, intf->port, NULL, 0); +} + +static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp, + const struct bpf_insn *filter, uint32_t filter_len) +{ + struct interface *intf; + int ret; + + TAILQ_FOREACH(intf, &interfaces, next) { + if (promiscuous_mode) + rte_eth_promiscuous_enable(intf->port); + + ret = rte_pdump_enable(intf->port, + RTE_PDUMP_ALL_QUEUES, + snaplen, + RTE_PDUMP_FLAG_RXTX, + snaplen, + r, mp, filter, filter_len); + if (ret < 0) + rte_exit(EXIT_FAILURE, + "Packet dump enable failed: %s\n", + rte_strerror(rte_errno)); + } +} + +/* + * Show current count of captured packets + * with backspaces to overwrite last value. + */ +static void show_count(uint64_t count) +{ + unsigned int i; + static unsigned int bt; + + for (i = 0; i < bt; i++) + fputc('\b', stderr); + + bt = fprintf(stderr, "%"PRIu64" ", count); +} + +/* Process all packets in ring and dump to capture file */ +static void process_ring(rte_pcapng_t *out, struct rte_ring *r) +{ + struct rte_mbuf *pkts[BURST_SIZE]; + unsigned int i, avail, n; + static unsigned int empty_count; + + n = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE, + &avail); + if (n == 0) { + /* don't consume endless amounts of cpu if idle */ + if (empty_count < SLEEP_THRESHOLD) + ++empty_count; + else + usleep(10); + return; + } + + empty_count = (avail == 0); + + for (i = 0; i < n; i++) { + struct rte_mbuf *m = pkts[i]; + struct interface *intf; + + intf = port2intf[m->port]; + if (likely(intf)) { + rte_pcapng_dump_packet(out, m->port, m, + RTE_PCAPNG_DIR_UNKNOWN, NULL); + ++intf->received; + } + rte_pktmbuf_free(m); + } + + packets_received += n; + + if (!quiet) + show_count(packets_received); +} + +int main(int argc, char **argv) +{ + struct rte_ring *r; + struct rte_mempool *mp; + rte_pcapng_t *out; + struct bpf_insn *bpf_filter = NULL; + uint32_t bpf_len = 0; + + prog = basename(argv[0]); + dpdk_init(); + + parse_opts(argc, argv); + + if (filter_str) + bpf_filter = compile_filter(&bpf_len); + + if (dump_bpf) + dump_filter(bpf_filter, bpf_len); + + if (TAILQ_EMPTY(&interfaces)) + set_default_interface(); + + r = create_ring(); + mp = create_mempool(); + out = create_output(); + if (out == NULL) + rte_exit(EXIT_FAILURE, "can not open output file: %s\n", + rte_strerror(rte_errno)); + + dump_interfaces(out); + + enable_pdump(r, mp, bpf_filter, bpf_len); + + signal(SIGINT, signal_handler); + signal(SIGPIPE, SIG_IGN); + + enable_primary_monitor(); + + if (!quiet) { + fprintf(stderr, "Packets captured: "); + show_count(0); + } + + while (!quit_signal) { + process_ring(out, r); + + if (packet_count != 0 && packets_received >= packet_count) + break; + } + + disable_primary_monitor(); + + print_pdump_stats(); + + rte_pcapng_close(out); + + cleanup_pdump_resources(); + rte_free(bpf_filter); + rte_ring_free(r); + rte_mempool_free(mp); + + return rte_eal_cleanup() ? EXIT_FAILURE : 0; +} diff --git a/app/capture/meson.build b/app/capture/meson.build new file mode 100644 index 000000000000..9558f10562bd --- /dev/null +++ b/app/capture/meson.build @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2019 Microsoft Corporation +pcap_dep = dependency('pcap', required: false) +if pcap_dep.found() + build = true +else + # pcap got a pkg-config file only in 1.9.0 and before that meson uses + # an internal pcap-config finder, which is not compatible with + # cross-compilation, so try to fallback to find_library + pcap_dep = cc.find_library('pcap', required: false) + if pcap_dep.found() and cc.has_header('pcap.h', dependencies: pcap_dep) + build = true + pkgconfig_extra_libs += '-lpcap' + else + build = false + reason = 'missing dependency, "libpcap"' + endif +endif + +sources = files('main.c') +ext_deps += pcap_dep +deps += ['ethdev', 'pdump', 'pcapng'] diff --git a/app/meson.build b/app/meson.build index b0e6afbbe9d9..a33198182133 100644 --- a/app/meson.build +++ b/app/meson.build @@ -6,6 +6,7 @@ if is_windows endif apps = [ + 'capture', 'pdump', 'proc-info', 'test-acl', diff --git a/config/common_base b/config/common_base index 0ccfcfae377d..e1bab8e77988 100644 --- a/config/common_base +++ b/config/common_base @@ -1073,3 +1073,8 @@ CONFIG_RTE_APP_CRYPTO_PERF=y # Compile the eventdev application # CONFIG_RTE_APP_EVENTDEV=y + +# +# Compile the capture application +# +CONFIG_RTE_APP_CAPTURE=n -- 2.20.1