2001-03-22 23:54:47 +00:00
|
|
|
/* proto_hier_stats.c
|
|
|
|
* Routines for calculating statistics based on protocol.
|
|
|
|
*
|
2001-06-19 23:08:57 +00:00
|
|
|
* $Id: proto_hier_stats.c,v 1.4 2001/06/19 23:08:55 guy Exp $
|
2001-03-22 23:54:47 +00:00
|
|
|
*
|
|
|
|
* Ethereal - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@zing.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 "globals.h"
|
|
|
|
#include "proto_hier_stats.h"
|
2001-03-24 02:07:22 +00:00
|
|
|
#include "progress_dlg.h"
|
2001-03-22 23:54:47 +00:00
|
|
|
#include <wtap.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <glib.h>
|
|
|
|
|
2001-03-24 02:07:22 +00:00
|
|
|
/* Update the progress bar this many times when scanning the packet list. */
|
|
|
|
#define N_PROGBAR_UPDATES 100
|
|
|
|
|
2001-03-22 23:54:47 +00:00
|
|
|
static GNode*
|
|
|
|
find_stat_node(GNode *parent_node, header_field_info *needle_hfinfo)
|
|
|
|
{
|
|
|
|
GNode *needle_node;
|
|
|
|
field_info *finfo;
|
|
|
|
ph_stats_node_t *stats;
|
|
|
|
|
|
|
|
needle_node = g_node_first_child(parent_node);
|
|
|
|
|
|
|
|
while (needle_node) {
|
|
|
|
finfo = needle_node->data;
|
|
|
|
if (finfo && finfo->hfinfo && finfo->hfinfo->id == needle_hfinfo->id) {
|
|
|
|
return needle_node;
|
|
|
|
}
|
|
|
|
needle_node = g_node_next_sibling(needle_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* None found. Create one. */
|
|
|
|
stats = g_new(ph_stats_node_t, 1);
|
|
|
|
|
|
|
|
/* Intialize counters */
|
|
|
|
stats->hfinfo = needle_hfinfo;
|
|
|
|
stats->num_pkts_total = 0;
|
|
|
|
stats->num_pkts_last = 0;
|
|
|
|
stats->num_bytes_total = 0;
|
|
|
|
stats->num_bytes_last = 0;
|
|
|
|
|
|
|
|
needle_node = g_node_new(stats);
|
|
|
|
g_node_append(parent_node, needle_node);
|
|
|
|
return needle_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_node(proto_item *proto_node, GNode *parent_stat_node, ph_stats_t *ps, guint pkt_len)
|
|
|
|
{
|
|
|
|
field_info *finfo;
|
|
|
|
ph_stats_node_t *stats;
|
|
|
|
proto_item *proto_sibling_node;
|
|
|
|
GNode *stat_node;
|
|
|
|
|
|
|
|
finfo = proto_node->data;
|
|
|
|
g_assert(finfo);
|
|
|
|
|
|
|
|
stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
|
|
|
|
|
|
|
|
/* Assert that the finfo is related to a protocol, not a field. */
|
|
|
|
g_assert(finfo->hfinfo->parent == -1);
|
|
|
|
|
|
|
|
stats = stat_node->data;
|
|
|
|
stats->num_pkts_total++;
|
|
|
|
stats->num_bytes_total += pkt_len;
|
|
|
|
|
|
|
|
proto_sibling_node = g_node_next_sibling(proto_node);
|
|
|
|
|
|
|
|
if (proto_sibling_node) {
|
|
|
|
process_node(proto_sibling_node, stat_node, ps, pkt_len);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
stats->num_pkts_last++;
|
|
|
|
stats->num_bytes_last += pkt_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_tree(proto_tree *protocol_tree, ph_stats_t* ps, guint pkt_len)
|
|
|
|
{
|
|
|
|
proto_item *proto_node;
|
|
|
|
|
|
|
|
proto_node = g_node_first_child(protocol_tree);
|
|
|
|
if (!proto_node) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
process_node(proto_node, ps->stats_tree, ps, pkt_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_frame(frame_data *frame, ph_stats_t* ps)
|
|
|
|
{
|
|
|
|
epan_dissect_t *edt;
|
|
|
|
union wtap_pseudo_header phdr;
|
|
|
|
proto_tree *protocol_tree;
|
|
|
|
guint8 pd[WTAP_MAX_PACKET_SIZE];
|
|
|
|
|
|
|
|
protocol_tree = proto_tree_create_root();
|
|
|
|
|
|
|
|
/* Load the frame from the capture file */
|
|
|
|
wtap_seek_read(cfile.wth, frame->file_off, &phdr,
|
|
|
|
pd, frame->cap_len);
|
|
|
|
|
|
|
|
/* Dissect the frame */
|
|
|
|
edt = epan_dissect_new(&phdr, pd, frame, protocol_tree);
|
|
|
|
|
|
|
|
/* Get stats from this protocol tree */
|
|
|
|
process_tree(protocol_tree, ps, frame->pkt_len);
|
|
|
|
|
|
|
|
/* Free our memory. */
|
|
|
|
epan_dissect_free(edt);
|
|
|
|
proto_tree_free(protocol_tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ph_stats_t*
|
|
|
|
ph_stats_new(void)
|
|
|
|
{
|
|
|
|
ph_stats_t *ps;
|
|
|
|
frame_data *frame;
|
|
|
|
guint tot_packets, tot_bytes;
|
2001-03-24 02:07:22 +00:00
|
|
|
progdlg_t *progbar;
|
|
|
|
gboolean stop_flag;
|
|
|
|
guint32 progbar_quantum;
|
|
|
|
guint32 progbar_nextstep;
|
2001-06-19 23:08:57 +00:00
|
|
|
unsigned int count;
|
2001-03-22 23:54:47 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2001-03-24 02:07:22 +00:00
|
|
|
/* 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 = cfile.count/N_PROGBAR_UPDATES;
|
|
|
|
/* Count of packets at which we've looked. */
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
stop_flag = FALSE;
|
|
|
|
progbar = create_progress_dlg("Computing protocol statistics", "Stop",
|
|
|
|
&stop_flag);
|
|
|
|
|
2001-03-22 23:54:47 +00:00
|
|
|
tot_packets = 0;
|
|
|
|
tot_bytes = 0;
|
|
|
|
|
2001-03-24 02:07:22 +00:00
|
|
|
for (frame = cfile.plist; frame != NULL; frame = frame->next) {
|
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
g_assert(cfile.count > 0);
|
|
|
|
|
|
|
|
update_progress_dlg(progbar,
|
|
|
|
(gfloat) count / cfile.count);
|
|
|
|
|
|
|
|
progbar_nextstep += progbar_quantum;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stop_flag) {
|
|
|
|
/* Well, the user decided to abort the statistics.
|
|
|
|
computation process Just stop. */
|
|
|
|
break;
|
2001-03-22 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
2001-03-24 02:07:22 +00:00
|
|
|
/* 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->flags.passed_dfilter) {
|
|
|
|
process_frame(frame, ps);
|
|
|
|
|
|
|
|
tot_packets++;
|
|
|
|
tot_bytes += frame->pkt_len;
|
|
|
|
}
|
2001-03-22 23:54:47 +00:00
|
|
|
|
2001-03-24 02:07:22 +00:00
|
|
|
count++;
|
|
|
|
}
|
2001-03-22 23:54:47 +00:00
|
|
|
|
2001-03-24 02:07:22 +00:00
|
|
|
/* We're done calculating the statistics; destroy the progress bar. */
|
|
|
|
destroy_progress_dlg(progbar);
|
|
|
|
|
|
|
|
if (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);
|
|
|
|
return NULL;
|
2001-03-22 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ps->tot_packets = tot_packets;
|
|
|
|
ps->tot_bytes = tot_bytes;
|
|
|
|
|
|
|
|
return ps;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
stat_node_free(GNode *node, gpointer data)
|
|
|
|
{
|
|
|
|
ph_stats_node_t *stats = node->data;
|
|
|
|
|
|
|
|
if (stats) {
|
|
|
|
g_free(stats);
|
|
|
|
}
|
2001-03-23 21:55:36 +00:00
|
|
|
return FALSE;
|
2001-03-22 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|