summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-pdu-transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-pdu-transport.c')
-rw-r--r--epan/dissectors/packet-pdu-transport.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/epan/dissectors/packet-pdu-transport.c b/epan/dissectors/packet-pdu-transport.c
new file mode 100644
index 00000000..ebaa926b
--- /dev/null
+++ b/epan/dissectors/packet-pdu-transport.c
@@ -0,0 +1,366 @@
+/* packet-pdu_transport.c
+ * PDU Transport dissector for FDN and others.
+ * By <lars.voelker@technica-engineering.de>
+ * Copyright 2020-2023 Dr. Lars Voelker
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ /*
+ * This is a dissector for PDUs transported over UDP or TCP.
+ * The transported PDUs are typically CAN, FlexRay, LIN, or other protocols.
+ *
+ * The format is as follows:
+ * uint32 ID
+ * uint32 length
+ * uint8[length] data
+ * (restart with ID, if more data exists)
+ *
+ * One known implementation of this protocol is the AUTOSAR Socket Adaptor
+ * with Header Option turned on.
+ * See AUTOSAR "Specification of Socket Adaptor" (SWS), Section 7.3 PDU Header option:
+ * https://www.autosar.org/fileadmin/user_upload/standards/classic/20-11/AUTOSAR_SWS_SocketAdaptor.pdf
+ */
+
+#include <config.h>
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/expert.h>
+#include <epan/uat.h>
+#include "packet-tcp.h"
+#include <epan/reassemble.h>
+#include "packet-udp.h"
+#include "packet-pdu-transport.h"
+#include <epan/decode_as.h>
+#include <epan/proto_data.h>
+
+void proto_register_pdu_transport(void);
+void proto_reg_handoff_pdu_transport(void);
+
+static int proto_pdu_transport = -1;
+static dissector_handle_t pdu_transport_handle_udp = NULL;
+static dissector_handle_t pdu_transport_handle_tcp = NULL;
+
+static dissector_table_t subdissector_table = NULL;
+
+#define PDU_TRANSPORT_NAME "PDU Transport"
+#define PDU_TRANSPORT_HDR_LEN 8
+
+/* header field */
+static int hf_pdu_transport_id = -1;
+static int hf_pdu_transport_length = -1;
+static int hf_pdu_transport_payload = -1;
+
+/* protocol tree items */
+static gint ett_pdu_transport = -1;
+
+/* expert info items */
+static expert_field ef_pdu_transport_message_truncated = EI_INIT;
+
+/********* UATs *********/
+
+typedef struct _generic_one_id_string {
+ guint id;
+ gchar *name;
+} generic_one_id_string_t;
+
+static void
+pdu_transport_free_key(gpointer key) {
+ wmem_free(wmem_epan_scope(), key);
+}
+
+static void
+simple_free(gpointer data) {
+ /* we need to free because of the g_strdup in post_update*/
+ g_free(data);
+}
+
+/* ID -> Name */
+static void *
+copy_generic_one_id_string_cb(void *n, const void *o, size_t size _U_) {
+ generic_one_id_string_t *new_rec = (generic_one_id_string_t *)n;
+ const generic_one_id_string_t *old_rec = (const generic_one_id_string_t *)o;
+
+ new_rec->name = g_strdup(old_rec->name);
+ new_rec->id = old_rec->id;
+ return new_rec;
+}
+
+static bool
+update_generic_one_identifier_32bit(void *r, char **err) {
+ generic_one_id_string_t *rec = (generic_one_id_string_t *)r;
+
+ if (rec->name == NULL || rec->name[0] == 0) {
+ *err = g_strdup("Name cannot be empty");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+free_generic_one_id_string_cb(void *r) {
+ generic_one_id_string_t *rec = (generic_one_id_string_t *)r;
+ /* freeing result of g_strdup */
+ g_free(rec->name);
+ rec->name = NULL;
+}
+
+static void
+post_update_one_id_string_template_cb(generic_one_id_string_t *data, guint data_num, GHashTable *ht) {
+ guint i;
+ int *key = NULL;
+
+ for (i = 0; i < data_num; i++) {
+ key = wmem_new(wmem_epan_scope(), int);
+ *key = data[i].id;
+
+ g_hash_table_insert(ht, key, g_strdup(data[i].name));
+ }
+}
+
+static char *
+ht_lookup_name(GHashTable *ht, unsigned int identifier) {
+ char *tmp = NULL;
+ unsigned int *id = NULL;
+
+ if (ht == NULL) {
+ return NULL;
+ }
+
+ id = wmem_new(wmem_epan_scope(), unsigned int);
+ *id = (unsigned int)identifier;
+ tmp = (char *)g_hash_table_lookup(ht, id);
+ wmem_free(wmem_epan_scope(), id);
+
+ return tmp;
+}
+
+/*** UAT pdu_transport_CM_IDs ***/
+#define DATAFILE_PDU_IDS "PDU_Transport_identifiers"
+
+static GHashTable *data_pdu_transport_pdus = NULL;
+static generic_one_id_string_t *pdu_transport_pdus = NULL;
+static guint pdu_transport_pdus_num = 0;
+
+UAT_HEX_CB_DEF(pdu_transport_pdus, id, generic_one_id_string_t)
+UAT_CSTRING_CB_DEF(pdu_transport_pdus, name, generic_one_id_string_t)
+
+static void
+post_update_pdu_transport_pdus_cb(void) {
+ /* destroy old hash table, if it exists */
+ if (data_pdu_transport_pdus) {
+ g_hash_table_destroy(data_pdu_transport_pdus);
+ data_pdu_transport_pdus = NULL;
+ }
+
+ /* create new hash table */
+ data_pdu_transport_pdus = g_hash_table_new_full(g_int_hash, g_int_equal, &pdu_transport_free_key, &simple_free);
+ post_update_one_id_string_template_cb(pdu_transport_pdus, pdu_transport_pdus_num, data_pdu_transport_pdus);
+}
+
+static int
+dissect_pdu_transport(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
+ proto_item *ti_top = NULL;
+ proto_item *ti = NULL;
+ proto_tree *pdu_transport_tree = NULL;
+ guint offset = 0;
+ tvbuff_t *subtvb = NULL;
+ gint tmp = 0;
+
+ guint32 length = 0;
+ guint32 pdu_id = 0;
+ const gchar *descr;
+
+ if (p_get_proto_data(pinfo->pool, pinfo, proto_pdu_transport, pinfo->curr_layer_num) != NULL) {
+ col_append_str(pinfo->cinfo, COL_INFO, ", ");
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ }
+
+ col_set_str(pinfo->cinfo, COL_INFO, "PDU");
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, PDU_TRANSPORT_NAME);
+ ti_top = proto_tree_add_item(tree, proto_pdu_transport, tvb, 0, -1, ENC_NA);
+ pdu_transport_tree = proto_item_add_subtree(ti_top, ett_pdu_transport);
+
+ if (tvb_captured_length_remaining(tvb, offset) < 8) {
+ expert_add_info(pinfo, ti_top, &ef_pdu_transport_message_truncated);
+ }
+
+ /* taken from packet-ip.c
+ * if pdu_transport is not referenced from any filters we don't need to worry about
+ * generating any tree items. We must do this after we created the actual
+ * protocol above so that proto hier stat still works though.
+ * XXX: Note that because of the following optimization expert items must
+ * not be generated inside of an 'if (tree) ...'
+ * so that Analyze ! Expert ... will work.
+ */
+
+ if (!proto_field_is_referenced(tree, proto_pdu_transport)) {
+ pdu_transport_tree = NULL;
+ }
+
+ ti = proto_tree_add_item_ret_uint(pdu_transport_tree, hf_pdu_transport_id, tvb, offset, 4, ENC_BIG_ENDIAN, &pdu_id);
+ offset += 4;
+
+ proto_tree_add_item_ret_uint(pdu_transport_tree, hf_pdu_transport_length, tvb, offset, 4, ENC_BIG_ENDIAN, &length);
+ offset += 4;
+
+ descr = ht_lookup_name(data_pdu_transport_pdus, pdu_id);
+
+ if (descr != NULL) {
+ proto_item_append_text(ti_top, ", ID 0x%x (%s), Length: %d", pdu_id, descr, length);
+ proto_item_append_text(ti, " (%s)", descr);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (ID: 0x%x, %s)", pdu_id, descr);
+ } else {
+ proto_item_append_text(ti_top, ", ID 0x%x, Length: %d", pdu_id, length);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (ID: 0x%x)", pdu_id);
+ }
+
+ p_add_proto_data(pinfo->pool, pinfo, proto_pdu_transport, pinfo->curr_layer_num, GUINT_TO_POINTER(pdu_id));
+
+ tmp = tvb_captured_length_remaining(tvb, offset);
+ if ((gint)length <= tmp) {
+ proto_tree_add_item(pdu_transport_tree, hf_pdu_transport_payload, tvb, offset, length, ENC_NA);
+ subtvb = tvb_new_subset_length_caplen(tvb, offset, length, length);
+ } else {
+ proto_tree_add_item(pdu_transport_tree, hf_pdu_transport_payload, tvb, offset, tmp, ENC_NA);
+ subtvb = tvb_new_subset_length_caplen(tvb, offset, tmp, length);
+ expert_add_info(pinfo, ti_top, &ef_pdu_transport_message_truncated);
+ }
+ if (subtvb != NULL) {
+ pdu_transport_info_t pdu_t_info;
+ pdu_t_info.id = pdu_id;
+
+ dissector_try_uint_new(subdissector_table, pdu_id, subtvb, pinfo, tree, FALSE, (void *)(&pdu_t_info));
+ }
+ offset += (gint)length;
+
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ return offset;
+}
+
+static guint
+get_pdu_transport_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) {
+ return PDU_TRANSPORT_HDR_LEN + (guint)tvb_get_ntohl(tvb, offset + 4);
+}
+
+static int
+dissect_pdu_transport_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
+ tcp_dissect_pdus(tvb, pinfo, tree, TRUE, PDU_TRANSPORT_HDR_LEN, get_pdu_transport_message_len, dissect_pdu_transport, data);
+ return tvb_reported_length(tvb);
+}
+
+static int
+dissect_pdu_transport_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) {
+ return udp_dissect_pdus(tvb, pinfo, tree, PDU_TRANSPORT_HDR_LEN, NULL, get_pdu_transport_message_len, dissect_pdu_transport, data);
+}
+
+
+static void
+pdu_transport_id_prompt(packet_info *pinfo, gchar *result) {
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "PDU Transport ID 0x%08x as",
+ GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_pdu_transport, pinfo->curr_layer_num)));
+}
+
+static gpointer
+pdu_transport_id_value(packet_info *pinfo) {
+ /* Limitation: This only returns the last proto_data, since udp_dissect_pdus gives us the same layer for all. */
+ return p_get_proto_data(pinfo->pool, pinfo, proto_pdu_transport, pinfo->curr_layer_num);
+}
+
+void
+proto_register_pdu_transport(void) {
+ module_t *pdu_transport_module = NULL;
+ expert_module_t *expert_module_pdu_transport = NULL;
+ uat_t *pdu_transport_pduid_uat = NULL;
+
+ static hf_register_info hf[] = {
+ { &hf_pdu_transport_id,
+ { "ID", "pdu_transport.id", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }},
+ { &hf_pdu_transport_length,
+ { "Length", "pdu_transport.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+ { &hf_pdu_transport_payload,
+ { "Payload", "pdu_transport.payload", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
+ };
+
+ static gint *ett[] = {
+ &ett_pdu_transport,
+ };
+
+ static ei_register_info ei[] = {
+ { &ef_pdu_transport_message_truncated,{ "pdu_transport.message_truncated",
+ PI_MALFORMED, PI_ERROR, "PDU Transport Truncated message!", EXPFILL } },
+ };
+
+ /* Decode As handling */
+ static build_valid_func pdu_transport_da_build_value[1] = {pdu_transport_id_value};
+ static decode_as_value_t pdu_transport_da_values = {pdu_transport_id_prompt, 1, pdu_transport_da_build_value};
+
+ static decode_as_t pdu_transport_da = { "pdu_transport", "pdu_transport.id", 1, 0, &pdu_transport_da_values,
+ NULL, NULL, decode_as_default_populate_list,
+ decode_as_default_reset, decode_as_default_change, NULL };
+
+ proto_pdu_transport = proto_register_protocol("PDU Transport Protocol", PDU_TRANSPORT_NAME, "pdu_transport");
+
+ proto_register_field_array(proto_pdu_transport, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ pdu_transport_module = prefs_register_protocol(proto_pdu_transport, NULL);
+ expert_module_pdu_transport = expert_register_protocol(proto_pdu_transport);
+ expert_register_field_array(expert_module_pdu_transport, ei, array_length(ei));
+
+ static uat_field_t pdu_transport_cm_id_uat_fields[] = {
+ UAT_FLD_HEX(pdu_transport_pdus, id, "ID", "ID (hex uint32)"),
+ UAT_FLD_CSTRING(pdu_transport_pdus, name, "Name", "Name of the PDU (string)"),
+ UAT_END_FIELDS
+ };
+
+ pdu_transport_pduid_uat = uat_new("pdu_transport Capture Modules",
+ sizeof(generic_one_id_string_t), /* record size */
+ DATAFILE_PDU_IDS, /* filename */
+ TRUE, /* from profile */
+ (void**)&pdu_transport_pdus, /* data_ptr */
+ &pdu_transport_pdus_num, /* numitems_ptr */
+ UAT_AFFECTS_DISSECTION, /* but not fields */
+ NULL, /* help */
+ copy_generic_one_id_string_cb, /* copy callback */
+ update_generic_one_identifier_32bit, /* update callback */
+ free_generic_one_id_string_cb, /* free callback */
+ post_update_pdu_transport_pdus_cb, /* post update callback */
+ NULL, /* reset callback */
+ pdu_transport_cm_id_uat_fields /* UAT field definitions */
+ );
+
+ prefs_register_uat_preference(pdu_transport_module, "_udf_pdu_transport_pdus", "PDUs",
+ "A table to define names and IDs of PDUs", pdu_transport_pduid_uat);
+
+ subdissector_table = register_dissector_table("pdu_transport.id", "PDU Transport ID", proto_pdu_transport, FT_UINT32, BASE_HEX);
+ register_decode_as(&pdu_transport_da);
+}
+
+void
+proto_reg_handoff_pdu_transport(void) {
+ pdu_transport_handle_udp = register_dissector("pdu_transport_over_udp", dissect_pdu_transport_udp, proto_pdu_transport);
+ pdu_transport_handle_tcp = register_dissector("pdu_transport_over_tcp", dissect_pdu_transport_tcp, proto_pdu_transport);
+
+ dissector_add_uint_range_with_preference("udp.port", "", pdu_transport_handle_udp);
+ dissector_add_uint_range_with_preference("tcp.port", "", pdu_transport_handle_tcp);
+}
+
+/*
+ * 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:
+ */