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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "../register.h"
44 #include "../globals.h"
45 #include "../stat_menu.h"
46 #include "../simple_dialog.h"
48 #include "gtk/graph_analysis.h"
49 #include "gtk/gui_stat_menu.h"
50 #include "gtk/dlg_utils.h"
51 #include "gtk/gui_utils.h"
52 #include "gtk/stock_icons.h"
53 #include "gtk/gtkglobals.h"
65 static int type_of_packets = DISPLAYED;
66 static int type_of_flow = GENERAL;
67 static int 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;
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 void flow_graph_data_init(void);
87 /****************************************************************************/
88 /* free up memory and initialize the pointers */
90 static void flow_graph_reset(void *ptr _U_)
93 graph_analysis_item_t *graph_item;
97 if (graph_analysis !=NULL){
99 /* free the graph data items */
100 list = g_list_first(graph_analysis->list);
103 graph_item = list->data;
104 g_free(graph_item->frame_label);
105 g_free(graph_item->comment);
107 list = g_list_next(list);
109 g_list_free(graph_analysis->list);
110 graph_analysis->nconv = 0;
111 graph_analysis->list = NULL;
116 /****************************************************************************/
117 void flow_graph_data_init(void) {
118 graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
119 graph_analysis->nconv = 0;
120 graph_analysis->list = NULL;
125 /****************************************************************************/
127 remove_tap_listener_flow_graph(void)
129 protect_thread_critical_region();
130 remove_tap_listener(&(tap_identifier));
131 unprotect_thread_critical_region();
133 have_frame_tap_listener=FALSE;
134 have_tcp_tap_listener=FALSE;
138 /****************************************************************************/
140 /****************************************************************************/
142 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
144 /* remove_tap_listeners */
145 remove_tap_listener_flow_graph();
147 /* Clean up memory used by tap */
148 flow_graph_reset(NULL);
150 /* Note that we no longer have a "Flow Graph" dialog box. */
151 flow_graph_dlg = NULL;
155 /****************************************************************************/
157 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
159 /* is the button now active? */
160 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
161 type_of_packets = ALL;
165 /****************************************************************************/
167 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
169 /* is the button now active? */
170 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
171 type_of_packets = DISPLAYED;
175 /****************************************************************************/
177 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
179 /* is the button now active? */
180 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
181 type_of_flow = GENERAL;
185 /****************************************************************************/
187 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
189 /* is the button now active? */
190 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
195 /****************************************************************************/
197 toggle_select_srcdst(GtkWidget *widget _U_, gpointer user_data _U_)
199 /* is the button now active? */
200 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(src_dst_rb))) {
201 node_addr_type = SRCDST;
205 /****************************************************************************/
207 toggle_select_netsrcdst(GtkWidget *widget _U_, gpointer user_data _U_)
209 /* is the button now active? */
210 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(net_src_dst_rb))) {
211 node_addr_type = NET_SRCDST;
215 /****************************************************************************/
216 /* Add a new frame into the graph */
217 static int flow_graph_frame_add_to_graph(packet_info *pinfo)
219 graph_analysis_item_t *gai;
227 if (node_addr_type == NET_SRCDST) {
228 if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
229 gai = g_malloc(sizeof(graph_analysis_item_t));
230 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
231 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
236 if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
237 gai = g_malloc(sizeof(graph_analysis_item_t));
238 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
239 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
244 gai->frame_num = pinfo->fd->num;
245 gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
247 gai->port_src=pinfo->srcport;
248 gai->port_dst=pinfo->destport;
250 gai->frame_label=NULL;
252 /* this code doesn't make sense.
253 g_free(gai->comment);
254 g_free(gai->frame_label);
258 if (pinfo->cinfo->col_first[COL_INFO]>=0){
260 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
261 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
262 colinfo = g_strdup(pinfo->cinfo->col_data[i]);
263 /* break; ? or g_free(colinfo); before g_strdup() */
268 if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
270 for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
271 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
272 protocol = g_strdup(pinfo->cinfo->col_data[i]);
273 /* break; ? or g_free(protocol); before g_strdup() */
279 if (colinfo != NULL) {
280 if (protocol != NULL) {
281 gai->frame_label = g_strdup_printf("%.19s", colinfo);
282 gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
284 gai->frame_label = g_strdup_printf("%.19s", colinfo);
285 gai->comment = g_strdup_printf("%s", colinfo);
288 /* This will probably never happen...*/
289 if (protocol != NULL) {
290 gai->frame_label = g_strdup_printf("%.19s", protocol);
291 gai->comment = g_strdup_printf("%s", protocol);
302 graph_analysis->list = g_list_append(graph_analysis->list, gai);
308 /****************************************************************************/
309 /* Add a new tcp frame into the graph */
310 static int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
312 graph_analysis_item_t *gai;
313 /* copied from packet-tcp */
314 const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
316 gboolean flags_found = FALSE;
319 gai = g_malloc(sizeof(graph_analysis_item_t));
320 gai->frame_num = pinfo->fd->num;
321 gai->time= nstime_to_sec(&pinfo->fd->rel_ts);
322 if (node_addr_type == NET_SRCDST) {
323 COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
324 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
326 COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
327 COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
329 gai->port_src=pinfo->srcport;
330 gai->port_dst=pinfo->destport;
333 for (i = 0; i < 8; i++) {
335 if (tcph->th_flags & bpos) {
337 g_strlcat(flags, ", ", sizeof(flags));
339 g_strlcat(flags, fstr[i], sizeof(flags));
343 if (flags[0] == '\0') {
344 g_snprintf (flags, sizeof(flags), "<None>");
347 if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
348 gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
351 gai->frame_label = g_strdup(flags);
354 gai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
360 graph_analysis->list = g_list_append(graph_analysis->list, gai);
368 /****************************************************************************/
369 /* whenever a frame packet is seen by the tap listener */
371 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
374 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
375 flow_graph_frame_add_to_graph(pinfo);
381 /****************************************************************************/
382 /* whenever a TCP packet is seen by the tap listener */
384 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
386 const struct tcpheader *tcph = tcp_info;
388 if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
389 flow_graph_tcp_add_to_graph(pinfo,tcph);
396 static void flow_graph_packet_draw(void *prs _U_)
401 /****************************************************************************/
403 flow_graph_on_ok (GtkButton *button _U_,
407 if ((have_frame_tap_listener==TRUE)
408 ||(have_tcp_tap_listener==TRUE))
410 /* remove_tap_listeners */
411 remove_tap_listener_flow_graph();
414 /* Scan for displayed packets (retap all packets) */
416 if (type_of_flow == GENERAL){
417 /* Register the tap listener */
419 if(have_frame_tap_listener==FALSE)
421 /* don't register tap listener, if we have it already */
422 register_tap_listener("frame", &tap_identifier, NULL,
424 flow_graph_frame_packet,
425 flow_graph_packet_draw
427 have_frame_tap_listener=TRUE;
430 cf_retap_packets(&cfile, TRUE);
432 else if (type_of_flow == TCP){
433 /* Register the tap listener */
435 if(have_tcp_tap_listener==FALSE)
437 /* don't register tap listener, if we have it already */
438 register_tap_listener("tcp", &tap_identifier, NULL,
440 flow_graph_tcp_packet,
441 flow_graph_packet_draw
443 have_tcp_tap_listener=TRUE;
446 cf_retap_packets(&cfile, FALSE);
449 if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
450 graph_analysis_update(graph_analysis_data); /* refresh it xxx */
453 graph_analysis_data->dlg.parent_w = user_data;
454 graph_analysis_create(graph_analysis_data);
460 /****************************************************************************/
462 /****************************************************************************/
464 static void flow_graph_dlg_create (void)
467 GtkWidget *flow_graph_dlg_w;
469 GtkWidget *hbuttonbox;
470 GtkWidget *bt_cancel, *bt_ok;
472 GtkWidget *top_label = NULL;
474 GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb, *node_addr_fr, *node_addr_tb;
477 GtkTooltips *tooltips = gtk_tooltips_new();
479 flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: Flow Graph");
481 gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 250, 150);
483 main_vb = gtk_vbox_new (FALSE, 0);
484 gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
485 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 7);
488 top_label = gtk_label_new ("Choose packets to include in the graph");
489 gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
492 gtk_widget_show(flow_graph_dlg_w);
494 /*** Packet range frame ***/
495 range_fr = gtk_frame_new("Choose packets");
496 gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 5);
498 range_tb = gtk_table_new(4, 4, FALSE);
499 gtk_container_set_border_width(GTK_CONTAINER(range_tb), 5);
500 gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
502 /* Process all packets */
503 select_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_All packets");
504 gtk_tooltips_set_tip (tooltips, select_all_rb,
505 ("Process all packets"), NULL);
506 g_signal_connect(select_all_rb, "toggled", G_CALLBACK(toggle_select_all), NULL);
507 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
508 if (type_of_packets == ALL) {
509 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
511 gtk_widget_show(select_all_rb);
513 /* Process displayed packets */
514 select_displayed_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb), "_Displayed packets");
515 gtk_tooltips_set_tip (tooltips, select_displayed_rb,
516 ("Process displayed packets"), NULL);
517 g_signal_connect(select_displayed_rb, "toggled", G_CALLBACK(toggle_select_displayed), NULL);
518 gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 0, 1, 1, 2);
519 if (type_of_packets == DISPLAYED) {
520 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
522 gtk_widget_show(select_displayed_rb);
524 gtk_widget_show(range_tb);
525 gtk_widget_show(range_fr);
527 /*** Flow type frame ***/
528 flow_type_fr = gtk_frame_new("Choose flow type");
529 gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 5);
531 flow_type_tb = gtk_table_new(4, 4, FALSE);
532 gtk_container_set_border_width(GTK_CONTAINER(flow_type_tb), 5);
533 gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
535 /* General information */
536 select_general_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_General flow");
537 gtk_tooltips_set_tip (tooltips, select_general_rb,
538 ("Show all packets, with general information"), NULL);
539 g_signal_connect(select_general_rb, "toggled", G_CALLBACK(toggle_select_general), NULL);
540 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
541 if (type_of_flow == GENERAL) {
542 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
544 gtk_widget_show(select_general_rb);
546 /* TCP specific information */
547 select_tcp_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_general_rb), "_TCP flow");
548 gtk_tooltips_set_tip (tooltips, select_tcp_rb,
549 ("Show only TCP packets, with TCP specific information"), NULL);
550 g_signal_connect(select_tcp_rb, "toggled", G_CALLBACK(toggle_select_tcp), NULL);
551 gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 0, 1, 1, 2);
552 if (type_of_flow == TCP) {
553 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
555 gtk_widget_show(select_tcp_rb);
557 gtk_widget_show(flow_type_tb);
558 gtk_widget_show(flow_type_fr);
560 /*** Node address type frame ***/
561 node_addr_fr = gtk_frame_new("Choose node address type");
562 gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 5);
564 node_addr_tb = gtk_table_new(4, 4, FALSE);
565 gtk_container_set_border_width(GTK_CONTAINER(node_addr_tb), 5);
566 gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
568 /* Source / Dest address */
569 src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Standard source/destination addresses");
570 gtk_tooltips_set_tip (tooltips, src_dst_rb,
571 ("Nodes in the diagram are identified with source and destination addresses"), NULL);
572 g_signal_connect(src_dst_rb, "toggled", G_CALLBACK(toggle_select_srcdst), NULL);
573 gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
574 if (node_addr_type == SRCDST) {
575 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
577 gtk_widget_show(src_dst_rb);
579 /* Network source / dest address */
580 net_src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(src_dst_rb), "_Network source/destination addresses");
581 gtk_tooltips_set_tip (tooltips, net_src_dst_rb,
582 ("Nodes in the diagram are identified with network source and destination addresses"), NULL);
583 g_signal_connect(net_src_dst_rb, "toggled", G_CALLBACK(toggle_select_netsrcdst), NULL);
584 gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 0, 1, 1, 2);
585 if (node_addr_type == NET_SRCDST) {
586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
588 gtk_widget_show(net_src_dst_rb);
590 gtk_widget_show(node_addr_tb);
591 gtk_widget_show(node_addr_fr);
594 hbuttonbox = gtk_hbutton_box_new ();
595 gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 5);
596 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
597 gtk_box_set_spacing (GTK_BOX (hbuttonbox), 30);
599 bt_ok = gtk_button_new_from_stock(GTK_STOCK_OK);
600 gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
601 gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
602 g_signal_connect(bt_ok, "clicked", G_CALLBACK(flow_graph_on_ok), flow_graph_dlg_w);
603 gtk_widget_show(bt_ok);
605 bt_cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
606 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_cancel);
607 GTK_WIDGET_SET_FLAGS(bt_cancel, GTK_CAN_DEFAULT);
608 gtk_tooltips_set_tip (tooltips, bt_cancel, "Cancel this dialog", NULL);
609 window_set_cancel_button(flow_graph_dlg_w, bt_cancel, window_cancel_button_cb);
611 g_signal_connect(flow_graph_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
612 g_signal_connect(flow_graph_dlg_w, "destroy", G_CALLBACK(flow_graph_on_destroy), NULL);
614 gtk_widget_show_all(flow_graph_dlg_w);
615 window_present(flow_graph_dlg_w);
617 flow_graph_dlg = flow_graph_dlg_w;
620 /****************************************************************************/
622 /****************************************************************************/
624 /* init function for tap */
626 flow_graph_init_tap(const char *dummy _U_, void* userdata _U_)
629 /* initialize graph items store */
630 flow_graph_data_init();
632 /* init the Graph Analysys */
633 graph_analysis_data = graph_analysis_init();
634 graph_analysis_data->graph_info = graph_analysis;
636 /* create dialog box if necessary */
637 if (flow_graph_dlg == NULL) {
638 flow_graph_dlg_create();
640 /* There's already a dialog box; reactivate it. */
641 reactivate_window(flow_graph_dlg);
647 /****************************************************************************/
648 /* entry point when called via the GTK menu */
649 static void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
651 flow_graph_init_tap("",NULL);
654 /****************************************************************************/
656 register_tap_listener_flow_graph(void)
658 register_stat_cmd_arg("flow_graph",flow_graph_init_tap,NULL);
659 register_stat_menu_item_stock("Flo_w Graph...",
660 REGISTER_STAT_GROUP_UNSORTED, WIRESHARK_STOCK_FLOW_GRAPH,
661 flow_graph_launch, NULL, NULL, NULL);