Removed some more "statement not reached" warnings.
[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         int ret = 0;
135
136         switch (ri->code) { 
137                 case RADIUS_ACCESS_REQUEST:
138                 case RADIUS_ACCESS_ACCEPT:
139                 case RADIUS_ACCESS_REJECT:
140                         radius_cat = ACCESS;
141                         break;
142                 case RADIUS_ACCOUNTING_REQUEST:
143                 case RADIUS_ACCOUNTING_RESPONSE:
144                         radius_cat = ACCOUNTING;
145                         break;
146                 case RADIUS_ACCESS_PASSWORD_REQUEST:
147                 case RADIUS_ACCESS_PASSWORD_ACK:
148                 case RADIUS_ACCESS_PASSWORD_REJECT:
149                         radius_cat = ACCESS_PASSWORD;
150                         break;
151                 case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
152                 case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
153                         radius_cat = ASCEND_ACCESS_EVENT;
154                         break;
155                 case RADIUS_DISCONNECT_REQUEST:
156                 case RADIUS_DISCONNECT_REQUEST_ACK:
157                 case RADIUS_DISCONNECT_REQUEST_NAK:
158                         radius_cat = DISCONNECT;
159                         break;
160                 case RADIUS_CHANGE_FILTER_REQUEST:
161                 case RADIUS_CHANGE_FILTER_REQUEST_ACK:
162                 case RADIUS_CHANGE_FILTER_REQUEST_NAK:
163                         radius_cat = CHANGE_FILTER;
164                         break;
165         }
166
167         switch (ri->code) {
168
169         case RADIUS_ACCESS_REQUEST:
170         case RADIUS_ACCOUNTING_REQUEST:
171         case RADIUS_ACCESS_PASSWORD_REQUEST:
172         case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
173         case RADIUS_DISCONNECT_REQUEST:
174         case RADIUS_CHANGE_FILTER_REQUEST:
175                 if(ri->is_duplicate){
176                         /* Duplicate is ignored */
177                         rs->radius_rtd[OVERALL].req_dup_num++;
178                         rs->radius_rtd[radius_cat].req_dup_num++;
179                 }
180                 else {
181                         rs->radius_rtd[OVERALL].open_req_num++;
182                         rs->radius_rtd[radius_cat].open_req_num++;
183                 }
184                 break;
185
186         case RADIUS_ACCESS_ACCEPT:
187         case RADIUS_ACCESS_REJECT:
188         case RADIUS_ACCOUNTING_RESPONSE:
189         case RADIUS_ACCESS_PASSWORD_ACK:
190         case RADIUS_ACCESS_PASSWORD_REJECT:
191         case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
192         case RADIUS_DISCONNECT_REQUEST_ACK:
193         case RADIUS_DISCONNECT_REQUEST_NAK:
194         case RADIUS_CHANGE_FILTER_REQUEST_ACK:
195         case RADIUS_CHANGE_FILTER_REQUEST_NAK:
196                 if(ri->is_duplicate){
197                         /* Duplicate is ignored */
198                         rs->radius_rtd[OVERALL].rsp_dup_num++;
199                         rs->radius_rtd[radius_cat].rsp_dup_num++;
200                 }
201                 else if (!ri->request_available) {
202                         /* no request was seen */
203                         rs->radius_rtd[OVERALL].disc_rsp_num++;
204                         rs->radius_rtd[radius_cat].disc_rsp_num++;
205                 }
206                 else {
207                         rs->radius_rtd[OVERALL].open_req_num--;
208                         rs->radius_rtd[radius_cat].open_req_num--;
209                         /* calculate time delta between request and response */
210                         nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
211
212                         time_stat_update(&(rs->radius_rtd[OVERALL].stats),&delta, pinfo);
213                         time_stat_update(&(rs->radius_rtd[radius_cat].stats),&delta, pinfo);
214
215                         ret = 1;
216                 }
217                 break;
218
219         default:
220                 break;
221         }
222
223         return ret;
224 }
225
226 static void
227 radiusstat_draw(void *prs)
228 {
229         radiusstat_t *rs=(radiusstat_t *)prs;
230         int i;
231         /* gtk1 using a scrollable clist*/
232         char *str[NUM_COLUMNS];
233
234         for(i=0;i<NUM_COLUMNS;i++) {
235                 str[i]=g_malloc(sizeof(char[256]));
236         }
237
238         /* clear list before printing */
239         gtk_clist_clear(rs->table);
240
241         for(i=0;i<NUM_TIMESTATS;i++) {
242                 /* nothing seen, nothing to do */
243                 if(rs->radius_rtd[i].stats.num==0){
244                         continue;
245                 }
246
247                 g_snprintf(str[0], sizeof(char[256]), "%s", val_to_str(i,radius_message_code,"Other"));
248                 g_snprintf(str[1], sizeof(char[256]), "%d", rs->radius_rtd[i].stats.num);
249                 g_snprintf(str[2], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.min)));
250                 g_snprintf(str[3], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.max)));
251                 g_snprintf(str[4], sizeof(char[256]), "%8.2f msec", get_average(&(rs->radius_rtd[i].stats.tot), rs->radius_rtd[i].stats.num));
252                 g_snprintf(str[5], sizeof(char[256]), "%6u", rs->radius_rtd[i].stats.min_num);
253                 g_snprintf(str[6], sizeof(char[256]), "%6u", rs->radius_rtd[i].stats.max_num);
254                 g_snprintf(str[7], sizeof(char[256]), "%4u", rs->radius_rtd[i].open_req_num);
255                 g_snprintf(str[8], sizeof(char[256]), "%4u", rs->radius_rtd[i].disc_rsp_num);
256                 g_snprintf(str[9], sizeof(char[256]), "%4u (%4.2f%%)", rs->radius_rtd[i].req_dup_num, 
257                         rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].req_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
258                 g_snprintf(str[10], sizeof(char[256]), "%4u (%4.2f%%)", rs->radius_rtd[i].rsp_dup_num, 
259                         rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].rsp_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
260
261                 gtk_clist_append(rs->table, str);
262         }
263
264         gtk_widget_show(GTK_WIDGET(rs->table));
265         for(i=0;i<NUM_COLUMNS;i++) {
266                 g_free(str[i]);
267         }
268 }
269
270 void protect_thread_critical_region(void);
271 void unprotect_thread_critical_region(void);
272 static void
273 win_destroy_cb(GtkWindow *win _U_, gpointer data)
274 {
275         radiusstat_t *rs=(radiusstat_t *)data;
276
277         protect_thread_critical_region();
278         remove_tap_listener(rs);
279         unprotect_thread_critical_region();
280
281         if(rs->filter){
282                 g_free(rs->filter);
283                 rs->filter=NULL;
284         }
285         g_free(rs);
286 }
287
288 static const gchar *titles[]={
289                         "Type",
290                         "Messages",
291                         "Min SRT",
292                         "Max SRT",
293                         "Avg SRT",
294                         "Min in Frame",
295                         "Max in Frame",
296                         "Open Requests",
297                         "Discarded Responses",
298                         "Repeated Requests",
299                         "Repeated Responses" };
300
301 static void
302 gtk_radiusstat_init(const char *optarg, void *userdata _U_)
303 {
304         radiusstat_t *rs;
305         const char *filter=NULL;
306         GString *error_string;
307         GtkWidget *bt_close;
308         GtkWidget *bbox;
309
310         if(strncmp(optarg,"radius,srt,",11) == 0){
311                 filter=optarg+11;
312         } else {
313                 filter="";
314         }
315
316         rs=g_malloc(sizeof(radiusstat_t));
317         rs->filter=g_strdup(filter);
318
319         radiusstat_reset(rs);
320
321         rs->win=window_new(GTK_WINDOW_TOPLEVEL, "RADIUS SRT");
322         gtk_window_set_default_size(GTK_WINDOW(rs->win), 600, 150);
323
324         rs->vbox=gtk_vbox_new(FALSE, 3);
325
326         init_main_stat_window(rs->win, rs->vbox, "RADIUS Service Response Time (SRT) Statistics", filter);
327
328         /* GTK1 using a scrollable clist*/
329         /* init a scrolled window*/
330         rs->scrolled_window = scrolled_window_new(NULL, NULL);
331
332         rs->table = create_stat_table(rs->scrolled_window, rs->vbox, NUM_COLUMNS, titles);
333
334         error_string=register_tap_listener("radius", rs, filter, radiusstat_reset, radiusstat_packet, radiusstat_draw);
335         if(error_string){
336                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
337                 g_string_free(error_string, TRUE);
338                 g_free(rs->filter);
339                 g_free(rs);
340                 return;
341         }
342
343         /* Button row. */
344         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
345         gtk_box_pack_start(GTK_BOX(rs->vbox), bbox, FALSE, FALSE, 0);
346
347         bt_close = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
348         window_set_cancel_button(rs->win, bt_close, window_cancel_button_cb);
349
350         SIGNAL_CONNECT(rs->win, "delete_event", window_delete_event_cb, NULL);
351         SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
352
353         gtk_widget_show_all(rs->win);
354         window_present(rs->win);
355         
356         cf_retap_packets(&cfile, FALSE);
357 }
358
359 static tap_dfilter_dlg radius_srt_dlg = {
360         "RADIUS Service Response Time (SRT) Statistics",
361         "radius,srt",
362         gtk_radiusstat_init,
363         -1
364 };
365
366 void
367 register_tap_listener_gtkradiusstat(void)
368 {
369         register_dfilter_stat(&radius_srt_dlg, "RADIUS",
370                     REGISTER_STAT_GROUP_RESPONSE_TIME);
371 }