/* 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 * 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 #include #include /* Should be first Wireshark include (other than config.h) */ #include #include #include #include #include #include #include /* 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: */