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