/src/wireshark/epan/dissectors/packet-mqtt-sn.c
Line | Count | Source |
1 | | /* packet-mqtt-sn.c |
2 | | * |
3 | | * Routines for MQTT-SN v1.2 <http://mqtt.org> |
4 | | * https://mqtt.org/mqtt-specification/ |
5 | | * https://groups.oasis-open.org/higherlogic/ws/public/download/66091/MQTT-SN_spec_v1.2.pdf |
6 | | * |
7 | | * Copyright (c) 2015, Jan-Hendrik Bolte <jabolte@uni-osnabrueck.de> |
8 | | * Copyright (c) 2015, University of Osnabrueck |
9 | | * |
10 | | * Wireshark - Network traffic analyzer |
11 | | * By Gerald Combs <gerald@wireshark.org> |
12 | | * Copyright 1998 Gerald Combs |
13 | | * |
14 | | * SPDX-License-Identifier: GPL-2.0-or-later |
15 | | */ |
16 | | |
17 | | #include "config.h" |
18 | | #include <epan/packet.h> |
19 | | #include <epan/tfs.h> |
20 | | #include <wsutil/array.h> |
21 | | |
22 | | /* MQTT-SN message types. */ |
23 | 0 | #define MQTTSN_ADVERTISE 0x00 |
24 | 0 | #define MQTTSN_SEARCHGW 0x01 |
25 | 0 | #define MQTTSN_GWINFO 0x02 |
26 | | #define MQTTSN_RESERVED_03 0x03 |
27 | 0 | #define MQTTSN_CONNECT 0x04 |
28 | 0 | #define MQTTSN_CONNACK 0x05 |
29 | 0 | #define MQTTSN_WILLTOPICREQ 0x06 |
30 | 0 | #define MQTTSN_WILLTOPIC 0x07 |
31 | 0 | #define MQTTSN_WILLMSGREQ 0x08 |
32 | 0 | #define MQTTSN_WILLMSG 0x09 |
33 | 0 | #define MQTTSN_REGISTER 0x0A |
34 | 0 | #define MQTTSN_REGACK 0x0B |
35 | 0 | #define MQTTSN_PUBLISH 0x0C |
36 | 0 | #define MQTTSN_PUBACK 0x0D |
37 | 0 | #define MQTTSN_PUBCOMP 0x0E |
38 | 0 | #define MQTTSN_PUBREC 0x0F |
39 | 0 | #define MQTTSN_PUBREL 0x10 |
40 | | #define MQTTSN_RESERVED_11 0x11 |
41 | 0 | #define MQTTSN_SUBSCRIBE 0x12 |
42 | 0 | #define MQTTSN_SUBACK 0x13 |
43 | 0 | #define MQTTSN_UNSUBSCRIBE 0x14 |
44 | 0 | #define MQTTSN_UNSUBACK 0x15 |
45 | 0 | #define MQTTSN_PINGREQ 0x16 |
46 | 0 | #define MQTTSN_PINGRESP 0x17 |
47 | 0 | #define MQTTSN_DISCONNECT 0x18 |
48 | | #define MQTTSN_RESERVED_19 0x19 |
49 | 0 | #define MQTTSN_WILLTOPICUPD 0x1A |
50 | 0 | #define MQTTSN_WILLTOPICRESP 0x1B |
51 | 0 | #define MQTTSN_WILLMSGUPD 0x1C |
52 | 0 | #define MQTTSN_WILLMSGRESP 0x1D |
53 | 0 | #define MQTTSN_ENCAPSULATED_MSG 0xFE |
54 | | |
55 | | /* Masks to extract flag values. */ |
56 | 16 | #define MQTTSN_MASK_DUP_FLAG 0x80 |
57 | 16 | #define MQTTSN_MASK_QOS 0x60 |
58 | 16 | #define MQTTSN_MASK_RETAIN 0x10 |
59 | 16 | #define MQTTSN_MASK_WILL 0x08 |
60 | 16 | #define MQTTSN_MASK_CLEAN 0x04 |
61 | 16 | #define MQTTSN_MASK_TOPIC_ID_TYPE 0x03 |
62 | | |
63 | | /* Mask to extract radius from control. */ |
64 | 16 | #define MQTTSN_MASK_CONTROL 0x03 |
65 | | |
66 | | /* MQTT-SN QoS levels. */ |
67 | | #define MQTTSN_QOS_ATMOST_ONCE 0x00 |
68 | | #define MQTTSN_QOS_ATLEAST_ONCE 0x01 |
69 | | #define MQTTSN_QOS_EXACTLY_ONCE 0x02 |
70 | | #define MQTTSN_QOS_NO_CONNECTION 0x03 |
71 | | |
72 | | /* MQTT-SN topic types. */ |
73 | 0 | #define MQTTSN_TOPIC_NORMAL_ID 0x00 |
74 | 0 | #define MQTTSN_TOPIC_PREDEF_ID 0x01 |
75 | 0 | #define MQTTSN_TOPIC_SHORT_NAME 0x02 |
76 | | #define MQTTSN_TOPIC_RESERVED 0x03 |
77 | | |
78 | | /* MQTT-SN connection return types. */ |
79 | | #define MQTTSN_CON_ACCEPTED 0x00 |
80 | | #define MQTTSN_CON_REFUSED_CONGESTION 0x01 |
81 | | #define MQTTSN_CON_REFUSED_INVALID_TOPIC_ID 0x02 |
82 | | #define MQTTSN_CON_REFUSED_NOT_SUPPORTED 0x03 |
83 | | |
84 | | void proto_register_mqttsn(void); |
85 | | void proto_reg_handoff_mqttsn(void); |
86 | | |
87 | | /* MQTT-SN message type values. */ |
88 | | static const value_string mqttsn_msgtype_vals[] = { |
89 | | { MQTTSN_ADVERTISE, "Advertise Gateway" }, |
90 | | { MQTTSN_SEARCHGW, "Search Gateway" }, |
91 | | { MQTTSN_GWINFO, "Gateway Info" }, |
92 | | { MQTTSN_RESERVED_03, "Reserved_03" }, |
93 | | { MQTTSN_CONNECT, "Connect Command" }, |
94 | | { MQTTSN_CONNACK, "Connect Ack" }, |
95 | | { MQTTSN_WILLTOPICREQ, "Will Topic Request" }, |
96 | | { MQTTSN_WILLTOPIC, "Will Topic" }, |
97 | | { MQTTSN_WILLMSGREQ, "Will Message Request" }, |
98 | | { MQTTSN_WILLMSG, "Will Message" }, |
99 | | { MQTTSN_REGISTER, "Register" }, |
100 | | { MQTTSN_REGACK, "Register Ack" }, |
101 | | { MQTTSN_PUBLISH, "Publish Message" }, |
102 | | { MQTTSN_PUBACK, "Publish Ack" }, |
103 | | { MQTTSN_PUBCOMP, "Publish Complete" }, |
104 | | { MQTTSN_PUBREC, "Publish Received" }, |
105 | | { MQTTSN_PUBREL, "Publish Release" }, |
106 | | { MQTTSN_RESERVED_11, "Reserved_11" }, |
107 | | { MQTTSN_SUBSCRIBE, "Subscribe Request" }, |
108 | | { MQTTSN_SUBACK, "Subscribe Ack" }, |
109 | | { MQTTSN_UNSUBSCRIBE, "Unsubscribe Request" }, |
110 | | { MQTTSN_UNSUBACK, "Unsubscribe Ack" }, |
111 | | { MQTTSN_PINGREQ, "Ping Request" }, |
112 | | { MQTTSN_PINGRESP, "Ping Response" }, |
113 | | { MQTTSN_DISCONNECT, "Disconnect Req" }, |
114 | | { MQTTSN_RESERVED_19, "Reserved_19" }, |
115 | | { MQTTSN_WILLTOPICUPD, "Will Topic Update" }, |
116 | | { MQTTSN_WILLTOPICRESP, "Will Topic Response" }, |
117 | | { MQTTSN_WILLMSGUPD, "Will Message Update" }, |
118 | | { MQTTSN_WILLMSGRESP, "Will Message Response" }, |
119 | | { MQTTSN_ENCAPSULATED_MSG, "Encapsulated Message" }, |
120 | | { 0, NULL } |
121 | | }; |
122 | | static value_string_ext mqttsn_msgtype_vals_ext = VALUE_STRING_EXT_INIT(mqttsn_msgtype_vals); |
123 | | |
124 | | /* MQTT-SN QoS level values. */ |
125 | | static const value_string mqttsn_qos_vals[] = { |
126 | | { MQTTSN_QOS_ATMOST_ONCE, "Fire and Forget" }, |
127 | | { MQTTSN_QOS_ATLEAST_ONCE, "Acknowledged deliver" }, |
128 | | { MQTTSN_QOS_EXACTLY_ONCE, "Assured Delivery" }, |
129 | | { MQTTSN_QOS_NO_CONNECTION, "No Connection required" }, |
130 | | { 0, NULL } |
131 | | }; |
132 | | |
133 | | /* MQTT-SN topic type values. */ |
134 | | static const value_string mqttsn_typeid_vals[] = { |
135 | | { MQTTSN_TOPIC_NORMAL_ID, "Normal ID" }, |
136 | | { MQTTSN_TOPIC_PREDEF_ID, "Pre-defined ID" }, |
137 | | { MQTTSN_TOPIC_SHORT_NAME, "Short Topic Name" }, |
138 | | { MQTTSN_TOPIC_RESERVED, "Reserved" }, |
139 | | { 0, NULL } |
140 | | }; |
141 | | |
142 | | /* MQTT-SN connection return type values. */ |
143 | | static const value_string mqttsn_return_vals[] = { |
144 | | { MQTTSN_CON_ACCEPTED, "Accepted" }, |
145 | | { MQTTSN_CON_REFUSED_CONGESTION, "Rejected: Congestion" }, |
146 | | { MQTTSN_CON_REFUSED_INVALID_TOPIC_ID, "Rejected: invalid topic ID" }, |
147 | | { MQTTSN_CON_REFUSED_NOT_SUPPORTED, "Rejected: not supported" }, |
148 | | { 0, NULL } |
149 | | }; |
150 | | |
151 | | /* MQTT-SN Handle */ |
152 | | static dissector_handle_t mqttsn_handle; |
153 | | |
154 | | /* Initialize the protocol and registered fields. */ |
155 | | static int proto_mqttsn; |
156 | | |
157 | | static int hf_mqttsn_msg; |
158 | | static int hf_mqttsn_msg_len; |
159 | | static int hf_mqttsn_msg_type; |
160 | | static int hf_mqttsn_flags; |
161 | | static int hf_mqttsn_dup; |
162 | | static int hf_mqttsn_qos; |
163 | | static int hf_mqttsn_retain; |
164 | | static int hf_mqttsn_will; |
165 | | static int hf_mqttsn_clean_session; |
166 | | static int hf_mqttsn_topic_id_type; |
167 | | static int hf_mqttsn_return_code; |
168 | | static int hf_mqttsn_gw_id; |
169 | | static int hf_mqttsn_gw_addr; |
170 | | static int hf_mqttsn_adv_interv; |
171 | | static int hf_mqttsn_radius; |
172 | | static int hf_mqttsn_protocol_id; |
173 | | static int hf_mqttsn_topic_id; |
174 | | static int hf_mqttsn_msg_id; |
175 | | static int hf_mqttsn_topic; |
176 | | static int hf_mqttsn_sleep_timer; |
177 | | static int hf_mqttsn_will_topic; |
178 | | static int hf_mqttsn_will_msg; |
179 | | static int hf_mqttsn_pub_msg; |
180 | | static int hf_mqttsn_client_id; |
181 | | static int hf_mqttsn_keep_alive; |
182 | | static int hf_mqttsn_control_info; |
183 | | static int hf_mqttsn_wireless_node_id; |
184 | | |
185 | | /* Initialize subtree pointers. */ |
186 | | static int ett_mqttsn_hdr; |
187 | | static int ett_mqttsn_msg; |
188 | | static int ett_mqttsn_flags; |
189 | | |
190 | | /* Dissect a single MQTT-SN packet. */ |
191 | | // NOLINTNEXTLINE(misc-no-recursion) |
192 | | static void dissect_mqttsn_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) |
193 | 0 | { |
194 | | /* Various variables. */ |
195 | 0 | int long_hdr_len = 0; |
196 | 0 | uint8_t mqttsn_msg_type_id; |
197 | 0 | uint16_t mqttsn_msg_len; |
198 | 0 | int8_t mqttsn_topic_id_type = MQTTSN_TOPIC_NORMAL_ID; |
199 | | |
200 | | /* Get the message length. */ |
201 | 0 | mqttsn_msg_len = tvb_get_uint8(tvb, offset); |
202 | | |
203 | | /* If the message length is equal to 1 then the next two bytes define the real message length. */ |
204 | 0 | if (mqttsn_msg_len == 1) |
205 | 0 | { |
206 | 0 | long_hdr_len = 1; |
207 | 0 | mqttsn_msg_len = tvb_get_ntohs(tvb, offset + 1); |
208 | 0 | } |
209 | | |
210 | | /* If this is an encapsulated message, we need to add the offset of the previous packet. */ |
211 | 0 | mqttsn_msg_len += offset; |
212 | | |
213 | | /* Get the message type id (in byte 1 or 3 - depending on the message length). */ |
214 | 0 | mqttsn_msg_type_id = tvb_get_uint8(tvb, offset + (long_hdr_len ? 3 : 1)); |
215 | |
|
216 | 0 | if (tree) |
217 | 0 | { |
218 | | /* Variables for the message items and trees. */ |
219 | 0 | proto_item *ti = NULL; |
220 | 0 | proto_item *ti_mqttsn = NULL; |
221 | 0 | proto_tree *mqttsn_tree = NULL; |
222 | 0 | proto_tree *mqttsn_msg_tree = NULL; |
223 | | |
224 | | /* Add MQTT-SN subtree to the main tree. */ |
225 | 0 | if (offset == 0) |
226 | 0 | { |
227 | 0 | ti = proto_tree_add_item(tree, proto_mqttsn, tvb, 0, -1, ENC_NA); |
228 | 0 | mqttsn_tree = proto_item_add_subtree(ti, ett_mqttsn_hdr); |
229 | 0 | } |
230 | | /* If this is an encapsulated message, we add the message to the existing subtree. */ |
231 | 0 | else |
232 | 0 | { |
233 | 0 | mqttsn_tree = tree; |
234 | 0 | } |
235 | | |
236 | | /* Add message subtree. */ |
237 | 0 | ti_mqttsn = proto_tree_add_item(mqttsn_tree, hf_mqttsn_msg, tvb, offset + (long_hdr_len ? 3 : 1), -1, ENC_NA); |
238 | 0 | mqttsn_msg_tree = proto_item_add_subtree(ti_mqttsn, ett_mqttsn_msg); |
239 | | |
240 | | /* | Message type (1 octet) | */ |
241 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_type, tvb, offset + (long_hdr_len ? 3 : 1), 1, ENC_BIG_ENDIAN); |
242 | 0 | offset += 1; |
243 | | |
244 | | /* | Message length (1 - 2 octets) | */ |
245 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_len, tvb, offset - 1 + (long_hdr_len ? 1 : 0), (long_hdr_len ? 2 : 1), ENC_BIG_ENDIAN); |
246 | 0 | offset += (long_hdr_len ? 3 : 1); |
247 | | |
248 | | /* |
249 | | * Only some message types contain flags (1 octet). |
250 | | * ______________________________________________________________________________________ |
251 | | * | | | | | | | |
252 | | * | bit 7 | bit 6 + 5 | bit 4 | bit 3 | bit 2 | bit 1 + 0 | |
253 | | * | DUP | QoS | Retain | Will | CleanSession | TopicIdType | |
254 | | * |_________|___________|____________|__________|__________________|___________________| |
255 | | */ |
256 | |
|
257 | 0 | static int * const connect_flags[] = { |
258 | 0 | &hf_mqttsn_will, |
259 | 0 | &hf_mqttsn_clean_session, |
260 | 0 | NULL |
261 | 0 | }; |
262 | |
|
263 | 0 | static int * const willtopic_flags[] = { |
264 | 0 | &hf_mqttsn_qos, |
265 | 0 | &hf_mqttsn_retain, |
266 | 0 | NULL |
267 | 0 | }; |
268 | |
|
269 | 0 | static int * const publish_flags[] = { |
270 | 0 | &hf_mqttsn_dup, |
271 | 0 | &hf_mqttsn_qos, |
272 | 0 | &hf_mqttsn_retain, |
273 | 0 | &hf_mqttsn_topic_id_type, |
274 | 0 | NULL |
275 | 0 | }; |
276 | |
|
277 | 0 | static int * const subscribe_flags[] = { |
278 | 0 | &hf_mqttsn_dup, |
279 | 0 | &hf_mqttsn_qos, |
280 | 0 | &hf_mqttsn_topic_id_type, |
281 | 0 | NULL |
282 | 0 | }; |
283 | |
|
284 | 0 | static int * const suback_flags[] = { |
285 | 0 | &hf_mqttsn_qos, |
286 | 0 | NULL |
287 | 0 | }; |
288 | |
|
289 | 0 | static int * const unsubscribe_flags[] = { |
290 | 0 | &hf_mqttsn_topic_id_type, |
291 | 0 | NULL |
292 | 0 | }; |
293 | |
|
294 | 0 | switch (mqttsn_msg_type_id) |
295 | 0 | { |
296 | | /* Connect */ |
297 | 0 | case MQTTSN_CONNECT: |
298 | | /* Add Will and CleanSession Type flags. */ |
299 | 0 | proto_tree_add_bitmask(mqttsn_msg_tree, tvb, offset, hf_mqttsn_flags, ett_mqttsn_flags, connect_flags, ENC_BIG_ENDIAN); |
300 | 0 | offset += 1; |
301 | 0 | break; |
302 | | |
303 | | /* Publish */ |
304 | 0 | case MQTTSN_PUBLISH: |
305 | | /* Add DUP, QoS, Retain and Topic ID Type flags. */ |
306 | 0 | mqttsn_topic_id_type = tvb_get_uint8(tvb, offset) & MQTTSN_MASK_TOPIC_ID_TYPE; |
307 | 0 | proto_tree_add_bitmask(mqttsn_msg_tree, tvb, offset, hf_mqttsn_flags, ett_mqttsn_flags, publish_flags, ENC_BIG_ENDIAN); |
308 | 0 | offset += 1; |
309 | 0 | break; |
310 | | |
311 | 0 | case MQTTSN_SUBSCRIBE: |
312 | 0 | mqttsn_topic_id_type = tvb_get_uint8(tvb, offset) & MQTTSN_MASK_TOPIC_ID_TYPE; |
313 | 0 | proto_tree_add_bitmask(mqttsn_msg_tree, tvb, offset, hf_mqttsn_flags, ett_mqttsn_flags, subscribe_flags, ENC_BIG_ENDIAN); |
314 | 0 | offset += 1; |
315 | 0 | break; |
316 | | |
317 | 0 | case MQTTSN_SUBACK: |
318 | 0 | proto_tree_add_bitmask(mqttsn_msg_tree, tvb, offset, hf_mqttsn_flags, ett_mqttsn_flags, suback_flags, ENC_BIG_ENDIAN); |
319 | 0 | offset += 1; |
320 | 0 | break; |
321 | | |
322 | 0 | case MQTTSN_UNSUBSCRIBE: |
323 | 0 | mqttsn_topic_id_type = tvb_get_uint8(tvb, offset) & MQTTSN_MASK_TOPIC_ID_TYPE; |
324 | 0 | proto_tree_add_bitmask(mqttsn_msg_tree, tvb, offset, hf_mqttsn_flags, ett_mqttsn_flags, unsubscribe_flags, ENC_BIG_ENDIAN); |
325 | 0 | offset += 1; |
326 | 0 | break; |
327 | | |
328 | 0 | case MQTTSN_WILLTOPIC: |
329 | 0 | case MQTTSN_WILLTOPICUPD: |
330 | | /* "An empty WILLTOPIC[UPD] message is a message without Flags |
331 | | * and WillTopic field (i.e. it is exactly 2 octets long)." */ |
332 | 0 | if (mqttsn_msg_len > 2) { |
333 | 0 | proto_tree_add_bitmask(mqttsn_msg_tree, tvb, offset, hf_mqttsn_flags, ett_mqttsn_flags, willtopic_flags, ENC_BIG_ENDIAN); |
334 | 0 | offset += 1; |
335 | 0 | } |
336 | 0 | break; |
337 | | |
338 | | /* Default Case */ |
339 | 0 | default: |
340 | 0 | break; |
341 | 0 | } |
342 | | |
343 | | /* Add message specific informations. */ |
344 | 0 | switch (mqttsn_msg_type_id) |
345 | 0 | { |
346 | | /* Advertise Gateway */ |
347 | 0 | case MQTTSN_ADVERTISE: |
348 | | /* | 1 - Gateway ID | 2,3 - Duration until next Advertise | */ |
349 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_gw_id, tvb, offset, 1, ENC_BIG_ENDIAN); |
350 | 0 | offset += 1; |
351 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_adv_interv, tvb, offset, 2, ENC_BIG_ENDIAN); |
352 | 0 | break; |
353 | | |
354 | | /* Search Gateway */ |
355 | 0 | case MQTTSN_SEARCHGW: |
356 | | /* | 1 - Broadcast Radius | */ |
357 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_radius, tvb, offset, 1, ENC_BIG_ENDIAN); |
358 | 0 | break; |
359 | | |
360 | | /* Gateway Information */ |
361 | 0 | case MQTTSN_GWINFO: |
362 | | /* | 1 - Gateway ID | 2:n - Gateway Address (optional) | */ |
363 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_gw_id, tvb, offset, 1, ENC_BIG_ENDIAN); |
364 | 0 | offset += 1; |
365 | | |
366 | | /* Gateway Address is only present if message is sent by client. */ |
367 | 0 | if (offset < mqttsn_msg_len) |
368 | 0 | { |
369 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_gw_addr, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
370 | 0 | } |
371 | 0 | break; |
372 | | |
373 | | /* Connect */ |
374 | 0 | case MQTTSN_CONNECT: |
375 | | /* | 1 - Protocol ID | 2,3 - Keep Alive Duration | 4:n - Client ID | */ |
376 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_protocol_id, tvb, offset, 1, ENC_BIG_ENDIAN); |
377 | 0 | offset += 1; |
378 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_keep_alive, tvb, offset, 2, ENC_BIG_ENDIAN); |
379 | 0 | offset += 2; |
380 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_client_id, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
381 | 0 | break; |
382 | | |
383 | | /* Connection Acknowledgement */ |
384 | 0 | case MQTTSN_CONNACK: |
385 | | /* | 1 - Return code | */ |
386 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); |
387 | 0 | break; |
388 | | |
389 | | /* Will Topic Request + Will Message Request */ |
390 | 0 | case MQTTSN_WILLTOPICREQ: |
391 | 0 | case MQTTSN_WILLMSGREQ: |
392 | | /* Will topic/message requests don't have a variable header. */ |
393 | 0 | break; |
394 | | |
395 | | /* Will Topic */ |
396 | 0 | case MQTTSN_WILLTOPIC: |
397 | | /* | 1:n - Will topic | */ |
398 | 0 | if (mqttsn_msg_len > 2) { |
399 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
400 | 0 | } |
401 | 0 | break; |
402 | | |
403 | | /* Will Message */ |
404 | 0 | case MQTTSN_WILLMSG: |
405 | | /* | 1:n - Will message | */ |
406 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_msg, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
407 | 0 | break; |
408 | | |
409 | | /* Register */ |
410 | 0 | case MQTTSN_REGISTER: |
411 | | /* | 1,2 - Topic ID | 3,4 - Message ID | 5:n - Topic name | */ |
412 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
413 | 0 | offset += 2; |
414 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
415 | 0 | offset += 2; |
416 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
417 | 0 | break; |
418 | | |
419 | | /* Register Acknowledgement */ |
420 | 0 | case MQTTSN_REGACK: |
421 | | /* | 1,2 - Topic ID | 3,4 - Message ID | 5 - Return code | */ |
422 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
423 | 0 | offset += 2; |
424 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
425 | 0 | offset += 2; |
426 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); |
427 | 0 | break; |
428 | | |
429 | | /* Publish */ |
430 | 0 | case MQTTSN_PUBLISH: |
431 | | /* | 1,2 - Topic ID | 3,4 - Message ID | 5:n - Publish data | */ |
432 | 0 | if (mqttsn_topic_id_type == MQTTSN_TOPIC_SHORT_NAME) { |
433 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic, tvb, offset, 2, ENC_ASCII); |
434 | 0 | } else { |
435 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
436 | 0 | } |
437 | 0 | offset += 2; |
438 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
439 | 0 | offset += 2; |
440 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_pub_msg, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
441 | 0 | break; |
442 | | |
443 | | /* Publish Acknowledgement */ |
444 | 0 | case MQTTSN_PUBACK: |
445 | | /* | 1,2 - Topic ID | 3,4 - Message ID | 5 - Return code | */ |
446 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
447 | 0 | offset += 2; |
448 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
449 | 0 | offset += 2; |
450 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); |
451 | 0 | break; |
452 | | |
453 | | /* Publish Complete + Publish Received + Publish Released */ |
454 | 0 | case MQTTSN_PUBCOMP: |
455 | 0 | case MQTTSN_PUBREC: |
456 | 0 | case MQTTSN_PUBREL: |
457 | | /* | 1,2 - Message ID | */ |
458 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
459 | 0 | break; |
460 | | |
461 | | /* Subscribe + Unsubscribe */ |
462 | 0 | case MQTTSN_SUBSCRIBE: |
463 | 0 | case MQTTSN_UNSUBSCRIBE: |
464 | | /* | 1,2 - Message ID | 5:n - Topic name _OR_ 5,6 - Topic ID | */ |
465 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
466 | 0 | offset += 2; |
467 | 0 | if (mqttsn_topic_id_type == MQTTSN_TOPIC_PREDEF_ID) { |
468 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
469 | 0 | } else if (mqttsn_topic_id_type == MQTTSN_TOPIC_SHORT_NAME) { |
470 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic, tvb, offset, 2, ENC_ASCII); |
471 | 0 | } else { |
472 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
473 | 0 | } |
474 | 0 | break; |
475 | | |
476 | | /* Subscribe Acknowledgment */ |
477 | 0 | case MQTTSN_SUBACK: |
478 | | /* | 1,2 - Topic ID | 3,4 - Message ID | 5 - Return code | */ |
479 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
480 | 0 | offset += 2; |
481 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
482 | 0 | offset += 2; |
483 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); |
484 | 0 | break; |
485 | | |
486 | | /* Unsubscribe Acknowledgement */ |
487 | 0 | case MQTTSN_UNSUBACK: |
488 | | /* | 1,2 - Message ID | */ |
489 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); |
490 | 0 | break; |
491 | | |
492 | | /* Ping Request */ |
493 | 0 | case MQTTSN_PINGREQ: |
494 | | /* | 1:n - Client ID (optional) | */ |
495 | 0 | if (offset < mqttsn_msg_len) |
496 | 0 | { |
497 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_client_id, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
498 | 0 | } |
499 | 0 | break; |
500 | | |
501 | | /* Ping Response */ |
502 | 0 | case MQTTSN_PINGRESP: |
503 | | /* Ping responses don't have a variable header. */ |
504 | 0 | break; |
505 | | |
506 | | /* Disconnect */ |
507 | 0 | case MQTTSN_DISCONNECT: |
508 | | /* | 1,2 - Sleep Time Duration (optional) | */ |
509 | 0 | if (offset < mqttsn_msg_len) |
510 | 0 | { |
511 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_sleep_timer, tvb, offset, 2, ENC_BIG_ENDIAN); |
512 | 0 | } |
513 | 0 | break; |
514 | | |
515 | | /* Will Topic Update */ |
516 | 0 | case MQTTSN_WILLTOPICUPD: |
517 | | /* | 1:n - Will topic | */ |
518 | 0 | if (mqttsn_msg_len > 2) { |
519 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
520 | 0 | } |
521 | 0 | break; |
522 | | |
523 | | /* Will Message Update */ |
524 | 0 | case MQTTSN_WILLMSGUPD: |
525 | | /* | 1:n - Will message | */ |
526 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_msg, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII); |
527 | 0 | break; |
528 | | |
529 | | /* Will Topic Response + Will Message Response */ |
530 | 0 | case MQTTSN_WILLTOPICRESP: |
531 | 0 | case MQTTSN_WILLMSGRESP: |
532 | | /* | 1 - Return code | */ |
533 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); |
534 | 0 | break; |
535 | | |
536 | 0 | case MQTTSN_ENCAPSULATED_MSG: |
537 | | /* | 1 - Control information | 2:n - Wireless Node ID | n+1:m - MQTT-SN message | */ |
538 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_control_info, tvb, offset, 1, ENC_BIG_ENDIAN); |
539 | 0 | offset += 1; |
540 | 0 | proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_wireless_node_id, tvb, offset, (mqttsn_msg_len - offset), ENC_BIG_ENDIAN); |
541 | 0 | offset += (mqttsn_msg_len - offset); |
542 | | |
543 | | /* Dissect encapsulated message (if present). */ |
544 | 0 | if (tvb_reported_length_remaining(tvb, offset) > 0) |
545 | 0 | { |
546 | 0 | increment_dissection_depth(pinfo); |
547 | 0 | dissect_mqttsn_packet(tvb, pinfo, mqttsn_msg_tree, offset); |
548 | 0 | decrement_dissection_depth(pinfo); |
549 | 0 | } |
550 | | |
551 | | /* Default Case */ |
552 | 0 | default: |
553 | 0 | break; |
554 | 0 | } |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | | /* Dissect a complete MQTT-SN message. */ |
559 | | static int dissect_mqttsn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
560 | 0 | { |
561 | | /* Various variables. */ |
562 | 0 | unsigned offset = 0; |
563 | 0 | uint8_t mqttsn_msg_type_id; |
564 | | |
565 | | /* |
566 | | * If the value in byte 0 is unequal to 1 then the value defines the |
567 | | * message length and the message type id is contained in byte 1. |
568 | | */ |
569 | 0 | if (tvb_get_uint8(tvb, 0) != 1) |
570 | 0 | { |
571 | 0 | mqttsn_msg_type_id = tvb_get_uint8(tvb, 1); |
572 | 0 | } |
573 | | /* |
574 | | * If the value in byte 0 is equal to 1 then the next two bytes define |
575 | | * the real message length and the message type id is contained in byte 3. |
576 | | */ |
577 | 0 | else |
578 | 0 | { |
579 | 0 | mqttsn_msg_type_id = tvb_get_uint8(tvb, 3); |
580 | 0 | } |
581 | | |
582 | | /* Add the protocol name to the protocol column. */ |
583 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "MQTT-SN"); |
584 | | |
585 | | /* Add the message type to the info column. */ |
586 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
587 | 0 | col_add_str(pinfo->cinfo, COL_INFO, val_to_str_ext(pinfo->pool, mqttsn_msg_type_id, &mqttsn_msgtype_vals_ext, "Unknown (0x%02x)")); |
588 | | |
589 | | /* Dissect a MQTT-SN packet. */ |
590 | 0 | dissect_mqttsn_packet(tvb, pinfo, tree, offset); |
591 | 0 | return tvb_captured_length(tvb); |
592 | 0 | } |
593 | | |
594 | | /* Register the protocol with Wireshark. */ |
595 | | void proto_register_mqttsn(void) |
596 | 16 | { |
597 | 16 | static hf_register_info hf_mqttsn[] = { |
598 | 16 | { &hf_mqttsn_msg, |
599 | 16 | { "Message", "mqttsn.msg", |
600 | 16 | FT_NONE, BASE_NONE, NULL, 0, |
601 | 16 | NULL, HFILL } |
602 | 16 | }, |
603 | 16 | { &hf_mqttsn_msg_len, |
604 | 16 | { "Message Length", "mqttsn.msg.len", |
605 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
606 | 16 | NULL, HFILL } |
607 | 16 | }, |
608 | 16 | { &hf_mqttsn_msg_type, |
609 | 16 | { "Message Type", "mqttsn.msg.type", |
610 | 16 | FT_UINT8, BASE_HEX | BASE_EXT_STRING, &mqttsn_msgtype_vals_ext, 0, |
611 | 16 | NULL, HFILL } |
612 | 16 | }, |
613 | 16 | { &hf_mqttsn_flags, |
614 | 16 | { "Flags", "mqttsn.flags", |
615 | 16 | FT_UINT8, BASE_HEX, NULL, 0, |
616 | 16 | NULL, HFILL } |
617 | 16 | }, |
618 | 16 | { &hf_mqttsn_dup, |
619 | 16 | { "DUP", "mqttsn.dup", |
620 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_DUP_FLAG, |
621 | 16 | NULL, HFILL } |
622 | 16 | }, |
623 | 16 | { &hf_mqttsn_will, |
624 | 16 | { "Will", "mqttsn.will", |
625 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_WILL, |
626 | 16 | NULL, HFILL } |
627 | 16 | }, |
628 | 16 | { &hf_mqttsn_clean_session, |
629 | 16 | { "Clean Session", "mqttsn.clean.session", |
630 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_CLEAN, |
631 | 16 | NULL, HFILL } |
632 | 16 | }, |
633 | 16 | { &hf_mqttsn_topic_id_type, |
634 | 16 | { "Topic ID Type", "mqttsn.topic.id.type", |
635 | 16 | FT_UINT8, BASE_HEX, VALS(mqttsn_typeid_vals), MQTTSN_MASK_TOPIC_ID_TYPE, |
636 | 16 | NULL, HFILL } |
637 | 16 | }, |
638 | 16 | { &hf_mqttsn_qos, |
639 | 16 | { "QoS", "mqttsn.qos", |
640 | 16 | FT_UINT8, BASE_HEX, VALS(mqttsn_qos_vals), MQTTSN_MASK_QOS, |
641 | 16 | NULL, HFILL } |
642 | 16 | }, |
643 | 16 | { &hf_mqttsn_retain, |
644 | 16 | { "Retain", "mqttsn.retain", |
645 | 16 | FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_RETAIN, |
646 | 16 | NULL, HFILL } |
647 | 16 | }, |
648 | 16 | { &hf_mqttsn_return_code, |
649 | 16 | { "Return Code", "mqttsn.return.code", |
650 | 16 | FT_UINT8, BASE_HEX, VALS(mqttsn_return_vals), 0x0, |
651 | 16 | NULL, HFILL } |
652 | 16 | }, |
653 | 16 | { &hf_mqttsn_gw_id, |
654 | 16 | { "Gateway ID", "mqttsn.gw.id", |
655 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
656 | 16 | NULL, HFILL } |
657 | 16 | }, |
658 | 16 | { &hf_mqttsn_gw_addr, |
659 | 16 | { "Gateway Address", "mqttsn.gw.addr", |
660 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
661 | 16 | NULL, HFILL } |
662 | 16 | }, |
663 | 16 | { &hf_mqttsn_adv_interv, |
664 | 16 | { "Advertise Interval", "mqttsn.adv.interv", |
665 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
666 | 16 | NULL, HFILL } |
667 | 16 | }, |
668 | 16 | { &hf_mqttsn_radius, |
669 | 16 | { "Broadcast Radius", "mqttsn.radius", |
670 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
671 | 16 | NULL, HFILL } |
672 | 16 | }, |
673 | 16 | { &hf_mqttsn_protocol_id, |
674 | 16 | { "Protocol ID", "mqttsn.protocol.id", |
675 | 16 | FT_UINT8, BASE_HEX, NULL, 0x0, |
676 | 16 | NULL, HFILL } |
677 | 16 | }, |
678 | 16 | { &hf_mqttsn_topic_id, |
679 | 16 | { "Topic ID", "mqttsn.topic.id", |
680 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
681 | 16 | NULL, HFILL } |
682 | 16 | }, |
683 | 16 | { &hf_mqttsn_msg_id, |
684 | 16 | { "Message ID", "mqttsn.msg.id", |
685 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
686 | 16 | NULL, HFILL } |
687 | 16 | }, |
688 | 16 | { &hf_mqttsn_topic, |
689 | 16 | { "Topic Name", "mqttsn.topic", |
690 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
691 | 16 | NULL, HFILL } |
692 | 16 | }, |
693 | 16 | { &hf_mqttsn_sleep_timer, |
694 | 16 | { "Sleep Timer", "mqttsn.sleep.timer", |
695 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
696 | 16 | NULL, HFILL } |
697 | 16 | }, |
698 | 16 | { &hf_mqttsn_will_topic, |
699 | 16 | { "Will Topic", "mqttsn.will.topic", |
700 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
701 | 16 | NULL, HFILL } |
702 | 16 | }, |
703 | 16 | { &hf_mqttsn_will_msg, |
704 | 16 | { "Will Message", "mqttsn.will.msg", |
705 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
706 | 16 | NULL, HFILL } |
707 | 16 | }, |
708 | 16 | { &hf_mqttsn_pub_msg, |
709 | 16 | { "Message", "mqttsn.pub.msg", |
710 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
711 | 16 | NULL, HFILL } |
712 | 16 | }, |
713 | 16 | { &hf_mqttsn_client_id, |
714 | 16 | { "Client ID", "mqttsn.client.id", |
715 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
716 | 16 | NULL, HFILL } |
717 | 16 | }, |
718 | 16 | { &hf_mqttsn_keep_alive, |
719 | 16 | { "Keep Alive", "mqttsn.keep.alive", |
720 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
721 | 16 | NULL, HFILL } |
722 | 16 | }, |
723 | 16 | { &hf_mqttsn_control_info, |
724 | 16 | { "Control", "mqttsn.control.info", |
725 | 16 | FT_UINT8, BASE_HEX, NULL, MQTTSN_MASK_CONTROL, |
726 | 16 | NULL, HFILL } |
727 | 16 | }, |
728 | 16 | { &hf_mqttsn_wireless_node_id, |
729 | 16 | { "Wireless Node ID", "mqttsn.wireless.node.id", |
730 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0, |
731 | 16 | NULL, HFILL } |
732 | 16 | } |
733 | 16 | }; |
734 | | |
735 | | /* Setup protocol subtree arrays. */ |
736 | 16 | static int* ett_mqttsn[] = { |
737 | 16 | &ett_mqttsn_hdr, |
738 | 16 | &ett_mqttsn_msg, |
739 | 16 | &ett_mqttsn_flags |
740 | 16 | }; |
741 | | |
742 | | /* Register protocol names and descriptions. */ |
743 | 16 | proto_mqttsn = proto_register_protocol("MQ Telemetry Transport Protocol for Sensor Networks", "MQTT-SN", "mqttsn"); |
744 | | |
745 | | /* Create the dissector handle. */ |
746 | 16 | mqttsn_handle = register_dissector("mqttsn", dissect_mqttsn, proto_mqttsn); |
747 | | |
748 | | /* Register fields and subtrees. */ |
749 | 16 | proto_register_field_array(proto_mqttsn, hf_mqttsn, array_length(hf_mqttsn)); |
750 | 16 | proto_register_subtree_array(ett_mqttsn, array_length(ett_mqttsn)); |
751 | 16 | } |
752 | | |
753 | | /* Dissector Handoff */ |
754 | | void proto_reg_handoff_mqttsn(void) |
755 | 16 | { |
756 | 16 | dissector_add_for_decode_as_with_preference("udp.port", mqttsn_handle); |
757 | 16 | } |
758 | | |
759 | | /* |
760 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
761 | | * |
762 | | * Local variables: |
763 | | * c-basic-offset: 4 |
764 | | * tab-width: 8 |
765 | | * indent-tabs-mode: nil |
766 | | * End: |
767 | | * |
768 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
769 | | * :indentSize=4:tabSize=8:noTabs=true: |
770 | | */ |