diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:33 +0000 |
commit | 9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9 (patch) | |
tree | 2784370cda9bbf2da9114d70f05399c0b229d28c /epan/dissectors/packet-irdma.c | |
parent | Adding debian version 4.2.6-1. (diff) | |
download | wireshark-9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9.tar.xz wireshark-9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9.zip |
Merging upstream version 4.4.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-irdma.c')
-rw-r--r-- | epan/dissectors/packet-irdma.c | 1977 |
1 files changed, 1977 insertions, 0 deletions
diff --git a/epan/dissectors/packet-irdma.c b/epan/dissectors/packet-irdma.c new file mode 100644 index 00000000..70ef4df6 --- /dev/null +++ b/epan/dissectors/packet-irdma.c @@ -0,0 +1,1977 @@ +/* packet-irdma.c + * + * Routines for IBM i RDMA dissection + * Copyright 2018, 2024 IBM Corporation + * Brian Jongekryg (bej@us.ibm.com, bej@arbin.net) + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * Dissector for IBM i base RDMA frame traffic + * captured via TRCCNN TYPE(*RDMA) command + * + * Subdissectors for IBM i RDMA endoint traffic can be registered + * by calling + * dissector_add_uint("irdma.ep.port", port, handle); + */ + +#include <config.h> + +#include <string.h> + +#include <epan/packet.h> /* Should be first Wireshark include (other than config.h) */ +#include <epan/conversation.h> +#include <epan/proto_data.h> +#include <epan/expert.h> +#include <epan/prefs.h> +#include <epan/addr_resolv.h> +#include <wsutil/utf8_entities.h> + +#include <packet-irdma.h> + +/* irdmaep conversation and packet analysis data */ +typedef struct irdmaep_buffer +{ + /* Size of buffer */ + uint32_t size; + + /* Offset of first byte available for use */ + uint32_t offset; + + /* Last data sequence number in buffer */ + uint32_t seq_num; + + /* Contents of buffer unknown */ + bool indeterminate; + +} irdmaep_buffer_t; + +#define IRDMAEP_MAX_DATA_BUFID 2 +typedef struct irdmaep_flow +{ + /* Last sent DATA sequence */ + bool data_seq_valid; + uint32_t data_seq; + + /* Last sent USER_RDMA data (RECVACK) ID, offset */ + bool recvack_valid; + uint32_t recvack_id; + uint32_t recvack_offset; + + /* Receive buffer status */ + /* Number of receive buffers */ + uint32_t recv_buffer_count; + + /* Most recent buffer used by sender */ + uint32_t recv_mrb; + + /* Calculated buffer size estimate */ + uint32_t recv_min_size; + + /* Track up to two remote receive buffers */ + irdmaep_buffer_t recv_buffer[IRDMAEP_MAX_DATA_BUFID]; + +} irdmaep_flow_t; + +typedef struct irdmaep_analysis +{ + /* Trace status for both directions of the flow. + Info for sends with smaller src port is recorded in flow1, + larger src port in flow2. */ + irdmaep_flow_t flow1; + irdmaep_flow_t flow2; + + /* Forward or reverse flow info based on current packet ports */ + irdmaep_flow_t *fwd_flow, *rev_flow; + + /* Server port number from CONNREQ or CONNACK to help determine + which payload dissector to call. */ + uint32_t server_port; + + /* Keep track of RDMA endpoint stream numbers */ + uint32_t stream; + + /* Track whether conversation was closed, so we know to start + new conversation if CONNREQ/ACK seen. */ + bool closed; + + /* Current link sequence number */ + uint32_t link; + + /* Link switch timestamps */ + nstime_t movereq_time; + + /* Conversation timestamps */ + nstime_t ts_first; + nstime_t ts_prev; + +} irdmaep_analysis_t; + +typedef struct irdmaep_packet_analysis +{ + /* Retransmitted packet? */ + bool retransmission; + + /* Out-of-order sequence (should never occur) */ + bool out_of_order; + + /* Bytes remaining available for sending/receiving */ + uint32_t rbuf_available; + + /* Bytes remaining in current/active receive buffer */ + uint32_t rbuf_cur; + + /* Max bytes available for next send */ + uint32_t rbuf_max; + + /* Is rbuf_available estimated or true value */ + bool rbuf_estimated; + + /* Does packet sequence indicate this is stale (prior link) */ + bool seq_stale; + + /* First packet on new link */ + bool linkswt; + + /* Time to move link */ + nstime_t movelink_time; + + /* Delta time from previous packet */ + nstime_t delta_time; + +} irdmaep_packet_analysis_t; + +/* Prototypes */ +void proto_reg_handoff_irdma(void); +void proto_register_irdma(void); + +static int dissect_irdma(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); +static int dissect_irdmaqp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); +static int dissect_irdmalink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); +static int dissect_irdmaep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); +static void dissect_data_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void irdmaep_add_rbuf_tree(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_packet_analysis_t *eppd); +static void dissect_reuse_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_user_recv_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_user_send_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_connect_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_close_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_buffer_msg_mkeyaddr(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_buffer_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_move_link_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_move_link_ack_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_move_link_cmp_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree); +static void dissect_irdmaep_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_pdata_t *data); +static irdmaep_analysis_t *init_irdmaep_conversation_data(packet_info *pinfo); +static void analyze_irdmaep_rbuffer(irdmaep_flow_t *flow, + irdmaep_packet_analysis_t *eppd); +static void *add_eppd(packet_info *pinfo); +static void *get_eppd(packet_info *pinfo); +static void *get_or_add_eppd(packet_info *pinfo); + +/* Initialize the protocol and registered fields */ +static int proto_irdma; +static int proto_irdmaqp; +static int proto_irdmalink; +static int proto_irdmaep; + +static int hf_irdma_hwdst; +static int hf_irdma_hwsrc; +static int hf_irdma_qpindex; +static int hf_irdma_ip6src; +static int hf_irdma_ip6dst; +static int hf_irdma_ip4src; +static int hf_irdma_ip4dst; +static int hf_irdma_hwaddr; +static int hf_irdma_ip6addr; +static int hf_irdma_ip4addr; + +static int hf_irdmaqp_type; +static int hf_irdmaqp_id; + +static int hf_irdmalink_type; +static int hf_irdmalink_groups; + +static int hf_irdmaep_type; +static int hf_irdmaep_len; +static int hf_irdmaep_grpid; +static int hf_irdmaep_grpid_ctr; +static int hf_irdmaep_grpid_time; +static int hf_irdmaep_grpid_hwaddr; +static int hf_irdmaep_srcport; +static int hf_irdmaep_dstport; +static int hf_irdmaep_port; +static int hf_irdmaep_stream; +static int hf_irdmaep_linkseq; +static int hf_irdmaep_sndbufsize; +static int hf_irdmaep_usrblksize; +static int hf_irdmaep_reason; +static int hf_irdmaep_rbuf; +static int hf_irdmaep_rkey; +static int hf_irdmaep_raddr; +static int hf_irdmaep_flags; +static int hf_irdmaep_flag_buf0_free; +static int hf_irdmaep_flag_buf1_free; +static int hf_irdmaep_rcvbufsize; +static int hf_irdmaep_sendseq; +static int hf_irdmaep_recvseq0; +static int hf_irdmaep_recvseq1; +static int hf_irdmaep_usndsent; +static int hf_irdmaep_usndid; +static int hf_irdmaep_urcvid; +static int hf_irdmaep_seqnum; +static int hf_irdmaep_bufid; +static int hf_irdmaep_offset; +static int hf_irdmaep_datalen; +static int hf_irdmaep_ulength; +static int hf_irdmaep_blength; +static int hf_irdmaep_recvid; +static int hf_irdmaep_rbufavail; +static int hf_irdmaep_rbufactive; +static int hf_irdmaep_rbufmax; +static int hf_irdmaep_move1; +static int hf_irdmaep_move2; +static int hf_irdmaep_ts_relative; +static int hf_irdmaep_ts_delta; + +static expert_field ei_irdmaep_analysis_stale; +static expert_field ei_irdmaep_analysis_dup; +static expert_field ei_irdmaep_analysis_oos; +static expert_field ei_irdmaep_connection_req; +static expert_field ei_irdmaep_connection_ack; +static expert_field ei_irdmaep_connection_lswt; + +/* Initialize the subtree pointers */ +static int ett_irdma; +static int ett_irdmaqp; +static int ett_irdmalink; +static int ett_irdmaep; +static int ett_irdmaep_grpid; +static int ett_irdmaep_rbuf; +static int ett_irdmaep_bufflags; +static int ett_irdmaep_rcvbuf_analyze; +static int ett_irdmaep_timestamps; + +static dissector_table_t irdmaep_dissector_table = NULL; + +static dissector_handle_t irdmaqp_handle; +static dissector_handle_t irdmalink_handle; +static dissector_handle_t irdmaep_handle; + +static uint32_t irdmaep_stream_count; + +/* Data passed from iRDMA dissector to QP, Link, Endpoint dissectors */ +typedef struct +{ + uint32_t qpindex; +} irdma_data_t; + +/* Miscellaneous utility functions */ +static inline bool +seq16_lt(uint16_t a, uint16_t b) +{ + return (int16_t) (a - b) < 0; +} + +static inline bool +seq16_gt(uint16_t a, uint16_t b) +{ + return (int16_t) (a - b) > 0; +} + +static inline bool +is_v4mapped(ws_in6_addr *addr) +{ + static uint8_t mapped[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF}; + return memcmp(addr, mapped, 12) == 0; +} + +static inline bool +is_v4compat(ws_in6_addr *addr) +{ + static uint8_t zeroes[12] = {0}; + return memcmp(addr, zeroes, 12) == 0 + && memcmp(&addr->bytes[12], zeroes, 4) != 0; +} + +static const value_string irdma_serv_names[] = { + {930, "mrdb-engine"}, + {931, "mrdb-objrep"}, + {932, "mrdb-objrepr"}, + {940, "ifs-mfs"}, + {946, "drda-ddm"}, + {0, NULL}}; + +static inline const char * +irdma_serv_name_lookup(unsigned port) +{ + return try_val_to_str(port, irdma_serv_names); +} + +static inline void +irdma_col_snprint_port(char *buf, unsigned long buf_siz, uint16_t val) +{ + const char *str; + + if (gbl_resolv_flags.transport_name && + (str = irdma_serv_name_lookup(val)) != NULL) { + snprintf(buf, buf_siz, "%s(%" PRIu16 ")", str, val); + } else { + snprintf(buf, buf_siz, "%" PRIu16, val); + } +} + +static void +irdma_col_append_ports(column_info *cinfo, const int col, uint16_t src, uint16_t dst) +{ + char buf_src[32], buf_dst[32]; + + irdma_col_snprint_port(buf_src, 32, src); + irdma_col_snprint_port(buf_dst, 32, dst); + col_append_lstr(cinfo, col, buf_src, " " UTF8_RIGHTWARDS_ARROW " ", buf_dst, COL_ADD_LSTR_TERMINATOR); +} + +static void +irdma_custom_format_port(char *str, uint32_t port) +{ + irdma_col_snprint_port(str, ITEM_LABEL_LENGTH, port); +} + +static void +irdma_custom_format_rbufsize(char *str, uint32_t bufsize) +{ + bufsize &= 0xFFF; + uint32_t kb_bufsize = bufsize * 4; + snprintf(str, ITEM_LABEL_LENGTH, "%" PRIu32 " (%" PRIu32 " KB)", bufsize, kb_bufsize); +} + +static void +irdma_init(void) +{ + irdmaep_stream_count = 0; +} + +/*********************************************************************/ +/*********************************************************************/ +/* RDMA pseudo-header data and functions */ +/*********************************************************************/ +/*********************************************************************/ + +/* IBM i RDMA pseudo-header from TRCCNN offsets */ +#define IRDMA_HDR_DST 0 +#define IRDMA_HDR_SRC 6 +#define IRDMA_HDR_QPINDEX 12 +#define IRDMA_HDR_SRCIP 14 +#define IRDMA_HDR_SRCIP4 26 +#define IRDMA_HDR_DSTIP 30 +#define IRDMA_HDR_DSTIP4 42 +#define IRDMA_HDR_LENGTH 46 + +/* Payload types for IBM i RDMA frames */ +#define IRDMA_PROTO_ENDPOINT 0 /* 0b */ +#define IRDMA_PROTO_QP 4 /* 100b */ +#define IRDMA_PROTO_LINK 5 /* 101b */ + +/* Dissect IBM i RDMA pseudo-header */ +static int +dissect_irdma(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti; + proto_item *irdma_item; + proto_tree *irdma_tree; + /* Other misc. local variables. */ + ws_in6_addr ipsrc; + bool ipv4 = false; + char *src_str, *dst_str; + irdma_data_t irdma_data = {0}; + dissector_handle_t subdissector_handle = NULL; + + /*** HEURISTICS ***/ + + /* First, if at all possible, do some heuristics to check if the packet + * cannot possibly belong to your protocol. This is especially important + * for protocols directly on top of TCP or UDP where port collisions are + * common place (e.g., even though your protocol uses a well known port, + * someone else may set up, for example, a web server on that port which, + * if someone analyzed that web server's traffic in Wireshark, would result + * in Wireshark handing an HTTP packet to your dissector). + * + * For example: + */ + + /* Check that the packet is long enough for it to belong to us. */ + if (tvb_reported_length(tvb) < IRDMA_HDR_LENGTH) + return 0; + + /* Check that the packet has a recognizable frame type. This probably + won't eliminate many bad packets. */ + uint8_t ftype = tvb_get_bits8(tvb, IRDMA_HDR_LENGTH * 8, 3); + if (ftype > 5) + return 0; + + /*** COLUMN DATA ***/ + + /* There are two normal columns to fill in: the 'Protocol' column which + * is narrow and generally just contains the constant string 'irdma', + * and the 'Info' column which can be much wider and contain misc. summary + * information (for example, the port number for TCP packets). + * + * If you are setting the column to a constant string, use "col_set_str()", + * as it's more efficient than the other "col_set_XXX()" calls. + * + * If + * - you may be appending to the column later OR + * - you have constructed the string locally OR + * - the string was returned from a call to val_to_str() + * then use "col_add_str()" instead, as that takes a copy of the string. + * + * The function "col_add_fstr()" can be used instead of "col_add_str()"; it + * takes "printf()"-like arguments. Don't use "col_add_fstr()" with a format + * string of "%s" - just use "col_add_str()" or "col_set_str()", as it's + * more efficient than "col_add_fstr()". + * + * For full details see section 1.4 of README.dissector. + */ + + /* Set the Protocol column to the constant string of irdma */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDMA"); + + /* Use first 3 bits beyond header to determine what type of frame this is + (QP management, Link Management, Endpoint) */ + if (ftype == IRDMA_PROTO_QP) + { + col_set_str(pinfo->cinfo, COL_INFO, "QP Management"); + subdissector_handle = irdmaqp_handle; + } + else + if (ftype == IRDMA_PROTO_LINK) + { + col_set_str(pinfo->cinfo, COL_INFO, "Link Management"); + subdissector_handle = irdmalink_handle; + } + else + { + col_set_str(pinfo->cinfo, COL_INFO, "RDMA Endpoint"); + subdissector_handle = irdmaep_handle; + } + + /*** PROTOCOL TREE ***/ + + /* Now we will create a sub-tree for our protocol and start adding fields + * to display under that sub-tree. Most of the time the only functions you + * will need are proto_tree_add_item() and proto_item_add_subtree(). + * + * NOTE: The offset and length values in the call to proto_tree_add_item() + * define what data bytes to highlight in the hex display window when the + * line in the protocol tree display corresponding to that item is selected. + * + * Supplying a length of -1 tells Wireshark to highlight all data from the + * offset to the end of the packet. + */ + + /* create display subtree for the protocol */ + irdma_item = proto_tree_add_item(tree, proto_irdma, tvb, 0, IRDMA_HDR_LENGTH, ENC_NA); + irdma_tree = proto_item_add_subtree(irdma_item, ett_irdma); + + set_address_tvb(&pinfo->dl_dst, AT_ETHER, 6, tvb, IRDMA_HDR_DST); + set_address_tvb(&pinfo->dl_src, AT_ETHER, 6, tvb, IRDMA_HDR_SRC); + + proto_tree_add_item(irdma_tree, hf_irdma_hwdst, tvb, IRDMA_HDR_DST, 6, ENC_NA); + ti = proto_tree_add_item(irdma_tree, hf_irdma_hwaddr, tvb, IRDMA_HDR_DST, 6, ENC_NA); + PROTO_ITEM_SET_HIDDEN(ti); + + proto_tree_add_item(irdma_tree, hf_irdma_hwsrc, tvb, IRDMA_HDR_SRC, 6, ENC_NA); + ti = proto_tree_add_item(irdma_tree, hf_irdma_hwaddr, tvb, IRDMA_HDR_SRC, 6, ENC_NA); + PROTO_ITEM_SET_HIDDEN(ti); + + proto_tree_add_item_ret_uint(irdma_tree, hf_irdma_qpindex, tvb, + IRDMA_HDR_QPINDEX, 2, ENC_BIG_ENDIAN, + &irdma_data.qpindex); + + /* Get source IP address and determine if we've got an IPv4 or IPv6 address */ + tvb_get_ipv6(tvb, IRDMA_HDR_SRCIP, &ipsrc); + ipv4 = is_v4mapped(&ipsrc) || is_v4compat(&ipsrc); + + if (ipv4) + { + set_address_tvb(&pinfo->net_dst, AT_IPv4, 4, tvb, IRDMA_HDR_DSTIP4); + copy_address_shallow(&pinfo->dst, &pinfo->net_dst); + + set_address_tvb(&pinfo->net_src, AT_IPv4, 4, tvb, IRDMA_HDR_SRCIP4); + copy_address_shallow(&pinfo->src, &pinfo->net_src); + + src_str = tvb_address_with_resolution_to_str(wmem_packet_scope(), tvb, AT_IPv4, IRDMA_HDR_SRCIP4); + dst_str = tvb_address_with_resolution_to_str(wmem_packet_scope(), tvb, AT_IPv4, IRDMA_HDR_DSTIP4); + + proto_tree_add_item(irdma_tree, hf_irdma_ip4src, tvb, IRDMA_HDR_SRCIP4, 4, ENC_BIG_ENDIAN); + ti = proto_tree_add_item(irdma_tree, hf_irdma_ip4addr, tvb, IRDMA_HDR_SRCIP4, 4, ENC_BIG_ENDIAN); + PROTO_ITEM_SET_HIDDEN(ti); + + proto_tree_add_item(irdma_tree, hf_irdma_ip4dst, tvb, IRDMA_HDR_DSTIP4, 4, ENC_BIG_ENDIAN); + ti = proto_tree_add_item(irdma_tree, hf_irdma_ip4addr, tvb, IRDMA_HDR_DSTIP4, 4, ENC_BIG_ENDIAN); + PROTO_ITEM_SET_HIDDEN(ti); + } + else + { + set_address_tvb(&pinfo->net_dst, AT_IPv6, 16, tvb, IRDMA_HDR_DSTIP); + copy_address_shallow(&pinfo->dst, &pinfo->net_dst); + + set_address_tvb(&pinfo->net_src, AT_IPv6, 16, tvb, IRDMA_HDR_SRCIP); + copy_address_shallow(&pinfo->src, &pinfo->net_src); + + src_str = tvb_address_with_resolution_to_str(wmem_packet_scope(), tvb, AT_IPv6, IRDMA_HDR_SRCIP); + dst_str = tvb_address_with_resolution_to_str(wmem_packet_scope(), tvb, AT_IPv6, IRDMA_HDR_DSTIP); + + proto_tree_add_item(irdma_tree, hf_irdma_ip6src, tvb, IRDMA_HDR_SRCIP, 16, ENC_NA); + ti = proto_tree_add_item(irdma_tree, hf_irdma_ip6addr, tvb, IRDMA_HDR_SRCIP, 16, ENC_NA); + PROTO_ITEM_SET_HIDDEN(ti); + + proto_tree_add_item(irdma_tree, hf_irdma_ip6dst, tvb, IRDMA_HDR_DSTIP, 16, ENC_NA); + ti = proto_tree_add_item(irdma_tree, hf_irdma_ip6addr, tvb, IRDMA_HDR_DSTIP, 16, ENC_NA); + PROTO_ITEM_SET_HIDDEN(ti); + } + + proto_item_append_text(irdma_item, ", Src: %s", src_str); + proto_item_append_text(irdma_item, ", Dst: %s", dst_str); + + call_dissector_with_data(subdissector_handle, + tvb_new_subset_remaining(tvb, IRDMA_HDR_LENGTH), + pinfo, tree, &irdma_data); + + /* Return the amount of data this dissector was able to dissect (which may + * or may not be the total captured packet as we return here). */ + return tvb_captured_length(tvb); +} + +/*********************************************************************/ +/*********************************************************************/ +/* RDMA QP data and functions */ +/*********************************************************************/ +/*********************************************************************/ +/* IBM i RDMA QP message types */ +#define IRDMA_QP_ECHOREQ 0x8000 +#define IRDMA_QP_ECHORSP 0x8001 + +/* IBM i RDMA QP message offsets */ +#define IRDMA_QP_TYPE 0 /* 16-bit message type */ +#define IRDMA_QP_ECHO_ID 2 /* 16-bit Echo req/rsp ID */ + +static const value_string irdmaqp_type_str[] = { + {IRDMA_QP_ECHOREQ, "Echo request"}, + {IRDMA_QP_ECHORSP, "Echo response"}, + {0, NULL}}; + +/* Dissect RDMA QP packets */ +static int +dissect_irdmaqp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + proto_item *ti; + proto_tree *qp_tree; + + irdma_data_t *irdma_data = (irdma_data_t *) data; + + if (tvb_reported_length(tvb) < 2) + return 0; + + /* Set the Protocol column */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDMA-QP"); + + uint16_t msg_type = tvb_get_ntohs(tvb, IRDMA_QP_TYPE); + const char *msg_type_str = val_to_str(msg_type, irdmaqp_type_str, + "Unknown message (0x%04X)"); + + col_add_fstr(pinfo->cinfo, COL_INFO, + "%-16s qp=%2" PRIu32, msg_type_str, irdma_data->qpindex); + + /* Create display subtree for the QP message */ + ti = proto_tree_add_item(tree, proto_irdmaqp, tvb, 0, -1, ENC_NA); + qp_tree = proto_item_add_subtree(ti, ett_irdmaqp); + + proto_tree_add_item(qp_tree, hf_irdmaqp_type, tvb, IRDMA_QP_TYPE, 2, + ENC_BIG_ENDIAN); + + switch (msg_type) + { + case IRDMA_QP_ECHOREQ: + case IRDMA_QP_ECHORSP: + { + uint32_t echo_id; + proto_tree_add_item_ret_uint(qp_tree, hf_irdmaqp_id, tvb, + IRDMA_QP_ECHO_ID, 2, + ENC_BIG_ENDIAN, &echo_id); + col_append_fstr(pinfo->cinfo, COL_INFO, ", id=%" PRIu32, echo_id); + break; + } + } + + return tvb_captured_length(tvb); +} + +/*********************************************************************/ +/*********************************************************************/ +/* RDMA Link data and functions */ +/*********************************************************************/ +/*********************************************************************/ +/* IBM i RDMA Link message types */ +#define IRDMA_LINK_STATUS 0xA000 + +/* IBM i RDMA Link message offsets */ +#define IRDMA_LINK_TYPE 0 /* 16-bit message type */ +#define IRDMA_LINK_STATUS_GROUPS 2 /* 16-bit count of active groups */ + +static const value_string irdmalink_type_str[] = { + {IRDMA_LINK_STATUS, "Link Status"}, + {0, NULL}}; + +/* Dissect RDMA Link packets */ +static int +dissect_irdmalink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *ti; + proto_tree *link_tree; + + if (tvb_reported_length(tvb) < 2) + return 0; + + /* Set the Protocol column */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDMA-Link"); + + uint16_t msg_type = tvb_get_ntohs(tvb, IRDMA_LINK_TYPE); + const char *msg_type_str = val_to_str(msg_type, irdmalink_type_str, + "Unknown message (0x%04X)"); + + col_add_str(pinfo->cinfo, COL_INFO, msg_type_str); + + /* Create display subtree for the Link message */ + ti = proto_tree_add_item(tree, proto_irdmalink, tvb, 0, -1, ENC_NA); + link_tree = proto_item_add_subtree(ti, ett_irdmalink); + + proto_tree_add_item(link_tree, hf_irdmalink_type, tvb, IRDMA_LINK_TYPE, 2, + ENC_BIG_ENDIAN); + + switch (msg_type) + { + case IRDMA_LINK_STATUS: + { + uint32_t groups; + proto_tree_add_item_ret_uint(link_tree, hf_irdmalink_groups, tvb, + IRDMA_LINK_STATUS_GROUPS, 2, + ENC_BIG_ENDIAN, &groups); + col_append_fstr(pinfo->cinfo, COL_INFO, ", groups=%" PRIu32, groups); + break; + } + } + + return tvb_captured_length(tvb); +} + +/*********************************************************************/ +/*********************************************************************/ +/* RDMA Endpoint data and functions */ +/*********************************************************************/ +/*********************************************************************/ +/* IBM i RDMA Endpoint preferences */ +static bool irdmaep_calculate_ts = true; +static bool irdmaep_no_subdissector_on_error = true; + +/* IBM i RDMA Endpoint message types */ +#define IRDMA_EP_CONNREQ 0x01 +#define IRDMA_EP_CONNACK 0x02 +#define IRDMA_EP_CONNBUF 0x03 +#define IRDMA_EP_RNEGREQ 0x04 +#define IRDMA_EP_RNEGACK 0x05 +#define IRDMA_EP_CLOSE 0x06 +#define IRDMA_EP_RESET 0x09 +#define IRDMA_EP_MOVEREQ 0x11 +#define IRDMA_EP_MOVEACK 0x12 +#define IRDMA_EP_MOVECMP 0x13 +#define IRDMA_EP_DATA 0x21 +#define IRDMA_EP_REUSEBUF 0x22 +#define IRDMA_EP_RECVREQ 0x23 +#define IRDMA_EP_RECVACK 0x24 +#define IRDMA_EP_RECVNAK 0x25 + +/* IBM i RDMA Endpoint message offsets */ +#define IRDMA_EP_TYPE 0 /* ( 1) Message type */ +#define IRDMA_EP_LEN 1 /* ( 1) Message length */ +#define IRDMA_EP_GRPID 2 /* (12) Group ID */ +#define IRDMA_EP_GRPID_CTR 2 /* ( 1) Uniqueness counter */ +#define IRDMA_EP_GRPID_TIME 3 /* ( 5) Bits 4-43 of TOD */ +#define IRDMA_EP_GRPID_MAC 8 /* ( 6) MAC address */ +#define IRDMA_EP_SPORT 14 /* ( 2) Source port */ +#define IRDMA_EP_DPORT 16 /* ( 2) Destination port */ +#define IRDMA_EP_LINKSEQ 18 /* ( 2) Link sequence number */ +#define IRDMA_EP_MIN_LENGTH 20 + +#define IRDMA_EP_CONNECT_BUFSIZE 20 /* ( 2) Send buffer size (* 4KB) */ +#define IRDMA_EP_CONNECT_USRBLKSIZE 22 /* ( 2) USER_RDMA receive block size (* 4KB) */ +#define IRDMA_EP_CONNECT_MIN_LENGTH 24 + +#define IRDMA_EP_CLOSE_REASON 20 /* ( 4) Close reason */ +#define IRDMA_EP_CLOSE_MIN_LENGTH 24 + +#define IRDMA_EP_BUFFER_RKEY 20 /* ( 4) x2 Receive buffer MKeys */ +#define IRDMA_EP_BUFFER_RADDR 28 /* ( 8) x2 Receive buffer addresses */ +#define IRDMA_EP_BUFFER_FLAGS 44 /* ( 4 bits) Flags */ +#define IRDMA_EP_BUFFER_RBUFSIZE 44 /* (12 bits) Size of each receive buffer (* 4KB) */ +#define IRDMA_EP_MOVELINK_SENDSEQ 46 /* ( 2) Last sent sequence number */ +#define IRDMA_EP_MOVELINK_RBUFSEQ 48 /* ( 2) x2 Last received sequence number */ +#define IRDMA_EP_MOVELINK_USNDSENT 52 /* ( 4) Number of bytes sent from current USER_RDMA */ +#define IRDMA_EP_MOVELINK_USNDID 56 /* ( 2) Current USER_RDMA recv ID being sent */ +#define IRDMA_EP_MOVELINKACK_URCVID 58 /* ( 2) Last USER_RDMA recv ID complete */ + +#define IRDMA_EP_MOVELINKCMP_URCVID 20 /* ( 2) Last USER_RDMA recv ID complete */ + +#define IRDMA_EP_DATA_SEQNUM 20 /* ( 2) Sequence number */ +#define IRDMA_EP_DATA_BUFID 22 /* ( 2) Buffer index */ +#define IRDMA_EP_DATA_OFFSET 24 /* ( 4) Buffer offset */ +#define IRDMA_EP_DATA_DATALEN 28 /* ( 4) Data length */ +#define IRDMA_EP_DATA_MSG_LEN 32 + +#define IRDMA_EP_REUSE_SEQNUM 20 /* ( 2) Sequence number */ +#define IRDMA_EP_REUSE_BUFID 22 /* ( 2) Buffer index */ + +#define IRDMA_EP_USERRECV_ULENGTH 20 /* ( 4) Total user receive length */ +#define IRDMA_EP_USERRECV_OFFSET 24 /* ( 4) Offset within full buffer */ +#define IRDMA_EP_USERRECV_RLENGTH 28 /* ( 4) Buffer length */ +#define IRDMA_EP_USERRECV_RECVID 32 /* ( 2) Receive identifier */ +#define IRDMA_EP_USERRECV_RKEY 34 /* ( 4) Buffer MKey */ +#define IRDMA_EP_USERRECV_RADDR 38 /* ( 8) Buffer address */ +#define IRDMA_EP_USERRECV_MSG_LEN 46 + +#define IRDMA_EP_USERSEND_ULENGTH 20 /* ( 4) Total user send length */ +#define IRDMA_EP_USERSEND_OFFSET 24 /* ( 4) Offset within full buffer */ +#define IRDMA_EP_USERSEND_SLENGTH 28 /* ( 4) Length sent */ +#define IRDMA_EP_USERSEND_RECVID 32 /* ( 2) Receive identifier */ +#define IRDMA_EP_USERSEND_MSG_LEN 34 + +static const value_string irdmaep_type_str[] = { + {IRDMA_EP_CONNREQ, "CONNREQ"}, + {IRDMA_EP_CONNACK, "CONNACK"}, + {IRDMA_EP_CONNBUF, "CONNBUF"}, + {IRDMA_EP_RNEGREQ, "RNEGREQ"}, + {IRDMA_EP_RNEGACK, "RNEGACK"}, + {IRDMA_EP_CLOSE, "CLOSE"}, + {IRDMA_EP_RESET, "RESET"}, + {IRDMA_EP_MOVEREQ, "MOVEREQ"}, + {IRDMA_EP_MOVEACK, "MOVEACK"}, + {IRDMA_EP_MOVECMP, "MOVECMP"}, + {IRDMA_EP_DATA, "DATA"}, + {IRDMA_EP_REUSEBUF, "REUSEBUF"}, + {IRDMA_EP_RECVREQ, "RECVREQ"}, + {IRDMA_EP_RECVACK, "RECVACK"}, + {IRDMA_EP_RECVNAK, "RECVNAK"}, + {0, NULL}}; + +static const value_string vals_free_inuse[] = { + {0, "In-use"}, + {1, "Free"}, + {0, NULL}}; + +/* Dissect RDMA Endpoint packets */ +static int +dissect_irdmaep(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *ti, *ti_seq, *ti_type; + proto_tree *ep_tree, *ts_tree; + proto_tree *grpid_tree; + address group_addr; + conversation_t *conv = NULL; + irdmaep_analysis_t *epd = NULL; + irdmaep_packet_analysis_t *eppd = NULL; + uint32_t linkseq; + nstime_t ts; + + if (tvb_reported_length(tvb) < IRDMA_EP_MIN_LENGTH) + return 0; + + /* Set the Protocol column */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDMA-EP"); + + col_clear(pinfo->cinfo, COL_INFO); + + pinfo->srcport = tvb_get_ntohs(tvb, IRDMA_EP_SPORT); + pinfo->destport = tvb_get_ntohs(tvb, IRDMA_EP_DPORT); + pinfo->ptype = PT_TCP; /* Not really TCP, but nothing else better */ + irdma_col_append_ports(pinfo->cinfo, COL_INFO, + pinfo->srcport, pinfo->destport); + + set_address(&group_addr, AT_STRINGZ, 25, + tvb_bytes_to_str(wmem_packet_scope(), tvb, IRDMA_EP_GRPID, 12)); + + uint8_t msg_type = tvb_get_uint8(tvb, IRDMA_EP_TYPE); + uint8_t msg_length = tvb_get_uint8(tvb, IRDMA_EP_LEN); + const char *msg_type_str = val_to_str(msg_type, irdmaep_type_str, + "UNKNOWN (0x%02X)"); + col_append_fstr(pinfo->cinfo, COL_INFO, " [%s]", msg_type_str); + + /* Find or create conversation for this packet */ + conv = find_conversation(pinfo->num, &group_addr, &group_addr, + CONVERSATION_NONE, pinfo->srcport, pinfo->destport, 0); + + /* If first time through the packets and conversation exists but message + is a CONNREQ, check if we really should be starting a new conversation + instead */ + if (!PINFO_FD_VISITED(pinfo) + && conv + && msg_type == IRDMA_EP_CONNREQ) + { + epd = (irdmaep_analysis_t *) conversation_get_proto_data(conv, proto_irdmaep); + if (epd && epd->closed) + conv = NULL; + } + + /* If this is a new conversation, allocate conversation data */ + if (!conv) + { + conv = conversation_new(pinfo->num, &group_addr, &group_addr, + CONVERSATION_NONE, pinfo->srcport, pinfo->destport, 0); + epd = init_irdmaep_conversation_data(pinfo); + conversation_add_proto_data(conv, proto_irdmaep, epd); + } + else + epd = (irdmaep_analysis_t *) conversation_get_proto_data(conv, proto_irdmaep); + + if (pinfo->num > conv->last_frame) + conv->last_frame = pinfo->num; + + epd->fwd_flow = &epd->flow1; + epd->rev_flow = &epd->flow2; + if (pinfo->srcport < pinfo->destport) + { + epd->fwd_flow = &epd->flow2; + epd->rev_flow = &epd->flow1; + } + + /* Create display subtree for the Endpoint message */ + ti = proto_tree_add_item(tree, proto_irdmaep, tvb, 0, msg_length, ENC_NA); + ep_tree = proto_item_add_subtree(ti, ett_irdmaep); + + ti_type = proto_tree_add_item(ep_tree, hf_irdmaep_type, tvb, + IRDMA_EP_TYPE, 1, ENC_NA); + proto_tree_add_item(ep_tree, hf_irdmaep_len, tvb, + IRDMA_EP_LEN, 1, ENC_NA); + + ti = proto_tree_add_item(ep_tree, hf_irdmaep_grpid, tvb, + IRDMA_EP_GRPID, 12, ENC_NA); + grpid_tree = proto_item_add_subtree(ti, ett_irdmaep_grpid); + + proto_tree_add_item(grpid_tree, hf_irdmaep_grpid_ctr, tvb, + IRDMA_EP_GRPID_CTR, 1, ENC_NA); + proto_tree_add_item(grpid_tree, hf_irdmaep_grpid_time, tvb, + IRDMA_EP_GRPID_TIME, 5, ENC_BIG_ENDIAN); + proto_tree_add_item(grpid_tree, hf_irdmaep_grpid_hwaddr, tvb, + IRDMA_EP_GRPID_MAC, 6, ENC_NA); + + + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_srcport, tvb, + IRDMA_EP_SPORT, 2, ENC_BIG_ENDIAN, + &pinfo->srcport); + ti = proto_tree_add_item(ep_tree, hf_irdmaep_port, tvb, + IRDMA_EP_SPORT, 2, ENC_BIG_ENDIAN); + PROTO_ITEM_SET_HIDDEN(ti); + + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_dstport, tvb, + IRDMA_EP_DPORT, 2, ENC_BIG_ENDIAN, + &pinfo->destport); + ti = proto_tree_add_item(ep_tree, hf_irdmaep_port, tvb, + IRDMA_EP_DPORT, 2, ENC_BIG_ENDIAN); + PROTO_ITEM_SET_HIDDEN(ti); + ti = proto_tree_add_uint(ep_tree, hf_irdmaep_stream, tvb, 0, 0, + epd->stream); + PROTO_ITEM_SET_GENERATED(ti); + ti_seq = proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_linkseq, tvb, + IRDMA_EP_LINKSEQ, 2, ENC_BIG_ENDIAN, + &linkseq); + + /* Record conversation info that should only be done on first pass + (when packets are seen in order) */ + if (!PINFO_FD_VISITED(pinfo)) + { + if (irdmaep_calculate_ts) + { + if (!eppd) + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + nstime_delta(&eppd->delta_time, &pinfo->abs_ts, &epd->ts_prev); + + nstime_copy(&epd->ts_prev, &pinfo->abs_ts); + } + + /* If this is a CLOSE or RESET request, remember so that if we see a + later CONNREQ with the same ports, we'll create a new conversation. */ + if (msg_type == IRDMA_EP_CLOSE + || msg_type == IRDMA_EP_RESET) + epd->closed = 1; + + /* If link seq not yet known, save current link seq */ + if (epd->link == 0xFFFFFFFF) + epd->link = linkseq; + + /* Update current link seq if it has increased */ + if (seq16_gt(linkseq, epd->link)) + { + epd->link = linkseq; + if (!eppd) + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + eppd->linkswt = 1; + } + + /* If packet is from prior link, set stale flag */ + if (seq16_lt(linkseq, epd->link)) + { + if (!eppd) + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + eppd->seq_stale = 1; + } + } + else + eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + + if (eppd) + { + if (eppd->linkswt) + expert_add_info(pinfo, ti_seq, &ei_irdmaep_connection_lswt); + + if (eppd->seq_stale) + expert_add_info(pinfo, ti_seq, &ei_irdmaep_analysis_stale); + } + + switch (msg_type) + { + case IRDMA_EP_DATA: + dissect_data_msg(tvb, pinfo, tree, epd, ep_tree); + break; + + case IRDMA_EP_REUSEBUF: + dissect_reuse_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_RECVREQ: + dissect_user_recv_msg(tvb, pinfo, tree, epd, ep_tree); + break; + + case IRDMA_EP_RECVACK: + case IRDMA_EP_RECVNAK: + dissect_user_send_msg(tvb, pinfo, tree, epd, ep_tree); + break; + + case IRDMA_EP_CONNREQ: + expert_add_info_format(pinfo, ti_type, &ei_irdmaep_connection_req, + "Connection request (CONNREQ): server port %u", + pinfo->destport); + dissect_connect_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_CONNACK: + expert_add_info_format(pinfo, ti_type, &ei_irdmaep_connection_ack, + "Connection acknowledgement (CONNACK): server port %u", + pinfo->srcport); + dissect_connect_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_RNEGREQ: + dissect_connect_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_CLOSE: + dissect_close_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_CONNBUF: + case IRDMA_EP_RNEGACK: + dissect_buffer_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_MOVEREQ: + if (!PINFO_FD_VISITED(pinfo)) + { + /* Save time of MOVEREQ so we can report link switch delay */ + epd->movereq_time = pinfo->abs_ts; + } + + dissect_move_link_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_MOVEACK: + dissect_move_link_ack_msg(tvb, pinfo, epd, ep_tree); + break; + + case IRDMA_EP_MOVECMP: + dissect_move_link_cmp_msg(tvb, pinfo, epd, ep_tree); + break; + } + + ts_tree = proto_tree_add_subtree(ep_tree, tvb, 0, 0, ett_irdmaep_timestamps, + &ti, "Timestamps"); + PROTO_ITEM_SET_GENERATED(ti); + nstime_delta(&ts, &pinfo->abs_ts, &epd->ts_first); + ti = proto_tree_add_time(ts_tree, hf_irdmaep_ts_relative, tvb, 0, 0, &ts); + PROTO_ITEM_SET_GENERATED(ti); + if (eppd && !nstime_is_unset(&eppd->delta_time)) + { + ti = proto_tree_add_time(ts_tree, hf_irdmaep_ts_delta, tvb, 0, 0, + &eppd->delta_time); + PROTO_ITEM_SET_GENERATED(ti); + } + + return tvb_captured_length(tvb); +} + +static void +dissect_data_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + proto_item *ti_seq; + irdmaep_packet_analysis_t *eppd = NULL; + irdmaep_pdata_t pdata = {IRDMAEP_DATA_TYPE}; + uint32_t bufid, seqnum, offset, datalen; + + ti_seq = proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_seqnum, tvb, + IRDMA_EP_DATA_SEQNUM, 2, ENC_BIG_ENDIAN, + &seqnum); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_bufid, tvb, + IRDMA_EP_DATA_BUFID, 2, ENC_BIG_ENDIAN, + &bufid); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_offset, tvb, + IRDMA_EP_DATA_OFFSET, 4, ENC_BIG_ENDIAN, + &offset); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_datalen, tvb, + IRDMA_EP_DATA_DATALEN, 4, ENC_BIG_ENDIAN, + &datalen); + + /* Update flow analysis only on first (in-order) pass */ + if (!PINFO_FD_VISITED(pinfo) + && bufid < IRDMAEP_MAX_DATA_BUFID) + { + /* Only validate sequence number if we know previous seq# */ + if (epd->fwd_flow->data_seq_valid) + { + uint16_t expected = epd->fwd_flow->data_seq + 1; + if (seqnum != expected) + { + /* Unexpected sequence number -- update packet data */ + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + if (seq16_lt(seqnum, expected)) + eppd->retransmission = 1; + else + eppd->out_of_order = 1; /* Should never occur! */ + } + } + + epd->fwd_flow->data_seq_valid = 1; + epd->fwd_flow->data_seq = seqnum; + + /* Update remote receive buffer status */ + /* First, if this gives info on receive buffer we didn't yet know about, + bump buffer count but set those buffers to indeterminate state */ + for (; epd->fwd_flow->recv_buffer_count <= bufid; ++epd->fwd_flow->recv_buffer_count) + epd->fwd_flow->recv_buffer[epd->fwd_flow->recv_buffer_count].indeterminate = 1; + + epd->fwd_flow->recv_mrb = bufid; + epd->fwd_flow->recv_buffer[bufid].seq_num = seqnum; + epd->fwd_flow->recv_buffer[bufid].offset = (offset + datalen + 0xF) & ~0xF; + epd->fwd_flow->recv_buffer[bufid].indeterminate = 0; + + /* Adjust discovered size of buffer (in case initial buffer info msg + wasn't captured for this flow) */ + if (epd->fwd_flow->recv_buffer[bufid].offset > epd->fwd_flow->recv_min_size) + epd->fwd_flow->recv_min_size = (epd->fwd_flow->recv_buffer[bufid].offset + 0xFFF) & ~0xFFF; + + if (!eppd) + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + analyze_irdmaep_rbuffer(epd->fwd_flow, eppd); + } + else + eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + + if (eppd) + { + if (eppd->retransmission) + { + col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Retransmission] "); + expert_add_info(pinfo, ti_seq, &ei_irdmaep_analysis_dup); + } + + if (eppd->out_of_order) + { + col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Out-of-order] "); + expert_add_info(pinfo, ti_seq, &ei_irdmaep_analysis_oos); + } + + irdmaep_add_rbuf_tree(tvb, pinfo, ep_tree, eppd); + } + + dissect_irdmaep_data(tvb_new_subset_remaining(tvb, IRDMA_EP_DATA_MSG_LEN), + pinfo, tree, &pdata); +} + +static void +irdmaep_add_rbuf_tree(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + irdmaep_packet_analysis_t *eppd) +{ + proto_item *ti; + proto_tree *rcvbuf_tree; + + ti = proto_tree_add_uint(tree, hf_irdmaep_rbufavail, tvb, 0, 0, + eppd->rbuf_available); + if (eppd->rbuf_estimated) + proto_item_append_text(ti, " (min)"); + PROTO_ITEM_SET_GENERATED(ti); + + rcvbuf_tree = proto_item_add_subtree(ti, ett_irdmaep_rcvbuf_analyze); + + ti = proto_tree_add_uint(rcvbuf_tree, hf_irdmaep_rbufactive, tvb, 0, 0, + eppd->rbuf_cur); + if (eppd->rbuf_estimated) + proto_item_append_text(ti, " (min)"); + PROTO_ITEM_SET_GENERATED(ti); + + ti = proto_tree_add_uint(rcvbuf_tree, hf_irdmaep_rbufmax, tvb, 0, 0, + eppd->rbuf_max); + if (eppd->rbuf_estimated) + proto_item_append_text(ti, " (min)"); + PROTO_ITEM_SET_GENERATED(ti); +} + +static void +dissect_reuse_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + irdmaep_packet_analysis_t *eppd = NULL; + uint32_t bufid, seqnum; + + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_seqnum, tvb, + IRDMA_EP_REUSE_SEQNUM, 2, ENC_BIG_ENDIAN, + &seqnum); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_bufid, tvb, + IRDMA_EP_REUSE_BUFID, 2, ENC_BIG_ENDIAN, + &bufid); + + /* Update receive buffer analysis only on first (in-order) pass */ + if (!PINFO_FD_VISITED(pinfo) + && bufid < IRDMAEP_MAX_DATA_BUFID) + { + /* Reuse msg reflects local receive buffer status, so update lcl_rbuf */ + + /* First, if this gives info on receive buffer we didn't yet know about, + bump buffer count but set those buffers to indeterminate state */ + for (; epd->rev_flow->recv_buffer_count <= bufid; ++epd->rev_flow->recv_buffer_count) + epd->rev_flow->recv_buffer[epd->rev_flow->recv_buffer_count].indeterminate = 1; + + if (epd->rev_flow->recv_buffer_count > 1 + || epd->rev_flow->recv_buffer[bufid].seq_num == seqnum + || epd->rev_flow->recv_buffer[bufid].indeterminate) + { + epd->rev_flow->recv_buffer[bufid].offset = 0; + epd->rev_flow->recv_buffer[bufid].indeterminate = 0; + } + + eppd = (irdmaep_packet_analysis_t *) add_eppd(pinfo); + analyze_irdmaep_rbuffer(epd->rev_flow, eppd); + } + else + eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + + if (eppd) + irdmaep_add_rbuf_tree(tvb, pinfo, ep_tree, eppd); +} + +static void +dissect_user_recv_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_analysis_t *epd _U_, proto_tree *ep_tree) +{ + proto_item *ti; + proto_tree *rbuf_tree; + + irdmaep_pdata_t pdata = {IRDMAEP_USERRDMA_TYPE, 0}; + + proto_tree_add_item(ep_tree, hf_irdmaep_ulength, tvb, + IRDMA_EP_USERRECV_ULENGTH, 4, ENC_BIG_ENDIAN); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_offset, tvb, + IRDMA_EP_USERRECV_OFFSET, 4, ENC_BIG_ENDIAN, + &pdata.userrdma_offset); + proto_tree_add_item(ep_tree, hf_irdmaep_blength, tvb, + IRDMA_EP_USERRECV_RLENGTH, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(ep_tree, hf_irdmaep_recvid, tvb, + IRDMA_EP_USERRECV_RECVID, 2, ENC_BIG_ENDIAN); + ti = proto_tree_add_item(ep_tree, hf_irdmaep_rbuf, tvb, + IRDMA_EP_USERRECV_RKEY, 12, ENC_NA); + rbuf_tree = proto_item_add_subtree(ti, ett_irdmaep_rbuf); + proto_tree_add_item(rbuf_tree, hf_irdmaep_rkey, tvb, + IRDMA_EP_USERRECV_RKEY, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(rbuf_tree, hf_irdmaep_raddr, tvb, + IRDMA_EP_USERRECV_RADDR, 8, ENC_BIG_ENDIAN); + + dissect_irdmaep_data(tvb_new_subset_remaining(tvb, IRDMA_EP_USERRECV_MSG_LEN), + pinfo, tree, &pdata); +} + +static void +dissect_user_send_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + proto_item *ti_ofs; + irdmaep_packet_analysis_t *eppd = NULL; + uint32_t recvid, offset, block_length, total_length; + + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_ulength, tvb, + IRDMA_EP_USERSEND_ULENGTH, 4, ENC_BIG_ENDIAN, + &total_length); + ti_ofs = proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_offset, tvb, + IRDMA_EP_USERSEND_OFFSET, 4, ENC_BIG_ENDIAN, + &offset); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_blength, tvb, + IRDMA_EP_USERSEND_SLENGTH, 4, ENC_BIG_ENDIAN, + &block_length); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_recvid, tvb, + IRDMA_EP_USERSEND_RECVID, 2, ENC_BIG_ENDIAN, + &recvid); + + /* Update flow analysis only on first (in-order) pass */ + if (!PINFO_FD_VISITED(pinfo)) + { + if (epd->fwd_flow->recvack_valid) + { + uint16_t nextid = epd->fwd_flow->recvack_id + 1; + + if ((recvid == epd->fwd_flow->recvack_id && offset == epd->fwd_flow->recvack_offset) + || (recvid == nextid && offset == 0)) + { + /* Expected ID, offset */ + } + else + { + /* Unexpected offset */ + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + if (recvid == epd->fwd_flow->recvack_id + && offset < epd->fwd_flow->recvack_offset) + eppd->retransmission = 1; + else + eppd->out_of_order = 1; /* Should never occur! */ + } + } + + epd->fwd_flow->recvack_valid = 1; + epd->fwd_flow->recvack_id = recvid; + epd->fwd_flow->recvack_offset = offset + block_length; + } + else + eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + + if (eppd) + { + if (eppd->retransmission) + { + col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Retransmission] "); + expert_add_info(pinfo, ti_ofs, &ei_irdmaep_analysis_dup); + } + + if (eppd->out_of_order) + { + col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Out-of-order] "); + expert_add_info(pinfo, ti_ofs, &ei_irdmaep_analysis_oos); + } + } + + irdmaep_pdata_t pdata = {IRDMAEP_USERRDMA_TYPE, offset}; + dissect_irdmaep_data(tvb_new_subset_remaining(tvb, IRDMA_EP_USERSEND_MSG_LEN), + pinfo, tree, &pdata); +} + +static void +dissect_connect_msg(tvbuff_t *tvb, packet_info *pinfo _U_, + irdmaep_analysis_t *epd _U_, proto_tree *ep_tree) +{ + proto_item *ti; + uint32_t bufsize; + + ti = proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_sndbufsize, tvb, + IRDMA_EP_CONNECT_BUFSIZE, 2, + ENC_BIG_ENDIAN, &bufsize); + proto_item_append_text(ti, " (%u KB)", bufsize * 4); + + uint8_t msg_type = tvb_get_uint8(tvb, IRDMA_EP_TYPE); + if (msg_type == IRDMA_EP_CONNREQ + || msg_type == IRDMA_EP_CONNACK) + { + ti = proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_usrblksize, tvb, + IRDMA_EP_CONNECT_USRBLKSIZE, 2, + ENC_BIG_ENDIAN, &bufsize); + proto_item_append_text(ti, " (%u KB)", bufsize * 4); + } +} + +static void +dissect_close_msg(tvbuff_t *tvb, packet_info *pinfo _U_, + irdmaep_analysis_t *epd _U_, proto_tree *ep_tree) +{ + proto_tree_add_item(ep_tree, hf_irdmaep_reason, tvb, + IRDMA_EP_CLOSE_REASON, 4, ENC_BIG_ENDIAN); +} + +static void +dissect_buffer_msg_mkeyaddr(tvbuff_t *tvb, packet_info *pinfo _U_, + irdmaep_analysis_t *epd _U_, proto_tree *ep_tree) +{ + proto_item *ti; + proto_tree *rbuf_tree; + + ti = proto_tree_add_item(ep_tree, hf_irdmaep_rbuf, tvb, + IRDMA_EP_BUFFER_RKEY, 24, ENC_NA); + rbuf_tree = proto_item_add_subtree(ti, ett_irdmaep_rbuf); + proto_tree_add_item(rbuf_tree, hf_irdmaep_rkey, tvb, + IRDMA_EP_BUFFER_RKEY, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(rbuf_tree, hf_irdmaep_raddr, tvb, + IRDMA_EP_BUFFER_RADDR, 8, ENC_BIG_ENDIAN); + proto_tree_add_item(rbuf_tree, hf_irdmaep_rkey, tvb, + IRDMA_EP_BUFFER_RKEY + 4, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(rbuf_tree, hf_irdmaep_raddr, tvb, + IRDMA_EP_BUFFER_RADDR + 8, 8, ENC_BIG_ENDIAN); +} + +static void +dissect_buffer_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + uint32_t rbufsize; + + dissect_buffer_msg_mkeyaddr(tvb, pinfo, epd, ep_tree); + + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_rcvbufsize, tvb, + IRDMA_EP_BUFFER_RBUFSIZE, 2, ENC_BIG_ENDIAN, + &rbufsize); + rbufsize *= 4096; + + /* Update receive buffer analysis only on first (in-order) pass */ + if (!PINFO_FD_VISITED(pinfo)) + { + if (tvb_get_uint32(tvb, IRDMA_EP_BUFFER_RKEY, ENC_BIG_ENDIAN)) + { + memset(&epd->rev_flow->recv_buffer[0], 0, + sizeof epd->rev_flow->recv_buffer[0]); + epd->rev_flow->recv_buffer[0].size = rbufsize; + if (epd->rev_flow->recv_buffer_count < 1) + epd->rev_flow->recv_buffer_count = 1; + } + + if (tvb_get_uint32(tvb, IRDMA_EP_BUFFER_RKEY + 4, ENC_BIG_ENDIAN)) + { + memset(&epd->rev_flow->recv_buffer[1], 0, + sizeof epd->rev_flow->recv_buffer[1]); + epd->rev_flow->recv_buffer[1].size = rbufsize; + if (epd->rev_flow->recv_buffer_count < 2) + epd->rev_flow->recv_buffer_count = 2; + } + } +} + +static void +dissect_move_link_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + dissect_buffer_msg_mkeyaddr(tvb, pinfo, epd, ep_tree); + + proto_item *ti; + proto_tree *flags_tree; + uint32_t rbufsize; + + ti = proto_tree_add_item(ep_tree, hf_irdmaep_flags, tvb, + IRDMA_EP_BUFFER_FLAGS, 1, ENC_NA); + flags_tree = proto_item_add_subtree(ti, ett_irdmaep_bufflags); + proto_tree_add_item(flags_tree, hf_irdmaep_flag_buf0_free, tvb, + IRDMA_EP_BUFFER_FLAGS, 1, ENC_NA); + proto_tree_add_item(flags_tree, hf_irdmaep_flag_buf1_free, tvb, + IRDMA_EP_BUFFER_FLAGS, 1, ENC_NA); + proto_tree_add_item_ret_uint(ep_tree, hf_irdmaep_rcvbufsize, tvb, + IRDMA_EP_BUFFER_RBUFSIZE, 2, ENC_BIG_ENDIAN, + &rbufsize); + + proto_tree_add_item(ep_tree, hf_irdmaep_sendseq, tvb, + IRDMA_EP_MOVELINK_SENDSEQ, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ep_tree, hf_irdmaep_recvseq0, tvb, + IRDMA_EP_MOVELINK_RBUFSEQ, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ep_tree, hf_irdmaep_recvseq1, tvb, + IRDMA_EP_MOVELINK_RBUFSEQ + 2, 2, ENC_BIG_ENDIAN); + proto_tree_add_item(ep_tree, hf_irdmaep_usndsent, tvb, + IRDMA_EP_MOVELINK_USNDSENT, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(ep_tree, hf_irdmaep_usndid, tvb, + IRDMA_EP_MOVELINK_USNDID, 2, ENC_BIG_ENDIAN); + + /* Update receive buffer analysis only on first (in-order) pass */ + if (!PINFO_FD_VISITED(pinfo)) + { + if (tvb_get_uint32(tvb, IRDMA_EP_BUFFER_RKEY, ENC_BIG_ENDIAN) + && (rbufsize & 0x8000)) + { + memset(&epd->rev_flow->recv_buffer[0], 0, + sizeof epd->rev_flow->recv_buffer[0]); + epd->rev_flow->recv_buffer[0].size = (rbufsize & 0xFFF) * 4096; + } + + if (tvb_get_uint32(tvb, IRDMA_EP_BUFFER_RKEY + 4, ENC_BIG_ENDIAN) + && (rbufsize & 0x4000)) + { + memset(&epd->rev_flow->recv_buffer[1], 0, + sizeof epd->rev_flow->recv_buffer[1]); + epd->rev_flow->recv_buffer[1].size = (rbufsize & 0xFFF) * 4096; + } + } +} + +static void +dissect_move_link_ack_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + proto_item *ti; + irdmaep_packet_analysis_t *eppd = NULL; + + if (!PINFO_FD_VISITED(pinfo)) + { + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + + nstime_delta(&eppd->movelink_time, &pinfo->abs_ts, &epd->movereq_time); + } + else + eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + + dissect_move_link_msg(tvb, pinfo, epd, ep_tree); + + proto_tree_add_item(ep_tree, hf_irdmaep_urcvid, tvb, + IRDMA_EP_MOVELINKACK_URCVID, 2, ENC_BIG_ENDIAN); + + ti = proto_tree_add_time(ep_tree, hf_irdmaep_move1, tvb, + IRDMA_EP_TYPE, 0, &eppd->movelink_time); + PROTO_ITEM_SET_GENERATED(ti); +} + +static void +dissect_move_link_cmp_msg(tvbuff_t *tvb, packet_info *pinfo, + irdmaep_analysis_t *epd, proto_tree *ep_tree) +{ + proto_item *ti; + irdmaep_packet_analysis_t *eppd = NULL; + + if (!PINFO_FD_VISITED(pinfo)) + { + eppd = (irdmaep_packet_analysis_t *) get_or_add_eppd(pinfo); + + nstime_delta(&eppd->movelink_time, &pinfo->abs_ts, &epd->movereq_time); + } + else + eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + + proto_tree_add_item(ep_tree, hf_irdmaep_urcvid, tvb, + IRDMA_EP_MOVELINKCMP_URCVID, 2, ENC_BIG_ENDIAN); + + ti = proto_tree_add_time(ep_tree, hf_irdmaep_move2, tvb, + IRDMA_EP_TYPE, 0, &eppd->movelink_time); + PROTO_ITEM_SET_GENERATED(ti); +} + +static void +dissect_irdmaep_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + irdmaep_pdata_t *pdata) +{ + irdmaep_packet_analysis_t *eppd = NULL; + + /* Should we try to call subdissector based on port? */ + if (!irdmaep_no_subdissector_on_error + || !(eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo)) + || !eppd->retransmission) + { + uint32_t low_port, high_port; + + if (pinfo->srcport < pinfo->destport) + { + low_port = pinfo->srcport; + high_port = pinfo->destport; + } + else + { + low_port = pinfo->destport; + high_port = pinfo->srcport; + } + + if (dissector_try_uint_new(irdmaep_dissector_table, low_port, + tvb, pinfo, tree, true, pdata)) + return; + + if (dissector_try_uint_new(irdmaep_dissector_table, high_port, + tvb, pinfo, tree, true, pdata)) + return; + } + + /* If no subdissector (or error and skipped calling), just dissect as data */ + call_data_dissector(tvb, pinfo, tree); +} + +static irdmaep_analysis_t * +init_irdmaep_conversation_data(packet_info *pinfo) +{ + irdmaep_analysis_t *epd + = wmem_new0(wmem_file_scope(), irdmaep_analysis_t); + + epd->stream = irdmaep_stream_count++; + epd->link = -1; + + nstime_copy(&epd->ts_first, &pinfo->abs_ts); + nstime_copy(&epd->ts_prev, &pinfo->abs_ts); + + return epd; +} + +static void * +get_eppd(packet_info *pinfo) +{ + return p_get_proto_data(wmem_file_scope(), pinfo, proto_irdmaep, + pinfo->curr_layer_num); +} + +static void * +add_eppd(packet_info *pinfo) +{ + irdmaep_packet_analysis_t *eppd + = wmem_new0(wmem_file_scope(), irdmaep_packet_analysis_t); + p_add_proto_data(wmem_file_scope(), pinfo, proto_irdmaep, + pinfo->curr_layer_num, eppd); + + nstime_set_unset(&eppd->delta_time); + return eppd; +} + +static void * +get_or_add_eppd(packet_info *pinfo) +{ + irdmaep_packet_analysis_t *eppd = (irdmaep_packet_analysis_t *) get_eppd(pinfo); + if (!eppd) + eppd = (irdmaep_packet_analysis_t *) add_eppd(pinfo); + + return eppd; +} + +static void +analyze_irdmaep_rbuffer(irdmaep_flow_t *flow, + irdmaep_packet_analysis_t *eppd) +{ + uint32_t bufid = flow->recv_mrb; + + /* Start by getting available space in the current receive + buffer */ + if (flow->recv_buffer[bufid].size) + eppd->rbuf_available + = eppd->rbuf_cur + = eppd->rbuf_max + = flow->recv_buffer[bufid].size + - flow->recv_buffer[bufid].offset; + else + { + eppd->rbuf_available + = eppd->rbuf_cur + = eppd->rbuf_max + = flow->recv_min_size + - flow->recv_buffer[bufid].offset; + eppd->rbuf_estimated = 1; + } + + /* If there's a second receive buffer and it's empty, include + it in receive buffer analysis */ + if (++bufid == IRDMAEP_MAX_DATA_BUFID) + bufid = 0; + if (flow->recv_buffer_count > 1 + && !flow->recv_buffer[bufid].indeterminate + && flow->recv_buffer[bufid].offset == 0) + { + uint32_t size = flow->recv_buffer[bufid].size + ? flow->recv_buffer[bufid].size + : flow->recv_min_size; + eppd->rbuf_available += size; + if (size > eppd->rbuf_max) + eppd->rbuf_max = size; + } +} + +/*********************************************************************/ +/*********************************************************************/ +/* Dissector registration functions */ +/*********************************************************************/ +/*********************************************************************/ + +/* Register the protocol with Wireshark */ +void +proto_register_irdma(void) +{ + module_t *irdmaep_module; + expert_module_t *expert_irdmaep; + + /*****************************************************************/ + /* RDMA pseudo-header field definitions */ + /*****************************************************************/ + static hf_register_info hf[] = { + { &hf_irdma_hwdst, + { "Destination", "irdma.dst", + FT_ETHER, BASE_NONE, NULL, 0x0, + "Destination Hardware Address", HFILL }}, + { &hf_irdma_hwsrc, + { "Source", "irdma.src", + FT_ETHER, BASE_NONE, NULL, 0x0, + "Source Hardware Address", HFILL }}, + { &hf_irdma_qpindex, + { "QP#", "irdma.qpidx", + FT_UINT16, BASE_DEC, NULL, 0x0, + "QP Index", HFILL}}, + { &hf_irdma_ip6src, + { "Source IP", "irdma.ipv6.src", + FT_IPv6, BASE_NONE, NULL, 0x0, + "Source IP Address", HFILL }}, + { &hf_irdma_ip6dst, + { "Destination IP", "irdma.ipv6.dst", + FT_IPv6, BASE_NONE, NULL, 0x0, + "Destination IP Address", HFILL }}, + { &hf_irdma_ip4src, + { "Source IP", "irdma.ipv4.src", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Source IP Address", HFILL }}, + { &hf_irdma_ip4dst, + { "Destination IP", "irdma.ipv4.dst", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Destination IP Address", HFILL }}, + { &hf_irdma_hwaddr, + { "Address", "irdma.addr", + FT_ETHER, BASE_NONE, NULL, 0x0, + "Source or Destination Hardware Address", HFILL}}, + { &hf_irdma_ip6addr, + { "IP Address", "irdma.ipv6.addr", + FT_IPv6, BASE_NONE, NULL, 0x0, + "Source or Destination IPv6 Address", HFILL}}, + { &hf_irdma_ip4addr, + { "IP Address", "irdma.ipv4.addr", + FT_IPv4, BASE_NONE, NULL, 0x0, + "Source or Destination IPv4 Address", HFILL}} + }; + + /*****************************************************************/ + /* RDMA QP field definitions */ + /*****************************************************************/ + static hf_register_info hfqp[] = { + { &hf_irdmaqp_type, + { "Type", "irdma.qp.type", + FT_UINT16, BASE_HEX, VALS(irdmaqp_type_str), 0x0, + "RDMA QP Message Type", HFILL }}, + { &hf_irdmaqp_id, + { "Identifier", "irdma.qp.echo.id", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA QP Echo Identifier", HFILL }} + }; + + /*****************************************************************/ + /* RDMA Link field definitions */ + /*****************************************************************/ + static hf_register_info hflink[] = { + { &hf_irdmalink_type, + { "Type", "irdma.link.type", + FT_UINT16, BASE_HEX, VALS(irdmalink_type_str), 0x0, + "RDMA Link Message Type", HFILL }}, + { &hf_irdmalink_groups, + { "Groups", "irdma.link.status.groups", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA Link Status Active Group Count", HFILL }} + }; + + /*****************************************************************/ + /* RDMA Endpoint field definitions */ + /*****************************************************************/ + static hf_register_info hfep[] = { + { &hf_irdmaep_type, + { "Type", "irdma.ep.type", + FT_UINT8, BASE_DEC_HEX, VALS(irdmaep_type_str), 0x0, + "RDMA Endpoint Message Type", HFILL }}, + { &hf_irdmaep_len, + { "Length", "irdma.ep.len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "RDMA Endpoint Message Length", HFILL }}, + { &hf_irdmaep_grpid, + { "Group ID", "irdma.ep.grpid", + FT_BYTES, BASE_NONE, NULL, 0x0, + "RDMA Endpoint Group Identifier", HFILL }}, + { &hf_irdmaep_grpid_ctr, + { "Counter", "irdma.ep.grpid.counter", + FT_UINT8, BASE_HEX_DEC, NULL, 0x0, + "RDMA Endpoint Group Identifier Uniqueness Counter", HFILL }}, + { &hf_irdmaep_grpid_time, + { "Time Generated", "irdma.ep.grpid.time", + FT_UINT48, BASE_HEX_DEC, NULL, 0x0, + "RDMA Endpoint Group Identifier Timestamp", HFILL }}, + { &hf_irdmaep_grpid_hwaddr, + { "Hardware Address", "irdma.ep.grpid.hwaddr", + FT_ETHER, BASE_NONE, NULL, 0x0, + "RDMA Endpoint Group Identifier Hardware Address", HFILL }}, + { &hf_irdmaep_srcport, + { "Source Port", "irdma.ep.srcport", + FT_UINT16, BASE_CUSTOM, CF_FUNC(irdma_custom_format_port), 0x0, + "RDMA Endpoint Source Port", HFILL }}, + { &hf_irdmaep_dstport, + { "Destination Port", "irdma.ep.dstport", + FT_UINT16, BASE_CUSTOM, CF_FUNC(irdma_custom_format_port), 0x0, + "RDMA Endpoint Destination Port", HFILL }}, + { &hf_irdmaep_port, + { "Source or Destination Port", "irdma.ep.port", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA Endpoint Source or Destination Port", HFILL }}, + { &hf_irdmaep_stream, + { "Stream index", "irdma.ep.stream", + FT_UINT32, BASE_DEC, NULL, 0x0, + "RDMA Endpoint Stream Index", HFILL }}, + { &hf_irdmaep_linkseq, + { "Link Sequence", "irdma.ep.linkseq", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA Endpoint Link Switch Sequence", HFILL }}, + { &hf_irdmaep_sndbufsize, + { "Send Buffer Size", "irdma.ep.sndbufsize", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA Endpoint Send Buffer Size", HFILL }}, + { &hf_irdmaep_usrblksize, + { "User Block Size", "irdma.ep.usrblksize", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA Endpoint USER_RDMA Block Size", HFILL }}, + { &hf_irdmaep_reason, + { "Reason", "irdma.ep.reason", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, /* TODO - Add Strings for reason? */ + "RDMA Endpoint Close Reason", HFILL }}, + { &hf_irdmaep_rbuf, + { "Remote Memory", "irdma.ep.rbuf", + FT_BYTES, BASE_NONE, NULL, 0x0, + "RDMA Endpoint Remote Memory Key/Address", HFILL }}, + { &hf_irdmaep_rkey, + { "Remote Memory Key", "irdma.ep.rbuf.key", + FT_UINT32, BASE_HEX, NULL, 0x0, + "RDMA Endpoint Remote Memory Key", HFILL }}, + { &hf_irdmaep_raddr, + { "Remote Memory Address", "irdma.ep.rbuf.addr", + FT_UINT64, BASE_HEX, NULL, 0x0, + "RDMA Endpoint Remote Memory Address", HFILL }}, + { &hf_irdmaep_flags, + { "Flags", "irdma.ep.buffer.flags", + FT_UINT8, BASE_HEX, NULL, 0xF0, + "RDMA Endpoint Buffer Flags", HFILL }}, + { &hf_irdmaep_flag_buf0_free, + { "Buffer #0", "irdma.ep.buffer.flags.buf0", + FT_UINT8, 1, VALS(vals_free_inuse), 0x80, + "RDMA Endpoint Buffer 0 Free Flag", HFILL }}, + { &hf_irdmaep_flag_buf1_free, + { "Buffer #1", "irdma.ep.buffer.flags.buf1", + FT_UINT8, 1, VALS(vals_free_inuse), 0x40, + "RDMA Endpoint Buffer 1 Free Flag", HFILL }}, + { &hf_irdmaep_rcvbufsize, + { "Receive Buffer Size", "irdma.ep.rcvbufsize", + FT_UINT16, BASE_CUSTOM, CF_FUNC(irdma_custom_format_rbufsize), 0x0, + "RDMA Endpoint Receive Buffer Size", HFILL }}, + { &hf_irdmaep_sendseq, + { "Last Send Sequence", "irdma.ep.sendseq", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Last Successful Data Sequence Number", HFILL }}, + { &hf_irdmaep_recvseq0, + { "Last Received Sequence (Buffer #0)", "irdma.ep.recvseq0", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Last Received Data Sequence Number", HFILL }}, + { &hf_irdmaep_recvseq1, + { "Last Received Sequence (Buffer #1)", "irdma.ep.recvseq1", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Last Received Data Sequence Number", HFILL }}, + { &hf_irdmaep_usndsent, + { "USER_RDMA Bytes Sent", "irdma.ep.usndsent", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Bytes Sent From Current USER_RDMA Transfer", HFILL }}, + { &hf_irdmaep_usndid, + { "USER_RDMA Send Identifier", "irdma.ep.usndid", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Current USER_RDMA Send Identifier", HFILL }}, + { &hf_irdmaep_urcvid, + { "USER_RDMA Receive Identifier Complete", "irdma.ep.urcvid", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Last USER_RDMA Receive Identifier Completed", HFILL }}, + { &hf_irdmaep_seqnum, + { "Send Sequence", "irdma.ep.seqnum", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Data Sequence Number", HFILL }}, + { &hf_irdmaep_bufid, + { "Buffer Identifier", "irdma.ep.bufid", + FT_UINT16, BASE_DEC, NULL, 0x0, + "RDMA Endpoint Buffer Identifier", HFILL }}, + { &hf_irdmaep_offset, + { "Buffer Offset", "irdma.ep.offset", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Buffer Offset", HFILL }}, + { &hf_irdmaep_datalen, + { "Length", "irdma.ep.length", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint Data Length", HFILL }}, + { &hf_irdmaep_ulength, + { "USER_RDMA Length", "irdma.ep.ulength", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint USER_RDMA Total Length", HFILL }}, + { &hf_irdmaep_blength, + { "USER_RDMA Block Length", "irdma.ep.blength", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint USER_RDMA Block Length", HFILL }}, + { &hf_irdmaep_recvid, + { "USER_RDMA Receive Identifier", "irdma.ep.recvid", + FT_UINT16, BASE_DEC_HEX, NULL, 0x0, + "RDMA Endpoint USER_RDMA Receive Identifier", HFILL }}, + { &hf_irdmaep_rbufavail, + { "Receive Buffer Available", "irdma.ep.analysis.rcvbuf", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Receive Buffer Bytes Available", HFILL }}, + { &hf_irdmaep_rbufactive, + { "Active Receive Buffer Available", "irdma.ep.analysis.rcvbuf.active", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Active Receive Buffer Bytes Available", HFILL }}, + { &hf_irdmaep_rbufmax, + { "Maximum Receive Buffer Available", "irdma.ep.analysis.rcvbuf.max", + FT_UINT32, BASE_DEC_HEX, NULL, 0x0, + "RDMA Maximum Receive Buffer Bytes Available", HFILL }}, + { &hf_irdmaep_move1, + { "Server link switch time", "irdma.ep.analysis.linkswt.server", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, + "RDMA server link switch time", HFILL }}, + { &hf_irdmaep_move2, + { "Link switch time", "irdma.ep.analysis.linkswt", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, + "RDMA link switch time", HFILL }}, + { &hf_irdmaep_ts_relative, + { "Time since first frame in this RDMA stream", "irdma.ep.time_relative", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, + "Time relative to first frame in this RDMA stream", HFILL }}, + { &hf_irdmaep_ts_delta, + { "Time since previous frame in this RDMA stream", "irdma.ep.time_delta", + FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, + "Time delta from previous frame in this RDMA stream", HFILL }} + }; + + /*****************************************************************/ + /* Protocol subtree array */ + /*****************************************************************/ + static int *ett[] = { + &ett_irdma, + &ett_irdmaqp, + &ett_irdmalink, + &ett_irdmaep, + &ett_irdmaep_grpid, + &ett_irdmaep_rbuf, + &ett_irdmaep_bufflags, + &ett_irdmaep_rcvbuf_analyze, + &ett_irdmaep_timestamps + }; + + /* Setup protocol expert items */ + static ei_register_info ei[] = { + { &ei_irdmaep_analysis_stale, + { "irdma.ep.analysis.stale", PI_SEQUENCE, PI_NOTE, + "Packet received from prior link", EXPFILL }}, + { &ei_irdmaep_analysis_dup, + { "irdma.ep.analysis.dup", PI_SEQUENCE, PI_CHAT, + "Duplicate data packet (retransmission)", EXPFILL }}, + { &ei_irdmaep_analysis_oos, + { "irdma.ep.analysis.oos", PI_SEQUENCE, PI_ERROR, + "Out of order data packet", EXPFILL }}, + { &ei_irdmaep_connection_req, + { "irdma.ep.connection.req", PI_SEQUENCE, PI_CHAT, + "Connection request (CONNREQ)", EXPFILL }}, + { &ei_irdmaep_connection_ack, + { "irdma.ep.connection.ack", PI_SEQUENCE, PI_CHAT, + "Connection acknowledgement (CONNACK)", EXPFILL }}, + { &ei_irdmaep_connection_lswt, + { "irdma.ep.connection.linkswt", PI_SEQUENCE, PI_NOTE, + "Link switch", EXPFILL }} + }; + + /*****************************************************************/ + /* Register the protocols */ + /*****************************************************************/ + proto_irdma = proto_register_protocol("IBM i RDMA", "iRDMA", "irdma"); + proto_irdmaqp = proto_register_protocol("IBM i RDMA QP", "iRDMA-QP", "irdma.qp"); + proto_irdmalink = proto_register_protocol("IBM i RDMA Link", "iRDMA-Link", "irdma.link"); + proto_irdmaep = proto_register_protocol("IBM i RDMA Endpoint", "iRDMA-EP", "irdma.ep"); + + register_init_routine(irdma_init); + + /* Required function calls to register the header fields and subtrees */ + proto_register_field_array(proto_irdma, hf, array_length(hf)); + proto_register_field_array(proto_irdmaqp, hfqp, array_length(hfqp)); + proto_register_field_array(proto_irdmalink, hflink, array_length(hflink)); + proto_register_field_array(proto_irdmaep, hfep, array_length(hfep)); + + proto_register_subtree_array(ett, array_length(ett)); + + // Register a dissector table to allow sub-dissectors to register based on EP port + irdmaep_dissector_table + = register_dissector_table("irdma.ep.port", "RDMA EP Port", proto_irdmaep, FT_UINT16, BASE_DEC); + + /* Required function calls to register expert items */ + expert_irdmaep = expert_register_protocol(proto_irdmaep); + expert_register_field_array(expert_irdmaep, ei, array_length(ei)); + + /* Register a preferences module (see section 2.6 of README.dissector + * for more details). Registration of a prefs callback is not required + * if there are no preferences that affect protocol registration (an example + * of a preference that would affect registration is a port preference). + * If the prefs callback is not needed, use NULL instead of + * proto_reg_handoff_irdma in the following. + */ + prefs_register_protocol(proto_irdma, proto_reg_handoff_irdma); + irdmaep_module = prefs_register_protocol(proto_irdmaep, NULL); + prefs_register_bool_preference(irdmaep_module, "calculate_timestamps", + "Calculate conversation timestamps", + "Calculate timestamps relative to the first frame and the previous frame in the RDMA conversation", + &irdmaep_calculate_ts); + prefs_register_bool_preference(irdmaep_module, "no_subdissector_on_error", + "Do not call subdissectors for error packets", + "Do not call any subdissectors for retransmitted segments", + &irdmaep_no_subdissector_on_error); + + register_dissector("irdma", dissect_irdma, proto_irdma); +} + +/* Simpler form of proto_reg_handoff_irdma which can be used if there are + * no prefs-dependent registration function calls. */ +void +proto_reg_handoff_irdma(void) +{ + /* Use create_dissector_handle() to get the handles to IBM i RDMA + * subdissectors. + */ + irdmaqp_handle = create_dissector_handle(dissect_irdmaqp, proto_irdmaqp); + irdmalink_handle = create_dissector_handle(dissect_irdmalink, proto_irdmalink); + irdmaep_handle = create_dissector_handle(dissect_irdmaep, proto_irdmaep); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |