Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-dji-uav.c
blob084063470ecd3feea40b4e1ce69fb7db454ce86d
1 /* packet-dji-uav.c
2 * Routines for the disassembly of the command protocol for the
3 * DJI Phantom 2 Vision+ UAV
4 * http://www.dji.com/product/phantom-2-vision-plus
5 * and possibly others.
7 * Copyright 2014,2015 Joerg Mayer (see AUTHORS file)
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include "config.h"
20 #include <epan/packet.h>
21 /* TCP desegmentation */
22 #include "packet-tcp.h"
23 /* Request Response tracking */
24 #include <epan/conversation.h>
25 #include <epan/prefs.h>
27 void proto_register_djiuav(void);
28 void proto_reg_handoff_djiuav(void);
30 static dissector_handle_t djiuav_handle;
32 /* Enable desegmentation of djiuav over TCP */
33 static bool djiuav_desegment = true;
35 /* Command/Response tracking */
36 typedef struct _djiuav_conv_info_t {
37 wmem_map_t *pdus;
38 } djiuav_conv_info_t;
40 typedef struct _djiuav_transaction_t {
41 uint16_t seqno;
42 uint8_t command;
43 uint32_t request_frame;
44 uint32_t reply_frame;
45 nstime_t request_time;
46 } djiuav_transaction_t;
48 /* Finally: Protocol specific stuff */
50 /* protocol handles */
51 static int proto_djiuav;
53 /* ett handles */
54 static int ett_djiuav;
56 /* hf elements */
57 static int hf_djiuav_magic;
58 static int hf_djiuav_length;
59 static int hf_djiuav_flags;
60 static int hf_djiuav_seqno;
61 static int hf_djiuav_cmd;
62 static int hf_djiuav_checksum;
63 #if 0
64 static int hf_djiuav_cmd04_unknown;
65 static int hf_djiuav_resp04_unknown;
66 #endif
67 static int hf_djiuav_cmd20_unknown;
68 #if 0
69 static int hf_djiuav_resp20_unknown;
70 #endif
71 static int hf_djiuav_cmdunk;
72 static int hf_djiuav_respunk;
73 static int hf_djiuav_extradata;
74 /* hf request/response tracking */
75 static int hf_djiuav_response_in;
76 static int hf_djiuav_response_to;
77 static int hf_djiuav_response_time;
79 #define PROTO_SHORT_NAME "DJIUAV"
80 #define PROTO_LONG_NAME "DJI UAV Drone Control Protocol"
82 #define PORT_DJIUAV 2001 /* Not IANA registered */
84 static const value_string djiuav_pdu_type[] = {
85 { 0x20, "Set Time" },
87 { 0, NULL }
90 static void
91 request_response_handling(tvbuff_t *tvb, packet_info *pinfo, proto_tree *djiuav_tree,
92 uint32_t offset)
94 conversation_t *conversation;
95 djiuav_conv_info_t *djiuav_info;
96 djiuav_transaction_t *djiuav_trans;
98 uint16_t seq_no;
99 bool is_cmd;
100 uint8_t packet_type;
102 is_cmd = (pinfo->match_uint == pinfo->destport);
103 seq_no = tvb_get_letohs(tvb, offset + 4);
104 packet_type = tvb_get_uint8(tvb, offset + 6);
106 conversation = find_or_create_conversation(pinfo);
107 djiuav_info = (djiuav_conv_info_t *)conversation_get_proto_data(conversation, proto_djiuav);
108 if (!djiuav_info) {
109 djiuav_info = wmem_new(wmem_file_scope(), djiuav_conv_info_t);
110 djiuav_info->pdus=wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
112 conversation_add_proto_data(conversation, proto_djiuav, djiuav_info);
114 if (!pinfo->fd->visited) {
115 if (is_cmd) {
116 djiuav_trans=wmem_new(wmem_file_scope(), djiuav_transaction_t);
117 djiuav_trans->request_frame=pinfo->num;
118 djiuav_trans->reply_frame=0;
119 djiuav_trans->request_time=pinfo->abs_ts;
120 djiuav_trans->seqno=seq_no;
121 djiuav_trans->command=packet_type;
122 wmem_map_insert(djiuav_info->pdus, GUINT_TO_POINTER((unsigned)seq_no), (void *)djiuav_trans);
123 } else {
124 djiuav_trans=(djiuav_transaction_t *)wmem_map_lookup(djiuav_info->pdus, GUINT_TO_POINTER((unsigned)seq_no));
125 if (djiuav_trans) {
126 /* Special case: djiuav seems to send 0x24 replies with seqno 0 and without a request */
127 if (djiuav_trans->reply_frame == 0)
128 djiuav_trans->reply_frame=pinfo->num;
131 } else {
132 djiuav_trans=(djiuav_transaction_t *)wmem_map_lookup(djiuav_info->pdus, GUINT_TO_POINTER((unsigned)seq_no));
135 /* djiuav_trans may be 0 in case it's a reply without a matching request */
137 if (djiuav_tree && djiuav_trans) {
138 if (is_cmd) {
139 if (djiuav_trans->reply_frame) {
140 proto_item *it;
142 it = proto_tree_add_uint(djiuav_tree, hf_djiuav_response_in,
143 tvb, 0, 0, djiuav_trans->reply_frame);
144 proto_item_set_generated(it);
146 } else {
147 if (djiuav_trans->request_frame) {
148 proto_item *it;
149 nstime_t ns;
151 it = proto_tree_add_uint(djiuav_tree, hf_djiuav_response_to,
152 tvb, 0, 0, djiuav_trans->request_frame);
153 proto_item_set_generated(it);
155 nstime_delta(&ns, &pinfo->abs_ts, &djiuav_trans->request_time);
156 it = proto_tree_add_time(djiuav_tree, hf_djiuav_response_time, tvb, 0, 0, &ns);
157 proto_item_set_generated(it);
163 static int
164 dissect_djiuav_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
166 proto_item *ti;
167 proto_tree *djiuav_tree = NULL;
168 uint32_t offset = 0;
169 uint32_t pdu_length;
170 uint8_t packet_type;
171 bool is_cmd;
173 is_cmd = (pinfo->match_uint == pinfo->destport);
174 packet_type = tvb_get_uint8(tvb, 6);
176 col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_SHORT_NAME);
177 col_add_str(pinfo->cinfo, COL_INFO, is_cmd?"C: ":"R: ");
178 col_append_str(pinfo->cinfo, COL_INFO, val_to_str(packet_type,
179 djiuav_pdu_type, "Type 0x%02x"));
181 ti = proto_tree_add_item(tree, proto_djiuav, tvb, offset, -1, ENC_NA);
182 djiuav_tree = proto_item_add_subtree(ti, ett_djiuav);
184 request_response_handling(tvb, pinfo, djiuav_tree, offset);
186 if (tree) {
187 proto_tree_add_item(djiuav_tree, hf_djiuav_magic, tvb, offset, 2,
188 ENC_BIG_ENDIAN);
189 offset += 2;
191 pdu_length = tvb_get_uint8(tvb, offset);
192 proto_tree_add_item(djiuav_tree, hf_djiuav_length, tvb, offset, 1,
193 ENC_NA);
194 offset += 1;
196 proto_tree_add_item(djiuav_tree, hf_djiuav_flags, tvb, offset, 1,
197 ENC_NA);
198 offset += 1;
200 proto_tree_add_item(djiuav_tree, hf_djiuav_seqno, tvb, offset, 2,
201 ENC_LITTLE_ENDIAN);
202 offset += 2;
204 proto_tree_add_item(djiuav_tree, hf_djiuav_cmd, tvb, offset, 1,
205 ENC_NA);
206 offset += 1;
208 if (is_cmd) { /* Command */
209 switch (packet_type) {
210 case 0x20: /* Set time */
211 /* FIXME: Properly decode this: year(lo) year(hi) month date hour minute second */
212 proto_tree_add_item(djiuav_tree, hf_djiuav_cmd20_unknown, tvb, offset, 7,
213 ENC_NA);
214 offset += 7;
215 break;
216 default:
217 proto_tree_add_item(djiuav_tree, hf_djiuav_cmdunk, tvb, offset, pdu_length - 8,
218 ENC_NA);
219 offset += (pdu_length - 8);
220 break;
222 } else { /* Response */
223 switch (packet_type) {
224 default:
225 proto_tree_add_item(djiuav_tree, hf_djiuav_respunk, tvb,
226 offset, pdu_length - 8, ENC_NA);
227 offset += (pdu_length - 8);
228 break;
231 if (offset < pdu_length - 1) { /* We guessed wrong about the cmd len */
232 proto_tree_add_item(djiuav_tree, hf_djiuav_extradata, tvb, offset,
233 pdu_length - 1 - offset, ENC_NA);
234 offset += pdu_length - 1 - offset;
236 /* FIXME: calculate XOR and validate transmitted value */
237 proto_tree_add_checksum(djiuav_tree, tvb, offset, hf_djiuav_checksum, -1, NULL, pinfo, 0, ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS);
238 offset += 1;
241 return offset;
244 static bool
245 test_djiuav(tvbuff_t *tvb)
247 /* Minimum of 8 bytes, beginning with magic bytes 0x55BB */
248 if ( tvb_captured_length(tvb) < 8 /* Size of a command with empty data is at least 8 */
249 || tvb_get_ntohs(tvb, 0) != 0x55BB
251 return false;
253 return true;
256 /* Get the length of the full pdu */
257 static unsigned
258 get_djiuav_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
260 return tvb_get_uint8(tvb, offset + 2);
263 static int
264 dissect_djiuav_static(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
266 if ( !test_djiuav(tvb) ) {
267 return 0;
269 tcp_dissect_pdus(tvb, pinfo, tree, djiuav_desegment, 8,
270 get_djiuav_pdu_len, dissect_djiuav_pdu, data);
272 return tvb_captured_length(tvb);
275 void
276 proto_register_djiuav(void)
278 static hf_register_info hf[] = {
280 /* DJIUAV header */
281 { &hf_djiuav_magic,
282 { "Protocol Magic", "djiuav.magic", FT_UINT16, BASE_HEX, NULL,
283 0x0, NULL, HFILL }},
285 { &hf_djiuav_length,
286 { "PDU Length", "djiuav.length", FT_UINT8, BASE_HEX, NULL,
287 0x0, NULL, HFILL }},
289 { &hf_djiuav_flags,
290 { "Flags", "djiuav.flags", FT_UINT8, BASE_HEX, NULL,
291 0x0, NULL, HFILL }},
293 { &hf_djiuav_seqno,
294 { "Sequence No", "djiuav.seqno", FT_UINT16, BASE_DEC, NULL,
295 0x0, NULL, HFILL }},
297 { &hf_djiuav_cmd,
298 { "PDU Type", "djiuav.pdutype", FT_UINT8, BASE_HEX, VALS(djiuav_pdu_type),
299 0x0, NULL, HFILL }},
301 { &hf_djiuav_checksum,
302 { "Checksum", "djiuav.checksum", FT_UINT8, BASE_HEX, NULL,
303 0x0, NULL, HFILL }},
305 /* 0x04 */
306 #if 0
307 { &hf_djiuav_cmd04_unknown,
308 { "C04 Unknown", "djiuav.cmd04.unknown", FT_UINT8, BASE_HEX, NULL,
309 0x0, NULL, HFILL }},
311 { &hf_djiuav_resp04_unknown,
312 { "R04 Unknown", "djiuav.resp04.unknown", FT_UINT8, BASE_HEX, NULL,
313 0x0, NULL, HFILL }},
314 #endif
315 /* Set time */
316 { &hf_djiuav_cmd20_unknown,
317 { "Time in BCD", "djiuav.cmd04.bcdtime", FT_BYTES, BASE_NONE, NULL,
318 0x0, NULL, HFILL }},
319 #if 0
320 { &hf_djiuav_resp20_unknown,
321 { "R20 Unknown", "djiuav.resp04.unknown", FT_UINT8, BASE_HEX, NULL,
322 0x0, NULL, HFILL }},
323 #endif
324 /* CMD Unknown */
325 { &hf_djiuav_cmdunk,
326 { "C Unknown", "djiuav.cmd.unknown", FT_BYTES, BASE_NONE, NULL,
327 0x0, NULL, HFILL }},
329 /* RESP Unknown */
330 { &hf_djiuav_respunk,
331 { "R Unknown", "djiuav.resp.unknown", FT_BYTES, BASE_NONE, NULL,
332 0x0, NULL, HFILL }},
334 /* Extra Data (unexpected) */
335 { &hf_djiuav_extradata,
336 { "Unexpected", "djiuav.unexpected", FT_BYTES, BASE_NONE, NULL,
337 0x0, NULL, HFILL }},
339 /* Request - Response tracking */
340 { &hf_djiuav_response_in,
341 { "Response In", "djiuav.response_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE),
342 0x0, "Matching response in frame", HFILL }},
344 { &hf_djiuav_response_to,
345 { "Request In", "djiuav.response_to",
346 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST),
347 0x0, "Matching command in frame", HFILL }},
349 { &hf_djiuav_response_time,
350 { "Response Time", "djiuav.response_time",
351 FT_RELATIVE_TIME, BASE_NONE, NULL,
352 0x0, "Time between Command and matching Response", HFILL }},
354 static int *ett[] = {
355 &ett_djiuav,
357 module_t *djiuav_module;
359 proto_djiuav = proto_register_protocol(PROTO_LONG_NAME, PROTO_SHORT_NAME, "djiuav");
360 proto_register_field_array(proto_djiuav, hf, array_length(hf));
361 proto_register_subtree_array(ett, array_length(ett));
363 /* Preferences */
364 djiuav_module = prefs_register_protocol(proto_djiuav, NULL);
366 prefs_register_bool_preference(djiuav_module, "desegment",
367 "Reassemble DJIUAV messages",
368 "Whether DJIUAV should reassemble messages spanning multiple"
369 " TCP segments (required to get useful results)",
370 &djiuav_desegment);
372 djiuav_handle = register_dissector("djiuav", dissect_djiuav_static, proto_djiuav);
375 void
376 proto_reg_handoff_djiuav(void)
378 dissector_add_uint_with_preference("tcp.port", PORT_DJIUAV, djiuav_handle);
382 * Editor modelines - https://www.wireshark.org/tools/modelines.html
384 * Local variables:
385 * c-basic-offset: 8
386 * tab-width: 8
387 * indent-tabs-mode: t
388 * End:
390 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
391 * :indentSize=8:tabSize=8:noTabs=false: