Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-nvme-tcp.c
Line
Count
Source
1
/* packet-nvme-tcp.c
2
 * Routines for NVM Express over Fabrics(TCP) dissection
3
 * Code by Solganik Alexander <solganik@gmail.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * Copyright (C) 2019 Lightbits Labs Ltd. - All Rights Reserved
14
*/
15
16
/*
17
 NVM Express is high speed interface for accessing solid state drives.
18
 NVM Express specifications are maintained by NVM Express industry
19
 association at http://www.nvmexpress.org.
20
21
 This file adds support to dissect NVM Express over fabrics packets
22
 for TCP. This adds very basic support for dissecting commands
23
 completions.
24
25
 Current dissection supports dissection of
26
 (a) NVMe cmd and cqe
27
 (b) NVMe Fabric command and cqe
28
 As part of it, it also calculates cmd completion latencies.
29
30
 NVM Express TCP TCP port assigned by IANA that maps to NVMe-oF service
31
 TCP port can be found at
32
 http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=NVM+Express
33
34
 */
35
36
#include "config.h"
37
#include <epan/packet.h>
38
#include <epan/prefs.h>
39
#include <epan/conversation.h>
40
#include <epan/crc32-tvb.h>
41
#include <epan/expert.h>
42
#include <epan/tfs.h>
43
#include <wsutil/array.h>
44
#include "packet-tcp.h"
45
#include "packet-nvme.h"
46
47
#include "packet-tls.h"
48
49
static int proto_nvme_tcp;
50
static dissector_handle_t nvmet_tcp_handle;
51
static dissector_handle_t nvmet_tls_handle;
52
53
16
#define NVME_TCP_PORT_RANGE    "4420,8009" /* IANA registered */
54
55
78
#define NVME_TCP_HEADER_SIZE 8
56
12
#define PDU_LEN_OFFSET_FROM_HEADER 4
57
static range_t *gPORT_RANGE;
58
static bool nvme_tcp_check_hdgst;
59
static bool nvme_tcp_check_ddgst;
60
6
#define NVME_TCP_DATA_PDU_SIZE 24
61
/* H2CTermReq/C2HTermReq header: the 8-byte common header, FES at offset 8,
62
 * FEI at offset 10 and reserved bytes out to offset 24, where the terminated
63
 * PDU's header starts. */
64
0
#define NVME_TCP_TERMREQ_PDU_SIZE 24
65
66
enum nvme_tcp_pdu_type {
67
    nvme_tcp_icreq = 0x0,
68
    nvme_tcp_icresp = 0x1,
69
    nvme_tcp_h2c_term = 0x2,
70
    nvme_tcp_c2h_term = 0x3,
71
    nvme_tcp_cmd = 0x4,
72
    nvme_tcp_rsp = 0x5,
73
    nvme_tcp_h2c_data = 0x6,
74
    nvme_tcp_c2h_data = 0x7,
75
    nvme_tcp_r2t = 0x9,
76
    nvme_tcp_kdreq = 0xa,
77
    nvme_tcp_kdresp = 0xb,
78
    NVMET_MAX_PDU_TYPE = nvme_tcp_kdresp
79
};
80
81
static const value_string nvme_tcp_pdu_type_vals[] = {
82
    { nvme_tcp_icreq, "ICReq" },
83
    { nvme_tcp_icresp, "ICResp" },
84
    { nvme_tcp_h2c_term, "H2CTerm" },
85
    { nvme_tcp_c2h_term, "C2HTerm" },
86
    { nvme_tcp_cmd, "CapsuleCommand" },
87
    { nvme_tcp_rsp, "CapsuleResponse" },
88
    { nvme_tcp_h2c_data, "H2CData" },
89
    { nvme_tcp_c2h_data, "C2HData" },
90
    { nvme_tcp_r2t, "Ready To Transfer" },
91
    { nvme_tcp_kdreq, "Kickstart Discovery Request" },
92
    { nvme_tcp_kdresp, "Kickstart Discovery Response" },
93
    { 0, NULL }
94
};
95
96
static const value_string nvme_tcp_termreq_fes[] = {
97
    {0x0, "Reserved"                        },
98
    {0x1, "Invalid PDU Header Field"        },
99
    {0x2, "PDU Sequence Error"              },
100
    {0x3, "Header Digest Error"             },
101
    {0x4, "Data Transfer Out of Range"      },
102
    {0x5, "R2T Limit Exceeded"              },
103
    {0x6, "Unsupported Parameter"           },
104
    {0,   NULL                              },
105
};
106
107
enum nvme_tcp_fatal_error_status
108
{
109
    NVME_TCP_FES_INVALID_PDU_HDR =      0x01,
110
    NVME_TCP_FES_PDU_SEQ_ERR =          0x02,
111
    NVME_TCP_FES_HDR_DIGEST_ERR =       0x03,
112
    NVME_TCP_FES_DATA_OUT_OF_RANGE =    0x04,
113
    NVME_TCP_FES_R2T_LIMIT_EXCEEDED =   0x05,
114
    NVME_TCP_FES_DATA_LIMIT_EXCEEDED =  0x05,
115
    NVME_TCP_FES_UNSUPPORTED_PARAM =    0x06,
116
};
117
118
enum nvme_tcp_pdu_flags {
119
    NVME_TCP_F_HDGST         = (1 << 0),
120
    NVME_TCP_F_DDGST         = (1 << 1),
121
    NVME_TCP_F_DATA_LAST     = (1 << 2),
122
    NVME_TCP_F_DATA_SUCCESS  = (1 << 3),
123
};
124
125
126
enum nvme_tcp_digest_option {
127
    NVME_TCP_HDR_DIGEST_ENABLE = (1 << 0),
128
    NVME_TCP_DATA_DIGEST_ENABLE = (1 << 1),
129
};
130
131
132
0
#define NVME_FABRIC_CMD_SIZE NVME_CMD_SIZE
133
0
#define NVME_FABRIC_CQE_SIZE NVME_CQE_SIZE
134
4
#define NVME_TCP_DIGEST_LENGTH  4
135
136
struct nvme_tcp_q_ctx {
137
    struct nvme_q_ctx n_q_ctx;
138
    /* H2CData requests, keyed [frame][cid]; kept apart from
139
     * n_q_ctx.data_requests (keyed SGL descriptors, other key shape) */
140
    wmem_tree_t *h2c_requests;
141
};
142
143
struct nvme_tcp_cmd_ctx {
144
    struct nvme_cmd_ctx n_cmd_ctx;
145
};
146
147
void proto_reg_handoff_nvme_tcp(void);
148
void proto_register_nvme_tcp(void);
149
150
151
static int hf_nvme_tcp_type;
152
static int hf_nvme_tcp_flags;
153
static int hf_pdu_flags_hdgst;
154
static int hf_pdu_flags_ddgst;
155
static int hf_pdu_flags_data_last;
156
static int hf_pdu_flags_data_success;
157
158
static int * const nvme_tcp_pdu_flags[] = {
159
    &hf_pdu_flags_hdgst,
160
    &hf_pdu_flags_ddgst,
161
    &hf_pdu_flags_data_last,
162
    &hf_pdu_flags_data_success,
163
    NULL
164
};
165
166
static int hf_nvme_tcp_hdgst;
167
static int hf_nvme_tcp_ddgst;
168
static int hf_nvme_tcp_hlen;
169
static int hf_nvme_tcp_pdo;
170
static int hf_nvme_tcp_plen;
171
static int hf_nvme_tcp_hdgst_status;
172
static int hf_nvme_tcp_ddgst_status;
173
174
/* NVMe tcp icreq/icresp fields */
175
static int hf_nvme_tcp_icreq;
176
static int hf_nvme_tcp_icreq_pfv;
177
static int hf_nvme_tcp_icreq_maxr2t;
178
static int hf_nvme_tcp_icreq_hpda;
179
static int hf_nvme_tcp_icreq_digest;
180
static int hf_nvme_tcp_icresp;
181
static int hf_nvme_tcp_icresp_pfv;
182
static int hf_nvme_tcp_icresp_cpda;
183
static int hf_nvme_tcp_icresp_digest;
184
static int hf_nvme_tcp_icresp_maxdata;
185
186
/* NVMe tcp c2h/h2c termreq fields */
187
static int hf_nvme_tcp_c2htermreq;
188
static int hf_nvme_tcp_c2htermreq_fes;
189
static int hf_nvme_tcp_c2htermreq_phfo;
190
static int hf_nvme_tcp_c2htermreq_phd;
191
static int hf_nvme_tcp_c2htermreq_upfo;
192
static int hf_nvme_tcp_c2htermreq_reserved;
193
static int hf_nvme_tcp_c2htermreq_data;
194
static int hf_nvme_tcp_h2ctermreq;
195
static int hf_nvme_tcp_h2ctermreq_fes;
196
static int hf_nvme_tcp_h2ctermreq_phfo;
197
static int hf_nvme_tcp_h2ctermreq_phd;
198
static int hf_nvme_tcp_h2ctermreq_upfo;
199
static int hf_nvme_tcp_h2ctermreq_reserved;
200
static int hf_nvme_tcp_h2ctermreq_data;
201
202
/* NVMe fabrics command */
203
static int hf_nvme_fabrics_cmd_cid;
204
205
/* NVMe fabrics command data*/
206
static int hf_nvme_fabrics_cmd_data;
207
static int hf_nvme_tcp_unknown_data;
208
209
static int hf_nvme_tcp_r2t_pdu;
210
static int hf_nvme_tcp_r2t_offset;
211
static int hf_nvme_tcp_r2t_length;
212
static int hf_nvme_tcp_r2t_resvd;
213
214
/* tracking Cmd and its respective CQE */
215
static int hf_nvme_tcp_cmd_pkt;
216
static int hf_nvme_fabrics_cmd_qid;
217
218
/* Data response fields */
219
static int hf_nvme_tcp_data_pdu;
220
static int hf_nvme_tcp_pdu_ttag;
221
static int hf_nvme_tcp_data_pdu_data_offset;
222
static int hf_nvme_tcp_data_pdu_data_length;
223
static int hf_nvme_tcp_data_pdu_data_resvd;
224
225
static int ett_nvme_tcp;
226
227
static expert_field ei_nvme_tcp_termreq_short;
228
229
static unsigned
230
get_nvme_tcp_pdu_len(packet_info *pinfo _U_,
231
                     tvbuff_t *tvb,
232
                     int offset,
233
                     void* data _U_)
234
12
{
235
12
    return tvb_get_letohl(tvb, offset + PDU_LEN_OFFSET_FROM_HEADER);
236
12
}
237
238
static void
239
dissect_nvme_tcp_icreq(tvbuff_t *tvb,
240
                       packet_info *pinfo,
241
                       int offset,
242
                       proto_tree *tree)
243
0
{
244
0
    proto_item *tf;
245
0
    proto_item *icreq_tree;
246
247
0
    col_set_str(pinfo->cinfo, COL_INFO, "Initialize Connection Request");
248
0
    tf = proto_tree_add_item(tree, hf_nvme_tcp_icreq, tvb, offset, 8, ENC_NA);
249
0
    icreq_tree = proto_item_add_subtree(tf, ett_nvme_tcp);
250
251
0
    proto_tree_add_item(icreq_tree, hf_nvme_tcp_icreq_pfv, tvb, offset, 2,
252
0
            ENC_LITTLE_ENDIAN);
253
0
    proto_tree_add_item(icreq_tree, hf_nvme_tcp_icreq_hpda, tvb, offset + 2, 1,
254
0
            ENC_NA);
255
0
    proto_tree_add_item(icreq_tree, hf_nvme_tcp_icreq_digest, tvb, offset + 3,
256
0
            1, ENC_NA);
257
0
    proto_tree_add_item(icreq_tree, hf_nvme_tcp_icreq_maxr2t, tvb, offset + 4,
258
0
            4, ENC_LITTLE_ENDIAN);
259
0
}
260
261
static void
262
dissect_nvme_tcp_icresp(tvbuff_t *tvb,
263
                        packet_info *pinfo,
264
                        int offset,
265
                        proto_tree *tree)
266
0
{
267
0
    proto_item *tf;
268
0
    proto_item *icresp_tree;
269
270
0
    col_set_str(pinfo->cinfo, COL_INFO, "Initialize Connection Response");
271
0
    tf = proto_tree_add_item(tree, hf_nvme_tcp_icresp, tvb, offset, 8, ENC_NA);
272
0
    icresp_tree = proto_item_add_subtree(tf, ett_nvme_tcp);
273
274
0
    proto_tree_add_item(icresp_tree, hf_nvme_tcp_icresp_pfv, tvb, offset, 2,
275
0
            ENC_LITTLE_ENDIAN);
276
0
    proto_tree_add_item(icresp_tree, hf_nvme_tcp_icresp_cpda, tvb, offset + 2,
277
0
            1, ENC_NA);
278
0
    proto_tree_add_item(icresp_tree, hf_nvme_tcp_icresp_digest, tvb, offset + 3,
279
0
            1, ENC_NA);
280
0
    proto_tree_add_item(icresp_tree, hf_nvme_tcp_icresp_maxdata, tvb,
281
0
            offset + 4, 4, ENC_LITTLE_ENDIAN);
282
0
}
283
284
static struct nvme_tcp_cmd_ctx*
285
bind_cmd_to_qctx(packet_info *pinfo,
286
                 struct nvme_q_ctx *q_ctx,
287
                 uint16_t cmd_id)
288
7
{
289
7
    struct nvme_tcp_cmd_ctx *ctx;
290
291
    /* wireshark will dissect same packet multiple times
292
     * when display is refreshed*/
293
7
    if (!PINFO_FD_VISITED(pinfo)) {
294
7
        ctx = wmem_new0(wmem_file_scope(), struct nvme_tcp_cmd_ctx);
295
7
        nvme_add_cmd_to_pending_list(pinfo, q_ctx, &ctx->n_cmd_ctx, (void*) ctx,
296
7
                cmd_id);
297
7
    } else {
298
        /* Already visited this frame */
299
0
        ctx = (struct nvme_tcp_cmd_ctx*) nvme_lookup_cmd_in_done_list(pinfo,
300
0
                q_ctx, cmd_id);
301
        /* if we have already visited frame but haven't found completion yet,
302
         * we won't find cmd in done q, so allocate a dummy ctx for doing
303
         * rest of the processing.
304
         */
305
0
        if (!ctx)
306
0
            ctx = wmem_new0(wmem_file_scope(), struct nvme_tcp_cmd_ctx);
307
0
    }
308
309
7
    return ctx;
310
7
}
311
312
static void
313
dissect_nvme_tcp_command(tvbuff_t *tvb,
314
                         packet_info *pinfo,
315
                         proto_tree *root_tree,
316
                         proto_tree *nvme_tcp_tree,
317
                         proto_item *nvme_tcp_ti,
318
                         struct nvme_tcp_q_ctx *queue, int offset,
319
                         uint32_t incapsuled_data_size,
320
                         uint32_t data_offset)
321
7
{
322
7
    struct nvme_tcp_cmd_ctx *cmd_ctx;
323
7
    uint16_t cmd_id;
324
7
    uint8_t opcode;
325
7
    const char *cmd_string;
326
327
7
    opcode = tvb_get_uint8(tvb, offset);
328
7
    cmd_id = tvb_get_uint16(tvb, offset + 2, ENC_LITTLE_ENDIAN);
329
7
    cmd_ctx = bind_cmd_to_qctx(pinfo, &queue->n_q_ctx, cmd_id);
330
331
    /* if record did not contain connect command we won't know qid,
332
     * so let's guess if this is an admin queue */
333
7
    if ((queue->n_q_ctx.qid == UINT16_MAX) && !nvme_is_io_queue_opcode(opcode))
334
7
        queue->n_q_ctx.qid = 0;
335
336
7
    if (opcode == NVME_FABRIC_OPC) {
337
0
        cmd_ctx->n_cmd_ctx.fabric = true;
338
0
        dissect_nvmeof_fabric_cmd(tvb, pinfo, nvme_tcp_tree, &queue->n_q_ctx, &cmd_ctx->n_cmd_ctx, offset, false);
339
0
        if (cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype == NVME_FCTYPE_CONNECT)
340
0
            queue->n_q_ctx.qid = cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.cnct.qid;
341
0
        cmd_string = get_nvmeof_cmd_string(cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype);
342
0
        proto_item_append_text(nvme_tcp_ti,
343
0
                ", Fabrics Type: %s (0x%02x) Cmd ID: 0x%04x", cmd_string,
344
0
                cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype, cmd_id);
345
0
        if (incapsuled_data_size > 0) {
346
0
            proto_tree *data_tree;
347
0
            proto_item *ti;
348
349
0
            ti = proto_tree_add_item(nvme_tcp_tree, hf_nvme_fabrics_cmd_data, tvb, offset, incapsuled_data_size, ENC_NA);
350
0
            data_tree = proto_item_add_subtree(ti, ett_nvme_tcp);
351
0
            dissect_nvmeof_cmd_data(tvb, pinfo, data_tree, offset + NVME_FABRIC_CMD_SIZE + data_offset, &queue->n_q_ctx, &cmd_ctx->n_cmd_ctx, incapsuled_data_size);
352
0
        }
353
0
        return;
354
0
    }
355
356
    /* In case of incapsuled nvme command tcp length is only a header */
357
7
    proto_item_set_len(nvme_tcp_ti, NVME_TCP_HEADER_SIZE);
358
7
    tvbuff_t *nvme_tvbuff;
359
7
    cmd_ctx->n_cmd_ctx.fabric = false;
360
7
    nvme_tvbuff = tvb_new_subset_remaining(tvb, NVME_TCP_HEADER_SIZE);
361
7
    cmd_string = nvme_get_opcode_string(opcode, queue->n_q_ctx.qid);
362
7
    dissect_nvme_cmd(nvme_tvbuff, pinfo, root_tree, &queue->n_q_ctx,
363
7
            &cmd_ctx->n_cmd_ctx);
364
7
    proto_item_append_text(nvme_tcp_ti,
365
7
            ", NVMe Opcode: %s (0x%02x) Cmd ID: 0x%04x", cmd_string, opcode,
366
7
            cmd_id);
367
368
    /* This is an inline write */
369
7
    if (incapsuled_data_size > 0) {
370
4
        tvbuff_t *nvme_data;
371
372
4
        nvme_data = tvb_new_subset_remaining(tvb, offset +
373
4
                NVME_CMD_SIZE + data_offset);
374
4
        dissect_nvme_data_response(nvme_data, pinfo, root_tree, &queue->n_q_ctx,
375
4
                &cmd_ctx->n_cmd_ctx, incapsuled_data_size, true);
376
4
    }
377
7
}
378
379
static uint32_t
380
dissect_nvme_tcp_data_pdu(tvbuff_t *tvb,
381
                          packet_info *pinfo,
382
                          int offset,
383
2
                          proto_tree *tree) {
384
2
    uint32_t data_length;
385
2
    proto_item *tf;
386
2
    proto_item *data_tree;
387
388
2
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "NVMe");
389
390
2
    tf = proto_tree_add_item(tree, hf_nvme_tcp_data_pdu, tvb, offset,
391
2
            NVME_TCP_DATA_PDU_SIZE - NVME_TCP_HEADER_SIZE, ENC_NA);
392
2
    data_tree = proto_item_add_subtree(tf, ett_nvme_tcp);
393
394
2
    proto_tree_add_item(data_tree, hf_nvme_fabrics_cmd_cid, tvb, offset, 2,
395
2
            ENC_LITTLE_ENDIAN);
396
397
2
    proto_tree_add_item(data_tree, hf_nvme_tcp_pdu_ttag, tvb, offset + 2, 2,
398
2
            ENC_LITTLE_ENDIAN);
399
400
2
    proto_tree_add_item(data_tree, hf_nvme_tcp_data_pdu_data_offset, tvb,
401
2
            offset + 4, 4, ENC_LITTLE_ENDIAN);
402
403
2
    data_length = tvb_get_uint32(tvb, offset + 8, ENC_LITTLE_ENDIAN);
404
2
    proto_tree_add_item(data_tree, hf_nvme_tcp_data_pdu_data_length, tvb,
405
2
            offset + 8, 4, ENC_LITTLE_ENDIAN);
406
407
2
    proto_tree_add_item(data_tree, hf_nvme_tcp_data_pdu_data_resvd, tvb,
408
2
            offset + 12, 4, ENC_NA);
409
410
2
    return data_length;
411
2
}
412
413
static void
414
dissect_nvme_tcp_c2h_data(tvbuff_t *tvb,
415
                          packet_info *pinfo,
416
                          proto_tree *root_tree,
417
                          proto_tree *nvme_tcp_tree,
418
                          proto_item *nvme_tcp_ti,
419
                          struct nvme_tcp_q_ctx *queue,
420
                          int offset,
421
                          uint32_t data_offset)
422
0
{
423
0
    struct nvme_tcp_cmd_ctx *cmd_ctx;
424
0
    uint32_t cmd_id;
425
0
    uint32_t data_length;
426
0
    tvbuff_t *nvme_data;
427
0
    const char *cmd_string;
428
429
0
    cmd_id = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
430
0
    data_length = dissect_nvme_tcp_data_pdu(tvb, pinfo, offset, nvme_tcp_tree);
431
432
    /* This can identify our packet uniquely  */
433
0
    if (!PINFO_FD_VISITED(pinfo)) {
434
0
        cmd_ctx = (struct nvme_tcp_cmd_ctx*) nvme_lookup_cmd_in_pending_list(
435
0
                &queue->n_q_ctx, cmd_id);
436
0
        if (!cmd_ctx) {
437
0
            proto_tree_add_item(root_tree, hf_nvme_tcp_unknown_data, tvb, offset + 16,
438
0
                                data_length, ENC_NA);
439
0
            return;
440
0
        }
441
442
        /* In order to later lookup for command context lets add this command
443
         * to data responses */
444
0
        cmd_ctx->n_cmd_ctx.data_tr_pkt_num[0] = pinfo->num;
445
0
        nvme_add_data_tr_pkt(&queue->n_q_ctx, &cmd_ctx->n_cmd_ctx, cmd_id, pinfo->num);
446
0
    } else {
447
0
        cmd_ctx = (struct nvme_tcp_cmd_ctx*) nvme_lookup_data_tr_pkt(&queue->n_q_ctx,
448
0
                                cmd_id, pinfo->num);
449
0
        if (!cmd_ctx) {
450
0
            proto_tree_add_item(root_tree, hf_nvme_tcp_unknown_data, tvb, offset + 16,
451
0
                                data_length, ENC_NA);
452
0
            return;
453
0
        }
454
0
    }
455
456
0
    nvme_publish_to_cmd_link(nvme_tcp_tree, tvb,
457
0
            hf_nvme_tcp_cmd_pkt, &cmd_ctx->n_cmd_ctx);
458
459
0
    if (cmd_ctx->n_cmd_ctx.fabric) {
460
0
        cmd_string = get_nvmeof_cmd_string(cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype);
461
0
        proto_item_append_text(nvme_tcp_ti,
462
0
                ", C2HData Fabrics Type: %s (0x%02x), Cmd ID: 0x%04x, Len: %u",
463
0
                cmd_string, cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype, cmd_id, data_length);
464
0
    } else {
465
0
        cmd_string = nvme_get_opcode_string(cmd_ctx->n_cmd_ctx.opcode,
466
0
                queue->n_q_ctx.qid);
467
0
        proto_item_append_text(nvme_tcp_ti,
468
0
                ", C2HData Opcode: %s (0x%02x), Cmd ID: 0x%04x, Len: %u",
469
0
                cmd_string, cmd_ctx->n_cmd_ctx.opcode, cmd_id, data_length);
470
0
    }
471
472
0
    nvme_data = tvb_new_subset_remaining(tvb, NVME_TCP_DATA_PDU_SIZE + data_offset);
473
474
0
    dissect_nvme_data_response(nvme_data, pinfo, root_tree, &queue->n_q_ctx,
475
0
            &cmd_ctx->n_cmd_ctx, data_length, false);
476
477
0
}
478
479
static void nvme_tcp_build_cmd_key(uint32_t *frame_num, uint32_t *cmd_id, wmem_tree_key_t *key)
480
0
{
481
0
    key[0].key = frame_num;
482
0
    key[0].length = 1;
483
0
    key[1].key = cmd_id;
484
0
    key[1].length = 1;
485
0
    key[2].key = NULL;
486
0
    key[2].length = 0;
487
0
}
488
489
static void nvme_tcp_add_data_request(packet_info *pinfo, struct nvme_tcp_q_ctx *q_ctx,
490
        struct nvme_tcp_cmd_ctx *cmd_ctx, uint16_t cmd_id)
491
0
{
492
0
    wmem_tree_key_t cmd_key[3];
493
0
    uint32_t cmd_id_key = cmd_id;
494
495
0
    nvme_tcp_build_cmd_key(&pinfo->num, &cmd_id_key, cmd_key);
496
0
    cmd_ctx->n_cmd_ctx.data_req_pkt_num = pinfo->num;
497
0
    cmd_ctx->n_cmd_ctx.data_tr_pkt_num[0] = 0;
498
0
    wmem_tree_insert32_array(q_ctx->h2c_requests, cmd_key, (void *)cmd_ctx);
499
0
}
500
501
static struct nvme_tcp_cmd_ctx* nvme_tcp_lookup_data_request(packet_info *pinfo,
502
        struct nvme_tcp_q_ctx *q_ctx,
503
        uint16_t cmd_id)
504
0
{
505
0
    wmem_tree_key_t cmd_key[3];
506
0
    uint32_t cmd_id_key = cmd_id;
507
508
0
    nvme_tcp_build_cmd_key(&pinfo->num, &cmd_id_key, cmd_key);
509
0
    return (struct nvme_tcp_cmd_ctx*)wmem_tree_lookup32_array(q_ctx->h2c_requests, cmd_key);
510
0
}
511
512
static void
513
dissect_nvme_tcp_h2c_data(tvbuff_t *tvb,
514
                          packet_info *pinfo,
515
                          proto_tree *root_tree,
516
                          proto_tree *nvme_tcp_tree,
517
                          proto_item *nvme_tcp_ti,
518
                          struct nvme_tcp_q_ctx *queue,
519
                          int offset,
520
                          uint32_t data_offset)
521
2
{
522
2
    struct nvme_tcp_cmd_ctx *cmd_ctx;
523
2
    uint16_t cmd_id;
524
2
    uint32_t data_length;
525
2
    tvbuff_t *nvme_data;
526
2
    const char *cmd_string;
527
528
2
    cmd_id = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
529
2
    data_length = dissect_nvme_tcp_data_pdu(tvb, pinfo, offset, nvme_tcp_tree);
530
531
2
    if (!PINFO_FD_VISITED(pinfo)) {
532
0
        cmd_ctx = (struct nvme_tcp_cmd_ctx*) nvme_lookup_cmd_in_pending_list(
533
0
                &queue->n_q_ctx, cmd_id);
534
0
        if (!cmd_ctx) {
535
0
            proto_tree_add_item(root_tree, hf_nvme_tcp_unknown_data, tvb, offset + 16,
536
0
                        data_length, ENC_NA);
537
0
            return;
538
0
        }
539
540
        /* Fill this for "adding data request call,
541
         * this will be the key to fetch data request later */
542
0
        nvme_tcp_add_data_request(pinfo, queue, cmd_ctx, cmd_id);
543
2
    } else {
544
2
        cmd_ctx = nvme_tcp_lookup_data_request(pinfo, queue, cmd_id);
545
2
        if (!cmd_ctx) {
546
0
            proto_tree_add_item(root_tree, hf_nvme_tcp_unknown_data, tvb, offset + 16,
547
0
                        data_length, ENC_NA);
548
0
            return;
549
0
        }
550
2
    }
551
552
2
    nvme_publish_to_cmd_link(nvme_tcp_tree, tvb,
553
2
                hf_nvme_tcp_cmd_pkt, &cmd_ctx->n_cmd_ctx);
554
555
    /* fabrics commands should not have h2cdata*/
556
2
    if (cmd_ctx->n_cmd_ctx.fabric) {
557
0
        cmd_string = get_nvmeof_cmd_string(cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype);
558
0
        proto_item_append_text(nvme_tcp_ti,
559
0
                ", H2CData Fabrics Type: %s (0x%02x), Cmd ID: 0x%04x, Len: %u",
560
0
                cmd_string, cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype, cmd_id, data_length);
561
0
        proto_tree_add_item(root_tree, hf_nvme_tcp_unknown_data, tvb, offset + 16,
562
0
                    data_length, ENC_NA);
563
0
        return;
564
0
    }
565
566
2
    cmd_string = nvme_get_opcode_string(cmd_ctx->n_cmd_ctx.opcode,
567
2
            queue->n_q_ctx.qid);
568
2
    proto_item_append_text(nvme_tcp_ti,
569
2
            ", H2CData Opcode: %s (0x%02x), Cmd ID: 0x%04x, Len: %u",
570
2
            cmd_string, cmd_ctx->n_cmd_ctx.opcode, cmd_id, data_length);
571
572
2
    nvme_data = tvb_new_subset_remaining(tvb, NVME_TCP_DATA_PDU_SIZE + data_offset);
573
2
    dissect_nvme_data_response(nvme_data, pinfo, root_tree, &queue->n_q_ctx,
574
2
            &cmd_ctx->n_cmd_ctx, data_length, false);
575
2
}
576
577
static void
578
dissect_nvme_tcp_h2ctermreq(tvbuff_t *tvb, packet_info *pinfo,
579
                            proto_tree *tree, uint32_t packet_len, int offset)
