├── TODO ├── LICENSE ├── README └── mqtt.lua /TODO: -------------------------------------------------------------------------------- 1 | - CONNACK Return codes 2 | - Unsubscribe Payload 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This program is free software: you can redistribute it and/or modify 2 | it under the terms of the GNU Lesser General Public License as published by 3 | the Free Software Foundation, either version 3 of the License, or 4 | (at your option) any later version. 5 | 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License 12 | along with this program. If not, see . 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Wireshark-MQTT 2 | =========================== 3 | 4 | MQTT dissector for Wireshark was developed for debugging 5 | libemqtt (https://github.com/menudoproblema/libemqtt) 6 | 7 | 8 | 9 | Usage 10 | ===== 11 | 12 | $ wireshark -X lua_script:mqtt.lua 13 | 14 | If you want to install this as a plugin just copy the mqtt.lua to 15 | a wireshark plugin folder. 16 | In windows this could be %APPDATA%\Wireshark\plugins 17 | 18 | 19 | 20 | Example 21 | ======= 22 | 23 | MQ Telemetry Transport, Message Type: CONNECT, QoS: 0 24 | > Fixed Header 25 | 0001 .... = Message Type: 0x01 26 | .... 0... = DUP Flag: 0 27 | .... .00. = QoS Level: 0 28 | .... ...0 = Retain: 0 29 | Remain Length: 36 30 | 31 | > Variable Header 32 | Protocol Name: MQIsdp 33 | Protocol Version: 3 34 | > Flags 35 | 1... .... = Username Flag: 1 36 | .0.. .... = Password Flag: 0 37 | ..0. .... = Will Retain Flag: 0 38 | ...0 0... = Will QoS Flag: 0 39 | .... .0.. = Will Flag: 0 40 | .... ..1. = Clean Session Flag: 1 41 | Keep Alive (secs): 300 42 | 43 | > Payload 44 | Client ID: libemqtt pub 45 | Username: username 46 | 47 | -------------------------------------------------------------------------------- /mqtt.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- mqtt.lua is free software: you can redistribute it and/or modify 3 | -- it under the terms of the GNU Lesser General Public License as published by 4 | -- the Free Software Foundation, either version 3 of the License, or 5 | -- (at your option) any later version. 6 | -- 7 | -- mqtt.lua is distributed in the hope that it will be useful, 8 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | -- GNU General Public License for more details. 11 | -- 12 | -- You should have received a copy of the GNU General Public License 13 | -- along with mqtt.lua. If not, see . 14 | -- 15 | -- 16 | -- Copyright 2012 Vicente Ruiz Rodríguez . All rights reserved. 17 | -- 18 | 19 | do 20 | -- Create a new dissector 21 | MQTTPROTO = Proto("MQTT", "MQ Telemetry Transport") 22 | local bitw = require("bit") 23 | local f = MQTTPROTO.fields 24 | -- Fix header: byte 1 25 | f.message_type = ProtoField.uint8("mqtt.message_type", "Message Type", base.HEX, nil, 0xF0) 26 | f.dup = ProtoField.uint8("mqtt.dup", "DUP Flag", base.DEC, nil, 0x08) 27 | f.qos = ProtoField.uint8("mqtt.qos", "QoS Level", base.DEC, nil, 0x06) 28 | f.retain = ProtoField.uint8("mqtt.retain", "Retain", base.DEC, nil, 0x01) 29 | -- Fix header: byte 2 30 | f.remain_length = ProtoField.uint8("mqtt.remain_length", "Remain Length") 31 | 32 | -- Connect 33 | f.connect_protocol_name = ProtoField.string("mqtt.connect.protocol_name", "Protocol Name") 34 | f.connect_protocol_version = ProtoField.uint8("mqtt.connect.protocol_version", "Protocol Version") 35 | f.connect_username = ProtoField.uint8("mqtt.connect.username", "Username Flag", base.DEC, nil, 0x80) 36 | f.connect_password = ProtoField.uint8("mqtt.connect.password", "Password Flag", base.DEC, nil, 0x40) 37 | f.connect_will_retain = ProtoField.uint8("mqtt.connect.will_retain", "Will Retain Flag", base.DEC, nil, 0x20) 38 | f.connect_will_qos = ProtoField.uint8("mqtt.connect.will_qos", "Will QoS Flag", base.DEC, nil, 0x18) 39 | f.connect_will = ProtoField.uint8("mqtt.connect.will", "Will Flag", base.DEC, nil, 0x04) 40 | f.connect_clean_session = ProtoField.uint8("mqtt.connect.clean_session", "Clean Session Flag", base.DEC, nil, 0x02) 41 | f.connect_keep_alive = ProtoField.uint16("mqtt.connect.keep_alive", "Keep Alive (secs)") 42 | f.connect_payload_clientid = ProtoField.string("mqtt.connect.payload.clientid", "Client ID") 43 | f.connect_payload_username = ProtoField.string("mqtt.connect.payload.username", "Username") 44 | f.connect_payload_password = ProtoField.string("mqtt.connect.payload.password", "Password") 45 | 46 | -- Publish 47 | f.publish_topic = ProtoField.string("mqtt.publish.topic", "Topic") 48 | f.publish_message_id = ProtoField.uint16("mqtt.publish.message_id", "Message ID") 49 | f.publish_data = ProtoField.string("mqtt.publish.data", "Data") 50 | 51 | -- Subscribe 52 | f.subscribe_message_id = ProtoField.uint16("mqtt.subscribe.message_id", "Message ID") 53 | f.subscribe_topic = ProtoField.string("mqtt.subscribe.topic", "Topic") 54 | f.subscribe_qos = ProtoField.uint8("mqtt.subscribe.qos", "QoS") 55 | 56 | -- SubAck 57 | f.suback_qos = ProtoField.uint8("mqtt.suback.qos", "QoS") 58 | 59 | -- Suback 60 | f.suback_message_id = ProtoField.uint16("mqtt.suback.message_id", "Message ID") 61 | f.suback_qos = ProtoField.uint8("mqtt.suback.qos", "QoS") 62 | -- 63 | f.payload_data = ProtoField.bytes("mqtt.payload", "Payload Data") 64 | 65 | -- decoding of fixed header remaining length 66 | -- according to MQTT V3.1 67 | function lengthDecode(buffer, offset) 68 | local multiplier = 1 69 | local value = 0 70 | local digit = 0 71 | repeat 72 | digit = buffer(offset, 1):uint() 73 | offset = offset + 1 74 | value = value + bitw.band(digit,127) * multiplier 75 | multiplier = multiplier * 128 76 | until (bitw.band(digit,128) == 0) 77 | return offset, value 78 | end 79 | 80 | -- The dissector function 81 | function MQTTPROTO.dissector(buffer, pinfo, tree) 82 | pinfo.cols.protocol = "MQTT" 83 | local msg_types = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } 84 | msg_types[1] = "CONNECT" 85 | msg_types[2] = "CONNACK" 86 | msg_types[3] = "PUBLISH" 87 | msg_types[4] = "PUBACK" 88 | msg_types[5] = "PUBREC" 89 | msg_types[6] = "PUBREL" 90 | msg_types[7] = "PUBCOMP" 91 | msg_types[8] = "SUBSCRIBE" 92 | msg_types[9] = "SUBACK" 93 | msg_types[10] = "UNSUBSCRIBE" 94 | msg_types[11] = "UNSUBACK" 95 | msg_types[12] = "PINGREQ" 96 | msg_types[13] = "PINGRESP" 97 | msg_types[14] = "DISCONNECT" 98 | 99 | local msgtype = buffer(0, 1) 100 | 101 | local offset = 1 102 | local remain_length =0 103 | offset, remain_length = lengthDecode(buffer, offset) 104 | 105 | local msgindex = msgtype:bitfield(0,4) 106 | 107 | local subtree = tree:add(MQTTPROTO, buffer()) 108 | local fixheader_subtree = subtree:add("Fixed Header", nil) 109 | 110 | subtree:append_text(", Message Type: " .. msg_types[msgindex]) 111 | pinfo.cols.info:set(msg_types[msgindex]) 112 | 113 | fixheader_subtree:add(f.message_type, msgtype) 114 | fixheader_subtree:add(f.dup, msgtype) 115 | fixheader_subtree:add(f.qos, msgtype) 116 | fixheader_subtree:add(f.retain, msgtype) 117 | 118 | fixheader_subtree:add(f.remain_length, remain_length) 119 | 120 | local fixhdr_qos = msgtype:bitfield(5,2) 121 | subtree:append_text(", QoS: " .. fixhdr_qos) 122 | 123 | if(msgindex == 1) then -- CONNECT 124 | local varheader_subtree = subtree:add("Variable Header", nil) 125 | 126 | local name_len = buffer(offset, 2):uint() 127 | offset = offset + 2 128 | local name = buffer(offset, name_len) 129 | offset = offset + name_len 130 | local version = buffer(offset, 1) 131 | offset = offset + 1 132 | local flags = buffer(offset, 1) 133 | offset = offset + 1 134 | local keepalive = buffer(offset, 2) 135 | offset = offset + 2 136 | 137 | varheader_subtree:add(f.connect_protocol_name, name) 138 | varheader_subtree:add(f.connect_protocol_version, version) 139 | 140 | local flags_subtree = varheader_subtree:add("Flags", nil) 141 | flags_subtree:add(f.connect_username, flags) 142 | flags_subtree:add(f.connect_password, flags) 143 | flags_subtree:add(f.connect_will_retain, flags) 144 | flags_subtree:add(f.connect_will_qos, flags) 145 | flags_subtree:add(f.connect_will, flags) 146 | flags_subtree:add(f.connect_clean_session, flags) 147 | 148 | varheader_subtree:add(f.connect_keep_alive, keepalive) 149 | 150 | local payload_subtree = subtree:add("Payload", nil) 151 | -- Client ID 152 | local clientid_len = buffer(offset, 2):uint() 153 | offset = offset + 2 154 | local clientid = buffer(offset, clientid_len) 155 | offset = offset + clientid_len 156 | payload_subtree:add(f.connect_payload_clientid, clientid) 157 | -- Flags 158 | if(flags:bitfield(0) == 1) then -- Username flag is true 159 | local username_len = buffer(offset, 2):uint() 160 | offset = offset + 2 161 | local username = buffer(offset, username_len) 162 | offset = offset + username_len 163 | payload_subtree:add(f.connect_payload_username, username) 164 | end 165 | 166 | if(flags:bitfield(1) == 1) then -- Password flag is true 167 | local password_len = buffer(offset, 2):uint() 168 | offset = offset + 2 169 | local password = buffer(offset, password_len) 170 | offset = offset + password_len 171 | payload_subtree:add(f.connect_payload_password, password) 172 | end 173 | 174 | 175 | elseif(msgindex == 3) then -- PUBLISH 176 | local varhdr_init = offset -- For calculating variable header size 177 | local varheader_subtree = subtree:add("Variable Header", nil) 178 | 179 | local topic_len = buffer(offset, 2):uint() 180 | offset = offset + 2 181 | local topic = buffer(offset, topic_len) 182 | offset = offset + topic_len 183 | 184 | varheader_subtree:add(f.publish_topic, topic) 185 | 186 | if(fixhdr_qos > 0) then 187 | local message_id = buffer(offset, 2) 188 | offset = offset + 2 189 | varheader_subtree:add(f.publish_message_id, message_id) 190 | end 191 | 192 | local payload_subtree = subtree:add("Payload", nil) 193 | -- Data 194 | local data_len = remain_length - (offset - varhdr_init) 195 | local data = buffer(offset, data_len) 196 | offset = offset + data_len 197 | payload_subtree:add(f.publish_data, data) 198 | 199 | 200 | elseif(msgindex == 8 or msgindex == 10) then -- SUBSCRIBE & UNSUBSCRIBE 201 | local varheader_subtree = subtree:add("Variable Header", nil) 202 | 203 | local message_id = buffer(offset, 2) 204 | offset = offset + 2 205 | varheader_subtree:add(f.subscribe_message_id, message_id) 206 | 207 | local payload_subtree = subtree:add("Payload", nil) 208 | while(offset < buffer:len()) do 209 | local topic_len = buffer(offset, 2):uint() 210 | offset = offset + 2 211 | local topic = buffer(offset, topic_len) 212 | offset = offset + topic_len 213 | local qos = buffer(offset, 1) 214 | offset = offset + 1 215 | 216 | payload_subtree:add(f.subscribe_topic, topic) 217 | if(msgindex == 8) then -- QoS byte only for subscription 218 | payload_subtree:add(f.subscribe_qos, qos) 219 | end 220 | end 221 | 222 | elseif(msgindex == 9 or msgindex == 11) then -- SUBACK & UNSUBACK 223 | local varheader_subtree = subtree:add("Variable Header", nil) 224 | 225 | local message_id = buffer(offset, 2) 226 | offset = offset + 2 227 | varheader_subtree:add(f.suback_message_id, message_id) 228 | 229 | local payload_subtree = subtree:add("Payload", nil) 230 | while(offset < buffer:len()) do 231 | local qos = buffer(offset, 1) 232 | offset = offset + 1 233 | payload_subtree:add(f.suback_qos, qos); 234 | end 235 | 236 | else 237 | if((buffer:len()-offset) > 0) then 238 | local payload_subtree = subtree:add("Payload", nil) 239 | payload_subtree:add(f.payload_data, buffer(offset, buffer:len()-offset)) 240 | end 241 | end 242 | 243 | end 244 | 245 | -- Register the dissector 246 | tcp_table = DissectorTable.get("tcp.port") 247 | tcp_table:add(1883, MQTTPROTO) 248 | end 249 | --------------------------------------------------------------------------------