From e1cbe02ccea7322fb4020aaa193ac2d2a7282911 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Thu, 28 Oct 2021 09:01:06 +0800 Subject: [PATCH] Add Management Component Transport Protocol (MCTP) dissector This change adds a protocol dissector for the Management Component Transport Protocol (MCTP). This is a failry simple datagram-based protocol for messaging between components within a single platform, typically over I2C, serial or PCIe. This dissector just implements the header fields, and sequence-number based message reassembly. Inner protocols will be added as follow-up changes. Linux has support for AF_MCTP data, so decode from the MCTP SLL ltype. Signed-off-by: Jeremy Kerr --- docbook/release-notes.adoc | 1 + epan/address.h | 4 +- epan/address_types.c | 35 ++++ epan/conversation.c | 4 + epan/conversation.h | 2 + epan/dissectors/CMakeLists.txt | 1 + epan/dissectors/packet-mctp.c | 360 +++++++++++++++++++++++++++++++++ epan/dissectors/packet-sll.c | 1 + epan/dissectors/packet-sll.h | 1 + epan/exported_pdu.c | 2 + wsutil/exported_pdu_tlvs.h | 1 + 11 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 epan/dissectors/packet-mctp.c diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc index e4d78b64e0..3503e5f11b 100644 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -80,6 +80,7 @@ SAP Internet Graphic Server (SAP IGS) SAP Network Interface (SAPNI) World of Warcraft World (WOWW) display filters have been changed to be more internally consistent. Support for almost all WoW 1.12 messages has been added. +Management Component Transport Protocol (MCTP) -- === Updated Protocol Support diff --git a/epan/address.h b/epan/address.h index 4196a6655e..ab40b6a3fb 100644 --- a/epan/address.h +++ b/epan/address.h @@ -47,6 +47,7 @@ typedef enum { AT_AX25, /* AX.25 */ AT_VINES, /* Banyan Vines address */ AT_NUMERIC, /* Numeric address type. */ + AT_MCTP, /* MCTP */ AT_END_OF_LIST /* Must be last in list */ } address_type; @@ -363,7 +364,8 @@ typedef enum { PT_I2C, PT_IBQP, /* Infiniband QP number */ PT_BLUETOOTH, - PT_IWARP_MPA /* iWarp MPA */ + PT_IWARP_MPA, /* iWarp MPA */ + PT_MCTP } port_type; #ifdef __cplusplus diff --git a/epan/address_types.c b/epan/address_types.c index fd9439425c..2f4a6c1116 100644 --- a/epan/address_types.c +++ b/epan/address_types.c @@ -583,6 +583,28 @@ static int numeric_addr_to_str(const address* addr, gchar *buf, int buf_len) return ret + 1; } +/****************************************************************************** + * AT_MCTP + ******************************************************************************/ + +static int mctp_addr_to_str(const address* addr, gchar *buf, int buf_len _U_) +{ + const guint8 *addr_data = (const guint8 *)addr->data; + gchar *bufp = buf; + + return g_snprintf(bufp, 3, "%d", addr_data[0]); +} + +static int mctp_addr_str_len(const address* addr _U_) +{ + return 3; +} + +static int mctp_len(void) +{ + return 1; +} + /****************************************************************************** * END OF PROVIDED ADDRESS TYPES ******************************************************************************/ @@ -759,6 +781,18 @@ void address_types_initialize(void) NULL, /* addr_name_res_str */ NULL, /* addr_name_res_len */ }; + static address_type_t mctp_address = { + AT_MCTP, /* addr_type */ + "AT_MCTP" , /* name */ + "MCTP Address", /* pretty_name */ + mctp_addr_to_str, /* addr_to_str */ + mctp_addr_str_len, /* addr_str_len */ + NULL, /* addr_to_byte */ + NULL, /* addr_col_filter */ + mctp_len, /* addr_fixed_len */ + NULL, /* addr_name_res_str */ + NULL, /* addr_name_res_len */ + }; num_dissector_addr_type = 0; @@ -779,6 +813,7 @@ void address_types_initialize(void) address_type_register(AT_AX25, &ax25_address ); address_type_register(AT_VINES, &vines_address ); address_type_register(AT_NUMERIC, &numeric_address ); + address_type_register(AT_MCTP, &mctp_address ); } /* Given an address type id, return an address_type_t* */ diff --git a/epan/conversation.c b/epan/conversation.c index 88384aaadc..f8d17afe28 100644 --- a/epan/conversation.c +++ b/epan/conversation.c @@ -1912,6 +1912,8 @@ conversation_type conversation_pt_to_conversation_type(port_type pt) return CONVERSATION_BLUETOOTH; case PT_IWARP_MPA: return CONVERSATION_IWARP_MPA; + case PT_MCTP: + return CONVERSATION_MCTP; } DISSECTOR_ASSERT(FALSE); @@ -1950,6 +1952,8 @@ endpoint_type conversation_pt_to_endpoint_type(port_type pt) return ENDPOINT_BLUETOOTH; case PT_IWARP_MPA: return ENDPOINT_IWARP_MPA; + case PT_MCTP: + return ENDPOINT_MCTP; } DISSECTOR_ASSERT(FALSE); diff --git a/epan/conversation.h b/epan/conversation.h index 95d39a19f1..886a9a6357 100644 --- a/epan/conversation.h +++ b/epan/conversation.h @@ -89,6 +89,7 @@ typedef enum { CONVERSATION_BT_UTP, /* BitTorrent uTP Connection ID */ CONVERSATION_LOG, /* Logging source */ CONVERSATION_LTP, /* LTP Engine ID and Session Number */ + CONVERSATION_MCTP, } conversation_type; /* @@ -133,6 +134,7 @@ typedef enum { #define ENDPOINT_IWARP_MPA CONVERSATION_IWARP_MPA #define ENDPOINT_BT_UTP CONVERSATION_BT_UTP #define ENDPOINT_LOG CONVERSATION_LOG +#define ENDPOINT_MCTP CONVERSATION_MCTP typedef conversation_type endpoint_type; diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index 9056fc1b8e..30dda05643 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -1439,6 +1439,7 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-mbtcp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-mc-nmf.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-mcpe.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-mctp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-mdp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-mdshdr.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-media.c diff --git a/epan/dissectors/packet-mctp.c b/epan/dissectors/packet-mctp.c new file mode 100644 index 0000000000..6baac3dfa3 --- /dev/null +++ b/epan/dissectors/packet-mctp.c @@ -0,0 +1,360 @@ +/* packet-mctp.c + * Routines for Management Component Transport Protocol (MCTP) packet + * disassembly + * Copyright 2022, Jeremy Kerr + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * MCTP is a datagram-based protocol for intra-platform communication, + * typically between a management controller and system devices. + * + * MCTP is defined by DMTF standard DSP0236: https://www.dmtf.org/dsp/DSP0236 + */ + +#include + +#include +#include +#include +#include + +#define MCTP_MIN_LENGTH 5 /* 4-byte header, plus message type */ + +void proto_register_mctp(void); +void proto_reg_handoff_mctp(void); + +static int proto_mctp = -1; + +static int hf_mctp_ver = -1; +static int hf_mctp_dst = -1; +static int hf_mctp_src = -1; +static int hf_mctp_flags = -1; +static int hf_mctp_flags_som = -1; +static int hf_mctp_flags_eom = -1; +static int hf_mctp_seq = -1; +static int hf_mctp_tag = -1; +static int hf_mctp_tag_to = -1; +static int hf_mctp_tag_value = -1; + +static gint ett_mctp = -1; +static gint ett_mctp_fst = -1; +static gint ett_mctp_flags = -1; +static gint ett_mctp_tag = -1; + +static const true_false_string tfs_tag_to = { "Sender", "Receiver" }; + +static int hf_mctp_fragments = -1; +static int hf_mctp_fragment = -1; +static int hf_mctp_fragment_overlap = -1; +static int hf_mctp_fragment_overlap_conflicts = -1; +static int hf_mctp_fragment_multiple_tails = -1; +static int hf_mctp_fragment_too_long_fragment = -1; +static int hf_mctp_fragment_error = -1; +static int hf_mctp_fragment_count = -1; +static int hf_mctp_reassembled_in = -1; +static int hf_mctp_reassembled_length = -1; +static int hf_mctp_reassembled_data = -1; + +static gint ett_mctp_fragment = -1; +static gint ett_mctp_fragments = -1; + +static const fragment_items mctp_frag_items = { + /* Fragment subtrees */ + &ett_mctp_fragment, + &ett_mctp_fragments, + /* Fragment fields */ + &hf_mctp_fragments, + &hf_mctp_fragment, + &hf_mctp_fragment_overlap, + &hf_mctp_fragment_overlap_conflicts, + &hf_mctp_fragment_multiple_tails, + &hf_mctp_fragment_too_long_fragment, + &hf_mctp_fragment_error, + &hf_mctp_fragment_count, + /* "Reassembled in" field */ + &hf_mctp_reassembled_in, + /* Reassembled length field */ + &hf_mctp_reassembled_length, + &hf_mctp_reassembled_data, + /* Tag */ + "Message fragments" +}; + +static const value_string flag_vals[] = { + { 0x00, "none" }, + { 0x01, "EOM" }, + { 0x02, "SOM" }, + { 0x03, "SOM|EOM" }, + { 0x00, NULL }, +}; + +static dissector_table_t mctp_dissector_table; +static reassembly_table mctp_reassembly_table; + +static int +dissect_mctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + void *data _U_) +{ + proto_tree *mctp_tree, *fst_tree; + guint len, ver, type, seq, fst; + bool save_fragmented; + proto_item *ti, *tti; + tvbuff_t *next_tvb; + guint8 tag; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "MCTP"); + col_clear(pinfo->cinfo, COL_INFO); + + /* Check that the packet is long enough for it to belong to us. */ + len = tvb_reported_length(tvb); + + if (len < MCTP_MIN_LENGTH) { + col_add_fstr(pinfo->cinfo, COL_INFO, "Bogus length %u, minimum %u", + len, MCTP_MIN_LENGTH); + return tvb_captured_length(tvb); + } + + ver = tvb_get_bits8(tvb, 4, 4); + if (ver != 1) { + col_add_fstr(pinfo->cinfo, COL_INFO, "Invalid version %u", ver); + return tvb_captured_length(tvb); + } + + /* Top-level protocol item & tree */ + ti = proto_tree_add_item(tree, proto_mctp, tvb, 0, 4, ENC_NA); + mctp_tree = proto_item_add_subtree(ti, ett_mctp); + + set_address_tvb(&pinfo->dl_dst, AT_MCTP, 1, tvb, 1); + set_address_tvb(&pinfo->dl_src, AT_MCTP, 1, tvb, 2); + copy_address_shallow(&pinfo->dst, &pinfo->dl_dst); + copy_address_shallow(&pinfo->src, &pinfo->dl_src); + + proto_item_append_text(ti, " Dst: %s, Src %s", + address_to_str(pinfo->pool, &pinfo->dst), + address_to_str(pinfo->pool, &pinfo->src)); + + /* Standard header fields */ + proto_tree_add_item(mctp_tree, hf_mctp_ver, tvb, 0, 1, ENC_NA); + proto_tree_add_item(mctp_tree, hf_mctp_dst, tvb, 1, 1, ENC_NA); + proto_tree_add_item(mctp_tree, hf_mctp_src, tvb, 2, 1, ENC_NA); + + static int * const mctp_flags[] = { + &hf_mctp_flags_som, + &hf_mctp_flags_eom, + NULL + }; + + static int * const mctp_tag[] = { + &hf_mctp_tag_to, + &hf_mctp_tag_value, + NULL, + }; + + fst = tvb_get_guint8(tvb, 3); + tag = fst & 0x0f; + fst_tree = proto_tree_add_subtree_format(mctp_tree, tvb, 3, 1, ett_mctp_fst, + &tti, "Flags %s, seq %d, tag %s%d", + val_to_str_const(fst >> 6, flag_vals, ""), + fst >> 4 & 0x3, + fst & 0x08 ? "TO:" : "", + fst & 0x7); + proto_tree_add_bitmask(fst_tree, tvb, 3, hf_mctp_flags, + ett_mctp_flags, mctp_flags, ENC_NA); + proto_tree_add_item_ret_uint(fst_tree, hf_mctp_seq, tvb, 3, 1, ENC_NA, &seq); + proto_tree_add_bitmask_with_flags(fst_tree, tvb, 3, hf_mctp_tag, + ett_mctp_tag, mctp_tag, ENC_NA, BMT_NO_FLAGS); + + /* use the tags as our port numbers */ + pinfo->ptype = PT_MCTP; + pinfo->srcport = tag; + pinfo->destport = tag ^ 0x08; /* flip tag-owner bit */ + + save_fragmented = pinfo->fragmented; + + col_set_str(pinfo->cinfo, COL_INFO, "MCTP message"); + + /* if we're not both the start and end of a message, handle as a + * fragment */ + if ((fst & 0xc0) != 0xc0) { + fragment_head *frag_msg = NULL; + tvbuff_t *new_tvb = NULL; + + pinfo->fragmented = true; + frag_msg = fragment_add_seq_next(&mctp_reassembly_table, + tvb, 4, pinfo, + fst & 0x7, NULL, + tvb_captured_length_remaining(tvb, 4), + !(fst & 0x40)); + + new_tvb = process_reassembled_data(tvb, 4, pinfo, + "reassembled Message", + frag_msg, &mctp_frag_items, + NULL, mctp_tree); + + if (fst & 0x40) + col_append_str(pinfo->cinfo, COL_INFO, " reassembled"); + else + col_append_fstr(pinfo->cinfo, COL_INFO, " frag %u", seq); + + next_tvb = new_tvb; + } else { + next_tvb = tvb_new_subset_remaining(tvb, 4); + } + + if (next_tvb) { + type = tvb_get_guint8(next_tvb, 0); + dissector_try_uint_new(mctp_dissector_table, type & 0x7f, next_tvb, + pinfo, tree, true, NULL); + } + + pinfo->fragmented = save_fragmented; + + return tvb_captured_length(tvb); +} + +void +proto_register_mctp(void) +{ + /* *INDENT-OFF* */ + /* Field definitions */ + static hf_register_info hf[] = { + { &hf_mctp_ver, + { "Version", "mctp.version", + FT_UINT8, BASE_DEC, NULL, 0x0f, + NULL, HFILL }, + }, + { &hf_mctp_dst, + { "Destination", "mctp.dst", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL }, + }, + { &hf_mctp_src, + { "Source", "mctp.src", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL }, + }, + { &hf_mctp_flags, + { "Flags", "mctp.flags", + FT_UINT8, BASE_HEX, NULL, 0xc0, + NULL, HFILL }, + }, + { &hf_mctp_flags_som, + { "Start of message", "mctp.flags.som", + FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x80, + NULL, HFILL }, + }, + { &hf_mctp_flags_eom, + { "End of message", "mctp.flags.eom", + FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x40, + NULL, HFILL }, + }, + { &hf_mctp_seq, + { "Sequence", "mctp.seq", + FT_UINT8, BASE_HEX, NULL, 0x30, + NULL, HFILL }, + }, + { &hf_mctp_tag, + { "Tag", "mctp.tag", + FT_UINT8, BASE_HEX, NULL, 0x0f, + NULL, HFILL }, + }, + { &hf_mctp_tag_to, + { "Tag owner", "mctp.tag.to", + FT_BOOLEAN, 8, TFS(&tfs_tag_to), 0x08, + NULL, HFILL }, + }, + { &hf_mctp_tag_value, + { "Tag value", "mctp.tag.value", + FT_UINT8, BASE_HEX, NULL, 0x07, + NULL, HFILL }, + }, + + /* generic fragmentation */ + {&hf_mctp_fragments, + {"Message fragments", "mctp.fragments", + FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment, + {"Message fragment", "mctp.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment_overlap, + {"Message fragment overlap", "mctp.fragment.overlap", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment_overlap_conflicts, + {"Message fragment overlapping with conflicting data", + "mctp.fragment.overlap.conflicts", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment_multiple_tails, + {"Message has multiple tail fragments", + "mctp.fragment.multiple_tails", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment_too_long_fragment, + {"Message fragment too long", "mctp.fragment.too_long_fragment", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment_error, + {"Message defragmentation error", "mctp.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_fragment_count, + {"Message fragment count", "mctp.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_reassembled_in, + {"Reassembled in", "mctp.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_reassembled_length, + {"Reassembled length", "mctp.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_mctp_reassembled_data, + {"Reassembled data", "mctp.reassembled.data", + FT_BYTES, SEP_SPACE, NULL, 0x00, NULL, HFILL } }, + }; + + /* protocol subtree */ + static gint *ett[] = { + &ett_mctp, + &ett_mctp_flags, + &ett_mctp_fst, + &ett_mctp_tag, + &ett_mctp_fragment, + &ett_mctp_fragments, + }; + + /* Register the protocol name and description */ + proto_mctp = proto_register_protocol("MCTP", "MCTP", "mctp"); + + /* Required function calls to register the header fields and subtrees */ + proto_register_field_array(proto_mctp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + mctp_dissector_table = register_dissector_table("mctp.type", "MCTP type", + proto_mctp, FT_UINT8, + BASE_HEX); + + reassembly_table_register(&mctp_reassembly_table, + &addresses_reassembly_table_functions); +} + +void +proto_reg_handoff_mctp(void) +{ + dissector_handle_t mctp_handle; + mctp_handle = create_dissector_handle(dissect_mctp, proto_mctp); + dissector_add_uint("sll.ltype", LINUX_SLL_P_MCTP, mctp_handle); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-sll.c b/epan/dissectors/packet-sll.c index f20176a76e..e2920a23c6 100644 --- a/epan/dissectors/packet-sll.c +++ b/epan/dissectors/packet-sll.c @@ -74,6 +74,7 @@ static const value_string ltype_vals[] = { { LINUX_SLL_P_IRDA_LAP, "IrDA LAP" }, { LINUX_SLL_P_ISI, "ISI" }, { LINUX_SLL_P_IEEE802154, "IEEE 802.15.4" }, + { LINUX_SLL_P_MCTP, "MCTP" }, { 0, NULL } }; diff --git a/epan/dissectors/packet-sll.h b/epan/dissectors/packet-sll.h index 9dcc5ca5e0..c34c7bb8c9 100644 --- a/epan/dissectors/packet-sll.h +++ b/epan/dissectors/packet-sll.h @@ -25,5 +25,6 @@ #define LINUX_SLL_P_IRDA_LAP 0x0017 /* IrDA Link Access Protocol */ #define LINUX_SLL_P_ISI 0x00F5 /* Intelligent Service Interface */ #define LINUX_SLL_P_IEEE802154 0x00f6 /* 802.15.4 on monitor inteface */ +#define LINUX_SLL_P_MCTP 0x00fa /* Management Component Transport Protocol */ #endif diff --git a/epan/exported_pdu.c b/epan/exported_pdu.c index 49b5de0892..9efcb2dfe1 100644 --- a/epan/exported_pdu.c +++ b/epan/exported_pdu.c @@ -114,6 +114,8 @@ static guint exp_pdu_ws_port_type_to_exp_pdu_port_type(port_type pt) return EXP_PDU_PT_BLUETOOTH; case PT_IWARP_MPA: return EXP_PDU_PT_IWARP_MPA; + case PT_MCTP: + return EXP_PDU_PT_MCTP; } DISSECTOR_ASSERT(FALSE); diff --git a/wsutil/exported_pdu_tlvs.h b/wsutil/exported_pdu_tlvs.h index b5325c4f27..c193eac83d 100644 --- a/wsutil/exported_pdu_tlvs.h +++ b/wsutil/exported_pdu_tlvs.h @@ -119,6 +119,7 @@ #define EXP_PDU_PT_BLUETOOTH 15 #define EXP_PDU_PT_TDMOP 16 #define EXP_PDU_PT_IWARP_MPA 17 +#define EXP_PDU_PT_MCTP 18 #define EXP_PDU_TAG_PORT_TYPE 24 /**< part type - 4 bytes, EXP_PDU_PT value */ #define EXP_PDU_TAG_SRC_PORT 25 /**< source port - 4 bytes (even for protocols with 2-byte ports) */