02514c21491f8a34b24b03467d740a68c377a895
[obnox/wireshark/wip.git] / epan / dissectors / packet-collectd.c
1 /* packet-collectd.c
2  * Routines for collectd (http://collectd.org/) network plugin dissection
3  *
4  * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org>
5  * Copyright 2009 Florian Forster <octo at verplant.org>
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
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.
17  *
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.
22  *
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.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <string.h>
33 #include <glib.h>
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36 #include <epan/expert.h>
37 #include <epan/stats_tree.h>
38
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
51
52 typedef struct value_data_s {
53         gchar *host;
54         gint host_off;
55         gint host_len;
56         guint64 time;
57         gchar *time_str;
58         gint time_off;
59         guint64 interval;
60         gint interval_off;
61         gchar *plugin;
62         gint plugin_off;
63         gint plugin_len;
64         gchar *plugin_instance;
65         gint plugin_instance_off;
66         gint plugin_instance_len;
67         gchar *type;
68         gint type_off;
69         gint type_len;
70         gchar *type_instance;
71         gint type_instance_off;
72         gint type_instance_len;
73 } value_data_t;
74
75 typedef struct notify_data_s {
76         gchar *host;
77         gint host_off;
78         gint host_len;
79         guint64 time;
80         gchar *time_str;
81         gint time_off;
82         guint64 severity;
83         gint severity_off;
84         gchar *message;
85         gint message_off;
86         gint message_len;
87 } notify_data_t;
88
89 struct string_counter_s;
90 typedef struct string_counter_s string_counter_t;
91 struct string_counter_s
92 {
93         gchar *string;
94         gint   count;
95         string_counter_t *next;
96 };
97
98 typedef struct tap_data_s {
99         gint values_num;
100
101         string_counter_t *hosts;
102         string_counter_t *plugins;
103         string_counter_t *types;
104 } tap_data_t;
105
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" },
119         { 0, NULL }
120 };
121
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" },
131         { 0, NULL }
132 };
133
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" },
141         { 0, NULL }
142 };
143
144 #define UDP_PORT_COLLECTD 25826
145 static guint collectd_udp_port = UDP_PORT_COLLECTD;
146
147 static gint proto_collectd              = -1;
148 static gint tap_collectd                = -1;
149
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;
174
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;
186
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;
192
193 /* Prototype for the handoff function */
194 void proto_reg_handoff_collectd (void);
195
196 static void
197 collectd_stats_tree_init (stats_tree *st)
198 {
199         st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
200         st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
201
202         st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
203                                                            st_collectd_values);
204         st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
205                                                               st_collectd_values);
206         st_collectd_values_types = stats_tree_create_pivot (st, "By type",
207                                                             st_collectd_values);
208 } /* void collectd_stats_tree_init */
209
210 static int
211 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
212                             epan_dissect_t *edt _U_, const void *user_data)
213 {
214         const tap_data_t *td;
215         string_counter_t *sc;
216
217         td = user_data;
218         if (td == NULL)
219                 return (-1);
220
221         tick_stat_node (st, "Packets", 0, FALSE);
222         increase_stat_node (st, "Values", 0, TRUE, td->values_num);
223
224         for (sc = td->hosts; sc != NULL; sc = sc->next)
225         {
226                 gint i;
227                 for (i = 0; i < sc->count; i++)
228                         stats_tree_tick_pivot (st, st_collectd_values_hosts,
229                                                sc->string);
230         }
231
232         for (sc = td->plugins; sc != NULL; sc = sc->next)
233         {
234                 gint i;
235                 for (i = 0; i < sc->count; i++)
236                         stats_tree_tick_pivot (st, st_collectd_values_plugins,
237                                                sc->string);
238         }
239
240         for (sc = td->types; sc != NULL; sc = sc->next)
241         {
242                 gint i;
243                 for (i = 0; i < sc->count; i++)
244                         stats_tree_tick_pivot (st, st_collectd_values_types,
245                                                sc->string);
246         }
247
248         return (1);
249 } /* int collectd_stats_tree_packet */
250
251 static void
252 collectd_stats_tree_register (void)
253 {
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 */
258
259 static int
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)
264 {
265         proto_tree *pt;
266         proto_item *pi;
267         gint type;
268         gint length;
269         gint size;
270
271         size = tvb_reported_length_remaining (tvb, offset);
272         if (size < 4)
273         {
274                 /* This should never happen, because `dissect_collectd' checks
275                  * for this condition already. */
276                 return (-1);
277         }
278
279         type   = tvb_get_ntohs(tvb, offset);
280         length = tvb_get_ntohs(tvb, offset + 2);
281
282         if (length > size)
283         {
284                 pi = proto_tree_add_text (tree_root, tvb, offset, length,
285                                           "collectd %s segment: Length = %i <BAD>",
286                                           val_to_str (type, part_names, "UNKNOWN"),
287                                           length);
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.");
291                 return (-1);
292         }
293
294         *ret_offset = offset + 4;
295         *ret_length = length - 4;
296
297         *ret_string = tvb_get_ephemeral_string (tvb, *ret_offset, *ret_length);
298
299         pi = proto_tree_add_text (tree_root, tvb, offset, length,
300                                   "collectd %s segment: \"%s\"",
301                                   val_to_str (type, part_names, "UNKNOWN"),
302                                   *ret_string);
303
304         if (ret_item != NULL)
305                 *ret_item = pi;
306
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, FALSE);
311
312         return (0);
313 } /* int dissect_collectd_string */
314
315 static int
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)
319 {
320         proto_tree *pt;
321         proto_item *pi;
322         gint type;
323         gint length;
324         gint size;
325
326         size = tvb_reported_length_remaining (tvb, offset);
327         if (size < 4)
328         {
329                 /* This should never happen, because `dissect_collectd' checks
330                  * for this condition already. */
331                 return (-1);
332         }
333
334         type   = tvb_get_ntohs(tvb, offset);
335         length = tvb_get_ntohs(tvb, offset + 2);
336
337         if (size < 12)
338         {
339                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
340                                           "collectd %s segment: <BAD>",
341                                           val_to_str (type, part_names, "UNKNOWN"));
342
343                 pt = proto_item_add_subtree (pi, ett_collectd_integer);
344                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
345                                      type);
346                 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
347                                      length);
348                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
349                                           "Garbage at end of packet: Length = %i <BAD>",
350                                           size - 4);
351                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
352                                         "Garbage at end of packet");
353
354                 return (-1);
355         }
356
357         if (length != 12)
358         {
359                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
360                                           "collectd %s segment: <BAD>",
361                                           val_to_str (type, part_names, "UNKNOWN"));
362
363                 pt = proto_item_add_subtree (pi, ett_collectd_integer);
364                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
365                                      type);
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.");
370
371                 return (-1);
372         }
373
374         *ret_offset = offset + 4;
375         *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
376
377         pi = proto_tree_add_text (tree_root, tvb, offset, length,
378                                   "collectd %s segment: %"G_GINT64_MODIFIER"u",
379                                   val_to_str (type, part_names, "UNKNOWN"),
380                                   *ret_value);
381         if (ret_item != NULL)
382                 *ret_item = pi;
383
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,
387                              length);
388         proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, FALSE);
389
390         return (0);
391 } /* int dissect_collectd_integer */
392
393 static void
394 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
395                         proto_tree *collectd_tree)
396 {
397         proto_item *pi;
398         proto_tree *values_tree, *value_tree;
399         gint i;
400
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"));
404
405         values_tree = proto_item_add_subtree (pi, ett_collectd_value);
406
407         for (i = 0; i < val_cnt; i++)
408         {
409                 gint value_offset;
410
411                 gint value_type_offset;
412                 guint8 value_type;
413
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 */
418
419                 value_type_offset = msg_off + 6 + i;
420                 value_type = tvb_get_guint8 (tvb, value_type_offset);
421
422                 switch (value_type) {
423                 case TYPE_VALUE_COUNTER:
424                 {
425                         guint64 val64;
426
427                         val64 = tvb_get_ntoh64 (tvb, value_offset);
428                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
429                                                   val_cnt * 9,
430                                                   "Counter: %"G_GINT64_MODIFIER"u", val64);
431
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);
439                         break;
440                 }
441
442                 case TYPE_VALUE_GAUGE:
443                 {
444                         gdouble val;
445
446                         val = tvb_get_letohieee_double (tvb, value_offset);
447                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
448                                                   val_cnt * 9,
449                                                   "Gauge: %g", val);
450
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);
459                         break;
460                 }
461
462                 case TYPE_VALUE_DERIVE:
463                 {
464                         gint64 val64;
465
466                         val64 = tvb_get_ntoh64 (tvb, value_offset);
467                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
468                                                   val_cnt * 9,
469                                                   "Derive: %"G_GINT64_MODIFIER"i", val64);
470
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);
478                         break;
479                 }
480
481                 case TYPE_VALUE_ABSOLUTE:
482                 {
483                         guint64 val64;
484
485                         val64 = tvb_get_ntoh64 (tvb, value_offset);
486                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
487                                                   val_cnt * 9,
488                                                   "Absolute: %"G_GINT64_MODIFIER"u", val64);
489
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);
497                         break;
498                 }
499
500                 default:
501                 {
502                         guint64 val64;
503
504                         val64 = tvb_get_ntoh64 (tvb, value_offset);
505                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
506                                                   val_cnt * 9,
507                                                   "Unknown: %"G_GINT64_MODIFIER"x",
508                                                   val64);
509
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);
516                         break;
517                 }
518                 } /* switch (value_type) */
519         } /* for (i = 0; i < val_cnt; i++) */
520 } /* void dissect_collectd_values */
521
522 static int
523 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
524                               value_data_t *vdispatch, proto_tree *tree_root)
525 {
526         proto_tree *pt;
527         proto_item *pi;
528         gint type;
529         gint length;
530         gint size;
531         gint values_count;
532         gint corrected_values_count;
533
534         size = tvb_reported_length_remaining (tvb, offset);
535         if (size < 4)
536         {
537                 /* This should never happen, because `dissect_collectd' checks
538                  * for this condition already. */
539                 return (-1);
540         }
541
542         type   = tvb_get_ntohs (tvb, offset);
543         length = tvb_get_ntohs (tvb, offset + 2);
544
545         if (size < 15)
546         {
547                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
548                                           "collectd %s segment: <BAD>",
549                                           val_to_str (type, part_names, "UNKNOWN"));
550
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,
554                                      length);
555                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
556                                           "Garbage at end of packet: Length = %i <BAD>",
557                                           size - 4);
558                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
559                                         "Garbage at end of packet");
560
561                 return (-1);
562         }
563
564         if ((length < 15) || ((length % 9) != 6))
565         {
566                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
567                                           "collectd %s segment: <BAD>",
568                                           val_to_str (type, part_names, "UNKNOWN"));
569
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.");
576
577                 return (-1);
578         }
579
580         values_count = tvb_get_ntohs (tvb, offset + 4);
581         corrected_values_count = (length - 6) / 9;
582
583         if (values_count != corrected_values_count)
584         {
585                 pi = proto_tree_add_text (tree_root, tvb, offset, length,
586                                           "collectd %s segment: %d (%d) value%s <BAD>",
587                                           val_to_str (type, part_names, "UNKNOWN"),
588                                           values_count, corrected_values_count,
589                                           plurality(values_count, "", "s"));
590         }
591         else
592         {
593                 pi = proto_tree_add_text (tree_root, tvb, offset, length,
594                                           "collectd %s segment: %d value%s",
595                                           val_to_str (type, part_names, "UNKNOWN"),
596                                           values_count,
597                                           plurality(values_count, "", "s"));
598         }
599
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);
603
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.");
610
611         values_count = corrected_values_count;
612
613         dissect_collectd_values (tvb, offset, values_count, pt);
614
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);
639         return (0);
640 } /* void dissect_collectd_part_values */
641
642 static int
643 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
644                             gint offset, proto_tree *tree_root)
645 {
646         proto_item *pi;
647         proto_tree *pt;
648         gint type;
649         gint length;
650         gint size;
651
652         size = tvb_reported_length_remaining (tvb, offset);
653         if (size < 4)
654         {
655                 /* This should never happen, because `dissect_collectd' checks
656                  * for this condition already. */
657                 return (-1);
658         }
659
660         type   = tvb_get_ntohs (tvb, offset);
661         length = tvb_get_ntohs (tvb, offset + 2);
662
663         if (size < 36) /* remaining packet size too small for signature */
664         {
665                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
666                                           "collectd %s segment: <BAD>",
667                                           val_to_str (type, part_names, "UNKNOWN"));
668
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,
672                                      length);
673                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
674                                           "Garbage at end of packet: Length = %i <BAD>",
675                                           size - 4);
676                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
677                                         "Garbage at end of packet");
678
679                 return (-1);
680         }
681
682         if (length < 36)
683         {
684                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
685                                           "collectd %s segment: <BAD>",
686                                           val_to_str (type, part_names, "UNKNOWN"));
687
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.");
694
695                 return (-1);
696         }
697
698         pi = proto_tree_add_text (tree_root, tvb, offset, length,
699                                   "collectd %s segment: HMAC-SHA-256",
700                                   val_to_str (type, part_names, "UNKNOWN"));
701
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,
705                              length);
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);
708
709         return (0);
710 } /* int dissect_collectd_signature */
711
712 static int
713 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
714                             gint offset, proto_tree *tree_root)
715 {
716         proto_item *pi;
717         proto_tree *pt;
718         gint type;
719         gint length;
720         gint size;
721         gint username_length;
722
723         size = tvb_reported_length_remaining (tvb, offset);
724         if (size < 4)
725         {
726                 /* This should never happen, because `dissect_collectd' checks
727                  * for this condition already. */
728                 return (-1);
729         }
730
731         type   = tvb_get_ntohs (tvb, offset);
732         length = tvb_get_ntohs (tvb, offset + 2);
733
734         if (size < 42) /* remaining packet size too small for signature */
735         {
736                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
737                                           "collectd %s segment: <BAD>",
738                                           val_to_str (type, part_names, "UNKNOWN"));
739
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,
743                                      length);
744                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
745                                           "Garbage at end of packet: Length = %i <BAD>",
746                                           size - 4);
747                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
748                                         "Garbage at end of packet");
749
750                 return (-1);
751         }
752
753         if (length < 42)
754         {
755                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
756                                           "collectd %s segment: <BAD>",
757                                           val_to_str (type, part_names, "UNKNOWN"));
758
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.");
765
766                 return (-1);
767         }
768
769         username_length = tvb_get_ntohs (tvb, offset + 4);
770         if (username_length > (length - 42))
771         {
772                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
773                                           "collectd %s segment: <BAD>",
774                                           val_to_str (type, part_names, "UNKNOWN"));
775
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.");
784
785                 return (-1);
786         }
787
788         pi = proto_tree_add_text (tree_root, tvb, offset, length,
789                                   "collectd %s segment: AES-256",
790                                   val_to_str (type, part_names, "UNKNOWN"));
791
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);
802
803         return (0);
804 } /* int dissect_collectd_encrypted */
805
806 static int
807 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
808 {
809         string_counter_t *entry;
810
811         if (ret_list == NULL)
812                 return (-1);
813
814         if (new_value == NULL)
815                 new_value = "(null)";
816
817         for (entry = *ret_list; entry != NULL; entry = entry->next)
818                 if (strcmp (new_value, entry->string) == 0)
819                 {
820                         entry->count++;
821                         return (0);
822                 }
823
824         entry = ep_alloc0 (sizeof (*entry));
825         entry->string = ep_strdup (new_value);
826         entry->count = 1;
827         entry->next = *ret_list;
828
829         *ret_list = entry;
830
831         return (0);
832 }
833
834 static void
835 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
836 {
837         static tap_data_t tap_data;
838
839         gint offset;
840         gint size;
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;
845         int status;
846         proto_item *pi;
847         proto_tree *collectd_tree;
848         proto_tree *pt;
849
850         memset(&vdispatch, '\0', sizeof(vdispatch));
851         memset(&ndispatch, '\0', sizeof(ndispatch));
852
853         col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
854         col_clear(pinfo->cinfo, COL_INFO);
855
856         offset = 0;
857         size = tvb_reported_length(tvb);
858
859         /* create the collectd protocol tree */
860         pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, FALSE);
861         collectd_tree = proto_item_add_subtree(pi, ett_collectd);
862
863         memset (&tap_data, 0, sizeof (tap_data));
864
865         status = 0;
866         while ((size > 0) && (status == 0))
867         {
868
869                 gint part_type;
870                 gint part_length;
871
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. */
877                 if (!tree)
878                 {
879                         /* Check for garbage at end of packet. */
880                         if (size < 4)
881                         {
882                                 pkt_errors++;
883                                 status = -1;
884                                 break;
885                         }
886
887                         part_type = tvb_get_ntohs (tvb, offset);
888                         part_length  = tvb_get_ntohs (tvb, offset+2);
889
890                         /* Check if part_length is in the valid range. */
891                         if ((part_length < 4) || (part_length > size))
892                         {
893                                 pkt_errors++;
894                                 status = -1;
895                                 break;
896                         }
897
898                         switch (part_type) {
899                         case TYPE_HOST:
900                                 vdispatch.host = tvb_get_ephemeral_string (tvb,
901                                                 offset + 4, part_length - 4);
902                                 if (pkt_host == NULL)
903                                         pkt_host = vdispatch.host;
904                                 break;
905                         case TYPE_TIME:
906                                 break;
907                         case TYPE_PLUGIN:
908                                 vdispatch.plugin = tvb_get_ephemeral_string (tvb,
909                                                 offset + 4, part_length - 4);
910                                 pkt_plugins++;
911                                 break;
912                         case TYPE_PLUGIN_INSTANCE:
913                                 break;
914                         case TYPE_TYPE:
915                                 vdispatch.type = tvb_get_ephemeral_string (tvb,
916                                                 offset + 4, part_length - 4);
917                                 break;
918                         case TYPE_TYPE_INSTANCE:
919                                 break;
920                         case TYPE_INTERVAL:
921                                 break;
922                         case TYPE_VALUES:
923                         {
924                                 pkt_values++;
925
926                                 tap_data.values_num++;
927                                 stats_account_string (&tap_data.hosts,
928                                                       vdispatch.host);
929                                 stats_account_string (&tap_data.plugins,
930                                                       vdispatch.plugin);
931                                 stats_account_string (&tap_data.types,
932                                                       vdispatch.type);
933
934                                 break;
935                         }
936                         case TYPE_MESSAGE:
937                                 pkt_messages++;
938                                 break;
939                         case TYPE_SEVERITY:
940                                 break;
941                         default:
942                                 pkt_unknown++;
943                         }
944
945                         offset  += part_length;
946                         size    -= part_length;
947                         continue;
948                 } /* if (!tree) */
949
950                 /* Now we do the same steps again, but much more thoroughly. */
951
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. */
956                 if (size < 4)
957                 {
958                         pi = proto_tree_add_text (collectd_tree, tvb,
959                                                   offset, -1,
960                                                   "Garbage at end of packet: Length = %i <BAD>",
961                                                   size);
962                         expert_add_info_format (pinfo, pi, PI_MALFORMED,
963                                                 PI_ERROR,
964                                                 "Garbage at end of packet");
965                         pkt_errors++;
966                         status = -1;
967                         break;
968                 }
969
970                 /* dissect a message entry */
971                 part_type = tvb_get_ntohs (tvb, offset);
972                 part_length  = tvb_get_ntohs (tvb, offset + 2);
973
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))
978                 {
979                         pi = proto_tree_add_text (collectd_tree, tvb,
980                                                   offset, part_length,
981                                                   "collectd %s segment: Length = %i <BAD>",
982                                                   val_to_str (part_type, part_names, "UNKNOWN"),
983                                                   part_length);
984
985                         pt = proto_item_add_subtree (pi, ett_collectd_invalid_length);
986                         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
987                                              2, part_type);
988                         pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
989                                              offset + 2, 2, part_length);
990
991                         if (part_length < 4)
992                                 expert_add_info_format (pinfo, pi,
993                                                         PI_MALFORMED, PI_ERROR,
994                                                         "Bad part length: Is %i, expected at least 4",
995                                                         part_length);
996                         else
997                                 expert_add_info_format (pinfo, pi,
998                                                         PI_MALFORMED, PI_ERROR,
999                                                         "Bad part length: Larger than remaining packet size.");
1000
1001                         pkt_errors++;
1002                         status = -1;
1003                         break;
1004                 }
1005
1006                 /* The header information looks okay, let's tend to the actual
1007                  * payload in this part. */
1008                 switch (part_type) {
1009                 case TYPE_HOST:
1010                 {
1011                         status = dissect_collectd_string (tvb, pinfo,
1012                                         hf_collectd_data_host,
1013                                         offset,
1014                                         &vdispatch.host_off,
1015                                         &vdispatch.host_len,
1016                                         &vdispatch.host,
1017                                         collectd_tree, /* item = */ NULL);
1018                         if (status != 0)
1019                                 pkt_errors++;
1020                         else
1021                         {
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;
1027                         }
1028
1029                         break;
1030                 }
1031
1032                 case TYPE_PLUGIN:
1033                 {
1034                         status = dissect_collectd_string (tvb, pinfo,
1035                                         hf_collectd_data_plugin,
1036                                         offset,
1037                                         &vdispatch.plugin_off,
1038                                         &vdispatch.plugin_len,
1039                                         &vdispatch.plugin,
1040                                         collectd_tree, /* item = */ NULL);
1041                         if (status != 0)
1042                                 pkt_errors++;
1043                         else
1044                                 pkt_plugins++;
1045
1046                         break;
1047                 }
1048
1049                 case TYPE_PLUGIN_INSTANCE:
1050                 {
1051                         status = dissect_collectd_string (tvb, pinfo,
1052                                         hf_collectd_data_plugin_inst,
1053                                         offset,
1054                                         &vdispatch.plugin_instance_off,
1055                                         &vdispatch.plugin_instance_len,
1056                                         &vdispatch.plugin_instance,
1057                                         collectd_tree, /* item = */ NULL);
1058                         if (status != 0)
1059                                 pkt_errors++;
1060
1061                         break;
1062                 }
1063
1064                 case TYPE_TYPE:
1065                 {
1066                         status = dissect_collectd_string (tvb, pinfo,
1067                                         hf_collectd_data_type,
1068                                         offset,
1069                                         &vdispatch.type_off,
1070                                         &vdispatch.type_len,
1071                                         &vdispatch.type,
1072                                         collectd_tree, /* item = */ NULL);
1073                         if (status != 0)
1074                                 pkt_errors++;
1075
1076                         break;
1077                 }
1078
1079                 case TYPE_TYPE_INSTANCE:
1080                 {
1081                         status = dissect_collectd_string (tvb, pinfo,
1082                                         hf_collectd_data_type_inst,
1083                                         offset,
1084                                         &vdispatch.type_instance_off,
1085                                         &vdispatch.type_instance_len,
1086                                         &vdispatch.type_instance,
1087                                         collectd_tree, /* item = */ NULL);
1088                         if (status != 0)
1089                                 pkt_errors++;
1090
1091                         break;
1092                 }
1093
1094                 case TYPE_TIME:
1095                 {
1096                         ndispatch.time_str = NULL;
1097                         vdispatch.time_str = NULL;
1098
1099                         pi = NULL;
1100                         status = dissect_collectd_integer (tvb, pinfo,
1101                                         hf_collectd_data_time,
1102                                         offset,
1103                                         &vdispatch.time_off,
1104                                         &vdispatch.time,
1105                                         collectd_tree, &pi);
1106                         if (status != 0)
1107                                 pkt_errors++;
1108                         else
1109                         {
1110                                 vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time, ABSOLUTE_TIME_LOCAL, TRUE);
1111
1112                                 ndispatch.time = vdispatch.time;
1113                                 ndispatch.time_str = vdispatch.time_str;
1114
1115                                 proto_item_set_text (pi, "collectd TIME segment: %"G_GINT64_MODIFIER"u (%s)",
1116                                                      vdispatch.time, vdispatch.time_str ? vdispatch.time_str : "(null)");
1117                         }
1118
1119                         break;
1120                 }
1121
1122                 case TYPE_INTERVAL:
1123                 {
1124                         status = dissect_collectd_integer (tvb, pinfo,
1125                                         hf_collectd_data_interval,
1126                                         offset,
1127                                         &vdispatch.interval_off,
1128                                         &vdispatch.interval,
1129                                         collectd_tree, /* item = */ NULL);
1130                         if (status != 0)
1131                                 pkt_errors++;
1132
1133                         break;
1134                 }
1135
1136                 case TYPE_VALUES:
1137                 {
1138                         status = dissect_collectd_part_values (tvb, pinfo,
1139                                         offset,
1140                                         &vdispatch,
1141                                         collectd_tree);
1142                         if (status != 0)
1143                                 pkt_errors++;
1144                         else
1145                                 pkt_values++;
1146
1147                         tap_data.values_num++;
1148                         stats_account_string (&tap_data.hosts,
1149                                               vdispatch.host);
1150                         stats_account_string (&tap_data.plugins,
1151                                               vdispatch.plugin);
1152                         stats_account_string (&tap_data.types,
1153                                               vdispatch.type);
1154
1155                         break;
1156                 }
1157
1158                 case TYPE_MESSAGE:
1159                 {
1160                         pi = NULL;
1161                         status = dissect_collectd_string (tvb, pinfo,
1162                                         hf_collectd_data_message,
1163                                         offset,
1164                                         &ndispatch.message_off,
1165                                         &ndispatch.message_len,
1166                                         &ndispatch.message,
1167                                         collectd_tree, &pi);
1168                         if (status != 0)
1169                         {
1170                                 pkt_errors++;
1171                                 break;
1172                         }
1173                         pkt_messages++;
1174
1175                         pt = proto_item_get_subtree (pi);
1176
1177                         /* tell what would be dispatched...  */
1178                         pi = proto_tree_add_text (pt, tvb, offset + 4,
1179                                                   part_length - 4,
1180                                                   "Dispatch simulation");
1181                         pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
1182                         proto_tree_add_text (pt, tvb, ndispatch.host_off,
1183                                              ndispatch.host_len,
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((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);
1195                         break;
1196                 }
1197
1198                 case TYPE_SEVERITY:
1199                 {
1200                         pi = NULL;
1201                         status = dissect_collectd_integer (tvb, pinfo,
1202                                         hf_collectd_data_severity,
1203                                         offset,
1204                                         &ndispatch.severity_off,
1205                                         &ndispatch.severity,
1206                                         collectd_tree, &pi);
1207                         if (status != 0)
1208                                 pkt_errors++;
1209                         else
1210                         {
1211                                 proto_item_set_text (pi,
1212                                                 "collectd SEVERITY segment: "
1213                                                 "%s (%"G_GINT64_MODIFIER"u)",
1214                                                 val_to_str ((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1215                                                 ndispatch.severity);
1216                         }
1217
1218                         break;
1219                 }
1220
1221                 case TYPE_SIGN_SHA256:
1222                 {
1223                         status = dissect_collectd_signature (tvb, pinfo,
1224                                                              offset,
1225                                                              collectd_tree);
1226                         if (status != 0)
1227                                 pkt_errors++;
1228
1229                         break;
1230                 }
1231
1232                 case TYPE_ENCR_AES256:
1233                 {
1234                         status = dissect_collectd_encrypted (tvb, pinfo,
1235                                         offset, collectd_tree);
1236                         if (status != 0)
1237                                 pkt_errors++;
1238
1239                         break;
1240                 }
1241
1242                 default:
1243                 {
1244                         pkt_unknown++;
1245                         pi = proto_tree_add_text (collectd_tree, tvb,
1246                                                   offset, part_length,
1247                                                   "collectd %s segment: %i bytes",
1248                                                   val_to_str(part_type, part_names, "UNKNOWN"),
1249                                                   part_length);
1250
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);
1258
1259                         expert_add_info_format (pinfo, pi,
1260                                                 PI_UNDECODED, PI_NOTE,
1261                                                 "Unknown part type %#x. Cannot decode data.",
1262                                                 part_type);
1263                 }
1264                 } /* switch (part_type) */
1265
1266                 offset  += part_length;
1267                 size    -= part_length;
1268         } /* while ((size > 4) && (status == 0)) */
1269
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",
1273                               pkt_host,
1274                               pkt_values, plurality (pkt_values, " ", "s"),
1275                               pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1276                               pkt_messages, plurality (pkt_messages, ", ", "s,"),
1277                               pkt_unknown,
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",
1281                               pkt_host,
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",
1289                               pkt_host,
1290                               pkt_values, plurality (pkt_values, " ", "s"),
1291                               pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1292                               pkt_messages, plurality (pkt_messages, ", ", "s,"),
1293                               pkt_unknown);
1294         else
1295                 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1296                               pkt_host,
1297                               pkt_values, plurality (pkt_values, " ", "s"),
1298                               pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1299                               pkt_messages, plurality (pkt_messages, "", "s"));
1300
1301         /* Dispatch tap data. */
1302         tap_queue_packet (tap_collectd, pinfo, &tap_data);
1303 } /* void dissect_collectd */
1304
1305 void proto_register_collectd(void)
1306 {
1307         module_t *collectd_module;
1308
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 }
1314                 },
1315                 { &hf_collectd_length,
1316                         { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1317                                 NULL, 0x0, NULL, HFILL }
1318                 },
1319                 { &hf_collectd_data,
1320                         { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1321                                 NULL, 0x0, NULL, HFILL }
1322                 },
1323                 { &hf_collectd_data_host,
1324                         { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1325                                 NULL, 0x0, NULL, HFILL }
1326                 },
1327                 { &hf_collectd_data_interval,
1328                         { "Interval", "collectd.data.interval", FT_UINT64, BASE_DEC,
1329                                 NULL, 0x0, NULL, HFILL }
1330                 },
1331                 { &hf_collectd_data_time,
1332                         { "Timestamp", "collectd.data.time", FT_UINT64, BASE_DEC,
1333                                 NULL, 0x0, NULL, HFILL }
1334                 },
1335                 { &hf_collectd_data_plugin,
1336                         { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1337                                 NULL, 0x0, NULL, HFILL }
1338                 },
1339                 { &hf_collectd_data_plugin_inst,
1340                         { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1341                                 NULL, 0x0, NULL, HFILL }
1342                 },
1343                 { &hf_collectd_data_type,
1344                         { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1345                                 NULL, 0x0, NULL, HFILL }
1346                 },
1347                 { &hf_collectd_data_type_inst,
1348                         { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1349                                 NULL, 0x0, NULL, HFILL }
1350                 },
1351                 { &hf_collectd_data_valcnt,
1352                         { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1353                                 NULL, 0x0, NULL, HFILL }
1354                 },
1355                 { &hf_collectd_val_type,
1356                         { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1357                                 VALS(valuetypenames), 0x0, NULL, HFILL }
1358                 },
1359                 { &hf_collectd_val_counter,
1360                         { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1361                                 NULL, 0x0, NULL, HFILL }
1362                 },
1363                 { &hf_collectd_val_gauge,
1364                         { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1365                                 NULL, 0x0, NULL, HFILL }
1366                 },
1367                 { &hf_collectd_val_derive,
1368                         { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1369                                 NULL, 0x0, NULL, HFILL }
1370                 },
1371                 { &hf_collectd_val_absolute,
1372                         { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1373                                 NULL, 0x0, NULL, HFILL }
1374                 },
1375                 { &hf_collectd_val_unknown,
1376                         { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1377                                 NULL, 0x0, NULL, HFILL }
1378                 },
1379                 { &hf_collectd_data_severity,
1380                         { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX,
1381                                 NULL, 0x0, NULL, HFILL }
1382                 },
1383                 { &hf_collectd_data_message,
1384                         { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1385                                 NULL, 0x0, NULL, HFILL }
1386                 },
1387                 { &hf_collectd_data_sighash,
1388                         { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1389                                 NULL, 0x0, NULL, HFILL }
1390                 },
1391                 { &hf_collectd_data_initvec,
1392                         { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1393                                 NULL, 0x0, NULL, HFILL }
1394                 },
1395                 { &hf_collectd_data_username_len,
1396                         { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1397                                 NULL, 0x0, NULL, HFILL }
1398                 },
1399                 { &hf_collectd_data_username,
1400                         { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1401                                 NULL, 0x0, NULL, HFILL }
1402                 },
1403                 { &hf_collectd_data_encrypted,
1404                         { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1405                                 NULL, 0x0, NULL, HFILL }
1406                 },
1407         };
1408
1409         /* Setup protocol subtree array */
1410         static gint *ett[] = {
1411                 &ett_collectd,
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,
1422         };
1423
1424         /* Register the protocol name and description */
1425         proto_collectd = proto_register_protocol("collectd network data",
1426                                                  "collectd", "collectd");
1427
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));
1431
1432         tap_collectd = register_tap ("collectd");
1433
1434         /*
1435          * Create an unsigned integer preference to allow the user to specify the
1436          * UDP port on which to capture DIS packets.
1437          */
1438         collectd_module = prefs_register_protocol (proto_collectd,
1439                                                    proto_reg_handoff_collectd);
1440
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 */
1446
1447 void proto_reg_handoff_collectd (void)
1448 {
1449         static gboolean first_run = TRUE;
1450         static gint registered_udp_port = -1;
1451         static dissector_handle_t collectd_handle;
1452
1453         if (first_run)
1454                 collectd_handle = create_dissector_handle (dissect_collectd,
1455                                                            proto_collectd);
1456
1457         /* Change the dissector registration if the preferences have been
1458          * changed. */
1459         if (registered_udp_port != -1)
1460                 dissector_delete_uint ("udp.port", registered_udp_port,
1461                                   collectd_handle);
1462
1463         dissector_add_uint ("udp.port", collectd_udp_port, collectd_handle);
1464         registered_udp_port = collectd_udp_port;
1465
1466         if (first_run)
1467                 collectd_stats_tree_register ();
1468
1469         first_run = FALSE;
1470 } /* void proto_reg_handoff_collectd */