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 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include <epan/epan.h>
35 #include <epan/packet.h>
36 #include <epan/filesystem.h>
37 #include <epan/stat_cmd_args.h>
38 #include <epan/to_str.h>
40 #include <epan/dissectors/packet-tcp.h>
41 #include <epan/strutil.h>
43 #include "../stat_menu.h"
44 #include "ui/simple_dialog.h"
46 #include "ui/gtk/graph_analysis.h"
47 #include "ui/gtk/gui_stat_menu.h"
48 #include "ui/gtk/dlg_utils.h"
49 #include "ui/gtk/gui_utils.h"
50 #include "ui/gtk/stock_icons.h"
51 #include "ui/gtk/gtkglobals.h"
52 #include "ui/gtk/main.h"
53 #include "ui/gtk/old-gtk-compat.h"
56 #define TYPE_OF_PACKETS_DISPLAYED 0
57 #define TYPE_OF_PACKETS_ALL 1
59 #define TYPE_OF_FLOW_GENERAL 0
60 #define TYPE_OF_FLOW_TCP 1
62 #define NODE_ADDR_TYPE_SRCDST 0
63 #define NODE_ADDR_TYPE_NET_SRCDST 1
65 static int type_of_packets = TYPE_OF_PACKETS_DISPLAYED;
66 static int type_of_flow = TYPE_OF_FLOW_GENERAL;
67 static int node_addr_type = NODE_ADDR_TYPE_SRCDST;
69 static int tap_identifier;
70 static gboolean have_frame_tap_listener=FALSE;
71 static gboolean have_tcp_tap_listener=FALSE;
72 static graph_analysis_info_t *graph_analysis = NULL;
73 static graph_analysis_data_t *graph_analysis_data = NULL;
75 static GtkWidget *flow_graph_dlg = NULL;
77 static GtkWidget *select_all_rb;
78 static GtkWidget *select_displayed_rb;
79 static GtkWidget *select_general_rb;
80 static GtkWidget *select_tcp_rb;
81 static GtkWidget *src_dst_rb;
82 static GtkWidget *net_src_dst_rb;
84 /****************************************************************************/
85 /* free up memory and initialize the pointers */
88 flow_graph_reset(void *ptr _U_)
90 graph_analysis_item_t *graph_item;
94 if (graph_analysis !=NULL){
96 /* free the graph data items */
97 list = g_list_first(graph_analysis->list);
100 graph_item = list->data;
101 g_free(graph_item->frame_label);
102 g_free(graph_item->comment);
104 list = g_list_next(list);
106 g_list_free(graph_analysis->list);
107 graph_analysis->nconv = 0;
108 graph_analysis->list = NULL;
112 /****************************************************************************/
114 flow_graph_data_init(void) {
115 graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
116 graph_analysis->nconv = 0;
117 graph_analysis->list = NULL;
121 /****************************************************************************/
123 remove_tap_listener_flow_graph(void)
125 protect_thread_critical_region();
126 remove_tap_listener(&(tap_identifier));
127 unprotect_thread_critical_region();
129 have_frame_tap_listener=FALSE;
130 have_tcp_tap_listener=FALSE;
134 /****************************************************************************/
136 /****************************************************************************/
138 flow_graph_on_destroy(GObject *object _U_, gpointer user_data _U_)
140 /* remove_tap_listeners */
141 remove_tap_listener_flow_graph();
143 /* Clean up memory used by tap */
144 flow_graph_reset(NULL);
146 g_assert(graph_analysis != NULL);
147 g_assert(graph_analysis_data != NULL);
149 g_free(graph_analysis);
150 graph_analysis = NULL;
152 g_free(graph_analysis_data);
153 graph_analysis_data = NULL;
155 /* Note that we no longer have a "Flow Graph" dialog box. */
156 flow_graph_dlg = NULL;
160 /****************************************************************************/
162 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
164 /* is the button now active? */
165 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
166 type_of_packets = TYPE_OF_PACKETS_ALL;
170 /****************************************************************************/
172 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
174 /* is the button now active? */
175 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
176 type_of_packets = TYPE_OF_PACKETS_DISPLAYED;
180 /****************************************************************************/
182 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
184 /* is the button now active? */
185 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
186 type_of_flow = TYPE_OF_FLOW_GENERAL;
190 /****************************************************************************/
192 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
194 /* is the button now active? */
195 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
196 type_of_flow = TYPE_OF_FLOW_TCP;
200 /****************************************************************************/
202 toggle_select_srcdst(GtkWidget *widget _U_, gpointer user_data _U_)
204 /* is the button now active? */
205 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(src_dst_rb))) {
206 node_addr_type = NODE_ADDR_TYPE_SRCDST;
210 /****************************************************************************/
212 toggle_select_netsrcdst(GtkWidget *widget _U_, gpointer user_data _U_)
214 /* is the button now active? */
215 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(net_src_dst_rb))) {
216 node_addr_type = NODE_ADDR_TYPE_NET_SRCDST;
220 /****************************************************************************/
221 /* Add a new frame into the graph */
223 flow_graph_frame_add_to_graph(packet_info *pinfo)
225 graph_analysis_item_t *gai;
233 if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
234 if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
235 gai = g_malloc(sizeof(graph_analysis_item_t));
236 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
237 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
242 if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
243 gai = g_malloc(sizeof(graph_analysis_item_t));
244 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
245 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
252 gai->port_src=pinfo->srcport;
253 gai->port_dst=pinfo->destport;
255 gai->frame_label=NULL;
257 /* this code doesn't make sense.
258 g_free(gai->comment);
259 g_free(gai->frame_label);
263 if (pinfo->cinfo->col_first[COL_INFO]>=0){
265 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
266 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
267 colinfo = g_strdup(pinfo->cinfo->col_data[i]);
268 /* break; ? or g_free(colinfo); before g_strdup() */
273 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
275 for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
276 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
277 protocol = g_strdup(pinfo->cinfo->col_data[i]);
278 /* break; ? or g_free(protocol); before g_strdup() */
284 if (colinfo != NULL) {
285 if (protocol != NULL) {
286 gai->frame_label = g_strdup_printf("%.19s", colinfo);
287 gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
289 gai->frame_label = g_strdup_printf("%.19s", colinfo);
290 gai->comment = g_strdup_printf("%s", colinfo);
293 /* This will probably never happen...*/
294 if (protocol != NULL) {
295 gai->frame_label = g_strdup_printf("%.19s", protocol);
296 gai->comment = g_strdup_printf("%s", protocol);
307 graph_analysis->list = g_list_append(graph_analysis->list, gai);
312 /****************************************************************************/
313 /* Add a new tcp frame into the graph */
315 flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
317 graph_analysis_item_t *gai;
318 /* copied from packet-tcp */
319 const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
321 gboolean flags_found = FALSE;
324 gai = g_malloc(sizeof(graph_analysis_item_t));
326 if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
327 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
328 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
330 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
331 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
333 gai->port_src=pinfo->srcport;
334 gai->port_dst=pinfo->destport;
337 for (i = 0; i < 8; i++) {
339 if (tcph->th_flags & bpos) {
341 g_strlcat(flags, ", ", sizeof(flags));
343 g_strlcat(flags, fstr[i], sizeof(flags));
347 if (flags[0] == '\0') {
348 g_snprintf (flags, sizeof(flags), "<None>");
351 if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
352 gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
355 gai->frame_label = g_strdup(flags);
358 if (tcph->th_flags & TH_ACK)
359 gai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
361 gai->comment = g_strdup_printf("Seq = %u",tcph->th_seq);
367 graph_analysis->list = g_list_append(graph_analysis->list, gai);
374 /****************************************************************************/
375 /* whenever a frame packet is seen by the tap listener */
377 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
379 if ((type_of_packets == TYPE_OF_PACKETS_ALL)||(pinfo->fd->flags.passed_dfilter==1)){
380 flow_graph_frame_add_to_graph(pinfo);
386 /****************************************************************************/
387 /* whenever a TCP packet is seen by the tap listener */
389 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
391 const struct tcpheader *tcph = tcp_info;
393 if ((type_of_packets == TYPE_OF_PACKETS_ALL)||(pinfo->fd->flags.passed_dfilter==1)){
394 flow_graph_tcp_add_to_graph(pinfo,tcph);
402 flow_graph_packet_draw(void *prs _U_)
407 /****************************************************************************/
409 flow_graph_on_ok (GtkButton *button _U_,
412 if ((have_frame_tap_listener==TRUE)
413 ||(have_tcp_tap_listener==TRUE))
415 /* remove_tap_listeners */
416 remove_tap_listener_flow_graph();
419 /* Scan for displayed packets (retap all packets) */
421 if (type_of_flow == TYPE_OF_FLOW_GENERAL){
422 /* Register the tap listener */
424 if(have_frame_tap_listener==FALSE)
426 /* don't register tap listener, if we have it already */
427 register_tap_listener("frame", &tap_identifier, NULL,
430 flow_graph_frame_packet,
431 flow_graph_packet_draw
433 have_frame_tap_listener=TRUE;
436 cf_retap_packets(&cfile);
438 else if (type_of_flow == TYPE_OF_FLOW_TCP){
439 /* Register the tap listener */
441 if(have_tcp_tap_listener==FALSE)
443 /* don't register tap listener, if we have it already */
444 register_tap_listener("tcp", &tap_identifier, NULL,
447 flow_graph_tcp_packet,
448 flow_graph_packet_draw
450 have_tcp_tap_listener=TRUE;
453 cf_retap_packets(&cfile);
456 if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
457 graph_analysis_update(graph_analysis_data); /* refresh it xxx */
460 graph_analysis_data->dlg.parent_w = user_data;
461 graph_analysis_create(graph_analysis_data);
466 /****************************************************************************/
468 /****************************************************************************/
471 flow_graph_dlg_create (void)
473 GtkWidget *flow_graph_dlg_w;
475 GtkWidget *hbuttonbox;
476 GtkWidget *bt_cancel, *bt_ok;
478 GtkWidget *top_label = NULL;
480 GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb, *node_addr_fr, *node_addr_tb;
482 flow_graph_dlg_w = dlg_window_new("Wireshark: Flow Graph"); /* transient_for top_level */
483 gtk_window_set_destroy_with_parent (GTK_WINDOW(flow_graph_dlg_w), TRUE);
485 gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 250, 150);
487 main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
488 gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
489 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 7);
492 top_label = gtk_label_new ("Choose packets to include in the graph");
493 gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
496 gtk_widget_show(flow_graph_dlg_w);
498 /*** Packet range frame ***/
499 range_fr = gtk_frame_new("Choose packets");
500 gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 5);
502 range_tb = gtk_table_new(4, 4, FALSE);
503 gtk_container_set_border_width(GTK_CONTAINER(range_tb), 5);
504 gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
506 /* Process all packets */
507 select_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_All packets");
508 gtk_widget_set_tooltip_text (select_all_rb, ("Process all packets"));
509 g_signal_connect(select_all_rb, "toggled", G_CALLBACK(toggle_select_all), NULL);
510 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
511 if (type_of_packets == TYPE_OF_PACKETS_ALL) {
512 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
514 gtk_widget_show(select_all_rb);
516 /* Process displayed packets */
517 select_displayed_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb),
518 "_Displayed packets");
519 gtk_widget_set_tooltip_text (select_displayed_rb, ("Process displayed packets"));
520 g_signal_connect(select_displayed_rb, "toggled", G_CALLBACK(toggle_select_displayed), NULL);
521 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 0, 1, 1, 2);
522 if (type_of_packets == TYPE_OF_PACKETS_DISPLAYED) {
523 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
525 gtk_widget_show(select_displayed_rb);
527 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, 5);
534 flow_type_tb = gtk_table_new(4, 4, FALSE);
535 gtk_container_set_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 = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_General flow");
540 gtk_widget_set_tooltip_text (select_general_rb, ("Show all packets, with general information"));
541 g_signal_connect(select_general_rb, "toggled", G_CALLBACK(toggle_select_general), NULL);
542 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
543 if (type_of_flow == TYPE_OF_FLOW_GENERAL) {
544 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
546 gtk_widget_show(select_general_rb);
548 /* TCP specific information */
549 select_tcp_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_general_rb),
551 gtk_widget_set_tooltip_text (select_tcp_rb, ("Show only TCP packets, with TCP specific information"));
552 g_signal_connect(select_tcp_rb, "toggled", G_CALLBACK(toggle_select_tcp), NULL);
553 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 0, 1, 1, 2);
554 if (type_of_flow == TYPE_OF_FLOW_TCP) {
555 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
557 gtk_widget_show(select_tcp_rb);
559 gtk_widget_show(flow_type_tb);
560 gtk_widget_show(flow_type_fr);
562 /*** Node address type frame ***/
563 node_addr_fr = gtk_frame_new("Choose node address type");
564 gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 5);
566 node_addr_tb = gtk_table_new(4, 4, FALSE);
567 gtk_container_set_border_width(GTK_CONTAINER(node_addr_tb), 5);
568 gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
570 /* Source / Dest address */
571 src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Standard source/destination addresses");
572 gtk_widget_set_tooltip_text (src_dst_rb,
573 ("Nodes in the diagram are identified with source and destination addresses"));
574 g_signal_connect(src_dst_rb, "toggled", G_CALLBACK(toggle_select_srcdst), NULL);
575 gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
576 if (node_addr_type == NODE_ADDR_TYPE_SRCDST) {
577 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
579 gtk_widget_show(src_dst_rb);
581 /* Network source / dest address */
582 net_src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(src_dst_rb),
583 "_Network source/destination addresses");
584 gtk_widget_set_tooltip_text (net_src_dst_rb,
585 ("Nodes in the diagram are identified with network source and destination addresses"));
586 g_signal_connect(net_src_dst_rb, "toggled", G_CALLBACK(toggle_select_netsrcdst), NULL);
587 gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 0, 1, 1, 2);
588 if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
589 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
591 gtk_widget_show(net_src_dst_rb);
593 gtk_widget_show(node_addr_tb);
594 gtk_widget_show(node_addr_fr);
597 hbuttonbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
598 gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 5);
599 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
600 gtk_box_set_spacing (GTK_BOX (hbuttonbox), 30);
602 bt_ok = gtk_button_new_from_stock(GTK_STOCK_OK);
603 gtk_box_pack_start(GTK_BOX(hbuttonbox), bt_ok, TRUE, TRUE, 0);
604 gtk_widget_set_tooltip_text (bt_ok, "Show the flow graph");
605 g_signal_connect(bt_ok, "clicked", G_CALLBACK(flow_graph_on_ok), flow_graph_dlg_w);
606 gtk_widget_show(bt_ok);
608 bt_cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
609 gtk_box_pack_start(GTK_BOX(hbuttonbox), bt_cancel, TRUE, TRUE, 0);
610 gtk_widget_set_can_default(bt_cancel, TRUE);
611 gtk_widget_set_tooltip_text (bt_cancel, "Cancel this dialog");
612 window_set_cancel_button(flow_graph_dlg_w, bt_cancel, window_cancel_button_cb);
614 g_signal_connect(flow_graph_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
615 g_signal_connect(flow_graph_dlg_w, "destroy", G_CALLBACK(flow_graph_on_destroy), NULL);
617 gtk_widget_show_all(flow_graph_dlg_w);
618 window_present(flow_graph_dlg_w);
620 flow_graph_dlg = flow_graph_dlg_w;
623 /****************************************************************************/
625 /****************************************************************************/
627 /* init function for tap */
629 flow_graph_init_tap(const char *dummy _U_, void* userdata _U_)
631 /* The storage allocated by flow_graph_data_init() and graph_analysis_init() */
632 /* will be considered to be "associated with" the flow_graph_dlg dialog box. */
633 /* It will be freed when the flow_graph_dlg dialog box is destroyed. */
634 if (flow_graph_dlg != NULL) {
635 g_assert(graph_analysis != NULL);
636 g_assert(graph_analysis_data != NULL);
637 /* There's already a dialog box; reactivate it. */
638 reactivate_window(flow_graph_dlg);
640 g_assert(graph_analysis == NULL);
641 g_assert(graph_analysis_data == NULL);
643 /* initialize graph items store */
644 flow_graph_data_init();
646 /* init the Graph Analysis */
647 graph_analysis_data = graph_analysis_init();
648 graph_analysis_data->graph_info = graph_analysis;
650 flow_graph_dlg_create();
655 /****************************************************************************/
656 /* entry point when called via the GTK menu */
658 flow_graph_launch(GtkAction *action _U_, gpointer user_data _U_)
660 flow_graph_init_tap("",NULL);
663 /****************************************************************************/
665 register_tap_listener_flow_graph(void)
667 register_stat_cmd_arg("flow_graph",flow_graph_init_tap,NULL);