Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-lwm2mtlv.c
blobcdd1e5c7de6edd270a49e9a87c3a86462a67eeb0
1 /* packet-lwm2mtlv.c
2 * Routines for LWM2M TLV dissection
3 * References:
4 * OMA LWM2M Specification: OMA-TS-LightweightM2M_Core-V1_1-20180710-A.pdf
5 * available from
6 * http://openmobilealliance.org/release/LightweightM2M/V1_1-20180710-A/
8 * Copyright 2016, Christoph Burger-Scheidlin
9 * Copyright 2018, Stig Bjorlykke <stig@bjorlykke.org>
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include "config.h"
20 #include <epan/packet.h>
21 #include <epan/to_str.h>
22 #include <epan/uat.h>
23 #include <wsutil/array.h>
24 #include <wsutil/str_util.h>
26 #include "packet-gsm_a_common.h"
27 #include "packet-media-type.h"
29 void proto_register_lwm2mtlv(void);
30 void proto_reg_handoff_lwm2mtlv(void);
32 static dissector_handle_t lwm2mtlv_handle;
34 static int proto_lwm2mtlv;
36 static int hf_lwm2mtlv_object_name;
37 static int hf_lwm2mtlv_resource_name;
38 static int hf_lwm2mtlv_header;
39 static int hf_lwm2mtlv_type_type;
40 static int hf_lwm2mtlv_type_length_of_identifier;
41 static int hf_lwm2mtlv_type_length_of_length;
42 static int hf_lwm2mtlv_type_length;
43 static int hf_lwm2mtlv_type_ignored;
45 static int hf_lwm2mtlv_identifier;
46 static int hf_lwm2mtlv_length;
47 static int hf_lwm2mtlv_value;
48 static int hf_lwm2mtlv_value_string;
49 static int hf_lwm2mtlv_value_integer;
50 static int hf_lwm2mtlv_value_unsigned_integer;
51 static int hf_lwm2mtlv_value_float;
52 static int hf_lwm2mtlv_value_double;
53 static int hf_lwm2mtlv_value_boolean;
54 static int hf_lwm2mtlv_value_timestamp;
56 static int hf_lwm2mtlv_object_instance;
57 static int hf_lwm2mtlv_resource_instance;
58 static int hf_lwm2mtlv_resource_array;
59 static int hf_lwm2mtlv_resource;
61 static int ett_lwm2mtlv;
62 static int ett_lwm2mtlv_header;
63 static int ett_lwm2mtlv_resource;
64 static int ett_lwm2mtlv_resource_instance;
65 static int ett_lwm2mtlv_resource_array;
66 static int ett_lwm2mtlv_object_instance;
67 static int ett_lwm2mtlv_location_velocity;
69 typedef enum {
70 OBJECT_INSTANCE = 0,
71 RESOURCE_INSTANCE = 1,
72 RESOURCE_ARRAY = 2,
73 RESOURCE = 3
74 } lwm2m_identifier_t;
76 static const value_string identifiers[] = {
77 { OBJECT_INSTANCE, "Object Instance" },
78 { RESOURCE_INSTANCE, "Resource Instance" },
79 { RESOURCE_ARRAY, "Multiple Resources" },
80 { RESOURCE, "Resource with value" },
81 { 0, NULL }
84 static const value_string length_identifier[] = {
85 { 0x00, "1 byte identifier" },
86 { 0x01, "2 bytes identifier" },
87 { 0, NULL }
90 static const value_string length_type[] = {
91 { 0x00, "No length field" },
92 { 0x01, "1 byte length field" },
93 { 0x02, "2 bytes length field" },
94 { 0x03, "3 bytes length field" },
95 { 0, NULL }
98 typedef struct
100 unsigned type;
101 unsigned length_of_identifier;
102 unsigned length_of_length;
103 unsigned length_of_value;
104 unsigned identifier;
105 unsigned length;
106 unsigned totalLength;
107 } lwm2mElement_t;
109 typedef struct _lwm2m_object_name_t {
110 unsigned object_id;
111 char *name;
112 } lwm2m_object_name_t;
114 typedef struct _lwm2m_resource_t {
115 unsigned object_id;
116 unsigned resource_id;
117 char *name;
118 unsigned data_type;
119 int *hf_id;
120 int ett_id;
121 char *field_name;
122 } lwm2m_resource_t;
124 static void parseArrayOfElements(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tlv_tree, int object_id, int resource_id);
126 /* RESOURCE_FILL initializes all the dynamic fields in a lwm2m_resource_t. */
127 #define RESOURCE_FILL NULL, -1, NULL
129 #define DATA_TYPE_NONE 0
130 #define DATA_TYPE_STRING 1
131 #define DATA_TYPE_INTEGER 2
132 #define DATA_TYPE_UNSIGNED_INTEGER 3
133 #define DATA_TYPE_FLOAT 4
134 #define DATA_TYPE_BOOLEAN 5
135 #define DATA_TYPE_OPAQUE 6
136 #define DATA_TYPE_TIME 7
137 #define DATA_TYPE_OBJLNK 8
138 #define DATA_TYPE_CORELNK 9
140 static const value_string data_types[] = {
141 { DATA_TYPE_NONE, "None" },
142 { DATA_TYPE_STRING, "String" },
143 { DATA_TYPE_INTEGER, "Integer" },
144 { DATA_TYPE_UNSIGNED_INTEGER, "Unsigned Integer" },
145 { DATA_TYPE_FLOAT, "Float" },
146 { DATA_TYPE_BOOLEAN, "Boolean" },
147 { DATA_TYPE_OPAQUE, "Opaque" },
148 { DATA_TYPE_TIME, "Time" },
149 { DATA_TYPE_OBJLNK, "Objlnk" },
150 { DATA_TYPE_CORELNK, "Corelnk" },
151 { 0, NULL }
154 /* LwM2M Objects defined by OMA (Normative) */
155 static const value_string lwm2m_oma_objects[] = {
156 { 0, "LwM2M Security" },
157 { 1, "LwM2M Server" },
158 { 2, "Access Control" },
159 { 3, "Device" },
160 { 4, "Connectivity Monitoring" },
161 { 5, "Firmware Update" },
162 { 6, "Location" },
163 { 7, "Connectivity Statistics" },
164 { 21, "OSCORE" },
165 { 0, NULL }
168 static lwm2m_resource_t lwm2m_oma_resources[] =
170 /* LwM2M Security (0) */
171 { 0, 0, "LwM2M Server URI", DATA_TYPE_STRING, RESOURCE_FILL },
172 { 0, 1, "Bootstrap-Server", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
173 { 0, 2, "Security Mode", DATA_TYPE_INTEGER, RESOURCE_FILL },
174 { 0, 3, "Public Key or Identity", DATA_TYPE_OPAQUE, RESOURCE_FILL },
175 { 0, 4, "Server Public Key", DATA_TYPE_OPAQUE, RESOURCE_FILL },
176 { 0, 5, "Secret Key", DATA_TYPE_OPAQUE, RESOURCE_FILL },
177 { 0, 6, "SMS Security Mode", DATA_TYPE_INTEGER, RESOURCE_FILL },
178 { 0, 7, "SMS Binding Key Parameters", DATA_TYPE_OPAQUE, RESOURCE_FILL },
179 { 0, 8, "SMS Binding Secret Keys", DATA_TYPE_OPAQUE, RESOURCE_FILL },
180 { 0, 9, "LwM2M Server SMS Number", DATA_TYPE_STRING, RESOURCE_FILL },
181 { 0, 10, "Short Server ID", DATA_TYPE_INTEGER, RESOURCE_FILL },
182 { 0, 11, "Client Hold Off Time", DATA_TYPE_INTEGER, RESOURCE_FILL },
183 { 0, 12, "Bootstrap-Server Account Timeout", DATA_TYPE_INTEGER, RESOURCE_FILL },
184 { 0, 13, "Matching Type", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
185 { 0, 14, "SNI", DATA_TYPE_STRING, RESOURCE_FILL },
186 { 0, 15, "Certificate Usage", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
187 { 0, 16, "TLS DTLS Ciphersuite", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
188 { 0, 17, "OSCORE Security Mode", DATA_TYPE_OBJLNK, RESOURCE_FILL },
189 { 0, 18, "Groups To Use by Client", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
190 { 0, 19, "Signature Algorithms Supported by Server", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
191 { 0, 20, "Signature Algorithms To Use by Client", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
192 { 0, 21, "Signature Algorithm Certs Supported by Server", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
193 { 0, 22, "TLS 1.3 Features To Use by Client", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
194 { 0, 23, "TLS Extensions Supported by Server", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
195 { 0, 24, "TLS Extensions To Use by Client", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
196 { 0, 25, "Secondary LwM2M Server URI", DATA_TYPE_STRING, RESOURCE_FILL },
197 { 0, 26, "MQTT Server", DATA_TYPE_OBJLNK, RESOURCE_FILL },
198 { 0, 27, "LwM2M COSE Security", DATA_TYPE_OBJLNK, RESOURCE_FILL },
199 { 0, 28, "RDS Destination Port", DATA_TYPE_INTEGER, RESOURCE_FILL },
200 { 0, 29, "RDS Source Port", DATA_TYPE_INTEGER, RESOURCE_FILL },
201 { 0, 30, "RDS Application ID", DATA_TYPE_STRING, RESOURCE_FILL },
203 /* LwM2M Server (1) */
204 { 1, 0, "Short Server ID", DATA_TYPE_INTEGER, RESOURCE_FILL },
205 { 1, 1, "Lifetime", DATA_TYPE_INTEGER, RESOURCE_FILL },
206 { 1, 2, "Default Minimum Period", DATA_TYPE_INTEGER, RESOURCE_FILL },
207 { 1, 3, "Default Maximum Period", DATA_TYPE_INTEGER, RESOURCE_FILL },
208 { 1, 4, "Disable", DATA_TYPE_NONE, RESOURCE_FILL },
209 { 1, 5, "Disable Timeout", DATA_TYPE_INTEGER, RESOURCE_FILL },
210 { 1, 6, "Notification Storing When Disabled or Offline", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
211 { 1, 7, "Binding", DATA_TYPE_STRING, RESOURCE_FILL },
212 { 1, 8, "Registration Update Trigger", DATA_TYPE_NONE, RESOURCE_FILL },
213 { 1, 9, "Bootstrap Request Trigger", DATA_TYPE_NONE, RESOURCE_FILL },
214 { 1, 10, "APN Link", DATA_TYPE_OBJLNK, RESOURCE_FILL },
215 { 1, 11, "TLS DTLS Alert Code", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
216 { 1, 12, "Last Bootstrapped", DATA_TYPE_TIME, RESOURCE_FILL },
217 { 1, 13, "Registration Priority Order", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
218 { 1, 14, "Initial Registration Delay Timer", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
219 { 1, 15, "Registration Failure Block", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
220 { 1, 16, "Bootstrap on Registration Failure", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
221 { 1, 17, "Communication Retry Count", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
222 { 1, 18, "Communication Retry Timer", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
223 { 1, 19, "Communication Sequence Delay Timer", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
224 { 1, 20, "Communication Sequence Retry Count", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
225 { 1, 21, "Trigger", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
226 { 1, 22, "Preferred Transport", DATA_TYPE_STRING, RESOURCE_FILL },
227 { 1, 23, "Mute Send", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
228 { 1, 24, "Alternate APN Links", DATA_TYPE_OBJLNK, RESOURCE_FILL },
229 { 1, 25, "Supported Server Versions", DATA_TYPE_STRING, RESOURCE_FILL },
230 { 1, 26, "Default Notification Mode", DATA_TYPE_INTEGER, RESOURCE_FILL },
231 { 1, 27, "Profile ID Hash Algorithm", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
233 /* Access Control (2) */
234 { 2, 0, "Object ID", DATA_TYPE_INTEGER, RESOURCE_FILL },
235 { 2, 1, "Object Instance ID", DATA_TYPE_INTEGER, RESOURCE_FILL },
236 { 2, 2, "ACL", DATA_TYPE_INTEGER, RESOURCE_FILL },
237 { 2, 3, "Access Control Owner", DATA_TYPE_INTEGER, RESOURCE_FILL },
239 /* Device (3) */
240 { 3, 0, "Manufacturer", DATA_TYPE_STRING, RESOURCE_FILL },
241 { 3, 1, "Model Number", DATA_TYPE_STRING, RESOURCE_FILL },
242 { 3, 2, "Serial Number", DATA_TYPE_STRING, RESOURCE_FILL },
243 { 3, 3, "Firmware Version", DATA_TYPE_STRING, RESOURCE_FILL },
244 { 3, 4, "Reboot", DATA_TYPE_NONE, RESOURCE_FILL },
245 { 3, 5, "Factory Reset", DATA_TYPE_NONE, RESOURCE_FILL },
246 { 3, 6, "Available Power Sources", DATA_TYPE_INTEGER, RESOURCE_FILL },
247 { 3, 7, "Power Source Voltage", DATA_TYPE_INTEGER, RESOURCE_FILL },
248 { 3, 8, "Power Source Current", DATA_TYPE_INTEGER, RESOURCE_FILL },
249 { 3, 9, "Battery Level", DATA_TYPE_INTEGER, RESOURCE_FILL },
250 { 3, 10, "Memory Free", DATA_TYPE_INTEGER, RESOURCE_FILL },
251 { 3, 11, "Error Code", DATA_TYPE_INTEGER, RESOURCE_FILL },
252 { 3, 12, "Reset Error Code", DATA_TYPE_NONE, RESOURCE_FILL },
253 { 3, 13, "Current Time", DATA_TYPE_TIME, RESOURCE_FILL },
254 { 3, 14, "UTC Offset", DATA_TYPE_STRING, RESOURCE_FILL },
255 { 3, 15, "Timezone", DATA_TYPE_STRING, RESOURCE_FILL },
256 { 3, 16, "Supported Binding and Modes", DATA_TYPE_STRING, RESOURCE_FILL },
257 { 3, 17, "Device Type", DATA_TYPE_STRING, RESOURCE_FILL },
258 { 3, 18, "Hardware Version", DATA_TYPE_STRING, RESOURCE_FILL },
259 { 3, 19, "Software Version", DATA_TYPE_STRING, RESOURCE_FILL },
260 { 3, 20, "Battery Status", DATA_TYPE_INTEGER, RESOURCE_FILL },
261 { 3, 21, "Memory Total", DATA_TYPE_INTEGER, RESOURCE_FILL },
262 { 3, 22, "ExtDevInfo", DATA_TYPE_OBJLNK, RESOURCE_FILL },
264 /* Connectivity Monitoring (4) */
265 { 4, 0, "Network Bearer", DATA_TYPE_INTEGER, RESOURCE_FILL },
266 { 4, 1, "Available Network Bearer", DATA_TYPE_INTEGER, RESOURCE_FILL },
267 { 4, 2, "Radio Signal Strength", DATA_TYPE_INTEGER, RESOURCE_FILL },
268 { 4, 3, "Link Quality", DATA_TYPE_INTEGER, RESOURCE_FILL },
269 { 4, 4, "IP Addresses", DATA_TYPE_STRING, RESOURCE_FILL },
270 { 4, 5, "Router IP Addresses", DATA_TYPE_STRING, RESOURCE_FILL },
271 { 4, 6, "Link Utilization", DATA_TYPE_INTEGER, RESOURCE_FILL },
272 { 4, 7, "APN", DATA_TYPE_STRING, RESOURCE_FILL },
273 { 4, 8, "Cell ID", DATA_TYPE_INTEGER, RESOURCE_FILL },
274 { 4, 9, "SMNC", DATA_TYPE_INTEGER, RESOURCE_FILL },
275 { 4, 10, "SMCC", DATA_TYPE_INTEGER, RESOURCE_FILL },
276 { 4, 11, "SignalSNR", DATA_TYPE_INTEGER, RESOURCE_FILL },
277 { 4, 12, "Location Area Code", DATA_TYPE_INTEGER, RESOURCE_FILL },
278 { 4, 13, "Coverage Enhancement Level", DATA_TYPE_INTEGER, RESOURCE_FILL },
280 /* Firmware Update (5) */
281 { 5, 0, "Package", DATA_TYPE_OPAQUE, RESOURCE_FILL },
282 { 5, 1, "Package URI", DATA_TYPE_STRING, RESOURCE_FILL },
283 { 5, 2, "Update", DATA_TYPE_NONE, RESOURCE_FILL },
284 { 5, 3, "State", DATA_TYPE_INTEGER, RESOURCE_FILL },
285 /* { 5, 4, "", DATA_TYPE_NONE, RESOURCE_FILL }, */
286 { 5, 5, "Update Result", DATA_TYPE_INTEGER, RESOURCE_FILL },
287 { 5, 6, "PkgName", DATA_TYPE_STRING, RESOURCE_FILL },
288 { 5, 7, "PkgVersion", DATA_TYPE_STRING, RESOURCE_FILL },
289 { 5, 8, "Firmware Update Protocol Support", DATA_TYPE_INTEGER, RESOURCE_FILL },
290 { 5, 9, "Firmware Update Delivery Method", DATA_TYPE_INTEGER, RESOURCE_FILL },
291 { 5, 10, "Cancel", DATA_TYPE_NONE, RESOURCE_FILL },
292 { 5, 11, "Severity", DATA_TYPE_INTEGER, RESOURCE_FILL },
293 { 5, 12, "Last State Change Time", DATA_TYPE_TIME, RESOURCE_FILL },
294 { 5, 13, "Maximum Defer Period", DATA_TYPE_UNSIGNED_INTEGER, RESOURCE_FILL },
295 { 5, 14, "Automatic Upgrade at Download", DATA_TYPE_BOOLEAN, RESOURCE_FILL },
297 /* Location (6) */
298 { 6, 0, "Latitude", DATA_TYPE_FLOAT, RESOURCE_FILL },
299 { 6, 1, "Longitude", DATA_TYPE_FLOAT, RESOURCE_FILL },
300 { 6, 2, "Altitude", DATA_TYPE_FLOAT, RESOURCE_FILL },
301 { 6, 3, "Radius", DATA_TYPE_FLOAT, RESOURCE_FILL },
302 { 6, 4, "Velocity", DATA_TYPE_OPAQUE, RESOURCE_FILL },
303 { 6, 5, "Timestamp", DATA_TYPE_TIME, RESOURCE_FILL },
304 { 6, 6, "Speed", DATA_TYPE_FLOAT, RESOURCE_FILL },
306 /* Connectivity Statistics (7) */
307 { 7, 0, "SMS Tx Counter", DATA_TYPE_INTEGER, RESOURCE_FILL },
308 { 7, 1, "SMS Rx Counter", DATA_TYPE_INTEGER, RESOURCE_FILL },
309 { 7, 2, "Tx Data", DATA_TYPE_INTEGER, RESOURCE_FILL },
310 { 7, 3, "Rx Data", DATA_TYPE_INTEGER, RESOURCE_FILL },
311 { 7, 4, "Max Message Size", DATA_TYPE_INTEGER, RESOURCE_FILL },
312 { 7, 5, "Average Message Size", DATA_TYPE_INTEGER, RESOURCE_FILL },
313 { 7, 6, "Start", DATA_TYPE_NONE, RESOURCE_FILL },
314 { 7, 7, "Stop", DATA_TYPE_NONE, RESOURCE_FILL },
315 { 7, 8, "Collection Period", DATA_TYPE_INTEGER, RESOURCE_FILL },
317 /* OSCORE (21) */
318 { 21, 0, "Master Secret", DATA_TYPE_STRING, RESOURCE_FILL },
319 { 21, 1, "Sender ID", DATA_TYPE_STRING, RESOURCE_FILL },
320 { 21, 2, "Recipient ID", DATA_TYPE_STRING, RESOURCE_FILL },
321 { 21, 3, "AEAD Algorithm", DATA_TYPE_INTEGER, RESOURCE_FILL },
322 { 21, 4, "HMAC Algorithm", DATA_TYPE_INTEGER, RESOURCE_FILL },
323 { 21, 5, "Master Salt", DATA_TYPE_STRING, RESOURCE_FILL },
324 { 21, 6, "ID Context", DATA_TYPE_OPAQUE, RESOURCE_FILL },
326 static const unsigned num_lwm2m_oma_resources = array_length(lwm2m_oma_resources);
328 typedef struct _lwm2m_allocated_fields_t {
329 hf_register_info *hf;
330 unsigned hf_size;
331 GArray *ett;
332 lwm2m_resource_t *float_resources;
333 unsigned num_float_resources;
334 } lwm2m_allocated_fields_t;
336 static lwm2m_allocated_fields_t oma_allocated_fields;
337 static lwm2m_allocated_fields_t uat_allocated_fields;
339 /* LwM2M Objects defined by User */
340 static lwm2m_object_name_t *lwm2m_uat_object_names;
341 static unsigned num_lwm2m_uat_object_names;
342 static lwm2m_resource_t *lwm2m_uat_resources;
343 static unsigned num_lwm2m_uat_resources;
345 static bool lwm2m_object_name_update_cb(void *record, char **error)
347 lwm2m_object_name_t *rec = (lwm2m_object_name_t *)record;
349 if (rec->name == NULL) {
350 *error = g_strdup("Object Name can't be empty");
351 return false;
354 g_strstrip(rec->name);
355 if (rec->name[0] == 0) {
356 *error = g_strdup("Object Name can't be empty");
357 return false;
360 *error = NULL;
361 return true;
364 static void *lwm2m_object_name_copy_cb(void *dest, const void *source, size_t len _U_)
366 const lwm2m_object_name_t *s = (const lwm2m_object_name_t *)source;
367 lwm2m_object_name_t *d = (lwm2m_object_name_t *)dest;
369 d->object_id = s->object_id;
370 d->name = g_strdup(s->name);
372 return d;
375 static void lwm2m_object_name_free_cb(void *record)
377 lwm2m_object_name_t *rec = (lwm2m_object_name_t *)record;
379 g_free(rec->name);
382 UAT_DEC_CB_DEF(object_name, object_id, lwm2m_object_name_t)
383 UAT_CSTRING_CB_DEF(object_name, name, lwm2m_object_name_t)
385 static bool lwm2m_resource_update_cb(void *record, char **error)
387 lwm2m_resource_t *rec = (lwm2m_resource_t *)record;
388 char c;
390 if (rec->name == NULL) {
391 *error = g_strdup("Resource Name can't be empty");
392 return false;
395 g_strstrip(rec->name);
396 if (rec->name[0] == 0) {
397 *error = g_strdup("Resource Name can't be empty");
398 return false;
401 g_free(rec->field_name);
402 rec->field_name = g_ascii_strdown(rec->name, -1);
403 for (size_t i = 0; i < strlen(rec->field_name); i++) {
404 if (rec->field_name[i] == ' ' || rec->field_name[i] == '.') {
405 rec->field_name[i] = '_';
409 /* Check for invalid characters (to avoid asserting out when registering the field). */
410 c = proto_check_field_name(rec->field_name);
411 if (c) {
412 *error = ws_strdup_printf("Resource Name can't contain '%c'", c);
413 return false;
416 *error = NULL;
417 return true;
420 static void *lwm2m_resource_copy_cb(void *dest, const void *source, size_t len _U_)
422 const lwm2m_resource_t *s = (const lwm2m_resource_t *)source;
423 lwm2m_resource_t *d = (lwm2m_resource_t *)dest;
425 d->object_id = s->object_id;
426 d->resource_id = s->resource_id;
427 d->name = g_strdup(s->name);
428 d->field_name = g_strdup(s->field_name);
429 d->data_type = s->data_type;
431 return d;
434 static void lwm2m_resource_free_cb(void *record)
436 lwm2m_resource_t *rec = (lwm2m_resource_t *)record;
438 g_free(rec->name);
439 g_free(rec->field_name);
442 static void lwm2m_add_resource(lwm2m_resource_t *resource, hf_register_info *hf, bool float_as_double)
444 char *resource_abbrev;
445 int *hf_id;
447 hf_id = g_new(int,1);
448 *hf_id = -1;
450 if (resource->field_name) {
451 resource_abbrev = g_strdup(resource->field_name);
452 } else {
453 resource_abbrev = g_ascii_strdown(resource->name, -1);
454 for (size_t i = 0; i < strlen(resource_abbrev); i++) {
455 if (resource_abbrev[i] == ' ' || resource_abbrev[i] == '.') {
456 resource_abbrev[i] = '_';
461 resource->hf_id = hf_id;
462 resource->ett_id = -1;
464 hf->p_id = hf_id;
465 hf->hfinfo.name = g_strdup(resource->name);
466 hf->hfinfo.abbrev = ws_strdup_printf("lwm2mtlv.resource.%s", resource_abbrev);
467 g_free (resource_abbrev);
469 switch (resource->data_type) {
470 case DATA_TYPE_STRING:
471 case DATA_TYPE_CORELNK:
472 hf->hfinfo.display = BASE_NONE;
473 hf->hfinfo.type = FT_STRING;
474 break;
475 case DATA_TYPE_INTEGER:
476 hf->hfinfo.display = BASE_DEC;
477 hf->hfinfo.type = FT_INT64;
478 break;
479 case DATA_TYPE_UNSIGNED_INTEGER:
480 hf->hfinfo.display = BASE_DEC;
481 hf->hfinfo.type = FT_UINT64;
482 break;
483 case DATA_TYPE_FLOAT:
484 hf->hfinfo.display = BASE_NONE;
485 hf->hfinfo.type = (float_as_double ? FT_DOUBLE : FT_FLOAT);
486 break;
487 case DATA_TYPE_BOOLEAN:
488 hf->hfinfo.display = BASE_DEC;
489 hf->hfinfo.type = FT_BOOLEAN;
490 break;
491 case DATA_TYPE_TIME:
492 hf->hfinfo.display = ABSOLUTE_TIME_LOCAL;
493 hf->hfinfo.type = FT_ABSOLUTE_TIME;
494 break;
495 case DATA_TYPE_OPAQUE:
496 case DATA_TYPE_OBJLNK:
497 default:
498 hf->hfinfo.display = BASE_NONE;
499 hf->hfinfo.type = FT_BYTES;
500 break;
502 hf->hfinfo.strings = NULL;
503 hf->hfinfo.bitmask = 0;
504 hf->hfinfo.blurb = NULL;
505 HFILL_INIT(*hf);
508 static void lwm2m_allocate_fields(lwm2m_allocated_fields_t *fields, lwm2m_resource_t *lwm2m_resources, unsigned num_lwm2m_resources)
510 unsigned resource_index;
512 fields->num_float_resources = 0;
513 for (unsigned i = 0; i < num_lwm2m_resources; i++) {
514 if (lwm2m_resources[i].data_type == DATA_TYPE_FLOAT) {
515 fields->num_float_resources++;
519 fields->hf_size = num_lwm2m_resources + fields->num_float_resources;
520 fields->hf = g_new0(hf_register_info, fields->hf_size);
521 fields->ett = g_array_new(true, true, sizeof(int*));
522 fields->float_resources = g_new0(lwm2m_resource_t, fields->num_float_resources);
524 resource_index = 0;
525 for (unsigned i = 0; i < num_lwm2m_resources; i++) {
526 int *ettp = &(lwm2m_resources[i].ett_id);
527 lwm2m_add_resource(&lwm2m_resources[i], &fields->hf[i], false);
528 g_array_append_val(fields->ett, ettp);
530 /* 8 bytes Float is handled as Double, allocate a separate resource for FT_DOUBLE */
531 if (lwm2m_resources[i].data_type == DATA_TYPE_FLOAT) {
532 unsigned hf_index = num_lwm2m_resources + resource_index;
533 memcpy(&fields->float_resources[resource_index], &lwm2m_resources[i], sizeof(lwm2m_resource_t));
534 lwm2m_add_resource(&fields->float_resources[resource_index++], &fields->hf[hf_index], true);
538 proto_register_field_array(proto_lwm2mtlv, fields->hf, fields->hf_size);
539 proto_register_subtree_array((int**)(void*)fields->ett->data, fields->ett->len);
541 resource_index = 0;
542 for (unsigned i = 0; i < num_lwm2m_resources; i++) {
543 /* Reuse the same ETT for Float and Double resources */
544 if (lwm2m_resources[i].data_type == DATA_TYPE_FLOAT) {
545 fields->float_resources[resource_index++].ett_id = lwm2m_resources[i].ett_id;
550 static const lwm2m_resource_t *lwm2m_search_float_resources(unsigned object_id, unsigned resource_id,
551 const lwm2m_allocated_fields_t *fields)
553 const lwm2m_resource_t *resource = NULL;
555 for (unsigned i = 0; i < fields->num_float_resources; i++) {
556 if ((object_id == fields->float_resources[i].object_id) &&
557 (resource_id == fields->float_resources[i].resource_id))
559 resource = &fields->float_resources[i];
560 break;
564 return resource;
567 static const lwm2m_resource_t *lwm2m_search_fields(unsigned object_id, unsigned resource_id, unsigned length_of_value,
568 const lwm2m_allocated_fields_t *fields,
569 const lwm2m_resource_t *lwm2m_resources, unsigned num_lwm2m_resources)
571 const lwm2m_resource_t *resource = NULL;
573 for (unsigned i = 0; i < num_lwm2m_resources; i++) {
574 if ((object_id == lwm2m_resources[i].object_id) &&
575 (resource_id == lwm2m_resources[i].resource_id))
577 /* 8 bytes Float is handled as Double, lookup the FT_DOUBLE resource */
578 if (length_of_value == 8 && lwm2m_resources[i].data_type == DATA_TYPE_FLOAT) {
579 resource = lwm2m_search_float_resources(object_id, resource_id, fields);
580 } else {
581 resource = &lwm2m_resources[i];
583 break;
587 return resource;
590 static void lwm2m_free_fields(lwm2m_allocated_fields_t *fields)
592 if (fields->hf) {
593 /* Deregister all fields */
594 for (unsigned i = 0; i < fields->hf_size; i++) {
595 proto_deregister_field(proto_lwm2mtlv, *(fields->hf[i].p_id));
596 g_free (fields->hf[i].p_id);
599 proto_add_deregistered_data(fields->hf);
600 fields->hf = NULL;
601 fields->hf_size = 0;
604 if (fields->ett) {
605 g_array_free(fields->ett, true);
606 fields->ett = NULL;
609 if (fields->float_resources) {
610 g_free(fields->float_resources);
611 fields->float_resources = NULL;
612 fields->num_float_resources = 0;
616 static void lwm2m_resource_post_update_cb(void)
618 lwm2m_free_fields(&uat_allocated_fields);
620 if (num_lwm2m_uat_resources) {
621 lwm2m_allocate_fields(&uat_allocated_fields, lwm2m_uat_resources, num_lwm2m_uat_resources);
625 static void lwm2m_resource_reset_cb(void)
627 lwm2m_free_fields(&uat_allocated_fields);
630 static int64_t
631 decodeVariableInt(tvbuff_t *tvb, const int offset, const unsigned length)
633 switch(length)
635 case 1:
636 return tvb_get_int8(tvb, offset);
637 case 2:
638 return tvb_get_ntohis(tvb, offset);
639 case 3:
640 return tvb_get_ntohi24(tvb, offset);
641 case 4:
642 return tvb_get_ntohil(tvb, offset);
643 case 5:
644 return tvb_get_ntohi40(tvb, offset);
645 case 6:
646 return tvb_get_ntohi48(tvb, offset);
647 case 7:
648 return tvb_get_ntohi56(tvb, offset);
649 case 8:
650 return tvb_get_ntohi64(tvb, offset);
651 default:
652 return 0;
656 UAT_DEC_CB_DEF(resource, object_id, lwm2m_resource_t)
657 UAT_DEC_CB_DEF(resource, resource_id, lwm2m_resource_t)
658 UAT_CSTRING_CB_DEF(resource, name, lwm2m_resource_t)
659 UAT_VS_DEF(resource, data_type, lwm2m_resource_t, unsigned, DATA_TYPE_NONE, "None")
661 static void
662 addTlvHeaderElements(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element)
664 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_type_type, tvb, 0, 1, ENC_BIG_ENDIAN);
665 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_type_length_of_identifier, tvb, 0, 1, ENC_BIG_ENDIAN);
666 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_type_length_of_length, tvb, 0, 1, ENC_BIG_ENDIAN);
667 if ( element->length_of_length == 0 ) {
668 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_type_length, tvb, 0, 1, ENC_BIG_ENDIAN);
669 } else {
670 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_type_ignored, tvb, 0, 1, ENC_BIG_ENDIAN);
673 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_identifier, tvb, 1, element->length_of_identifier, ENC_BIG_ENDIAN);
675 if ( element->length_of_length > 0 ) {
676 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_length, tvb, 1+element->length_of_identifier, element->length_of_length, ENC_BIG_ENDIAN);
680 static void
681 addTlvHeaderTree(tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element)
683 proto_item *item = NULL;
684 proto_tree *header_tree = NULL;
686 unsigned valueOffset = 1 + element->length_of_identifier + element->length_of_length;
688 item = proto_tree_add_item(tlv_tree, hf_lwm2mtlv_header, tvb, 0, valueOffset, ENC_NA);
689 header_tree = proto_item_add_subtree(item, ett_lwm2mtlv_header);
690 addTlvHeaderElements(tvb, header_tree, element);
693 static proto_tree*
694 addElementTree(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, const lwm2m_resource_t *resource)
696 proto_item *item = NULL;
697 char *identifier = NULL;
698 int ett_id;
700 if (resource) {
701 identifier = wmem_strdup_printf(pinfo->pool, "[%02u] %s", element->identifier, resource->name);
702 } else {
703 identifier = wmem_strdup_printf(pinfo->pool, "[%02u]", element->identifier);
706 switch ( element->type )
708 case OBJECT_INSTANCE:
709 item = proto_tree_add_item(tlv_tree, hf_lwm2mtlv_object_instance, tvb, 0, element->totalLength, ENC_NA);
710 proto_item_append_text(item, " %02u", element->identifier);
711 return proto_item_add_subtree(item, ett_lwm2mtlv_object_instance);
713 case RESOURCE_INSTANCE:
714 item = proto_tree_add_item(tlv_tree, hf_lwm2mtlv_resource_instance, tvb, 0, element->totalLength, ENC_NA);
715 proto_item_set_text(item, "%02u", element->identifier);
716 return proto_item_add_subtree(item, ett_lwm2mtlv_resource_instance);
718 case RESOURCE_ARRAY:
719 ett_id = resource ? resource->ett_id : ett_lwm2mtlv_resource_array;
720 item = proto_tree_add_item(tlv_tree, hf_lwm2mtlv_resource_array, tvb, 0, element->totalLength, ENC_NA);
721 proto_item_set_text(item, "%s", identifier);
722 return proto_item_add_subtree(item, ett_id);
724 case RESOURCE:
725 ett_id = resource ? resource->ett_id : ett_lwm2mtlv_resource;
726 item = proto_tree_add_item(tlv_tree, hf_lwm2mtlv_resource, tvb, 0, element->totalLength, ENC_NA);
727 proto_item_set_text(item, "%s", identifier);
728 return proto_item_add_subtree(item, ett_id);
730 return NULL;
733 static void
734 addValueInterpretations(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, const lwm2m_resource_t *resource)
736 unsigned valueOffset;
737 if ( element->length_of_value == 0 ) return;
739 valueOffset = 1 + element->length_of_identifier + element->length_of_length;
741 if (resource && resource->data_type != DATA_TYPE_NONE) {
742 switch (resource->data_type) {
743 case DATA_TYPE_STRING:
744 case DATA_TYPE_CORELNK:
746 const uint8_t *strval;
747 proto_tree_add_item_ret_string(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_UTF_8, pinfo->pool, &strval);
748 proto_item_append_text(tlv_tree, ": %s", format_text(pinfo->pool, strval, strlen(strval)));
749 break;
751 case DATA_TYPE_INTEGER:
752 proto_tree_add_item(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
753 proto_item_append_text(tlv_tree, ": %" PRId64, decodeVariableInt(tvb, valueOffset, element->length_of_value));
754 break;
755 case DATA_TYPE_UNSIGNED_INTEGER:
757 uint64_t value;
758 proto_tree_add_item_ret_uint64(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN, &value);
759 proto_item_append_text(tlv_tree, ": %" PRIu64, value);
760 break;
762 case DATA_TYPE_FLOAT:
763 proto_tree_add_item(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
764 if (element->length_of_value == 4) {
765 proto_item_append_text(tlv_tree, ": %." G_STRINGIFY(FLT_DIG) "g", tvb_get_ieee_float(tvb, valueOffset, ENC_BIG_ENDIAN));
766 } else {
767 proto_item_append_text(tlv_tree, ": %." G_STRINGIFY(DBL_DIG) "g", tvb_get_ieee_double(tvb, valueOffset, ENC_BIG_ENDIAN));
769 break;
770 case DATA_TYPE_BOOLEAN:
772 bool boolval;
773 proto_tree_add_item_ret_boolean(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN, &boolval);
774 proto_item_append_text(tlv_tree, ": %s", boolval ? "True" : "False");
775 break;
777 case DATA_TYPE_TIME:
779 nstime_t ts;
780 ts.secs = (time_t)decodeVariableInt(tvb, valueOffset, element->length_of_value);
781 ts.nsecs = 0;
782 proto_tree_add_time(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, &ts);
783 proto_item_append_text(tlv_tree, ": %s", abs_time_to_str(pinfo->pool, &ts, ABSOLUTE_TIME_LOCAL, false));
784 break;
786 case DATA_TYPE_OBJLNK:
788 uint16_t lnk1 = tvb_get_uint16(tvb, valueOffset, ENC_BIG_ENDIAN);
789 uint16_t lnk2 = tvb_get_uint16(tvb, valueOffset + 2, ENC_BIG_ENDIAN);
790 proto_tree_add_bytes_format(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, NULL, "%u:%u", lnk1, lnk2);
791 proto_item_append_text(tlv_tree, ": %u:%u", lnk1, lnk2);
792 break;
794 case DATA_TYPE_OPAQUE:
795 default:
797 proto_item *ti = proto_tree_add_item(tlv_tree, *resource->hf_id, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
799 proto_item_append_text(tlv_tree, ": %s", tvb_bytes_to_str(pinfo->pool, tvb, valueOffset, element->length_of_value));
801 if (resource->object_id == 6 && resource->resource_id == 4) {
802 proto_tree *pt = proto_item_add_subtree(ti, ett_lwm2mtlv_location_velocity);
803 dissect_description_of_velocity(tvb, pt, pinfo, valueOffset, element->length_of_value, NULL, 0);
805 break;
808 } else {
809 uint8_t *str = tvb_get_string_enc(pinfo->pool, tvb, valueOffset, element->length_of_value, ENC_UTF_8);
810 if (isprint_utf8_string(str, element->length_of_value)) {
811 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_string, tvb, valueOffset, element->length_of_value, ENC_UTF_8);
812 } else {
813 str = tvb_bytes_to_str(pinfo->pool, tvb, valueOffset, element->length_of_value);
815 proto_item_append_text(tlv_tree, ": %s", str);
817 switch(element->length_of_value) {
818 case 0x01:
819 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
820 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_unsigned_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
821 if (tvb_get_uint8(tvb, valueOffset) < 2) {
822 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_boolean, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
824 break;
825 case 0x02:
826 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
827 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_unsigned_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
828 break;
829 case 0x04:
830 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
831 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_unsigned_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
832 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_float, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
833 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_timestamp, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
834 break;
835 case 0x08:
836 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
837 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_unsigned_integer, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
838 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_double, tvb, valueOffset, element->length_of_value, ENC_BIG_ENDIAN);
839 /* apparently, wireshark does not deal well with 8 bytes. */
840 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value_timestamp, tvb, valueOffset+4, element->length_of_value-4, ENC_BIG_ENDIAN);
841 break;
846 static void
847 // NOLINTNEXTLINE(misc-no-recursion)
848 addValueTree(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, int object_id, int resource_id, const lwm2m_resource_t *resource)
850 unsigned valueOffset = 1 + element->length_of_identifier + element->length_of_length;
852 if (resource && (element->type == RESOURCE || element->type == RESOURCE_ARRAY)) {
853 proto_item *ti = proto_tree_add_string(tlv_tree, hf_lwm2mtlv_resource_name, tvb, 0, 0, resource->name);
854 proto_item_set_generated(ti);
857 if ( element->type == RESOURCE || element->type == RESOURCE_INSTANCE ) {
858 proto_tree_add_item(tlv_tree, hf_lwm2mtlv_value, tvb, valueOffset, element->length_of_value, ENC_NA);
859 addValueInterpretations(pinfo, tvb, tlv_tree, element, resource);
860 } else {
861 tvbuff_t* sub = tvb_new_subset_length(tvb, valueOffset, element->length_of_value);
862 parseArrayOfElements(pinfo, sub, tlv_tree, object_id, resource_id);
866 static void
867 // NOLINTNEXTLINE(misc-no-recursion)
868 addTlvElement(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tlv_tree, lwm2mElement_t *element, int object_id, int resource_id)
870 proto_tree *element_tree = NULL;
871 const lwm2m_resource_t *resource = NULL;
873 if (object_id != -1 && resource_id != -1) {
874 /* First search user configured objects */
875 resource = lwm2m_search_fields(object_id, resource_id, element->length_of_value,
876 &uat_allocated_fields, lwm2m_uat_resources, num_lwm2m_uat_resources);
878 if (resource == NULL) {
879 /* Then search OMA objects */
880 resource = lwm2m_search_fields(object_id, resource_id, element->length_of_value,
881 &oma_allocated_fields, lwm2m_oma_resources, num_lwm2m_oma_resources);
885 element_tree = addElementTree(pinfo, tvb, tlv_tree, element, resource);
886 addTlvHeaderTree(tvb, element_tree, element);
887 addValueTree(pinfo, tvb, element_tree, element, object_id, resource_id, resource);
890 static uint64_t
891 decodeVariableUInt(tvbuff_t *tvb, const int offset, const unsigned length)
893 switch(length)
895 case 1:
896 return tvb_get_uint8(tvb, offset);
897 case 2:
898 return tvb_get_ntohs(tvb, offset);
899 case 3:
900 return tvb_get_ntoh24(tvb, offset);
901 case 4:
902 return tvb_get_ntohl(tvb, offset);
903 case 5:
904 return tvb_get_ntoh40(tvb, offset);
905 case 6:
906 return tvb_get_ntoh48(tvb, offset);
907 case 7:
908 return tvb_get_ntoh56(tvb, offset);
909 case 8:
910 return tvb_get_ntoh64(tvb, offset);
911 default:
912 return 0;
916 static unsigned parseTLVHeader(tvbuff_t *tvb, lwm2mElement_t *element)
918 unsigned type_field = tvb_get_uint8(tvb, 0);
919 element->type = (( type_field >> 6 ) & 0x03 );
920 element->length_of_identifier = (( type_field >> 5 ) & 0x01 ) + 1;
921 element->length_of_length = (( type_field >> 3 ) & 0x03 );
922 element->length_of_value = (( type_field >> 0 ) & 0x07 );
924 /* It is ok to shorten identifier and length_of_value, they are never more than 24 bits long */
925 element->identifier = (unsigned) decodeVariableUInt(tvb, 1, element->length_of_identifier);
926 if ( element->length_of_length > 0 ) {
927 element->length_of_value = (unsigned) decodeVariableUInt(tvb, 1 + element->length_of_identifier, element->length_of_length);
930 element->totalLength = 1 + element->length_of_identifier + element->length_of_length + element->length_of_value;
932 return element->totalLength;
935 // NOLINTNEXTLINE(misc-no-recursion)
936 static void parseArrayOfElements(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tlv_tree, int object_id, int resource_id)
938 unsigned length;
939 unsigned offset = 0;
940 unsigned elementLength = 0;
941 unsigned element_count = 0;
942 lwm2mElement_t element;
944 length = tvb_reported_length(tvb);
946 increment_dissection_depth(pinfo);
947 while ( length > 0 ) {
948 tvbuff_t* sub = tvb_new_subset_length(tvb, offset, length);
949 elementLength = parseTLVHeader(sub, &element);
950 if (element.type == RESOURCE || element.type == RESOURCE_ARRAY) {
951 resource_id = (int)element.identifier;
953 addTlvElement(pinfo, sub, tlv_tree, &element, object_id, resource_id);
954 element_count++;
956 length -= elementLength;
957 offset += elementLength;
958 if ( elementLength == 0 )
960 break;
963 decrement_dissection_depth(pinfo);
965 proto_item_append_text(tlv_tree, " (%u element%s)", element_count, plurality(element_count, "", "s"));
968 static int
969 dissect_lwm2mtlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
971 proto_tree* lwm2mtlv_tree;
972 proto_item* lwm2mtlv_item;
973 media_content_info_t *content_info = (media_content_info_t *) data;
974 int object_id = -1;
975 int resource_id = -1;
977 if (content_info && content_info->media_str && content_info->media_str[0]) {
978 char **ids = wmem_strsplit(pinfo->pool, content_info->media_str, "/", 5);
980 /* URI path is defined as:
981 * ids[1] = Object ID
982 * ids[2] = Object Instance
983 * ids[3] = Resource ID
984 * ids[4] = Resource Instance
986 if (ids && ids[0] && ids[1]) {
987 object_id = (int)strtol(ids[1], NULL, 10);
989 if (ids[2] && ids[3]) {
990 resource_id = (int)strtol(ids[1], NULL, 10);
995 if (tree) { /* we are being asked for details */
996 lwm2mtlv_item = proto_tree_add_item(tree, proto_lwm2mtlv, tvb, 0, -1, ENC_NA);
997 lwm2mtlv_tree = proto_item_add_subtree(lwm2mtlv_item, ett_lwm2mtlv);
999 if (object_id != -1) {
1000 const char *object_name = NULL;
1002 for (unsigned i = 0; i < num_lwm2m_uat_object_names; i++) {
1003 if ((unsigned)object_id == lwm2m_uat_object_names[i].object_id) {
1004 object_name = lwm2m_uat_object_names[i].name;
1005 break;
1009 if (!object_name) {
1010 object_name = val_to_str_const(object_id, lwm2m_oma_objects, "");
1013 if (object_name && object_name[0]) {
1014 proto_item *ti = proto_tree_add_string(lwm2mtlv_tree, hf_lwm2mtlv_object_name, tvb, 0, 0, object_name);
1015 proto_item_set_generated(ti);
1016 proto_item_append_text(lwm2mtlv_item, ", %s", object_name);
1020 parseArrayOfElements(pinfo, tvb, lwm2mtlv_tree, object_id, resource_id);
1022 return tvb_captured_length(tvb);
1025 static void lwm2m_shutdown_routine(void)
1027 lwm2m_free_fields(&oma_allocated_fields);
1030 void proto_register_lwm2mtlv(void)
1032 static hf_register_info hf[] = {
1033 { &hf_lwm2mtlv_object_name,
1034 { "Object Name", "lwm2mtlv.object_name",
1035 FT_STRING, BASE_NONE, NULL, 0,
1036 NULL, HFILL }
1038 { &hf_lwm2mtlv_resource_name,
1039 { "Resource Name", "lwm2mtlv.resource_name",
1040 FT_STRING, BASE_NONE, NULL, 0,
1041 NULL, HFILL }
1043 { &hf_lwm2mtlv_header,
1044 { "TLV header", "lwm2mtlv.header",
1045 FT_NONE, BASE_NONE, NULL, 0x0,
1046 NULL, HFILL }
1048 { &hf_lwm2mtlv_type_type,
1049 { "Type of Identifier", "lwm2mtlv.type.type",
1050 FT_UINT8, BASE_DEC, VALS(identifiers), 0xC0,
1051 NULL, HFILL }
1053 { &hf_lwm2mtlv_type_length_of_identifier,
1054 { "Length of Identifier", "lwm2mtlv.type.loi",
1055 FT_UINT8, BASE_DEC, VALS(length_identifier), 0x20,
1056 NULL, HFILL }
1058 { &hf_lwm2mtlv_type_length_of_length,
1059 { "Length of Length", "lwm2mtlv.type.lol",
1060 FT_UINT8, BASE_DEC, VALS(length_type), 0x18,
1061 NULL, HFILL }
1063 { &hf_lwm2mtlv_type_length,
1064 { "Length", "lwm2mtlv.type.length",
1065 FT_UINT8, BASE_DEC, NULL, 0x07,
1066 NULL, HFILL }
1068 { &hf_lwm2mtlv_type_ignored,
1069 { "Ignored", "lwm2mtlv.type.ignored",
1070 FT_UINT8, BASE_DEC, NULL, 0x07,
1071 NULL, HFILL }
1073 { &hf_lwm2mtlv_identifier,
1074 { "Identifier", "lwm2mtlv.identifier",
1075 FT_UINT16, BASE_DEC, NULL, 0,
1076 NULL, HFILL }
1078 { &hf_lwm2mtlv_length,
1079 { "Length", "lwm2mtlv.length",
1080 FT_UINT32, BASE_DEC, NULL, 0,
1081 NULL, HFILL }
1083 { &hf_lwm2mtlv_value,
1084 { "Value", "lwm2mtlv.value",
1085 FT_BYTES, BASE_NONE, NULL, 0,
1086 NULL, HFILL }
1088 { &hf_lwm2mtlv_value_string,
1089 { "As String", "lwm2mtlv.value.string",
1090 FT_STRING, BASE_NONE, NULL, 0,
1091 NULL, HFILL }
1093 { &hf_lwm2mtlv_value_integer,
1094 { "As Integer", "lwm2mtlv.value.integer",
1095 FT_INT64, BASE_DEC, NULL, 0,
1096 NULL, HFILL }
1098 { &hf_lwm2mtlv_value_unsigned_integer,
1099 { "As Unsigned Integer", "lwm2mtlv.value.unsigned_integer",
1100 FT_UINT64, BASE_DEC, NULL, 0,
1101 NULL, HFILL }
1103 { &hf_lwm2mtlv_value_float,
1104 { "As Float", "lwm2mtlv.value.float",
1105 FT_FLOAT, BASE_NONE, NULL, 0,
1106 NULL, HFILL }
1108 { &hf_lwm2mtlv_value_double,
1109 { "As Double", "lwm2mtlv.value.double",
1110 FT_DOUBLE, BASE_NONE, NULL, 0,
1111 NULL, HFILL }
1113 { &hf_lwm2mtlv_value_boolean,
1114 { "As Boolean", "lwm2mtlv.value.boolean",
1115 FT_BOOLEAN, BASE_NONE, NULL, 0,
1116 NULL, HFILL }
1118 { &hf_lwm2mtlv_value_timestamp,
1119 { "As Timestamp", "lwm2mtlv.value.timestamp",
1120 FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
1121 NULL, HFILL }
1123 { &hf_lwm2mtlv_object_instance,
1124 { "Object Instance", "lwm2mtlv.object_instance",
1125 FT_NONE, BASE_NONE, NULL, 0,
1126 NULL, HFILL }
1128 { &hf_lwm2mtlv_resource_instance,
1129 { "Resource Instance", "lwm2mtlv.resource_instance",
1130 FT_NONE, BASE_NONE, NULL, 0,
1131 NULL, HFILL }
1133 { &hf_lwm2mtlv_resource_array,
1134 { "Resource Array", "lwm2mtlv.resource_array",
1135 FT_NONE, BASE_NONE, NULL, 0,
1136 NULL, HFILL }
1138 { &hf_lwm2mtlv_resource,
1139 { "Resource", "lwm2mtlv.resource",
1140 FT_NONE, BASE_NONE, NULL, 0,
1141 NULL, HFILL }
1145 static int* ett[] = {
1146 &ett_lwm2mtlv,
1147 &ett_lwm2mtlv_header,
1148 &ett_lwm2mtlv_resource,
1149 &ett_lwm2mtlv_resource_instance,
1150 &ett_lwm2mtlv_resource_array,
1151 &ett_lwm2mtlv_object_instance,
1152 &ett_lwm2mtlv_location_velocity
1155 static uat_field_t lwm2m_object_name_flds[] = {
1156 UAT_FLD_DEC(object_name, object_id, "Object ID", "Object ID"),
1157 UAT_FLD_CSTRING(object_name, name, "Object Name", "Object Name"),
1158 UAT_END_FIELDS
1161 static uat_field_t lwm2m_resource_flds[] = {
1162 UAT_FLD_DEC(resource, object_id, "Object ID", "Object ID"),
1163 UAT_FLD_DEC(resource, resource_id, "Resource ID", "Resource ID"),
1164 UAT_FLD_CSTRING(resource, name, "Resource Name", "Resource Name"),
1165 UAT_FLD_VS(resource, data_type, "Data Type", data_types, "Data Type"),
1166 UAT_END_FIELDS
1169 uat_t *object_name_uat = uat_new("User Object Names",
1170 sizeof(lwm2m_object_name_t),
1171 "lwm2m_object_names",
1172 true,
1173 &lwm2m_uat_object_names,
1174 &num_lwm2m_uat_object_names,
1175 UAT_AFFECTS_DISSECTION,
1176 "ChLwM2MResourceNames",
1177 lwm2m_object_name_copy_cb,
1178 lwm2m_object_name_update_cb,
1179 lwm2m_object_name_free_cb,
1180 NULL,
1181 NULL,
1182 lwm2m_object_name_flds);
1184 uat_t *resource_uat = uat_new("User Resource Names",
1185 sizeof(lwm2m_resource_t),
1186 "lwm2m_resource_names",
1187 true,
1188 &lwm2m_uat_resources,
1189 &num_lwm2m_uat_resources,
1190 UAT_AFFECTS_DISSECTION|UAT_AFFECTS_FIELDS,
1191 "ChLwM2MResourceNames",
1192 lwm2m_resource_copy_cb,
1193 lwm2m_resource_update_cb,
1194 lwm2m_resource_free_cb,
1195 lwm2m_resource_post_update_cb,
1196 lwm2m_resource_reset_cb,
1197 lwm2m_resource_flds);
1199 module_t *lwm2mtlv_module;
1201 /* Register our configuration options */
1202 proto_lwm2mtlv = proto_register_protocol ("Lightweight M2M TLV", "LwM2M-TLV","lwm2mtlv");
1204 proto_register_field_array(proto_lwm2mtlv, hf, array_length(hf));
1205 proto_register_subtree_array(ett, array_length(ett));
1207 lwm2mtlv_handle = register_dissector("lwm2mtlv", dissect_lwm2mtlv, proto_lwm2mtlv);
1209 /* Register the dissector shutdown function */
1210 register_shutdown_routine(lwm2m_shutdown_routine);
1212 lwm2mtlv_module = prefs_register_protocol(proto_lwm2mtlv, NULL);
1214 prefs_register_uat_preference(lwm2mtlv_module, "object_table",
1215 "Object Names",
1216 "User Object Names",
1217 object_name_uat);
1219 prefs_register_uat_preference(lwm2mtlv_module, "resource_table",
1220 "Resource Names",
1221 "User Resource Names",
1222 resource_uat);
1224 lwm2m_allocate_fields(&oma_allocated_fields, lwm2m_oma_resources, num_lwm2m_oma_resources);
1227 void
1228 proto_reg_handoff_lwm2mtlv(void)
1230 dissector_add_string("media_type", "application/vnd.oma.lwm2m+tlv", lwm2mtlv_handle);
1234 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1236 * Local variables:
1237 * c-basic-offset: 8
1238 * tab-width: 8
1239 * indent-tabs-mode: t
1240 * End:
1242 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1243 * :indentSize=8:tabSize=8:noTabs=false: