2 * sip_stat 2004 Martin Mathieson
4 * $Id: sip_stat.c,v 1.3 2004/03/30 18:55:47 guy Exp $
5 * Copied from http_stat.c
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@ethereal.com>
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.
34 #include <epan/packet_info.h>
35 #include <epan/epan.h>
38 #include "simple_dialog.h"
40 #include "dlg_utils.h"
42 #include "../register.h"
43 #include "../packet-sip.h"
44 #include "../globals.h"
45 #include "compat_macros.h"
46 #include "../tap_dfilter_dlg.h"
47 #include "tap_dfilter_dlg.h"
49 #define SUM_STR_MAX 1024
51 /* Used to keep track of the statistics for an entire program interface */
52 typedef struct _sip_stats_t {
55 GHashTable *hash_responses;
56 GHashTable *hash_requests;
57 guint32 packets; /* number of sip packets, including continuations */
58 GtkWidget *packets_label;
60 GtkWidget *request_box; /* container for INVITE, ... */
62 GtkWidget *informational_table; /* Status code between 100 and 199 */
63 GtkWidget *success_table; /* 200 and 299 */
64 GtkWidget *redirection_table; /* 300 and 399 */
65 GtkWidget *client_error_table; /* 400 and 499 */
66 GtkWidget *server_errors_table; /* 500 and 599 */
67 GtkWidget *global_failures_table; /* 600 and 699 */
70 /* Used to keep track of the stats for a specific response code
71 * for example it can be { 3, 404, "Not Found" ,...}
72 * which means we captured 3 reply sip/1.1 404 Not Found */
73 typedef struct _sip_response_code_t {
74 guint32 packets; /* 3 */
75 guint response_code; /* 404 */
76 gchar *name; /* "Not Found" */
77 GtkWidget *widget; /* Label where we display it */
78 GtkWidget *table; /* Table in which we put it,
79 e.g. client_error_table */
80 sipstat_t *sp; /* Pointer back to main struct */
81 } sip_response_code_t;
83 /* Used to keep track of the stats for a specific request string */
84 typedef struct _sip_request_method_t {
85 gchar *response; /* eg. : INVITE */
88 sipstat_t *sp; /* Pointer back to main struct */
89 } sip_request_method_t;
91 /* TODO: extra codes to be added from SIP extensions? */
92 static const value_string vals_status_code[] = {
95 { 181, "Call Is Being Forwarded"},
97 { 183, "Session Progress"},
98 { 199, "Informational - Others" },
102 { 299, "Success - Others"}, /* used to keep track of other Success packets */
104 { 300, "Multiple Choices"},
105 { 301, "Moved Permanently"},
106 { 302, "Moved Temporarily"},
108 { 380, "Alternative Service"},
109 { 399, "Redirection - Others"},
111 { 400, "Bad Request"},
112 { 401, "Unauthorized"},
113 { 402, "Payment Required"},
116 { 405, "Method Not Allowed"},
117 { 406, "Not Acceptable"},
118 { 407, "Proxy Authentication Required"},
119 { 408, "Request Timeout"},
121 { 413, "Request Entity Too Large"},
122 { 414, "Request-URI Too Long"},
123 { 415, "Unsupported Media Type"},
124 { 416, "Unsupported URI Scheme"},
125 { 420, "Bad Extension"},
126 { 421, "Extension Required"},
127 { 423, "Interval Too Brief"},
128 { 480, "Temporarily Unavailable"},
129 { 481, "Call/Transaction Does Not Exist"},
130 { 482, "Loop Detected"},
131 { 483, "Too Many Hops"},
132 { 484, "Address Incomplete"},
135 { 487, "Request Terminated"},
136 { 488, "Not Acceptable Here"},
138 { 491, "Request Pending"},
139 { 493, "Undecipherable"},
140 { 499, "Client Error - Others"},
142 { 500, "Server Internal Error"},
143 { 501, "Not Implemented"},
144 { 502, "Bad Gateway"},
145 { 503, "Service Unavailable"},
146 { 504, "Server Time-out"},
147 { 505, "Version Not Supported"},
148 { 513, "Message Too Large"},
149 { 599, "Server Error - Others"},
151 { 600, "Busy Everywhere"},
153 { 604, "Does Not Exist Anywhere"},
154 { 606, "Not Acceptable"},
155 { 699, "Global Failure - Others"},
160 /* Create tables for responses and requests */
162 sip_init_hash(sipstat_t *sp)
166 /* Create responses table */
167 sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
169 /* Add all response codes */
170 for (i=0 ; vals_status_code[i].strptr ; i++)
172 gint *key = g_malloc (sizeof(gint));
173 sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t));
174 *key = vals_status_code[i].value;
176 sc->response_code = *key;
177 sc->name=vals_status_code[i].strptr;
181 g_hash_table_insert(sc->sp->hash_responses, key, sc);
184 /* Create empty requests table */
185 sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
188 /* Draw the entry for an individual request message */
190 sip_draw_hash_requests(gchar *key _U_ , sip_request_method_t *data, gchar * unused _U_)
192 gchar string_buff[SUM_STR_MAX];
194 if (data->packets==0)
199 /* Build string showing method and count */
200 g_snprintf(string_buff, sizeof(string_buff),
201 " %-11s : %3d packets", data->response, data->packets);
202 if (data->widget==NULL)
204 /* Create new label */
205 data->widget=gtk_label_new(string_buff);
206 gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0, 0.5);
207 gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
208 gtk_widget_show(data->widget);
212 /* Update existing label */
213 gtk_label_set(GTK_LABEL(data->widget), string_buff);
217 /* Draw an individual response entry */
219 sip_draw_hash_responses(gint * key _U_ , sip_response_code_t *data, gchar * unused _U_)
221 gchar string_buff[SUM_STR_MAX];
225 g_warning("C'est quoi ce borderl key=%d\n", *key);
227 if (data->packets==0)
232 /* Create an entry in the relevant box of the window */
233 if (data->widget==NULL)
237 guint i = data->response_code;
239 /* Out of valid range - ignore */
240 if ((i<100)||(i>=700))
245 /* Find the table matching the code */
248 data->table = data->sp->informational_table;
252 data->table = data->sp->success_table;
256 data->table = data->sp->redirection_table;
260 data->table = data->sp->client_error_table;
264 data->table = data->sp->server_errors_table;
268 data->table = data->sp->global_failures_table;
271 /* Get number of rows in table */
272 x = GTK_TABLE(data->table)->nrows;
274 /* Create a new label with this response, e.g. "SIP 180 Ringing" */
275 g_snprintf(string_buff, sizeof(string_buff),
276 "SIP %3d %s ", data->response_code, data->name);
277 tmp = gtk_label_new(string_buff);
279 /* Insert the label in the correct place in the table */
280 gtk_table_attach_defaults(GTK_TABLE(data->table), tmp, 0, 1, x, x+1);
281 gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
282 gtk_widget_show(tmp);
284 /* Show number of packets */
285 g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
286 data->widget=gtk_label_new(string_buff);
288 /* Show this widget in the right place */
289 gtk_table_attach_defaults(GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
290 gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
291 gtk_widget_show(data->widget);
293 gtk_table_resize(GTK_TABLE(data->table), x+1, 4);
297 /* Just update the existing label string */
298 g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
299 gtk_label_set(GTK_LABEL(data->widget), string_buff);
306 sip_free_hash(gpointer key, gpointer value, gpointer user_data _U_)
313 sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_)
319 sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_)
325 sipstat_reset(void *psp)
331 g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
332 g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
336 /* Main entry point to SIP tap */
338 sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
340 sip_info_value_t *value=pri;
341 sipstat_t *sp = (sipstat_t *)psp;
343 /* Total number of packets, including continuation packets */
346 /* Looking at both requests and responses */
347 if (value->response_code != 0)
350 guint *key = g_malloc(sizeof(guint));
351 sip_response_code_t *sc;
353 /* Look up response code in hash table */
354 *key = value->response_code;
355 sc = g_hash_table_lookup(sp->hash_responses, key);
358 /* Non-standard status code ; we classify it as others
359 * in the relevant category
360 * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
362 int i = value->response_code;
363 if ((i<100) || (i>=700))
365 /* Forget about crazy values */
370 *key=199; /* Hopefully, this status code will never be used */
393 /* Now look up this fallback code to get its text description */
394 sc = g_hash_table_lookup(sp->hash_responses, key);
402 else if (value->request_method)
405 sip_request_method_t *sc;
407 /* Look up the request method in the table */
408 sc = g_hash_table_lookup(sp->hash_requests, value->request_method);
411 /* First of this type. Create structure and initialise */
412 sc=g_malloc(sizeof(sip_request_method_t));
413 sc->response = g_strdup(value->request_method);
417 /* Insert it into request table */
418 g_hash_table_insert(sp->hash_requests, sc->response, sc);
422 /* Already existed, just update count for that method */
425 /* g_free(value->request_method); */
429 /* No request method set. Just ignore */
436 /* Redraw the whole stats window */
438 sipstat_draw(void *psp)
440 gchar string_buff[SUM_STR_MAX];
443 /* Set summary label */
444 g_snprintf(string_buff, sizeof(string_buff),
445 "SIP stats (%d packets)", sp->packets);
446 gtk_label_set(GTK_LABEL(sp->packets_label), string_buff);
448 /* Draw responses and requests from their tables */
449 g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_draw_hash_responses, NULL);
450 g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_draw_hash_requests, NULL);
452 gtk_widget_show_all(sp->win);
456 /* since the gtk2 implementation of tap is multithreaded we must protect
457 * remove_tap_listener() from modifying the list while draw_tap_listener()
458 * is running. the other protected block is in main.c
460 * there should not be any other critical regions in gtk2
462 void protect_thread_critical_region(void);
463 void unprotect_thread_critical_region(void);
465 /* When window is destroyed, clean up */
467 win_destroy_cb(GtkWindow *win _U_, gpointer data)
469 sipstat_t *sp=(sipstat_t *)data;
471 protect_thread_critical_region();
472 remove_tap_listener(sp);
473 unprotect_thread_critical_region();
475 g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_free_hash, NULL);
476 g_hash_table_destroy(sp->hash_responses);
477 g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_free_hash, NULL);
478 g_hash_table_destroy(sp->hash_requests);
483 /* Create a new instance of gtk_sipstat. */
485 gtk_sipstat_init(char *optarg)
489 GString *error_string;
491 GtkWidget *main_vb, *separator,
492 *informational_fr, *success_fr, *redirection_fr,
493 *client_errors_fr, *server_errors_fr, *global_failures_fr,
497 if (strncmp (optarg, "sip,stat,", 9) == 0)
499 /* Skip those characters from filter to display */
508 /* Create sip stats window structure */
509 sp = g_malloc(sizeof(sipstat_t));
511 /* Set title to include any filter given */
514 sp->filter = g_strdup(filter);
515 title = g_strdup_printf("SIP statistics with filter: %s", filter);
520 title = g_strdup("SIP statistics");
523 /* Create underlying window */
524 sp->win = window_new(GTK_WINDOW_TOPLEVEL, title);
527 /* Set destroy callback for underlying window */
528 SIGNAL_CONNECT(sp->win, "destroy", win_destroy_cb, sp);
531 /* Create container for all widgets */
532 main_vb = gtk_vbox_new(FALSE, 10);
533 gtk_container_border_width(GTK_CONTAINER(main_vb), 10);
534 gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
535 gtk_widget_show(main_vb);
537 /* Initialise & show number of packets */
539 sp->packets_label = gtk_label_new("SIP stats (0 SIP packets)");
540 gtk_container_add(GTK_CONTAINER(main_vb), sp->packets_label);
541 gtk_widget_show(sp->packets_label);
544 /* Informational response frame */
545 informational_fr = gtk_frame_new("Informational SIP 1xx");
546 gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
547 gtk_widget_show(informational_fr);
549 /* Information table (within that frame) */
550 sp->informational_table = gtk_table_new(0, 2, FALSE);
551 gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
552 gtk_widget_show(sp->informational_table);
555 /* Success table and frame */
556 success_fr = gtk_frame_new ("Success SIP 2xx");
557 gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
558 gtk_widget_show(success_fr);
560 sp->success_table = gtk_table_new(0, 2, FALSE);
561 gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
562 gtk_widget_show(sp->success_table);
565 /* Redirection table and frame */
566 redirection_fr = gtk_frame_new ("Redirection SIP 3xx");
567 gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
568 gtk_widget_show(redirection_fr);
570 sp->redirection_table = gtk_table_new(0, 2, FALSE);
571 gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
572 gtk_widget_show(sp->redirection_table);
575 /* Client Errors table and frame */
576 client_errors_fr = gtk_frame_new("Client errors SIP 4xx");
577 gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
578 gtk_widget_show(client_errors_fr);
580 sp->client_error_table = gtk_table_new(0, 2, FALSE);
581 gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
582 gtk_widget_show(sp->client_error_table);
585 /* Server Errors table and frame */
586 server_errors_fr = gtk_frame_new("Server errors SIP 5xx");
587 gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
588 gtk_widget_show(server_errors_fr);
590 sp->server_errors_table = gtk_table_new(0, 2, FALSE);
591 gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
592 gtk_widget_show(sp->server_errors_table);
595 /* Global Failures table and frame */
596 global_failures_fr = gtk_frame_new("Global failures SIP 6xx");
597 gtk_container_add(GTK_CONTAINER(main_vb), global_failures_fr);
598 gtk_widget_show(global_failures_fr);
600 sp->global_failures_table = gtk_table_new(0, 2, FALSE);
601 gtk_container_add(GTK_CONTAINER(global_failures_fr), sp->global_failures_table);
602 gtk_widget_show(sp->global_failures_table);
605 /* Separator between requests and responses */
606 separator = gtk_hseparator_new();
607 gtk_container_add(GTK_CONTAINER(main_vb), separator);
610 /* Request table and frame */
611 request_fr = gtk_frame_new("List of request methods");
612 gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
613 gtk_container_border_width(GTK_CONTAINER(request_fr), 0);
614 gtk_widget_show(request_fr);
616 sp->request_box = gtk_vbox_new(FALSE, 10);
617 gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
618 gtk_widget_show(sp->request_box);
621 /* Register this tap listener now */
622 error_string = register_tap_listener("sip",
630 /* Error. We failed to attach to the tap. Clean up */
631 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
634 g_string_free(error_string, TRUE);
638 /* Display up-to-date contents */
639 gtk_widget_show_all(sp->win);
641 retap_packets(&cfile);
644 static tap_dfilter_dlg sip_stat_dlg = {
645 "SIP Packet Counter",
651 /* Register this tap listener and add menu item. */
653 register_tap_listener_gtksipstat(void)
655 register_ethereal_tap("sip,stat", gtk_sipstat_init);
657 register_tap_menu_item("SIP", REGISTER_TAP_GROUP_NONE,
658 gtk_tap_dfilter_dlg_cb, NULL, NULL, &(sip_stat_dlg));