/src/wireshark/epan/dissectors/packet-ath.c
Line | Count | Source |
1 | | /* packet-ath.c |
2 | | * Routines for ATH (Apache Tribes Heartbeat) dissection |
3 | | * Copyright 2015, Eugene Adell <eugene.adell@d2-si.eu> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | | |
14 | | #include <epan/packet.h> |
15 | | #include <epan/expert.h> |
16 | | #include <epan/to_str.h> |
17 | | |
18 | | void proto_register_ath(void); |
19 | | void proto_reg_handoff_ath(void); |
20 | | |
21 | | static dissector_handle_t ath_handle; |
22 | | |
23 | | /* IMPORTANT IMPLEMENTATION NOTES |
24 | | * |
25 | | * You need to be looking at: |
26 | | * |
27 | | * http://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html |
28 | | * |
29 | | * Tomcat clustering uses two protocols : |
30 | | * |
31 | | * - UDP heartbeats to maintain a status of all the members of the cluster |
32 | | * |
33 | | * - TCP RMI to send data across members |
34 | | * |
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. |
38 | | * |
39 | | */ |
40 | | |
41 | 14 | #define ATH_PORT 45564 /* Not IANA registered */ |
42 | | |
43 | | static int proto_ath; |
44 | | |
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; |
63 | | |
64 | | static int ett_ath; |
65 | | |
66 | | static expert_field ei_ath_hlen_invalid; |
67 | | static expert_field ei_ath_hmark_invalid; |
68 | | |
69 | | static bool |
70 | | test_ath(tvbuff_t *tvb) |
71 | 0 | { |
72 | | /* Apache Tribes packets start with "TRIBES-B" in ASCII. |
73 | | * tvb_strneql returns -1 if there aren't enough bytes. |
74 | | */ |
75 | 0 | if (tvb_strneql(tvb, 0, "TRIBES-B", 8) != 0) { |
76 | 0 | return false; |
77 | 0 | } |
78 | | |
79 | 0 | return true; |
80 | 0 | } |
81 | | |
82 | | static int |
83 | | dissect_ath(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
84 | 0 | { |
85 | 0 | int offset = 0; |
86 | | |
87 | | /* various lengths as reported in the packet itself */ |
88 | 0 | uint8_t hlen = 0; |
89 | 0 | int32_t clen = 0; |
90 | 0 | int32_t dlen = 0; |
91 | 0 | int32_t plen = 0; |
92 | | |
93 | | /* detect the Tribes (Tomcat) version */ |
94 | 0 | int tribes_version_mark; |
95 | | |
96 | | /* store the info */ |
97 | 0 | const char *info_srcaddr = ""; |
98 | 0 | const char *info_domain = ""; |
99 | 0 | const char *info_command = ""; |
100 | |
|
101 | 0 | proto_item *ti, *hlen_item; |
102 | 0 | proto_tree *ath_tree; |
103 | |
|
104 | 0 | if (!test_ath(tvb)) { |
105 | 0 | return 0; |
106 | 0 | } |
107 | | |
108 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATH"); |
109 | | |
110 | | /* Clear out stuff in the info column */ |
111 | 0 | col_clear(pinfo->cinfo,COL_INFO); |
112 | |
|
113 | 0 | ti = proto_tree_add_item(tree, proto_ath, tvb, 0, -1, ENC_NA); |
114 | 0 | ath_tree = proto_item_add_subtree(ti, ett_ath); |
115 | | |
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 |
121 | | */ |
122 | 0 | tribes_version_mark = tvb_get_ntohs(tvb, tvb_reported_length(tvb) - 2); |
123 | | |
124 | | /* dissecting a Tomcat 6 packet |
125 | | */ |
126 | 0 | if (tribes_version_mark == 11589) { /* "-E" */ |
127 | | |
128 | | /* BEGIN |
129 | | */ |
130 | 0 | proto_tree_add_item(ath_tree, hf_ath_begin, tvb, offset, 8, ENC_ASCII); |
131 | 0 | offset += 8; |
132 | | |
133 | | /* LENGTH |
134 | | */ |
135 | 0 | proto_tree_add_item(ath_tree, hf_ath_length, tvb, offset, 4, ENC_BIG_ENDIAN); |
136 | 0 | offset += 4; |
137 | | |
138 | | /* ALIVE TIME |
139 | | */ |
140 | 0 | proto_tree_add_item(ath_tree, hf_ath_alive, tvb, offset, 8, ENC_BIG_ENDIAN); |
141 | 0 | offset += 8; |
142 | | |
143 | | /* PORT |
144 | | */ |
145 | 0 | proto_tree_add_item(ath_tree, hf_ath_port, tvb, offset, 4, ENC_BIG_ENDIAN); |
146 | 0 | offset += 4; |
147 | | |
148 | | /* SECURE PORT |
149 | | */ |
150 | 0 | proto_tree_add_item(ath_tree, hf_ath_sport, tvb, offset, 4, ENC_BIG_ENDIAN); |
151 | 0 | offset += 4; |
152 | | |
153 | | /* HOST LENGTH |
154 | | */ |
155 | 0 | hlen_item = proto_tree_add_item(ath_tree, hf_ath_hlen, tvb, offset, 1, ENC_BIG_ENDIAN); |
156 | 0 | hlen = tvb_get_uint8(tvb, offset); |
157 | 0 | offset += 1; |
158 | | |
159 | | /* HOST |
160 | | */ |
161 | 0 | if (hlen == 4) { |
162 | 0 | proto_tree_add_item(ath_tree, hf_ath_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN); |
163 | 0 | info_srcaddr = tvb_ip_to_str(pinfo->pool, tvb, offset); |
164 | 0 | } else if (hlen == 6) { |
165 | 0 | proto_tree_add_item(ath_tree, hf_ath_ipv6, tvb, offset, 6, ENC_NA); |
166 | 0 | info_srcaddr = tvb_ip6_to_str(pinfo->pool, tvb, offset); |
167 | 0 | } else { |
168 | 0 | expert_add_info(pinfo, hlen_item, &ei_ath_hlen_invalid); |
169 | 0 | } |
170 | 0 | offset += hlen; |
171 | | |
172 | | /* COMMAND LENGTH |
173 | | */ |
174 | 0 | proto_tree_add_item_ret_int(ath_tree, hf_ath_clen, tvb, offset, 4, ENC_BIG_ENDIAN, &clen); |
175 | 0 | offset += 4; |
176 | | |
177 | | /* COMMAND |
178 | | */ |
179 | 0 | proto_tree_add_item(ath_tree, hf_ath_comm, tvb, offset, clen, ENC_ASCII); |
180 | 0 | if (clen != -1) |
181 | 0 | info_command = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, clen, ENC_ASCII); |
182 | 0 | offset += clen; |
183 | | |
184 | | /* DOMAIN LENGTH |
185 | | */ |
186 | 0 | proto_tree_add_item_ret_int(ath_tree, hf_ath_dlen, tvb, offset, 4, ENC_BIG_ENDIAN, &dlen); |
187 | 0 | offset += 4; |
188 | | |
189 | | /* DOMAIN |
190 | | */ |
191 | 0 | proto_tree_add_item(ath_tree, hf_ath_domain, tvb, offset, dlen, ENC_ASCII); |
192 | 0 | if (dlen != 0) |
193 | 0 | info_domain = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, dlen, ENC_ASCII); |
194 | 0 | offset += dlen; |
195 | | |
196 | | /* UNIQUEID |
197 | | */ |
198 | 0 | proto_tree_add_item(ath_tree, hf_ath_unique, tvb, offset, 16, ENC_NA); |
199 | 0 | offset += 16; |
200 | | |
201 | | /* PAYLOAD LENGTH |
202 | | */ |
203 | 0 | proto_tree_add_item_ret_int(ath_tree, hf_ath_plen, tvb, offset, 4, ENC_BIG_ENDIAN, &plen); |
204 | 0 | offset += 4; |
205 | | |
206 | | /* PAYLOAD |
207 | | */ |
208 | 0 | proto_tree_add_item(ath_tree, hf_ath_payload, tvb, offset, plen, ENC_ASCII); |
209 | 0 | offset += plen; |
210 | | |
211 | | /* END |
212 | | */ |
213 | 0 | proto_tree_add_item(ath_tree, hf_ath_end, tvb, offset, 8, ENC_ASCII); |
214 | 0 | } |
215 | | |
216 | | /* dissecting a Tomcat 7/8 packet |
217 | | */ |
218 | 0 | else if (tribes_version_mark == 256) { |
219 | | |
220 | | /* BEGIN |
221 | | */ |
222 | 0 | proto_tree_add_item(ath_tree, hf_ath_begin, tvb, offset, 8, ENC_ASCII); |
223 | 0 | offset += 8; |
224 | |
|
225 | 0 | proto_tree_add_item(ath_tree, hf_ath_padding, tvb, offset, 2, ENC_BIG_ENDIAN); |
226 | 0 | offset += 2; |
227 | | |
228 | | /* LENGTH |
229 | | */ |
230 | 0 | proto_tree_add_item(ath_tree, hf_ath_length, tvb, offset, 4, ENC_BIG_ENDIAN); |
231 | 0 | offset += 4; |
232 | | |
233 | | /* ALIVE TIME |
234 | | */ |
235 | 0 | proto_tree_add_item(ath_tree, hf_ath_alive, tvb, offset, 8, ENC_BIG_ENDIAN); |
236 | 0 | offset += 8; |
237 | | |
238 | | /* PORT |
239 | | */ |
240 | 0 | proto_tree_add_item(ath_tree, hf_ath_port, tvb, offset, 4, ENC_BIG_ENDIAN); |
241 | 0 | offset += 4; |
242 | | |
243 | | /* SECURE PORT |
244 | | */ |
245 | 0 | proto_tree_add_item(ath_tree, hf_ath_sport, tvb, offset, 4, ENC_BIG_ENDIAN); |
246 | 0 | offset += 4; |
247 | | |
248 | | /* UDP PORT, only in Tomcat 7/8 |
249 | | */ |
250 | 0 | proto_tree_add_item(ath_tree, hf_ath_uport, tvb, offset, 4, ENC_BIG_ENDIAN); |
251 | 0 | offset += 4; |
252 | | |
253 | | /* HOST LENGTH |
254 | | */ |
255 | 0 | hlen_item = proto_tree_add_item(ath_tree, hf_ath_hlen, tvb, offset, 1, ENC_BIG_ENDIAN); |
256 | 0 | hlen = tvb_get_uint8(tvb, offset); |
257 | 0 | offset += 1; |
258 | | |
259 | | /* HOST |
260 | | */ |
261 | 0 | if (hlen == 4) { |
262 | 0 | proto_tree_add_item(ath_tree, hf_ath_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN); |
263 | 0 | info_srcaddr = tvb_ip_to_str(pinfo->pool, tvb, offset); |
264 | 0 | } else if (hlen == 6) { |
265 | 0 | proto_tree_add_item(ath_tree, hf_ath_ipv6, tvb, offset, 6, ENC_NA); |
266 | 0 | info_srcaddr = tvb_ip6_to_str(pinfo->pool, tvb, offset); |
267 | 0 | } else { |
268 | 0 | expert_add_info(pinfo, hlen_item, &ei_ath_hlen_invalid); |
269 | 0 | } |
270 | 0 | offset += hlen; |
271 | | |
272 | | /* COMMAND LENGTH |
273 | | */ |
274 | 0 | proto_tree_add_item_ret_int(ath_tree, hf_ath_clen, tvb, offset, 4, ENC_BIG_ENDIAN, &clen); |
275 | 0 | offset += 4; |
276 | | |
277 | | /* COMMAND |
278 | | */ |
279 | 0 | proto_tree_add_item(ath_tree, hf_ath_comm, tvb, offset, clen, ENC_ASCII); |
280 | 0 | if (clen != -1) |
281 | 0 | info_command = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, clen, ENC_ASCII); |
282 | 0 | offset += clen; |
283 | | |
284 | | /* DOMAIN LENGTH |
285 | | */ |
286 | 0 | proto_tree_add_item_ret_int(ath_tree, hf_ath_dlen, tvb, offset, 4, ENC_BIG_ENDIAN, &dlen); |
287 | 0 | offset += 4; |
288 | | |
289 | | /* DOMAIN |
290 | | */ |
291 | 0 | proto_tree_add_item(ath_tree, hf_ath_domain, tvb, offset, dlen, ENC_ASCII); |
292 | 0 | if (dlen != 0) |
293 | 0 | info_domain = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, dlen, ENC_ASCII); |
294 | 0 | offset += dlen; |
295 | | |
296 | | /* UNIQUEID |
297 | | */ |
298 | 0 | proto_tree_add_item(ath_tree, hf_ath_unique, tvb, offset, 16, ENC_NA); |
299 | 0 | offset += 16; |
300 | | |
301 | | /* PAYLOAD LENGTH |
302 | | */ |
303 | 0 | proto_tree_add_item_ret_int(ath_tree, hf_ath_plen, tvb, offset, 4, ENC_BIG_ENDIAN, &plen); |
304 | 0 | offset += 4; |
305 | | |
306 | | /* PAYLOAD |
307 | | */ |
308 | 0 | proto_tree_add_item(ath_tree, hf_ath_payload, tvb, offset, plen, ENC_ASCII); |
309 | 0 | offset += plen; |
310 | | |
311 | | /* END |
312 | | */ |
313 | 0 | proto_tree_add_item(ath_tree, hf_ath_end, tvb, offset, 8, ENC_ASCII); |
314 | |
|
315 | 0 | } else { |
316 | 0 | proto_tree_add_expert(tree, pinfo, &ei_ath_hmark_invalid, tvb, offset, -1); |
317 | 0 | return tvb_captured_length(tvb); |
318 | 0 | } |
319 | | |
320 | | /* set the INFO column, and we're done ! |
321 | | */ |
322 | 0 | if (strcmp(info_command, "") != 0) { |
323 | 0 | if (strcmp(info_command, "BABY-ALEX") == 0) { |
324 | 0 | if (strcmp(info_domain, "") != 0) { |
325 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s is leaving domain %s", info_srcaddr, info_domain); |
326 | 0 | } else { |
327 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s is leaving default domain", info_srcaddr); |
328 | 0 | } |
329 | 0 | } else { |
330 | 0 | if (strcmp(info_domain, "") != 0) { |
331 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Heartbeat from %s to domain %s", info_srcaddr, info_domain); |
332 | 0 | } else { |
333 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Heartbeat from %s to default domain", info_srcaddr); |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } else { |
337 | 0 | if (strcmp(info_domain, "") != 0) { |
338 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Heartbeat from %s to domain %s", info_srcaddr, info_domain); |
339 | 0 | } else { |
340 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "Heartbeat from %s to default domain", info_srcaddr); |
341 | 0 | } |
342 | 0 | } |
343 | |
|
344 | 0 | return tvb_captured_length(tvb); |
345 | 0 | } |
346 | | |
347 | | void |
348 | | proto_register_ath(void) |
349 | 14 | { |
350 | | |
351 | 14 | expert_module_t* expert_ath; |
352 | | |
353 | 14 | static hf_register_info hf[] = { |
354 | 14 | { &hf_ath_begin, |
355 | 14 | { "Begin", "ath.begin", FT_STRING, BASE_NONE, NULL, 0x0, "Begin mark", |
356 | 14 | HFILL } |
357 | 14 | }, |
358 | 14 | { &hf_ath_padding, |
359 | 14 | { "Padding", "ath.padding", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, |
360 | 14 | HFILL } |
361 | 14 | }, |
362 | 14 | { &hf_ath_length, |
363 | 14 | { "Length", "ath.length", FT_UINT32, BASE_DEC, NULL, 0x0, "Data Length", |
364 | 14 | HFILL } |
365 | 14 | }, |
366 | 14 | { &hf_ath_alive, |
367 | 14 | { "Alive Time", "ath.alive", FT_UINT64, BASE_DEC, NULL, 0x0, "Alive Time counter", |
368 | 14 | HFILL } |
369 | 14 | }, |
370 | 14 | { &hf_ath_port, |
371 | 14 | { "Port", "ath.port", FT_UINT32, BASE_DEC, NULL, 0x0, "RMI Port", |
372 | 14 | HFILL } |
373 | 14 | }, |
374 | 14 | { &hf_ath_sport, |
375 | 14 | { "Secure Port", "ath.sport", FT_INT32, BASE_DEC, NULL, 0x0, "RMI Secure Port", |
376 | 14 | HFILL } |
377 | 14 | }, |
378 | 14 | { &hf_ath_uport, |
379 | 14 | { "UDP Port", "ath.uport", FT_INT32, BASE_DEC, NULL, 0x0, "RMI UDP Port", |
380 | 14 | HFILL } |
381 | 14 | }, |
382 | 14 | { &hf_ath_hlen, |
383 | 14 | { "Host Length", "ath.hlen", FT_INT8, BASE_DEC, NULL, 0x0, "Host IP Length", |
384 | 14 | HFILL } |
385 | 14 | }, |
386 | 14 | { &hf_ath_ipv4, |
387 | 14 | { "Host", "ath.ipv4", FT_IPv4, BASE_NONE, NULL, 0x0, "IPv4 Host", |
388 | 14 | HFILL } |
389 | 14 | }, |
390 | 14 | { &hf_ath_ipv6, |
391 | 14 | { "Host", "ath.ipv6", FT_IPv6, BASE_NONE, NULL, 0x0, "IPv6 Host", |
392 | 14 | HFILL } |
393 | 14 | }, |
394 | 14 | { &hf_ath_clen, |
395 | 14 | { "Command Length", "ath.clen", FT_INT32, BASE_DEC, NULL, 0x0, "Command Length for members", |
396 | 14 | HFILL } |
397 | 14 | }, |
398 | 14 | { &hf_ath_comm, |
399 | 14 | { "Command", "ath.comm", FT_STRING, BASE_NONE, NULL, 0x0, "Command for members", |
400 | 14 | HFILL } |
401 | 14 | }, |
402 | 14 | { &hf_ath_dlen, |
403 | 14 | { "Domain Length", "ath.dlen", FT_INT32, BASE_DEC, NULL, 0x0, "Cluster Domain Length", |
404 | 14 | HFILL } |
405 | 14 | }, |
406 | 14 | { &hf_ath_domain, |
407 | 14 | { "Domain", "ath.domain", FT_STRING, BASE_NONE, NULL, 0x0, "Cluster Domain", |
408 | 14 | HFILL } |
409 | 14 | }, |
410 | 14 | { &hf_ath_unique, |
411 | 14 | { "uniqueId", "ath.unique", FT_BYTES, BASE_NONE, NULL, 0x0, "UniqueID identifier", |
412 | 14 | HFILL } |
413 | 14 | }, |
414 | 14 | { &hf_ath_plen, |
415 | 14 | { "Payload Length", "ath.plen", FT_INT32, BASE_DEC, NULL, 0x0, "Packet Payload Length", |
416 | 14 | HFILL } |
417 | 14 | }, |
418 | 14 | { &hf_ath_payload, |
419 | 14 | { "Payload", "ath.payload", FT_STRING, BASE_NONE, NULL, 0x0, "Packet Payload", |
420 | 14 | HFILL } |
421 | 14 | }, |
422 | 14 | { &hf_ath_end, |
423 | 14 | { "End", "ath.end", FT_STRING, BASE_NONE, NULL, 0x0, "End mark", |
424 | 14 | HFILL } |
425 | 14 | }, |
426 | 14 | }; |
427 | | |
428 | 14 | static ei_register_info ei[] = { |
429 | 14 | { &ei_ath_hlen_invalid, { "ath.hlen.invalid", PI_MALFORMED, PI_ERROR, "Decode aborted: invalid IP length", EXPFILL }}, |
430 | 14 | { &ei_ath_hmark_invalid, { "ath.hmark.invalid", PI_MALFORMED, PI_ERROR, "Decode aborted: not an ATH packet", EXPFILL }}, |
431 | 14 | }; |
432 | | |
433 | 14 | static int *ett[] = { |
434 | 14 | &ett_ath, |
435 | 14 | }; |
436 | | |
437 | 14 | proto_ath = proto_register_protocol("Apache Tribes Heartbeat Protocol", "ATH", "ath"); |
438 | 14 | proto_register_field_array(proto_ath, hf, array_length(hf)); |
439 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
440 | 14 | expert_ath = expert_register_protocol(proto_ath); |
441 | 14 | expert_register_field_array(expert_ath, ei, array_length(ei)); |
442 | | |
443 | 14 | ath_handle = register_dissector("ath", dissect_ath, proto_ath); |
444 | 14 | } |
445 | | |
446 | | void |
447 | | proto_reg_handoff_ath(void) |
448 | 14 | { |
449 | 14 | dissector_add_uint_with_preference("udp.port", ATH_PORT, ath_handle); |
450 | 14 | } |
451 | | |
452 | | /* |
453 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
454 | | * |
455 | | * Local variables: |
456 | | * c-basic-offset: 2 |
457 | | * tab-width: 8 |
458 | | * indent-tabs-mode: nil |
459 | | * End: |
460 | | * |
461 | | * vi: set shiftwidth=2 tabstop=8 expandtab: |
462 | | * :indentSize=2:tabSize=8:noTabs=true: |
463 | | */ |