1 /* packet-pdu_transport.c
2 * PDU Transport dissector for FDN and others.
3 * By <lars.voelker@technica-engineering.de>
4 * Copyright 2020-2023 Dr. Lars Voelker
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * This is a dissector for PDUs transported over UDP or TCP.
15 * The transported PDUs are typically CAN, FlexRay, LIN, or other protocols.
17 * The format is as follows:
21 * (restart with ID, if more data exists)
23 * One known implementation of this protocol is the AUTOSAR Socket Adaptor
24 * with Header Option turned on.
25 * See AUTOSAR "Specification of Socket Adaptor" (SWS), Section 7.3 PDU Header option:
26 * https://www.autosar.org/fileadmin/user_upload/standards/classic/20-11/AUTOSAR_SWS_SocketAdaptor.pdf
31 #include <epan/packet.h>
32 #include <epan/prefs.h>
33 #include <epan/expert.h>
35 #include "packet-tcp.h"
36 #include <epan/reassemble.h>
37 #include "packet-udp.h"
38 #include "packet-pdu-transport.h"
39 #include <epan/decode_as.h>
40 #include <epan/proto_data.h>
42 void proto_register_pdu_transport(void);
43 void proto_reg_handoff_pdu_transport(void);
45 static int proto_pdu_transport
;
46 static dissector_handle_t pdu_transport_handle_udp
;
47 static dissector_handle_t pdu_transport_handle_tcp
;
49 static dissector_table_t subdissector_table
;
51 #define PDU_TRANSPORT_NAME "PDU Transport"
52 #define PDU_TRANSPORT_HDR_LEN 8
55 static int hf_pdu_transport_id
;
56 static int hf_pdu_transport_length
;
57 static int hf_pdu_transport_payload
;
59 /* protocol tree items */
60 static int ett_pdu_transport
;
62 /* expert info items */
63 static expert_field ei_pdu_transport_message_truncated
;
65 /********* UATs *********/
67 typedef struct _generic_one_id_string
{
70 } generic_one_id_string_t
;
73 pdu_transport_free_key(void *key
) {
74 wmem_free(wmem_epan_scope(), key
);
78 simple_free(void *data
) {
79 /* we need to free because of the g_strdup in post_update*/
85 copy_generic_one_id_string_cb(void *n
, const void *o
, size_t size _U_
) {
86 generic_one_id_string_t
*new_rec
= (generic_one_id_string_t
*)n
;
87 const generic_one_id_string_t
*old_rec
= (const generic_one_id_string_t
*)o
;
89 new_rec
->name
= g_strdup(old_rec
->name
);
90 new_rec
->id
= old_rec
->id
;
95 update_generic_one_identifier_32bit(void *r
, char **err
) {
96 generic_one_id_string_t
*rec
= (generic_one_id_string_t
*)r
;
98 if (rec
->name
== NULL
|| rec
->name
[0] == 0) {
99 *err
= g_strdup("Name cannot be empty");
107 free_generic_one_id_string_cb(void *r
) {
108 generic_one_id_string_t
*rec
= (generic_one_id_string_t
*)r
;
109 /* freeing result of g_strdup */
115 post_update_one_id_string_template_cb(generic_one_id_string_t
*data
, unsigned data_num
, GHashTable
*ht
) {
119 for (i
= 0; i
< data_num
; i
++) {
120 key
= wmem_new(wmem_epan_scope(), int);
123 g_hash_table_insert(ht
, key
, g_strdup(data
[i
].name
));
128 ht_lookup_name(GHashTable
*ht
, unsigned int identifier
) {
130 unsigned int *id
= NULL
;
136 id
= wmem_new(wmem_epan_scope(), unsigned int);
137 *id
= (unsigned int)identifier
;
138 tmp
= (char *)g_hash_table_lookup(ht
, id
);
139 wmem_free(wmem_epan_scope(), id
);
144 /*** UAT pdu_transport_CM_IDs ***/
145 #define DATAFILE_PDU_IDS "PDU_Transport_identifiers"
147 static GHashTable
*data_pdu_transport_pdus
;
148 static generic_one_id_string_t
*pdu_transport_pdus
;
149 static unsigned pdu_transport_pdus_num
;
151 UAT_HEX_CB_DEF(pdu_transport_pdus
, id
, generic_one_id_string_t
)
152 UAT_CSTRING_CB_DEF(pdu_transport_pdus
, name
, generic_one_id_string_t
)
155 post_update_pdu_transport_pdus_cb(void) {
156 /* destroy old hash table, if it exists */
157 if (data_pdu_transport_pdus
) {
158 g_hash_table_destroy(data_pdu_transport_pdus
);
159 data_pdu_transport_pdus
= NULL
;
162 /* create new hash table */
163 data_pdu_transport_pdus
= g_hash_table_new_full(g_int_hash
, g_int_equal
, &pdu_transport_free_key
, &simple_free
);
164 post_update_one_id_string_template_cb(pdu_transport_pdus
, pdu_transport_pdus_num
, data_pdu_transport_pdus
);
168 dissect_pdu_transport(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
169 proto_item
*ti_top
= NULL
;
170 proto_item
*ti
= NULL
;
171 proto_tree
*pdu_transport_tree
= NULL
;
173 tvbuff_t
*subtvb
= NULL
;
180 if (p_get_proto_data(pinfo
->pool
, pinfo
, proto_pdu_transport
, pinfo
->curr_proto_layer_num
) != NULL
) {
181 col_append_str(pinfo
->cinfo
, COL_INFO
, ", ");
182 col_set_fence(pinfo
->cinfo
, COL_INFO
);
185 col_set_str(pinfo
->cinfo
, COL_INFO
, "PDU");
186 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PDU_TRANSPORT_NAME
);
187 ti_top
= proto_tree_add_item(tree
, proto_pdu_transport
, tvb
, 0, -1, ENC_NA
);
188 pdu_transport_tree
= proto_item_add_subtree(ti_top
, ett_pdu_transport
);
190 if (tvb_captured_length_remaining(tvb
, offset
) < 8) {
191 expert_add_info(pinfo
, ti_top
, &ei_pdu_transport_message_truncated
);
194 /* taken from packet-ip.c
195 * if pdu_transport is not referenced from any filters we don't need to worry about
196 * generating any tree items. We must do this after we created the actual
197 * protocol above so that proto hier stat still works though.
198 * XXX: Note that because of the following optimization expert items must
199 * not be generated inside of an 'if (tree) ...'
200 * so that Analyze ! Expert ... will work.
203 if (!proto_field_is_referenced(tree
, proto_pdu_transport
)) {
204 pdu_transport_tree
= NULL
;
207 ti
= proto_tree_add_item_ret_uint(pdu_transport_tree
, hf_pdu_transport_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &pdu_id
);
210 proto_tree_add_item_ret_uint(pdu_transport_tree
, hf_pdu_transport_length
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &length
);
213 descr
= ht_lookup_name(data_pdu_transport_pdus
, pdu_id
);
216 proto_item_append_text(ti_top
, ", ID 0x%x (%s), Length: %d", pdu_id
, descr
, length
);
217 proto_item_append_text(ti
, " (%s)", descr
);
218 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (ID: 0x%x, %s)", pdu_id
, descr
);
220 proto_item_append_text(ti_top
, ", ID 0x%x, Length: %d", pdu_id
, length
);
221 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (ID: 0x%x)", pdu_id
);
224 p_add_proto_data(pinfo
->pool
, pinfo
, proto_pdu_transport
, pinfo
->curr_proto_layer_num
, GUINT_TO_POINTER(pdu_id
));
226 tmp
= tvb_captured_length_remaining(tvb
, offset
);
227 if ((int)length
<= tmp
) {
228 proto_tree_add_item(pdu_transport_tree
, hf_pdu_transport_payload
, tvb
, offset
, length
, ENC_NA
);
229 subtvb
= tvb_new_subset_length_caplen(tvb
, offset
, length
, length
);
231 proto_tree_add_item(pdu_transport_tree
, hf_pdu_transport_payload
, tvb
, offset
, tmp
, ENC_NA
);
232 subtvb
= tvb_new_subset_length_caplen(tvb
, offset
, tmp
, length
);
233 expert_add_info(pinfo
, ti_top
, &ei_pdu_transport_message_truncated
);
235 if (subtvb
!= NULL
) {
236 pdu_transport_info_t pdu_t_info
;
237 pdu_t_info
.id
= pdu_id
;
239 dissector_try_uint_with_data(subdissector_table
, pdu_id
, subtvb
, pinfo
, tree
, false, (void *)(&pdu_t_info
));
241 offset
+= (int)length
;
243 col_set_fence(pinfo
->cinfo
, COL_INFO
);
248 get_pdu_transport_message_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
) {
249 return PDU_TRANSPORT_HDR_LEN
+ (unsigned)tvb_get_ntohl(tvb
, offset
+ 4);
253 dissect_pdu_transport_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
254 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, PDU_TRANSPORT_HDR_LEN
, get_pdu_transport_message_len
, dissect_pdu_transport
, data
);
255 return tvb_reported_length(tvb
);
259 dissect_pdu_transport_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
) {
260 return udp_dissect_pdus(tvb
, pinfo
, tree
, PDU_TRANSPORT_HDR_LEN
, NULL
, get_pdu_transport_message_len
, dissect_pdu_transport
, data
);
265 pdu_transport_id_prompt(packet_info
*pinfo
, char *result
) {
266 snprintf(result
, MAX_DECODE_AS_PROMPT_LEN
, "PDU Transport ID 0x%08x as",
267 GPOINTER_TO_UINT(p_get_proto_data(pinfo
->pool
, pinfo
, proto_pdu_transport
, pinfo
->curr_proto_layer_num
)));
271 pdu_transport_id_value(packet_info
*pinfo
) {
272 /* Limitation: This only returns the last proto_data, since udp_dissect_pdus gives us the same layer for all. */
273 return p_get_proto_data(pinfo
->pool
, pinfo
, proto_pdu_transport
, pinfo
->curr_proto_layer_num
);
277 proto_register_pdu_transport(void) {
278 module_t
*pdu_transport_module
= NULL
;
279 expert_module_t
*expert_module_pdu_transport
= NULL
;
280 uat_t
*pdu_transport_pduid_uat
= NULL
;
282 static hf_register_info hf
[] = {
283 { &hf_pdu_transport_id
,
284 { "ID", "pdu_transport.id", FT_UINT32
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
285 { &hf_pdu_transport_length
,
286 { "Length", "pdu_transport.length", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
287 { &hf_pdu_transport_payload
,
288 { "Payload", "pdu_transport.payload", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
291 static int *ett
[] = {
295 static ei_register_info ei
[] = {
296 { &ei_pdu_transport_message_truncated
,{ "pdu_transport.message_truncated",
297 PI_MALFORMED
, PI_ERROR
, "PDU Transport Truncated message!", EXPFILL
} },
300 /* Decode As handling */
301 static build_valid_func pdu_transport_da_build_value
[1] = {pdu_transport_id_value
};
302 static decode_as_value_t pdu_transport_da_values
= {pdu_transport_id_prompt
, 1, pdu_transport_da_build_value
};
304 static decode_as_t pdu_transport_da
= { "pdu_transport", "pdu_transport.id", 1, 0, &pdu_transport_da_values
,
305 NULL
, NULL
, decode_as_default_populate_list
,
306 decode_as_default_reset
, decode_as_default_change
, NULL
};
308 proto_pdu_transport
= proto_register_protocol("PDU Transport Protocol", PDU_TRANSPORT_NAME
, "pdu_transport");
310 proto_register_field_array(proto_pdu_transport
, hf
, array_length(hf
));
311 proto_register_subtree_array(ett
, array_length(ett
));
313 pdu_transport_module
= prefs_register_protocol(proto_pdu_transport
, NULL
);
314 expert_module_pdu_transport
= expert_register_protocol(proto_pdu_transport
);
315 expert_register_field_array(expert_module_pdu_transport
, ei
, array_length(ei
));
317 static uat_field_t pdu_transport_cm_id_uat_fields
[] = {
318 UAT_FLD_HEX(pdu_transport_pdus
, id
, "ID", "ID (hex uint32)"),
319 UAT_FLD_CSTRING(pdu_transport_pdus
, name
, "Name", "Name of the PDU (string)"),
323 pdu_transport_pduid_uat
= uat_new("pdu_transport Capture Modules",
324 sizeof(generic_one_id_string_t
), /* record size */
325 DATAFILE_PDU_IDS
, /* filename */
326 true, /* from profile */
327 (void**)&pdu_transport_pdus
, /* data_ptr */
328 &pdu_transport_pdus_num
, /* numitems_ptr */
329 UAT_AFFECTS_DISSECTION
, /* but not fields */
331 copy_generic_one_id_string_cb
, /* copy callback */
332 update_generic_one_identifier_32bit
, /* update callback */
333 free_generic_one_id_string_cb
, /* free callback */
334 post_update_pdu_transport_pdus_cb
, /* post update callback */
335 NULL
, /* reset callback */
336 pdu_transport_cm_id_uat_fields
/* UAT field definitions */
339 prefs_register_uat_preference(pdu_transport_module
, "_udf_pdu_transport_pdus", "PDUs",
340 "A table to define names and IDs of PDUs", pdu_transport_pduid_uat
);
342 subdissector_table
= register_dissector_table("pdu_transport.id", "PDU Transport ID", proto_pdu_transport
, FT_UINT32
, BASE_HEX
);
343 register_decode_as(&pdu_transport_da
);
347 proto_reg_handoff_pdu_transport(void) {
348 pdu_transport_handle_udp
= register_dissector("pdu_transport_over_udp", dissect_pdu_transport_udp
, proto_pdu_transport
);
349 pdu_transport_handle_tcp
= register_dissector("pdu_transport_over_tcp", dissect_pdu_transport_tcp
, proto_pdu_transport
);
351 dissector_add_uint_range_with_preference("udp.port", "", pdu_transport_handle_udp
);
352 dissector_add_uint_range_with_preference("tcp.port", "", pdu_transport_handle_tcp
);
356 * Editor modelines - https://www.wireshark.org/tools/modelines.html
361 * indent-tabs-mode: nil
364 * vi: set shiftwidth=4 tabstop=8 expandtab:
365 * :indentSize=4:tabSize=8:noTabs=true: