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_cmd_args.h>
37 #include "../stat_menu.h"
38 #include "gui_stat_menu.h"
39 #include "dlg_utils.h"
40 #include "gui_utils.h"
41 #include "compat_macros.h"
42 #include "gtkglobals.h"
44 #include "simple_dialog.h"
46 #include <epan/to_str.h>
48 #include <epan/dissectors/packet-tcp.h>
57 static int type_of_packets = DISPLAYED;
58 static int type_of_flow = GENERAL;
60 static int tap_identifier;
61 static gboolean have_frame_tap_listener=FALSE;
62 static gboolean have_tcp_tap_listener=FALSE;
63 static graph_analysis_info_t *graph_analysis = NULL;
64 static graph_analysis_data_t *graph_analysis_data;
66 static GtkWidget *flow_graph_dlg = NULL;
68 static GtkWidget *select_all_rb;
69 static GtkWidget *select_displayed_rb;
70 static GtkWidget *select_general_rb;
71 static GtkWidget *select_tcp_rb;
73 void flow_graph_data_init(void);
76 /****************************************************************************/
77 /* free up memory and initialize the pointers */
79 static void flow_graph_reset(void *ptr _U_)
82 graph_analysis_item_t *graph_item;
86 if (graph_analysis !=NULL){
88 /* free the graph data items */
89 list = g_list_first(graph_analysis->list);
92 graph_item = list->data;
93 g_free(graph_item->frame_label);
94 g_free(graph_item->comment);
96 list = g_list_next(list);
98 g_list_free(graph_analysis->list);
99 graph_analysis->nconv = 0;
100 graph_analysis->list = NULL;
105 /****************************************************************************/
106 void flow_graph_data_init(void) {
107 graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
108 graph_analysis->nconv = 0;
109 graph_analysis->list = NULL;
113 /* XXX just copied from gtk/rpc_stat.c */
114 void protect_thread_critical_region(void);
115 void unprotect_thread_critical_region(void);
117 /****************************************************************************/
119 remove_tap_listener_flow_graph(void)
121 protect_thread_critical_region();
122 remove_tap_listener(&(tap_identifier));
123 unprotect_thread_critical_region();
125 have_frame_tap_listener=FALSE;
126 have_tcp_tap_listener=FALSE;
130 /****************************************************************************/
132 /****************************************************************************/
134 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
136 /* remove_tap_listeners */
137 remove_tap_listener_flow_graph();
139 /* Clean up memory used by tap */
140 flow_graph_reset(NULL);
142 /* Note that we no longer have a "Flow Graph" dialog box. */
143 flow_graph_dlg = NULL;
147 /****************************************************************************/
149 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
151 /* is the button now active? */
152 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
153 type_of_packets = ALL;
157 /****************************************************************************/
159 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
161 /* is the button now active? */
162 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
163 type_of_packets = DISPLAYED;
167 /****************************************************************************/
169 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
171 /* is the button now active? */
172 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
173 type_of_flow = GENERAL;
177 /****************************************************************************/
179 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
181 /* is the button now active? */
182 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
188 /****************************************************************************/
189 /* Add a new frame into the graph */
190 static int flow_graph_frame_add_to_graph(packet_info *pinfo)
192 graph_analysis_item_t *gai;
195 gai = g_malloc(sizeof(graph_analysis_item_t));
196 gai->frame_num = pinfo->fd->num;
197 gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
198 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
199 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
200 gai->port_src=pinfo->srcport;
201 gai->port_dst=pinfo->destport;
203 gai->frame_label=NULL;
205 if (pinfo->cinfo->col_first[COL_INFO]>=0){
207 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
208 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
209 if (gai->frame_label!=NULL){
210 g_free(gai->frame_label);
212 gai->comment = g_strdup(pinfo->cinfo->col_data[i]);
217 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
219 for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
220 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
221 if (gai->frame_label!=NULL){
222 g_free(gai->frame_label);
224 gai->frame_label = g_strdup(pinfo->cinfo->col_data[i]);
234 graph_analysis->list = g_list_append(graph_analysis->list, gai);
240 /****************************************************************************/
241 /* Add a new tcp frame into the graph */
242 static int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
244 graph_analysis_item_t *gai;
245 /* copied from packet-tcp */
246 const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
249 gchar flags[64] = "<None>";
251 gai = g_malloc(sizeof(graph_analysis_item_t));
252 gai->frame_num = pinfo->fd->num;
253 gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
254 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
255 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
256 gai->port_src=pinfo->srcport;
257 gai->port_dst=pinfo->destport;
259 for (i = 0; i < 8; i++) {
261 if (tcph->th_flags & bpos) {
263 strcpy(&flags[fpos], ", ");
266 strcpy(&flags[fpos], fstr[i]);
271 if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
272 gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
275 gai->frame_label = g_strdup(flags);
278 gai->comment = g_strdup_printf("Seq = %i Ack = %i",tcph->th_seq, tcph->th_ack);
284 graph_analysis->list = g_list_append(graph_analysis->list, gai);
292 /****************************************************************************/
293 /* whenever a frame packet is seen by the tap listener */
295 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
298 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
299 flow_graph_frame_add_to_graph(pinfo);
305 /****************************************************************************/
306 /* whenever a TCP packet is seen by the tap listener */
308 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
310 const struct tcpheader *tcph = tcp_info;
312 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
313 flow_graph_tcp_add_to_graph(pinfo,tcph);
320 static void flow_graph_packet_draw(void *prs _U_)
325 /****************************************************************************/
327 flow_graph_on_ok (GtkButton *button _U_,
328 gpointer user_data _U_)
331 if ((have_frame_tap_listener==TRUE)
332 ||(have_tcp_tap_listener==TRUE))
334 /* remove_tap_listeners */
335 remove_tap_listener_flow_graph();
338 /* Scan for displayed packets (retap all packets) */
340 if (type_of_flow == GENERAL){
341 /* Register the tap listener */
343 if(have_frame_tap_listener==FALSE)
345 /* don't register tap listener, if we have it already */
346 register_tap_listener("frame", &tap_identifier, NULL,
348 flow_graph_frame_packet,
349 flow_graph_packet_draw
351 have_frame_tap_listener=TRUE;
354 cf_retap_packets(&cfile, TRUE);
356 else if (type_of_flow == TCP){
357 /* Register the tap listener */
359 if(have_tcp_tap_listener==FALSE)
361 /* don't register tap listener, if we have it already */
362 register_tap_listener("tcp", &tap_identifier, NULL,
364 flow_graph_tcp_packet,
365 flow_graph_packet_draw
367 have_tcp_tap_listener=TRUE;
370 cf_retap_packets(&cfile, FALSE);
373 if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
374 graph_analysis_update(graph_analysis_data); /* refresh it xxx */
377 graph_analysis_create(graph_analysis_data);
383 /****************************************************************************/
385 /****************************************************************************/
387 static void flow_graph_dlg_create (void)
390 GtkWidget *flow_graph_dlg_w;
392 GtkWidget *hbuttonbox;
393 GtkWidget *bt_close, *bt_ok;
395 GtkWidget *top_label = NULL;
397 GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb;
398 #if GTK_MAJOR_VERSION < 2
399 GtkAccelGroup *accel_group;
403 GtkTooltips *tooltips = gtk_tooltips_new();
405 flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Flow Graph");
407 gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 350, 150);
409 main_vb = gtk_vbox_new (FALSE, 0);
410 gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
411 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
413 #if GTK_MAJOR_VERSION < 2
414 /* Accelerator group for the accelerators (or, as they're called in
415 Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
416 Ctrl+<key> is an accelerator). */
417 accel_group = gtk_accel_group_new();
418 gtk_window_add_accel_group(GTK_WINDOW(main_vb), accel_group);
422 top_label = gtk_label_new ("Choose packets to include in the graph");
423 gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
426 gtk_widget_show(flow_graph_dlg_w);
428 /*** Packet range frame ***/
429 range_fr = gtk_frame_new("Choose packets");
430 gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
432 range_tb = gtk_table_new(4, 4, FALSE);
433 gtk_container_border_width(GTK_CONTAINER(range_tb), 5);
434 gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
436 /* Process all packets */
437 select_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_All packets", accel_group);
438 gtk_tooltips_set_tip (tooltips, select_all_rb,
439 ("Process all packets"), NULL);
440 SIGNAL_CONNECT(select_all_rb, "toggled", toggle_select_all, NULL);
441 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
442 if (type_of_packets == ALL) {
443 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
445 gtk_widget_show(select_all_rb);
447 /* Process displayed packets */
448 select_displayed_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Displayed packets", accel_group);
449 gtk_tooltips_set_tip (tooltips, select_displayed_rb,
450 ("Process displayed packets"), NULL);
451 SIGNAL_CONNECT(select_displayed_rb, "toggled", toggle_select_displayed, NULL);
452 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 1, 2, 0, 1);
453 if (type_of_packets == DISPLAYED) {
454 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
456 gtk_widget_show(select_displayed_rb);
458 gtk_widget_show(range_tb);
460 gtk_widget_show(range_fr);
462 /*** Flow type frame ***/
463 flow_type_fr = gtk_frame_new("Choose flow type");
464 gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 0);
466 flow_type_tb = gtk_table_new(4, 4, FALSE);
467 gtk_container_border_width(GTK_CONTAINER(flow_type_tb), 5);
468 gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
470 /* General information */
471 select_general_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_General flow", accel_group);
472 gtk_tooltips_set_tip (tooltips, select_general_rb,
473 ("Show all packets, with general information"), NULL);
474 SIGNAL_CONNECT(select_general_rb, "toggled", toggle_select_general, NULL);
475 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
476 if (type_of_flow == GENERAL) {
477 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
479 gtk_widget_show(select_general_rb);
481 /* TCP specific information */
482 select_tcp_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_general_rb, "_TCP flow", accel_group);
483 gtk_tooltips_set_tip (tooltips, select_tcp_rb,
484 ("Show only TCP packets, with TCP specific information"), NULL);
485 SIGNAL_CONNECT(select_tcp_rb, "toggled", toggle_select_tcp, NULL);
486 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 1, 2, 0, 1);
487 if (type_of_flow == TCP) {
488 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
491 gtk_widget_show(select_tcp_rb);
493 gtk_widget_show(flow_type_tb);
494 gtk_widget_show(flow_type_fr);
497 hbuttonbox = gtk_hbutton_box_new ();
498 gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
499 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
500 gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
502 bt_ok = BUTTON_NEW_FROM_STOCK(GTK_STOCK_OK);
503 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
504 gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
505 SIGNAL_CONNECT(bt_ok, "clicked", flow_graph_on_ok, NULL);
506 gtk_widget_show(bt_ok);
508 bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
509 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
510 GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
511 gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
512 window_set_cancel_button(flow_graph_dlg_w, bt_close, window_cancel_button_cb);
514 SIGNAL_CONNECT(flow_graph_dlg_w, "delete_event", window_delete_event_cb, NULL);
515 SIGNAL_CONNECT(flow_graph_dlg_w, "destroy", flow_graph_on_destroy, NULL);
517 gtk_widget_show_all(flow_graph_dlg_w);
518 window_present(flow_graph_dlg_w);
520 flow_graph_dlg = flow_graph_dlg_w;
523 /****************************************************************************/
525 /****************************************************************************/
527 /* init function for tap */
529 flow_graph_init_tap(const char *dummy _U_)
532 /* initialize graph items store */
533 flow_graph_data_init();
535 /* init the Graph Analysys */
536 graph_analysis_data = graph_analysis_init();
537 graph_analysis_data->graph_info = graph_analysis;
539 /* create dialog box if necessary */
540 if (flow_graph_dlg == NULL) {
541 flow_graph_dlg_create();
543 /* There's already a dialog box; reactivate it. */
544 reactivate_window(flow_graph_dlg);
550 /****************************************************************************/
551 /* entry point when called via the GTK menu */
552 static void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
554 flow_graph_init_tap("");
557 /****************************************************************************/
559 register_tap_listener_flow_graph(void)
561 register_stat_cmd_arg("flow_graph",flow_graph_init_tap);
562 register_stat_menu_item("Flo_w Graph...", REGISTER_STAT_GROUP_NONE,
563 flow_graph_launch, NULL, NULL, NULL);