3 * Routines for Hotline Command-Response Transaction (HCrt)
4 * Protocol specifications (draft) are available here
5 * https://github.com/ShepardSiegel/hotline/tree/master/doc
7 * Copyright 2013 Dario Lombardo (lomato@gmail.com)
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
17 #include <epan/packet.h>
18 #include <epan/prefs.h>
19 #include <epan/expert.h>
22 static int proto_hcrt
;
24 #define HCRT_UDP_PORTS_DEFAULT "47000"
26 static unsigned ethertype_pref
= 0xf052;
28 static int hf_hcrt_header
;
29 static int hf_hcrt_message_tag
;
30 static int hf_hcrt_message_type
;
31 static int hf_hcrt_am
;
32 static int hf_hcrt_do
;
33 static int hf_hcrt_1st_dword_enable
;
34 static int hf_hcrt_last_dword_enable
;
35 static int hf_hcrt_resp_code
;
36 static int hf_hcrt_adl
;
37 static int hf_hcrt_last
;
38 static int hf_hcrt_body
;
39 static int hf_hcrt_addr_32
;
40 static int hf_hcrt_addr_64
;
41 static int hf_hcrt_data_32
;
42 static int hf_hcrt_data_64
;
43 static int hf_hcrt_command_nop
;
46 static int ett_hcrt_msg
;
47 static int ett_hcrt_hdr
;
48 static int ett_hcrt_body
;
50 static expert_field ei_hcrt_error
;
52 void proto_reg_handoff_hcrt(void);
53 void proto_register_hcrt(void);
55 static dissector_handle_t hcrt_handle
;
57 #define HCRT_HDR_LEN 4
60 #define HCRT_WRITE 0x1
62 #define HCRT_RESPONSE 0x3
64 #define ADDR_MODE_32 1
65 #define ADDR_MODE_64 2
68 static const value_string hcrt_message_types
[] = {
76 /* Addressing modes */
77 static const value_string hcrt_ams
[] = {
83 /* Discovery operations */
84 static const true_false_string hcrt_dos
= {
89 static const value_string dword_enable_vals
[] = {
100 static const value_string response_codes
[] = {
121 static void dissect_hcrt_body(tvbuff_t
* tvb
, proto_tree
* tree
, unsigned* offset
,
122 int type
, int addr_mode
, int adl
, int body_len
)
125 proto_tree
* hcrt_body_tree
;
128 ti_body
= proto_tree_add_item(tree
, hf_hcrt_body
, tvb
, *offset
, body_len
, ENC_NA
);
129 hcrt_body_tree
= proto_item_add_subtree(ti_body
, ett_hcrt_body
);
133 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_command_nop
, tvb
, *offset
,
137 if (addr_mode
== ADDR_MODE_32
) {
139 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_addr_32
, tvb
, *offset
,
140 4, ENC_LITTLE_ENDIAN
);
143 for (i
= 1; i
<= adl
; i
++) {
144 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_data_32
, tvb
,
145 *offset
+ i
* 4, 4, ENC_LITTLE_ENDIAN
);
149 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_addr_64
, tvb
, *offset
,
150 8, ENC_LITTLE_ENDIAN
);
153 for (i
= 1; i
<= adl
; i
++)
154 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_data_64
, tvb
,
155 *offset
+ i
* 8, 8, ENC_LITTLE_ENDIAN
);
159 if (addr_mode
== ADDR_MODE_32
) {
161 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_addr_32
, tvb
, *offset
, 4,
165 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_addr_64
, tvb
, *offset
, 8,
171 proto_tree_add_item(hcrt_body_tree
, hf_hcrt_command_nop
, tvb
, *offset
,
176 DISSECTOR_ASSERT_NOT_REACHED();
180 (*offset
) += body_len
;
183 /* Returns true if this is the last message */
184 static bool dissect_hcrt_header(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
,
185 unsigned* offset
, uint8_t b0_first
, uint8_t b0_current
)
188 proto_tree
* hcrt_hdr_tree
;
192 ti_hdr
= proto_tree_add_item(tree
, hf_hcrt_header
, tvb
, *offset
, 4, ENC_NA
);
193 hcrt_hdr_tree
= proto_item_add_subtree(ti_hdr
, ett_hcrt_hdr
);
195 if (b0_first
!= b0_current
) {
196 expert_add_info_format(pinfo
, hcrt_hdr_tree
, &ei_hcrt_error
,
197 "Invalid Byte 0 in Header. Must be equal in all HCrt messages. "
198 "Expected: %.2X, got: %.2X", b0_first
, b0_current
);
201 type
= (b0_current
& 0x30) >> 4;
205 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_message_tag
, tvb
,
208 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_message_type
, tvb
,
210 /* Addressing Mode */
211 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_am
, tvb
,
213 /* Discovery Operation */
214 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_do
, tvb
,
219 if (type
!= HCRT_RESPONSE
) {
220 /* 1st DWORD enable */
221 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_1st_dword_enable
, tvb
,
225 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_resp_code
, tvb
,
229 if (type
!= HCRT_RESPONSE
) {
230 /* Last DWORD enable */
231 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_last_dword_enable
, tvb
,
236 /* == Byte 2 & 3 == */
238 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_adl
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
240 proto_tree_add_item(hcrt_hdr_tree
, hf_hcrt_last
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
243 last
= (tvb_get_letohs(tvb
, *offset
) & 0x8000) != 0;
248 /* Return true if this is the last message */
249 static bool dissect_hcrt_message(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
,
250 unsigned* offset
, uint8_t b0_first
, int i
)
256 proto_tree
* hcrt_msg_tree
;
260 /* Save byte 0 of current packet */
261 b0_current
= tvb_get_uint8(tvb
, *offset
);
263 /* Get details from header */
264 adl
= tvb_get_letohs(tvb
, *offset
+ 2) & 0x0FFF;
265 addr_mode
= (1 + ((b0_current
& 0x40) >> 6));
266 type
= (b0_current
& 0x30) >> 4;
270 body_len
= 4 * addr_mode
* adl
;
273 body_len
= 4 * addr_mode
* (adl
+ 1);
276 body_len
= 4 * addr_mode
;
279 body_len
= 4 * addr_mode
* adl
;
282 DISSECTOR_ASSERT_NOT_REACHED();
286 hcrt_msg_tree
= proto_tree_add_subtree_format(tree
, tvb
, *offset
,
287 HCRT_HDR_LEN
+ body_len
, ett_hcrt_msg
, NULL
, "Message %d", i
);
289 last
= dissect_hcrt_header(tvb
, pinfo
, hcrt_msg_tree
, offset
, b0_first
, b0_current
);
290 dissect_hcrt_body(tvb
, hcrt_msg_tree
, offset
, type
, addr_mode
, adl
, body_len
);
295 static int dissect_hcrt(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data _U_
)
299 proto_tree
* hcrt_tree
;
306 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "HCrt");
307 col_clear(pinfo
->cinfo
, COL_INFO
);
309 /* Save byte 0 of first message. Will be checked against byte 0 of other messages */
310 b0_first
= tvb_get_uint8(tvb
, 0);
312 tag
= b0_first
& 0x0F;
313 type
= (b0_first
& 0x30) >> 4;
314 adl
= tvb_get_letohs(tvb
, 2) & 0x0FFF;
316 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Type: %s, Tag: 0x%X, ADL: %u",
317 val_to_str(type
, hcrt_message_types
, "Unknown (0x%02x)"), tag
, adl
);
320 if (type
== HCRT_READ
|| type
== HCRT_WRITE
) {
321 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ", Address: 0x%.8X", tvb_get_letohl(tvb
, 4));
323 if (type
== HCRT_WRITE
) {
324 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ", Data: 0x%.8X", tvb_get_letohl(tvb
, 8));
329 ti
= proto_tree_add_item(tree
, proto_hcrt
, tvb
, 0, -1, ENC_NA
);
330 hcrt_tree
= proto_item_add_subtree(ti
, ett_hcrt
);
332 while (!dissect_hcrt_message(tvb
, pinfo
, hcrt_tree
, &offset
, b0_first
, i
)) {
335 return tvb_captured_length(tvb
);
338 void proto_register_hcrt(void)
340 expert_module_t
* expert_hcrt
;
341 module_t
* hcrt_module
;
343 static hf_register_info hf
[] = {
345 { "Header", "hcrt.hdr",
350 { &hf_hcrt_message_tag
,
356 { &hf_hcrt_message_type
,
357 { "Type", "hcrt.type",
359 VALS(hcrt_message_types
), 0x30,
363 { "Addressing Mode", "hcrt.am",
365 VALS(hcrt_ams
), 0x40,
369 { "Discovery Operation", "hcrt.do",
371 TFS(&hcrt_dos
), 0x80,
374 { &hf_hcrt_1st_dword_enable
,
375 { "1st DWORD enable", "hcrt.first_dword_enable",
377 VALS(dword_enable_vals
), 0xF0,
380 { &hf_hcrt_last_dword_enable
,
381 { "Last DWORD enable", "hcrt.last_dword_enable",
383 VALS(dword_enable_vals
), 0x0F,
386 { &hf_hcrt_resp_code
,
387 { "Response code", "hcrt.response_code",
389 VALS(response_codes
), 0xF0,
399 { "Last message", "hcrt.last",
405 { "Body", "hcrt.body",
411 { "Address", "hcrt.address32",
417 { "Address", "hcrt.address64",
423 { "Data", "hcrt.data32",
429 { "Data", "hcrt.data64",
434 { &hf_hcrt_command_nop
,
435 { "Command", "hcrt.command_nop",
442 static ei_register_info ei
[] = {
443 { &ei_hcrt_error
, { "hcrt.error", PI_MALFORMED
, PI_ERROR
, "Unusual error code", EXPFILL
}}
446 /* Setup protocol subtree array */
447 static int* ett
[] = {
454 proto_hcrt
= proto_register_protocol ("Hotline Command-Response Transaction protocol", "HCrt", "hcrt");
456 proto_register_field_array(proto_hcrt
, hf
, array_length(hf
));
457 proto_register_subtree_array(ett
, array_length(ett
));
458 expert_hcrt
= expert_register_protocol(proto_hcrt
);
459 expert_register_field_array(expert_hcrt
, ei
, array_length(ei
));
461 hcrt_module
= prefs_register_protocol(proto_hcrt
, proto_reg_handoff_hcrt
);
462 prefs_register_uint_preference(hcrt_module
,
463 "dissector_ethertype",
465 "The ethernet type used for L2 communications",
466 10, ðertype_pref
);
468 hcrt_handle
= register_dissector("hcrt", dissect_hcrt
, proto_hcrt
);
471 void proto_reg_handoff_hcrt(void)
473 static bool hcrt_prefs_initialized
= false;
474 static int hcrt_ethertype
;
476 if (!hcrt_prefs_initialized
) {
477 /* Also register as a dissector that can be selected by a TCP port number via
479 dissector_add_for_decode_as_with_preference("tcp.port", hcrt_handle
);
480 dissector_add_uint_range_with_preference("udp.port", HCRT_UDP_PORTS_DEFAULT
, hcrt_handle
);
481 hcrt_prefs_initialized
= true;
483 dissector_delete_uint("ethertype", hcrt_ethertype
, hcrt_handle
);
486 hcrt_ethertype
= ethertype_pref
;
488 dissector_add_uint("ethertype", hcrt_ethertype
, hcrt_handle
);
492 * Editor modelines - https://www.wireshark.org/tools/modelines.html
497 * indent-tabs-mode: nil
500 * vi: set shiftwidth=4 tabstop=8 expandtab:
501 * :indentSize=4:tabSize=8:noTabs=true: