Coverage Report

Created: 2026-05-14 06:28

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