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-mctp.c
Line
Count
Source
1
/* packet-mctp.c
2
 * Routines for Management Component Transport Protocol (MCTP) packet
3
 * disassembly
4
 * Copyright 2022, Jeremy Kerr <jk@codeconstruct.com.au>
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
/*
14
 * MCTP is a datagram-based protocol for intra-platform communication,
15
 * typically between a management controller and system devices.
16
 *
17
 * MCTP is defined by DMTF standard DSP0236: https://www.dmtf.org/dsp/DSP0236
18
 */
19
20
#include <config.h>
21
22
#include <epan/packet.h>
23
#include <epan/reassemble.h>
24
#include <epan/to_str.h>
25
#include <epan/tfs.h>
26
#include <wsutil/array.h>
27
#include "packet-mctp.h"
28
#include "packet-sll.h"
29
30
0
#define MCTP_MIN_LENGTH 5       /* 4-byte header, plus message type */
31
32
void proto_register_mctp(void);
33
void proto_reg_handoff_mctp(void);
34
35
static int proto_mctp;
36
37
static int hf_mctp_ver;
38
static int hf_mctp_dst;
39
static int hf_mctp_src;
40
static int hf_mctp_flags;
41
static int hf_mctp_flags_som;
42
static int hf_mctp_flags_eom;
43
static int hf_mctp_seq;
44
static int hf_mctp_tag;
45
static int hf_mctp_tag_to;
46
static int hf_mctp_tag_value;
47
static int hf_mctp_msg_ic;
48
static int hf_mctp_msg_type;
49
50
static int ett_mctp;
51
static int ett_mctp_fst;
52
static int ett_mctp_flags;
53
static int ett_mctp_tag;
54
static int ett_mctp_type;
55
56
static const true_false_string tfs_tag_to = { "Sender", "Receiver" };
57
58
static int hf_mctp_fragments;
59
static int hf_mctp_fragment;
60
static int hf_mctp_fragment_overlap;
61
static int hf_mctp_fragment_overlap_conflicts;
62
static int hf_mctp_fragment_multiple_tails;
63
static int hf_mctp_fragment_too_long_fragment;
64
static int hf_mctp_fragment_error;
65
static int hf_mctp_fragment_count;
66
static int hf_mctp_reassembled_in;
67
static int hf_mctp_reassembled_length;
68
static int hf_mctp_reassembled_data;
69
70
static int ett_mctp_fragment;
71
static int ett_mctp_fragments;
72
73
static const fragment_items mctp_frag_items = {
74
    /* Fragment subtrees */
75
    &ett_mctp_fragment,
76
    &ett_mctp_fragments,
77
    /* Fragment fields */
78
    &hf_mctp_fragments,
79
    &hf_mctp_fragment,
80
    &hf_mctp_fragment_overlap,
81
    &hf_mctp_fragment_overlap_conflicts,
82
    &hf_mctp_fragment_multiple_tails,
83
    &hf_mctp_fragment_too_long_fragment,
84
    &hf_mctp_fragment_error,
85
    &hf_mctp_fragment_count,
86
    /* "Reassembled in" field */
87
    &hf_mctp_reassembled_in,
88
    /* Reassembled length field */
89
    &hf_mctp_reassembled_length,
90
    &hf_mctp_reassembled_data,
91
    /* Tag */
92
    "Message fragments"
93
};
94
95
static const value_string flag_vals[] = {
96
    { 0x00, "none" },
97
    { 0x01, "EOM" },
98
    { 0x02, "SOM" },
99
    { 0x03, "SOM|EOM" },
100
    { 0x00, NULL },
101
};
102
103
static const value_string type_vals[] = {
104
    { MCTP_TYPE_CONTROL, "MCTP Control Protocol" },
105
    { MCTP_TYPE_PLDM, "PLDM" },
106
    { MCTP_TYPE_NCSI, "NC-SI" },
107
    { MCTP_TYPE_ETHERNET, "Ethernet" },
108
    { MCTP_TYPE_NVME, "NVMe-MI" },
109
    { 0, NULL },
110
};
111
112
static dissector_table_t mctp_dissector_table;
113
static dissector_table_t mctp_encap_dissector_table;
114
static reassembly_table mctp_reassembly_table;
115
116
static int
117
dissect_mctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
118
        void *data _U_)
119
0
{
120
0
    proto_tree *mctp_tree, *fst_tree;
121
0
    unsigned len, ver, type, seq, fst;
122
0
    bool save_fragmented;
123
0
    proto_item *ti, *tti;
124
0
    tvbuff_t *next_tvb;
125
0
    uint8_t tag;
126
127
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "MCTP");
128
0
    col_clear(pinfo->cinfo, COL_INFO);
129
130
    /* Check that the packet is long enough for it to belong to us. */
131
0
    len = tvb_reported_length(tvb);
