3 * Allows to display a flow graph of the currently displayed packets
5 * Copyright 2004, Ericsson , Spain
6 * By Francisco Alcoba <francisco.alcoba@ericsson.com>
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@ethereal.com>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include "epan/filesystem.h"
36 #include "graph_analysis.h"
37 #include <epan/stat_cmd_args.h>
38 #include "../stat_menu.h"
39 #include "gui_stat_menu.h"
40 #include "dlg_utils.h"
41 #include "gui_utils.h"
42 #include "compat_macros.h"
43 #include "gtkglobals.h"
45 #include "simple_dialog.h"
47 #include <epan/to_str.h>
49 #include <epan/dissectors/packet-tcp.h>
60 static int type_of_packets = DISPLAYED;
61 static int type_of_flow = GENERAL;
62 static int node_addr_type = SRCDST;
64 static int tap_identifier;
65 static gboolean have_frame_tap_listener=FALSE;
66 static gboolean have_tcp_tap_listener=FALSE;
67 static graph_analysis_info_t *graph_analysis = NULL;
68 static graph_analysis_data_t *graph_analysis_data;
70 static GtkWidget *flow_graph_dlg = NULL;
72 static GtkWidget *select_all_rb;
73 static GtkWidget *select_displayed_rb;
74 static GtkWidget *select_general_rb;
75 static GtkWidget *select_tcp_rb;
76 static GtkWidget *src_dst_rb;
77 static GtkWidget *net_src_dst_rb;
79 void flow_graph_data_init(void);
82 /****************************************************************************/
83 /* free up memory and initialize the pointers */
85 static void flow_graph_reset(void *ptr _U_)
88 graph_analysis_item_t *graph_item;
92 if (graph_analysis !=NULL){
94 /* free the graph data items */
95 list = g_list_first(graph_analysis->list);
98 graph_item = list->data;
99 g_free(graph_item->frame_label);
100 g_free(graph_item->comment);
102 list = g_list_next(list);
104 g_list_free(graph_analysis->list);
105 graph_analysis->nconv = 0;
106 graph_analysis->list = NULL;
111 /****************************************************************************/
112 void flow_graph_data_init(void) {
113 graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
114 graph_analysis->nconv = 0;
115 graph_analysis->list = NULL;
119 /* XXX just copied from gtk/rpc_stat.c */
120 void protect_thread_critical_region(void);
121 void unprotect_thread_critical_region(void);
123 /****************************************************************************/
125 remove_tap_listener_flow_graph(void)
127 protect_thread_critical_region();
128 remove_tap_listener(&(tap_identifier));
129 unprotect_thread_critical_region();
131 have_frame_tap_listener=FALSE;
132 have_tcp_tap_listener=FALSE;
136 /****************************************************************************/
138 /****************************************************************************/
140 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
142 /* remove_tap_listeners */
143 remove_tap_listener_flow_graph();
145 /* Clean up memory used by tap */
146 flow_graph_reset(NULL);
148 /* Note that we no longer have a "Flow Graph" dialog box. */
149 flow_graph_dlg = NULL;
153 /****************************************************************************/
155 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
157 /* is the button now active? */
158 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
159 type_of_packets = ALL;
163 /****************************************************************************/
165 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
167 /* is the button now active? */
168 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
169 type_of_packets = DISPLAYED;
173 /****************************************************************************/
175 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
177 /* is the button now active? */
178 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
179 type_of_flow = GENERAL;
183 /****************************************************************************/
185 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
187 /* is the button now active? */
188 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
193 /****************************************************************************/
195 toggle_select_srcdst(GtkWidget *widget _U_, gpointer user_data _U_)
197 /* is the button now active? */
198 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(src_dst_rb))) {
199 node_addr_type = SRCDST;
203 /****************************************************************************/
205 toggle_select_netsrcdst(GtkWidget *widget _U_, gpointer user_data _U_)
207 /* is the button now active? */
208 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(net_src_dst_rb))) {
209 node_addr_type = NET_SRCDST;
213 /****************************************************************************/
214 /* Add a new frame into the graph */
215 static int flow_graph_frame_add_to_graph(packet_info *pinfo)
217 graph_analysis_item_t *gai;
225 gai = g_malloc(sizeof(graph_analysis_item_t));
226 gai->frame_num = pinfo->fd->num;
227 gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
228 if (node_addr_type == NET_SRCDST) {
229 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
230 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
232 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
233 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
235 gai->port_src=pinfo->srcport;
236 gai->port_dst=pinfo->destport;
238 gai->frame_label=NULL;
240 if (gai->comment!=NULL){
241 g_free(gai->comment);
243 if (gai->frame_label!=NULL){
244 g_free(gai->frame_label);
248 if (pinfo->cinfo->col_first[COL_INFO]>=0){
250 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
251 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
252 colinfo = g_strdup(pinfo->cinfo->col_data[i]);
257 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
259 for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
260 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
261 protocol = g_strdup(pinfo->cinfo->col_data[i]);
267 if (colinfo != NULL) {
268 if (protocol != NULL) {
269 gai->frame_label = g_strdup_printf("%.19s", colinfo);
270 gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
272 gai->frame_label = g_strdup_printf("%.19s", colinfo);
273 gai->comment = g_strdup_printf("%s", colinfo);
276 /* This will probably never happen...*/
277 if (protocol != NULL) {
278 gai->frame_label = g_strdup_printf("%.19s", protocol);
279 gai->comment = g_strdup_printf("%s", protocol);
281 gai->frame_label = NULL;
297 graph_analysis->list = g_list_append(graph_analysis->list, gai);
303 /****************************************************************************/
304 /* Add a new tcp frame into the graph */
305 static int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
307 graph_analysis_item_t *gai;
308 /* copied from packet-tcp */
309 const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
312 gchar flags[64] = "<None>";
314 gai = g_malloc(sizeof(graph_analysis_item_t));
315 gai->frame_num = pinfo->fd->num;
316 gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
317 if (node_addr_type == NET_SRCDST) {
318 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
319 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
321 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
322 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
324 gai->port_src=pinfo->srcport;
325 gai->port_dst=pinfo->destport;
327 for (i = 0; i < 8; i++) {
329 if (tcph->th_flags & bpos) {
331 strcpy(&flags[fpos], ", ");
334 strcpy(&flags[fpos], fstr[i]);
339 if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
340 gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
343 gai->frame_label = g_strdup(flags);
346 gai->comment = g_strdup_printf("Seq = %i Ack = %i",tcph->th_seq, tcph->th_ack);
352 graph_analysis->list = g_list_append(graph_analysis->list, gai);
360 /****************************************************************************/
361 /* whenever a frame packet is seen by the tap listener */
363 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
366 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
367 flow_graph_frame_add_to_graph(pinfo);
373 /****************************************************************************/
374 /* whenever a TCP packet is seen by the tap listener */
376 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
378 const struct tcpheader *tcph = tcp_info;
380 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
381 flow_graph_tcp_add_to_graph(pinfo,tcph);
388 static void flow_graph_packet_draw(void *prs _U_)
393 /****************************************************************************/
395 flow_graph_on_ok (GtkButton *button _U_,
396 gpointer user_data _U_)
399 if ((have_frame_tap_listener==TRUE)
400 ||(have_tcp_tap_listener==TRUE))
402 /* remove_tap_listeners */
403 remove_tap_listener_flow_graph();
406 /* Scan for displayed packets (retap all packets) */
408 if (type_of_flow == GENERAL){
409 /* Register the tap listener */
411 if(have_frame_tap_listener==FALSE)
413 /* don't register tap listener, if we have it already */
414 register_tap_listener("frame", &tap_identifier, NULL,
416 flow_graph_frame_packet,
417 flow_graph_packet_draw
419 have_frame_tap_listener=TRUE;
422 cf_retap_packets(&cfile, TRUE);
424 else if (type_of_flow == TCP){
425 /* Register the tap listener */
427 if(have_tcp_tap_listener==FALSE)
429 /* don't register tap listener, if we have it already */
430 register_tap_listener("tcp", &tap_identifier, NULL,
432 flow_graph_tcp_packet,
433 flow_graph_packet_draw
435 have_tcp_tap_listener=TRUE;
438 cf_retap_packets(&cfile, FALSE);
441 if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
442 graph_analysis_update(graph_analysis_data); /* refresh it xxx */
445 graph_analysis_create(graph_analysis_data);
451 /****************************************************************************/
453 /****************************************************************************/
455 static void flow_graph_dlg_create (void)
458 GtkWidget *flow_graph_dlg_w;
460 GtkWidget *hbuttonbox;
461 GtkWidget *bt_close, *bt_ok;
463 GtkWidget *top_label = NULL;
465 GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb, *node_addr_fr, *node_addr_tb;
466 #if GTK_MAJOR_VERSION < 2
467 GtkAccelGroup *accel_group;
471 GtkTooltips *tooltips = gtk_tooltips_new();
473 flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Flow Graph");
475 gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 350, 150);
477 main_vb = gtk_vbox_new (FALSE, 0);
478 gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
479 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
481 #if GTK_MAJOR_VERSION < 2
482 /* Accelerator group for the accelerators (or, as they're called in
483 Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
484 Ctrl+<key> is an accelerator). */
485 accel_group = gtk_accel_group_new();
486 gtk_window_add_accel_group(GTK_WINDOW(main_vb), accel_group);
490 top_label = gtk_label_new ("Choose packets to include in the graph");
491 gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
494 gtk_widget_show(flow_graph_dlg_w);
496 /*** Packet range frame ***/
497 range_fr = gtk_frame_new("Choose packets");
498 gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
500 range_tb = gtk_table_new(4, 4, FALSE);
501 gtk_container_border_width(GTK_CONTAINER(range_tb), 5);
502 gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
504 /* Process all packets */
505 select_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_All packets", accel_group);
506 gtk_tooltips_set_tip (tooltips, select_all_rb,
507 ("Process all packets"), NULL);
508 SIGNAL_CONNECT(select_all_rb, "toggled", toggle_select_all, NULL);
509 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
510 if (type_of_packets == ALL) {
511 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
513 gtk_widget_show(select_all_rb);
515 /* Process displayed packets */
516 select_displayed_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Displayed packets", accel_group);
517 gtk_tooltips_set_tip (tooltips, select_displayed_rb,
518 ("Process displayed packets"), NULL);
519 SIGNAL_CONNECT(select_displayed_rb, "toggled", toggle_select_displayed, NULL);
520 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 1, 2, 0, 1);
521 if (type_of_packets == DISPLAYED) {
522 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
524 gtk_widget_show(select_displayed_rb);
526 gtk_widget_show(range_tb);
528 gtk_widget_show(range_fr);
530 /*** Flow type frame ***/
531 flow_type_fr = gtk_frame_new("Choose flow type");
532 gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 0);
534 flow_type_tb = gtk_table_new(4, 4, FALSE);
535 gtk_container_border_width(GTK_CONTAINER(flow_type_tb), 5);
536 gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
538 /* General information */
539 select_general_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_General flow", accel_group);
540 gtk_tooltips_set_tip (tooltips, select_general_rb,
541 ("Show all packets, with general information"), NULL);
542 SIGNAL_CONNECT(select_general_rb, "toggled", toggle_select_general, NULL);
543 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
544 if (type_of_flow == GENERAL) {
545 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
547 gtk_widget_show(select_general_rb);
549 /* TCP specific information */
550 select_tcp_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_general_rb, "_TCP flow", accel_group);
551 gtk_tooltips_set_tip (tooltips, select_tcp_rb,
552 ("Show only TCP packets, with TCP specific information"), NULL);
553 SIGNAL_CONNECT(select_tcp_rb, "toggled", toggle_select_tcp, NULL);
554 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 1, 2, 0, 1);
555 if (type_of_flow == TCP) {
556 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
559 gtk_widget_show(select_tcp_rb);
561 gtk_widget_show(flow_type_tb);
562 gtk_widget_show(flow_type_fr);
564 /*** Node address type frame ***/
565 node_addr_fr = gtk_frame_new("Choose node address type");
566 gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 0);
568 node_addr_tb = gtk_table_new(4, 4, FALSE);
569 gtk_container_border_width(GTK_CONTAINER(node_addr_tb), 5);
570 gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
572 /* Source / Dest address */
573 src_dst_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_Standard source/destination addresses", accel_group);
574 gtk_tooltips_set_tip (tooltips, src_dst_rb,
575 ("Nodes in the diagram are identified with source and destination addresses"), NULL);
576 SIGNAL_CONNECT(src_dst_rb, "toggled", toggle_select_srcdst, NULL);
577 gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
578 if (node_addr_type == SRCDST) {
579 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
581 gtk_widget_show(src_dst_rb);
583 /* Network source / dest address */
584 net_src_dst_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(src_dst_rb, "_Network source/destination addresses", accel_group);
585 gtk_tooltips_set_tip (tooltips, net_src_dst_rb,
586 ("Nodes in the diagram are identified with network source and destination addresses"), NULL);
587 SIGNAL_CONNECT(net_src_dst_rb, "toggled", toggle_select_netsrcdst, NULL);
588 gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 1, 2, 0, 1);
589 if (node_addr_type == NET_SRCDST) {
590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
593 gtk_widget_show(net_src_dst_rb);
595 gtk_widget_show(node_addr_tb);
596 gtk_widget_show(node_addr_fr);
599 hbuttonbox = gtk_hbutton_box_new ();
600 gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
601 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
602 gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
604 bt_ok = BUTTON_NEW_FROM_STOCK(GTK_STOCK_OK);
605 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
606 gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
607 SIGNAL_CONNECT(bt_ok, "clicked", flow_graph_on_ok, NULL);
608 gtk_widget_show(bt_ok);
610 bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
611 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
612 GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
613 gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
614 window_set_cancel_button(flow_graph_dlg_w, bt_close, window_cancel_button_cb);
616 SIGNAL_CONNECT(flow_graph_dlg_w, "delete_event", window_delete_event_cb, NULL);
617 SIGNAL_CONNECT(flow_graph_dlg_w, "destroy", flow_graph_on_destroy, NULL);
619 gtk_widget_show_all(flow_graph_dlg_w);
620 window_present(flow_graph_dlg_w);
622 flow_graph_dlg = flow_graph_dlg_w;
625 /****************************************************************************/
627 /****************************************************************************/
629 /* init function for tap */
631 flow_graph_init_tap(const char *dummy _U_)
634 /* initialize graph items store */
635 flow_graph_data_init();
637 /* init the Graph Analysys */
638 graph_analysis_data = graph_analysis_init();
639 graph_analysis_data->graph_info = graph_analysis;
641 /* create dialog box if necessary */
642 if (flow_graph_dlg == NULL) {
643 flow_graph_dlg_create();
645 /* There's already a dialog box; reactivate it. */
646 reactivate_window(flow_graph_dlg);
652 /****************************************************************************/
653 /* entry point when called via the GTK menu */
654 static void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
656 flow_graph_init_tap("");
659 /****************************************************************************/
661 register_tap_listener_flow_graph(void)
663 register_stat_cmd_arg("flow_graph",flow_graph_init_tap);
664 register_stat_menu_item("Flo_w Graph...", REGISTER_STAT_GROUP_NONE,
665 flow_graph_launch, NULL, NULL, NULL);