Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-iser.c
Line
Count
Source
1
/* packet-iser.c
2
 * Routines for iSCSI RDMA Extensions dissection
3
 * Copyright 2014, Mellanox Technologies Ltd.
4
 * Code by Yan Burman.
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
#include "config.h"
14
15
#include <stdlib.h>
16
17
#include <epan/packet.h>
18
#include <epan/prefs.h>
19
#include <epan/conversation.h>
20
#include <epan/addr_resolv.h>
21
22
#include "packet-infiniband.h"
23
24
14
#define ISER_WSV    0x08
25
14
#define ISER_RSV    0x04
26
14
#define ISER_REJ    0x01
27
28
0
#define ISER_ISCSI_CTRL    0x10
29
0
#define ISER_HELLO         0x20
30
0
#define ISER_HELLORPLY     0x30
31
32
14
#define ISER_OPCODE_MASK      0xf0
33
0
#define ISER_SPECIFIC_MASK    0x0f
34
35
337
#define ISER_HDR_SZ   (1 + 3 + 4 + 8 + 4 + 8)
36
337
#define ISCSI_HDR_SZ  48
37
38
337
#define ISER_ISCSI_HDR_SZ (ISER_HDR_SZ + ISCSI_HDR_SZ)
39
40
0
#define SID_ULP_MASK   0x00000000FF000000
41
0
#define SID_PROTO_MASK 0x0000000000FF0000
42
0
#define SID_PORT_MASK  0x000000000000FFFF
43
44
0
#define SID_ULP         0x01
45
0
#define SID_PROTO_TCP   0x06
46
14
#define TCP_PORT_ISER_RANGE    "3260"
47
48
0
#define SID_MASK (SID_ULP_MASK | SID_PROTO_MASK)
49
0
#define SID_ULP_TCP ((SID_ULP << 3 * 8) | (SID_PROTO_TCP << 2 * 8))
50
51
void proto_reg_handoff_iser(void);
52
void proto_register_iser(void);
53
54
static dissector_handle_t iser_handle;
55
56
static int proto_iser;
57
static dissector_handle_t iscsi_handler;
58
59
static int proto_ib;
60
61
/* iSER Header */
62
static int hf_iser_flags;
63
static int hf_iser_opcode_f;
64
static int hf_iser_RSV_f;
65
static int hf_iser_WSV_f;
66
static int hf_iser_REJ_f;
67
static int hf_iser_write_stag;
68
static int hf_iser_write_va;
69
static int hf_iser_read_stag;
70
static int hf_iser_read_va;
71
static int hf_iser_ird;
72
static int hf_iser_ord;
73
74
/* Initialize the subtree pointers */
75
static int ett_iser;
76
static int ett_iser_flags;
77
78
/* global preferences */
79
static range_t *gPORT_RANGE;
80
81
static const value_string iser_flags_opcode[] = {
82
    { ISER_ISCSI_CTRL >> 4, "iSCSI Control-Type PDU"},
83
    { ISER_HELLO >> 4, "Hello Message"},
84
    { ISER_HELLORPLY >> 4, "HelloReply Message"},
85
    {0, NULL},
86
};
87
88
static int * const flags_fields[] = {
89
    &hf_iser_opcode_f,
90
    &hf_iser_WSV_f,
91
    &hf_iser_RSV_f,
92
    NULL
93
};
94
static int * const hello_flags_fields[] = {
95
    &hf_iser_opcode_f,
96
    NULL
97
};
98
static int * const hellorply_flags_fields[] = {
99
    &hf_iser_opcode_f,
100
    &hf_iser_REJ_f,
101
    NULL
102
};
103
104
static int dissect_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
105
0
{
106
0
    tvbuff_t *next_tvb;
107
    /* Set up structures needed to add the protocol subtree and manage it */
108
0
    proto_item *ti;
109
0
    proto_tree *iser_tree;
110
0
    unsigned offset = 0;
111
0
    uint8_t flags, vers, opcode;
112
113
0
    if (tvb_reported_length(tvb) < ISER_ISCSI_HDR_SZ)
114
0
        return 0;
115
116
0
    flags = tvb_get_uint8(tvb, 0);
117
0
    opcode = flags & ISER_OPCODE_MASK;
118
119
    /* Check if the opcode is valid */
120
0
    switch (opcode) {
121
0
    case ISER_ISCSI_CTRL:
122
0
        switch (flags & ISER_SPECIFIC_MASK) {
123
0
        case 0:
124
0
        case ISER_WSV:
125
0
        case ISER_RSV:
126
0
        case ISER_RSV|ISER_WSV:
127
0
            break;
128
129
0
        default:
130
0
            return 0;
131
0
        }
132
0
        break;
133
134
0
    case ISER_HELLO:
135
0
    case ISER_HELLORPLY:
136
0
        vers = tvb_get_uint8(tvb, 1);
137
0
        if ((vers & 0xf) != 10)
138
0
            return 0;
139
0
        if (((vers >> 4) & 0x0f) != 10)
140
0
            return 0;
141
0
        break;
142
143
0
    default:
144
0
        return 0;
145
0
    }
146
147
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "iSER");
148
    /* Clear out stuff in the info column */
149
0
    col_clear(pinfo->cinfo, COL_INFO);
150
151
    /* Set info only for hello, since for iscsi, the iscsi dissector will */
152
0
    switch (opcode) {
153
0
    case ISER_HELLO:
154
0
        col_set_str(pinfo->cinfo, COL_INFO, "iSER Hello");
155
0
        break;
156
157
0
    case ISER_HELLORPLY:
158
0
        col_set_str(pinfo->cinfo, COL_INFO, "iSER HelloRply");
159
0
        break;
160
0
    }
161
162
0
    if (tree) {
163
        /* create display subtree for the protocol */
164
0
        ti = proto_tree_add_item(tree, proto_iser, tvb, 0, ISER_HDR_SZ, ENC_NA);
165
166
0
        iser_tree = proto_item_add_subtree(ti, ett_iser);
167
168
0
        switch (opcode) {
169
0
        case ISER_ISCSI_CTRL:
170
0
            proto_tree_add_bitmask(iser_tree, tvb, offset, hf_iser_flags,
171
0
                    ett_iser_flags, flags_fields, ENC_LITTLE_ENDIAN);
172
0
            offset += 4;
173
0
            proto_tree_add_item(iser_tree, hf_iser_write_stag, tvb,
174
0
                    offset, 4, ENC_BIG_ENDIAN);
175
0
            offset += 4;
176
0
            proto_tree_add_item(iser_tree, hf_iser_write_va, tvb,
177
0
                    offset, 8, ENC_BIG_ENDIAN);
178
0
            offset += 8;
179
0
            proto_tree_add_item(iser_tree, hf_iser_read_stag, tvb,
180
0
                    offset, 4, ENC_BIG_ENDIAN);
181
0
            offset += 4;
182
0
            proto_tree_add_item(iser_tree, hf_iser_read_va, tvb,
183
0
                    offset, 8, ENC_BIG_ENDIAN);
184
0
            break;
185
186
0
        case ISER_HELLO:
187
0
            proto_tree_add_bitmask(iser_tree, tvb, offset, hf_iser_flags,
188
0
                    ett_iser_flags, hello_flags_fields, ENC_LITTLE_ENDIAN);
189
0
            offset += 2;
190
0
            proto_tree_add_item(iser_tree, hf_iser_ird, tvb,
191
0
                    offset, 2, ENC_BIG_ENDIAN);
192
0
            break;
193
194
0
        case ISER_HELLORPLY:
195
0
            proto_tree_add_bitmask(iser_tree, tvb, offset, hf_iser_flags,
196
0
                    ett_iser_flags, hellorply_flags_fields, ENC_LITTLE_ENDIAN);
197
0
            offset += 2;
198
0
            proto_tree_add_item(iser_tree, hf_iser_ord, tvb,
199
0
                    offset, 2, ENC_BIG_ENDIAN);
200
0
            break;
201
0
        }
202
0
    }
203
204
0
    if (opcode == ISER_ISCSI_CTRL) {
205
0
            next_tvb = tvb_new_subset_remaining(tvb, ISER_HDR_SZ);
206
0
            call_dissector(iscsi_handler, next_tvb, pinfo, tree);
207
0
    }
208
209
0
    return ISER_HDR_SZ;
210
0
}
211
212
static bool
213
dissect_iser(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
214
        void *data _U_)
215
337
{
216
337
    conversation_t *conv;
217
337
    conversation_infiniband_data *convo_data = NULL;
218
219
337
    if (tvb_reported_length(tvb) < ISER_ISCSI_HDR_SZ)
220
281
        return false;
221
222
    /* first try to find a conversation between the two current hosts. in most cases this
223
       will not work since we do not have the source QP. this WILL succeed when we're still
224
       in the process of CM negotiations */
225
56
    conv = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst,
226
56
                             CONVERSATION_IBQP, pinfo->srcport, pinfo->destport, 0);
227
228
56
    if (!conv) {
229
        /* if not, try to find an established RC channel. recall Infiniband conversations are
230
           registered with one side of the channel. since the packet is only guaranteed to
231
           contain the qpn of the destination, we'll use this */
232
0
        conv = find_conversation(pinfo->num, &pinfo->dst, &pinfo->dst,
233
0
                                 CONVERSATION_IBQP, pinfo->destport, pinfo->destport, NO_ADDR_B|NO_PORT_B);
234
235
0
        if (!conv)
236
0
            return false;   /* nothing to do with no conversation context */
237
0
    }
238
239
56
    convo_data = (conversation_infiniband_data *)conversation_get_proto_data(conv, proto_ib);
240
241
56
    if (!convo_data)
242
56
        return false;
243
244
0
    if ((convo_data->service_id & SID_MASK) != SID_ULP_TCP)
245
0
        return false;   /* the service id doesn't match that of TCP ULP - nothing for us to do here */
246
247
0
    if (!(value_is_in_range(gPORT_RANGE, (uint32_t)(convo_data->service_id & SID_PORT_MASK))))
248
0
        return false;   /* the port doesn't match that of iSER - nothing for us to do here */
249
250
0
    dissect_packet(tvb, pinfo, tree, data);
251
0
    return true;
