2 * Routines for collectd (http://collectd.org/) network plugin dissection
4 * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org>
5 * Copyright 2009-2013 Florian Forster <octo at collectd.org>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/expert.h>
18 #include <epan/stats_tree.h>
19 #include <epan/to_str.h>
21 #include <wsutil/str_util.h>
23 #define STR_NONNULL(str) ((str) ? ((const gchar*)str) : "(null)")
25 #define TYPE_HOST 0x0000
26 #define TYPE_TIME 0x0001
27 #define TYPE_TIME_HR 0x0008
28 #define TYPE_PLUGIN 0x0002
29 #define TYPE_PLUGIN_INSTANCE 0x0003
30 #define TYPE_TYPE 0x0004
31 #define TYPE_TYPE_INSTANCE 0x0005
32 #define TYPE_VALUES 0x0006
33 #define TYPE_INTERVAL 0x0007
34 #define TYPE_INTERVAL_HR 0x0009
35 #define TYPE_MESSAGE 0x0100
36 #define TYPE_SEVERITY 0x0101
37 #define TYPE_SIGN_SHA256 0x0200
38 #define TYPE_ENCR_AES256 0x0210
40 void proto_register_collectd(void);
42 typedef struct value_data_s {
53 const guint8 *plugin_instance;
54 gint plugin_instance_off;
55 gint plugin_instance_len;
59 const guint8 *type_instance;
60 gint type_instance_off;
61 gint type_instance_len;
64 typedef struct notify_data_s {
72 const guint8 *message;
77 struct string_counter_s;
78 typedef struct string_counter_s string_counter_t;
79 struct string_counter_s
83 string_counter_t *next;
86 typedef struct tap_data_s {
89 string_counter_t *hosts;
90 string_counter_t *plugins;
91 string_counter_t *types;
94 static const value_string part_names[] = {
95 { TYPE_VALUES, "VALUES" },
96 { TYPE_TIME, "TIME" },
97 { TYPE_TIME_HR, "TIME_HR" },
98 { TYPE_INTERVAL, "INTERVAL" },
99 { TYPE_INTERVAL_HR, "INTERVAL_HR" },
100 { TYPE_HOST, "HOST" },
101 { TYPE_PLUGIN, "PLUGIN" },
102 { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" },
103 { TYPE_TYPE, "TYPE" },
104 { TYPE_TYPE_INSTANCE, "TYPE_INSTANCE" },
105 { TYPE_MESSAGE, "MESSAGE" },
106 { TYPE_SEVERITY, "SEVERITY" },
107 { TYPE_SIGN_SHA256, "SIGNATURE" },
108 { TYPE_ENCR_AES256, "ENCRYPTED_DATA" },
112 #define TYPE_VALUE_COUNTER 0x00
113 #define TYPE_VALUE_GAUGE 0x01
114 #define TYPE_VALUE_DERIVE 0x02
115 #define TYPE_VALUE_ABSOLUTE 0x03
116 static const value_string valuetypenames[] = {
117 { TYPE_VALUE_COUNTER, "COUNTER" },
118 { TYPE_VALUE_GAUGE, "GAUGE" },
119 { TYPE_VALUE_DERIVE, "DERIVE" },
120 { TYPE_VALUE_ABSOLUTE, "ABSOLUTE" },
124 #define SEVERITY_FAILURE 0x01
125 #define SEVERITY_WARNING 0x02
126 #define SEVERITY_OKAY 0x04
127 static const val64_string severity_names[] = {
128 { SEVERITY_FAILURE, "FAILURE" },
129 { SEVERITY_WARNING, "WARNING" },
130 { SEVERITY_OKAY, "OKAY" },
134 #define UDP_PORT_COLLECTD 25826 /* Not IANA registered */
136 static gint proto_collectd = -1;
137 static gint tap_collectd = -1;
139 static gint hf_collectd_type = -1;
140 static gint hf_collectd_length = -1;
141 static gint hf_collectd_data = -1;
142 static gint hf_collectd_data_host = -1;
143 static gint hf_collectd_data_time = -1;
144 static gint hf_collectd_data_interval = -1;
145 static gint hf_collectd_data_plugin = -1;
146 static gint hf_collectd_data_plugin_inst= -1;
147 static gint hf_collectd_data_type = -1;
148 static gint hf_collectd_data_type_inst = -1;
149 static gint hf_collectd_data_valcnt = -1;
150 static gint hf_collectd_val_type = -1;
151 static gint hf_collectd_val_counter = -1;
152 static gint hf_collectd_val_gauge = -1;
153 static gint hf_collectd_val_derive = -1;
154 static gint hf_collectd_val_absolute = -1;
155 static gint hf_collectd_val_unknown = -1;
156 static gint hf_collectd_data_severity = -1;
157 static gint hf_collectd_data_message = -1;
158 static gint hf_collectd_data_sighash = -1;
159 static gint hf_collectd_data_initvec = -1;
160 static gint hf_collectd_data_username_len = -1;
161 static gint hf_collectd_data_username = -1;
162 static gint hf_collectd_data_encrypted = -1;
164 static gint ett_collectd = -1;
165 static gint ett_collectd_string = -1;
166 static gint ett_collectd_integer = -1;
167 static gint ett_collectd_part_value = -1;
168 static gint ett_collectd_value = -1;
169 static gint ett_collectd_valinfo = -1;
170 static gint ett_collectd_signature = -1;
171 static gint ett_collectd_encryption = -1;
172 static gint ett_collectd_dispatch = -1;
173 static gint ett_collectd_invalid_length = -1;
174 static gint ett_collectd_unknown = -1;
176 static gint st_collectd_packets = -1;
177 static gint st_collectd_values = -1;
178 static gint st_collectd_values_hosts = -1;
179 static gint st_collectd_values_plugins = -1;
180 static gint st_collectd_values_types = -1;
182 static expert_field ei_collectd_type = EI_INIT;
183 static expert_field ei_collectd_invalid_length = EI_INIT;
184 static expert_field ei_collectd_data_valcnt = EI_INIT;
185 static expert_field ei_collectd_garbage = EI_INIT;
187 /* Prototype for the handoff function */
188 void proto_reg_handoff_collectd (void);
191 collectd_time_to_nstime (guint64 t)
193 nstime_t nstime = NSTIME_INIT_ZERO;;
194 nstime.secs = (time_t) (t / 1073741824);
195 nstime.nsecs = (int) (((double) (t % 1073741824)) / 1.073741824);
201 collectd_stats_tree_init (stats_tree *st)
203 st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
204 st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
206 st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
208 st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
210 st_collectd_values_types = stats_tree_create_pivot (st, "By type",
212 } /* void collectd_stats_tree_init */
215 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
216 epan_dissect_t *edt _U_, const void *user_data)
218 const tap_data_t *td;
219 string_counter_t *sc;
221 td = (const tap_data_t *)user_data;
225 tick_stat_node (st, "Packets", 0, FALSE);
226 increase_stat_node (st, "Values", 0, TRUE, td->values_num);
228 for (sc = td->hosts; sc != NULL; sc = sc->next)
231 for (i = 0; i < sc->count; i++)
232 stats_tree_tick_pivot (st, st_collectd_values_hosts,
236 for (sc = td->plugins; sc != NULL; sc = sc->next)
239 for (i = 0; i < sc->count; i++)
240 stats_tree_tick_pivot (st, st_collectd_values_plugins,
244 for (sc = td->types; sc != NULL; sc = sc->next)
247 for (i = 0; i < sc->count; i++)
248 stats_tree_tick_pivot (st, st_collectd_values_types,
253 } /* int collectd_stats_tree_packet */
256 collectd_stats_tree_register (void)
258 stats_tree_register ("collectd", "collectd", "Collectd", 0,
259 collectd_stats_tree_packet,
260 collectd_stats_tree_init, NULL);
261 } /* void register_collectd_stat_trees */
264 collectd_proto_tree_add_assembled_metric (tvbuff_t *tvb,
265 gint offset, gint length,
266 value_data_t const *vdispatch, proto_tree *root)
268 proto_item *root_item;
272 subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6,
273 ett_collectd_dispatch, &root_item, "Assembled metric");
274 PROTO_ITEM_SET_GENERATED (root_item);
276 proto_tree_add_string (subtree, hf_collectd_data_host, tvb,
277 vdispatch->host_off, vdispatch->host_len,
278 STR_NONNULL (vdispatch->host));
280 proto_tree_add_string (subtree, hf_collectd_data_plugin, tvb,
281 vdispatch->plugin_off, vdispatch->plugin_len,
282 STR_NONNULL (vdispatch->plugin));
284 if (vdispatch->plugin_instance)
285 proto_tree_add_string (subtree,
286 hf_collectd_data_plugin_inst, tvb,
287 vdispatch->plugin_instance_off,
288 vdispatch->plugin_instance_len,
289 vdispatch->plugin_instance);
291 proto_tree_add_string (subtree, hf_collectd_data_type, tvb,
292 vdispatch->type_off, vdispatch->type_len,
293 STR_NONNULL (vdispatch->type));
295 if (vdispatch->type_instance)
296 proto_tree_add_string (subtree,
297 hf_collectd_data_type_inst, tvb,
298 vdispatch->type_instance_off,
299 vdispatch->type_instance_len,
300 vdispatch->type_instance);
302 nstime = collectd_time_to_nstime (vdispatch->time_value);
303 proto_tree_add_time (subtree, hf_collectd_data_time, tvb,
304 vdispatch->time_off, /* length = */ 8, &nstime);
306 nstime = collectd_time_to_nstime (vdispatch->interval);
307 proto_tree_add_time (subtree, hf_collectd_data_interval, tvb,
308 vdispatch->interval_off, /* length = */ 8, &nstime);
312 collectd_proto_tree_add_assembled_notification (tvbuff_t *tvb,
313 gint offset, gint length,
314 notify_data_t const *ndispatch, proto_tree *root)
316 proto_item *root_item;
320 subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6,
321 ett_collectd_dispatch, &root_item, "Assembled notification");
322 PROTO_ITEM_SET_GENERATED (root_item);
324 proto_tree_add_string (subtree, hf_collectd_data_host, tvb,
325 ndispatch->host_off, ndispatch->host_len,
326 STR_NONNULL (ndispatch->host));
328 nstime = collectd_time_to_nstime (ndispatch->time_value);
329 proto_tree_add_time (subtree, hf_collectd_data_time, tvb,
330 ndispatch->time_off, /* length = */ 8, &nstime);
332 proto_tree_add_uint64 (subtree, hf_collectd_data_severity, tvb,
333 ndispatch->severity_off, /* length = */ 8,
334 ndispatch->severity);
336 proto_tree_add_string (subtree, hf_collectd_data_message, tvb,
337 ndispatch->message_off, ndispatch->message_len,
342 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
343 gint offset, gint *ret_offset, gint *ret_length,
344 const guint8 **ret_string, proto_tree *tree_root,
345 proto_item **ret_item)
353 size = tvb_reported_length_remaining (tvb, offset);
356 /* This should never happen, because `dissect_collectd' checks
357 * for this condition already. */
361 type = tvb_get_ntohs(tvb, offset);
362 length = tvb_get_ntohs(tvb, offset + 2);
364 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
365 ett_collectd_string, &pi, "collectd %s segment: ",
366 val_to_str_const (type, part_names, "UNKNOWN"));
370 proto_item_append_text(pt, "Length = %i <BAD>", length);
371 expert_add_info_format(pinfo, pt, &ei_collectd_invalid_length,
372 "String part with invalid part length: "
373 "Part is longer than rest of package.");
377 *ret_offset = offset + 4;
378 *ret_length = length - 4;
380 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
381 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
382 proto_tree_add_item_ret_string (pt, type_hf, tvb, *ret_offset, *ret_length, ENC_ASCII|ENC_NA, wmem_packet_scope(), ret_string);
384 proto_item_append_text(pt, "\"%s\"", *ret_string);
386 if (ret_item != NULL)
390 } /* int dissect_collectd_string */
393 dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
394 gint offset, gint *ret_offset, guint64 *ret_value,
395 proto_tree *tree_root, proto_item **ret_item)
403 size = tvb_reported_length_remaining (tvb, offset);
406 /* This should never happen, because `dissect_collectd' checks
407 * for this condition already. */
411 type = tvb_get_ntohs(tvb, offset);
412 length = tvb_get_ntohs(tvb, offset + 2);
416 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
417 ett_collectd_integer, NULL, "collectd %s segment: <BAD>",
418 val_to_str_const (type, part_names, "UNKNOWN"));
420 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
422 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
424 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
425 "Garbage at end of packet: Length = %i <BAD>",
433 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
434 ett_collectd_integer, &pi, "collectd %s segment: <BAD>",
435 val_to_str_const (type, part_names, "UNKNOWN"));
437 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
439 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
440 offset + 2, 2, length);
441 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
442 "Invalid length field for an integer part.");
447 *ret_offset = offset + 4;
448 *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
450 /* Convert the version 4.* time format to the version 5.* time format. */
451 if ((type == TYPE_TIME) || (type == TYPE_INTERVAL))
452 *ret_value *= 1073741824;
454 /* Create an entry in the protocol tree for this part. The value is
455 * printed depending on the "type" variable: TIME{,_HR} as absolute
456 * time, INTERVAL{,_HR} as relative time, uint64 otherwise. */
457 if ((type == TYPE_TIME) || (type == TYPE_TIME_HR))
462 nstime = collectd_time_to_nstime (*ret_value);
463 strtime = abs_time_to_str (wmem_packet_scope(), &nstime, ABSOLUTE_TIME_LOCAL, /* show_zone = */ TRUE);
464 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
465 ett_collectd_integer, &pi, "collectd %s segment: %s",
466 val_to_str_const (type, part_names, "UNKNOWN"),
467 STR_NONNULL (strtime));
469 else if ((type == TYPE_INTERVAL) || (type == TYPE_INTERVAL_HR))
474 nstime = collectd_time_to_nstime (*ret_value);
475 strtime = rel_time_to_str (wmem_packet_scope(), &nstime);
476 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
477 ett_collectd_integer, &pi, "collectd %s segment: %s",
478 val_to_str_const (type, part_names, "UNKNOWN"),
483 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
484 ett_collectd_integer, &pi, "collectd %s segment: %"G_GINT64_MODIFIER"u",
485 val_to_str_const (type, part_names, "UNKNOWN"),
489 if (ret_item != NULL)
492 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
493 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
495 if ((type == TYPE_TIME) || (type == TYPE_INTERVAL)
496 || (type == TYPE_TIME_HR) || (type == TYPE_INTERVAL_HR))
500 nstime = collectd_time_to_nstime (*ret_value);
501 proto_tree_add_time (pt, type_hf, tvb, offset + 4, 8, &nstime);
505 proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, ENC_BIG_ENDIAN);
509 } /* int dissect_collectd_integer */
512 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
513 proto_tree *collectd_tree)
515 proto_tree *values_tree, *value_tree;
518 values_tree = proto_tree_add_subtree_format(collectd_tree, tvb, msg_off + 6, val_cnt * 9,
519 ett_collectd_value, NULL, "%d value%s", val_cnt,
520 plurality (val_cnt, "", "s"));
522 for (i = 0; i < val_cnt; i++)
526 gint value_type_offset;
529 /* Calculate the offsets of the type byte and the actual value. */
530 value_offset = msg_off + 6
531 + val_cnt /* value types */
532 + (i * 8); /* previous values */
534 value_type_offset = msg_off + 6 + i;
535 value_type = tvb_get_guint8 (tvb, value_type_offset);
537 switch (value_type) {
538 case TYPE_VALUE_COUNTER:
542 val64 = tvb_get_ntoh64 (tvb, value_offset);
543 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
544 val_cnt * 9, ett_collectd_valinfo, NULL,
545 "Counter: %"G_GINT64_MODIFIER"u", val64);
547 proto_tree_add_item (value_tree, hf_collectd_val_type,
548 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
549 proto_tree_add_item (value_tree,
550 hf_collectd_val_counter, tvb,
551 value_offset, 8, ENC_BIG_ENDIAN);
555 case TYPE_VALUE_GAUGE:
559 val = tvb_get_letohieee_double (tvb, value_offset);
560 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
561 val_cnt * 9, ett_collectd_valinfo, NULL,
564 proto_tree_add_item (value_tree, hf_collectd_val_type,
565 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
566 /* Set the `little endian' flag to TRUE here, because
567 * collectd stores doubles in x86 representation. */
568 proto_tree_add_item (value_tree, hf_collectd_val_gauge,
569 tvb, value_offset, 8, ENC_LITTLE_ENDIAN);
573 case TYPE_VALUE_DERIVE:
577 val64 = tvb_get_ntoh64 (tvb, value_offset);
578 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
579 val_cnt * 9, ett_collectd_valinfo, NULL,
580 "Derive: %"G_GINT64_MODIFIER"i", val64);
582 proto_tree_add_item (value_tree, hf_collectd_val_type,
583 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
584 proto_tree_add_item (value_tree,
585 hf_collectd_val_derive, tvb,
586 value_offset, 8, ENC_BIG_ENDIAN);
590 case TYPE_VALUE_ABSOLUTE:
594 val64 = tvb_get_ntoh64 (tvb, value_offset);
595 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
596 val_cnt * 9, ett_collectd_valinfo, NULL,
597 "Absolute: %"G_GINT64_MODIFIER"u", val64);
599 proto_tree_add_item (value_tree, hf_collectd_val_type,
600 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
601 proto_tree_add_item (value_tree,
602 hf_collectd_val_absolute, tvb,
603 value_offset, 8, ENC_BIG_ENDIAN);
611 val64 = tvb_get_ntoh64 (tvb, value_offset);
612 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
613 val_cnt * 9, ett_collectd_valinfo, NULL,
614 "Unknown: %"G_GINT64_MODIFIER"x",
617 proto_tree_add_item (value_tree, hf_collectd_val_type,
618 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
619 proto_tree_add_item (value_tree, hf_collectd_val_unknown,
620 tvb, value_offset, 8, ENC_BIG_ENDIAN);
623 } /* switch (value_type) */
624 } /* for (i = 0; i < val_cnt; i++) */
625 } /* void dissect_collectd_values */
628 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
629 value_data_t *vdispatch, proto_tree *tree_root)
637 gint corrected_values_count;
639 size = tvb_reported_length_remaining (tvb, offset);
642 /* This should never happen, because `dissect_collectd' checks
643 * for this condition already. */
647 type = tvb_get_ntohs (tvb, offset);
648 length = tvb_get_ntohs (tvb, offset + 2);
652 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
653 ett_collectd_part_value, NULL, "collectd %s segment: <BAD>",
654 val_to_str_const (type, part_names, "UNKNOWN"));
656 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
657 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
659 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
660 "Garbage at end of packet: Length = %i <BAD>",
665 if ((length < 15) || ((length % 9) != 6))
667 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
668 ett_collectd_part_value, &pi, "collectd %s segment: <BAD>",
669 val_to_str_const (type, part_names, "UNKNOWN"));
671 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
672 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
673 offset + 2, 2, length);
674 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
675 "Invalid length field for a values part.");
680 values_count = tvb_get_ntohs (tvb, offset + 4);
681 corrected_values_count = (length - 6) / 9;
683 if (values_count != corrected_values_count)
685 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
686 ett_collectd_part_value, NULL,
687 "collectd %s segment: %d (%d) value%s <BAD>",
688 val_to_str_const (type, part_names, "UNKNOWN"),
689 values_count, corrected_values_count,
690 plurality(values_count, "", "s"));
694 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
695 ett_collectd_part_value, NULL,
696 "collectd %s segment: %d value%s",
697 val_to_str_const (type, part_names, "UNKNOWN"),
699 plurality(values_count, "", "s"));
702 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
703 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
705 pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb,
706 offset + 4, 2, ENC_BIG_ENDIAN);
707 if (values_count != corrected_values_count)
708 expert_add_info(pinfo, pi, &ei_collectd_data_valcnt);
710 values_count = corrected_values_count;
712 dissect_collectd_values (tvb, offset, values_count, pt);
713 collectd_proto_tree_add_assembled_metric (tvb, offset + 6, length - 6,
717 } /* void dissect_collectd_part_values */
720 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
721 gint offset, proto_tree *tree_root)
729 size = tvb_reported_length_remaining (tvb, offset);
732 /* This should never happen, because `dissect_collectd' checks
733 * for this condition already. */
737 type = tvb_get_ntohs (tvb, offset);
738 length = tvb_get_ntohs (tvb, offset + 2);
740 if (size < 36) /* remaining packet size too small for signature */
742 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
743 ett_collectd_signature, NULL, "collectd %s segment: <BAD>",
744 val_to_str_const (type, part_names, "UNKNOWN"));
746 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
747 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
749 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
750 "Garbage at end of packet: Length = %i <BAD>",
757 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
758 ett_collectd_signature, NULL, "collectd %s segment: <BAD>",
759 val_to_str_const (type, part_names, "UNKNOWN"));
761 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
762 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
763 offset + 2, 2, length);
764 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
765 "Invalid length field for a signature part.");
770 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
771 ett_collectd_signature, NULL, "collectd %s segment: HMAC-SHA-256",
772 val_to_str_const (type, part_names, "UNKNOWN"));
774 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
775 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
777 proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, ENC_NA);
778 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, ENC_ASCII|ENC_NA);
781 } /* int dissect_collectd_signature */
784 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
785 gint offset, proto_tree *tree_root)
792 gint username_length;
794 size = tvb_reported_length_remaining (tvb, offset);
797 /* This should never happen, because `dissect_collectd' checks
798 * for this condition already. */
802 type = tvb_get_ntohs (tvb, offset);
803 length = tvb_get_ntohs (tvb, offset + 2);
805 if (size < 42) /* remaining packet size too small for signature */
807 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
808 ett_collectd_encryption, NULL, "collectd %s segment: <BAD>",
809 val_to_str_const (type, part_names, "UNKNOWN"));
811 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
812 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
814 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
815 "Garbage at end of packet: Length = %i <BAD>",
822 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
823 ett_collectd_encryption, NULL, "collectd %s segment: <BAD>",
824 val_to_str_const (type, part_names, "UNKNOWN"));
826 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
827 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
828 offset + 2, 2, length);
829 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
830 "Invalid length field for an encryption part.");
835 username_length = tvb_get_ntohs (tvb, offset + 4);
836 if (username_length > (length - 42))
838 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
839 ett_collectd_encryption, NULL, "collectd %s segment: <BAD>",
840 val_to_str_const (type, part_names, "UNKNOWN"));
842 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
843 proto_tree_add_uint (pt, hf_collectd_length, tvb,
844 offset + 2, 2, length);
845 pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb,
846 offset + 4, 2, length);
847 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
848 "Invalid username length field for an encryption part.");
853 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
854 ett_collectd_encryption, NULL, "collectd %s segment: AES-256",
855 val_to_str_const (type, part_names, "UNKNOWN"));
857 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
858 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
859 proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length);
860 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, ENC_ASCII|ENC_NA);
861 proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
862 offset + (6 + username_length), 16, ENC_NA);
863 proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb,
864 offset + (22 + username_length),
865 length - (22 + username_length), ENC_NA);
868 } /* int dissect_collectd_encrypted */
871 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
873 string_counter_t *entry;
875 if (ret_list == NULL)
878 if (new_value == NULL)
879 new_value = "(null)";
881 for (entry = *ret_list; entry != NULL; entry = entry->next)
882 if (strcmp (new_value, entry->string) == 0)
888 entry = (string_counter_t *)wmem_alloc0 (wmem_packet_scope(), sizeof (*entry));
889 entry->string = wmem_strdup (wmem_packet_scope(), new_value);
891 entry->next = *ret_list;
899 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
901 static tap_data_t tap_data;
905 const guint8 *pkt_host = NULL;
906 gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0;
907 value_data_t vdispatch;
908 notify_data_t ndispatch;
911 proto_tree *collectd_tree;
914 memset(&vdispatch, '\0', sizeof(vdispatch));
915 memset(&ndispatch, '\0', sizeof(ndispatch));
917 col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
918 col_clear(pinfo->cinfo, COL_INFO);
921 size = tvb_reported_length(tvb);
923 /* create the collectd protocol tree */
924 pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, ENC_NA);
925 collectd_tree = proto_item_add_subtree(pi, ett_collectd);
927 memset (&tap_data, 0, sizeof (tap_data));
930 while ((size > 0) && (status == 0))
936 /* Let's handle the easy case first real quick: All we do here
937 * is extract a host name and count the number of values,
938 * plugins and notifications. The payload is not checked at
939 * all, but the same checks are run on the part_length stuff -
940 * it's important to keep an eye on that. */
943 /* Check for garbage at end of packet. */
950 part_type = tvb_get_ntohs (tvb, offset);
951 part_length = tvb_get_ntohs (tvb, offset+2);
953 /* Check if part_length is in the valid range. */
954 if ((part_length < 4) || (part_length > size))
962 vdispatch.host = tvb_get_string_enc(wmem_packet_scope(), tvb,
963 offset + 4, part_length - 4, ENC_ASCII);
964 if (pkt_host == NULL)
965 pkt_host = vdispatch.host;
971 vdispatch.plugin = tvb_get_string_enc(wmem_packet_scope(), tvb,
972 offset + 4, part_length - 4, ENC_ASCII);
975 case TYPE_PLUGIN_INSTANCE:
978 vdispatch.type = tvb_get_string_enc(wmem_packet_scope(), tvb,
979 offset + 4, part_length - 4, ENC_ASCII);
981 case TYPE_TYPE_INSTANCE:
984 case TYPE_INTERVAL_HR:
990 tap_data.values_num++;
991 stats_account_string (&tap_data.hosts,
993 stats_account_string (&tap_data.plugins,
995 stats_account_string (&tap_data.types,
1009 offset += part_length;
1010 size -= part_length;
1014 /* Now we do the same steps again, but much more thoroughly. */
1016 /* Check if there are at least four bytes left first.
1017 * Four bytes are used to read the type and the length
1018 * of the next part. If there's less, there's some garbage
1019 * at the end of the packet. */
1022 proto_tree_add_expert_format(pi, pinfo, &ei_collectd_garbage, tvb,
1024 "Garbage at end of packet: Length = %i <BAD>",
1030 /* dissect a message entry */
1031 part_type = tvb_get_ntohs (tvb, offset);
1032 part_length = tvb_get_ntohs (tvb, offset + 2);
1034 /* Check if the length of the part is in the valid range. Don't
1035 * confuse this with the above: Here we check the information
1036 * provided in the packet.. */
1037 if ((part_length < 4) || (part_length > size))
1039 pt = proto_tree_add_subtree_format(collectd_tree, tvb,
1040 offset, part_length, ett_collectd_invalid_length, NULL,
1041 "collectd %s segment: Length = %i <BAD>",
1042 val_to_str_const (part_type, part_names, "UNKNOWN"),
1045 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
1047 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
1048 offset + 2, 2, part_length);
1050 if (part_length < 4)
1051 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
1052 "Bad part length: Is %i, expected at least 4",
1055 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
1056 "Bad part length: Larger than remaining packet size.");
1062 /* The header information looks okay, let's tend to the actual
1063 * payload in this part. */
1064 switch (part_type) {
1067 status = dissect_collectd_string (tvb, pinfo,
1068 hf_collectd_data_host,
1070 &vdispatch.host_off,
1071 &vdispatch.host_len,
1073 collectd_tree, /* item = */ NULL);
1078 if (pkt_host == NULL)
1079 pkt_host = vdispatch.host;
1080 ndispatch.host_off = vdispatch.host_off;
1081 ndispatch.host_len = vdispatch.host_len;
1082 ndispatch.host = vdispatch.host;
1090 status = dissect_collectd_string (tvb, pinfo,
1091 hf_collectd_data_plugin,
1093 &vdispatch.plugin_off,
1094 &vdispatch.plugin_len,
1096 collectd_tree, /* item = */ NULL);
1105 case TYPE_PLUGIN_INSTANCE:
1107 status = dissect_collectd_string (tvb, pinfo,
1108 hf_collectd_data_plugin_inst,
1110 &vdispatch.plugin_instance_off,
1111 &vdispatch.plugin_instance_len,
1112 &vdispatch.plugin_instance,
1113 collectd_tree, /* item = */ NULL);
1122 status = dissect_collectd_string (tvb, pinfo,
1123 hf_collectd_data_type,
1125 &vdispatch.type_off,
1126 &vdispatch.type_len,
1128 collectd_tree, /* item = */ NULL);
1135 case TYPE_TYPE_INSTANCE:
1137 status = dissect_collectd_string (tvb, pinfo,
1138 hf_collectd_data_type_inst,
1140 &vdispatch.type_instance_off,
1141 &vdispatch.type_instance_len,
1142 &vdispatch.type_instance,
1143 collectd_tree, /* item = */ NULL);
1154 status = dissect_collectd_integer (tvb, pinfo,
1155 hf_collectd_data_time,
1157 &vdispatch.time_off,
1158 &vdispatch.time_value,
1159 collectd_tree, &pi);
1167 case TYPE_INTERVAL_HR:
1169 status = dissect_collectd_integer (tvb, pinfo,
1170 hf_collectd_data_interval,
1172 &vdispatch.interval_off,
1173 &vdispatch.interval,
1174 collectd_tree, /* item = */ NULL);
1183 status = dissect_collectd_part_values (tvb, pinfo,
1192 tap_data.values_num++;
1193 stats_account_string (&tap_data.hosts,
1195 stats_account_string (&tap_data.plugins,
1197 stats_account_string (&tap_data.types,
1206 status = dissect_collectd_string (tvb, pinfo,
1207 hf_collectd_data_message,
1209 &ndispatch.message_off,
1210 &ndispatch.message_len,
1212 collectd_tree, &pi);
1220 pt = proto_item_get_subtree (pi);
1222 collectd_proto_tree_add_assembled_notification (tvb,
1223 offset + 4, part_length - 1,
1232 status = dissect_collectd_integer (tvb, pinfo,
1233 hf_collectd_data_severity,
1235 &ndispatch.severity_off,
1236 &ndispatch.severity,
1237 collectd_tree, &pi);
1242 proto_item_set_text (pi,
1243 "collectd SEVERITY segment: "
1244 "%s (%"G_GINT64_MODIFIER"u)",
1245 val64_to_str_const (ndispatch.severity, severity_names, "UNKNOWN"),
1246 ndispatch.severity);
1252 case TYPE_SIGN_SHA256:
1254 status = dissect_collectd_signature (tvb, pinfo,
1263 case TYPE_ENCR_AES256:
1265 status = dissect_collectd_encrypted (tvb, pinfo,
1266 offset, collectd_tree);
1276 pt = proto_tree_add_subtree_format(collectd_tree, tvb,
1277 offset, part_length, ett_collectd_unknown, NULL,
1278 "collectd %s segment: %i bytes",
1279 val_to_str_const(part_type, part_names, "UNKNOWN"),
1282 pi = proto_tree_add_uint (pt, hf_collectd_type, tvb,
1283 offset, 2, part_type);
1284 proto_tree_add_uint (pt, hf_collectd_length, tvb,
1285 offset + 2, 2, part_length);
1286 proto_tree_add_item (pt, hf_collectd_data, tvb,
1287 offset + 4, part_length - 4, ENC_NA);
1289 expert_add_info_format(pinfo, pi, &ei_collectd_type,
1290 "Unknown part type %#x. Cannot decode data.",
1293 } /* switch (part_type) */
1295 offset += part_length;
1296 size -= part_length;
1297 } /* while ((size > 4) && (status == 0)) */
1299 if (pkt_errors && pkt_unknown)
1300 col_add_fstr (pinfo->cinfo, COL_INFO,
1301 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s",
1303 pkt_values, plurality (pkt_values, " ", "s"),
1304 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1305 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1307 pkt_errors, plurality (pkt_errors, "", "s"));
1308 else if (pkt_errors)
1309 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s",
1311 pkt_values, plurality (pkt_values, " ", "s"),
1312 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1313 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1314 pkt_errors, plurality (pkt_errors, "", "s"));
1315 else if (pkt_unknown)
1316 col_add_fstr (pinfo->cinfo, COL_INFO,
1317 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown",
1319 pkt_values, plurality (pkt_values, " ", "s"),
1320 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1321 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1324 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1326 pkt_values, plurality (pkt_values, " ", "s"),
1327 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1328 pkt_messages, plurality (pkt_messages, "", "s"));
1330 /* Dispatch tap data. */
1331 tap_queue_packet (tap_collectd, pinfo, &tap_data);
1332 return tvb_captured_length(tvb);
1333 } /* void dissect_collectd */
1335 void proto_register_collectd(void)
1337 expert_module_t* expert_collectd;
1339 /* Setup list of header fields */
1340 static hf_register_info hf[] = {
1341 { &hf_collectd_type,
1342 { "Type", "collectd.type", FT_UINT16, BASE_HEX,
1343 VALS(part_names), 0x0, NULL, HFILL }
1345 { &hf_collectd_length,
1346 { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1347 NULL, 0x0, NULL, HFILL }
1349 { &hf_collectd_data,
1350 { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1351 NULL, 0x0, NULL, HFILL }
1353 { &hf_collectd_data_host,
1354 { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1355 NULL, 0x0, NULL, HFILL }
1357 { &hf_collectd_data_interval,
1358 { "Interval", "collectd.data.interval", FT_RELATIVE_TIME, BASE_NONE,
1359 NULL, 0x0, NULL, HFILL }
1361 { &hf_collectd_data_time,
1362 { "Timestamp", "collectd.data.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL,
1363 NULL, 0x0, NULL, HFILL }
1365 { &hf_collectd_data_plugin,
1366 { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1367 NULL, 0x0, NULL, HFILL }
1369 { &hf_collectd_data_plugin_inst,
1370 { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1371 NULL, 0x0, NULL, HFILL }
1373 { &hf_collectd_data_type,
1374 { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1375 NULL, 0x0, NULL, HFILL }
1377 { &hf_collectd_data_type_inst,
1378 { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1379 NULL, 0x0, NULL, HFILL }
1381 { &hf_collectd_data_valcnt,
1382 { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1383 NULL, 0x0, NULL, HFILL }
1385 { &hf_collectd_val_type,
1386 { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1387 VALS(valuetypenames), 0x0, NULL, HFILL }
1389 { &hf_collectd_val_counter,
1390 { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1391 NULL, 0x0, NULL, HFILL }
1393 { &hf_collectd_val_gauge,
1394 { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1395 NULL, 0x0, NULL, HFILL }
1397 { &hf_collectd_val_derive,
1398 { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1399 NULL, 0x0, NULL, HFILL }
1401 { &hf_collectd_val_absolute,
1402 { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1403 NULL, 0x0, NULL, HFILL }
1405 { &hf_collectd_val_unknown,
1406 { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1407 NULL, 0x0, NULL, HFILL }
1409 { &hf_collectd_data_severity,
1410 { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX | BASE_VAL64_STRING,
1411 VALS64(severity_names),
1414 { &hf_collectd_data_message,
1415 { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1416 NULL, 0x0, NULL, HFILL }
1418 { &hf_collectd_data_sighash,
1419 { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1420 NULL, 0x0, NULL, HFILL }
1422 { &hf_collectd_data_initvec,
1423 { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1424 NULL, 0x0, NULL, HFILL }
1426 { &hf_collectd_data_username_len,
1427 { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1428 NULL, 0x0, NULL, HFILL }
1430 { &hf_collectd_data_username,
1431 { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1432 NULL, 0x0, NULL, HFILL }
1434 { &hf_collectd_data_encrypted,
1435 { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1436 NULL, 0x0, NULL, HFILL }
1440 /* Setup protocol subtree array */
1441 static gint *ett[] = {
1443 &ett_collectd_string,
1444 &ett_collectd_integer,
1445 &ett_collectd_part_value,
1446 &ett_collectd_value,
1447 &ett_collectd_valinfo,
1448 &ett_collectd_signature,
1449 &ett_collectd_encryption,
1450 &ett_collectd_dispatch,
1451 &ett_collectd_invalid_length,
1452 &ett_collectd_unknown,
1455 static ei_register_info ei[] = {
1456 { &ei_collectd_invalid_length, { "collectd.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }},
1457 { &ei_collectd_garbage, { "collectd.garbage", PI_MALFORMED, PI_ERROR, "Garbage at end of packet", EXPFILL }},
1458 { &ei_collectd_data_valcnt, { "collectd.data.valcnt.mismatch", PI_MALFORMED, PI_WARN, "Number of values and length of part do not match. Assuming length is correct.", EXPFILL }},
1459 { &ei_collectd_type, { "collectd.type.unknown", PI_UNDECODED, PI_NOTE, "Unknown part type", EXPFILL }},
1462 /* Register the protocol name and description */
1463 proto_collectd = proto_register_protocol("collectd network data", "collectd", "collectd");
1465 /* Required function calls to register the header fields and subtrees used */
1466 proto_register_field_array(proto_collectd, hf, array_length(hf));
1467 proto_register_subtree_array(ett, array_length(ett));
1468 expert_collectd = expert_register_protocol(proto_collectd);
1469 expert_register_field_array(expert_collectd, ei, array_length(ei));
1471 tap_collectd = register_tap ("collectd");
1474 void proto_reg_handoff_collectd (void)
1476 dissector_handle_t collectd_handle;
1478 collectd_handle = create_dissector_handle(dissect_collectd, proto_collectd);
1479 dissector_add_uint_with_preference("udp.port", UDP_PORT_COLLECTD, collectd_handle);
1481 collectd_stats_tree_register ();
1482 } /* void proto_reg_handoff_collectd */
1485 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1490 * indent-tabs-mode: t
1493 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1494 * :indentSize=8:tabSize=8:noTabs=false: