For proto_tree_add_item(..., proto_xxx, ...)use ENC_NA as the encoding arg.
[obnox/wireshark/wip.git] / epan / dissectors / packet-collectd.c
index b4f41719dddb00010daf2e16b584e2515ac95d23..741e79d49ab6bd0d403f41bd325145f0012b46c2 100644 (file)
 # 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>
+#include <epan/stats_tree.h>
 
 #define TYPE_HOST            0x0000
 #define TYPE_TIME            0x0001
@@ -86,6 +86,23 @@ typedef struct notify_data_s {
        gint message_len;
 } notify_data_t;
 
+struct string_counter_s;
+typedef struct string_counter_s string_counter_t;
+struct string_counter_s
+{
+       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" },
@@ -104,9 +121,13 @@ static const value_string part_names[] = {
 
 #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 }
 };
 
@@ -121,9 +142,10 @@ static const value_string severity_names[] = {
 };
 
 #define UDP_PORT_COLLECTD 25826
-static gint collectd_udp_port = UDP_PORT_COLLECTD;
+static guint collectd_udp_port = UDP_PORT_COLLECTD;
 
 static gint proto_collectd             = -1;
+static gint tap_collectd                = -1;
 
 static gint hf_collectd_type           = -1;
 static gint hf_collectd_length         = -1;
@@ -139,6 +161,8 @@ 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;
@@ -160,9 +184,78 @@ 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;
+
 /* Prototype for the handoff function */
 void proto_reg_handoff_collectd (void);
 
+static void
+collectd_stats_tree_init (stats_tree *st)
+{
+       st_collectd_packets = stats_tree_create_node (st, "Packets", 0, FALSE);
+       st_collectd_values = stats_tree_create_node (st, "Values", 0, 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 int
+collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_,
+                           epan_dissect_t *edt _U_, const void *user_data)
+{
+       const tap_data_t *td;
+       string_counter_t *sc;
+
+       td = user_data;
+       if (td == NULL)
+               return (-1);
+
+       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 (1);
+} /* 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 int
 dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, gint type_hf,
                         gint offset, gint *ret_offset, gint *ret_length,
@@ -339,10 +432,10 @@ dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
                        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);
+                                            tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
                        proto_tree_add_item (value_tree,
                                             hf_collectd_val_counter, tvb,
-                                            value_offset, 8, FALSE);
+                                            value_offset, 8, ENC_BIG_ENDIAN);
                        break;
                }
 
@@ -358,11 +451,49 @@ dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
                        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);
+                                            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, TRUE);
+                                            tvb, value_offset, 8, ENC_LITTLE_ENDIAN);
+                       break;
+               }
+
+               case TYPE_VALUE_DERIVE:
+               {
+                       gint64 val64;
+
+                       val64 = tvb_get_ntoh64 (tvb, value_offset);
+                       pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
+                                                 val_cnt * 9,
+                                                 "Derive: %"G_GINT64_MODIFIER"i", 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, 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);
+                       pi = proto_tree_add_text (values_tree, tvb, msg_off + 6,
+                                                 val_cnt * 9,
+                                                 "Absolute: %"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, ENC_BIG_ENDIAN);
+                       proto_tree_add_item (value_tree,
+                                            hf_collectd_val_absolute, tvb,
+                                            value_offset, 8, ENC_BIG_ENDIAN);
                        break;
                }
 
@@ -379,9 +510,9 @@ dissect_collectd_values(tvbuff_t *tvb, gint msg_off, gint val_cnt,
                        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);
+                                            tvb, value_type_offset, 1, ENC_BIG_ENDIAN);
                        proto_tree_add_item (value_tree, hf_collectd_val_unknown,
-                                            tvb, value_offset, 8, FALSE);
+                                            tvb, value_offset, 8, ENC_BIG_ENDIAN);
                        break;
                }
                } /* switch (value_type) */
@@ -471,7 +602,7 @@ dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
        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);
+                                 offset + 4, 2, ENC_BIG_ENDIAN);
        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. "
@@ -485,23 +616,23 @@ dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, gint offset,
        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);
+                            "Host: %s", vdispatch->host ? vdispatch->host : "(null)");
        proto_tree_add_text (pt, tvb, vdispatch->plugin_off,
                             vdispatch->plugin_len,
-                            "Plugin: %s", vdispatch->plugin);
+                            "Plugin: %s", vdispatch->plugin ? vdispatch->plugin : "(null)");
        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);
+                            "Type: %s", vdispatch->type ? vdispatch->type : "(null)");
        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);
+                            vdispatch->time, vdispatch->time_str ? vdispatch->time_str : "(null)");
        proto_tree_add_text (pt, tvb, vdispatch->interval_off, 8,
                             "Interval: %"G_GINT64_MODIFIER"u",
                             vdispatch->interval);
@@ -572,8 +703,8 @@ dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo,
        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, FALSE);
-       proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 36, length - 36, FALSE);
+       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|ENC_NA);
 
        return (0);
 } /* int dissect_collectd_signature */
@@ -662,19 +793,49 @@ dissect_collectd_encrypted (tvbuff_t *tvb, packet_info *pinfo,
        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, FALSE);
+       proto_tree_add_item (pt, hf_collectd_data_username, tvb, offset + 6, username_length, ENC_ASCII|ENC_NA);
        proto_tree_add_item (pt, hf_collectd_data_initvec, tvb,
-                            offset + (6 + username_length), 16, FALSE);
+                            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), FALSE);
+                            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 = ep_alloc0 (sizeof (*entry));
+       entry->string = ep_strdup (new_value);
+       entry->count = 1;
+       entry->next = *ret_list;
+
+       *ret_list = entry;
+
+       return (0);
+}
+
 static void
 dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
