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 1F0584568C; Tue, 23 Jul 2024 04:45:54 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 1413740E19; Tue, 23 Jul 2024 04:45:52 +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 770A9402C3 for ; Tue, 23 Jul 2024 04:45:48 +0200 (CEST) Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-70d333d5890so938146b3a.0 for ; Mon, 22 Jul 2024 19:45:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1721702747; x=1722307547; 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=ZPu4kQpUd8pQ+k1qAFRK8Pv39JcFz7fB9PNdBSTl444=; b=jf3NlNRmbd5btmX0oLrUmbYdjkLYVQ31s/NsAiP5RE3uOg45mjrzIc98Bc/fAPxG9J gfWUVbcYjYwRDRKxhUoKVuYK8oa51YwYw2WS3kPg1l8RDrAOIcunh4FsIxxKFvTP87NJ ezoLqg9FdyV1Xv6Ut9Vlxathtql/1gcIDzB6HJ4tIoogy2hQcj4Q8vUSg1VPxPG4KO/z Xn7lc6kXj4bONA62RaRFAQ5voTn/MpoSChghNbgMXfT8EsZEFvqFX7a/JHrdeBZErXyb G2ecYSpLTwshbEp6viGO7fOsz5d+Rka76rk/1lHa51qjrqx6AVfgpO111Z5UHTmbkHKt cTCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721702747; x=1722307547; 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=ZPu4kQpUd8pQ+k1qAFRK8Pv39JcFz7fB9PNdBSTl444=; b=kqPLucp4F+lzW1ob+FVNy8RsQL3XMEJiuAau1574GiAPr9Wsko2spDNh4Fa7Zd5v8Z iG+kAwojzKV0LOYZ4QKWo9lO3OJNu/TKutKWHMHxiEk/zBp0ucx7A9AzcrxaOPFPfxMg rc0zLCYfWhcnGeZNbjhWWa1FR+EK1W2Jvsb3nQnvBTTHCaBUfRT6qd61vpqAiLnmdNKH Dig48d7PSspxwqCgnmoJY6m7jfiz+OI42ZbFEFrXd1TNuXlRrhmL3htwuCcEtHGMfyJd DelLjfRHXy1EaQVn9m6ZSVYHAALcf5x1TIT1CIz6u7JVWr8tcwawJ+10uHNZPWUs/sXA KfXA== X-Gm-Message-State: AOJu0YzPlEHFs1rB53pRe+txIg1R2cCESfA576Qh+aL41BI6mGVrrPKM XmsW3YU3klhd5dvce/iAWLZZ+26O6Ul6sp9wtLHf77Z19ESPPwl041vb5o17DHfCqiNMBNeqUgz 2hac= X-Google-Smtp-Source: AGHT+IHrbpmJNn3OXOZM//+RXvcgo3mwLeTFa5npbg/BlTJ+EIB+ABntw7Q6W5qJP5w1Tw6cGpVuqQ== X-Received: by 2002:a05:6a20:a113:b0:1c0:eba5:e187 with SMTP id adf61e73a8af0-1c422847e65mr14326625637.6.1721702747453; Mon, 22 Jul 2024 19:45:47 -0700 (PDT) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2ccf808f05bsm7838924a91.40.2024.07.22.19.45.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Jul 2024 19:45:47 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Subject: [PATCH v2 1/3] net: add new packet dissector Date: Mon, 22 Jul 2024 19:44:10 -0700 Message-ID: <20240723024537.980016-2-stephen@networkplumber.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240723024537.980016-1-stephen@networkplumber.org> References: <20240312220129.70667-1-stephen@networkplumber.org> <20240723024537.980016-1-stephen@networkplumber.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 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 The function rte_dissect_mbuf is used to decode the contents of an mbuf and print them in human readable format similar to what tshark uses. For now, handles IP, IPv6, TCP, UDP, ICMP and ARP. The intention is to extend this to be able to handle more nested tunnel types as well as higher level protocols such as DNS and HTTP. Signed-off-by: Stephen Hemminger --- lib/net/meson.build | 2 + lib/net/rte_dissect.c | 357 ++++++++++++++++++++++++++++++++++++++++++ lib/net/rte_dissect.h | 40 +++++ lib/net/version.map | 7 + 4 files changed, 406 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..40376a8842 --- /dev/null +++ b/lib/net/rte_dissect.c @@ -0,0 +1,357 @@ +/* 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 +#include + +typedef struct dissect_ctx { + jmp_buf jmpenv; /* unwind when dump_len is reached */ + uint32_t offset; /* current offset */ + uint32_t dump_len; /* maximum depth in packet to look at */ +} dissect_ctx_t; + +static void +dissect_eth(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb); + +/* Read data from segmented mbuf, but stop if would go past max length */ +static const void * +dissect_read(dissect_ctx_t *ctx, const struct rte_mbuf *m, + void *buf, size_t len) +{ + /* If this header would be past the requested length + * then unwind back to end the string. + */ + if (ctx->dump_len != 0 && ctx->offset + len > ctx->dump_len) + longjmp(ctx->jmpenv, 1); + + return rte_pktmbuf_read(m, ctx->offset, len, buf); +} + +static void +dissect_arp(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_arp_hdr *arp; + struct rte_arp_hdr _arp; + uint16_t ar_op; + char buf[128]; + + arp = dissect_read(ctx, mb, &_arp, sizeof(_arp)); + if (unlikely(arp == NULL)) { + fprintf(f, "truncated ARP! "); + return; + } + + 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, buf, sizeof(buf)); + fprintf(f, "Who has %s? ", buf); + + rte_ether_format_addr(buf, sizeof(buf), &arp->arp_data.arp_sha); + fprintf(f, "Tell %s ", buf); + break; + case RTE_ARP_OP_REPLY: + inet_ntop(AF_INET, &arp->arp_data.arp_sip, buf, sizeof(buf)); + fprintf(f, "%s is at", buf); + + rte_ether_format_addr(buf, sizeof(buf), &arp->arp_data.arp_sha); + fprintf(f, "%s ", buf); + break; + case RTE_ARP_OP_INVREQUEST: + rte_ether_format_addr(buf, sizeof(buf), &arp->arp_data.arp_tha); + fprintf(f, "Who is %s? ", buf); + + rte_ether_format_addr(buf, sizeof(buf), &arp->arp_data.arp_sha); + fprintf(f, "Tell %s ", buf); + break; + + case RTE_ARP_OP_INVREPLY: + rte_ether_format_addr(buf, sizeof(buf), &arp->arp_data.arp_sha); + fprintf(f, "%s is at ", buf); + + inet_ntop(AF_INET, &arp->arp_data.arp_sip, buf, sizeof(buf)); + fprintf(f, "%s ", buf); + break; + default: + fprintf(f, "Unknown ARP %#x ", ar_op); + break; + } +} + +static void +dissect_vxlan(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_vxlan_hdr *vxlan; + struct rte_vxlan_hdr _vxlan; + + vxlan = dissect_read(ctx, mb, &_vxlan, sizeof(_vxlan)); + fprintf(f, "VXLAN "); + if (vxlan->flag_i) { + uint32_t vni = rte_be_to_cpu_32(vxlan->vx_vni); + + fprintf(f, "%#x ", vni >> 8); + } + dissect_eth(ctx, f, mb); +} + +static void +dissect_udp(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_udp_hdr *udph; + struct rte_udp_hdr _udp; + uint16_t src_port, dst_port; + + udph = dissect_read(ctx, mb, &_udp, sizeof(_udp)); + if (unlikely(udph == NULL)) { + fprintf(f, "truncated UDP! "); + return; + } + + src_port = rte_be_to_cpu_16(udph->src_port); + dst_port = rte_be_to_cpu_16(udph->dst_port); + + switch (dst_port) { + case RTE_VXLAN_DEFAULT_PORT: + dissect_vxlan(ctx, f, mb); + break; + default: + fprintf(f, "UDP %u %u → %u ", rte_be_to_cpu_16(udph->dgram_len), src_port, dst_port); + } +} + +static void +dissect_tcp(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_tcp_hdr *tcph; + struct rte_tcp_hdr _tcp; + uint16_t src_port, dst_port; + + tcph = dissect_read(ctx, mb, &_tcp, sizeof(_tcp)); + if (unlikely(tcph == NULL)) { + fprintf(f, "truncated TCP! "); + return; + } + + src_port = rte_be_to_cpu_16(tcph->src_port); + dst_port = rte_be_to_cpu_16(tcph->dst_port); + + fprintf(f, "TCP %u → %u", + src_port, dst_port); +#define PRINT_TCP_FLAG(flag) \ + if (tcph->tcp_flags & RTE_TCP_ ## flag ## _FLAG) \ + fprintf(f, " [" #flag" ]") + + PRINT_TCP_FLAG(URG); + PRINT_TCP_FLAG(ACK); + PRINT_TCP_FLAG(RST); + PRINT_TCP_FLAG(SYN); + PRINT_TCP_FLAG(FIN); +#undef PRINT_TCP_FLAG + + fprintf(f, "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)); +} + +static void +dissect_icmp(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_icmp_hdr *icmp; + struct rte_icmp_hdr _icmp; + static const char * const icmp_types[256] = { + [RTE_IP_ICMP_ECHO_REPLY] = "ICMP Reply", + [RTE_IP_ICMP_ECHO_REQUEST] = "ICMP Request", + [RTE_ICMP6_ECHO_REPLY] = "ICMPv6 Reply", + [RTE_ICMP6_ECHO_REQUEST] = "ICMPv6 Request", + [133] = "ICMPv6 Router Solicitation", + [134] = "ICMPv6 Router Solicitation", + }; + + icmp = dissect_read(ctx, mb, &_icmp, sizeof(_icmp)); + if (unlikely(icmp == NULL)) { + fprintf(f, "truncated ICMP! "); + } else { + const char *name = icmp_types[icmp->icmp_type]; + + if (name != NULL) + fprintf(f, "%s ", name); + else + fprintf(f, "ICMP type %u ", icmp->icmp_type); + } +} + +static void +dissect_ipv4(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_ipv4_hdr *ip_hdr; + struct rte_ipv4_hdr _ip_hdr; + char sbuf[INET_ADDRSTRLEN], dbuf[INET_ADDRSTRLEN]; + + ip_hdr = dissect_read(ctx, mb, &_ip_hdr, sizeof(_ip_hdr)); + if (unlikely(ip_hdr == NULL)) { + fprintf(f, "truncated IP! "); + return; + } + + inet_ntop(AF_INET, &ip_hdr->src_addr, sbuf, sizeof(sbuf)); + inet_ntop(AF_INET, &ip_hdr->dst_addr, dbuf, sizeof(dbuf)); + fprintf(f, "%s → %s ", sbuf, dbuf); + + ctx->offset += ip_hdr->ihl * 4; + switch (ip_hdr->next_proto_id) { + case IPPROTO_UDP: + return dissect_udp(ctx, f, mb); + case IPPROTO_TCP: + return dissect_tcp(ctx, f, mb); + case IPPROTO_ICMP: + return dissect_icmp(ctx, f, mb); + default: + /* TODO dissect tunnels */ + fprintf(f, "IP proto %#x ", ip_hdr->next_proto_id); + } +} + +static void +dissect_ipv6(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + 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; + + ip6_hdr = dissect_read(ctx, mb, &_ip6_hdr, sizeof(_ip6_hdr)); + if (unlikely(ip6_hdr == NULL)) { + fprintf(f, "truncated IPv6! "); + return; + } + ctx->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)); + fprintf(f, "%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: + return dissect_udp(ctx, f, mb); + case IPPROTO_TCP: + return dissect_tcp(ctx, f, mb); + case IPPROTO_ICMPV6: + return dissect_icmp(ctx, f, mb); + + 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(ctx, mb, &_xh, sizeof(xh)); + if (unlikely(xh == NULL)) { + fprintf(f, "truncated IPV6 option! "); + return; + } + ctx->offset += (xh->hdr_len + 1) * 8; + proto = xh->next_hdr; + continue; + } + + case IPPROTO_FRAGMENT: + fprintf(f, "FRAG "); + return; + + case IPPROTO_NONE: + fprintf(f, "NONE "); + return; + + default: + fprintf(f, "IPv6 proto %u ", proto); + return; + } + } + + fprintf(f, "Too many extensions!"); +} + +static void +dissect_eth(dissect_ctx_t *ctx, FILE *f, const struct rte_mbuf *mb) +{ + const struct rte_ether_hdr *eth_hdr; + struct rte_ether_hdr _eth_hdr; + uint16_t eth_type; + char sbuf[RTE_ETHER_ADDR_FMT_SIZE], dbuf[RTE_ETHER_ADDR_FMT_SIZE]; + + eth_hdr = dissect_read(ctx, mb, &_eth_hdr, sizeof(_eth_hdr)); + if (unlikely(eth_hdr == NULL)) { + fprintf(f, "missing Eth header!"); + return; + } + + ctx->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 + = (const struct rte_vlan_hdr *)(eth_hdr + 1); + eth_type = vh->eth_proto; + ctx->offset += sizeof(*vh); + + fprintf(f, "%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(sbuf, sizeof(dbuf), ð_hdr->dst_addr); + fprintf(f, "%s → %s ARP ", sbuf, dbuf); + + dissect_arp(ctx, f, mb); + break; + case RTE_ETHER_TYPE_IPV4: + dissect_ipv4(ctx, f, mb); + break; + + case RTE_ETHER_TYPE_IPV6: + dissect_ipv6(ctx, f, mb); + break; + default: + fprintf(f, "Ethernet proto %#x ", eth_type); + } +} + +void +rte_dissect_mbuf(FILE *f, const struct rte_mbuf *m, uint32_t dump_len) +{ + dissect_ctx_t ctx = { + .dump_len = dump_len, + }; + + if (setjmp(ctx.jmpenv) == 0) + dissect_eth(&ctx, f, m); + + putc('\n', f); +} diff --git a/lib/net/rte_dissect.h b/lib/net/rte_dissect.h new file mode 100644 index 0000000000..de14eeeb02 --- /dev/null +++ b/lib/net/rte_dissect.h @@ -0,0 +1,40 @@ +/* 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; + +/** + * + * Print packets in format (single line) similar to tshark to a file. + * + * @param f + * A pointer to a file for output + * @param m + * The packet mbuf. + * @param dump_len + * Maximum offset in packet to examine. + * If is zero then dump the whole packet. + */ +__rte_experimental +void +rte_dissect_mbuf(FILE *f, 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; +}; -- 2.43.0