summaryrefslogtreecommitdiffstats
path: root/ui/proto_hier_stats.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ui/proto_hier_stats.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/ui/proto_hier_stats.c b/ui/proto_hier_stats.c
new file mode 100644
index 00000000..6069b008
--- /dev/null
+++ b/ui/proto_hier_stats.c
@@ -0,0 +1,371 @@
+/* proto_hier_stats.c
+ * Routines for calculating statistics based on protocol.
+ *
+ * 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 <string.h>
+
+#include "file.h"
+#include "frame_tvbuff.h"
+#include "ui/proto_hier_stats.h"
+#include "ui/progress_dlg.h"
+#include "epan/epan_dissect.h"
+#include "epan/proto.h"
+#include <wsutil/ws_assert.h>
+
+/* Update the progress bar this many times when scanning the packet list. */
+#define N_PROGBAR_UPDATES 100
+
+#define STAT_NODE_STATS(n) ((ph_stats_node_t*)(n)->data)
+#define STAT_NODE_HFINFO(n) (STAT_NODE_STATS(n)->hfinfo)
+
+static int pc_proto_id = -1;
+
+ static GNode*
+find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
+{
+ GNode *needle_stat_node, *up_parent_stat_node;
+ header_field_info *hfinfo;
+ ph_stats_node_t *stats;
+
+ /* Look down the tree */
+ needle_stat_node = g_node_first_child(parent_stat_node);
+
+ while (needle_stat_node) {
+ hfinfo = STAT_NODE_HFINFO(needle_stat_node);
+ if (hfinfo && hfinfo->id == needle_hfinfo->id) {
+ return needle_stat_node;
+ }
+ needle_stat_node = g_node_next_sibling(needle_stat_node);
+ }
+
+ /* Look up the tree */
+ up_parent_stat_node = parent_stat_node;
+ while (up_parent_stat_node && up_parent_stat_node->parent)
+ {
+ needle_stat_node = g_node_first_child(up_parent_stat_node->parent);
+ while (needle_stat_node) {
+ hfinfo = STAT_NODE_HFINFO(needle_stat_node);
+ if (hfinfo && hfinfo->id == needle_hfinfo->id) {
+ return needle_stat_node;
+ }
+ needle_stat_node = g_node_next_sibling(needle_stat_node);
+ }
+
+ up_parent_stat_node = up_parent_stat_node->parent;
+ }
+
+ /* None found. Create one. */
+ stats = g_new(ph_stats_node_t, 1);
+
+ /* Initialize counters */
+ stats->hfinfo = needle_hfinfo;
+ stats->num_pkts_total = 0;
+ stats->num_pdus_total = 0;
+ stats->num_pkts_last = 0;
+ stats->num_bytes_total = 0;
+ stats->num_bytes_last = 0;
+ stats->last_pkt = 0;
+
+ needle_stat_node = g_node_new(stats);
+ g_node_append(parent_stat_node, needle_stat_node);
+ return needle_stat_node;
+}
+
+
+ static void
+process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps)
+{
+ field_info *finfo;
+ ph_stats_node_t *stats;
+ proto_node *proto_sibling_node;
+ GNode *stat_node;
+
+ finfo = PNODE_FINFO(ptree_node);
+ /* We don't fake protocol nodes we expect them to have a field_info.
+ * Even with a faked proto tree, we don't fake nodes when PTREE_FINFO(tree)
+ * is NULL in order to avoid crashes here and elsewhere. (See epan/proto.c)
+ */
+ ws_assert(finfo);
+
+ stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
+
+ stats = STAT_NODE_STATS(stat_node);
+ /* Only increment the total packet count once per packet for a given
+ * node, since there could be multiple PDUs in a frame.
+ * (All the other statistics should be incremented every time,
+ * including the count for how often a protocol was the last
+ * protocol in a packet.)
+ */
+ if (stats->last_pkt != ps->tot_packets) {
+ stats->num_pkts_total++;
+ stats->last_pkt = ps->tot_packets;
+ }
+ stats->num_pdus_total++;
+ stats->num_bytes_total += finfo->length + finfo->appendix_length;
+
+ proto_sibling_node = ptree_node->next;
+
+ /* Skip entries that are not protocols, e.g.
+ * toplevel tree item of desegmentation "[Reassembled TCP Segments]")
+ * XXX: We should probably skip PINOs with field_type FT_BYTES too.
+ *
+ * XXX: We look at siblings not children, and thus don't descend into
+ * the tree to pick up embedded protocols not added to the toplevel of
+ * the tree.
+ */
+ while (proto_sibling_node && !proto_registrar_is_protocol(PNODE_FINFO(proto_sibling_node)->hfinfo->id)) {
+ proto_sibling_node = proto_sibling_node->next;
+ }
+
+ if (proto_sibling_node) {
+ process_node(proto_sibling_node, stat_node, ps);
+ } else {
+ stats->num_pkts_last++;
+ stats->num_bytes_last += finfo->length + finfo->appendix_length;
+ }
+}
+
+
+
+ static void
+process_tree(proto_tree *protocol_tree, ph_stats_t* ps)
+{
+ proto_node *ptree_node;
+
+ /*
+ * Skip over non-protocols and comments. (Packet comments are a PINO
+ * with FT_PROTOCOL field type). This keeps us from having a top-level
+ * "Packet comments" item that steals items from "Frame".
+ */
+ ptree_node = ((proto_node *)protocol_tree)->first_child;
+ while (ptree_node && (ptree_node->finfo->hfinfo->id == pc_proto_id || !proto_registrar_is_protocol(ptree_node->finfo->hfinfo->id))) {
+ ptree_node = ptree_node->next;
+ }
+
+ if (!ptree_node) {
+ return;
+ }
+
+ process_node(ptree_node, ps->stats_tree, ps);
+}
+
+ static gboolean
+process_record(capture_file *cf, frame_data *frame, column_info *cinfo,
+ wtap_rec *rec, Buffer *buf, ph_stats_t* ps)
+{
+ epan_dissect_t edt;
+ double cur_time;
+
+ /* Load the record from the capture file */
+ if (!cf_read_record(cf, frame, rec, buf))
+ return FALSE; /* failure */
+
+ /* Dissect the record tree not visible */
+ epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
+ /* Don't fake protocols. We need them for the protocol hierarchy */
+ epan_dissect_fake_protocols(&edt, FALSE);
+ epan_dissect_run(&edt, cf->cd_t, rec,
+ frame_tvbuff_new_buffer(&cf->provider, frame, buf),
+ frame, cinfo);
+
+ /* Get stats from this protocol tree */
+ process_tree(edt.tree, ps);
+
+ if (frame->has_ts) {
+ /* Update times */
+ cur_time = nstime_to_sec(&frame->abs_ts);
+ if (cur_time < ps->first_time)
+ ps->first_time = cur_time;
+ if (cur_time > ps->last_time)
+ ps->last_time = cur_time;
+ }
+
+ /* Free our memory. */
+ epan_dissect_cleanup(&edt);
+
+ return TRUE; /* success */
+}
+
+ ph_stats_t*
+ph_stats_new(capture_file *cf)
+{
+ ph_stats_t *ps;
+ guint32 framenum;
+ frame_data *frame;
+ progdlg_t *progbar = NULL;
+ int count;
+ wtap_rec rec;
+ Buffer buf;
+ float progbar_val;
+ gchar status_str[100];
+ int progbar_nextstep;
+ int progbar_quantum;
+
+ if (!cf) return NULL;
+
+ if (cf->read_lock) {
+ ws_warning("Failing to compute protocol hierarchy stats on \"%s\" since a read is in progress", cf->filename);
+ return NULL;
+ }
+ cf->read_lock = TRUE;
+
+ cf->stop_flag = FALSE;
+
+ pc_proto_id = proto_registrar_get_id_byname("pkt_comment");
+
+ /* Initialize the data */
+ ps = g_new(ph_stats_t, 1);
+ ps->tot_packets = 0;
+ ps->tot_bytes = 0;
+ ps->stats_tree = g_node_new(NULL);
+ ps->first_time = 0.0;
+ ps->last_time = 0.0;
+
+ /* Update the progress bar when it gets to this value. */
+ progbar_nextstep = 0;
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ progbar_quantum = cf->count/N_PROGBAR_UPDATES;
+ /* Count of packets at which we've looked. */
+ count = 0;
+ /* Progress so far. */
+ progbar_val = 0.0f;
+
+ wtap_rec_init(&rec);
+ ws_buffer_init(&buf, 1514);
+
+ for (framenum = 1; framenum <= cf->count; framenum++) {
+ frame = frame_data_sequence_find(cf->provider.frames, framenum);
+
+ /* Create the progress bar if necessary.
+ We check on every iteration of the loop, so that
+ it takes no longer than the standard time to create
+ it (otherwise, for a large file, we might take
+ considerably longer than that standard time in order
+ to get to the next progress bar step). */
+ if (progbar == NULL)
+ progbar = delayed_create_progress_dlg(
+ cf->window, "Computing",
+ "protocol hierarchy statistics",
+ TRUE, &cf->stop_flag, progbar_val);
+
+ /* Update the progress bar, but do it only N_PROGBAR_UPDATES
+ times; when we update it, we have to run the GTK+ main
+ loop to get it to repaint what's pending, and doing so
+ may involve an "ioctl()" to see if there's any pending
+ input from an X server, and doing that for every packet
+ can be costly, especially on a big file. */
+ if (count >= progbar_nextstep) {
+ /* let's not divide by zero. I should never be started
+ * with count == 0, so let's assert that
+ */
+ ws_assert(cf->count > 0);
+
+ progbar_val = (gfloat) count / cf->count;
+
+ if (progbar != NULL) {
+ snprintf(status_str, sizeof(status_str),
+ "%4u of %u frames", count, cf->count);
+ update_progress_dlg(progbar, progbar_val, status_str);
+ }
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+ if (cf->stop_flag) {
+ /* Well, the user decided to abort the statistics.
+ computation process Just stop. */
+ break;
+ }
+
+ /* Skip frames that are hidden due to the display filter.
+ XXX - should the progress bar count only packets that
+ passed the display filter? If so, it should
+ probably do so for other loops (see "file.c") that
+ look only at those packets. */
+ if (frame->passed_dfilter) {
+
+ if (frame->has_ts) {
+ if (ps->tot_packets == 0) {
+ double cur_time = nstime_to_sec(&frame->abs_ts);
+ ps->first_time = cur_time;
+ ps->last_time = cur_time;
+ }
+ }
+
+ /* We throw away the statistics if we quit in the middle,
+ * so increment this first so that the count starts at 1
+ * when processing records, since we initialize the stat
+ * nodes' last_pkt to 0.
+ */
+ ps->tot_packets++;
+
+ /* we don't care about colinfo */
+ if (!process_record(cf, frame, NULL, &rec, &buf, ps)) {
+ /*
+ * Give up, and set "stop_flag" so we
+ * just abort rather than popping up
+ * the statistics window.
+ */
+ cf->stop_flag = TRUE;
+ break;
+ }
+
+ ps->tot_bytes += frame->pkt_len;
+ }
+
+ count++;
+ }
+
+ wtap_rec_cleanup(&rec);
+ ws_buffer_free(&buf);
+
+ /* We're done calculating the statistics; destroy the progress bar
+ if it was created. */
+ if (progbar != NULL)
+ destroy_progress_dlg(progbar);
+
+ if (cf->stop_flag) {
+ /*
+ * We quit in the middle; throw away the statistics
+ * and return NULL, so our caller doesn't pop up a
+ * window with the incomplete statistics.
+ */
+ ph_stats_free(ps);
+ ps = NULL;
+ }
+
+ ws_assert(cf->read_lock);
+ cf->read_lock = FALSE;
+
+ return ps;
+}
+
+ static gboolean
+stat_node_free(GNode *node, gpointer data _U_)
+{
+ ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
+ g_free(stats);
+ return FALSE;
+}
+
+ void
+ph_stats_free(ph_stats_t *ps)
+{
+ if (ps->stats_tree) {
+ g_node_traverse(ps->stats_tree, G_IN_ORDER,
+ G_TRAVERSE_ALL, -1,
+ stat_node_free, NULL);
+ g_node_destroy(ps->stats_tree);
+ }
+
+ g_free(ps);
+}