From Jay Moran via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=4697 :
[obnox/wireshark/wip.git] / tap-sipstat.c
1 /* tap_sipstat.c
2  * sip message counter for wireshark
3  *
4  * $Id$
5  * Copied from gtk/sip_stat.c and tap-httpstat.c
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 #include <stdio.h>
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #include <string.h>
37 #include "epan/packet_info.h"
38 #include <epan/tap.h>
39 #include <epan/stat_cmd_args.h>
40 #include "epan/value_string.h"
41 #include "register.h"
42 #include <epan/dissectors/packet-sip.h>
43
44 /* used to keep track of the statictics for an entire program interface */
45 typedef struct _sip_stats_t {
46         char            *filter;
47         guint32         packets;        /* number of sip packets, including continuations */
48         guint32         resent_packets;
49         guint32         average_setup_time;
50         guint32         max_setup_time;
51         guint32         min_setup_time;
52         guint32         no_of_completed_calls;
53         guint64         total_setup_time;
54         GHashTable      *hash_responses;
55         GHashTable      *hash_requests;
56 } sipstat_t;
57
58 /* used to keep track of the stats for a specific response code
59  * for example it can be { 3, 404, "Not Found" ,...}
60  * which means we captured 3 reply sip/1.1 404 Not Found */
61 typedef struct _sip_response_code_t {
62         guint32          packets;               /* 3 */
63         guint            response_code;         /* 404 */
64         const gchar     *name;                  /* Not Found */
65         sipstat_t       *sp;
66 } sip_response_code_t;
67
68 /* used to keep track of the stats for a specific request string */
69 typedef struct _sip_request_method_t {
70         gchar           *response;      /* eg. : INVITE */
71         guint32          packets;
72         sipstat_t       *sp;
73 } sip_request_method_t;
74
75 /* TODO: extra codes to be added from SIP extensions? */
76 static const value_string vals_status_code[] = {
77     { 100, "Trying"},
78     { 180, "Ringing"},
79     { 181, "Call Is Being Forwarded"},
80     { 182, "Queued"},
81     { 183, "Session Progress"},
82     { 199, "Informational - Others" },
83
84     { 200, "OK"},
85     { 202, "Accepted"},
86     { 204, "No Notification"},
87     { 299, "Success - Others"}, /* used to keep track of other Success packets */
88
89     { 300, "Multiple Choices"},
90     { 301, "Moved Permanently"},
91     { 302, "Moved Temporarily"},
92     { 305, "Use Proxy"},
93     { 380, "Alternative Service"},
94     { 399, "Redirection - Others"},
95
96     { 400, "Bad Request"},
97     { 401, "Unauthorized"},
98     { 402, "Payment Required"},
99     { 403, "Forbidden"},
100     { 404, "Not Found"},
101     { 405, "Method Not Allowed"},
102     { 406, "Not Acceptable"},
103     { 407, "Proxy Authentication Required"},
104     { 408, "Request Timeout"},
105     { 410, "Gone"},
106     { 412, "Conditional Request Failed"},
107     { 413, "Request Entity Too Large"},
108     { 414, "Request-URI Too Long"},
109     { 415, "Unsupported Media Type"},
110     { 416, "Unsupported URI Scheme"},
111     { 420, "Bad Extension"},
112     { 421, "Extension Required"},
113     { 422, "Session Timer Too Small"},
114     { 423, "Interval Too Brief"},
115     { 428, "Use Identity Header"},
116     { 429, "Provide Referrer Identity"},
117     { 430, "Flow Failed"},
118     { 433, "Anonymity Disallowed"},
119     { 436, "Bad Identity-Info"},
120     { 437, "Unsupported Certificate"},
121     { 438, "Invalid Identity Header"},
122     { 439, "First Hop Lacks Outbound Support"},
123     { 440, "Max-Breadth Exceeded"},
124     { 470, "Consent Needed"},
125     { 480, "Temporarily Unavailable"},
126     { 481, "Call/Transaction Does Not Exist"},
127     { 482, "Loop Detected"},
128     { 483, "Too Many Hops"},
129     { 484, "Address Incomplete"},
130     { 485, "Ambiguous"},
131     { 486, "Busy Here"},
132     { 487, "Request Terminated"},
133     { 488, "Not Acceptable Here"},
134     { 489, "Bad Event"},
135     { 491, "Request Pending"},
136     { 493, "Undecipherable"},
137     { 494, "Security Agreement Required"},
138     { 499, "Client Error - Others"},
139
140     { 500, "Server Internal Error"},
141     { 501, "Not Implemented"},
142     { 502, "Bad Gateway"},
143     { 503, "Service Unavailable"},
144     { 504, "Server Time-out"},
145     { 505, "Version Not Supported"},
146     { 513, "Message Too Large"},
147     { 599, "Server Error - Others"},
148
149     { 600, "Busy Everywhere"},
150     { 603, "Decline"},
151     { 604, "Does Not Exist Anywhere"},
152     { 606, "Not Acceptable"},
153     { 699, "Global Failure - Others"},
154
155     { 0,        NULL}
156 };
157
158 /* Create tables for responses and requests */
159 static void
160 sip_init_hash(sipstat_t *sp)
161 {
162     int i;
163
164     /* Create responses table */
165     sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
166
167     /* Add all response codes */
168     for (i=0 ; vals_status_code[i].strptr ; i++)
169     {
170         gint *key = g_malloc (sizeof(gint));
171         sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t));
172         *key = vals_status_code[i].value;
173         sc->packets=0;
174         sc->response_code =  *key;
175         sc->name=vals_status_code[i].strptr;
176         sc->sp = sp;
177         g_hash_table_insert(sc->sp->hash_responses, key, sc);
178     }
179
180     /* Create empty requests table */
181     sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
182 }
183
184 static void
185 sip_draw_hash_requests( gchar *key _U_ , sip_request_method_t *data, gchar * format)
186 {
187         if (data->packets==0)
188                 return;
189         printf( format, data->response, data->packets);
190 }
191
192 static void
193 sip_draw_hash_responses( gint * key _U_ , sip_response_code_t *data, char * format)
194 {
195         if (data==NULL) {
196                 g_warning("C'est quoi ce borderl key=%d\n", *key);
197                 exit(EXIT_FAILURE);
198         }
199         if (data->packets==0)
200                 return;
201         printf(format,  data->response_code, data->name, data->packets );
202 }
203
204 /* NOT USED at this moment */
205 /*
206 static void
207 sip_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
208 {
209         g_free(key);
210         g_free(value);
211 }
212 */
213
214 static void
215 sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_ )
216 {
217         data->packets = 0;
218 }
219 static void
220 sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_ )
221 {
222         data->packets = 0;
223 }
224
225 static void
226 sipstat_reset(void *psp  )
227 {
228         sipstat_t *sp=psp;
229         if (sp) {
230                 sp->packets = 0;
231                 sp->resent_packets = 0;
232                 sp->average_setup_time = 0;
233                 sp->max_setup_time = 0;
234                 sp->min_setup_time = 0;
235                 sp->no_of_completed_calls = 0;
236                 sp->total_setup_time = 0;
237
238                 g_hash_table_foreach( sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
239                 g_hash_table_foreach( sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
240         }
241 }
242
243
244 /* Main entry point to SIP tap */
245 static int
246 sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
247 {
248     const sip_info_value_t *value=pri;
249     sipstat_t *sp = (sipstat_t *)psp;
250     
251     /* Total number of packets, including continuation packets */
252     sp->packets++;
253
254         /* Calculate average setup time */
255         if (value->setup_time){
256                 sp->no_of_completed_calls++;
257                 /* Check if it's the first value */
258                 if ( sp->total_setup_time == 0 ){
259                         sp->average_setup_time = value->setup_time;
260                         sp->total_setup_time = value->setup_time;
261                         sp->max_setup_time = value->setup_time;
262                         sp->min_setup_time = value->setup_time;
263                 }else{
264                         sp->total_setup_time = sp->total_setup_time + value->setup_time;
265                         if (sp->max_setup_time < value->setup_time){
266                                 sp->max_setup_time = value->setup_time;
267                         }
268                         if (sp->min_setup_time > value->setup_time){
269                                 sp->min_setup_time = value->setup_time;
270                         }
271                         /* Calculate average */
272                         sp->average_setup_time = (guint32)(sp->total_setup_time / sp->no_of_completed_calls); 
273                 }
274         }
275     
276     /* Update resent count if flag set */
277     if (value->resend)
278     {
279         sp->resent_packets++;
280     }
281
282     
283     /* Looking at both requests and responses */
284     if (value->response_code != 0)
285     {
286         /* Responses */
287         guint *key = g_malloc(sizeof(guint));
288         sip_response_code_t *sc;
289
290         /* Look up response code in hash table */
291         *key = value->response_code;
292         sc = g_hash_table_lookup(sp->hash_responses, key);
293         if (sc==NULL)
294         {
295             /* Non-standard status code ; we classify it as others
296              * in the relevant category
297              * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
298              */
299             int i = value->response_code;
300             if ((i<100) || (i>=700))
301             {
302                 /* Forget about crazy values */
303                 return 0;
304             }
305             else if (i<200)
306             {
307                 *key=199;       /* Hopefully, this status code will never be used */
308             }
309             else if (i<300)
310             {
311                 *key=299;
312             }
313             else if (i<400)
314             {
315                 *key=399;
316             }
317             else if (i<500)
318             {
319                 *key=499;
320             }
321             else if (i<600)
322             {
323                 *key=599;
324             }
325             else
326             {
327                 *key = 699;
328             }
329
330             /* Now look up this fallback code to get its text description */
331             sc = g_hash_table_lookup(sp->hash_responses, key);
332             if (sc==NULL)
333             {
334                 return 0;
335             }
336         }
337         sc->packets++;
338     }
339     else if (value->request_method)
340     {
341         /* Requests */
342         sip_request_method_t *sc;
343
344         /* Look up the request method in the table */
345         sc = g_hash_table_lookup(sp->hash_requests, value->request_method);
346         if (sc == NULL)
347         {
348             /* First of this type. Create structure and initialise */
349             sc=g_malloc(sizeof(sip_request_method_t));
350             sc->response = g_strdup(value->request_method);
351             sc->packets = 1;
352             sc->sp = sp;
353             /* Insert it into request table */
354             g_hash_table_insert(sp->hash_requests, sc->response, sc);
355         }
356         else
357         {
358             /* Already existed, just update count for that method */
359             sc->packets++;
360         }
361         /* g_free(value->request_method); */
362     }
363     else
364     {
365         /* No request method set. Just ignore */
366         return 0;
367     }
368
369     return 1;
370 }
371
372 static void
373 sipstat_draw(void *psp  )
374 {
375         sipstat_t *sp=psp;
376         printf("\n");
377         printf("===================================================================\n");
378         if (sp->filter == NULL)
379                 printf("SIP Statistics\n");
380         else
381                 printf("SIP Statistics with filter %s\n", sp->filter);
382
383         printf("\nNumber of SIP messages: %d", sp->packets);
384         printf("\nNumber of resent SIP messages: %d\n", sp->resent_packets);
385         printf( "\n* SIP Status Codes in reply packets\n");
386         g_hash_table_foreach( sp->hash_responses, (GHFunc)sip_draw_hash_responses,
387                 "  SIP %3d %-15s : %5d Packets\n");
388         printf("\n* List of SIP Request methods\n");
389         g_hash_table_foreach( sp->hash_requests,  (GHFunc)sip_draw_hash_requests,
390                 "  %-15s : %5d Packets\n");
391         printf( "\n* Average setup time %d ms\n Min %d ms\n Max %d ms\n", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time);
392         printf("===================================================================\n");
393 }
394
395 static void
396 sipstat_init(const char *optarg, void* userdata _U_)
397 {
398         sipstat_t *sp;
399         const char *filter=NULL;
400         GString *error_string;
401
402         if (strncmp (optarg, "sip,stat,", 9) == 0){
403                 filter=optarg+9;
404         } else {
405                 filter=NULL;
406         }
407
408         sp = g_malloc( sizeof(sipstat_t) );
409         if(filter){
410                 sp->filter=g_strdup(filter);
411         } else {
412                 sp->filter=NULL;
413         }
414         /*g_hash_table_foreach( sip_status, (GHFunc)sip_reset_hash_responses, NULL);*/
415
416
417         error_string = register_tap_listener(
418                         "sip",
419                         sp,
420                         filter,
421                         0,
422                         sipstat_reset,
423                         sipstat_packet,
424                         sipstat_draw);
425         if (error_string){
426                 /* error, we failed to attach to the tap. clean up */
427                 g_free(sp->filter);
428                 g_free(sp);
429                 fprintf (stderr, "tshark: Couldn't register sip,stat tap: %s\n",
430                                 error_string->str);
431                 g_string_free(error_string, TRUE);
432                 exit(1);
433         }
434
435         sp->packets = 0;
436         sp->resent_packets = 0;
437         sip_init_hash(sp);
438 }
439
440 void
441 register_tap_listener_sipstat(void)
442 {
443         register_stat_cmd_arg("sip,stat", sipstat_init,NULL);
444 }