+       static tap_data_t tap_data;
+
        gint offset;
        gint size;
        gchar *pkt_host = NULL;
@@ -696,9 +857,11 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        size = tvb_reported_length(tvb);
 
        /* create the collectd protocol tree */
-       pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, FALSE);
+       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))
        {
@@ -734,25 +897,42 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
                        switch (part_type) {
                        case TYPE_HOST:
+                               vdispatch.host = tvb_get_ephemeral_string (tvb,
+                                               offset + 4, part_length - 4);
                                if (pkt_host == NULL)
-                                       pkt_host = tvb_get_ephemeral_string(tvb, offset+4, part_length-4);
+                                       pkt_host = vdispatch.host;
                                break;
                        case TYPE_TIME:
                                break;
                        case TYPE_PLUGIN:
+                               vdispatch.plugin = tvb_get_ephemeral_string (tvb,
+                                               offset + 4, part_length - 4);
                                pkt_plugins++;
                                break;
                        case TYPE_PLUGIN_INSTANCE:
                                break;
                        case TYPE_TYPE:
+                               vdispatch.type = tvb_get_ephemeral_string (tvb,
+                                               offset + 4, part_length - 4);
                                break;
                        case TYPE_TYPE_INSTANCE:
                                break;
                        case TYPE_INTERVAL:
                                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;
@@ -927,13 +1107,13 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                                pkt_errors++;
                        else
                        {
-                               vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time);
+                               vdispatch.time_str = abs_time_secs_to_str ((time_t) vdispatch.time, ABSOLUTE_TIME_LOCAL, TRUE);
 
                                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);
+                                                    vdispatch.time, vdispatch.time_str ? vdispatch.time_str : "(null)");
                        }
 
                        break;
@@ -964,6 +1144,14 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                        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;
                }
 
@@ -993,10 +1181,10 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                        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);
+                                            "Host: %s", ndispatch.host ? ndispatch.host : "(null)");
                        proto_tree_add_text (pt, tvb, ndispatch.time_off, 8,
                                             "Timestamp: %"G_GINT64_MODIFIER"u (%s)",
-                                            ndispatch.time, ndispatch.time_str);
+                                            ndispatch.time, ndispatch.time_str ? ndispatch.time_str : "(null)");
                        proto_tree_add_text (pt, tvb, ndispatch.severity_off, 8,
                                             "Severity: %s (%#"G_GINT64_MODIFIER"x)",
                                             val_to_str((gint32)ndispatch.severity, severity_names, "UNKNOWN"),
@@ -1066,7 +1254,7 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                        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);
+                                            offset + 4, part_length - 4, ENC_NA);
 
                        expert_add_info_format (pinfo, pi,
                                                PI_UNDECODED, PI_NOTE,
@@ -1109,6 +1297,9 @@ dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                              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);
 } /* void dissect_collectd */
 
 void proto_register_collectd(void)
@@ -1126,7 +1317,7 @@ void proto_register_collectd(void)
                                NULL, 0x0, NULL, HFILL }
                },
                { &hf_collectd_data,
-                       { "Payload", "collectd.data", FT_BYTES, BASE_HEX,
+                       { "Payload", "collectd.data", FT_BYTES, BASE_NONE,
                                NULL, 0x0, NULL, HFILL }
                },
                { &hf_collectd_data_host,
@@ -1173,6 +1364,14 @@ void proto_register_collectd(void)
                        { "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 }
@@ -1186,11 +1385,11 @@ void proto_register_collectd(void)
                                NULL, 0x0, NULL, HFILL }
                },
                { &hf_collectd_data_sighash,
-                       { "Signature", "collectd.data.sighash", FT_BYTES, BASE_HEX,
+                       { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE,
                                NULL, 0x0, NULL, HFILL }
                },
                { &hf_collectd_data_initvec,
-                       { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_HEX,
+                       { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE,
                                NULL, 0x0, NULL, HFILL }
                },
                { &hf_collectd_data_username_len,
@@ -1202,7 +1401,7 @@ void proto_register_collectd(void)
                                NULL, 0x0, NULL, HFILL }
                },
                { &hf_collectd_data_encrypted,
-                       { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_HEX,
+                       { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE,
                                NULL, 0x0, NULL, HFILL }
                },
        };
@@ -1230,6 +1429,8 @@ void proto_register_collectd(void)
        proto_register_field_array(proto_collectd, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
 
+       tap_collectd = register_tap ("collectd");
+
        /*
         * Create an unsigned integer preference to allow the user to specify the
         * UDP port on which to capture DIS packets.
@@ -1256,11 +1457,14 @@ void proto_reg_handoff_collectd (void)
        /* Change the dissector registration if the preferences have been
         * changed. */
        if (registered_udp_port != -1)
-               dissector_delete ("udp.port", registered_udp_port,
+               dissector_delete_uint ("udp.port", registered_udp_port,
                                  collectd_handle);
 
-       dissector_add ("udp.port", collectd_udp_port, collectd_handle);
+       dissector_add_uint ("udp.port", collectd_udp_port, collectd_handle);
        registered_udp_port = collectd_udp_port;
 
+       if (first_run)
+               collectd_stats_tree_register ();
+
        first_run = FALSE;
 } /* void proto_reg_handoff_collectd */