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 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <epan/packet.h>
29 #include <epan/prefs.h>
30 #include <epan/expert.h>
31 #include <epan/stats_tree.h>
32 #include <epan/to_str.h>
34 #define STR_NONNULL(str) ((str) ? (str) : "(null)")
36 #define TYPE_HOST 0x0000
37 #define TYPE_TIME 0x0001
38 #define TYPE_TIME_HR 0x0008
39 #define TYPE_PLUGIN 0x0002
40 #define TYPE_PLUGIN_INSTANCE 0x0003
41 #define TYPE_TYPE 0x0004
42 #define TYPE_TYPE_INSTANCE 0x0005
43 #define TYPE_VALUES 0x0006
44 #define TYPE_INTERVAL 0x0007
45 #define TYPE_INTERVAL_HR 0x0009
46 #define TYPE_MESSAGE 0x0100
47 #define TYPE_SEVERITY 0x0101
48 #define TYPE_SIGN_SHA256 0x0200
49 #define TYPE_ENCR_AES256 0x0210
51 void proto_register_collectd(void);
53 typedef struct value_data_s {
64 gchar *plugin_instance;
65 gint plugin_instance_off;
66 gint plugin_instance_len;
71 gint type_instance_off;
72 gint type_instance_len;
75 typedef struct notify_data_s {
88 struct string_counter_s;
89 typedef struct string_counter_s string_counter_t;
90 struct string_counter_s
94 string_counter_t *next;
97 typedef struct tap_data_s {
100 string_counter_t *hosts;
101 string_counter_t *plugins;
102 string_counter_t *types;
105 static const value_string part_names[] = {
106 { TYPE_VALUES, "VALUES" },
107 { TYPE_TIME, "TIME" },
108 { TYPE_TIME_HR, "TIME_HR" },
109 { TYPE_INTERVAL, "INTERVAL" },
110 { TYPE_INTERVAL_HR, "INTERVAL_HR" },
111 { TYPE_HOST, "HOST" },
112 { TYPE_PLUGIN, "PLUGIN" },
113 { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" },
114 { TYPE_TYPE, "TYPE" },
115 { TYPE_TYPE_INSTANCE, "TYPE_INSTANCE" },
116 { TYPE_MESSAGE, "MESSAGE" },
117 { TYPE_SEVERITY, "SEVERITY" },
118 { TYPE_SIGN_SHA256, "SIGNATURE" },
119 { TYPE_ENCR_AES256, "ENCRYPTED_DATA" },
123 #define TYPE_VALUE_COUNTER 0x00
124 #define TYPE_VALUE_GAUGE 0x01
125 #define TYPE_VALUE_DERIVE 0x02
126 #define TYPE_VALUE_ABSOLUTE 0x03
127 static const value_string valuetypenames[] = {
128 { TYPE_VALUE_COUNTER, "COUNTER" },
129 { TYPE_VALUE_GAUGE, "GAUGE" },
130 { TYPE_VALUE_DERIVE, "DERIVE" },
131 { TYPE_VALUE_ABSOLUTE, "ABSOLUTE" },
135 #define SEVERITY_FAILURE 0x01
136 #define SEVERITY_WARNING 0x02
137 #define SEVERITY_OKAY 0x04
138 static const val64_string severity_names[] = {
139 { SEVERITY_FAILURE, "FAILURE" },
140 { SEVERITY_WARNING, "WARNING" },
141 { SEVERITY_OKAY, "OKAY" },
145 #define UDP_PORT_COLLECTD 25826
146 static guint collectd_udp_port = UDP_PORT_COLLECTD;
148 static gint proto_collectd = -1;
149 static gint tap_collectd = -1;
151 static gint hf_collectd_type = -1;
152 static gint hf_collectd_length = -1;
153 static gint hf_collectd_data = -1;
154 static gint hf_collectd_data_host = -1;
155 static gint hf_collectd_data_time = -1;
156 static gint hf_collectd_data_interval = -1;
157 static gint hf_collectd_data_plugin = -1;
158 static gint hf_collectd_data_plugin_inst= -1;
159 static gint hf_collectd_data_type = -1;
160 static gint hf_collectd_data_type_inst = -1;
161 static gint hf_collectd_data_valcnt = -1;
162 static gint hf_collectd_val_type = -1;
163 static gint hf_collectd_val_counter = -1;
164 static gint hf_collectd_val_gauge = -1;
165 static gint hf_collectd_val_derive = -1;
166 static gint hf_collectd_val_absolute = -1;
167 static gint hf_collectd_val_unknown = -1;
168 static gint hf_collectd_data_severity = -1;
169 static gint hf_collectd_data_message = -1;
170 static gint hf_collectd_data_sighash = -1;
171 static gint hf_collectd_data_initvec = -1;
172 static gint hf_collectd_data_username_len = -1;
173 static gint hf_collectd_data_username = -1;
174 static gint hf_collectd_data_encrypted = -1;
176 static gint ett_collectd = -1;
177 static gint ett_collectd_string = -1;
178 static gint ett_collectd_integer = -1;
179 static gint ett_collectd_part_value = -1;
180 static gint ett_collectd_value = -1;
181 static gint ett_collectd_valinfo = -1;
182 static gint ett_collectd_signature = -1;
183 static gint ett_collectd_encryption = -1;
184 static gint ett_collectd_dispatch = -1;
185 static gint ett_collectd_invalid_length = -1;
186 static gint ett_collectd_unknown = -1;
188 static gint st_collectd_packets = -1;
189 static gint st_collectd_values = -1;
190 static gint st_collectd_values_hosts = -1;
191 static gint st_collectd_values_plugins = -1;
192 static gint st_collectd_values_types = -1;
194 static expert_field ei_collectd_type = EI_INIT;
195 static expert_field ei_collectd_invalid_length = EI_INIT;
196 static expert_field ei_collectd_data_valcnt = EI_INIT;
197 static expert_field ei_collectd_garbage = EI_INIT;
199 /* Prototype for the handoff function */
200 void proto_reg_handoff_collectd (void);
203 collectd_time_to_nstime (guint64 t)
205 nstime_t nstime = { 0, 0 };
206 nstime.secs = (time_t) (t / 1073741824);
207 nstime.nsecs = (int) (((double) (t % 1073741824)) / 1.073741824);
213 collectd_stats_tree_init (stats_tree *st)
215 st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
216 st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
218 st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
220 st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
222 st_collectd_values_types = stats_tree_create_pivot (st, "By type",
224 } /* void collectd_stats_tree_init */
227 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
228 epan_dissect_t *edt _U_, const void *user_data)
230 const tap_data_t *td;
231 string_counter_t *sc;
233 td = (const tap_data_t *)user_data;
237 tick_stat_node (st, "Packets", 0, FALSE);
238 increase_stat_node (st, "Values", 0, TRUE, td->values_num);
240 for (sc = td->hosts; sc != NULL; sc = sc->next)
243 for (i = 0; i < sc->count; i++)
244 stats_tree_tick_pivot (st, st_collectd_values_hosts,
248 for (sc = td->plugins; sc != NULL; sc = sc->next)
251 for (i = 0; i < sc->count; i++)
252 stats_tree_tick_pivot (st, st_collectd_values_plugins,
256 for (sc = td->types; sc != NULL; sc = sc->next)
259 for (i = 0; i < sc->count; i++)
260 stats_tree_tick_pivot (st, st_collectd_values_types,
265 } /* int collectd_stats_tree_packet */
268 collectd_stats_tree_register (void)
270 stats_tree_register ("collectd", "collectd", "Collectd", 0,
271 collectd_stats_tree_packet,
272 collectd_stats_tree_init, NULL);
273 } /* void register_collectd_stat_trees */
276 collectd_proto_tree_add_assembled_metric (tvbuff_t *tvb,
277 gint offset, gint length,
278 value_data_t const *vdispatch, proto_tree *root)
280 proto_item *root_item;
284 subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6,
285 ett_collectd_dispatch, &root_item, "Assembled metric");
286 PROTO_ITEM_SET_GENERATED (root_item);
288 proto_tree_add_string (subtree, hf_collectd_data_host, tvb,
289 vdispatch->host_off, vdispatch->host_len,
290 STR_NONNULL (vdispatch->host));
292 proto_tree_add_string (subtree, hf_collectd_data_plugin, tvb,
293 vdispatch->plugin_off, vdispatch->plugin_len,
294 STR_NONNULL (vdispatch->plugin));
296 if (vdispatch->plugin_instance)
297 proto_tree_add_string (subtree,
298 hf_collectd_data_plugin_inst, tvb,
299 vdispatch->plugin_instance_off,
300 vdispatch->plugin_instance_len,
301 vdispatch->plugin_instance);
303 proto_tree_add_string (subtree, hf_collectd_data_type, tvb,
304 vdispatch->type_off, vdispatch->type_len,
305 STR_NONNULL (vdispatch->type));
307 if (vdispatch->type_instance)
308 proto_tree_add_string (subtree,
309 hf_collectd_data_type_inst, tvb,
310 vdispatch->type_instance_off,
311 vdispatch->type_instance_len,
312 vdispatch->type_instance);
314 nstime = collectd_time_to_nstime (vdispatch->time_value);
315 proto_tree_add_time (subtree, hf_collectd_data_time, tvb,
316 vdispatch->time_off, /* length = */ 8, &nstime);
318 nstime = collectd_time_to_nstime (vdispatch->interval);
319 proto_tree_add_time (subtree, hf_collectd_data_interval, tvb,
320 vdispatch->interval_off, /* length = */ 8, &nstime);
324 collectd_proto_tree_add_assembled_notification (tvbuff_t *tvb,
325 gint offset, gint length,
326 notify_data_t const *ndispatch, proto_tree *root)
328 proto_item *root_item;
332 subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6,
333 ett_collectd_dispatch, &root_item, "Assembled notification");
334 PROTO_ITEM_SET_GENERATED (root_item);
336 proto_tree_add_string (subtree, hf_collectd_data_host, tvb,
337 ndispatch->host_off, ndispatch->host_len,
338 STR_NONNULL (ndispatch->host));
340 nstime = collectd_time_to_nstime (ndispatch->time_value);
341 proto_tree_add_time (subtree, hf_collectd_data_time, tvb,
342 ndispatch->time_off, /* length = */ 8, &nstime);
344 proto_tree_add_uint64 (subtree, hf_collectd_data_severity, tvb,
345 ndispatch->severity_off, /* length = */ 8,
346 ndispatch->severity);
348 proto_tree_add_string (subtree, hf_collectd_data_message, tvb,
349 ndispatch->message_off, ndispatch->message_len,
354 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
355 gint offset, gint *ret_offset, gint *ret_length,
356 gchar **ret_string, proto_tree *tree_root,
357 proto_item **ret_item)
365 size = tvb_reported_length_remaining (tvb, offset);
368 /* This should never happen, because `dissect_collectd' checks
369 * for this condition already. */
373 type = tvb_get_ntohs(tvb, offset);
374 length = tvb_get_ntohs(tvb, offset + 2);
376 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
377 ett_collectd_string, &pi, "collectd %s segment: ",
378 val_to_str_const (type, part_names, "UNKNOWN"));
382 proto_item_append_text(pt, "Length = %i <BAD>", length);
383 expert_add_info_format(pinfo, pt, &ei_collectd_invalid_length,
384 "String part with invalid part length: "
385 "Part is longer than rest of package.");
389 *ret_offset = offset + 4;
390 *ret_length = length - 4;
392 *ret_string = tvb_get_string_enc(wmem_packet_scope(), tvb, *ret_offset, *ret_length, ENC_ASCII);
393 proto_item_append_text(pt, "\"%s\"", *ret_string);
395 if (ret_item != NULL)
398 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
399 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
400 proto_tree_add_item (pt, type_hf, tvb, *ret_offset, *ret_length, ENC_ASCII|ENC_NA);
403 } /* int dissect_collectd_string */
406 dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
407 gint offset, gint *ret_offset, guint64 *ret_value,
408 proto_tree *tree_root, proto_item **ret_item)
416 size = tvb_reported_length_remaining (tvb, offset);
419 /* This should never happen, because `dissect_collectd' checks
420 * for this condition already. */
424 type = tvb_get_ntohs(tvb, offset);
425 length = tvb_get_ntohs(tvb, offset + 2);
429 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
430 ett_collectd_integer, NULL, "collectd %s segment: <BAD>",
431 val_to_str_const (type, part_names, "UNKNOWN"));
433 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
435 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
437 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
438 "Garbage at end of packet: Length = %i <BAD>",
446 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
447 ett_collectd_integer, &pi, "collectd %s segment: <BAD>",
448 val_to_str_const (type, part_names, "UNKNOWN"));
450 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
452 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
453 offset + 2, 2, length);
454 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
455 "Invalid length field for an integer part.");
460 *ret_offset = offset + 4;
461 *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
463 /* Convert the version 4.* time format to the version 5.* time format. */
464 if ((type == TYPE_TIME) || (type == TYPE_INTERVAL))
465 *ret_value *= 1073741824;
467 /* Create an entry in the protocol tree for this part. The value is
468 * printed depending on the "type" variable: TIME{,_HR} as absolute
469 * time, INTERVAL{,_HR} as relative time, uint64 otherwise. */
470 if ((type == TYPE_TIME) || (type == TYPE_TIME_HR))
475 nstime = collectd_time_to_nstime (*ret_value);
476 strtime = abs_time_to_str (wmem_packet_scope(), &nstime, ABSOLUTE_TIME_LOCAL, /* show_zone = */ TRUE);
477 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
478 ett_collectd_integer, &pi, "collectd %s segment: %s",
479 val_to_str_const (type, part_names, "UNKNOWN"),
480 STR_NONNULL (strtime));
482 else if ((type == TYPE_INTERVAL) || (type == TYPE_INTERVAL_HR))
487 nstime = collectd_time_to_nstime (*ret_value);
488 strtime = rel_time_to_str (wmem_packet_scope(), &nstime);
489 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
490 ett_collectd_integer, &pi, "collectd %s segment: %s",
491 val_to_str_const (type, part_names, "UNKNOWN"),
496 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
497 ett_collectd_integer, &pi, "collectd %s segment: %"G_GINT64_MODIFIER"u",
498 val_to_str_const (type, part_names, "UNKNOWN"),
502 if (ret_item != NULL)
505 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
506 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
508 if ((type == TYPE_TIME) || (type == TYPE_INTERVAL)
509 || (type == TYPE_TIME_HR) || (type == TYPE_INTERVAL_HR))
513 nstime = collectd_time_to_nstime (*ret_value);
514 proto_tree_add_time (pt, type_hf, tvb, offset + 4, 8, &nstime);
518 proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, ENC_BIG_ENDIAN);
522 } /* int dissect_collectd_integer */
525 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
526 proto_tree *collectd_tree)
528 proto_tree *values_tree, *value_tree;
531 values_tree = proto_tree_add_subtree_format(collectd_tree, tvb, msg_off + 6, val_cnt * 9,
532 ett_collectd_value, NULL, "%d value%s", val_cnt,
533 plurality (val_cnt, "", "s"));
535 for (i = 0; i < val_cnt; i++)
539 gint value_type_offset;
542 /* Calculate the offsets of the type byte and the actual value. */
543 value_offset = msg_off + 6
544 + val_cnt /* value types */
545 + (i * 8); /* previous values */
547 value_type_offset = msg_off + 6 + i;
548 value_type = tvb_get_guint8 (tvb, value_type_offset);
550 switch (value_type) {
551 case TYPE_VALUE_COUNTER:
555 val64 = tvb_get_ntoh64 (tvb, value_offset);
556 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
557 val_cnt * 9, ett_collectd_valinfo, NULL,
558 "Counter: %"G_GINT64_MODIFIER"u", val64);
560 proto_tree_add_item (value_tree, hf_collectd_val_type,
561 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
562 proto_tree_add_item (value_tree,
563 hf_collectd_val_counter, tvb,
564 value_offset, 8, ENC_BIG_ENDIAN);
568 case TYPE_VALUE_GAUGE:
572 val = tvb_get_letohieee_double (tvb, value_offset);
573 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
574 val_cnt * 9, ett_collectd_valinfo, NULL,
577 proto_tree_add_item (value_tree, hf_collectd_val_type,
578 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
579 /* Set the `little endian' flag to TRUE here, because
580 * collectd stores doubles in x86 representation. */
581 proto_tree_add_item (value_tree, hf_collectd_val_gauge,
582 tvb, value_offset, 8, ENC_LITTLE_ENDIAN);
586 case TYPE_VALUE_DERIVE:
590 val64 = tvb_get_ntoh64 (tvb, value_offset);
591 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
592 val_cnt * 9, ett_collectd_valinfo, NULL,
593 "Derive: %"G_GINT64_MODIFIER"i", val64);
595 proto_tree_add_item (value_tree, hf_collectd_val_type,
596 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
597 proto_tree_add_item (value_tree,
598 hf_collectd_val_derive, tvb,
599 value_offset, 8, ENC_BIG_ENDIAN);
603 case TYPE_VALUE_ABSOLUTE:
607 val64 = tvb_get_ntoh64 (tvb, value_offset);
608 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
609 val_cnt * 9, ett_collectd_valinfo, NULL,
610 "Absolute: %"G_GINT64_MODIFIER"u", val64);
612 proto_tree_add_item (value_tree, hf_collectd_val_type,
613 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
614 proto_tree_add_item (value_tree,
615 hf_collectd_val_absolute, tvb,
616 value_offset, 8, ENC_BIG_ENDIAN);
624 val64 = tvb_get_ntoh64 (tvb, value_offset);
625 value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6,
626 val_cnt * 9, ett_collectd_valinfo, NULL,
627 "Unknown: %"G_GINT64_MODIFIER"x",
630 proto_tree_add_item (value_tree, hf_collectd_val_type,
631 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
632 proto_tree_add_item (value_tree, hf_collectd_val_unknown,
633 tvb, value_offset, 8, ENC_BIG_ENDIAN);
636 } /* switch (value_type) */
637 } /* for (i = 0; i < val_cnt; i++) */
638 } /* void dissect_collectd_values */
641 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
642 value_data_t *vdispatch, proto_tree *tree_root)
650 gint corrected_values_count;
652 size = tvb_reported_length_remaining (tvb, offset);
655 /* This should never happen, because `dissect_collectd' checks
656 * for this condition already. */
660 type = tvb_get_ntohs (tvb, offset);
661 length = tvb_get_ntohs (tvb, offset + 2);
665 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
666 ett_collectd_part_value, NULL, "collectd %s segment: <BAD>",
667 val_to_str_const (type, part_names, "UNKNOWN"));
669 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
670 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
672 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
673 "Garbage at end of packet: Length = %i <BAD>",
678 if ((length < 15) || ((length % 9) != 6))
680 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
681 ett_collectd_part_value, &pi, "collectd %s segment: <BAD>",
682 val_to_str_const (type, part_names, "UNKNOWN"));
684 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
685 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
686 offset + 2, 2, length);
687 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
688 "Invalid length field for a values part.");
693 values_count = tvb_get_ntohs (tvb, offset + 4);
694 corrected_values_count = (length - 6) / 9;
696 if (values_count != corrected_values_count)
698 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
699 ett_collectd_part_value, NULL,
700 "collectd %s segment: %d (%d) value%s <BAD>",
701 val_to_str_const (type, part_names, "UNKNOWN"),
702 values_count, corrected_values_count,
703 plurality(values_count, "", "s"));
707 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
708 ett_collectd_part_value, NULL,
709 "collectd %s segment: %d value%s",
710 val_to_str_const (type, part_names, "UNKNOWN"),
712 plurality(values_count, "", "s"));
715 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
716 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
718 pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb,
719 offset + 4, 2, ENC_BIG_ENDIAN);
720 if (values_count != corrected_values_count)
721 expert_add_info(pinfo, pi, &ei_collectd_data_valcnt);
723 values_count = corrected_values_count;
725 dissect_collectd_values (tvb, offset, values_count, pt);
726 collectd_proto_tree_add_assembled_metric (tvb, offset + 6, length - 6,
730 } /* void dissect_collectd_part_values */
733 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
734 gint offset, proto_tree *tree_root)
742 size = tvb_reported_length_remaining (tvb, offset);
745 /* This should never happen, because `dissect_collectd' checks
746 * for this condition already. */
750 type = tvb_get_ntohs (tvb, offset);
751 length = tvb_get_ntohs (tvb, offset + 2);
753 if (size < 36) /* remaining packet size too small for signature */
755 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
756 ett_collectd_signature, NULL, "collectd %s segment: <BAD>",
757 val_to_str_const (type, part_names, "UNKNOWN"));
759 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
760 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
762 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
763 "Garbage at end of packet: Length = %i <BAD>",
770 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
771 ett_collectd_signature, NULL, "collectd %s segment: <BAD>",
772 val_to_str_const (type, part_names, "UNKNOWN"));
774 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
775 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
776 offset + 2, 2, length);
777 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
778 "Invalid length field for a signature part.");
783 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
784 ett_collectd_signature, NULL, "collectd %s segment: HMAC-SHA-256",
785 val_to_str_const (type, part_names, "UNKNOWN"));
787 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
788 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
790 proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, ENC_NA);
791 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, ENC_ASCII|ENC_NA);
794 } /* int dissect_collectd_signature */
797 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
798 gint offset, proto_tree *tree_root)
805 gint username_length;
807 size = tvb_reported_length_remaining (tvb, offset);
810 /* This should never happen, because `dissect_collectd' checks
811 * for this condition already. */
815 type = tvb_get_ntohs (tvb, offset);
816 length = tvb_get_ntohs (tvb, offset + 2);
818 if (size < 42) /* remaining packet size too small for signature */
820 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
821 ett_collectd_encryption, NULL, "collectd %s segment: <BAD>",
822 val_to_str_const (type, part_names, "UNKNOWN"));
824 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
825 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
827 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
828 "Garbage at end of packet: Length = %i <BAD>",
835 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
836 ett_collectd_encryption, NULL, "collectd %s segment: <BAD>",
837 val_to_str_const (type, part_names, "UNKNOWN"));
839 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
840 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
841 offset + 2, 2, length);
842 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
843 "Invalid length field for an encryption part.");
848 username_length = tvb_get_ntohs (tvb, offset + 4);
849 if (username_length > (length - 42))
851 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1,
852 ett_collectd_encryption, NULL, "collectd %s segment: <BAD>",
853 val_to_str_const (type, part_names, "UNKNOWN"));
855 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
856 proto_tree_add_uint (pt, hf_collectd_length, tvb,
857 offset + 2, 2, length);
858 pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb,
859 offset + 4, 2, length);
860 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
861 "Invalid username length field for an encryption part.");
866 pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length,
867 ett_collectd_encryption, NULL, "collectd %s segment: AES-256",
868 val_to_str_const (type, part_names, "UNKNOWN"));
870 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
871 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
872 proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length);
873 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, ENC_ASCII|ENC_NA);
874 proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
875 offset + (6 + username_length), 16, ENC_NA);
876 proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb,
877 offset + (22 + username_length),
878 length - (22 + username_length), ENC_NA);
881 } /* int dissect_collectd_encrypted */
884 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
886 string_counter_t *entry;
888 if (ret_list == NULL)
891 if (new_value == NULL)
892 new_value = "(null)";
894 for (entry = *ret_list; entry != NULL; entry = entry->next)
895 if (strcmp (new_value, entry->string) == 0)
901 entry = (string_counter_t *)wmem_alloc0 (wmem_packet_scope(), sizeof (*entry));
902 entry->string = wmem_strdup (wmem_packet_scope(), new_value);
904 entry->next = *ret_list;
912 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
914 static tap_data_t tap_data;
918 gchar *pkt_host = NULL;
919 gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0;
920 value_data_t vdispatch;
921 notify_data_t ndispatch;
924 proto_tree *collectd_tree;
927 memset(&vdispatch, '\0', sizeof(vdispatch));
928 memset(&ndispatch, '\0', sizeof(ndispatch));
930 col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
931 col_clear(pinfo->cinfo, COL_INFO);
934 size = tvb_reported_length(tvb);
936 /* create the collectd protocol tree */
937 pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, ENC_NA);
938 collectd_tree = proto_item_add_subtree(pi, ett_collectd);
940 memset (&tap_data, 0, sizeof (tap_data));
943 while ((size > 0) && (status == 0))
949 /* Let's handle the easy case first real quick: All we do here
950 * is extract a host name and count the number of values,
951 * plugins and notifications. The payload is not checked at
952 * all, but the same checks are run on the part_length stuff -
953 * it's important to keep an eye on that. */
956 /* Check for garbage at end of packet. */
963 part_type = tvb_get_ntohs (tvb, offset);
964 part_length = tvb_get_ntohs (tvb, offset+2);
966 /* Check if part_length is in the valid range. */
967 if ((part_length < 4) || (part_length > size))
975 vdispatch.host = tvb_get_string_enc(wmem_packet_scope(), tvb,
976 offset + 4, part_length - 4, ENC_ASCII);
977 if (pkt_host == NULL)
978 pkt_host = vdispatch.host;
984 vdispatch.plugin = tvb_get_string_enc(wmem_packet_scope(), tvb,
985 offset + 4, part_length - 4, ENC_ASCII);
988 case TYPE_PLUGIN_INSTANCE:
991 vdispatch.type = tvb_get_string_enc(wmem_packet_scope(), tvb,
992 offset + 4, part_length - 4, ENC_ASCII);
994 case TYPE_TYPE_INSTANCE:
997 case TYPE_INTERVAL_HR:
1003 tap_data.values_num++;
1004 stats_account_string (&tap_data.hosts,
1006 stats_account_string (&tap_data.plugins,
1008 stats_account_string (&tap_data.types,
1022 offset += part_length;
1023 size -= part_length;
1027 /* Now we do the same steps again, but much more thoroughly. */
1029 /* Check if there are at least four bytes left first.
1030 * Four bytes are used to read the type and the length
1031 * of the next part. If there's less, there's some garbage
1032 * at the end of the packet. */
1035 proto_tree_add_expert_format(pi, pinfo, &ei_collectd_garbage, tvb,
1037 "Garbage at end of packet: Length = %i <BAD>",
1043 /* dissect a message entry */
1044 part_type = tvb_get_ntohs (tvb, offset);
1045 part_length = tvb_get_ntohs (tvb, offset + 2);
1047 /* Check if the length of the part is in the valid range. Don't
1048 * confuse this with the above: Here we check the information
1049 * provided in the packet.. */
1050 if ((part_length < 4) || (part_length > size))
1052 pt = proto_tree_add_subtree_format(collectd_tree, tvb,
1053 offset, part_length, ett_collectd_invalid_length, NULL,
1054 "collectd %s segment: Length = %i <BAD>",
1055 val_to_str_const (part_type, part_names, "UNKNOWN"),
1058 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
1060 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
1061 offset + 2, 2, part_length);
1063 if (part_length < 4)
1064 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
1065 "Bad part length: Is %i, expected at least 4",
1068 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
1069 "Bad part length: Larger than remaining packet size.");
1075 /* The header information looks okay, let's tend to the actual
1076 * payload in this part. */
1077 switch (part_type) {
1080 status = dissect_collectd_string (tvb, pinfo,
1081 hf_collectd_data_host,
1083 &vdispatch.host_off,
1084 &vdispatch.host_len,
1086 collectd_tree, /* item = */ NULL);
1091 if (pkt_host == NULL)
1092 pkt_host = vdispatch.host;
1093 ndispatch.host_off = vdispatch.host_off;
1094 ndispatch.host_len = vdispatch.host_len;
1095 ndispatch.host = vdispatch.host;
1103 status = dissect_collectd_string (tvb, pinfo,
1104 hf_collectd_data_plugin,
1106 &vdispatch.plugin_off,
1107 &vdispatch.plugin_len,
1109 collectd_tree, /* item = */ NULL);
1118 case TYPE_PLUGIN_INSTANCE:
1120 status = dissect_collectd_string (tvb, pinfo,
1121 hf_collectd_data_plugin_inst,
1123 &vdispatch.plugin_instance_off,
1124 &vdispatch.plugin_instance_len,
1125 &vdispatch.plugin_instance,
1126 collectd_tree, /* item = */ NULL);
1135 status = dissect_collectd_string (tvb, pinfo,
1136 hf_collectd_data_type,
1138 &vdispatch.type_off,
1139 &vdispatch.type_len,
1141 collectd_tree, /* item = */ NULL);
1148 case TYPE_TYPE_INSTANCE:
1150 status = dissect_collectd_string (tvb, pinfo,
1151 hf_collectd_data_type_inst,
1153 &vdispatch.type_instance_off,
1154 &vdispatch.type_instance_len,
1155 &vdispatch.type_instance,
1156 collectd_tree, /* item = */ NULL);
1167 status = dissect_collectd_integer (tvb, pinfo,
1168 hf_collectd_data_time,
1170 &vdispatch.time_off,
1171 &vdispatch.time_value,
1172 collectd_tree, &pi);
1180 case TYPE_INTERVAL_HR:
1182 status = dissect_collectd_integer (tvb, pinfo,
1183 hf_collectd_data_interval,
1185 &vdispatch.interval_off,
1186 &vdispatch.interval,
1187 collectd_tree, /* item = */ NULL);
1196 status = dissect_collectd_part_values (tvb, pinfo,
1205 tap_data.values_num++;
1206 stats_account_string (&tap_data.hosts,
1208 stats_account_string (&tap_data.plugins,
1210 stats_account_string (&tap_data.types,
1219 status = dissect_collectd_string (tvb, pinfo,
1220 hf_collectd_data_message,
1222 &ndispatch.message_off,
1223 &ndispatch.message_len,
1225 collectd_tree, &pi);
1233 pt = proto_item_get_subtree (pi);
1235 collectd_proto_tree_add_assembled_notification (tvb,
1236 offset + 4, part_length - 1,
1245 status = dissect_collectd_integer (tvb, pinfo,
1246 hf_collectd_data_severity,
1248 &ndispatch.severity_off,
1249 &ndispatch.severity,
1250 collectd_tree, &pi);
1255 proto_item_set_text (pi,
1256 "collectd SEVERITY segment: "
1257 "%s (%"G_GINT64_MODIFIER"u)",
1258 val64_to_str_const (ndispatch.severity, severity_names, "UNKNOWN"),
1259 ndispatch.severity);
1265 case TYPE_SIGN_SHA256:
1267 status = dissect_collectd_signature (tvb, pinfo,
1276 case TYPE_ENCR_AES256:
1278 status = dissect_collectd_encrypted (tvb, pinfo,
1279 offset, collectd_tree);
1289 pt = proto_tree_add_subtree_format(collectd_tree, tvb,
1290 offset, part_length, ett_collectd_unknown, NULL,
1291 "collectd %s segment: %i bytes",
1292 val_to_str_const(part_type, part_names, "UNKNOWN"),
1295 pi = proto_tree_add_uint (pt, hf_collectd_type, tvb,
1296 offset, 2, part_type);
1297 proto_tree_add_uint (pt, hf_collectd_length, tvb,
1298 offset + 2, 2, part_length);
1299 proto_tree_add_item (pt, hf_collectd_data, tvb,
1300 offset + 4, part_length - 4, ENC_NA);
1302 expert_add_info_format(pinfo, pi, &ei_collectd_type,
1303 "Unknown part type %#x. Cannot decode data.",
1306 } /* switch (part_type) */
1308 offset += part_length;
1309 size -= part_length;
1310 } /* while ((size > 4) && (status == 0)) */
1312 if (pkt_errors && pkt_unknown)
1313 col_add_fstr (pinfo->cinfo, COL_INFO,
1314 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s",
1316 pkt_values, plurality (pkt_values, " ", "s"),
1317 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1318 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1320 pkt_errors, plurality (pkt_errors, "", "s"));
1321 else if (pkt_errors)
1322 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s",
1324 pkt_values, plurality (pkt_values, " ", "s"),
1325 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1326 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1327 pkt_errors, plurality (pkt_errors, "", "s"));
1328 else if (pkt_unknown)
1329 col_add_fstr (pinfo->cinfo, COL_INFO,
1330 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown",
1332 pkt_values, plurality (pkt_values, " ", "s"),
1333 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1334 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1337 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1339 pkt_values, plurality (pkt_values, " ", "s"),
1340 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1341 pkt_messages, plurality (pkt_messages, "", "s"));
1343 /* Dispatch tap data. */
1344 tap_queue_packet (tap_collectd, pinfo, &tap_data);
1345 } /* void dissect_collectd */
1347 void proto_register_collectd(void)
1349 module_t *collectd_module;
1350 expert_module_t* expert_collectd;
1352 /* Setup list of header fields */
1353 static hf_register_info hf[] = {
1354 { &hf_collectd_type,
1355 { "Type", "collectd.type", FT_UINT16, BASE_HEX,
1356 VALS(part_names), 0x0, NULL, HFILL }
1358 { &hf_collectd_length,
1359 { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1360 NULL, 0x0, NULL, HFILL }
1362 { &hf_collectd_data,
1363 { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1364 NULL, 0x0, NULL, HFILL }
1366 { &hf_collectd_data_host,
1367 { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1368 NULL, 0x0, NULL, HFILL }
1370 { &hf_collectd_data_interval,
1371 { "Interval", "collectd.data.interval", FT_RELATIVE_TIME, BASE_NONE,
1372 NULL, 0x0, NULL, HFILL }
1374 { &hf_collectd_data_time,
1375 { "Timestamp", "collectd.data.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL,
1376 NULL, 0x0, NULL, HFILL }
1378 { &hf_collectd_data_plugin,
1379 { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1380 NULL, 0x0, NULL, HFILL }
1382 { &hf_collectd_data_plugin_inst,
1383 { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1384 NULL, 0x0, NULL, HFILL }
1386 { &hf_collectd_data_type,
1387 { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1388 NULL, 0x0, NULL, HFILL }
1390 { &hf_collectd_data_type_inst,
1391 { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1392 NULL, 0x0, NULL, HFILL }
1394 { &hf_collectd_data_valcnt,
1395 { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1396 NULL, 0x0, NULL, HFILL }
1398 { &hf_collectd_val_type,
1399 { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1400 VALS(valuetypenames), 0x0, NULL, HFILL }
1402 { &hf_collectd_val_counter,
1403 { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1404 NULL, 0x0, NULL, HFILL }
1406 { &hf_collectd_val_gauge,
1407 { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1408 NULL, 0x0, NULL, HFILL }
1410 { &hf_collectd_val_derive,
1411 { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1412 NULL, 0x0, NULL, HFILL }
1414 { &hf_collectd_val_absolute,
1415 { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1416 NULL, 0x0, NULL, HFILL }
1418 { &hf_collectd_val_unknown,
1419 { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1420 NULL, 0x0, NULL, HFILL }
1422 { &hf_collectd_data_severity,
1423 { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX | BASE_VAL64_STRING,
1424 VALS64(severity_names),
1427 { &hf_collectd_data_message,
1428 { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1429 NULL, 0x0, NULL, HFILL }
1431 { &hf_collectd_data_sighash,
1432 { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1433 NULL, 0x0, NULL, HFILL }
1435 { &hf_collectd_data_initvec,
1436 { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1437 NULL, 0x0, NULL, HFILL }
1439 { &hf_collectd_data_username_len,
1440 { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1441 NULL, 0x0, NULL, HFILL }
1443 { &hf_collectd_data_username,
1444 { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1445 NULL, 0x0, NULL, HFILL }
1447 { &hf_collectd_data_encrypted,
1448 { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1449 NULL, 0x0, NULL, HFILL }
1453 /* Setup protocol subtree array */
1454 static gint *ett[] = {
1456 &ett_collectd_string,
1457 &ett_collectd_integer,
1458 &ett_collectd_part_value,
1459 &ett_collectd_value,
1460 &ett_collectd_valinfo,
1461 &ett_collectd_signature,
1462 &ett_collectd_encryption,
1463 &ett_collectd_dispatch,
1464 &ett_collectd_invalid_length,
1465 &ett_collectd_unknown,
1468 static ei_register_info ei[] = {
1469 { &ei_collectd_invalid_length, { "collectd.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }},
1470 { &ei_collectd_garbage, { "collectd.garbage", PI_MALFORMED, PI_ERROR, "Garbage at end of packet", EXPFILL }},
1471 { &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 }},
1472 { &ei_collectd_type, { "collectd.type.unknown", PI_UNDECODED, PI_NOTE, "Unknown part type", EXPFILL }},
1475 /* Register the protocol name and description */
1476 proto_collectd = proto_register_protocol("collectd network data", "collectd", "collectd");
1478 /* Required function calls to register the header fields and subtrees used */
1479 proto_register_field_array(proto_collectd, hf, array_length(hf));
1480 proto_register_subtree_array(ett, array_length(ett));
1481 expert_collectd = expert_register_protocol(proto_collectd);
1482 expert_register_field_array(expert_collectd, ei, array_length(ei));
1484 tap_collectd = register_tap ("collectd");
1487 * Create an unsigned integer preference to allow the user to specify the
1488 * UDP port on which to capture DIS packets.
1490 collectd_module = prefs_register_protocol (proto_collectd,
1491 proto_reg_handoff_collectd);
1493 prefs_register_uint_preference (collectd_module, "udp.port",
1494 "collectd UDP port",
1495 "Set the UDP port for collectd messages",
1496 10, &collectd_udp_port);
1497 } /* void proto_register_collectd */
1499 void proto_reg_handoff_collectd (void)
1501 static gboolean first_run = TRUE;
1502 static gint registered_udp_port = -1;
1503 static dissector_handle_t collectd_handle;
1506 collectd_handle = create_dissector_handle (dissect_collectd,
1509 /* Change the dissector registration if the preferences have been
1511 if (registered_udp_port != -1)
1512 dissector_delete_uint ("udp.port", registered_udp_port,
1515 dissector_add_uint ("udp.port", collectd_udp_port, collectd_handle);
1516 registered_udp_port = collectd_udp_port;
1519 collectd_stats_tree_register ();
1522 } /* void proto_reg_handoff_collectd */
1525 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1530 * indent-tabs-mode: t
1533 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1534 * :indentSize=8:tabSize=8:noTabs=false: