2 * Routines for ENTTEC packet disassembly
4 * Copyright (c) 2003,2004 by Erwin Rol <erwin@erwinrol.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1999 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <epan/packet.h>
18 #include <epan/prefs.h>
22 * http://www.enttec.com/docs/enttec_protocol.pdf
25 /* Define UDP/TCP ports for ENTTEC */
27 #define UDP_PORT_ENTTEC 0x0D05 /* Not IANA registered */
28 #define TCP_PORT_ENTTEC 0x0D05 /* Not IANA registered */
31 #define ENTTEC_HEAD_ESPR 0x45535052
32 #define ENTTEC_HEAD_ESPP 0x45535050
33 #define ENTTEC_HEAD_ESAP 0x45534150
34 #define ENTTEC_HEAD_ESDD 0x45534444
35 #define ENTTEC_HEAD_ESNC 0x45534E43
36 #define ENTTEC_HEAD_ESZZ 0x45535A5A
38 static const value_string enttec_head_vals
[] = {
39 { ENTTEC_HEAD_ESPR
, "Poll Reply" },
40 { ENTTEC_HEAD_ESPP
, "Poll" },
41 { ENTTEC_HEAD_ESAP
, "Ack/nAck" },
42 { ENTTEC_HEAD_ESDD
, "DMX Data" },
43 { ENTTEC_HEAD_ESNC
, "Config" },
44 { ENTTEC_HEAD_ESZZ
, "Reset" },
48 #define ENTTEC_DATA_TYPE_DMX 0x01
49 #define ENTTEC_DATA_TYPE_CHAN_VAL 0x02
50 #define ENTTEC_DATA_TYPE_RLE 0x04
52 static const value_string enttec_data_type_vals
[] = {
53 { ENTTEC_DATA_TYPE_DMX
, "Uncompressed DMX" },
54 { ENTTEC_DATA_TYPE_CHAN_VAL
, "Channel+Value" },
55 { ENTTEC_DATA_TYPE_RLE
, "RLE Compressed DMX" },
59 void proto_register_enttec(void);
60 void proto_reg_handoff_enttec(void);
62 static dissector_handle_t enttec_udp_handle
, enttec_tcp_handle
;
64 /* Define the enttec proto */
65 static int proto_enttec
;
68 static int hf_enttec_head
;
71 static int hf_enttec_poll_type
;
74 static int hf_enttec_poll_reply_mac
;
75 static int hf_enttec_poll_reply_node_type
;
76 static int hf_enttec_poll_reply_version
;
77 static int hf_enttec_poll_reply_switch
;
78 static int hf_enttec_poll_reply_name
;
79 static int hf_enttec_poll_reply_option
;
80 static int hf_enttec_poll_reply_tos
;
81 static int hf_enttec_poll_reply_ttl
;
84 static int hf_enttec_dmx_data_universe
;
85 static int hf_enttec_dmx_data_start_code
;
86 static int hf_enttec_dmx_data_type
;
87 static int hf_enttec_dmx_data_size
;
88 static int hf_enttec_dmx_data_data
;
89 static int hf_enttec_dmx_data_data_filter
;
90 static int hf_enttec_dmx_data_dmx_data
;
92 /* Define the tree for enttec */
93 static int ett_enttec
;
96 * Here are the global variables associated with the preferences
100 static int global_disp_chan_val_type
;
101 static int global_disp_col_count
= 16;
102 static int global_disp_chan_nr_type
;
105 dissect_enttec_poll_reply(tvbuff_t
*tvb
, unsigned offset
, proto_tree
*tree
)
107 proto_tree_add_item(tree
, hf_enttec_poll_reply_mac
, tvb
,
111 proto_tree_add_item(tree
, hf_enttec_poll_reply_node_type
, tvb
,
112 offset
, 2, ENC_BIG_ENDIAN
);
115 proto_tree_add_item(tree
, hf_enttec_poll_reply_version
, tvb
,
116 offset
, 1, ENC_BIG_ENDIAN
);
119 proto_tree_add_item(tree
, hf_enttec_poll_reply_switch
, tvb
,
120 offset
, 1, ENC_BIG_ENDIAN
);
123 proto_tree_add_item(tree
, hf_enttec_poll_reply_name
, tvb
,
124 offset
, 10, ENC_ASCII
);
127 proto_tree_add_item(tree
, hf_enttec_poll_reply_option
, tvb
,
128 offset
, 1, ENC_BIG_ENDIAN
);
131 proto_tree_add_item(tree
, hf_enttec_poll_reply_tos
, tvb
,
132 offset
, 1, ENC_BIG_ENDIAN
);
135 proto_tree_add_item(tree
, hf_enttec_poll_reply_ttl
, tvb
,
136 offset
, 1, ENC_BIG_ENDIAN
);
145 dissect_enttec_poll(tvbuff_t
*tvb
, unsigned offset
, proto_tree
*tree
)
147 proto_tree_add_item(tree
, hf_enttec_poll_type
, tvb
,
148 offset
, 1, ENC_BIG_ENDIAN
);
155 dissect_enttec_ack(tvbuff_t
*tvb _U_
, unsigned offset
, proto_tree
*tree _U_
)
162 dissect_enttec_dmx_data(tvbuff_t
*tvb
, packet_info
*pinfo
, unsigned offset
, proto_tree
*tree
)
164 static const char* chan_format
[] = {
169 static const char* string_format
[] = {
174 uint8_t *dmx_data
= (uint8_t *)wmem_alloc(pinfo
->pool
, 512 * sizeof(uint8_t));
175 uint16_t *dmx_data_offset
= (uint16_t *)wmem_alloc(pinfo
->pool
, 513 * sizeof(uint16_t)); /* 1 extra for last offset */
176 wmem_strbuf_t
*dmx_epstr
;
180 uint16_t length
,r
,c
,row_count
;
181 uint8_t v
,type
,count
;
182 uint16_t ci
,ui
,i
,start_offset
,end_offset
;
184 proto_tree_add_item(tree
, hf_enttec_dmx_data_universe
, tvb
,
185 offset
, 1, ENC_BIG_ENDIAN
);
188 proto_tree_add_item(tree
, hf_enttec_dmx_data_start_code
, tvb
,
189 offset
, 1, ENC_BIG_ENDIAN
);
192 type
= tvb_get_uint8(tvb
, offset
);
193 proto_tree_add_item(tree
, hf_enttec_dmx_data_type
, tvb
,
194 offset
, 1, ENC_BIG_ENDIAN
);
197 length
= tvb_get_ntohs(tvb
, offset
);
198 proto_tree_add_item(tree
, hf_enttec_dmx_data_size
, tvb
,
199 offset
, 2, ENC_BIG_ENDIAN
);
203 * XXX - we should handle a too-long length better.
208 if (type
== ENTTEC_DATA_TYPE_RLE
) {
209 /* uncompress the DMX data */
212 while (ci
< length
&& ui
< 512) {
213 v
= tvb_get_uint8(tvb
, offset
+ci
);
216 count
= tvb_get_uint8(tvb
, offset
+ci
);
218 v
= tvb_get_uint8(tvb
, offset
+ci
);
220 for (i
=0;i
< count
&& ui
< 512;i
++) {
222 dmx_data_offset
[ui
] = ci
-3;
225 } else if (v
== 0xFD) {
227 v
= tvb_get_uint8(tvb
, offset
+ci
);
229 dmx_data_offset
[ui
] = ci
;
234 dmx_data_offset
[ui
] = ci
;
239 dmx_data_offset
[ui
] = ci
;
241 for (ui
=0; ui
< length
;ui
++) {
242 dmx_data
[ui
] = tvb_get_uint8(tvb
, offset
+ui
);
243 dmx_data_offset
[ui
] = ui
;
245 dmx_data_offset
[ui
] = ui
;
249 if ((type
== ENTTEC_DATA_TYPE_DMX
|| type
== ENTTEC_DATA_TYPE_RLE
) && global_disp_col_count
> 0) {
250 hi
= proto_tree_add_item(tree
,
251 hf_enttec_dmx_data_data
,
257 si
= proto_item_add_subtree(hi
, ett_enttec
);
259 row_count
= (ui
/global_disp_col_count
) + ((ui
%global_disp_col_count
) == 0 ? 0 : 1);
260 dmx_epstr
= wmem_strbuf_create(pinfo
->pool
);
261 for (r
=0; r
< row_count
;r
++) {
262 for (c
=0;(c
< global_disp_col_count
) && (((r
*global_disp_col_count
)+c
) < ui
);c
++) {
263 if ((global_disp_col_count
> 1) && (c
% (global_disp_col_count
/2)) == 0) {
264 wmem_strbuf_append_c(dmx_epstr
, ' ');
266 v
= dmx_data
[(r
*global_disp_col_count
)+c
];
267 if (global_disp_chan_val_type
== 0) {
270 wmem_strbuf_append(dmx_epstr
, "FL ");
272 wmem_strbuf_append_printf(dmx_epstr
, chan_format
[global_disp_chan_val_type
], v
);
275 wmem_strbuf_append_printf(dmx_epstr
, chan_format
[global_disp_chan_val_type
], v
);
279 start_offset
= dmx_data_offset
[(r
*global_disp_col_count
)];
280 end_offset
= dmx_data_offset
[(r
*global_disp_col_count
)+c
];
282 proto_tree_add_none_format(si
,hf_enttec_dmx_data_dmx_data
, tvb
,
284 end_offset
-start_offset
,
285 string_format
[global_disp_chan_nr_type
], (r
*global_disp_col_count
)+1,
286 wmem_strbuf_get_str(dmx_epstr
));
287 wmem_strbuf_truncate(dmx_epstr
, 0);
290 item
= proto_tree_add_item(si
, hf_enttec_dmx_data_data_filter
, tvb
,
291 offset
, length
, ENC_NA
);
292 proto_item_set_hidden(item
);
297 proto_tree_add_item(tree
, hf_enttec_dmx_data_data_filter
, tvb
,
298 offset
, length
, ENC_NA
);
308 dissect_enttec_reset(tvbuff_t
*tvb _U_
, unsigned offset
, proto_tree
*tree _U_
)
315 dissect_enttec_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
319 proto_tree
*ti
, *enttec_tree
;
322 * If not enough bytes for the header word, not an ENTTEC packet.
324 if (!tvb_bytes_exist(tvb
, offset
, 4))
327 head
= tvb_get_ntohl(tvb
, offset
);
330 case ENTTEC_HEAD_ESPR
:
331 case ENTTEC_HEAD_ESPP
:
332 case ENTTEC_HEAD_ESAP
:
333 case ENTTEC_HEAD_ESDD
:
334 case ENTTEC_HEAD_ESZZ
:
342 * Not a known DMX-over-UDP packet type, so probably not ENTTEC.
347 /* Set the protocol column */
348 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ENTTEC");
350 /* Clear out stuff in the info column */
351 col_add_str(pinfo
->cinfo
, COL_INFO
,
352 val_to_str(head
, enttec_head_vals
, "Unknown (0x%08x)"));
354 ti
= proto_tree_add_item(tree
, proto_enttec
, tvb
, offset
, -1, ENC_NA
);
355 enttec_tree
= proto_item_add_subtree(ti
, ett_enttec
);
357 proto_tree_add_item(enttec_tree
, hf_enttec_head
, tvb
,
358 offset
, 4, ENC_BIG_ENDIAN
);
362 case ENTTEC_HEAD_ESPR
:
363 offset
= dissect_enttec_poll_reply( tvb
, offset
, enttec_tree
);
366 case ENTTEC_HEAD_ESPP
:
367 offset
= dissect_enttec_poll( tvb
, offset
, enttec_tree
);
370 case ENTTEC_HEAD_ESAP
:
371 offset
= dissect_enttec_ack( tvb
, offset
, enttec_tree
);
374 case ENTTEC_HEAD_ESDD
:
375 offset
= dissect_enttec_dmx_data( tvb
, pinfo
, offset
, enttec_tree
);
378 case ENTTEC_HEAD_ESZZ
:
379 offset
= dissect_enttec_reset( tvb
, offset
, enttec_tree
);
387 dissect_enttec_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
391 proto_tree
*ti
,*enttec_tree
;
394 * If not enough bytes for the header word, don't try to
395 * reassemble to get 4 bytes of header word, as we don't
396 * know whether this will be an ENTTEC Config packet.
398 if (!tvb_bytes_exist(tvb
, offset
, 4))
401 head
= tvb_get_ntohl(tvb
, offset
);
402 if (head
!= ENTTEC_HEAD_ESNC
) {
404 * Not a config packet, so probably not ENTTEC.
409 /* XXX - reassemble to end of connection? */
411 /* Set the protocol column */
412 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ENTTEC");
414 /* Clear out stuff in the info column */
415 col_add_str(pinfo
->cinfo
, COL_INFO
,
416 val_to_str(head
, enttec_head_vals
, "Unknown (0x%08x)"));
418 ti
= proto_tree_add_item(tree
, proto_enttec
, tvb
, offset
, -1, ENC_NA
);
419 enttec_tree
= proto_item_add_subtree(ti
, ett_enttec
);
421 proto_tree_add_item(enttec_tree
, hf_enttec_head
, tvb
,
422 offset
, 4, ENC_BIG_ENDIAN
);
423 /* XXX - dissect the rest of the packet */
425 return tvb_captured_length(tvb
);
429 proto_register_enttec(void)
431 static hf_register_info hf
[] = {
434 { "Head", "enttec.head",
435 FT_UINT32
, BASE_HEX
, VALS(enttec_head_vals
), 0x0,
437 { &hf_enttec_poll_reply_mac
,
438 { "MAC", "enttec.poll_reply.mac",
439 FT_ETHER
, BASE_NONE
, NULL
, 0x0,
441 { &hf_enttec_poll_reply_node_type
,
442 { "Node Type", "enttec.poll_reply.node_type",
443 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
445 { &hf_enttec_poll_reply_version
,
446 { "Version", "enttec.poll_reply.version",
447 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
449 { &hf_enttec_poll_reply_switch
,
450 { "Switch settings", "enttec.poll_reply.switch_settings",
451 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
453 { &hf_enttec_poll_reply_name
,
454 { "Name", "enttec.poll_reply.name",
455 FT_STRING
, BASE_NONE
, NULL
, 0x0,
457 { &hf_enttec_poll_reply_option
,
458 { "Option Field", "enttec.poll_reply.option_field",
459 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
461 { &hf_enttec_poll_reply_tos
,
462 { "TOS", "enttec.poll_reply.tos",
463 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
465 { &hf_enttec_poll_reply_ttl
,
466 { "TTL", "enttec.poll_reply.ttl",
467 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
469 { &hf_enttec_dmx_data_universe
,
470 { "Universe", "enttec.dmx_data.universe",
471 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
473 { &hf_enttec_dmx_data_start_code
,
474 { "Start Code", "enttec.dmx_data.start_code",
475 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
477 { &hf_enttec_dmx_data_type
,
478 { "Data Type", "enttec.dmx_data.type",
479 FT_UINT8
, BASE_HEX
, VALS(enttec_data_type_vals
), 0x0,
481 { &hf_enttec_dmx_data_size
,
482 { "Data Size", "enttec.dmx_data.size",
483 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
485 { &hf_enttec_dmx_data_data
,
486 { "DMX Data", "enttec.dmx_data.data",
487 FT_NONE
, BASE_NONE
, NULL
, 0x0,
489 { &hf_enttec_dmx_data_data_filter
,
490 { "DMX Data", "enttec.dmx_data.data_filter",
491 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
493 { &hf_enttec_dmx_data_dmx_data
,
494 { "DMX Data", "enttec.dmx_data.dmx_data",
495 FT_NONE
, BASE_NONE
, NULL
, 0x0,
497 { &hf_enttec_poll_type
,
498 { "Reply Type", "enttec.poll.reply_type",
499 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
503 static int *ett
[] = {
507 module_t
*enttec_module
;
509 static const enum_val_t disp_chan_val_types
[] = {
510 { "pro", "Percent", 0 },
511 { "hex", "Hexadecimal", 1 },
512 { "dec", "Decimal", 2 },
516 static const enum_val_t disp_chan_nr_types
[] = {
517 { "hex", "Hexadecimal", 0 },
518 { "dec", "Decimal", 1 },
522 static const enum_val_t col_count
[] = {
531 proto_enttec
= proto_register_protocol("ENTTEC", "ENTTEC","enttec");
532 proto_register_field_array(proto_enttec
,hf
,array_length(hf
));
533 proto_register_subtree_array(ett
,array_length(ett
));
535 enttec_udp_handle
= register_dissector("enttec.udp", dissect_enttec_udp
,proto_enttec
);
536 enttec_tcp_handle
= register_dissector("enttec.tcp", dissect_enttec_tcp
,proto_enttec
);
538 enttec_module
= prefs_register_protocol(proto_enttec
, NULL
);
540 prefs_register_enum_preference(enttec_module
, "dmx_disp_chan_val_type",
541 "DMX Display channel value type",
542 "The way DMX values are displayed",
543 &global_disp_chan_val_type
,
544 disp_chan_val_types
, false);
546 prefs_register_enum_preference(enttec_module
, "dmx_disp_chan_nr_type",
547 "DMX Display channel nr. type",
548 "The way DMX channel numbers are displayed",
549 &global_disp_chan_nr_type
,
550 disp_chan_nr_types
, false);
552 prefs_register_enum_preference(enttec_module
, "dmx_disp_col_count",
553 "DMX Display Column Count",
554 "The number of columns for the DMX display",
555 &global_disp_col_count
,
559 /* The registration hand-off routing */
561 proto_reg_handoff_enttec(void) {
562 dissector_add_uint_with_preference("tcp.port",TCP_PORT_ENTTEC
,enttec_tcp_handle
);
563 dissector_add_uint_with_preference("udp.port",UDP_PORT_ENTTEC
,enttec_udp_handle
);
567 * Editor modelines - https://www.wireshark.org/tools/modelines.html
572 * indent-tabs-mode: t
575 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
576 * :indentSize=8:tabSize=8:noTabs=false: