Move the stats.[ch] stuff into epan, so plugins can use it.
[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.h>
37 #include "stat_menu.h"
38 #include "dlg_utils.h"
39 #include "ui_util.h"
40 #include "compat_macros.h"
41 #include "gtkglobals.h"
42
43 #include "simple_dialog.h"
44
45 #include <epan/to_str.h>
46 #include <epan/tap.h>
47 #include <epan/dissectors/packet-tcp.h>
48
49 #include <string.h>
50
51 #define DISPLAYED 0
52 #define ALL 1
53 #define GENERAL 0
54 #define TCP 1
55
56 static int type_of_packets = DISPLAYED;
57 static int type_of_flow = GENERAL;
58
59 static int tap_identifier;
60 static gboolean have_frame_tap_listener=FALSE;
61 static gboolean have_tcp_tap_listener=FALSE;
62 static graph_analysis_info_t *graph_analysis = NULL;
63 static graph_analysis_data_t *graph_analysis_data;
64
65 static GtkWidget *flow_graph_dlg = NULL;
66
67 static GtkWidget *select_all_rb;
68 static GtkWidget *select_displayed_rb;
69 static GtkWidget *select_general_rb;
70 static GtkWidget *select_tcp_rb;
71
72 void flow_graph_data_init(void);
73
74
75 /****************************************************************************/
76 /* free up memory and initialize the pointers */
77
78 void flow_graph_reset(void *ptr _U_)
79 {
80
81         graph_analysis_item_t *graph_item;
82         
83         GList* list;
84
85         if (graph_analysis !=NULL){
86
87                 /* free the graph data items */
88                 list = g_list_first(graph_analysis->list);
89                 while (list)
90                 {
91                         graph_item = list->data;
92                         g_free(graph_item->frame_label);
93                         g_free(graph_item->comment);
94                         g_free(list->data);
95                         list = g_list_next(list);
96                 }
97                 g_list_free(graph_analysis->list);
98                 graph_analysis->nconv = 0;
99                 graph_analysis->list = NULL;
100         }
101         return;
102 }
103
104 /****************************************************************************/
105 void flow_graph_data_init(void) {
106         graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
107         graph_analysis->nconv = 0;
108         graph_analysis->list = NULL;
109         return;
110 }
111
112 /* XXX just copied from gtk/rpc_stat.c */
113 void protect_thread_critical_region(void);
114 void unprotect_thread_critical_region(void);
115
116 /****************************************************************************/
117 void
118 remove_tap_listener_flow_graph(void)
119 {
120         protect_thread_critical_region();
121         remove_tap_listener(&(tap_identifier));
122         unprotect_thread_critical_region();
123
124         have_frame_tap_listener=FALSE;
125         have_tcp_tap_listener=FALSE;
126 }
127
128
129 /****************************************************************************/
130 /* CALLBACKS                                                                */
131 /****************************************************************************/
132 static void
133 flow_graph_on_destroy(GtkObject *object _U_, gpointer user_data _U_)
134 {
135         /* remove_tap_listeners */
136         remove_tap_listener_flow_graph();
137
138         /* Clean up memory used by tap */
139         flow_graph_reset(NULL);
140
141         /* Note that we no longer have a "Flow Graph" dialog box. */
142         flow_graph_dlg = NULL;
143 }
144
145         
146 /****************************************************************************/
147 static void
148 toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
149 {
150   /* is the button now active? */
151         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
152                 type_of_packets = ALL;
153         }
154 }
155
156 /****************************************************************************/
157 static void
158 toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
159 {
160   /* is the button now active? */
161         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
162                 type_of_packets = DISPLAYED;
163         }
164 }
165
166 /****************************************************************************/
167 static void
168 toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
169 {
170   /* is the button now active? */
171         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
172                 type_of_flow = GENERAL;
173         }
174 }
175
176 /****************************************************************************/
177 static void
178 toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
179 {
180   /* is the button now active? */
181         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
182                 type_of_flow = TCP;
183         }
184 }
185
186
187 /****************************************************************************/
188 /* Add a new frame into the graph */
189 int flow_graph_frame_add_to_graph(packet_info *pinfo)
190 {
191         graph_analysis_item_t *gai;
192         int i;
193
194         gai = g_malloc(sizeof(graph_analysis_item_t));
195         gai->frame_num = pinfo->fd->num;
196         gai->time= (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
197         COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
198         COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
199         gai->port_src=pinfo->srcport;
200         gai->port_dst=pinfo->destport;
201         gai->comment=NULL;
202         gai->frame_label=NULL;
203
204         if (pinfo->cinfo->col_first[COL_INFO]>=0){
205                 
206                 for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
207                 if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
208                                 if (gai->frame_label!=NULL){
209                                         g_free(gai->frame_label);
210                                 }
211                                 gai->comment = g_strdup(pinfo->cinfo->col_data[i]);
212                         }
213                 }
214         }
215
216         if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
217                 
218                 for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
219                 if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
220                                 if (gai->frame_label!=NULL){
221                                         g_free(gai->frame_label);
222                                 }
223                                 gai->frame_label = g_strdup(pinfo->cinfo->col_data[i]);
224                         }
225                 }
226         }
227
228
229         gai->line_style=1;
230         gai->conv_num=0;
231         gai->display=TRUE;
232
233         graph_analysis->list = g_list_append(graph_analysis->list, gai);
234
235         return 1;
236
237 }
238
239 /****************************************************************************/
240 /* Add a new tcp frame into the graph */
241 int flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
242 {
243         graph_analysis_item_t *gai;
244         /* copied from packet-tcp */
245         const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
246         guint i, bpos;
247         guint fpos = 0;
248         gchar flags[64] = "<None>";
249
250         gai = g_malloc(sizeof(graph_analysis_item_t));
251         gai->frame_num = pinfo->fd->num;
252         gai->time= (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
253         COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
254         COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
255         gai->port_src=pinfo->srcport;
256         gai->port_dst=pinfo->destport;
257
258     for (i = 0; i < 8; i++) {
259       bpos = 1 << i;
260       if (tcph->th_flags & bpos) {
261         if (fpos) {
262           strcpy(&flags[fpos], ", ");
263           fpos += 2;
264         }
265         strcpy(&flags[fpos], fstr[i]);
266         fpos += 3;
267       }
268     }
269     flags[fpos] = '\0';
270     if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
271       gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
272         }
273         else{
274       gai->frame_label = g_strdup(flags);
275         }      
276
277         gai->comment = g_strdup_printf("Seq = %i Ack = %i",tcph->th_seq, tcph->th_ack);
278
279         gai->line_style=1;
280         gai->conv_num=0;
281         gai->display=TRUE;
282
283         graph_analysis->list = g_list_append(graph_analysis->list, gai);
284
285         return 1;
286
287 }
288
289
290
291 /****************************************************************************/
292 /* whenever a frame packet is seen by the tap listener */
293 static int 
294 flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
295 {
296
297         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
298                 flow_graph_frame_add_to_graph(pinfo);  
299         }
300         
301         return 1;
302 }
303
304 /****************************************************************************/
305 /* whenever a TCP packet is seen by the tap listener */
306 static int 
307 flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
308 {
309         const struct tcpheader *tcph = tcp_info;
310
311         if ((type_of_packets == ALL)||(pinfo->fd->flags.passed_dfilter==1)){
312                 flow_graph_tcp_add_to_graph(pinfo,tcph);  
313         }
314         
315         return 1;
316 }
317
318
319 void flow_graph_packet_draw(void *prs _U_)
320 {
321         return; 
322 }
323
324 /****************************************************************************/
325 static void
326 flow_graph_on_ok                    (GtkButton       *button _U_,
327                                         gpointer         user_data _U_)
328 {
329
330         if ((have_frame_tap_listener==TRUE)
331                 ||(have_tcp_tap_listener==TRUE))
332         { 
333                 /* remove_tap_listeners */
334                 remove_tap_listener_flow_graph();
335         }
336         
337         /* Scan for displayed packets (redissect all packets) */
338
339         if (type_of_flow == GENERAL){
340                 /* Register the tap listener */
341
342                 if(have_frame_tap_listener==FALSE)
343                 {
344                         /* don't register tap listener, if we have it already */
345                         register_tap_listener("frame", &tap_identifier, NULL,
346                                 flow_graph_reset, 
347                                 flow_graph_frame_packet, 
348                                 flow_graph_packet_draw
349                                 );
350                         have_frame_tap_listener=TRUE;
351                 }
352
353                 cf_redissect_packets(&cfile);
354         }
355         else if (type_of_flow == TCP){
356         /* Register the tap listener */
357
358                 if(have_tcp_tap_listener==FALSE)
359                 {
360                         /* don't register tap listener, if we have it already */
361                         register_tap_listener("tcp", &tap_identifier, NULL,
362                                 flow_graph_reset, 
363                                 flow_graph_tcp_packet, 
364                                 flow_graph_packet_draw
365                                 );
366                         have_tcp_tap_listener=TRUE;
367                 }
368
369                 cf_retap_packets(&cfile);
370         }
371
372         if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
373                 graph_analysis_update(graph_analysis_data);             /* refresh it xxx */
374         }
375         else{
376                 graph_analysis_create(graph_analysis_data);
377         }
378
379 }
380
381
382 /****************************************************************************/
383 /* INTERFACE                                                                */
384 /****************************************************************************/
385
386 static void flow_graph_dlg_create (void)
387 {
388         
389         GtkWidget *flow_graph_dlg_w;
390         GtkWidget *main_vb;
391         GtkWidget *hbuttonbox;
392         GtkWidget *bt_close, *bt_ok;
393 #if 0
394         GtkWidget *top_label = NULL;
395 #endif
396         GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb;
397 #if GTK_MAJOR_VERSION < 2
398   GtkAccelGroup *accel_group;
399 #endif
400
401
402         GtkTooltips *tooltips = gtk_tooltips_new();
403
404         flow_graph_dlg_w=window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Packet flow graph");
405
406         gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 350, 150);
407
408         main_vb = gtk_vbox_new (FALSE, 0);
409         gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
410         gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
411
412 #if GTK_MAJOR_VERSION < 2
413   /* Accelerator group for the accelerators (or, as they're called in
414      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
415      Ctrl+<key> is an accelerator). */
416   accel_group = gtk_accel_group_new();
417   gtk_window_add_accel_group(GTK_WINDOW(main_vb), accel_group);
418 #endif
419
420 #if 0
421         top_label = gtk_label_new ("Choose packets to include in the graph");
422         gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
423 #endif
424         
425         gtk_widget_show(flow_graph_dlg_w);
426
427         /*** Packet range frame ***/
428         range_fr = gtk_frame_new("Choose packets");
429         gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
430
431     range_tb = gtk_table_new(4, 4, FALSE);
432     gtk_container_border_width(GTK_CONTAINER(range_tb), 5);
433         gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
434
435         /* Process all packets */
436         select_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_All packets", accel_group);
437         gtk_tooltips_set_tip (tooltips, select_all_rb, 
438                 ("Process all packets"), NULL);
439         SIGNAL_CONNECT(select_all_rb, "toggled", toggle_select_all, NULL);
440         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
441         if (type_of_packets == ALL) {
442                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
443         }
444         gtk_widget_show(select_all_rb);
445
446         /* Process displayed packets */
447         select_displayed_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_all_rb, "_Displayed packets", accel_group);
448         gtk_tooltips_set_tip (tooltips, select_displayed_rb, 
449                 ("Process displayed packets"), NULL);
450         SIGNAL_CONNECT(select_displayed_rb, "toggled", toggle_select_displayed, NULL);
451         gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 1, 2, 0, 1);
452         if (type_of_packets == DISPLAYED) {
453                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
454         }
455         gtk_widget_show(select_displayed_rb);
456
457         gtk_widget_show(range_tb);
458
459         gtk_widget_show(range_fr);
460
461         /*** Flow type frame ***/
462         flow_type_fr = gtk_frame_new("Choose flow type");
463         gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 0);
464
465     flow_type_tb = gtk_table_new(4, 4, FALSE);
466     gtk_container_border_width(GTK_CONTAINER(flow_type_tb), 5);
467         gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
468
469         /* General information */
470         select_general_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "_General flow", accel_group);
471         gtk_tooltips_set_tip (tooltips, select_general_rb, 
472                 ("Show all packets, with general information"), NULL);
473         SIGNAL_CONNECT(select_general_rb, "toggled", toggle_select_general, NULL);
474         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
475         if (type_of_flow == GENERAL) {
476                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
477         }
478         gtk_widget_show(select_general_rb);
479
480         /* TCP specific information */
481         select_tcp_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(select_general_rb, "_TCP flow", accel_group);
482         gtk_tooltips_set_tip (tooltips, select_tcp_rb, 
483                 ("Show only TCP packets, with TCP specific information"), NULL);
484         SIGNAL_CONNECT(select_tcp_rb, "toggled", toggle_select_tcp, NULL);
485         gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 1, 2, 0, 1);
486         if (type_of_flow == TCP) {
487                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
488         }
489
490         gtk_widget_show(select_tcp_rb);
491
492         gtk_widget_show(flow_type_tb);
493         gtk_widget_show(flow_type_fr);
494
495         /* button row */
496         hbuttonbox = gtk_hbutton_box_new ();
497         gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
498         gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
499         gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbuttonbox), 30);
500
501         bt_ok = BUTTON_NEW_FROM_STOCK(GTK_STOCK_OK);
502         gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
503         gtk_tooltips_set_tip (tooltips, bt_ok, "Show the flow graph", NULL);
504         SIGNAL_CONNECT(bt_ok, "clicked", flow_graph_on_ok, NULL);
505         gtk_widget_show(bt_ok);
506
507         bt_close = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
508         gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
509         GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
510         gtk_tooltips_set_tip (tooltips, bt_close, "Close this dialog", NULL);
511         window_set_cancel_button(flow_graph_dlg_w, bt_close, window_cancel_button_cb);
512
513         SIGNAL_CONNECT(flow_graph_dlg_w, "delete_event", window_delete_event_cb, NULL);
514         SIGNAL_CONNECT(flow_graph_dlg_w, "destroy", flow_graph_on_destroy, NULL);
515
516         gtk_widget_show_all(flow_graph_dlg_w);
517         window_present(flow_graph_dlg_w);
518
519         flow_graph_dlg = flow_graph_dlg_w;
520 }       
521
522 /****************************************************************************/
523 /* PUBLIC                                                                   */
524 /****************************************************************************/
525
526 /* init function for tap */
527 static void
528 flow_graph_init_tap(const char *dummy _U_)
529 {
530
531         /* initialize graph items store */
532         flow_graph_data_init();
533         
534         /* init the Graph Analysys */
535         graph_analysis_data = graph_analysis_init();
536         graph_analysis_data->graph_info = graph_analysis;
537
538         /* create dialog box if necessary */
539         if (flow_graph_dlg == NULL) {
540                 flow_graph_dlg_create();
541         } else {
542                 /* There's already a dialog box; reactivate it. */
543                 reactivate_window(flow_graph_dlg);
544         }
545
546 }
547
548
549 /****************************************************************************/
550 /* entry point when called via the GTK menu */
551 void flow_graph_launch(GtkWidget *w _U_, gpointer data _U_)
552 {
553         flow_graph_init_tap("");
554 }
555
556 /****************************************************************************/
557 void
558 register_tap_listener_flow_graph(void)
559 {
560         register_stat_cmd_arg("flow_graph",flow_graph_init_tap);
561         register_stat_menu_item("Flow graph", REGISTER_STAT_GROUP_NONE,
562             flow_graph_launch, NULL, NULL, NULL);
563             
564 }
565