Add a "register_dfilter_stat()", to register stats that take a display
[obnox/wireshark/wip.git] / gtk / flow_graph.c
1 /* flow_graph.c
2  * Allows to display a flow graph of the currently displayed packets
3  *
4  * Copyright 2004, Ericsson , Spain
5  * By Francisco Alcoba <francisco.alcoba@ericsson.com>
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29
30 #include "register.h"
31
32 #include "globals.h"
33 #include "epan/filesystem.h"
34
35 #include "graph_analysis.h"
36 #include <epan/stat_cmd_args.h>
37 #include "../stat_menu.h"
38 #include "gtk_stat_menu.h"
39 #include "dlg_utils.h"
40 #include "gui_utils.h"
41 #include "compat_macros.h"
42 #include "gtkglobals.h"
43
44 #include "simple_dialog.h"
45
46 #include <epan/to_str.h>
47 #include <epan/tap.h>
48 #include <epan/dissectors/packet-tcp.h>
49
50 #include <string.h>
51
52 #define DISPLAYED 0
53 #define ALL 1
54 #define GENERAL 0
55 #define TCP 1
56
57 static int type_of_packets = DISPLAYED;
58 static int type_of_flow = GENERAL;
59
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;
65
66 static GtkWidget *flow_graph_dlg = NULL;
67
68 static GtkWidget *select_all_rb;
69 static GtkWidget *select_displayed_rb;
70 static GtkWidget *select_general_rb;
71 static GtkWidget *select_tcp_rb;
72
73 void flow_graph_data_init(void);
74
75
76 /****************************************************************************/
77 /* free up memory and initialize the pointers */
78
79 static void flow_graph_reset(void *ptr _U_)
80 {
81
82         graph_analysis_item_t *graph_item;
83         
84         GList* list;
85
86         if (graph_analysis !=NULL){
87
88                 /* free the graph data items */
89                 list = g_list_first(graph_analysis->list);
90                 while (list)
91                 {
92                         graph_item = list->data;
93                         g_free(graph_item->frame_label);
94                         g_free(graph_item->comment);
95                         g_free(list->data);
96                         list = g_list_next(list);
97                 }
98                 g_list_free(graph_analysis->list);
99                 graph_analysis->nconv = 0;
100                 graph_analysis->list = NULL;
101         }
102         return;
103 }
104
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;
110         return;
111 }
112
113 /* XXX just copied from gtk/rpc_stat.c */
114 void protect_thread_critical_region(void);
115 void unprotect_thread_critical_region(void);
116
117 /****************************************************************************/
118 static void
119 remove_tap_listener_flow_graph(void)
120 {
121         protect_thread_critical_region();
122         remove_tap_listener(&(tap_identifier));
123         unprotect_thread_critical_region();
124
125         have_frame_tap_listener=FALSE;
126         have_tcp_tap_listener=FALSE;
127 }
128
129
130 /****************************************************************************/
131 /* CALLBACKS                                                                */
132 /****************************************************************************/
133 static void
134 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
135 {
136         /* remove_tap_listeners */
137         remove_tap_listener_flow_graph();
138
139         /* Clean up memory used by tap */
140         flow_graph_reset(NULL);
141
142         /* Note that we no longer have a "Flow Graph" dialog box. */
143         flow_graph_dlg = NULL;
144 }
145
146         
147 /****************************************************************************/
148 static void
149 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
150 {
151   /* is the button now active? */
152         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
153                 type_of_packets = ALL;
154         }
155 }
156
157 /****************************************************************************/
158 static void
159 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
160 {
161   /* is the button now active? */
162         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
163                 type_of_packets = DISPLAYED;
164         }
165 }
166
167 /****************************************************************************/
168 static void
169 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
170 {
171   /* is the button now active? */
172         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
173                 type_of_flow = GENERAL;
174         }
175 }
176
177 /****************************************************************************/
178 static void
179 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
180 {
181   /* is the button now active? */
182         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
183                 type_of_flow = TCP;
184         }
185 }
186
187
188 /****************************************************************************/
189 /* Add a new frame into the graph */
190 static int flow_graph_frame_add_to_graph(packet_info *pinfo)
191 {
192         graph_analysis_item_t *gai;
193         int i;
194
195         gai = g_malloc(sizeof(graph_analysis_item_t));
196         gai->frame_num = pinfo->fd->num;
197         gai->time= (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
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;
202         gai->comment=NULL;
203         gai->frame_label=NULL;
204
205         if (pinfo->cinfo->col_first[COL_INFO]>=0){
206                 
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);
211                                 }
212                                 gai->comment = g_strdup(pinfo->cinfo->col_data[i]);
213                         }
214                 }
215         }
216
217         if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
218                 
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);
223                                 }
224                                 gai->frame_label = g_strdup(pinfo->cinfo->col_data[i]);
225                         }
226                 }
227         }
228
229
230         gai->line_style=1;
231         gai->conv_num=0;
232         gai->display=TRUE;
233
234         graph_analysis->list = g_list_append(graph_analysis->list, gai);
235
236         return 1;
237
238 }
239
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)
243 {
244         graph_analysis_item_t *gai;
245         /* copied from packet-tcp */
246         const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
247         guint i, bpos;
248         guint fpos = 0;
249         gchar flags[64] = "<None>";
250
251         gai = g_malloc(sizeof(graph_analysis_item_t));
252         gai->frame_num = pinfo->fd->num;
253         gai->time= (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
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;
258
259     for (i = 0; i < 8; i++) {
260       bpos = 1 << i;
261       if (tcph->th_flags & bpos) {
262         if (fpos) {
263           strcpy(&flags[fpos], ", ");
264           fpos += 2;
265         }
266         strcpy(&flags[fpos], fstr[i]);
267         fpos += 3;
268       }
269     }
270     flags[fpos] = '\0';
271     if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
272       gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
273         }
274         else{
275       gai->frame_label = g_strdup(flags);
276         }      
277
278         gai->comment = g_strdup_printf("Seq = %i Ack = %i",tcph->th_seq, tcph->th_ack);
279
280         gai->line_style=1;
281         gai->conv_num=0;
282         gai->display=TRUE;
283
284         graph_analysis->list = g_list_append(graph_analysis->list, gai);
285
286         return 1;
287
288 }
289
290
291
292 /****************************************************************************/
293 /* whenever a frame packet is seen by the tap listener */
294 static int 
295 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
296 {
297
298         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
299                 flow_graph_frame_add_to_graph(pinfo);  
300         }
301         
302         return 1;
303 }
304
305 /****************************************************************************/
306 /* whenever a TCP packet is seen by the tap listener */
307 static int 
308 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
309 {
310         const struct tcpheader *tcph = tcp_info;
311
312         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
313                 flow_graph_tcp_add_to_graph(pinfo,tcph);  
314         }
315         
316         return 1;
317 }
318
319
320 static void flow_graph_packet_draw(void *prs _U_)
321 {
322         return; 
323 }
324
325 /****************************************************************************/
326 static void
327 flow_graph_on_ok                    (GtkButton       *button _U_,
328                                         gpointer         user_data _U_)
329 {
330
331         if ((have_frame_tap_listener==TRUE)
332                 ||(have_tcp_tap_listener==TRUE))
333         { 
334                 /* remove_tap_listeners */
335                 remove_tap_listener_flow_graph();
336         }
337         
338         /* Scan for displayed packets (redissect all packets) */
339
340         if (type_of_flow == GENERAL){
341                 /* Register the tap listener */
342
343                 if(have_frame_tap_listener==FALSE)
344                 {
345                         /* don't register tap listener, if we have it already */
346                         register_tap_listener("frame", &tap_identifier, NULL,
347                                 flow_graph_reset, 
348                                 flow_graph_frame_packet, 
349                                 flow_graph_packet_draw
350                                 );
351                         have_frame_tap_listener=TRUE;
352                 }
353
354                 cf_redissect_packets(&cfile);
355         }
356         else if (type_of_flow == TCP){
357         /* Register the tap listener */
358
359                 if(have_tcp_tap_listener==FALSE)
360                 {
361                         /* don't register tap listener, if we have it already */
362                         register_tap_listener("tcp", &tap_identifier, NULL,
363                                 flow_graph_reset, 
364                                 flow_graph_tcp_packet, 
365                                 flow_graph_packet_draw
366                                 );
367                         have_tcp_tap_listener=TRUE;
368                 }
369
370                 cf_retap_packets(&cfile);
371         }
372
373         if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
374                 graph_analysis_update(graph_analysis_data);             /* refresh it xxx */
375         }
376         else{
377                 graph_analysis_create(graph_analysis_data);
378         }
379
380 }
381
382
383 /****************************************************************************/
384 /* INTERFACE                                                                */
385 /****************************************************************************/
386
387 static void flow_graph_dlg_create (void)
388 {
389         
390         GtkWidget *flow_graph_dlg_w;
391         GtkWidget *main_vb;
392         GtkWidget *hbuttonbox;
393         GtkWidget *bt_close, *bt_ok;
394 #if 0
395         GtkWidget *top_label = NULL;
396 #endif
397         GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb;
398 #if GTK_MAJOR_VERSION < 2
399   GtkAccelGroup *accel_group;
400 #endif
401
402
403         GtkTooltips *tooltips = gtk_tooltips_new();
404
405         flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Packet flow graph");
406
407         gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 350, 150);
408
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);
412
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);
419 #endif
420
421 #if 0
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);
424 #endif
425         
426         gtk_widget_show(flow_graph_dlg_w);
427
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);
431
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);
435
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);
444         }
445         gtk_widget_show(select_all_rb);
446
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);
455         }
456         gtk_widget_show(select_displayed_rb);
457
458         gtk_widget_show(range_tb);
459
460         gtk_widget_show(range_fr);
461
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);
465
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);
469
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);
478         }
479         gtk_widget_show(select_general_rb);
480
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);
489         }
490
491         gtk_widget_show(select_tcp_rb);
492
493         gtk_widget_show(flow_type_tb);
494         gtk_widget_show(flow_type_fr);
495
496         /* button row */
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);
501
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);
507
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);
513
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);
516
517         gtk_widget_show_all(flow_graph_dlg_w);
518         window_present(flow_graph_dlg_w);
519
520         flow_graph_dlg = flow_graph_dlg_w;
521 }       
522
523 /****************************************************************************/
524 /* PUBLIC                                                                   */
525 /****************************************************************************/
526
527 /* init function for tap */
528 static void
529 flow_graph_init_tap(const char *dummy _U_)
530 {
531
532         /* initialize graph items store */
533         flow_graph_data_init();
534         
535         /* init the Graph Analysys */
536         graph_analysis_data = graph_analysis_init();
537         graph_analysis_data->graph_info = graph_analysis;
538
539         /* create dialog box if necessary */
540         if (flow_graph_dlg == NULL) {
541                 flow_graph_dlg_create();
542         } else {
543                 /* There's already a dialog box; reactivate it. */
544                 reactivate_window(flow_graph_dlg);
545         }
546
547 }
548
549
550 /****************************************************************************/
551 /* entry point when called via the GTK menu */
552 static void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
553 {
554         flow_graph_init_tap("");
555 }
556
557 /****************************************************************************/
558 void
559 register_tap_listener_flow_graph(void)
560 {
561         register_stat_cmd_arg("flow_graph",flow_graph_init_tap);
562         register_stat_menu_item("Flow graph", REGISTER_STAT_GROUP_NONE,
563             flow_graph_launch, NULL, NULL, NULL);
564             
565 }
566