aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
Diffstat (limited to 'epan')
-rw-r--r--epan/dissectors/CMakeLists.txt2
-rw-r--r--epan/dissectors/Makefile.am3
-rw-r--r--epan/dissectors/packet-nvme-rdma.c1064
-rw-r--r--epan/dissectors/packet-nvme.c551
-rw-r--r--epan/dissectors/packet-nvme.h95
5 files changed, 1715 insertions, 0 deletions
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index ee77cedfcd..aa827b95f5 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -985,6 +985,8 @@ set(DISSECTOR_SRC
packet-ntlmssp.c
packet-ntp.c
packet-null.c
+ packet-nvme.c
+ packet-nvme-rdma.c
packet-nwmtp.c
packet-nwp.c
packet-oampdu.c
diff --git a/epan/dissectors/Makefile.am b/epan/dissectors/Makefile.am
index d1440eb064..adb5405901 100644
--- a/epan/dissectors/Makefile.am
+++ b/epan/dissectors/Makefile.am
@@ -1007,6 +1007,8 @@ DISSECTOR_SRC = \
packet-ntlmssp.c \
packet-ntp.c \
packet-null.c \
+ packet-nvme.c \
+ packet-nvme-rdma.c \
packet-nwmtp.c \
packet-nwp.c \
packet-oampdu.c \
@@ -1643,6 +1645,7 @@ DISSECTOR_INCLUDES = \
packet-nlm.h \
packet-ntlmssp.h \
packet-ntp.h \
+ packet-nvme.h \
packet-ocsp.h \
packet-opensafety.h \
packet-osi.h \
diff --git a/epan/dissectors/packet-nvme-rdma.c b/epan/dissectors/packet-nvme-rdma.c
new file mode 100644
index 0000000000..5ac99c17c5
--- /dev/null
+++ b/epan/dissectors/packet-nvme-rdma.c
@@ -0,0 +1,1064 @@
+/* packet-nvme-rdma.c
+ * Routines for NVM Express over Fabrics(RDMA) dissection
+ * Copyright 2016
+ * Code by Parav Pandit
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+NVM Express is high speed interface for accessing solid state drives.
+NVM Express specifications are maintained by NVM Express industry
+association at http://www.nvmexpress.org.
+
+This file adds support to dissect NVM Express over fabrics packets
+for RDMA. This adds very basic support for dissecting commands
+completions.
+
+Current dissection supports dissection of
+(a) NVMe cmd and cqe
+(b) NVMe Fabric command and cqe
+As part of it, it also calculates cmd completion latencies.
+
+This protocol is similar to iSCSI and SCSI dissection where iSCSI is
+transport protocol for carying SCSI commands and responses. Similarly
+NVMe Fabrics - RDMA transport protocol carries NVMe commands.
+
+ +----------+
+ | NVMe |
+ +------+---+
+ |
++-----------+---------+
+| NVMe Fabrics |
++----+-----------+----+
+ | |
++----+---+ +---+----+
+| RDMA | | FC |
++--------+ +--------+
+
+References:
+NVMe Express fabrics specification is located at
+http://www.nvmexpress.org/wp-content/uploads/NVMe_over_Fabrics_1_0_Gold_20160605.pdf
+
+NVMe Express specification is located at
+http://www.nvmexpress.org/wp-content/uploads/NVM-Express-1_2a.pdf
+
+NVM Express RDMA TCP port assigned by IANA that maps to RDMA IP service
+TCP port can be found at
+http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=NVM+Express
+
+*/
+#include "config.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/conversation.h>
+#include <epan/addr_resolv.h>
+
+#include "packet-infiniband.h"
+#include "packet-nvme.h"
+
+#define SID_ULP_MASK 0x00000000FF000000
+#define SID_PROTO_MASK 0x0000000000FF0000
+#define SID_PORT_MASK 0x000000000000FFFF
+
+#define SID_ULP 0x01
+#define SID_PROTO_TCP 0x06
+#define NVME_RDMA_TCP_PORT_RANGE "4220"
+
+#define SID_MASK (SID_ULP_MASK | SID_PROTO_MASK)
+#define SID_ULP_TCP ((SID_ULP << 3 * 8) | (SID_PROTO_TCP << 2 * 8))
+
+#define NVME_FABRICS_RDMA "NVMe Fabrics RDMA"
+
+#define NVME_FABRIC_CMD_SIZE NVME_CMD_SIZE
+#define NVME_FABRIC_CQE_SIZE NVME_CQE_SIZE
+
+#define NVME_FABRIC_OPC 0x7F
+
+#define NVME_FCTYPE_CONNECT 0x1
+#define NVME_FCTYPE_AUTH_RECV 0x6
+#define NVME_FCTYPE_PROP_GET 0x4
+#define NVME_FCTYPE_PROP_SET 0x0
+
+static const value_string fctype_tbl[] = {
+ { NVME_FCTYPE_CONNECT, "Connect"},
+ { NVME_FCTYPE_PROP_GET, "Property Get"},
+ { NVME_FCTYPE_PROP_SET, "Property Set"},
+ { NVME_FCTYPE_AUTH_RECV, "Authentication Recv"},
+ { 0, NULL}
+};
+
+static const value_string prop_offset_tbl[] = {
+ { 0x0, "Controller Capabilities"},
+ { 0x8, "Version"},
+ { 0xc, "Reserved"},
+ { 0x10, "Reserved"},
+ { 0x14, "Controller Configuration"},
+ { 0x18, "Reserved"},
+ { 0x1c, "Controller Status"},
+ { 0x20, "NVM Subsystem Reset"},
+ { 0x24, "Reserved"},
+ { 0x28, "Reserved"},
+ { 0x30, "Reserved"},
+ { 0x38, "Reserved"},
+ { 0x3c, "Reserved"},
+ { 0x40, "Reserved"},
+ { 0, NULL}
+};
+
+static const value_string attr_size_tbl[] = {
+ { 0, "4 bytes"},
+ { 1, "8 bytes"},
+ { 0, NULL}
+};
+
+struct nvme_rdma_q_ctx {
+ struct nvme_q_ctx n_q_ctx;
+};
+
+struct nvme_rdma_cmd_ctx {
+ struct nvme_cmd_ctx n_cmd_ctx;
+ guint8 fctype; /* fabric cmd type */
+};
+
+void proto_reg_handoff_nvme_rdma(void);
+void proto_register_nvme_rdma(void);
+
+static int proto_nvme_rdma = -1;
+static dissector_handle_t ib_handler;
+static int proto_ib = -1;
+
+/* NVMe Fabrics RDMA CM Private data */
+static int hf_nvme_rdma_cm_req_recfmt = -1;
+static int hf_nvme_rdma_cm_req_qid = -1;
+static int hf_nvme_rdma_cm_req_hrqsize = -1;
+static int hf_nvme_rdma_cm_req_hsqsize = -1;
+static int hf_nvme_rdma_cm_req_reserved = -1;
+
+static int hf_nvme_rdma_cm_rsp_recfmt = -1;
+static int hf_nvme_rdma_cm_rsp_crqsize = -1;
+static int hf_nvme_rdma_cm_rsp_reserved = -1;
+
+static int hf_nvme_rdma_cm_rej_recfmt = -1;
+static int hf_nvme_rdma_cm_rej_status = -1;
+static int hf_nvme_rdma_cm_rej_reserved = -1;
+
+/* NVMe Fabric Cmd */
+static int hf_nvme_rdma_cmd = -1;
+static int hf_nvme_rdma_from_host_unknown_data = -1;
+
+static int hf_nvme_rdma_cmd_opc = -1;
+static int hf_nvme_rdma_cmd_rsvd = -1;
+static int hf_nvme_rdma_cmd_cid = -1;
+static int hf_nvme_rdma_cmd_fctype = -1;
+static int hf_nvme_rdma_cmd_connect_rsvd1 = -1;
+static int hf_nvme_rdma_cmd_connect_sgl1 = -1;
+static int hf_nvme_rdma_cmd_connect_recfmt = -1;
+static int hf_nvme_rdma_cmd_connect_qid = -1;
+static int hf_nvme_rdma_cmd_connect_sqsize = -1;
+static int hf_nvme_rdma_cmd_connect_cattr = -1;
+static int hf_nvme_rdma_cmd_connect_rsvd2 = -1;
+static int hf_nvme_rdma_cmd_connect_kato = -1;
+static int hf_nvme_rdma_cmd_connect_rsvd3 = -1;
+
+static int hf_nvme_rdma_cmd_prop_attr_rsvd = -1;
+static int hf_nvme_rdma_cmd_prop_attr_rsvd1 = -1;
+static int hf_nvme_rdma_cmd_prop_attr_size = -1;
+static int hf_nvme_rdma_cmd_prop_attr_rsvd2 = -1;
+static int hf_nvme_rdma_cmd_prop_attr_offset = -1;
+static int hf_nvme_rdma_cmd_prop_attr_get_rsvd3 = -1;
+static int hf_nvme_rdma_cmd_prop_attr_set_4B_value = -1;
+static int hf_nvme_rdma_cmd_prop_attr_set_4B_value_rsvd = -1;
+static int hf_nvme_rdma_cmd_prop_attr_set_8B_value = -1;
+static int hf_nvme_rdma_cmd_prop_attr_set_rsvd3 = -1;
+
+static int hf_nvme_rdma_cmd_generic_rsvd1 = -1;
+static int hf_nvme_rdma_cmd_generic_field = -1;
+
+/* NVMe Fabric CQE */
+static int hf_nvme_rdma_cqe = -1;
+static int hf_nvme_rdma_cqe_sts = -1;
+static int hf_nvme_rdma_cqe_sqhd = -1;
+static int hf_nvme_rdma_cqe_rsvd = -1;
+static int hf_nvme_rdma_cqe_cid = -1;
+static int hf_nvme_rdma_cqe_status = -1;
+static int hf_nvme_rdma_cqe_status_rsvd = -1;
+
+static int hf_nvme_rdma_cqe_connect_cntlid = -1;
+static int hf_nvme_rdma_cqe_connect_authreq = -1;
+static int hf_nvme_rdma_cqe_connect_rsvd = -1;
+static int hf_nvme_rdma_cqe_prop_set_rsvd = -1;
+
+static int hf_nvme_rdma_to_host_unknown_data = -1;
+
+/* tracking Cmd and its respective CQE */
+static int hf_nvme_rdma_cmd_pkt = -1;
+static int hf_nvme_rdma_cqe_pkt = -1;
+static int hf_nvme_rdma_cmd_latency = -1;
+static int hf_nvme_rdma_cmd_qid = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_cm = -1;
+static gint ett_data = -1;
+
+static range_t *gPORT_RANGE;
+
+static conversation_infiniband_data *get_conversion_data(conversation_t *conv)
+{
+ conversation_infiniband_data *conv_data;
+
+ conv_data = (conversation_infiniband_data *)conversation_get_proto_data(conv, proto_ib);
+ if (!conv_data)
+ return NULL;
+
+ if ((conv_data->service_id & SID_MASK) != SID_ULP_TCP)
+ return NULL; /* the service id doesn't match that of TCP ULP - nothing for us to do here */
+
+ if (!(value_is_in_range(gPORT_RANGE, (guint32)(conv_data->service_id & SID_PORT_MASK))))
+ return NULL; /* the port doesn't match that of NVM Express Fabrics - nothing for us to do here */
+ return conv_data;
+}
+
+static conversation_t*
+find_ib_conversation(packet_info *pinfo, conversation_infiniband_data **uni_conv_data)
+{
+ conversation_t *conv;
+ conversation_infiniband_data *conv_data;
+
+ conv = find_conversation(pinfo->num, &pinfo->dst, &pinfo->dst,
+ PT_IBQP, pinfo->destport, pinfo->destport,
+ NO_ADDR_B|NO_PORT_B);
+ if (!conv)
+ return NULL; /* nothing to do with no conversation context */
+
+ conv_data = get_conversion_data(conv);
+ *uni_conv_data = conv_data;
+ if (!conv_data)
+ return NULL;
+
+ /* now that we found unidirectional conversation, find bidirectional
+ * conversation, so that we can relate to nvme q.
+ */
+ return find_conversation(pinfo->num, &pinfo->src, &pinfo->dst,
+ PT_IBQP, pinfo->srcport, pinfo->destport, 0);
+}
+
+static guint16 find_nvme_qid(packet_info *pinfo)
+{
+ conversation_t *conv;
+ conversation_infiniband_data *conv_data;
+ guint16 qid;
+
+ conv = find_conversation(pinfo->num, &pinfo->dst, &pinfo->dst,
+ PT_IBQP, pinfo->destport, pinfo->destport,
+ NO_ADDR_B|NO_PORT_B);
+ if (!conv)
+ return 0; /* nothing to do with no conversation context */
+
+ conv_data = get_conversion_data(conv);
+ if (!conv_data)
+ return 0;
+
+ if (conv_data->client_to_server == FALSE) {
+ memcpy(&qid, &conv_data->mad_private_data[178], 2);
+ return qid;
+ }
+ conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->src,
+ PT_IBQP, conv_data->src_qp, conv_data->src_qp,
+ NO_ADDR_B|NO_PORT_B);
+ conv_data = get_conversion_data(conv);
+ if (!conv_data)
+ return 0;
+ memcpy(&qid, &conv_data->mad_private_data[178], 2);
+ return qid;
+}
+
+static struct nvme_rdma_q_ctx*
+find_add_q_ctx(packet_info *pinfo, conversation_t *conv)
+{
+ struct nvme_rdma_q_ctx *q_ctx;
+ guint16 qid;
+
+ q_ctx = (struct nvme_rdma_q_ctx*)conversation_get_proto_data(conv, proto_nvme_rdma);
+ if (!q_ctx) {
+ qid = find_nvme_qid(pinfo);
+ q_ctx = wmem_new(wmem_file_scope(), struct nvme_rdma_q_ctx);
+ q_ctx->n_q_ctx.pending_cmds = wmem_tree_new(wmem_file_scope());
+ q_ctx->n_q_ctx.done_cmds = wmem_tree_new(wmem_file_scope());
+ q_ctx->n_q_ctx.qid = qid;
+ conversation_add_proto_data(conv, proto_nvme_rdma, q_ctx);
+ }
+ return q_ctx;
+}
+
+static conversation_infiniband_data*
+find_ib_cm_conversation(packet_info *pinfo)
+{
+ conversation_t *conv;
+
+ conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst,
+ PT_IBQP, pinfo->srcport, pinfo->destport, 0);
+ if (!conv)
+ return NULL;
+
+ return get_conversion_data(conv);
+}
+
+static void dissect_rdma_cm_req_packet(tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_tree *cm_tree;
+ proto_item *ti, *qid_item;
+ /* private data is at offset of 36 bytes */
+ int offset = 36;
+ guint16 qid;
+
+ /* create display subtree for private data */
+ ti = proto_tree_add_item(tree, proto_nvme_rdma, tvb, offset, 32, ENC_NA);
+ cm_tree = proto_item_add_subtree(ti, ett_cm);
+
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_req_recfmt, tvb,
+ offset + 0, 2, ENC_LITTLE_ENDIAN);
+
+ qid_item = proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_req_qid, tvb,
+ offset + 2, 2, ENC_LITTLE_ENDIAN);
+ qid = tvb_get_guint16(tvb, offset + 2, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(qid_item, " %s", qid ? "IOQ" : "AQ");
+
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_req_hrqsize, tvb,
+ offset + 4, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_req_hsqsize, tvb,
+ offset + 6, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_req_reserved, tvb,
+ offset + 8, 24, ENC_NA);
+}
+
+static void dissect_rdma_cm_rsp_packet(tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_tree *cm_tree;
+ proto_item *ti;
+
+ /* create display subtree for the private datat that start at offset 0 */
+ ti = proto_tree_add_item(tree, proto_nvme_rdma, tvb, 0, 32, ENC_NA);
+ cm_tree = proto_item_add_subtree(ti, ett_cm);
+
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_rsp_recfmt, tvb,
+ 0, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_rsp_crqsize, tvb,
+ 2, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_rsp_reserved, tvb,
+ 4, 28, ENC_NA);
+}
+
+static void dissect_rdma_cm_rej_packet(tvbuff_t *tvb, proto_tree *tree)
+{
+ proto_tree *cm_tree;
+ proto_item *ti;
+
+ /* create display subtree for the private datat that start at offset 0 */
+ ti = proto_tree_add_item(tree, proto_nvme_rdma, tvb, 0, 32, ENC_NA);
+ cm_tree = proto_item_add_subtree(ti, ett_cm);
+
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_rej_recfmt, tvb,
+ 0, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_rej_status, tvb,
+ 2, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cm_tree, hf_nvme_rdma_cm_rej_reserved, tvb,
+ 4, 28, ENC_NA);
+}
+
+static int dissect_rdma_cm_packet(tvbuff_t *tvb, proto_tree *tree,
+ guint16 cm_attribute_id)
+{
+ switch (cm_attribute_id) {
+ case ATTR_CM_REQ:
+ dissect_rdma_cm_req_packet(tvb, tree);
+ break;
+ case ATTR_CM_REP:
+ dissect_rdma_cm_rsp_packet(tvb, tree);
+ break;
+ case ATTR_CM_REJ:
+ dissect_rdma_cm_rej_packet(tvb, tree);
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+static int
+dissect_nvme_ib_cm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ void *data)
+{
+ conversation_infiniband_data *conv_data = NULL;
+ struct infinibandinfo *info = (struct infinibandinfo *)data;
+
+ conv_data = find_ib_cm_conversation(pinfo);
+ if (!conv_data)
+ return FALSE;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, NVME_FABRICS_RDMA);
+ return dissect_rdma_cm_packet(tvb, tree, info->cm_attribute_id);
+}
+
+static void dissect_nvme_fabric_connect_cmd(proto_tree *cmd_tree, tvbuff_t *cmd_tvb)
+{
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_rsvd1, cmd_tvb,
+ 5, 19, ENC_NA);
+ dissect_nvme_cmd_sgl(cmd_tvb, cmd_tree, hf_nvme_rdma_cmd_connect_sgl1);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_recfmt, cmd_tvb,
+ 40, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_qid, cmd_tvb,
+ 42, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_sqsize, cmd_tvb,
+ 44, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_cattr, cmd_tvb,
+ 46, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_rsvd2, cmd_tvb,
+ 47, 1, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_kato, cmd_tvb,
+ 48, 4, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_connect_rsvd3, cmd_tvb,
+ 52, 12, ENC_NA);
+}
+
+static guint8 dissect_nvme_fabric_prop_cmd_common(proto_tree *cmd_tree, tvbuff_t *cmd_tvb)
+{
+ proto_item *attr_item, *offset_item;
+ guint32 offset;
+ guint8 attr;
+
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_rsvd, cmd_tvb,
+ 5, 35, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_rsvd1, cmd_tvb,
+ 40, 1, ENC_LITTLE_ENDIAN);
+ attr_item = proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_size, cmd_tvb,
+ 40, 1, ENC_LITTLE_ENDIAN);
+ attr = tvb_get_guint8(cmd_tvb, 40) & 0x7;
+ proto_item_append_text(attr_item, " %s",
+ val_to_str(attr, attr_size_tbl, "Reserved"));
+
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_rsvd2, cmd_tvb,
+ 41, 3, ENC_NA);
+
+ offset_item = proto_tree_add_item_ret_uint(cmd_tree, hf_nvme_rdma_cmd_prop_attr_offset,
+ cmd_tvb, 44, 4, ENC_LITTLE_ENDIAN, &offset);
+ proto_item_append_text(offset_item, " %s",
+ val_to_str(offset, prop_offset_tbl, "Unknown Property"));
+ return attr;
+}
+
+static void dissect_nvme_fabric_prop_get_cmd(proto_tree *cmd_tree, tvbuff_t *cmd_tvb)
+{
+ dissect_nvme_fabric_prop_cmd_common(cmd_tree, cmd_tvb);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_get_rsvd3, cmd_tvb,
+ 48, 16, ENC_NA);
+}
+
+static void dissect_nvme_fabric_prop_set_cmd(proto_tree *cmd_tree, tvbuff_t *cmd_tvb)
+{
+ guint8 attr;
+
+ attr = dissect_nvme_fabric_prop_cmd_common(cmd_tree, cmd_tvb);
+ if (attr == 0) {
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_set_4B_value, cmd_tvb,
+ 48, 4, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_set_4B_value_rsvd, cmd_tvb,
+ 52, 4, ENC_LITTLE_ENDIAN);
+ } else {
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_set_8B_value, cmd_tvb,
+ 48, 8, ENC_LITTLE_ENDIAN);
+ }
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_prop_attr_set_rsvd3, cmd_tvb,
+ 56, 8, ENC_NA);
+}
+
+static void dissect_nvme_fabric_generic_cmd(proto_tree *cmd_tree, tvbuff_t *cmd_tvb)
+{
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_generic_rsvd1, cmd_tvb,
+ 5, 35, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_generic_field, cmd_tvb,
+ 40, 24, ENC_NA);
+}
+
+static struct nvme_rdma_cmd_ctx*
+bind_cmd_to_qctx(packet_info *pinfo, struct nvme_q_ctx *q_ctx,
+ guint16 cmd_id)
+{
+ struct nvme_rdma_cmd_ctx *ctx;
+
+ if (!PINFO_FD_VISITED(pinfo)) {
+ ctx = wmem_new0(wmem_file_scope(), struct nvme_rdma_cmd_ctx);
+
+ nvme_add_cmd_to_pending_list(pinfo, q_ctx,
+ &ctx->n_cmd_ctx, (void*)ctx, cmd_id);
+ } else {
+ /* Already visited this frame */
+ ctx = (struct nvme_rdma_cmd_ctx*)
+ nvme_lookup_cmd_in_done_list(pinfo, q_ctx, cmd_id);
+ /* if we have already visited frame but haven't found completion yet,
+ * we won't find cmd in done q, so allocate a dummy ctx for doing
+ * rest of the processing.
+ */
+ if (!ctx)
+ ctx = wmem_new0(wmem_file_scope(), struct nvme_rdma_cmd_ctx);
+ }
+ return ctx;
+}
+
+static void
+dissect_nvme_fabric_cmd(tvbuff_t *nvme_tvb, proto_tree *nvme_tree,
+ struct nvme_rdma_cmd_ctx *cmd_ctx)
+{
+ proto_tree *cmd_tree;
+ tvbuff_t *cmd_tvb;
+ proto_item *ti, *opc_item, *fctype_item;
+ guint8 fctype;
+
+ fctype = tvb_get_guint8(nvme_tvb, 4);
+ cmd_ctx->fctype = fctype;
+
+ cmd_tvb = tvb_new_subset_length(nvme_tvb, 0, NVME_FABRIC_CMD_SIZE);
+
+ ti = proto_tree_add_item(nvme_tree, hf_nvme_rdma_cmd, cmd_tvb, 0,
+ NVME_FABRIC_CMD_SIZE, ENC_NA);
+ cmd_tree = proto_item_add_subtree(ti, ett_data);
+
+ opc_item = proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_opc, cmd_tvb,
+ 0, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(opc_item, "%s", " Fabric Cmd");
+
+ nvme_publish_cmd_to_cqe_link(cmd_tree, cmd_tvb, hf_nvme_rdma_cqe_pkt,
+ &cmd_ctx->n_cmd_ctx);
+
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_rsvd, cmd_tvb,
+ 1, 1, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_cid, cmd_tvb,
+ 2, 2, ENC_LITTLE_ENDIAN);
+
+ fctype_item = proto_tree_add_item(cmd_tree, hf_nvme_rdma_cmd_fctype, cmd_tvb,
+ 4, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(fctype_item, " %s",
+ val_to_str(fctype, fctype_tbl, "Unknown FcType"));
+
+ switch(fctype) {
+ case NVME_FCTYPE_CONNECT:
+ dissect_nvme_fabric_connect_cmd(cmd_tree, cmd_tvb);
+ break;
+ case NVME_FCTYPE_PROP_GET:
+ dissect_nvme_fabric_prop_get_cmd(cmd_tree, cmd_tvb);
+ break;
+ case NVME_FCTYPE_PROP_SET:
+ dissect_nvme_fabric_prop_set_cmd(cmd_tree, cmd_tvb);
+ break;
+ case NVME_FCTYPE_AUTH_RECV:
+ default:
+ dissect_nvme_fabric_generic_cmd(cmd_tree, cmd_tvb);
+ break;
+ }
+}
+
+static void
+dissect_nvme_rdma_cmd(tvbuff_t *nvme_tvb, packet_info *pinfo, proto_tree *root_tree,
+ proto_tree *nvme_tree, struct nvme_rdma_q_ctx *q_ctx)
+{
+ struct nvme_rdma_cmd_ctx *cmd_ctx;
+ guint16 cmd_id;
+ guint8 opcode;
+
+ opcode = tvb_get_guint8(nvme_tvb, 0);
+ cmd_id = tvb_get_guint16(nvme_tvb, 2, ENC_LITTLE_ENDIAN);
+ cmd_ctx = bind_cmd_to_qctx(pinfo, &q_ctx->n_q_ctx, cmd_id);
+ if (opcode == NVME_FABRIC_OPC) {
+ cmd_ctx->n_cmd_ctx.fabric = TRUE;
+ dissect_nvme_fabric_cmd(nvme_tvb, nvme_tree, cmd_ctx);
+ } else {
+ cmd_ctx->n_cmd_ctx.fabric = FALSE;
+ dissect_nvme_cmd(nvme_tvb, pinfo, root_tree, &q_ctx->n_q_ctx,
+ &cmd_ctx->n_cmd_ctx);
+ }
+}
+
+static void
+dissect_nvme_from_host(tvbuff_t *nvme_tvb, packet_info *pinfo,
+ proto_tree *root_tree, proto_tree *nvme_tree,
+ struct infinibandinfo *info,
+ struct nvme_rdma_q_ctx *q_ctx,
+ guint len)
+
+{
+ switch (info->opCode) {
+ case RC_SEND_ONLY:
+ if (len == NVME_FABRIC_CMD_SIZE)
+ dissect_nvme_rdma_cmd(nvme_tvb, pinfo, root_tree, nvme_tree, q_ctx);
+ else
+ proto_tree_add_item(nvme_tree, hf_nvme_rdma_from_host_unknown_data, nvme_tvb,
+ 0, len, ENC_NA);
+ break;
+ default:
+ proto_tree_add_item(nvme_tree, hf_nvme_rdma_from_host_unknown_data, nvme_tvb,
+ 0, len, ENC_NA);
+ break;
+ }
+}
+
+static void
+dissect_nvme_rdma_cqe_status_8B(proto_tree *cqe_tree, tvbuff_t *cqe_tvb,
+ struct nvme_rdma_cmd_ctx *cmd_ctx)
+{
+ switch (cmd_ctx->fctype) {
+ case NVME_FCTYPE_CONNECT:
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_connect_cntlid, cqe_tvb,
+ 0, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_connect_authreq, cqe_tvb,
+ 2, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_connect_rsvd, cqe_tvb,
+ 4, 4, ENC_NA);
+ break;
+ case NVME_FCTYPE_PROP_GET:
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_sts, cqe_tvb,
+ 0, 8, ENC_LITTLE_ENDIAN);
+ break;
+ case NVME_FCTYPE_PROP_SET:
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_prop_set_rsvd, cqe_tvb,
+ 0, 8, ENC_NA);
+ break;
+ case NVME_FCTYPE_AUTH_RECV:
+ default:
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_sts, cqe_tvb,
+ 0, 8, ENC_LITTLE_ENDIAN);
+ break;
+ };
+}
+
+static void
+dissect_nvme_fabric_cqe(tvbuff_t *nvme_tvb,
+ proto_tree *nvme_tree,
+ struct nvme_rdma_cmd_ctx *cmd_ctx)
+{
+ proto_tree *cqe_tree;
+ proto_item *ti;
+ tvbuff_t *cqe_tvb;
+
+ cqe_tvb = tvb_new_subset_length(nvme_tvb, 0, NVME_FABRIC_CQE_SIZE);
+
+ ti = proto_tree_add_item(nvme_tree, hf_nvme_rdma_cqe, nvme_tvb,
+ 0, NVME_FABRIC_CQE_SIZE, ENC_NA);
+ proto_item_append_text(ti, " (For Cmd: %s)", val_to_str(cmd_ctx->fctype,
+ fctype_tbl, "Unknown Cmd"));
+
+ cqe_tree = proto_item_add_subtree(ti, ett_data);
+
+ nvme_publish_cqe_to_cmd_link(cqe_tree, cqe_tvb, hf_nvme_rdma_cmd_pkt, &cmd_ctx->n_cmd_ctx);
+ nvme_publish_cmd_latency(cqe_tree, &cmd_ctx->n_cmd_ctx, hf_nvme_rdma_cmd_latency);
+
+ dissect_nvme_rdma_cqe_status_8B(cqe_tree, cqe_tvb, cmd_ctx);
+
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_sqhd, cqe_tvb,
+ 8, 2, ENC_NA);
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_rsvd, cqe_tvb,
+ 10, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_cid, cqe_tvb,
+ 12, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_status, cqe_tvb,
+ 14, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_rdma_cqe_status_rsvd, cqe_tvb,
+ 14, 2, ENC_LITTLE_ENDIAN);
+}
+
+static void
+dissect_nvme_rdma_cqe(tvbuff_t *nvme_tvb, packet_info *pinfo,
+ proto_tree *root_tree, proto_tree *nvme_tree,
+ struct nvme_rdma_q_ctx *q_ctx)
+{
+ struct nvme_rdma_cmd_ctx *cmd_ctx;
+ guint16 cmd_id;
+
+ cmd_id = tvb_get_guint16(nvme_tvb, 12, ENC_LITTLE_ENDIAN);
+
+ if (!PINFO_FD_VISITED(pinfo)) {
+
+ cmd_ctx = (struct nvme_rdma_cmd_ctx*)
+ nvme_lookup_cmd_in_pending_list(&q_ctx->n_q_ctx, cmd_id);
+ if (!cmd_ctx)
+ goto not_found;
+
+ /* we have already seen this cqe, or an identical one */
+ if (cmd_ctx->n_cmd_ctx.cqe_pkt_num)
+ goto not_found;
+
+ cmd_ctx->n_cmd_ctx.cqe_pkt_num = pinfo->num;
+ nvme_add_cmd_cqe_to_done_list(&q_ctx->n_q_ctx, &cmd_ctx->n_cmd_ctx, cmd_id);
+ } else {
+ /* Already visited this frame */
+ cmd_ctx = (struct nvme_rdma_cmd_ctx*)
+ nvme_lookup_cmd_in_done_list(pinfo, &q_ctx->n_q_ctx, cmd_id);
+ if (!cmd_ctx)
+ goto not_found;
+ }
+
+ nvme_update_cmd_end_info(pinfo, &cmd_ctx->n_cmd_ctx);
+
+ if (cmd_ctx->n_cmd_ctx.fabric)
+ dissect_nvme_fabric_cqe(nvme_tvb, nvme_tree, cmd_ctx);
+ else
+ dissect_nvme_cqe(nvme_tvb, pinfo, root_tree, &cmd_ctx->n_cmd_ctx);
+ return;
+
+not_found:
+ proto_tree_add_item(nvme_tree, hf_nvme_rdma_to_host_unknown_data, nvme_tvb,
+ 0, NVME_FABRIC_CQE_SIZE, ENC_NA);
+}
+
+static void
+dissect_nvme_to_host(tvbuff_t *nvme_tvb, packet_info *pinfo,
+ proto_tree *root_tree, proto_tree *nvme_tree,
+ struct infinibandinfo *info,
+ struct nvme_rdma_q_ctx *q_ctx, guint len)
+{
+ switch (info->opCode) {
+ case RC_SEND_ONLY:
+ if (len == NVME_FABRIC_CQE_SIZE)
+ dissect_nvme_rdma_cqe(nvme_tvb, pinfo, root_tree, nvme_tree, q_ctx);
+ else
+ proto_tree_add_item(nvme_tree, hf_nvme_rdma_to_host_unknown_data, nvme_tvb,
+ 0, len, ENC_NA);
+ break;
+ default:
+ proto_tree_add_item(nvme_tree, hf_nvme_rdma_to_host_unknown_data, nvme_tvb,
+ 0, len, ENC_NA);
+ break;
+ }
+}
+
+static int
+dissect_nvme_ib(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ struct infinibandinfo *info = (struct infinibandinfo *)data;
+ conversation_infiniband_data *conv_data = NULL;
+ conversation_t *conv;
+ proto_tree *nvme_tree;
+ proto_item *ti;
+ struct nvme_rdma_q_ctx *q_ctx;
+ guint len = tvb_reported_length(tvb);
+
+ conv = find_ib_conversation(pinfo, &conv_data);
+ if (!conv)
+ return FALSE;
+
+ q_ctx = find_add_q_ctx(pinfo, conv);
+ if (!q_ctx)
+ return FALSE;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, NVME_FABRICS_RDMA);
+
+ ti = proto_tree_add_item(tree, proto_nvme_rdma, tvb, 0, len, ENC_NA);
+ nvme_tree = proto_item_add_subtree(ti, ett_data);
+
+ nvme_publish_qid(nvme_tree, hf_nvme_rdma_cmd_qid, q_ctx->n_q_ctx.qid);
+
+ if (conv_data->client_to_server)
+ dissect_nvme_from_host(tvb, pinfo, tree, nvme_tree, info, q_ctx, len);
+ else
+ dissect_nvme_to_host(tvb, pinfo, tree, nvme_tree, info, q_ctx, len);
+
+ return TRUE;
+}
+
+void
+proto_register_nvme_rdma(void)
+{
+ module_t *nvme_rdma_module;
+ static hf_register_info hf[] = {
+ /* IB RDMA CM fields */
+ { &hf_nvme_rdma_cm_req_recfmt,
+ { "Recfmt", "nvme-rdma.cm.req.recfmt",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_req_qid,
+ { "Qid", "nvme-rdma.cm.req.qid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_req_hrqsize,
+ { "HrqSize", "nvme-rdma.cm.req.hrqsize",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_req_hsqsize,
+ { "HsqSize", "nvme-rdma.cm.req.hsqsize",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_req_reserved,
+ { "Reserved", "nvme-rdma.cm.req.reserved",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_rsp_recfmt,
+ { "Recfmt", "nvme-rdma.cm.rsp.recfmt",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_rsp_crqsize,
+ { "CrqSize", "nvme-rdma.cm.rsp.crqsize",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_rsp_reserved,
+ { "Reserved", "nvme-rdma.cm.rsp.reserved",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_rej_recfmt,
+ { "Recfmt", "nvme-rdma.cm.rej.recfmt",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_rej_status,
+ { "Status", "nvme-rdma.cm.rej.status",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cm_rej_reserved,
+ { "Reserved", "nvme-rdma.cm.rej.reserved",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ /* IB RDMA NVMe Command fields */
+ { &hf_nvme_rdma_cmd,
+ { "Cmd", "nvme-rdma.cmd",
+ FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_from_host_unknown_data,
+ { "Dissection unsupported", "nvme-rdma.unknown_data",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_opc,
+ { "Opcode", "nvme-rdma.cmd.opc",
+ FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_rsvd,
+ { "Reserved", "nvme-rdma.cmd.rsvd",
+ FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_cid,
+ { "Command ID", "nvme-rdma.cmd.cid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_fctype,
+ { "Fabric Cmd Type", "nvme-rdma.cmd.fctype",
+ FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_rsvd1,
+ { "Reserved", "nvme-rdma.cmd.connect.rsvd1",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_sgl1,
+ { "SGL1", "nvme-rdma.cmd.connect.sgl1",
+ FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_recfmt,
+ { "Record Format", "nvme-rdma.cmd.connect.recfmt",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_qid,
+ { "Queue ID", "nvme-rdma.cmd.connect.qid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_sqsize,
+ { "SQ Size", "nvme-rdma.cmd.connect.sqsize",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_cattr,
+ { "Connect Attributes", "nvme-rdma.cmd.connect.cattr",
+ FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_rsvd2,
+ { "Reserved", "nvme-rdma.cmd.connect.rsvd2",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_kato,
+ { "Keep Alive Timeout", "nvme-rdma.cmd.connect.kato",
+ FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_connect_rsvd3,
+ { "Reserved", "nvme-rdma.cmd.connect.rsvd3",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_rsvd,
+ { "Reserved", "nvme-rdma.cmd.prop_attr.rsvd",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_rsvd1,
+ { "Reserved", "nvme-rdma.cmd.prop_attr.rsvd1",
+ FT_UINT8, BASE_HEX, NULL, 0xf8, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_size,
+ { "Property Size", "nvme-rdma.cmd.prop_attr.size",
+ FT_UINT8, BASE_HEX, NULL, 0x7, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_rsvd2,
+ { "Reserved", "nvme-rdma.cmd.prop_attr.rsvd2",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_offset,
+ { "Offset", "nvme-rdma.cmd.prop_attr.offset",
+ FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_get_rsvd3,
+ { "Reserved", "nvme-rdma.cmd.prop_attr.get.rsvd3",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_set_4B_value,
+ { "Value", "nvme-rdma.cmd.prop_attr.set.value.4B",
+ FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_set_4B_value_rsvd,
+ { "Reserved", "nvme-rdma.cmd.prop_attr.set.value.rsvd",
+ FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_set_8B_value,
+ { "Value", "nvme-rdma.cmd.prop_attr.set.value.8B",
+ FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_prop_attr_set_rsvd3,
+ { "Reserved", "nvme-rdma.cmd.prop_attr.set.rsvd3",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_generic_rsvd1,
+ { "Reserved", "nvme-rdma.cmd.generic.rsvd1",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_generic_field,
+ { "Fabric Cmd specific field", "nvme-rdma.cmd.generic.field",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ /* IB RDMA NVMe Response fields */
+ { &hf_nvme_rdma_cqe,
+ { "Cqe", "nvme-rdma.cqe",
+ FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_sts,
+ { "Cmd specific Status", "nvme-rdma.cqe.sts",
+ FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_sqhd,
+ { "SQ Head Pointer", "nvme-rdma.cqe.sqhd",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_rsvd,
+ { "Reserved", "nvme-rdma.cqe.rsvd",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_cid,
+ { "Command ID", "nvme-rdma.cqe.cid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_status,
+ { "Status", "nvme-rdma.cqe.status",
+ FT_UINT16, BASE_HEX, NULL, 0xfffe, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_status_rsvd,
+ { "Reserved", "nvme-rdma.cqe.status.rsvd",
+ FT_UINT16, BASE_HEX, NULL, 0x1, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_connect_cntlid,
+ { "Controller ID", "nvme-rdma.cqe.connect.cntrlid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_connect_authreq,
+ { "Authentication Required", "nvme-rdma.cqe.connect.authreq",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_connect_rsvd,
+ { "Reserved", "nvme-rdma.cqe.connect.rsvd",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cqe_prop_set_rsvd,
+ { "Reserved", "nvme-rdma.cqe.prop_set.rsvd",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_to_host_unknown_data,
+ { "Dissection unsupported", "nvme-rdma.unknown_data",
+ FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_rdma_cmd_pkt,
+ { "Fabric Cmd in", "nvme-rdma.cmd_pkt",
+ FT_FRAMENUM, BASE_NONE, NULL, 0,
+ "The Cmd for this transaction is in this frame", HFILL }
+ },
+ { &hf_nvme_rdma_cqe_pkt,
+ { "Fabric Cqe in", "nvme-rdma.cqe_pkt",
+ FT_FRAMENUM, BASE_NONE, NULL, 0,
+ "The Cqe for this transaction is in this frame", HFILL }
+ },
+ { &hf_nvme_rdma_cmd_latency,
+ { "Cmd Latency", "nvme-rdma.cmd_latency",
+ FT_DOUBLE, BASE_NONE, NULL, 0x0,
+ "The time between the command and completion, in usec", HFILL }
+ },
+ { &hf_nvme_rdma_cmd_qid,
+ { "Cmd Qid", "nvme-rdma.cmd.qid",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "Qid on which comamnd is issued", HFILL }
+ },
+ };
+ static gint *ett[] = {
+ &ett_cm,
+ &ett_data,
+ };
+
+ proto_nvme_rdma = proto_register_protocol("NVM Express Fabrics RDMA",
+ NVME_FABRICS_RDMA, "nvme-rdma");
+
+ proto_register_field_array(proto_nvme_rdma, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ /* Register preferences */
+ //nvme_rdma_module = prefs_register_protocol(proto_nvme_rdma, proto_reg_handoff_nvme_rdma);
+ nvme_rdma_module = prefs_register_protocol(proto_nvme_rdma, NULL);
+
+ range_convert_str(&gPORT_RANGE, NVME_RDMA_TCP_PORT_RANGE, MAX_TCP_PORT);
+ prefs_register_range_preference(nvme_rdma_module,
+ "subsystem_ports",
+ "Subsystem Ports Range",
+ "Range of NVMe Subsystem ports"
+ "(default " NVME_RDMA_TCP_PORT_RANGE ")",
+ &gPORT_RANGE, MAX_TCP_PORT);
+}
+
+void
+proto_reg_handoff_nvme_rdma(void)
+{
+ heur_dissector_add("infiniband.mad.cm.private", dissect_nvme_ib_cm,
+ "NVMe Fabrics RDMA CM packets",
+ "nvme_rdma_cm_private", proto_nvme_rdma, HEURISTIC_ENABLE);
+ heur_dissector_add("infiniband.payload", dissect_nvme_ib,
+ "NVMe Fabrics RDMA packets",
+ "nvme_rdma", proto_nvme_rdma, HEURISTIC_ENABLE);
+ ib_handler = find_dissector_add_dependency("infiniband", proto_nvme_rdma);
+ proto_ib = dissector_handle_get_protocol_index(ib_handler);
+}
+
+/*
+ * Editor modelines - http://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:
+ */
diff --git a/epan/dissectors/packet-nvme.c b/epan/dissectors/packet-nvme.c
new file mode 100644
index 0000000000..c4c61d46c7
--- /dev/null
+++ b/epan/dissectors/packet-nvme.c
@@ -0,0 +1,551 @@
+/* packet-nvme.c
+ * Routines for NVM Express dissection
+ * Copyright 2016
+ * Code by Parav Pandit
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* This file dissects NVMe packets received from the underlying
+ * fabric such as RDMA, FC.
+ * This is fabric agnostic dissector and depends on cmd_ctx and q_ctx
+ * It currently aligns to below specification.
+ * http://www.nvmexpress.org/wp-content/uploads/NVM-Express-1_2a.pdf
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <epan/packet.h>
+
+#include "packet-nvme.h"
+
+void proto_register_nvme(void);
+
+static int proto_nvme = -1;
+
+/* NVMe Cmd fields */
+
+static int hf_nvme_cmd_opc = -1;
+static int hf_nvme_cmd_rsvd = -1;
+static int hf_nvme_cmd_cid = -1;
+static int hf_nvme_cmd_fuse_op = -1;
+static int hf_nvme_cmd_psdt = -1;
+static int hf_nvme_cmd_nsid = -1;
+static int hf_nvme_cmd_rsvd1 = -1;
+static int hf_nvme_cmd_mptr = -1;
+static int hf_nvme_cmd_sgl = -1;
+static int hf_nvme_cmd_sgl_desc_type = -1;
+static int hf_nvme_cmd_sgl_desc_sub_type = -1;
+
+/* NVMe CQE fields */
+static int hf_nvme_cqe_sts = -1;
+static int hf_nvme_cqe_sqhd = -1;
+static int hf_nvme_cqe_rsvd = -1;
+static int hf_nvme_cqe_cid = -1;
+static int hf_nvme_cqe_status = -1;
+static int hf_nvme_cqe_status_rsvd = -1;
+
+/* tracking Cmd and its respective CQE */
+static int hf_nvme_cmd_pkt = -1;
+static int hf_nvme_cqe_pkt = -1;
+static int hf_nvme_cmd_latency = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_data = -1;
+
+#define NVME_AQ_OPC_DELETE_SQ 0x0
+#define NVME_AQ_OPC_CREATE_SQ 0x1
+#define NVME_AQ_OPC_GET_LOG_PAGE 0x2
+#define NVME_AQ_OPC_DELETE_CQ 0x4
+#define NVME_AQ_OPC_CREATE_CQ 0x5
+#define NVME_AQ_OPC_IDENTIFY 0x6
+#define NVME_AQ_OPC_ABORT 0x8
+#define NVME_AQ_OPC_SET_FEATURES 0x9
+#define NVME_AQ_OPC_GET_FEATURES 0xa
+#define NVME_AQ_OPC_ASYNC_EVE_REQ 0xc
+#define NVME_AQ_OPC_NS_MGMT 0xd
+#define NVME_AQ_OPC_FW_COMMIT 0x10
+#define NVME_AQ_OPC_FW_IMG_DOWNLOAD 0x11
+#define NVME_AQ_OPC_NS_ATTACH 0x15
+#define NVME_AQ_OPC_KEEP_ALIVE 0x18
+
+#define NVME_IOQ_OPC_FLUSH 0x0
+#define NVME_IOQ_OPC_WRITE 0x1
+#define NVME_IOQ_OPC_READ 0x2
+#define NVME_IOQ_OPC_WRITE_UNCORRECTABLE 0x4
+#define NVME_IOQ_OPC_COMPARE 0x5
+#define NVME_IOQ_OPC_WRITE_ZEROS 0x8
+#define NVME_IOQ_OPC_DATASET_MGMT 0x9
+#define NVME_IOQ_OPC_RESV_REG 0xd
+#define NVME_IOQ_OPC_RESV_REPORT 0xe
+#define NVME_IOQ_OPC_RESV_ACQUIRE 0x11
+#define NVME_IOQ_OPC_RESV_RELEASE 0x15
+
+#define NVME_CQE_SCT_GENERIC 0x0
+#define NVME_CQE_SCT_SPECIFIC 0x1
+#define NVME_CQE_SCT_MDI 0x2
+#define NVME_CQE_SCT_VENDOR 0x7
+
+#define NVME_CQE_SCODE_SUCCESS 0x0
+#define NVME_CQE_SCODE_INVALID_OPCODE 0x1
+#define NVME_CQE_SCODE_INVALID_FIELD 0x2
+#define NVME_CQE_SCODE_CID_CONFLICT 0x3
+#define NVME_CQE_SCODE_DATA_XFER_ERR 0x4
+#define NVME_CQE_SCODE_CMD_ABORTED 0x5
+#define NVME_CQE_SCODE_INTERNAL_ERR 0x6
+#define NVME_CQE_SCODE_CMD_ABORT_REQ 0x7
+#define NVME_CQE_SCODE_CMD_ABORT_SQD 0x8
+#define NVME_CQE_SCODE_CMD_ABORT_FF 0x9
+#define NVME_CQE_SCODE_CMD_ABORT_MF 0xa
+#define NVME_CQE_SCODE_INVALID_NS 0xb
+#define NVME_CQE_SCODE_CMD_SEQ_ERR 0xc
+
+#define NVME_CQE_SCODE_INVALID_SGL_DESC 0xd
+#define NVME_CQE_SCODE_INVALID_NUM_SGLS 0xe
+#define NVME_CQE_SCODE_INVALID_SGL_LEN 0xf
+#define NVME_CQE_SCODE_INVALID_MD_SGL_LEN 0x10
+#define NVME_CQE_SCODE_INVALID_SGL_DESC_TYPE 0x11
+#define NVME_CQE_SCODE_INVALID_CMB_USE 0x12
+#define NVME_CQE_SCODE_INVALID_PRP_OFFSET 0x13
+#define NVME_CQE_SCODE_INVALID_ATOMIC_WRITE_EXCEEDED 0x14
+#define NVME_CQE_SCODE_INVALID_SGL_OFFSET 0x16
+#define NVME_CQE_SCODE_INVALID_SGL_SUB_TYPE 0x17
+#define NVME_CQE_SCODE_INVALID_INCONSISTENT_HOSTID 0x18
+#define NVME_CQE_SCODE_INVALID_KA_TIMER_EXPIRED 0x19
+#define NVME_CQE_SCODE_INVALID_KA_TIMEOUT_INVALID 0x1a
+
+static const value_string aq_opc_tbl[] = {
+ { NVME_AQ_OPC_DELETE_SQ, "Delete SQ"},
+ { NVME_AQ_OPC_CREATE_SQ, "Create SQ"},
+ { NVME_AQ_OPC_GET_LOG_PAGE, "Get Log Page"},
+ { NVME_AQ_OPC_DELETE_CQ, "Delete CQ"},
+ { NVME_AQ_OPC_CREATE_CQ, "Create CQ"},
+ { NVME_AQ_OPC_IDENTIFY, "Identify"},
+ { NVME_AQ_OPC_ABORT, "Abort"},
+ { NVME_AQ_OPC_SET_FEATURES, "Set Features"},
+ { NVME_AQ_OPC_GET_FEATURES, "Get Features"},
+ { NVME_AQ_OPC_ASYNC_EVE_REQ, "Async Event Request"},
+ { NVME_AQ_OPC_NS_MGMT, "Namespace Management"},
+ { NVME_AQ_OPC_FW_COMMIT, "Firmware Commit"},
+ { NVME_AQ_OPC_FW_IMG_DOWNLOAD, "Firmware Image Download"},
+ { NVME_AQ_OPC_NS_ATTACH, "Namespace attach"},
+ { NVME_AQ_OPC_KEEP_ALIVE, "Kepp Alive"},
+ { 0, NULL}
+};
+
+static const value_string ioq_opc_tbl[] = {
+ { NVME_IOQ_OPC_FLUSH, "Flush"},
+ { NVME_IOQ_OPC_WRITE, "Write"},
+ { NVME_IOQ_OPC_READ, "Read"},
+ { NVME_IOQ_OPC_WRITE_UNCORRECTABLE, "Write Uncorrectable"},
+ { NVME_IOQ_OPC_COMPARE, "Compare"},
+ { NVME_IOQ_OPC_WRITE_ZEROS, "Write Zero"},
+ { NVME_IOQ_OPC_DATASET_MGMT, "Dataset Management"},
+ { NVME_IOQ_OPC_RESV_REG, "Reserve Register"},
+ { NVME_IOQ_OPC_RESV_REPORT, "Reserve Report"},
+ { NVME_IOQ_OPC_RESV_ACQUIRE, "Reserve Acquire"},
+ { NVME_IOQ_OPC_RESV_RELEASE, "Reserve Release"},
+ { 0, NULL}
+};
+
+#define NVME_CMD_SGL_DATA_DESC 0x0
+#define NVME_CMD_SGL_BIT_BUCKET_DESC 0x1
+#define NVME_CMD_SGL_SEGMENT_DESC 0x2
+#define NVME_CMD_SGL_LAST_SEGMENT_DESC 0x3
+#define NVME_CMD_SGL_KEYED_DATA_DESC 0x4
+#define NVME_CMD_SGL_VENDOR_DESC 0xf
+
+static const value_string sgl_type_tbl[] = {
+ { NVME_CMD_SGL_DATA_DESC, "Data Block"},
+ { NVME_CMD_SGL_BIT_BUCKET_DESC, "Bit Bucket"},
+ { NVME_CMD_SGL_SEGMENT_DESC, "Segment"},
+ { NVME_CMD_SGL_LAST_SEGMENT_DESC, "Last Segment"},
+ { NVME_CMD_SGL_KEYED_DATA_DESC, "Keyed Data Block"},
+ { NVME_CMD_SGL_VENDOR_DESC, "Vendor Specific"},
+ { 0, NULL}
+};
+
+#define NVME_CMD_SGL_SUB_DESC_ADDR 0x0
+#define NVME_CMD_SGL_SUB_DESC_OFFSET 0x1
+#define NVME_CMD_SGL_SUB_DESC_TRANSPORT 0xf
+
+static const value_string sgl_sub_type_tbl[] = {
+ { NVME_CMD_SGL_SUB_DESC_ADDR, "Address"},
+ { NVME_CMD_SGL_SUB_DESC_OFFSET, "Offset"},
+ { NVME_CMD_SGL_SUB_DESC_TRANSPORT, "Transport specific"},
+ { 0, NULL}
+};
+
+void
+nvme_publish_qid(proto_tree *tree, int field_index, guint16 qid)
+{
+ proto_item *cmd_ref_item;
+
+ cmd_ref_item = proto_tree_add_uint_format_value(tree, field_index, NULL,
+ 0, 0, qid,
+ qid ? "%d (IOQ)" : "%d (AQ)",
+ qid);
+
+ PROTO_ITEM_SET_GENERATED(cmd_ref_item);
+}
+
+static void nvme_build_pending_cmd_key(wmem_tree_key_t *cmd_key, guint32 *key)
+{
+ cmd_key[0].length = 1;
+ cmd_key[0].key = key;
+ cmd_key[1].length = 0;
+ cmd_key[1].key = NULL;
+}
+
+static void
+nvme_build_done_cmd_key(wmem_tree_key_t *cmd_key, guint32 *key, guint32 *frame_num)
+{
+ cmd_key[0].length = 1;
+ cmd_key[0].key = key;
+ cmd_key[1].length = 1;
+ cmd_key[1].key = frame_num;
+ cmd_key[2].length = 0;
+ cmd_key[2].key = NULL;
+}
+
+void
+nvme_add_cmd_to_pending_list(packet_info *pinfo, struct nvme_q_ctx *q_ctx,
+ struct nvme_cmd_ctx *cmd_ctx,
+ void *ctx, guint16 cmd_id)
+{
+ wmem_tree_key_t cmd_key[3];
+ guint32 key = cmd_id;
+
+ cmd_ctx->cmd_pkt_num = pinfo->num;
+ cmd_ctx->cqe_pkt_num = 0;
+ cmd_ctx->cmd_start_time = pinfo->abs_ts;
+ nstime_set_zero(&cmd_ctx->cmd_end_time);
+
+ /* this is a new cmd, create a new command context and map it to the
+ unmatched table
+ */
+ nvme_build_pending_cmd_key(cmd_key, &key);
+ wmem_tree_insert32_array(q_ctx->pending_cmds, cmd_key, (void *)ctx);
+}
+
+void* nvme_lookup_cmd_in_pending_list(struct nvme_q_ctx *q_ctx, guint16 cmd_id)
+{
+ wmem_tree_key_t cmd_key[3];
+ guint32 key = cmd_id;
+
+ nvme_build_pending_cmd_key(cmd_key, &key);
+ return wmem_tree_lookup32_array(q_ctx->pending_cmds, cmd_key);
+}
+
+void
+nvme_add_cmd_cqe_to_done_list(struct nvme_q_ctx *q_ctx,
+ struct nvme_cmd_ctx *cmd_ctx, guint16 cmd_id)
+{
+ wmem_tree_key_t cmd_key[3];
+ guint32 key = cmd_id;
+ guint32 frame_num;
+
+ nvme_build_done_cmd_key(cmd_key, &key, &frame_num);
+
+ /* found matchng entry. Add entries to the matched table for both cmd and cqe.
+ */
+ frame_num = cmd_ctx->cqe_pkt_num;
+ wmem_tree_insert32_array(q_ctx->done_cmds, cmd_key, (void*)cmd_ctx);
+
+ frame_num = cmd_ctx->cmd_pkt_num;
+ wmem_tree_insert32_array(q_ctx->done_cmds, cmd_key, (void*)cmd_ctx);
+}
+
+void*
+nvme_lookup_cmd_in_done_list(packet_info *pinfo, struct nvme_q_ctx *q_ctx,
+ guint16 cmd_id)
+{
+ wmem_tree_key_t cmd_key[3];
+ guint32 key = cmd_id;
+ guint32 frame_num = pinfo->num;
+
+ nvme_build_done_cmd_key(cmd_key, &key, &frame_num);
+
+ return wmem_tree_lookup32_array(q_ctx->done_cmds, cmd_key);
+}
+
+void
+nvme_publish_cmd_latency(proto_tree *tree, struct nvme_cmd_ctx *cmd_ctx,
+ int field_index)
+{
+ proto_item *cmd_ref_item;
+ nstime_t ns;
+ double cmd_latency;
+
+ nstime_delta(&ns, &cmd_ctx->cmd_end_time, &cmd_ctx->cmd_start_time);
+ cmd_latency = nstime_to_msec(&ns);
+ cmd_ref_item = proto_tree_add_double_format_value(tree, field_index,
+ NULL, 0, 0, cmd_latency,
+ "%.3f ms", cmd_latency);
+ PROTO_ITEM_SET_GENERATED(cmd_ref_item);
+}
+
+void nvme_update_cmd_end_info(packet_info *pinfo, struct nvme_cmd_ctx *cmd_ctx)
+{
+ cmd_ctx->cmd_end_time = pinfo->abs_ts;
+ cmd_ctx->cqe_pkt_num = pinfo->num;
+}
+
+void
+nvme_publish_cqe_to_cmd_link(proto_tree *cqe_tree, tvbuff_t *nvme_tvb,
+ int hf_index, struct nvme_cmd_ctx *cmd_ctx)
+{
+ proto_item *cqe_ref_item;
+ cqe_ref_item = proto_tree_add_uint(cqe_tree, hf_index,
+ nvme_tvb, 0, 0, cmd_ctx->cmd_pkt_num);
+ PROTO_ITEM_SET_GENERATED(cqe_ref_item);
+}
+
+void
+nvme_publish_cmd_to_cqe_link(proto_tree *cmd_tree, tvbuff_t *cmd_tvb,
+ int hf_index, struct nvme_cmd_ctx *cmd_ctx)
+{
+ proto_item *cmd_ref_item;
+
+ if (cmd_ctx->cqe_pkt_num) {
+ cmd_ref_item = proto_tree_add_uint(cmd_tree, hf_index,
+ cmd_tvb, 0, 0, cmd_ctx->cqe_pkt_num);
+ PROTO_ITEM_SET_GENERATED(cmd_ref_item);
+ }
+}
+
+void dissect_nvme_cmd_sgl(tvbuff_t *cmd_tvb, proto_tree *cmd_tree,
+ int field_index)
+{
+ proto_item *ti, *sgl_tree, *type_item, *sub_type_item;
+ guint8 sgl_identifier, desc_type, desc_sub_type;
+ int offset = 24;
+
+ ti = proto_tree_add_item(cmd_tree, field_index, cmd_tvb, offset,
+ 16, ENC_NA);
+ sgl_tree = proto_item_add_subtree(ti, ett_data);
+
+ sgl_identifier = tvb_get_guint8(cmd_tvb, offset + 15);
+ desc_type = (sgl_identifier & 0xff) >> 4;
+ desc_sub_type = sgl_identifier & 0x0f;
+
+ type_item = proto_tree_add_item(sgl_tree, hf_nvme_cmd_sgl_desc_type,
+ cmd_tvb, offset + 15, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(type_item, " %s",
+ val_to_str(desc_type, sgl_type_tbl, "Reserved"));
+
+ sub_type_item = proto_tree_add_item(sgl_tree, hf_nvme_cmd_sgl_desc_sub_type,
+ cmd_tvb,
+ offset + 15, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(sub_type_item, " %s",
+ val_to_str(desc_sub_type, sgl_sub_type_tbl, "Reserved"));
+}
+
+void
+dissect_nvme_cmd(tvbuff_t *nvme_tvb, packet_info *pinfo, proto_tree *root_tree,
+ struct nvme_q_ctx *q_ctx, struct nvme_cmd_ctx *cmd_ctx)
+{
+ proto_tree *cmd_tree;
+ tvbuff_t *cmd_tvb;
+ proto_item *ti, *opc_item;
+ guint8 opcode;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "NVMe");
+ ti = proto_tree_add_item(root_tree, proto_nvme, nvme_tvb, 0,
+ NVME_CMD_SIZE, ENC_NA);
+ proto_item_append_text(ti, " (Cmd)");
+ cmd_tree = proto_item_add_subtree(ti, ett_data);
+ cmd_tvb = tvb_new_subset_length(nvme_tvb, 0, NVME_CMD_SIZE);
+
+ opcode = tvb_get_guint8(cmd_tvb, 0);
+ opc_item = proto_tree_add_item(cmd_tree, hf_nvme_cmd_opc, cmd_tvb,
+ 0, 1, ENC_LITTLE_ENDIAN);
+ if (q_ctx->qid)
+ proto_item_append_text(opc_item, " %s",
+ val_to_str(opcode, ioq_opc_tbl, "Reserved"));
+ else
+ proto_item_append_text(opc_item, " %s",
+ val_to_str(opcode, aq_opc_tbl, "Reserved"));
+
+ nvme_publish_cmd_to_cqe_link(cmd_tree, cmd_tvb, hf_nvme_cqe_pkt, cmd_ctx);
+
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_fuse_op, cmd_tvb,
+ 1, 1, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_rsvd, cmd_tvb,
+ 1, 1, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_psdt, cmd_tvb,
+ 1, 1, ENC_NA);
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_cid, cmd_tvb,
+ 2, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_nsid, cmd_tvb,
+ 4, 4, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_rsvd1, cmd_tvb,
+ 8, 8, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cmd_tree, hf_nvme_cmd_mptr, cmd_tvb,
+ 16, 8, ENC_LITTLE_ENDIAN);
+
+ dissect_nvme_cmd_sgl(cmd_tvb, cmd_tree, hf_nvme_cmd_sgl);
+}
+
+void
+dissect_nvme_cqe(tvbuff_t *nvme_tvb, packet_info *pinfo, proto_tree *root_tree,
+ struct nvme_cmd_ctx *cmd_ctx)
+{
+ proto_tree *cqe_tree;
+ proto_item *ti;
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "NVMe");
+ ti = proto_tree_add_item(root_tree, proto_nvme, nvme_tvb, 0,
+ NVME_CQE_SIZE, ENC_NA);
+ proto_item_append_text(ti, " (Cqe)");
+ cqe_tree = proto_item_add_subtree(ti, ett_data);
+
+ nvme_publish_cqe_to_cmd_link(cqe_tree, nvme_tvb, hf_nvme_cmd_pkt, cmd_ctx);
+ nvme_publish_cmd_latency(cqe_tree, cmd_ctx, hf_nvme_cmd_latency);
+
+ proto_tree_add_item(cqe_tree, hf_nvme_cqe_sts, nvme_tvb,
+ 0, 8, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_cqe_sqhd, nvme_tvb,
+ 8, 2, ENC_NA);
+ proto_tree_add_item(cqe_tree, hf_nvme_cqe_rsvd, nvme_tvb,
+ 10, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_cqe_cid, nvme_tvb,
+ 12, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_cqe_status, nvme_tvb,
+ 14, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cqe_tree, hf_nvme_cqe_status_rsvd, nvme_tvb,
+ 14, 2, ENC_LITTLE_ENDIAN);
+}
+
+void
+proto_register_nvme(void)
+{
+ static hf_register_info hf[] = {
+ /* NVMe Command fields */
+ { &hf_nvme_cmd_opc,
+ { "Opcode", "nvme.cmd.opc",
+ FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_fuse_op,
+ { "Fuse Operation", "nvme.cmd.fuse_op",
+ FT_UINT8, BASE_HEX, NULL, 0x3, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_rsvd,
+ { "Reserved", "nvme.cmd.rsvd",
+ FT_UINT8, BASE_HEX, NULL, 0x3c, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_psdt,
+ { "PRP Or SGL", "nvme.cmd.psdt",
+ FT_UINT8, BASE_HEX, NULL, 0xc0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_cid,
+ { "Command ID", "nvme.cmd.cid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_nsid,
+ { "Namespace Id", "nvme.cmd.nsid",
+ FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_rsvd1,
+ { "Reserved", "nvme.cmd.rsvd1",
+ FT_UINT64, BASE_HEX, NULL, 0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_mptr,
+ { "Metadata Pointer", "nvme.cmd.mptr",
+ FT_UINT64, BASE_HEX, NULL, 0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_sgl,
+ { "SGL1", "nvme.cmd.sgl1",
+ FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_sgl_desc_sub_type,
+ { "Descriptor Sub Type", "nvme.cmd.sgl.subtype",
+ FT_UINT8, BASE_HEX, NULL, 0x0f, NULL, HFILL}
+ },
+
+ { &hf_nvme_cmd_sgl_desc_type,
+ { "Descriptor Type", "nvme.cmd.sgl.type",
+ FT_UINT8, BASE_HEX, NULL, 0xf0, NULL, HFILL}
+ },
+
+ /* NVMe Response fields */
+ { &hf_nvme_cqe_sts,
+ { "Cmd specific Status", "nvme.cqe.sts",
+ FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_cqe_sqhd,
+ { "SQ Head Pointer", "nvme.cqe.sqhd",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_cqe_rsvd,
+ { "Reserved", "nvme.cqe.sqhd",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_cqe_cid,
+ { "Command ID", "nvme.cqe.cid",
+ FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
+ },
+ { &hf_nvme_cqe_status,
+ { "Status", "nvme.cqe.status",
+ FT_UINT16, BASE_HEX, NULL, 0xfffe, NULL, HFILL}
+ },
+ { &hf_nvme_cqe_status_rsvd,
+ { "Reserved", "nvme.cqe.status.rsvd",
+ FT_UINT16, BASE_HEX, NULL, 0x1, NULL, HFILL}
+ },
+ { &hf_nvme_cmd_pkt,
+ { "Cmd in", "nvme.cmd_pkt",
+ FT_FRAMENUM, BASE_NONE, NULL, 0,
+ "The Cmd for this transaction is in this frame", HFILL }
+ },
+ { &hf_nvme_cqe_pkt,
+ { "Cqe in", "nvme.cqe_pkt",
+ FT_FRAMENUM, BASE_NONE, NULL, 0,
+ "The Cqe for this transaction is in this frame", HFILL }
+ },
+ { &hf_nvme_cmd_latency,
+ { "Cmd Latency", "nvme.cmd_latency",
+ FT_DOUBLE, BASE_NONE, NULL, 0x0,
+ "The time between the command and completion, in usec", HFILL }
+ },
+ };
+ static gint *ett[] = {
+ &ett_data,
+ };
+
+ proto_nvme = proto_register_protocol("NVM Express", "nvme", "nvme");
+
+ proto_register_field_array(proto_nvme, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+/*
+ * Editor modelines - http://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:
+ */
diff --git a/epan/dissectors/packet-nvme.h b/epan/dissectors/packet-nvme.h
new file mode 100644
index 0000000000..7f2fcdd2d2
--- /dev/null
+++ b/epan/dissectors/packet-nvme.h
@@ -0,0 +1,95 @@
+/* packet-nvme.h
+ * data structures for NVMe Dissection
+ * Copyright 2016
+ * Code by Parav Pandit
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef _PACKET_NVME_H_
+#define _PACKET_NVME_H_
+
+#define NVME_CMD_SIZE 64
+#define NVME_CQE_SIZE 16
+
+struct nvme_q_ctx {
+ wmem_tree_t *pending_cmds;
+ wmem_tree_t *done_cmds;
+ guint16 qid;
+};
+
+struct nvme_cmd_ctx {
+ guint32 cmd_pkt_num; /* pkt number of the cmd */
+ guint32 cqe_pkt_num; /* pkt number of the cqe */
+
+ nstime_t cmd_start_time;
+ nstime_t cmd_end_time;
+ gboolean fabric; /* indicate whether cmd fabric type or not */
+};
+
+void
+nvme_publish_qid(proto_tree *tree, int field_index, guint16 qid);
+
+void
+nvme_publish_cmd_latency(proto_tree *tree, struct nvme_cmd_ctx *cmd_ctx,
+ int field_index);
+void
+nvme_publish_cqe_to_cmd_link(proto_tree *cqe_tree, tvbuff_t *cqe_tvb,
+ int hf_index, struct nvme_cmd_ctx *cmd_ctx);
+void
+nvme_publish_cmd_to_cqe_link(proto_tree *cmd_tree, tvbuff_t *cqe_tvb,
+ int hf_index, struct nvme_cmd_ctx *cmd_ctx);
+
+void nvme_update_cmd_end_info(packet_info *pinfo, struct nvme_cmd_ctx *cmd_ctx);
+
+void
+nvme_add_cmd_to_pending_list(packet_info *pinfo, struct nvme_q_ctx *q_ctx,
+ struct nvme_cmd_ctx *cmd_ctx,
+ void *ctx, guint16 cmd_id);
+void* nvme_lookup_cmd_in_pending_list(struct nvme_q_ctx *q_ctx, guint16 cmd_id);
+
+void
+nvme_add_cmd_cqe_to_done_list(struct nvme_q_ctx *q_ctx,
+ struct nvme_cmd_ctx *cmd_ctx, guint16 cmd_id);
+void*
+nvme_lookup_cmd_in_done_list(packet_info *pinfo, struct nvme_q_ctx *q_ctx,
+ guint16 cmd_id);
+
+void dissect_nvme_cmd_sgl(tvbuff_t *cmd_tvb, proto_tree *cmd_tree,
+ int field_index);
+void
+dissect_nvme_cmd(tvbuff_t *nvme_tvb, packet_info *pinfo, proto_tree *root_tree,
+ struct nvme_q_ctx *q_ctx, struct nvme_cmd_ctx *cmd_ctx);
+void
+dissect_nvme_cqe(tvbuff_t *nvme_tvb, packet_info *pinfo, proto_tree *root_tree,
+ struct nvme_cmd_ctx *cmd_ctx);
+
+#endif
+
+/*
+ * Editor modelines - http://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:
+ */