HACK: pinfo->private_data points to smb_info again
[wireshark-wip.git] / epan / dissectors / packet-collectd.c
blobf67ee9fa6aaa4c5d1a2676e4a1f6e7f66462abb0
1 /* packet-collectd.c
2 * Routines for collectd (http://collectd.org/) network plugin dissection
4 * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org>
5 * Copyright 2009-2013 Florian Forster <octo at collectd.org>
7 * $Id$
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "config.h"
30 #include <string.h>
31 #include <glib.h>
32 #include <epan/packet.h>
33 #include <epan/prefs.h>
34 #include <epan/expert.h>
35 #include <epan/stats_tree.h>
36 #include <epan/wmem/wmem.h>
38 #define STR_NONNULL(str) ((str) ? (str) : "(null)")
40 #define TYPE_HOST 0x0000
41 #define TYPE_TIME 0x0001
42 #define TYPE_TIME_HR 0x0008
43 #define TYPE_PLUGIN 0x0002
44 #define TYPE_PLUGIN_INSTANCE 0x0003
45 #define TYPE_TYPE 0x0004
46 #define TYPE_TYPE_INSTANCE 0x0005
47 #define TYPE_VALUES 0x0006
48 #define TYPE_INTERVAL 0x0007
49 #define TYPE_INTERVAL_HR 0x0009
50 #define TYPE_MESSAGE 0x0100
51 #define TYPE_SEVERITY 0x0101
52 #define TYPE_SIGN_SHA256 0x0200
53 #define TYPE_ENCR_AES256 0x0210
55 void proto_register_collectd(void);
57 typedef struct value_data_s {
58 gchar *host;
59 gint host_off;
60 gint host_len;
61 guint64 time;
62 gint time_off;
63 guint64 interval;
64 gint interval_off;
65 gchar *plugin;
66 gint plugin_off;
67 gint plugin_len;
68 gchar *plugin_instance;
69 gint plugin_instance_off;
70 gint plugin_instance_len;
71 gchar *type;
72 gint type_off;
73 gint type_len;
74 gchar *type_instance;
75 gint type_instance_off;
76 gint type_instance_len;
77 } value_data_t;
79 typedef struct notify_data_s {
80 gchar *host;
81 gint host_off;
82 gint host_len;
83 guint64 time;
84 gint time_off;
85 guint64 severity;
86 gint severity_off;
87 gchar *message;
88 gint message_off;
89 gint message_len;
90 } notify_data_t;
92 struct string_counter_s;
93 typedef struct string_counter_s string_counter_t;
94 struct string_counter_s
96 const gchar *string;
97 gint count;
98 string_counter_t *next;
101 typedef struct tap_data_s {
102 gint values_num;
104 string_counter_t *hosts;
105 string_counter_t *plugins;
106 string_counter_t *types;
107 } tap_data_t;
109 static const value_string part_names[] = {
110 { TYPE_VALUES, "VALUES" },
111 { TYPE_TIME, "TIME" },
112 { TYPE_TIME_HR, "TIME_HR" },
113 { TYPE_INTERVAL, "INTERVAL" },
114 { TYPE_INTERVAL_HR, "INTERVAL_HR" },
115 { TYPE_HOST, "HOST" },
116 { TYPE_PLUGIN, "PLUGIN" },
117 { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" },
118 { TYPE_TYPE, "TYPE" },
119 { TYPE_TYPE_INSTANCE, "TYPE_INSTANCE" },
120 { TYPE_MESSAGE, "MESSAGE" },
121 { TYPE_SEVERITY, "SEVERITY" },
122 { TYPE_SIGN_SHA256, "SIGNATURE" },
123 { TYPE_ENCR_AES256, "ENCRYPTED_DATA" },
124 { 0, NULL }
127 #define TYPE_VALUE_COUNTER 0x00
128 #define TYPE_VALUE_GAUGE 0x01
129 #define TYPE_VALUE_DERIVE 0x02
130 #define TYPE_VALUE_ABSOLUTE 0x03
131 static const value_string valuetypenames[] = {
132 { TYPE_VALUE_COUNTER, "COUNTER" },
133 { TYPE_VALUE_GAUGE, "GAUGE" },
134 { TYPE_VALUE_DERIVE, "DERIVE" },
135 { TYPE_VALUE_ABSOLUTE, "ABSOLUTE" },
136 { 0, NULL }
139 #define SEVERITY_FAILURE 0x01
140 #define SEVERITY_WARNING 0x02
141 #define SEVERITY_OKAY 0x04
142 static const val64_string severity_names[] = {
143 { SEVERITY_FAILURE, "FAILURE" },
144 { SEVERITY_WARNING, "WARNING" },
145 { SEVERITY_OKAY, "OKAY" },
146 { 0, NULL }
149 #define UDP_PORT_COLLECTD 25826
150 static guint collectd_udp_port = UDP_PORT_COLLECTD;
152 static gint proto_collectd = -1;
153 static gint tap_collectd = -1;
155 static gint hf_collectd_type = -1;
156 static gint hf_collectd_length = -1;
157 static gint hf_collectd_data = -1;
158 static gint hf_collectd_data_host = -1;
159 static gint hf_collectd_data_time = -1;
160 static gint hf_collectd_data_interval = -1;
161 static gint hf_collectd_data_plugin = -1;
162 static gint hf_collectd_data_plugin_inst= -1;
163 static gint hf_collectd_data_type = -1;
164 static gint hf_collectd_data_type_inst = -1;
165 static gint hf_collectd_data_valcnt = -1;
166 static gint hf_collectd_val_type = -1;
167 static gint hf_collectd_val_counter = -1;
168 static gint hf_collectd_val_gauge = -1;
169 static gint hf_collectd_val_derive = -1;
170 static gint hf_collectd_val_absolute = -1;
171 static gint hf_collectd_val_unknown = -1;
172 static gint hf_collectd_data_severity = -1;
173 static gint hf_collectd_data_message = -1;
174 static gint hf_collectd_data_sighash = -1;
175 static gint hf_collectd_data_initvec = -1;
176 static gint hf_collectd_data_username_len = -1;
177 static gint hf_collectd_data_username = -1;
178 static gint hf_collectd_data_encrypted = -1;
180 static gint ett_collectd = -1;
181 static gint ett_collectd_string = -1;
182 static gint ett_collectd_integer = -1;
183 static gint ett_collectd_part_value = -1;
184 static gint ett_collectd_value = -1;
185 static gint ett_collectd_valinfo = -1;
186 static gint ett_collectd_signature = -1;
187 static gint ett_collectd_encryption = -1;
188 static gint ett_collectd_dispatch = -1;
189 static gint ett_collectd_invalid_length = -1;
190 static gint ett_collectd_unknown = -1;
192 static gint st_collectd_packets = -1;
193 static gint st_collectd_values = -1;
194 static gint st_collectd_values_hosts = -1;
195 static gint st_collectd_values_plugins = -1;
196 static gint st_collectd_values_types = -1;
198 static expert_field ei_collectd_type = EI_INIT;
199 static expert_field ei_collectd_invalid_length = EI_INIT;
200 static expert_field ei_collectd_data_valcnt = EI_INIT;
201 static expert_field ei_collectd_garbage = EI_INIT;
203 /* Prototype for the handoff function */
204 void proto_reg_handoff_collectd (void);
206 static nstime_t
207 collectd_time_to_nstime (guint64 t)
209 nstime_t nstime = { 0, 0 };
210 nstime.secs = (time_t) (t / 1073741824);
211 nstime.nsecs = (int) (((double) (t % 1073741824)) / 1.073741824);
213 return (nstime);
216 static void
217 collectd_stats_tree_init (stats_tree *st)
219 st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
220 st_collectd_values = stats_tree_create_node (st, "Values", 0, TRUE);
222 st_collectd_values_hosts = stats_tree_create_pivot (st, "By host",
223 st_collectd_values);
224 st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin",
225 st_collectd_values);
226 st_collectd_values_types = stats_tree_create_pivot (st, "By type",
227 st_collectd_values);
228 } /* void collectd_stats_tree_init */
230 static int
231 collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
232 epan_dissect_t *edt _U_, const void *user_data)
234 const tap_data_t *td;
235 string_counter_t *sc;
237 td = (const tap_data_t *)user_data;
238 if (td == NULL)
239 return (-1);
241 tick_stat_node (st, "Packets", 0, FALSE);
242 increase_stat_node (st, "Values", 0, TRUE, td->values_num);
244 for (sc = td->hosts; sc != NULL; sc = sc->next)
246 gint i;
247 for (i = 0; i < sc->count; i++)
248 stats_tree_tick_pivot (st, st_collectd_values_hosts,
249 sc->string);
252 for (sc = td->plugins; sc != NULL; sc = sc->next)
254 gint i;
255 for (i = 0; i < sc->count; i++)
256 stats_tree_tick_pivot (st, st_collectd_values_plugins,
257 sc->string);
260 for (sc = td->types; sc != NULL; sc = sc->next)
262 gint i;
263 for (i = 0; i < sc->count; i++)
264 stats_tree_tick_pivot (st, st_collectd_values_types,
265 sc->string);
268 return (1);
269 } /* int collectd_stats_tree_packet */
271 static void
272 collectd_stats_tree_register (void)
274 stats_tree_register ("collectd", "collectd", "Collectd", 0,
275 collectd_stats_tree_packet,
276 collectd_stats_tree_init, NULL);
277 } /* void register_collectd_stat_trees */
279 static void
280 collectd_proto_tree_add_assembled_metric (tvbuff_t *tvb,
281 gint offset, gint length,
282 value_data_t const *vdispatch, proto_tree *root)
284 proto_item *root_item;
285 proto_tree *subtree;
286 nstime_t nstime;
288 root_item = proto_tree_add_text (root, tvb, offset + 6, length - 6,
289 "Assembled metric");
290 PROTO_ITEM_SET_GENERATED (root_item);
292 subtree = proto_item_add_subtree (root_item, ett_collectd_dispatch);
294 proto_tree_add_string (subtree, hf_collectd_data_host, tvb,
295 vdispatch->host_off, vdispatch->host_len,
296 STR_NONNULL (vdispatch->host));
298 proto_tree_add_string (subtree, hf_collectd_data_plugin, tvb,
299 vdispatch->plugin_off, vdispatch->plugin_len,
300 STR_NONNULL (vdispatch->plugin));
302 if (vdispatch->plugin_instance)
303 proto_tree_add_string (subtree,
304 hf_collectd_data_plugin_inst, tvb,
305 vdispatch->plugin_instance_off,
306 vdispatch->plugin_instance_len,
307 vdispatch->plugin_instance);
309 proto_tree_add_string (subtree, hf_collectd_data_type, tvb,
310 vdispatch->type_off, vdispatch->type_len,
311 STR_NONNULL (vdispatch->type));
313 if (vdispatch->type_instance)
314 proto_tree_add_string (subtree,
315 hf_collectd_data_type_inst, tvb,
316 vdispatch->type_instance_off,
317 vdispatch->type_instance_len,
318 vdispatch->type_instance);
320 nstime = collectd_time_to_nstime (vdispatch->time);
321 proto_tree_add_time (subtree, hf_collectd_data_time, tvb,
322 vdispatch->time_off, /* length = */ 8, &nstime);
324 nstime = collectd_time_to_nstime (vdispatch->interval);
325 proto_tree_add_time (subtree, hf_collectd_data_interval, tvb,
326 vdispatch->interval_off, /* length = */ 8, &nstime);
329 static void
330 collectd_proto_tree_add_assembled_notification (tvbuff_t *tvb,
331 gint offset, gint length,
332 notify_data_t const *ndispatch, proto_tree *root)
334 proto_item *root_item;
335 proto_tree *subtree;
336 nstime_t nstime;
338 root_item = proto_tree_add_text (root, tvb, offset + 6, length - 6,
339 "Assembled notification");
340 PROTO_ITEM_SET_GENERATED (root_item);
342 subtree = proto_item_add_subtree (root_item, ett_collectd_dispatch);
344 proto_tree_add_string (subtree, hf_collectd_data_host, tvb,
345 ndispatch->host_off, ndispatch->host_len,
346 STR_NONNULL (ndispatch->host));
348 nstime = collectd_time_to_nstime (ndispatch->time);
349 proto_tree_add_time (subtree, hf_collectd_data_time, tvb,
350 ndispatch->time_off, /* length = */ 8, &nstime);
352 proto_tree_add_uint64 (subtree, hf_collectd_data_severity, tvb,
353 ndispatch->severity_off, /* length = */ 8,
354 ndispatch->severity);
356 proto_tree_add_string (subtree, hf_collectd_data_message, tvb,
357 ndispatch->message_off, ndispatch->message_len,
358 ndispatch->message);
361 static int
362 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
363 gint offset, gint *ret_offset, gint *ret_length,
364 gchar **ret_string, proto_tree *tree_root,
365 proto_item **ret_item)
367 proto_tree *pt;
368 proto_item *pi;
369 gint type;
370 gint length;
371 gint size;
373 size = tvb_reported_length_remaining (tvb, offset);
374 if (size < 4)
376 /* This should never happen, because `dissect_collectd' checks
377 * for this condition already. */
378 return (-1);
381 type = tvb_get_ntohs(tvb, offset);
382 length = tvb_get_ntohs(tvb, offset + 2);
384 if (length > size)
386 pi = proto_tree_add_text (tree_root, tvb, offset, length,
387 "collectd %s segment: Length = %i <BAD>",
388 val_to_str_const (type, part_names, "UNKNOWN"),
389 length);
390 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
391 "String part with invalid part length: "
392 "Part is longer than rest of package.");
393 return (-1);
396 *ret_offset = offset + 4;
397 *ret_length = length - 4;
399 *ret_string = tvb_get_string (wmem_packet_scope(), tvb, *ret_offset, *ret_length);
401 pi = proto_tree_add_text (tree_root, tvb, offset, length,
402 "collectd %s segment: \"%s\"",
403 val_to_str_const (type, part_names, "UNKNOWN"),
404 *ret_string);
406 if (ret_item != NULL)
407 *ret_item = pi;
409 pt = proto_item_add_subtree (pi, ett_collectd_string);
410 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
411 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
412 proto_tree_add_item (pt, type_hf, tvb, *ret_offset, *ret_length, ENC_ASCII|ENC_NA);
414 return (0);
415 } /* int dissect_collectd_string */
417 static int
418 dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
419 gint offset, gint *ret_offset, guint64 *ret_value,
420 proto_tree *tree_root, proto_item **ret_item)
422 proto_tree *pt;
423 proto_item *pi;
424 gint type;
425 gint length;
426 gint size;
428 size = tvb_reported_length_remaining (tvb, offset);
429 if (size < 4)
431 /* This should never happen, because `dissect_collectd' checks
432 * for this condition already. */
433 return (-1);
436 type = tvb_get_ntohs(tvb, offset);
437 length = tvb_get_ntohs(tvb, offset + 2);
439 if (size < 12)
441 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
442 "collectd %s segment: <BAD>",
443 val_to_str_const (type, part_names, "UNKNOWN"));
445 pt = proto_item_add_subtree (pi, ett_collectd_integer);
446 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
447 type);
448 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
449 length);
450 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
451 "Garbage at end of packet: Length = %i <BAD>",
452 size - 4);
454 return (-1);
457 if (length != 12)
459 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
460 "collectd %s segment: <BAD>",
461 val_to_str_const (type, part_names, "UNKNOWN"));
463 pt = proto_item_add_subtree (pi, ett_collectd_integer);
464 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2,
465 type);
466 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
467 offset + 2, 2, length);
468 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
469 "Invalid length field for an integer part.");
471 return (-1);
474 *ret_offset = offset + 4;
475 *ret_value = tvb_get_ntoh64 (tvb, offset + 4);
477 /* Convert the version 4.* time format to the version 5.* time format. */
478 if ((type == TYPE_TIME) || (type == TYPE_INTERVAL))
479 *ret_value *= 1073741824;
481 /* Create an entry in the protocol tree for this part. The value is
482 * printed depending on the "type" variable: TIME{,_HR} as absolute
483 * time, INTERVAL{,_HR} as relative time, uint64 otherwise. */
484 if ((type == TYPE_TIME) || (type == TYPE_TIME_HR))
486 nstime_t nstime;
487 gchar *strtime;
489 nstime = collectd_time_to_nstime (*ret_value);
490 strtime = abs_time_to_str (&nstime, ABSOLUTE_TIME_LOCAL, /* show_zone = */ TRUE);
491 pi = proto_tree_add_text (tree_root, tvb, offset, length,
492 "collectd %s segment: %s",
493 val_to_str_const (type, part_names, "UNKNOWN"),
494 STR_NONNULL (strtime));
496 else if ((type == TYPE_INTERVAL) || (type == TYPE_INTERVAL_HR))
498 nstime_t nstime;
499 gchar *strtime;
501 nstime = collectd_time_to_nstime (*ret_value);
502 strtime = rel_time_to_str (&nstime);
503 pi = proto_tree_add_text (tree_root, tvb, offset, length,
504 "collectd %s segment: %s",
505 val_to_str_const (type, part_names, "UNKNOWN"),
506 strtime);
508 else
510 pi = proto_tree_add_text (tree_root, tvb, offset, length,
511 "collectd %s segment: %"G_GINT64_MODIFIER"u",
512 val_to_str_const (type, part_names, "UNKNOWN"),
513 *ret_value);
516 if (ret_item != NULL)
517 *ret_item = pi;
519 pt = proto_item_add_subtree (pi, ett_collectd_integer);
520 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
521 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
522 length);
523 if ((type == TYPE_TIME) || (type == TYPE_INTERVAL)
524 || (type == TYPE_TIME_HR) || (type == TYPE_INTERVAL_HR))
526 nstime_t nstime;
528 nstime = collectd_time_to_nstime (*ret_value);
529 proto_tree_add_time (pt, type_hf, tvb, offset + 4, 8, &nstime);
531 else
533 proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, ENC_BIG_ENDIAN);
536 return (0);
537 } /* int dissect_collectd_integer */
539 static void
540 dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
541 proto_tree *collectd_tree)
543 proto_item *pi;
544 proto_tree *values_tree, *value_tree;
545 gint i;
547 pi = proto_tree_add_text (collectd_tree, tvb, msg_off + 6, val_cnt * 9,
548 "%d value%s", val_cnt,
549 plurality (val_cnt, "", "s"));
551 values_tree = proto_item_add_subtree (pi, ett_collectd_value);
553 for (i = 0; i < val_cnt; i++)
555 gint value_offset;
557 gint value_type_offset;
558 guint8 value_type;
560 /* Calculate the offsets of the type byte and the actual value. */
561 value_offset = msg_off + 6
562 + val_cnt /* value types */
563 + (i * 8); /* previous values */
565 value_type_offset = msg_off + 6 + i;
566 value_type = tvb_get_guint8 (tvb, value_type_offset);
568 switch (value_type) {
569 case TYPE_VALUE_COUNTER:
571 guint64 val64;
573 val64 = tvb_get_ntoh64 (tvb, value_offset);
574 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
575 val_cnt * 9,
576 "Counter: %"G_GINT64_MODIFIER"u", val64);
578 value_tree = proto_item_add_subtree (pi,
579 ett_collectd_valinfo);
580 proto_tree_add_item (value_tree, hf_collectd_val_type,
581 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
582 proto_tree_add_item (value_tree,
583 hf_collectd_val_counter, tvb,
584 value_offset, 8, ENC_BIG_ENDIAN);
585 break;
588 case TYPE_VALUE_GAUGE:
590 gdouble val;
592 val = tvb_get_letohieee_double (tvb, value_offset);
593 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
594 val_cnt * 9,
595 "Gauge: %g", val);
597 value_tree = proto_item_add_subtree (pi,
598 ett_collectd_valinfo);
599 proto_tree_add_item (value_tree, hf_collectd_val_type,
600 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
601 /* Set the `little endian' flag to TRUE here, because
602 * collectd stores doubles in x86 representation. */
603 proto_tree_add_item (value_tree, hf_collectd_val_gauge,
604 tvb, value_offset, 8, ENC_LITTLE_ENDIAN);
605 break;
608 case TYPE_VALUE_DERIVE:
610 gint64 val64;
612 val64 = tvb_get_ntoh64 (tvb, value_offset);
613 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
614 val_cnt * 9,
615 "Derive: %"G_GINT64_MODIFIER"i", val64);
617 value_tree = proto_item_add_subtree (pi,
618 ett_collectd_valinfo);
619 proto_tree_add_item (value_tree, hf_collectd_val_type,
620 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
621 proto_tree_add_item (value_tree,
622 hf_collectd_val_derive, tvb,
623 value_offset, 8, ENC_BIG_ENDIAN);
624 break;
627 case TYPE_VALUE_ABSOLUTE:
629 guint64 val64;
631 val64 = tvb_get_ntoh64 (tvb, value_offset);
632 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
633 val_cnt * 9,
634 "Absolute: %"G_GINT64_MODIFIER"u", val64);
636 value_tree = proto_item_add_subtree (pi,
637 ett_collectd_valinfo);
638 proto_tree_add_item (value_tree, hf_collectd_val_type,
639 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
640 proto_tree_add_item (value_tree,
641 hf_collectd_val_absolute, tvb,
642 value_offset, 8, ENC_BIG_ENDIAN);
643 break;
646 default:
648 guint64 val64;
650 val64 = tvb_get_ntoh64 (tvb, value_offset);
651 pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
652 val_cnt * 9,
653 "Unknown: %"G_GINT64_MODIFIER"x",
654 val64);
656 value_tree = proto_item_add_subtree (pi,
657 ett_collectd_valinfo);
658 proto_tree_add_item (value_tree, hf_collectd_val_type,
659 tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
660 proto_tree_add_item (value_tree, hf_collectd_val_unknown,
661 tvb, value_offset, 8, ENC_BIG_ENDIAN);
662 break;
664 } /* switch (value_type) */
665 } /* for (i = 0; i < val_cnt; i++) */
666 } /* void dissect_collectd_values */
668 static int
669 dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
670 value_data_t *vdispatch, proto_tree *tree_root)
672 proto_tree *pt;
673 proto_item *pi;
674 gint type;
675 gint length;
676 gint size;
677 gint values_count;
678 gint corrected_values_count;
680 size = tvb_reported_length_remaining (tvb, offset);
681 if (size < 4)
683 /* This should never happen, because `dissect_collectd' checks
684 * for this condition already. */
685 return (-1);
688 type = tvb_get_ntohs (tvb, offset);
689 length = tvb_get_ntohs (tvb, offset + 2);
691 if (size < 15)
693 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
694 "collectd %s segment: <BAD>",
695 val_to_str_const (type, part_names, "UNKNOWN"));
697 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
698 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
699 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
700 length);
701 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
702 "Garbage at end of packet: Length = %i <BAD>",
703 size - 4);
704 return (-1);
707 if ((length < 15) || ((length % 9) != 6))
709 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
710 "collectd %s segment: <BAD>",
711 val_to_str_const (type, part_names, "UNKNOWN"));
713 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
714 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
715 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
716 offset + 2, 2, length);
717 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
718 "Invalid length field for a values part.");
720 return (-1);
723 values_count = tvb_get_ntohs (tvb, offset + 4);
724 corrected_values_count = (length - 6) / 9;
726 if (values_count != corrected_values_count)
728 pi = proto_tree_add_text (tree_root, tvb, offset, length,
729 "collectd %s segment: %d (%d) value%s <BAD>",
730 val_to_str_const (type, part_names, "UNKNOWN"),
731 values_count, corrected_values_count,
732 plurality(values_count, "", "s"));
734 else
736 pi = proto_tree_add_text (tree_root, tvb, offset, length,
737 "collectd %s segment: %d value%s",
738 val_to_str_const (type, part_names, "UNKNOWN"),
739 values_count,
740 plurality(values_count, "", "s"));
743 pt = proto_item_add_subtree (pi, ett_collectd_part_value);
744 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
745 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
747 pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb,
748 offset + 4, 2, ENC_BIG_ENDIAN);
749 if (values_count != corrected_values_count)
750 expert_add_info(pinfo, pi, &ei_collectd_data_valcnt);
752 values_count = corrected_values_count;
754 dissect_collectd_values (tvb, offset, values_count, pt);
755 collectd_proto_tree_add_assembled_metric (tvb, offset + 6, length - 6,
756 vdispatch, pt);
758 return (0);
759 } /* void dissect_collectd_part_values */
761 static int
762 dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
763 gint offset, proto_tree *tree_root)
765 proto_item *pi;
766 proto_tree *pt;
767 gint type;
768 gint length;
769 gint size;
771 size = tvb_reported_length_remaining (tvb, offset);
772 if (size < 4)
774 /* This should never happen, because `dissect_collectd' checks
775 * for this condition already. */
776 return (-1);
779 type = tvb_get_ntohs (tvb, offset);
780 length = tvb_get_ntohs (tvb, offset + 2);
782 if (size < 36) /* remaining packet size too small for signature */
784 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
785 "collectd %s segment: <BAD>",
786 val_to_str_const (type, part_names, "UNKNOWN"));
788 pt = proto_item_add_subtree (pi, ett_collectd_signature);
789 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
790 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
791 length);
792 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
793 "Garbage at end of packet: Length = %i <BAD>",
794 size - 4);
795 return (-1);
798 if (length < 36)
800 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
801 "collectd %s segment: <BAD>",
802 val_to_str_const (type, part_names, "UNKNOWN"));
804 pt = proto_item_add_subtree (pi, ett_collectd_signature);
805 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
806 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
807 offset + 2, 2, length);
808 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
809 "Invalid length field for a signature part.");
811 return (-1);
814 pi = proto_tree_add_text (tree_root, tvb, offset, length,
815 "collectd %s segment: HMAC-SHA-256",
816 val_to_str_const (type, part_names, "UNKNOWN"));
818 pt = proto_item_add_subtree (pi, ett_collectd_signature);
819 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
820 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2,
821 length);
822 proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, ENC_NA);
823 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, ENC_ASCII|ENC_NA);
825 return (0);
826 } /* int dissect_collectd_signature */
828 static int
829 dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
830 gint offset, proto_tree *tree_root)
832 proto_item *pi;
833 proto_tree *pt;
834 gint type;
835 gint length;
836 gint size;
837 gint username_length;
839 size = tvb_reported_length_remaining (tvb, offset);
840 if (size < 4)
842 /* This should never happen, because `dissect_collectd' checks
843 * for this condition already. */
844 return (-1);
847 type = tvb_get_ntohs (tvb, offset);
848 length = tvb_get_ntohs (tvb, offset + 2);
850 if (size < 42) /* remaining packet size too small for signature */
852 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
853 "collectd %s segment: <BAD>",
854 val_to_str_const (type, part_names, "UNKNOWN"));
856 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
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,
859 length);
860 proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1,
861 "Garbage at end of packet: Length = %i <BAD>",
862 size - 4);
863 return (-1);
866 if (length < 42)
868 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
869 "collectd %s segment: <BAD>",
870 val_to_str_const (type, part_names, "UNKNOWN"));
872 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
873 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
874 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
875 offset + 2, 2, length);
876 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
877 "Invalid length field for an encryption part.");
879 return (-1);
882 username_length = tvb_get_ntohs (tvb, offset + 4);
883 if (username_length > (length - 42))
885 pi = proto_tree_add_text (tree_root, tvb, offset, -1,
886 "collectd %s segment: <BAD>",
887 val_to_str_const (type, part_names, "UNKNOWN"));
889 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
890 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
891 proto_tree_add_uint (pt, hf_collectd_length, tvb,
892 offset + 2, 2, length);
893 pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb,
894 offset + 4, 2, length);
895 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
896 "Invalid username length field for an encryption part.");
898 return (-1);
901 pi = proto_tree_add_text (tree_root, tvb, offset, length,
902 "collectd %s segment: AES-256",
903 val_to_str_const (type, part_names, "UNKNOWN"));
905 pt = proto_item_add_subtree (pi, ett_collectd_encryption);
906 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type);
907 proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length);
908 proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length);
909 proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, ENC_ASCII|ENC_NA);
910 proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
911 offset + (6 + username_length), 16, ENC_NA);
912 proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb,
913 offset + (22 + username_length),
914 length - (22 + username_length), ENC_NA);
916 return (0);
917 } /* int dissect_collectd_encrypted */
919 static int
920 stats_account_string (string_counter_t **ret_list, const gchar *new_value)
922 string_counter_t *entry;
924 if (ret_list == NULL)
925 return (-1);
927 if (new_value == NULL)
928 new_value = "(null)";
930 for (entry = *ret_list; entry != NULL; entry = entry->next)
931 if (strcmp (new_value, entry->string) == 0)
933 entry->count++;
934 return (0);
937 entry = (string_counter_t *)wmem_alloc0 (wmem_packet_scope(), sizeof (*entry));
938 entry->string = wmem_strdup (wmem_packet_scope(), new_value);
939 entry->count = 1;
940 entry->next = *ret_list;
942 *ret_list = entry;
944 return (0);
947 static void
948 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
950 static tap_data_t tap_data;
952 gint offset;
953 gint size;
954 gchar *pkt_host = NULL;
955 gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0;
956 value_data_t vdispatch;
957 notify_data_t ndispatch;
958 int status;
959 proto_item *pi;
960 proto_tree *collectd_tree;
961 proto_tree *pt;
963 memset(&vdispatch, '\0', sizeof(vdispatch));
964 memset(&ndispatch, '\0', sizeof(ndispatch));
966 col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd");
967 col_clear(pinfo->cinfo, COL_INFO);
969 offset = 0;
970 size = tvb_reported_length(tvb);
972 /* create the collectd protocol tree */
973 pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, ENC_NA);
974 collectd_tree = proto_item_add_subtree(pi, ett_collectd);
976 memset (&tap_data, 0, sizeof (tap_data));
978 status = 0;
979 while ((size > 0) && (status == 0))
982 gint part_type;
983 gint part_length;
985 /* Let's handle the easy case first real quick: All we do here
986 * is extract a host name and count the number of values,
987 * plugins and notifications. The payload is not checked at
988 * all, but the same checks are run on the part_length stuff -
989 * it's important to keep an eye on that. */
990 if (!tree)
992 /* Check for garbage at end of packet. */
993 if (size < 4)
995 pkt_errors++;
996 break;
999 part_type = tvb_get_ntohs (tvb, offset);
1000 part_length = tvb_get_ntohs (tvb, offset+2);
1002 /* Check if part_length is in the valid range. */
1003 if ((part_length < 4) || (part_length > size))
1005 pkt_errors++;
1006 break;
1009 switch (part_type) {
1010 case TYPE_HOST:
1011 vdispatch.host = tvb_get_string (wmem_packet_scope(), tvb,
1012 offset + 4, part_length - 4);
1013 if (pkt_host == NULL)
1014 pkt_host = vdispatch.host;
1015 break;
1016 case TYPE_TIME:
1017 case TYPE_TIME_HR:
1018 break;
1019 case TYPE_PLUGIN:
1020 vdispatch.plugin = tvb_get_string (wmem_packet_scope(), tvb,
1021 offset + 4, part_length - 4);
1022 pkt_plugins++;
1023 break;
1024 case TYPE_PLUGIN_INSTANCE:
1025 break;
1026 case TYPE_TYPE:
1027 vdispatch.type = tvb_get_string (wmem_packet_scope(), tvb,
1028 offset + 4, part_length - 4);
1029 break;
1030 case TYPE_TYPE_INSTANCE:
1031 break;
1032 case TYPE_INTERVAL:
1033 case TYPE_INTERVAL_HR:
1034 break;
1035 case TYPE_VALUES:
1037 pkt_values++;
1039 tap_data.values_num++;
1040 stats_account_string (&tap_data.hosts,
1041 vdispatch.host);
1042 stats_account_string (&tap_data.plugins,
1043 vdispatch.plugin);
1044 stats_account_string (&tap_data.types,
1045 vdispatch.type);
1047 break;
1049 case TYPE_MESSAGE:
1050 pkt_messages++;
1051 break;
1052 case TYPE_SEVERITY:
1053 break;
1054 default:
1055 pkt_unknown++;
1058 offset += part_length;
1059 size -= part_length;
1060 continue;
1061 } /* if (!tree) */
1063 /* Now we do the same steps again, but much more thoroughly. */
1065 /* Check if there are at least four bytes left first.
1066 * Four bytes are used to read the type and the length
1067 * of the next part. If there's less, there's some garbage
1068 * at the end of the packet. */
1069 if (size < 4)
1071 proto_tree_add_expert_format(pi, pinfo, &ei_collectd_garbage, tvb,
1072 offset, -1,
1073 "Garbage at end of packet: Length = %i <BAD>",
1074 size);
1075 pkt_errors++;
1076 break;
1079 /* dissect a message entry */
1080 part_type = tvb_get_ntohs (tvb, offset);
1081 part_length = tvb_get_ntohs (tvb, offset + 2);
1083 /* Check if the length of the part is in the valid range. Don't
1084 * confuse this with the above: Here we check the information
1085 * provided in the packet.. */
1086 if ((part_length < 4) || (part_length > size))
1088 pi = proto_tree_add_text (collectd_tree, tvb,
1089 offset, part_length,
1090 "collectd %s segment: Length = %i <BAD>",
1091 val_to_str_const (part_type, part_names, "UNKNOWN"),
1092 part_length);
1094 pt = proto_item_add_subtree (pi, ett_collectd_invalid_length);
1095 proto_tree_add_uint (pt, hf_collectd_type, tvb, offset,
1096 2, part_type);
1097 pi = proto_tree_add_uint (pt, hf_collectd_length, tvb,
1098 offset + 2, 2, part_length);
1100 if (part_length < 4)
1101 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
1102 "Bad part length: Is %i, expected at least 4",
1103 part_length);
1104 else
1105 expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length,
1106 "Bad part length: Larger than remaining packet size.");
1108 pkt_errors++;
1109 break;
1112 /* The header information looks okay, let's tend to the actual
1113 * payload in this part. */
1114 switch (part_type) {
1115 case TYPE_HOST:
1117 status = dissect_collectd_string (tvb, pinfo,
1118 hf_collectd_data_host,
1119 offset,
1120 &vdispatch.host_off,
1121 &vdispatch.host_len,
1122 &vdispatch.host,
1123 collectd_tree, /* item = */ NULL);
1124 if (status != 0)
1125 pkt_errors++;
1126 else
1128 if (pkt_host == NULL)
1129 pkt_host = vdispatch.host;
1130 ndispatch.host_off = vdispatch.host_off;
1131 ndispatch.host_len = vdispatch.host_len;
1132 ndispatch.host = vdispatch.host;
1135 break;
1138 case TYPE_PLUGIN:
1140 status = dissect_collectd_string (tvb, pinfo,
1141 hf_collectd_data_plugin,
1142 offset,
1143 &vdispatch.plugin_off,
1144 &vdispatch.plugin_len,
1145 &vdispatch.plugin,
1146 collectd_tree, /* item = */ NULL);
1147 if (status != 0)
1148 pkt_errors++;
1149 else
1150 pkt_plugins++;
1152 break;
1155 case TYPE_PLUGIN_INSTANCE:
1157 status = dissect_collectd_string (tvb, pinfo,
1158 hf_collectd_data_plugin_inst,
1159 offset,
1160 &vdispatch.plugin_instance_off,
1161 &vdispatch.plugin_instance_len,
1162 &vdispatch.plugin_instance,
1163 collectd_tree, /* item = */ NULL);
1164 if (status != 0)
1165 pkt_errors++;
1167 break;
1170 case TYPE_TYPE:
1172 status = dissect_collectd_string (tvb, pinfo,
1173 hf_collectd_data_type,
1174 offset,
1175 &vdispatch.type_off,
1176 &vdispatch.type_len,
1177 &vdispatch.type,
1178 collectd_tree, /* item = */ NULL);
1179 if (status != 0)
1180 pkt_errors++;
1182 break;
1185 case TYPE_TYPE_INSTANCE:
1187 status = dissect_collectd_string (tvb, pinfo,
1188 hf_collectd_data_type_inst,
1189 offset,
1190 &vdispatch.type_instance_off,
1191 &vdispatch.type_instance_len,
1192 &vdispatch.type_instance,
1193 collectd_tree, /* item = */ NULL);
1194 if (status != 0)
1195 pkt_errors++;
1197 break;
1200 case TYPE_TIME:
1201 case TYPE_TIME_HR:
1203 pi = NULL;
1204 status = dissect_collectd_integer (tvb, pinfo,
1205 hf_collectd_data_time,
1206 offset,
1207 &vdispatch.time_off,
1208 &vdispatch.time,
1209 collectd_tree, &pi);
1210 if (status != 0)
1211 pkt_errors++;
1213 break;
1216 case TYPE_INTERVAL:
1217 case TYPE_INTERVAL_HR:
1219 status = dissect_collectd_integer (tvb, pinfo,
1220 hf_collectd_data_interval,
1221 offset,
1222 &vdispatch.interval_off,
1223 &vdispatch.interval,
1224 collectd_tree, /* item = */ NULL);
1225 if (status != 0)
1226 pkt_errors++;
1228 break;
1231 case TYPE_VALUES:
1233 status = dissect_collectd_part_values (tvb, pinfo,
1234 offset,
1235 &vdispatch,
1236 collectd_tree);
1237 if (status != 0)
1238 pkt_errors++;
1239 else
1240 pkt_values++;
1242 tap_data.values_num++;
1243 stats_account_string (&tap_data.hosts,
1244 vdispatch.host);
1245 stats_account_string (&tap_data.plugins,
1246 vdispatch.plugin);
1247 stats_account_string (&tap_data.types,
1248 vdispatch.type);
1250 break;
1253 case TYPE_MESSAGE:
1255 pi = NULL;
1256 status = dissect_collectd_string (tvb, pinfo,
1257 hf_collectd_data_message,
1258 offset,
1259 &ndispatch.message_off,
1260 &ndispatch.message_len,
1261 &ndispatch.message,
1262 collectd_tree, &pi);
1263 if (status != 0)
1265 pkt_errors++;
1266 break;
1268 pkt_messages++;
1270 pt = proto_item_get_subtree (pi);
1272 collectd_proto_tree_add_assembled_notification (tvb,
1273 offset + 4, part_length - 1,
1274 &ndispatch, pt);
1276 break;
1279 case TYPE_SEVERITY:
1281 pi = NULL;
1282 status = dissect_collectd_integer (tvb, pinfo,
1283 hf_collectd_data_severity,
1284 offset,
1285 &ndispatch.severity_off,
1286 &ndispatch.severity,
1287 collectd_tree, &pi);
1288 if (status != 0)
1289 pkt_errors++;
1290 else
1292 proto_item_set_text (pi,
1293 "collectd SEVERITY segment: "
1294 "%s (%"G_GINT64_MODIFIER"u)",
1295 val64_to_str_const (ndispatch.severity, severity_names, "UNKNOWN"),
1296 ndispatch.severity);
1299 break;
1302 case TYPE_SIGN_SHA256:
1304 status = dissect_collectd_signature (tvb, pinfo,
1305 offset,
1306 collectd_tree);
1307 if (status != 0)
1308 pkt_errors++;
1310 break;
1313 case TYPE_ENCR_AES256:
1315 status = dissect_collectd_encrypted (tvb, pinfo,
1316 offset, collectd_tree);
1317 if (status != 0)
1318 pkt_errors++;
1320 break;
1323 default:
1325 pkt_unknown++;
1326 pi = proto_tree_add_text (collectd_tree, tvb,
1327 offset, part_length,
1328 "collectd %s segment: %i bytes",
1329 val_to_str_const(part_type, part_names, "UNKNOWN"),
1330 part_length);
1332 pt = proto_item_add_subtree(pi, ett_collectd_unknown);
1333 pi = proto_tree_add_uint (pt, hf_collectd_type, tvb,
1334 offset, 2, part_type);
1335 proto_tree_add_uint (pt, hf_collectd_length, tvb,
1336 offset + 2, 2, part_length);
1337 proto_tree_add_item (pt, hf_collectd_data, tvb,
1338 offset + 4, part_length - 4, ENC_NA);
1340 expert_add_info_format(pinfo, pi, &ei_collectd_type,
1341 "Unknown part type %#x. Cannot decode data.",
1342 part_type);
1344 } /* switch (part_type) */
1346 offset += part_length;
1347 size -= part_length;
1348 } /* while ((size > 4) && (status == 0)) */
1350 if (pkt_errors && pkt_unknown)
1351 col_add_fstr (pinfo->cinfo, COL_INFO,
1352 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s",
1353 pkt_host,
1354 pkt_values, plurality (pkt_values, " ", "s"),
1355 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1356 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1357 pkt_unknown,
1358 pkt_errors, plurality (pkt_errors, "", "s"));
1359 else if (pkt_errors)
1360 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s",
1361 pkt_host,
1362 pkt_values, plurality (pkt_values, " ", "s"),
1363 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1364 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1365 pkt_errors, plurality (pkt_errors, "", "s"));
1366 else if (pkt_unknown)
1367 col_add_fstr (pinfo->cinfo, COL_INFO,
1368 "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown",
1369 pkt_host,
1370 pkt_values, plurality (pkt_values, " ", "s"),
1371 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1372 pkt_messages, plurality (pkt_messages, ", ", "s,"),
1373 pkt_unknown);
1374 else
1375 col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s",
1376 pkt_host,
1377 pkt_values, plurality (pkt_values, " ", "s"),
1378 pkt_plugins, plurality (pkt_plugins, ", ", "s,"),
1379 pkt_messages, plurality (pkt_messages, "", "s"));
1381 /* Dispatch tap data. */
1382 tap_queue_packet (tap_collectd, pinfo, &tap_data);
1383 } /* void dissect_collectd */
1385 void proto_register_collectd(void)
1387 module_t *collectd_module;
1388 expert_module_t* expert_collectd;
1390 /* Setup list of header fields */
1391 static hf_register_info hf[] = {
1392 { &hf_collectd_type,
1393 { "Type", "collectd.type", FT_UINT16, BASE_HEX,
1394 VALS(part_names), 0x0, NULL, HFILL }
1396 { &hf_collectd_length,
1397 { "Length", "collectd.len", FT_UINT16, BASE_DEC,
1398 NULL, 0x0, NULL, HFILL }
1400 { &hf_collectd_data,
1401 { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
1402 NULL, 0x0, NULL, HFILL }
1404 { &hf_collectd_data_host,
1405 { "Host name", "collectd.data.host", FT_STRING, BASE_NONE,
1406 NULL, 0x0, NULL, HFILL }
1408 { &hf_collectd_data_interval,
1409 { "Interval", "collectd.data.interval", FT_RELATIVE_TIME, BASE_NONE,
1410 NULL, 0x0, NULL, HFILL }
1412 { &hf_collectd_data_time,
1413 { "Timestamp", "collectd.data.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL,
1414 NULL, 0x0, NULL, HFILL }
1416 { &hf_collectd_data_plugin,
1417 { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE,
1418 NULL, 0x0, NULL, HFILL }
1420 { &hf_collectd_data_plugin_inst,
1421 { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE,
1422 NULL, 0x0, NULL, HFILL }
1424 { &hf_collectd_data_type,
1425 { "Type", "collectd.data.type", FT_STRING, BASE_NONE,
1426 NULL, 0x0, NULL, HFILL }
1428 { &hf_collectd_data_type_inst,
1429 { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE,
1430 NULL, 0x0, NULL, HFILL }
1432 { &hf_collectd_data_valcnt,
1433 { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC,
1434 NULL, 0x0, NULL, HFILL }
1436 { &hf_collectd_val_type,
1437 { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX,
1438 VALS(valuetypenames), 0x0, NULL, HFILL }
1440 { &hf_collectd_val_counter,
1441 { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC,
1442 NULL, 0x0, NULL, HFILL }
1444 { &hf_collectd_val_gauge,
1445 { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE,
1446 NULL, 0x0, NULL, HFILL }
1448 { &hf_collectd_val_derive,
1449 { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC,
1450 NULL, 0x0, NULL, HFILL }
1452 { &hf_collectd_val_absolute,
1453 { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC,
1454 NULL, 0x0, NULL, HFILL }
1456 { &hf_collectd_val_unknown,
1457 { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX,
1458 NULL, 0x0, NULL, HFILL }
1460 { &hf_collectd_data_severity,
1461 { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX | BASE_VAL64_STRING,
1462 VALS(severity_names),
1463 0x0, NULL, HFILL }
1465 { &hf_collectd_data_message,
1466 { "Message", "collectd.data.message", FT_STRING, BASE_NONE,
1467 NULL, 0x0, NULL, HFILL }
1469 { &hf_collectd_data_sighash,
1470 { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
1471 NULL, 0x0, NULL, HFILL }
1473 { &hf_collectd_data_initvec,
1474 { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
1475 NULL, 0x0, NULL, HFILL }
1477 { &hf_collectd_data_username_len,
1478 { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC,
1479 NULL, 0x0, NULL, HFILL }
1481 { &hf_collectd_data_username,
1482 { "Username", "collectd.data.username", FT_STRING, BASE_NONE,
1483 NULL, 0x0, NULL, HFILL }
1485 { &hf_collectd_data_encrypted,
1486 { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
1487 NULL, 0x0, NULL, HFILL }
1491 /* Setup protocol subtree array */
1492 static gint *ett[] = {
1493 &ett_collectd,
1494 &ett_collectd_string,
1495 &ett_collectd_integer,
1496 &ett_collectd_part_value,
1497 &ett_collectd_value,
1498 &ett_collectd_valinfo,
1499 &ett_collectd_signature,
1500 &ett_collectd_encryption,
1501 &ett_collectd_dispatch,
1502 &ett_collectd_invalid_length,
1503 &ett_collectd_unknown,
1506 static ei_register_info ei[] = {
1507 { &ei_collectd_invalid_length, { "collectd.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }},
1508 { &ei_collectd_garbage, { "collectd.garbage", PI_MALFORMED, PI_ERROR, "Garbage at end of packet", EXPFILL }},
1509 { &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 }},
1510 { &ei_collectd_type, { "collectd.type.unknown", PI_UNDECODED, PI_NOTE, "Unknown part type", EXPFILL }},
1513 /* Register the protocol name and description */
1514 proto_collectd = proto_register_protocol("collectd network data", "collectd", "collectd");
1516 /* Required function calls to register the header fields and subtrees used */
1517 proto_register_field_array(proto_collectd, hf, array_length(hf));
1518 proto_register_subtree_array(ett, array_length(ett));
1519 expert_collectd = expert_register_protocol(proto_collectd);
1520 expert_register_field_array(expert_collectd, ei, array_length(ei));
1522 tap_collectd = register_tap ("collectd");
1525 * Create an unsigned integer preference to allow the user to specify the
1526 * UDP port on which to capture DIS packets.
1528 collectd_module = prefs_register_protocol (proto_collectd,
1529 proto_reg_handoff_collectd);
1531 prefs_register_uint_preference (collectd_module, "udp.port",
1532 "collectd UDP port",
1533 "Set the UDP port for collectd messages",
1534 10, &collectd_udp_port);
1535 } /* void proto_register_collectd */
1537 void proto_reg_handoff_collectd (void)
1539 static gboolean first_run = TRUE;
1540 static gint registered_udp_port = -1;
1541 static dissector_handle_t collectd_handle;
1543 if (first_run)
1544 collectd_handle = create_dissector_handle (dissect_collectd,
1545 proto_collectd);
1547 /* Change the dissector registration if the preferences have been
1548 * changed. */
1549 if (registered_udp_port != -1)
1550 dissector_delete_uint ("udp.port", registered_udp_port,
1551 collectd_handle);
1553 dissector_add_uint ("udp.port", collectd_udp_port, collectd_handle);
1554 registered_udp_port = collectd_udp_port;
1556 if (first_run)
1557 collectd_stats_tree_register ();
1559 first_run = FALSE;
1560 } /* void proto_reg_handoff_collectd */