2 * Routines for ATH (Apache Tribes Heartbeat) dissection
3 * Copyright 2015, Eugene Adell <eugene.adell@d2-si.eu>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <epan/packet.h>
15 #include <epan/expert.h>
16 #include <epan/to_str.h>
18 void proto_register_ath(void);
19 void proto_reg_handoff_ath(void);
21 static dissector_handle_t ath_handle
;
23 /* IMPORTANT IMPLEMENTATION NOTES
25 * You need to be looking at:
27 * http://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html
29 * Tomcat clustering uses two protocols :
31 * - UDP heartbeats to maintain a status of all the members of the cluster
33 * - TCP RMI to send data across members
35 * This dissector is about UDP heartbeats, that we will call ATH, standing for
36 * Apache Tribes Heartbeat. Tribes is the name of the clustering libraries
37 * package of Apache Tomcat.
41 #define ATH_PORT 45564 /* Not IANA registered */
45 static int hf_ath_begin
;
46 static int hf_ath_padding
;
47 static int hf_ath_length
;
48 static int hf_ath_alive
;
49 static int hf_ath_port
;
50 static int hf_ath_sport
;
51 static int hf_ath_uport
;
52 static int hf_ath_hlen
;
53 static int hf_ath_ipv4
;
54 static int hf_ath_ipv6
;
55 static int hf_ath_clen
;
56 static int hf_ath_comm
;
57 static int hf_ath_dlen
;
58 static int hf_ath_domain
;
59 static int hf_ath_unique
;
60 static int hf_ath_plen
;
61 static int hf_ath_payload
;
62 static int hf_ath_end
;
66 static expert_field ei_ath_hlen_invalid
;
67 static expert_field ei_ath_hmark_invalid
;
70 test_ath(tvbuff_t
*tvb
)
72 /* Apache Tribes packets start with "TRIBES-B" in ASCII.
73 * tvb_strneql returns -1 if there aren't enough bytes.
75 if (tvb_strneql(tvb
, 0, "TRIBES-B", 8) != 0) {
83 dissect_ath(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
87 /* various lengths as reported in the packet itself */
93 /* detect the Tribes (Tomcat) version */
94 int tribes_version_mark
;
97 const char *info_srcaddr
= "";
98 const char *info_domain
= "";
99 const char *info_command
= "";
101 proto_item
*ti
, *hlen_item
;
102 proto_tree
*ath_tree
;
104 if (!test_ath(tvb
)) {
108 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ATH");
110 /* Clear out stuff in the info column */
111 col_clear(pinfo
->cinfo
,COL_INFO
);
113 ti
= proto_tree_add_item(tree
, proto_ath
, tvb
, 0, -1, ENC_NA
);
114 ath_tree
= proto_item_add_subtree(ti
, ett_ath
);
116 /* Determine the Tribes version, which means determining the Tomcat version.
117 * There are 2 versions : one for Tomcat 6, and one for Tomcat 7/8
118 * We know that Tomcat 6 packets end with "-E" (Ox2d 0x45 or 11589 in decimal)
119 * and Tomcat 7/8 packets end with "Ox01 0x00" (256 in decimal)
120 * This is why we read these 2 last bytes of the packet
122 tribes_version_mark
= tvb_get_ntohs(tvb
, tvb_reported_length(tvb
) - 2);
124 /* dissecting a Tomcat 6 packet
126 if (tribes_version_mark
== 11589) { /* "-E" */
130 proto_tree_add_item(ath_tree
, hf_ath_begin
, tvb
, offset
, 8, ENC_ASCII
);
135 proto_tree_add_item(ath_tree
, hf_ath_length
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
140 proto_tree_add_item(ath_tree
, hf_ath_alive
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
145 proto_tree_add_item(ath_tree
, hf_ath_port
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
150 proto_tree_add_item(ath_tree
, hf_ath_sport
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
155 hlen_item
= proto_tree_add_item(ath_tree
, hf_ath_hlen
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
156 hlen
= tvb_get_uint8(tvb
, offset
);
162 proto_tree_add_item(ath_tree
, hf_ath_ipv4
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
163 info_srcaddr
= tvb_ip_to_str(pinfo
->pool
, tvb
, offset
);
164 } else if (hlen
== 6) {
165 proto_tree_add_item(ath_tree
, hf_ath_ipv6
, tvb
, offset
, 6, ENC_NA
);
166 info_srcaddr
= tvb_ip6_to_str(pinfo
->pool
, tvb
, offset
);
168 expert_add_info(pinfo
, hlen_item
, &ei_ath_hlen_invalid
);
174 proto_tree_add_item_ret_int(ath_tree
, hf_ath_clen
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &clen
);
179 proto_tree_add_item(ath_tree
, hf_ath_comm
, tvb
, offset
, clen
, ENC_ASCII
);
181 info_command
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, clen
, ENC_ASCII
);
186 proto_tree_add_item_ret_int(ath_tree
, hf_ath_dlen
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &dlen
);
191 proto_tree_add_item(ath_tree
, hf_ath_domain
, tvb
, offset
, dlen
, ENC_ASCII
);
193 info_domain
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, dlen
, ENC_ASCII
);
198 proto_tree_add_item(ath_tree
, hf_ath_unique
, tvb
, offset
, 16, ENC_NA
);
203 proto_tree_add_item_ret_int(ath_tree
, hf_ath_plen
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &plen
);
208 proto_tree_add_item(ath_tree
, hf_ath_payload
, tvb
, offset
, plen
, ENC_ASCII
);
213 proto_tree_add_item(ath_tree
, hf_ath_end
, tvb
, offset
, 8, ENC_ASCII
);
216 /* dissecting a Tomcat 7/8 packet
218 else if (tribes_version_mark
== 256) {
222 proto_tree_add_item(ath_tree
, hf_ath_begin
, tvb
, offset
, 8, ENC_ASCII
);
225 proto_tree_add_item(ath_tree
, hf_ath_padding
, tvb
, offset
, 2, ENC_ASCII
|ENC_NA
);
230 proto_tree_add_item(ath_tree
, hf_ath_length
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
235 proto_tree_add_item(ath_tree
, hf_ath_alive
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
240 proto_tree_add_item(ath_tree
, hf_ath_port
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
245 proto_tree_add_item(ath_tree
, hf_ath_sport
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
248 /* UDP PORT, only in Tomcat 7/8
250 proto_tree_add_item(ath_tree
, hf_ath_uport
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
255 hlen_item
= proto_tree_add_item(ath_tree
, hf_ath_hlen
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
256 hlen
= tvb_get_uint8(tvb
, offset
);
262 proto_tree_add_item(ath_tree
, hf_ath_ipv4
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
263 info_srcaddr
= tvb_ip_to_str(pinfo
->pool
, tvb
, offset
);
264 } else if (hlen
== 6) {
265 proto_tree_add_item(ath_tree
, hf_ath_ipv6
, tvb
, offset
, 6, ENC_NA
);
266 info_srcaddr
= tvb_ip6_to_str(pinfo
->pool
, tvb
, offset
);
268 expert_add_info(pinfo
, hlen_item
, &ei_ath_hlen_invalid
);
274 proto_tree_add_item_ret_int(ath_tree
, hf_ath_clen
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &clen
);
279 proto_tree_add_item(ath_tree
, hf_ath_comm
, tvb
, offset
, clen
, ENC_ASCII
);
281 info_command
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, clen
, ENC_ASCII
);
286 proto_tree_add_item_ret_int(ath_tree
, hf_ath_dlen
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &dlen
);
291 proto_tree_add_item(ath_tree
, hf_ath_domain
, tvb
, offset
, dlen
, ENC_ASCII
);
293 info_domain
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, dlen
, ENC_ASCII
);
298 proto_tree_add_item(ath_tree
, hf_ath_unique
, tvb
, offset
, 16, ENC_NA
);
303 proto_tree_add_item_ret_int(ath_tree
, hf_ath_plen
, tvb
, offset
, 4, ENC_BIG_ENDIAN
, &plen
);
308 proto_tree_add_item(ath_tree
, hf_ath_payload
, tvb
, offset
, plen
, ENC_ASCII
);
313 proto_tree_add_item(ath_tree
, hf_ath_end
, tvb
, offset
, 8, ENC_ASCII
);
316 proto_tree_add_expert(tree
, pinfo
, &ei_ath_hmark_invalid
, tvb
, offset
, -1);
317 return tvb_captured_length(tvb
);
320 /* set the INFO column, and we're done !
322 if (strcmp(info_command
, "") != 0) {
323 if (strcmp(info_command
, "BABY-ALEX") == 0) {
324 if (strcmp(info_domain
, "") != 0) {
325 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s is leaving domain %s", info_srcaddr
, info_domain
);
327 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s is leaving default domain", info_srcaddr
);
330 if (strcmp(info_domain
, "") != 0) {
331 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Heartbeat from %s to domain %s", info_srcaddr
, info_domain
);
333 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Heartbeat from %s to default domain", info_srcaddr
);
337 if (strcmp(info_domain
, "") != 0) {
338 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Heartbeat from %s to domain %s", info_srcaddr
, info_domain
);
340 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Heartbeat from %s to default domain", info_srcaddr
);
344 return tvb_captured_length(tvb
);
348 proto_register_ath(void)
351 expert_module_t
* expert_ath
;
353 static hf_register_info hf
[] = {
355 { "Begin", "ath.begin", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Begin mark",
359 { "Padding", "ath.padding", FT_UINT16
, BASE_HEX
, NULL
, 0x0, NULL
,
363 { "Length", "ath.length", FT_UINT32
, BASE_DEC
, NULL
, 0x0, "Data Length",
367 { "Alive Time", "ath.alive", FT_UINT64
, BASE_DEC
, NULL
, 0x0, "Alive Time counter",
371 { "Port", "ath.port", FT_UINT32
, BASE_DEC
, NULL
, 0x0, "RMI Port",
375 { "Secure Port", "ath.sport", FT_INT32
, BASE_DEC
, NULL
, 0x0, "RMI Secure Port",
379 { "UDP Port", "ath.uport", FT_INT32
, BASE_DEC
, NULL
, 0x0, "RMI UDP Port",
383 { "Host Length", "ath.hlen", FT_INT8
, BASE_DEC
, NULL
, 0x0, "Host IP Length",
387 { "Host", "ath.ipv4", FT_IPv4
, BASE_NONE
, NULL
, 0x0, "IPv4 Host",
391 { "Host", "ath.ipv6", FT_IPv6
, BASE_NONE
, NULL
, 0x0, "IPv6 Host",
395 { "Command Length", "ath.clen", FT_INT32
, BASE_DEC
, NULL
, 0x0, "Command Length for members",
399 { "Command", "ath.comm", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Command for members",
403 { "Domain Length", "ath.dlen", FT_INT32
, BASE_DEC
, NULL
, 0x0, "Cluster Domain Length",
407 { "Domain", "ath.domain", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Cluster Domain",
411 { "uniqueId", "ath.unique", FT_BYTES
, BASE_NONE
, NULL
, 0x0, "UniqueID identifier",
415 { "Payload Length", "ath.plen", FT_INT32
, BASE_DEC
, NULL
, 0x0, "Packet Payload Length",
419 { "Payload", "ath.payload", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Packet Payload",
423 { "End", "ath.end", FT_STRING
, BASE_NONE
, NULL
, 0x0, "End mark",
428 static ei_register_info ei
[] = {
429 { &ei_ath_hlen_invalid
, { "ath.hlen.invalid", PI_MALFORMED
, PI_ERROR
, "Decode aborted: invalid IP length", EXPFILL
}},
430 { &ei_ath_hmark_invalid
, { "ath.hmark.invalid", PI_MALFORMED
, PI_ERROR
, "Decode aborted: not an ATH packet", EXPFILL
}},
433 static int *ett
[] = {
437 proto_ath
= proto_register_protocol("Apache Tribes Heartbeat Protocol", "ATH", "ath");
438 proto_register_field_array(proto_ath
, hf
, array_length(hf
));
439 proto_register_subtree_array(ett
, array_length(ett
));
440 expert_ath
= expert_register_protocol(proto_ath
);
441 expert_register_field_array(expert_ath
, ei
, array_length(ei
));
443 ath_handle
= register_dissector("ath", dissect_ath
, proto_ath
);
447 proto_reg_handoff_ath(void)
449 dissector_add_uint_with_preference("udp.port", ATH_PORT
, ath_handle
);
453 * Editor modelines - https://www.wireshark.org/tools/modelines.html
458 * indent-tabs-mode: nil
461 * vi: set shiftwidth=2 tabstop=8 expandtab:
462 * :indentSize=2:tabSize=8:noTabs=true: