2 * Routines for Sparkplug dissection
3 * Copyright 2021 Graham Bloice <graham.bloice<at>trihedral.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <epan/packet.h>
14 #include <epan/expert.h>
19 * https://cirrus-link.com/mqtt-sparkplug-tahu/
21 * and the specification is at
23 * https://www.eclipse.org/tahu/spec/Sparkplug%20Topic%20Namespace%20and%20State%20ManagementV2.2-with%20appendix%20B%20format%20-%20Eclipse.pdf
28 /* (Required to prevent [-Wmissing-prototypes] warnings */
29 void proto_reg_handoff_sparkplug(void);
30 void proto_register_sparkplug(void);
32 /* Initialize the protocol field */
33 static int proto_sparkplugb
;
35 /* Initialize the subtree pointers */
36 static int ett_sparkplugb
;
37 static int ett_sparkplugb_namespace
;
39 /* The handle to the protobuf dissector */
40 dissector_handle_t protobuf_handle
;
43 static int hf_sparkplugb_namespace
;
44 static int hf_sparkplugb_groupid
;
45 static int hf_sparkplugb_messagetype
;
46 static int hf_sparkplugb_edgenodeid
;
47 static int hf_sparkplugb_deviceid
;
49 /* The expert info items */
50 static expert_field ei_sparkplugb_missing_groupid
;
51 static expert_field ei_sparkplugb_missing_messagetype
;
52 static expert_field ei_sparkplugb_missing_edgenodeid
;
55 dissect_sparkplugb(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
58 proto_tree
*sparkplugb_tree
, *namespace_tree
;
59 gchar
**topic_elements
, **current_element
;
60 char *topic
= (char *)data
;
62 /* Confirm the expected topic data is present */
66 /* Parse the topic into the elements */
67 topic_elements
= g_strsplit(topic
, "/", 5);
69 /* Heuristic check that the first element of the topic is the SparkplugB namespace */
70 if (!topic_elements
|| (g_strcmp0("spBv1.0", topic_elements
[0]) != 0)) {
71 g_strfreev(topic_elements
);
75 /* Make entries in Protocol column */
76 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "SparkplugB");
78 /* Adjust the info column */
79 col_clear(pinfo
->cinfo
, COL_INFO
);
80 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, "SparkplugB");
82 /* create display subtree for the protocol */
83 ti
= proto_tree_add_item(tree
, proto_sparkplugb
, tvb
, 0, -1, ENC_NA
);
84 sparkplugb_tree
= proto_item_add_subtree(ti
, ett_sparkplugb
);
86 /* Add the elements parsed out from the topic string */
87 namespace_tree
= proto_tree_add_subtree(sparkplugb_tree
, tvb
, 0, 0, ett_sparkplugb_namespace
, &ti
, "Topic Namespace");
88 proto_item_set_generated(ti
);
90 current_element
= topic_elements
;
91 ti
= proto_tree_add_string(namespace_tree
, hf_sparkplugb_namespace
, tvb
, 0, 0, current_element
[0]);
92 proto_item_set_generated(ti
);
95 if (current_element
[0]) {
96 ti
= proto_tree_add_string(namespace_tree
, hf_sparkplugb_groupid
, tvb
, 0, 0, current_element
[0]);
97 proto_item_set_generated(ti
);
100 expert_add_info(pinfo
, namespace_tree
, &ei_sparkplugb_missing_groupid
);
104 /* Adjust the info column text with the message type */
105 current_element
+= 1;
106 if (current_element
[0]) {
107 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, current_element
[0]);
108 col_set_fence(pinfo
->cinfo
, COL_INFO
);
110 ti
= proto_tree_add_string(namespace_tree
, hf_sparkplugb_messagetype
, tvb
, 0, 0, current_element
[0]);
111 proto_item_set_generated(ti
);
114 expert_add_info(pinfo
, namespace_tree
, &ei_sparkplugb_missing_messagetype
);
118 current_element
+= 1;
119 if (current_element
[0]) {
120 ti
= proto_tree_add_string(namespace_tree
, hf_sparkplugb_edgenodeid
, tvb
, 0, 0, current_element
[0]);
121 proto_item_set_generated(ti
);
124 expert_add_info(pinfo
, namespace_tree
, &ei_sparkplugb_missing_edgenodeid
);
128 /* Device ID is optional */
129 current_element
+= 1;
130 if (current_element
[0]) {
131 ti
= proto_tree_add_string(namespace_tree
, hf_sparkplugb_deviceid
, tvb
, 0, 0, current_element
[0]);
132 proto_item_set_generated(ti
);
135 g_strfreev(topic_elements
);
137 /* Now handoff the Payload message to the protobuf dissector */
138 call_dissector_with_data(protobuf_handle
, tvb
, pinfo
, sparkplugb_tree
, "message,com.cirruslink.sparkplug.protobuf.Payload");
143 static bool dissect_sparkplugb_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void *data
)
145 return (bool)dissect_sparkplugb(tvb
, pinfo
, parent_tree
, data
);
148 void proto_register_sparkplug(void)
150 expert_module_t
* expert_sparkplugb
;
152 static int *ett
[] = {
154 &ett_sparkplugb_namespace
157 static hf_register_info hf
[] = {
158 {&hf_sparkplugb_namespace
, {"Namespace", "sparkplugb.namespace", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
159 {&hf_sparkplugb_groupid
, {"Group ID", "sparkplugb.groupid", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
160 {&hf_sparkplugb_messagetype
, {"Message Type", "sparkplugb.messagetype", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
161 {&hf_sparkplugb_edgenodeid
, {"Edge Node ID", "sparkplugb.edgenodeid", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
162 {&hf_sparkplugb_deviceid
, {"Device ID", "sparkplugb.deviceid", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
165 static ei_register_info ei
[] = {
166 { &ei_sparkplugb_missing_groupid
, { "sparkplugb.missing_groupid", PI_MALFORMED
, PI_ERROR
, "Missing Group ID", EXPFILL
}},
167 { &ei_sparkplugb_missing_messagetype
, { "sparkplugb.missing_messagetype", PI_MALFORMED
, PI_ERROR
, "Missing Message Type", EXPFILL
}},
168 { &ei_sparkplugb_missing_edgenodeid
, { "sparkplugb.missing_edgenodeid", PI_MALFORMED
, PI_ERROR
, "Missing Edge Node ID", EXPFILL
}},
171 /* Register the protocol name and description, fields and trees */
172 proto_sparkplugb
= proto_register_protocol("SparkplugB", "SparkplugB", "sparkplugb");
173 register_dissector("sparkplugb", dissect_sparkplugb
, proto_sparkplugb
);
175 proto_register_field_array(proto_sparkplugb
, hf
, array_length(hf
));
176 proto_register_subtree_array(ett
, array_length(ett
));
178 expert_sparkplugb
= expert_register_protocol(proto_sparkplugb
);
179 expert_register_field_array(expert_sparkplugb
, ei
, array_length(ei
));
182 void proto_reg_handoff_sparkplug(void)
184 protobuf_handle
= find_dissector_add_dependency("protobuf", proto_sparkplugb
);
186 /* register as heuristic dissector with MQTT */
187 heur_dissector_add("mqtt.topic", dissect_sparkplugb_heur
, "SparkplugB over MQTT",
188 "sparkplugb_mqtt", proto_sparkplugb
, HEURISTIC_ENABLE
);