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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36 #include <epan/expert.h>
37 #include <epan/stats_tree.h>
39 #define TYPE_HOST 0x0000
40 #define TYPE_TIME 0x0001
41 #define TYPE_PLUGIN 0x0002
42 #define TYPE_PLUGIN_INSTANCE 0x0003
43 #define TYPE_TYPE 0x0004
44 #define TYPE_TYPE_INSTANCE 0x0005
45 #define TYPE_VALUES 0x0006
46 #define TYPE_INTERVAL 0x0007
47 #define TYPE_MESSAGE 0x0100
48 #define TYPE_SEVERITY 0x0101
49 #define TYPE_SIGN_SHA256 0x0200
50 #define TYPE_ENCR_AES256 0x0210
52 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 {
89 struct string_counter_s;
90 typedef struct string_counter_s string_counter_t;
91 struct string_counter_s
95 string_counter_t *next;
98 typedef struct tap_data_s {
101 string_counter_t *hosts;
102 string_counter_t *plugins;
103 string_counter_t *types;
106 static const value_string part_names[] = {
107 { TYPE_VALUES, "VALUES" },
108 { TYPE_TIME, "TIME" },
109 { TYPE_INTERVAL, "INTERVAL" },
110 { TYPE_HOST, "HOST" },
111 { TYPE_PLUGIN, "PLUGIN" },
112 { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" },
113 { TYPE_TYPE, "TYPE" },
114 { TYPE_TYPE_INSTANCE, "TYPE_INSTANCE" },
115 { TYPE_MESSAGE, "MESSAGE" },
116 { TYPE_SEVERITY, "SEVERITY" },
117 { TYPE_SIGN_SHA256, "SIGNATURE" },
118 { TYPE_ENCR_AES256, "ENCRYPTED_DATA" },
122 #define TYPE_VALUE_COUNTER 0x00
123 #define TYPE_VALUE_GAUGE 0x01
124 #define TYPE_VALUE_DERIVE 0x02
125 #define TYPE_VALUE_ABSOLUTE 0x03
126 static const value_string valuetypenames[] = {
127 { TYPE_VALUE_COUNTER, "COUNTER" },
128 { TYPE_VALUE_GAUGE, "GAUGE" },
129 { TYPE_VALUE_DERIVE, "DERIVE" },
130 { TYPE_VALUE_ABSOLUTE, "ABSOLUTE" },
134 #define SEVERITY_FAILURE 0x01
135 #define SEVERITY_WARNING 0x02
136 #define SEVERITY_OKAY 0x04
137 static const value_string severity_names[] = {
138 { SEVERITY_FAILURE, "FAILURE" },
139 { SEVERITY_WARNING, "WARNING" },
140 { SEVERITY_OKAY, "OKAY" },
144 #define UDP_PORT_COLLECTD 25826
145 static guint collectd_udp_port = UDP_PORT_COLLECTD;
147 static gint proto_collectd = -1;
148 static gint tap_collectd = -1;
150 static gint hf_collectd_type = -1;
151 static gint hf_collectd_length = -1;
152 static gint hf_collectd_data = -1;
153 static gint hf_collectd_data_host = -1;
154 static gint hf_collectd_data_time = -1;
155 static gint hf_collectd_data_interval = -1;
156 static gint hf_collectd_data_plugin = -1;
157 static gint hf_collectd_data_plugin_inst= -1;
158 static gint hf_collectd_data_type = -1;
159 static gint hf_collectd_data_type_inst = -1;
160 static gint hf_collectd_data_valcnt = -1;
161 static gint hf_collectd_val_type = -1;
162 static gint hf_collectd_val_counter = -1;
163 static gint hf_collectd_val_gauge = -1;
164 static gint hf_collectd_val_derive = -1;
165 static gint hf_collectd_val_absolute = -1;
166 static gint hf_collectd_val_unknown = -1;
167 static gint hf_collectd_data_severity = -1;
168 static gint hf_collectd_data_message = -1;
169 static gint hf_collectd_data_sighash = -1;
170 static gint hf_collectd_data_initvec = -1;
171 static gint hf_collectd_data_username_len = -1;
172 static gint hf_collectd_data_username = -1;
173 static gint hf_collectd_data_encrypted = -1;
175 static gint ett_collectd = -1;
176 static gint ett_collectd_string = -1;
177 static gint ett_collectd_integer = -1;
178 static gint ett_collectd_part_value = -1;
179 static gint ett_collectd_value = -1;
180 static gint ett_collectd_valinfo = -1;
181 static gint ett_collectd_signature = -1;
182 static gint ett_collectd_encryption = -1;
183 static gint ett_collectd_dispatch = -1;
184 static gint ett_collectd_invalid_length = -1;
185 static gint ett_collectd_unknown = -1;
187 static gint st_collectd_packets = -1;
188 static gint st_collectd_values = -1;
189 static gint st_collectd_values_hosts = -1;
190 static gint st_collectd_values_plugins = -1;
191 static gint st_collectd_values_types = -1;
193 /* Prototype for the handoff function */
194 void proto_reg_handoff_collectd (void);
197 collectd_stats_tree_init (stats_tree *st)
199 st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
200 st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
202 st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
204 st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
206 st_collectd_values_types = stats_tree_create_pivot (st, "By type",
208 } /* void collectd_stats_tree_init */
211 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
212 epan_dissect_t *edt _U_, const void *user_data)
214 const tap_data_t *td;
215 string_counter_t *sc;
221 tick_stat_node (st, "Packets", 0, FALSE);
222 increase_stat_node (st, "Values", 0, TRUE, td->values_num);
224 for (sc = td->hosts; sc != NULL; sc = sc->next)
227 for (i = 0; i < sc->count; i++)
228 stats_tree_tick_pivot (st, st_collectd_values_hosts,
232 for (sc = td->plugins; sc != NULL; sc = sc->next)
235 for (i = 0; i < sc->count; i++)
236 stats_tree_tick_pivot (st, st_collectd_values_plugins,
240 for (sc = td->types; sc != NULL; sc = sc->next)
243 for (i = 0; i < sc->count; i++)
244 stats_tree_tick_pivot (st, st_collectd_values_types,
249 } /* int collectd_stats_tree_packet */
252 collectd_stats_tree_register (void)
254 stats_tree_register ("collectd", "collectd", "Collectd", 0,
255 collectd_stats_tree_packet,
256 collectd_stats_tree_init, NULL);
257 } /* void register_collectd_stat_trees */
260 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
261 gint offset, gint *ret_offset, gint *ret_length,
262 gchar **ret_string, proto_tree *tree_root,
263 proto_item **ret_item)
271 size = tvb_reported_length_remaining (tvb, offset);
274 /* This should never happen, because `dissect_collectd' checks
275 * for this condition already. */
279 type = tvb_get_ntohs(tvb, offset);
280 length = tvb_get_ntohs(tvb, offset + 2);
284 pi = proto_tree_add_text (tree_root, tvb, offset, length,
285 "collectd %s segment: Length = %i <BAD>",
286 val_to_str_const (type, part_names, "UNKNOWN"),
288 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
289 "String part with invalid part length: "
290 "Part is longer than rest of package.");
294 *ret_offset = offset + 4;
295 *ret_length = length - 4;
297 *ret_string = tvb_get_ephemeral_string (tvb, *ret_offset, *ret_length);
299 pi = proto_tree_add_text (tree_root, tvb, offset, length,
300 "collectd %s segment: \"%s\"",
301 val_to_str_const (type, part_names, "UNKNOWN"),
304 if (ret_item != NULL)
307 pt = proto_item_add_subtree (pi, ett_collectd_string);
308 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
309 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
310 proto_tree_add_item (pt, type_hf, tvb, *ret_offset, *ret_length, ENC_ASCII|ENC_NA);
313 } /* int dissect_collectd_string */
316 dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
317 gint offset, gint *ret_offset, guint64 *ret_value,
318 proto_tree *tree_root, proto_item **ret_item)
326 size = tvb_reported_length_remaining (tvb, offset);
329 /* This should never happen, because `dissect_collectd' checks
330 * for this condition already. */
334 type = tvb_get_ntohs(tvb, offset);
335 length = tvb_get_ntohs(tvb, offset + 2);
339 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
340 "collectd %s segment: <BAD>",
341 val_to_str_const (type, part_names, "UNKNOWN"));
343 pt = proto_item_add_subtree (pi, ett_collectd_integer);
344 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
346 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
348 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
349 "Garbage at end of packet: Length = %i <BAD>",
351 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
352 "Garbage at end of packet");
359 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
360 "collectd %s segment: <BAD>",
361 val_to_str_const (type, part_names, "UNKNOWN"));
363 pt = proto_item_add_subtree (pi, ett_collectd_integer);
364 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
366 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
367 offset + 2, 2, length);
368 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
369 "Invalid length field for an integer part.");
374 *ret_offset = offset + 4;
375 *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
377 pi = proto_tree_add_text (tree_root, tvb, offset, length,
378 "collectd %s segment: %"G_GINT64_MODIFIER"u",
379 val_to_str_const (type, part_names, "UNKNOWN"),
381 if (ret_item != NULL)
384 pt = proto_item_add_subtree (pi, ett_collectd_integer);
385 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
386 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
388 proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, ENC_BIG_ENDIAN);
391 } /* int dissect_collectd_integer */
394 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
395 proto_tree *collectd_tree)
398 proto_tree *values_tree, *value_tree;
401 pi = proto_tree_add_text (collectd_tree, tvb, msg_off + 6, val_cnt * 9,
402 "%d value%s", val_cnt,
403 plurality (val_cnt, "", "s"));
405 values_tree = proto_item_add_subtree (pi, ett_collectd_value);
407 for (i = 0; i < val_cnt; i++)
411 gint value_type_offset;
414 /* Calculate the offsets of the type byte and the actual value. */
415 value_offset = msg_off + 6
416 + val_cnt /* value types */
417 + (i * 8); /* previous values */
419 value_type_offset = msg_off + 6 + i;
420 value_type = tvb_get_guint8 (tvb, value_type_offset);
422 switch (value_type) {
423 case TYPE_VALUE_COUNTER:
427 val64 = tvb_get_ntoh64 (tvb, value_offset);
428 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
430 "Counter: %"G_GINT64_MODIFIER"u", val64);
432 value_tree = proto_item_add_subtree (pi,
433 ett_collectd_valinfo);
434 proto_tree_add_item (value_tree, hf_collectd_val_type,
435 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
436 proto_tree_add_item (value_tree,
437 hf_collectd_val_counter, tvb,
438 value_offset, 8, ENC_BIG_ENDIAN);
442 case TYPE_VALUE_GAUGE:
446 val = tvb_get_letohieee_double (tvb, value_offset);
447 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
451 value_tree = proto_item_add_subtree (pi,
452 ett_collectd_valinfo);
453 proto_tree_add_item (value_tree, hf_collectd_val_type,
454 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
455 /* Set the `little endian' flag to TRUE here, because
456 * collectd stores doubles in x86 representation. */
457 proto_tree_add_item (value_tree, hf_collectd_val_gauge,
458 tvb, value_offset, 8, ENC_LITTLE_ENDIAN);
462 case TYPE_VALUE_DERIVE:
466 val64 = tvb_get_ntoh64 (tvb, value_offset);
467 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
469 "Derive: %"G_GINT64_MODIFIER"i", val64);
471 value_tree = proto_item_add_subtree (pi,
472 ett_collectd_valinfo);
473 proto_tree_add_item (value_tree, hf_collectd_val_type,
474 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
475 proto_tree_add_item (value_tree,
476 hf_collectd_val_derive, tvb,
477 value_offset, 8, ENC_BIG_ENDIAN);
481 case TYPE_VALUE_ABSOLUTE:
485 val64 = tvb_get_ntoh64 (tvb, value_offset);
486 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
488 "Absolute: %"G_GINT64_MODIFIER"u", val64);
490 value_tree = proto_item_add_subtree (pi,
491 ett_collectd_valinfo);
492 proto_tree_add_item (value_tree, hf_collectd_val_type,
493 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
494 proto_tree_add_item (value_tree,
495 hf_collectd_val_absolute, tvb,
496 value_offset, 8, ENC_BIG_ENDIAN);
504 val64 = tvb_get_ntoh64 (tvb, value_offset);
505 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
507 "Unknown: %"G_GINT64_MODIFIER"x",
510 value_tree = proto_item_add_subtree (pi,
511 ett_collectd_valinfo);
512 proto_tree_add_item (value_tree, hf_collectd_val_type,
513 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
514 proto_tree_add_item (value_tree, hf_collectd_val_unknown,
515 tvb, value_offset, 8, ENC_BIG_ENDIAN);
518 } /* switch (value_type) */
519 } /* for (i = 0; i < val_cnt; i++) */
520 } /* void dissect_collectd_values */
523 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
524 value_data_t *vdispatch, proto_tree *tree_root)
532 gint corrected_values_count;
534 size = tvb_reported_length_remaining (tvb, offset);
537 /* This should never happen, because `dissect_collectd' checks
538 * for this condition already. */
542 type = tvb_get_ntohs (tvb, offset);
543 length = tvb_get_ntohs (tvb, offset + 2);
547 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
548 "collectd %s segment: <BAD>",
549 val_to_str_const (type, part_names, "UNKNOWN"));
551 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
552 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
553 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
555 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
556 "Garbage at end of packet: Length = %i <BAD>",
558 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
559 "Garbage at end of packet");
564 if ((length < 15) || ((length % 9) != 6))
566 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
567 "collectd %s segment: <BAD>",
568 val_to_str_const (type, part_names, "UNKNOWN"));
570 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
571 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
572 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
573 offset + 2, 2, length);
574 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
575 "Invalid length field for a values part.");
580 values_count = tvb_get_ntohs (tvb, offset + 4);
581 corrected_values_count = (length - 6) / 9;
583 if (values_count != corrected_values_count)
585 pi = proto_tree_add_text (tree_root, tvb, offset, length,
586 "collectd %s segment: %d (%d) value%s <BAD>",
587 val_to_str_const (type, part_names, "UNKNOWN"),
588 values_count, corrected_values_count,
589 plurality(values_count, "", "s"));
593 pi = proto_tree_add_text (tree_root, tvb, offset, length,
594 "collectd %s segment: %d value%s",
595 val_to_str_const (type, part_names, "UNKNOWN"),
597 plurality(values_count, "", "s"));
600 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
601 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
602 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
604 pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb,
605 offset + 4, 2, ENC_BIG_ENDIAN);
606 if (values_count != corrected_values_count)
607 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_WARN,
608 "Number of values and length of part do not match. "
609 "Assuming length is correct.");
611 values_count = corrected_values_count;
613 dissect_collectd_values (tvb, offset, values_count, pt);
615 /* tell what would be dispatched... */
616 pi = proto_tree_add_text (pt, tvb, offset + 6, length - 6, "Dispatch simulation");
617 pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
618 proto_tree_add_text (pt, tvb, vdispatch->host_off, vdispatch->host_len,
619 "Host: %s", vdispatch->host ? vdispatch->host : "(null)");
620 proto_tree_add_text (pt, tvb, vdispatch->plugin_off,
621 vdispatch->plugin_len,
622 "Plugin: %s", vdispatch->plugin ? vdispatch->plugin : "(null)");
623 if (vdispatch->plugin_instance)
624 proto_tree_add_text (pt, tvb, vdispatch->plugin_instance_off,
625 vdispatch->plugin_instance_len,
626 "Plugin instance: %s", vdispatch->plugin_instance);
627 proto_tree_add_text (pt, tvb, vdispatch->type_off, vdispatch->type_len,
628 "Type: %s", vdispatch->type ? vdispatch->type : "(null)");
629 if (vdispatch->type_instance)
630 proto_tree_add_text(pt, tvb, vdispatch->type_instance_off,
631 vdispatch->type_instance_len,
632 "Type instance: %s", vdispatch->type_instance);
633 proto_tree_add_text (pt, tvb, vdispatch->time_off, 8,
634 "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
635 vdispatch->time, vdispatch->time_str ? vdispatch->time_str : "(null)");
636 proto_tree_add_text (pt, tvb, vdispatch->interval_off, 8,
637 "Interval: %"G_GINT64_MODIFIER"u",
638 vdispatch->interval);
640 } /* void dissect_collectd_part_values */
643 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
644 gint offset, proto_tree *tree_root)
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);
663 if (size < 36) /* remaining packet size too small for signature */
665 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
666 "collectd %s segment: <BAD>",
667 val_to_str_const (type, part_names, "UNKNOWN"));
669 pt = proto_item_add_subtree (pi, ett_collectd_signature);
670 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
671 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
673 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
674 "Garbage at end of packet: Length = %i <BAD>",
676 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
677 "Garbage at end of packet");
684 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
685 "collectd %s segment: <BAD>",
686 val_to_str_const (type, part_names, "UNKNOWN"));
688 pt = proto_item_add_subtree (pi, ett_collectd_signature);
689 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
690 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
691 offset + 2, 2, length);
692 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
693 "Invalid length field for a signature part.");
698 pi = proto_tree_add_text (tree_root, tvb, offset, length,
699 "collectd %s segment: HMAC-SHA-256",
700 val_to_str_const (type, part_names, "UNKNOWN"));
702 pt = proto_item_add_subtree (pi, ett_collectd_signature);
703 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
704 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
706 proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, ENC_NA);
707 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, ENC_ASCII|ENC_NA);
710 } /* int dissect_collectd_signature */
713 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
714 gint offset, proto_tree *tree_root)
721 gint username_length;
723 size = tvb_reported_length_remaining (tvb, offset);
726 /* This should never happen, because `dissect_collectd' checks
727 * for this condition already. */
731 type = tvb_get_ntohs (tvb, offset);
732 length = tvb_get_ntohs (tvb, offset + 2);
734 if (size < 42) /* remaining packet size too small for signature */
736 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
737 "collectd %s segment: <BAD>",
738 val_to_str_const (type, part_names, "UNKNOWN"));
740 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
741 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
742 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
744 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
745 "Garbage at end of packet: Length = %i <BAD>",
747 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
748 "Garbage at end of packet");
755 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
756 "collectd %s segment: <BAD>",
757 val_to_str_const (type, part_names, "UNKNOWN"));
759 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
760 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
761 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
762 offset + 2, 2, length);
763 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
764 "Invalid length field for an encryption part.");
769 username_length = tvb_get_ntohs (tvb, offset + 4);
770 if (username_length > (length - 42))
772 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
773 "collectd %s segment: <BAD>",
774 val_to_str_const (type, part_names, "UNKNOWN"));
776 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
777 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
778 proto_tree_add_uint (pt, hf_collectd_length, tvb,
779 offset + 2, 2, length);
780 pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb,
781 offset + 4, 2, length);
782 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
783 "Invalid username length field for an encryption part.");
788 pi = proto_tree_add_text (tree_root, tvb, offset, length,
789 "collectd %s segment: AES-256",
790 val_to_str_const (type, part_names, "UNKNOWN"));
792 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
793 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
794 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
795 proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length);
796 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, ENC_ASCII|ENC_NA);
797 proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
798 offset + (6 + username_length), 16, ENC_NA);
799 proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb,
800 offset + (22 + username_length),
801 length - (22 + username_length), ENC_NA);
804 } /* int dissect_collectd_encrypted */
807 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
809 string_counter_t *entry;
811 if (ret_list == NULL)
814 if (new_value == NULL)
815 new_value = "(null)";
817 for (entry = *ret_list; entry != NULL; entry = entry->next)
818 if (strcmp (new_value, entry->string) == 0)
824 entry = ep_alloc0 (sizeof (*entry));
825 entry->string = ep_strdup (new_value);
827 entry->next = *ret_list;
835 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
837 static tap_data_t tap_data;
841 gchar *pkt_host = NULL;
842 gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0;
843 value_data_t vdispatch;
844 notify_data_t ndispatch;
847 proto_tree *collectd_tree;
850 memset(&vdispatch, '\0', sizeof(vdispatch));
851 memset(&ndispatch, '\0', sizeof(ndispatch));
853 col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
854 col_clear(pinfo->cinfo, COL_INFO);
857 size = tvb_reported_length(tvb);
859 /* create the collectd protocol tree */
860 pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, ENC_NA);
861 collectd_tree = proto_item_add_subtree(pi, ett_collectd);
863 memset (&tap_data, 0, sizeof (tap_data));
866 while ((size > 0) && (status == 0))
872 /* Let's handle the easy case first real quick: All we do here
873 * is extract a host name and count the number of values,
874 * plugins and notifications. The payload is not checked at
875 * all, but the same checks are run on the part_length stuff -
876 * it's important to keep an eye on that. */
879 /* Check for garbage at end of packet. */
887 part_type = tvb_get_ntohs (tvb, offset);
888 part_length = tvb_get_ntohs (tvb, offset+2);
890 /* Check if part_length is in the valid range. */
891 if ((part_length < 4) || (part_length > size))
900 vdispatch.host = tvb_get_ephemeral_string (tvb,
901 offset + 4, part_length - 4);
902 if (pkt_host == NULL)
903 pkt_host = vdispatch.host;
908 vdispatch.plugin = tvb_get_ephemeral_string (tvb,
909 offset + 4, part_length - 4);
912 case TYPE_PLUGIN_INSTANCE:
915 vdispatch.type = tvb_get_ephemeral_string (tvb,
916 offset + 4, part_length - 4);
918 case TYPE_TYPE_INSTANCE:
926 tap_data.values_num++;
927 stats_account_string (&tap_data.hosts,
929 stats_account_string (&tap_data.plugins,
931 stats_account_string (&tap_data.types,
945 offset += part_length;
950 /* Now we do the same steps again, but much more thoroughly. */
952 /* Check if there are at least four bytes left first.
953 * Four bytes are used to read the type and the length
954 * of the next part. If there's less, there's some garbage
955 * at the end of the packet. */
958 pi = proto_tree_add_text (collectd_tree, tvb,
960 "Garbage at end of packet: Length = %i <BAD>",
962 expert_add_info_format (pinfo, pi, PI_MALFORMED,
964 "Garbage at end of packet");
970 /* dissect a message entry */
971 part_type = tvb_get_ntohs (tvb, offset);
972 part_length = tvb_get_ntohs (tvb, offset + 2);
974 /* Check if the length of the part is in the valid range. Don't
975 * confuse this with the above: Here we check the information
976 * provided in the packet.. */
977 if ((part_length < 4) || (part_length > size))
979 pi = proto_tree_add_text (collectd_tree, tvb,
981 "collectd %s segment: Length = %i <BAD>",
982 val_to_str_const (part_type, part_names, "UNKNOWN"),
985 pt = proto_item_add_subtree (pi, ett_collectd_invalid_length);
986 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
988 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
989 offset + 2, 2, part_length);
992 expert_add_info_format (pinfo, pi,
993 PI_MALFORMED, PI_ERROR,
994 "Bad part length: Is %i, expected at least 4",
997 expert_add_info_format (pinfo, pi,
998 PI_MALFORMED, PI_ERROR,
999 "Bad part length: Larger than remaining packet size.");
1006 /* The header information looks okay, let's tend to the actual
1007 * payload in this part. */
1008 switch (part_type) {
1011 status = dissect_collectd_string (tvb, pinfo,
1012 hf_collectd_data_host,
1014 &vdispatch.host_off,
1015 &vdispatch.host_len,
1017 collectd_tree, /* item = */ NULL);
1022 if (pkt_host == NULL)
1023 pkt_host = vdispatch.host;
1024 ndispatch.host_off = vdispatch.host_off;
1025 ndispatch.host_len = vdispatch.host_len;
1026 ndispatch.host = vdispatch.host;
1034 status = dissect_collectd_string (tvb, pinfo,
1035 hf_collectd_data_plugin,
1037 &vdispatch.plugin_off,
1038 &vdispatch.plugin_len,
1040 collectd_tree, /* item = */ NULL);
1049 case TYPE_PLUGIN_INSTANCE:
1051 status = dissect_collectd_string (tvb, pinfo,
1052 hf_collectd_data_plugin_inst,
1054 &vdispatch.plugin_instance_off,
1055 &vdispatch.plugin_instance_len,
1056 &vdispatch.plugin_instance,
1057 collectd_tree, /* item = */ NULL);
1066 status = dissect_collectd_string (tvb, pinfo,
1067 hf_collectd_data_type,
1069 &vdispatch.type_off,
1070 &vdispatch.type_len,
1072 collectd_tree, /* item = */ NULL);
1079 case TYPE_TYPE_INSTANCE:
1081 status = dissect_collectd_string (tvb, pinfo,
1082 hf_collectd_data_type_inst,
1084 &vdispatch.type_instance_off,
1085 &vdispatch.type_instance_len,
1086 &vdispatch.type_instance,
1087 collectd_tree, /* item = */ NULL);
1096 ndispatch.time_str = NULL;
1097 vdispatch.time_str = NULL;
1100 status = dissect_collectd_integer (tvb, pinfo,
1101 hf_collectd_data_time,
1103 &vdispatch.time_off,
1105 collectd_tree, &pi);
1110 vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time, ABSOLUTE_TIME_LOCAL, TRUE);
1112 ndispatch.time = vdispatch.time;
1113 ndispatch.time_str = vdispatch.time_str;
1115 proto_item_set_text (pi, "collectd TIME segment: %"G_GINT64_MODIFIER"u (%s)",
1116 vdispatch.time, vdispatch.time_str ? vdispatch.time_str : "(null)");
1124 status = dissect_collectd_integer (tvb, pinfo,
1125 hf_collectd_data_interval,
1127 &vdispatch.interval_off,
1128 &vdispatch.interval,
1129 collectd_tree, /* item = */ NULL);
1138 status = dissect_collectd_part_values (tvb, pinfo,
1147 tap_data.values_num++;
1148 stats_account_string (&tap_data.hosts,
1150 stats_account_string (&tap_data.plugins,
1152 stats_account_string (&tap_data.types,
1161 status = dissect_collectd_string (tvb, pinfo,
1162 hf_collectd_data_message,
1164 &ndispatch.message_off,
1165 &ndispatch.message_len,
1167 collectd_tree, &pi);
1175 pt = proto_item_get_subtree (pi);
1177 /* tell what would be dispatched... */
1178 pi = proto_tree_add_text (pt, tvb, offset + 4,
1180 "Dispatch simulation");
1181 pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
1182 proto_tree_add_text (pt, tvb, ndispatch.host_off,
1184 "Host: %s", ndispatch.host ? ndispatch.host : "(null)");
1185 proto_tree_add_text (pt, tvb, ndispatch.time_off, 8,
1186 "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
1187 ndispatch.time, ndispatch.time_str ? ndispatch.time_str : "(null)");
1188 proto_tree_add_text (pt, tvb, ndispatch.severity_off, 8,
1189 "Severity: %s (%#"G_GINT64_MODIFIER"x)",
1190 val_to_str_const((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1191 ndispatch.severity);
1192 proto_tree_add_text (pt, tvb, ndispatch.message_off,
1193 ndispatch.message_len,
1194 "Message: %s", ndispatch.message);
1201 status = dissect_collectd_integer (tvb, pinfo,
1202 hf_collectd_data_severity,
1204 &ndispatch.severity_off,
1205 &ndispatch.severity,
1206 collectd_tree, &pi);
1211 proto_item_set_text (pi,
1212 "collectd SEVERITY segment: "
1213 "%s (%"G_GINT64_MODIFIER"u)",
1214 val_to_str_const ((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1215 ndispatch.severity);
1221 case TYPE_SIGN_SHA256:
1223 status = dissect_collectd_signature (tvb, pinfo,
1232 case TYPE_ENCR_AES256:
1234 status = dissect_collectd_encrypted (tvb, pinfo,
1235 offset, collectd_tree);
1245 pi = proto_tree_add_text (collectd_tree, tvb,
1246 offset, part_length,
1247 "collectd %s segment: %i bytes",
1248 val_to_str_const(part_type, part_names, "UNKNOWN"),
1251 pt = proto_item_add_subtree(pi, ett_collectd_unknown);
1252 pi = proto_tree_add_uint (pt, hf_collectd_type, tvb,
1253 offset, 2, part_type);
1254 proto_tree_add_uint (pt, hf_collectd_length, tvb,
1255 offset + 2, 2, part_length);
1256 proto_tree_add_item (pt, hf_collectd_data, tvb,
1257 offset + 4, part_length - 4, ENC_NA);
1259 expert_add_info_format (pinfo, pi,
1260 PI_UNDECODED, PI_NOTE,
1261 "Unknown part type %#x. Cannot decode data.",
1264 } /* switch (part_type) */
1266 offset += part_length;
1267 size -= part_length;
1268 } /* while ((size > 4) && (status == 0)) */
1270 if (pkt_errors && pkt_unknown)
1271 col_add_fstr (pinfo->cinfo, COL_INFO,
1272 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s",
1274 pkt_values, plurality (pkt_values, " ", "s"),
1275 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1276 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1278 pkt_errors, plurality (pkt_errors, "", "s"));
1279 else if (pkt_errors)
1280 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s",
1282 pkt_values, plurality (pkt_values, " ", "s"),
1283 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1284 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1285 pkt_errors, plurality (pkt_errors, "", "s"));
1286 else if (pkt_unknown)
1287 col_add_fstr (pinfo->cinfo, COL_INFO,
1288 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown",
1290 pkt_values, plurality (pkt_values, " ", "s"),
1291 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1292 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1295 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1297 pkt_values, plurality (pkt_values, " ", "s"),
1298 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1299 pkt_messages, plurality (pkt_messages, "", "s"));
1301 /* Dispatch tap data. */
1302 tap_queue_packet (tap_collectd, pinfo, &tap_data);
1303 } /* void dissect_collectd */
1305 void proto_register_collectd(void)
1307 module_t *collectd_module;
1309 /* Setup list of header fields */
1310 static hf_register_info hf[] = {
1311 { &hf_collectd_type,
1312 { "Type", "collectd.type", FT_UINT16, BASE_HEX,
1313 VALS(part_names), 0x0, NULL, HFILL }
1315 { &hf_collectd_length,
1316 { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1317 NULL, 0x0, NULL, HFILL }
1319 { &hf_collectd_data,
1320 { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1321 NULL, 0x0, NULL, HFILL }
1323 { &hf_collectd_data_host,
1324 { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1325 NULL, 0x0, NULL, HFILL }
1327 { &hf_collectd_data_interval,
1328 { "Interval", "collectd.data.interval", FT_UINT64, BASE_DEC,
1329 NULL, 0x0, NULL, HFILL }
1331 { &hf_collectd_data_time,
1332 { "Timestamp", "collectd.data.time", FT_UINT64, BASE_DEC,
1333 NULL, 0x0, NULL, HFILL }
1335 { &hf_collectd_data_plugin,
1336 { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1337 NULL, 0x0, NULL, HFILL }
1339 { &hf_collectd_data_plugin_inst,
1340 { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1341 NULL, 0x0, NULL, HFILL }
1343 { &hf_collectd_data_type,
1344 { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1345 NULL, 0x0, NULL, HFILL }
1347 { &hf_collectd_data_type_inst,
1348 { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1349 NULL, 0x0, NULL, HFILL }
1351 { &hf_collectd_data_valcnt,
1352 { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1353 NULL, 0x0, NULL, HFILL }
1355 { &hf_collectd_val_type,
1356 { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1357 VALS(valuetypenames), 0x0, NULL, HFILL }
1359 { &hf_collectd_val_counter,
1360 { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1361 NULL, 0x0, NULL, HFILL }
1363 { &hf_collectd_val_gauge,
1364 { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1365 NULL, 0x0, NULL, HFILL }
1367 { &hf_collectd_val_derive,
1368 { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1369 NULL, 0x0, NULL, HFILL }
1371 { &hf_collectd_val_absolute,
1372 { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1373 NULL, 0x0, NULL, HFILL }
1375 { &hf_collectd_val_unknown,
1376 { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1377 NULL, 0x0, NULL, HFILL }
1379 { &hf_collectd_data_severity,
1380 { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX,
1381 NULL, 0x0, NULL, HFILL }
1383 { &hf_collectd_data_message,
1384 { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1385 NULL, 0x0, NULL, HFILL }
1387 { &hf_collectd_data_sighash,
1388 { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1389 NULL, 0x0, NULL, HFILL }
1391 { &hf_collectd_data_initvec,
1392 { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1393 NULL, 0x0, NULL, HFILL }
1395 { &hf_collectd_data_username_len,
1396 { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1397 NULL, 0x0, NULL, HFILL }
1399 { &hf_collectd_data_username,
1400 { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1401 NULL, 0x0, NULL, HFILL }
1403 { &hf_collectd_data_encrypted,
1404 { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1405 NULL, 0x0, NULL, HFILL }
1409 /* Setup protocol subtree array */
1410 static gint *ett[] = {
1412 &ett_collectd_string,
1413 &ett_collectd_integer,
1414 &ett_collectd_part_value,
1415 &ett_collectd_value,
1416 &ett_collectd_valinfo,
1417 &ett_collectd_signature,
1418 &ett_collectd_encryption,
1419 &ett_collectd_dispatch,
1420 &ett_collectd_invalid_length,
1421 &ett_collectd_unknown,
1424 /* Register the protocol name and description */
1425 proto_collectd = proto_register_protocol("collectd network data",
1426 "collectd", "collectd");
1428 /* Required function calls to register the header fields and subtrees used */
1429 proto_register_field_array(proto_collectd, hf, array_length(hf));
1430 proto_register_subtree_array(ett, array_length(ett));
1432 tap_collectd = register_tap ("collectd");
1435 * Create an unsigned integer preference to allow the user to specify the
1436 * UDP port on which to capture DIS packets.
1438 collectd_module = prefs_register_protocol (proto_collectd,
1439 proto_reg_handoff_collectd);
1441 prefs_register_uint_preference (collectd_module, "udp.port",
1442 "collectd UDP port",
1443 "Set the UDP port for collectd messages",
1444 10, &collectd_udp_port);
1445 } /* void proto_register_collectd */
1447 void proto_reg_handoff_collectd (void)
1449 static gboolean first_run = TRUE;
1450 static gint registered_udp_port = -1;
1451 static dissector_handle_t collectd_handle;
1454 collectd_handle = create_dissector_handle (dissect_collectd,
1457 /* Change the dissector registration if the preferences have been
1459 if (registered_udp_port != -1)
1460 dissector_delete_uint ("udp.port", registered_udp_port,
1463 dissector_add_uint ("udp.port", collectd_udp_port, collectd_handle);
1464 registered_udp_port = collectd_udp_port;
1467 collectd_stats_tree_register ();
1470 } /* void proto_reg_handoff_collectd */