get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/60652/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 60652,
    "url": "https://patches.dpdk.org/api/patches/60652/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20191007165232.14535-9-stephen@networkplumber.org/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20191007165232.14535-9-stephen@networkplumber.org>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20191007165232.14535-9-stephen@networkplumber.org",
    "date": "2019-10-07T16:52:32",
    "name": "[RFC,8/8] app/capture: add packet capture using pcapng",
    "commit_ref": null,
    "pull_url": null,
    "state": "changes-requested",
    "archived": true,
    "hash": "201d4b8207f5cdb7ed3f42c4f57f15805d2da039",
    "submitter": {
        "id": 27,
        "url": "https://patches.dpdk.org/api/people/27/?format=api",
        "name": "Stephen Hemminger",
        "email": "stephen@networkplumber.org"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20191007165232.14535-9-stephen@networkplumber.org/mbox/",
    "series": [
        {
            "id": 6728,
            "url": "https://patches.dpdk.org/api/series/6728/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=6728",
            "date": "2019-10-07T16:52:24",
            "name": "Packet Capture enhancements",
            "version": 1,
            "mbox": "https://patches.dpdk.org/series/6728/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/60652/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/60652/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id CE0941D41B;\n\tMon,  7 Oct 2019 18:53:00 +0200 (CEST)",
            "from mail-pf1-f196.google.com (mail-pf1-f196.google.com\n\t[209.85.210.196]) by dpdk.org (Postfix) with ESMTP id 0561C1D159\n\tfor <dev@dpdk.org>; Mon,  7 Oct 2019 18:52:52 +0200 (CEST)",
            "by mail-pf1-f196.google.com with SMTP id b128so9054155pfa.1\n\tfor <dev@dpdk.org>; Mon, 07 Oct 2019 09:52:51 -0700 (PDT)",
            "from hermes.lan (204-195-22-127.wavecable.com. [204.195.22.127])\n\tby smtp.gmail.com with ESMTPSA id\n\tw5sm15920979pfn.96.2019.10.07.09.52.48\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 07 Oct 2019 09:52:48 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=networkplumber-org.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=azyukN55pVDW6mlaa8A9WYpGM6aRxYm5VnHp3tcG+D8=;\n\tb=Gc0U+F3JYVQvkEYlKdrWqIH3HpEdgIVOGVB6VjwPyE+2byO7cOLnj+/6PMkC28VynK\n\t/UvuVtqvwzN/YWB6uOxzM2yc/tYKe+kMsnWHrVEQPGgGKk7HETj47gUnT7oj3byMplLN\n\t1g3GpStNjUFwGVKyosd8KtejhwpaNCQ1hZ68OCsE4nFU/0jxm4Tfb6EUu4xirrKTG3fb\n\tNfpNPxTM/NG2qP3b4xngjxf54C/befjNMLQYvgK3wpdvkO6ZO5qCaR55b0jHvM+eikzr\n\tX7fhkKSCXAbTVSg2658k172Smiu3SUcIE5LyjQy2ce0/okjPdBQixtOvEVtDqLeq2Adn\n\tDoPg==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=azyukN55pVDW6mlaa8A9WYpGM6aRxYm5VnHp3tcG+D8=;\n\tb=Nlc5toKPtIeq7uKGs7czW9/oTZ8esS9qk6CofhU7kZPDlfh0fj0Lz0wVVsoLEw4zia\n\tmdo3egL3PAlWzm46UfwwwGDr4GhKMg887mhqjI9qDB9zKDtnlK/Zjp653RZKoaHHQKZu\n\t/xGhrTJWlTx/pLn14Tx8nzHWIALB41KiRdx309jr/n2r+CT+YDjkiDYGQ9KrLF/aYsdf\n\t/cxwFYgZgJJCZ2evrKj478ep8dYS8QWgXhIAwa11tHNflxiU8GEsSpnFLM0NThaViYOd\n\tNd47POBEOBh+bViC+Iv1XoiOFZBLToQJTeUahqbrNQsL9LPdvkzBRcKEcDMBXUIMTh/o\n\t54WA==",
        "X-Gm-Message-State": "APjAAAUlazBAewzjtc3KrHWOYKcb9IVv2NWkfcenq7fARUdQUmIkEoq8\n\tFSwgYrFmEug7fw1aer14zifodsPcD4CtIw==",
        "X-Google-Smtp-Source": "APXvYqy3emsWbFW+J8yLMMTd8GBoGPoB4/PFSuUamBaF2XAFOxGByyHbeYO0I8yaf5q5KlvnitfcuA==",
        "X-Received": "by 2002:a63:5d07:: with SMTP id r7mr6016641pgb.318.1570467169462;\n\tMon, 07 Oct 2019 09:52:49 -0700 (PDT)",
        "From": "Stephen Hemminger <stephen@networkplumber.org>",
        "To": "dev@dpdk.org",
        "Cc": "Stephen Hemminger <stephen@networkplumber.org>",
        "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 <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "New application (dpdk-capture) with syntax analogous to tshark's\ndumpcap command. It runs as a secondary process and produces\ncapture output in pcapng format.\n\nIt does not use DPDK style EAL arguments; instead the flags\nare meant to be the same as dumpcap.\n\nThe program depends on libpcap since it uses the pcap_compile()\nfunction to compile a string into a BPF program.\n\nSigned-off-by: Stephen Hemminger <stephen@networkplumber.org>\n---\n app/Makefile            |   1 +\n app/capture/Makefile    |  19 ++\n app/capture/main.c      | 675 ++++++++++++++++++++++++++++++++++++++++\n app/capture/meson.build |  22 ++\n app/meson.build         |   1 +\n config/common_base      |   5 +\n 6 files changed, 723 insertions(+)\n create mode 100644 app/capture/Makefile\n create mode 100644 app/capture/main.c\n create mode 100644 app/capture/meson.build",
    "diff": "diff --git a/app/Makefile b/app/Makefile\nindex 28acbceca904..509cd7f4de13 100644\n--- a/app/Makefile\n+++ b/app/Makefile\n@@ -7,6 +7,7 @@ DIRS-$(CONFIG_RTE_APP_TEST) += test\n DIRS-$(CONFIG_RTE_TEST_PMD) += test-pmd\n DIRS-$(CONFIG_RTE_PROC_INFO) += proc-info\n DIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += pdump\n+DIRS-$(CONFIG_RTE_APP_CAPTURE) += capture\n DIRS-$(CONFIG_RTE_LIBRTE_ACL) += test-acl\n DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += test-cmdline\n DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += test-pipeline\ndiff --git a/app/capture/Makefile b/app/capture/Makefile\nnew file mode 100644\nindex 000000000000..78ff7d2e97bf\n--- /dev/null\n+++ b/app/capture/Makefile\n@@ -0,0 +1,19 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2019 Microsoft Corporation\n+\n+include $(RTE_SDK)/mk/rte.vars.mk\n+\n+ifeq ($(CONFIG_RTE_LIBRTE_PCAPNG),y)\n+\n+APP = dpdk-capture\n+\n+CFLAGS += -DALLOW_EXPERIMENTAL_API\n+CFLAGS += -O3\n+CFLAGS += $(WERROR_FLAGS)\n+LDLIBS += -lpcap\n+\n+SRCS-y := main.c\n+\n+include $(RTE_SDK)/mk/rte.app.mk\n+\n+endif\ndiff --git a/app/capture/main.c b/app/capture/main.c\nnew file mode 100644\nindex 000000000000..394c1edcc01b\n--- /dev/null\n+++ b/app/capture/main.c\n@@ -0,0 +1,675 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2019 Microsoft Corporation\n+ */\n+\n+#include <stdio.h>\n+#include <stdint.h>\n+#include <stdlib.h>\n+#include <signal.h>\n+#include <string.h>\n+#include <inttypes.h>\n+#include <stdbool.h>\n+#include <unistd.h>\n+#include <getopt.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <sys/utsname.h>\n+#include <fcntl.h>\n+#include <sys/queue.h>\n+#include <net/if.h>\n+\n+#include <rte_eal.h>\n+#include <rte_version.h>\n+#include <rte_alarm.h>\n+#include <rte_ether.h>\n+#include <rte_ethdev.h>\n+#include <rte_mempool.h>\n+#include <rte_pdump.h>\n+#include <rte_string_fns.h>\n+#include <rte_malloc.h>\n+#include <rte_pcapng.h>\n+\n+#include <pcap/pcap.h>\n+\n+#define RING_NAME \"capture-ring\"\n+#define MONITOR_INTERVAL  (500 * 1000)\n+#define MBUF_POOL_CACHE_SIZE 32\n+#define BURST_SIZE 32\n+#define SLEEP_THRESHOLD 1000\n+\n+static const char *prog;\n+static volatile bool quit_signal;\n+static bool group_read;\n+static bool quiet;\n+static bool promiscuous_mode = true;\n+static bool use_pcapng = true;\n+static char *output_name;\n+static const char *filter_str;\n+static unsigned int ring_size = 2048;\n+static uint64_t packet_count, packets_received;\n+static const char *capture_comment;\n+static uint16_t snaplen = UINT16_MAX;\n+static bool dump_bpf;\n+\n+struct interface {\n+\tuint64_t received;\n+\tuint64_t dropped;\n+\tuint16_t port;\n+\tchar name[RTE_ETH_NAME_MAX_LEN];\n+\n+\tstruct rte_rxtx_callback *rx_cb[RTE_MAX_QUEUES_PER_PORT];\n+\n+\tTAILQ_ENTRY(interface) next;\n+};\n+\n+TAILQ_HEAD(interface_list, interface);\n+struct interface_list interfaces = TAILQ_HEAD_INITIALIZER(interfaces);\n+\n+static struct interface *port2intf[RTE_MAX_ETHPORTS];\n+\n+static void usage(void)\n+{\n+\tprintf(\"Usage: %s [options] ...\\n\\n\", prog);\n+\tprintf(\"Interface:\\n\"\n+\t       \"  -i <interface>         name or port index of interface\\n\"\n+\t       \"  -f <capture filter>    packet filter in libpcap filter syntax\\n\"\n+\t       \"  -s <snaplen>           packet snapshot length (default: infinite)\\n\"\n+\t       \"  -p                     don't put interface in promiscuous mode\\n\"\n+\t       \"  -D                     print list of interfaces and exit\\n\"\n+\t       \"  -d                     print generated BPF code for capture filter\\n\"\n+\t       \"  -S                     print statistics for each interface\\n\\n\");\n+\tprintf(\"Stop condition:\\n\"\n+\t       \"  -c <packet count>      stop after N packets (default: infinite)\\n\\n\");\n+\tprintf(\"Output file:\\n\"\n+\t       \"  -w <filename>          name of file to save (default: tempfile)\\n\"\n+\t       \"  -g                     enable group read access of output file\\n\"\n+\t       \"  -n                     use pcapng format instead of pcap (default)\\n\"\n+\t       \"  -P                     use libpcap format instead of pcapng\\n\"\n+\t       \"  --capture-comment <comment>\\n\"\n+\t       \"                         add capture comment to output file\\n\");\n+\tprintf(\"Miscellaneous\\n\"\n+\t       \"  -N <packet limit>      maximum number of packets buffered (default: %u)\\n\",\n+\t       ring_size);\n+\tprintf(\"  -q                     don't report packet capture counts\\n\"\n+\t       \"  -v                     print version information and exit\\n\"\n+\t       \"  -h                     display this help and exit\\n\");\n+}\n+\n+static void version(void)\n+{\n+\tprintf(\"%s 1.0 (DPDK %s)\\n\", prog, rte_version());\n+}\n+\n+/* Parse numeric argument from command line */\n+static unsigned int get_uint(const char *arg, const char *name,\n+\t\t\t     unsigned int limit)\n+{\n+\tunsigned long u;\n+\tchar *endp;\n+\n+\tu = strtoul(arg, &endp, 0);\n+\tif (*arg == '\\0' || *endp != '\\0')\n+\t\trte_exit(EXIT_FAILURE,\n+\t\t\t \"Specified %s \\\"%s\\\" is not a valid number\\n\",\n+\t\t\t name, arg);\n+\tif (limit && u > limit)\n+\t\trte_exit(EXIT_FAILURE,\n+\t\t\t \"Specified %s \\\"%s\\\" is too large (greater than %u)\\n\",\n+\t\t\t name, arg, limit);\n+\n+\treturn u;\n+}\n+\n+/* Add interface to list of interfaces to capture */\n+static void add_interface(uint16_t port, const char *name)\n+{\n+\tstruct interface *intf;\n+\n+\tintf = malloc(sizeof(*intf));\n+\tif (!intf)\n+\t\trte_exit(EXIT_FAILURE, \"no memory for interface\\n\");\n+\n+\tmemset(intf, 0, sizeof(*intf));\n+\tstrlcpy(intf->name, name, sizeof(intf->name));\n+\n+\tprintf(\"Capturing on '%s'\\n\", name);\n+\n+\tport2intf[port] = intf;\n+\tTAILQ_INSERT_TAIL(&interfaces, intf, next);\n+}\n+\n+/* Select all valid DPDK interfaces */\n+static void select_all_interfaces(void)\n+{\n+\tchar name[RTE_ETH_NAME_MAX_LEN];\n+\tuint16_t p;\n+\n+\tRTE_ETH_FOREACH_DEV(p) {\n+\t\tif (rte_eth_dev_get_name_by_port(p, name) < 0)\n+\t\t\tcontinue;\n+\t\tadd_interface(p, name);\n+\t}\n+}\n+\n+/*\n+ * Choose interface to capture if no -i option given.\n+ * Select the first DPDK port, this matches what dumpcap does.\n+ */\n+static void set_default_interface(void)\n+{\n+\tchar name[RTE_ETH_NAME_MAX_LEN];\n+\tuint16_t p;\n+\n+\tRTE_ETH_FOREACH_DEV(p) {\n+\t\tif (rte_eth_dev_get_name_by_port(p, name) < 0)\n+\t\t\tcontinue;\n+\t\tadd_interface(p, name);\n+\t\treturn;\n+\t}\n+\trte_exit(EXIT_FAILURE, \"No usable interfaces found\\n\");\n+}\n+\n+/* Lookup interface by name or port and add it to the list */\n+static void select_interface(const char *arg)\n+{\n+\tuint16_t port;\n+\n+\tif (strcmp(arg, \"*\"))\n+\t\tselect_all_interfaces();\n+\telse if (rte_eth_dev_get_port_by_name(arg, &port) == 0)\n+\t\tadd_interface(port, arg);\n+\telse {\n+\t\tchar name[RTE_ETH_NAME_MAX_LEN];\n+\n+\t\tport = get_uint(arg, \"port_number\", UINT16_MAX);\n+\t\tif (rte_eth_dev_get_name_by_port(port, name) < 0)\n+\t\t\trte_exit(EXIT_FAILURE, \"Invalid port number %u\\n\",\n+\t\t\t\t port);\n+\t\tadd_interface(port, name);\n+\t}\n+}\n+\n+/* Display list of possible interfaces that can be used. */\n+static void show_interfaces(void)\n+{\n+\tchar name[RTE_ETH_NAME_MAX_LEN];\n+\tuint16_t p;\n+\n+\tRTE_ETH_FOREACH_DEV(p) {\n+\t\tif (rte_eth_dev_get_name_by_port(p, name) < 0)\n+\t\t\tcontinue;\n+\t\tprintf(\"%u. %s\\n\", p, name);\n+\t}\n+}\n+\n+static struct bpf_insn *compile_filter(uint32_t *len)\n+{\n+\tstruct bpf_program fcode;\n+\tpcap_t *pcap;\n+\tvoid *fmem;\n+\tsize_t sz;\n+\n+\tpcap = pcap_open_dead(DLT_EN10MB, snaplen);\n+\tif (!pcap)\n+\t\trte_exit(EXIT_FAILURE, \"can not open pcap\\n\");\n+\n+\tif (pcap_compile(pcap, &fcode, filter_str,\n+\t\t\t 1, PCAP_NETMASK_UNKNOWN) != 0)\n+\t\trte_exit(EXIT_FAILURE, \"pcap filter string not valid (%s)\\n\",\n+\t\t\t pcap_geterr(pcap));\n+\n+\t/*\n+\t * Need to put filter in shared memory where it can\n+\t * be read by primary process.\n+\t */\n+\t*len = fcode.bf_len;\n+\tsz = fcode.bf_len * sizeof(struct bpf_insn);\n+\tfmem = rte_malloc(\"pcap_filter\", sz, 0);\n+\tif (!fmem)\n+\t\trte_exit(EXIT_FAILURE, \"rte_malloc for filter failed\\n\");\n+\n+\trte_memcpy(fmem, fcode.bf_insns, sz);\n+\tpcap_freecode(&fcode);\n+\tpcap_close(pcap);\n+\n+\treturn fmem;\n+}\n+\n+static void dump_filter(const struct bpf_insn *insn, uint32_t len)\n+{\n+\tunsigned int i;\n+\n+\tif (insn == NULL)\n+\t\trte_exit(EXIT_FAILURE, \"no filter specified\\n\");\n+\n+\tfor (i = 0; i < len; insn++, i++)\n+\t\tprintf(\"%s\\n\", bpf_image(insn, i));\n+\n+\texit(0);\n+}\n+\n+/*\n+ * Parse command line options.\n+ * These are chosen to be similar to dumpcap command.\n+ */\n+static void parse_opts(int argc, char **argv)\n+{\n+\tstatic const struct option long_options[] = {\n+\t\t{ \"capture-comment\", required_argument, NULL, 0 },\n+\t\t{ \"help\",    no_argument, NULL, 'h' },\n+\t\t{ \"version\", no_argument, NULL, 'v' },\n+\t\t{ NULL },\n+\t};\n+\tint option_index, c;\n+\n+\tfor (;;) {\n+\t\tc = getopt_long(argc, argv, \"i:f:ds:c:w:gN:pqvhDnP\",\n+\t\t\t\tlong_options, &option_index);\n+\t\tif (c == -1)\n+\t\t\tbreak;\n+\n+\t\tswitch (c) {\n+\t\tcase 0:\n+\t\t\tswitch (option_index) {\n+\t\t\tcase 0:\n+\t\t\t\tcapture_comment = optarg;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\tusage();\n+\t\t\t\texit(1);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase 'i':\n+\t\t\tselect_interface(optarg);\n+\t\t\tbreak;\n+\t\tcase 'f':\n+\t\t\tfilter_str = optarg;\n+\t\t\tbreak;\n+\t\tcase 'd':\n+\t\t\tdump_bpf = true;\n+\t\t\tbreak;\n+\t\tcase 's':\n+\t\t\tsnaplen = get_uint(optarg, \"snap_len\", 0);\n+\t\t\tbreak;\n+\t\tcase 'D':\n+\t\t\tshow_interfaces();\n+\t\t\texit(0);\n+\t\tcase 'c':\n+\t\t\tpacket_count = get_uint(optarg, \"packet_count\", 0);\n+\t\t\tbreak;\n+\t\tcase 'w':\n+\t\t\toutput_name = optarg;\n+\t\t\tbreak;\n+\t\tcase 'g':\n+\t\t\tgroup_read = true;\n+\t\t\tbreak;\n+\t\tcase 'N':\n+\t\t\tring_size = get_uint(optarg, \"packet_limit\", 0);\n+\t\t\tbreak;\n+\t\tcase 'p':\n+\t\t\tpromiscuous_mode = false;\n+\t\t\tbreak;\n+\t\tcase 'q':\n+\t\t\tquiet = true;\n+\t\t\tbreak;\n+\t\tcase 'n':\n+\t\t\tuse_pcapng = true;\n+\t\t\tbreak;\n+\t\tcase 'P':\n+\t\t\tuse_pcapng = false;\n+\t\t\tbreak;\n+\t\tcase 'v':\n+\t\t\tversion();\n+\t\t\texit(0);\n+\t\tcase 'h':\n+\t\t\tusage();\n+\t\t\texit(0);\n+\t\tdefault:\n+\t\t\tfprintf(stderr, \"Invalid option: %s\", argv[optind - 1]);\n+\t\t\tusage();\n+\t\t\texit(1);\n+\t\t}\n+\t}\n+}\n+\n+static void\n+signal_handler(int sig_num __rte_unused)\n+{\n+\tquit_signal = 1;\n+}\n+\n+static void\n+cleanup_pdump_resources(void)\n+{\n+\tstruct interface *intf;\n+\n+\tTAILQ_FOREACH(intf, &interfaces, next) {\n+\t\trte_pdump_disable(intf->port,\n+\t\t\t\t  RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX);\n+\t\tif (promiscuous_mode)\n+\t\t\trte_eth_promiscuous_disable(intf->port);\n+\t}\n+}\n+\n+/* Alarm signal handler, used to check that primary process */\n+static void\n+monitor_primary(void *arg __rte_unused)\n+{\n+\tif (quit_signal)\n+\t\treturn;\n+\n+\tif (rte_eal_primary_proc_alive(NULL)) {\n+\t\trte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);\n+\t\treturn;\n+\t}\n+\n+\tfprintf(stderr, \"Primary process is no longer active, exiting...\\n\");\n+\tquit_signal = 1;\n+}\n+\n+/* Setup handler to check when primary exits. */\n+static void\n+enable_primary_monitor(void)\n+{\n+\tint ret;\n+\n+\t/* Once primary exits, so will pdump. */\n+\tret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);\n+\tif (ret < 0)\n+\t\tfprintf(stderr, \"Fail to enable monitor:%d\\n\", ret);\n+}\n+\n+static void\n+disable_primary_monitor(void)\n+{\n+\tint ret;\n+\n+\tret = rte_eal_alarm_cancel(monitor_primary, NULL);\n+\tif (ret < 0)\n+\t\tfprintf(stderr, \"Fail to disable monitor:%d\\n\", ret);\n+}\n+\n+static void\n+print_pdump_stats(void)\n+{\n+\tstruct interface *intf;\n+\n+\tfputc('\\n', stderr);\n+\tTAILQ_FOREACH(intf, &interfaces, next) {\n+\t\tfprintf(stderr, \"Packets received/dropped on interface '%s': \"\n+\t\t       \"%\"PRIu64 \"/%\" PRIu64 \"\\n\", intf->name,\n+\t\t       intf->received, intf->dropped);\n+\t}\n+}\n+\n+/*\n+ * Start DPDK EAL with arguments.\n+ * Unlike most DPDK programs, for usabilty,\n+ * the arguments to EAL do not come from user command line.\n+ */\n+static void dpdk_init(void)\n+{\n+\tconst char *args[] = {\n+\t\tprog,\n+\t\t\"--log-level\", \"error\",\n+\t\t\"--proc-type\", \"secondary\",\n+\t};\n+\tint eal_argc = RTE_DIM(args);\n+\tchar **eal_argv;\n+\tsize_t i;\n+\n+\t/* Make a mutable copy of args because... */\n+\teal_argv = calloc(sizeof(char *), RTE_DIM(args) + 1);\n+\tif (!eal_argv)\n+\t\trte_exit(EXIT_FAILURE, \"EAL arg alloc failed\\n\");\n+\n+\tfor (i = 0; i < RTE_DIM(args); i++)\n+\t\teal_argv[i] = strdup(args[i]);\n+\n+\tif (rte_eal_init(eal_argc, eal_argv) < 0)\n+\t\trte_panic(\"EAL init failed\\n\");\n+\n+\tif (rte_eth_dev_count_avail() == 0)\n+\t\trte_exit(EXIT_FAILURE, \"No Ethernet ports - bye\\n\");\n+}\n+\n+/* Create packet ring shared between callbacks and process */\n+static struct rte_ring *create_ring(void)\n+{\n+\tstruct rte_ring *pring;\n+\tsize_t size, log2;\n+\n+\t/* Find next power of 2 >= size. */\n+\tsize = ring_size;\n+\tlog2 = sizeof(size) * 8 - __builtin_clzl(size - 1);\n+\tsize = 1u << log2;\n+\n+\tif (size != ring_size) {\n+\t\tfprintf(stderr, \"Ring size %u rounded up to %zu\\n\",\n+\t\t\tring_size, size);\n+\t\tring_size = size;\n+\t}\n+\n+\tpring = rte_ring_lookup(RING_NAME);\n+\tif (pring == NULL) {\n+\t\tpring = rte_ring_create(RING_NAME, ring_size,\n+\t\t\t\t\trte_socket_id(), 0);\n+\t\tif (pring == NULL)\n+\t\t\trte_exit(EXIT_FAILURE, \"Could not create ring :%s\\n\",\n+\t\t\t\t rte_strerror(rte_errno));\n+\t}\n+\treturn pring;\n+}\n+\n+static struct rte_mempool *create_mempool(void)\n+{\n+\tstatic const char pool_name[] = \"capture_mbufs\";\n+\tsize_t num_mbufs = 2 * ring_size;\n+\tstruct rte_mempool *mp;\n+\n+\tmp = rte_mempool_lookup(pool_name);\n+\tif (mp)\n+\t\treturn mp;\n+\n+\tmp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs,\n+\t\t\t\t\t    MBUF_POOL_CACHE_SIZE, 0,\n+\t\t\t\t\t    RTE_MIN(snaplen,\n+\t\t\t\t\t\t    RTE_MBUF_DEFAULT_BUF_SIZE),\n+\t\t\t\t\t    rte_socket_id(), \"ring_mp_sc\");\n+\tif (mp == NULL)\n+\t\trte_exit(EXIT_FAILURE,\n+\t\t\t \"Mempool (%s) creation failed: %s\\n\", pool_name,\n+\t\t\t rte_strerror(rte_errno));\n+\n+\treturn mp;\n+}\n+\n+static rte_pcapng_t *create_output(void)\n+{\n+\tint fd;\n+\n+\t/* If no filename specified make a tempfile name */\n+\tif (output_name == NULL) {\n+\t\tstruct interface *intf;\n+\t\tstruct tm *tm;\n+\t\ttime_t now;\n+\t\tchar ts[32];\n+\n+\t\tintf = TAILQ_FIRST(&interfaces);\n+\t\tnow = time(NULL);\n+\t\ttm = localtime(&now);\n+\t\tif (!tm)\n+\t\t\trte_panic(\"localtime failed\\n\");\n+\n+\t\tstrftime(ts, sizeof(ts), \"%Y%m%d%H%M%S\", tm);\n+\t\tif (asprintf(&output_name, \"/tmp/%s_%u_%s_%s.pcapng\",\n+\t\t\t     prog, intf->port, intf->name, ts) < 0)\n+\t\t\trte_panic(\"asprintf failed\\n\");\n+\t}\n+\n+\tif (strcmp(output_name, \"-\") == 0)\n+\t\tfd = STDOUT_FILENO;\n+\telse {\n+\t\tmode_t mode = group_read ? 0640 : 0600;\n+\n+\t\tfd = open(output_name, O_WRONLY | O_APPEND | O_CREAT, mode);\n+\t\tif (fd < 0)\n+\t\t\trte_exit(EXIT_FAILURE, \"Can not open \\\"%s\\\": %s\\n\",\n+\t\t\t\t output_name, strerror(errno));\n+\t}\n+\n+\treturn rte_pcapng_fdopen(fd, NULL, NULL, prog, capture_comment, 0);\n+}\n+\n+/*\n+ * Take list of interfaces (from command line)\n+ * and put records for them at start of capture file.\n+ */\n+static void dump_interfaces(rte_pcapng_t *out)\n+{\n+\tstruct interface *intf;\n+\n+\tTAILQ_FOREACH(intf, &interfaces, next)\n+\t\trte_pcapng_add_interface(out, intf->port, NULL, 0);\n+}\n+\n+static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp,\n+\t\t\t const struct bpf_insn *filter, uint32_t filter_len)\n+{\n+\tstruct interface *intf;\n+\tint ret;\n+\n+\tTAILQ_FOREACH(intf, &interfaces, next) {\n+\t\tif (promiscuous_mode)\n+\t\t\trte_eth_promiscuous_enable(intf->port);\n+\n+\t\tret = rte_pdump_enable(intf->port,\n+\t\t\t\t       RTE_PDUMP_ALL_QUEUES,\n+\t\t\t\t       snaplen,\n+\t\t\t\t       RTE_PDUMP_FLAG_RXTX,\n+\t\t\t\t       snaplen,\n+\t\t\t\t       r, mp, filter, filter_len);\n+\t\tif (ret < 0)\n+\t\t\trte_exit(EXIT_FAILURE,\n+\t\t\t\t \"Packet dump enable failed: %s\\n\",\n+\t\t\t\t rte_strerror(rte_errno));\n+\t}\n+}\n+\n+/*\n+ * Show current count of captured packets\n+ * with backspaces to overwrite last value.\n+ */\n+static void show_count(uint64_t count)\n+{\n+\tunsigned int i;\n+\tstatic unsigned int bt;\n+\n+\tfor (i = 0; i < bt; i++)\n+\t\tfputc('\\b', stderr);\n+\n+\tbt = fprintf(stderr, \"%\"PRIu64\" \", count);\n+}\n+\n+/* Process all packets in ring and dump to capture file */\n+static void process_ring(rte_pcapng_t *out, struct rte_ring *r)\n+{\n+\tstruct rte_mbuf *pkts[BURST_SIZE];\n+\tunsigned int i, avail, n;\n+\tstatic unsigned int empty_count;\n+\n+\tn = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE,\n+\t\t\t\t      &avail);\n+\tif (n == 0) {\n+\t\t/* don't consume endless amounts of cpu if idle */\n+\t\tif (empty_count < SLEEP_THRESHOLD)\n+\t\t\t++empty_count;\n+\t\telse\n+\t\t\tusleep(10);\n+\t\treturn;\n+\t}\n+\n+\tempty_count = (avail == 0);\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tstruct rte_mbuf *m = pkts[i];\n+\t\tstruct interface *intf;\n+\n+\t\tintf = port2intf[m->port];\n+\t\tif (likely(intf)) {\n+\t\t\trte_pcapng_dump_packet(out, m->port, m,\n+\t\t\t\t\t       RTE_PCAPNG_DIR_UNKNOWN, NULL);\n+\t\t\t++intf->received;\n+\t\t}\n+\t\trte_pktmbuf_free(m);\n+\t}\n+\n+\tpackets_received += n;\n+\n+\tif (!quiet)\n+\t\tshow_count(packets_received);\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\tstruct rte_ring *r;\n+\tstruct rte_mempool *mp;\n+\trte_pcapng_t *out;\n+\tstruct bpf_insn *bpf_filter = NULL;\n+\tuint32_t bpf_len = 0;\n+\n+\tprog = basename(argv[0]);\n+\tdpdk_init();\n+\n+\tparse_opts(argc, argv);\n+\n+\tif (filter_str)\n+\t\tbpf_filter = compile_filter(&bpf_len);\n+\n+\tif (dump_bpf)\n+\t\tdump_filter(bpf_filter, bpf_len);\n+\n+\tif (TAILQ_EMPTY(&interfaces))\n+\t\tset_default_interface();\n+\n+\tr = create_ring();\n+\tmp = create_mempool();\n+\tout = create_output();\n+\tif (out == NULL)\n+\t\trte_exit(EXIT_FAILURE, \"can not open output file: %s\\n\",\n+\t\t\t rte_strerror(rte_errno));\n+\n+\tdump_interfaces(out);\n+\n+\tenable_pdump(r, mp, bpf_filter, bpf_len);\n+\n+\tsignal(SIGINT, signal_handler);\n+\tsignal(SIGPIPE, SIG_IGN);\n+\n+\tenable_primary_monitor();\n+\n+\tif (!quiet) {\n+\t\tfprintf(stderr, \"Packets captured: \");\n+\t\tshow_count(0);\n+\t}\n+\n+\twhile (!quit_signal) {\n+\t\tprocess_ring(out, r);\n+\n+\t\tif (packet_count != 0 && packets_received >= packet_count)\n+\t\t\tbreak;\n+\t}\n+\n+\tdisable_primary_monitor();\n+\n+\tprint_pdump_stats();\n+\n+\trte_pcapng_close(out);\n+\n+\tcleanup_pdump_resources();\n+\trte_free(bpf_filter);\n+\trte_ring_free(r);\n+\trte_mempool_free(mp);\n+\n+\treturn rte_eal_cleanup() ? EXIT_FAILURE : 0;\n+}\ndiff --git a/app/capture/meson.build b/app/capture/meson.build\nnew file mode 100644\nindex 000000000000..9558f10562bd\n--- /dev/null\n+++ b/app/capture/meson.build\n@@ -0,0 +1,22 @@\n+# SPDX-License-Identifier: BSD-3-Clause\n+# Copyright(c) 2019 Microsoft Corporation\n+pcap_dep = dependency('pcap', required: false)\n+if pcap_dep.found()\n+\tbuild = true\n+else\n+\t# pcap got a pkg-config file only in 1.9.0 and before that meson uses\n+\t# an internal pcap-config finder, which is not compatible with\n+\t# cross-compilation, so try to fallback to find_library\n+\tpcap_dep = cc.find_library('pcap', required: false)\n+\tif pcap_dep.found() and cc.has_header('pcap.h', dependencies: pcap_dep)\n+\t\tbuild = true\n+\t\tpkgconfig_extra_libs += '-lpcap'\n+\telse\n+\t\tbuild = false\n+\t\treason = 'missing dependency, \"libpcap\"'\n+\tendif\n+endif\n+\n+sources = files('main.c')\n+ext_deps += pcap_dep\n+deps += ['ethdev', 'pdump', 'pcapng']\ndiff --git a/app/meson.build b/app/meson.build\nindex b0e6afbbe9d9..a33198182133 100644\n--- a/app/meson.build\n+++ b/app/meson.build\n@@ -6,6 +6,7 @@ if is_windows\n endif\n \n apps = [\n+\t'capture',\n \t'pdump',\n \t'proc-info',\n \t'test-acl',\ndiff --git a/config/common_base b/config/common_base\nindex 0ccfcfae377d..e1bab8e77988 100644\n--- a/config/common_base\n+++ b/config/common_base\n@@ -1073,3 +1073,8 @@ CONFIG_RTE_APP_CRYPTO_PERF=y\n # Compile the eventdev application\n #\n CONFIG_RTE_APP_EVENTDEV=y\n+\n+#\n+# Compile the capture application\n+#\n+CONFIG_RTE_APP_CAPTURE=n\n",
    "prefixes": [
        "RFC",
        "8/8"
    ]
}