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