132
133
0
    if (len < MCTP_MIN_LENGTH) {
134
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "Bogus length %u, minimum %u",
135
0
                     len, MCTP_MIN_LENGTH);
136
0
        return tvb_captured_length(tvb);
137
0
    }
138
139
0
    ver = tvb_get_bits8(tvb, 4, 4);
140
0
    if (ver != 1) {
141
0
        col_add_fstr(pinfo->cinfo, COL_INFO, "Invalid version %u", ver);
142
0
        return tvb_captured_length(tvb);
143
0
    }
144
145
    /* Top-level protocol item & tree */
146
0
    ti = proto_tree_add_item(tree, proto_mctp, tvb, 0, 4, ENC_NA);
147
0
    mctp_tree = proto_item_add_subtree(ti, ett_mctp);
148
149
0
    set_address_tvb(&pinfo->dl_dst, AT_MCTP, 1, tvb, 1);
150
0
    set_address_tvb(&pinfo->dl_src, AT_MCTP, 1, tvb, 2);
151
0
    copy_address_shallow(&pinfo->dst, &pinfo->dl_dst);
152
0
    copy_address_shallow(&pinfo->src, &pinfo->dl_src);
153
154
0
    proto_item_append_text(ti, " Dst: %s, Src %s",
155
0
            address_to_str(pinfo->pool, &pinfo->dst),
156
0
            address_to_str(pinfo->pool, &pinfo->src));
157
158
    /* Standard header fields */
159
0
    proto_tree_add_item(mctp_tree, hf_mctp_ver, tvb, 0, 1, ENC_NA);
160
0
    proto_tree_add_item(mctp_tree, hf_mctp_dst, tvb, 1, 1, ENC_NA);
161
0
    proto_tree_add_item(mctp_tree, hf_mctp_src, tvb, 2, 1, ENC_NA);
162
163
0
    static int * const mctp_flags[] = {
164
0
        &hf_mctp_flags_som,
165
0
        &hf_mctp_flags_eom,
166
0
        NULL
167
0
    };
168
169
0
    static int * const mctp_tag[] = {
170
0
        &hf_mctp_tag_to,
171
0
        &hf_mctp_tag_value,
172
0
        NULL,
173
0
    };
174
175
0
    fst = tvb_get_uint8(tvb, 3);
176
0
    tag = fst & 0x0f;
177
0
    fst_tree = proto_tree_add_subtree_format(mctp_tree, tvb, 3, 1, ett_mctp_fst,
178
0
                                      &tti, "Flags %s, seq %d, tag %s%d",
179
0
                                      val_to_str_const(fst >> 6, flag_vals, ""),
180
0
                                      fst >> 4 & 0x3,
181
0
                                      fst & 0x08 ? "TO:" : "",
182
0
                                      fst & 0x7);
183
0
    proto_tree_add_bitmask(fst_tree, tvb, 3, hf_mctp_flags,
184
0
                           ett_mctp_flags, mctp_flags, ENC_NA);
185
0
    proto_tree_add_item_ret_uint(fst_tree, hf_mctp_seq, tvb, 3, 1, ENC_NA, &seq);
186
0
    proto_tree_add_bitmask_with_flags(fst_tree, tvb, 3, hf_mctp_tag,
187
0
                           ett_mctp_tag, mctp_tag, ENC_NA, BMT_NO_FLAGS);
188
189
    /* use the tags as our port numbers */
190
0
    pinfo->ptype = PT_MCTP;
191
0
    pinfo->srcport = tag;
192
0
    pinfo->destport = tag ^ 0x08; /* flip tag-owner bit */
193
194
0
    save_fragmented = pinfo->fragmented;
195
196
0
    col_set_str(pinfo->cinfo, COL_INFO, "MCTP message");
197
198
    /* if we're not both the start and end of a message, handle as a
199
     * fragment */
200
0
    if ((fst & 0xc0) != 0xc0) {
201
0
        fragment_head *frag_msg = NULL;
202
0
        tvbuff_t *new_tvb = NULL;
203
204
0
        pinfo->fragmented = true;
205
0
        frag_msg = fragment_add_seq_next(&mctp_reassembly_table,
206
0
                                         tvb, 4, pinfo,
207
0
                                         fst & 0x7, NULL,
208
0
                                         tvb_captured_length_remaining(tvb, 4),
209
0
                                         !(fst & 0x40));
210
211
0
        new_tvb = process_reassembled_data(tvb, 4, pinfo,
212
0
                                           "reassembled Message",
213
0
                                           frag_msg, &mctp_frag_items,
214
0
                                           NULL, mctp_tree);
215
216
0
        if (fst & 0x40)
217
0
            col_append_str(pinfo->cinfo, COL_INFO, " reassembled");
218
0
        else
219
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " frag %u", seq);
220
221
0
        next_tvb = new_tvb;
222
0
    } else {
223
0
        next_tvb = tvb_new_subset_remaining(tvb, 4);
224
0
    }
225
226
0
    if (next_tvb) {
227
0
        proto_tree *type_tree;
228
0
        int rc;
229
230
0
        type = tvb_get_uint8(next_tvb, 0);
231
0
        type_tree = proto_tree_add_subtree_format(mctp_tree, next_tvb, 0, 1,
232
0
                                                  ett_mctp_type,
233
0
                                                  &tti, "Type: %s (0x%x)%s",
234
0
                                                  val_to_str_const(type & 0x7f,
235
0
                                                                   type_vals,
236
0
                                                                   "unknown"),
237
0
                                                  type & 0x7f,
238
0
                                                  type & 0x80 ? " + IC" : "");
239
240
0
        proto_tree_add_item(type_tree, hf_mctp_msg_type, next_tvb, 0, 1,
241
0
                            ENC_NA);
242
0
        proto_tree_add_item(type_tree, hf_mctp_msg_ic, next_tvb, 0, 1,
243
0
                            ENC_NA);
244
245
0
        rc = dissector_try_uint_with_data(mctp_dissector_table, type & 0x7f,
246
0
                                    next_tvb, pinfo, tree, true, NULL);
247
248
0
        if (!rc && !(type & 0x80)) {
249
0
            tvbuff_t *encap_tvb = tvb_new_subset_remaining(next_tvb, 1);
250
0
            dissector_try_uint_with_data(mctp_encap_dissector_table, type,
251
0
                                   encap_tvb, pinfo, tree, true, NULL);
252
0
        }
253
0
    }
254
255
0
    pinfo->fragmented = save_fragmented;
256
257
0
    return tvb_captured_length(tvb);
258
0
}
259
260
void
261
proto_register_mctp(void)
262
14
{
263
    /* *INDENT-OFF* */
264
    /* Field definitions */
265
14
    static hf_register_info hf[] = {
266
14
        { &hf_mctp_ver,
267
14
          { "Version", "mctp.version",
268
14
            FT_UINT8, BASE_DEC, NULL, 0x0f,
269
14
            NULL, HFILL },
270
14
        },
271
14
        { &hf_mctp_dst,
272
14
          { "Destination", "mctp.dst",
273
14
            FT_UINT8, BASE_DEC, NULL, 0x00,
274
14
            NULL, HFILL },
275
14
        },
276
14
        { &hf_mctp_src,
277
14
          { "Source", "mctp.src",
278
14
            FT_UINT8, BASE_DEC, NULL, 0x00,
279
14
            NULL, HFILL },
280
14
        },
281
14
        { &hf_mctp_flags,
282
14
          { "Flags", "mctp.flags",
283
14
            FT_UINT8, BASE_HEX, NULL, 0xc0,
284
14
            NULL, HFILL },
285
14
        },
286
14
        { &hf_mctp_flags_som,
287
14
          { "Start of message", "mctp.flags.som",
288
14
            FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x80,
289
14
            NULL, HFILL },
290
14
        },
291
14
        { &hf_mctp_flags_eom,
292
14
          { "End of message", "mctp.flags.eom",
293
14
            FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x40,
294
14
            NULL, HFILL },
295
14
        },
296
14
        { &hf_mctp_seq,
297
14
          { "Sequence", "mctp.seq",
298
14
            FT_UINT8, BASE_HEX, NULL, 0x30,
299
14
            NULL, HFILL },
300
14
        },
301
14
        { &hf_mctp_tag,
302
14
          { "Tag", "mctp.tag",
303
14
            FT_UINT8, BASE_HEX, NULL, 0x0f,
304
14
            NULL, HFILL },
305
14
        },
306
14
        { &hf_mctp_tag_to,
307
14
          { "Tag owner", "mctp.tag.to",
308
14
            FT_BOOLEAN, 8, TFS(&tfs_tag_to), 0x08,
309
14
            NULL, HFILL },
310
14
        },
311
14
        { &hf_mctp_tag_value,
312
14
          { "Tag value", "mctp.tag.value",
313
14
            FT_UINT8, BASE_HEX, NULL, 0x07,
314
14
            NULL, HFILL },
315
14
        },
316
317
        /* message header */
318
14
        { &hf_mctp_msg_ic,
319
14
          { "Integrity check", "mctp.msg.ic",
320
14
            FT_BOOLEAN, 8, TFS(&tfs_present_absent), 0x80,
321
14
            NULL, HFILL },
322
14
        },
323
14
        { &hf_mctp_msg_type,
324
14
          { "Message type", "mctp.msg.type",
325
14
            FT_UINT8, BASE_HEX, VALS(type_vals), 0x7f,
326
14
            NULL, HFILL },
327
14
        },
328
329
        /* generic fragmentation */
330
14
        {&hf_mctp_fragments,
331
14
            {"Message fragments", "mctp.fragments",
332
14
                FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
333
14
        {&hf_mctp_fragment,
334
14
            {"Message fragment", "mctp.fragment",
335
14
                FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
336
14
        {&hf_mctp_fragment_overlap,
337
14
            {"Message fragment overlap", "mctp.fragment.overlap",
338
14
                FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
339
14
        {&hf_mctp_fragment_overlap_conflicts,
340
14
            {"Message fragment overlapping with conflicting data",
341
14
                "mctp.fragment.overlap.conflicts",
342
14
                FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
343
14
        {&hf_mctp_fragment_multiple_tails,
344
14
            {"Message has multiple tail fragments",
345
14
                "mctp.fragment.multiple_tails",
346
14
                FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
347
14
        {&hf_mctp_fragment_too_long_fragment,
348
14
            {"Message fragment too long", "mctp.fragment.too_long_fragment",
349
14
                FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
350
14
        {&hf_mctp_fragment_error,
351
14
            {"Message defragmentation error", "mctp.fragment.error",
352
14
                FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
353
14
        {&hf_mctp_fragment_count,
354
14
            {"Message fragment count", "mctp.fragment.count",
355
14
                FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
356
14
        {&hf_mctp_reassembled_in,
357
14
            {"Reassembled in", "mctp.reassembled.in",
358
14
                FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
359
14
        {&hf_mctp_reassembled_length,
360
14
            {"Reassembled length", "mctp.reassembled.length",
361
14
                FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
362
14
        {&hf_mctp_reassembled_data,
363
14
            {"Reassembled data", "mctp.reassembled.data",
364
14
                FT_BYTES, SEP_SPACE, NULL, 0x00, NULL, HFILL } },
365
14
    };
366
367
    /* protocol subtree */
368
14
    static int *ett[] = {
369
14
        &ett_mctp,
370
14
        &ett_mctp_flags,
371
14
        &ett_mctp_fst,
372
14
        &ett_mctp_tag,
373
14
        &ett_mctp_type,
374
14
        &ett_mctp_fragment,
375
14
        &ett_mctp_fragments,
376
14
    };
377
378
    /* Register the protocol name and description */
379
14
    proto_mctp = proto_register_protocol("MCTP", "MCTP", "mctp");
380
381
    /* Required function calls to register the header fields and subtrees */
382
14
    proto_register_field_array(proto_mctp, hf, array_length(hf));
383
14
    proto_register_subtree_array(ett, array_length(ett));
384
385
    /* We have two dissector tables here, both keyed off the type byte, but
386
     * with different decode semantics:
387
     *
388
     * mctp.type: for protocols that are "MCTP-aware" - they perform their
389
     *    own decoding of the type byte, including the IC bit, and possibly the
390
     *    message integrity check (which is type-specific!). For example,
391
     *    NVMe-MI, which includes the type byte in packet specifications
392
     *
393
     * mctp.encap-type: for protocols that are trivially encapsulated in a
394
     *    MCTP message, and do not handle the type byte themselves. For
395
     *    example, NC-SI over MCTP, which just wraps a NC-SI packet within
396
     *    a MCTP message.
397
     *
398
     * it doesn't make sense to allow encap-type decoders to also have the IC
399
     * bit set, as there is no specification for what format the message
400
     * integrity check is in. So, we disallow the IC bit in the type field
401
     * for those dissectors.
402
     */
403
14
    mctp_dissector_table = register_dissector_table("mctp.type", "MCTP type",
404
14
                                                    proto_mctp, FT_UINT8,
405
14
                                                    BASE_HEX);
406
14
    mctp_encap_dissector_table = register_dissector_table("mctp.encap-type",
407
14
                                                          "MCTP encapsulated type",
408
14
                                                          proto_mctp, FT_UINT8,
409
14
                                                          BASE_HEX);
410
411
14
    reassembly_table_register(&mctp_reassembly_table,
412
14
                              &addresses_reassembly_table_functions);
413
14
}
414
415
void
416
proto_reg_handoff_mctp(void)
417
14
{
418
14
    dissector_handle_t mctp_handle;
419
14
    mctp_handle = create_dissector_handle(dissect_mctp, proto_mctp);
420
14
    dissector_add_uint("sll.ltype", LINUX_SLL_P_MCTP, mctp_handle);
421
14
}
422
423
/*
424
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
425
 *
426
 * Local variables:
427
 * c-basic-offset: 4
428
 * tab-width: 8
429
 * indent-tabs-mode: nil
430
 * End:
431
 *
432
 * vi: set shiftwidth=4 tabstop=8 expandtab:
433
 * :indentSize=4:tabSize=8:noTabs=true:
434
 */