UDS: Adding support for Routine ID and Data ID resolution

This commit is contained in:
Dr. Lars Völker 2021-05-13 21:03:02 +02:00 committed by Wireshark GitLab Utility
parent 771872cc7e
commit 9a400118df
1 changed files with 273 additions and 13 deletions

View File

@ -11,11 +11,15 @@
#include "config.h"
#include <epan/packet.h>
#include <epan/uat.h>
#include <wsutil/bits_ctz.h>
void proto_register_uds(void);
void proto_reg_handoff_uds(void);
#define DATAFILE_UDS_ROUTINE_IDS "UDS_routine_identifiers"
#define DATAFILE_UDS_DATA_IDS "UDS_data_identifiers"
#define UDS_SERVICES_DSC 0x10
#define UDS_SERVICES_ER 0x11
#define UDS_SERVICES_CDTCI 0x14
@ -376,6 +380,171 @@ static int proto_uds = -1;
static dissector_handle_t uds_handle;
/*** Configuration ***/
typedef struct _generic_one_id_string {
guint id;
gchar *name;
} generic_one_id_string_t;
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 gboolean
update_generic_one_identifier_16bit(void *r, char **err) {
generic_one_id_string_t *rec = (generic_one_id_string_t *)r;
if (rec->id > 0xffff) {
*err = g_strdup_printf("We currently only support 16 bit identifiers (ID: %i Name: %s)", rec->id, rec->name);
return FALSE;
}
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));
}
}
char *
generic_lookup_uint16(guint16 id, GHashTable *ht) {
guint32 tmp = (guint32)id;
if (ht == NULL) {
return NULL;
}
return (char *)g_hash_table_lookup(ht, &tmp);
}
static void
simple_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);
}
/* Routine IDs */
static generic_one_id_string_t *uds_uat_routine_ids = NULL;
static guint uds_uat_routine_id_num = 0;
static GHashTable *uds_ht_routine_ids = NULL;
UAT_HEX_CB_DEF(uds_uat_routine_ids, id, generic_one_id_string_t)
UAT_CSTRING_CB_DEF(uds_uat_routine_ids, name, generic_one_id_string_t)
static void
post_update_uds_routine_cb(void) {
/* destroy old hash table, if it exists */
if (uds_ht_routine_ids) {
g_hash_table_destroy(uds_ht_routine_ids);
}
/* create new hash table */
uds_ht_routine_ids = g_hash_table_new_full(g_int_hash, g_int_equal, &simple_free_key, &simple_free);
post_update_one_id_string_template_cb(uds_uat_routine_ids, uds_uat_routine_id_num, uds_ht_routine_ids);
}
char *
uds_lookup_routine_name(guint16 id) {
return generic_lookup_uint16(id, uds_ht_routine_ids);
}
void
protoitem_append_routine_name(proto_item *ti, guint16 data_identifier) {
gchar *routine_name = uds_lookup_routine_name((guint16)data_identifier);
if (routine_name != NULL) {
proto_item_append_text(ti, " (%s)", routine_name);
}
}
void
infocol_append_routine_name(packet_info *pinfo, guint16 routine_identifier) {
gchar *routine_name = uds_lookup_routine_name((guint16)routine_identifier);
if (routine_name != NULL) {
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", routine_name);
}
}
/* Data IDs */
static generic_one_id_string_t *uds_uat_data_ids = NULL;
static guint uds_uat_data_id_num = 0;
static GHashTable *uds_ht_data_ids = NULL;
UAT_HEX_CB_DEF(uds_uat_data_ids, id, generic_one_id_string_t)
UAT_CSTRING_CB_DEF(uds_uat_data_ids, name, generic_one_id_string_t)
static void
post_update_uds_data_cb(void) {
/* destroy old hash table, if it exists */
if (uds_ht_data_ids) {
g_hash_table_destroy(uds_ht_data_ids);
}
/* create new hash table */
uds_ht_data_ids = g_hash_table_new_full(g_int_hash, g_int_equal, &simple_free_key, &simple_free);
post_update_one_id_string_template_cb(uds_uat_data_ids, uds_uat_data_id_num, uds_ht_data_ids);
}
char *
uds_lookup_data_name(guint16 id) {
return generic_lookup_uint16(id, uds_ht_data_ids);
}
void
protoitem_append_data_name(proto_item *ti, guint16 data_identifier) {
gchar *data_name = uds_lookup_data_name((guint16)data_identifier);
if (data_name != NULL) {
proto_item_append_text(ti, " (%s)", data_name);
}
}
void
infocol_append_data_name(packet_info *pinfo, guint16 data_identifier) {
gchar *data_name = uds_lookup_data_name((guint16)data_identifier);
if (data_name != NULL) {
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", data_name);
}
}
/*** Configuration End ***/
static
guint8 masked_guint8_value(const guint8 value, const guint8 mask)
{
@ -471,11 +640,16 @@ dissect_uds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void* data
/* Can't know the size of the data for each identifier, Decode like if there is only one idenfifier */
guint32 record_length = data_length - UDS_RDBI_DATA_RECORD_OFFSET;
guint32 data_identifier;
proto_tree_add_item_ret_uint(subtree, hf_uds_rdbi_data_identifier, tvb, UDS_RDBI_DATA_IDENTIFIER_OFFSET,
UDS_RDBI_DATA_IDENTIFIER_LEN, ENC_BIG_ENDIAN, &data_identifier);
ti = proto_tree_add_item_ret_uint(subtree, hf_uds_rdbi_data_identifier, tvb,
UDS_RDBI_DATA_IDENTIFIER_OFFSET, UDS_RDBI_DATA_IDENTIFIER_LEN,
ENC_BIG_ENDIAN, &data_identifier);
protoitem_append_data_name(ti, (guint16)data_identifier);
proto_tree_add_item(subtree, hf_uds_rdbi_data_record, tvb, UDS_RDBI_DATA_RECORD_OFFSET,
record_length, ENC_NA);
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x %s", data_identifier,
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x", data_identifier);
infocol_append_data_name(pinfo, data_identifier);
col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
tvb_bytes_to_str_punct(wmem_packet_scope(), tvb, UDS_RDBI_DATA_RECORD_OFFSET,
record_length, ' '));
} else {
@ -483,9 +657,12 @@ dissect_uds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void* data
guint32 offset = UDS_RDBI_DATA_IDENTIFIER_OFFSET;
while (identifier_length > 0) {
guint32 data_identifier;
proto_tree_add_item_ret_uint(subtree, hf_uds_rdbi_data_identifier, tvb, offset,
UDS_RDBI_DATA_IDENTIFIER_LEN, ENC_BIG_ENDIAN, &data_identifier);
ti = proto_tree_add_item_ret_uint(subtree, hf_uds_rdbi_data_identifier, tvb, offset,
UDS_RDBI_DATA_IDENTIFIER_LEN, ENC_BIG_ENDIAN, &data_identifier);
protoitem_append_data_name(ti, (guint16)data_identifier);
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x", data_identifier);
infocol_append_data_name(pinfo, data_identifier);
offset += UDS_RDBI_DATA_IDENTIFIER_LEN;
identifier_length -= UDS_RDBI_DATA_IDENTIFIER_LEN;
}
@ -520,15 +697,20 @@ dissect_uds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void* data
case UDS_SERVICES_WDBI:
subtree = proto_tree_add_subtree(uds_tree, tvb, 0, -1, ett_uds_wdbi, NULL, service_name);
proto_tree_add_item_ret_uint(subtree, hf_uds_wdbi_data_identifier, tvb, UDS_WDBI_DATA_IDENTIFIER_OFFSET,
UDS_WDBI_DATA_IDENTIFIER_LEN, ENC_BIG_ENDIAN, &enum_val);
ti = proto_tree_add_item_ret_uint(subtree, hf_uds_wdbi_data_identifier, tvb,
UDS_WDBI_DATA_IDENTIFIER_OFFSET, UDS_WDBI_DATA_IDENTIFIER_LEN,
ENC_BIG_ENDIAN, &enum_val);
protoitem_append_data_name(ti, (guint16)enum_val);
if (sid & UDS_REPLY_MASK) {
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x", enum_val);
infocol_append_data_name(pinfo, enum_val);
} else {
guint32 record_length = data_length - UDS_WDBI_DATA_RECORD_OFFSET;
proto_tree_add_item(subtree, hf_uds_wdbi_data_record, tvb, UDS_WDBI_DATA_RECORD_OFFSET,
record_length, ENC_NA);
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x %s", enum_val,
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x", enum_val);
infocol_append_data_name(pinfo, enum_val);
col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
tvb_bytes_to_str_punct(wmem_packet_scope(), tvb, UDS_WDBI_DATA_RECORD_OFFSET,
record_length, ' '));
}
@ -539,15 +721,19 @@ dissect_uds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void* data
guint32 state_length = data_length - UDS_IOCBI_STATE_OFFSET;
subtree = proto_tree_add_subtree(uds_tree, tvb, 0, -1, ett_uds_iocbi, NULL, service_name);
proto_tree_add_item_ret_uint(subtree, hf_uds_iocbi_data_identifier, tvb, UDS_IOCBI_DATA_IDENTIFIER_OFFSET,
UDS_IOCBI_DATA_IDENTIFIER_LEN, ENC_BIG_ENDIAN, &data_identifier);
ti = proto_tree_add_item_ret_uint(subtree, hf_uds_iocbi_data_identifier, tvb,
UDS_IOCBI_DATA_IDENTIFIER_OFFSET, UDS_IOCBI_DATA_IDENTIFIER_LEN,
ENC_BIG_ENDIAN, &data_identifier);
protoitem_append_data_name(ti, (guint16)data_identifier);
proto_tree_add_item_ret_uint(subtree, hf_uds_iocbi_parameter, tvb, UDS_IOCBI_PARAMETER_OFFSET,
UDS_IOCBI_PARAMETER_LEN, ENC_BIG_ENDIAN, &enum_val);
UDS_IOCBI_PARAMETER_LEN, ENC_BIG_ENDIAN, &enum_val);
proto_tree_add_item(subtree, hf_uds_iocbi_state, tvb, UDS_IOCBI_STATE_OFFSET,
state_length, ENC_NA);
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x %s %s", data_identifier,
col_append_fstr(pinfo->cinfo, COL_INFO, " 0x%04x", data_identifier);
infocol_append_data_name(pinfo, data_identifier);
col_append_fstr(pinfo->cinfo, COL_INFO, " %s %s",
val_to_str(enum_val, uds_iocbi_parameters, "Unknown (0x%02x)"),
tvb_bytes_to_str_punct(wmem_packet_scope(), tvb, UDS_IOCBI_STATE_OFFSET,
state_length, ' '));
@ -560,11 +746,13 @@ dissect_uds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void* data
proto_tree_add_item_ret_uint(subtree, hf_uds_rc_type, tvb, UDS_RC_TYPE_OFFSET,
UDS_RC_TYPE_LEN, ENC_BIG_ENDIAN, &enum_val);
proto_tree_add_item_ret_uint(subtree, hf_uds_rc_identifier, tvb, UDS_RC_ROUTINE_OFFSET,
ti = proto_tree_add_item_ret_uint(subtree, hf_uds_rc_identifier, tvb, UDS_RC_ROUTINE_OFFSET,
UDS_RC_ROUTINE_LEN, ENC_BIG_ENDIAN, &identifier);
protoitem_append_routine_name(ti, identifier);
col_append_fstr(pinfo->cinfo, COL_INFO, " %s 0x%04x",
val_to_str(enum_val, uds_rc_types, "Unknown (0x%02x)"), identifier);
infocol_append_routine_name(pinfo, identifier);
if (sid & UDS_REPLY_MASK) {
guint32 rc_data_len = data_length - UDS_RC_INFO_OFFSET;
if (rc_data_len > 0) {
@ -716,9 +904,23 @@ dissect_uds(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void* data
return tvb_captured_length(tvb);
}
void
pref_update_uds(void) {
if (uds_ht_routine_ids && uds_uat_routine_id_num == 0) {
g_hash_table_destroy(uds_ht_routine_ids);
uds_ht_routine_ids = NULL;
}
if (uds_ht_data_ids && uds_uat_data_id_num == 0) {
g_hash_table_destroy(uds_ht_data_ids);
uds_ht_data_ids = NULL;
}
}
void
proto_register_uds(void)
{
module_t* uds_module;
static hf_register_info hf[] = {
{
&hf_uds_service,
@ -1065,6 +1267,9 @@ proto_register_uds(void)
},
};
uat_t* uds_routine_ids_uat;
uat_t* uds_data_ids_uat;
/* Setup protocol subtree array */
static gint *ett[] =
{
@ -1094,6 +1299,61 @@ proto_register_uds(void)
proto_register_subtree_array(ett, array_length(ett));
uds_handle = register_dissector("uds", dissect_uds, proto_uds);
/* Register preferences */
uds_module = prefs_register_protocol(proto_uds, &pref_update_uds);
/* UATs for user_data fields */
static uat_field_t uds_routine_id_uat_fields[] = {
UAT_FLD_HEX(uds_uat_routine_ids, id, "Routine ID", "Routine Identifier (16bit hex without leading 0x)"),
UAT_FLD_CSTRING(uds_uat_routine_ids, name, "Routine Name", "Name of the Routine ID (string)"),
UAT_END_FIELDS
};
/* UATs */
uds_routine_ids_uat = uat_new("UDS Routine Identifier List",
sizeof(generic_one_id_string_t), /* record size */
DATAFILE_UDS_ROUTINE_IDS, /* filename */
TRUE, /* from profile */
(void**)&uds_uat_routine_ids, /* data_ptr */
&uds_uat_routine_id_num, /* numitems_ptr */
UAT_AFFECTS_DISSECTION, /* but not fields */
NULL, /* help */
copy_generic_one_id_string_cb, /* copy callback */
update_generic_one_identifier_16bit, /* update callback */
free_generic_one_id_string_cb, /* free callback */
post_update_uds_routine_cb, /* post update callback */
NULL, /* reset callback */
uds_routine_id_uat_fields /* UAT field definitions */
);
prefs_register_uat_preference(uds_module, "_uds_routine_id_list", "UDS Routine Identifier List",
"A table to define names of UDS Routines", uds_routine_ids_uat);
static uat_field_t uds_data_id_uat_fields[] = {
UAT_FLD_HEX(uds_uat_data_ids, id, "Data ID", "Data Identifier (16bit hex without leading 0x)"),
UAT_FLD_CSTRING(uds_uat_data_ids, name, "Data Name", "Name of the Data ID (string)"),
UAT_END_FIELDS
};
uds_data_ids_uat = uat_new("UDS Data Identifier List",
sizeof(generic_one_id_string_t), /* record size */
DATAFILE_UDS_DATA_IDS, /* filename */
TRUE, /* from profile */
(void**)&uds_uat_data_ids, /* data_ptr */
&uds_uat_data_id_num, /* numitems_ptr */
UAT_AFFECTS_DISSECTION, /* but not fields */
NULL, /* help */
copy_generic_one_id_string_cb, /* copy callback */
update_generic_one_identifier_16bit, /* update callback */
free_generic_one_id_string_cb, /* free callback */
post_update_uds_data_cb, /* post update callback */
NULL, /* reset callback */
uds_data_id_uat_fields /* UAT field definitions */
);
prefs_register_uat_preference(uds_module, "_uds_data_id_list", "UDS Data Identifier List",
"A table to define names of UDS Data Identifier", uds_data_ids_uat);
}
void