From patchwork Fri Aug 2 19:56:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 142879 X-Patchwork-Delegate: ferruh.yigit@amd.com 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 E55AB45720; Fri, 2 Aug 2024 21:58:41 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4CB5642793; Fri, 2 Aug 2024 21:58:37 +0200 (CEST) Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) by mails.dpdk.org (Postfix) with ESMTP id 858DD40E01 for ; Fri, 2 Aug 2024 21:58:35 +0200 (CEST) Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-7a1d48e0a5fso5080692a12.3 for ; Fri, 02 Aug 2024 12:58:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1722628714; x=1723233514; 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=LPoPMuWjjrTWYXAFqW4Fi54RT23qllLTTIghqYDZ7iE=; b=JZDITCSuCO8Sutn7dlmTleEssqkgLMxJJ4IC2Wa4XS1lUGac2ZsC7U4gNq8VteW1lC fxIGV+iNclns/yj9fgraMNGFmSZsI1Cf+ynFDv/qTNIdVcS6TstTaprgb2vQXKMvb4xO p+EODHX3mgPKEZwrrQrLPjk6nGrewTcHxUsOukLONBQmf9XIvCtOPpyR/bCa8xwt8Nmo 9PvIHP/XBFXptjOlLi2Cl+vD70bWaL23VUGrsgsnk/ipj4/4z3U10jjKJeH+z9DqCBEn cThp6Q4eu5Ctb/DW4yZD8g4Zm6kNIExVekuCmfj1BadOSZZOnM3fh1JGphz4lpBqcIbX onjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722628714; x=1723233514; 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=LPoPMuWjjrTWYXAFqW4Fi54RT23qllLTTIghqYDZ7iE=; b=MJGlUgid/wzpO+97PKabJtR/oKoSCpGOgbtBOV+n8VhZQPHbeJ/sRdDgYl7IdSJWA6 mTNfZK1aO1OL4tRoIUWEwFPSyOrOXcmVXPLHzbfR7cYqJW9TBLD9O1AY1jtmJ94SPaje 0AYB6jdh03B3v/PEiU44rfxWUNM2V0WZdgsGpbloXiFfqNbF3bPnSaH1O/xSx3aEJhuJ 6qGs9Pg+cD2yqIJw1+XQhircgytoeUmnhwgpgDfHE+wQANdW3XsxUuPI15e1L0Wy7dSP pIYbhzWJ0mBeksgHjnYqs29lbXXTTuttxEWlekjnwxEf/XlX3xabG4Kg16nKoDVb8ieJ 7oow== X-Gm-Message-State: AOJu0YxNXLsxlUt6rHfpXbRJfc5GG6t2XCSAGm8SXOHIWMZROF60vgwa hKb9ymZqk0V+2+657DAfX6TUeYTdfZGH5UNEgkiiRkycrkGaKoJGWVZWApVwOM1mXtfYxcFIqkV N X-Google-Smtp-Source: AGHT+IHbtFVo6DNkBNdpW3AFTKY7mSLgYZXun2HqjwVnJgZLuAaPjsAAtiOslHjcHfvM19g+2l6gow== X-Received: by 2002:a05:6a20:72a5:b0:1c3:a63a:cef2 with SMTP id adf61e73a8af0-1c69958569amr6231435637.28.1722628714473; Fri, 02 Aug 2024 12:58:34 -0700 (PDT) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7106ecfc344sm1697180b3a.151.2024.08.02.12.58.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 02 Aug 2024 12:58:34 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Subject: [PATCH v7 1/4] net: add more icmp types Date: Fri, 2 Aug 2024 12:56:52 -0700 Message-ID: <20240802195824.1336603-2-stephen@networkplumber.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240802195824.1336603-1-stephen@networkplumber.org> References: <20240312220129.70667-1-stephen@networkplumber.org> <20240802195824.1336603-1-stephen@networkplumber.org> MIME-Version: 1.0 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 Add more defines for additional defined ICMP types. Signed-off-by: Stephen Hemminger --- lib/net/rte_icmp.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/net/rte_icmp.h b/lib/net/rte_icmp.h index 4bf64d70ad..b51b60a6d2 100644 --- a/lib/net/rte_icmp.h +++ b/lib/net/rte_icmp.h @@ -54,10 +54,24 @@ struct rte_icmp_hdr { } __rte_packed; /* ICMP packet types */ -#define RTE_IP_ICMP_ECHO_REPLY 0 -#define RTE_IP_ICMP_ECHO_REQUEST 8 -#define RTE_ICMP6_ECHO_REQUEST 128 -#define RTE_ICMP6_ECHO_REPLY 129 +#define RTE_IP_ICMP_ECHO_REPLY 0 +#define RTE_IP_ICMP_DEST_UNREACH 3 +#define RTE_IP_ICMP_SOURCE_QUENCH 4 +#define RTE_IP_ICMP_REDIRECT 5 +#define RTE_IP_ICMP_ECHO_REQUEST 8 +#define RTE_IP_ICMP_TIME_EXCEEDED 11 +#define RTE_IP_ICMP_PARAMETERPROB 12 +#define RTE_IP_ICMP_TIMESTAMP 13 +#define RTE_IP_ICMP_TIMESTAMPREPLY 14 +#define RTE_IP_ICMP_INFO_REQUEST 15 +#define RTE_IP_ICMP_INFO_REPLY 16 + +#define RTE_ICMP6_ECHO_REQUEST 128 +#define RTE_ICMP6_ECHO_REPLY 129 +#define RTE_ND_ROUTER_SOLICIT 133 +#define RTE_ND_ROUTER_ADVERT 134 +#define RTE_ND_NEIGHBOR_SOLICIT 135 +#define RTE_ND_NEIGHBOR_ADVERT 136 #ifdef __cplusplus } From patchwork Fri Aug 2 19:56:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 142880 X-Patchwork-Delegate: ferruh.yigit@amd.com 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 9B4FE45720; Fri, 2 Aug 2024 21:58:48 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 92154427C3; Fri, 2 Aug 2024 21:58:38 +0200 (CEST) Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.169]) by mails.dpdk.org (Postfix) with ESMTP id 47FDD410F1 for ; Fri, 2 Aug 2024 21:58:36 +0200 (CEST) Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-7106e2d0ec1so1224085b3a.2 for ; Fri, 02 Aug 2024 12:58:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1722628715; x=1723233515; 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=pM1aI4lS/LrdzLQ+uHI/+A+S/bQmJizMB7MzkThxO2I=; b=DSlDUUQy50JJ8Ruex0lI/YmNPBHGrJ2k9v+3LaNIrt/VfPBKWVDj+7DrhOrsOgtCL7 lPHroCw0+CcMwg21nZYG395+lTL5omkGpmMQI1Dy3U2e/V3t1qolqLXdL44EXWB6nsMC 7Z6uXX7sdU6JffBOUXbKsrK+rW36KTj3q8yoFGxnjrKn9EVSaZwPTnmKOP4EDoOcMtKw uKksJxOTlprzCRdqbW0976o++YhI0B6UDd34V4YXHvAIvzrC/VK0p358xliypfe+9+fT +LKvrsfJFtMYVcVqGtQO6cR1RkLjnAFimQ4/KkNX8u051A1407NHafk4aI7tA5fMB3ye oFgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722628715; x=1723233515; 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=pM1aI4lS/LrdzLQ+uHI/+A+S/bQmJizMB7MzkThxO2I=; b=Ovs3EISbhe0lyc4V6ORNhSKA1yr4q0uBD8/KckIuoFJ+uopTDqlIGtixMPp9I7h37J DXf8CYSOe9UYlSMNfk/531rZSt7tDg3EwMKS+LV0LpQC+4VIk9TRIMJ/LCA8Ua3x0QJM ANZRYLwsq82N1EOmjs6F55VUuPYcBzXzDmZBC7l3Kh9TyNoDq9nPZUhlO1taS/Ftn8Ez x3HcdYvexuTbeoyniCRViycFy8NDgSzH8xSsSut26LQnfcTtyBclr5IALBDoiXlhHath b4dCQlElUyIIRT6w/8HJQn+mNWx6vWVRU26njcgM+RGg9aOwT4qkTGRG2Htj5xZsC6Hm t1EQ== X-Gm-Message-State: AOJu0YyHq1/6wtWWy+MG0Iv2Hq9hOIipwQi0RiwJUaFjjr0Hlsm2Hxpt PuSoTnl/le7NmHGP94cDnycGEowzdF4D0afrO0gvpDbFHyscjiXE+56+aJUoh49DR3B0BQvAk05 j X-Google-Smtp-Source: AGHT+IEDMPTgNrI85j38UVTJccVy7xFm1oZMN0oEfKytYFm0hDRFJOUGFBEFYTrg+cizz2y2rCqGRw== X-Received: by 2002:a05:6a00:3a14:b0:70d:3354:a194 with SMTP id d2e1a72fcca58-7106cf8aa01mr6138576b3a.2.1722628715277; Fri, 02 Aug 2024 12:58:35 -0700 (PDT) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7106ecfc344sm1697180b3a.151.2024.08.02.12.58.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 02 Aug 2024 12:58:34 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Subject: [PATCH v7 2/4] net: add new packet dissector Date: Fri, 2 Aug 2024 12:56:53 -0700 Message-ID: <20240802195824.1336603-3-stephen@networkplumber.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240802195824.1336603-1-stephen@networkplumber.org> References: <20240312220129.70667-1-stephen@networkplumber.org> <20240802195824.1336603-1-stephen@networkplumber.org> MIME-Version: 1.0 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 The function rte_dissect_mbuf is used to decode the contents of an mbuf into ah uman readable format similar to what tshark uses. For now, handles IP, IPv6, TCP, UDP, ICMP and ARP. Signed-off-by: Stephen Hemminger --- lib/net/meson.build | 2 + lib/net/rte_dissect.c | 428 ++++++++++++++++++++++++++++++++++++++++++ lib/net/rte_dissect.h | 45 +++++ lib/net/version.map | 7 + 4 files changed, 482 insertions(+) create mode 100644 lib/net/rte_dissect.c create mode 100644 lib/net/rte_dissect.h diff --git a/lib/net/meson.build b/lib/net/meson.build index 0b69138949..48edf17ea3 100644 --- a/lib/net/meson.build +++ b/lib/net/meson.build @@ -2,6 +2,7 @@ # Copyright(c) 2017-2020 Intel Corporation headers = files( + 'rte_dissect.h', 'rte_ip.h', 'rte_tcp.h', 'rte_udp.h', @@ -30,6 +31,7 @@ headers = files( sources = files( 'rte_arp.c', + 'rte_dissect.c', 'rte_ether.c', 'rte_net.c', 'rte_net_crc.c', diff --git a/lib/net/rte_dissect.c b/lib/net/rte_dissect.c new file mode 100644 index 0000000000..de9e8f4c56 --- /dev/null +++ b/lib/net/rte_dissect.c @@ -0,0 +1,428 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 Stephen Hemminger + * + * Print packets in format similar to tshark. + * Output should be one line per mbuf + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Forward declaration - Ethernet can be nested */ +static int +dissect_eth(char *buf, size_t size, const struct rte_mbuf *mb, + uint32_t offset, uint32_t dump_len); + +/* + * Read data from segmented mbuf and put it into buf , but stop if would go past max length + * See rte_pktmbuf_read() + */ +static const void * +dissect_read(const struct rte_mbuf *m, uint32_t offset, uint32_t len, + void *buf, uint32_t dump_len) +{ + + /* If this header would be past the requested length */ + if (dump_len > 0 && offset + len > dump_len) + return NULL; + + return rte_pktmbuf_read(m, offset, len, buf); +} + +/* + * Print to string buffer and adjust result + * Returns true on success, false if buffer is exhausted. + */ +static __rte_format_printf(3, 4) int +dissect_print(char **buf, size_t *sz, const char *fmt, ...) +{ + va_list ap; + int count; + + va_start(ap, fmt); + count = vsnprintf(*buf, *sz, fmt, ap); + va_end(ap); + + /* error or string is full */ + if (count < 0 || count >= (int)*sz) { + *sz = 0; + } else { + *buf += count; + *sz -= count; + } + return count; +} + +static int +dissect_arp(char *buf, size_t size, const struct rte_mbuf *mb, + uint32_t offset, uint32_t dump_len) +{ + const struct rte_arp_hdr *arp; + struct rte_arp_hdr _arp; + int count = 0; + char abuf[64]; + + arp = dissect_read(mb, offset, sizeof(_arp), &_arp, dump_len); + if (arp == NULL) + return snprintf(buf, size, "Missing ARP header"); + + offset += sizeof(_arp); + + uint16_t ar_op = rte_be_to_cpu_16(arp->arp_opcode); + switch (ar_op) { + case RTE_ARP_OP_REQUEST: + inet_ntop(AF_INET, &arp->arp_data.arp_tip, abuf, sizeof(abuf)); + count += dissect_print(&buf, &size, "Who has %s? ", abuf); + + rte_ether_format_addr(abuf, sizeof(abuf), &arp->arp_data.arp_sha); + count += dissect_print(&buf, &size, "Tell %s ", abuf); + break; + + case RTE_ARP_OP_REPLY: + inet_ntop(AF_INET, &arp->arp_data.arp_sip, abuf, sizeof(abuf)); + count += dissect_print(&buf, &size, "%s is at", abuf); + + rte_ether_format_addr(abuf, sizeof(abuf), &arp->arp_data.arp_sha); + count += dissect_print(&buf, &size, "%s ", abuf); + break; + + case RTE_ARP_OP_INVREQUEST: + rte_ether_format_addr(abuf, sizeof(abuf), &arp->arp_data.arp_tha); + count += dissect_print(&buf, &size, "Who is %s? ", abuf); + + rte_ether_format_addr(abuf, sizeof(abuf), &arp->arp_data.arp_sha); + count += dissect_print(&buf, &size, "Tell %s ", abuf); + break; + + case RTE_ARP_OP_INVREPLY: + rte_ether_format_addr(abuf, sizeof(buf), &arp->arp_data.arp_sha); + count += dissect_print(&buf, &size, "%s is at ", abuf); + + inet_ntop(AF_INET, &arp->arp_data.arp_sip, abuf, sizeof(abuf)); + count += dissect_print(&buf, &size, "%s ", abuf); + break; + + default: + count += dissect_print(&buf, &size, "Unknown ARP %#x ", ar_op); + break; + } + + return count; +} + +static int +dissect_vxlan(char *buf, size_t size, const struct rte_mbuf *mb, uint32_t offset, uint32_t dump_len) +{ + const struct rte_vxlan_hdr *vxlan; + struct rte_vxlan_hdr _vxlan; + int count = 0; + + vxlan = dissect_read(mb, offset, sizeof(_vxlan), &_vxlan, dump_len); + if (vxlan == NULL) + return snprintf(buf, size, "Missing VXLAN header"); + + offset += sizeof(_vxlan); + + if (vxlan->flag_i) { + uint32_t vni = rte_be_to_cpu_32(vxlan->vx_vni); + + count += dissect_print(&buf, &size, "%#x ", vni >> 8); + } + + count += dissect_eth(buf, size, mb, offset, dump_len); + return count; +} + +static int +dissect_udp(char *buf, size_t size, const struct rte_mbuf *mb, uint32_t offset, uint32_t dump_len) +{ + const struct rte_udp_hdr *udph; + struct rte_udp_hdr _udp; + uint16_t src_port, dst_port, len; + + udph = dissect_read(mb, offset, sizeof(_udp), &_udp, dump_len); + if (udph == NULL) + return snprintf(buf, size, "Missing UDP header"); + + offset += sizeof(_udp); + src_port = rte_be_to_cpu_16(udph->src_port); + dst_port = rte_be_to_cpu_16(udph->dst_port); + len = rte_be_to_cpu_16(udph->dgram_len); + + switch (dst_port) { + case RTE_VXLAN_DEFAULT_PORT: + return dissect_vxlan(buf, size, mb, offset, dump_len); + default: + return dissect_print(&buf, &size, "UDP %u %u → %u ", len, src_port, dst_port); + } +} + +static int +dissect_tcp(char *buf, size_t size, const struct rte_mbuf *mb, uint32_t offset, uint32_t dump_len) +{ + const struct rte_tcp_hdr *tcph; + struct rte_tcp_hdr _tcp; + uint16_t src_port, dst_port; + int count; + + tcph = dissect_read(mb, offset, sizeof(_tcp), &_tcp, dump_len); + if (tcph == NULL) + return snprintf(buf, size, "Missing TCP header"); + + offset += sizeof(_tcp); + src_port = rte_be_to_cpu_16(tcph->src_port); + dst_port = rte_be_to_cpu_16(tcph->dst_port); + + count = dissect_print(&buf, &size, "TCP %u → %u", src_port, dst_port); + +#define PRINT_TCP_FLAG(flag) do { \ + if (tcph->tcp_flags & RTE_TCP_ ## flag ## _FLAG) \ + count += dissect_print(&buf, &size, " [ " #flag " ]"); \ + } while (0) + + PRINT_TCP_FLAG(URG); + PRINT_TCP_FLAG(ACK); + PRINT_TCP_FLAG(RST); + PRINT_TCP_FLAG(SYN); + PRINT_TCP_FLAG(FIN); +#undef PRINT_TCP_FLAG + + count += dissect_print(&buf, &size, "Seq=%u Ack=%u Win=%u ", + rte_be_to_cpu_16(tcph->sent_seq), + rte_be_to_cpu_16(tcph->recv_ack), + rte_be_to_cpu_16(tcph->rx_win)); + return count; +} + +static int +dissect_icmp(char *buf, size_t size, const struct rte_mbuf *mb, uint32_t offset, uint32_t dump_len) +{ + const struct rte_icmp_hdr *icmp; + struct rte_icmp_hdr _icmp; + static const char * const icmp_types[256] = { + [RTE_IP_ICMP_ECHO_REPLY] = "ICMP Echo Reply", + [RTE_IP_ICMP_DEST_UNREACH] = "ICMP Destination Unreachable", + [RTE_IP_ICMP_SOURCE_QUENCH] = "ICMP Source Quench", + [RTE_IP_ICMP_REDIRECT] = "ICMP Redirect", + [RTE_IP_ICMP_ECHO_REQUEST] = "ICMP Echo Request", + [RTE_IP_ICMP_TIME_EXCEEDED] = "ICMP Time Exceeded", + [RTE_IP_ICMP_PARAMETERPROB] = "ICMP Parameter Problem", + [RTE_IP_ICMP_TIMESTAMP] = "ICMP Timestamp Request", + [RTE_IP_ICMP_TIMESTAMPREPLY] = "ICMP Timestamp Reply", + [RTE_IP_ICMP_INFO_REQUEST] = "ICMP Info Request", + [RTE_IP_ICMP_INFO_REPLY] = "ICMP Info Reply", + + [RTE_ICMP6_ECHO_REPLY] = "ICMPv6 Echo Reply", + [RTE_ICMP6_ECHO_REQUEST] = "ICMPv6 Echo Request", + [RTE_ND_ROUTER_SOLICIT] = "ICMPv6 Router Solicitation", + [RTE_ND_ROUTER_ADVERT] = "ICMPv6 Router Advertisement", + [RTE_ND_NEIGHBOR_SOLICIT] = "ICMPv6 Neighbor Solicitation", + [RTE_ND_NEIGHBOR_ADVERT] = "ICMPv6 Neighbor Advertisement", + }; + + icmp = dissect_read(mb, offset, sizeof(_icmp), &_icmp, dump_len); + if (icmp == NULL) + return snprintf(buf, size, "Missing ICMP header"); + + offset += sizeof(_icmp); + const char *name = icmp_types[icmp->icmp_type]; + if (name != NULL) + return dissect_print(&buf, &size, "%s ", name); + else + return dissect_print(&buf, &size, "ICMP %u ", icmp->icmp_type); +} + +static int +dissect_ipv4(char *buf, size_t size, const struct rte_mbuf *mb, + uint32_t offset, uint32_t dump_len) +{ + const struct rte_ipv4_hdr *ip_hdr; + struct rte_ipv4_hdr _ip_hdr; + char sbuf[INET_ADDRSTRLEN], dbuf[INET_ADDRSTRLEN]; + int count; + + ip_hdr = dissect_read(mb, offset, sizeof(_ip_hdr), &_ip_hdr, dump_len); + if (ip_hdr == NULL) + return snprintf(buf, size, "Missing IP header"); + + inet_ntop(AF_INET, &ip_hdr->src_addr, sbuf, sizeof(sbuf)); + inet_ntop(AF_INET, &ip_hdr->dst_addr, dbuf, sizeof(dbuf)); + count = dissect_print(&buf, &size, "%s → %s ", sbuf, dbuf); + + offset += ip_hdr->ihl * 4; + switch (ip_hdr->next_proto_id) { + case IPPROTO_UDP: + count += dissect_udp(buf, size, mb, offset, dump_len); + break; + case IPPROTO_TCP: + count += dissect_tcp(buf, size, mb, offset, dump_len); + break; + case IPPROTO_ICMP: + count += dissect_icmp(buf, size, mb, offset, dump_len); + break; + default: + /* TODO dissect tunnels */ + count += dissect_print(&buf, &size, "IP %#x ", ip_hdr->next_proto_id); + } + return count; +} + +static int +dissect_ipv6(char *buf, size_t size, const struct rte_mbuf *mb, + uint32_t offset, uint32_t dump_len) +{ + const struct rte_ipv6_hdr *ip6_hdr; + struct rte_ipv6_hdr _ip6_hdr; + char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN]; + uint16_t proto; + unsigned int i; + int count; + + ip6_hdr = dissect_read(mb, offset, sizeof(_ip6_hdr), &_ip6_hdr, dump_len); + if (ip6_hdr == NULL) + return snprintf(buf, size, "Missing IPv6 header"); + + offset += sizeof(*ip6_hdr); + inet_ntop(AF_INET6, ip6_hdr->src_addr, sbuf, sizeof(sbuf)); + inet_ntop(AF_INET6, ip6_hdr->dst_addr, dbuf, sizeof(dbuf)); + count = dissect_print(&buf, &size, "%s → %s ", sbuf, dbuf); + +#define MAX_EXT_HDRS 5 + proto = ip6_hdr->proto; + for (i = 0; i < MAX_EXT_HDRS; i++) { + switch (proto) { + case IPPROTO_UDP: + count += dissect_udp(buf, size, mb, offset, dump_len); + return count; + + case IPPROTO_TCP: + count += dissect_tcp(buf, size, mb, offset, dump_len); + return count; + + case IPPROTO_ICMPV6: + count += dissect_icmp(buf, size, mb, offset, dump_len); + return count; + + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + { + const struct rte_ipv6_routing_ext *xh; + struct rte_ipv6_routing_ext _xh; + + xh = dissect_read(mb, offset, sizeof(xh), &_xh, dump_len); + if (xh == NULL) + return count; + + offset += (xh->hdr_len + 1) * 8; + proto = xh->next_hdr; + continue; + } + + case IPPROTO_FRAGMENT: + count += dissect_print(&buf, &size, "%s", "FRAG "); + return count; + + case IPPROTO_NONE: + count += dissect_print(&buf, &size, "%s", "NONE "); + return count; + + default: + count += dissect_print(&buf, &size, "IPv6 %#x ", proto); + return count; + } + } + return count; +} + +/* + * Format up a string describing contents of packet in tshark like style. + */ +static int +dissect_eth(char *buf, size_t size, const struct rte_mbuf *mb, + uint32_t offset, uint32_t dump_len) +{ + const struct rte_ether_hdr *eth_hdr; + struct rte_ether_hdr _eth_hdr; + uint16_t eth_type; + int count = 0; + char sbuf[RTE_ETHER_ADDR_FMT_SIZE], dbuf[RTE_ETHER_ADDR_FMT_SIZE]; + + eth_hdr = dissect_read(mb, offset, sizeof(_eth_hdr), &_eth_hdr, dump_len); + if (unlikely(eth_hdr == NULL)) + return snprintf(buf, size, "Missing ETH header"); + + offset += sizeof(*eth_hdr); + eth_type = rte_be_to_cpu_16(eth_hdr->ether_type); + if (eth_type == RTE_ETHER_TYPE_VLAN || eth_type == RTE_ETHER_TYPE_QINQ) { + const struct rte_vlan_hdr *vh; + struct rte_vlan_hdr _vh; + + vh = dissect_read(mb, offset, sizeof(_vh), &_vh, dump_len); + if (unlikely(vh == NULL)) + return snprintf(buf, size, "Missing VLAN header"); + + eth_type = vh->eth_proto; + offset += sizeof(*vh); + + count += dissect_print(&buf, &size, "%s %#x ", + (eth_type == RTE_ETHER_TYPE_VLAN) ? "VLAN" : "QINQ", + rte_be_to_cpu_16(vh->vlan_tci)); + } + + switch (eth_type) { + case RTE_ETHER_TYPE_ARP: + rte_ether_format_addr(sbuf, sizeof(sbuf), ð_hdr->src_addr); + rte_ether_format_addr(dbuf, sizeof(dbuf), ð_hdr->dst_addr); + count += dissect_print(&buf, &size, "%s → %s ARP ", sbuf, dbuf); + count += dissect_arp(buf, size, mb, offset, dump_len); + break; + + case RTE_ETHER_TYPE_IPV4: + count += dissect_ipv4(buf, size, mb, offset, dump_len); + break; + + case RTE_ETHER_TYPE_IPV6: + count += dissect_ipv6(buf, size, mb, offset, dump_len); + break; + + default: + count += dissect_print(&buf, &size, "ETH %#x ", eth_type); + } + + return count; +} + +int +rte_dissect_mbuf(char *buf, size_t size, const struct rte_mbuf *m, uint32_t dump_len) +{ + int count; + + count = dissect_eth(buf, size, m, 0, dump_len); + if (count <= 0) + return count; + + /* output was truncated, but redact the trailing blank */ + if (count >= (int)size) + return count - 1; + + if (buf[count] == ' ') + buf[count--] = '\0'; + + return count; +} diff --git a/lib/net/rte_dissect.h b/lib/net/rte_dissect.h new file mode 100644 index 0000000000..343e9daa4d --- /dev/null +++ b/lib/net/rte_dissect.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2024 Stephen Hemminger + */ + +#ifndef _RTE_NET_DISSECT_H_ +#define _RTE_NET_DISSECT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +struct rte_mbuf; + +/** + * + * Format description of packet to a string buffer + * + * @param buf + * A pointer to buffer for the resulting line. + * @param size + * The format buffer size. + * @param m + * The packet mbuf. + * @param dump_len + * Maximum offset in packet to examine. + * If is zero then dump the whole packet. + * @return + * Returns the number of bytes printed (excluding null byte at end of string). + * if output was truncated returns the number of bytes that would have been printed. + */ +__rte_experimental +int +rte_dissect_mbuf(char *buf, size_t size, const struct rte_mbuf *m, uint32_t dump_len); + +#ifdef __cplusplus +} +#endif + + +#endif /* _RTE_NET_DISSECT_H_ */ diff --git a/lib/net/version.map b/lib/net/version.map index 3e293c4715..d7b9e9c0e7 100644 --- a/lib/net/version.map +++ b/lib/net/version.map @@ -12,3 +12,10 @@ DPDK_24 { local: *; }; + +EXPERIMENTAL { + global: + + # added in 24.11 + rte_dissect_mbuf; +}; From patchwork Fri Aug 2 19:56:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 142881 X-Patchwork-Delegate: ferruh.yigit@amd.com 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 E488C45720; Fri, 2 Aug 2024 21:58:55 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id CBB62427CB; Fri, 2 Aug 2024 21:58:39 +0200 (CEST) Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) by mails.dpdk.org (Postfix) with ESMTP id E1B974270A for ; Fri, 2 Aug 2024 21:58:36 +0200 (CEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-70d1d6369acso2667836b3a.0 for ; Fri, 02 Aug 2024 12:58:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1722628716; x=1723233516; 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=cBEkq0VzPSt4ZEIGlbT+vA6hyEiQOvWEOQHNyJasuUA=; b=RMJeYvugUl2FvzkHbXMdU9tiskO9moEPxb31w2DRD7/GadZ+Cfs12nxdXrCiyEVQ7P HGgxwy9fOAEJdiRa6kScWQDr1h3n+SL/+GnbSDjo/YtibCsinTVT/8EG6OrtlNbBrfIb 0CrTOHbUdFE4AygQSzvqEJaajw4crUuNLLl8eCAfe+ZEw6/5gXzm7GeMb2PvcoHhMd6N zkH3d4ZUx7vkxFJtIKW7UH7u8iToUowy5vNEW7Jo/s7EdgBTTx1OEeMSZXV8sK0kuF8T SzjzoKNhsvWohIQCGZExLTReZWkWdYOCdSlCyveVBXAxGMl5H944y8QAIeh+KA1ekzQj kSfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722628716; x=1723233516; 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=cBEkq0VzPSt4ZEIGlbT+vA6hyEiQOvWEOQHNyJasuUA=; b=StQGCwHW3NdNUxhLDKomZnVmv0pCTRLh6R4+mgZ7AxL0Zqw+ddUJMlO1oX1cxPEsTs TFPl3leRfC1CagEt93teB/VdGUsiEzobePhprwBw7dg04/QeujNkemB0y6u8NDrXudyy TfbU/htesRyOpJju78Xdey57FydaO62m8d/JRIiczjYLNMsYtYnuIsGyZbkQZToMI6jN RdznuzDYNtNgSd2q7HtkIpOPSl0Qm62koAZMAdAoUW5GL9l1xkVo/axdF8lSvD74Hnv+ pBv5Oq16UjBiF7jFVoNCMydcdJiIVz09zqyBYf503R8MMs0jXaLtSHuH/90F4G3diMhj Lc9g== X-Gm-Message-State: AOJu0YxbvD+OdcqCAive0kRwdpviuVJdoqfwrNXocLkfFdm+kjs33qnt xEQU4/t0tPIY5KmiDhZN1keO9akec6p1zh2NMN2iOMfgaCfeY8tNEbjvLppcgZhBlfH5AHUmjUR W X-Google-Smtp-Source: AGHT+IEtmy3ZP2Bz02pG2PD5GqQuBLnPmlW0FUPiTROD+sL80DLLiTmnB0ExtPHbNJNVRcdfFiKngg== X-Received: by 2002:a05:6a21:788a:b0:1c4:d14f:248f with SMTP id adf61e73a8af0-1c69a5f0268mr6667103637.13.1722628715994; Fri, 02 Aug 2024 12:58:35 -0700 (PDT) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7106ecfc344sm1697180b3a.151.2024.08.02.12.58.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 02 Aug 2024 12:58:35 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Subject: [PATCH v7 3/4] test: add test for packet dissector Date: Fri, 2 Aug 2024 12:56:54 -0700 Message-ID: <20240802195824.1336603-4-stephen@networkplumber.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240802195824.1336603-1-stephen@networkplumber.org> References: <20240312220129.70667-1-stephen@networkplumber.org> <20240802195824.1336603-1-stephen@networkplumber.org> MIME-Version: 1.0 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 Add some tests for new packet dissector. Signed-off-by: Stephen Hemminger --- app/test/meson.build | 1 + app/test/test_dissect.c | 302 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 303 insertions(+) create mode 100644 app/test/test_dissect.c diff --git a/app/test/meson.build b/app/test/meson.build index e29258e6ec..9cd2051320 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -62,6 +62,7 @@ source_file_deps = { 'test_debug.c': [], 'test_devargs.c': ['kvargs'], 'test_dispatcher.c': ['dispatcher'], + 'test_dissect.c': ['net'], 'test_distributor.c': ['distributor'], 'test_distributor_perf.c': ['distributor'], 'test_dmadev.c': ['dmadev', 'bus_vdev'], diff --git a/app/test/test_dissect.c b/app/test/test_dissect.c new file mode 100644 index 0000000000..08734134d5 --- /dev/null +++ b/app/test/test_dissect.c @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2024 Stephen Hemminger + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +#ifndef LINE_MAX +#define LINE_MAX 2048 +#endif + +#define TOTAL_PACKETS 100 +#define PACKET_LEN 1000 +#define ETH_IP_UDP_VXLAN_SIZE (sizeof(struct rte_ether_hdr) + \ + sizeof(struct rte_ipv4_hdr) + \ + sizeof(struct rte_udp_hdr) + \ + sizeof(struct rte_vxlan_hdr)) + + +static uint16_t port_id; +static const char null_dev[] = "net_null0"; + +static void +add_header(struct rte_mbuf *mb, uint32_t plen, + rte_be16_t src_port, rte_be16_t dst_port) +{ + struct { + struct rte_ether_hdr eth; + struct rte_ipv4_hdr ip; + struct rte_udp_hdr udp; + } pkt = { + .eth = { + .dst_addr.addr_bytes = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + .ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4), + }, + .ip = { + .version_ihl = RTE_IPV4_VHL_DEF, + .time_to_live = 1, + .next_proto_id = IPPROTO_UDP, + .src_addr = rte_cpu_to_be_32(RTE_IPV4_LOOPBACK), + .dst_addr = rte_cpu_to_be_32(RTE_IPV4_BROADCAST), + }, + .udp = { + .dst_port = dst_port, + .src_port = src_port, + }, + }; + + rte_eth_random_addr(pkt.eth.src_addr.addr_bytes); + + plen -= sizeof(struct rte_ether_hdr); + pkt.ip.total_length = rte_cpu_to_be_16(plen); + pkt.ip.hdr_checksum = rte_ipv4_cksum(&pkt.ip); + + plen -= sizeof(struct rte_ipv4_hdr); + pkt.udp.dgram_len = rte_cpu_to_be_16(plen); + + /* Copy header into mbuf */ + memcpy(rte_pktmbuf_append(mb, sizeof(pkt)), &pkt, sizeof(pkt)); +} + +static void +add_vxlan(struct rte_mbuf *mb, rte_be32_t vni) +{ + struct rte_vxlan_hdr *vxlan; + + vxlan = (struct rte_vxlan_hdr *)rte_pktmbuf_append(mb, sizeof(*vxlan)); + memset(vxlan, 0, sizeof(*vxlan)); + vxlan->flag_i = 1; + vxlan->vx_vni = vni; +} + + +static void +fill_data(struct rte_mbuf *mb, uint32_t len) +{ + uint32_t i; + char *ptr = rte_pktmbuf_append(mb, len); + char c = '!'; + + /* traditional barber pole pattern */ + for (i = 0; i < len; i++) { + ptr[i] = c++; + if (c == 0x7f) + c = '!'; + } +} + +static void +mbuf_prep(struct rte_mbuf *mb, uint8_t buf[], uint32_t buf_len) +{ + mb->buf_addr = buf; + rte_mbuf_iova_set(mb, (uintptr_t)buf); + mb->buf_len = buf_len; + rte_mbuf_refcnt_set(mb, 1); + + /* set pool pointer to dummy value, test doesn't use it */ + mb->pool = (void *)buf; + + rte_pktmbuf_reset(mb); +} + +static int +test_setup(void) +{ + port_id = rte_eth_dev_count_avail(); + + /* Make a dummy null device to snoop on */ + if (rte_vdev_init(null_dev, NULL) != 0) { + fprintf(stderr, "Failed to create vdev '%s'\n", null_dev); + goto fail; + } + return 0; + +fail: + rte_vdev_uninit(null_dev); + return -1; +} + +static void +test_cleanup(void) +{ + rte_vdev_uninit(null_dev); +} + + +static int +test_simple(void) +{ + struct rte_mbuf mb; + uint8_t buf[RTE_MBUF_DEFAULT_BUF_SIZE]; + uint32_t data_len = PACKET_LEN; + rte_be16_t src_port = rte_rand_max(UINT16_MAX); + const rte_be16_t dst_port = rte_cpu_to_be_16(9); /* Discard port */ + char obuf[LINE_MAX] = { }; + char result[LINE_MAX] = { }; + int ret; + + /* make a dummy packet */ + mbuf_prep(&mb, buf, sizeof(buf)); + add_header(&mb, data_len, src_port, dst_port); + fill_data(&mb, data_len - mb.data_off); + + /* construct the expected result */ + int len = snprintf(result, sizeof(result), + "127.0.0.1 → 224.0.0.0 UDP 966 %u → 9", + rte_be_to_cpu_16(src_port)); + + ret = rte_dissect_mbuf(obuf, sizeof(obuf), &mb, 0); + TEST_ASSERT(ret > 0, "Dissect returned: %d", ret); + + TEST_ASSERT_BUFFERS_ARE_EQUAL(obuf, result, len, + "Dissect string differs:\nexpect \"%s\"\n got \"%s\"", + result, obuf); + + return TEST_SUCCESS; +} + +static int +test_buffer(void) +{ + struct rte_mbuf mb; + uint8_t buf[RTE_MBUF_DEFAULT_BUF_SIZE]; + uint32_t data_len = PACKET_LEN; + rte_be16_t src_port = rte_rand_max(UINT16_MAX); + const rte_be16_t dst_port = rte_cpu_to_be_16(9); /* Discard port */ + char *obuf = NULL; + char result[LINE_MAX] = { }; + int ret; + + /* make a dummy packet */ + mbuf_prep(&mb, buf, sizeof(buf)); + add_header(&mb, data_len, src_port, dst_port); + fill_data(&mb, data_len - mb.data_off); + + /* construct the expected result */ + int len = snprintf(result, sizeof(result), + "127.0.0.1 → 224.0.0.0 UDP 966 %u → 9", + rte_be_to_cpu_16(src_port)); + + /* call rte_dissect first to determine buffer length needed. */ + ret = rte_dissect_mbuf(obuf, 0, &mb, 0); + TEST_ASSERT(ret == len, "Dissect with NULL returned %d not %d", ret, len); + + size_t size = (size_t) ret + 1; /* One extra byte for '\0' */ + obuf = malloc(size); + TEST_ASSERT_NOT_NULL(obuf, "Malloc for buf failed"); + + ret = rte_dissect_mbuf(obuf, size, &mb, 0); + TEST_ASSERT(ret == len, "Dissect with buffer returned %d not %d", ret, len); + + TEST_ASSERT_BUFFERS_ARE_EQUAL(obuf, result, len, + "Dissect string differs:\nexpect \"%s\"\n got \"%s\"", + result, obuf); + free(obuf); + return TEST_SUCCESS; +} + +static int +test_truncated(void) +{ + struct rte_mbuf mb; + uint8_t buf[RTE_MBUF_DEFAULT_BUF_SIZE]; + uint32_t pkt_len, data_len = PACKET_LEN; + rte_be16_t dst_port = rte_cpu_to_be_16(RTE_VXLAN_DEFAULT_PORT); + char obuf[LINE_MAX]; + int ret; + + /* make a really nested vxlan packet */ + mbuf_prep(&mb, buf, sizeof(buf)); + pkt_len = data_len; + do { + rte_be16_t src_port = rte_rand_max(UINT16_MAX); + uint32_t vni = rte_rand_max(1ul << 24); + + add_header(&mb, data_len, src_port, dst_port); + add_vxlan(&mb, vni); + pkt_len -= ETH_IP_UDP_VXLAN_SIZE; + } while (pkt_len > ETH_IP_UDP_VXLAN_SIZE); + + fill_data(&mb, pkt_len); + + /* dissect it but snip off some amount of data */ + for (unsigned int i = 0; i < TOTAL_PACKETS; i++) { + uint32_t snaplen = rte_rand_max(pkt_len); + + ret = rte_dissect_mbuf(obuf, sizeof(obuf), &mb, snaplen); + TEST_ASSERT(ret > 0, "Truncated len %u failed: %d", + snaplen, ret); + } + + return TEST_SUCCESS; +} + +static int +test_fuzz(void) +{ + struct rte_mbuf mb; + uint8_t buf[RTE_MBUF_DEFAULT_BUF_SIZE]; + uint32_t data_len = PACKET_LEN; + const rte_be16_t dst_port = rte_cpu_to_be_16(rte_rand_max(1024)); + const rte_be16_t src_port = rte_rand_max(UINT16_MAX); + char obuf[LINE_MAX]; + int ret; + + /* make a dummy packet */ + mbuf_prep(&mb, buf, sizeof(buf)); + add_header(&mb, data_len, src_port, dst_port); + fill_data(&mb, data_len - mb.data_off); + + /* randomly flip bits in it */ + for (unsigned int i = 0; i < TOTAL_PACKETS; i++) { + uint32_t bit = rte_rand_max(data_len) * 8; + uint8_t *bp = buf + bit / 8; + uint8_t mask = 1u << (bit % 8); + + /* twiddle one bit */ + *bp ^= mask; + ret = rte_dissect_mbuf(obuf, sizeof(obuf), &mb, 0); + TEST_ASSERT(ret > 0, "Fuzz bit %u failed", bit); + *bp ^= mask; + } + + return TEST_SUCCESS; +} + +static struct +unit_test_suite test_dissect_suite = { + .setup = test_setup, + .teardown = test_cleanup, + .suite_name = "Test Dissect Unit Test Suite", + .unit_test_cases = { + TEST_CASE(test_simple), + TEST_CASE(test_buffer), + TEST_CASE(test_truncated), + TEST_CASE(test_fuzz), + TEST_CASES_END() + } +}; + +static int +test_dissect(void) +{ + return unit_test_suite_runner(&test_dissect_suite); +} + +REGISTER_FAST_TEST(dissect_autotest, true, true, test_dissect); From patchwork Fri Aug 2 19:56:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 142882 X-Patchwork-Delegate: ferruh.yigit@amd.com 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 1218345720; Fri, 2 Aug 2024 21:59:06 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 86C30427E5; Fri, 2 Aug 2024 21:58:41 +0200 (CEST) Received: from mail-pg1-f171.google.com (mail-pg1-f171.google.com [209.85.215.171]) by mails.dpdk.org (Postfix) with ESMTP id B063C427AD for ; Fri, 2 Aug 2024 21:58:37 +0200 (CEST) Received: by mail-pg1-f171.google.com with SMTP id 41be03b00d2f7-7a264a24ea7so5646148a12.3 for ; Fri, 02 Aug 2024 12:58:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1722628717; x=1723233517; 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=J1R1odZ0oHOJF5vgnVEzIvFRm7WKvcbCRzqOKQsMEMY=; b=VrsxefGqasDh4Ku1rYOxTijtHWQOVnCdA5IcTb1I9JyAK6VrZOZSrBeDAZyWT65s5a LWTpHUi/7VDuJULJen7qD2deCTZclM8chE3mrdo3TIRBn7495Bnf8H/dXqXeMbudpDjN jNyjJcRXjX3yDVQX5EMr5ieg+Lp0CwufuPcS2ewVPJkduZXtMlncTu9O8hx+64r5I6JN c/rv5sbcJHGud2qwGDTfos5H+LHBrIG/nPW4pxiKG+PBvhw1wnVurNGvTiE65q4HCAV1 Tve+hZHAsxFR0/B5mwvWVy1UpJ7azFTla6vdkzvPZZAl2v8iND8CU63wXWHNPvGypeFc 7diQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1722628717; x=1723233517; 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=J1R1odZ0oHOJF5vgnVEzIvFRm7WKvcbCRzqOKQsMEMY=; b=V9s+GVbko1NAR2LLaY7hUfj/OQT8rkPzG0GAuFLmfVWzquaYrwKAmLBThlOTUe2VJv g+Up4QGo03ZGE2V0rJto36bNNBWCLejfVa5tak+8mTTpS9h3W2jGWVTWKUsYEsqWupG7 8dvIQdF43t1ksBsEj0OD1DFTptcrLDcp2ER2wHD9HPCPAYRfw4p7Fe6PBTzjkyDZtOt0 w03RbnrGCrvHYtnBkN7ReqcwBeSso041yPcRCF6ULQaFfgfvvpQ2Htb3+/NF8ZYDEQDA y0N4GGHhAxFO+pcRHMj9CA9RDUH7t8Sx89/W7+4ljLuDiJ5UWKU6wTQx3Nri3ZNzgBBu Vydg== X-Gm-Message-State: AOJu0YyFJTEToAgI7bskeXppDFAfqGmT3otF8RUQA9VuWjGpDPynlJuh 4KJTJhD3zFcLavB+XL04NOX0I8iWYsjlOJsAJShRdGJ02RuwzLVDbR95qIHq+JsfjZW4NXAF3x/ 3 X-Google-Smtp-Source: AGHT+IGXqDNfLHPrKsnNUOE0Jvc1N79/mptofbCYIi1Vilia1kAgwYbbGF9W9W12rXhMIq+pwPZkeQ== X-Received: by 2002:a05:6a21:4603:b0:1c3:ff33:2471 with SMTP id adf61e73a8af0-1c699579845mr5515093637.8.1722628716802; Fri, 02 Aug 2024 12:58:36 -0700 (PDT) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7106ecfc344sm1697180b3a.151.2024.08.02.12.58.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 02 Aug 2024 12:58:36 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger , Ori Kam , Aman Singh Subject: [PATCH v7 4/4] test-pmd: add more packet verbose decode options Date: Fri, 2 Aug 2024 12:56:55 -0700 Message-ID: <20240802195824.1336603-5-stephen@networkplumber.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240802195824.1336603-1-stephen@networkplumber.org> References: <20240312220129.70667-1-stephen@networkplumber.org> <20240802195824.1336603-1-stephen@networkplumber.org> MIME-Version: 1.0 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 The existing verbose levels 1..3 provide a messy multi-line output per packet. I found this unhelpful when diagnosing many types of problems like packet flow. This patch keeps the previous levels and adds two new levels: 4: one line per packet is printed in a format resembling tshark output. With addresses and protocol info. 5: dump packet in hex. Useful if the driver is messing up the data. Signed-off-by: Stephen Hemminger --- app/test-pmd/cmdline_flow.c | 3 +- app/test-pmd/config.c | 33 ++++++--- app/test-pmd/testpmd.h | 11 +++ app/test-pmd/util.c | 77 +++++++++++++++++++-- doc/guides/testpmd_app_ug/testpmd_funcs.rst | 5 +- 5 files changed, 111 insertions(+), 18 deletions(-) diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c index d04280eb3e..a010fcf61a 100644 --- a/app/test-pmd/cmdline_flow.c +++ b/app/test-pmd/cmdline_flow.c @@ -14143,7 +14143,8 @@ cmd_set_raw_parsed(const struct buffer *in) upper_layer = proto; } } - if (verbose_level & 0x1) + + if (verbose_level > 0) printf("total data size is %zu\n", (*total_size)); RTE_ASSERT((*total_size) <= ACTION_RAW_ENCAP_MAX_DATA); memmove(data, (data_tail - (*total_size)), *total_size); diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c index 6f0beafa27..b5b5f3b464 100644 --- a/app/test-pmd/config.c +++ b/app/test-pmd/config.c @@ -6246,26 +6246,37 @@ configure_rxtx_dump_callbacks(uint16_t verbose) return; #endif - RTE_ETH_FOREACH_DEV(portid) - { - if (verbose == 1 || verbose > 2) + RTE_ETH_FOREACH_DEV(portid) { + switch (verbose) { + case VERBOSE_OFF: + remove_rx_dump_callbacks(portid); + remove_tx_dump_callbacks(portid); + break; + case VERBOSE_RX: add_rx_dump_callbacks(portid); - else + remove_tx_dump_callbacks(portid); + break; + case VERBOSE_TX: + add_tx_dump_callbacks(portid); remove_rx_dump_callbacks(portid); - if (verbose >= 2) + break; + default: + add_rx_dump_callbacks(portid); add_tx_dump_callbacks(portid); - else - remove_tx_dump_callbacks(portid); + } } } void set_verbose_level(uint16_t vb_level) { - printf("Change verbose level from %u to %u\n", - (unsigned int) verbose_level, (unsigned int) vb_level); - verbose_level = vb_level; - configure_rxtx_dump_callbacks(verbose_level); + if (vb_level < VERBOSE_MAX) { + printf("Change verbose level from %u to %u\n", verbose_level, vb_level); + verbose_level = vb_level; + configure_rxtx_dump_callbacks(verbose_level); + } else { + fprintf(stderr, "Verbose level %u is out of range\n", vb_level); + } } void diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 9facd7f281..3d7a2b6dac 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -489,6 +489,17 @@ enum dcb_mode_enable extern uint8_t xstats_hide_zero; /**< Hide zero values for xstats display */ +enum verbose_mode { + VERBOSE_OFF = 0, + VERBOSE_RX, + VERBOSE_TX, + VERBOSE_BOTH, + VERBOSE_DISSECT, + VERBOSE_HEX, + VERBOSE_MAX +}; + + /* globals used for configuration */ extern uint8_t record_core_cycles; /**< Enables measurement of CPU cycles */ extern uint8_t record_burst_stats; /**< Enables display of RX and TX bursts */ diff --git a/app/test-pmd/util.c b/app/test-pmd/util.c index bf9b639d95..f277e7f035 100644 --- a/app/test-pmd/util.c +++ b/app/test-pmd/util.c @@ -5,9 +5,11 @@ #include +#include #include #include #include +#include #include #include #include @@ -16,6 +18,7 @@ #include "testpmd.h" #define MAX_STRING_LEN 8192 +#define MAX_DUMP_LEN 1024 #define MKDUMPSTR(buf, buf_size, cur_len, ...) \ do { \ @@ -67,9 +70,10 @@ get_timestamp(const struct rte_mbuf *mbuf) timestamp_dynfield_offset, rte_mbuf_timestamp_t *); } -static inline void -dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], - uint16_t nb_pkts, int is_rx) +/* More verbose older style packet decode */ +static void +dump_pkt_verbose(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], + uint16_t nb_pkts, int is_rx) { struct rte_mbuf *mb; const struct rte_ether_hdr *eth_hdr; @@ -90,8 +94,6 @@ dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], size_t cur_len = 0; uint64_t restore_info_dynflag; - if (!nb_pkts) - return; restore_info_dynflag = rte_flow_restore_info_dynflag(); MKDUMPSTR(print_buf, buf_size, cur_len, "port %u/queue %u: %s %u packets\n", port_id, queue, @@ -299,6 +301,71 @@ dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], } } +/* Brief tshark style one line output which is + * number time_delta Source Destination Protocol len info + */ +static void +dump_pkt_brief(uint16_t port, uint16_t queue, struct rte_mbuf *pkts[], uint16_t nb_pkts, int is_rx) +{ + static uint64_t start_cycles; + static RTE_ATOMIC(uint64_t) packet_count = 1; + uint64_t now; + uint64_t count; + double interval; + uint16_t i; + + now = rte_rdtsc(); + if (start_cycles == 0) { + start_cycles = now; + printf("Seq# Time Port:Que R Description\n"); + } + + interval = (double)(now - start_cycles) / (double)rte_get_tsc_hz(); + + count = rte_atomic_fetch_add_explicit(&packet_count, nb_pkts, rte_memory_order_relaxed); + + for (i = 0; i < nb_pkts; i++) { + const struct rte_mbuf *mb = pkts[i]; + char str[256]; + + rte_dissect_mbuf(str, sizeof(str), mb, 0); + printf("%6"PRIu64" %11.9f %4u:%-3u %c %s\n", + count + i, interval, port, queue, is_rx ? 'R' : 'T', str); + } +} + +/* Hex dump of packet data */ +static void +dump_pkt_hex(struct rte_mbuf *pkts[], uint16_t nb_pkts) +{ + uint16_t i; + + for (i = 0; i < nb_pkts; i++) + rte_pktmbuf_dump(stdout, pkts[i], MAX_DUMP_LEN); + +} + +static uint16_t +dump_pkt_burst(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], + uint16_t nb_pkts, int is_rx) +{ + if (unlikely(nb_pkts == 0)) + return 0; + + switch (verbose_level) { + case VERBOSE_RX ... VERBOSE_BOTH: + dump_pkt_verbose(port_id, queue, pkts, nb_pkts, is_rx); + break; + case VERBOSE_DISSECT: + dump_pkt_brief(port_id, queue, pkts, nb_pkts, is_rx); + break; + case VERBOSE_HEX: + dump_pkt_hex(pkts, nb_pkts); + } + fflush(stdout); + return nb_pkts; +} + uint16_t dump_rx_pkts(uint16_t port_id, uint16_t queue, struct rte_mbuf *pkts[], uint16_t nb_pkts, __rte_unused uint16_t max_pkts, diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst index f00ab07605..b9ce7698db 100644 --- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst +++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst @@ -677,7 +677,10 @@ Available levels are as following: * ``0`` silent except for error. * ``1`` fully verbose except for Tx packets. * ``2`` fully verbose except for Rx packets. -* ``> 2`` fully verbose. +* ``3`` fully verbose except for Tx and Rx packets. +* ``4`` dissected protocol information for Tx and Rx packets. +* ``5`` hex dump of packets + set log ~~~~~~~