Use correct type for udp port preference.
[gd/wireshark/.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 <stdio.h> */
33 #include <string.h>
34 #include <glib.h>
35 #include <epan/packet.h>
36 #include <epan/prefs.h>
37 #include <epan/expert.h>
38 #include <epan/stats_tree.h>
39
40 #define TYPE_HOST            0x0000
41 #define TYPE_TIME            0x0001
42 #define TYPE_PLUGIN          0x0002
43 #define TYPE_PLUGIN_INSTANCE 0x0003
44 #define TYPE_TYPE            0x0004
45 #define TYPE_TYPE_INSTANCE   0x0005
46 #define TYPE_VALUES          0x0006
47 #define TYPE_INTERVAL        0x0007
48 #define TYPE_MESSAGE         0x0100
49 #define TYPE_SEVERITY        0x0101
50 #define TYPE_SIGN_SHA256     0x0200
51 #define TYPE_ENCR_AES256     0x0210
52
53 typedef struct value_data_s {
54         gchar *host;
55         gint host_off;
56         gint host_len;
57         guint64 time;
58         gchar *time_str;
59         gint time_off;
60         guint64 interval;
61         gint interval_off;
62         gchar *plugin;
63         gint plugin_off;
64         gint plugin_len;
65         gchar *plugin_instance;
66         gint plugin_instance_off;
67         gint plugin_instance_len;
68         gchar *type;
69         gint type_off;
70         gint type_len;
71         gchar *type_instance;
72         gint type_instance_off;
73         gint type_instance_len;
74 } value_data_t;
75
76 typedef struct notify_data_s {
77         gchar *host;
78         gint host_off;
79         gint host_len;
80         guint64 time;
81         gchar *time_str;
82         gint time_off;
83         guint64 severity;
84         gint severity_off;
85         gchar *message;
86         gint message_off;
87         gint message_len;
88 } notify_data_t;
89
90 struct string_counter_s;
91 typedef struct string_counter_s string_counter_t;
92 struct string_counter_s
93 {
94         gchar *string;
95         gint   count;
96         string_counter_t *next;
97 };
98
99 typedef struct tap_data_s {
100         gint values_num;
101
102         string_counter_t *hosts;
103         string_counter_t *plugins;
104         string_counter_t *types;
105 } tap_data_t;
106
107 static const value_string part_names[] = {
108         { TYPE_VALUES,          "VALUES" },
109         { TYPE_TIME,            "TIME" },
110         { TYPE_INTERVAL,        "INTERVAL" },
111         { TYPE_HOST,            "HOST" },
112         { TYPE_PLUGIN,          "PLUGIN" },
113         { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" },
114         { TYPE_TYPE,            "TYPE" },
115         { TYPE_TYPE_INSTANCE,   "TYPE_INSTANCE" },
116         { TYPE_MESSAGE,         "MESSAGE" },
117         { TYPE_SEVERITY,        "SEVERITY" },
118         { TYPE_SIGN_SHA256,     "SIGNATURE" },
119         { TYPE_ENCR_AES256,     "ENCRYPTED_DATA" },
120         { 0, NULL }
121 };
122
123 #define TYPE_VALUE_COUNTER  0x00
124 #define TYPE_VALUE_GAUGE    0x01
125 #define TYPE_VALUE_DERIVE   0x02
126 #define TYPE_VALUE_ABSOLUTE 0x03
127 static const value_string valuetypenames[] = {
128         { TYPE_VALUE_COUNTER,   "COUNTER" },
129         { TYPE_VALUE_GAUGE,     "GAUGE" },
130         { TYPE_VALUE_DERIVE,    "DERIVE" },
131         { TYPE_VALUE_ABSOLUTE,  "ABSOLUTE" },
132         { 0, NULL }
133 };
134
135 #define SEVERITY_FAILURE  0x01
136 #define SEVERITY_WARNING  0x02
137 #define SEVERITY_OKAY     0x04
138 static const value_string severity_names[] = {
139         { SEVERITY_FAILURE,  "FAILURE" },
140         { SEVERITY_WARNING,  "WARNING" },
141         { SEVERITY_OKAY,     "OKAY" },
142         { 0, NULL }
143 };
144
145 #define UDP_PORT_COLLECTD 25826
146 static guint collectd_udp_port = UDP_PORT_COLLECTD;
147
148 static gint proto_collectd              = -1;
149 static gint tap_collectd                = -1;
150
151 static gint hf_collectd_type            = -1;
152 static gint hf_collectd_length          = -1;
153 static gint hf_collectd_data            = -1;
154 static gint hf_collectd_data_host       = -1;
155 static gint hf_collectd_data_time       = -1;
156 static gint hf_collectd_data_interval   = -1;
157 static gint hf_collectd_data_plugin     = -1;
158 static gint hf_collectd_data_plugin_inst= -1;
159 static gint hf_collectd_data_type       = -1;
160 static gint hf_collectd_data_type_inst  = -1;
161 static gint hf_collectd_data_valcnt     = -1;
162 static gint hf_collectd_val_type        = -1;
163 static gint hf_collectd_val_counter     = -1;
164 static gint hf_collectd_val_gauge       = -1;
165 static gint hf_collectd_val_derive      = -1;
166 static gint hf_collectd_val_absolute    = -1;
167 static gint hf_collectd_val_unknown     = -1;
168 static gint hf_collectd_data_severity   = -1;
169 static gint hf_collectd_data_message    = -1;
170 static gint hf_collectd_data_sighash    = -1;
171 static gint hf_collectd_data_initvec    = -1;
172 static gint hf_collectd_data_username_len = -1;
173 static gint hf_collectd_data_username   = -1;
174 static gint hf_collectd_data_encrypted  = -1;
175
176 static gint ett_collectd                = -1;
177 static gint ett_collectd_string         = -1;
178 static gint ett_collectd_integer        = -1;
179 static gint ett_collectd_part_value     = -1;
180 static gint ett_collectd_value          = -1;
181 static gint ett_collectd_valinfo        = -1;
182 static gint ett_collectd_signature      = -1;
183 static gint ett_collectd_encryption     = -1;
184 static gint ett_collectd_dispatch       = -1;
185 static gint ett_collectd_invalid_length = -1;
186 static gint ett_collectd_unknown        = -1;
187
188 static gint st_collectd_packets = -1;
189 static gint st_collectd_values  = -1;
190 static gint st_collectd_values_hosts   = -1;
191 static gint st_collectd_values_plugins = -1;
192 static gint st_collectd_values_types   = -1;
193
194 /* Prototype for the handoff function */
195 void proto_reg_handoff_collectd (void);
196
197 static void
198 collectd_stats_tree_init (stats_tree *st)
199 {
200         st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
201         st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
202
203         st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
204                                                            st_collectd_values);
205         st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
206                                                               st_collectd_values);
207         st_collectd_values_types = stats_tree_create_pivot (st, "By type",
208                                                             st_collectd_values);
209 } /* void collectd_stats_tree_init */
210
211 static int
212 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
213                             epan_dissect_t *edt _U_, const void *user_data)
214 {
215         const tap_data_t *td;
216         string_counter_t *sc;
217
218         td = user_data;
219         if (td == NULL)
220                 return (-1);
221
222         tick_stat_node (st, "Packets", 0, FALSE);
223         increase_stat_node (st, "Values", 0, TRUE, td->values_num);
224
225         for (sc = td->hosts; sc != NULL; sc = sc->next)
226         {
227                 gint i;
228                 for (i = 0; i < sc->count; i++)
229                         stats_tree_tick_pivot (st, st_collectd_values_hosts,
230                                                sc->string);
231         }
232
233         for (sc = td->plugins; sc != NULL; sc = sc->next)
234         {
235                 gint i;
236                 for (i = 0; i < sc->count; i++)
237                         stats_tree_tick_pivot (st, st_collectd_values_plugins,
238                                                sc->string);
239         }
240
241         for (sc = td->types; sc != NULL; sc = sc->next)
242         {
243                 gint i;
244                 for (i = 0; i < sc->count; i++)
245                         stats_tree_tick_pivot (st, st_collectd_values_types,
246                                                sc->string);
247         }
248
249         return (1);
250 } /* int collectd_stats_tree_packet */
251
252 static void
253 collectd_stats_tree_register (void)
254 {
255         stats_tree_register ("collectd", "collectd", "Collectd", 0,
256                              collectd_stats_tree_packet,
257                              collectd_stats_tree_init, NULL);
258 } /* void register_collectd_stat_trees */
259
260 static int
261 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
262                          gint offset, gint *ret_offset, gint *ret_length,
263                          gchar **ret_string, proto_tree *tree_root,
264                          proto_item **ret_item)
265 {
266         proto_tree *pt;
267         proto_item *pi;
268         gint type;
269         gint length;
270         gint size;
271
272         size = tvb_reported_length_remaining (tvb, offset);
273         if (size < 4)
274         {
275                 /* This should never happen, because `dissect_collectd' checks
276                  * for this condition already. */
277                 return (-1);
278         }
279
280         type   = tvb_get_ntohs(tvb, offset);
281         length = tvb_get_ntohs(tvb, offset + 2);
282
283         if (length > size)
284         {
285                 pi = proto_tree_add_text (tree_root, tvb, offset, length,
286                                           "collectd %s segment: Length = %i <BAD>",
287                                           val_to_str (type, part_names, "UNKNOWN"),
288                                           length);
289                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
290                                         "String part with invalid part length: "
291                                         "Part is longer than rest of package.");
292                 return (-1);
293         }
294
295         *ret_offset = offset + 4;
296         *ret_length = length - 4;
297
298         *ret_string = tvb_get_ephemeral_string (tvb, *ret_offset, *ret_length);
299
300         pi = proto_tree_add_text (tree_root, tvb, offset, length,
301                                   "collectd %s segment: \"%s\"",
302                                   val_to_str (type, part_names, "UNKNOWN"),
303                                   *ret_string);
304
305         if (ret_item != NULL)
306                 *ret_item = pi;
307
308         pt = proto_item_add_subtree (pi, ett_collectd_string);
309         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
310         proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
311         proto_tree_add_item (pt, type_hf, tvb, *ret_offset, *ret_length, FALSE);
312
313         return (0);
314 } /* int dissect_collectd_string */
315
316 static int
317 dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
318                           gint offset, gint *ret_offset, guint64 *ret_value,
319                           proto_tree *tree_root, proto_item **ret_item)
320 {
321         proto_tree *pt;
322         proto_item *pi;
323         gint type;
324         gint length;
325         gint size;
326
327         size = tvb_reported_length_remaining (tvb, offset);
328         if (size < 4)
329         {
330                 /* This should never happen, because `dissect_collectd' checks
331                  * for this condition already. */
332                 return (-1);
333         }
334
335         type   = tvb_get_ntohs(tvb, offset);
336         length = tvb_get_ntohs(tvb, offset + 2);
337
338         if (size < 12)
339         {
340                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
341                                           "collectd %s segment: <BAD>",
342                                           val_to_str (type, part_names, "UNKNOWN"));
343
344                 pt = proto_item_add_subtree (pi, ett_collectd_integer);
345                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
346                                      type);
347                 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
348                                      length);
349                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
350                                           "Garbage at end of packet: Length = %i <BAD>",
351                                           size - 4);
352                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
353                                         "Garbage at end of packet");
354
355                 return (-1);
356         }
357
358         if (length != 12)
359         {
360                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
361                                           "collectd %s segment: <BAD>",
362                                           val_to_str (type, part_names, "UNKNOWN"));
363
364                 pt = proto_item_add_subtree (pi, ett_collectd_integer);
365                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
366                                      type);
367                 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
368                                           offset + 2, 2, length);
369                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
370                                         "Invalid length field for an integer part.");
371
372                 return (-1);
373         }
374
375         *ret_offset = offset + 4;
376         *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
377
378         pi = proto_tree_add_text (tree_root, tvb, offset, length,
379                                   "collectd %s segment: %"G_GINT64_MODIFIER"u",
380                                   val_to_str (type, part_names, "UNKNOWN"),
381                                   *ret_value);
382         if (ret_item != NULL)
383                 *ret_item = pi;
384
385         pt = proto_item_add_subtree (pi, ett_collectd_integer);
386         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
387         proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
388                              length);
389         proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, FALSE);
390
391         return (0);
392 } /* int dissect_collectd_integer */
393
394 static void
395 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
396                         proto_tree *collectd_tree)
397 {
398         proto_item *pi;
399         proto_tree *values_tree, *value_tree;
400         gint i;
401
402         pi = proto_tree_add_text (collectd_tree, tvb, msg_off + 6, val_cnt * 9,
403                                   "%d value%s", val_cnt,
404                                   plurality (val_cnt, "", "s"));
405
406         values_tree = proto_item_add_subtree (pi, ett_collectd_value);
407
408         for (i = 0; i < val_cnt; i++)
409         {
410                 gint value_offset;
411
412                 gint value_type_offset;
413                 guint8 value_type;
414
415                 /* Calculate the offsets of the type byte and the actual value. */
416                 value_offset = msg_off + 6
417                                 + val_cnt  /* value types */
418                                 + (i * 8); /* previous values */
419
420                 value_type_offset = msg_off + 6 + i;
421                 value_type = tvb_get_guint8 (tvb, value_type_offset);
422
423                 switch (value_type) {
424                 case TYPE_VALUE_COUNTER:
425                 {
426                         guint64 val64;
427
428                         val64 = tvb_get_ntoh64 (tvb, value_offset);
429                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
430                                                   val_cnt * 9,
431                                                   "Counter: %"G_GINT64_MODIFIER"u", val64);
432
433                         value_tree = proto_item_add_subtree (pi,
434                                         ett_collectd_valinfo);
435                         proto_tree_add_item (value_tree, hf_collectd_val_type,
436                                              tvb, value_type_offset, 1, FALSE);
437                         proto_tree_add_item (value_tree,
438                                              hf_collectd_val_counter, tvb,
439                                              value_offset, 8, FALSE);
440                         break;
441                 }
442
443                 case TYPE_VALUE_GAUGE:
444                 {
445                         gdouble val;
446
447                         val = tvb_get_letohieee_double (tvb, value_offset);
448                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
449                                                   val_cnt * 9,
450                                                   "Gauge: %g", val);
451
452                         value_tree = proto_item_add_subtree (pi,
453                                         ett_collectd_valinfo);
454                         proto_tree_add_item (value_tree, hf_collectd_val_type,
455                                              tvb, value_type_offset, 1, FALSE);
456                         /* Set the `little endian' flag to TRUE here, because
457                          * collectd stores doubles in x86 representation. */
458                         proto_tree_add_item (value_tree, hf_collectd_val_gauge,
459                                              tvb, value_offset, 8, TRUE);
460                         break;
461                 }
462
463                 case TYPE_VALUE_DERIVE:
464                 {
465                         gint64 val64;
466
467                         val64 = tvb_get_ntoh64 (tvb, value_offset);
468                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
469                                                   val_cnt * 9,
470                                                   "Derive: %"G_GINT64_MODIFIER"i", val64);
471
472                         value_tree = proto_item_add_subtree (pi,
473                                         ett_collectd_valinfo);
474                         proto_tree_add_item (value_tree, hf_collectd_val_type,
475                                              tvb, value_type_offset, 1, FALSE);
476                         proto_tree_add_item (value_tree,
477                                              hf_collectd_val_derive, tvb,
478                                              value_offset, 8, FALSE);
479                         break;
480                 }
481
482                 case TYPE_VALUE_ABSOLUTE:
483                 {
484                         guint64 val64;
485
486                         val64 = tvb_get_ntoh64 (tvb, value_offset);
487                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
488                                                   val_cnt * 9,
489                                                   "Absolute: %"G_GINT64_MODIFIER"u", val64);
490
491                         value_tree = proto_item_add_subtree (pi,
492                                         ett_collectd_valinfo);
493                         proto_tree_add_item (value_tree, hf_collectd_val_type,
494                                              tvb, value_type_offset, 1, FALSE);
495                         proto_tree_add_item (value_tree,
496                                              hf_collectd_val_absolute, tvb,
497                                              value_offset, 8, FALSE);
498                         break;
499                 }
500
501                 default:
502                 {
503                         guint64 val64;
504
505                         val64 = tvb_get_ntoh64 (tvb, value_offset);
506                         pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
507                                                   val_cnt * 9,
508                                                   "Unknown: %"G_GINT64_MODIFIER"x",
509                                                   val64);
510
511                         value_tree = proto_item_add_subtree (pi,
512                                         ett_collectd_valinfo);
513                         proto_tree_add_item (value_tree, hf_collectd_val_type,
514                                              tvb, value_type_offset, 1, FALSE);
515                         proto_tree_add_item (value_tree, hf_collectd_val_unknown,
516                                              tvb, value_offset, 8, FALSE);
517                         break;
518                 }
519                 } /* switch (value_type) */
520         } /* for (i = 0; i < val_cnt; i++) */
521 } /* void dissect_collectd_values */
522
523 static int
524 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
525                               value_data_t *vdispatch, proto_tree *tree_root)
526 {
527         proto_tree *pt;
528         proto_item *pi;
529         gint type;
530         gint length;
531         gint size;
532         gint values_count;
533         gint corrected_values_count;
534
535         size = tvb_reported_length_remaining (tvb, offset);
536         if (size < 4)
537         {
538                 /* This should never happen, because `dissect_collectd' checks
539                  * for this condition already. */
540                 return (-1);
541         }
542
543         type   = tvb_get_ntohs (tvb, offset);
544         length = tvb_get_ntohs (tvb, offset + 2);
545
546         if (size < 15)
547         {
548                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
549                                           "collectd %s segment: <BAD>",
550                                           val_to_str (type, part_names, "UNKNOWN"));
551
552                 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
553                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
554                 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
555                                      length);
556                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
557                                           "Garbage at end of packet: Length = %i <BAD>",
558                                           size - 4);
559                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
560                                         "Garbage at end of packet");
561
562                 return (-1);
563         }
564
565         if ((length < 15) || ((length % 9) != 6))
566         {
567                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
568                                           "collectd %s segment: <BAD>",
569                                           val_to_str (type, part_names, "UNKNOWN"));
570
571                 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
572                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
573                 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
574                                           offset + 2, 2, length);
575                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
576                                         "Invalid length field for a values part.");
577
578                 return (-1);
579         }
580
581         values_count = tvb_get_ntohs (tvb, offset + 4);
582         corrected_values_count = (length - 6) / 9;
583
584         if (values_count != corrected_values_count)
585         {
586                 pi = proto_tree_add_text (tree_root, tvb, offset, length,
587                                           "collectd %s segment: %d (%d) value%s <BAD>",
588                                           val_to_str (type, part_names, "UNKNOWN"),
589                                           values_count, corrected_values_count,
590                                           plurality(values_count, "", "s"));
591         }
592         else
593         {
594                 pi = proto_tree_add_text (tree_root, tvb, offset, length,
595                                           "collectd %s segment: %d value%s",
596                                           val_to_str (type, part_names, "UNKNOWN"),
597                                           values_count,
598                                           plurality(values_count, "", "s"));
599         }
600
601         pt = proto_item_add_subtree (pi, ett_collectd_part_value);
602         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
603         proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
604
605         pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb,
606                                   offset + 4, 2, FALSE);
607         if (values_count != corrected_values_count)
608                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_WARN,
609                                         "Number of values and length of part do not match. "
610                                         "Assuming length is correct.");
611
612         values_count = corrected_values_count;
613
614         dissect_collectd_values (tvb, offset, values_count, pt);
615
616         /* tell what would be dispatched...  */
617         pi = proto_tree_add_text (pt, tvb, offset + 6, length - 6, "Dispatch simulation");
618         pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
619         proto_tree_add_text (pt, tvb, vdispatch->host_off, vdispatch->host_len,
620                              "Host: %s", vdispatch->host);
621         proto_tree_add_text (pt, tvb, vdispatch->plugin_off,
622                              vdispatch->plugin_len,
623                              "Plugin: %s", vdispatch->plugin);
624         if (vdispatch->plugin_instance)
625                 proto_tree_add_text (pt, tvb, vdispatch->plugin_instance_off,
626                                      vdispatch->plugin_instance_len,
627                                      "Plugin instance: %s", vdispatch->plugin_instance);
628         proto_tree_add_text (pt, tvb, vdispatch->type_off, vdispatch->type_len,
629                              "Type: %s", vdispatch->type);
630         if (vdispatch->type_instance)
631                 proto_tree_add_text(pt, tvb, vdispatch->type_instance_off,
632                                     vdispatch->type_instance_len,
633                                     "Type instance: %s", vdispatch->type_instance);
634         proto_tree_add_text (pt, tvb, vdispatch->time_off, 8,
635                              "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
636                              vdispatch->time, vdispatch->time_str);
637         proto_tree_add_text (pt, tvb, vdispatch->interval_off, 8,
638                              "Interval: %"G_GINT64_MODIFIER"u",
639                              vdispatch->interval);
640         return (0);
641 } /* void dissect_collectd_part_values */
642
643 static int
644 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
645                             gint offset, proto_tree *tree_root)
646 {
647         proto_item *pi;
648         proto_tree *pt;
649         gint type;
650         gint length;
651         gint size;
652
653         size = tvb_reported_length_remaining (tvb, offset);
654         if (size < 4)
655         {
656                 /* This should never happen, because `dissect_collectd' checks
657                  * for this condition already. */
658                 return (-1);
659         }
660
661         type   = tvb_get_ntohs (tvb, offset);
662         length = tvb_get_ntohs (tvb, offset + 2);
663
664         if (size < 36) /* remaining packet size too small for signature */
665         {
666                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
667                                           "collectd %s segment: <BAD>",
668                                           val_to_str (type, part_names, "UNKNOWN"));
669
670                 pt = proto_item_add_subtree (pi, ett_collectd_signature);
671                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
672                 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
673                                      length);
674                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
675                                           "Garbage at end of packet: Length = %i <BAD>",
676                                           size - 4);
677                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
678                                         "Garbage at end of packet");
679
680                 return (-1);
681         }
682
683         if (length < 36)
684         {
685                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
686                                           "collectd %s segment: <BAD>",
687                                           val_to_str (type, part_names, "UNKNOWN"));
688
689                 pt = proto_item_add_subtree (pi, ett_collectd_signature);
690                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
691                 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
692                                           offset + 2, 2, length);
693                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
694                                         "Invalid length field for a signature part.");
695
696                 return (-1);
697         }
698
699         pi = proto_tree_add_text (tree_root, tvb, offset, length,
700                                   "collectd %s segment: HMAC-SHA-256",
701                                   val_to_str (type, part_names, "UNKNOWN"));
702
703         pt = proto_item_add_subtree (pi, ett_collectd_signature);
704         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
705         proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
706                              length);
707         proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, FALSE);
708         proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, FALSE);
709
710         return (0);
711 } /* int dissect_collectd_signature */
712
713 static int
714 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
715                             gint offset, proto_tree *tree_root)
716 {
717         proto_item *pi;
718         proto_tree *pt;
719         gint type;
720         gint length;
721         gint size;
722         gint username_length;
723
724         size = tvb_reported_length_remaining (tvb, offset);
725         if (size < 4)
726         {
727                 /* This should never happen, because `dissect_collectd' checks
728                  * for this condition already. */
729                 return (-1);
730         }
731
732         type   = tvb_get_ntohs (tvb, offset);
733         length = tvb_get_ntohs (tvb, offset + 2);
734
735         if (size < 42) /* remaining packet size too small for signature */
736         {
737                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
738                                           "collectd %s segment: <BAD>",
739                                           val_to_str (type, part_names, "UNKNOWN"));
740
741                 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
742                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
743                 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
744                                      length);
745                 pi = proto_tree_add_text (pt, tvb, offset + 4, -1,
746                                           "Garbage at end of packet: Length = %i <BAD>",
747                                           size - 4);
748                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
749                                         "Garbage at end of packet");
750
751                 return (-1);
752         }
753
754         if (length < 42)
755         {
756                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
757                                           "collectd %s segment: <BAD>",
758                                           val_to_str (type, part_names, "UNKNOWN"));
759
760                 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
761                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
762                 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
763                                           offset + 2, 2, length);
764                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
765                                         "Invalid length field for an encryption part.");
766
767                 return (-1);
768         }
769
770         username_length = tvb_get_ntohs (tvb, offset + 4);
771         if (username_length > (length - 42))
772         {
773                 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
774                                           "collectd %s segment: <BAD>",
775                                           val_to_str (type, part_names, "UNKNOWN"));
776
777                 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
778                 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
779                 proto_tree_add_uint (pt, hf_collectd_length, tvb,
780                                      offset + 2, 2, length);
781                 pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb,
782                                           offset + 4, 2, length);
783                 expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR,
784                                         "Invalid username length field for an encryption part.");
785
786                 return (-1);
787         }
788
789         pi = proto_tree_add_text (tree_root, tvb, offset, length,
790                                   "collectd %s segment: AES-256",
791                                   val_to_str (type, part_names, "UNKNOWN"));
792
793         pt = proto_item_add_subtree (pi, ett_collectd_encryption);
794         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
795         proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
796         proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length);
797         proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, FALSE);
798         proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
799                              offset + (6 + username_length), 16, FALSE);
800         proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb,
801                              offset + (22 + username_length),
802                              length - (22 + username_length), FALSE);
803
804         return (0);
805 } /* int dissect_collectd_encrypted */
806
807 static int
808 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
809 {
810         string_counter_t *entry;
811
812         if (ret_list == NULL)
813                 return (-1);
814
815         if (new_value == NULL)
816                 new_value = "(null)";
817
818         for (entry = *ret_list; entry != NULL; entry = entry->next)
819                 if (strcmp (new_value, entry->string) == 0)
820                 {
821                         entry->count++;
822                         return (0);
823                 }
824
825         entry = ep_alloc (sizeof (*entry));
826         if (entry == NULL)
827                 return (-1);
828         memset (entry, 0, sizeof (*entry));
829
830         entry->string = ep_strdup (new_value);
831         if (entry->string == NULL)
832                 return (-1);
833         entry->count = 1;
834         entry->next = *ret_list;
835
836         *ret_list = entry;
837
838         return (0);
839 }
840
841 static void
842 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
843 {
844         static tap_data_t tap_data;
845
846         gint offset;
847         gint size;
848         gchar *pkt_host = NULL;
849         gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0;
850         value_data_t vdispatch;
851         notify_data_t ndispatch;
852         int status;
853         proto_item *pi;
854         proto_tree *collectd_tree;
855         proto_tree *pt;
856
857         memset(&vdispatch, '\0', sizeof(vdispatch));
858         memset(&ndispatch, '\0', sizeof(ndispatch));
859
860         col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
861         col_clear(pinfo->cinfo, COL_INFO);
862
863         offset = 0;
864         size = tvb_reported_length(tvb);
865
866         /* create the collectd protocol tree */
867         pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, FALSE);
868         collectd_tree = proto_item_add_subtree(pi, ett_collectd);
869
870         memset (&tap_data, 0, sizeof (tap_data));
871
872         status = 0;
873         while ((size > 0) && (status == 0))
874         {
875
876                 gint part_type;
877                 gint part_length;
878
879                 /* Let's handle the easy case first real quick: All we do here
880                  * is extract a host name and count the number of values,
881                  * plugins and notifications. The payload is not checked at
882                  * all, but the same checks are run on the part_length stuff -
883                  * it's important to keep an eye on that. */
884                 if (!tree)
885                 {
886                         /* Check for garbage at end of packet. */
887                         if (size < 4)
888                         {
889                                 pkt_errors++;
890                                 status = -1;
891                                 break;
892                         }
893
894                         part_type = tvb_get_ntohs (tvb, offset);
895                         part_length  = tvb_get_ntohs (tvb, offset+2);
896
897                         /* Check if part_length is in the valid range. */
898                         if ((part_length < 4) || (part_length > size))
899                         {
900                                 pkt_errors++;
901                                 status = -1;
902                                 break;
903                         }
904
905                         switch (part_type) {
906                         case TYPE_HOST:
907                                 vdispatch.host = tvb_get_ephemeral_string (tvb,
908                                                 offset + 4, part_length - 4);
909                                 if (pkt_host == NULL)
910                                         pkt_host = vdispatch.host;
911                                 break;
912                         case TYPE_TIME:
913                                 break;
914                         case TYPE_PLUGIN:
915                                 vdispatch.plugin = tvb_get_ephemeral_string (tvb,
916                                                 offset + 4, part_length - 4);
917                                 pkt_plugins++;
918                                 break;
919                         case TYPE_PLUGIN_INSTANCE:
920                                 break;
921                         case TYPE_TYPE:
922                                 vdispatch.type = tvb_get_ephemeral_string (tvb,
923                                                 offset + 4, part_length - 4);
924                                 break;
925                         case TYPE_TYPE_INSTANCE:
926                                 break;
927                         case TYPE_INTERVAL:
928                                 break;
929                         case TYPE_VALUES:
930                         {
931                                 pkt_values++;
932
933                                 tap_data.values_num++;
934                                 stats_account_string (&tap_data.hosts,
935                                                       vdispatch.host);
936                                 stats_account_string (&tap_data.plugins,
937                                                       vdispatch.plugin);
938                                 stats_account_string (&tap_data.types,
939                                                       vdispatch.type);
940
941                                 break;
942                         }
943                         case TYPE_MESSAGE:
944                                 pkt_messages++;
945                                 break;
946                         case TYPE_SEVERITY:
947                                 break;
948                         default:
949                                 pkt_unknown++;
950                         }
951
952                         offset  += part_length;
953                         size    -= part_length;
954                         continue;
955                 } /* if (!tree) */
956
957                 /* Now we do the same steps again, but much more thoroughly. */
958
959                 /* Check if there are at least four bytes left first.
960                  * Four bytes are used to read the type and the length
961                  * of the next part. If there's less, there's some garbage
962                  * at the end of the packet. */
963                 if (size < 4)
964                 {
965                         pi = proto_tree_add_text (collectd_tree, tvb,
966                                                   offset, -1,
967                                                   "Garbage at end of packet: Length = %i <BAD>",
968                                                   size);
969                         expert_add_info_format (pinfo, pi, PI_MALFORMED,
970                                                 PI_ERROR,
971                                                 "Garbage at end of packet");
972                         pkt_errors++;
973                         status = -1;
974                         break;
975                 }
976
977                 /* dissect a message entry */
978                 part_type = tvb_get_ntohs (tvb, offset);
979                 part_length  = tvb_get_ntohs (tvb, offset + 2);
980
981                 /* Check if the length of the part is in the valid range. Don't
982                  * confuse this with the above: Here we check the information
983                  * provided in the packet.. */
984                 if ((part_length < 4) || (part_length > size))
985                 {
986                         pi = proto_tree_add_text (collectd_tree, tvb,
987                                                   offset, part_length,
988                                                   "collectd %s segment: Length = %i <BAD>",
989                                                   val_to_str (part_type, part_names, "UNKNOWN"),
990                                                   part_length);
991
992                         pt = proto_item_add_subtree (pi, ett_collectd_invalid_length);
993                         proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
994                                              2, part_type);
995                         pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
996                                              offset + 2, 2, part_length);
997
998                         if (part_length < 4)
999                                 expert_add_info_format (pinfo, pi,
1000                                                         PI_MALFORMED, PI_ERROR,
1001                                                         "Bad part length: Is %i, expected at least 4",
1002                                                         part_length);
1003                         else
1004                                 expert_add_info_format (pinfo, pi,
1005                                                         PI_MALFORMED, PI_ERROR,
1006                                                         "Bad part length: Larger than remaining packet size.");
1007
1008                         pkt_errors++;
1009                         status = -1;
1010                         break;
1011                 }
1012
1013                 /* The header information looks okay, let's tend to the actual
1014                  * payload in this part. */
1015                 switch (part_type) {
1016                 case TYPE_HOST:
1017                 {
1018                         status = dissect_collectd_string (tvb, pinfo,
1019                                         hf_collectd_data_host,
1020                                         offset,
1021                                         &vdispatch.host_off,
1022                                         &vdispatch.host_len,
1023                                         &vdispatch.host,
1024                                         collectd_tree, /* item = */ NULL);
1025                         if (status != 0)
1026                                 pkt_errors++;
1027                         else
1028                         {
1029                                 if (pkt_host == NULL)
1030                                         pkt_host = vdispatch.host;
1031                                 ndispatch.host_off = vdispatch.host_off;
1032                                 ndispatch.host_len = vdispatch.host_len;
1033                                 ndispatch.host = vdispatch.host;
1034                         }
1035
1036                         break;
1037                 }
1038
1039                 case TYPE_PLUGIN:
1040                 {
1041                         status = dissect_collectd_string (tvb, pinfo,
1042                                         hf_collectd_data_plugin,
1043                                         offset,
1044                                         &vdispatch.plugin_off,
1045                                         &vdispatch.plugin_len,
1046                                         &vdispatch.plugin,
1047                                         collectd_tree, /* item = */ NULL);
1048                         if (status != 0)
1049                                 pkt_errors++;
1050                         else
1051                                 pkt_plugins++;
1052
1053                         break;
1054                 }
1055
1056                 case TYPE_PLUGIN_INSTANCE:
1057                 {
1058                         status = dissect_collectd_string (tvb, pinfo,
1059                                         hf_collectd_data_plugin_inst,
1060                                         offset,
1061                                         &vdispatch.plugin_instance_off,
1062                                         &vdispatch.plugin_instance_len,
1063                                         &vdispatch.plugin_instance,
1064                                         collectd_tree, /* item = */ NULL);
1065                         if (status != 0)
1066                                 pkt_errors++;
1067
1068                         break;
1069                 }
1070
1071                 case TYPE_TYPE:
1072                 {
1073                         status = dissect_collectd_string (tvb, pinfo,
1074                                         hf_collectd_data_type,
1075                                         offset,
1076                                         &vdispatch.type_off,
1077                                         &vdispatch.type_len,
1078                                         &vdispatch.type,
1079                                         collectd_tree, /* item = */ NULL);
1080                         if (status != 0)
1081                                 pkt_errors++;
1082
1083                         break;
1084                 }
1085
1086                 case TYPE_TYPE_INSTANCE:
1087                 {
1088                         status = dissect_collectd_string (tvb, pinfo,
1089                                         hf_collectd_data_type_inst,
1090                                         offset,
1091                                         &vdispatch.type_instance_off,
1092                                         &vdispatch.type_instance_len,
1093                                         &vdispatch.type_instance,
1094                                         collectd_tree, /* item = */ NULL);
1095                         if (status != 0)
1096                                 pkt_errors++;
1097
1098                         break;
1099                 }
1100
1101                 case TYPE_TIME:
1102                 {
1103                         ndispatch.time_str = NULL;
1104                         vdispatch.time_str = NULL;
1105
1106                         pi = NULL;
1107                         status = dissect_collectd_integer (tvb, pinfo,
1108                                         hf_collectd_data_time,
1109                                         offset,
1110                                         &vdispatch.time_off,
1111                                         &vdispatch.time,
1112                                         collectd_tree, &pi);
1113                         if (status != 0)
1114                                 pkt_errors++;
1115                         else
1116                         {
1117                                 vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time);
1118
1119                                 ndispatch.time = vdispatch.time;
1120                                 ndispatch.time_str = vdispatch.time_str;
1121
1122                                 proto_item_set_text (pi, "collectd TIME segment: %"G_GINT64_MODIFIER"u (%s)",
1123                                                      vdispatch.time, vdispatch.time_str);
1124                         }
1125
1126                         break;
1127                 }
1128
1129                 case TYPE_INTERVAL:
1130                 {
1131                         status = dissect_collectd_integer (tvb, pinfo,
1132                                         hf_collectd_data_interval,
1133                                         offset,
1134                                         &vdispatch.interval_off,
1135                                         &vdispatch.interval,
1136                                         collectd_tree, /* item = */ NULL);
1137                         if (status != 0)
1138                                 pkt_errors++;
1139
1140                         break;
1141                 }
1142
1143                 case TYPE_VALUES:
1144                 {
1145                         status = dissect_collectd_part_values (tvb, pinfo,
1146                                         offset,
1147                                         &vdispatch,
1148                                         collectd_tree);
1149                         if (status != 0)
1150                                 pkt_errors++;
1151                         else
1152                                 pkt_values++;
1153
1154                         tap_data.values_num++;
1155                         stats_account_string (&tap_data.hosts,
1156                                               vdispatch.host);
1157                         stats_account_string (&tap_data.plugins,
1158                                               vdispatch.plugin);
1159                         stats_account_string (&tap_data.types,
1160                                               vdispatch.type);
1161
1162                         break;
1163                 }
1164
1165                 case TYPE_MESSAGE:
1166                 {
1167                         pi = NULL;
1168                         status = dissect_collectd_string (tvb, pinfo,
1169                                         hf_collectd_data_message,
1170                                         offset,
1171                                         &ndispatch.message_off,
1172                                         &ndispatch.message_len,
1173                                         &ndispatch.message,
1174                                         collectd_tree, &pi);
1175                         if (status != 0)
1176                         {
1177                                 pkt_errors++;
1178                                 break;
1179                         }
1180                         pkt_messages++;
1181
1182                         pt = proto_item_get_subtree (pi);
1183
1184                         /* tell what would be dispatched...  */
1185                         pi = proto_tree_add_text (pt, tvb, offset + 4,
1186                                                   part_length - 4,
1187                                                   "Dispatch simulation");
1188                         pt = proto_item_add_subtree(pi, ett_collectd_dispatch);
1189                         proto_tree_add_text (pt, tvb, ndispatch.host_off,
1190                                              ndispatch.host_len,
1191                                              "Host: %s", ndispatch.host);
1192                         proto_tree_add_text (pt, tvb, ndispatch.time_off, 8,
1193                                              "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
1194                                              ndispatch.time, ndispatch.time_str);
1195                         proto_tree_add_text (pt, tvb, ndispatch.severity_off, 8,
1196                                              "Severity: %s (%#"G_GINT64_MODIFIER"x)",
1197                                              val_to_str((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1198                                              ndispatch.severity);
1199                         proto_tree_add_text (pt, tvb, ndispatch.message_off,
1200                                              ndispatch.message_len,
1201                                              "Message: %s", ndispatch.message);
1202                         break;
1203                 }
1204
1205                 case TYPE_SEVERITY:
1206                 {
1207                         pi = NULL;
1208                         status = dissect_collectd_integer (tvb, pinfo,
1209                                         hf_collectd_data_severity,
1210                                         offset,
1211                                         &ndispatch.severity_off,
1212                                         &ndispatch.severity,
1213                                         collectd_tree, &pi);
1214                         if (status != 0)
1215                                 pkt_errors++;
1216                         else
1217                         {
1218                                 proto_item_set_text (pi,
1219                                                 "collectd SEVERITY segment: "
1220                                                 "%s (%"G_GINT64_MODIFIER"u)",
1221                                                 val_to_str ((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
1222                                                 ndispatch.severity);
1223                         }
1224
1225                         break;
1226                 }
1227
1228                 case TYPE_SIGN_SHA256:
1229                 {
1230                         status = dissect_collectd_signature (tvb, pinfo,
1231                                                              offset,
1232                                                              collectd_tree);
1233                         if (status != 0)
1234                                 pkt_errors++;
1235
1236                         break;
1237                 }
1238
1239                 case TYPE_ENCR_AES256:
1240                 {
1241                         status = dissect_collectd_encrypted (tvb, pinfo,
1242                                         offset, collectd_tree);
1243                         if (status != 0)
1244                                 pkt_errors++;
1245
1246                         break;
1247                 }
1248
1249                 default:
1250                 {
1251                         pkt_unknown++;
1252                         pi = proto_tree_add_text (collectd_tree, tvb,
1253                                                   offset, part_length,
1254                                                   "collectd %s segment: %i bytes",
1255                                                   val_to_str(part_type, part_names, "UNKNOWN"),
1256                                                   part_length);
1257
1258                         pt = proto_item_add_subtree(pi, ett_collectd_unknown);
1259                         pi = proto_tree_add_uint (pt, hf_collectd_type, tvb,
1260                                                   offset, 2, part_type);
1261                         proto_tree_add_uint (pt, hf_collectd_length, tvb,
1262                                                   offset + 2, 2, part_length);
1263                         proto_tree_add_item (pt, hf_collectd_data, tvb,
1264                                              offset + 4, part_length - 4, FALSE);
1265
1266                         expert_add_info_format (pinfo, pi,
1267                                                 PI_UNDECODED, PI_NOTE,
1268                                                 "Unknown part type %#x. Cannot decode data.",
1269                                                 part_type);
1270                 }
1271                 } /* switch (part_type) */
1272
1273                 offset  += part_length;
1274                 size    -= part_length;
1275         } /* while ((size > 4) && (status == 0)) */
1276
1277         if (pkt_errors && pkt_unknown)
1278                 col_add_fstr (pinfo->cinfo, COL_INFO,
1279                               "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s",
1280                               pkt_host,
1281                               pkt_values, plurality (pkt_values, " ", "s"),
1282                               pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1283                               pkt_messages, plurality (pkt_messages, ", ", "s,"),
1284                               pkt_unknown,
1285                               pkt_errors, plurality (pkt_errors, "", "s"));
1286         else if (pkt_errors)
1287                 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s",
1288                               pkt_host,
1289                               pkt_values, plurality (pkt_values, " ", "s"),
1290                               pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1291                               pkt_messages, plurality (pkt_messages, ", ", "s,"),
1292                               pkt_errors, plurality (pkt_errors, "", "s"));
1293         else if (pkt_unknown)
1294                 col_add_fstr (pinfo->cinfo, COL_INFO,
1295                               "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown",
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                               pkt_unknown);
1301         else
1302                 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1303                               pkt_host,
1304                               pkt_values, plurality (pkt_values, " ", "s"),
1305                               pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1306                               pkt_messages, plurality (pkt_messages, "", "s"));
1307
1308         /* Dispatch tap data. */
1309         tap_queue_packet (tap_collectd, pinfo, &tap_data);
1310 } /* void dissect_collectd */
1311
1312 void proto_register_collectd(void)
1313 {
1314         module_t *collectd_module;
1315
1316         /* Setup list of header fields */
1317         static hf_register_info hf[] = {
1318                 { &hf_collectd_type,
1319                         { "Type", "collectd.type", FT_UINT16, BASE_HEX,
1320                                 VALS(part_names), 0x0, NULL, HFILL }
1321                 },
1322                 { &hf_collectd_length,
1323                         { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1324                                 NULL, 0x0, NULL, HFILL }
1325                 },
1326                 { &hf_collectd_data,
1327                         { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1328                                 NULL, 0x0, NULL, HFILL }
1329                 },
1330                 { &hf_collectd_data_host,
1331                         { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1332                                 NULL, 0x0, NULL, HFILL }
1333                 },
1334                 { &hf_collectd_data_interval,
1335                         { "Interval", "collectd.data.interval", FT_UINT64, BASE_DEC,
1336                                 NULL, 0x0, NULL, HFILL }
1337                 },
1338                 { &hf_collectd_data_time,
1339                         { "Timestamp", "collectd.data.time", FT_UINT64, BASE_DEC,
1340                                 NULL, 0x0, NULL, HFILL }
1341                 },
1342                 { &hf_collectd_data_plugin,
1343                         { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1344                                 NULL, 0x0, NULL, HFILL }
1345                 },
1346                 { &hf_collectd_data_plugin_inst,
1347                         { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1348                                 NULL, 0x0, NULL, HFILL }
1349                 },
1350                 { &hf_collectd_data_type,
1351                         { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1352                                 NULL, 0x0, NULL, HFILL }
1353                 },
1354                 { &hf_collectd_data_type_inst,
1355                         { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1356                                 NULL, 0x0, NULL, HFILL }
1357                 },
1358                 { &hf_collectd_data_valcnt,
1359                         { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1360                                 NULL, 0x0, NULL, HFILL }
1361                 },
1362                 { &hf_collectd_val_type,
1363                         { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1364                                 VALS(valuetypenames), 0x0, NULL, HFILL }
1365                 },
1366                 { &hf_collectd_val_counter,
1367                         { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1368                                 NULL, 0x0, NULL, HFILL }
1369                 },
1370                 { &hf_collectd_val_gauge,
1371                         { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1372                                 NULL, 0x0, NULL, HFILL }
1373                 },
1374                 { &hf_collectd_val_derive,
1375                         { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1376                                 NULL, 0x0, NULL, HFILL }
1377                 },
1378                 { &hf_collectd_val_absolute,
1379                         { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1380                                 NULL, 0x0, NULL, HFILL }
1381                 },
1382                 { &hf_collectd_val_unknown,
1383                         { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1384                                 NULL, 0x0, NULL, HFILL }
1385                 },
1386                 { &hf_collectd_data_severity,
1387                         { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX,
1388                                 NULL, 0x0, NULL, HFILL }
1389                 },
1390                 { &hf_collectd_data_message,
1391                         { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1392                                 NULL, 0x0, NULL, HFILL }
1393                 },
1394                 { &hf_collectd_data_sighash,
1395                         { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1396                                 NULL, 0x0, NULL, HFILL }
1397                 },
1398                 { &hf_collectd_data_initvec,
1399                         { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1400                                 NULL, 0x0, NULL, HFILL }
1401                 },
1402                 { &hf_collectd_data_username_len,
1403                         { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1404                                 NULL, 0x0, NULL, HFILL }
1405                 },
1406                 { &hf_collectd_data_username,
1407                         { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1408                                 NULL, 0x0, NULL, HFILL }
1409                 },
1410                 { &hf_collectd_data_encrypted,
1411                         { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1412                                 NULL, 0x0, NULL, HFILL }
1413                 },
1414         };
1415
1416         /* Setup protocol subtree array */
1417         static gint *ett[] = {
1418                 &ett_collectd,
1419                 &ett_collectd_string,
1420                 &ett_collectd_integer,
1421                 &ett_collectd_part_value,
1422                 &ett_collectd_value,
1423                 &ett_collectd_valinfo,
1424                 &ett_collectd_signature,
1425                 &ett_collectd_encryption,
1426                 &ett_collectd_dispatch,
1427                 &ett_collectd_invalid_length,
1428                 &ett_collectd_unknown,
1429         };
1430
1431         /* Register the protocol name and description */
1432         proto_collectd = proto_register_protocol("collectd network data",
1433                                                  "collectd", "collectd");
1434
1435         /* Required function calls to register the header fields and subtrees used */
1436         proto_register_field_array(proto_collectd, hf, array_length(hf));
1437         proto_register_subtree_array(ett, array_length(ett));
1438
1439         tap_collectd = register_tap ("collectd");
1440
1441         /*
1442          * Create an unsigned integer preference to allow the user to specify the
1443          * UDP port on which to capture DIS packets.
1444          */
1445         collectd_module = prefs_register_protocol (proto_collectd,
1446                                                    proto_reg_handoff_collectd);
1447
1448         prefs_register_uint_preference (collectd_module, "udp.port",
1449                                         "collectd UDP port",
1450                                         "Set the UDP port for collectd messages",
1451                                         10, &collectd_udp_port);
1452 } /* void proto_register_collectd */
1453
1454 void proto_reg_handoff_collectd (void)
1455 {
1456         static gboolean first_run = TRUE;
1457         static gint registered_udp_port = -1;
1458         static dissector_handle_t collectd_handle;
1459
1460         if (first_run)
1461                 collectd_handle = create_dissector_handle (dissect_collectd,
1462                                                            proto_collectd);
1463
1464         /* Change the dissector registration if the preferences have been
1465          * changed. */
1466         if (registered_udp_port != -1)
1467                 dissector_delete ("udp.port", registered_udp_port,
1468                                   collectd_handle);
1469
1470         dissector_add ("udp.port", collectd_udp_port, collectd_handle);
1471         registered_udp_port = collectd_udp_port;
1472
1473         if (first_run)
1474                 collectd_stats_tree_register ();
1475
1476         first_run = FALSE;
1477 } /* void proto_reg_handoff_collectd */