aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt18
-rw-r--r--NEWS4
-rw-r--r--docbook/release-notes.adoc1
-rw-r--r--epan/dissectors/CMakeLists.txt1
-rw-r--r--epan/dissectors/packet-mqtt.c2
-rw-r--r--epan/dissectors/packet-sparkplug.c169
-rw-r--r--packaging/nsis/uninstall.nsi2
-rw-r--r--packaging/nsis/wireshark.nsi6
-rw-r--r--packaging/wix/ComponentGroups.wxi14
-rw-r--r--packaging/wix/DirectoryStructure.wxi1
-rw-r--r--packaging/wix/Features.wxi1
-rw-r--r--packaging/wix/InputPaths.wxi1
-rw-r--r--protobuf/sparkplug_b.proto197
13 files changed, 414 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3c4fd38cf6..04f3b03b5e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2274,6 +2274,23 @@ add_custom_command(
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
+file(GLOB _protobuf_src_files RELATIVE "${CMAKE_SOURCE_DIR}" protobuf/*.proto)
+set (_protobuf_data_files)
+foreach(_data_file ${_protobuf_src_files})
+ list(APPEND _protobuf_data_files "${DATAFILE_DIR}/${_data_file}")
+endforeach()
+
+add_custom_command(
+ OUTPUT ${_protobuf_data_files}
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${DATAFILE_DIR}/protobuf"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${_protobuf_src_files}"
+ "${DATAFILE_DIR}/protobuf"
+ VERBATIM
+ DEPENDS ${_protobuf_src_files}
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+)
+
file(GLOB _profiles_src_files RELATIVE "${CMAKE_SOURCE_DIR}" profiles/*/*)
set (_profiles_data_files)
foreach(_data_file ${_profiles_src_files})
@@ -2290,6 +2307,7 @@ list(APPEND copy_data_files_depends
${_dtds_data_files}
${_diameter_data_files}
${_radius_data_files}
+ ${_protobuf_data_files}
${_profiles_data_files}
)
diff --git a/NEWS b/NEWS
index d77903c1c4..8eaa1bf868 100644
--- a/NEWS
+++ b/NEWS
@@ -86,8 +86,8 @@ Wireshark 3.5.0 Release Notes
New Protocol Support
Kerberos SPAKE, O-RAN fronthaul UC-plane (O-RAN), PDU Transport
- Protocol, R09.x (R09), State Synchronization Protocol (SSyncP), and
- UAVCAN\CAN
+ Protocol, R09.x (R09), SparkplugB, State Synchronization Protocol (SSyncP),
+ and UAVCAN\CAN.
Updated Protocol Support
diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc
index b5e122fbc6..23d29f70e6 100644
--- a/docbook/release-notes.adoc
+++ b/docbook/release-notes.adoc
@@ -88,6 +88,7 @@ They previously shipped with Npcap 1.20.
Kerberos SPAKE
O-RAN fronthaul UC-plane (O-RAN)
PDU Transport Protocol
+SparkplugB
State Synchronization Protocol (SSyncP)
UAVCAN\CAN
R09.x (R09)
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index 48e607c198..d430db1959 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -1773,6 +1773,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-someip.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-someip-sd.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-soupbintcp.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-sparkplug.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-spdy.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-spice.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-spp.c
diff --git a/epan/dissectors/packet-mqtt.c b/epan/dissectors/packet-mqtt.c
index c8503e3780..79fbbc5e39 100644
--- a/epan/dissectors/packet-mqtt.c
+++ b/epan/dissectors/packet-mqtt.c
@@ -752,7 +752,7 @@ static gboolean mqtt_user_decode_message(proto_tree *tree, proto_tree *mqtt_tree
message_decode_entry->payload_proto_name);
proto_item_set_generated(ti);
- call_dissector(message_decode_entry->payload_proto, msg_tvb, pinfo, tree);
+ call_dissector_with_data(message_decode_entry->payload_proto, msg_tvb, pinfo, tree, (void*)topic_str);
}
}
diff --git a/epan/dissectors/packet-sparkplug.c b/epan/dissectors/packet-sparkplug.c
new file mode 100644
index 0000000000..86d888acfe
--- /dev/null
+++ b/epan/dissectors/packet-sparkplug.c
@@ -0,0 +1,169 @@
+/* packet-sparkplug.c
+ * Routines for Sparkplug dissection
+ * Copyright 2021 Graham Bloice <graham.bloice<at>trihedral.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <epan/packet.h>
+#include <epan/expert.h>
+
+/*
+ * See
+ *
+ * https://cirrus-link.com/mqtt-sparkplug-tahu/
+ *
+ * and the specification is at
+ *
+ * https://www.eclipse.org/tahu/spec/Sparkplug%20Topic%20Namespace%20and%20State%20ManagementV2.2-with%20appendix%20B%20format%20-%20Eclipse.pdf
+ *
+ */
+
+/* Prototypes */
+/* (Required to prevent [-Wmissing-prototypes] warnings */
+void proto_reg_handoff_sparkplug(void);
+
+/* Initialize the protocol field */
+static int proto_sparkplugb = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_sparkplugb = -1;
+static gint ett_sparkplugb_namespace = -1;
+
+/* The handle to the protobuf dissector */
+dissector_handle_t protobuf_handle = NULL;
+
+/* The hf items */
+static int hf_sparkplugb_namespace = -1;
+static int hf_sparkplugb_groupid = -1;
+static int hf_sparkplugb_messagetype = -1;
+static int hf_sparkplugb_edgenodeid = -1;
+static int hf_sparkplugb_deviceid = -1;
+
+/* The expert info items */
+static expert_field ei_sparkplugb_missing_groupid = EI_INIT;
+static expert_field ei_sparkplugb_missing_messagetype = EI_INIT;
+static expert_field ei_sparkplugb_missing_edgenodeid = EI_INIT;
+
+static gboolean
+dissect_sparkplugb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ proto_item *ti;
+ proto_tree *sparkplugb_tree, *namespace_tree;
+ gchar **topic_elements, **current_element;
+ char *topic = (char *)data;
+
+ /* Parse the topic into the elements */
+ topic_elements = g_strsplit(topic, "/", 5);
+
+ /* Heuristic check that the first element of the topic is the SparkplugB namespace */
+ if (!topic_elements || (strcmp("spBv1.0", topic_elements[0]) != 0)) {
+ return FALSE;
+ }
+
+ /* Make entries in Protocol column */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SparkplugB");
+
+ /* Adjust the info column */
+ col_clear(pinfo->cinfo, COL_INFO);
+ col_set_str(pinfo->cinfo, COL_INFO, "SparkplugB");
+
+ /* create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_sparkplugb, tvb, 0, -1, ENC_NA);
+ sparkplugb_tree = proto_item_add_subtree(ti, ett_sparkplugb);
+
+ /* Add the elements parsed out from the topic string */
+ namespace_tree = proto_tree_add_subtree(sparkplugb_tree, tvb, 0, 0, ett_sparkplugb_namespace, &ti, "Topic Namespace");
+ proto_item_set_generated(ti);
+
+ current_element = topic_elements;
+ ti = proto_tree_add_string(namespace_tree, hf_sparkplugb_namespace, tvb, 0, 0, current_element[0]);
+ proto_item_set_generated(ti);
+
+ current_element += 1;
+ ti = proto_tree_add_string(namespace_tree, hf_sparkplugb_groupid, tvb, 0, 0, current_element[0]);
+ proto_item_set_generated(ti);
+ if (!current_element[0]) {
+ expert_add_info(pinfo, ti, &ei_sparkplugb_missing_groupid);
+ }
+
+ /* Adjust the info colum text with the message type */
+ current_element += 1;
+ if (current_element[0]) {
+ col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, current_element[0]);
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ }
+
+ ti = proto_tree_add_string(namespace_tree, hf_sparkplugb_messagetype, tvb, 0, 0, current_element[0]);
+ proto_item_set_generated(ti);
+ if (!current_element[0]) {
+ expert_add_info(pinfo, ti, &ei_sparkplugb_missing_messagetype);
+ }
+
+ current_element += 1;
+ ti = proto_tree_add_string(namespace_tree, hf_sparkplugb_edgenodeid, tvb, 0, 0, current_element[0]);
+ proto_item_set_generated(ti);
+ if (!current_element[0]) {
+ expert_add_info(pinfo, ti, &ei_sparkplugb_missing_edgenodeid);
+ }
+
+ /* Device ID is optional */
+ current_element += 1;
+ ti = proto_tree_add_string(namespace_tree, hf_sparkplugb_deviceid, tvb, 0, 0, current_element[0]);
+ proto_item_set_generated(ti);
+
+ g_strfreev(topic_elements);
+
+ /* Now handoff the Payload message to the protobuf dissector */
+ call_dissector_with_data(protobuf_handle, tvb, pinfo, sparkplugb_tree, "message,com.cirruslink.sparkplug.protobuf.Payload");
+
+ return TRUE;
+}
+
+void proto_register_sparkplug(void)
+{
+ expert_module_t* expert_sparkplugb;
+
+ static gint *ett[] = {
+ &ett_sparkplugb,
+ &ett_sparkplugb_namespace
+ };
+
+ static hf_register_info hf[] = {
+ {&hf_sparkplugb_namespace, {"Namespace", "sparkplugb.namespace", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_sparkplugb_groupid, {"Group ID", "sparkplugb.groupid", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_sparkplugb_messagetype, {"Message Type", "sparkplugb.messagetype", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_sparkplugb_edgenodeid, {"Edge Node ID", "sparkplugb.edgenodeid", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ {&hf_sparkplugb_deviceid, {"Device ID", "sparkplugb.deviceid", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
+ };
+
+ static ei_register_info ei[] = {
+ { &ei_sparkplugb_missing_groupid, { "sparkplugb.missing_groupid", PI_MALFORMED, PI_ERROR, "Missing Group ID", EXPFILL }},
+ { &ei_sparkplugb_missing_messagetype, { "sparkplugb.missing_messagetype", PI_MALFORMED, PI_ERROR, "Missing Message Type", EXPFILL }},
+ { &ei_sparkplugb_missing_edgenodeid, { "sparkplugb.missing_edgenodeid", PI_MALFORMED, PI_ERROR, "Missing Edge Node ID", EXPFILL }},
+ };
+
+ /* Register the protocol name and description, fields and trees */
+ proto_sparkplugb = proto_register_protocol("SparkplugB", "SparkplugB", "sparkplugb");
+ register_dissector("sparkplugb", dissect_sparkplugb, proto_sparkplugb);
+
+ proto_register_field_array(proto_sparkplugb, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ expert_sparkplugb = expert_register_protocol(proto_sparkplugb);
+ expert_register_field_array(expert_sparkplugb, ei, array_length(ei));
+}
+
+void proto_reg_handoff_sparkplug(void)
+{
+ protobuf_handle = find_dissector_add_dependency("protobuf", proto_sparkplugb);
+
+ /* register as heuristic dissector with MQTT */
+ heur_dissector_add("mqtt.topic", dissect_sparkplugb, "SparkplugB over MQTT",
+ "sparkplugb_mqtt", proto_sparkplugb, HEURISTIC_ENABLE);
+}
diff --git a/packaging/nsis/uninstall.nsi b/packaging/nsis/uninstall.nsi
index c8493ae91b..3d115aa53f 100644
--- a/packaging/nsis/uninstall.nsi
+++ b/packaging/nsis/uninstall.nsi
@@ -215,6 +215,7 @@ Delete "$INSTDIR\snmp\*.*"
Delete "$INSTDIR\snmp\mibs\*.*"
Delete "$INSTDIR\styles\translations\*.*"
Delete "$INSTDIR\styles\*.*"
+Delete "$INSTDIR\protobuf\*.*"
Delete "$INSTDIR\tpncp\*.*"
Delete "$INSTDIR\translations\*.*"
Delete "$INSTDIR\ui\*.*"
@@ -295,6 +296,7 @@ RMDir "$INSTDIR\snmp\mibs"
RMDir "$INSTDIR\snmp"
RMDir "$INSTDIR\radius"
RMDir "$INSTDIR\dtds"
+RMDir "$INSTDIR\protobuf"
RMDir "$INSTDIR\tpncp"
RMDir "$INSTDIR\translations"
RMDir "$INSTDIR\ui"
diff --git a/packaging/nsis/wireshark.nsi b/packaging/nsis/wireshark.nsi
index 7af2a5c6e5..b00bf59c82 100644
--- a/packaging/nsis/wireshark.nsi
+++ b/packaging/nsis/wireshark.nsi
@@ -857,6 +857,12 @@ SetOutPath $INSTDIR
; Create the extcap directory
CreateDirectory $INSTDIR\extcap
+;
+; install the protobuf .proto definitions in the protobuf subdirectory
+;
+SetOutPath $INSTDIR\protobuf
+File "${STAGING_DIR}\protobuf\sparkplug_b.proto"
+
; Install the TPNCP DAT file in the "tpncp" subdirectory
; of the installation directory.
SetOutPath $INSTDIR\tpncp
diff --git a/packaging/wix/ComponentGroups.wxi b/packaging/wix/ComponentGroups.wxi
index 59c6a1c84a..0dd35eff3e 100644
--- a/packaging/wix/ComponentGroups.wxi
+++ b/packaging/wix/ComponentGroups.wxi
@@ -221,6 +221,20 @@
</ComponentGroup>
</Fragment>
+ <!-- protobuf TLV definitions -->
+ <Fragment>
+ <DirectoryRef Id="dirProtobuf">
+ <Component Id="cmpProtobuf_sparkplug_b_proto" Guid="*">
+ <File Id="filProtobuf_sparkplug_b_proto" KeyPath="yes" Source="$(var.Protobuf.Dir)\sparkplug_b.proto" />
+ </Component>
+ </DirectoryRef>
+ </Fragment>
+ <Fragment>
+ <ComponentGroup Id="CG.Protobuf">
+ <ComponentRef Id="cmpProtobuf_sparkplug_b_proto" />
+ </ComponentGroup>
+ </Fragment>
+
<!-- TShark -->
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
diff --git a/packaging/wix/DirectoryStructure.wxi b/packaging/wix/DirectoryStructure.wxi
index fe70b66393..fe1b9f6d68 100644
--- a/packaging/wix/DirectoryStructure.wxi
+++ b/packaging/wix/DirectoryStructure.wxi
@@ -30,6 +30,7 @@
<Directory Id="dirTpncp" Name="tpncp"/>
<Directory Id="dirTranslations" Name="translations"/>
<Directory Id="dirWimaxasncp" Name="wimaxasncp"/>
+ <Directory Id="dirProtobuf" Name="protobuf"/>
</Directory>
</Directory>
diff --git a/packaging/wix/Features.wxi b/packaging/wix/Features.wxi
index 58837cba21..35aefc60de 100644
--- a/packaging/wix/Features.wxi
+++ b/packaging/wix/Features.wxi
@@ -20,6 +20,7 @@
<ComponentGroupRef Id="CG.Dtds" />
<ComponentGroupRef Id="CG.Tpncp" />
<ComponentGroupRef Id="CG.Wimaxasncp" />
+ <ComponentGroupRef Id="CG.Protobuf" />
<ComponentGroupRef Id="CG.RequiredDependencies" />
</Feature>
diff --git a/packaging/wix/InputPaths.wxi b/packaging/wix/InputPaths.wxi
index 0fae77b721..5ff623559a 100644
--- a/packaging/wix/InputPaths.wxi
+++ b/packaging/wix/InputPaths.wxi
@@ -15,6 +15,7 @@
<?define Dtds.Dir ="$(var.Staging.Dir)\dtds" ?>
<?define Tpncp.Dir ="$(var.Staging.Dir)\tpncp" ?>
<?define Wimaxasncp.Dir ="$(var.Staging.Dir)\wimaxasncp" ?>
+ <?define Protobuf.Dir ="$(var.Staging.Dir)\protobuf" ?>
<?define Help.Dir ="$(var.Staging.Dir)\help" ?>
<?define Epan.Lua.Dir ="..\..\epan\wslua" ?>
diff --git a/protobuf/sparkplug_b.proto b/protobuf/sparkplug_b.proto
new file mode 100644
index 0000000000..bda645cc91
--- /dev/null
+++ b/protobuf/sparkplug_b.proto
@@ -0,0 +1,197 @@
+syntax = "proto2";
+
+//
+// To compile:
+// cd client_libraries/java
+// protoc --proto_path=../../ --java_out=src/main/java ../../sparkplug_b.proto
+//
+package com.cirruslink.sparkplug.protobuf;
+
+option java_package = "com.cirruslink.sparkplug.protobuf";
+option java_outer_classname = "SparkplugBProto";
+
+message Payload {
+ /*
+ // Indexes of Data Types
+
+ // Unknown placeholder for future expansion.
+ Unknown = 0;
+
+ // Basic Types
+ Int8 = 1;
+ Int16 = 2;
+ Int32 = 3;
+ Int64 = 4;
+ UInt8 = 5;
+ UInt16 = 6;
+ UInt32 = 7;
+ UInt64 = 8;
+ Float = 9;
+ Double = 10;
+ Boolean = 11;
+ String = 12;
+ DateTime = 13;
+ Text = 14;
+
+ // Additional Metric Types
+ UUID = 15;
+ DataSet = 16;
+ Bytes = 17;
+ File = 18;
+ Template = 19;
+
+ // Additional PropertyValue Types
+ PropertySet = 20;
+ PropertySetList = 21;
+
+ */
+
+ message Template {
+
+ message Parameter {
+ optional string name = 1;
+ optional uint32 type = 2;
+
+ oneof value {
+ uint32 int_value = 3;
+ uint64 long_value = 4;
+ float float_value = 5;
+ double double_value = 6;
+ bool boolean_value = 7;
+ string string_value = 8;
+ ParameterValueExtension extension_value = 9;
+ }
+
+ message ParameterValueExtension {
+ extensions 1 to max;
+ }
+ }
+
+ optional string version = 1; // The version of the Template to prevent mismatches
+ repeated Metric metrics = 2; // Each metric is the name of the metric and the datatype of the member but does not contain a value
+ repeated Parameter parameters = 3;
+ optional string template_ref = 4; // Reference to a template if this is extending a Template or an instance - must exist if an instance
+ optional bool is_definition = 5;
+ extensions 6 to max;
+ }
+
+ message DataSet {
+
+ message DataSetValue {
+
+ oneof value {
+ uint32 int_value = 1;
+ uint64 long_value = 2;
+ float float_value = 3;
+ double double_value = 4;
+ bool boolean_value = 5;
+ string string_value = 6;
+ DataSetValueExtension extension_value = 7;
+ }
+
+ message DataSetValueExtension {
+ extensions 1 to max;
+ }
+ }
+
+ message Row {
+ repeated DataSetValue elements = 1;
+ extensions 2 to max; // For third party extensions
+ }
+
+ optional uint64 num_of_columns = 1;
+ repeated string columns = 2;
+ repeated uint32 types = 3;
+ repeated Row rows = 4;
+ extensions 5 to max; // For third party extensions
+ }
+
+ message PropertyValue {
+
+ optional uint32 type = 1;
+ optional bool is_null = 2;
+
+ oneof value {
+ uint32 int_value = 3;
+ uint64 long_value = 4;
+ float float_value = 5;
+ double double_value = 6;
+ bool boolean_value = 7;
+ string string_value = 8;
+ PropertySet propertyset_value = 9;
+ PropertySetList propertysets_value = 10; // List of Property Values
+ PropertyValueExtension extension_value = 11;
+ }
+
+ message PropertyValueExtension {
+ extensions 1 to max;
+ }
+ }
+
+ message PropertySet {
+ repeated string keys = 1; // Names of the properties
+ repeated PropertyValue values = 2;
+ extensions 3 to max;
+ }
+
+ message PropertySetList {
+ repeated PropertySet propertyset = 1;
+ extensions 2 to max;
+ }
+
+ message MetaData {
+ // Bytes specific metadata
+ optional bool is_multi_part = 1;
+
+ // General metadata
+ optional string content_type = 2; // Content/Media type
+ optional uint64 size = 3; // File size, String size, Multi-part size, etc
+ optional uint64 seq = 4; // Sequence number for multi-part messages
+
+ // File metadata
+ optional string file_name = 5; // File name
+ optional string file_type = 6; // File type (i.e. xml, json, txt, cpp, etc)
+ optional string md5 = 7; // md5 of data
+
+ // Catchalls and future expansion
+ optional string description = 8; // Could be anything such as json or xml of custom properties
+ extensions 9 to max;
+ }
+
+ message Metric {
+
+ optional string name = 1; // Metric name - should only be included on birth
+ optional uint64 alias = 2; // Metric alias - tied to name on birth and included in all later DATA messages
+ optional uint64 timestamp = 3; // Timestamp associated with data acquisition time
+ optional uint32 datatype = 4; // DataType of the metric/tag value
+ optional bool is_historical = 5; // If this is historical data and should not update real time tag
+ optional bool is_transient = 6; // Tells consuming clients such as MQTT Engine to not store this as a tag
+ optional bool is_null = 7; // If this is null - explicitly say so rather than using -1, false, etc for some datatypes.
+ optional MetaData metadata = 8; // Metadata for the payload
+ optional PropertySet properties = 9;
+
+ oneof value {
+ uint32 int_value = 10;
+ uint64 long_value = 11;
+ float float_value = 12;
+ double double_value = 13;
+ bool boolean_value = 14;
+ string string_value = 15;
+ bytes bytes_value = 16; // Bytes, File
+ DataSet dataset_value = 17;
+ Template template_value = 18;
+ MetricValueExtension extension_value = 19;
+ }
+
+ message MetricValueExtension {
+ extensions 1 to max;
+ }
+ }
+
+ optional uint64 timestamp = 1; // Timestamp at message sending time
+ repeated Metric metrics = 2; // Repeated forever - no limit in Google Protobufs
+ optional uint64 seq = 3; // Sequence number
+ optional string uuid = 4; // UUID to track message type in terms of schema definitions
+ optional bytes body = 5; // To optionally bypass the whole definition above
+ extensions 6 to max; // For third party extensions
+}