252
0
}
253
254
void
255
proto_register_iser(void)
256
14
{
257
14
    module_t *iser_module;
258
14
    static hf_register_info hf[] = {
259
14
        { &hf_iser_flags,
260
14
            { "Flags", "iser.flags",
261
14
               FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}
262
14
        },
263
14
        { &hf_iser_opcode_f,
264
14
            { "Opcode", "iser.flags.opcode",
265
14
               FT_UINT8, BASE_HEX, VALS(iser_flags_opcode),
266
14
               ISER_OPCODE_MASK, NULL, HFILL}
267
14
        },
268
14
        { &hf_iser_RSV_f,
269
14
            { "RSV", "iser.flags.rsv",
270
14
               FT_BOOLEAN, 8, NULL, ISER_RSV, "Read STag Valid", HFILL}
271
14
        },
272
14
        { &hf_iser_WSV_f,
273
14
            { "WSV", "iser.flags.wsv",
274
14
               FT_BOOLEAN, 8, NULL, ISER_WSV, "Write STag Valid", HFILL}
275
14
        },
276
14
        { &hf_iser_REJ_f,
277
14
            { "REJ", "iser.flags.rej",
278
14
               FT_BOOLEAN, 8, NULL, ISER_REJ, "Target reject connection", HFILL}
279
14
        },
280
14
        { &hf_iser_write_stag,
281
14
            { "Write STag", "iser.write_stag",
282
14
               FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}
283
14
        },
284
14
        { &hf_iser_write_va,
285
14
            { "Write Base Offset", "iser.write_base_offset",
286
14
               FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}
287
14
        },
288
14
        { &hf_iser_read_stag,
289
14
            { "Read STag", "iser.read_stag",
290
14
               FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}
291
14
        },
292
14
        { &hf_iser_read_va,
293
14
            { "Read Base Offset", "iser.read_base_offset",
294
14
               FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL}
295
14
        },
296
14
        { &hf_iser_ird,
297
14
            { "iSER-IRD", "iser.ird",
298
14
               FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
299
14
        },
300
14
        { &hf_iser_ord,
301
14
            { "iSER-ORD", "iser.ord",
302
14
               FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}
303
14
        }
304
14
    };
305
306
14
    static int *ett[] = {
307
14
        &ett_iser,
308
14
        &ett_iser_flags
309
14
    };
310
311
14
    proto_iser = proto_register_protocol (
312
14
        "iSCSI Extensions for RDMA", /* name       */
313
14
        "iSER",      /* short name */
314
14
        "iser"       /* abbrev     */
315
14
        );
316
317
14
    proto_register_field_array(proto_iser, hf, array_length(hf));
318
14
    proto_register_subtree_array(ett, array_length(ett));
319
320
    /* Register preferences */
321
14
    iser_module = prefs_register_protocol(proto_iser, proto_reg_handoff_iser);
322
323
14
    prefs_register_static_text_preference(iser_module, "use_decode_as",
324
14
        "Heuristic matching preferences removed.  Use Infiniband protocol preferences or Decode As.",
325
14
        "Simple heuristics can still be enable (may generate false positives) through Infiniband protocol preferences."
326
14
        "To force iSER dissection use Decode As");
327
328
14
    prefs_register_obsolete_preference(iser_module, "manual_en");
329
330
14
    prefs_register_obsolete_preference(iser_module, "addr_a");
331
14
    prefs_register_obsolete_preference(iser_module, "addr_a_type");
332
14
    prefs_register_obsolete_preference(iser_module, "addr_a_id");
333
14
    prefs_register_obsolete_preference(iser_module, "addr_a_qp");
334
335
14
    prefs_register_obsolete_preference(iser_module, "addr_b");
336
14
    prefs_register_obsolete_preference(iser_module, "addr_b_type");
337
14
    prefs_register_obsolete_preference(iser_module, "addr_b_id");
338
14
    prefs_register_obsolete_preference(iser_module, "addr_b_qp");
339
340
14
    range_convert_str(wmem_epan_scope(), &gPORT_RANGE, TCP_PORT_ISER_RANGE, MAX_TCP_PORT);
341
14
    prefs_register_range_preference(iser_module,
342
14
                                    "target_ports",
343
14
                                    "Target Ports Range",
344
14
                                    "Range of iSER target ports"
345
14
                                    "(default " TCP_PORT_ISER_RANGE ")",
346
14
                                    &gPORT_RANGE, MAX_TCP_PORT);
347
348
14
    iser_handle = register_dissector("iser",  dissect_packet, proto_iser );
349
14
}
350
351
void
352
proto_reg_handoff_iser(void)
353
14
{
354
14
    heur_dissector_add("infiniband.payload", dissect_iser, "iSER Infiniband", "iser_infiniband", proto_iser, HEURISTIC_ENABLE);
355
14
    heur_dissector_add("infiniband.mad.cm.private", dissect_iser, "iSER in PrivateData of CM packets", "iser_ib_private", proto_iser, HEURISTIC_ENABLE);
356
357
14
    dissector_add_for_decode_as("infiniband", iser_handle);
358
359
14
    iscsi_handler = find_dissector_add_dependency("iscsi", proto_iser);
360
14
    proto_ib = proto_get_id_by_filter_name( "infiniband" );
361
14
}
362
363
/*
364
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
365
 *
366
 * Local variables:
367
 * c-basic-offset: 4
368
 * tab-width: 8
369
 * indent-tabs-mode: nil
370
 * End:
371
 *
372
 * vi: set shiftwidth=4 tabstop=8 expandtab:
373
 * :indentSize=4:tabSize=8:noTabs=true:
374
 */