580
0
{
581
0
    proto_item *tf;
582
0
    proto_item *h2ctermreq_tree;
583
0
    uint16_t fes;
584
585
0
    col_set_str(pinfo->cinfo, COL_INFO,
586
0
                "Host to Controller Termination Request");
587
0
    tf = proto_tree_add_item(tree, hf_nvme_tcp_h2ctermreq,
588
0
                             tvb, offset, 8, ENC_NA);
589
0
    h2ctermreq_tree = proto_item_add_subtree(tf, ett_nvme_tcp);
590
591
    /* tcp_dissect_pdus() only guarantees PLEN >= NVME_TCP_HEADER_SIZE, so a
592
     * short PLEN would underflow the trailing data length below. */
593
0
    if (packet_len < NVME_TCP_TERMREQ_PDU_SIZE) {
594
0
        proto_tree_add_expert_format_remaining(h2ctermreq_tree, pinfo,
595
0
                &ei_nvme_tcp_termreq_short, tvb, offset,
596
0
                "PDU length %u is shorter than the %u byte Termination Request header",
597
0
                packet_len, NVME_TCP_TERMREQ_PDU_SIZE);
598
0
        return;
599
0
    }
600
601
0
    proto_tree_add_item(h2ctermreq_tree, hf_nvme_tcp_h2ctermreq_fes,
602
0
                        tvb, offset + 8, 2, ENC_LITTLE_ENDIAN);
603
0
    fes = tvb_get_uint16(tvb, offset + 8, ENC_LITTLE_ENDIAN);
604
0
    switch (fes) {
605
0
    case NVME_TCP_FES_INVALID_PDU_HDR:
606
0
        proto_tree_add_item(h2ctermreq_tree, hf_nvme_tcp_h2ctermreq_phfo,
607
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
608
0
        break;
609
0
    case NVME_TCP_FES_HDR_DIGEST_ERR:
610
0
        proto_tree_add_item(h2ctermreq_tree, hf_nvme_tcp_h2ctermreq_phd,
611
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
612
0
        break;
613
0
    case NVME_TCP_FES_UNSUPPORTED_PARAM:
614
0
        proto_tree_add_item(h2ctermreq_tree, hf_nvme_tcp_h2ctermreq_upfo,
615
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
616
0
        break;
617
0
    default:
618
0
        proto_tree_add_item(h2ctermreq_tree, hf_nvme_tcp_h2ctermreq_reserved,
619
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
620
0
        break;
621
0
    }
622
0
    proto_tree_add_item(h2ctermreq_tree, hf_nvme_tcp_h2ctermreq_data,
623
0
                        tvb, offset + NVME_TCP_TERMREQ_PDU_SIZE,
624
0
                        packet_len - NVME_TCP_TERMREQ_PDU_SIZE, ENC_NA);
625
0
}
626
627
static void
628
dissect_nvme_tcp_c2htermreq(tvbuff_t *tvb, packet_info *pinfo,
629
                            proto_tree *tree, uint32_t packet_len, int offset)
630
0
{
631
0
    proto_item *tf;
632
0
    proto_item *c2htermreq_tree;
633
0
    uint16_t fes;
634
635
0
    col_set_str(pinfo->cinfo, COL_INFO,
636
0
                "Controller to Host Termination Request");
637
0
    tf = proto_tree_add_item(tree, hf_nvme_tcp_c2htermreq,
638
0
                             tvb, offset, 8, ENC_NA);
639
0
    c2htermreq_tree = proto_item_add_subtree(tf, ett_nvme_tcp);
640
641
    /* tcp_dissect_pdus() only guarantees PLEN >= NVME_TCP_HEADER_SIZE, so a
642
     * short PLEN would underflow the trailing data length below. */
643
0
    if (packet_len < NVME_TCP_TERMREQ_PDU_SIZE) {
644
0
        proto_tree_add_expert_format_remaining(c2htermreq_tree, pinfo,
645
0
                &ei_nvme_tcp_termreq_short, tvb, offset,
646
0
                "PDU length %u is shorter than the %u byte Termination Request header",
647
0
                packet_len, NVME_TCP_TERMREQ_PDU_SIZE);
648
0
        return;
649
0
    }
650
651
0
    proto_tree_add_item(c2htermreq_tree, hf_nvme_tcp_c2htermreq_fes, tvb, offset + 8, 2,
652
0
                        ENC_LITTLE_ENDIAN);
653
0
    fes = tvb_get_uint16(tvb, offset + 8, ENC_LITTLE_ENDIAN);
654
0
    switch (fes) {
655
0
    case NVME_TCP_FES_INVALID_PDU_HDR:
656
0
        proto_tree_add_item(c2htermreq_tree, hf_nvme_tcp_c2htermreq_phfo,
657
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
658
0
        break;
659
0
    case NVME_TCP_FES_HDR_DIGEST_ERR:
660
0
        proto_tree_add_item(c2htermreq_tree, hf_nvme_tcp_c2htermreq_phd,
661
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
662
0
        break;
663
0
    case NVME_TCP_FES_UNSUPPORTED_PARAM:
664
0
        proto_tree_add_item(c2htermreq_tree, hf_nvme_tcp_c2htermreq_upfo,
665
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
666
0
        break;
667
0
    default:
668
0
        proto_tree_add_item(c2htermreq_tree, hf_nvme_tcp_c2htermreq_reserved,
669
0
                            tvb, offset + 10, 4, ENC_LITTLE_ENDIAN);
670
0
        break;
671
0
    }
672
0
    proto_tree_add_item(c2htermreq_tree, hf_nvme_tcp_c2htermreq_data,
673
0
                        tvb, offset + NVME_TCP_TERMREQ_PDU_SIZE,
674
0
                        packet_len - NVME_TCP_TERMREQ_PDU_SIZE, ENC_NA);
675
0
}
676
677
static void
678
dissect_nvme_tcp_cqe(tvbuff_t *tvb,
679
                     packet_info *pinfo,
680
                     proto_tree *root_tree,
681
                     proto_tree *nvme_tree,
682
                     proto_item *ti,
683
                     struct nvme_tcp_q_ctx *queue,
684
                     int offset)
685
0
{
686
0
    struct nvme_tcp_cmd_ctx *cmd_ctx;
687
0
    uint16_t cmd_id;
688
0
    const char *cmd_string;
689
690
0
    cmd_id = tvb_get_uint16(tvb, offset + 12, ENC_LITTLE_ENDIAN);
691
692
    /* wireshark will dissect packet several times when display is refreshed
693
     * we need to track state changes only once */
694
0
    if (!PINFO_FD_VISITED(pinfo)) {
695
0
        cmd_ctx = (struct nvme_tcp_cmd_ctx*) nvme_lookup_cmd_in_pending_list(
696
0
                &queue->n_q_ctx, cmd_id);
697
0
        if (!cmd_ctx || cmd_ctx->n_cmd_ctx.cqe_pkt_num) {
698
0
            proto_tree_add_item(nvme_tree, hf_nvme_tcp_unknown_data, tvb, offset,
699
0
                                NVME_FABRIC_CQE_SIZE, ENC_NA);
700
0
            return;
701
0
        }
702
703
0
        cmd_ctx->n_cmd_ctx.cqe_pkt_num = pinfo->num;
704
0
        nvme_add_cmd_cqe_to_done_list(&queue->n_q_ctx, &cmd_ctx->n_cmd_ctx,
705
0
                cmd_id);
706
707
0
    } else {
708
0
        cmd_ctx = (struct nvme_tcp_cmd_ctx *) nvme_lookup_cmd_in_done_list(pinfo,
709
0
                                                                           &queue->n_q_ctx, cmd_id);
710
0
        if (!cmd_ctx) {
711
0
            proto_tree_add_item(nvme_tree, hf_nvme_tcp_unknown_data, tvb, offset,
712
0
                                NVME_FABRIC_CQE_SIZE, ENC_NA);
713
0
            return;
714
0
        }
715
0
    }
716
717
0
    nvme_update_cmd_end_info(pinfo, &cmd_ctx->n_cmd_ctx);
718
719
0
    if (cmd_ctx->n_cmd_ctx.fabric) {
720
0
        cmd_string = get_nvmeof_cmd_string(cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype);
721
0
        proto_item_append_text(ti,
722
0
                ", Cqe Fabrics Cmd: %s (0x%02x) Cmd ID: 0x%04x", cmd_string,
723
0
               cmd_ctx->n_cmd_ctx.cmd_ctx.fabric_cmd.fctype , cmd_id);
724
725
0
        dissect_nvmeof_fabric_cqe(tvb, pinfo, nvme_tree, &cmd_ctx->n_cmd_ctx, offset);
726
0
    } else {
727
0
        tvbuff_t *nvme_tvb;
728
0
        proto_item_set_len(ti, NVME_TCP_HEADER_SIZE);
729
0
        cmd_string = nvme_get_opcode_string(cmd_ctx->n_cmd_ctx.opcode,
730
0
                queue->n_q_ctx.qid);
731
732
0
        proto_item_append_text(ti, ", Cqe NVMe Cmd: %s (0x%02x) Cmd ID: 0x%04x",
733
0
                cmd_string, cmd_ctx->n_cmd_ctx.opcode, cmd_id);
734
        /* get incapsuled nvme command */
735
0
        nvme_tvb = tvb_new_subset_remaining(tvb, NVME_TCP_HEADER_SIZE);
736
0
        dissect_nvme_cqe(nvme_tvb, pinfo, root_tree, &queue->n_q_ctx, &cmd_ctx->n_cmd_ctx);
737
0
    }
738
0
}
739
740
static void
741
dissect_nvme_tcp_r2t(tvbuff_t *tvb,
742
                     packet_info *pinfo,
743
                     int offset,
744
                     proto_tree *tree)
745
0
{
746
0
    proto_item *tf;
747
0
    proto_item *r2t_tree;
748
749
0
    tf = proto_tree_add_item(tree, hf_nvme_tcp_r2t_pdu, tvb, offset, -1,
750
0
            ENC_NA);
751
0
    r2t_tree = proto_item_add_subtree(tf, ett_nvme_tcp);
752
753
0
    col_append_sep_fstr(pinfo->cinfo, COL_INFO, " | ", "Ready To Transfer");
754
755
0
    proto_tree_add_item(r2t_tree, hf_nvme_fabrics_cmd_cid, tvb, offset, 2,
756
0
            ENC_LITTLE_ENDIAN);
757
0
    proto_tree_add_item(r2t_tree, hf_nvme_tcp_pdu_ttag, tvb, offset + 2, 2,
758
0
            ENC_LITTLE_ENDIAN);
759
0
    proto_tree_add_item(r2t_tree, hf_nvme_tcp_r2t_offset, tvb, offset + 4, 4,
760
0
            ENC_LITTLE_ENDIAN);
761
0
    proto_tree_add_item(r2t_tree, hf_nvme_tcp_r2t_length, tvb, offset + 8, 4,
762
0
            ENC_LITTLE_ENDIAN);
763
0
    proto_tree_add_item(r2t_tree, hf_nvme_tcp_r2t_resvd, tvb, offset + 12, 4,
764
0
            ENC_NA);
765
0
}
766
767
static int
768
dissect_nvme_tcp_pdu(tvbuff_t *tvb,
769
                     packet_info *pinfo,
770
                     proto_tree *tree,
771
                     void* data _U_)
772
12
{
773
12
    conversation_t *conversation;
774
12
    struct nvme_tcp_q_ctx *q_ctx;
775
12
    proto_item *ti;
776
12
    unsigned offset = 0;
777
12
    int nvme_tcp_pdu_offset;
778
12
    proto_tree *nvme_tcp_tree;
779
12
    unsigned packet_type;
780
12
    uint8_t hlen, pdo;
781
12
    uint8_t pdu_flags;
782
12
    uint32_t plen;
783
12
    uint32_t incapsuled_data_size;
784
12
    uint32_t pdu_data_offset = 0;
785
786
12
    conversation = find_or_create_conversation(pinfo);
787
12
    q_ctx = (struct nvme_tcp_q_ctx *)
788
12
            conversation_get_proto_data(conversation, proto_nvme_tcp);
789
790
12
    if (!q_ctx) {
791
11
        q_ctx = wmem_new0(wmem_file_scope(), struct nvme_tcp_q_ctx);
792
11
        q_ctx->n_q_ctx.pending_cmds = wmem_tree_new(wmem_file_scope());
793
11
        q_ctx->n_q_ctx.done_cmds = wmem_tree_new(wmem_file_scope());
794
11
        q_ctx->h2c_requests = wmem_tree_new(wmem_file_scope());
795
11
        q_ctx->n_q_ctx.data_responses = wmem_tree_new(wmem_file_scope());
796
        /* Initially set to non-0 so that by default queues are io queues
797
         * this is required to be able to dissect correctly even
798
         * if we miss connect command*/
799
11
        q_ctx->n_q_ctx.qid = UINT16_MAX;
800
11
        conversation_add_proto_data(conversation, proto_nvme_tcp, q_ctx);
801
11
    }
802
803
12
    ti = proto_tree_add_item(tree, proto_nvme_tcp, tvb, 0, -1, ENC_NA);
804
12
    nvme_tcp_tree = proto_item_add_subtree(ti, ett_nvme_tcp);
805
806
12
    if (q_ctx->n_q_ctx.qid != UINT16_MAX)
807
0
        nvme_publish_qid(nvme_tcp_tree, hf_nvme_fabrics_cmd_qid,
808
0
                q_ctx->n_q_ctx.qid);
809
810
12
    packet_type = tvb_get_uint8(tvb, offset);
811
12
    proto_tree_add_item(nvme_tcp_tree, hf_nvme_tcp_type, tvb, offset, 1,
812
12
            ENC_NA);
813
814
12
    pdu_flags = tvb_get_uint8(tvb, offset + 1);
815
12
    proto_tree_add_bitmask_value(nvme_tcp_tree, tvb, offset + 1, hf_nvme_tcp_flags,
816
12
            ett_nvme_tcp, nvme_tcp_pdu_flags, (uint64_t)pdu_flags);
817
818
12
    hlen = tvb_get_uint8(tvb, offset + 2);
819
12
    proto_tree_add_item(nvme_tcp_tree, hf_nvme_tcp_hlen, tvb, offset + 2, 1,
820
12
            ENC_NA);
821
822
12
    pdo = tvb_get_uint8(tvb, offset + 3);
823
12
    proto_tree_add_uint(nvme_tcp_tree, hf_nvme_tcp_pdo, tvb, offset + 3, 1,
824
12
            pdo);
825
12
    proto_tree_add_item_ret_uint(nvme_tcp_tree, hf_nvme_tcp_plen, tvb, offset + 4, 4,
826
12
            ENC_LITTLE_ENDIAN, &plen);
827
12
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "NVMe/TCP");
828
829
12
    if (pdu_flags & NVME_TCP_F_HDGST) {
830
1
        unsigned hdgst_flags = PROTO_CHECKSUM_NO_FLAGS;
831
1
        uint32_t crc = 0;
832
833
1
        if (nvme_tcp_check_hdgst) {
834
0
            hdgst_flags = PROTO_CHECKSUM_VERIFY;
835
0
            crc = ~crc32c_tvb_offset_calculate(tvb, 0, hlen, ~0);
836
0
        }
837
1
        proto_tree_add_checksum(nvme_tcp_tree, tvb, hlen, hf_nvme_tcp_hdgst,
838
1
                    hf_nvme_tcp_hdgst_status, NULL, pinfo,
839
1
                    crc, ENC_NA, hdgst_flags);
840
1
        pdu_data_offset = NVME_TCP_DIGEST_LENGTH;
841
1
    }
842
843
12
    nvme_tcp_pdu_offset = offset + NVME_TCP_HEADER_SIZE;
844
12
    incapsuled_data_size = plen - hlen - pdu_data_offset;
845
846
    /* check for overflow (invalid packet)*/
847
12
    if (incapsuled_data_size > tvb_reported_length(tvb)) {
848
0
        proto_tree_add_item(nvme_tcp_tree, hf_nvme_tcp_unknown_data,
849
0
                               tvb, NVME_TCP_HEADER_SIZE, -1, ENC_NA);
850
0
        return tvb_reported_length(tvb);
851
0
    }
852
853
12
    if (pdu_flags & NVME_TCP_F_DDGST) {
854
1
        unsigned ddgst_flags = PROTO_CHECKSUM_NO_FLAGS;
855
1
        uint32_t crc = 0;
856
857
        /* Check that data has enough space (invalid packet) */
858
1
        if (incapsuled_data_size <= NVME_TCP_DIGEST_LENGTH) {
859
0
            proto_tree_add_item(nvme_tcp_tree, hf_nvme_tcp_unknown_data,
860
0
                                           tvb, NVME_TCP_HEADER_SIZE, -1, ENC_NA);
861
0
            return tvb_reported_length(tvb);
862
0
        }
863
864
1
        incapsuled_data_size -= NVME_TCP_DIGEST_LENGTH;
865
1
        if (nvme_tcp_check_ddgst) {
866
0
            ddgst_flags = PROTO_CHECKSUM_VERIFY;
867
0
            crc = ~crc32c_tvb_offset_calculate(tvb, pdo,
868
0
                                               incapsuled_data_size, ~0);
869
0
        }
870
1
        proto_tree_add_checksum(nvme_tcp_tree, tvb,
871
1
                         plen - NVME_TCP_DIGEST_LENGTH, hf_nvme_tcp_ddgst,
872
1
                         hf_nvme_tcp_ddgst_status, NULL, pinfo,
873
1
                         crc, ENC_NA, ddgst_flags);
874
1
    }
875
876
12
    switch (packet_type) {
877
0
    case nvme_tcp_icreq:
878
0
        dissect_nvme_tcp_icreq(tvb, pinfo, nvme_tcp_pdu_offset, nvme_tcp_tree);
879
0
        proto_item_set_len(ti, hlen);
880
0
        break;
881
0
    case nvme_tcp_icresp:
882
0
        dissect_nvme_tcp_icresp(tvb, pinfo, nvme_tcp_pdu_offset, nvme_tcp_tree);
883
0
        proto_item_set_len(ti, hlen);
884
0
        break;
885
7
    case nvme_tcp_cmd:
886
7
        dissect_nvme_tcp_command(tvb, pinfo, tree, nvme_tcp_tree, ti, q_ctx,
887
7
                nvme_tcp_pdu_offset, incapsuled_data_size, pdu_data_offset);
888
7
        break;
889
0
    case nvme_tcp_rsp:
890
0
        dissect_nvme_tcp_cqe(tvb, pinfo, tree, nvme_tcp_tree, ti, q_ctx,
891
0
                nvme_tcp_pdu_offset);
892
0
        proto_item_set_len(ti, NVME_TCP_HEADER_SIZE);
893
0
        break;
894
0
    case nvme_tcp_c2h_data:
895
0
        dissect_nvme_tcp_c2h_data(tvb, pinfo, tree, nvme_tcp_tree, ti, q_ctx,
896
0
                nvme_tcp_pdu_offset, pdu_data_offset);
897
0
        proto_item_set_len(ti, NVME_TCP_DATA_PDU_SIZE);
898
0
        break;
899
2
    case nvme_tcp_h2c_data:
900
2
        dissect_nvme_tcp_h2c_data(tvb, pinfo, tree, nvme_tcp_tree, ti, q_ctx,
901
2
                nvme_tcp_pdu_offset, pdu_data_offset);
902
2
        proto_item_set_len(ti, NVME_TCP_DATA_PDU_SIZE);
903
2
        break;
904
0
    case nvme_tcp_r2t:
905
0
        dissect_nvme_tcp_r2t(tvb, pinfo, nvme_tcp_pdu_offset, nvme_tcp_tree);
906
0
        break;
907
0
    case nvme_tcp_h2c_term:
908
0
        dissect_nvme_tcp_h2ctermreq(tvb, pinfo, tree, plen, offset);
909
0
        break;
910
0
    case nvme_tcp_c2h_term:
911
0
        dissect_nvme_tcp_c2htermreq(tvb, pinfo, tree, plen, offset);
912
0
        break;
913
1
    default:
914
        // TODO: nvme_tcp_kdreq, nvme_tcp_kdresp
915
1
        proto_tree_add_item(nvme_tcp_tree, hf_nvme_tcp_unknown_data, tvb,
916
1
                offset, plen, ENC_NA);
917
1
        break;
918
12
    }
919
920
0
    return tvb_reported_length(tvb);
921
12
}
922
923
static int
924
dissect_nvme_tcp(tvbuff_t *tvb,
925
                 packet_info *pinfo,
926
                 proto_tree *tree,
927
                 void *data)
928
12
{
929
12
    col_clear(pinfo->cinfo, COL_INFO);
930
12
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "NVMe/TCP");
931
12
    tcp_dissect_pdus(tvb, pinfo, tree, true, NVME_TCP_HEADER_SIZE,
932
12
            get_nvme_tcp_pdu_len, dissect_nvme_tcp_pdu, data);
933
934
12
    return tvb_reported_length(tvb);
935
12
}
936
937
static bool
938
test_nvme(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
939
15
{
940
    /* This is not the strongest heuristic, but the port is IANA assigned,
941
     * so this is not a normal heuristic dissector but simply to distinguish
942
     * between NVMe/TCP and NVMe/TLS/TCP, and also to detect PDU starts.
943
     */
944
15
    if (tvb_captured_length_remaining(tvb, offset) < NVME_TCP_HEADER_SIZE) {
945
0
        return false;
946
0
    }
947
948
15
    if (tvb_get_uint8(tvb, offset) > NVMET_MAX_PDU_TYPE) {
949
3
        return false;
950
3
    }
951
952
12
    offset += 2;
953
12
    if (tvb_get_uint8(tvb, offset) < NVME_TCP_HEADER_SIZE) {
954
        // Header length - we could strengthen by using the PDU type.
955
1
        return false;
956
1
    }
957
958
    // Next byte is PDU Data Offset. Reserved in most types. (Does that
959
    // mean zero? That would strengthen the heuristic.)
960
961
11
    offset += 2;
962
11
    if (tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN) < NVME_TCP_HEADER_SIZE) {
963
        // PDU Length (inc. header) - could strengthen by using the PDU type.
964
0
        return false;
965
0
    }
966
967
11
    return true;
968
11
}
969
970
static int
971
dissect_nvme_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
972
15
{
973
    /* NVMe/TCP allows PDUs to span TCP segments (see Figure 5 of the NVMe/TCP
974
     * Transport Specification.) Also, some connections are over TLS.
975
     * Luckily, the PDU types for NVMe/TCP occupy the first byte, same as
976
     * the Content Type for TLS Records, and while these PDU types go to 11,
977
     * TLS Content Types start at 20 (and won't change, to enable multiplexing,
978
     * see RFC 9443.)
979
     *
980
     * So if this doesn't look like the start of a NVMe/TCP PDU, reject it.
981
     * It might be TLS, or it might be the middle of a PDU.
982
     */
983
15
    if (!test_nvme(pinfo, tvb, 0, data)) {
984
4
        return 0;
985
        /* The TLS heuristic dissector should catch the TLS version. */
986
4
    }
987
988
    /* The start of a PDU. Set the other handle for this connection.
989
     * We can call tcp_dissect_pdus safely starting from here.
990
     */
991
11
    conversation_t *conversation = find_or_create_conversation(pinfo);
992
11
    conversation_set_dissector_from_frame_number(conversation, pinfo->num, nvmet_tls_handle);
993
994
11
    return dissect_nvme_tcp(tvb, pinfo, tree, data);
995
15
}
996
997
16
void proto_register_nvme_tcp(void) {
998
999
16
    static hf_register_info hf[] = {
1000
16
       { &hf_nvme_tcp_type,
1001
16
           { "Pdu Type", "nvme-tcp.type",
1002
16
             FT_UINT8, BASE_DEC, VALS(nvme_tcp_pdu_type_vals),
1003
16
             0x0, NULL, HFILL } },
1004
16
       { &hf_nvme_tcp_flags,
1005
16
           { "Pdu Specific Flags", "nvme-tcp.flags",
1006
16
             FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
1007
16
       { &hf_pdu_flags_hdgst,
1008
16
           { "PDU Header Digest", "nvme-tcp.flags.pdu.hdgst",
1009
16
             FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1010
16
             NVME_TCP_F_HDGST, NULL, HFILL} },
1011
16
       { &hf_pdu_flags_ddgst,
1012
16
           { "PDU Data Digest", "nvme-tcp.flags.pdu.ddgst",
1013
16
             FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1014
16
             NVME_TCP_F_DDGST, NULL, HFILL} },
1015
16
       { &hf_pdu_flags_data_last,
1016
16
           { "PDU Data Last", "nvme-tcp.flags.pdu.data_last",
1017
16
              FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1018
16
              NVME_TCP_F_DATA_LAST, NULL, HFILL} },
1019
16
       { &hf_pdu_flags_data_success,
1020
16
          { "PDU Data Success", "nvme-tcp.flags.pdu.data_success",
1021
16
            FT_BOOLEAN, 8, TFS(&tfs_set_notset),
1022
16
            NVME_TCP_F_DATA_SUCCESS, NULL, HFILL} },
1023
16
       { &hf_nvme_tcp_hdgst,
1024
16
           { "PDU Header Digest", "nvme-tcp.hdgst",
1025
16
            FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
1026
16
        { &hf_nvme_tcp_ddgst,
1027
16
           { "PDU Data Digest", "nvme-tcp.ddgst",
1028
16
            FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
1029
16
        { &hf_nvme_tcp_hdgst_status,
1030
16
          { "Header Digest Status",    "nvme-tcp.hdgst.status",
1031
16
            FT_UINT8, BASE_NONE, VALS(proto_checksum_vals),
1032
16
            0x0, NULL, HFILL }},
1033
16
        { &hf_nvme_tcp_ddgst_status,
1034
16
          { "Data Digest Status",    "nvme-tcp.ddgst.status",
1035
16
            FT_UINT8, BASE_NONE, VALS(proto_checksum_vals),
1036
16
            0x0, NULL, HFILL }},
1037
16
       { &hf_nvme_tcp_hlen,
1038
16
           { "Pdu Header Length", "nvme-tcp.hlen",
1039
16
             FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1040
16
       { &hf_nvme_tcp_pdo,
1041
16
           { "Pdu Data Offset", "nvme-tcp.pdo",
1042
16
             FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1043
16
       { &hf_nvme_tcp_plen,
1044
16
           { "Packet Length", "nvme-tcp.plen",
1045
16
            FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1046
16
       { &hf_nvme_tcp_icreq,
1047
16
           { "ICReq", "nvme-tcp.icreq",
1048
16
             FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1049
16
       { &hf_nvme_tcp_icreq_pfv,
1050
16
           { "Pdu Version Format", "nvme-tcp.icreq.pfv",
1051
16
            FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1052
16
       { &hf_nvme_tcp_icreq_maxr2t,
1053
16
           { "Maximum r2ts per request", "nvme-tcp.icreq.maxr2t",
1054
16
             FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1055
16
       { &hf_nvme_tcp_icreq_hpda,
1056
16
           { "Host Pdu data alignment", "nvme-tcp.icreq.hpda",
1057
16
             FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1058
16
       { &hf_nvme_tcp_icreq_digest,
1059
16
           { "Digest Types Enabled", "nvme-tcp.icreq.digest",
1060
16
             FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1061
16
       { &hf_nvme_tcp_icresp,
1062
16
           { "ICResp", "nvme-tcp.icresp",
1063
16
             FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1064
16
       { &hf_nvme_tcp_icresp_pfv,
1065
16
           { "Pdu Version Format", "nvme-tcp.icresp.pfv",
1066
16
             FT_UINT16, BASE_DEC, NULL, 0x0,
1067
16
             NULL, HFILL } },
1068
16
       { &hf_nvme_tcp_icresp_cpda,
1069
16
           { "Controller Pdu data alignment", "nvme-tcp.icresp.cpda",
1070
16
             FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1071
16
       { &hf_nvme_tcp_icresp_digest,
1072
16
           { "Digest types enabled", "nvme-tcp.icresp.digest",
1073
16
             FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1074
16
       { &hf_nvme_tcp_icresp_maxdata,
1075
16
           { "Maximum data capsules per r2t supported", "nvme-tcp.icresp.maxdata",
1076
16
             FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
1077
       /* NVMe tcp c2h/h2c termreq fields */
1078
16
       { &hf_nvme_tcp_c2htermreq,
1079
16
           { "C2HTermReq", "nvme-tcp.c2htermreq",
1080
16
             FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1081
16
       { &hf_nvme_tcp_c2htermreq_fes,
1082
16
           { "Fatal error status", "nvme-tcp.c2htermreq.fes",
1083
16
             FT_UINT16, BASE_HEX, VALS(nvme_tcp_termreq_fes),
1084
16
             0x0, NULL, HFILL } },
1085
16
       { &hf_nvme_tcp_c2htermreq_phfo,
1086
16
           { "PDU header field offset", "nvme-tcp.c2htermreq.phfo",
1087
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1088
16
       { &hf_nvme_tcp_c2htermreq_phd,
1089
16
           { "PDU header digest", "nvme-tcp.c2htermreq.phd",
1090
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1091
16
       { &hf_nvme_tcp_c2htermreq_upfo,
1092
16
           { "Unsupported parameter field offset", "nvme-tcp.c2htermreq.upfo",
1093
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1094
16
       { &hf_nvme_tcp_c2htermreq_reserved,
1095
16
           { "Reserved", "nvme-tcp.c2htermreq.reserved",
1096
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1097
16
       { &hf_nvme_tcp_c2htermreq_data,
1098
16
           { "Terminated PDU header", "nvme-tcp.c2htermreq.data",
1099
16
             FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
1100
16
       { &hf_nvme_tcp_h2ctermreq,
1101
16
           { "H2CTermReq", "nvme-tcp.h2ctermreq",
1102
16
             FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1103
16
       { &hf_nvme_tcp_h2ctermreq_fes,
1104
16
           { "Fatal error status", "nvme-tcp.h2ctermreq.fes",
1105
16
             FT_UINT16, BASE_HEX, VALS(nvme_tcp_termreq_fes),
1106
16
             0x0, NULL, HFILL } },
1107
16
       { &hf_nvme_tcp_h2ctermreq_phfo,
1108
16
           { "PDU header field offset", "nvme-tcp.h2ctermreq.phfo",
1109
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1110
16
       { &hf_nvme_tcp_h2ctermreq_phd,
1111
16
           { "PDU header digest", "nvme-tcp.h2ctermreq.phd",
1112
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1113
16
       { &hf_nvme_tcp_h2ctermreq_upfo,
1114
16
           { "Unsupported parameter field offset", "nvme-tcp.h2ctermreq.upfo",
1115
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1116
16
       { &hf_nvme_tcp_h2ctermreq_reserved,
1117
16
           { "Reserved", "nvme-tcp.h2ctermreq.reserved",
1118
16
             FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
1119
16
       { &hf_nvme_tcp_h2ctermreq_data,
1120
16
           { "Terminated PDU header", "nvme-tcp.h2ctermreq.data",
1121
16
             FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
1122
16
       { &hf_nvme_fabrics_cmd_cid,
1123
16
           { "Command ID", "nvme-tcp.cmd.cid",
1124
16
             FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
1125
16
       { &hf_nvme_tcp_unknown_data,
1126
16
           { "Unknown Data", "nvme-tcp.unknown_data",
1127
16
             FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1128
       /* NVMe command data */
1129
16
       { &hf_nvme_fabrics_cmd_data,
1130
16
           { "Data", "nvme-tcp.cmd.data",
1131
16
             FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1132
16
       { &hf_nvme_tcp_cmd_pkt,
1133
16
            { "Cmd in", "nvme-tcp.cmd_pkt",
1134
16
              FT_FRAMENUM, BASE_NONE, NULL, 0,
1135
16
              "The Cmd for this transaction is in this frame", HFILL } },
1136
16
       { &hf_nvme_fabrics_cmd_qid,
1137
16
           { "Cmd Qid", "nvme-tcp.cmd.qid",
1138
16
             FT_UINT16, BASE_HEX, NULL, 0x0,
1139
16
             "Qid on which command is issued", HFILL } },
1140
      /* NVMe TCP data response */
1141
16
      { &hf_nvme_tcp_data_pdu,
1142
16
           { "NVMe/TCP Data PDU", "nvme-tcp.data",
1143
16
             FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1144
16
      { &hf_nvme_tcp_pdu_ttag,
1145
16
           { "Transfer Tag", "nvme-tcp.ttag",
1146
16
             FT_UINT16, BASE_HEX, NULL, 0x0,
1147
16
             "Transfer tag (controller generated)", HFILL } },
1148
16
      { &hf_nvme_tcp_data_pdu_data_offset,
1149
16
           { "Data Offset", "nvme-tcp.data.offset",
1150
16
             FT_UINT32, BASE_DEC, NULL, 0x0,
1151
16
             "Offset from the start of the command data", HFILL } },
1152
16
      { &hf_nvme_tcp_data_pdu_data_length,
1153
16
           { "Data Length", "nvme-tcp.data.length",
1154
16
             FT_UINT32, BASE_DEC, NULL, 0x0,
1155
16
             "Length of the data stream", HFILL } },
1156
16
      { &hf_nvme_tcp_data_pdu_data_resvd,
1157
16
           { "Reserved", "nvme-tcp.data.rsvd",
1158
16
             FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1159
      /* NVMEe TCP R2T pdu */
1160
16
      { &hf_nvme_tcp_r2t_pdu,
1161
16
           { "R2T", "nvme-tcp.r2t",
1162
16
              FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
1163
16
      { &hf_nvme_tcp_r2t_offset,
1164
16
           { "R2T Offset", "nvme-tcp.r2t.offset",
1165
16
             FT_UINT32, BASE_DEC, NULL, 0x0,
1166
16
             "Offset from the start of the command data", HFILL } },
1167
16
      { &hf_nvme_tcp_r2t_length,
1168
16
           { "R2T Length", "nvme-tcp.r2t.length",
1169
16
             FT_UINT32, BASE_DEC, NULL, 0x0,
1170
16
             "Length of the data stream", HFILL } },
1171
16
      { &hf_nvme_tcp_r2t_resvd,
1172
16
           { "Reserved", "nvme-tcp.r2t.rsvd",
1173
16
             FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }
1174
16
    };
1175
1176
16
    static int *ett[] = {
1177
16
        &ett_nvme_tcp
1178
16
    };
1179
1180
16
    static ei_register_info ei[] = {
1181
16
        { &ei_nvme_tcp_termreq_short,
1182
16
            { "nvme-tcp.termreq_too_short", PI_MALFORMED, PI_ERROR,
1183
16
              "Termination Request PDU is shorter than its header", EXPFILL } },
1184
16
    };
1185
1186
16
    expert_module_t *expert_nvme_tcp;
1187
1188
16
    proto_nvme_tcp = proto_register_protocol("NVM Express Fabrics TCP",
1189
16
            "NVMe/TCP", "nvme-tcp");
1190
1191
16
    proto_register_field_array(proto_nvme_tcp, hf, array_length(hf));
1192
16
    proto_register_subtree_array(ett, array_length(ett));
1193
1194
16
    expert_nvme_tcp = expert_register_protocol(proto_nvme_tcp);
1195
16
    expert_register_field_array(expert_nvme_tcp, ei, array_length(ei));
1196
1197
    /* These names actually work for their purpose. Note if we're already
1198
     * over TLS we don't need to do heuristics (it can't be more TLS instead
1199
     * instead, and since we managed to decrypt the TLS we shouldn't have
1200
     * missing frames and thus aren't in the middle of a PDU.)
1201
     */
1202
16
    nvmet_tcp_handle = register_dissector("nvme-tcp", dissect_nvme_tcp_heur,
1203
16
            proto_nvme_tcp);
1204
16
    nvmet_tls_handle = register_dissector_with_description("nvme-tls",
1205
16
            "NVMe-over-TCP with TLS", dissect_nvme_tcp, proto_nvme_tcp);
1206
16
}
1207
1208
16
void proto_reg_handoff_nvme_tcp(void) {
1209
16
    module_t *nvme_tcp_module;
1210
16
    nvme_tcp_module = prefs_register_protocol(proto_nvme_tcp, NULL);
1211
16
    range_convert_str(wmem_epan_scope(), &gPORT_RANGE, NVME_TCP_PORT_RANGE,
1212
16
            MAX_TCP_PORT);
1213
16
    prefs_register_range_preference(nvme_tcp_module,
1214
16
                                    "subsystem_ports",
1215
16
                                    "Subsystem Ports Range",
1216
16
                                    "Range of NVMe Subsystem ports"
1217
16
                                    "(default " NVME_TCP_PORT_RANGE ")",
1218
16
                                    &gPORT_RANGE,
1219
16
                                    MAX_TCP_PORT);
1220
16
    prefs_register_bool_preference(nvme_tcp_module, "check_hdgst",
1221
16
        "Validate PDU header digest",
1222
16
        "Whether to validate the PDU header digest or not.",
1223
16
        &nvme_tcp_check_hdgst);
1224
16
    prefs_register_bool_preference(nvme_tcp_module, "check_ddgst",
1225
16
            "Validate PDU data digest",
1226
16
            "Whether to validate the PDU data digest or not.",
1227
16
            &nvme_tcp_check_ddgst);
1228
16
    ssl_dissector_add(0, nvmet_tls_handle);
1229
16
    dissector_add_uint_range("tcp.port", gPORT_RANGE, nvmet_tcp_handle);
1230
16
    dissector_add_uint_range("tls.port", gPORT_RANGE, nvmet_tls_handle);
1231
16
}
1232
1233
/*
1234
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1235
 *
1236
 * Local variables:
1237
 * c-basic-offset: 4
1238
 * tab-width: 8
1239
 * indent-tabs-mode: nil
1240
 * End:
1241
 *
1242
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1243
 * :indentSize=4:tabSize=8:noTabs=true:
1244
 */