From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id C156446B2F; Wed, 9 Jul 2025 19:37:27 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 5F29340E40; Wed, 9 Jul 2025 19:36:31 +0200 (CEST) Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) by mails.dpdk.org (Postfix) with ESMTP id 3B96240E1E for ; Wed, 9 Jul 2025 19:36:26 +0200 (CEST) Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2353a2bc210so1689745ad.2 for ; Wed, 09 Jul 2025 10:36:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1752082585; x=1752687385; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=GjqmnX/yLipOBbTR3vGxGLubpIiI2lREWGeLft5bKOI=; b=ksaD+3ZhmJVNk5eGurrpiFJqLZxsz8gtk2QK5lF5ibQY3nSX0TMuVqWGfQ8qBOENGx DWA/dnkmqxlPXVbV65a/TApnWSnouu8J/v5owsqMWuWQlvdVhSiye+sDdBdrlFL4wtCQ 8hpbeuj+uPLxnnHJjR20f+frTg/KhBiqLNGvKAhFcDNMFQy5kNjB1ES5jyDR4yiEqK8Z BcRUdTHNoJV1wzakbjjxN+ho3e7NvjvgS6OjO7Sz6a7xTfcd2uGXZlmE3w6mjXrg2Kpa igWxtccFhT9x5u9vQAHrj2mTd8CcPCaKDBQwxgj0bzEo3YB9BP5GiI5jrAvF3T3U/kUx KDzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752082585; x=1752687385; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=GjqmnX/yLipOBbTR3vGxGLubpIiI2lREWGeLft5bKOI=; b=p2HeEEqjyIwOBZkd2XJGwjGug/n62ThmIpjNV2Rx2z3Ip6a6Qy462SRsZB6HSCbCxR I559PIDsvpC7PYhmi4kvV1rf+CDYRpuv8Yq0+D58tG/omWSqG7EksCVb1Law6NN0QKiN wm6o7I70VY80j7bieovDRZzxBT+RjrS0z5DzCrIrfFeNviLuzTLuvQhvDpOoXpDovqD5 iKPbR1y75RojmU9sBPWvARwCk/LrWrulyKQdRHwqgdq0AzbawCm2iRS8QUhYvq5AcR5A u1xRtkJJmWEJFSr+I/+3hGYP5oxqdMtkvcJSbSnFESLml+hSGIT+oOKbyOM1WcY/g1BT NlDg== X-Gm-Message-State: AOJu0YzszZVNIaRa6YKy73UR90IDQdqGRKbC20/h7M8nRfjT/R2U3+88 dQz603qSBjhH7ZMTigcqHeIPSSnHbDzZiPvyd6ya1oD6zdzeReb0nx5e5fvJJ8RlBfuj5YfTmuV Em85b X-Gm-Gg: ASbGnct/EAwNXQJal26006PPEh6areBNsfC6APaGo/GM0J0q2P4Y/yURuSI0XWV2tTp UsxZxuzf8dbHc6c1xhJtzozuemfPrQjWoRwKJTKrrAxkk+UYHUY11q0JgBg8Epo6sRl4R8cUrxW i2siIsQQKTJej0KqAgijPqeotAuC+I7yWSX4cA6nUyKvx1dP/cVfkVJ4LZEcAIy2iu2S1yEwVeq Z/sEA1GTHbLFXMj2VG2Gc8qN/AS9CTkpAVuA2TF0jJA45/YFoXXsL2pk/eZq6SoEB8eSr7yTvSF HU7aNVyOlN87qH+A8lqsWMWXleH4vWurekyh88/kiKVT+aaPzhjQIYqK/qJ/lYKP0x+4DGNvvQy TgoPFVnmX5TlJlxGqyQIr8Cy3tvt6M+b+kTyb X-Google-Smtp-Source: AGHT+IG9JQOCvzhsxi96ROSIlBge8+I/P2bJtnMXL47Xss0lS32s/ZulIXOhg+5y1CDqsjCQ78Jffg== X-Received: by 2002:a17:903:2448:b0:229:1619:ab58 with SMTP id d9443c01a7336-23de24f5a39mr12556635ad.43.1752082585196; Wed, 09 Jul 2025 10:36:25 -0700 (PDT) Received: from hermes.lan (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-23c845b7467sm138525475ad.230.2025.07.09.10.36.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 09 Jul 2025 10:36:24 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger , Reshma Pattan Subject: [RFC v2 11/12] app/dumpcap: use port mirror instead of pdump Date: Wed, 9 Jul 2025 10:33:37 -0700 Message-ID: <20250709173611.6390-12-stephen@networkplumber.org> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250709173611.6390-1-stephen@networkplumber.org> References: <0250411234927.114568-1-stephen@networkplumber.org> <20250709173611.6390-1-stephen@networkplumber.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Use the new port mirror API instead of pdump. Signed-off-by: Stephen Hemminger --- app/dumpcap/main.c | 443 +++++++++++++++++++++++++++++++++------- app/dumpcap/meson.build | 2 +- 2 files changed, 373 insertions(+), 72 deletions(-) diff --git a/app/dumpcap/main.c b/app/dumpcap/main.c index 3d3c0dbc66..700b1d46da 100644 --- a/app/dumpcap/main.c +++ b/app/dumpcap/main.c @@ -36,8 +36,6 @@ #include #include #include -#include -#include #include #include #include @@ -92,16 +90,28 @@ struct capture_options { struct interface { TAILQ_ENTRY(interface) next; uint16_t port; + struct rte_bpf *filter; + uint64_t filteraccept; struct capture_options opts; - struct rte_bpf_prm *bpf_prm; char name[RTE_ETH_NAME_MAX_LEN]; + struct rte_eth_stats start_stats; const char *ifname; const char *ifdescr; }; +static int timestamp_dynfield = -1; +static uint64_t timestamp_dynflag; +static int mirror_origin_dynfield = -1; +static uint64_t mirror_origin_dynflag; +static uint64_t mirror_ingress_dynflag; +static uint64_t mirror_egress_dynflag; + +static bool filtering; + TAILQ_HEAD(interface_list, interface); static struct interface_list interfaces = TAILQ_HEAD_INITIALIZER(interfaces); +static struct interface *port2intf[RTE_MAX_ETHPORTS]; /* Can do either pcap or pcapng format output */ typedef union { @@ -239,14 +249,16 @@ static void find_interfaces(void) TAILQ_FOREACH(intf, &interfaces, next) { /* if name is valid then just record port */ - if (rte_eth_dev_get_port_by_name(intf->name, &intf->port) == 0) - continue; + if (rte_eth_dev_get_port_by_name(intf->name, &intf->port) != 0) { + /* maybe got passed port number string as name */ + intf->port = get_uint(intf->name, "port_number", UINT16_MAX); + if (rte_eth_dev_get_name_by_port(intf->port, intf->name) < 0) + rte_exit(EXIT_FAILURE, "Invalid port number %u\n", + intf->port); + } - /* maybe got passed port number string as name */ - intf->port = get_uint(intf->name, "port_number", UINT16_MAX); - if (rte_eth_dev_get_name_by_port(intf->port, intf->name) < 0) - rte_exit(EXIT_FAILURE, "Invalid port number %u\n", - intf->port); + if (rte_eth_stats_get(intf->port, &intf->start_stats) < 0) + rte_exit(EXIT_FAILURE, "Could not read stats for port %u\n", intf->port); } } @@ -266,6 +278,9 @@ static void set_default_interface(void) intf = add_interface(name); intf->port = p; + + if (rte_eth_stats_get(intf->port, &intf->start_stats) < 0) + rte_exit(EXIT_FAILURE, "Could not read stats for default port %u\n", intf->port); return; } rte_exit(EXIT_FAILURE, "No usable interfaces found\n"); @@ -295,10 +310,17 @@ static void compile_filters(void) struct bpf_program bf; pcap_t *pcap; + /* cache for filter packets */ + port2intf[intf->port] = intf; + + if (intf->opts.filter == NULL) + continue; + pcap = pcap_open_dead(DLT_EN10MB, intf->opts.snap_len); if (!pcap) rte_exit(EXIT_FAILURE, "can not open pcap\n"); + if (pcap_compile(pcap, &bf, intf->opts.filter, 1, PCAP_NETMASK_UNKNOWN) != 0) { fprintf(stderr, @@ -316,7 +338,7 @@ static void compile_filters(void) rte_strerror(rte_errno), rte_errno); if (dump_bpf) { - printf("cBPF program (%u insns)\n", bf.bf_len); + printf("cops program (%u insns)\n", bf.bf_len); bpf_dump(&bf, 1); printf("\neBPF program (%u insns)\n", bpf_prm->nb_ins); @@ -324,11 +346,18 @@ static void compile_filters(void) exit(0); } - intf->bpf_prm = bpf_prm; + + intf->filter = rte_bpf_load(bpf_prm); + if (intf->filter == NULL) + rte_exit(EXIT_FAILURE, + "BPF load failed '%s'\n%s(%d)\n", + intf->name, rte_strerror(rte_errno), rte_errno); /* Don't care about original program any more */ pcap_freecode(&bf); pcap_close(pcap); + + filtering = true; } } @@ -518,13 +547,12 @@ static void statistics_loop(void) } static void -cleanup_pdump_resources(void) +disable_mirror_ports(uint16_t mirror_port) { struct interface *intf; TAILQ_FOREACH(intf, &interfaces, next) { - rte_pdump_disable(intf->port, - RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX); + rte_eth_remove_mirror(intf->port, mirror_port); if (intf->opts.promisc_mode) rte_eth_promiscuous_disable(intf->port); } @@ -552,7 +580,6 @@ 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); @@ -571,23 +598,24 @@ disable_primary_monitor(void) static void report_packet_stats(dumpcap_out_t out) { - struct rte_pdump_stats pdump_stats; struct interface *intf; - uint64_t ifrecv, ifdrop; - double percent; fputc('\n', stderr); + TAILQ_FOREACH(intf, &interfaces, next) { - if (rte_pdump_stats(intf->port, &pdump_stats) < 0) + uint64_t ifrecv, ifdrop; + struct rte_eth_stats stats; + double percent; + + if (rte_eth_stats_get(intf->port, &stats) < 0) continue; - /* do what Wiretap does */ - ifrecv = pdump_stats.accepted + pdump_stats.filtered; - ifdrop = pdump_stats.nombuf + pdump_stats.ringfull; + ifrecv = stats.ipackets - intf->start_stats.ipackets; + ifdrop = (stats.rx_nombuf - intf->start_stats.rx_nombuf) + + (stats.imissed - intf->start_stats.imissed); if (use_pcapng) - rte_pcapng_write_stats(out.pcapng, intf->port, - ifrecv, ifdrop, NULL); + rte_pcapng_write_stats(out.pcapng, intf->port, ifrecv, ifdrop, NULL); if (ifrecv == 0) percent = 0; @@ -668,12 +696,21 @@ static void dpdk_init(void) rte_exit(EXIT_FAILURE, "Can not restore original CPU affinity\n"); } -/* Create packet ring shared between callbacks and process */ -static struct rte_ring *create_ring(void) +/* Create ring port to redirect packet to. + * This could be much simpler if the ring PMD API (rte_eth_from_rings) + * worked from a secondary process, but it doesn't. + */ +static struct rte_ring * +create_ring_dev(char *vdev_name, uint16_t *mirror_port) { - struct rte_ring *ring; + struct rte_eth_dev_owner owner = { 0 }; + struct rte_eth_conf dev_conf = { 0 }; + struct rte_ring *ring = NULL; char ring_name[RTE_RING_NAMESIZE]; + char vdev_args[128]; size_t size, log2; + uint16_t port; + int ret; /* Find next power of 2 >= size. */ size = ring_size; @@ -686,17 +723,60 @@ static struct rte_ring *create_ring(void) ring_size = size; } - /* Want one ring per invocation of program */ - snprintf(ring_name, sizeof(ring_name), - "dumpcap-%d", getpid()); + ret = rte_eth_dev_owner_new(&owner.id); + if (ret < 0) + rte_exit(EXIT_FAILURE, "rte_eth_dev_owner_new failed: %s\n", + strerror(-ret)); - ring = rte_ring_create(ring_name, ring_size, - rte_socket_id(), 0); + /* Give the vdev a unique name */ + snprintf(ring_name, sizeof(ring_name), "dumpcap%"PRIu64, owner.id); + ring = rte_ring_create(ring_name, ring_size, rte_socket_id(), + RING_F_MP_RTS_ENQ | RING_F_SC_DEQ); if (ring == NULL) rte_exit(EXIT_FAILURE, "Could not create ring :%s\n", rte_strerror(rte_errno)); + strlcpy(owner.name, ring_name, sizeof(owner.name)); + + snprintf(vdev_name, RTE_DEV_NAME_MAX_LEN, + "net_ring-dumpcap%"PRIu64, owner.id); + snprintf(vdev_args, sizeof(vdev_args), + "ring=%s,timestamp", ring_name); + + if (rte_eal_hotplug_add("vdev", vdev_name, vdev_args) < 0) + rte_exit(EXIT_FAILURE, + "rte_eal_hotplug_add of %s failed:%s\n", + vdev_name, rte_strerror(rte_errno)); + + ret = rte_eth_dev_get_port_by_name(vdev_name, &port); + if (ret != 0) { + fprintf(stderr, "Could not port for %s: %s\n", + vdev_name, strerror(-ret)); + goto unplug; + } + + ret = rte_eth_dev_owner_set(port, &owner); + if (ret != 0) { + fprintf(stderr, "Could not set owner for port for %u: %s\n", + port, strerror(-ret)); + goto unplug; + } + + ret = rte_eth_dev_configure(port, 1, 1, &dev_conf); + if (ret < 0) { + fprintf(stderr, "Could not configure port %u: %s\n", + port, strerror(-ret)); + goto unplug; + } + + *mirror_port = port; return ring; + +unplug: + rte_eal_hotplug_remove("vdev", vdev_name); + rte_ring_free(ring); + rte_eal_cleanup(); + exit(EXIT_FAILURE); } static struct rte_mempool *create_mempool(void) @@ -796,7 +876,7 @@ static dumpcap_out_t create_output(void) version(), capture_comment); if (ret.pcapng == NULL) rte_exit(EXIT_FAILURE, "pcapng_fdopen failed: %s\n", - strerror(rte_errno)); + rte_strerror(rte_errno)); free(os); TAILQ_FOREACH(intf, &interfaces, next) { @@ -823,37 +903,32 @@ static dumpcap_out_t create_output(void) return ret; } -static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp) +static int enable_mirror(uint16_t mirror_port, struct rte_mempool *mpool) { struct interface *intf; unsigned int count = 0; - uint32_t flags; int ret; - flags = RTE_PDUMP_FLAG_RXTX; - if (use_pcapng) - flags |= RTE_PDUMP_FLAG_PCAPNG; + ret = rte_eth_dev_start(mirror_port); + if (ret < 0) + rte_exit(EXIT_FAILURE, "Could not start mirror port %u: %s\n", + mirror_port, strerror(-ret)); TAILQ_FOREACH(intf, &interfaces, next) { - ret = rte_pdump_enable_bpf(intf->port, RTE_PDUMP_ALL_QUEUES, - flags, intf->opts.snap_len, - r, mp, intf->bpf_prm); + struct rte_eth_mirror_conf conf = { + .mp = mpool, + .target = mirror_port, + .snaplen = intf->opts.snap_len, + .flags = RTE_ETH_MIRROR_ORIGIN_FLAG | RTE_ETH_MIRROR_TIMESTAMP_FLAG, + .direction = RTE_ETH_MIRROR_DIRECTION_INGRESS | + RTE_ETH_MIRROR_DIRECTION_EGRESS, + }; + + ret = rte_eth_add_mirror(intf->port, &conf); if (ret < 0) { - const struct interface *intf2; - - /* unwind any previous enables */ - TAILQ_FOREACH(intf2, &interfaces, next) { - if (intf == intf2) - break; - rte_pdump_disable(intf2->port, - RTE_PDUMP_ALL_QUEUES, RTE_PDUMP_FLAG_RXTX); - if (intf2->opts.promisc_mode) - rte_eth_promiscuous_disable(intf2->port); - } - rte_exit(EXIT_FAILURE, - "Packet dump enable on %u:%s failed %s\n", - intf->port, intf->name, - rte_strerror(rte_errno)); + fprintf(stderr, "Port mirror on %u:%s failed %s\n", + intf->port, intf->name, strerror(-ret)); + return -1; } if (intf->opts.promisc_mode) { @@ -868,6 +943,60 @@ static void enable_pdump(struct rte_ring *r, struct rte_mempool *mp) } ++count; } + return count; +} + +static int setup_mbuf(void) +{ + int offset; + + offset = rte_mbuf_dynfield_lookup(RTE_MBUF_DYNFIELD_TIMESTAMP_NAME, NULL); + if (offset < 0) { + fprintf(stderr, "Could not find timestamp dynamic field\n"); + return -1; + } + timestamp_dynfield = offset; + + offset = rte_mbuf_dynflag_lookup(RTE_MBUF_DYNFLAG_RX_TIMESTAMP_NAME, NULL); + if (offset < 0) { + fprintf(stderr, "Could not find timestamp flag\n"); + return -1; + } + timestamp_dynflag = RTE_BIT64(offset); + + offset = rte_mbuf_dynfield_lookup(RTE_MBUF_DYNFIELD_MIRROR_ORIGIN, NULL); + if (offset < 0) { + fprintf(stderr, "Could not find mirror origin dynamic field\n"); + return -1; + } + mirror_origin_dynfield = offset; + + offset = rte_mbuf_dynflag_lookup(RTE_MBUF_DYNFLAG_MIRROR_ORIGIN, NULL); + if (offset < 0) { + fprintf(stderr, "Could not find mirror origin dynamic flag\n"); + return -1; + } + mirror_origin_dynflag = RTE_BIT64(offset); + + offset = rte_mbuf_dynflag_lookup(RTE_MBUF_DYNFLAG_MIRROR_INGRESS, NULL); + if (offset < 0) { + fprintf(stderr, "Could not find mirror ingress flag\n"); + return -1; + } + mirror_ingress_dynflag = RTE_BIT64(offset); + + offset = rte_mbuf_dynflag_lookup(RTE_MBUF_DYNFLAG_MIRROR_EGRESS, NULL); + if (offset < 0) { + fprintf(stderr, "Could not find mirror egress flag\n"); + return -1; + } + mirror_egress_dynflag = RTE_BIT64(offset); + return 0; +} + +static void show_capturing(unsigned int count) +{ + struct interface *intf; fputs("Capturing on ", stdout); TAILQ_FOREACH(intf, &interfaces, next) { @@ -898,6 +1027,47 @@ static void show_count(uint64_t count) bt = fprintf(stderr, "%"PRIu64" ", count); } +static ssize_t +pcapng_write_packets(rte_pcapng_t *pcapng, struct rte_mbuf *pkts[], uint16_t n) +{ + struct rte_mbuf *towrite[BURST_SIZE]; + unsigned int count = 0; + + for (unsigned int i = 0; i < n; i++) { + struct rte_mbuf *m = pkts[i]; + enum rte_pcapng_direction direction = RTE_PCAPNG_DIRECTION_UNKNOWN; + + /* If no origin flag then why did we get this? */ + if (unlikely(!(m->ol_flags & mirror_origin_dynflag))) { + fprintf(stderr, "Missing origin info in packet\n"); + return -1; + } + + const struct rte_mbuf_origin *origin + = RTE_MBUF_DYNFIELD(m, mirror_origin_dynfield, rte_mbuf_origin_t *); + + if (m->ol_flags & mirror_ingress_dynflag) + direction = RTE_PCAPNG_DIRECTION_IN; + else if (m->ol_flags & mirror_egress_dynflag) + direction = RTE_PCAPNG_DIRECTION_OUT; + + uint64_t timestamp; + if (m->ol_flags & timestamp_dynflag) + timestamp = *RTE_MBUF_DYNFIELD(m, timestamp_dynfield, + rte_mbuf_timestamp_t *); + else + timestamp = rte_get_tsc_cycles(); + + if (rte_pcapng_insert(m, origin->queue_id, direction, origin->original_len, + timestamp, NULL) < 0) + continue; /* skip no headroom? */ + + towrite[count++] = m; + } + + return rte_pcapng_write_packets(pcapng, towrite, count); +} + /* Write multiple packets in older pcap format */ static ssize_t pcap_write_packets(pcap_dumper_t *dumper, @@ -930,16 +1100,127 @@ pcap_write_packets(pcap_dumper_t *dumper, return total; } +/* Do packet filter on incoming packets. + * Some issues to note here: + * - would be better to do before putting into ring + * but that causes ethdev to have dependency on BPF. + * - the filter is per-interface and there maybe more than one interface + * combined into the ring. That means filter is executed once + * per packet (not as burst). + */ +static unsigned int +filter_packets(struct rte_mbuf *pkts[], unsigned int n) +{ + uint64_t results[BURST_SIZE]; + uint32_t matches = 0; + + for (unsigned int i = 0; i < n; i++) { + struct rte_mbuf *m = pkts[i]; + + /* all packets should have origin info, if not just skip */ + if (unlikely(!(m->ol_flags & mirror_origin_dynflag))) + continue; + + const struct rte_mbuf_origin *origin + = RTE_MBUF_DYNFIELD(m, mirror_origin_dynfield, rte_mbuf_origin_t *); + uint16_t port_id = origin->port_id; + + if (unlikely(port_id >= RTE_MAX_ETHPORTS)) + continue; + + const struct interface *intf = port2intf[port_id]; + if (intf == NULL || intf->filter == NULL) + continue; + + if (rte_bpf_exec_burst(intf->filter, (void **)&m, &results[i], 1) == 1) + ++matches; + } + + if (matches == n) + return n; /* no packets skipped */ + + /* need to shuffle out the skipped packets */ + unsigned int count = 0; + struct rte_mbuf *towrite[BURST_SIZE]; + for (unsigned int i = 0; i < n; i++) { + if (results[i]) + towrite[count++] = pkts[i]; + else + rte_pktmbuf_free(pkts[i]); + } + memcpy(pkts, towrite, count * sizeof(struct rte_mbuf *)); + return count; +} + +/* Make sure configuration of mbuf pool has enough headroom for both vlans */ +static_assert(RTE_PKTMBUF_HEADROOM > 2 * sizeof(struct rte_vlan_hdr), + "not enough mbuf headroom for vlan insertion"); + +/* + * More general version of rte_vlan_insert() + * Note: mbufs are allocated by rte_eth_mirror_burst() from the + * pool that was passed when setting up mirror. + */ +static void +vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci) +{ + struct rte_ether_hdr *nh, tmph; + const struct rte_ether_hdr *oh; + struct rte_vlan_hdr *vh; + + RTE_ASSERT(!RTE_MBUF_DIRECT(m)); + RTE_ASSERT(rte_mbuf_refcnt_read(m) == 1); + + oh = rte_pktmbuf_read(m, 0, sizeof(*oh), &tmph); + RTE_ASSERT(oh != NULL); + + /* overlay new header */ + nh = (struct rte_ether_hdr *) + rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr)); + + RTE_ASSERT(nh != NULL); + + memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN); + nh->ether_type = rte_cpu_to_be_16(ether_type); + + vh = (struct rte_vlan_hdr *) (nh + 1); + vh->vlan_tci = rte_cpu_to_be_16(tci); +} + +/* Filtering and pcap file format require that VLAN not be offloaded */ +static void +remove_vlan_offload(struct rte_mbuf *pkts[], unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) { + struct rte_mbuf *m = pkts[i]; + + if (m->ol_flags & mirror_ingress_dynflag) { + if (m->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED) + vlan_insert(m, RTE_ETHER_TYPE_VLAN, m->vlan_tci); + if (m->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED) + vlan_insert(m, RTE_ETHER_TYPE_QINQ, m->vlan_tci_outer); + } + if (m->ol_flags & mirror_egress_dynflag) { + if (m->ol_flags & RTE_MBUF_F_TX_VLAN) + vlan_insert(m, RTE_ETHER_TYPE_VLAN, m->vlan_tci); + if (m->ol_flags & RTE_MBUF_F_TX_QINQ) + vlan_insert(m, RTE_ETHER_TYPE_QINQ, m->vlan_tci_outer); + + } + } +} + /* Process all packets in ring and dump to capture file */ -static int process_ring(dumpcap_out_t out, struct rte_ring *r) +static int process_ring(dumpcap_out_t out, struct rte_ring *ring) { struct rte_mbuf *pkts[BURST_SIZE]; - unsigned int avail, n; + unsigned int n, avail; static unsigned int empty_count; ssize_t written; - n = rte_ring_sc_dequeue_burst(r, (void **) pkts, BURST_SIZE, - &avail); + n = rte_ring_sc_dequeue_burst(ring, (void **)pkts, BURST_SIZE, &avail); if (n == 0) { /* don't consume endless amounts of cpu if idle */ if (empty_count < SLEEP_THRESHOLD) @@ -948,11 +1229,18 @@ static int process_ring(dumpcap_out_t out, struct rte_ring *r) usleep(10); return 0; } - empty_count = (avail == 0); + remove_vlan_offload(pkts, n); + + if (filtering) { + n = filter_packets(pkts, n); + if (n == 0) + return 0; + } + if (use_pcapng) - written = rte_pcapng_write_packets(out.pcapng, pkts, n); + written = pcapng_write_packets(out.pcapng, pkts, n); else written = pcap_write_packets(out.dumper, pkts, n); @@ -971,15 +1259,18 @@ static int process_ring(dumpcap_out_t out, struct rte_ring *r) int main(int argc, char **argv) { - struct rte_ring *r; struct rte_mempool *mp; struct sigaction action = { .sa_flags = SA_RESTART, .sa_handler = signal_handler, }; struct sigaction origaction; + struct rte_ring *ring = NULL; + char vdev_name[RTE_DEV_NAME_MAX_LEN]; + uint16_t mirror_port = UINT16_MAX; dumpcap_out_t out; char *p; + int ret; p = strrchr(argv[0], '/'); if (p == NULL) @@ -1018,12 +1309,21 @@ int main(int argc, char **argv) exit(0); } - r = create_ring(); mp = create_mempool(); + ring = create_ring_dev(vdev_name, &mirror_port); out = create_output(); + ret = enable_mirror(mirror_port, mp); + if (ret < 0) + goto cleanup; + + ret = setup_mbuf(); + if (ret < 0) + goto cleanup; + + show_capturing(ret); + start_time = time(NULL); - enable_pdump(r, mp); if (!quiet) { fprintf(stderr, "Packets captured: "); @@ -1031,7 +1331,7 @@ int main(int argc, char **argv) } while (!rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed)) { - if (process_ring(out, r) < 0) { + if (process_ring(out, ring) < 0) { fprintf(stderr, "pcapng file write failed; %s\n", strerror(errno)); break; @@ -1048,19 +1348,20 @@ int main(int argc, char **argv) break; } - disable_primary_monitor(); - if (rte_eal_primary_proc_alive(NULL)) report_packet_stats(out); +cleanup: if (use_pcapng) rte_pcapng_close(out.pcapng); else pcap_dump_close(out.dumper); - cleanup_pdump_resources(); + disable_primary_monitor(); + disable_mirror_ports(mirror_port); - rte_ring_free(r); + rte_eal_hotplug_remove("vdev", vdev_name); + rte_ring_free(ring); rte_mempool_free(mp); return rte_eal_cleanup() ? EXIT_FAILURE : 0; diff --git a/app/dumpcap/meson.build b/app/dumpcap/meson.build index 69c016c780..6212291d40 100644 --- a/app/dumpcap/meson.build +++ b/app/dumpcap/meson.build @@ -14,4 +14,4 @@ endif ext_deps += pcap_dep sources = files('main.c') -deps += ['ethdev', 'pdump', 'pcapng', 'bpf'] +deps += ['net_ring', 'ethdev', 'pcapng', 'bpf'] -- 2.47.2