2 * Allows to display a flow graph of the currently displayed packets
4 * Copyright 2004, Ericsson , Spain
5 * By Francisco Alcoba <francisco.alcoba@ericsson.com>
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #include "epan/filesystem.h"
35 #include "graph_analysis.h"
36 #include <epan/stat.h>
37 #include "stat_menu.h"
38 #include "dlg_utils.h"
40 #include "compat_macros.h"
41 #include "gtkglobals.h"
43 #include "simple_dialog.h"
45 #include <epan/to_str.h>
47 #include <epan/dissectors/packet-tcp.h>
56 static int type_of_packets = DISPLAYED;
57 static int type_of_flow = GENERAL;
59 static int tap_identifier;
60 static gboolean have_frame_tap_listener=FALSE;
61 static gboolean have_tcp_tap_listener=FALSE;
62 static graph_analysis_info_t *graph_analysis = NULL;
63 static graph_analysis_data_t *graph_analysis_data;
65 static GtkWidget *flow_graph_dlg = NULL;
67 static GtkWidget *select_all_rb;
68 static GtkWidget *select_displayed_rb;
69 static GtkWidget *select_general_rb;
70 static GtkWidget *select_tcp_rb;
72 void flow_graph_data_init(void);
75 /****************************************************************************/
76 /* free up memory and initialize the pointers */
78 void flow_graph_reset(void *ptr _U_)
81 graph_analysis_item_t *graph_item;
85 if (graph_analysis !=NULL){
87 /* free the graph data items */
88 list = g_list_first(graph_analysis->list);
91 graph_item = list->data;
92 g_free(graph_item->frame_label);
93 g_free(graph_item->comment);
95 list = g_list_next(list);
97 g_list_free(graph_analysis->list);
98 graph_analysis->nconv = 0;
99 graph_analysis->list = NULL;
104 /****************************************************************************/
105 void flow_graph_data_init(void) {
106 graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
107 graph_analysis->nconv = 0;
108 graph_analysis->list = NULL;
112 /* XXX just copied from gtk/rpc_stat.c */
113 void protect_thread_critical_region(void);
114 void unprotect_thread_critical_region(void);
116 /****************************************************************************/
118 remove_tap_listener_flow_graph(void)
120 protect_thread_critical_region();
121 remove_tap_listener(&(tap_identifier));
122 unprotect_thread_critical_region();
124 have_frame_tap_listener=FALSE;
125 have_tcp_tap_listener=FALSE;
129 /****************************************************************************/
131 /****************************************************************************/
133 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
135 /* remove_tap_listeners */
136 remove_tap_listener_flow_graph();
138 /* Clean up memory used by tap */
139 flow_graph_reset(NULL);
141 /* Note that we no longer have a "Flow Graph" dialog box. */
142 flow_graph_dlg = NULL;
146 /****************************************************************************/
148 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
150 /* is the button now active? */
151 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
152 type_of_packets = ALL;
156 /****************************************************************************/
158 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
160 /* is the button now active? */
161 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
162 type_of_packets = DISPLAYED;
166 /****************************************************************************/
168 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
170 /* is the button now active? */
171 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
172 type_of_flow = GENERAL;
176 /****************************************************************************/
178 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
180 /* is the button now active? */
181 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
187 /****************************************************************************/
188 /* Add a new frame into the graph */
189 int flow_graph_frame_add_to_graph(packet_info *pinfo)
191 graph_analysis_item_t *gai;
194 gai = g_malloc(sizeof(graph_analysis_item_t));
195 gai->frame_num = pinfo->fd->num;
196 gai->time= (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
197 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
198 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
199 gai->port_src=pinfo->srcport;
200 gai->port_dst=pinfo->destport;
202 gai->frame_label=NULL;
204 if (pinfo->cinfo->col_first[COL_INFO]>=0){
206 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
207 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
208 if (gai->frame_label!=NULL){
209 g_free(gai->frame_label);
211 gai->comment = g_strdup(pinfo->cinfo->col_data[i]);
216 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
218 for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
219 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
220 if (gai->frame_label!=NULL){
221 g_free(gai->frame_label);
223 gai->frame_label = g_strdup(pinfo->cinfo->col_data[i]);
233 graph_analysis->list = g_list_append(graph_analysis->list, gai);
239 /****************************************************************************/
240 /* Add a new tcp frame into the graph */
241 int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
243 graph_analysis_item_t *gai;
244 /* copied from packet-tcp */
245 const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
248 gchar flags[64] = "<None>";
250 gai = g_malloc(sizeof(graph_analysis_item_t));
251 gai->frame_num = pinfo->fd->num;
252 gai->time= (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
253 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
254 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
255 gai->port_src=pinfo->srcport;
256 gai->port_dst=pinfo->destport;
258 for (i = 0; i < 8; i++) {
260 if (tcph->th_flags & bpos) {
262 strcpy(&flags[fpos], ", ");
265 strcpy(&flags[fpos], fstr[i]);
270 if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
271 gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
274 gai->frame_label = g_strdup(flags);
277 gai->comment = g_strdup_printf("Seq = %i Ack = %i",tcph->th_seq, tcph->th_ack);
283 graph_analysis->list = g_list_append(graph_analysis->list, gai);
291 /****************************************************************************/
292 /* whenever a frame packet is seen by the tap listener */
294 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
297 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
298 flow_graph_frame_add_to_graph(pinfo);
304 /****************************************************************************/
305 /* whenever a TCP packet is seen by the tap listener */
307 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
309 const struct tcpheader *tcph = tcp_info;
311 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
312 flow_graph_tcp_add_to_graph(pinfo,tcph);
319 void flow_graph_packet_draw(void *prs _U_)
324 /****************************************************************************/
326 flow_graph_on_ok (GtkButton *button _U_,
327 gpointer user_data _U_)
330 if ((have_frame_tap_listener==TRUE)
331 ||(have_tcp_tap_listener==TRUE))
333 /* remove_tap_listeners */
334 remove_tap_listener_flow_graph();
337 /* Scan for displayed packets (redissect all packets) */
339 if (type_of_flow == GENERAL){
340 /* Register the tap listener */
342 if(have_frame_tap_listener==FALSE)
344 /* don't register tap listener, if we have it already */
345 register_tap_listener("frame", &tap_identifier, NULL,
347 flow_graph_frame_packet,
348 flow_graph_packet_draw
350 have_frame_tap_listener=TRUE;
353 cf_redissect_packets(&cfile);
355 else if (type_of_flow == TCP){
356 /* Register the tap listener */
358 if(have_tcp_tap_listener==FALSE)
360 /* don't register tap listener, if we have it already */
361 register_tap_listener("tcp", &tap_identifier, NULL,
363 flow_graph_tcp_packet,
364 flow_graph_packet_draw
366 have_tcp_tap_listener=TRUE;
369 cf_retap_packets(&cfile);
372 if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
373 graph_analysis_update(graph_analysis_data); /* refresh it xxx */
376 graph_analysis_create(graph_analysis_data);
382 /****************************************************************************/
384 /****************************************************************************/
386 static void flow_graph_dlg_create (void)
389 GtkWidget *flow_graph_dlg_w;
391 GtkWidget *hbuttonbox;
392 GtkWidget *bt_close, *bt_ok;
394 GtkWidget *top_label = NULL;
396 GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb;
397 #if GTK_MAJOR_VERSION < 2
398 GtkAccelGroup *accel_group;
402 GtkTooltips *tooltips = gtk_tooltips_new();
404 flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Packet flow graph");
406 gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 350, 150);
408 main_vb = gtk_vbox_new (FALSE, 0);
409 gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
410 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
412 #if GTK_MAJOR_VERSION < 2
413 /* Accelerator group for the accelerators (or, as they're called in
414 Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
415 Ctrl+<key> is an accelerator). */
416 accel_group = gtk_accel_group_new();
417 gtk_window_add_accel_group(GTK_WINDOW(main_vb), accel_group);
421 top_label = gtk_label_new ("Choose packets to include in the graph");
422 gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
425 gtk_widget_show(flow_graph_dlg_w);
427 /*** Packet range frame ***/
428 range_fr = gtk_frame_new("Choose packets");
429 gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
431 range_tb = gtk_table_new(4, 4, FALSE);
432 gtk_container_border_width(GTK_CONTAINER(range_tb), 5);
433 gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
435 /* Process all packets */
436 select_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_All packets", accel_group);
437 gtk_tooltips_set_tip (tooltips, select_all_rb,
438 ("Process all packets"), NULL);
439 SIGNAL_CONNECT(select_all_rb, "toggled", toggle_select_all, NULL);
440 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
441 if (type_of_packets == ALL) {
442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
444 gtk_widget_show(select_all_rb);
446 /* Process displayed packets */
447 select_displayed_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Displayed packets", accel_group);
448 gtk_tooltips_set_tip (tooltips, select_displayed_rb,
449 ("Process displayed packets"), NULL);
450 SIGNAL_CONNECT(select_displayed_rb, "toggled", toggle_select_displayed, NULL);
451 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 1, 2, 0, 1);
452 if (type_of_packets == DISPLAYED) {
453 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
455 gtk_widget_show(select_displayed_rb);
457 gtk_widget_show(range_tb);
459 gtk_widget_show(range_fr);
461 /*** Flow type frame ***/
462 flow_type_fr = gtk_frame_new("Choose flow type");
463 gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 0);
465 flow_type_tb = gtk_table_new(4, 4, FALSE);
466 gtk_container_border_width(GTK_CONTAINER(flow_type_tb), 5);
467 gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
469 /* General information */
470 select_general_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_General flow", accel_group);
471 gtk_tooltips_set_tip (tooltips, select_general_rb,
472 ("Show all packets, with general information"), NULL);
473 SIGNAL_CONNECT(select_general_rb, "toggled", toggle_select_general, NULL);
474 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
475 if (type_of_flow == GENERAL) {
476 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
478 gtk_widget_show(select_general_rb);
480 /* TCP specific information */
481 select_tcp_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_general_rb, "_TCP flow", accel_group);
482 gtk_tooltips_set_tip (tooltips, select_tcp_rb,
483 ("Show only TCP packets, with TCP specific information"), NULL);
484 SIGNAL_CONNECT(select_tcp_rb, "toggled", toggle_select_tcp, NULL);
485 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 1, 2, 0, 1);
486 if (type_of_flow == TCP) {
487 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
490 gtk_widget_show(select_tcp_rb);
492 gtk_widget_show(flow_type_tb);
493 gtk_widget_show(flow_type_fr);
496 hbuttonbox = gtk_hbutton_box_new ();
497 gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
498 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
499 gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
501 bt_ok = BUTTON_NEW_FROM_STOCK(GTK_STOCK_OK);
502 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
503 gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
504 SIGNAL_CONNECT(bt_ok, "clicked", flow_graph_on_ok, NULL);
505 gtk_widget_show(bt_ok);
507 bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
508 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
509 GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
510 gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
511 window_set_cancel_button(flow_graph_dlg_w, bt_close, window_cancel_button_cb);
513 SIGNAL_CONNECT(flow_graph_dlg_w, "delete_event", window_delete_event_cb, NULL);
514 SIGNAL_CONNECT(flow_graph_dlg_w, "destroy", flow_graph_on_destroy, NULL);
516 gtk_widget_show_all(flow_graph_dlg_w);
517 window_present(flow_graph_dlg_w);
519 flow_graph_dlg = flow_graph_dlg_w;
522 /****************************************************************************/
524 /****************************************************************************/
526 /* init function for tap */
528 flow_graph_init_tap(const char *dummy _U_)
531 /* initialize graph items store */
532 flow_graph_data_init();
534 /* init the Graph Analysys */
535 graph_analysis_data = graph_analysis_init();
536 graph_analysis_data->graph_info = graph_analysis;
538 /* create dialog box if necessary */
539 if (flow_graph_dlg == NULL) {
540 flow_graph_dlg_create();
542 /* There's already a dialog box; reactivate it. */
543 reactivate_window(flow_graph_dlg);
549 /****************************************************************************/
550 /* entry point when called via the GTK menu */
551 void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
553 flow_graph_init_tap("");
556 /****************************************************************************/
558 register_tap_listener_flow_graph(void)
560 register_stat_cmd_arg("flow_graph",flow_graph_init_tap);
561 register_stat_menu_item("Flow graph", REGISTER_STAT_GROUP_NONE,
562 flow_graph_launch, NULL, NULL, NULL);