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+
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)
153 struct wtap_pkthdr phdr;
157 wtap_phdr_init(&phdr);
159 /* Load the record from the capture file */
160 ws_buffer_init(&buf, 1500);
161 if (!cf_read_record_r(cf, frame, &phdr, &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, &phdr, frame_tvbuff_new_buffer(frame, &buf), frame, cinfo);
170 /* Get stats from this protocol tree */
171 process_tree(edt.tree, ps);
173 if (frame->flags.has_ts) {
175 cur_time = nstime_to_sec(&frame->abs_ts);
176 if (cur_time < ps->first_time)
177 ps->first_time = cur_time;
178 if (cur_time > ps->last_time)
179 ps->last_time = cur_time;
182 /* Free our memory. */
183 epan_dissect_cleanup(&edt);
184 wtap_phdr_cleanup(&phdr);
185 ws_buffer_free(&buf);
187 return TRUE; /* success */
191 ph_stats_new(capture_file *cf)
196 guint tot_packets, tot_bytes;
197 progdlg_t *progbar = NULL;
202 gchar status_str[100];
203 int progbar_nextstep;
206 if (!cf) return NULL;
208 pc_proto_id = proto_registrar_get_id_byname("pkt_comment");
210 /* Initialize the data */
211 ps = g_new(ph_stats_t, 1);
214 ps->stats_tree = g_node_new(NULL);
215 ps->first_time = 0.0;
218 /* Update the progress bar when it gets to this value. */
219 progbar_nextstep = 0;
220 /* When we reach the value that triggers a progress bar update,
221 bump that value by this amount. */
222 progbar_quantum = cf->count/N_PROGBAR_UPDATES;
223 /* Count of packets at which we've looked. */
225 /* Progress so far. */
229 g_get_current_time(&start_time);
234 for (framenum = 1; framenum <= cf->count; framenum++) {
235 frame = frame_data_sequence_find(cf->frame_set_info.frames, framenum);
237 /* Create the progress bar if necessary.
238 We check on every iteration of the loop, so that
239 it takes no longer than the standard time to create
240 it (otherwise, for a large file, we might take
241 considerably longer than that standard time in order
242 to get to the next progress bar step). */
244 progbar = delayed_create_progress_dlg(
245 cf->window, "Computing",
246 "protocol hierarchy statistics",
247 TRUE, &stop_flag, &start_time, progbar_val);
249 /* Update the progress bar, but do it only N_PROGBAR_UPDATES
250 times; when we update it, we have to run the GTK+ main
251 loop to get it to repaint what's pending, and doing so
252 may involve an "ioctl()" to see if there's any pending
253 input from an X server, and doing that for every packet
254 can be costly, especially on a big file. */
255 if (count >= progbar_nextstep) {
256 /* let's not divide by zero. I should never be started
257 * with count == 0, so let's assert that
259 g_assert(cf->count > 0);
261 progbar_val = (gfloat) count / cf->count;
263 if (progbar != NULL) {
264 g_snprintf(status_str, sizeof(status_str),
265 "%4u of %u frames", count, cf->count);
266 update_progress_dlg(progbar, progbar_val, status_str);
269 progbar_nextstep += progbar_quantum;
273 /* Well, the user decided to abort the statistics.
274 computation process Just stop. */
278 /* Skip frames that are hidden due to the display filter.
279 XXX - should the progress bar count only packets that
280 passed the display filter? If so, it should
281 probably do so for other loops (see "file.c") that
282 look only at those packets. */
283 if (frame->flags.passed_dfilter) {
285 if (frame->flags.has_ts) {
286 if (tot_packets == 0) {
287 double cur_time = nstime_to_sec(&frame->abs_ts);
288 ps->first_time = cur_time;
289 ps->last_time = cur_time;
293 /* we don't care about colinfo */
294 if (!process_record(cf, frame, NULL, ps)) {
296 * Give up, and set "stop_flag" so we
297 * just abort rather than popping up
298 * the statistics window.
305 tot_bytes += frame->pkt_len;
311 /* We're done calculating the statistics; destroy the progress bar
312 if it was created. */
314 destroy_progress_dlg(progbar);
318 * We quit in the middle; throw away the statistics
319 * and return NULL, so our caller doesn't pop up a
320 * window with the incomplete statistics.
326 ps->tot_packets = tot_packets;
327 ps->tot_bytes = tot_bytes;
333 stat_node_free(GNode *node, gpointer data _U_)
335 ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
341 ph_stats_free(ph_stats_t *ps)
343 if (ps->stats_tree) {
344 g_node_traverse(ps->stats_tree, G_IN_ORDER,
346 stat_node_free, NULL);
347 g_node_destroy(ps->stats_tree);
354 * Editor modelines - https://www.wireshark.org/tools/modelines.html
359 * indent-tabs-mode: nil
362 * ex: set shiftwidth=4 tabstop=8 expandtab:
363 * :indentSize=4:tabSize=8:noTabs=true: