diff options
author | Jeff Morriss <jeff.morriss@ulticom.com> | 2009-09-14 01:36:26 +0000 |
---|---|---|
committer | Jeff Morriss <jeff.morriss@ulticom.com> | 2009-09-14 01:36:26 +0000 |
commit | d642dc9f41fc7617de64e3d1199a9341a800b3fd (patch) | |
tree | ba1ad31cb703b1850005b67a6056fbec702cb60b /epan/dissectors/packet-collectd.c | |
parent | 9accb15581073a3329dbcbdc49db2bb27f9f85fd (diff) |
From Bruno and Florian Forster via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=3170
This patch adds support to Wireshark for dissecting UDP packets used by
collectd's network plugin in order to transmit data from ones host to another
host (e.g. centralized storage of statistics while data is collectd on
individual systems)
The current dissector understands the part types supported by collectd-4.5
series and gracefully processes future part types (flagging them as unknown).
In regard to protocol errors or bad packets checks are based on the various
length fields used, parts are marked with warning when length is unexpected;
marked with error when length breaks minimal rules.
svn path=/trunk/; revision=29887
Diffstat (limited to 'epan/dissectors/packet-collectd.c')
-rw-r--r-- | epan/dissectors/packet-collectd.c | 1038 |
1 files changed, 1038 insertions, 0 deletions
diff --git a/epan/dissectors/packet-collectd.c b/epan/dissectors/packet-collectd.c new file mode 100644 index 0000000000..35d7a85c62 --- /dev/null +++ b/epan/dissectors/packet-collectd.c @@ -0,0 +1,1038 @@ +/* packet-collectd.c + * Routines for collectd (http://collectd.org/) network plugin dissection + * + * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org> + * Copyright 2009 Florian Forster <octo at verplant.org> + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* #include <stdio.h> */ +#include <string.h> +#include <glib.h> +#include <epan/packet.h> +#include <epan/prefs.h> +#include <epan/expert.h> + +#define TYPE_HOST 0x0000 +#define TYPE_TIME 0x0001 +#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_MESSAGE 0x0100 +#define TYPE_SEVERITY 0x0101 + +typedef struct value_data_s { + gchar *host; + gint host_off; + gint host_len; + guint64 time; + gchar *time_str; + gint time_off; + guint64 interval; + gint interval_off; + gchar *plugin; + gint plugin_off; + gint plugin_len; + gchar *plugin_instance; + gint plugin_instance_off; + gint plugin_instance_len; + gchar *type; + gint type_off; + gint type_len; + gchar *type_instance; + gint type_instance_off; + gint type_instance_len; +} value_data_t; + +typedef struct notify_data_s { + gchar *host; + gint host_off; + gint host_len; + guint64 time; + gchar *time_str; + gint time_off; + guint64 severity; + gint severity_off; + gchar *message; + gint message_off; + gint message_len; +} notify_data_t; + +static const value_string part_names[] = { + { TYPE_VALUES, "VALUES" }, + { TYPE_TIME, "TIME" }, + { TYPE_INTERVAL, "INTERVAL" }, + { 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" }, + { 0, NULL } +}; + +#define TYPE_VALUE_COUNTER 0x00 +#define TYPE_VALUE_GAUGE 0x01 +static const value_string valuetypenames[] = { + { TYPE_VALUE_COUNTER, "COUNTER" }, + { TYPE_VALUE_GAUGE, "GAUGE" }, + { 0, NULL } +}; + +#define SEVERITY_FAILURE 0x01 +#define SEVERITY_WARNING 0x02 +#define SEVERITY_OKAY 0x04 +static const value_string severity_names[] = { + { SEVERITY_FAILURE, "FAILURE" }, + { SEVERITY_WARNING, "WARNING" }, + { SEVERITY_OKAY, "OKAY" }, + { 0, NULL } +}; + +#define UDP_PORT_COLLECTD 25826 +static gint collectd_udp_port = UDP_PORT_COLLECTD; + +static gint proto_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_unknown = -1; +static gint hf_collectd_data_severity = -1; +static gint hf_collectd_data_message = -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_dispatch = -1; +static gint ett_collectd_invalid_length = -1; +static gint ett_collectd_unknown = -1; + +/* Prototype for the handoff function */ +void proto_reg_handoff_collectd (void); + +static int +dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf, + gint offset, gint *ret_offset, gint *ret_length, + gchar **ret_string, proto_tree *tree_root, + proto_item **ret_item) +{ + proto_tree *pt; + proto_item *pi; + gint type; + gint length; + gint size; + + size = tvb_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 (length > size) + { + pi = proto_tree_add_protocol_format (tree_root, + proto_collectd, tvb, + offset, length, + "collectd %s segment: Length = %i <BAD>", + val_to_str (type, part_names, "UNKNOWN"), + length); + expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR, + "String part with invalid part length: " + "Part is longer than rest of package."); + return (-1); + } + + *ret_offset = offset + 4; + *ret_length = length - 4; + + *ret_string = tvb_get_ephemeral_string (tvb, *ret_offset, *ret_length); + + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, tvb, + offset, length, + "collectd %s segment: \"%s\"", + val_to_str (type, part_names, "UNKNOWN"), + *ret_string); + + if (ret_item != NULL) + *ret_item = pi; + + pt = proto_item_add_subtree (pi, ett_collectd_string); + 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, type_hf, tvb, *ret_offset, *ret_length, FALSE); + + 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_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) + { + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, + tvb, offset, -1, "collectd %s segment: <BAD>", + val_to_str (type, part_names, "UNKNOWN")); + + pt = proto_item_add_subtree (pi, ett_collectd_integer); + 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_protocol_format (pt, proto_collectd, + tvb, offset + 4, -1, + "Garbage at end of packet: Length = %i <BAD>", + size - 4); + expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR, + "Garbage at end of packet"); + + return (-1); + } + + if (length != 12) + { + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, + tvb, offset, -1, "collectd %s segment: <BAD>", + val_to_str (type, part_names, "UNKNOWN")); + + pt = proto_item_add_subtree (pi, ett_collectd_integer); + 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, PI_MALFORMED, PI_ERROR, + "Invalid length field for an integer part."); + + return (-1); + } + + *ret_offset = offset + 4; + *ret_value = tvb_get_ntoh64 (tvb, offset + 4); + + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, tvb, + offset, length, + "collectd %s segment: %"G_GINT64_MODIFIER"u", + val_to_str (type, part_names, "UNKNOWN"), + *ret_value); + if (ret_item != NULL) + *ret_item = pi; + + pt = proto_item_add_subtree (pi, ett_collectd_integer); + 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, type_hf, tvb, offset + 4, 8, FALSE); + + 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_item *pi; + proto_tree *values_tree, *value_tree; + gint i; + + pi = proto_tree_add_text (collectd_tree, tvb, msg_off + 6, val_cnt * 9, + "%d value%s", val_cnt, + plurality (val_cnt, "", "s")); + + values_tree = proto_item_add_subtree (pi, ett_collectd_value); + + 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); + pi = proto_tree_add_text (values_tree, tvb, msg_off + 6, + val_cnt * 9, + "Counter: %"G_GINT64_MODIFIER"u", val64); + + value_tree = proto_item_add_subtree (pi, + ett_collectd_valinfo); + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, FALSE); + proto_tree_add_item (value_tree, + hf_collectd_val_counter, tvb, + value_offset, 8, FALSE); + break; + } + + case TYPE_VALUE_GAUGE: + { + gdouble val; + + val = tvb_get_letohieee_double (tvb, value_offset); + pi = proto_tree_add_text (values_tree, tvb, msg_off + 6, + val_cnt * 9, "Gauge: %g", val); + + value_tree = proto_item_add_subtree (pi, + ett_collectd_valinfo); + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, FALSE); + /* 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, TRUE); + break; + } + + default: + { + guint64 val64; + + val64 = tvb_get_ntoh64 (tvb, value_offset); + pi = proto_tree_add_text (values_tree, tvb, msg_off + 6, + val_cnt * 9, + "Unknown: %"G_GINT64_MODIFIER"x", + val64); + + value_tree = proto_item_add_subtree (pi, + ett_collectd_valinfo); + proto_tree_add_item (value_tree, hf_collectd_val_type, + tvb, value_type_offset, 1, FALSE); + proto_tree_add_item (value_tree, hf_collectd_val_unknown, + tvb, value_offset, 8, FALSE); + 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_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) + { + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, + tvb, offset, -1, "collectd %s segment: <BAD>", + val_to_str (type, part_names, "UNKNOWN")); + + pt = proto_item_add_subtree (pi, ett_collectd_part_value); + 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_protocol_format (pt, proto_collectd, tvb, + offset + 4, -1, + "Garbage at end of packet: Length = %i <BAD>", + size - 4); + expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_ERROR, + "Garbage at end of packet"); + + return (-1); + } + + if ((length < 15) || ((length % 9) != 6)) + { + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, + tvb, offset, -1, "collectd %s segment: <BAD>", + val_to_str (type, part_names, "UNKNOWN")); + + pt = proto_item_add_subtree (pi, ett_collectd_part_value); + 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, PI_MALFORMED, PI_ERROR, + "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) + { + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, + tvb, offset, length, + "collectd %s segment: %d (%d) value%s <BAD>", + val_to_str (type, part_names, "UNKNOWN"), + values_count, corrected_values_count, + (values_count == 1) ? "" : "s"); + } + else + { + pi = proto_tree_add_protocol_format (tree_root, proto_collectd, + tvb, offset, length, + "collectd %s segment: %d value%s", + val_to_str (type, part_names, "UNKNOWN"), + values_count, + (values_count == 1) ? "" : "s"); + } + + pt = proto_item_add_subtree (pi, ett_collectd_part_value); + 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, FALSE); + if (values_count != corrected_values_count) + expert_add_info_format (pinfo, pi, PI_MALFORMED, PI_WARN, + "Number of values and length of part do not match. " + "Assuming length is correct."); + + values_count = corrected_values_count; + + dissect_collectd_values (tvb, offset, values_count, pt); + + /* tell what would be dispatched... */ + pi = proto_tree_add_text (pt, tvb, offset + 6, length - 6, "Dispatch simulation"); + pt = proto_item_add_subtree(pi, ett_collectd_dispatch); + proto_tree_add_text (pt, tvb, vdispatch->host_off, vdispatch->host_len, + "Host: %s", vdispatch->host); + proto_tree_add_text (pt, tvb, vdispatch->plugin_off, + vdispatch->plugin_len, + "Plugin: %s", vdispatch->plugin); + if (vdispatch->plugin_instance) + proto_tree_add_text (pt, tvb, vdispatch->plugin_instance_off, + vdispatch->plugin_instance_len, + "Plugin instance: %s", vdispatch->plugin_instance); + proto_tree_add_text (pt, tvb, vdispatch->type_off, vdispatch->type_len, + "Type: %s", vdispatch->type); + if (vdispatch->type_instance) + proto_tree_add_text(pt, tvb, vdispatch->type_instance_off, + vdispatch->type_instance_len, + "Type instance: %s", vdispatch->type_instance); + proto_tree_add_text (pt, tvb, vdispatch->time_off, 8, + "Timestamp: %"G_GINT64_MODIFIER"u (%s)", + vdispatch->time, vdispatch->time_str); + proto_tree_add_text (pt, tvb, vdispatch->interval_off, 8, + "Interval: %"G_GINT64_MODIFIER"u", + vdispatch->interval); + return (0); +} /* int dissect_collectd_part_values */ + +static void +dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint offset; + gint size; + gchar *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, FALSE); + collectd_tree = proto_item_add_subtree(pi, ett_collectd); + + 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++; + status = -1; + 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++; + status = -1; + break; + } + + switch (part_type) { + case TYPE_HOST: + if (pkt_host == NULL) + pkt_host = tvb_get_ephemeral_string(tvb, offset+4, part_length-4); + break; + case TYPE_TIME: + break; + case TYPE_PLUGIN: + pkt_plugins++; + break; + case TYPE_PLUGIN_INSTANCE: + break; + case TYPE_TYPE: + break; + case TYPE_TYPE_INSTANCE: + break; + case TYPE_INTERVAL: + break; + case TYPE_VALUES: + pkt_values++; + 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) { + pi = proto_tree_add_protocol_format (collectd_tree, + proto_collectd, tvb, + offset, -1, + "Garbage at end of packet: Length = %i <BAD>", + size); + expert_add_info_format (pinfo, pi, + PI_MALFORMED, PI_ERROR, + "Garbage at end of packet"); + pkt_errors++; + status = -1; + 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)) { + pi = proto_tree_add_protocol_format (collectd_tree, + proto_collectd, tvb, + offset, part_length, + "collectd %s segment: Length = %i <BAD>", + val_to_str (part_type, part_names, "UNKNOWN"), + part_length); + + pt = proto_item_add_subtree (pi, ett_collectd_invalid_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, + PI_MALFORMED, PI_ERROR, + "Bad part length: Is %i, expected at least 4", + part_length); + else + expert_add_info_format (pinfo, pi, + PI_MALFORMED, PI_ERROR, + "Bad part length: Larger than remaining packet size."); + + pkt_errors++; + status = -1; + 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: { + ndispatch.time_str = NULL; + vdispatch.time_str = NULL; + + pi = NULL; + status = dissect_collectd_integer (tvb, pinfo, + hf_collectd_data_time, + offset, + &vdispatch.time_off, + &vdispatch.time, + collectd_tree, &pi); + if (status != 0) + pkt_errors++; + else { + vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time); + + ndispatch.time = vdispatch.time; + ndispatch.time_str = vdispatch.time_str; + + proto_item_set_text (pi, "collectd TIME segment: %"G_GINT64_MODIFIER"u (%s)", + vdispatch.time, vdispatch.time_str); + } + + break; + } + + case TYPE_INTERVAL: { + 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++; + + 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); + + /* tell what would be dispatched... */ + pi = proto_tree_add_text (pt, tvb, offset + 4, + part_length - 4, + "Dispatch simulation"); + pt = proto_item_add_subtree(pi, ett_collectd_dispatch); + proto_tree_add_text (pt, tvb, ndispatch.host_off, + ndispatch.host_len, + "Host: %s", ndispatch.host); + proto_tree_add_text (pt, tvb, ndispatch.time_off, 8, + "Timestamp: %"G_GINT64_MODIFIER"u (%s)", + ndispatch.time, ndispatch.time_str); + proto_tree_add_text (pt, tvb, ndispatch.severity_off, 8, + "Severity: %s (%#"G_GINT64_MODIFIER"x)", + val_to_str(ndispatch.severity, severity_names, "UNKNOWN"), + ndispatch.severity); + proto_tree_add_text (pt, tvb, ndispatch.message_off, + ndispatch.message_len, + "Message: %s", ndispatch.message); + 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 (%"G_GINT64_MODIFIER"u)", + val_to_str (ndispatch.severity, severity_names, "UNKNOWN"), + ndispatch.severity); + } + + break; + } + + default: { + pkt_unknown++; + pi = proto_tree_add_protocol_format (collectd_tree, + proto_collectd, tvb, + offset, part_length, + "collectd %s segment: %i bytes", + val_to_str(part_type, part_names, "UNKNOWN"), + part_length); + + pt = proto_item_add_subtree(pi, ett_collectd_unknown); + 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, FALSE); + + expert_add_info_format (pinfo, pi, + PI_UNDECODED, PI_NOTE, + "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")); +} /* void dissect_collectd */ + +void proto_register_collectd(void) +{ + module_t *collectd_module; + + /* 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_HEX, + 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_UINT64, BASE_DEC, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_time, + { "Timestamp", "collectd.data.time", FT_UINT64, BASE_DEC, + 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_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, + NULL, 0x0, NULL, HFILL } + }, + { &hf_collectd_data_message, + { "Message", "collectd.data.message", FT_STRING, 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_dispatch, + &ett_collectd_invalid_length, + &ett_collectd_unknown, + }; + + /* 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)); + + /* + * Create an unsigned integer preference to allow the user to specify the + * UDP port on which to capture DIS packets. + */ + collectd_module = prefs_register_protocol (proto_collectd, + proto_reg_handoff_collectd); + + prefs_register_uint_preference (collectd_module, "udp.port", + "collectd UDP port", + "Set the UDP port for collectd messages", + 10, &collectd_udp_port); +} /* void proto_register_collectd */ + +void proto_reg_handoff_collectd (void) +{ + static gboolean first_run = TRUE; + static gint registered_udp_port = -1; + static dissector_handle_t collectd_handle; + + if (first_run) + collectd_handle = create_dissector_handle (dissect_collectd, + proto_collectd); + + /* Change the dissector registration if the preferences have been + * changed. */ + if (registered_udp_port != -1) + dissector_delete ("udp.port", registered_udp_port, + collectd_handle); + + dissector_add ("udp.port", collectd_udp_port, collectd_handle); + registered_udp_port = collectd_udp_port; + + first_run = FALSE; +} /* void proto_reg_handoff_collectd */ |