2 * Routines for LWM2M TLV dissection
4 * OMA LWM2M Specification: OMA-TS-LightweightM2M_Core-V1_1-20180710-A.pdf
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
20 #include <epan/packet.h>
21 #include <epan/to_str.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
;
71 RESOURCE_INSTANCE
= 1,
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" },
84 static const value_string length_identifier
[] = {
85 { 0x00, "1 byte identifier" },
86 { 0x01, "2 bytes identifier" },
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" },
101 unsigned length_of_identifier
;
102 unsigned length_of_length
;
103 unsigned length_of_value
;
106 unsigned totalLength
;
109 typedef struct _lwm2m_object_name_t
{
112 } lwm2m_object_name_t
;
114 typedef struct _lwm2m_resource_t
{
116 unsigned resource_id
;
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" },
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" },
160 { 4, "Connectivity Monitoring" },
161 { 5, "Firmware Update" },
163 { 7, "Connectivity Statistics" },
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
},
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
},
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
},
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
;
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");
354 g_strstrip(rec
->name
);
355 if (rec
->name
[0] == 0) {
356 *error
= g_strdup("Object Name can't be empty");
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
);
375 static void lwm2m_object_name_free_cb(void *record
)
377 lwm2m_object_name_t
*rec
= (lwm2m_object_name_t
*)record
;
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
;
390 if (rec
->name
== NULL
) {
391 *error
= g_strdup("Resource Name can't be empty");
395 g_strstrip(rec
->name
);
396 if (rec
->name
[0] == 0) {
397 *error
= g_strdup("Resource Name can't be empty");
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
);
412 *error
= ws_strdup_printf("Resource Name can't contain '%c'", c
);
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
;
434 static void lwm2m_resource_free_cb(void *record
)
436 lwm2m_resource_t
*rec
= (lwm2m_resource_t
*)record
;
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
;
447 hf_id
= g_new(int,1);
450 if (resource
->field_name
) {
451 resource_abbrev
= g_strdup(resource
->field_name
);
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;
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
;
475 case DATA_TYPE_INTEGER
:
476 hf
->hfinfo
.display
= BASE_DEC
;
477 hf
->hfinfo
.type
= FT_INT64
;
479 case DATA_TYPE_UNSIGNED_INTEGER
:
480 hf
->hfinfo
.display
= BASE_DEC
;
481 hf
->hfinfo
.type
= FT_UINT64
;
483 case DATA_TYPE_FLOAT
:
484 hf
->hfinfo
.display
= BASE_NONE
;
485 hf
->hfinfo
.type
= (float_as_double
? FT_DOUBLE
: FT_FLOAT
);
487 case DATA_TYPE_BOOLEAN
:
488 hf
->hfinfo
.display
= BASE_DEC
;
489 hf
->hfinfo
.type
= FT_BOOLEAN
;
492 hf
->hfinfo
.display
= ABSOLUTE_TIME_LOCAL
;
493 hf
->hfinfo
.type
= FT_ABSOLUTE_TIME
;
495 case DATA_TYPE_OPAQUE
:
496 case DATA_TYPE_OBJLNK
:
498 hf
->hfinfo
.display
= BASE_NONE
;
499 hf
->hfinfo
.type
= FT_BYTES
;
502 hf
->hfinfo
.strings
= NULL
;
503 hf
->hfinfo
.bitmask
= 0;
504 hf
->hfinfo
.blurb
= NULL
;
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
);
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
);
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
];
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
);
581 resource
= &lwm2m_resources
[i
];
590 static void lwm2m_free_fields(lwm2m_allocated_fields_t
*fields
)
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
);
605 g_array_free(fields
->ett
, true);
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
);
631 decodeVariableInt(tvbuff_t
*tvb
, const int offset
, const unsigned length
)
636 return tvb_get_int8(tvb
, offset
);
638 return tvb_get_ntohis(tvb
, offset
);
640 return tvb_get_ntohi24(tvb
, offset
);
642 return tvb_get_ntohil(tvb
, offset
);
644 return tvb_get_ntohi40(tvb
, offset
);
646 return tvb_get_ntohi48(tvb
, offset
);
648 return tvb_get_ntohi56(tvb
, offset
);
650 return tvb_get_ntohi64(tvb
, offset
);
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")
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
);
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
);
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
);
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
;
701 identifier
= wmem_strdup_printf(pinfo
->pool
, "[%02u] %s", element
->identifier
, resource
->name
);
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
);
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
);
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
);
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
)));
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
));
755 case DATA_TYPE_UNSIGNED_INTEGER
:
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
);
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
));
767 proto_item_append_text(tlv_tree
, ": %." G_STRINGIFY(DBL_DIG
) "g", tvb_get_ieee_double(tvb
, valueOffset
, ENC_BIG_ENDIAN
));
770 case DATA_TYPE_BOOLEAN
:
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");
780 ts
.secs
= (time_t)decodeVariableInt(tvb
, valueOffset
, element
->length_of_value
);
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));
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
);
794 case DATA_TYPE_OPAQUE
:
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);
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
);
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
) {
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
);
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
);
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
);
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
);
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
);
861 tvbuff_t
* sub
= tvb_new_subset_length(tvb
, valueOffset
, element
->length_of_value
);
862 parseArrayOfElements(pinfo
, sub
, tlv_tree
, object_id
, resource_id
);
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
);
891 decodeVariableUInt(tvbuff_t
*tvb
, const int offset
, const unsigned length
)
896 return tvb_get_uint8(tvb
, offset
);
898 return tvb_get_ntohs(tvb
, offset
);
900 return tvb_get_ntoh24(tvb
, offset
);
902 return tvb_get_ntohl(tvb
, offset
);
904 return tvb_get_ntoh40(tvb
, offset
);
906 return tvb_get_ntoh48(tvb
, offset
);
908 return tvb_get_ntoh56(tvb
, offset
);
910 return tvb_get_ntoh64(tvb
, offset
);
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
)
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
);
956 length
-= elementLength
;
957 offset
+= elementLength
;
958 if ( elementLength
== 0 )
963 decrement_dissection_depth(pinfo
);
965 proto_item_append_text(tlv_tree
, " (%u element%s)", element_count
, plurality(element_count
, "", "s"));
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
;
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:
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
;
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,
1038 { &hf_lwm2mtlv_resource_name
,
1039 { "Resource Name", "lwm2mtlv.resource_name",
1040 FT_STRING
, BASE_NONE
, NULL
, 0,
1043 { &hf_lwm2mtlv_header
,
1044 { "TLV header", "lwm2mtlv.header",
1045 FT_NONE
, BASE_NONE
, NULL
, 0x0,
1048 { &hf_lwm2mtlv_type_type
,
1049 { "Type of Identifier", "lwm2mtlv.type.type",
1050 FT_UINT8
, BASE_DEC
, VALS(identifiers
), 0xC0,
1053 { &hf_lwm2mtlv_type_length_of_identifier
,
1054 { "Length of Identifier", "lwm2mtlv.type.loi",
1055 FT_UINT8
, BASE_DEC
, VALS(length_identifier
), 0x20,
1058 { &hf_lwm2mtlv_type_length_of_length
,
1059 { "Length of Length", "lwm2mtlv.type.lol",
1060 FT_UINT8
, BASE_DEC
, VALS(length_type
), 0x18,
1063 { &hf_lwm2mtlv_type_length
,
1064 { "Length", "lwm2mtlv.type.length",
1065 FT_UINT8
, BASE_DEC
, NULL
, 0x07,
1068 { &hf_lwm2mtlv_type_ignored
,
1069 { "Ignored", "lwm2mtlv.type.ignored",
1070 FT_UINT8
, BASE_DEC
, NULL
, 0x07,
1073 { &hf_lwm2mtlv_identifier
,
1074 { "Identifier", "lwm2mtlv.identifier",
1075 FT_UINT16
, BASE_DEC
, NULL
, 0,
1078 { &hf_lwm2mtlv_length
,
1079 { "Length", "lwm2mtlv.length",
1080 FT_UINT32
, BASE_DEC
, NULL
, 0,
1083 { &hf_lwm2mtlv_value
,
1084 { "Value", "lwm2mtlv.value",
1085 FT_BYTES
, BASE_NONE
, NULL
, 0,
1088 { &hf_lwm2mtlv_value_string
,
1089 { "As String", "lwm2mtlv.value.string",
1090 FT_STRING
, BASE_NONE
, NULL
, 0,
1093 { &hf_lwm2mtlv_value_integer
,
1094 { "As Integer", "lwm2mtlv.value.integer",
1095 FT_INT64
, BASE_DEC
, NULL
, 0,
1098 { &hf_lwm2mtlv_value_unsigned_integer
,
1099 { "As Unsigned Integer", "lwm2mtlv.value.unsigned_integer",
1100 FT_UINT64
, BASE_DEC
, NULL
, 0,
1103 { &hf_lwm2mtlv_value_float
,
1104 { "As Float", "lwm2mtlv.value.float",
1105 FT_FLOAT
, BASE_NONE
, NULL
, 0,
1108 { &hf_lwm2mtlv_value_double
,
1109 { "As Double", "lwm2mtlv.value.double",
1110 FT_DOUBLE
, BASE_NONE
, NULL
, 0,
1113 { &hf_lwm2mtlv_value_boolean
,
1114 { "As Boolean", "lwm2mtlv.value.boolean",
1115 FT_BOOLEAN
, BASE_NONE
, NULL
, 0,
1118 { &hf_lwm2mtlv_value_timestamp
,
1119 { "As Timestamp", "lwm2mtlv.value.timestamp",
1120 FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_LOCAL
, NULL
, 0,
1123 { &hf_lwm2mtlv_object_instance
,
1124 { "Object Instance", "lwm2mtlv.object_instance",
1125 FT_NONE
, BASE_NONE
, NULL
, 0,
1128 { &hf_lwm2mtlv_resource_instance
,
1129 { "Resource Instance", "lwm2mtlv.resource_instance",
1130 FT_NONE
, BASE_NONE
, NULL
, 0,
1133 { &hf_lwm2mtlv_resource_array
,
1134 { "Resource Array", "lwm2mtlv.resource_array",
1135 FT_NONE
, BASE_NONE
, NULL
, 0,
1138 { &hf_lwm2mtlv_resource
,
1139 { "Resource", "lwm2mtlv.resource",
1140 FT_NONE
, BASE_NONE
, NULL
, 0,
1145 static int* ett
[] = {
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"),
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"),
1169 uat_t
*object_name_uat
= uat_new("User Object Names",
1170 sizeof(lwm2m_object_name_t
),
1171 "lwm2m_object_names",
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
,
1182 lwm2m_object_name_flds
);
1184 uat_t
*resource_uat
= uat_new("User Resource Names",
1185 sizeof(lwm2m_resource_t
),
1186 "lwm2m_resource_names",
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",
1216 "User Object Names",
1219 prefs_register_uat_preference(lwm2mtlv_module
, "resource_table",
1221 "User Resource Names",
1224 lwm2m_allocate_fields(&oma_allocated_fields
, lwm2m_oma_resources
, num_lwm2m_oma_resources
);
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
1239 * indent-tabs-mode: t
1242 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1243 * :indentSize=8:tabSize=8:noTabs=false: