2 * Routines for calculating statistics based on protocol.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include "frame_tvbuff.h"
17 #include "ui/proto_hier_stats.h"
18 #include "ui/progress_dlg.h"
19 #include "epan/epan_dissect.h"
20 #include "epan/proto.h"
22 /* Update the progress bar this many times when scanning the packet list. */
23 #define N_PROGBAR_UPDATES 100
25 #define STAT_NODE_STATS(n) ((ph_stats_node_t*)(n)->data)
26 #define STAT_NODE_HFINFO(n) (STAT_NODE_STATS(n)->hfinfo)
28 static int pc_proto_id = -1;
31 find_stat_node(GNode *parent_stat_node, header_field_info *needle_hfinfo)
33 GNode *needle_stat_node, *up_parent_stat_node;
34 header_field_info *hfinfo;
35 ph_stats_node_t *stats;
37 /* Look down the tree */
38 needle_stat_node = g_node_first_child(parent_stat_node);
40 while (needle_stat_node) {
41 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
42 if (hfinfo && hfinfo->id == needle_hfinfo->id) {
43 return needle_stat_node;
45 needle_stat_node = g_node_next_sibling(needle_stat_node);
48 /* Look up the tree */
49 up_parent_stat_node = parent_stat_node;
50 while (up_parent_stat_node && up_parent_stat_node->parent)
52 needle_stat_node = g_node_first_child(up_parent_stat_node->parent);
53 while (needle_stat_node) {
54 hfinfo = STAT_NODE_HFINFO(needle_stat_node);
55 if (hfinfo && hfinfo->id == needle_hfinfo->id) {
56 return needle_stat_node;
58 needle_stat_node = g_node_next_sibling(needle_stat_node);
61 up_parent_stat_node = up_parent_stat_node->parent;
64 /* None found. Create one. */
65 stats = g_new(ph_stats_node_t, 1);
67 /* Intialize counters */
68 stats->hfinfo = needle_hfinfo;
69 stats->num_pkts_total = 0;
70 stats->num_pkts_last = 0;
71 stats->num_bytes_total = 0;
72 stats->num_bytes_last = 0;
74 needle_stat_node = g_node_new(stats);
75 g_node_append(parent_stat_node, needle_stat_node);
76 return needle_stat_node;
81 process_node(proto_node *ptree_node, GNode *parent_stat_node, ph_stats_t *ps)
84 ph_stats_node_t *stats;
85 proto_node *proto_sibling_node;
88 finfo = PNODE_FINFO(ptree_node);
89 /* We don't fake protocol nodes we expect them to have a field_info.
90 * Dissection with faked proto tree? */
93 /* If the field info isn't related to a protocol but to a field,
94 * don't count them, as they don't belong to any protocol.
95 * (happens e.g. for toplevel tree item of desegmentation "[Reassembled TCP Segments]") */
96 if (finfo->hfinfo->parent != -1) {
97 /* Skip this element, use parent status node */
98 stat_node = parent_stat_node;
99 stats = STAT_NODE_STATS(stat_node);
101 stat_node = find_stat_node(parent_stat_node, finfo->hfinfo);
103 stats = STAT_NODE_STATS(stat_node);
104 stats->num_pkts_total++;
105 stats->num_bytes_total += finfo->length;
108 proto_sibling_node = ptree_node->next;
110 if (proto_sibling_node) {
111 /* If the name does not exist for this proto_sibling_node, then it is
112 * not a normal protocol in the top-level tree. It was instead
113 * added as a normal tree such as IPv6's Hop-by-hop Option Header and
114 * should be skipped when creating the protocol hierarchy display. */
115 if(strlen(PNODE_FINFO(proto_sibling_node)->hfinfo->name) == 0 && ptree_node->next)
116 proto_sibling_node = proto_sibling_node->next;
118 process_node(proto_sibling_node, stat_node, ps);
120 stats->num_pkts_last++;
121 stats->num_bytes_last += finfo->length;
128 process_tree(proto_tree *protocol_tree, ph_stats_t* ps)
130 proto_node *ptree_node;
133 * If our first item is a comment, skip over it. This keeps
134 * us from having a top-level "Packet comments" item that
135 * steals items from "Frame".
137 ptree_node = ((proto_node *)protocol_tree)->first_child;
138 if (ptree_node && ptree_node->finfo->hfinfo->id == pc_proto_id) {
139 ptree_node = ptree_node->next;
146 process_node(ptree_node, ps->stats_tree, ps);
150 process_record(capture_file *cf, frame_data *frame, column_info *cinfo, ph_stats_t* ps)
159 /* Load the record from the capture file */
160 ws_buffer_init(&buf, 1500);
161 if (!cf_read_record_r(cf, frame, &rec, &buf))
162 return FALSE; /* failure */
164 /* Dissect the record tree not visible */
165 epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
166 /* Don't fake protocols. We need them for the protocol hierarchy */
167 epan_dissect_fake_protocols(&edt, FALSE);
168 epan_dissect_run(&edt, cf->cd_t, &rec,
169 frame_tvbuff_new_buffer(&cf->provider, frame, &buf),
172 /* Get stats from this protocol tree */
173 process_tree(edt.tree, ps);
177 cur_time = nstime_to_sec(&frame->abs_ts);
178 if (cur_time < ps->first_time)
179 ps->first_time = cur_time;
180 if (cur_time > ps->last_time)
181 ps->last_time = cur_time;
184 /* Free our memory. */
185 epan_dissect_cleanup(&edt);
186 wtap_rec_cleanup(&rec);
187 ws_buffer_free(&buf);
189 return TRUE; /* success */
193 ph_stats_new(capture_file *cf)
198 guint tot_packets, tot_bytes;
199 progdlg_t *progbar = NULL;
204 gchar status_str[100];
205 int progbar_nextstep;
208 if (!cf) return NULL;
210 pc_proto_id = proto_registrar_get_id_byname("pkt_comment");
212 /* Initialize the data */
213 ps = g_new(ph_stats_t, 1);
216 ps->stats_tree = g_node_new(NULL);
217 ps->first_time = 0.0;
220 /* Update the progress bar when it gets to this value. */
221 progbar_nextstep = 0;
222 /* When we reach the value that triggers a progress bar update,
223 bump that value by this amount. */
224 progbar_quantum = cf->count/N_PROGBAR_UPDATES;
225 /* Count of packets at which we've looked. */
227 /* Progress so far. */
231 g_get_current_time(&start_time);
236 for (framenum = 1; framenum <= cf->count; framenum++) {
237 frame = frame_data_sequence_find(cf->provider.frames, framenum);
239 /* Create the progress bar if necessary.
240 We check on every iteration of the loop, so that
241 it takes no longer than the standard time to create
242 it (otherwise, for a large file, we might take
243 considerably longer than that standard time in order
244 to get to the next progress bar step). */
246 progbar = delayed_create_progress_dlg(
247 cf->window, "Computing",
248 "protocol hierarchy statistics",
249 TRUE, &stop_flag, &start_time, progbar_val);
251 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
252 times; when we update it, we have to run the GTK+ main
253 loop to get it to repaint what's pending, and doing so
254 may involve an "ioctl()" to see if there's any pending
255 input from an X server, and doing that for every packet
256 can be costly, especially on a big file. */
257 if (count >= progbar_nextstep) {
258 /* let's not divide by zero. I should never be started
259 * with count == 0, so let's assert that
261 g_assert(cf->count > 0);
263 progbar_val = (gfloat) count / cf->count;
265 if (progbar != NULL) {
266 g_snprintf(status_str, sizeof(status_str),
267 "%4u of %u frames", count, cf->count);
268 update_progress_dlg(progbar, progbar_val, status_str);
271 progbar_nextstep += progbar_quantum;
275 /* Well, the user decided to abort the statistics.
276 computation process Just stop. */
280 /* Skip frames that are hidden due to the display filter.
281 XXX - should the progress bar count only packets that
282 passed the display filter? If so, it should
283 probably do so for other loops (see "file.c") that
284 look only at those packets. */
285 if (frame->passed_dfilter) {
288 if (tot_packets == 0) {
289 double cur_time = nstime_to_sec(&frame->abs_ts);
290 ps->first_time = cur_time;
291 ps->last_time = cur_time;
295 /* we don't care about colinfo */
296 if (!process_record(cf, frame, NULL, ps)) {
298 * Give up, and set "stop_flag" so we
299 * just abort rather than popping up
300 * the statistics window.
307 tot_bytes += frame->pkt_len;
313 /* We're done calculating the statistics; destroy the progress bar
314 if it was created. */
316 destroy_progress_dlg(progbar);
320 * We quit in the middle; throw away the statistics
321 * and return NULL, so our caller doesn't pop up a
322 * window with the incomplete statistics.
328 ps->tot_packets = tot_packets;
329 ps->tot_bytes = tot_bytes;
335 stat_node_free(GNode *node, gpointer data _U_)
337 ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
343 ph_stats_free(ph_stats_t *ps)
345 if (ps->stats_tree) {
346 g_node_traverse(ps->stats_tree, G_IN_ORDER,
348 stat_node_free, NULL);
349 g_node_destroy(ps->stats_tree);
356 * Editor modelines - https://www.wireshark.org/tools/modelines.html
361 * indent-tabs-mode: nil
364 * ex: set shiftwidth=4 tabstop=8 expandtab:
365 * :indentSize=4:tabSize=8:noTabs=true: