Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */