diff options
author | Jan-Hendrik Bolte <jabolte@uos.de> | 2015-07-20 14:37:49 +0200 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2015-08-07 08:52:19 +0000 |
commit | 0c9998ddfdb425fef2a43badb09690ef2a8e73ce (patch) | |
tree | d91eaf1f8527a16a86ead6bdf35530ce727db32b /epan/dissectors/packet-mqtt-sn.c | |
parent | 4557c17f7aad5e26fb840ea90e51de9dabf94e68 (diff) |
Add dissector for MQTT-SN v1.2
Bug: 11388
Change-Id: I5d961b19132a9904080e9494467826d15659c54e
Reviewed-on: https://code.wireshark.org/review/9711
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-mqtt-sn.c')
-rw-r--r-- | epan/dissectors/packet-mqtt-sn.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/epan/dissectors/packet-mqtt-sn.c b/epan/dissectors/packet-mqtt-sn.c new file mode 100644 index 0000000000..a9a9454365 --- /dev/null +++ b/epan/dissectors/packet-mqtt-sn.c @@ -0,0 +1,713 @@ +/* packet-mqtt-sn.c + * + * Routines for MQTT-SN v1.2 <http://mqtt.org> + * + * Copyright (c) 2015, Jan-Hendrik Bolte <jabolte@uni-osnabrueck.de> + * Copyright (c) 2015, University of Osnabrueck + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include <epan/packet.h> + +/* MQTT-SN message types. */ +#define MQTTSN_ADVERTISE 0x00 +#define MQTTSN_SEARCHGW 0x01 +#define MQTTSN_GWINFO 0x02 +#define MQTTSN_RESERVED_03 0x03 +#define MQTTSN_CONNECT 0x04 +#define MQTTSN_CONNACK 0x05 +#define MQTTSN_WILLTOPICREQ 0x06 +#define MQTTSN_WILLTOPIC 0x07 +#define MQTTSN_WILLMSGREQ 0x08 +#define MQTTSN_WILLMSG 0x09 +#define MQTTSN_REGISTER 0x0A +#define MQTTSN_REGACK 0x0B +#define MQTTSN_PUBLISH 0x0C +#define MQTTSN_PUBACK 0x0D +#define MQTTSN_PUBCOMP 0x0E +#define MQTTSN_PUBREC 0x0F +#define MQTTSN_PUBREL 0x10 +#define MQTTSN_RESERVED_11 0x11 +#define MQTTSN_SUBSCRIBE 0x12 +#define MQTTSN_SUBACK 0x13 +#define MQTTSN_UNSUBSCRIBE 0x14 +#define MQTTSN_UNSUBACK 0x15 +#define MQTTSN_PINGREQ 0x16 +#define MQTTSN_PINGRESP 0x17 +#define MQTTSN_DISCONNECT 0x18 +#define MQTTSN_RESERVED_19 0x19 +#define MQTTSN_WILLTOPICUPD 0x1A +#define MQTTSN_WILLTOPICRESP 0x1B +#define MQTTSN_WILLMSGUPD 0x1C +#define MQTTSN_WILLMSGRESP 0x1D +#define MQTTSN_ENCAPSULATED_MSG 0xFE + +/* Masks to extract flag values. */ +#define MQTTSN_MASK_DUP_FLAG 0x80 +#define MQTTSN_MASK_QOS 0x60 +#define MQTTSN_MASK_RETAIN 0x10 +#define MQTTSN_MASK_WILL 0x08 +#define MQTTSN_MASK_CLEAN 0x04 +#define MQTTSN_MASK_TOPIC_ID_TYPE 0x03 + +/* Mask to extract radius from control. */ +#define MQTTSN_MASK_CONTROL 0x03 + +/* MQTT-SN QoS levels. */ +#define MQTTSN_QOS_ATMOST_ONCE 0x00 +#define MQTTSN_QOS_ATLEAST_ONCE 0x01 +#define MQTTSN_QOS_EXACTLY_ONCE 0x02 +#define MQTTSN_QOS_NO_CONNECTION 0x03 + +/* MQTT-SN topic types. */ +#define MQTTSN_TOPIC_NORMAL_ID 0x00 +#define MQTTSN_TOPIC_PREDEF_ID 0x01 +#define MQTTSN_TOPIC_SHORT_NAME 0x02 +#define MQTTSN_TOPIC_RESERVED 0x03 + +/* MQTT-SN connection return types. */ +#define MQTTSN_CON_ACCEPTED 0x00 +#define MQTTSN_CON_REFUSED_CONGESTION 0x01 +#define MQTTSN_CON_REFUSED_INVALID_TOPIC_ID 0x02 +#define MQTTSN_CON_REFUSED_NOT_SUPPORTED 0x03 + +void proto_register_mqttsn(void); +void proto_reg_handoff_mqttsn(void); + +/* MQTT-SN message type values. */ +static const value_string mqttsn_msgtype_vals[] = { + { MQTTSN_ADVERTISE, "Advertise Gateway" }, + { MQTTSN_SEARCHGW, "Search Gateway" }, + { MQTTSN_GWINFO, "Gateway Info" }, + { MQTTSN_RESERVED_03, "Reserved_03" }, + { MQTTSN_CONNECT, "Connect Command" }, + { MQTTSN_CONNACK, "Connect Ack" }, + { MQTTSN_WILLTOPICREQ, "Will Topic Request" }, + { MQTTSN_WILLTOPIC, "Will Topic" }, + { MQTTSN_WILLMSGREQ, "Will Message Request" }, + { MQTTSN_WILLMSG, "Will Message" }, + { MQTTSN_REGISTER, "Register" }, + { MQTTSN_REGACK, "Register Ack" }, + { MQTTSN_PUBLISH, "Publish Message" }, + { MQTTSN_PUBACK, "Publish Ack" }, + { MQTTSN_PUBCOMP, "Publish Complete" }, + { MQTTSN_PUBREC, "Publish Received" }, + { MQTTSN_PUBREL, "Publish Release" }, + { MQTTSN_RESERVED_11, "Reserved_11" }, + { MQTTSN_SUBSCRIBE, "Subscribe Request" }, + { MQTTSN_SUBACK, "Subscribe Ack" }, + { MQTTSN_UNSUBSCRIBE, "Unsubscribe Request" }, + { MQTTSN_UNSUBACK, "Unsubscribe Ack" }, + { MQTTSN_PINGREQ, "Ping Request" }, + { MQTTSN_PINGRESP, "Ping Response" }, + { MQTTSN_DISCONNECT, "Disconnect Req" }, + { MQTTSN_RESERVED_19, "Reserved_19" }, + { MQTTSN_WILLTOPICUPD, "Will Topic Update" }, + { MQTTSN_WILLTOPICRESP, "Will Topic Response" }, + { MQTTSN_WILLMSGUPD, "Will Message Update" }, + { MQTTSN_WILLMSGRESP, "Will Message Response" }, + { MQTTSN_ENCAPSULATED_MSG, "Encapsulated Message" }, + { 0, NULL } +}; +static value_string_ext mqttsn_msgtype_vals_ext = VALUE_STRING_EXT_INIT(mqttsn_msgtype_vals); + +/* MQTT-SN QoS level values. */ +static const value_string mqttsn_qos_vals[] = { + { MQTTSN_QOS_ATMOST_ONCE, "Fire and Forget" }, + { MQTTSN_QOS_ATLEAST_ONCE, "Acknowledged deliver" }, + { MQTTSN_QOS_EXACTLY_ONCE, "Assured Delivery" }, + { MQTTSN_QOS_NO_CONNECTION, "No Connection required" }, + { 0, NULL } +}; + +/* MQTT-SN topic type values. */ +static const value_string mqttsn_typeid_vals[] = { + { MQTTSN_TOPIC_NORMAL_ID, "Normal ID" }, + { MQTTSN_TOPIC_PREDEF_ID, "Pre-defined ID" }, + { MQTTSN_TOPIC_SHORT_NAME, "Short Topic Name" }, + { MQTTSN_TOPIC_RESERVED, "Reserved" }, + { 0, NULL } +}; + +/* MQTT-SN connection return type values. */ +static const value_string mqttsn_return_vals[] = { + { MQTTSN_CON_ACCEPTED, "Accepted" }, + { MQTTSN_CON_REFUSED_CONGESTION, "Rejected: Congestion" }, + { MQTTSN_CON_REFUSED_INVALID_TOPIC_ID, "Rejected: invalid topic ID" }, + { MQTTSN_CON_REFUSED_NOT_SUPPORTED, "Rejected: not supported" }, + { 0, NULL } +}; + +/* MQTT-SN Handle */ +static dissector_handle_t mqttsn_handle; + +/* Initialize the protocol and registered fields. */ +static int proto_mqttsn = -1; + +static int hf_mqttsn_msg = -1; +static int hf_mqttsn_msg_len = -1; +static int hf_mqttsn_msg_type = -1; +static int hf_mqttsn_dup = -1; +static int hf_mqttsn_qos = -1; +static int hf_mqttsn_retain = -1; +static int hf_mqttsn_will = -1; +static int hf_mqttsn_clean_session = -1; +static int hf_mqttsn_topic_id_type = -1; +static int hf_mqttsn_return_code = -1; +static int hf_mqttsn_gw_id = -1; +static int hf_mqttsn_gw_addr = -1; +static int hf_mqttsn_adv_interv = -1; +static int hf_mqttsn_radius = -1; +static int hf_mqttsn_protocol_id = -1; +static int hf_mqttsn_topic_id = -1; +static int hf_mqttsn_msg_id = -1; +static int hf_mqttsn_topic = -1; +static int hf_mqttsn_topic_name_or_id = -1; +static int hf_mqttsn_sleep_timer = -1; +static int hf_mqttsn_will_topic = -1; +static int hf_mqttsn_will_msg = -1; +static int hf_mqttsn_pub_msg = -1; +static int hf_mqttsn_client_id = -1; +static int hf_mqttsn_keep_alive = -1; +static int hf_mqttsn_control_info = -1; +static int hf_mqttsn_wireless_node_id = -1; + +/* Initialize subtree pointers. */ +static gint ett_mqttsn_hdr = -1; +static gint ett_mqttsn_msg = -1; +static gint ett_mqttsn_flags = -1; + +/* Dissect a single MQTT-SN packet. */ +static void dissect_mqttsn_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) +{ + /* Various variables. */ + int long_hdr_len = 0; + guint8 mqttsn_msg_type_id; + guint16 mqttsn_msg_len; + + /* Get the message length. */ + mqttsn_msg_len = tvb_get_guint8(tvb, offset); + + /* If the message length is equal to 1 then the next two bytes define the real message length. */ + if (mqttsn_msg_len == 1) + { + long_hdr_len = 1; + mqttsn_msg_len = tvb_get_ntohs(tvb, offset + 1); + } + + /* If this is an encapsulated message, we need to add the offset of the previous packet. */ + mqttsn_msg_len += offset; + + /* Get the message type id (in byte 1 or 3 - depending on the message length). */ + mqttsn_msg_type_id = tvb_get_guint8(tvb, offset + (long_hdr_len ? 3 : 1)); + + if (tree) + { + /* Variables for the message items and trees. */ + proto_item *ti = NULL; + proto_item *ti_mqttsn = NULL; + proto_tree *mqttsn_tree = NULL; + proto_tree *mqttsn_msg_tree = NULL; + proto_tree *mqttsn_flag_tree = NULL; + + /* Add MQTT-SN subtree to the main tree. */ + if (offset == 0) + { + ti = proto_tree_add_item(tree, proto_mqttsn, tvb, 0, -1, ENC_NA); + mqttsn_tree = proto_item_add_subtree(ti, ett_mqttsn_hdr); + } + /* If this is an encapsulated message, we add the message to the existing subtree. */ + else + { + mqttsn_tree = tree; + } + + /* Add message subtree. */ + ti_mqttsn = proto_tree_add_item(mqttsn_tree, hf_mqttsn_msg, tvb, offset + (long_hdr_len ? 3 : 1), -1, ENC_NA); + mqttsn_msg_tree = proto_item_add_subtree(ti_mqttsn, ett_mqttsn_msg); + + /* | Message type (1 octet) | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_type, tvb, offset + (long_hdr_len ? 3 : 1), 1, ENC_BIG_ENDIAN); + offset += 1; + + /* | Message length (1 - 2 octets) | */ + 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); + offset += (long_hdr_len ? 3 : 1); + + /* + * Only some message types contain flags (1 octet). + * ______________________________________________________________________________________ + * | | | | | | | + * | bit 7 | bit 6 + 5 | bit 4 | bit 3 | bit 2 | bit 1 + 0 | + * | DUP | QoS | Retain | Will | CleanSession | TopicIdType | + * |_________|___________|____________|__________|__________________|___________________| + */ + switch (mqttsn_msg_type_id) + { + /* Connect */ + case MQTTSN_CONNECT: + /* Add Will, CleanSession and Topic ID Type flags. */ + mqttsn_flag_tree = proto_item_add_subtree(ti_mqttsn, ett_mqttsn_flags); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_will, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_clean_session, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_topic_id_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + break; + + /* Publish */ + case MQTTSN_PUBLISH: + /* Add DUP, QoS, Retain and Topic ID Type flags. */ + mqttsn_flag_tree = proto_item_add_subtree(ti_mqttsn, ett_mqttsn_flags); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_dup, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_qos, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_retain, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_topic_id_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + break; + + /* Will Topic + Subscribe + Subscribe Acknowledgement + Unsubscribe + Will Topic Update */ + case MQTTSN_WILLTOPIC: + case MQTTSN_SUBSCRIBE: + case MQTTSN_SUBACK: + case MQTTSN_UNSUBSCRIBE: + case MQTTSN_WILLTOPICUPD: + /* Add Topic ID Type flag. */ + mqttsn_flag_tree = proto_item_add_subtree(ti_mqttsn, ett_mqttsn_flags); + proto_tree_add_item(mqttsn_flag_tree, hf_mqttsn_topic_id_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + break; + + /* Default Case */ + default: + break; + } + + /* Add message specific informations. */ + switch (mqttsn_msg_type_id) + { + /* Advertise Gateway */ + case MQTTSN_ADVERTISE: + /* | 1 - Gateway ID | 2,3 - Duration until next Advertise | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_gw_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_adv_interv, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + + /* Search Gateway */ + case MQTTSN_SEARCHGW: + /* | 1 - Broadcast Radius | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_radius, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + /* Gateway Information */ + case MQTTSN_GWINFO: + /* | 1 - Gateway ID | 2:n - Gateway Address (optional) | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_gw_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* Gateway Address is only present if message is sent by client. */ + if (offset < mqttsn_msg_len) + { + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_gw_addr, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + } + break; + + /* Connect */ + case MQTTSN_CONNECT: + /* | 1 - Protocol ID | 2,3 - Keep Alive Duration | 4:n - Client ID | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_protocol_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_keep_alive, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_client_id, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Connection Acknowledgement */ + case MQTTSN_CONNACK: + /* | 1 - Return code | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + /* Will Topic Request + Will Message Request */ + case MQTTSN_WILLTOPICREQ: + case MQTTSN_WILLMSGREQ: + /* Will topic/message requests don't have a variable header. */ + break; + + /* Will Topic */ + case MQTTSN_WILLTOPIC: + /* | 1:n - Will topic | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Will Message */ + case MQTTSN_WILLMSG: + /* | 1:n - Will message | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_msg, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Register */ + case MQTTSN_REGISTER: + /* | 1,2 - Topic ID | 3,4 - Message ID | 5:n - Topic name | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Register Acknowledgement */ + case MQTTSN_REGACK: + /* | 1,2 - Topic ID | 3,4 - Message ID | 5 - Return code | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + /* Publish */ + case MQTTSN_PUBLISH: + /* | 1,2 - Topic ID | 3,4 - Message ID | 5:n - Publish data | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_pub_msg, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Publish Acknowledgement */ + case MQTTSN_PUBACK: + /* | 1,2 - Topic ID | 3,4 - Message ID | 5 - Return code | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + /* Publish Complete + Publish Received + Publish Released */ + case MQTTSN_PUBCOMP: + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + /* | 1,2 - Message ID | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + + /* Subscribe + Unsubscribe */ + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + /* | 1,2 - Message ID | 5:n - Topic name _OR_ 5,6 - Topic ID | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_name_or_id, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Subscribe Acknowledgment */ + case MQTTSN_SUBACK: + /* | 1,2 - Topic ID | 3,4 - Message ID | 5 - Return code | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_topic_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + /* Unsubscribe Acknowledgement */ + case MQTTSN_UNSUBACK: + /* | 1,2 - Message ID | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN); + break; + + /* Ping Request */ + case MQTTSN_PINGREQ: + /* | 1:n - Client ID (optional) | */ + if (offset < mqttsn_msg_len) + { + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_client_id, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + } + break; + + /* Ping Response */ + case MQTTSN_PINGRESP: + /* Ping responses don't have a variable header. */ + break; + + /* Disconnect */ + case MQTTSN_DISCONNECT: + /* | 1,2 - Sleep Time Duration (optional) | */ + if (offset < mqttsn_msg_len) + { + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_sleep_timer, tvb, offset, 2, ENC_BIG_ENDIAN); + } + break; + + /* Will Topic Update */ + case MQTTSN_WILLTOPICUPD: + /* | 1:n - Will topic | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_topic, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Will Message Update */ + case MQTTSN_WILLMSGUPD: + /* | 1:n - Will message | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_will_msg, tvb, offset, (mqttsn_msg_len - offset), ENC_ASCII|ENC_NA); + break; + + /* Will Topic Response + Will Message Response */ + case MQTTSN_WILLTOPICRESP: + case MQTTSN_WILLMSGRESP: + /* | 1 - Return code | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_return_code, tvb, offset, 1, ENC_BIG_ENDIAN); + break; + + case MQTTSN_ENCAPSULATED_MSG: + /* | 1 - Control information | 2:n - Wireless Node ID | n+1:m - MQTT-SN message | */ + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_control_info, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(mqttsn_msg_tree, hf_mqttsn_wireless_node_id, tvb, offset, (mqttsn_msg_len - offset), ENC_BIG_ENDIAN); + offset += (mqttsn_msg_len - offset); + + /* Dissect encapsulated message (if present). */ + if (tvb_reported_length_remaining(tvb, offset) > 0) + { + dissect_mqttsn_packet(tvb, pinfo, mqttsn_msg_tree, offset); + } + + /* Default Case */ + default: + break; + } + } +} + +/* Dissect a complete MQTT-SN message. */ +static void dissect_mqttsn(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Various variables. */ + int offset = 0; + guint8 mqttsn_msg_type_id; + + /* + * If the value in byte 0 is unequal to 1 then the value defines the + * message length and the message type id is contained in byte 1. + */ + if (tvb_get_guint8(tvb, 0) != 1) + { + mqttsn_msg_type_id = tvb_get_guint8(tvb, 1); + } + /* + * If the value in byte 0 is equal to 1 then the next two bytes define + * the real message length and the message type id is contained in byte 3. + */ + else + { + mqttsn_msg_type_id = tvb_get_guint8(tvb, 3); + } + + /* Add the protcol name to the protocol column. */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "MQTT-SN"); + + /* Add the message type to the info column. */ + col_clear(pinfo->cinfo, COL_INFO); + col_add_str(pinfo->cinfo, COL_INFO, val_to_str_ext(mqttsn_msg_type_id, &mqttsn_msgtype_vals_ext, "Unknown (0x%02x)")); + + /* Dissect a MQTT-SN packet. */ + dissect_mqttsn_packet(tvb, pinfo, tree, offset); +} + +/* Register the protocol with Wireshark. */ +void proto_register_mqttsn(void) +{ + static hf_register_info hf_mqttsn[] = { + { &hf_mqttsn_msg, + { "Message", "mqttsn.msg", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_mqttsn_msg_len, + { "Message Length", "mqttsn.msg.len", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_msg_type, + { "Message Type", "mqttsn.msg.type", + FT_UINT8, BASE_HEX | BASE_EXT_STRING, &mqttsn_msgtype_vals_ext, 0, + NULL, HFILL } + }, + { &hf_mqttsn_dup, + { "DUP", "mqttsn.dup", + FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_DUP_FLAG, + NULL, HFILL } + }, + { &hf_mqttsn_will, + { "Will", "mqttsn.will", + FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_WILL, + NULL, HFILL } + }, + { &hf_mqttsn_clean_session, + { "Clean Session", "mqttsn.clean.session", + FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_CLEAN, + NULL, HFILL } + }, + { &hf_mqttsn_topic_id_type, + { "Topic ID Type", "mqttsn.topic.id.type", + FT_UINT8, BASE_HEX, VALS(mqttsn_typeid_vals), MQTTSN_MASK_TOPIC_ID_TYPE, + NULL, HFILL } + }, + { &hf_mqttsn_qos, + { "QoS", "mqttsn.qos", + FT_UINT8, BASE_HEX, VALS(mqttsn_qos_vals), MQTTSN_MASK_QOS, + NULL, HFILL } + }, + { &hf_mqttsn_retain, + { "Retain", "mqttsn.retain", + FT_BOOLEAN, 8, TFS(&tfs_yes_no), MQTTSN_MASK_RETAIN, + NULL, HFILL } + }, + { &hf_mqttsn_return_code, + { "Return Code", "mqttsn.return.code", + FT_UINT8, BASE_HEX, VALS(mqttsn_return_vals), 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_gw_id, + { "Gateway ID", "mqttsn.gw.id", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_gw_addr, + { "Gateway Address", "mqttsn.gw.addr", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_adv_interv, + { "Advertise Interval", "mqttsn.adv.interv", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_radius, + { "Broadcast Radius", "mqttsn.radius", + FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_protocol_id, + { "Protocol ID", "mqttsn.protocol.id", + FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_topic_id, + { "Topic ID", "mqttsn.topic.id", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_msg_id, + { "Message ID", "mqttsn.msg.id", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_topic, + { "Topic Name", "mqttsn.topic", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_topic_name_or_id, + { "Topic Name/ID", "mqttsn.topic.name.or.id", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_sleep_timer, + { "Sleep Timer", "mqttsn.sleep.timer", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_will_topic, + { "Will Topic", "mqttsn.will.topic", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_will_msg, + { "Will Message", "mqttsn.will.msg", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_pub_msg, + { "Message", "mqttsn.pub.msg", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_client_id, + { "Client ID", "mqttsn.client.id", + FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_keep_alive, + { "Keep Alive", "mqttsn.keep.alive", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + { &hf_mqttsn_control_info, + { "Control", "mqttsn.control.info", + FT_UINT8, BASE_HEX, NULL, MQTTSN_MASK_CONTROL, + NULL, HFILL } + }, + { &hf_mqttsn_wireless_node_id, + { "Wireless Node ID", "mqttsn.wireless.node.id", + FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL } + } + }; + + /* Setup protocol subtree arrays. */ + static gint* ett_mqttsn[] = { + &ett_mqttsn_hdr, + &ett_mqttsn_msg, + &ett_mqttsn_flags + }; + + /* Register protocol names and descriptions. */ + proto_mqttsn = proto_register_protocol("MQ Telemetry Transport Protocol for Sensor Networks", "MQTT-SN", "mqttsn"); + + /* Create the dissector handle. */ + mqttsn_handle = create_dissector_handle(dissect_mqttsn, proto_mqttsn); + + /* Register fields and subtrees. */ + proto_register_field_array(proto_mqttsn, hf_mqttsn, array_length(hf_mqttsn)); + proto_register_subtree_array(ett_mqttsn, array_length(ett_mqttsn)); +} + +/* Dissector Handoff */ +void proto_reg_handoff_mqttsn(void) +{ + dissector_add_handle("udp.port", mqttsn_handle); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |