├── 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 |
--------------------------------------------------------------------------------