2 * Routines for collectd (http://collectd.org/) network plugin dissection
4 * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org>
5 * Copyright 2009 Florian Forster <octo at verplant.org>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 /* #include <stdio.h> */
35 #include <epan/packet.h>
36 #include <epan/prefs.h>
37 #include <epan/expert.h>
38 #include <epan/stats_tree.h>
40 #define TYPE_HOST 0x0000
41 #define TYPE_TIME 0x0001
42 #define TYPE_PLUGIN 0x0002
43 #define TYPE_PLUGIN_INSTANCE 0x0003
44 #define TYPE_TYPE 0x0004
45 #define TYPE_TYPE_INSTANCE 0x0005
46 #define TYPE_VALUES 0x0006
47 #define TYPE_INTERVAL 0x0007
48 #define TYPE_MESSAGE 0x0100
49 #define TYPE_SEVERITY 0x0101
50 #define TYPE_SIGN_SHA256 0x0200
51 #define TYPE_ENCR_AES256 0x0210
53 typedef struct value_data_s {
65 gchar *plugin_instance;
66 gint plugin_instance_off;
67 gint plugin_instance_len;
72 gint type_instance_off;
73 gint type_instance_len;
76 typedef struct notify_data_s {
90 struct string_counter_s;
91 typedef struct string_counter_s string_counter_t;
92 struct string_counter_s
96 string_counter_t *next;
99 typedef struct tap_data_s {
102 string_counter_t *hosts;
103 string_counter_t *plugins;
104 string_counter_t *types;
107 static const value_string part_names[] = {
108 { TYPE_VALUES, "VALUES" },
109 { TYPE_TIME, "TIME" },
110 { TYPE_INTERVAL, "INTERVAL" },
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 value_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 /* Prototype for the handoff function */
195 void proto_reg_handoff_collectd (void);
198 collectd_stats_tree_init (stats_tree *st)
200 st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
201 st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
203 st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
205 st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
207 st_collectd_values_types = stats_tree_create_pivot (st, "By type",
209 } /* void collectd_stats_tree_init */
212 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
213 epan_dissect_t *edt _U_, const void *user_data)
215 const tap_data_t *td;
216 string_counter_t *sc;
222 tick_stat_node (st, "Packets", 0, FALSE);
223 increase_stat_node (st, "Values", 0, TRUE, td->values_num);
225 for (sc = td->hosts; sc != NULL; sc = sc->next)
228 for (i = 0; i < sc->count; i++)
229 stats_tree_tick_pivot (st, st_collectd_values_hosts,
233 for (sc = td->plugins; sc != NULL; sc = sc->next)
236 for (i = 0; i < sc->count; i++)
237 stats_tree_tick_pivot (st, st_collectd_values_plugins,
241 for (sc = td->types; sc != NULL; sc = sc->next)
244 for (i = 0; i < sc->count; i++)
245 stats_tree_tick_pivot (st, st_collectd_values_types,
250 } /* int collectd_stats_tree_packet */
253 collectd_stats_tree_register (void)
255 stats_tree_register ("collectd", "collectd", "Collectd", 0,
256 collectd_stats_tree_packet,
257 collectd_stats_tree_init, NULL);
258 } /* void register_collectd_stat_trees */
261 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
262 gint offset, gint *ret_offset, gint *ret_length,
263 gchar **ret_string, proto_tree *tree_root,
264 proto_item **ret_item)
272 size = tvb_reported_length_remaining (tvb, offset);
275 /* This should never happen, because `dissect_collectd' checks
276 * for this condition already. */
280 type = tvb_get_ntohs(tvb, offset);
281 length = tvb_get_ntohs(tvb, offset + 2);
285 pi = proto_tree_add_text (tree_root, tvb, offset, length,
286 "collectd %s segment: Length = %i <BAD>",
287 val_to_str (type, part_names, "UNKNOWN"),
289 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
290 "String part with invalid part length: "
291 "Part is longer than rest of package.");
295 *ret_offset = offset + 4;
296 *ret_length = length - 4;
298 *ret_string = tvb_get_ephemeral_string (tvb, *ret_offset, *ret_length);
300 pi = proto_tree_add_text (tree_root, tvb, offset, length,
301 "collectd %s segment: \"%s\"",
302 val_to_str (type, part_names, "UNKNOWN"),
305 if (ret_item != NULL)
308 pt = proto_item_add_subtree (pi, ett_collectd_string);
309 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
310 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
311 proto_tree_add_item (pt, type_hf, tvb, *ret_offset, *ret_length, FALSE);
314 } /* int dissect_collectd_string */
317 dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
318 gint offset, gint *ret_offset, guint64 *ret_value,
319 proto_tree *tree_root, proto_item **ret_item)
327 size = tvb_reported_length_remaining (tvb, offset);
330 /* This should never happen, because `dissect_collectd' checks
331 * for this condition already. */
335 type = tvb_get_ntohs(tvb, offset);
336 length = tvb_get_ntohs(tvb, offset + 2);
340 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
341 "collectd %s segment: <BAD>",
342 val_to_str (type, part_names, "UNKNOWN"));
344 pt = proto_item_add_subtree (pi, ett_collectd_integer);
345 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
347 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
349 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
350 "Garbage at end of packet: Length = %i <BAD>",
352 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
353 "Garbage at end of packet");
360 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
361 "collectd %s segment: <BAD>",
362 val_to_str (type, part_names, "UNKNOWN"));
364 pt = proto_item_add_subtree (pi, ett_collectd_integer);
365 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
367 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
368 offset + 2, 2, length);
369 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
370 "Invalid length field for an integer part.");
375 *ret_offset = offset + 4;
376 *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
378 pi = proto_tree_add_text (tree_root, tvb, offset, length,
379 "collectd %s segment: %"G_GINT64_MODIFIER"u",
380 val_to_str (type, part_names, "UNKNOWN"),
382 if (ret_item != NULL)
385 pt = proto_item_add_subtree (pi, ett_collectd_integer);
386 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
387 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
389 proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, FALSE);
392 } /* int dissect_collectd_integer */
395 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
396 proto_tree *collectd_tree)
399 proto_tree *values_tree, *value_tree;
402 pi = proto_tree_add_text (collectd_tree, tvb, msg_off + 6, val_cnt * 9,
403 "%d value%s", val_cnt,
404 plurality (val_cnt, "", "s"));
406 values_tree = proto_item_add_subtree (pi, ett_collectd_value);
408 for (i = 0; i < val_cnt; i++)
412 gint value_type_offset;
415 /* Calculate the offsets of the type byte and the actual value. */
416 value_offset = msg_off + 6
417 + val_cnt /* value types */
418 + (i * 8); /* previous values */
420 value_type_offset = msg_off + 6 + i;
421 value_type = tvb_get_guint8 (tvb, value_type_offset);
423 switch (value_type) {
424 case TYPE_VALUE_COUNTER:
428 val64 = tvb_get_ntoh64 (tvb, value_offset);
429 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
431 "Counter: %"G_GINT64_MODIFIER"u", val64);
433 value_tree = proto_item_add_subtree (pi,
434 ett_collectd_valinfo);
435 proto_tree_add_item (value_tree, hf_collectd_val_type,
436 tvb, value_type_offset, 1, FALSE);
437 proto_tree_add_item (value_tree,
438 hf_collectd_val_counter, tvb,
439 value_offset, 8, FALSE);
443 case TYPE_VALUE_GAUGE:
447 val = tvb_get_letohieee_double (tvb, value_offset);
448 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
452 value_tree = proto_item_add_subtree (pi,
453 ett_collectd_valinfo);
454 proto_tree_add_item (value_tree, hf_collectd_val_type,
455 tvb, value_type_offset, 1, FALSE);
456 /* Set the `little endian' flag to TRUE here, because
457 * collectd stores doubles in x86 representation. */
458 proto_tree_add_item (value_tree, hf_collectd_val_gauge,
459 tvb, value_offset, 8, TRUE);
463 case TYPE_VALUE_DERIVE:
467 val64 = tvb_get_ntoh64 (tvb, value_offset);
468 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
470 "Derive: %"G_GINT64_MODIFIER"i", val64);
472 value_tree = proto_item_add_subtree (pi,
473 ett_collectd_valinfo);
474 proto_tree_add_item (value_tree, hf_collectd_val_type,
475 tvb, value_type_offset, 1, FALSE);
476 proto_tree_add_item (value_tree,
477 hf_collectd_val_derive, tvb,
478 value_offset, 8, FALSE);
482 case TYPE_VALUE_ABSOLUTE:
486 val64 = tvb_get_ntoh64 (tvb, value_offset);
487 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
489 "Absolute: %"G_GINT64_MODIFIER"u", val64);
491 value_tree = proto_item_add_subtree (pi,
492 ett_collectd_valinfo);
493 proto_tree_add_item (value_tree, hf_collectd_val_type,
494 tvb, value_type_offset, 1, FALSE);
495 proto_tree_add_item (value_tree,
496 hf_collectd_val_absolute, tvb,
497 value_offset, 8, FALSE);
505 val64 = tvb_get_ntoh64 (tvb, value_offset);
506 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
508 "Unknown: %"G_GINT64_MODIFIER"x",
511 value_tree = proto_item_add_subtree (pi,
512 ett_collectd_valinfo);
513 proto_tree_add_item (value_tree, hf_collectd_val_type,
514 tvb, value_type_offset, 1, FALSE);
515 proto_tree_add_item (value_tree, hf_collectd_val_unknown,
516 tvb, value_offset, 8, FALSE);
519 } /* switch (value_type) */
520 } /* for (i = 0; i < val_cnt; i++) */
521 } /* void dissect_collectd_values */
524 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
525 value_data_t *vdispatch, proto_tree *tree_root)
533 gint corrected_values_count;
535 size = tvb_reported_length_remaining (tvb, offset);
538 /* This should never happen, because `dissect_collectd' checks
539 * for this condition already. */
543 type = tvb_get_ntohs (tvb, offset);
544 length = tvb_get_ntohs (tvb, offset + 2);
548 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
549 "collectd %s segment: <BAD>",
550 val_to_str (type, part_names, "UNKNOWN"));
552 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
553 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
554 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
556 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
557 "Garbage at end of packet: Length = %i <BAD>",
559 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
560 "Garbage at end of packet");
565 if ((length < 15) || ((length % 9) != 6))
567 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
568 "collectd %s segment: <BAD>",
569 val_to_str (type, part_names, "UNKNOWN"));
571 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
572 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
573 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
574 offset + 2, 2, length);
575 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
576 "Invalid length field for a values part.");
581 values_count = tvb_get_ntohs (tvb, offset + 4);
582 corrected_values_count = (length - 6) / 9;
584 if (values_count != corrected_values_count)
586 pi = proto_tree_add_text (tree_root, tvb, offset, length,
587 "collectd %s segment: %d (%d) value%s <BAD>",
588 val_to_str (type, part_names, "UNKNOWN"),
589 values_count, corrected_values_count,
590 plurality(values_count, "", "s"));
594 pi = proto_tree_add_text (tree_root, tvb, offset, length,
595 "collectd %s segment: %d value%s",
596 val_to_str (type, part_names, "UNKNOWN"),
598 plurality(values_count, "", "s"));
601 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
602 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
603 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
605 pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb,
606 offset + 4, 2, FALSE);
607 if (values_count != corrected_values_count)
608 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_WARN,
609 "Number of values and length of part do not match. "
610 "Assuming length is correct.");
612 values_count = corrected_values_count;
614 dissect_collectd_values (tvb, offset, values_count, pt);
616 /* tell what would be dispatched... */
617 pi = proto_tree_add_text (pt, tvb, offset + 6, length - 6, "Dispatch simulation");
618 pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
619 proto_tree_add_text (pt, tvb, vdispatch->host_off, vdispatch->host_len,
620 "Host: %s", vdispatch->host);
621 proto_tree_add_text (pt, tvb, vdispatch->plugin_off,
622 vdispatch->plugin_len,
623 "Plugin: %s", vdispatch->plugin);
624 if (vdispatch->plugin_instance)
625 proto_tree_add_text (pt, tvb, vdispatch->plugin_instance_off,
626 vdispatch->plugin_instance_len,
627 "Plugin instance: %s", vdispatch->plugin_instance);
628 proto_tree_add_text (pt, tvb, vdispatch->type_off, vdispatch->type_len,
629 "Type: %s", vdispatch->type);
630 if (vdispatch->type_instance)
631 proto_tree_add_text(pt, tvb, vdispatch->type_instance_off,
632 vdispatch->type_instance_len,
633 "Type instance: %s", vdispatch->type_instance);
634 proto_tree_add_text (pt, tvb, vdispatch->time_off, 8,
635 "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
636 vdispatch->time, vdispatch->time_str);
637 proto_tree_add_text (pt, tvb, vdispatch->interval_off, 8,
638 "Interval: %"G_GINT64_MODIFIER"u",
639 vdispatch->interval);
641 } /* void dissect_collectd_part_values */
644 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
645 gint offset, proto_tree *tree_root)
653 size = tvb_reported_length_remaining (tvb, offset);
656 /* This should never happen, because `dissect_collectd' checks
657 * for this condition already. */
661 type = tvb_get_ntohs (tvb, offset);
662 length = tvb_get_ntohs (tvb, offset + 2);
664 if (size < 36) /* remaining packet size too small for signature */
666 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
667 "collectd %s segment: <BAD>",
668 val_to_str (type, part_names, "UNKNOWN"));
670 pt = proto_item_add_subtree (pi, ett_collectd_signature);
671 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
672 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
674 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
675 "Garbage at end of packet: Length = %i <BAD>",
677 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
678 "Garbage at end of packet");
685 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
686 "collectd %s segment: <BAD>",
687 val_to_str (type, part_names, "UNKNOWN"));
689 pt = proto_item_add_subtree (pi, ett_collectd_signature);
690 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
691 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
692 offset + 2, 2, length);
693 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
694 "Invalid length field for a signature part.");
699 pi = proto_tree_add_text (tree_root, tvb, offset, length,
700 "collectd %s segment: HMAC-SHA-256",
701 val_to_str (type, part_names, "UNKNOWN"));
703 pt = proto_item_add_subtree (pi, ett_collectd_signature);
704 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
705 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
707 proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, FALSE);
708 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, FALSE);
711 } /* int dissect_collectd_signature */
714 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
715 gint offset, proto_tree *tree_root)
722 gint username_length;
724 size = tvb_reported_length_remaining (tvb, offset);
727 /* This should never happen, because `dissect_collectd' checks
728 * for this condition already. */
732 type = tvb_get_ntohs (tvb, offset);
733 length = tvb_get_ntohs (tvb, offset + 2);
735 if (size < 42) /* remaining packet size too small for signature */
737 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
738 "collectd %s segment: <BAD>",
739 val_to_str (type, part_names, "UNKNOWN"));
741 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
742 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
743 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
745 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
746 "Garbage at end of packet: Length = %i <BAD>",
748 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
749 "Garbage at end of packet");
756 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
757 "collectd %s segment: <BAD>",
758 val_to_str (type, part_names, "UNKNOWN"));
760 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
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, PI_MALFORMED, PI_ERROR,
765 "Invalid length field for an encryption part.");
770 username_length = tvb_get_ntohs (tvb, offset + 4);
771 if (username_length > (length - 42))
773 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
774 "collectd %s segment: <BAD>",
775 val_to_str (type, part_names, "UNKNOWN"));
777 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
778 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
779 proto_tree_add_uint (pt, hf_collectd_length, tvb,
780 offset + 2, 2, length);
781 pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb,
782 offset + 4, 2, length);
783 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
784 "Invalid username length field for an encryption part.");
789 pi = proto_tree_add_text (tree_root, tvb, offset, length,
790 "collectd %s segment: AES-256",
791 val_to_str (type, part_names, "UNKNOWN"));
793 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
794 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
795 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
796 proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length);
797 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, FALSE);
798 proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
799 offset + (6 + username_length), 16, FALSE);
800 proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb,
801 offset + (22 + username_length),
802 length - (22 + username_length), FALSE);
805 } /* int dissect_collectd_encrypted */
808 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
810 string_counter_t *entry;
812 if (ret_list == NULL)
815 if (new_value == NULL)
816 new_value = "(null)";
818 for (entry = *ret_list; entry != NULL; entry = entry->next)
819 if (strcmp (new_value, entry->string) == 0)
825 entry = ep_alloc (sizeof (*entry));
828 memset (entry, 0, sizeof (*entry));
830 entry->string = ep_strdup (new_value);
831 if (entry->string == NULL)
834 entry->next = *ret_list;
842 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
844 static tap_data_t tap_data;
848 gchar *pkt_host = NULL;
849 gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0;
850 value_data_t vdispatch;
851 notify_data_t ndispatch;
854 proto_tree *collectd_tree;
857 memset(&vdispatch, '\0', sizeof(vdispatch));
858 memset(&ndispatch, '\0', sizeof(ndispatch));
860 col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
861 col_clear(pinfo->cinfo, COL_INFO);
864 size = tvb_reported_length(tvb);
866 /* create the collectd protocol tree */
867 pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, FALSE);
868 collectd_tree = proto_item_add_subtree(pi, ett_collectd);
870 memset (&tap_data, 0, sizeof (tap_data));
873 while ((size > 0) && (status == 0))
879 /* Let's handle the easy case first real quick: All we do here
880 * is extract a host name and count the number of values,
881 * plugins and notifications. The payload is not checked at
882 * all, but the same checks are run on the part_length stuff -
883 * it's important to keep an eye on that. */
886 /* Check for garbage at end of packet. */
894 part_type = tvb_get_ntohs (tvb, offset);
895 part_length = tvb_get_ntohs (tvb, offset+2);
897 /* Check if part_length is in the valid range. */
898 if ((part_length < 4) || (part_length > size))
907 vdispatch.host = tvb_get_ephemeral_string (tvb,
908 offset + 4, part_length - 4);
909 if (pkt_host == NULL)
910 pkt_host = vdispatch.host;
915 vdispatch.plugin = tvb_get_ephemeral_string (tvb,
916 offset + 4, part_length - 4);
919 case TYPE_PLUGIN_INSTANCE:
922 vdispatch.type = tvb_get_ephemeral_string (tvb,
923 offset + 4, part_length - 4);
925 case TYPE_TYPE_INSTANCE:
933 tap_data.values_num++;
934 stats_account_string (&tap_data.hosts,
936 stats_account_string (&tap_data.plugins,
938 stats_account_string (&tap_data.types,
952 offset += part_length;
957 /* Now we do the same steps again, but much more thoroughly. */
959 /* Check if there are at least four bytes left first.
960 * Four bytes are used to read the type and the length
961 * of the next part. If there's less, there's some garbage
962 * at the end of the packet. */
965 pi = proto_tree_add_text (collectd_tree, tvb,
967 "Garbage at end of packet: Length = %i <BAD>",
969 expert_add_info_format (pinfo, pi, PI_MALFORMED,
971 "Garbage at end of packet");
977 /* dissect a message entry */
978 part_type = tvb_get_ntohs (tvb, offset);
979 part_length = tvb_get_ntohs (tvb, offset + 2);
981 /* Check if the length of the part is in the valid range. Don't
982 * confuse this with the above: Here we check the information
983 * provided in the packet.. */
984 if ((part_length < 4) || (part_length > size))
986 pi = proto_tree_add_text (collectd_tree, tvb,
988 "collectd %s segment: Length = %i <BAD>",
989 val_to_str (part_type, part_names, "UNKNOWN"),
992 pt = proto_item_add_subtree (pi, ett_collectd_invalid_length);
993 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
995 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
996 offset + 2, 2, part_length);
999 expert_add_info_format (pinfo, pi,
1000 PI_MALFORMED, PI_ERROR,
1001 "Bad part length: Is %i, expected at least 4",
1004 expert_add_info_format (pinfo, pi,
1005 PI_MALFORMED, PI_ERROR,
1006 "Bad part length: Larger than remaining packet size.");
1013 /* The header information looks okay, let's tend to the actual
1014 * payload in this part. */
1015 switch (part_type) {
1018 status = dissect_collectd_string (tvb, pinfo,
1019 hf_collectd_data_host,
1021 &vdispatch.host_off,
1022 &vdispatch.host_len,
1024 collectd_tree, /* item = */ NULL);
1029 if (pkt_host == NULL)
1030 pkt_host = vdispatch.host;
1031 ndispatch.host_off = vdispatch.host_off;
1032 ndispatch.host_len = vdispatch.host_len;
1033 ndispatch.host = vdispatch.host;
1041 status = dissect_collectd_string (tvb, pinfo,
1042 hf_collectd_data_plugin,
1044 &vdispatch.plugin_off,
1045 &vdispatch.plugin_len,
1047 collectd_tree, /* item = */ NULL);
1056 case TYPE_PLUGIN_INSTANCE:
1058 status = dissect_collectd_string (tvb, pinfo,
1059 hf_collectd_data_plugin_inst,
1061 &vdispatch.plugin_instance_off,
1062 &vdispatch.plugin_instance_len,
1063 &vdispatch.plugin_instance,
1064 collectd_tree, /* item = */ NULL);
1073 status = dissect_collectd_string (tvb, pinfo,
1074 hf_collectd_data_type,
1076 &vdispatch.type_off,
1077 &vdispatch.type_len,
1079 collectd_tree, /* item = */ NULL);
1086 case TYPE_TYPE_INSTANCE:
1088 status = dissect_collectd_string (tvb, pinfo,
1089 hf_collectd_data_type_inst,
1091 &vdispatch.type_instance_off,
1092 &vdispatch.type_instance_len,
1093 &vdispatch.type_instance,
1094 collectd_tree, /* item = */ NULL);
1103 ndispatch.time_str = NULL;
1104 vdispatch.time_str = NULL;
1107 status = dissect_collectd_integer (tvb, pinfo,
1108 hf_collectd_data_time,
1110 &vdispatch.time_off,
1112 collectd_tree, &pi);
1117 vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time);
1119 ndispatch.time = vdispatch.time;
1120 ndispatch.time_str = vdispatch.time_str;
1122 proto_item_set_text (pi, "collectd TIME segment: %"G_GINT64_MODIFIER"u (%s)",
1123 vdispatch.time, vdispatch.time_str);
1131 status = dissect_collectd_integer (tvb, pinfo,
1132 hf_collectd_data_interval,
1134 &vdispatch.interval_off,
1135 &vdispatch.interval,
1136 collectd_tree, /* item = */ NULL);
1145 status = dissect_collectd_part_values (tvb, pinfo,
1154 tap_data.values_num++;
1155 stats_account_string (&tap_data.hosts,
1157 stats_account_string (&tap_data.plugins,
1159 stats_account_string (&tap_data.types,
1168 status = dissect_collectd_string (tvb, pinfo,
1169 hf_collectd_data_message,
1171 &ndispatch.message_off,
1172 &ndispatch.message_len,
1174 collectd_tree, &pi);
1182 pt = proto_item_get_subtree (pi);
1184 /* tell what would be dispatched... */
1185 pi = proto_tree_add_text (pt, tvb, offset + 4,
1187 "Dispatch simulation");
1188 pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
1189 proto_tree_add_text (pt, tvb, ndispatch.host_off,
1191 "Host: %s", ndispatch.host);
1192 proto_tree_add_text (pt, tvb, ndispatch.time_off, 8,
1193 "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
1194 ndispatch.time, ndispatch.time_str);
1195 proto_tree_add_text (pt, tvb, ndispatch.severity_off, 8,
1196 "Severity: %s (%#"G_GINT64_MODIFIER"x)",
1197 val_to_str((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1198 ndispatch.severity);
1199 proto_tree_add_text (pt, tvb, ndispatch.message_off,
1200 ndispatch.message_len,
1201 "Message: %s", ndispatch.message);
1208 status = dissect_collectd_integer (tvb, pinfo,
1209 hf_collectd_data_severity,
1211 &ndispatch.severity_off,
1212 &ndispatch.severity,
1213 collectd_tree, &pi);
1218 proto_item_set_text (pi,
1219 "collectd SEVERITY segment: "
1220 "%s (%"G_GINT64_MODIFIER"u)",
1221 val_to_str ((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1222 ndispatch.severity);
1228 case TYPE_SIGN_SHA256:
1230 status = dissect_collectd_signature (tvb, pinfo,
1239 case TYPE_ENCR_AES256:
1241 status = dissect_collectd_encrypted (tvb, pinfo,
1242 offset, collectd_tree);
1252 pi = proto_tree_add_text (collectd_tree, tvb,
1253 offset, part_length,
1254 "collectd %s segment: %i bytes",
1255 val_to_str(part_type, part_names, "UNKNOWN"),
1258 pt = proto_item_add_subtree(pi, ett_collectd_unknown);
1259 pi = proto_tree_add_uint (pt, hf_collectd_type, tvb,
1260 offset, 2, part_type);
1261 proto_tree_add_uint (pt, hf_collectd_length, tvb,
1262 offset + 2, 2, part_length);
1263 proto_tree_add_item (pt, hf_collectd_data, tvb,
1264 offset + 4, part_length - 4, FALSE);
1266 expert_add_info_format (pinfo, pi,
1267 PI_UNDECODED, PI_NOTE,
1268 "Unknown part type %#x. Cannot decode data.",
1271 } /* switch (part_type) */
1273 offset += part_length;
1274 size -= part_length;
1275 } /* while ((size > 4) && (status == 0)) */
1277 if (pkt_errors && pkt_unknown)
1278 col_add_fstr (pinfo->cinfo, COL_INFO,
1279 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s",
1281 pkt_values, plurality (pkt_values, " ", "s"),
1282 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1283 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1285 pkt_errors, plurality (pkt_errors, "", "s"));
1286 else if (pkt_errors)
1287 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s",
1289 pkt_values, plurality (pkt_values, " ", "s"),
1290 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1291 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1292 pkt_errors, plurality (pkt_errors, "", "s"));
1293 else if (pkt_unknown)
1294 col_add_fstr (pinfo->cinfo, COL_INFO,
1295 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown",
1297 pkt_values, plurality (pkt_values, " ", "s"),
1298 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1299 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1302 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1304 pkt_values, plurality (pkt_values, " ", "s"),
1305 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1306 pkt_messages, plurality (pkt_messages, "", "s"));
1308 /* Dispatch tap data. */
1309 tap_queue_packet (tap_collectd, pinfo, &tap_data);
1310 } /* void dissect_collectd */
1312 void proto_register_collectd(void)
1314 module_t *collectd_module;
1316 /* Setup list of header fields */
1317 static hf_register_info hf[] = {
1318 { &hf_collectd_type,
1319 { "Type", "collectd.type", FT_UINT16, BASE_HEX,
1320 VALS(part_names), 0x0, NULL, HFILL }
1322 { &hf_collectd_length,
1323 { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1324 NULL, 0x0, NULL, HFILL }
1326 { &hf_collectd_data,
1327 { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1328 NULL, 0x0, NULL, HFILL }
1330 { &hf_collectd_data_host,
1331 { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1332 NULL, 0x0, NULL, HFILL }
1334 { &hf_collectd_data_interval,
1335 { "Interval", "collectd.data.interval", FT_UINT64, BASE_DEC,
1336 NULL, 0x0, NULL, HFILL }
1338 { &hf_collectd_data_time,
1339 { "Timestamp", "collectd.data.time", FT_UINT64, BASE_DEC,
1340 NULL, 0x0, NULL, HFILL }
1342 { &hf_collectd_data_plugin,
1343 { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1344 NULL, 0x0, NULL, HFILL }
1346 { &hf_collectd_data_plugin_inst,
1347 { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1348 NULL, 0x0, NULL, HFILL }
1350 { &hf_collectd_data_type,
1351 { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1352 NULL, 0x0, NULL, HFILL }
1354 { &hf_collectd_data_type_inst,
1355 { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1356 NULL, 0x0, NULL, HFILL }
1358 { &hf_collectd_data_valcnt,
1359 { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1360 NULL, 0x0, NULL, HFILL }
1362 { &hf_collectd_val_type,
1363 { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1364 VALS(valuetypenames), 0x0, NULL, HFILL }
1366 { &hf_collectd_val_counter,
1367 { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1368 NULL, 0x0, NULL, HFILL }
1370 { &hf_collectd_val_gauge,
1371 { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1372 NULL, 0x0, NULL, HFILL }
1374 { &hf_collectd_val_derive,
1375 { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1376 NULL, 0x0, NULL, HFILL }
1378 { &hf_collectd_val_absolute,
1379 { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1380 NULL, 0x0, NULL, HFILL }
1382 { &hf_collectd_val_unknown,
1383 { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1384 NULL, 0x0, NULL, HFILL }
1386 { &hf_collectd_data_severity,
1387 { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX,
1388 NULL, 0x0, NULL, HFILL }
1390 { &hf_collectd_data_message,
1391 { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1392 NULL, 0x0, NULL, HFILL }
1394 { &hf_collectd_data_sighash,
1395 { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1396 NULL, 0x0, NULL, HFILL }
1398 { &hf_collectd_data_initvec,
1399 { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1400 NULL, 0x0, NULL, HFILL }
1402 { &hf_collectd_data_username_len,
1403 { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1404 NULL, 0x0, NULL, HFILL }
1406 { &hf_collectd_data_username,
1407 { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1408 NULL, 0x0, NULL, HFILL }
1410 { &hf_collectd_data_encrypted,
1411 { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1412 NULL, 0x0, NULL, HFILL }
1416 /* Setup protocol subtree array */
1417 static gint *ett[] = {
1419 &ett_collectd_string,
1420 &ett_collectd_integer,
1421 &ett_collectd_part_value,
1422 &ett_collectd_value,
1423 &ett_collectd_valinfo,
1424 &ett_collectd_signature,
1425 &ett_collectd_encryption,
1426 &ett_collectd_dispatch,
1427 &ett_collectd_invalid_length,
1428 &ett_collectd_unknown,
1431 /* Register the protocol name and description */
1432 proto_collectd = proto_register_protocol("collectd network data",
1433 "collectd", "collectd");
1435 /* Required function calls to register the header fields and subtrees used */
1436 proto_register_field_array(proto_collectd, hf, array_length(hf));
1437 proto_register_subtree_array(ett, array_length(ett));
1439 tap_collectd = register_tap ("collectd");
1442 * Create an unsigned integer preference to allow the user to specify the
1443 * UDP port on which to capture DIS packets.
1445 collectd_module = prefs_register_protocol (proto_collectd,
1446 proto_reg_handoff_collectd);
1448 prefs_register_uint_preference (collectd_module, "udp.port",
1449 "collectd UDP port",
1450 "Set the UDP port for collectd messages",
1451 10, &collectd_udp_port);
1452 } /* void proto_register_collectd */
1454 void proto_reg_handoff_collectd (void)
1456 static gboolean first_run = TRUE;
1457 static gint registered_udp_port = -1;
1458 static dissector_handle_t collectd_handle;
1461 collectd_handle = create_dissector_handle (dissect_collectd,
1464 /* Change the dissector registration if the preferences have been
1466 if (registered_udp_port != -1)
1467 dissector_delete ("udp.port", registered_udp_port,
1470 dissector_add ("udp.port", collectd_udp_port, collectd_handle);
1471 registered_udp_port = collectd_udp_port;
1474 collectd_stats_tree_register ();
1477 } /* void proto_reg_handoff_collectd */