2 * sip_stat 2004 Martin Mathieson
5 * Copied from http_stat.c
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
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.
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.
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.
33 #include <epan/packet_info.h>
34 #include <epan/epan.h>
36 #include <epan/dissectors/packet-sip.h>
38 #include "../simple_dialog.h"
39 #include "../globals.h"
40 #include "../stat_menu.h"
42 #include "gtk/gui_utils.h"
43 #include "gtk/dlg_utils.h"
44 #include "gtk/tap_dfilter_dlg.h"
48 #define SUM_STR_MAX 1024
50 /* Used to keep track of the statistics for an entire program interface */
51 typedef struct _sip_stats_t {
54 GHashTable *hash_responses;
55 GHashTable *hash_requests;
56 guint32 packets; /* number of sip packets, including continuations */
57 guint32 resent_packets;
58 guint32 average_setup_time;
59 guint32 max_setup_time;
60 guint32 min_setup_time;
61 guint32 no_of_completed_calls;
62 guint64 total_setup_time;
63 GtkWidget *packets_label;
64 GtkWidget *resent_label;
65 GtkWidget *average_setup_time_label;
67 GtkWidget *request_box; /* container for INVITE, ... */
69 GtkWidget *informational_table; /* Status code between 100 and 199 */
70 GtkWidget *success_table; /* 200 and 299 */
71 GtkWidget *redirection_table; /* 300 and 399 */
72 GtkWidget *client_error_table; /* 400 and 499 */
73 GtkWidget *server_errors_table; /* 500 and 599 */
74 GtkWidget *global_failures_table; /* 600 and 699 */
77 /* Used to keep track of the stats for a specific response code
78 * for example it can be { 3, 404, "Not Found" ,...}
79 * which means we captured 3 reply sip/1.1 404 Not Found */
80 typedef struct _sip_response_code_t {
81 guint32 packets; /* 3 */
82 guint response_code; /* 404 */
83 const gchar *name; /* "Not Found" */
84 GtkWidget *widget; /* Label where we display it */
85 GtkWidget *table; /* Table in which we put it,
86 e.g. client_error_table */
87 sipstat_t *sp; /* Pointer back to main struct */
88 } sip_response_code_t;
90 /* Used to keep track of the stats for a specific request string */
91 typedef struct _sip_request_method_t {
92 gchar *response; /* eg. : INVITE */
95 sipstat_t *sp; /* Pointer back to main struct */
96 } sip_request_method_t;
98 /* TODO: extra codes to be added from SIP extensions? */
99 static const value_string vals_status_code[] = {
102 { 181, "Call Is Being Forwarded"},
104 { 183, "Session Progress"},
105 { 199, "Informational - Others" },
109 { 204, "No Notification"},
110 { 299, "Success - Others"}, /* used to keep track of other Success packets */
112 { 300, "Multiple Choices"},
113 { 301, "Moved Permanently"},
114 { 302, "Moved Temporarily"},
116 { 380, "Alternative Service"},
117 { 399, "Redirection - Others"},
119 { 400, "Bad Request"},
120 { 401, "Unauthorized"},
121 { 402, "Payment Required"},
124 { 405, "Method Not Allowed"},
125 { 406, "Not Acceptable"},
126 { 407, "Proxy Authentication Required"},
127 { 408, "Request Timeout"},
129 { 412, "Conditional Request Failed"},
130 { 413, "Request Entity Too Large"},
131 { 414, "Request-URI Too Long"},
132 { 415, "Unsupported Media Type"},
133 { 416, "Unsupported URI Scheme"},
134 { 420, "Bad Extension"},
135 { 421, "Extension Required"},
136 { 422, "Session Timer Too Small"},
137 { 423, "Interval Too Brief"},
138 { 428, "Use Identity Header"},
139 { 429, "Provide Referrer Identity"},
140 { 430, "Flow Failed"},
141 { 433, "Anonymity Disallowed"},
142 { 436, "Bad Identity-Info"},
143 { 437, "Unsupported Certificate"},
144 { 438, "Invalid Identity Header"},
145 { 439, "First Hop Lacks Outbound Support"},
146 { 440, "Max-Breadth Exceeded"},
147 { 470, "Consent Needed"},
148 { 480, "Temporarily Unavailable"},
149 { 481, "Call/Transaction Does Not Exist"},
150 { 482, "Loop Detected"},
151 { 483, "Too Many Hops"},
152 { 484, "Address Incomplete"},
155 { 487, "Request Terminated"},
156 { 488, "Not Acceptable Here"},
158 { 491, "Request Pending"},
159 { 493, "Undecipherable"},
160 { 494, "Security Agreement Required"},
161 { 499, "Client Error - Others"},
163 { 500, "Server Internal Error"},
164 { 501, "Not Implemented"},
165 { 502, "Bad Gateway"},
166 { 503, "Service Unavailable"},
167 { 504, "Server Time-out"},
168 { 505, "Version Not Supported"},
169 { 513, "Message Too Large"},
170 { 599, "Server Error - Others"},
172 { 600, "Busy Everywhere"},
174 { 604, "Does Not Exist Anywhere"},
175 { 606, "Not Acceptable"},
176 { 699, "Global Failure - Others"},
181 void register_tap_listener_gtksipstat(void);
184 /* Create tables for responses and requests */
186 sip_init_hash(sipstat_t *sp)
190 /* Create responses table */
191 sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
193 /* Add all response codes */
194 for (i=0 ; vals_status_code[i].strptr ; i++)
196 gint *key = g_malloc (sizeof(gint));
197 sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t));
198 *key = vals_status_code[i].value;
200 sc->response_code = *key;
201 sc->name=vals_status_code[i].strptr;
205 g_hash_table_insert(sc->sp->hash_responses, key, sc);
208 /* Create empty requests table */
209 sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
212 /* Draw the entry for an individual request message */
214 sip_draw_hash_requests(gchar *key _U_ , sip_request_method_t *data, gchar * unused _U_)
216 gchar string_buff[SUM_STR_MAX];
218 g_assert(data!=NULL);
220 if (data->packets==0)
225 /* Build string showing method and count */
226 g_snprintf(string_buff, sizeof(string_buff),
227 " %-11s : %3d packets", data->response, data->packets);
228 if (data->widget==NULL)
230 /* Create new label */
231 data->widget=gtk_label_new(string_buff);
232 gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0f, 0.5f);
233 gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
234 gtk_widget_show(data->widget);
238 /* Update existing label */
239 gtk_label_set_text(GTK_LABEL(data->widget), string_buff);
243 /* Draw an individual response entry */
245 sip_draw_hash_responses(gint * key _U_ , sip_response_code_t *data, gchar * unused _U_)
247 gchar string_buff[SUM_STR_MAX];
249 g_assert(data!=NULL);
251 if (data->packets==0)
256 /* Create an entry in the relevant box of the window */
257 if (data->widget==NULL)
261 guint i = data->response_code;
263 /* Out of valid range - ignore */
264 if ((i<100)||(i>=700))
269 /* Find the table matching the code */
272 data->table = data->sp->informational_table;
276 data->table = data->sp->success_table;
280 data->table = data->sp->redirection_table;
284 data->table = data->sp->client_error_table;
288 data->table = data->sp->server_errors_table;
292 data->table = data->sp->global_failures_table;
295 /* Get number of rows in table */
296 x = GTK_TABLE(data->table)->nrows;
298 /* Create a new label with this response, e.g. "SIP 180 Ringing" */
299 g_snprintf(string_buff, sizeof(string_buff),
300 "SIP %3d %s ", data->response_code, data->name);
301 tmp = gtk_label_new(string_buff);
303 /* Insert the label in the correct place in the table */
304 gtk_table_attach_defaults(GTK_TABLE(data->table), tmp, 0, 1, x, x+1);
305 gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
306 gtk_widget_show(tmp);
308 /* Show number of packets */
309 g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
310 data->widget=gtk_label_new(string_buff);
312 /* Show this widget in the right place */
313 gtk_table_attach_defaults(GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
314 gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
315 gtk_widget_show(data->widget);
317 gtk_table_resize(GTK_TABLE(data->table), x+1, 4);
321 /* Just update the existing label string */
322 g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
323 gtk_label_set_text(GTK_LABEL(data->widget), string_buff);
330 sip_free_hash(gpointer key, gpointer value, gpointer user_data _U_)
337 sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_)
343 sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_)
349 sipstat_reset(void *psp)
355 sp->resent_packets = 0;
356 sp->average_setup_time = 0;
357 sp->max_setup_time = 0;
358 sp->max_setup_time = 0;
359 sp->no_of_completed_calls = 0;
360 sp->total_setup_time = 0;
361 g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
362 g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
366 /* Main entry point to SIP tap */
368 sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
370 const sip_info_value_t *value=pri;
371 sipstat_t *sp = (sipstat_t *)psp;
373 /* Total number of packets, including continuation packets */
376 /* Update resent count if flag set */
379 sp->resent_packets++;
382 /* Calculate average setup time */
383 if (value->setup_time){
384 sp->no_of_completed_calls++;
385 /* Check if it's the first value */
386 if ( sp->total_setup_time == 0 ){
387 sp->average_setup_time = value->setup_time;
388 sp->total_setup_time = value->setup_time;
389 sp->max_setup_time = value->setup_time;
390 sp->min_setup_time = value->setup_time;
392 sp->total_setup_time = sp->total_setup_time + value->setup_time;
393 if (sp->max_setup_time < value->setup_time){
394 sp->max_setup_time = value->setup_time;
396 if (sp->min_setup_time > value->setup_time){
397 sp->min_setup_time = value->setup_time;
399 /* Calculate average */
400 sp->average_setup_time = (guint32)(sp->total_setup_time / sp->no_of_completed_calls);
404 /* Looking at both requests and responses */
405 if (value->response_code != 0)
408 guint *key = g_malloc(sizeof(guint));
409 sip_response_code_t *sc;
411 /* Look up response code in hash table */
412 *key = value->response_code;
413 sc = g_hash_table_lookup(sp->hash_responses, key);
416 /* Non-standard status code ; we classify it as others
417 * in the relevant category
418 * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
420 int i = value->response_code;
421 if ((i<100) || (i>=700))
423 /* Forget about crazy values */
428 *key=199; /* Hopefully, this status code will never be used */
451 /* Now look up this fallback code to get its text description */
452 sc = g_hash_table_lookup(sp->hash_responses, key);
460 else if (value->request_method)
463 sip_request_method_t *sc;
465 /* Look up the request method in the table */
466 sc = g_hash_table_lookup(sp->hash_requests, value->request_method);
469 /* First of this type. Create structure and initialise */
470 sc=g_malloc(sizeof(sip_request_method_t));
471 sc->response = g_strdup(value->request_method);
475 /* Insert it into request table */
476 g_hash_table_insert(sp->hash_requests, sc->response, sc);
480 /* Already existed, just update count for that method */
483 /* g_free(value->request_method); */
487 /* No request method set. Just ignore */
494 /* Redraw the whole stats window */
496 sipstat_draw(void *psp)
498 gchar string_buff[SUM_STR_MAX];
501 /* Set summary label */
502 g_snprintf(string_buff, sizeof(string_buff),
503 "SIP stats (%d packets)", sp->packets);
504 gtk_label_set_text(GTK_LABEL(sp->packets_label), string_buff);
506 /* Set resend count label */
507 g_snprintf(string_buff, sizeof(string_buff),
508 "(%d resent packets)", sp->resent_packets);
509 gtk_label_set_text(GTK_LABEL(sp->resent_label), string_buff);
511 /* Draw responses and requests from their tables */
512 g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_draw_hash_responses, NULL);
513 g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_draw_hash_requests, NULL);
515 /* Set resend count label */
516 g_snprintf(string_buff, sizeof(string_buff),
517 "Average setup time %d ms\n Min %d ms\n Max %d ms", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time);
518 gtk_label_set_text(GTK_LABEL(sp->average_setup_time_label), string_buff);
520 gtk_widget_show_all(sp->win);
524 /* since the gtk2 implementation of tap is multithreaded we must protect
525 * remove_tap_listener() from modifying the list while draw_tap_listener()
526 * is running. the other protected block is in main.c
528 * there should not be any other critical regions in gtk2
530 /* When window is destroyed, clean up */
532 win_destroy_cb(GtkWindow *win _U_, gpointer data)
534 sipstat_t *sp=(sipstat_t *)data;
536 protect_thread_critical_region();
537 remove_tap_listener(sp);
538 unprotect_thread_critical_region();
540 g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_free_hash, NULL);
541 g_hash_table_destroy(sp->hash_responses);
542 g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_free_hash, NULL);
543 g_hash_table_destroy(sp->hash_requests);
549 /* Create a new instance of gtk_sipstat. */
551 gtk_sipstat_init(const char *optarg, void *userdata _U_)
554 const char *filter = NULL;
555 GString *error_string;
557 GtkWidget *main_vb, *separator,
558 *informational_fr, *success_fr, *redirection_fr,
559 *client_errors_fr, *server_errors_fr, *global_failures_fr,
565 if (strncmp (optarg, "sip,stat,", 9) == 0)
567 /* Skip those characters from filter to display */
576 /* Create sip stats window structure */
577 sp = g_malloc(sizeof(sipstat_t));
578 sp->win = dlg_window_new("sip-stat"); /* transient_for top_level */
579 gtk_window_set_destroy_with_parent (GTK_WINDOW(sp->win), TRUE);
581 /* Set title to include any filter given */
584 sp->filter = g_strdup(filter);
585 title = g_strdup_printf("SIP statistics with filter: %s", filter);
590 title = g_strdup("SIP statistics");
593 gtk_window_set_title(GTK_WINDOW(sp->win), title);
597 /* Create container for all widgets */
598 main_vb = gtk_vbox_new(FALSE, 12);
599 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
600 gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
602 /* Initialise & show number of packets */
604 sp->packets_label = gtk_label_new("SIP stats (0 SIP packets)");
605 gtk_container_add(GTK_CONTAINER(main_vb), sp->packets_label);
607 sp->resent_packets = 0;
608 sp->resent_label = gtk_label_new("(0 resent packets)");
609 gtk_container_add(GTK_CONTAINER(main_vb), sp->resent_label);
610 gtk_widget_show(sp->resent_label);
613 /* Informational response frame */
614 informational_fr = gtk_frame_new("Informational SIP 1xx");
615 gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
617 /* Information table (within that frame) */
618 sp->informational_table = gtk_table_new(0, 2, FALSE);
619 gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
621 /* Success table and frame */
622 success_fr = gtk_frame_new ("Success SIP 2xx");
623 gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
625 sp->success_table = gtk_table_new(0, 2, FALSE);
626 gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
628 /* Redirection table and frame */
629 redirection_fr = gtk_frame_new ("Redirection SIP 3xx");
630 gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
632 sp->redirection_table = gtk_table_new(0, 2, FALSE);
633 gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
635 /* Client Errors table and frame */
636 client_errors_fr = gtk_frame_new("Client errors SIP 4xx");
637 gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
639 sp->client_error_table = gtk_table_new(0, 2, FALSE);
640 gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
642 /* Server Errors table and frame */
643 server_errors_fr = gtk_frame_new("Server errors SIP 5xx");
644 gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
646 sp->server_errors_table = gtk_table_new(0, 2, FALSE);
647 gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
649 /* Global Failures table and frame */
650 global_failures_fr = gtk_frame_new("Global failures SIP 6xx");
651 gtk_container_add(GTK_CONTAINER(main_vb), global_failures_fr);
653 sp->global_failures_table = gtk_table_new(0, 2, FALSE);
654 gtk_container_add(GTK_CONTAINER(global_failures_fr), sp->global_failures_table);
657 /* Separator between requests and responses */
658 separator = gtk_hseparator_new();
659 gtk_container_add(GTK_CONTAINER(main_vb), separator);
661 /* Request table and frame */
662 request_fr = gtk_frame_new("List of request methods");
663 gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
664 gtk_container_set_border_width(GTK_CONTAINER(request_fr), 0);
666 sp->request_box = gtk_vbox_new(FALSE, 10);
667 gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
669 sp->average_setup_time = 0;
670 sp->max_setup_time =0;
671 sp->min_setup_time =0;
672 sp->average_setup_time_label = gtk_label_new("(Not calculated)");
673 gtk_container_add(GTK_CONTAINER(main_vb), sp->average_setup_time_label);
674 gtk_widget_show(sp->average_setup_time_label);
677 /* Register this tap listener now */
678 error_string = register_tap_listener("sip",
687 /* Error. We failed to attach to the tap. Clean up */
688 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
691 g_string_free(error_string, TRUE);
696 bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
697 gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
699 bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
700 window_set_cancel_button(sp->win, bt_close, window_cancel_button_cb);
702 g_signal_connect(sp->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
703 g_signal_connect(sp->win, "destroy", G_CALLBACK(win_destroy_cb), sp);
705 /* Display up-to-date contents */
706 gtk_widget_show_all(sp->win);
707 window_present(sp->win);
710 cf_retap_packets(&cfile);
711 gdk_window_raise(sp->win->window);
714 static tap_dfilter_dlg sip_stat_dlg = {
715 "SIP Packet Counter",
721 /* Register this tap listener and add menu item. */
723 register_tap_listener_gtksipstat(void)
725 register_dfilter_stat(&sip_stat_dlg, "_SIP", REGISTER_STAT_GROUP_TELEPHONY);