diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/packet-collectd.c | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-collectd.c')
-rw-r--r-- | epan/dissectors/packet-collectd.c | 1496 |
1 files changed, 1496 insertions, 0 deletions
diff --git a/epan/dissectors/packet-collectd.c b/epan/dissectors/packet-collectd.c new file mode 100644 index 00000000..276ddfc3 --- /dev/null +++ b/epan/dissectors/packet-collectd.c @@ -0,0 +1,1496 @@ +/* packet-collectd.c + * Routines for collectd (http://collectd.org/) network plugin dissection + * + * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org> + * Copyright 2009-2013 Florian Forster <octo at collectd.org> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <epan/packet.h> +#include <epan/expert.h> +#include <epan/stats_tree.h> +#include <epan/to_str.h> + +#include <wsutil/str_util.h> + +#define STR_NONNULL(str) ((str) ? ((const gchar*)str) : "(null)") + +#define TYPE_HOST 0x0000 +#define TYPE_TIME 0x0001 +#define TYPE_TIME_HR 0x0008 +#define TYPE_PLUGIN 0x0002 +#define TYPE_PLUGIN_INSTANCE 0x0003 +#define TYPE_TYPE 0x0004 +#define TYPE_TYPE_INSTANCE 0x0005 +#define TYPE_VALUES 0x0006 +#define TYPE_INTERVAL 0x0007 +#define TYPE_INTERVAL_HR 0x0009 +#define TYPE_MESSAGE 0x0100 +#define TYPE_SEVERITY 0x0101 +#define TYPE_SIGN_SHA256 0x0200 +#define TYPE_ENCR_AES256 0x0210 + +void proto_register_collectd(void); + +static dissector_handle_t collectd_handle; + +typedef struct value_data_s { + const guint8 *host; + gint host_off; + gint host_len; + guint64 time_value; + gint time_off; + guint64 interval; + gint interval_off; + const guint8 *plugin; + gint plugin_off; + gint plugin_len; + const guint8 *plugin_instance; + gint plugin_instance_off; + gint plugin_instance_len; + const guint8 *type; + gint type_off; + gint type_len; + const guint8 *type_instance; + gint type_instance_off; + gint type_instance_len; +} value_data_t; + +typedef struct notify_data_s { + const guint8 *host; + gint host_off; + gint host_len; + guint64 time_value; + gint time_off; + guint64 severity; + gint severity_off; + const guint8 *message; + gint message_off; + gint message_len; +} notify_data_t; + +struct string_counter_s; +typedef struct string_counter_s string_counter_t; +struct string_counter_s +{ + const gchar *string; + gint count; + string_counter_t *next; +}; + +typedef struct tap_data_s { + gint values_num; + + string_counter_t *hosts; + string_counter_t *plugins; + string_counter_t *types; +} tap_data_t; + +static const value_string part_names[] = { + { TYPE_VALUES, "VALUES" }, + { TYPE_TIME, "TIME" }, + { TYPE_TIME_HR, "TIME_HR" }, + { TYPE_INTERVAL, "INTERVAL" }, + { TYPE_INTERVAL_HR, "INTERVAL_HR" }, + { TYPE_HOST, "HOST" }, + { TYPE_PLUGIN, "PLUGIN" }, + { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" }, + { TYPE_TYPE, "TYPE" }, + { TYPE_TYPE_INSTANCE, "TYPE_INSTANCE" }, + { TYPE_MESSAGE, "MESSAGE" }, + { TYPE_SEVERITY, "SEVERITY" }, + { TYPE_SIGN_SHA256, "SIGNATURE" }, + { TYPE_ENCR_AES256, "ENCRYPTED_DATA" }, + { 0, NULL } +}; + +#define TYPE_VALUE_COUNTER 0x00 +#define TYPE_VALUE_GAUGE 0x01 +#define TYPE_VALUE_DERIVE 0x02 +#define TYPE_VALUE_ABSOLUTE 0x03 +static const value_string valuetypenames[] = { + { TYPE_VALUE_COUNTER, "COUNTER" }, + { TYPE_VALUE_GAUGE, "GAUGE" }, + { TYPE_VALUE_DERIVE, "DERIVE" }, + { TYPE_VALUE_ABSOLUTE, "ABSOLUTE" }, + { 0, NULL } +}; + +#define SEVERITY_FAILURE 0x01 +#define SEVERITY_WARNING 0x02 +#define SEVERITY_OKAY 0x04 +static const val64_string severity_names[] = { + { SEVERITY_FAILURE, "FAILURE" }, + { SEVERITY_WARNING, "WARNING" }, + { SEVERITY_OKAY, "OKAY" }, + { 0, NULL } +}; + +#define UDP_PORT_COLLECTD 25826 /* Not IANA registered */ + +static gint proto_collectd = -1; +static gint tap_collectd = -1; + +static gint hf_collectd_type = -1; +static gint hf_collectd_length = -1; +static gint hf_collectd_data = -1; +static gint hf_collectd_data_host = -1; +static gint hf_collectd_data_time = -1; +static gint hf_collectd_data_interval = -1; +static gint hf_collectd_data_plugin = -1; +static gint hf_collectd_data_plugin_inst= -1; +static gint hf_collectd_data_type = -1; +static gint hf_collectd_data_type_inst = -1; +static gint hf_collectd_data_valcnt = -1; +static gint hf_collectd_val_type = -1; +static gint hf_collectd_val_counter = -1; +static gint hf_collectd_val_gauge = -1; +static gint hf_collectd_val_derive = -1; +static gint hf_collectd_val_absolute = -1; +static gint hf_collectd_val_unknown = -1; +static gint hf_collectd_data_severity = -1; +static gint hf_collectd_data_message = -1; +static gint hf_collectd_data_sighash = -1; +static gint hf_collectd_data_initvec = -1; +static gint hf_collectd_data_username_len = -1; +static gint hf_collectd_data_username = -1; +static gint hf_collectd_data_encrypted = -1; + +static gint ett_collectd = -1; +static gint ett_collectd_string = -1; +static gint ett_collectd_integer = -1; +static gint ett_collectd_part_value = -1; +static gint ett_collectd_value = -1; +static gint ett_collectd_valinfo = -1; +static gint ett_collectd_signature = -1; +static gint ett_collectd_encryption = -1; +static gint ett_collectd_dispatch = -1; +static gint ett_collectd_invalid_length = -1; +static gint ett_collectd_unknown = -1; + +static gint st_collectd_packets = -1; +static gint st_collectd_values = -1; +static gint st_collectd_values_hosts = -1; +static gint st_collectd_values_plugins = -1; +static gint st_collectd_values_types = -1; + +static expert_field ei_collectd_type = EI_INIT; +static expert_field ei_collectd_invalid_length = EI_INIT; +static expert_field ei_collectd_data_valcnt = EI_INIT; +static expert_field ei_collectd_garbage = EI_INIT; + +/* Prototype for the handoff function */ +void proto_reg_handoff_collectd (void); + +static nstime_t +collectd_time_to_nstime (guint64 t) +{ + nstime_t nstime = NSTIME_INIT_ZERO;; + nstime.secs = (time_t) (t / 1073741824); + nstime.nsecs = (int) (((double) (t % 1073741824)) / 1.073741824); + + return (nstime); +} + +static void +collectd_stats_tree_init (stats_tree *st) +{ + st_collectd_packets = stats_tree_create_node (st, "Packets", 0, STAT_DT_INT, FALSE); + st_collectd_values = stats_tree_create_node (st, "Values", 0, STAT_DT_INT, TRUE); + + st_collectd_values_hosts = stats_tree_create_pivot (st, "By host", + st_collectd_values); + st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin", + st_collectd_values); + st_collectd_values_types = stats_tree_create_pivot (st, "By type", + st_collectd_values); +} /* void collectd_stats_tree_init */ + +static tap_packet_status +collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_, + epan_dissect_t *edt _U_, const void *user_data, tap_flags_t flags _U_) +{ + const tap_data_t *td; + string_counter_t *sc; + + td = (const tap_data_t *)user_data; + if (td == NULL) + return (TAP_PACKET_DONT_REDRAW); + + tick_stat_node (st, "Packets", 0, FALSE); + increase_stat_node (st, "Values", 0, TRUE, td->values_num); + + for (sc = td->hosts; sc != NULL; sc = sc->next) + { + gint i; + for (i = 0; i < sc->count; i++) + stats_tree_tick_pivot (st, st_collectd_values_hosts, + sc->string); + } + + for (sc = td->plugins; sc != NULL; sc = sc->next) + { + gint i; + for (i = 0; i < sc->count; i++) + stats_tree_tick_pivot (st, st_collectd_values_plugins, + sc->string); + } + + for (sc = td->types; sc != NULL; sc = sc->next) + { + gint i; + for (i = 0; i < sc->count; i++) + stats_tree_tick_pivot (st, st_collectd_values_types, + sc->string); + } + + return (TAP_PACKET_REDRAW); +} /* int collectd_stats_tree_packet */ + +static void +collectd_stats_tree_register (void) +{ + stats_tree_register ("collectd", "collectd", "Collectd", 0, + collectd_stats_tree_packet, + collectd_stats_tree_init, NULL); +} /* void register_collectd_stat_trees */ + +static void +collectd_proto_tree_add_assembled_metric (tvbuff_t *tvb, + gint offset, gint length, + value_data_t const *vdispatch, proto_tree *root) +{ + proto_item *root_item; + proto_tree *subtree; + nstime_t nstime; + + subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6, + ett_collectd_dispatch, &root_item, "Assembled metric"); + proto_item_set_generated (root_item); + + proto_tree_add_string (subtree, hf_collectd_data_host, tvb, + vdispatch->host_off, vdispatch->host_len, + STR_NONNULL (vdispatch->host)); + + proto_tree_add_string (subtree, hf_collectd_data_plugin, tvb, + vdispatch->plugin_off, vdispatch->plugin_len, + STR_NONNULL (vdispatch->plugin)); + + if (vdispatch->plugin_instance) + proto_tree_add_string (subtree, + hf_collectd_data_plugin_inst, tvb, + vdispatch->plugin_instance_off, + vdispatch->plugin_instance_len, + vdispatch->plugin_instance); + + proto_tree_add_string (subtree, hf_collectd_data_type, tvb, + vdispatch->type_off, vdispatch->type_len, + STR_NONNULL (vdispatch->type)); + + if (vdispatch->type_instance) + proto_tree_add_string (subtree, + hf_collectd_data_type_inst, tvb, + vdispatch->type_instance_off, + vdispatch->type_instance_len, + vdispatch->type_instance); + + nstime = collectd_time_to_nstime (vdispatch->time_value); + proto_tree_add_time (subtree, hf_collectd_data_time, tvb, + vdispatch->time_off, /* length = */ 8, &nstime); + + nstime = collectd_time_to_nstime (vdispatch->interval); + proto_tree_add_time (subtree, hf_collectd_data_interval, tvb, + vdispatch->interval_off, /* length = */ 8, &nstime); +} + +static void +collectd_proto_tree_add_assembled_notification (tvbuff_t *tvb, + gint offset, gint length, + notify_data_t const *ndispatch, proto_tree *root) +{ + proto_item *root_item; + proto_tree *subtree; + nstime_t nstime; + + subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6, + ett_collectd_dispatch, &root_item, "Assembled notification"); + proto_item_set_generated (root_item); + + proto_tree_add_string (subtree, hf_collectd_data_host, tvb, + ndispatch->host_off, ndispatch->host_len, + STR_NONNULL (ndispatch->host)); + + nstime = collectd_time_to_nstime (ndispatch->time_value); + proto_tree_add_time (subtree, hf_collectd_data_time, tvb, + ndispatch->time_off, /* length = */ 8, &nstime); + + proto_tree_add_uint64 (subtree, hf_collectd_data_severity, tvb, + ndispatch->severity_off, /* length = */ 8, + ndispatch->severity); + + proto_tree_add_string (subtree, hf_collectd_data_message, tvb, + ndispatch->message_off, ndispatch->message_len, + ndispatch->message); +} + +static int +dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf, + gint offset, gint *ret_offset, gint *ret_length, + const guint8 **ret_string, proto_tree *tree_root, + proto_item **ret_item) +{ + proto_tree *pt; + proto_item *pi; + gint type; + gint length; + gint size; + + size = tvb_reported_length_remaining (tvb, offset); + if (size < 4) + { + /* This should never happen, because `dissect_collectd' checks + * for this condition already. */ + return (-1); + } + + type = tvb_get_ntohs(tvb, offset); + length = tvb_get_ntohs(tvb, offset + 2); + + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_string, &pi, "collectd %s segment: ", + val_to_str_const (type, part_names, "UNKNOWN")); + + if (length > size) + { + proto_item_append_text(pt, "Length = %i <BAD>", length); + expert_add_info_format(pinfo, pt, &ei_collectd_invalid_length, + "String part with invalid part length: " + "Part is longer than rest of package."); + return (-1); + } + + *ret_offset = offset + 4; + *ret_length = length - 4; + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length); + proto_tree_add_item_ret_string (pt, type_hf, tvb, *ret_offset, *ret_length, ENC_ASCII|ENC_NA, wmem_packet_scope(), ret_string); + + proto_item_append_text(pt, "\"%s\"", *ret_string); + + if (ret_item != NULL) + *ret_item = pi; + + return (0); +} /* int dissect_collectd_string */ + +static int +dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, gint type_hf, + gint offset, gint *ret_offset, guint64 *ret_value, + proto_tree *tree_root, proto_item **ret_item) +{ + proto_tree *pt; + proto_item *pi; + gint type; + gint length; + gint size; + + size = tvb_reported_length_remaining (tvb, offset); + if (size < 4) + { + /* This should never happen, because `dissect_collectd' checks + * for this condition already. */ + return (-1); + } + + type = tvb_get_ntohs(tvb, offset); + length = tvb_get_ntohs(tvb, offset + 2); + + if (size < 12) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_integer, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, + type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, + length); + proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, + "Garbage at end of packet: Length = %i <BAD>", + size - 4); + + return (-1); + } + + if (length != 12) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_integer, &pi, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, + type); + pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, length); + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Invalid length field for an integer part."); + + return (-1); + } + + *ret_offset = offset + 4; + *ret_value = tvb_get_ntoh64 (tvb, offset + 4); + + /* Convert the version 4.* time format to the version 5.* time format. */ + if ((type == TYPE_TIME) || (type == TYPE_INTERVAL)) + *ret_value *= 1073741824; + + /* Create an entry in the protocol tree for this part. The value is + * printed depending on the "type" variable: TIME{,_HR} as absolute + * time, INTERVAL{,_HR} as relative time, uint64 otherwise. */ + if ((type == TYPE_TIME) || (type == TYPE_TIME_HR)) + { + nstime_t nstime; + gchar *strtime; + + nstime = collectd_time_to_nstime (*ret_value); + strtime = abs_time_to_str (wmem_packet_scope(), &nstime, ABSOLUTE_TIME_LOCAL, /* show_zone = */ TRUE); + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_integer, &pi, "collectd %s segment: %s", + val_to_str_const (type, part_names, "UNKNOWN"), + STR_NONNULL (strtime)); + } + else if ((type == TYPE_INTERVAL) || (type == TYPE_INTERVAL_HR)) + { + nstime_t nstime; + gchar *strtime; + + nstime = collectd_time_to_nstime (*ret_value); + strtime = rel_time_to_str (wmem_packet_scope(), &nstime); + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_integer, &pi, "collectd %s segment: %s", + val_to_str_const (type, part_names, "UNKNOWN"), + strtime); + } + else + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_integer, &pi, "collectd %s segment: %"PRIu64, + val_to_str_const (type, part_names, "UNKNOWN"), + *ret_value); + } + + if (ret_item != NULL) + *ret_item = pi; + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, + length); + if ((type == TYPE_TIME) || (type == TYPE_INTERVAL) + || (type == TYPE_TIME_HR) || (type == TYPE_INTERVAL_HR)) + { + nstime_t nstime; + + nstime = collectd_time_to_nstime (*ret_value); + proto_tree_add_time (pt, type_hf, tvb, offset + 4, 8, &nstime); + } + else + { + proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, ENC_BIG_ENDIAN); + } + + return (0); +} /* int dissect_collectd_integer */ + +static void +dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt, + proto_tree *collectd_tree) +{ + proto_tree *values_tree, *value_tree; + gint i; + + values_tree = proto_tree_add_subtree_format(collectd_tree, tvb, msg_off + 6, val_cnt * 9, + ett_collectd_value, NULL, "%d value%s", val_cnt, + plurality (val_cnt, "", "s")); + + for (i = 0; i < val_cnt; i++) + { + gint value_offset; + + gint value_type_offset; + guint8 value_type; + + /* Calculate the offsets of the type byte and the actual value. */ + value_offset = msg_off + 6 + + val_cnt /* value types */ + + (i * 8); /* previous values */ + + value_type_offset = msg_off + 6 + i; + value_type = tvb_get_guint8 (tvb, value_type_offset); + + switch (value_type) { + case TYPE_VALUE_COUNTER: + { + guint64 val64; + + val64 = tvb_get_ntoh64 (tvb, value_offset); + value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, + val_cnt * 9, ett_collectd_valinfo, NULL, + "Counter: %"PRIu64, val64); + + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item (value_tree, + hf_collectd_val_counter, tvb, + value_offset, 8, ENC_BIG_ENDIAN); + break; + } + + case TYPE_VALUE_GAUGE: + { + gdouble val; + + val = tvb_get_letohieee_double (tvb, value_offset); + value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, + val_cnt * 9, ett_collectd_valinfo, NULL, + "Gauge: %g", val); + + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, ENC_BIG_ENDIAN); + /* Set the `little endian' flag to TRUE here, because + * collectd stores doubles in x86 representation. */ + proto_tree_add_item (value_tree, hf_collectd_val_gauge, + tvb, value_offset, 8, ENC_LITTLE_ENDIAN); + break; + } + + case TYPE_VALUE_DERIVE: + { + gint64 val64; + + val64 = tvb_get_ntoh64 (tvb, value_offset); + value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, + val_cnt * 9, ett_collectd_valinfo, NULL, + "Derive: %"PRIi64, val64); + + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item (value_tree, + hf_collectd_val_derive, tvb, + value_offset, 8, ENC_BIG_ENDIAN); + break; + } + + case TYPE_VALUE_ABSOLUTE: + { + guint64 val64; + + val64 = tvb_get_ntoh64 (tvb, value_offset); + value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, + val_cnt * 9, ett_collectd_valinfo, NULL, + "Absolute: %"PRIu64, val64); + + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item (value_tree, + hf_collectd_val_absolute, tvb, + value_offset, 8, ENC_BIG_ENDIAN); + break; + } + + default: + { + guint64 val64; + + val64 = tvb_get_ntoh64 (tvb, value_offset); + value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, + val_cnt * 9, ett_collectd_valinfo, NULL, + "Unknown: %"PRIx64, + val64); + + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item (value_tree, hf_collectd_val_unknown, + tvb, value_offset, 8, ENC_BIG_ENDIAN); + break; + } + } /* switch (value_type) */ + } /* for (i = 0; i < val_cnt; i++) */ +} /* void dissect_collectd_values */ + +static int +dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset, + value_data_t *vdispatch, proto_tree *tree_root) +{ + proto_tree *pt; + proto_item *pi; + gint type; + gint length; + gint size; + gint values_count; + gint corrected_values_count; + + size = tvb_reported_length_remaining (tvb, offset); + if (size < 4) + { + /* This should never happen, because `dissect_collectd' checks + * for this condition already. */ + return (-1); + } + + type = tvb_get_ntohs (tvb, offset); + length = tvb_get_ntohs (tvb, offset + 2); + + if (size < 15) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_part_value, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, + length); + proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, + "Garbage at end of packet: Length = %i <BAD>", + size - 4); + return (-1); + } + + if ((length < 15) || ((length % 9) != 6)) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_part_value, &pi, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, length); + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Invalid length field for a values part."); + + return (-1); + } + + values_count = tvb_get_ntohs (tvb, offset + 4); + corrected_values_count = (length - 6) / 9; + + if (values_count != corrected_values_count) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_part_value, NULL, + "collectd %s segment: %d (%d) value%s <BAD>", + val_to_str_const (type, part_names, "UNKNOWN"), + values_count, corrected_values_count, + plurality(values_count, "", "s")); + } + else + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_part_value, NULL, + "collectd %s segment: %d value%s", + val_to_str_const (type, part_names, "UNKNOWN"), + values_count, + plurality(values_count, "", "s")); + } + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length); + + pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb, + offset + 4, 2, ENC_BIG_ENDIAN); + if (values_count != corrected_values_count) + expert_add_info(pinfo, pi, &ei_collectd_data_valcnt); + + values_count = corrected_values_count; + + dissect_collectd_values (tvb, offset, values_count, pt); + collectd_proto_tree_add_assembled_metric (tvb, offset + 6, length - 6, + vdispatch, pt); + + return (0); +} /* void dissect_collectd_part_values */ + +static int +dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo, + gint offset, proto_tree *tree_root) +{ + proto_item *pi; + proto_tree *pt; + gint type; + gint length; + gint size; + + size = tvb_reported_length_remaining (tvb, offset); + if (size < 4) + { + /* This should never happen, because `dissect_collectd' checks + * for this condition already. */ + return (-1); + } + + type = tvb_get_ntohs (tvb, offset); + length = tvb_get_ntohs (tvb, offset + 2); + + if (size < 36) /* remaining packet size too small for signature */ + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_signature, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, + length); + proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, + "Garbage at end of packet: Length = %i <BAD>", + size - 4); + return (-1); + } + + if (length < 36) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_signature, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, length); + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Invalid length field for a signature part."); + + return (-1); + } + + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_signature, NULL, "collectd %s segment: HMAC-SHA-256", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, + length); + proto_tree_add_item (pt, hf_collectd_data_sighash, tvb, offset + 4, 32, ENC_NA); + proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, ENC_ASCII); + + return (0); +} /* int dissect_collectd_signature */ + +static int +dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo, + gint offset, proto_tree *tree_root) +{ + proto_item *pi; + proto_tree *pt; + gint type; + gint length; + gint size; + gint username_length; + + size = tvb_reported_length_remaining (tvb, offset); + if (size < 4) + { + /* This should never happen, because `dissect_collectd' checks + * for this condition already. */ + return (-1); + } + + type = tvb_get_ntohs (tvb, offset); + length = tvb_get_ntohs (tvb, offset + 2); + + if (size < 42) /* remaining packet size too small for signature */ + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_encryption, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, + length); + proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, + "Garbage at end of packet: Length = %i <BAD>", + size - 4); + return (-1); + } + + if (length < 42) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_encryption, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, length); + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Invalid length field for an encryption part."); + + return (-1); + } + + username_length = tvb_get_ntohs (tvb, offset + 4); + if (username_length > (length - 42)) + { + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, + ett_collectd_encryption, NULL, "collectd %s segment: <BAD>", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, length); + pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, + offset + 4, 2, length); + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Invalid username length field for an encryption part."); + + return (-1); + } + + pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, + ett_collectd_encryption, NULL, "collectd %s segment: AES-256", + val_to_str_const (type, part_names, "UNKNOWN")); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length); + proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, offset + 4, 2, username_length); + proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, ENC_ASCII); + proto_tree_add_item (pt, hf_collectd_data_initvec, tvb, + offset + (6 + username_length), 16, ENC_NA); + proto_tree_add_item (pt, hf_collectd_data_encrypted, tvb, + offset + (22 + username_length), + length - (22 + username_length), ENC_NA); + + return (0); +} /* int dissect_collectd_encrypted */ + +static int +stats_account_string (string_counter_t **ret_list, const gchar *new_value) +{ + string_counter_t *entry; + + if (ret_list == NULL) + return (-1); + + if (new_value == NULL) + new_value = "(null)"; + + for (entry = *ret_list; entry != NULL; entry = entry->next) + if (strcmp (new_value, entry->string) == 0) + { + entry->count++; + return (0); + } + + entry = (string_counter_t *)wmem_alloc0 (wmem_packet_scope(), sizeof (*entry)); + entry->string = wmem_strdup (wmem_packet_scope(), new_value); + entry->count = 1; + entry->next = *ret_list; + + *ret_list = entry; + + return (0); +} + +static int +dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + static tap_data_t tap_data; + + gint offset; + gint size; + const guint8 *pkt_host = NULL; + gint pkt_plugins = 0, pkt_values = 0, pkt_messages = 0, pkt_unknown = 0, pkt_errors = 0; + value_data_t vdispatch; + notify_data_t ndispatch; + int status; + proto_item *pi; + proto_tree *collectd_tree; + proto_tree *pt; + + memset(&vdispatch, '\0', sizeof(vdispatch)); + memset(&ndispatch, '\0', sizeof(ndispatch)); + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd"); + col_clear(pinfo->cinfo, COL_INFO); + + offset = 0; + size = tvb_reported_length(tvb); + + /* create the collectd protocol tree */ + pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, ENC_NA); + collectd_tree = proto_item_add_subtree(pi, ett_collectd); + + memset (&tap_data, 0, sizeof (tap_data)); + + status = 0; + while ((size > 0) && (status == 0)) + { + + gint part_type; + gint part_length; + + /* Let's handle the easy case first real quick: All we do here + * is extract a host name and count the number of values, + * plugins and notifications. The payload is not checked at + * all, but the same checks are run on the part_length stuff - + * it's important to keep an eye on that. */ + if (!tree) + { + /* Check for garbage at end of packet. */ + if (size < 4) + { + pkt_errors++; + break; + } + + part_type = tvb_get_ntohs (tvb, offset); + part_length = tvb_get_ntohs (tvb, offset+2); + + /* Check if part_length is in the valid range. */ + if ((part_length < 4) || (part_length > size)) + { + pkt_errors++; + break; + } + + switch (part_type) { + case TYPE_HOST: + vdispatch.host = tvb_get_string_enc(wmem_packet_scope(), tvb, + offset + 4, part_length - 4, ENC_ASCII); + if (pkt_host == NULL) + pkt_host = vdispatch.host; + break; + case TYPE_TIME: + case TYPE_TIME_HR: + break; + case TYPE_PLUGIN: + vdispatch.plugin = tvb_get_string_enc(wmem_packet_scope(), tvb, + offset + 4, part_length - 4, ENC_ASCII); + pkt_plugins++; + break; + case TYPE_PLUGIN_INSTANCE: + break; + case TYPE_TYPE: + vdispatch.type = tvb_get_string_enc(wmem_packet_scope(), tvb, + offset + 4, part_length - 4, ENC_ASCII); + break; + case TYPE_TYPE_INSTANCE: + break; + case TYPE_INTERVAL: + case TYPE_INTERVAL_HR: + break; + case TYPE_VALUES: + { + pkt_values++; + + tap_data.values_num++; + stats_account_string (&tap_data.hosts, + vdispatch.host); + stats_account_string (&tap_data.plugins, + vdispatch.plugin); + stats_account_string (&tap_data.types, + vdispatch.type); + + break; + } + case TYPE_MESSAGE: + pkt_messages++; + break; + case TYPE_SEVERITY: + break; + default: + pkt_unknown++; + } + + offset += part_length; + size -= part_length; + continue; + } /* if (!tree) */ + + /* Now we do the same steps again, but much more thoroughly. */ + + /* Check if there are at least four bytes left first. + * Four bytes are used to read the type and the length + * of the next part. If there's less, there's some garbage + * at the end of the packet. */ + if (size < 4) + { + proto_tree_add_expert_format(pi, pinfo, &ei_collectd_garbage, tvb, + offset, -1, + "Garbage at end of packet: Length = %i <BAD>", + size); + pkt_errors++; + break; + } + + /* dissect a message entry */ + part_type = tvb_get_ntohs (tvb, offset); + part_length = tvb_get_ntohs (tvb, offset + 2); + + /* Check if the length of the part is in the valid range. Don't + * confuse this with the above: Here we check the information + * provided in the packet.. */ + if ((part_length < 4) || (part_length > size)) + { + pt = proto_tree_add_subtree_format(collectd_tree, tvb, + offset, part_length, ett_collectd_invalid_length, NULL, + "collectd %s segment: Length = %i <BAD>", + val_to_str_const (part_type, part_names, "UNKNOWN"), + part_length); + + proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, + 2, part_type); + pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, part_length); + + if (part_length < 4) + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Bad part length: Is %i, expected at least 4", + part_length); + else + expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, + "Bad part length: Larger than remaining packet size."); + + pkt_errors++; + break; + } + + /* The header information looks okay, let's tend to the actual + * payload in this part. */ + switch (part_type) { + case TYPE_HOST: + { + status = dissect_collectd_string (tvb, pinfo, + hf_collectd_data_host, + offset, + &vdispatch.host_off, + &vdispatch.host_len, + &vdispatch.host, + collectd_tree, /* item = */ NULL); + if (status != 0) + pkt_errors++; + else + { + if (pkt_host == NULL) + pkt_host = vdispatch.host; + ndispatch.host_off = vdispatch.host_off; + ndispatch.host_len = vdispatch.host_len; + ndispatch.host = vdispatch.host; + } + + break; + } + + case TYPE_PLUGIN: + { + status = dissect_collectd_string (tvb, pinfo, + hf_collectd_data_plugin, + offset, + &vdispatch.plugin_off, + &vdispatch.plugin_len, + &vdispatch.plugin, + collectd_tree, /* item = */ NULL); + if (status != 0) + pkt_errors++; + else + pkt_plugins++; + + break; + } + + case TYPE_PLUGIN_INSTANCE: + { + status = dissect_collectd_string (tvb, pinfo, + hf_collectd_data_plugin_inst, + offset, + &vdispatch.plugin_instance_off, + &vdispatch.plugin_instance_len, + &vdispatch.plugin_instance, + collectd_tree, /* item = */ NULL); + if (status != 0) + pkt_errors++; + + break; + } + + case TYPE_TYPE: + { + status = dissect_collectd_string (tvb, pinfo, + hf_collectd_data_type, + offset, + &vdispatch.type_off, + &vdispatch.type_len, + &vdispatch.type, + collectd_tree, /* item = */ NULL); + if (status != 0) + pkt_errors++; + + break; + } + + case TYPE_TYPE_INSTANCE: + { + status = dissect_collectd_string (tvb, pinfo, + hf_collectd_data_type_inst, + offset, + &vdispatch.type_instance_off, + &vdispatch.type_instance_len, + &vdispatch.type_instance, + collectd_tree, /* item = */ NULL); + if (status != 0) + pkt_errors++; + + break; + } + + case TYPE_TIME: + case TYPE_TIME_HR: + { + pi = NULL; + status = dissect_collectd_integer (tvb, pinfo, + hf_collectd_data_time, + offset, + &vdispatch.time_off, + &vdispatch.time_value, + collectd_tree, &pi); + if (status != 0) + pkt_errors++; + + break; + } + + case TYPE_INTERVAL: + case TYPE_INTERVAL_HR: + { + status = dissect_collectd_integer (tvb, pinfo, + hf_collectd_data_interval, + offset, + &vdispatch.interval_off, + &vdispatch.interval, + collectd_tree, /* item = */ NULL); + if (status != 0) + pkt_errors++; + + break; + } + + case TYPE_VALUES: + { + status = dissect_collectd_part_values (tvb, pinfo, + offset, + &vdispatch, + collectd_tree); + if (status != 0) + pkt_errors++; + else + pkt_values++; + + tap_data.values_num++; + stats_account_string (&tap_data.hosts, + vdispatch.host); + stats_account_string (&tap_data.plugins, + vdispatch.plugin); + stats_account_string (&tap_data.types, + vdispatch.type); + + break; + } + + case TYPE_MESSAGE: + { + pi = NULL; + status = dissect_collectd_string (tvb, pinfo, + hf_collectd_data_message, + offset, + &ndispatch.message_off, + &ndispatch.message_len, + &ndispatch.message, + collectd_tree, &pi); + if (status != 0) + { + pkt_errors++; + break; + } + pkt_messages++; + + pt = proto_item_get_subtree (pi); + + collectd_proto_tree_add_assembled_notification (tvb, + offset + 4, part_length - 1, + &ndispatch, pt); + + break; + } + + case TYPE_SEVERITY: + { + pi = NULL; + status = dissect_collectd_integer (tvb, pinfo, + hf_collectd_data_severity, + offset, + &ndispatch.severity_off, + &ndispatch.severity, + collectd_tree, &pi); + if (status != 0) + pkt_errors++; + else + { + proto_item_set_text (pi, + "collectd SEVERITY segment: " + "%s (%"PRIu64")", + val64_to_str_const (ndispatch.severity, severity_names, "UNKNOWN"), + ndispatch.severity); + } + + break; + } + + case TYPE_SIGN_SHA256: + { + status = dissect_collectd_signature (tvb, pinfo, + offset, + collectd_tree); + if (status != 0) + pkt_errors++; + + break; + } + + case TYPE_ENCR_AES256: + { + status = dissect_collectd_encrypted (tvb, pinfo, + offset, collectd_tree); + if (status != 0) + pkt_errors++; + + break; + } + + default: + { + pkt_unknown++; + pt = proto_tree_add_subtree_format(collectd_tree, tvb, + offset, part_length, ett_collectd_unknown, NULL, + "collectd %s segment: %i bytes", + val_to_str_const(part_type, part_names, "UNKNOWN"), + part_length); + + pi = proto_tree_add_uint (pt, hf_collectd_type, tvb, + offset, 2, part_type); + proto_tree_add_uint (pt, hf_collectd_length, tvb, + offset + 2, 2, part_length); + proto_tree_add_item (pt, hf_collectd_data, tvb, + offset + 4, part_length - 4, ENC_NA); + + expert_add_info_format(pinfo, pi, &ei_collectd_type, + "Unknown part type %#x. Cannot decode data.", + part_type); + } + } /* switch (part_type) */ + + offset += part_length; + size -= part_length; + } /* while ((size > 4) && (status == 0)) */ + + if (pkt_errors && pkt_unknown) + col_add_fstr (pinfo->cinfo, COL_INFO, + "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown, %d error%s", + pkt_host, + pkt_values, plurality (pkt_values, " ", "s"), + pkt_plugins, plurality (pkt_plugins, ", ", "s,"), + pkt_messages, plurality (pkt_messages, ", ", "s,"), + pkt_unknown, + pkt_errors, plurality (pkt_errors, "", "s")); + else if (pkt_errors) + col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s %d error%s", + pkt_host, + pkt_values, plurality (pkt_values, " ", "s"), + pkt_plugins, plurality (pkt_plugins, ", ", "s,"), + pkt_messages, plurality (pkt_messages, ", ", "s,"), + pkt_errors, plurality (pkt_errors, "", "s")); + else if (pkt_unknown) + col_add_fstr (pinfo->cinfo, COL_INFO, + "Host=%s, %2d value%s for %d plugin%s %d message%s %d unknown", + pkt_host, + pkt_values, plurality (pkt_values, " ", "s"), + pkt_plugins, plurality (pkt_plugins, ", ", "s,"), + pkt_messages, plurality (pkt_messages, ", ", "s,"), + pkt_unknown); + else + col_add_fstr (pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s", + pkt_host, + pkt_values, plurality (pkt_values, " ", "s"), + pkt_plugins, plurality (pkt_plugins, ", ", "s,"), + pkt_messages, plurality (pkt_messages, "", "s")); + + /* Dispatch tap data. */ + tap_queue_packet (tap_collectd, pinfo, &tap_data); + return tvb_captured_length(tvb); +} /* void dissect_collectd */ + +void proto_register_collectd(void) +{ + expert_module_t* expert_collectd; + + /* Setup list of header fields */ + static hf_register_info hf[] = { + { &hf_collectd_type, + { "Type", "collectd.type", FT_UINT16, BASE_HEX, + VALS(part_names), 0x0, NULL, HFILL } + }, + { &hf_collectd_length, + { "Length", "collectd.len", FT_UINT16, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data, + { "Payload", "collectd.data", FT_BYTES, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_host, + { "Host name", "collectd.data.host", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_interval, + { "Interval", "collectd.data.interval", FT_RELATIVE_TIME, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_time, + { "Timestamp", "collectd.data.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_plugin, + { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_plugin_inst, + { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_type, + { "Type", "collectd.data.type", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_type_inst, + { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_valcnt, + { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_val_type, + { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX, + VALS(valuetypenames), 0x0, NULL, HFILL } + }, + { &hf_collectd_val_counter, + { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_val_gauge, + { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_val_derive, + { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_val_absolute, + { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_val_unknown, + { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_severity, + { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX | BASE_VAL64_STRING, + VALS64(severity_names), + 0x0, NULL, HFILL } + }, + { &hf_collectd_data_message, + { "Message", "collectd.data.message", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_sighash, + { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_initvec, + { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_username_len, + { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_username, + { "Username", "collectd.data.username", FT_STRING, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_encrypted, + { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE, + NULL, 0x0, NULL, HFILL } + }, + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_collectd, + &ett_collectd_string, + &ett_collectd_integer, + &ett_collectd_part_value, + &ett_collectd_value, + &ett_collectd_valinfo, + &ett_collectd_signature, + &ett_collectd_encryption, + &ett_collectd_dispatch, + &ett_collectd_invalid_length, + &ett_collectd_unknown, + }; + + static ei_register_info ei[] = { + { &ei_collectd_invalid_length, { "collectd.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }}, + { &ei_collectd_garbage, { "collectd.garbage", PI_MALFORMED, PI_ERROR, "Garbage at end of packet", EXPFILL }}, + { &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 }}, + { &ei_collectd_type, { "collectd.type.unknown", PI_UNDECODED, PI_NOTE, "Unknown part type", EXPFILL }}, + }; + + /* Register the protocol name and description */ + proto_collectd = proto_register_protocol("collectd network data", "collectd", "collectd"); + + /* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_collectd, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_collectd = expert_register_protocol(proto_collectd); + expert_register_field_array(expert_collectd, ei, array_length(ei)); + + tap_collectd = register_tap ("collectd"); + + collectd_handle = register_dissector("collectd", dissect_collectd, proto_collectd); +} + +void proto_reg_handoff_collectd (void) +{ + dissector_add_uint_with_preference("udp.port", UDP_PORT_COLLECTD, collectd_handle); + + collectd_stats_tree_register (); +} /* void proto_reg_handoff_collectd */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ |