From Alejandro Vaquero:
[obnox/wireshark/wip.git] / gtk / radius_stat.c
1 /* radius_stat.c
2  * radius-statistics for Wireshark
3  * Copyright 2006 Alejandro Vaquero <alejandrovaquero@yahoo.com>
4  *
5  * 
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif
33
34 #include <string.h>
35
36 #include <gtk/gtk.h>
37
38 #include <epan/packet_info.h>
39 #include <epan/epan.h>
40 #include <epan/value_string.h>
41
42 #include <epan/tap.h>
43 #include "../register.h"
44 #include <epan/dissectors/packet-radius.h>
45 #include "../timestats.h"
46 #include "gui_stat_util.h"
47 #include "compat_macros.h"
48 #include "../simple_dialog.h"
49 #include "dlg_utils.h"
50 #include "../file.h"
51 #include "../globals.h"
52 #include "../stat_menu.h"
53 #include "../tap_dfilter_dlg.h"
54 #include "gui_utils.h"
55
56
57 #define NUM_TIMESTATS 8
58 #define NUM_COLUMNS 11
59
60 /* Summary of response-time calculations*/
61 typedef struct _radius_rtd_t {
62         guint32 open_req_num;
63         guint32 disc_rsp_num;
64         guint32 req_dup_num;
65         guint32 rsp_dup_num;
66         timestat_t stats;
67 } radius_rtd_t;
68
69 /* used to keep track of the statistics for an entire program interface */
70 typedef struct _radiusstat_t {
71         GtkWidget *win;
72         GtkWidget *vbox;
73         char *filter;
74         GtkWidget *scrolled_window;
75         GtkCList *table;
76         radius_rtd_t radius_rtd[NUM_TIMESTATS];
77 } radiusstat_t;
78
79 static const value_string radius_message_code[] = {
80   {  0, "Overall"},
81   {  1, "Access"},
82   {  2, "Accounting"},
83   {  3, "Access Password"},
84   {  4, "Ascend Access Event"},
85   {  5, "Diconnect"},
86   {  6, "Change Filter"},
87   {  7, "Other"},
88 };
89
90 typedef enum _radius_category {
91         OVERALL,
92         ACCESS,
93         ACCOUNTING,
94         ACCESS_PASSWORD,
95         ASCEND_ACCESS_EVENT,
96         DISCONNECT,
97         CHANGE_FILTER,
98         OTHERS
99 }radius_category;
100
101 static void
102 radiusstat_reset(void *prs)
103 {
104         radiusstat_t *rs=(radiusstat_t *)prs;
105         int i;
106
107
108         for(i=0;i<NUM_TIMESTATS;i++) {
109                 rs->radius_rtd[i].stats.num=0;
110                 rs->radius_rtd[i].stats.min_num=0;
111                 rs->radius_rtd[i].stats.max_num=0;
112                 rs->radius_rtd[i].stats.min.secs=0;
113         rs->radius_rtd[i].stats.min.nsecs=0;
114         rs->radius_rtd[i].stats.max.secs=0;
115         rs->radius_rtd[i].stats.max.nsecs=0;
116         rs->radius_rtd[i].stats.tot.secs=0;
117         rs->radius_rtd[i].stats.tot.nsecs=0;
118                 rs->radius_rtd[i].open_req_num = 0;
119                 rs->radius_rtd[i].disc_rsp_num = 0;
120                 rs->radius_rtd[i].req_dup_num = 0;
121                 rs->radius_rtd[i].rsp_dup_num = 0;
122         }
123
124 }
125
126
127 static int
128 radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
129 {
130         radiusstat_t *rs=(radiusstat_t *)prs;
131         const radius_info_t *ri=pri;
132         nstime_t delta;
133         radius_category radius_cat = OTHERS;
134
135         switch (ri->code) { 
136                 case RADIUS_ACCESS_REQUEST:
137                 case RADIUS_ACCESS_ACCEPT:
138                 case RADIUS_ACCESS_REJECT:
139                         radius_cat = ACCESS;
140                         break;
141                 case RADIUS_ACCOUNTING_REQUEST:
142                 case RADIUS_ACCOUNTING_RESPONSE:
143                         radius_cat = ACCOUNTING;
144                         break;
145                 case RADIUS_ACCESS_PASSWORD_REQUEST:
146                 case RADIUS_ACCESS_PASSWORD_ACK:
147                 case RADIUS_ACCESS_PASSWORD_REJECT:
148                         radius_cat = ACCESS_PASSWORD;
149                         break;
150                 case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
151                 case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
152                         radius_cat = ASCEND_ACCESS_EVENT;
153                         break;
154                 case RADIUS_DISCONNECT_REQUEST:
155                 case RADIUS_DISCONNECT_REQUEST_ACK:
156                 case RADIUS_DISCONNECT_REQUEST_NAK:
157                         radius_cat = DISCONNECT;
158                         break;
159                 case RADIUS_CHANGE_FILTER_REQUEST:
160                 case RADIUS_CHANGE_FILTER_REQUEST_ACK:
161                 case RADIUS_CHANGE_FILTER_REQUEST_NAK:
162                         radius_cat = CHANGE_FILTER;
163                         break;
164         }
165
166         switch (ri->code) {
167
168         case RADIUS_ACCESS_REQUEST:
169         case RADIUS_ACCOUNTING_REQUEST:
170         case RADIUS_ACCESS_PASSWORD_REQUEST:
171         case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
172         case RADIUS_DISCONNECT_REQUEST:
173         case RADIUS_CHANGE_FILTER_REQUEST:
174                 if(ri->is_duplicate){
175                         /* Duplicate is ignored */
176                         rs->radius_rtd[OVERALL].req_dup_num++;
177                         rs->radius_rtd[radius_cat].req_dup_num++;
178                         return 0;
179                 }
180                 else {
181                         rs->radius_rtd[OVERALL].open_req_num++;
182                         rs->radius_rtd[radius_cat].open_req_num++;
183                         return 0;
184                 }
185         break;
186
187         case RADIUS_ACCESS_ACCEPT:
188         case RADIUS_ACCESS_REJECT:
189         case RADIUS_ACCOUNTING_RESPONSE:
190         case RADIUS_ACCESS_PASSWORD_ACK:
191         case RADIUS_ACCESS_PASSWORD_REJECT:
192         case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
193         case RADIUS_DISCONNECT_REQUEST_ACK:
194         case RADIUS_DISCONNECT_REQUEST_NAK:
195         case RADIUS_CHANGE_FILTER_REQUEST_ACK:
196         case RADIUS_CHANGE_FILTER_REQUEST_NAK:
197                 if(ri->is_duplicate){
198                         /* Duplicate is ignored */
199                         rs->radius_rtd[OVERALL].rsp_dup_num++;
200                         rs->radius_rtd[radius_cat].rsp_dup_num++;
201                         return 0;
202                 }
203                 else if (!ri->request_available) {
204                         /* no request was seen */
205                         rs->radius_rtd[OVERALL].disc_rsp_num++;
206                         rs->radius_rtd[radius_cat].disc_rsp_num++;
207                         return 0;
208                 }
209                 else {
210                         rs->radius_rtd[OVERALL].open_req_num--;
211                         rs->radius_rtd[radius_cat].open_req_num--;
212                         /* calculate time delta between request and response */
213                         nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
214
215                         time_stat_update(&(rs->radius_rtd[OVERALL].stats),&delta, pinfo);
216                         time_stat_update(&(rs->radius_rtd[radius_cat].stats),&delta, pinfo);
217
218                         return 1;
219                 }
220         break;
221
222         default:
223                 return 0;
224         break;
225         }
226 }
227
228 static void
229 radiusstat_draw(void *prs)
230 {
231         radiusstat_t *rs=(radiusstat_t *)prs;
232         int i;
233         /* gtk1 using a scrollable clist*/
234         char *str[NUM_COLUMNS];
235
236         for(i=0;i<NUM_COLUMNS;i++) {
237                 str[i]=g_malloc(sizeof(char[256]));
238         }
239
240         /* clear list before printing */
241         gtk_clist_clear(rs->table);
242
243         for(i=0;i<NUM_TIMESTATS;i++) {
244                 /* nothing seen, nothing to do */
245                 if(rs->radius_rtd[i].stats.num==0){
246                         continue;
247                 }
248
249                 g_snprintf(str[0], sizeof(char[256]), "%s", val_to_str(i,radius_message_code,"Other"));
250                 g_snprintf(str[1], sizeof(char[256]), "%d", rs->radius_rtd[i].stats.num);
251                 g_snprintf(str[2], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.min)));
252                 g_snprintf(str[3], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.max)));
253                 g_snprintf(str[4], sizeof(char[256]), "%8.2f msec", get_average(&(rs->radius_rtd[i].stats.tot), rs->radius_rtd[i].stats.num));
254                 g_snprintf(str[5], sizeof(char[256]), "%6u", rs->radius_rtd[i].stats.min_num);
255                 g_snprintf(str[6], sizeof(char[256]), "%6u", rs->radius_rtd[i].stats.max_num);
256                 g_snprintf(str[7], sizeof(char[256]), "%4u", rs->radius_rtd[i].open_req_num);
257                 g_snprintf(str[8], sizeof(char[256]), "%4u", rs->radius_rtd[i].disc_rsp_num);
258                 g_snprintf(str[9], sizeof(char[256]), "%4u (%4.2f%%)", rs->radius_rtd[i].req_dup_num, 
259                         rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].req_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
260                 g_snprintf(str[10], sizeof(char[256]), "%4u (%4.2f%%)", rs->radius_rtd[i].rsp_dup_num, 
261                         rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].rsp_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
262
263                 gtk_clist_append(rs->table, str);
264         }
265
266         gtk_widget_show(GTK_WIDGET(rs->table));
267         for(i=0;i<NUM_COLUMNS;i++) {
268                 g_free(str[i]);
269         }
270 }
271
272 void protect_thread_critical_region(void);
273 void unprotect_thread_critical_region(void);
274 static void
275 win_destroy_cb(GtkWindow *win _U_, gpointer data)
276 {
277         radiusstat_t *rs=(radiusstat_t *)data;
278
279         protect_thread_critical_region();
280         remove_tap_listener(rs);
281         unprotect_thread_critical_region();
282
283         if(rs->filter){
284                 g_free(rs->filter);
285                 rs->filter=NULL;
286         }
287         g_free(rs);
288 }
289
290 static const gchar *titles[]={
291                         "Type",
292                         "Messages",
293                         "Min SRT",
294                         "Max SRT",
295                         "Avg SRT",
296                         "Min in Frame",
297                         "Max in Frame",
298                         "Open Requests",
299                         "Discarded Responses",
300                         "Repeated Requests",
301                         "Repeated Responses" };
302
303 static void
304 gtk_radiusstat_init(const char *optarg, void *userdata _U_)
305 {
306         radiusstat_t *rs;
307         const char *filter=NULL;
308         GString *error_string;
309         GtkWidget *bt_close;
310         GtkWidget *bbox;
311
312         if(strncmp(optarg,"radius,srt,",11) == 0){
313                 filter=optarg+11;
314         } else {
315                 filter="";
316         }
317
318         rs=g_malloc(sizeof(radiusstat_t));
319         rs->filter=g_strdup(filter);
320
321         radiusstat_reset(rs);
322
323         rs->win=window_new(GTK_WINDOW_TOPLEVEL, "RADIUS SRT");
324         gtk_window_set_default_size(GTK_WINDOW(rs->win), 600, 150);
325
326         rs->vbox=gtk_vbox_new(FALSE, 3);
327
328         init_main_stat_window(rs->win, rs->vbox, "RADIUS Service Response Time (SRT) Statistics", filter);
329
330         /* GTK1 using a scrollable clist*/
331         /* init a scrolled window*/
332         rs->scrolled_window = scrolled_window_new(NULL, NULL);
333
334         rs->table = create_stat_table(rs->scrolled_window, rs->vbox, NUM_COLUMNS, titles);
335
336         error_string=register_tap_listener("radius", rs, filter, radiusstat_reset, radiusstat_packet, radiusstat_draw);
337         if(error_string){
338                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
339                 g_string_free(error_string, TRUE);
340                 g_free(rs->filter);
341                 g_free(rs);
342                 return;
343         }
344
345         /* Button row. */
346         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
347         gtk_box_pack_start(GTK_BOX(rs->vbox), bbox, FALSE, FALSE, 0);
348
349         bt_close = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
350         window_set_cancel_button(rs->win, bt_close, window_cancel_button_cb);
351
352         SIGNAL_CONNECT(rs->win, "delete_event", window_delete_event_cb, NULL);
353         SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
354
355         gtk_widget_show_all(rs->win);
356         window_present(rs->win);
357         
358         cf_retap_packets(&cfile, FALSE);
359 }
360
361 static tap_dfilter_dlg radius_srt_dlg = {
362         "RADIUS Service Response Time (SRT) Statistics",
363         "radius,srt",
364         gtk_radiusstat_init,
365         -1
366 };
367
368 void
369 register_tap_listener_gtkradiusstat(void)
370 {
371         register_dfilter_stat(&radius_srt_dlg, "RADIUS",
372                     REGISTER_STAT_GROUP_RESPONSE_TIME);
373 }