2 * Routines for IDN dissection
3 * By Maxim Kropp <maxim.kropp@hotmail.de>
4 * Copyright 2017 Maxim Kropp
6 * Supervised by Matthias Frank <matthew@cs.uni-bonn.de>
7 * Copyright 2017 Matthias Frank, Institute of Computer Science 4, University of Bonn
9 * Stream Specification: https://www.ilda.com/resources/StandardsDocs/ILDA_IDN-Stream_rev001.pdf
10 * This specification only defines IDN messages, the other packet commands
11 * are part of the hello specification which is not released yet.
13 * Wireshark - Network traffic analyzer
14 * By Gerald Combs <gerald@wireshark.org>
15 * Copyright 1998 Gerald Combs
17 * SPDX-License-Identifier: GPL-2.0-or-later
21 #include <epan/packet.h>
22 #include <epan/conversation.h>
24 #include <wsutil/array.h>
28 #define MAX_CHANNELS 512
29 #define MAX_BUFFER 2048
32 #define IDNCMD_VOID 0x00
33 #define IDNCMD_PING_REQUEST 0x08
34 #define IDNCMD_PING_RESPONSE 0x09
35 #define IDNCMD_SCAN_REQUEST 0x10
36 #define IDNCMD_SCAN_RESPONSE 0x11
37 #define IDNCMD_SERVICEMAP_REQUEST 0x12
38 #define IDNCMD_SERVICEMAP_RESPONSE 0x13
39 #define IDNCMD_MESSAGE 0x40
40 #define IDNCMD_MESSAGE_ACKREQ 0x41
41 #define IDNCMD_MESSAGE_CLOSE 0x44
42 #define IDNCMD_MESSAGE_ACKREQ_CLOSE 0x45
43 #define IDNCMD_MESSAGE_ACK 0x47
46 #define IDNCT_VOID 0x00
47 #define IDNCT_LP_WAVE_SAMPLE 0x01
48 #define IDNCT_LP_FRAME_CHUNK 0x02
49 #define IDNCT_LP_FRAME_FF 0x03
50 #define IDNCT_LP_FRAME_SF 0xC0
51 #define IDNCT_OCTET_SEGMENT 0x10
52 #define IDNCT_OCTET_STRING 0x11
53 #define IDNCT_DIMMER_LEVELS 0x18
55 /* Service Modes (CONT = continuous stream, DISC = discrete stream) */
56 #define IDNSM_VOID 0x00
57 #define IDNSM_LP_GRAPHIC_CONT 0x01
58 #define IDNSM_LP_GRAPHIC_DISC 0x02
59 #define IDNSM_LP_EFFECTS_CONT 0x03
60 #define IDNSM_LP_EFFECTS_DISC 0x04
61 #define IDNSM_DMX512_CONT 0x05
62 #define IDNSM_DMX512_DISC 0x06
65 #define IDNTAG_PRECISION 0x4010
66 #define IDNTAG_WAVELENGTH_PREFIX 0x5C00
67 #define IDNTAG_INTENSITY 0x5C10
68 #define IDNTAG_BEAM_BRUSH 0x5C20
69 #define IDNTAG_BREAK_START 0x1000
70 #define IDNTAG_BREAK_END 0x100F
71 #define IDNTAG_SPACE_MOD_START 0x1100
72 #define IDNTAG_SPACE_MOD_END 0x11FF
73 #define IDNTAG_NOP 0x4000
74 #define IDNTAG_HINT0 0x4100
75 #define IDNTAG_HINT1 0x4101
76 #define IDNTAG_COLOR_START 0x5000
77 #define IDNTAG_COLOR_END 0x53FF
78 #define IDNTAG_COLOR_RED 0x527E
79 #define IDNTAG_COLOR_GREEN 0x5214
80 #define IDNTAG_COLOR_BLUE 0x51CC
81 #define IDNTAG_OPTIONAL_U1 0x51BD
82 #define IDNTAG_OPTIONAL_U2 0x5241
83 #define IDNTAG_OPTIONAL_U3 0x51E8
84 #define IDNTAG_OPTIONAL_U4 0x4201
85 #define IDNTAG_COORD_X 0x4200
86 #define IDNTAG_COORD_X_END 0x420F
87 #define IDNTAG_COORD_Y 0x4210
88 #define IDNTAG_COORD_Y_END 0x421F
89 #define IDNTAG_COORD_Z 0x4220
90 #define IDNTAG_COORD_Z_END 0x422F
91 #define IDNTAG_DIMMER_START 0x0040
92 #define IDNTAG_DIMMER_END 0x004F
95 #define IDNO_VOID_AREA 0xF
98 bool has_config_header
;
109 char *sample_column_string
;
113 } configuration_info
;
115 void proto_register_idn(void);
116 void proto_reg_handoff_idn(void);
118 static dissector_handle_t idn_handle
;
120 static int proto_idn
;
123 static int ett_idn_header_tree
;
124 static int ett_idn_scanreply_header_tree
;
125 static int ett_idn_channel_message_header_tree
;
126 static int ett_protocol_version
;
127 static int ett_status
;
128 static int ett_idn_cnl
;
129 static int ett_configuration_header
;
130 static int ett_chunk_header_tree
;
131 static int ett_chunk_header_flags
;
134 static int ett_dic_tree
;
136 static int ett_subdata
;
137 static int ett_dmx_subtree
;
140 static int hf_idn_command
;
141 static int hf_idn_flags
;
142 static int hf_idn_sequence
;
143 static int hf_idn_total_size
;
145 /* Scanreply Header */
146 static int hf_idn_struct_size
;
147 static int hf_idn_protocol_version
;
148 static int hf_idn_protocol_version_major
;
149 static int hf_idn_protocol_version_minor
;
150 static int hf_idn_status
;
151 static int hf_idn_malfn
;
152 static int hf_idn_offline
;
153 static int hf_idn_xcld
;
154 static int hf_idn_ocpd
;
155 static int hf_idn_rt
;
156 static int hf_idn_reserved8
;
157 static int hf_idn_unit_id
;
158 static int hf_idn_name
;
160 /* Service Map Response */
161 static int hf_idn_entry_size
;
162 static int hf_idn_relay_count
;
163 static int hf_idn_service_count
;
164 static int hf_idn_relay_number
;
166 /* Channel Message Header */
167 static int hf_idn_cnl
;
168 static int hf_idn_most_significant_bit_cnl
;
169 static int hf_idn_cclf
;
170 static int hf_idn_channel_id
;
171 static int hf_idn_chunk_type
;
172 static int hf_idn_timestamp
;
174 /* Configuration Header */
175 static int hf_idn_scwc
;
176 static int hf_idn_cfl
;
177 static int hf_idn_sdm
;
178 static int hf_idn_close
;
179 static int hf_idn_routing
;
180 static int hf_idn_service_id
;
181 static int hf_idn_service_mode
;
184 static int hf_idn_chunk_header_flags
;
185 static int hf_idn_two_bits_reserved_1
;
186 static int hf_idn_two_bits_reserved_2
;
187 static int hf_idn_three_bits_reserved
;
188 static int hf_idn_four_bits_reserved
;
189 static int hf_idn_scm
;
190 static int hf_idn_once
;
191 static int hf_idn_duration
;
192 static int hf_idn_chunk_data_sequence
;
193 static int hf_idn_offset
;
194 static int hf_idn_dlim
;
195 static int hf_idn_reserved
;
198 static int hf_idn_gts
;
199 static int hf_idn_gts_void
;
200 static int hf_idn_boundary
;
201 static int hf_idn_gts_word
;
202 static int hf_idn_gts_break
;
203 static int hf_idn_gts_space_modifier
;
204 static int hf_idn_gts_hint
;
205 static int hf_idn_gts_category
;
206 static int hf_idn_gts_subcategory
;
207 static int hf_idn_gts_identifier
;
208 static int hf_idn_gts_parameter
;
209 static int hf_idn_gts_glin
;
210 static int hf_idn_gts_clin
;
211 static int hf_idn_gts_cbal
;
212 static int hf_idn_gts_ctim
;
213 static int hf_idn_gts_nop
;
214 static int hf_idn_gts_precision
;
215 static int hf_idn_gts_cscl
;
216 static int hf_idn_gts_iscl
;
217 static int hf_idn_gts_sht
;
218 static int hf_idn_gts_u4
;
219 static int hf_idn_gts_x
;
220 static int hf_idn_gts_y
;
221 static int hf_idn_gts_z
;
222 static int hf_idn_gts_color
;
223 static int hf_idn_gts_wavelength_prefix
;
224 static int hf_idn_gts_intensity
;
225 static int hf_idn_gts_beam_brush
;
226 static int hf_idn_gts_sample
;
227 static int hf_idn_dmx_octet
;
228 static int hf_idn_dmx_identifier
;
229 static int hf_idn_dmx_parameter
;
230 static int hf_idn_dmx_void
;
231 static int hf_idn_octet
;
232 static int hf_idn_dmx_base
;
233 static int hf_idn_dmx_count
;
234 static int hf_idn_dmx_dls
;
235 static int hf_idn_dmx_unknown
;
237 /* Acknowledgement */
238 static int hf_idn_result_code
;
239 static int hf_idn_event_flags
;
241 static const value_string command_code
[] = {
242 { IDNCMD_VOID
, "VOID" },
243 { IDNCMD_PING_REQUEST
, "PING_REQUEST" },
244 { IDNCMD_PING_RESPONSE
, "PING_RESPONSE" },
245 { IDNCMD_SCAN_REQUEST
, "SCAN_REQUEST" },
246 { IDNCMD_SCAN_RESPONSE
, "SCAN_RESPONSE" },
247 { IDNCMD_SERVICEMAP_REQUEST
, "SERVICEMAP_REQUEST" },
248 { IDNCMD_SERVICEMAP_RESPONSE
, "SERVICEMAP_RESPONSE" },
249 { IDNCMD_MESSAGE
, "MESSAGE" },
250 { IDNCMD_MESSAGE_ACKREQ
, "MESSAGE_ACKREQ" },
251 { IDNCMD_MESSAGE_CLOSE
, "MESSAGE_CLOSE" },
252 { IDNCMD_MESSAGE_ACKREQ_CLOSE
, "MESSAGE_ACKREQ_CLOSE" },
253 { IDNCMD_MESSAGE_ACK
, "MESSAGE_ACK" },
256 static const value_string chunk_type
[] = {
257 { IDNCT_VOID
, "VOID" },
258 { IDNCT_LP_WAVE_SAMPLE
, "Laser Projector Wave Samples" },
259 { IDNCT_LP_FRAME_CHUNK
, "Laser Projector Frame Samples (entire chunk)" },
260 { IDNCT_LP_FRAME_FF
, "Laser Projector Frame Samples (first fragment)" },
261 { IDNCT_OCTET_SEGMENT
, "Octet Segment" },
262 { IDNCT_OCTET_STRING
, "Octet String" },
263 { IDNCT_DIMMER_LEVELS
, "Dimmer Levels" },
264 { IDNCT_LP_FRAME_SF
, "Laser Projector Frame Samples (sequel fragment)" },
267 static const value_string cfl_string
[] = {
268 { 0x30, "DATA_MATCH" },
273 static const value_string service_mode_string
[] = {
274 { IDNSM_VOID
, "VOID" },
275 { IDNSM_LP_GRAPHIC_CONT
, "Laser Projector Graphic (Continuous)" },
276 { IDNSM_LP_GRAPHIC_DISC
, "Laser Projector Graphic (Discrete)" },
277 { IDNSM_LP_EFFECTS_CONT
, "Laser Projector Effects (Continuous)" },
278 { IDNSM_LP_EFFECTS_DISC
, "Laser Projector Effects (Discrete)" },
279 { IDNSM_DMX512_CONT
, "DMX512 (Continuous)" },
280 { IDNSM_DMX512_DISC
, "DMX512 (Discrete)" },
283 static const value_string gts_glin
[] = {
284 { 0, "Projector specific" },
285 { 1, "Geometrically corrected and linear, aspect ratio 1:1" },
287 { 3, "No transformation" },
290 static const value_string gts_clin
[] = {
291 { 0, "Projector specific" },
292 { 1, "Power linear (half value SHALL be half power)" },
293 { 2, "Visually linear (half value SHALL be half brightness)" },
294 { 3, "No transformation" },
297 static const value_string gts_cbal
[] = {
298 { 0, "Projector specific" },
299 { 1, "White balanced" },
301 { 3, "No transformation" },
304 static const value_string gts_ctim
[] = {
305 { 0, "Projector specific" },
306 { 1, "Coordinates and colors correlated in time" },
308 { 3, "No transformation" },
311 static const value_string idn_color
[] = {
315 { 445, "Optional(U1), used as deep blue" },
316 { 577, "Optional(U2), used as yellow" },
317 { 488, "Optional(U3), used as cyan" },
320 static const value_string result_code
[] = {
321 { 0x00, "Message successfully received and passed to the IDN session" },
322 { 0xEB, "Empty (no message) close command without established connection" },
323 { 0xEC, "All sessions are occupied by clients (new connection refused)" },
324 { 0xED, "The client group is excluded from streaming" },
325 { 0xEE, "Invalid payload" },
326 { 0xEF, "Any other processing error" },
330 static int get_service_match(uint8_t flags
) {
334 static void determine_message_type(packet_info
*pinfo
, message_info
*minfo
) {
336 switch(minfo
->chunk_type
) {
338 col_append_str(pinfo
->cinfo
, COL_INFO
, "-VOID");
340 case IDNCT_LP_WAVE_SAMPLE
:
341 col_append_str(pinfo
->cinfo
, COL_INFO
, "-WAVE");
343 case IDNCT_LP_FRAME_CHUNK
:
344 col_append_str(pinfo
->cinfo
, COL_INFO
, "-FRAME");
346 case IDNCT_LP_FRAME_FF
:
347 col_append_str(pinfo
->cinfo
, COL_INFO
, "-FIRST");
349 case IDNCT_DIMMER_LEVELS
:
350 col_append_str(pinfo
->cinfo
, COL_INFO
, "-DMX");
353 case IDNCT_OCTET_STRING
:
354 col_append_str(pinfo
->cinfo
, COL_INFO
, "-DMX");
357 case IDNCT_OCTET_SEGMENT
:
358 col_append_str(pinfo
->cinfo
, COL_INFO
, "-DMX");
361 case IDNCT_LP_FRAME_SF
:
362 if(minfo
->has_config_header
) {
363 col_append_str(pinfo
->cinfo
, COL_INFO
, "-LAST");
365 col_append_str(pinfo
->cinfo
, COL_INFO
, "-SEQ");
369 col_append_str(pinfo
->cinfo
, COL_INFO
, "-UNKNOWN");
373 static void determine_color(uint16_t catsub
, configuration_info
*config
) {
374 char *column_str
= config
->sample_column_string
;
375 const int l
= (const int)strlen(column_str
);
377 case IDNTAG_COLOR_RED
:
378 snprintf(column_str
+l
, MAX_BUFFER
-l
, " R");
380 case IDNTAG_COLOR_GREEN
:
381 snprintf(column_str
+l
, MAX_BUFFER
-l
, " G");
383 case IDNTAG_COLOR_BLUE
:
384 snprintf(column_str
+l
, MAX_BUFFER
-l
, " B");
386 case IDNTAG_OPTIONAL_U1
:
387 snprintf(column_str
+l
, MAX_BUFFER
-l
, " U1");
389 case IDNTAG_OPTIONAL_U2
:
390 snprintf(column_str
+l
, MAX_BUFFER
-l
, " U2");
392 case IDNTAG_OPTIONAL_U3
:
393 snprintf(column_str
+l
, MAX_BUFFER
-l
, " U3");
396 snprintf(column_str
+l
, MAX_BUFFER
-l
, " C");
400 static int dissect_idn_message_acknowledgement(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
401 proto_tree
*idn_message_acknowledgement_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_idn_header_tree
, NULL
, "Message Acknowledgement");
402 proto_tree_add_item(idn_message_acknowledgement_tree
, hf_idn_struct_size
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
404 proto_tree_add_item(idn_message_acknowledgement_tree
, hf_idn_result_code
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
406 proto_tree_add_item(idn_message_acknowledgement_tree
, hf_idn_event_flags
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
411 static configuration_info
*get_configuration_info(packet_info
*pinfo
, int channel_id
) {
412 configuration_info
*config
= NULL
;
414 conversation_element_t
*conv_key
= wmem_alloc_array(pinfo
->pool
, conversation_element_t
, 6);
415 conv_key
[0].type
= CE_ADDRESS
;
416 conv_key
[0].addr_val
= pinfo
->src
;
417 conv_key
[1].type
= CE_PORT
;
418 conv_key
[1].port_val
= pinfo
->srcport
;
419 conv_key
[2].type
= CE_ADDRESS
;
420 conv_key
[2].addr_val
= pinfo
->dst
;
421 conv_key
[3].type
= CE_PORT
;
422 conv_key
[3].port_val
= pinfo
->destport
;
423 conv_key
[4].type
= CE_UINT
;
424 conv_key
[4].uint_val
= channel_id
;
425 conv_key
[5].type
= CE_CONVERSATION_TYPE
;
426 conv_key
[5].conversation_type_val
= CONVERSATION_IDN
;
428 conversation_t
*conv
= find_conversation_full(pinfo
->num
, conv_key
);
430 wmem_tree_t
*config_tree
= (wmem_tree_t
*)conversation_get_proto_data(conv
, proto_idn
);
432 config
= (configuration_info
*)wmem_tree_lookup32_le(config_tree
, pinfo
->num
);
436 col_append_str(pinfo
->cinfo
, COL_INFO
, ", no valid Configuration");
440 static int dissect_idn_dmx_sample_values(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_dmx_subtree
, uint16_t data_size
, int base
) {
443 char values
[MAX_BUFFER
];
445 for(i
=0; i
+16<=data_size
; i
+=16) {
448 l
+= snprintf(values
+l
, MAX_BUFFER
-l
, " %3d", tvb_get_uint8(tvb
, offset
+j
));
450 proto_tree_add_int_format(idn_dmx_subtree
, hf_idn_gts_sample
, tvb
, offset
, 16, 16, "%3d: %s", base
+i
, values
);
453 rest
= data_size
- i
;
456 for(j
=0; j
<rest
; j
++){
457 l
+= snprintf(values
+l
, MAX_BUFFER
-l
, " %3d", tvb_get_uint8(tvb
, offset
+j
));
459 proto_tree_add_int_format(idn_dmx_subtree
, hf_idn_gts_sample
, tvb
, offset
, rest
, rest
, "%3d: %s", base
+i
, values
);
465 static void set_laser_sample_values_string(tvbuff_t
*tvb
, int offset
, configuration_info
*config
, char *values
) {
469 if((config
->dic_precision
)[2] == 1)
470 l
+= snprintf(values
, MAX_BUFFER
, "%5d", tvb_get_uint16(tvb
, offset
, 2));
472 l
+= snprintf(values
, MAX_BUFFER
, "%5d", tvb_get_uint8(tvb
, offset
));
474 for(i
=1; i
<config
->sample_size
&& (l
< MAX_BUFFER
-100); i
++){
475 if((config
->dic_precision
)[i
+1] == 1) {
477 }else if((config
->dic_precision
)[i
+2] == 1) {
478 l
+= snprintf(values
+l
, MAX_BUFFER
-l
, " %5d", tvb_get_uint16(tvb
, offset
+i
, 2));
481 l
+= snprintf(values
+l
, MAX_BUFFER
-l
, " %5d", tvb_get_uint8(tvb
, offset
+i
));
486 static int dissect_idn_octet_segment(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
489 char values
[MAX_BUFFER
];
491 int data_size
= tvb_reported_length_remaining(tvb
, offset
);
492 proto_tree
*idn_samples_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, data_size
, ett_data
, NULL
, "Octets");
494 for(i
=0; i
+16<=data_size
; i
+=16) {
496 for(j
=0; j
<16 && (l
< MAX_BUFFER
-100); j
++){
497 l
+= snprintf(values
+l
, MAX_BUFFER
-l
, " %3d", tvb_get_int8(tvb
, offset
+j
));
499 proto_tree_add_int_format(idn_samples_tree
, hf_idn_gts_sample
, tvb
, offset
, 16, 16, "%s", values
);
502 rest
= data_size
- i
;
505 for(j
=0; j
<rest
&& (l
< MAX_BUFFER
-100); j
++){
506 l
+= snprintf(values
+l
, MAX_BUFFER
-l
, " %3d", tvb_get_int8(tvb
, offset
+j
));
508 proto_tree_add_int_format(idn_samples_tree
, hf_idn_gts_sample
, tvb
, offset
, rest
, rest
, "%s", values
);
514 static int dissect_idn_dmx_data(tvbuff_t
*tvb
, packet_info
*pinfo
, int offset
, proto_tree
*idn_tree
, configuration_info
*config
) {
516 int *count
= config
->count
;
517 int *base
= config
->base
;
519 int data_size
= tvb_reported_length_remaining(tvb
, offset
);
520 proto_tree
*idn_samples_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, data_size
, ett_data
, NULL
, "Channels");
521 proto_tree
*idn_dmx_subtree
;
523 for(i
=0; i
<config
->word_count
; i
++) {
524 base_value
= base
[i
]-1;
528 data_size
= count
[i
];
529 if(data_size
+ base_value
> MAX_CHANNELS
) {
530 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (Error: over %5d Channels)", MAX_CHANNELS
);
533 idn_dmx_subtree
= proto_tree_add_subtree_format(idn_samples_tree
, tvb
, offset
, data_size
, ett_dmx_subtree
, NULL
, "Range: %3d - %3d", base
[i
], base_value
+data_size
);
535 int base_size
= MAX_CHANNELS
- base_value
;
536 data_size
= tvb_reported_length_remaining(tvb
, offset
);
537 if(data_size
> base_size
) {
538 data_size
= base_size
;
540 if(data_size
+ base_value
> MAX_CHANNELS
) {
541 data_size
= MAX_CHANNELS
- base_value
;
543 idn_dmx_subtree
= proto_tree_add_subtree_format(idn_samples_tree
, tvb
, offset
, data_size
, ett_dmx_subtree
, NULL
, "Range: %3d - %3d", base
[i
], base_value
+data_size
);
545 offset
= dissect_idn_dmx_sample_values(tvb
, offset
, idn_dmx_subtree
, data_size
, base_value
);
550 static int dissect_idn_laser_data(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, configuration_info
*config
) {
551 char values
[MAX_BUFFER
];
554 int laser_data_size
= tvb_reported_length_remaining(tvb
, offset
);
556 if (config
->sample_size
== 0) {
557 /* TODO: log expert info error? */
561 int sample_size
= laser_data_size
/config
->sample_size
;
562 proto_tree
*idn_samples_tree
= proto_tree_add_subtree_format(idn_tree
, tvb
, offset
, laser_data_size
, ett_data
, NULL
, "Samples %s", config
->sample_column_string
);
563 proto_tree
*idn_samples_subtree
= NULL
;
565 for(i
=1; i
<=sample_size
; i
++) {
566 if((i
-1)%10 == 0 && i
+10 > sample_size
) {
567 idn_samples_subtree
= proto_tree_add_subtree_format(idn_samples_tree
, tvb
, offset
, tvb_reported_length_remaining(tvb
, offset
), ett_subdata
, NULL
, "Samples %3d - %3d", i
, sample_size
);
568 }else if((i
-1)%10 == 0) {
569 idn_samples_subtree
= proto_tree_add_subtree_format(idn_samples_tree
, tvb
, offset
, config
->sample_size
*10, ett_subdata
, NULL
, "Samples %3d - %3d", i
, i
+9);
571 set_laser_sample_values_string(tvb
, offset
, config
, values
);
572 proto_tree_add_int_format(idn_samples_subtree
, hf_idn_gts_sample
, tvb
, offset
, config
->sample_size
, config
->sample_size
, "Sample %3d: %s", i
, values
);
573 offset
+= config
->sample_size
;
578 static int dissect_idn_dimmer_levels_chunk_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
579 static int * const dimmer_levels_chunk_flags
[] = {
580 &hf_idn_two_bits_reserved_1
,
582 &hf_idn_four_bits_reserved
,
585 proto_tree
*chunk_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_chunk_header_tree
, NULL
, "Dimmer Levels Chunk Header");
586 proto_tree_add_bitmask(chunk_header_tree
, tvb
, offset
, hf_idn_chunk_header_flags
, ett_chunk_header_flags
, dimmer_levels_chunk_flags
, ENC_BIG_ENDIAN
);
588 proto_tree_add_item(chunk_header_tree
, hf_idn_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
590 proto_tree_add_item(chunk_header_tree
, hf_idn_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
592 proto_tree_add_item(chunk_header_tree
, hf_idn_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
597 static int dissect_idn_octet_string_chunk_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
598 static int * const octet_string_chunk_flags
[] = {
599 &hf_idn_two_bits_reserved_1
,
601 &hf_idn_four_bits_reserved
,
604 proto_tree
*chunk_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_chunk_header_tree
, NULL
, "Octet String Chunk Header");
605 proto_tree_add_bitmask(chunk_header_tree
, tvb
, offset
, hf_idn_chunk_header_flags
, ett_chunk_header_flags
, octet_string_chunk_flags
, ENC_BIG_ENDIAN
);
607 proto_tree_add_item(chunk_header_tree
, hf_idn_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
609 proto_tree_add_item(chunk_header_tree
, hf_idn_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
611 proto_tree_add_item(chunk_header_tree
, hf_idn_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
616 static int dissect_idn_octet_segment_chunk_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
617 static int * const octet_segment_chunk_flags
[] = {
618 &hf_idn_two_bits_reserved_1
,
620 &hf_idn_three_bits_reserved
,
624 proto_tree
*chunk_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_chunk_header_tree
, NULL
, "Octet Segment Chunk Header");
625 proto_tree_add_bitmask(chunk_header_tree
, tvb
, offset
, hf_idn_chunk_header_flags
, ett_chunk_header_flags
, octet_segment_chunk_flags
, ENC_BIG_ENDIAN
);
627 proto_tree_add_item(chunk_header_tree
, hf_idn_chunk_data_sequence
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
629 proto_tree_add_item(chunk_header_tree
, hf_idn_offset
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
634 static int dissect_idn_frame_chunk_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
635 static int * const frame_sample_chunk_flags
[] = {
636 &hf_idn_two_bits_reserved_1
,
638 &hf_idn_three_bits_reserved
,
642 proto_tree
*chunk_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_chunk_header_tree
, NULL
, "Frame Sample Chunk Header");
643 proto_tree_add_bitmask(chunk_header_tree
, tvb
, offset
, hf_idn_chunk_header_flags
, ett_chunk_header_flags
, frame_sample_chunk_flags
, ENC_BIG_ENDIAN
);
645 proto_tree_add_item(chunk_header_tree
, hf_idn_duration
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
650 static int dissect_idn_wave_chunk_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
651 static int * const wave_sample_chunk_flags
[] = {
652 &hf_idn_two_bits_reserved_1
,
654 &hf_idn_four_bits_reserved
,
657 proto_tree
*chunk_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_chunk_header_tree
, NULL
, "Wave Sample Chunk Header");
658 proto_tree_add_bitmask(chunk_header_tree
, tvb
, offset
, hf_idn_chunk_header_flags
, ett_chunk_header_flags
, wave_sample_chunk_flags
, ENC_BIG_ENDIAN
);
660 proto_tree_add_item(chunk_header_tree
, hf_idn_duration
, tvb
, offset
, 3, ENC_BIG_ENDIAN
);
665 static int dissect_idn_chunk_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, message_info
*minfo
) {
666 switch(minfo
->chunk_type
) {
667 case IDNCT_LP_WAVE_SAMPLE
:
668 offset
= dissect_idn_wave_chunk_header(tvb
, offset
, idn_tree
);
670 case IDNCT_LP_FRAME_CHUNK
:
671 offset
= dissect_idn_frame_chunk_header(tvb
, offset
, idn_tree
);
673 case IDNCT_LP_FRAME_FF
:
674 offset
= dissect_idn_frame_chunk_header(tvb
, offset
, idn_tree
);
676 case IDNCT_OCTET_SEGMENT
:
677 offset
= dissect_idn_octet_segment_chunk_header(tvb
, offset
, idn_tree
);
679 case IDNCT_OCTET_STRING
:
680 offset
= dissect_idn_octet_string_chunk_header(tvb
, offset
, idn_tree
);
682 case IDNCT_DIMMER_LEVELS
:
683 offset
= dissect_idn_dimmer_levels_chunk_header(tvb
, offset
, idn_tree
);
691 static int dissect_idn_dmx_gts(tvbuff_t
*tvb
, int offset
, proto_tree
*gts_tree
, const int hf_hdr
, int *dictionary_size
) {
692 static int * const gts
[] = {
693 &hf_idn_dmx_identifier
,
694 &hf_idn_dmx_parameter
,
697 proto_tree_add_bitmask(gts_tree
, tvb
, offset
, hf_hdr
, ett_dic
, gts
, ENC_BIG_ENDIAN
);
700 (*dictionary_size
)++;
705 static int dissect_idn_dimmer_level_subset(tvbuff_t
*tvb
, int offset
, proto_tree
*gts_tree
, configuration_info
*config
, int i
, int *dictionary_size
) {
706 uint8_t dls
= tvb_get_uint8(tvb
, offset
);
707 offset
= dissect_idn_dmx_gts(tvb
, offset
, gts_tree
, hf_idn_dmx_dls
, dictionary_size
);
710 proto_tree_add_item(gts_tree
, hf_idn_dmx_base
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
711 config
->base
[i
-1] = tvb_get_uint16(tvb
, offset
, 2);
713 (*dictionary_size
) += 2;
715 proto_tree_add_item(gts_tree
, hf_idn_dmx_count
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
716 config
->count
[i
-1] = tvb_get_uint8(tvb
, offset
);
718 (*dictionary_size
)++;
720 config
->count
[i
-1] = -1;
727 static int dissect_idn_dmx_dictionary(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, configuration_info
*config
) {
729 bool words_found
= 0;
730 int dictionary_size
= 0;
731 uint8_t idepar
; /* idetifier + parameter */
732 proto_tree
*gts_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, -1, ett_dic_tree
, NULL
, "Dictionary");
734 for(i
=1; i
<=config
->word_count
; i
++) {
735 idepar
= tvb_get_uint8(tvb
, offset
);
737 if(idepar
<= IDNO_VOID_AREA
) {
739 proto_tree_add_item(gts_tree
, hf_idn_dmx_void
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
741 dictionary_size
+= 1;
745 offset
= dissect_idn_dmx_gts(tvb
, offset
, gts_tree
, hf_idn_dmx_unknown
, NULL
);
746 for(j
=1; j
<=idepar
; j
++) {
747 proto_tree_add_item(gts_tree
, hf_idn_octet
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
749 dictionary_size
+= 1;
756 }else if(idepar
>= IDNTAG_DIMMER_START
&& idepar
<= IDNTAG_DIMMER_END
) {
757 offset
= dissect_idn_dimmer_level_subset(tvb
, offset
, gts_tree
, config
, i
, &dictionary_size
);
759 offset
= dissect_idn_dmx_gts(tvb
, offset
, gts_tree
, hf_idn_dmx_unknown
, &dictionary_size
);
762 if(i
== config
->word_count
&& !words_found
) {
763 curr_size
= dictionary_size
;
764 while(curr_size
%4 != 0 && i
> 0) {
771 proto_item_set_len(gts_tree
, dictionary_size
);
776 static int dissect_idn_laser_gts(tvbuff_t
*tvb
, int offset
, proto_tree
*gts_tree
, const int hf_hdr
, int *dictionary_size
, configuration_info
*config
, bool is_sample
) {
777 static int * const gts
[] = {
778 &hf_idn_gts_category
,
779 &hf_idn_gts_subcategory
,
780 &hf_idn_gts_identifier
,
781 &hf_idn_gts_parameter
,
785 proto_tree_add_bitmask(gts_tree
, tvb
, offset
, hf_hdr
, ett_dic
, gts
, ENC_BIG_ENDIAN
);
788 *dictionary_size
+= 2;
789 if(config
&& is_sample
)
790 config
->sample_size
++;
795 static int dissect_idn_x_area(tvbuff_t
*tvb
, int offset
, proto_tree
*gts_tree
, uint16_t catsub
, int *dictionary_size
, configuration_info
*config
) {
796 char *column_str
= config
->sample_column_string
;
797 const int l
= (const int)strlen(column_str
);
799 if(catsub
== IDNTAG_OPTIONAL_U4
) {
800 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_u4
, dictionary_size
, config
, 1);
801 snprintf(column_str
+l
, MAX_BUFFER
-l
, " U4");
803 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_x
, dictionary_size
, config
, 1);
804 snprintf(column_str
+l
, MAX_BUFFER
-l
, " X");
810 static int dissect_idn_laser_dictionary(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, configuration_info
*config
) {
812 int dictionary_size
= 0;
813 char *column_str
= config
->sample_column_string
;
814 uint16_t catsub
; /* category + subcategory */
815 proto_tree
*gts_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, -1, ett_dic_tree
, NULL
, "Dictionary");
817 snprintf(column_str
, MAX_BUFFER
, "(");
818 for(i
=1; i
<=config
->word_count
*2; i
++) {
819 catsub
= tvb_get_uint16(tvb
, offset
, 2);
820 const int l
= (const int)strlen(column_str
);
822 if(catsub
<= IDNO_VOID_AREA
) {
823 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_void
, &dictionary_size
, config
, 0);
825 for(j
=0; j
<catsub
; j
++) {
826 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_void
, &dictionary_size
, config
, 0);
829 }else if(catsub
== IDNTAG_PRECISION
) {
830 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_precision
, &dictionary_size
, config
, 1);
831 (config
->dic_precision
)[i
] = 1;
832 }else if(catsub
>= IDNTAG_BREAK_START
&& catsub
<= IDNTAG_BREAK_END
) {
833 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_break
, &dictionary_size
, config
, 0);
834 }else if(catsub
>= IDNTAG_SPACE_MOD_START
&& catsub
<= IDNTAG_SPACE_MOD_END
) {
835 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_space_modifier
, &dictionary_size
, config
, 0);
836 }else if(catsub
== IDNTAG_NOP
) {
837 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_nop
, &dictionary_size
, config
, 1);
838 snprintf(column_str
+l
, MAX_BUFFER
-l
, " NOP");
839 }else if(catsub
>= IDNTAG_HINT0
&& catsub
<= IDNTAG_HINT1
) {
840 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_hint
, &dictionary_size
, config
, 1);
841 snprintf(column_str
+l
, MAX_BUFFER
-l
, " H");
842 }else if(catsub
>= IDNTAG_COORD_X
&& catsub
<= IDNTAG_COORD_X_END
) {
843 offset
= dissect_idn_x_area(tvb
, offset
, gts_tree
, catsub
, &dictionary_size
, config
);
844 }else if(catsub
>= IDNTAG_COORD_Y
&& catsub
<= IDNTAG_COORD_Y_END
) {
845 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_y
, &dictionary_size
, config
, 1);
846 snprintf(column_str
+l
, MAX_BUFFER
-l
, " Y");
847 }else if(catsub
>= IDNTAG_COORD_Z
&& catsub
<= IDNTAG_COORD_Z_END
) {
848 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_z
, &dictionary_size
, config
, 1);
849 snprintf(column_str
+l
, MAX_BUFFER
-l
, " Z");
850 }else if(catsub
>= IDNTAG_COLOR_START
&& catsub
<= IDNTAG_COLOR_END
) {
851 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_color
, &dictionary_size
, config
, 1);
852 determine_color(catsub
, config
);
853 }else if(catsub
== IDNTAG_WAVELENGTH_PREFIX
) {
854 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_wavelength_prefix
, &dictionary_size
, config
, 1);
855 snprintf(column_str
+l
, MAX_BUFFER
-l
, " WP");
856 }else if(catsub
== IDNTAG_INTENSITY
) {
857 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_intensity
, &dictionary_size
, config
, 1);
858 snprintf(column_str
+l
, MAX_BUFFER
-l
, " I");
859 }else if(catsub
== IDNTAG_BEAM_BRUSH
) {
860 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts_beam_brush
, &dictionary_size
, config
, 1);
861 snprintf(column_str
+l
, MAX_BUFFER
-l
, " BB");
863 offset
= dissect_idn_laser_gts(tvb
, offset
, gts_tree
, hf_idn_gts
, &dictionary_size
, config
, 1);
864 snprintf(column_str
+l
, MAX_BUFFER
-l
, " U/R");
867 proto_item_set_len(gts_tree
, dictionary_size
);
868 const int l
= (const int)strlen(column_str
);
869 snprintf(column_str
+l
, MAX_BUFFER
-l
, " )");
874 static int dissect_idn_channel_configuration_header(tvbuff_t
*tvb
, packet_info
*pinfo
, int offset
, proto_tree
*idn_tree
, int channel_id
, configuration_info
**config_p
) {
875 conversation_t
*conv
;
878 static int * const channel_and_service_configuration_flags
[] = {
879 &hf_idn_two_bits_reserved_1
,
881 &hf_idn_two_bits_reserved_2
,
887 col_append_str(pinfo
->cinfo
, COL_INFO
, " (Configuration Header)");
888 proto_tree
*configuration_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_configuration_header
, NULL
, "Channel Configuration Header");
889 proto_tree_add_item(configuration_header_tree
, hf_idn_scwc
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
890 word_count
= tvb_get_uint8(tvb
, offset
);
892 proto_tree_add_bitmask(configuration_header_tree
, tvb
, offset
, hf_idn_cfl
, ett_cfl
, channel_and_service_configuration_flags
, ENC_BIG_ENDIAN
);
893 sdm
= get_service_match(tvb_get_uint8(tvb
, offset
));
895 proto_tree_add_item(configuration_header_tree
, hf_idn_service_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
897 proto_tree_add_item(configuration_header_tree
, hf_idn_service_mode
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
901 conversation_element_t
*conv_key
= wmem_alloc_array(pinfo
->pool
, conversation_element_t
, 6);
902 conv_key
[0].type
= CE_ADDRESS
;
903 conv_key
[0].addr_val
= pinfo
->src
;
904 conv_key
[1].type
= CE_PORT
;
905 conv_key
[1].port_val
= pinfo
->srcport
;
906 conv_key
[2].type
= CE_ADDRESS
;
907 conv_key
[2].addr_val
= pinfo
->dst
;
908 conv_key
[3].type
= CE_PORT
;
909 conv_key
[3].port_val
= pinfo
->destport
;
910 conv_key
[4].type
= CE_UINT
;
911 conv_key
[4].uint_val
= channel_id
;
912 conv_key
[5].type
= CE_CONVERSATION_TYPE
;
913 conv_key
[5].conversation_type_val
= CONVERSATION_IDN
;
915 configuration_info
*config
;
916 conv
= find_conversation_full(pinfo
->num
, conv_key
);
917 if (!(conv
&& conv
->setup_frame
== pinfo
->num
)) {
918 conv
= conversation_new_full(pinfo
->num
, conv_key
);
920 wmem_tree_t
*config_tree
= (wmem_tree_t
*)conversation_get_proto_data(conv
, proto_idn
);
922 config_tree
= wmem_tree_new(wmem_file_scope());
923 conversation_add_proto_data(conv
, proto_idn
, config_tree
);
925 /* XXX: It wastes some memory to allocate a new configuration if it
926 * hasn't changed since the last time it was sent, so we could use
927 * lookup32_le and see if it's the same as the previous, but that
928 * requires doing so after parsing the rest of the configuration.
930 config
= (configuration_info
*)wmem_tree_lookup32(config_tree
, pinfo
->num
);
932 /* sample size increments as we parse the dictionary, so reset.
933 * The other values shouldn't change, though we'll waste time
934 * overwriting the array with the same values.
936 config
->sample_size
= 0;
938 config
= wmem_new0(wmem_file_scope(), configuration_info
);
939 config
->word_count
= word_count
;
941 config
->sample_size
= 0;
942 config
->dic_precision
= wmem_alloc0_array(wmem_file_scope(), char, (255*2)+1);
943 config
->sample_column_string
= wmem_alloc0_array(wmem_file_scope(), char, MAX_BUFFER
);
944 config
->count
= wmem_alloc0_array(wmem_file_scope(), int, word_count
+1);
945 config
->base
= wmem_alloc0_array(wmem_file_scope(), int, word_count
+1);
946 wmem_tree_insert32(config_tree
, pinfo
->num
, config
);
954 static int dissect_idn_channel_configuration(tvbuff_t
*tvb
, packet_info
*pinfo
, int offset
, proto_tree
*idn_tree
, message_info
*minfo
, configuration_info
**config_p
) {
955 offset
= dissect_idn_channel_configuration_header(tvb
, pinfo
, offset
, idn_tree
, minfo
->channel_id
, config_p
);
957 configuration_info
*config
= *config_p
;
958 if(config
->word_count
> 0) {
959 if(minfo
->chunk_type
== IDNCT_OCTET_SEGMENT
) {
961 }else if(minfo
->is_dmx
) {
962 offset
= dissect_idn_dmx_dictionary(tvb
, offset
, idn_tree
, config
);
964 offset
= dissect_idn_laser_dictionary(tvb
, offset
, idn_tree
, config
);
971 static int dissect_idn_message_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, message_info
*minfo
) {
973 static int * const cnl_data
[] = {
974 &hf_idn_most_significant_bit_cnl
,
980 proto_tree
*idn_channel_message_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 8, ett_idn_channel_message_header_tree
, NULL
, "Channel Message Header");
981 proto_tree_add_item(idn_channel_message_header_tree
, hf_idn_total_size
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
982 minfo
->total_size
= tvb_get_uint16(tvb
, offset
, 2);
984 proto_tree_add_bitmask(idn_channel_message_header_tree
, tvb
, offset
, hf_idn_cnl
, ett_idn_cnl
, cnl_data
, ENC_BIG_ENDIAN
);
986 cnl
= tvb_get_uint8(tvb
, offset
);
987 minfo
->has_config_header
= cnl
& 0x40;
988 minfo
->channel_id
= cnl
& 0x3f;
991 proto_tree_add_item(idn_channel_message_header_tree
, hf_idn_chunk_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
992 minfo
->chunk_type
= tvb_get_uint8(tvb
, offset
);
994 proto_tree_add_item(idn_channel_message_header_tree
, hf_idn_timestamp
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
1000 static int dissect_idn_message(tvbuff_t
*tvb
, packet_info
*pinfo
, int offset
, proto_tree
*idn_tree
) {
1002 configuration_info
*config
= NULL
;
1003 message_info
*minfo
= wmem_new(pinfo
->pool
, message_info
);
1005 offset
= dissect_idn_message_header(tvb
, offset
, idn_tree
, minfo
);
1006 determine_message_type(pinfo
, minfo
);
1007 if(minfo
->total_size
== 8)
1010 if(minfo
->has_config_header
&& minfo
->chunk_type
!= IDNCT_LP_FRAME_SF
) {
1011 offset
= dissect_idn_channel_configuration(tvb
, pinfo
, offset
, idn_tree
, minfo
, &config
);
1012 }else if(minfo
->chunk_type
!= IDNCT_VOID
) {
1013 config
= get_configuration_info(pinfo
, minfo
->channel_id
);
1017 if(config
->word_count
== 0 && minfo
->chunk_type
!= IDNCT_OCTET_SEGMENT
) {
1018 col_append_str(pinfo
->cinfo
, COL_INFO
, ", SCWC is zero/unknown");
1022 if(minfo
->chunk_type
!= IDNCT_VOID
&& minfo
->chunk_type
!= IDNCT_LP_FRAME_SF
) {
1023 scm
= get_service_match(tvb_get_uint8(tvb
, offset
));
1025 offset
= dissect_idn_chunk_header(tvb
, offset
, idn_tree
, minfo
);
1027 if(config
->sdm
!= scm
) {
1028 col_append_str(pinfo
->cinfo
, COL_INFO
, ", SCM doesn't match SDM");
1031 }else if(minfo
->chunk_type
== IDNCT_VOID
) {
1035 if(minfo
->chunk_type
== IDNCT_OCTET_SEGMENT
) {
1036 offset
= dissect_idn_octet_segment(tvb
, offset
, idn_tree
);
1037 }else if(minfo
->is_dmx
) {
1038 offset
= dissect_idn_dmx_data(tvb
, pinfo
, offset
, idn_tree
, config
);
1040 offset
= dissect_idn_laser_data(tvb
, offset
, idn_tree
, config
);
1046 static int dissect_idn_servicemap_entry(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
1047 uint8_t service_id
= tvb_get_uint8(tvb
, offset
);
1048 proto_tree
*idn_servicemap_entry_tree
= NULL
;
1049 char *name
= (char *)tvb_get_string_enc(wmem_file_scope(), tvb
, offset
+4, 20, ENC_ASCII
);
1051 char tree_title
[MAX_BUFFER
];
1052 if(service_id
== 0) {
1053 snprintf(tree_title
, MAX_BUFFER
, "Relay Entry - %s", name
);
1054 idn_servicemap_entry_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 24, ett_idn_header_tree
, NULL
, tree_title
);
1056 snprintf(tree_title
, MAX_BUFFER
, "Service Entry - %s", name
);
1057 idn_servicemap_entry_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 24, ett_idn_header_tree
, NULL
, tree_title
);
1060 proto_tree_add_item(idn_servicemap_entry_tree
, hf_idn_service_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1062 proto_tree_add_item(idn_servicemap_entry_tree
, hf_idn_service_mode
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1064 proto_tree_add_item(idn_servicemap_entry_tree
, hf_idn_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1066 proto_tree_add_item(idn_servicemap_entry_tree
, hf_idn_relay_number
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1068 proto_tree_add_item(idn_servicemap_entry_tree
, hf_idn_name
, tvb
, offset
, 20, ENC_ASCII
);
1073 static int dissect_idn_servicemap_response_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, uint8_t *relay_count
, uint8_t *service_count
) {
1074 proto_tree
*idn_servicemap_response_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 4, ett_idn_header_tree
, NULL
, "Service Map Response Header");
1075 proto_tree_add_item(idn_servicemap_response_header_tree
, hf_idn_struct_size
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1077 proto_tree_add_item(idn_servicemap_response_header_tree
, hf_idn_entry_size
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1079 *relay_count
= tvb_get_uint8(tvb
, offset
);
1080 proto_tree_add_item(idn_servicemap_response_header_tree
, hf_idn_relay_count
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1082 *service_count
= tvb_get_uint8(tvb
, offset
);
1083 proto_tree_add_item(idn_servicemap_response_header_tree
, hf_idn_service_count
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1088 static int dissect_idn_servicemap_response(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
1089 uint8_t relay_count
, service_count
;
1090 uint16_t map_entries_size
;
1092 offset
= dissect_idn_servicemap_response_header(tvb
, offset
, idn_tree
, &relay_count
, &service_count
);
1093 map_entries_size
= relay_count
+ service_count
;
1094 proto_tree
*idn_servicemap_entries_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, map_entries_size
*24, ett_idn_header_tree
, NULL
, "Service Map Entries");
1095 for(int i
=0; i
<map_entries_size
; i
++)
1096 offset
= dissect_idn_servicemap_entry(tvb
, offset
, idn_servicemap_entries_tree
);
1101 static int dissect_idn_scan_response(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
) {
1102 static int * const protocol_version
[] = {
1103 &hf_idn_protocol_version_major
,
1104 &hf_idn_protocol_version_minor
,
1107 static int * const status
[] = {
1112 &hf_idn_three_bits_reserved
,
1117 proto_tree
*idn_scanreply_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, 40, ett_idn_header_tree
, NULL
, "Scan Response");
1118 proto_tree_add_item(idn_scanreply_header_tree
, hf_idn_struct_size
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1120 proto_tree_add_bitmask(idn_scanreply_header_tree
, tvb
, offset
, hf_idn_protocol_version
, ett_protocol_version
, protocol_version
, ENC_BIG_ENDIAN
);
1122 proto_tree_add_bitmask(idn_scanreply_header_tree
, tvb
, offset
, hf_idn_status
, ett_status
, status
, ENC_BIG_ENDIAN
);
1124 proto_tree_add_item(idn_scanreply_header_tree
, hf_idn_reserved8
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1126 proto_tree_add_item(idn_scanreply_header_tree
, hf_idn_unit_id
, tvb
, offset
, 16, ENC_NA
);
1128 proto_tree_add_item(idn_scanreply_header_tree
, hf_idn_name
, tvb
, offset
, 20, ENC_ASCII
);
1133 static int dissect_idn_header(tvbuff_t
*tvb
, int offset
, proto_tree
*idn_tree
, uint8_t packet_type
) {
1134 proto_tree
*idn_header_tree
= proto_tree_add_subtree(idn_tree
, tvb
, offset
, -1, ett_idn_header_tree
, NULL
, "IDN Header");
1135 proto_tree_add_item(idn_header_tree
, hf_idn_command
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1137 if(packet_type
== IDNCMD_VOID
|| packet_type
== IDNCMD_PING_RESPONSE
) {
1138 proto_item_set_len(idn_header_tree
, offset
);
1141 proto_tree_add_item(idn_header_tree
, hf_idn_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1143 proto_tree_add_item(idn_header_tree
, hf_idn_sequence
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1145 proto_item_set_len(idn_header_tree
, offset
);
1149 static int dissect_idn(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
) {
1151 uint8_t packet_type
= tvb_get_uint8(tvb
, 0);
1152 proto_item
*ti
= proto_tree_add_item(tree
, proto_idn
, tvb
, 0, -1, ENC_NA
);
1153 proto_tree
*idn_tree
= proto_item_add_subtree(ti
, ett_idn
);
1155 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "IDN");
1156 col_clear(pinfo
->cinfo
, COL_INFO
);
1157 col_add_str(pinfo
->cinfo
, COL_INFO
, val_to_str(packet_type
, command_code
, "Unknown (0x%02x)"));
1159 offset
= dissect_idn_header(tvb
, offset
, idn_tree
, packet_type
);
1161 switch (packet_type
) {
1162 case IDNCMD_SCAN_RESPONSE
:
1163 dissect_idn_scan_response(tvb
, offset
, idn_tree
);
1165 case IDNCMD_SERVICEMAP_RESPONSE
:
1166 offset
= dissect_idn_servicemap_response(tvb
, offset
, idn_tree
);
1168 case IDNCMD_MESSAGE
:
1169 case IDNCMD_MESSAGE_ACKREQ
:
1170 case IDNCMD_MESSAGE_CLOSE
:
1171 case IDNCMD_MESSAGE_ACKREQ_CLOSE
:
1172 offset
= dissect_idn_message(tvb
, pinfo
, offset
, idn_tree
);
1174 case IDNCMD_MESSAGE_ACK
:
1175 offset
= dissect_idn_message_acknowledgement(tvb
, offset
, idn_tree
);
1184 void proto_register_idn(void) {
1185 static hf_register_info hf
[] = {
1187 { "Command code", "idn.command",
1189 VALS(command_code
), 0x0,
1193 { "Flags", "idn.flags",
1199 { "Sequence counter", "idn.sequence",
1200 FT_UINT16
, BASE_DEC
,
1204 { &hf_idn_total_size
,
1205 { "Total Size", "idn.total_size",
1206 FT_UINT16
, BASE_DEC
,
1210 { &hf_idn_struct_size
,
1211 { "Struct Size", "idn.struct_size",
1216 { &hf_idn_protocol_version
,
1217 { "Protocol Version", "idn.protocol_version",
1222 { &hf_idn_protocol_version_major
,
1223 { "Major", "idn.protocol_version_major",
1228 { &hf_idn_protocol_version_minor
,
1229 { "Minor", "idn.protocol_version_minor",
1235 { "Status", "idn.status",
1241 { "Malfunction", "idn.status_malfn",
1247 { "Offline", "idn.offline",
1253 { "Excluded", "idn.xcld",
1259 { "Occupied", "idn.ocpd",
1265 { "Realtime", "idn.rt",
1270 { &hf_idn_reserved8
,
1271 { "Reserved", "idn.reserved8",
1277 { "Unit ID", "idn.unit_id",
1278 FT_BYTES
, SEP_SPACE
,
1283 { "Name", "idn.name",
1284 FT_STRING
, BASE_NONE
,
1288 { &hf_idn_entry_size
,
1289 { "Entry Size", "idn.entry_size",
1294 { &hf_idn_relay_count
,
1295 { "Relay Count", "idn.relay_count",
1300 { &hf_idn_service_count
,
1301 { "Service Count", "idn.service_count",
1307 { "Channel configuration and routing information (CNL)", "idn.cnl",
1312 { &hf_idn_most_significant_bit_cnl
,
1313 { "Most significant bit (always 1)", "idn.most_significant_bit_cnl",
1319 { "Channel Configuration and Last Fragment bit (CCLF)", "idn.cclf",
1324 { &hf_idn_channel_id
,
1325 { "Channel ID (opened Channels)", "idn.channel_id",
1330 { &hf_idn_chunk_type
,
1331 { "Chunk Type", "idn.chunk_type",
1333 VALS(chunk_type
), 0x0,
1336 { &hf_idn_timestamp
,
1337 { "Timestamp", "idn.timestamp",
1338 FT_UINT32
, BASE_DEC
,
1343 { "Service Configuration Word Count (SCWC)", "idn.scwc",
1349 { "Channel and service configuration Flags (CFL)", "idn.cfl",
1351 VALS(cfl_string
), 0x0,
1355 { "Service Data Match (SDM)", "idn.sdm",
1361 { "Close", "idn.close",
1367 { "Routing", "idn.routing",
1372 { &hf_idn_service_id
,
1373 { "Service ID", "idn.service_id",
1378 { &hf_idn_relay_number
,
1379 { "Relay Number", "idn.relay_number",
1384 { &hf_idn_service_mode
,
1385 { "Service Mode", "idn.service_mode",
1387 VALS(service_mode_string
), 0x0,
1390 { &hf_idn_chunk_header_flags
,
1391 { "Chunk Header Flags", "idn.chunk_header_flags",
1396 { &hf_idn_two_bits_reserved_1
,
1397 { "Reserved", "idn.zero_zero",
1402 { &hf_idn_two_bits_reserved_2
,
1403 { "Reserved", "idn.zero_zero",
1409 { "Service configuration match (SCM)", "idn.scm",
1414 { &hf_idn_three_bits_reserved
,
1415 { "Reserved", "idn.three_bit_reserved",
1420 { &hf_idn_four_bits_reserved
,
1421 { "Reserved", "idn.three_bit_reserved",
1427 { "Once", "idn.once",
1433 { "Delimiter (DLIM)", "idn.dlim",
1439 { "Duration", "idn.frame_sample_duration",
1440 FT_UINT24
, BASE_DEC
,
1444 { &hf_idn_chunk_data_sequence
,
1445 { "Sequence", "idn.octet_segment_sequence",
1451 { "Offset", "idn.offset",
1452 FT_UINT16
, BASE_HEX
,
1457 { "Reserved", "idn.reserved",
1463 { "Unknown", "idn.unknown",
1464 FT_UINT16
, BASE_HEX
,
1469 { "Void", "idn.gts_void",
1470 FT_UINT16
, BASE_HEX
,
1475 { "Void (32-bit boundary)", "idn.gts_boundary",
1476 FT_UINT32
, BASE_HEX
,
1481 { "16-bit word", "idn.gts_word",
1482 FT_UINT16
, BASE_HEX
,
1486 { &hf_idn_gts_break
,
1487 { "Break", "idn.gts_break",
1488 FT_UINT16
, BASE_HEX
,
1492 { &hf_idn_gts_space_modifier
,
1493 { "Space Modifier", "idn.gts_space_modifier",
1494 FT_UINT16
, BASE_HEX
,
1499 { "Hint", "idn.gts_hint",
1500 FT_UINT16
, BASE_HEX
,
1504 { &hf_idn_gts_category
,
1505 { "Category", "idn.gts_category",
1506 FT_UINT16
, BASE_DEC
,
1510 { &hf_idn_gts_subcategory
,
1511 { "Subcategory", "idn.gts_subcategory",
1512 FT_UINT16
, BASE_DEC
,
1516 { &hf_idn_gts_identifier
,
1517 { "Identifier", "idn.gts_identifier",
1518 FT_UINT16
, BASE_DEC
,
1522 { &hf_idn_gts_parameter
,
1523 { "Parameter", "idn.gts_parameter",
1524 FT_UINT16
, BASE_DEC
,
1529 { "Graphic Space Linearity (GLIN)", "idn.gts_glin",
1530 FT_UINT16
, BASE_DEC
,
1531 VALS(gts_glin
), 0x00C0,
1535 { "Color Space Linearity (CLIN)", "idn.gts_clin",
1536 FT_UINT16
, BASE_DEC
,
1537 VALS(gts_clin
), 0x0030,
1541 { "Color Balance (CBAL)", "idn.gts_cbal",
1542 FT_UINT16
, BASE_DEC
,
1543 VALS(gts_cbal
), 0x000C,
1547 { "Color Timing (CTIM)", "idn.gts_ctim",
1548 FT_UINT16
, BASE_DEC
,
1549 VALS(gts_ctim
), 0x0003,
1553 { "No Operation (NOP)", "idn.gts_nop",
1554 FT_UINT16
, BASE_HEX
,
1558 { &hf_idn_gts_precision
,
1559 { "Precision", "idn.gts_precision",
1560 FT_UINT16
, BASE_HEX
,
1565 { "Color scale (CSCL)", "idn.gts_cscl",
1566 FT_UINT16
, BASE_DEC
,
1571 { "Intensity scale (ISCL)", "idn.gts_iscl",
1572 FT_UINT16
, BASE_DEC
,
1577 { "Shutter (SHT)", "idn.gts_sht",
1578 FT_UINT16
, BASE_DEC
,
1583 { "Optional(U4), used as X-prime", "idn.gts_u4",
1584 FT_UINT16
, BASE_HEX
,
1590 FT_UINT16
, BASE_HEX
,
1596 FT_UINT16
, BASE_HEX
,
1602 FT_UINT16
, BASE_HEX
,
1606 { &hf_idn_gts_color
,
1607 { "Color", "idn.gts_color",
1608 FT_UINT16
, BASE_DEC
,
1609 VALS(idn_color
), 0x03FF,
1612 { &hf_idn_gts_wavelength_prefix
,
1613 { "Wavelength Prefix", "idn.gts_wavelength_prefix",
1614 FT_UINT16
, BASE_HEX
,
1618 { &hf_idn_gts_intensity
,
1619 { "Intensity/blanking", "idn.gts_intensity",
1620 FT_UINT16
, BASE_HEX
,
1624 { &hf_idn_gts_beam_brush
,
1625 { "Beam-Brush", "idn.gts_beam_brush",
1626 FT_UINT16
, BASE_HEX
,
1630 { &hf_idn_gts_sample
,
1631 { "Sample", "idn.gts_sample",
1636 { &hf_idn_dmx_octet
,
1637 { "Octet", "idn.gts_octet",
1642 { &hf_idn_dmx_identifier
,
1643 { "Identifier", "idn.gts_dmx_identifier",
1648 { &hf_idn_dmx_parameter
,
1649 { "Parameter", "idn.gts_dmx_parameter",
1655 { "Void", "idn.gts_dmx_void",
1661 { "Octet", "idn.gts_dmx_octet",
1667 { "Dimmer Level Subset", "idn.dmx_dls",
1673 { "Base", "idn.dmx_base",
1674 FT_UINT16
, BASE_DEC
,
1678 { &hf_idn_dmx_count
,
1679 { "Count", "idn.dmx_count",
1684 { &hf_idn_dmx_unknown
,
1685 { "Unknown", "idn.dmx_unknown",
1690 { &hf_idn_result_code
,
1691 { "Result Code", "idn.result_code",
1693 VALS(result_code
), 0x0,
1696 { &hf_idn_event_flags
,
1697 { "Event Flags", "idn.event_flags",
1698 FT_UINT16
, BASE_HEX
,
1704 static int *ett
[] = {
1706 &ett_idn_header_tree
,
1707 &ett_idn_scanreply_header_tree
,
1708 &ett_idn_channel_message_header_tree
,
1709 &ett_protocol_version
,
1713 &ett_configuration_header
,
1714 &ett_chunk_header_tree
,
1715 &ett_chunk_header_flags
,
1723 proto_idn
= proto_register_protocol (
1724 "Ilda Digital Network Protocol",
1729 proto_register_field_array(proto_idn
, hf
, array_length(hf
));
1730 proto_register_subtree_array(ett
, array_length(ett
));
1732 idn_handle
= register_dissector("idn", dissect_idn
, proto_idn
);
1735 void proto_reg_handoff_idn(void) {
1736 dissector_add_uint("udp.port", IDN_PORT
, idn_handle
);
1740 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1745 * indent-tabs-mode: t
1748 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1749 * :indentSize=8:tabSize=8:noTabs=false: