diff --git a/epan/dissectors/packet-uds.c b/epan/dissectors/packet-uds.c index e4e63744b4..2d8990a580 100644 --- a/epan/dissectors/packet-uds.c +++ b/epan/dissectors/packet-uds.c @@ -11,11 +11,15 @@ #include "config.h" #include +#include #include 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