From patchwork Fri Sep 3 00:47:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 97851 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 4795CA0C4D; Fri, 3 Sep 2021 02:47:43 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 290DB40E25; Fri, 3 Sep 2021 02:47:40 +0200 (CEST) Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) by mails.dpdk.org (Postfix) with ESMTP id 2E52C40DDE for ; Fri, 3 Sep 2021 02:47:38 +0200 (CEST) Received: by mail-pg1-f178.google.com with SMTP id w8so3832623pgf.5 for ; Thu, 02 Sep 2021 17:47:38 -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=ZubgKPb4kP9eM/zgTpm/Xfc9SKjo1XI4XPXJxmTTeN4=; b=ej2QHkPD6APofCiEPDFjegtVkWughI1Cpr6A28/Tei7xau/qVrWBKDKWf6F1OB9hxW FRl1fyUcbdk+Mi7Ek8OHg9e6FREYofyTVQIXcabX3MHvtt4CCSqni7pksvTs9jKyKd2v 4VGNNXLrUmKmYG/ZgcKWtspnr3pKS5YrDmisyfR9Cn+h+YMKd+tqqOOYcZ1EIdwIFlCi nV80iCA3T1aav+VdQvQLIjpUkRXZ+O14SDE32ikHAhTLRLy+T+HzXcl0SRGK7o0p9MXJ lgcxkkgLpQUAMB+8jo/6YxXoYebSKBiA+QQ16hB1NXi3rby9K8BtWqEy5TBXLMGTShqi s+OQ== 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=ZubgKPb4kP9eM/zgTpm/Xfc9SKjo1XI4XPXJxmTTeN4=; b=atoRhSxFXEi4HiaqxwPqnSVsiRfoBX6NquOwt/wlH3yoNjOtgkPHsv+TcJTkVfvs+B n+zIspBIiuBCLHaXDedDJCzXSr+senxW6uyU3wr7RWxat7FThLELUS59kSmC/SziB1Op 1gxDXlyfga9hEgwcWBe+6j5ldTh5L+DssnrUQISQi9d+84dm1V4ZxBlGE7sxsrhhkNU7 Hrf+F142+xqIKc4eI4SzkE/WVV8msOca/83MycUlIf/bkp3hM0phdZ0+p8Z+0U9ar6lL Qo4KMovC2c0+1NgmPdUC+w8B6rFFyOKjnDI13v1WVtdZpM1ZZuYjIFfE5qIOGWMm+I/h 3UoA== X-Gm-Message-State: AOAM533jVvWRPTeaQDLxcr7PK2YDjVDyhHiTeq5qG4uNQA1eRBAignBU gQgQUmZYi/Xac5ksNnvapl7qyvbn10F5pA== X-Google-Smtp-Source: ABdhPJw7u4ISNEz3qL27vkcRK2zxuzBLtOgi2OAWyp0bejvC4YFwqFqiOS6xt0pA/ChXBmkb+6Q/vQ== X-Received: by 2002:a63:111f:: with SMTP id g31mr1102324pgl.80.1630630056427; Thu, 02 Sep 2021 17:47:36 -0700 (PDT) Received: from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123]) by smtp.gmail.com with ESMTPSA id cq8sm3179033pjb.31.2021.09.02.17.47.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Sep 2021 17:47:35 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Date: Thu, 2 Sep 2021 17:47:28 -0700 Message-Id: <20210903004732.109023-2-stephen@networkplumber.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210903004732.109023-1-stephen@networkplumber.org> References: <20210903004732.109023-1-stephen@networkplumber.org> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 1/5] librte_pcapng: add new library for writing pcapng files X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 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" This is utility library for writing pcapng format files used by Wireshark family of utilities. Older tcpdump also knows how to read (but not write) this format. See draft RFC https://www.ietf.org/id/draft-tuexen-opsawg-pcapng-03.html and https://github.com/pcapng/pcapng/ Signed-off-by: Stephen Hemminger --- lib/meson.build | 1 + lib/pcapng/meson.build | 8 + lib/pcapng/pcapng_proto.h | 129 +++++++++ lib/pcapng/rte_pcapng.c | 543 ++++++++++++++++++++++++++++++++++++++ lib/pcapng/rte_pcapng.h | 175 ++++++++++++ lib/pcapng/version.map | 12 + 6 files changed, 868 insertions(+) create mode 100644 lib/pcapng/meson.build create mode 100644 lib/pcapng/pcapng_proto.h create mode 100644 lib/pcapng/rte_pcapng.c create mode 100644 lib/pcapng/rte_pcapng.h create mode 100644 lib/pcapng/version.map diff --git a/lib/meson.build b/lib/meson.build index 1673ca4323c0..514be90b09ec 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -41,6 +41,7 @@ libraries = [ 'latencystats', 'lpm', 'member', + 'pcapng', 'power', 'pdump', 'rawdev', diff --git a/lib/pcapng/meson.build b/lib/pcapng/meson.build new file mode 100644 index 000000000000..fe636bdf3c0b --- /dev/null +++ b/lib/pcapng/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2019 Microsoft Corporation + +version = 1 +sources = files('rte_pcapng.c') +headers = files('rte_pcapng.h') + +deps += ['ethdev'] diff --git a/lib/pcapng/pcapng_proto.h b/lib/pcapng/pcapng_proto.h new file mode 100644 index 000000000000..47161d8a1213 --- /dev/null +++ b/lib/pcapng/pcapng_proto.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2019-2020 Microsoft Corporation + * + * PCAP Next Generation Capture File writer + * + * See: https://github.com/pcapng/pcapng/ for the file format. + */ + +enum pcapng_block_types { + PCAPNG_INTERFACE_BLOCK = 1, + PCAPNG_PACKET_BLOCK, /* Obsolete */ + PCAPNG_SIMPLE_PACKET_BLOCK, + PCAPNG_NAME_RESOLUTION_BLOCK, + PCAPNG_INTERFACE_STATS_BLOCK, + PCAPNG_ENHANCED_PACKET_BLOCK, + + PCAPNG_SECTION_BLOCK = 0x0A0D0D0A, +}; + +struct pcapng_option { + uint16_t code; + uint16_t length; + uint8_t data[]; +}; + +#define PCAPNG_BYTE_ORDER_MAGIC 0x1A2B3C4D +#define PCAPNG_MAJOR_VERS 1 +#define PCAPNG_MINOR_VERS 0 + +enum pcapng_opt { + PCAPNG_OPT_END = 0, + PCAPNG_OPT_COMMENT = 1, +}; + +struct pcapng_section_header { + uint32_t block_type; + uint32_t block_length; + uint32_t byte_order_magic; + uint16_t major_version; + uint16_t minor_version; + uint64_t section_length; +}; + +enum pcapng_section_opt { + PCAPNG_SHB_HARDWARE = 2, + PCAPNG_SHB_OS = 3, + PCAPNG_SHB_USERAPPL = 4, +}; + +struct pcapng_interface_block { + uint32_t block_type; /* 1 */ + uint32_t block_length; + uint16_t link_type; + uint16_t reserved; + uint32_t snap_len; +}; + +enum pcapng_interface_options { + PCAPNG_IFB_NAME = 2, + PCAPNG_IFB_DESCRIPTION, + PCAPNG_IFB_IPV4ADDR, + PCAPNG_IFB_IPV6ADDR, + PCAPNG_IFB_MACADDR, + PCAPNG_IFB_EUIADDR, + PCAPNG_IFB_SPEED, + PCAPNG_IFB_TSRESOL, + PCAPNG_IFB_TZONE, + PCAPNG_IFB_FILTER, + PCAPNG_IFB_OS, + PCAPNG_IFB_FCSLEN, + PCAPNG_IFB_TSOFFSET, + PCAPNG_IFB_HARDWARE, +}; + +struct pcapng_enhance_packet_block { + uint32_t block_type; /* 6 */ + uint32_t block_length; + uint32_t interface_id; + uint32_t timestamp_hi; + uint32_t timestamp_lo; + uint32_t capture_length; + uint32_t original_length; +}; + +/* Flags values */ +#define PCAPNG_IFB_INBOUND 0b01 +#define PCAPNG_IFB_OUTBOUND 0b10 + +enum pcapng_epb_options { + PCAPNG_EPB_FLAGS = 2, + PCAPNG_EPB_HASH, + PCAPNG_EPB_DROPCOUNT, + PCAPNG_EPB_PACKETID, + PCAPNG_EPB_QUEUE, + PCAPNG_EPB_VERDICT, +}; + +enum pcapng_epb_hash { + PCAPNG_HASH_2COMP = 0, + PCAPNG_HASH_XOR, + PCAPNG_HASH_CRC32, + PCAPNG_HASH_MD5, + PCAPNG_HASH_SHA1, + PCAPNG_HASH_TOEPLITZ, +}; + +struct pcapng_simple_packet { + uint32_t block_type; /* 3 */ + uint32_t block_length; + uint32_t packet_length; +}; + +struct pcapng_statistics { + uint32_t block_type; /* 5 */ + uint32_t block_length; + uint32_t interface_id; + uint32_t timestamp_hi; + uint32_t timestamp_lo; +}; + +enum pcapng_isb_options { + PCAPNG_ISB_STARTTIME = 2, + PCAPNG_ISB_ENDTIME, + PCAPNG_ISB_IFRECV, + PCAPNG_ISB_IFDROP, + PCAPNG_ISB_FILTERACCEPT, + PCAPNG_ISB_OSDROP, + PCAPNG_ISB_USRDELIV, +}; diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c new file mode 100644 index 000000000000..4bfc1a5240f0 --- /dev/null +++ b/lib/pcapng/rte_pcapng.c @@ -0,0 +1,543 @@ +/* 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 "pcapng_proto.h" + +/* conversion from DPDK speed to PCAPNG */ +#define PCAPNG_MBPS_SPEED 1000000ull + +/* Format of the capture file handle */ +struct rte_pcapng { + int outfd; /* output file */ + /* DPDK port id to interface index in file */ + uint32_t port_index[RTE_MAX_ETHPORTS]; +}; + +/* For converting TSC cycles to PCAPNG ns format */ +struct pcapng_time { + uint64_t ns; + uint64_t cycles; +} pcapng_time; + +int rte_pcapng_init(void) +{ + struct timespec ts; + + pcapng_time.cycles = rte_get_tsc_cycles(); + if (clock_gettime(CLOCK_REALTIME, &ts) < 0) + return -1; + + pcapng_time.ns = rte_timespec_to_ns(&ts); + return 0; +} + +/* PCAPNG timestamps are in nanoseconds */ +static uint64_t pcapng_tsc_to_ns(uint64_t cycles) +{ + uint64_t delta; + + delta = cycles - pcapng_time.cycles; + return pcapng_time.ns + (delta * NSEC_PER_SEC) / rte_get_tsc_hz(); +} + +/* length of option including padding */ +static size_t pcapng_optlen(uint16_t len) +{ + return RTE_ALIGN(sizeof(struct pcapng_option) + len, + sizeof(uint32_t)); +} + +/* build TLV option and return location of next */ +static struct pcapng_option * +pcapng_add_option(struct pcapng_option *popt, uint16_t code, + const void *data, uint16_t len) +{ + popt->code = code; + popt->length = len; + memcpy(popt->data, data, len); + + return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len)); +} + +/* + * Write required initial section header describing the capture + */ +static int +pcapng_section_block(rte_pcapng_t *self, + const char *os, const char *hw, + const char *app, const char *comment) +{ + struct pcapng_section_header *hdr; + struct pcapng_option *opt; + void *buf; + size_t len; + ssize_t cc; + + len = sizeof(*hdr); + if (hw) + len += pcapng_optlen(strlen(hw)); + if (os) + len += pcapng_optlen(strlen(os)); + if (app) + len += pcapng_optlen(strlen(app)); + if (comment) + len += pcapng_optlen(strlen(comment)); + + len += pcapng_optlen(0); + len += sizeof(uint32_t); + + buf = calloc(1, len); + if (!buf) + return -1; + + hdr = (struct pcapng_section_header *)buf; + *hdr = (struct pcapng_section_header) { + .block_type = PCAPNG_SECTION_BLOCK, + .block_length = len, + .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC, + .major_version = PCAPNG_MAJOR_VERS, + .minor_version = PCAPNG_MINOR_VERS, + .section_length = UINT64_MAX, + }; + hdr->block_length = len; + + opt = (struct pcapng_option *)(hdr + 1); + if (comment) + opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, + comment, strlen(comment)); + if (hw) + opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE, + hw, strlen(hw)); + if (os) + opt = pcapng_add_option(opt, PCAPNG_SHB_OS, + os, strlen(os)); + if (app) + opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL, + app, strlen(app)); + + opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); + /* clone block_length after option */ + memcpy(opt, &hdr->block_length, sizeof(uint32_t)); + + cc = write(self->outfd, buf, len); + free(buf); + + return cc; +} + +/* Write the PCAPNG section header at start of file */ +static ssize_t +pcapng_interface_block(rte_pcapng_t *self, const char *if_name, + uint64_t if_speed, const uint8_t *mac_addr, + const char *if_hw, const char *comment) +{ + struct pcapng_interface_block *hdr; + struct pcapng_option *opt; + const uint8_t tsresol = 9; /* nanosecond resolution */ + size_t len = sizeof(*hdr); + ssize_t cc; + void *buf; + + len += pcapng_optlen(sizeof(tsresol)); + if (if_name) + len += pcapng_optlen(strlen(if_name)); + if (mac_addr) + len += pcapng_optlen(6); + if (if_speed) + len += pcapng_optlen(sizeof(uint64_t)); + if (if_hw) + len += pcapng_optlen(strlen(if_hw)); + if (comment) + len += pcapng_optlen(strlen(comment)); + + len += pcapng_optlen(0); + len += sizeof(uint32_t); + buf = calloc(1, len); + if (!buf) + return -ENOMEM; + + hdr = (struct pcapng_interface_block *)buf; + hdr->block_type = PCAPNG_INTERFACE_BLOCK; + hdr->link_type = 1; /* Ethernet */ + hdr->block_length = len; + + opt = (struct pcapng_option *)(hdr + 1); + if (if_name) + opt = pcapng_add_option(opt, PCAPNG_IFB_NAME, + if_name, strlen(if_name)); + if (mac_addr) + opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR, + mac_addr, RTE_ETHER_ADDR_LEN); + if (if_speed) + opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED, + &if_speed, sizeof(uint64_t)); + opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL, + &tsresol, sizeof(tsresol)); + if (if_hw) + opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE, + if_hw, strlen(if_hw)); + if (comment) + opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, + comment, strlen(comment)); + + opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); + + memcpy(opt, &hdr->block_length, sizeof(uint32_t)); + cc = write(self->outfd, buf, len); + free(buf); + + return cc; +} + +static int +pcapng_add_interface(rte_pcapng_t *self, uint16_t port) +{ + struct rte_eth_dev_info dev_info; + struct rte_ether_addr macaddr; + const struct rte_device *dev; + struct rte_eth_link link; + char ifname[IF_NAMESIZE]; + char ifhw[256]; + uint64_t speed = 0; + + if (rte_eth_dev_info_get(port, &dev_info) < 0) + return -1; + + /* make something like an interface name */ + if (if_indextoname(dev_info.if_index, ifname) == NULL) + snprintf(ifname, IF_NAMESIZE, "dpdk:%u", port); + + /* make a useful device hardware string */ + dev = dev_info.device; + if (dev) + snprintf(ifhw, sizeof(ifhw), + "%s-%s", dev->bus->name, dev->name); + + /* DPDK reports in units of Mbps */ + rte_eth_link_get(port, &link); + if (link.link_status == ETH_LINK_UP) + speed = link.link_speed * PCAPNG_MBPS_SPEED; + + rte_eth_macaddr_get(port, &macaddr); + + return pcapng_interface_block(self, ifname, speed, + macaddr.addr_bytes, + dev ? ifhw : NULL, NULL); +} + +/* + * Write the list of possible interfaces at the start + * of the file. + */ +static int +pcapng_interfaces(rte_pcapng_t *self) +{ + uint16_t port_id; + uint16_t index = 0; + + RTE_ETH_FOREACH_DEV(port_id) { + /* The list if ports in pcapng needs to be contiguous */ + self->port_index[port_id] = index++; + if (pcapng_add_interface(self, port_id) < 0) + return -1; + } + return 0; +} + +/* + * Write an Interface statistics block at the end of capture. + */ +int +rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id, + uint64_t ifrecv, uint64_t ifdrop, + uint64_t filteraccept, uint64_t dropped) +{ + struct pcapng_statistics *hdr; + struct pcapng_option *opt; + uint64_t ns; + size_t len; + uint8_t buf[512]; + + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL); + + ns = pcapng_tsc_to_ns(rte_get_tsc_cycles()); + + hdr = (struct pcapng_statistics *)buf; + opt = (struct pcapng_option *)(hdr + 1); + + opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV, + &ifrecv, sizeof(ifrecv)); + opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP, + &ifdrop, sizeof(ifdrop)); + opt = pcapng_add_option(opt, PCAPNG_ISB_FILTERACCEPT, + &filteraccept, sizeof(filteraccept)); + opt = pcapng_add_option(opt, PCAPNG_ISB_OSDROP, + &dropped, sizeof(dropped)); + + opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); + + len = sizeof(*hdr) + + 4 * pcapng_optlen(sizeof(uint64_t)) + + pcapng_optlen(0) + + sizeof(uint32_t); + + hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK; + hdr->block_length = len; + hdr->interface_id = self->port_index[port_id]; + + hdr->timestamp_hi = ns >> 32; + hdr->timestamp_lo = (uint32_t)ns; + + /* clone block_length after option */ + memcpy(opt, &len, sizeof(uint32_t)); + + return write(self->outfd, buf, len); +} + +/* + * The mbufs created use the Pcapng standard enhanced packet block. + * + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 0 | Block Type = 0x00000006 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 4 | Block Total Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 8 | Interface ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 12 | Timestamp (High) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 16 | Timestamp (Low) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 20 | Captured Packet Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 24 | Original Packet Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 28 / / + * / Packet Data / + * / variable length, padded to 32 bits / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Code = 0x0002 | Option Length = 0x004 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Flags (direction) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Option Code = 0x0006 | Option Length = 0x002 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Queue id | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Block Total Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* Make a copy of original mbuf with pcapng header and options */ +struct rte_mbuf * +rte_pcapng_copy(uint16_t port_id, uint16_t queue, + const struct rte_mbuf *md, + struct rte_mempool *mp, + uint32_t length, uint64_t cycles, + enum rte_pcapng_direction direction) +{ + struct pcapng_enhance_packet_block *epb; + struct pcapng_option *opt; + size_t optlen; + uint32_t orig_len, data_len, padding, flags; + struct rte_mbuf *mc; + uint64_t ns; + +#ifdef RTE_LIBRTE_ETHDEV_DEBUG + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL); +#endif + ns = pcapng_tsc_to_ns(cycles); + + orig_len = rte_pktmbuf_pkt_len(md); + + /* Take snapshot of the data */ + mc = rte_pktmbuf_copy(md, mp, 0, length); + if (unlikely(mc == NULL)) + return NULL; + + /* If packet had offloaded VLAN, expand it */ + if (md->ol_flags & ~(PKT_RX_VLAN_STRIPPED | PKT_TX_VLAN)) { + if (rte_vlan_insert(&mc) != 0) + goto fail; + + orig_len += sizeof(struct rte_vlan_hdr); + } + + /* pad the packet to 32 bit boundary */ + data_len = rte_pktmbuf_data_len(mc); + padding = RTE_ALIGN_CEIL(data_len, 32) - data_len; + if (padding > 0) { + void *tail = rte_pktmbuf_append(mc, padding); + + if (tail == NULL) + goto fail; + memset(tail, 0, padding); + } + + /* reserve trailing options and block length */ + optlen = pcapng_optlen(sizeof(flags)) + pcapng_optlen(sizeof(queue)); + opt = (struct pcapng_option *) + rte_pktmbuf_append(mc, optlen + sizeof(uint32_t)); + if (unlikely(opt == NULL)) + goto fail; + + switch (direction) { + case RTE_PCAPNG_DIRECTION_IN: + flags = PCAPNG_IFB_INBOUND; + break; + case RTE_PCAPNG_DIRECTION_OUT: + flags = PCAPNG_IFB_OUTBOUND; + break; + default: + flags = 0; + } + + opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS, + &flags, sizeof(flags)); + + opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE, + &queue, sizeof(queue)); + + /* Add PCAPNG packet header */ + epb = (struct pcapng_enhance_packet_block *) + rte_pktmbuf_prepend(mc, sizeof(*epb)); + if (unlikely(epb == NULL)) + goto fail; + + epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK; + epb->block_length = rte_pktmbuf_data_len(mc); + + /* Interface index is filled in later during write */ + mc->port = port_id; + + epb->timestamp_hi = ns >> 32; + epb->timestamp_lo = (uint32_t)ns; + epb->capture_length = data_len; + epb->original_length = orig_len; + + /* set trailer of block length */ + *(uint32_t *)opt = epb->block_length; + + return mc; + +fail: + rte_pktmbuf_free(mc); + return NULL; +} + +/* Count how many segments are in this array of mbufs */ +static unsigned int +mbuf_burst_segs(struct rte_mbuf *pkts[], unsigned int n) +{ + unsigned int i, iovcnt; + + for (iovcnt = 0, i = 0; i < n; i++) { + const struct rte_mbuf *m = pkts[i]; + + __rte_mbuf_sanity_check(m, 1); + + iovcnt += m->nb_segs; + } + return iovcnt; +} + +/* Write pre-formatted packets to file. */ +ssize_t +rte_pcapng_write_packets(rte_pcapng_t *self, + struct rte_mbuf *pkts[], uint16_t nb_pkts) +{ + int iovcnt = mbuf_burst_segs(pkts, nb_pkts); + struct iovec iov[iovcnt]; + unsigned int i, cnt; + ssize_t ret; + + for (i = cnt = 0; i < nb_pkts; i++) { + struct rte_mbuf *m = pkts[i]; + struct pcapng_enhance_packet_block *epb; + + /* sanity check that is really a pcapng mbuf */ + epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *); + if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK || + epb->block_length != rte_pktmbuf_data_len(m))) { + rte_errno = EINVAL; + return -1; + } + + /* + * The DPDK port is recorded during pcapng_copy. + * Map that to PCAPNG interface in file. + */ + epb->interface_id = self->port_index[m->port]; + do { + iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *); + iov[cnt].iov_len = rte_pktmbuf_data_len(m); + ++cnt; + } while ((m = m->next)); + } + + ret = writev(self->outfd, iov, iovcnt); + if (unlikely(ret < 0)) + rte_errno = errno; + return ret; +} + +/* Create new pcapng writer handle */ +rte_pcapng_t * +rte_pcapng_fdopen(int fd, + const char *osname, const char *hardware, + const char *appname, const char *comment) +{ + rte_pcapng_t *self; + + self = malloc(sizeof(*self)); + if (!self) { + rte_errno = ENOMEM; + return NULL; + } + + self->outfd = fd; + + if (pcapng_section_block(self, osname, hardware, appname, comment) < 0) + goto fail; + + if (pcapng_interfaces(self) < 0) + goto fail; + + return self; +fail: + free(self); + return NULL; +} + +void +rte_pcapng_close(rte_pcapng_t *self) +{ + close(self->outfd); + free(self); +} diff --git a/lib/pcapng/rte_pcapng.h b/lib/pcapng/rte_pcapng.h new file mode 100644 index 000000000000..2fa56ef9bcbc --- /dev/null +++ b/lib/pcapng/rte_pcapng.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2019 Microsoft Corporation + */ + +/** + * @file + * RTE pcapng + * + * @warning + * @b EXPERIMENTAL: + * All functions in this file may be changed or removed without prior notice. + * + * Pcapng is an evolution from the pcap format, created to address some of + * its deficiencies. Namely, the lack of extensibility and inability to store + * additional information. + * + * For details about the file format see RFC: + * https://www.ietf.org/id/draft-tuexen-opsawg-pcapng-03.html + * and + * https://github.com/pcapng/pcapng/ + */ + +#ifndef _RTE_PCAPNG_H_ +#define _RTE_PCAPNG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the pcapng library. + * + * Computes the time offset for timestamps. + * + * @return + * 0 on success, -1 on error + */ +__rte_experimental +int rte_pcapng_init(void); + +/* Opaque handle used for functions in this library. */ +typedef struct rte_pcapng rte_pcapng_t; + +/** + * Write data to existing open file + * + * @param fd + * file descriptor + * @param osname + * Optional description of the operting system. + * Examples: "Debian 11", "Windows Server 22" + * @param hardware + * Optional description of the hardware used to create this file. + * Examples: "x86 Virtual Machine" + * @param appname + * Optional: application name recorded in the pcapng file. + * Example: "dpdk-dumpcap 1.0 (DPDK 20.11)" + * @param comment + * Optional comment to add to file header. + * @return + * handle to library, or NULL in case of error (and rte_errno is set). + */ +__rte_experimental +rte_pcapng_t * +rte_pcapng_fdopen(int fd, + const char *osname, const char *hardware, + const char *appname, const char *comment); + +/** + * Close capture file + * + * @param self + * handle to library + */ +__rte_experimental +void +rte_pcapng_close(rte_pcapng_t *self); + +/** + * Direction flag + * These should match Enhanced Packet Block flag bits + */ +enum rte_pcapng_direction { + RTE_PCAPNG_DIRECTION_UNKNOWN = 0, + RTE_PCAPNG_DIRECTION_IN = 1, + RTE_PCAPNG_DIRECTION_OUT = 2, +}; + +/** + * Format an mbuf for writing to file + * + * @param port_id + * The Ethernet port on which packet was receive + * or is going to be transmitted. + * @param queue + * The queue on the Ethernet port. + * @param mp + * The mempool from which the "clone" mbufs are allocated. + * @param m + * The mbuf to copy + * @param length + * The upper limit on bytes to copy. Passing UINT32_MAX + * means all data (after offset). + * @param timestamp + * The timestamp in TSC cycles. + * @param direction + * The direction of the packer: receive, transmit or unknown. + * + * @return + * - The pointer to the new mbuf formatted for pcapng_write + * - NULL if allocation fails. + * + */ +__rte_experimental +struct rte_mbuf * +rte_pcapng_copy(uint16_t port_id, uint16_t queue, + const struct rte_mbuf *m, struct rte_mempool *mp, + uint32_t length, uint64_t timestamp, + enum rte_pcapng_direction direction); + +/** + * Write packets to the capture file. + * + * Packets to be captured are copied by rte_pcapng_mbuf() + * and then this function is called to write them to the file. + * @warning + * Do not pass original mbufs + * + * @param self + * The handle to the packet capture file + * @param pkts + * The address of an array of *nb_pkts* pointers to *rte_mbuf* structures + * which contain the output packets + * @param nb_pkts + * The number of packets to write to the file. + * @return + * The number of bytes written to file, -1 on failure to write file. + * The mbuf's in *pkts* are always freed. + */ +__rte_experimental +ssize_t +rte_pcapng_write_packets(rte_pcapng_t *self, + struct rte_mbuf *pkts[], uint16_t nb_pkts); + +/** + * Write an Interface statistics block. + * For statistics, use 0 if don't know or care to report it. + * Should be called before closing capture to report results. + * + * @param self + * The handle to the packet capture file + * @param port + * The Ethernet port to report stats on. + * @param received + * The number of packets received since the start of capturing. + * @param missed + * The number of packets missed by the device. + * @param accepted + * The number of packets accepted by the filter. + * @param dropped + * The number of packets dropped by the application (OS) + * @return + * number of bytes written to file, -1 on failure to write file + */ +__rte_experimental +int +rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port, + uint64_t received, uint64_t missed, + uint64_t accepted, uint64_t dropped); + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_PCAPNG_H_ */ diff --git a/lib/pcapng/version.map b/lib/pcapng/version.map new file mode 100644 index 000000000000..e6405f3bee01 --- /dev/null +++ b/lib/pcapng/version.map @@ -0,0 +1,12 @@ +EXPERIMENTAL { + global: + + rte_pcapng_close; + rte_pcapng_copy; + rte_pcapng_fdopen; + rte_pcapng_init; + rte_pcapng_write_packets; + rte_pcapng_write_stats; + + local: *; +}; From patchwork Fri Sep 3 00:47:29 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 97852 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id D5FB2A0C4D; Fri, 3 Sep 2021 02:47:50 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B7EAE410E6; Fri, 3 Sep 2021 02:47:41 +0200 (CEST) Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) by mails.dpdk.org (Postfix) with ESMTP id 37EE440DFD for ; Fri, 3 Sep 2021 02:47:39 +0200 (CEST) Received: by mail-pl1-f182.google.com with SMTP id k17so2286195pls.0 for ; Thu, 02 Sep 2021 17:47:39 -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=FuhNhRpraSzn0b95FhD9sX5V3s3OU5oUWrA3zv85fJ0=; b=tJyecenwTAT+2zJSrDGd4m8VUaONsTFgWdaO0sPcf+W54aD2gQMgw3kToDQKcKZd4/ CZM1L4A/p8Sk1jyaGCtdCvst7PqjDXU7qEOs/CYr7wh8qaZN/gsdbw/f/ubKE+2sZXuZ lPgXhKQIQnLuPMq1XbNXeZaNXVvp3SKZLA/7I26KAjaifbBGSB3NqF/6OD3FWo7XYaBM NH1nFNmtpQs9ui3Z6zfdJTAhaN1VIGO3Yr72eE5rZ+CSxaTn+BgKJ1Pul80BRLBJ5QV/ ZrD3hBaN8FWIsSjqmrliCEmGVarOOKVjOZDL3jN9b7a1aReho6bFLv2aIzT+YiGOidUJ Ebzg== 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=FuhNhRpraSzn0b95FhD9sX5V3s3OU5oUWrA3zv85fJ0=; b=S+ll3kOUaXZMD8ANQi7KdrApTsFiPKakcaVWmLG+uO7AvS/U8D7bpQe51H1llwE45x cG8oP2on5DNtyGGxAw1JIaMYZZHwzwdtX3fSwPFG1TxgkHapcIyUeSFedlTo2KB5J4FS eSzXc3pRNRUcOeyRwtRzIzc/0mj6aC2QLiX4M14mUe3Kv8uwftvpCm6dUy+EBEaPbKPm igLQs6o/lqI7PseCeOExTG8cFsoylIe3/NpFdwebUUKi1Toq32twg5K2bLZl3LQBSDrv HJa2YcUnbzZhu1c5Oim6ikTZzeWWU7JdsqCdaJ94nZJiV2Qv9snezBUhqRYt12d+izoA jSUg== X-Gm-Message-State: AOAM530eY6kRpcD4sWLmJ8SMRbEEZU2rSBq4wz6MyFWbPgpjEKxPohep OIWA3PHWwubdGe7czCt/Vhx6rCowEsFRYQ== X-Google-Smtp-Source: ABdhPJybHnFRJNSEw5dvB0uu4iMnVPg14Pp2e5SV23TCI74QzkIJm3YujYzyZCkr0UNAn20ane9Asg== X-Received: by 2002:a17:903:234d:b0:138:96ac:66c4 with SMTP id c13-20020a170903234d00b0013896ac66c4mr752458plh.18.1630630057444; Thu, 02 Sep 2021 17:47:37 -0700 (PDT) Received: from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123]) by smtp.gmail.com with ESMTPSA id cq8sm3179033pjb.31.2021.09.02.17.47.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Sep 2021 17:47:36 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Date: Thu, 2 Sep 2021 17:47:29 -0700 Message-Id: <20210903004732.109023-3-stephen@networkplumber.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210903004732.109023-1-stephen@networkplumber.org> References: <20210903004732.109023-1-stephen@networkplumber.org> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 2/5] pdump: support pcapng and filtering X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 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" This enhances the DPDK pdump library to support new pcapng format and filtering via BPF. The internal client/server protocol is changed to support two versions: the original pdump basic version and a new pcapng version. The internal version number (not part of exposed API or ABI) is intentionally increased to cause any attempt to try mismatched primary/secondary process to fail. Add new API to do allow filtering of captured packets with DPDK BPF (eBPF) filter program. It keeps statistics on packets captured, filtered, and missed (because ring was full). Signed-off-by: Stephen Hemminger --- lib/meson.build | 4 +- lib/pdump/meson.build | 2 +- lib/pdump/rte_pdump.c | 386 +++++++++++++++++++++++++++++------------- lib/pdump/rte_pdump.h | 117 ++++++++++++- lib/pdump/version.map | 8 + 5 files changed, 394 insertions(+), 123 deletions(-) diff --git a/lib/meson.build b/lib/meson.build index 514be90b09ec..b59bec494275 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -26,6 +26,7 @@ libraries = [ 'timer', # eventdev depends on this 'acl', 'bbdev', + 'bpf', 'bitratestats', 'cfgfile', 'compressdev', @@ -43,7 +44,6 @@ libraries = [ 'member', 'pcapng', 'power', - 'pdump', 'rawdev', 'regexdev', 'rib', @@ -55,10 +55,10 @@ libraries = [ 'ipsec', # ipsec lib depends on net, crypto and security 'fib', #fib lib depends on rib 'port', # pkt framework libs which use other libs from above + 'pdump', # pdump lib depends on bpf pcapng 'table', 'pipeline', 'flow_classify', # flow_classify lib depends on pkt framework table lib - 'bpf', 'graph', 'node', ] diff --git a/lib/pdump/meson.build b/lib/pdump/meson.build index 3a95eabde6a6..51ceb2afdec5 100644 --- a/lib/pdump/meson.build +++ b/lib/pdump/meson.build @@ -3,4 +3,4 @@ sources = files('rte_pdump.c') headers = files('rte_pdump.h') -deps += ['ethdev'] +deps += ['ethdev', 'bpf', 'pcapng'] diff --git a/lib/pdump/rte_pdump.c b/lib/pdump/rte_pdump.c index 382217bc1564..3237c54af69e 100644 --- a/lib/pdump/rte_pdump.c +++ b/lib/pdump/rte_pdump.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "rte_pdump.h" @@ -27,30 +28,26 @@ enum pdump_operation { ENABLE = 2 }; +/* + * Note: version numbers intentionally start at 3 + * in order to catch any application built with older out + * version of DPDK using incompatiable client request format. + */ enum pdump_version { - V1 = 1 + PDUMP_CLIENT_LEGACY = 3, + PDUMP_CLIENT_PCAPNG = 4, }; struct pdump_request { uint16_t ver; uint16_t op; - uint32_t flags; - union pdump_data { - struct enable_v1 { - char device[RTE_DEV_NAME_MAX_LEN]; - uint16_t queue; - struct rte_ring *ring; - struct rte_mempool *mp; - void *filter; - } en_v1; - struct disable_v1 { - char device[RTE_DEV_NAME_MAX_LEN]; - uint16_t queue; - struct rte_ring *ring; - struct rte_mempool *mp; - void *filter; - } dis_v1; - } data; + uint16_t flags; + uint16_t queue; + struct rte_ring *ring; + struct rte_mempool *mp; + const struct rte_bpf *filter; + uint32_t snaplen; + char device[RTE_DEV_NAME_MAX_LEN]; }; struct pdump_response { @@ -63,36 +60,67 @@ static struct pdump_rxtx_cbs { struct rte_ring *ring; struct rte_mempool *mp; const struct rte_eth_rxtx_callback *cb; - void *filter; + const struct rte_bpf *filter; + enum pdump_version ver; + uint32_t snaplen; + struct rte_pdump_stats stats; } rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT], tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT]; -static inline void -pdump_copy(struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) +static void +pdump_copy(uint16_t port_id, uint16_t queue, + enum rte_pcapng_direction direction, + struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) { unsigned int i; int ring_enq; uint16_t d_pkts = 0; struct rte_mbuf *dup_bufs[nb_pkts]; - struct pdump_rxtx_cbs *cbs; + struct pdump_rxtx_cbs *cbs = user_params; + uint64_t ts; struct rte_ring *ring; struct rte_mempool *mp; struct rte_mbuf *p; + uint64_t bpf_rc[nb_pkts]; + + if (cbs->filter && + !rte_bpf_exec_burst(cbs->filter, (void **)pkts, bpf_rc, nb_pkts)) + return; /* our work here is done */ - cbs = user_params; + ts = rte_get_tsc_cycles(); ring = cbs->ring; mp = cbs->mp; for (i = 0; i < nb_pkts; i++) { - p = rte_pktmbuf_copy(pkts[i], mp, 0, UINT32_MAX); - if (p) + /* + * Similar behavior to rte_bpf_eth callback. + * if BPF program returns zero value for a given packet, + * then it will be ignored. + */ + if (cbs->filter && bpf_rc[i] == 0) + continue; + + /* + * If using pcapng then want to wrap packets + * otherwise a simple copy. + */ + if (cbs->ver == PDUMP_CLIENT_PCAPNG) + p = rte_pcapng_copy(port_id, queue, + pkts[i], mp, cbs->snaplen, + ts, direction); + else + p = rte_pktmbuf_copy(pkts[i], mp, 0, cbs->snaplen); + + if (likely(p != NULL)) dup_bufs[d_pkts++] = p; } + cbs->stats.accepted += d_pkts; ring_enq = rte_ring_enqueue_burst(ring, (void *)dup_bufs, d_pkts, NULL); if (unlikely(ring_enq < d_pkts)) { unsigned int drops = d_pkts - ring_enq; + cbs->stats.missed += drops; PDUMP_LOG(DEBUG, "only %d of packets enqueued to ring\n", ring_enq); rte_pktmbuf_free_bulk(&dup_bufs[ring_enq], drops); @@ -100,43 +128,50 @@ pdump_copy(struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) } static uint16_t -pdump_rx(uint16_t port __rte_unused, uint16_t qidx __rte_unused, +pdump_rx(uint16_t port, uint16_t queue, struct rte_mbuf **pkts, uint16_t nb_pkts, uint16_t max_pkts __rte_unused, void *user_params) { - pdump_copy(pkts, nb_pkts, user_params); + pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN, + pkts, nb_pkts, user_params); return nb_pkts; } static uint16_t -pdump_tx(uint16_t port __rte_unused, uint16_t qidx __rte_unused, +pdump_tx(uint16_t port, uint16_t queue, struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) { - pdump_copy(pkts, nb_pkts, user_params); + pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT, + pkts, nb_pkts, user_params); return nb_pkts; } static int -pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, - struct rte_ring *ring, struct rte_mempool *mp, - uint16_t operation) +pdump_register_rx_callbacks(enum pdump_version ver, + uint16_t end_q, uint16_t port, uint16_t queue, + struct rte_ring *ring, struct rte_mempool *mp, + uint16_t operation, uint32_t snaplen) { uint16_t qid; - struct pdump_rxtx_cbs *cbs = NULL; qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue; for (; qid < end_q; qid++) { - cbs = &rx_cbs[port][qid]; - if (cbs && operation == ENABLE) { + struct pdump_rxtx_cbs *cbs = &rx_cbs[port][qid]; + + if (operation == ENABLE) { if (cbs->cb) { PDUMP_LOG(ERR, "rx callback for port=%d queue=%d, already exists\n", port, qid); return -EEXIST; } + cbs->ver = ver; cbs->ring = ring; cbs->mp = mp; + cbs->snaplen = snaplen; + memset(&cbs->stats, 0, sizeof(cbs->stats)); + cbs->cb = rte_eth_add_first_rx_callback(port, qid, pdump_rx, cbs); if (cbs->cb == NULL) { @@ -145,8 +180,7 @@ pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, rte_errno); return rte_errno; } - } - if (cbs && operation == DISABLE) { + } else if (operation == DISABLE) { int ret; if (cbs->cb == NULL) { @@ -170,26 +204,30 @@ pdump_register_rx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, } static int -pdump_register_tx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, - struct rte_ring *ring, struct rte_mempool *mp, - uint16_t operation) +pdump_register_tx_callbacks(enum pdump_version ver, + uint16_t end_q, uint16_t port, uint16_t queue, + struct rte_ring *ring, struct rte_mempool *mp, + uint16_t operation, uint32_t snaplen) { uint16_t qid; - struct pdump_rxtx_cbs *cbs = NULL; qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue; for (; qid < end_q; qid++) { - cbs = &tx_cbs[port][qid]; - if (cbs && operation == ENABLE) { + struct pdump_rxtx_cbs *cbs = &tx_cbs[port][qid]; + + if (operation == ENABLE) { if (cbs->cb) { PDUMP_LOG(ERR, "tx callback for port=%d queue=%d, already exists\n", port, qid); return -EEXIST; } + cbs->ver = ver; cbs->ring = ring; cbs->mp = mp; + cbs->snaplen = snaplen; + memset(&cbs->stats, 0, sizeof(cbs->stats)); cbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx, cbs); if (cbs->cb == NULL) { @@ -198,8 +236,7 @@ pdump_register_tx_callbacks(uint16_t end_q, uint16_t port, uint16_t queue, rte_errno); return rte_errno; } - } - if (cbs && operation == DISABLE) { + } else if (operation == DISABLE) { int ret; if (cbs->cb == NULL) { @@ -233,32 +270,25 @@ set_pdump_rxtx_cbs(const struct pdump_request *p) struct rte_ring *ring; struct rte_mempool *mp; + if (!(p->ver == PDUMP_CLIENT_LEGACY || + p->ver == PDUMP_CLIENT_PCAPNG)) { + PDUMP_LOG(ERR, + "incorrect client version %u\n", p->ver); + return -EINVAL; + } + flags = p->flags; operation = p->op; - if (operation == ENABLE) { - ret = rte_eth_dev_get_port_by_name(p->data.en_v1.device, - &port); - if (ret < 0) { - PDUMP_LOG(ERR, - "failed to get port id for device id=%s\n", - p->data.en_v1.device); - return -EINVAL; - } - queue = p->data.en_v1.queue; - ring = p->data.en_v1.ring; - mp = p->data.en_v1.mp; - } else { - ret = rte_eth_dev_get_port_by_name(p->data.dis_v1.device, - &port); - if (ret < 0) { - PDUMP_LOG(ERR, - "failed to get port id for device id=%s\n", - p->data.dis_v1.device); - return -EINVAL; - } - queue = p->data.dis_v1.queue; - ring = p->data.dis_v1.ring; - mp = p->data.dis_v1.mp; + queue = p->queue; + ring = p->ring; + mp = p->mp; + + ret = rte_eth_dev_get_port_by_name(p->device, &port); + if (ret < 0) { + PDUMP_LOG(ERR, + "failed to get port id for device id=%s\n", + p->device); + return -EINVAL; } /* validation if packet capture is for all queues */ @@ -296,8 +326,8 @@ set_pdump_rxtx_cbs(const struct pdump_request *p) /* register RX callback */ if (flags & RTE_PDUMP_FLAG_RX) { end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1; - ret = pdump_register_rx_callbacks(end_q, port, queue, ring, mp, - operation); + ret = pdump_register_rx_callbacks(p->ver, end_q, port, queue, ring, mp, + operation, p->snaplen); if (ret < 0) return ret; } @@ -305,8 +335,8 @@ set_pdump_rxtx_cbs(const struct pdump_request *p) /* register TX callback */ if (flags & RTE_PDUMP_FLAG_TX) { end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1; - ret = pdump_register_tx_callbacks(end_q, port, queue, ring, mp, - operation); + ret = pdump_register_tx_callbacks(p->ver, end_q, port, queue, ring, mp, + operation, p->snaplen); if (ret < 0) return ret; } @@ -349,6 +379,8 @@ rte_pdump_init(void) { int ret; + rte_pcapng_init(); + ret = rte_mp_action_register(PDUMP_MP, pdump_server); if (ret && rte_errno != ENOTSUP) return -1; @@ -392,14 +424,21 @@ pdump_validate_ring_mp(struct rte_ring *ring, struct rte_mempool *mp) static int pdump_validate_flags(uint32_t flags) { - if (flags != RTE_PDUMP_FLAG_RX && flags != RTE_PDUMP_FLAG_TX && - flags != RTE_PDUMP_FLAG_RXTX) { + if ((flags & RTE_PDUMP_FLAG_RXTX) == 0) { PDUMP_LOG(ERR, "invalid flags, should be either rx/tx/rxtx\n"); rte_errno = EINVAL; return -1; } + /* mask off the flags we know about */ + if (flags & ~(RTE_PDUMP_FLAG_RXTX | RTE_PDUMP_FLAG_PCAPNG)) { + PDUMP_LOG(ERR, + "unknown flags: %#x\n", flags); + rte_errno = ENOTSUP; + return -1; + } + return 0; } @@ -426,12 +465,12 @@ pdump_validate_port(uint16_t port, char *name) } static int -pdump_prepare_client_request(char *device, uint16_t queue, - uint32_t flags, - uint16_t operation, - struct rte_ring *ring, - struct rte_mempool *mp, - void *filter) +pdump_prepare_client_request(const char *device, uint16_t queue, + uint32_t flags, uint32_t snaplen, + uint16_t operation, + struct rte_ring *ring, + struct rte_mempool *mp, + const struct rte_bpf *filter) { int ret = -1; struct rte_mp_msg mp_req, *mp_rep; @@ -440,23 +479,23 @@ pdump_prepare_client_request(char *device, uint16_t queue, struct pdump_request *req = (struct pdump_request *)mp_req.param; struct pdump_response *resp; - req->ver = 1; - req->flags = flags; + memset(req, 0, sizeof(*req)); + if (flags & RTE_PDUMP_FLAG_PCAPNG) + req->ver = PDUMP_CLIENT_PCAPNG; + else + req->ver = PDUMP_CLIENT_LEGACY; + + req->flags = flags & RTE_PDUMP_FLAG_RXTX; req->op = operation; + req->queue = queue; + strlcpy(req->device, device,sizeof(req->device)); + if ((operation & ENABLE) != 0) { - strlcpy(req->data.en_v1.device, device, - sizeof(req->data.en_v1.device)); - req->data.en_v1.queue = queue; - req->data.en_v1.ring = ring; - req->data.en_v1.mp = mp; - req->data.en_v1.filter = filter; - } else { - strlcpy(req->data.dis_v1.device, device, - sizeof(req->data.dis_v1.device)); - req->data.dis_v1.queue = queue; - req->data.dis_v1.ring = NULL; - req->data.dis_v1.mp = NULL; - req->data.dis_v1.filter = NULL; + req->queue = queue; + req->ring = ring; + req->mp = mp; + req->filter = filter; + req->snaplen = snaplen; } strlcpy(mp_req.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN); @@ -477,11 +516,17 @@ pdump_prepare_client_request(char *device, uint16_t queue, return ret; } -int -rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags, - struct rte_ring *ring, - struct rte_mempool *mp, - void *filter) +/* + * There are two versions of this function, because although original API + * left place holder for future filter, it never checked the value. + * Therefore the API can't depend on application passing a non + * bogus value. + */ +static int +pdump_enable(uint16_t port, uint16_t queue, + uint32_t flags, uint32_t snaplen, + struct rte_ring *ring, struct rte_mempool *mp, + const struct rte_bpf *filter) { int ret; char name[RTE_DEV_NAME_MAX_LEN]; @@ -496,20 +541,42 @@ rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags, if (ret < 0) return ret; - ret = pdump_prepare_client_request(name, queue, flags, - ENABLE, ring, mp, filter); + if (snaplen == 0) + snaplen = UINT32_MAX; - return ret; + return pdump_prepare_client_request(name, queue, flags, snaplen, + ENABLE, ring, mp, filter); } int -rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, - uint32_t flags, - struct rte_ring *ring, - struct rte_mempool *mp, - void *filter) +rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags, + struct rte_ring *ring, + struct rte_mempool *mp, + void *filter __rte_unused) { - int ret = 0; + return pdump_enable(port, queue, flags, 0, + ring, mp, NULL); +} + +int +rte_pdump_enable_bpf(uint16_t port, uint16_t queue, + uint32_t flags, uint32_t snaplen, + struct rte_ring *ring, + struct rte_mempool *mp, + const struct rte_bpf *filter) +{ + return pdump_enable(port, queue, flags, snaplen, + ring, mp, filter); +} + +static int +pdump_enable_by_deviceid(const char *device_id, uint16_t queue, + uint32_t flags, uint32_t snaplen, + struct rte_ring *ring, + struct rte_mempool *mp, + const struct rte_bpf *filter) +{ + int ret; ret = pdump_validate_ring_mp(ring, mp); if (ret < 0) @@ -518,10 +585,30 @@ rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, if (ret < 0) return ret; - ret = pdump_prepare_client_request(device_id, queue, flags, - ENABLE, ring, mp, filter); + return pdump_prepare_client_request(device_id, queue, flags, snaplen, + ENABLE, ring, mp, filter); +} - return ret; +int +rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, + uint32_t flags, + struct rte_ring *ring, + struct rte_mempool *mp, + void *filter __rte_unused) +{ + return pdump_enable_by_deviceid(device_id, queue, flags, 0, + ring, mp, NULL); +} + +int +rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue, + uint32_t flags, uint32_t snaplen, + struct rte_ring *ring, + struct rte_mempool *mp, + const struct rte_bpf *filter) +{ + return pdump_enable_by_deviceid(device_id, queue, flags, snaplen, + ring, mp, filter); } int @@ -537,8 +624,8 @@ rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags) if (ret < 0) return ret; - ret = pdump_prepare_client_request(name, queue, flags, - DISABLE, NULL, NULL, NULL); + ret = pdump_prepare_client_request(name, queue, flags, 0, + DISABLE, NULL, NULL, NULL); return ret; } @@ -553,8 +640,73 @@ rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue, if (ret < 0) return ret; - ret = pdump_prepare_client_request(device_id, queue, flags, - DISABLE, NULL, NULL, NULL); + ret = pdump_prepare_client_request(device_id, queue, flags, 0, + DISABLE, NULL, NULL, NULL); return ret; } + +static void +pdump_sum_stats(struct rte_pdump_stats *total, + const struct pdump_rxtx_cbs *cbs, + uint16_t nq) +{ + uint16_t qid; + + memset(total, 0, sizeof(*total)); + + for (qid = 0; qid < nq; qid++) { + total->received += cbs[qid].stats.received; + total->missed += cbs[qid].stats.missed; + total->accepted += cbs[qid].stats.accepted; + } +} + +int +rte_pdump_get_stats(uint16_t port, uint16_t queue, + struct rte_pdump_stats *rx_stats, + struct rte_pdump_stats *tx_stats) +{ + uint16_t nb_rx_q = 0, nb_tx_q = 0; + + if (port >= RTE_MAX_ETHPORTS) { + PDUMP_LOG(ERR, "Invalid port id %u\n", port); + rte_errno = EINVAL; + return -1; + } + + if (queue == RTE_PDUMP_ALL_QUEUES) { + struct rte_eth_dev_info dev_info; + int ret; + + ret = rte_eth_dev_info_get(port, &dev_info); + if (ret != 0) { + PDUMP_LOG(ERR, + "Error during getting device (port %u) info: %s\n", + port, strerror(-ret)); + return ret; + } + nb_rx_q = dev_info.nb_rx_queues; + nb_tx_q = dev_info.nb_tx_queues; + } else if (queue >= RTE_MAX_QUEUES_PER_PORT) { + PDUMP_LOG(ERR, "Invalid queue id %u\n", queue); + rte_errno = EINVAL; + return -1; + } + + if (rx_stats) { + if (queue == RTE_PDUMP_ALL_QUEUES) + pdump_sum_stats(rx_stats, &rx_cbs[port][0], nb_rx_q); + else + *rx_stats = rx_cbs[port][queue].stats; + } + + if (tx_stats) { + if (queue == RTE_PDUMP_ALL_QUEUES) + pdump_sum_stats(tx_stats, &tx_cbs[port][0], nb_tx_q); + else + *tx_stats = tx_cbs[port][queue].stats; + } + + return 0; +} diff --git a/lib/pdump/rte_pdump.h b/lib/pdump/rte_pdump.h index 6b00fc17aeb2..992331fddffb 100644 --- a/lib/pdump/rte_pdump.h +++ b/lib/pdump/rte_pdump.h @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -26,7 +27,9 @@ enum { RTE_PDUMP_FLAG_RX = 1, /* receive direction */ RTE_PDUMP_FLAG_TX = 2, /* transmit direction */ /* both receive and transmit directions */ - RTE_PDUMP_FLAG_RXTX = (RTE_PDUMP_FLAG_RX|RTE_PDUMP_FLAG_TX) + RTE_PDUMP_FLAG_RXTX = (RTE_PDUMP_FLAG_RX|RTE_PDUMP_FLAG_TX), + + RTE_PDUMP_FLAG_PCAPNG = 4, /* format for pcapng */ }; /** @@ -68,7 +71,7 @@ rte_pdump_uninit(void); * @param mp * mempool on to which original packets will be mirrored or duplicated. * @param filter - * place holder for packet filtering. + * Unused should be NULL. * * @return * 0 on success, -1 on error, rte_errno is set accordingly. @@ -80,6 +83,42 @@ rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags, struct rte_mempool *mp, void *filter); +/** + * @warning + * @b EXPERIMENTAL: this API may change, or be removed, without prior notice + * + * Enables packet capturing on given port and queue with filtering. + * + * @param port + * port on which packet capturing should be enabled. + * @param queue + * queue of a given port on which packet capturing should be enabled. + * users should pass on value UINT16_MAX to enable packet capturing on all + * queues of a given port. + * @param flags + * flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX + * on which packet capturing should be enabled for a given port and queue. + * @param snaplen + * snapshot length. No more than snaplen bytes of the network packet + * will be saved. Use 0 or 262144 to capture all of the packet. + * @param ring + * ring on which captured packets will be enqueued for user. + * @param mp + * mempool on to which original packets will be mirrored or duplicated. + * @param filter + * BPF filter to run to filter packes (can be NULL) + * + * @return + * 0 on success, -1 on error, rte_errno is set accordingly. + */ +__rte_experimental +int +rte_pdump_enable_bpf(uint16_t port, uint16_t queue, + uint32_t flags, uint32_t snaplen, + struct rte_ring *ring, + struct rte_mempool *mp, + const struct rte_bpf *filter); + /** * Disables packet capturing on given port and queue. * @@ -118,7 +157,7 @@ rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags); * @param mp * mempool on to which original packets will be mirrored or duplicated. * @param filter - * place holder for packet filtering. + * unused should be NULL * * @return * 0 on success, -1 on error, rte_errno is set accordingly. @@ -131,6 +170,44 @@ rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, struct rte_mempool *mp, void *filter); +/** + * @warning + * @b EXPERIMENTAL: this API may change, or be removed, without prior notice + * + * Enables packet capturing on given device id and queue with filtering. + * device_id can be name or pci address of device. + * + * @param device_id + * device id on which packet capturing should be enabled. + * @param queue + * queue of a given device id on which packet capturing should be enabled. + * users should pass on value UINT16_MAX to enable packet capturing on all + * queues of a given device id. + * @param flags + * flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX + * on which packet capturing should be enabled for a given port and queue. + * @param snaplen + * snapshot length. No more than snaplen bytes of the network packet + * will be saved. Use 0 or 262144 to capture all of the packet. + * @param ring + * ring on which captured packets will be enqueued for user. + * @param mp + * mempool on to which original packets will be mirrored or duplicated. + * @param filter + * BPF filter to run to filter packes (can be NULL) + * + * @return + * 0 on success, -1 on error, rte_errno is set accordingly. + */ +__rte_experimental +int +rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue, + uint32_t flags, uint32_t snaplen, + struct rte_ring *ring, + struct rte_mempool *mp, + const struct rte_bpf *filter); + + /** * Disables packet capturing on given device_id and queue. * device_id can be name or pci address of device. @@ -153,6 +230,40 @@ int rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue, uint32_t flags); +struct rte_pdump_stats { + uint64_t received; /**< callback called */ + uint64_t accepted; /**< allowed by filter */ + uint64_t missed; /**< ring full */ +}; + +/** + * Query packet capture statistics. + * + * @param port + * port on which packet capturing should be enabled. + * @param queue + * queue of a given port on which packet capturing should be enabled. + * users should pass on value UINT16_MAX to enable packet capturing on all + * queues of a given port. + * @param rx_stats + * A pointer to a structure of type *rte_pdump_stats* to be filled with + * the values of the capture statistics: + * - *received* with the total of received packets. + * - *accepted* with the total of packets matched by the filter. + * - *missed* with the total of packets missed because of ring full. + * @param tx_stats + * - *received* with the total of transmitted packets. + * - *accepted* with the total of packets matched by the filter. + * - *missed* with the total of packets missed because of ring full. + * @return + * Zero if successful. Non-zero otherwise. + */ +__rte_experimental +int +rte_pdump_get_stats(uint16_t port, uint16_t queue, + struct rte_pdump_stats *rx_stats, + struct rte_pdump_stats *tx_stats); + #ifdef __cplusplus } #endif diff --git a/lib/pdump/version.map b/lib/pdump/version.map index f0a9d12c9a9e..b4c20b56f237 100644 --- a/lib/pdump/version.map +++ b/lib/pdump/version.map @@ -10,3 +10,11 @@ DPDK_22 { local: *; }; + +EXPERIMENTAL { + global: + + rte_pdump_enable_bpf; + rte_pdump_enable_bpf_by_deviceid; + rte_pdump_get_stats; +}; From patchwork Fri Sep 3 00:47:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 97853 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 813CBA0C4D; Fri, 3 Sep 2021 02:47:56 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 0A973410F6; Fri, 3 Sep 2021 02:47:43 +0200 (CEST) Received: from mail-pj1-f48.google.com (mail-pj1-f48.google.com [209.85.216.48]) by mails.dpdk.org (Postfix) with ESMTP id 2841840E09 for ; Fri, 3 Sep 2021 02:47:40 +0200 (CEST) Received: by mail-pj1-f48.google.com with SMTP id z24-20020a17090acb1800b0018e87a24300so2802485pjt.0 for ; Thu, 02 Sep 2021 17:47:40 -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=InVqe02STFFcSAprsGkZvhpYC328mAQaar12wdYF6vQ=; b=wIRkLrbEPj/PKmPK7C+OA/lFu0vVGagxOSrA4iTq9Z/U5ddm8GhZ/Av/IF4CHo8Snv TZK7L4ojjlpiuU1Mpd/nvZ311BBw3zg2pRA95YFmCGTXVjfVdtP95vCFTQCtSMuEvbxw MOsOKJ5ol8oFIqCkydSirdN0lHKBdAdUsPsMLoXA8QRm6nJUHahGtKjo7ZlfAU+2ukSh zKAGbqNLL58jg68XGS7wkLw33kwEromgndsjCbzNZtuypizfda7k0XjcG7XHiZ4vn4TI Gtdt+aH0nHr4SpxyvBlz9NGu89JhOys8rovCXzPHvQlXDPuHPnFW85WqGrgCfnH6KFFi L9hQ== 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=InVqe02STFFcSAprsGkZvhpYC328mAQaar12wdYF6vQ=; b=Za97cy9U9DdMdihYlFO0R236VLBH7GjCb4UUwFormPodeUnoXXE3B4mO6j3sSUmVxz qdlONxFjmk1KY5A/ZWvG6CvWbdD2k47614gZl4FF54evnnoEBz4wMH/+Pn1nSc7394ut WsGE0L00+kklNqMM1Mw7N4DjJ86CA4x//fJpjhDvrwSgGdrPl2ExEq6n5/jnIeyPuW+I InKTc17k1/yw94x1mbSIxFA1LsSTX5ymBm3kAgm0olqKC/sjpY3p4rw6LZpbQTs6iZVR 6IW3z308oslxDuVKvy4RNa3hUhCbQIkdwy8RdV0albk+H7gbedwRa0MPL16UoiYeOY00 LU8Q== X-Gm-Message-State: AOAM533AQs9vFjnSbGD0smIJVsILI+QSgPS4JmxK96zYINHNNd07gj8C KkNYIkgrcAi8ZR5PCxZYHrcXRoEc9Zb9ig== X-Google-Smtp-Source: ABdhPJzVvI8tX6WZAeYvPzhzLI3/vx2QDFmHRaMKpIcqG8+uqC9+fc88IcIUGNhHj9EGFfqAQWoLIQ== X-Received: by 2002:a17:902:a3cd:b0:138:f21a:536c with SMTP id q13-20020a170902a3cd00b00138f21a536cmr919937plb.23.1630630058457; Thu, 02 Sep 2021 17:47:38 -0700 (PDT) Received: from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123]) by smtp.gmail.com with ESMTPSA id cq8sm3179033pjb.31.2021.09.02.17.47.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Sep 2021 17:47:37 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Date: Thu, 2 Sep 2021 17:47:30 -0700 Message-Id: <20210903004732.109023-4-stephen@networkplumber.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210903004732.109023-1-stephen@networkplumber.org> References: <20210903004732.109023-1-stephen@networkplumber.org> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 3/5] app/dumpcap: add new packet capture application X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 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" This is a new packet capture application to replace existing pdump. The new application works like Wireshark dumpcap program and supports the pdump API features. It is not complete yet some features such as filtering are not implemented. Signed-off-by: Stephen Hemminger --- app/dumpcap/main.c | 829 ++++++++++++++++++++++++++++++++++++++++ app/dumpcap/meson.build | 11 + app/meson.build | 1 + 3 files changed, 841 insertions(+) create mode 100644 app/dumpcap/main.c create mode 100644 app/dumpcap/meson.build diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c new file mode 100644 index 000000000000..09fc7dcb5a21 --- /dev/null +++ b/app/dumpcap/main.c @@ -0,0 +1,829 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2019-2020 Microsoft Corporation + * + * DPDK application to dump network traffic + * This is designed to look and act like the Wireshark + * dumpcap program. + */ + +#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 +#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 *progname; +static bool quit_signal; +static bool group_read; +static bool quiet; +static bool promiscuous_mode = true; +static bool use_pcapng = true; +static bool show_statistics; +static char *output_name; +static const char *filter_str; +static unsigned int ring_size = 2048; +static uint64_t packets_received; +static size_t file_size; +static const char *capture_comment; +static uint32_t snaplen = 65535; +static bool dump_bpf; +static struct timespec start_time; + +static struct { + double duration; + unsigned long packets; + size_t size; +} stop; + +struct interface { + TAILQ_ENTRY(interface) next; + uint64_t missed; + uint16_t port; + char name[RTE_ETH_NAME_MAX_LEN]; + + struct rte_rxtx_callback *rx_cb[RTE_MAX_QUEUES_PER_PORT]; +}; + +TAILQ_HEAD(interface_list, interface); +static 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", progname); + printf("Capture Interface:\n" + " -i name or port index of interface\n" + " -f packet filter in libpcap filter syntax\n" + " -s , --snapshot-length \n" + " packet snapshot length (def: appropriate maximum)\n" + " -p, --no-promiscuous-mode\n" + " don't capture in promiscuous mode\n" + " -D, --list-interfaces print list of interfaces and exit\n" + " -d print generated BPF code for capture filter\n" + " -S print statistics for each interface once per second\n" + "\n" + "Stop conditions:\n" + " -c stop after n packets (def: infinite)\n" + " -a ..., --autostop ...\n" + " duration:NUM - stop after NUM seconds\n" + " filesize:NUM - stop this file after NUM kB\n" + " packets:NUM - stop after NUM packets\n" + "Output (files):\n" + " -w name of file to save (def: tempfile)\n" + " -g enable group read access on the output file(s)\n" + " -n use pcapng format instead of pcap (default)\n" + " -P use libpcap format instead of pcapng\n" + " --capture-comment \n" + " add a capture comment to the output file\n" + "\n" + "Miscellaneous:\n" + " -q don't report packet capture counts\n" + " -v, --version print version information and exit\n" + " -h, --help display this help and exit\n" + "\n" + "Use Ctrl-C to stop capturing at any time.\n"); +} + +static const char *version(void) +{ + static char str[128]; + + snprintf(str, sizeof(str), + "%s 1.0 (%s)\n", progname, rte_version()); + return str; +} + +/* Parse numeric argument from command line */ +static unsigned long 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; +} + +/* Set auto stop values */ +static void autostop(char *opt) +{ + char *value, *endp; + + value = strchr(opt, ':'); + if (value == NULL) + rte_exit(EXIT_FAILURE, + "Missing colon in auto stop parameter\n"); + + *value++ = '\0'; + if (strcmp(opt, "duration") == 0) { + stop.duration = strtod(value, &endp); + if (*value == '\0' || *endp != '\0' || stop.duration < 0) + rte_exit(EXIT_FAILURE, + "Invalid duration \"%s\"\n", value); + } else if (strcmp(opt, "filesize") == 0) { + stop.size = get_uint(value, "filesize", 0) * 1024; + } else if (strcmp(opt, "packets") == 0) { + stop.packets = get_uint(value, "packets", 0); + } else { + rte_exit(EXIT_FAILURE, + "Unknown autostop parameter \"%s\"\n", opt); + } +} + +/* 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 rte_bpf *compile_filter(void) +{ + struct bpf_program fcode; + pcap_t *pcap; + + 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)); + + rte_exit(EXIT_FAILURE, "filter not implemented yet\n"); + + /* + * Need to convert classic BPF to eBPF and put in shared memory + * be read by primary process. + */ + pcap_freecode(&fcode); + pcap_close(pcap); + + rte_exit(EXIT_FAILURE, "not implemented\n"); + return NULL; +} + +/* + * 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[] = { + { "autostop", required_argument, NULL, 'a' }, + { "capture-comment", required_argument, NULL, 0 }, + { "help", no_argument, NULL, 'h' }, + { "interface", required_argument, NULL, 'i' }, + { "list-interfaces", no_argument, NULL, 'D' }, + { "no-promiscuous-mode", no_argument, NULL, 'p' }, + { "output-file", required_argument, NULL, 'w' }, + { "ring-buffer", required_argument, NULL, 'b' }, + { "snapshot-length", required_argument, NULL, 's' }, + { "version", no_argument, NULL, 'v' }, + { NULL }, + }; + int option_index, c; + + for (;;) { + c = getopt_long(argc, argv, "a:b:c:dDf:ghi:nN:pPqs:Svw:", + 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 'a': + autostop(optarg); + break; + case 'b': + rte_exit(EXIT_FAILURE, + "multiple files not implemented\n"); + break; + case 'c': + stop.packets = get_uint(optarg, "packet_count", 0); + break; + case 'd': + dump_bpf = true; + break; + case 'D': + show_interfaces(); + exit(0); + case 'f': + filter_str = optarg; + break; + case 'g': + group_read = true; + break; + case 'h': + printf("%s\n\n", version()); + usage(); + exit(0); + case 'i': + select_interface(optarg); + break; + case 'n': + use_pcapng = true; + break; + case 'N': + ring_size = get_uint(optarg, "packet_limit", 0); + break; + case 'p': + promiscuous_mode = false; + break; + case 'P': + use_pcapng = false; + break; + case 'q': + quiet = true; + break; + case 's': + snaplen = get_uint(optarg, "snap_len", 0); + break; + case 'S': + show_statistics = true; + break; + case 'w': + output_name = optarg; + break; + case 'v': + printf("%s\n", version()); + exit(0); + default: + fprintf(stderr, "Invalid option: %s\n", + argv[optind - 1]); + usage(); + exit(1); + } + } +} + +static void +signal_handler(int sig_num __rte_unused) +{ + __atomic_store_n(&quit_signal, true, __ATOMIC_RELAXED); +} + +static double elapsed(void) +{ + struct timespec now; + double secs; + + clock_gettime(CLOCK_MONOTONIC_COARSE, &now); + secs = now.tv_sec - start_time.tv_sec; + secs += (now.tv_nsec - start_time.tv_nsec) / 1.e9; + return secs; +} + +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 (__atomic_load_n(&quit_signal, __ATOMIC_RELAXED)) + 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"); + __atomic_store_n(&quit_signal, true, __ATOMIC_RELAXED); +} + +/* 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 +report_packet_stats(rte_pcapng_t *out) +{ + struct interface *intf; + struct rte_pdump_stats rxstats, txstats; + struct rte_eth_stats stats; + uint64_t received, accepted, dropped, ifdrop; + + fputc('\n', stderr); + TAILQ_FOREACH(intf, &interfaces, next) { + if (rte_pdump_get_stats(intf->port, UINT16_MAX, + &rxstats, &txstats) < 0) + continue; + + if (rte_eth_stats_get(intf->port, &stats) < 0) + ifdrop = 0; + else + ifdrop = stats.imissed - intf->missed; + + received = rxstats.received + txstats.received; + accepted = rxstats.accepted + txstats.accepted; + dropped = rxstats.missed + txstats.missed; + + if (use_pcapng) + rte_pcapng_write_stats(out, intf->port, + received, ifdrop, accepted, dropped); + fprintf(stderr, + "Packets received/dropped on interface '%s': " + "%"PRIu64 "/%" PRIu64 " (%.1f)\n", + intf->name, received, dropped, + received ? 100. * received / (received + dropped) : 0.); + } +} + +/* + * Start DPDK EAL with arguments. + * Unlike most DPDK programs, this application does not use the + * typical EAL command line arguments. + * We don't want to expose all the DPDK internals to the user. + */ +static void dpdk_init(void) +{ + const char *args[] = { + progname, "--proc-type", "secondary", "--log-level", "error" + }; + const int eal_argc = RTE_DIM(args); + char **eal_argv; + unsigned int i; + + /* DPDK API requires mutable versions of command line arguments. */ + eal_argv = calloc(eal_argc + 1, sizeof(char *)); + if (eal_argv == NULL) + rte_panic("No memory\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 found\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; + uint16_t data_size = RTE_MBUF_DEFAULT_BUF_SIZE; + + mp = rte_mempool_lookup(pool_name); + if (mp) + return mp; + + if (snaplen < data_size) + data_size = snaplen; + + mp = rte_pktmbuf_pool_create_by_ops(pool_name, num_mbufs, + MBUF_POOL_CACHE_SIZE, 0, + data_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 void *create_output(void) +{ + struct utsname uts; + char os[256]; + 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.%s", + progname, intf->port, intf->name, ts, + use_pcapng ? "pcapng" : "pcap") < 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_CREAT, mode); + if (fd < 0) + rte_exit(EXIT_FAILURE, "Can not open \"%s\": %s\n", + output_name, strerror(errno)); + } + + if (use_pcapng) { + rte_pcapng_t *pcapng; + + if (uname(&uts) < 0) + strcpy(os, "unknown"); + else + snprintf(os, sizeof(os), "%s %s", + uts.sysname, uts.release); + + pcapng = rte_pcapng_fdopen(fd, os, NULL, version(), capture_comment); + if (pcapng == NULL) + rte_exit(EXIT_FAILURE, "pcapng_fdopen failed: %s\n", + strerror(rte_errno)); + return pcapng; + } else { + pcap_dumper_t *dumper; + pcap_t *pcap; + + pcap = pcap_open_dead_with_tstamp_precision(DLT_EN10MB, snaplen, + PCAP_TSTAMP_PRECISION_NANO); + if (pcap == NULL) + rte_exit(EXIT_FAILURE, "pcap_open_dead failed\n"); + + dumper = pcap_dump_fopen(pcap, fdopen(fd, "w")); + if (dumper == NULL) + rte_exit(EXIT_FAILURE, "pcap_dump_fopen failed: %s\n", + pcap_geterr(pcap)); + return dumper; + } +} + +static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp, + struct rte_bpf *filter) +{ + struct rte_eth_stats stats; + struct interface *intf; + uint32_t flags; + int ret; + + flags = RTE_PDUMP_FLAG_RXTX; + if (use_pcapng) + flags |= RTE_PDUMP_FLAG_PCAPNG; + + TAILQ_FOREACH(intf, &interfaces, next) { + if (promiscuous_mode) + rte_eth_promiscuous_enable(intf->port); + + if (rte_eth_stats_get(intf->port, &stats) < 0) + intf->missed = 0; + else + intf->missed = stats.imissed; + + ret = rte_pdump_enable_bpf(intf->port, RTE_PDUMP_ALL_QUEUES, + flags, snaplen, + r, mp, filter); + 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); +} + +/* Write multiple packets in older pcap format */ +static ssize_t +pcap_write_packets(pcap_dumper_t *dumper, + struct rte_mbuf *pkts[], uint16_t n) +{ + uint8_t temp_data[snaplen]; + struct pcap_pkthdr header; + uint16_t i; + size_t total = 0; + + gettimeofday(&header.ts, NULL); + + for (i = 0; i < n; i++) { + struct rte_mbuf *m = pkts[i]; + + header.len = rte_pktmbuf_pkt_len(m); + header.caplen = RTE_MIN(header.len, snaplen); + + pcap_dump((u_char *)dumper, &header, + rte_pktmbuf_read(m, 0, header.caplen, temp_data)); + + total += sizeof(header) + header.len; + } + + return total; +} + +/* Process all packets in ring and dump to capture file */ +static int process_ring(void *out, struct rte_ring *r) +{ + struct rte_mbuf *pkts[BURST_SIZE]; + unsigned int avail, n; + static unsigned int empty_count; + ssize_t written; + + 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 0; + } + + empty_count = (avail == 0); + + if (use_pcapng) + written = rte_pcapng_write_packets(out, pkts, n); + else + written = pcap_write_packets(out, pkts, n); + + rte_pktmbuf_free_bulk(pkts, n); + + if (written < 0) + return -1; + + file_size += written; + packets_received += n; + if (!quiet) + show_count(packets_received); + + return 0; +} + +int main(int argc, char **argv) +{ + struct rte_bpf *bpf_filter = NULL; + struct rte_ring *r; + struct rte_mempool *mp; + void *out; + + progname = basename(argv[0]); + + dpdk_init(); + parse_opts(argc, argv); + + if (filter_str) + bpf_filter = compile_filter(); + + if (dump_bpf) + fprintf(stderr, "dump filter not implemented yet\n"); + + 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)); + + if (clock_gettime(CLOCK_MONOTONIC_COARSE, &start_time) < 0) + rte_exit(EXIT_FAILURE, "clock_gettime() failed: %s\n", + strerror(errno)); + + enable_pdump(r, mp, bpf_filter); + + signal(SIGINT, signal_handler); + signal(SIGPIPE, SIG_IGN); + + enable_primary_monitor(); + + if (!quiet) { + fprintf(stderr, "Packets captured: "); + show_count(0); + } + + while (!__atomic_load_n(&quit_signal, __ATOMIC_RELAXED)) { + if (process_ring(out, r) < 0) { + fprintf(stderr, "pcapng file write failed; %s\n", + strerror(errno)); + break; + } + + if (stop.size && file_size >= stop.size) + break; + + if (stop.packets && packets_received >= stop.packets) + break; + + if (stop.duration != 0 && elapsed() > stop.duration) + break; + } + + disable_primary_monitor(); + + report_packet_stats(out); + if (use_pcapng) + rte_pcapng_close(out); + else + pcap_dump_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/dumpcap/meson.build b/app/dumpcap/meson.build new file mode 100644 index 000000000000..7a98994d3ce4 --- /dev/null +++ b/app/dumpcap/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2019 Microsoft Corporation + +if is_windows + build = false + reason = 'not supported on Windows' + subdir_done() +endif + +sources = files('main.c') +deps += ['ethdev', 'pdump', 'pcapng'] diff --git a/app/meson.build b/app/meson.build index 4c6049807cc3..e41a2e390236 100644 --- a/app/meson.build +++ b/app/meson.build @@ -2,6 +2,7 @@ # Copyright(c) 2017-2019 Intel Corporation apps = [ + 'dumpcap', 'pdump', 'proc-info', 'test-acl', From patchwork Fri Sep 3 00:47:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 97854 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id D10C2A0C4D; Fri, 3 Sep 2021 02:48:01 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 357FE41100; Fri, 3 Sep 2021 02:47:44 +0200 (CEST) Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) by mails.dpdk.org (Postfix) with ESMTP id 234F240041 for ; Fri, 3 Sep 2021 02:47:41 +0200 (CEST) Received: by mail-pg1-f173.google.com with SMTP id r2so3825617pgl.10 for ; Thu, 02 Sep 2021 17:47:41 -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=BD/za4PZGzBtcQ40Y5JqOc5jLeyflDSGbqRv4jFc1Ug=; b=sGpl/i/6XizuSfeHVz3yKpG4gHAQr8jmxLBls83n0CS4bbKmeXfNDPnJy7itA6poQz 2AYbhSP12/Fm3sIsc8EUbf5bZa4hVnIU3dIDETezI/2PeeQ/1P1p8m7263qwPS4iUF8+ Hdi2lX+l4OXGktXEMaFdcKG7N6c2tbZEL5Ao2CHZphrQvHYjQYb83OJH/rAeGrFLh86U uc5WtUSVSkW1qkBrVDnsE+g/wGH1XN0rZ3KP4ZGqOQes3caOlxrTvV5LSnt5pCRgjs5h ja0UoB4PoEM+UAKR0vRpADYvsgvRonAvegpB8tvnpvV03RqBo+vhmCHGSzveY4e1hv6q mklg== 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=BD/za4PZGzBtcQ40Y5JqOc5jLeyflDSGbqRv4jFc1Ug=; b=S/3XkpgQ7Oi1ZwQCwRYjbC23BnQQu4thQW+EJezn7ykKyDD4zVb+zcwN+6Z1cDlYjm 40q8GcMlI+r5SFDr43qr96f378kz+hvB1yGdku6dXVEmt9eXUQclxDYL7xh65V4eDQCi mivoflLipB0rdJLPZiVnS1KB6GbOMtAig+FTo4LEFiScRnAw8Bl5hD23JQO7z9569ttz y950G+bZxPkLeZlL0AU+C41V8ikheAaiocZsaqm0GPgZNtQDJjwOG0zzVWgGH1CnbLrA COODm0RRWUVFBe87OVuRt3O0Uc+6TOT9dYFwnjZp5Tp39KkxkaFQ2zlNRhBMDfDnK/4J vKLg== X-Gm-Message-State: AOAM5320N7H1ENomFdmSDGKM2C8xb2umVrt98PPfqY3pOEVDjUOaDYqp 6lZ3ZII4PePtBSC4DQ7D2K3FwKFxDJyRLg== X-Google-Smtp-Source: ABdhPJwx6jLx1BvgriuzCk1A37BTn+d1eh4yMivPZXgDeWZ5Db/csGYGeYe8YEcw67V9WXn5E2apXg== X-Received: by 2002:a05:6a00:1255:b0:3f5:26ee:ca2c with SMTP id u21-20020a056a00125500b003f526eeca2cmr1031758pfi.62.1630630059412; Thu, 02 Sep 2021 17:47:39 -0700 (PDT) Received: from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123]) by smtp.gmail.com with ESMTPSA id cq8sm3179033pjb.31.2021.09.02.17.47.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Sep 2021 17:47:38 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Date: Thu, 2 Sep 2021 17:47:31 -0700 Message-Id: <20210903004732.109023-5-stephen@networkplumber.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210903004732.109023-1-stephen@networkplumber.org> References: <20210903004732.109023-1-stephen@networkplumber.org> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 4/5] doc: changes for new pcapng and dumpcap X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 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" Describe the new packet capture library and utilities Signed-off-by: Stephen Hemminger --- doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + doc/guides/howto/packet_capture_framework.rst | 67 ++++++++-------- doc/guides/prog_guide/index.rst | 1 + doc/guides/prog_guide/pcapng_lib.rst | 24 ++++++ doc/guides/prog_guide/pdump_lib.rst | 28 +++++-- doc/guides/rel_notes/release_21_11.rst | 10 +++ doc/guides/tools/dumpcap.rst | 80 +++++++++++++++++++ doc/guides/tools/index.rst | 1 + 9 files changed, 174 insertions(+), 39 deletions(-) create mode 100644 doc/guides/prog_guide/pcapng_lib.rst create mode 100644 doc/guides/tools/dumpcap.rst diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index 1992107a0356..ee07394d1c78 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -223,3 +223,4 @@ The public API headers are grouped by topics: [experimental APIs] (@ref rte_compat.h), [ABI versioning] (@ref rte_function_versioning.h), [version] (@ref rte_version.h) + [pcapng] (@ref rte_pcapng.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index 325a0195c6ab..aba17799a9a1 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -58,6 +58,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/metrics \ @TOPDIR@/lib/node \ @TOPDIR@/lib/net \ + @TOPDIR@/lib/pcapng \ @TOPDIR@/lib/pci \ @TOPDIR@/lib/pdump \ @TOPDIR@/lib/pipeline \ diff --git a/doc/guides/howto/packet_capture_framework.rst b/doc/guides/howto/packet_capture_framework.rst index c31bac52340e..78baa609a021 100644 --- a/doc/guides/howto/packet_capture_framework.rst +++ b/doc/guides/howto/packet_capture_framework.rst @@ -1,18 +1,19 @@ .. SPDX-License-Identifier: BSD-3-Clause Copyright(c) 2017 Intel Corporation. -DPDK pdump Library and pdump Tool -================================= +DPDK packet capture libraries and tools +======================================= This document describes how the Data Plane Development Kit (DPDK) Packet Capture Framework is used for capturing packets on DPDK ports. It is intended for users of DPDK who want to know more about the Packet Capture feature and for those who want to monitor traffic on DPDK-controlled devices. -The DPDK packet capture framework was introduced in DPDK v16.07. The DPDK -packet capture framework consists of the DPDK pdump library and DPDK pdump -tool. - +The DPDK packet capture framework was introduced in DPDK v16.07 and +enhanced in 21.1. The DPDK packet capture framework consists of the +libraries for collecting packets ``librte_pdump`` and writing packets +to a file ``librte_pcapng``. There are two sample applications: +``dpdk-dumpcap`` and older ``dpdk-pdump``. Introduction ------------ @@ -22,43 +23,46 @@ allow users to initialize the packet capture framework and to enable or disable packet capture. The library works on a multi process communication model and its usage is recommended for debugging purposes. -The :ref:`dpdk-pdump ` tool is developed based on the -``librte_pdump`` library. It runs as a DPDK secondary process and is capable -of enabling or disabling packet capture on DPDK ports. The ``dpdk-pdump`` tool -provides command-line options with which users can request enabling or -disabling of the packet capture on DPDK ports. +The :ref:`librte_pcapng ` library provides the APIs to format +packets and write them to a file in Pcapng format. + + +The :ref:`dpdk-dumpcap ` is a tool that captures packets in +like Wireshark dumpcap does for Linux. It runs as a DPDK secondary process and +captures packets from one or more interfaces and writes them to a file +in Pcapng format. The ``dpdk-dumpcap`` tool is designed to take +most of the same options as the Wireshark ``dumpcap`` command. -The application which initializes the packet capture framework will be a primary process -and the application that enables or disables the packet capture will -be a secondary process. The primary process sends the Rx and Tx packets from the DPDK ports -to the secondary process. +Without any options it will use the packet capture framework to +capture traffic from the first available DPDK port. In DPDK the ``testpmd`` application can be used to initialize the packet -capture framework and acts as a server, and the ``dpdk-pdump`` tool acts as a +capture framework and acts as a server, and the ``dpdk-dumpcap`` tool acts as a client. To view Rx or Tx packets of ``testpmd``, the application should be -launched first, and then the ``dpdk-pdump`` tool. Packets from ``testpmd`` -will be sent to the tool, which then sends them on to the Pcap PMD device and -that device writes them to the Pcap file or to an external interface depending -on the command-line option used. +launched first, and then the ``dpdk-dumpcap`` tool. Packets from ``testpmd`` +will be sent to the tool, and then to the Pcapng file. Some things to note: -* The ``dpdk-pdump`` tool can only be used in conjunction with a primary +* All tools using ``librte_pdump`` can only be used in conjunction with a primary application which has the packet capture framework initialized already. In dpdk, only ``testpmd`` is modified to initialize packet capture framework, - other applications remain untouched. So, if the ``dpdk-pdump`` tool has to + other applications remain untouched. So, if the ``dpdk-dumpcap`` tool has to be used with any application other than the testpmd, the user needs to explicitly modify that application to call the packet capture framework initialization code. Refer to the ``app/test-pmd/testpmd.c`` code and look for ``pdump`` keyword to see how this is done. -* The ``dpdk-pdump`` tool depends on the libpcap based PMD. +* The ``dpdk-pdump`` tool is an older tool created as demonstration of ``librte_pdump`` + library. The ``dpdk-pdump`` tool provides more limited functionality and + and depends on the Pcap PMD. It is retained only for compatibility reasons; + users should use ``dpdk-dumpcap`` instead. Test Environment ---------------- -The overview of using the Packet Capture Framework and the ``dpdk-pdump`` tool +The overview of using the Packet Capture Framework and the ``dpdk-dumpcap`` utility for packet capturing on the DPDK port in :numref:`figure_packet_capture_framework`. @@ -66,13 +70,13 @@ for packet capturing on the DPDK port in .. figure:: img/packet_capture_framework.* - Packet capturing on a DPDK port using the dpdk-pdump tool. + Packet capturing on a DPDK port using the dpdk-dumpcap utility. Running the Application ----------------------- -The following steps demonstrate how to run the ``dpdk-pdump`` tool to capture +The following steps demonstrate how to run the ``dpdk-dumpcap`` tool to capture Rx side packets on dpdk_port0 in :numref:`figure_packet_capture_framework` and inspect them using ``tcpdump``. @@ -80,16 +84,15 @@ inspect them using ``tcpdump``. sudo /app/dpdk-testpmd -c 0xf0 -n 4 -- -i --port-topology=chained -#. Launch the pdump tool as follows:: +#. Launch the dpdk-dump as follows:: - sudo /app/dpdk-pdump -- \ - --pdump 'port=0,queue=*,rx-dev=/tmp/capture.pcap' + sudo /app/dpdk-dumpcap -w /tmp/capture.pcapng #. Send traffic to dpdk_port0 from traffic generator. - Inspect packets captured in the file capture.pcap using a tool - that can interpret Pcap files, for example tcpdump:: + Inspect packets captured in the file capture.pcap using a tool such as + tcpdump or tshark that can interpret Pcapng files:: - $tcpdump -nr /tmp/capture.pcap + $ tcpdump -nr /tmp/capture.pcapng reading from file /tmp/capture.pcap, link-type EN10MB (Ethernet) 11:11:36.891404 IP 4.4.4.4.whois++ > 3.3.3.3.whois++: UDP, length 18 11:11:36.891442 IP 4.4.4.4.whois++ > 3.3.3.3.whois++: UDP, length 18 diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst index 2dce507f46a3..b440c77c2ba1 100644 --- a/doc/guides/prog_guide/index.rst +++ b/doc/guides/prog_guide/index.rst @@ -43,6 +43,7 @@ Programmer's Guide ip_fragment_reassembly_lib generic_receive_offload_lib generic_segmentation_offload_lib + pcapng_lib pdump_lib multi_proc_support kernel_nic_interface diff --git a/doc/guides/prog_guide/pcapng_lib.rst b/doc/guides/prog_guide/pcapng_lib.rst new file mode 100644 index 000000000000..36379b530a57 --- /dev/null +++ b/doc/guides/prog_guide/pcapng_lib.rst @@ -0,0 +1,24 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2016 Intel Corporation. + +.. _pcapng_library: + +Packet Capture File Writer +========================== + +Pcapng is a library for creating files in Pcapng file format. +The Pcapng file format is the default capture file format for modern +network capture processing tools. It can be read by wireshark and tcpdump. + +Usage +----- + +Before the library can be used the function ``rte_pcapng_init`` +should be called once to initialize timestamp computation. + + +References +---------- +* Draft RFC https://www.ietf.org/id/draft-tuexen-opsawg-pcapng-03.html + +* Project repository https://github.com/pcapng/pcapng/ diff --git a/doc/guides/prog_guide/pdump_lib.rst b/doc/guides/prog_guide/pdump_lib.rst index 62c0b015b2fe..9af91415e5ea 100644 --- a/doc/guides/prog_guide/pdump_lib.rst +++ b/doc/guides/prog_guide/pdump_lib.rst @@ -3,10 +3,10 @@ .. _pdump_library: -The librte_pdump Library -======================== +The Packet Capture Library +========================== -The ``librte_pdump`` library provides a framework for packet capturing in DPDK. +The DPDK ``pdump`` library provides a framework for packet capturing in DPDK. The library does the complete copy of the Rx and Tx mbufs to a new mempool and hence it slows down the performance of the applications, so it is recommended to use this library for debugging purposes. @@ -23,11 +23,19 @@ or disable the packet capture, and to uninitialize it. * ``rte_pdump_enable()``: This API enables the packet capture on a given port and queue. - Note: The filter option in the API is a place holder for future enhancements. + +* ``rte_pdump_enable_bpf()`` + This API enables the packet capture on a given port and queue. + It also allows setting an optional filter using DPDK BPF interpreter and + setting the captured packet length. * ``rte_pdump_enable_by_deviceid()``: This API enables the packet capture on a given device id (``vdev name or pci address``) and queue. - Note: The filter option in the API is a place holder for future enhancements. + +* ``rte_pdump_enable_bpf_by_deviceid()`` + This API enables the packet capture on a given device id (``vdev name or pci address``) and queue. + It also allows seating an optional filter using DPDK BPF interpreter and + setting the captured packet length. * ``rte_pdump_disable()``: This API disables the packet capture on a given port and queue. @@ -61,6 +69,12 @@ and enables the packet capture by registering the Ethernet RX and TX callbacks f and queue combinations. Then the primary process will mirror the packets to the new mempool and enqueue them to the rte_ring that secondary process have passed to these APIs. +The packet ring supports one of two formats. The default format enqueues copies of the original packets +into the rte_ring. If the ``RTE_PDUMP_FLAG_PCAPNG`` is set the mbuf data is extended with header and trailer +to match the format of Pcapng enhanced packet block. The enhanced packet block has meta-data such as the +timestamp, port and queue the packet was captured on. It is up to the application consuming the +packets from the ring to select the format desired. + The library APIs ``rte_pdump_disable()`` and ``rte_pdump_disable_by_deviceid()`` disables the packet capture. For the calls to these APIs from secondary process, the library creates the "pdump disable" request and sends the request to the primary process over the multi process channel. The primary process takes this request and @@ -74,5 +88,5 @@ function. Use Case: Packet Capturing -------------------------- -The DPDK ``app/pdump`` tool is developed based on this library to capture packets in DPDK. -Users can use this as an example to develop their own packet capturing tools. +The DPDK ``app/dpdk-dumpcap`` utility uses this library +to capture packets in DPDK. diff --git a/doc/guides/rel_notes/release_21_11.rst b/doc/guides/rel_notes/release_21_11.rst index 675b5738348b..ee24cbfdb99d 100644 --- a/doc/guides/rel_notes/release_21_11.rst +++ b/doc/guides/rel_notes/release_21_11.rst @@ -62,6 +62,16 @@ New Features * Added bus-level parsing of the devargs syntax. * Kept compatibility with the legacy syntax as parsing fallback. +* **Enhance Packet capture.** + + * New dpdk-dumpcap program that has most of the features of the + wireshark dumpcap utility including capture of multiple interfaces, + stopping after number of bytes, packets. + * New library for writing pcapng packet capture files. + * Enhancement to the pdump library to support: + * Packet filter with BPF. + * Pcapng format with timestamps and meta-data. + * Fixes packet capture with stripped VLAN tags. Removed Items ------------- diff --git a/doc/guides/tools/dumpcap.rst b/doc/guides/tools/dumpcap.rst new file mode 100644 index 000000000000..90993bd5be5e --- /dev/null +++ b/doc/guides/tools/dumpcap.rst @@ -0,0 +1,80 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2020 Microsoft Corporation. + +.. _dumpcap_tool: + +dpdk-dumpcap Application +======================== + +The ``dpdk-dumpcap`` tool is a Data Plane Development Kit (DPDK) +network traffic dump tool. The interface is similar to the dumpcap tool in Wireshark. +It runs as a secondary DPDK process and lets you capture packets that are +coming into and out of a DPDK primary process. +The ``dpdk-dumpcap`` writes files in Pcapng packet format using +capture file format is pcapng. + +Without any options set it will use DPDK to capture traffic from the first +available DPDK interface and write the received raw packet data, along +with timestamps into a pcapng file. + +If the ``-w`` option is not specified, ``dpdk-dumpcap`` writes to a newly +create file with a name chosen based on interface name and timestamp. +If ``-w`` option is specified, then that file is used. + + .. Note:: + * The ``dpdk-dumpcap`` tool can only be used in conjunction with a primary + application which has the packet capture framework initialized already. + In dpdk, only the ``testpmd`` is modified to initialize packet capture + framework, other applications remain untouched. So, if the ``dpdk-dumpcap`` + tool has to be used with any application other than the testpmd, user + needs to explicitly modify that application to call packet capture + framework initialization code. Refer ``app/test-pmd/testpmd.c`` + code to see how this is done. + + * The ``dpdk-dumpcap`` tool runs as a DPDK secondary process. It exits when + the primary application exits. + + +Running the Application +----------------------- + +To list interfaces available for capture use ``--list-interfaces``. + +To capture on multiple interfaces at once, use multiple ``-I`` flags. + +Example +------- + +.. code-block:: console + + # .//app/dpdk-dumpcap --list-interfaces + 0. 000:00:03.0 + 1. 000:00:03.1 + + # .//app/dpdk-dumpcap -I 0000:00:03.0 -c 6 -w /tmp/sample.pcapng + Packets captured: 6 + Packets received/dropped on interface '0000:00:03.0' 6/0 + + +Limitations +----------- +The following options of Wireshark ``dumpcap`` are not yet implemented: + + * ``-f `` -- needs translation from classic BPF to eBPF. + + * ``-b|--ring-buffer`` -- more complex file management. + + * ``-C `` -- doesn't make sense in DPDK model. + + * ``-t`` -- use a thread per interface + + * Timestamp type. + + * Link data types. Only EN10MB (Ethernet) is supported. + + * Wireless related options: ``-I|--monitor-mode`` and ``-k `` + + +.. Note:: + * The options to ``dpdk-dumpcap`` are like the Wireshark dumpcap program and + are not the same as ``dpdk-pdump`` and other DPDK applications. diff --git a/doc/guides/tools/index.rst b/doc/guides/tools/index.rst index 93dde4148e90..b71c12b8f2dd 100644 --- a/doc/guides/tools/index.rst +++ b/doc/guides/tools/index.rst @@ -8,6 +8,7 @@ DPDK Tools User Guides :maxdepth: 2 :numbered: + dumpcap proc_info pdump pmdinfo From patchwork Fri Sep 3 00:47:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 97855 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id E6A4FA0C4D; Fri, 3 Sep 2021 02:48:08 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id CA48441124; Fri, 3 Sep 2021 02:47:45 +0200 (CEST) Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) by mails.dpdk.org (Postfix) with ESMTP id ADC2D410E0 for ; Fri, 3 Sep 2021 02:47:41 +0200 (CEST) Received: by mail-pl1-f172.google.com with SMTP id k17so2286242pls.0 for ; Thu, 02 Sep 2021 17:47:41 -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=XyqQtx1VKvwB71wOpLr2p4bGL/oB0DPWsDpls3n0BsQ=; b=HrR6sNupR8y4mGGHxWOJrPMCIZkeqrPiuDmkbXFnl02t8/9NOPo7fTJvTMEdXiJkcd /BTvZK3P/n+kf1UzssLjDA+5hlSlRsIJrWK2n9OgzfRJf2xQaBOy3lrNm4WponP29TkZ 6Yxp9byNOe5+X3n1lq8SLWBEeardGEdja4DTEU/MKyJU73XV7KrOkWXHN9WcOIBmRDA6 UZ53xLr38AzsB8rJQGZbBgcrPmhQR2M7329XGUoGKgQa55RQT8hzY5Ypia61rhZl5yHA GGTi/KMR0ubnXBIaLiPxz5tx/8ZDToMCvmOldRPH6iymvUU8kCGJFE7WcdUjRCIXRBTV X6QQ== 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=XyqQtx1VKvwB71wOpLr2p4bGL/oB0DPWsDpls3n0BsQ=; b=CoHzwgJRJ19Z6y1YDz0d2M+I2JcgOyXlgV2b1tQgs8LjVUBKeY5WZg+qf9OHnoNS/G +YdNIuHEldpM7FDXRu/9pR11leJ1adt8Jc56qzUS7BAfTT+8y4Iqp4WCyTEOHyzL5RGZ NmVm0JkCOhrHzs3fZq1KlaLM3bVjx8sXnQCSKNNG/X707IRG7lIvPeTV3EeU1yDBcHjb zda9T50SOdDp171sjo6Cq+NAnZxCbDuWPYwhElJYA5VmVARXQ6NZGP0YiyY022Y0Sn8w Zxmo18Gcn2UqvZKExsnYE0/ip6HRcxtqaIANGOPbxFSGeQTVXAG/YSfKNSuF871UoZQT 8CvQ== X-Gm-Message-State: AOAM530ewh0w++oqmNR6qtbTO+rfkVPHJT2FyUVEvDmQ5r1enPUOpRfi H6YpUAp9PTLJ2rRxn89YKrRKiPdXzTSOWQ== X-Google-Smtp-Source: ABdhPJw0Yp1bSVz4cKEGkSYXQKUrDZh7zrnPCEhCOPq0krI67gNB9I09L+RCmo4fzXM9kvPMFxzkUg== X-Received: by 2002:a17:90a:3b0e:: with SMTP id d14mr6976232pjc.164.1630630060450; Thu, 02 Sep 2021 17:47:40 -0700 (PDT) Received: from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123]) by smtp.gmail.com with ESMTPSA id cq8sm3179033pjb.31.2021.09.02.17.47.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Sep 2021 17:47:39 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Date: Thu, 2 Sep 2021 17:47:32 -0700 Message-Id: <20210903004732.109023-6-stephen@networkplumber.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210903004732.109023-1-stephen@networkplumber.org> References: <20210903004732.109023-1-stephen@networkplumber.org> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 5/5] MAINTAINERS: add entry for new pcapng and dumper X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 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" Claim responsibility for the new code. Signed-off-by: Stephen Hemminger --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 266f5ac1dae8..06384ac2702d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1429,6 +1429,12 @@ F: app/test/test_pdump.* F: app/pdump/ F: doc/guides/tools/pdump.rst +Packet dump +M: Stephen Hemminger +F: lib/pcapng/ +F: doc/guides/prog_guide/pcapng_lib.rst +F: app/dumpcap/ +F: doc/guides/tools/dumpcap.rst Packet Framework ----------------