From 171d92cc148ee782fe5c900e11a1d8976ca77662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stig=20Bj=C3=B8rlykke?= Date: Tue, 15 May 2018 09:24:52 +0200 Subject: lwm2mtlv: Add Resource name tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add one fixed table for OMA (Normative) defined resource names and one table for user defined resource names. All resources are identified by a object ID and a resource ID. Show number of elements in arrays instead of number of bytes. Next iteration will add proper hf entries for OMA elements. Change-Id: I4d6c053a7c448cc65692ba1d1e92a2033ff3b397 Reviewed-on: https://code.wireshark.org/review/27551 Petri-Dish: Stig Bjørlykke Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- epan/dissectors/packet-coap.c | 6 +- epan/dissectors/packet-lwm2mtlv.c | 560 +++++++++++++++++++++++++++++++++----- 2 files changed, 503 insertions(+), 63 deletions(-) diff --git a/epan/dissectors/packet-coap.c b/epan/dissectors/packet-coap.c index ae08aba66c..2a5dac9dc8 100644 --- a/epan/dissectors/packet-coap.c +++ b/epan/dissectors/packet-coap.c @@ -27,6 +27,7 @@ #include #include "packet-dtls.h" #include "packet-coap.h" +#include "packet-http.h" void proto_register_coap(void); @@ -920,6 +921,7 @@ dissect_coap_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *coap_tree, p tvbuff_t *payload_tvb; guint payload_length = offset_end - offset; const char *coap_ctype_str_dis; + http_message_info_t message_info; char str_payload[80]; /* coinfo->ctype_value == DEFAULT_COAP_CTYPE_VALUE: No Content-Format option present */ @@ -965,8 +967,10 @@ dissect_coap_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *coap_tree, p PROTO_ITEM_SET_GENERATED(length_item); payload_tvb = tvb_new_subset_length(tvb, offset, payload_length); + message_info.type = HTTP_OTHERS; + message_info.media_str = wmem_strbuf_get_str(coinfo->uri_str_strbuf); dissector_try_string(media_type_dissector_table, coap_ctype_str_dis, - payload_tvb, pinfo, parent_tree, NULL); + payload_tvb, pinfo, parent_tree, &message_info); if (coinfo->object_security && !oscore) { proto_item_set_text(payload_item, "Encrypted OSCORE Data"); diff --git a/epan/dissectors/packet-lwm2mtlv.c b/epan/dissectors/packet-lwm2mtlv.c index 81b72a25b1..991c624896 100644 --- a/epan/dissectors/packet-lwm2mtlv.c +++ b/epan/dissectors/packet-lwm2mtlv.c @@ -16,14 +16,17 @@ #include "config.h" - #include +#include +#include #include +#include "packet-http.h" + void proto_register_lwm2mtlv(void); void proto_reg_handoff_lwm2mtlv(void); -static void parseArrayOfElements(tvbuff_t *tvb, proto_tree *tlv_tree); +static void parseArrayOfElements(tvbuff_t *tvb, proto_tree *tlv_tree, const char *uri_path); static int proto_lwm2mtlv = -1; @@ -91,9 +94,320 @@ typedef struct guint identifier; guint length; guint totalLength; - gboolean is_valid_utf8_string; } lwm2mElement_t; +typedef struct _lwm2m_resource_t { + guint object_id; + guint resource_id; + char *name; + guint data_type; + gint *hf_id; + char *field_name; +} lwm2m_resource_t; + +#define DATA_TYPE_NONE 0 +#define DATA_TYPE_STRING 1 +#define DATA_TYPE_INTEGER 2 +#define DATA_TYPE_FLOAT 3 +#define DATA_TYPE_BOOLEAN 4 +#define DATA_TYPE_OPAQUE 5 +#define DATA_TYPE_TIME 6 +#define DATA_TYPE_OBJLNK 7 + +static const value_string data_types[] = { + { DATA_TYPE_NONE, "None" }, + { DATA_TYPE_STRING, "String" }, + { DATA_TYPE_INTEGER, "Integer" }, + { DATA_TYPE_FLOAT, "Float" }, + { DATA_TYPE_BOOLEAN, "Boolean" }, + { DATA_TYPE_OPAQUE, "Opaque" }, + { DATA_TYPE_TIME, "Time" }, + { DATA_TYPE_OBJLNK, "Objlnk" }, + { 0, NULL } +}; + +/* LwM2M Objects defined by OMA (Normative) */ +static lwm2m_resource_t lwm2m_oma_resources[] = +{ + /* LwM2M Security (0) */ + { 0, 0, "LwM2M Server URI", DATA_TYPE_STRING, NULL, NULL }, + { 0, 1, "Bootstrap-Server", DATA_TYPE_BOOLEAN, NULL, NULL }, + { 0, 2, "Security Mode", DATA_TYPE_INTEGER, NULL, NULL }, + { 0, 3, "Public Key or Identity", DATA_TYPE_OPAQUE, NULL, NULL }, + { 0, 4, "Server Public Key", DATA_TYPE_OPAQUE, NULL, NULL }, + { 0, 5, "Secret Key", DATA_TYPE_OPAQUE, NULL, NULL }, + { 0, 6, "SMS Security Mode", DATA_TYPE_INTEGER, NULL, NULL }, + { 0, 7, "SMS Binding Key Parameters", DATA_TYPE_OPAQUE, NULL, NULL }, + { 0, 8, "SMS Binding Secret Keys", DATA_TYPE_OPAQUE, NULL, NULL }, + { 0, 9, "LwM2M Server SMS Number", DATA_TYPE_STRING, NULL, NULL }, + { 0, 10, "Short Server ID", DATA_TYPE_INTEGER, NULL, NULL }, + { 0, 11, "Client Hold Off Time", DATA_TYPE_INTEGER, NULL, NULL }, + { 0, 12, "Bootstrap-Server Account Timeout", DATA_TYPE_INTEGER, NULL, NULL }, + + /* LwM2M Server (1) */ + { 1, 0, "Short Server ID", DATA_TYPE_INTEGER, NULL, NULL }, + { 1, 1, "Lifetime", DATA_TYPE_INTEGER, NULL, NULL }, + { 1, 2, "Default Minimum Period", DATA_TYPE_INTEGER, NULL, NULL }, + { 1, 3, "Default Maximum Period", DATA_TYPE_INTEGER, NULL, NULL }, + { 1, 4, "Disable", DATA_TYPE_NONE, NULL, NULL }, + { 1, 5, "Disable Timeout", DATA_TYPE_INTEGER, NULL, NULL }, + { 1, 6, "Notification Storing When Disabled or Offline", DATA_TYPE_BOOLEAN, NULL, NULL }, + { 1, 7, "Binding", DATA_TYPE_STRING, NULL, NULL }, + + /* Access Control (2) */ + { 2, 0, "Object ID", DATA_TYPE_INTEGER, NULL, NULL }, + { 2, 1, "Object Instance ID", DATA_TYPE_INTEGER, NULL, NULL }, + { 2, 2, "ACL", DATA_TYPE_INTEGER, NULL, NULL }, + { 2, 3, "Access Control Owner", DATA_TYPE_INTEGER, NULL, NULL }, + + /* Device (3) */ + { 3, 0, "Manufacturer", DATA_TYPE_STRING, NULL, NULL }, + { 3, 1, "Model Number", DATA_TYPE_STRING, NULL, NULL }, + { 3, 2, "Serial Number", DATA_TYPE_STRING, NULL, NULL }, + { 3, 3, "Firmware Version", DATA_TYPE_STRING, NULL, NULL }, + { 3, 4, "Reboot", DATA_TYPE_NONE, NULL, NULL }, + { 3, 5, "Factory Reset", DATA_TYPE_NONE, NULL, NULL }, + { 3, 6, "Available Power Sources", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 7, "Power Source Voltage", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 8, "Power Source Current", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 9, "Battery Level", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 10, "Memory Free", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 11, "Error Code", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 12, "Reset Error Code", DATA_TYPE_NONE, NULL, NULL }, + { 3, 13, "Current Time", DATA_TYPE_TIME, NULL, NULL }, + { 3, 14, "UTC Offset", DATA_TYPE_STRING, NULL, NULL }, + { 3, 15, "Timezone", DATA_TYPE_STRING, NULL, NULL }, + { 3, 16, "Supported Binding and Modes", DATA_TYPE_STRING, NULL, NULL }, + { 3, 17, "Device Type", DATA_TYPE_STRING, NULL, NULL }, + { 3, 18, "Hardware Version", DATA_TYPE_STRING, NULL, NULL }, + { 3, 19, "Software Version", DATA_TYPE_STRING, NULL, NULL }, + { 3, 20, "Battery Status", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 21, "Memory Total", DATA_TYPE_INTEGER, NULL, NULL }, + { 3, 22, "ExtDevInfo", DATA_TYPE_OBJLNK, NULL, NULL }, + + /* Connectivity Monitoring (4) */ + { 4, 0, "Network Bearer", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 1, "Available Network Bearer", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 2, "Radio Signal Strength", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 3, "Link Quality", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 4, "IP Addresses", DATA_TYPE_STRING, NULL, NULL }, + { 4, 5, "Router IP Addresses", DATA_TYPE_STRING, NULL, NULL }, + { 4, 6, "Link Utilization", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 7, "APN", DATA_TYPE_STRING, NULL, NULL }, + { 4, 8, "Cell ID", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 9, "SMNC", DATA_TYPE_INTEGER, NULL, NULL }, + { 4, 10, "SMCC", DATA_TYPE_INTEGER, NULL, NULL }, + + /* Firmware Update (5) */ + { 5, 0, "Package", DATA_TYPE_OPAQUE, NULL, NULL }, + { 5, 1, "Package URI", DATA_TYPE_STRING, NULL, NULL }, + { 5, 2, "Update", DATA_TYPE_NONE, NULL, NULL }, + { 5, 3, "State", DATA_TYPE_INTEGER, NULL, NULL }, + /* { 5, 4, "", DATA_TYPE_NONE, NULL, NULL }, */ + { 5, 5, "Update Result", DATA_TYPE_INTEGER, NULL, NULL }, + { 5, 6, "PkgName", DATA_TYPE_STRING, NULL, NULL }, + { 5, 7, "PkgVersion", DATA_TYPE_STRING, NULL, NULL }, + { 5, 8, "Firmware Update Protocol Support", DATA_TYPE_INTEGER, NULL, NULL }, + { 5, 9, "Firmware Update Delivery Method", DATA_TYPE_INTEGER, NULL, NULL }, + + /* Location (6) */ + { 6, 0, "Latitude", DATA_TYPE_FLOAT, NULL, NULL }, + { 6, 1, "Longitude", DATA_TYPE_FLOAT, NULL, NULL }, + { 6, 2, "Altitude", DATA_TYPE_FLOAT, NULL, NULL }, + { 6, 3, "Radius", DATA_TYPE_FLOAT, NULL, NULL }, + { 6, 4, "Velocity", DATA_TYPE_OPAQUE, NULL, NULL }, + { 6, 5, "Timestamp", DATA_TYPE_TIME, NULL, NULL }, + { 6, 6, "Speed", DATA_TYPE_FLOAT, NULL, NULL }, + + /* Connectivity Statistics (7) */ + { 7, 0, "SMS Tx Counter", DATA_TYPE_INTEGER, NULL, NULL }, + { 7, 1, "SMS Rx Counter", DATA_TYPE_INTEGER, NULL, NULL }, + { 7, 2, "Tx Data", DATA_TYPE_INTEGER, NULL, NULL }, + { 7, 3, "Rx Data", DATA_TYPE_INTEGER, NULL, NULL }, + { 7, 4, "Max Message Size", DATA_TYPE_INTEGER, NULL, NULL }, + { 7, 5, "Average Message Size", DATA_TYPE_INTEGER, NULL, NULL }, + { 7, 6, "Start", DATA_TYPE_NONE, NULL, NULL }, + { 7, 7, "Stop", DATA_TYPE_NONE, NULL, NULL }, + { 7, 8, "Collection Period", DATA_TYPE_INTEGER, NULL, NULL }, +}; + +/* LwM2M Objects defined by User */ +static lwm2m_resource_t *lwm2m_uat_resources; +static guint num_lwm2m_uat_resources; + +static gboolean lwm2m_resource_update_cb(void *record, char **error) +{ + lwm2m_resource_t *rec = (lwm2m_resource_t *)record; + char c; + + if (rec->name == NULL) { + *error = g_strdup("Resource Name can't be empty"); + return FALSE; + } + + g_strstrip(rec->name); + if (rec->name[0] == 0) { + *error = g_strdup("Resource Name can't be empty"); + return FALSE; + } + + g_free(rec->field_name); + rec->field_name = g_ascii_strdown(rec->name, -1); + for (size_t i = 0; i < strlen(rec->field_name); i++) { + if (rec->field_name[i] == ' ' || rec->field_name[i] == '.') { + rec->field_name[i] = '_'; + } + } + + /* Check for invalid characters (to avoid asserting out when registering the field). */ + c = proto_check_field_name(rec->field_name); + if (c) { + *error = g_strdup_printf("Resource Name can't contain '%c'", c); + return FALSE; + } + + *error = NULL; + return TRUE; +} + +static void *lwm2m_resource_copy_cb(void *dest, const void *source, size_t len _U_) +{ + const lwm2m_resource_t *s = (const lwm2m_resource_t *)source; + lwm2m_resource_t *d = (lwm2m_resource_t *)dest; + + d->object_id = s->object_id; + d->resource_id = s->resource_id; + d->name = g_strdup(s->name); + d->field_name = g_strdup(s->field_name); + d->data_type = s->data_type; + + return d; +} + +static void lwm2m_resource_free_cb(void *record) +{ + lwm2m_resource_t *rec = (lwm2m_resource_t *)record; + + g_free(rec->name); + g_free(rec->field_name); +} + +static void lwm2m_add_resource(lwm2m_resource_t *resource, hf_register_info *hf) +{ + gchar *resource_abbrev; + gint *hf_id; + + hf_id = g_new(gint,1); + *hf_id = -1; + + if (resource->field_name) { + resource_abbrev = g_strdup(resource->field_name); + } else { + resource_abbrev = g_ascii_strdown(resource->name, -1); + for (size_t i = 0; i < strlen(resource_abbrev); i++) { + if (resource_abbrev[i] == ' ' || resource_abbrev[i] == '.') { + resource_abbrev[i] = '_'; + } + } + } + + resource->hf_id = hf_id; + + hf->p_id = hf_id; + hf->hfinfo.name = g_strdup(resource->name); + hf->hfinfo.abbrev = g_strdup_printf("lwm2mtlv.resource.%s", resource_abbrev); + g_free (resource_abbrev); + + switch (resource->data_type) { + case DATA_TYPE_STRING: + hf->hfinfo.display = BASE_NONE; + hf->hfinfo.type = FT_STRING; + break; + case DATA_TYPE_INTEGER: + hf->hfinfo.display = BASE_DEC; + hf->hfinfo.type = FT_INT64; + break; + case DATA_TYPE_FLOAT: + hf->hfinfo.display = BASE_NONE; + hf->hfinfo.type = FT_FLOAT; + break; + case DATA_TYPE_BOOLEAN: + hf->hfinfo.display = BASE_DEC; + hf->hfinfo.type = FT_BOOLEAN; + break; + case DATA_TYPE_TIME: + hf->hfinfo.display = ABSOLUTE_TIME_LOCAL; + hf->hfinfo.type = FT_ABSOLUTE_TIME; + break; + case DATA_TYPE_OPAQUE: + case DATA_TYPE_OBJLNK: + default: + hf->hfinfo.display = BASE_NONE; + hf->hfinfo.type = FT_BYTES; + break; + } + hf->hfinfo.strings = NULL; + hf->hfinfo.bitmask = 0; + hf->hfinfo.blurb = NULL; + HFILL_INIT(*hf); +} + +static void lwm2m_resource_post_update_cb(void) +{ + static hf_register_info *hf; + static guint hf_size; + + if (hf) { + /* Deregister all fields */ + for (guint i = 0; i < hf_size; i++) { + proto_deregister_field(proto_lwm2mtlv, *(hf[i].p_id)); + g_free (hf[i].p_id); + } + proto_add_deregistered_data(hf); + } + + if (num_lwm2m_uat_resources) { + hf = g_new0(hf_register_info, num_lwm2m_uat_resources); + hf_size = 0; + + for (guint i = 0; i < num_lwm2m_uat_resources; i++) { + lwm2m_add_resource(&lwm2m_uat_resources[i], &hf[hf_size++]); + } + + proto_register_field_array(proto_lwm2mtlv, hf, hf_size); + } +} + +static gint64 +decodeVariableInt(tvbuff_t *tvb, const gint offset, const guint length) +{ + switch(length) + { + case 1: + return tvb_get_gint8(tvb, offset); + case 2: + return tvb_get_ntohis(tvb, offset); + case 3: + return tvb_get_ntohi24(tvb, offset); + case 4: + return tvb_get_ntohil(tvb, offset); + case 5: + return tvb_get_ntohi40(tvb, offset); + case 6: + return tvb_get_ntohi48(tvb, offset); + case 7: + return tvb_get_ntohi56(tvb, offset); + case 8: + return tvb_get_ntohi64(tvb, offset); + default: + return 0; + } +} + +UAT_DEC_CB_DEF(resource, object_id, lwm2m_resource_t) +UAT_DEC_CB_DEF(resource, resource_id, lwm2m_resource_t) +UAT_CSTRING_CB_DEF(resource, name, lwm2m_resource_t) +UAT_VS_DEF(resource, data_type, lwm2m_resource_t, guint, DATA_TYPE_NONE, "None") static void addTlvHeaderElements(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element) @@ -128,107 +442,178 @@ addTlvHeaderTree(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element) } static proto_tree* -addElementTree(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element) +addElementTree(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, const lwm2m_resource_t *resource) { - guint valueOffset = 1 + element->length_of_identifier + element->length_of_length; - guint8 *str; + gchar *identifier = NULL; + + if (resource) { + identifier = wmem_strdup_printf(wmem_packet_scope(), "[%02u] %s", element->identifier, resource->name); + } else { + identifier = wmem_strdup_printf(wmem_packet_scope(), "[%02u]", element->identifier); + } switch ( element->type ) { case OBJECT_INSTANCE: return proto_tree_add_subtree_format(tlv_tree, tvb, 0, element->totalLength, ett_lwm2mtlv_objectInstance, NULL, - "Object Instance %02u (%u Bytes)", element->identifier, element->length_of_value); + "Object Instance %02u", element->identifier); case RESOURCE_INSTANCE: - str = tvb_get_string_enc(wmem_packet_scope(), tvb, valueOffset, element->length_of_value, ENC_UTF_8); - if (isprint_utf8_string(str, element->length_of_value)) { - element->is_valid_utf8_string = TRUE; - } else { - str = tvb_bytes_to_str(wmem_packet_scope(), tvb, valueOffset, element->length_of_value); - } return proto_tree_add_subtree_format(tlv_tree, tvb, 0, element->totalLength, ett_lwm2mtlv_resourceInstance, NULL, - "%02u: %s", element->identifier, str); + "%02u", element->identifier); case RESOURCE_ARRAY: return proto_tree_add_subtree_format(tlv_tree, tvb, 0, element->totalLength, ett_lwm2mtlv_resourceArray, NULL, - "%02u: (Array of %u Bytes)", element->identifier, element->length_of_value); + "%s", identifier); case RESOURCE: - str = tvb_get_string_enc(wmem_packet_scope(), tvb, valueOffset, element->length_of_value, ENC_UTF_8); - if (isprint_utf8_string(str, element->length_of_value)) { - element->is_valid_utf8_string = TRUE; - } else { - str = tvb_bytes_to_str(wmem_packet_scope(), tvb, valueOffset, element->length_of_value); - } return proto_tree_add_subtree_format(tlv_tree, tvb, 0, element->totalLength, ett_lwm2mtlv_resource, NULL, - "%02u: %s", element->identifier, str); + "%s", identifier); } return NULL; } static void -addValueInterpretations(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element) +addValueInterpretations(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, const lwm2m_resource_t *resource) { guint valueOffset; if ( element->length_of_value == 0 ) return; valueOffset = 1 + element->length_of_identifier + element->length_of_length; - if (element->is_valid_utf8_string) { - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_string, tvb, valueOffset, element->length_of_value, ENC_UTF_8|ENC_NA); - } - - switch(element->length_of_value) - { - case 0x01: - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - if (tvb_get_guint8(tvb, valueOffset) < 2) { - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_boolean, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + if (resource && resource->data_type != DATA_TYPE_NONE) { + switch (resource->data_type) { + case DATA_TYPE_STRING: + { + const guint8 *strval; + proto_tree_add_item_ret_string(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_UTF_8, wmem_packet_scope(), &strval); + proto_item_append_text(tlv_tree, ": %s", strval); + break; + } + case DATA_TYPE_INTEGER: + proto_tree_add_item(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + proto_item_append_text(tlv_tree, ": %" G_GINT64_FORMAT, decodeVariableInt(tvb, valueOffset, element->length_of_value)); + break; + case DATA_TYPE_FLOAT: + proto_tree_add_item(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + proto_item_append_text(tlv_tree, ": %." G_STRINGIFY(FLT_DIG) "g", tvb_get_ieee_float(tvb, valueOffset, ENC_BIG_ENDIAN)); + break; + case DATA_TYPE_BOOLEAN: + { + gboolean boolval; + proto_tree_add_item_ret_boolean(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN, &boolval); + proto_item_append_text(tlv_tree, ": %s", boolval ? "True" : "False"); + break; + } + case DATA_TYPE_TIME: + { + nstime_t ts; + ts.secs = (time_t)decodeVariableInt(tvb, valueOffset, element->length_of_value); + ts.nsecs = 0; + proto_tree_add_time(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, &ts); + proto_item_append_text(tlv_tree, ": %s", abs_time_to_str(wmem_packet_scope(), &ts, ABSOLUTE_TIME_LOCAL, FALSE)); + break; + } + case DATA_TYPE_OPAQUE: + case DATA_TYPE_OBJLNK: + default: + proto_tree_add_item(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + proto_item_append_text(tlv_tree, ": %s", tvb_bytes_to_str(wmem_packet_scope(), tvb, valueOffset, element->length_of_value)); + break; + } + } else { + guint8 *str = tvb_get_string_enc(wmem_packet_scope(), tvb, valueOffset, element->length_of_value, ENC_UTF_8); + if (isprint_utf8_string(str, element->length_of_value)) { + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_string, tvb, valueOffset, element->length_of_value, ENC_UTF_8|ENC_NA); + } else { + str = tvb_bytes_to_str(wmem_packet_scope(), tvb, valueOffset, element->length_of_value); + } + proto_item_append_text(tlv_tree, ": %s", str); + + switch(element->length_of_value) { + case 0x01: + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + if (tvb_get_guint8(tvb, valueOffset) < 2) { + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_boolean, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + } + break; + case 0x02: + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + break; + case 0x04: + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_float, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_timestamp, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + break; + case 0x08: + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_double, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); + /* apparently, wireshark does not deal well with 8 bytes. */ + proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_timestamp, tvb, valueOffset+4, element->length_of_value-4, ENC_BIG_ENDIAN); + break; } - break; - case 0x02: - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - break; - case 0x04: - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_float, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_timestamp, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - break; - case 0x08: - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_double, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN); - /* apparently, wireshark does not deal well with 8 bytes. */ - proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_timestamp, tvb, valueOffset+4, element->length_of_value-4, ENC_BIG_ENDIAN); - break; } } static void -addValueTree(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element) +addValueTree(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, const char *uri_path, const lwm2m_resource_t *resource) { guint valueOffset = 1 + element->length_of_identifier + element->length_of_length; if ( element->type == RESOURCE || element->type == RESOURCE_INSTANCE ) { proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value, tvb, valueOffset, element->length_of_value, ENC_NA); - addValueInterpretations(tvb, tlv_tree, element); + addValueInterpretations(tvb, tlv_tree, element, resource); } else { tvbuff_t* sub = tvb_new_subset_length(tvb, valueOffset, element->length_of_value); - parseArrayOfElements(sub, tlv_tree); + parseArrayOfElements(sub, tlv_tree, uri_path); } } static void -addTlvElement(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element) +addTlvElement(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, const char *uri_path) { proto_tree *element_tree = NULL; + const lwm2m_resource_t *resource = NULL; + + gchar **ids = wmem_strsplit(wmem_packet_scope(), uri_path, "/", 4); + if (ids && ids[0] && ids[1] && ids[2]) { + /* URI path is defined as: + * ids[0] = Object ID + * ids[1] = Object Instance + * ids[2] = Resource ID + * ids[3] = Resource Instance + */ + guint object_id = (guint)strtol(ids[0], NULL, 10); + guint resource_id = (guint)strtol(ids[2], NULL, 10); + + /* First search OMA objects */ + for (guint i = 0; i < array_length(lwm2m_oma_resources); i++) { + if ((object_id == lwm2m_oma_resources[i].object_id) && + (resource_id == lwm2m_oma_resources[i].resource_id)) + { + resource = &lwm2m_oma_resources[i]; + break; + } + } + + /* Then search user configured objects */ + for (guint i = 0; i < num_lwm2m_uat_resources; i++) { + if ((object_id == lwm2m_uat_resources[i].object_id) && + (resource_id == lwm2m_uat_resources[i].resource_id)) + { + resource = &lwm2m_uat_resources[i]; + break; + } + } + } - element_tree = addElementTree(tvb, tlv_tree, element); + element_tree = addElementTree(tvb, tlv_tree, element, resource); addTlvHeaderTree(tvb, element_tree, element); - addValueTree(tvb, element_tree, element); + addValueTree(tvb, element_tree, element, uri_path, resource); } static guint64 -decodeVariableInt(tvbuff_t *tvb, const gint offset, const guint length) +decodeVariableUInt(tvbuff_t *tvb, const gint offset, const guint length) { switch(length) { @@ -262,30 +647,35 @@ static guint parseTLVHeader(tvbuff_t *tvb, lwm2mElement_t *element) element->length_of_value = (( type_field >> 0 ) & 0x07 ); /* It is ok to shorten identifier and length_of_value, they are never more than 24 bits long */ - element->identifier = (guint) decodeVariableInt(tvb, 1, element->length_of_identifier); + element->identifier = (guint) decodeVariableUInt(tvb, 1, element->length_of_identifier); if ( element->length_of_length > 0 ) { - element->length_of_value = (guint) decodeVariableInt(tvb, 1 + element->length_of_identifier, element->length_of_length); + element->length_of_value = (guint) decodeVariableUInt(tvb, 1 + element->length_of_identifier, element->length_of_length); } element->totalLength = 1 + element->length_of_identifier + element->length_of_length + element->length_of_value; - element->is_valid_utf8_string = FALSE; return element->totalLength; } -static void parseArrayOfElements(tvbuff_t *tvb, proto_tree *tlv_tree) +static void parseArrayOfElements(tvbuff_t *tvb, proto_tree *tlv_tree, const char *uri_path) { guint length; guint offset = 0; guint elementLength = 0; + guint element_count = 0; lwm2mElement_t element; + const char *next_uri_path = NULL; length = tvb_reported_length(tvb); while ( length > 0 ) { tvbuff_t* sub = tvb_new_subset_length(tvb, offset, length); elementLength = parseTLVHeader(sub, &element); - addTlvElement(sub, tlv_tree, &element); + if (uri_path) { + next_uri_path = wmem_strdup_printf(wmem_packet_scope(), "%s/%d", uri_path, element.identifier); + } + addTlvElement(sub, tlv_tree, &element, next_uri_path); + element_count++; length -= elementLength; offset += elementLength; @@ -294,19 +684,27 @@ static void parseArrayOfElements(tvbuff_t *tvb, proto_tree *tlv_tree) break; } } + + proto_item_append_text(tlv_tree, " (%u element%s)", element_count, plurality(element_count, "", "s")); } static int -dissect_lwm2mtlv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) +dissect_lwm2mtlv(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void *data) { proto_tree* lwm2mtlv_tree; proto_item* lwm2mtlv_item; + http_message_info_t *message_info = (http_message_info_t *) data; + const char *uri_path = NULL; + + if (message_info && message_info->media_str && message_info->media_str[0]) { + uri_path = message_info->media_str; + } if (tree) { /* we are being asked for details */ lwm2mtlv_item = proto_tree_add_item(tree, proto_lwm2mtlv, tvb, 0, -1, ENC_NA); lwm2mtlv_tree = proto_item_add_subtree(lwm2mtlv_item, ett_lwm2mtlv); - parseArrayOfElements(tvb, lwm2mtlv_tree); + parseArrayOfElements(tvb, lwm2mtlv_tree, uri_path); } return tvb_captured_length(tvb); } @@ -400,6 +798,31 @@ void proto_register_lwm2mtlv(void) &ett_lwm2mtlv_objectInstance }; + static uat_field_t lwm2m_resource_flds[] = { + UAT_FLD_DEC(resource, object_id, "Object ID", "Object ID"), + UAT_FLD_DEC(resource, resource_id, "Resource ID", "Resource ID"), + UAT_FLD_CSTRING(resource, name, "Resource Name", "Resource Name"), + UAT_FLD_VS(resource, data_type, "Data Type", data_types, "Data Type"), + UAT_END_FIELDS + }; + + uat_t *resource_uat = uat_new("User Resource Names", + sizeof(lwm2m_resource_t), + "lwm2m_resource_names", + TRUE, + &lwm2m_uat_resources, + &num_lwm2m_uat_resources, + UAT_AFFECTS_DISSECTION|UAT_AFFECTS_FIELDS, + "ChLwM2MResourceNames", + lwm2m_resource_copy_cb, + lwm2m_resource_update_cb, + lwm2m_resource_free_cb, + lwm2m_resource_post_update_cb, + NULL, + lwm2m_resource_flds); + + module_t *lwm2mtlv_module; + /* Register our configuration options */ proto_lwm2mtlv = proto_register_protocol ( "Lightweight M2M TLV", @@ -411,6 +834,19 @@ void proto_register_lwm2mtlv(void) proto_register_subtree_array(ett, array_length(ett)); register_dissector("lwm2mtlv", dissect_lwm2mtlv, proto_lwm2mtlv); + + lwm2mtlv_module = prefs_register_protocol(proto_lwm2mtlv, NULL); + + prefs_register_uat_preference(lwm2mtlv_module, "resource_table", + "Resource Names", + "User Resource Names", + resource_uat); + + hf_register_info *hf2 = g_new0(hf_register_info, array_length(lwm2m_oma_resources)); + for (guint i = 0; i < array_length(lwm2m_oma_resources); i++) { + lwm2m_add_resource(&lwm2m_oma_resources[i], &hf2[i]); + } + proto_register_field_array(proto_lwm2mtlv, hf2, array_length(lwm2m_oma_resources)); } void -- cgit v1.2.3