9.7. How to produce protocol statistics (stats)

Given that you have a tap interface for the protocol, you can use this to produce some interesting statistics (well presumably interesting!) from protocol traces.

This can be done in a separate plugin, or in the same plugin that is doing the dissection. The latter scheme is better, as the tap and stats module typically rely on sharing protocol specific data, which might get out of step between two different plugins.

Here is a mechanism to produce statistics from the above TAP interface.

Initialising a stats interface. 

#include <epan/stats_tree.h>

void proto_reg_handoff_foo(void) {
       ...
    stats_tree_register("foo", "foo", "Foo" STATS_TREE_MENU_SEPARATOR "Packet Types", 0,
        foo_stats_tree_packet, foo_stats_tree_init, NULL);
}

The interface entry point, proto_reg_handoff_foo(), calls the stats_tree_register() function, which takes three strings, an integer, and three callback functions:

  1. This is the tap name that was registered using register_tap().
  2. An abbreviation of the stats name.
  3. The name of the stats module. STATS_TREE_MENU_SEPARATOR can be used to make sub menus.
  4. Flags for per-packet callback, taken from epan/stats_tree.h.
  5. The function that will called to generate the stats.
  6. A function that can be called to initialise the stats data.
  7. A function that will be called to clean up the stats data.

In this case we only need the first two functions, as there is nothing specific to clean up.

[Note]Note

If you are registering statistics from a plugin, then your plugin should have a plugin interface entry point called plugin_register_tap_listener(), which should call stats_tree_register_plugin() instead of stats_tree_register().

Initialising a stats session. 

static const uint8_t* st_str_packets = "Total Packets";
static const uint8_t* st_str_packet_types = "FOO Packet Types";
static int st_node_packets = -1;
static int st_node_packet_types = -1;

static void foo_stats_tree_init(stats_tree* st)
{
    st_node_packets = stats_tree_create_node(st, st_str_packets, 0, STAT_DT_INT, true);
    st_node_packet_types = stats_tree_create_pivot(st, st_str_packet_types, st_node_packets);
}

In this case we create a new tree node, to handle the total packets, and as a child of that we create a pivot table to handle the stats about different packet types.

Generating the stats. 

static tap_packet_status foo_stats_tree_packet(stats_tree* st, packet_info* pinfo, epan_dissect_t* edt, const void* p, tap_flags_t flags)
{
    struct FooTap *pi = (struct FooTap *)p;
    tick_stat_node(st, st_str_packets, 0, false);
    stats_tree_tick_pivot(st, st_node_packet_types,
            val_to_str(pi->packet_type, packettypenames, "Unknown packet type (%d)"));
    return TAP_PACKET_REDRAW;
}

In this case the processing of the stats is quite simple. First we call the tick_stat_node for the st_str_packets packet node, to count packets. Then a call to stats_tree_tick_pivot() on the st_node_packet_types subtree allows us to record statistics by packet type.

[Note]Note

Notice that stats trees and pivots are identified by their name string, not by the identifier returned by stats_tree_create_node()/stats_tree_create_pivot().