ANNOUNCEMENT: Live Wireshark University & Allegro Packets online APAC Wireshark Training Session
April 17th, 2024 | 14:30-16:00 SGT (UTC+8) | Online

Wireshark-dev: [Wireshark-dev] Radius Statistics Patch

From: Alejandro Vaquero <alejandrovaquero@xxxxxxxxx>
Date: Sun, 22 Oct 2006 18:17:18 -0700
Hi All,
find attached a patch for the Radius dissector to add statistics in the "Service Response Time" menu and the tshark stats.

Regards
Alejandro
Index: config.nmake
===================================================================
--- config.nmake	(revision 19652)
+++ config.nmake	(working copy)
@@ -205,14 +205,16 @@
 # If you don't have PORTAUDIO, comment this line out, so that 
 # PORTAUDIO_DIR isn't defined.
 #
-PORTAUDIO_DIR=$(WIRESHARK_LIBS)\portaudio_v18_1
+#PORTAUDIO_DIR=$(WIRESHARK_LIBS)\portaudio_v18_1
+PORTAUDIO_DIR=$(WIRESHARK_LIBS)\portaudio_v19
+
 #PORTAUDIO_DIR=$(WIRESHARK_LIBS)\portaudio_v19
 
 #
 # Version number of PortAudio
 # 
-PORTAUDIO_VERSION=18
-#PORTAUDIO_VERSION=19
+#PORTAUDIO_VERSION=18
+PORTAUDIO_VERSION=19
 
 #
 # Mandatory for GTK >= 2: Iconv
Index: epan/dissectors/packet-radius.c
===================================================================
--- epan/dissectors/packet-radius.c	(revision 19652)
+++ epan/dissectors/packet-radius.c	(working copy)
@@ -4,6 +4,7 @@
  * Copyright 1999 Johan Feyaerts
  * Changed 03/12/2003 Rui Carmo (http://the.taoofmac.com - added all 3GPP VSAs, some parsing)
  * Changed 07/2005 Luis Ontanon <luis.ontanon@xxxxxxxxx> - use FreeRADIUS' dictionary
+ * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@xxxxxxxxx> - add Conversations support
  *
  * $Id$
  *
@@ -60,6 +61,8 @@
 #include <epan/crypt-md5.h>
 #include <epan/sminmpec.h>
 #include <epan/filesystem.h>
+#include <epan/conversation.h>
+#include <epan/tap.h>
 #include <epan/addr_resolv.h>
 #include <epan/emem.h>
 
@@ -86,38 +89,20 @@
 #define UDP_PORT_RADACCT	1646
 #define UDP_PORT_RADACCT_NEW	1813
 
-#define RADIUS_ACCESS_REQUEST			1
-#define RADIUS_ACCESS_ACCEPT			2
-#define RADIUS_ACCESS_REJECT			3
-#define RADIUS_ACCOUNTING_REQUEST		4
-#define RADIUS_ACCOUNTING_RESPONSE		5
-#define RADIUS_ACCOUNTING_STATUS		6
-#define RADIUS_ACCESS_PASSWORD_REQUEST		7
-#define RADIUS_ACCESS_PASSWORD_ACK		8
-#define RADIUS_ACCESS_PASSWORD_REJECT		9
-#define RADIUS_ACCOUNTING_MESSAGE		10
-#define RADIUS_ACCESS_CHALLENGE			11
-#define RADIUS_STATUS_SERVER			12
-#define RADIUS_STATUS_CLIENT			13
+static radius_dictionary_t* dict = NULL;
 
-#define RADIUS_VENDOR_SPECIFIC_CODE		26
-#define RADIUS_ASCEND_ACCESS_NEXT_CODE		29
-#define RADIUS_ASCEND_ACCESS_NEW_PIN		30
-#define RADIUS_ASCEND_PASSWORD_EXPIRED		32
-#define RADIUS_ASCEND_ACCESS_EVENT_REQUEST	33
-#define RADIUS_ASCEND_ACCESS_EVENT_RESPONSE	34
-#define RADIUS_DISCONNECT_REQUEST		40
-#define RADIUS_DISCONNECT_REQUEST_ACK		41
-#define RADIUS_DISCONNECT_REQUEST_NAK		42
-#define RADIUS_CHANGE_FILTER_REQUEST		43
-#define RADIUS_CHANGE_FILTER_REQUEST_ACK	44
-#define RADIUS_CHANGE_FILTER_REQUEST_NAK	45
-#define RADIUS_EAP_MESSAGE_CODE				79
-#define RADIUS_RESERVED				255
+static int proto_radius = -1;
 
-static radius_dictionary_t* dict = NULL;
+static int hf_radius_req = -1;
+static int hf_radius_rsp = -1;
+static int hf_radius_req_frame = -1;
+static int hf_radius_rsp_frame = -1;
+static int hf_radius_time = -1;
 
-static int proto_radius = -1;
+static int hf_radius_dup = -1;
+static int hf_radius_req_dup = -1;
+static int hf_radius_rsp_dup = -1;
+
 static int hf_radius_id = -1;
 static int hf_radius_code = -1;
 static int hf_radius_length = -1;
@@ -134,6 +119,11 @@
 static gint ett_radius_avp = -1;
 static gint ett_eap = -1;
 
+/*
+ * Define the tap for radius
+ */
+static int radius_tap = -1;
+
 radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1};
 
 radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1 };
@@ -150,6 +140,8 @@
 
 static const value_string* radius_vendors = NULL;
 
+static radius_info_t rad_info;
+
 static const value_string radius_vals[] =
 {
 	{RADIUS_ACCESS_REQUEST,		"Access-Request"},
@@ -181,6 +173,67 @@
 	{0, NULL}
 };
 
+/*
+ * Init Hash table stuff for converation
+ */
+
+typedef struct _radius_call_info_key
+{
+	guint code;
+	guint ident;
+	conversation_t *conversation;
+	nstime_t req_time;
+} radius_call_info_key;
+
+static GMemChunk *radius_call_info_key_chunk;
+static GMemChunk *radius_call_info_value_chunk;
+static GHashTable *radius_calls;
+
+/* Compare 2 keys */
+static gint radius_call_equal(gconstpointer k1, gconstpointer k2)
+{
+	const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
+	const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
+
+	if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
+		nstime_t delta;
+
+		nstime_delta(&delta, &key1->req_time, &key2->req_time);
+		if (abs(nstime_to_sec(&delta)) > (double) 5) return 0;
+
+		if (key1->code == key2->code)
+			return 1;
+		/* check the request and response are of the same code type */
+		if (key1->code == RADIUS_ACCESS_REQUEST && ( key2->code == RADIUS_ACCESS_ACCEPT || key2->code == RADIUS_ACCESS_REJECT ) )
+			return 1;
+
+		if (key1->code == RADIUS_ACCOUNTING_REQUEST && key2->code == RADIUS_ACCOUNTING_RESPONSE )
+			return 1;
+
+		if (key1->code == RADIUS_ACCESS_PASSWORD_REQUEST && ( key2->code == RADIUS_ACCESS_PASSWORD_ACK || key2->code == RADIUS_ACCESS_PASSWORD_REJECT ) )
+			return 1;
+
+		if (key1->code == RADIUS_ASCEND_ACCESS_EVENT_REQUEST && key2->code == RADIUS_ASCEND_ACCESS_EVENT_RESPONSE )
+			return 1;
+
+		if (key1->code == RADIUS_DISCONNECT_REQUEST && ( key2->code == RADIUS_DISCONNECT_REQUEST_ACK || key2->code == RADIUS_DISCONNECT_REQUEST_NAK ) )
+			return 1;
+
+		if (key1->code == RADIUS_CHANGE_FILTER_REQUEST && ( key2->code == RADIUS_CHANGE_FILTER_REQUEST_ACK || key2->code == RADIUS_CHANGE_FILTER_REQUEST_NAK ) )
+			return 1;
+	} 
+	return 0;
+}
+
+/* Calculate a hash key */
+static guint radius_call_hash(gconstpointer k)
+{
+	const radius_call_info_key* key = (const radius_call_info_key*) k;
+
+	return key->ident + /*key->code + */ key->conversation->index;
+}
+
+
 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb) {
 	int len;
 	guint32 ip;
@@ -790,7 +843,24 @@
 	guint rhident;
 	guint avplength;
 	e_radiushdr rh;
-	
+
+	conversation_t* conversation;
+	radius_call_info_key radius_call_key;
+	radius_call_info_key *new_radius_call_key = NULL;
+	radius_call_t *radius_call = NULL;
+	nstime_t delta;
+	static address null_address = { AT_NONE, 0, NULL };
+
+	/* Initialise stat info for passing to tap */
+	rad_info.code = 0;
+	rad_info.ident = 0;
+	rad_info.req_time.secs = 0;
+	rad_info.req_time.nsecs = 0;
+	rad_info.is_duplicate = FALSE;
+	rad_info.request_available = FALSE;
+	rad_info.req_num = 0; /* frame number request seen */
+	rad_info.rspcode = 0;
+
 	if (check_col(pinfo->cinfo, COL_PROTOCOL))
 		col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
 	if (check_col(pinfo->cinfo, COL_INFO))
@@ -813,6 +883,10 @@
 		*  is 4096.
 		*/
 	
+	/* tap stat info */
+	rad_info.code = rhcode;
+	rad_info.ident = rhident;
+
 	if (check_col(pinfo->cinfo, COL_INFO))
 	{
 		col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
@@ -856,15 +930,225 @@
 	}
 	tvb_memcpy(tvb,authenticator,4,AUTHENTICATOR_LENGTH);
 
-	if (tree && avplength > 0) {
-		/* list the attribute value pairs */
-		avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
-					    avplength, "Attribute Value Pairs");
-		avptree = proto_item_add_subtree(avptf, ett_radius_avp);
-			
-		dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
-					      avplength);
+	if (tree) {
+
+		/* Conversation support REQUEST/RESPONSES */
+		switch (rhcode)
+		{
+			case RADIUS_ACCESS_REQUEST:
+			case RADIUS_ACCOUNTING_REQUEST:
+			case RADIUS_ACCESS_PASSWORD_REQUEST:
+			case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
+			case RADIUS_DISCONNECT_REQUEST:
+			case RADIUS_CHANGE_FILTER_REQUEST:
+				proto_tree_add_boolean_hidden(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
+				/* Keep track of the address and port whence the call came
+				 *  so that we can match up requests with replies.
+				 * 
+				 * Because it is UDP and the reply can come from any IP
+				 * and port (not necessarly the request dest), we only 
+				 * track the source IP and port of the request to match 
+				 * the reply. 
+				 */
+
+				/*
+				 * XXX - can we just use NO_ADDR_B?  Unfortunately,
+				 * you currently still have to pass a non-null
+				 * pointer for the second address argument even
+				 * if you do that.
+				 */
+				conversation = find_conversation(pinfo->fd->num, &pinfo->src,
+					                                 &null_address, pinfo->ptype, pinfo->srcport,
+					                                 pinfo->destport, 0);
+				if (conversation == NULL)
+				{
+					/* It's not part of any conversation - create a new one. */
+					conversation = conversation_new(pinfo->fd->num, &pinfo->src,
+					                                &null_address, pinfo->ptype, pinfo->srcport,
+					                                pinfo->destport, 0);
+				}
+
+				/* Prepare the key data */
+				radius_call_key.code = rhcode;
+				radius_call_key.ident = rhident;
+				radius_call_key.conversation = conversation;
+				radius_call_key.req_time = pinfo->fd->abs_ts;
+
+				/* Look up the request */
+				radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
+				if (radius_call != NULL)
+				{
+					/* We've seen a request with this ID, with the same 
+					   destination, before - but was it *this* request? */
+					if (pinfo->fd->num != radius_call->req_num)
+					{
+						/* No, so it's a duplicate request. Mark it as such. */
+						rad_info.is_duplicate = TRUE;
+						rad_info.req_num = radius_call->req_num;
+						if (check_col(pinfo->cinfo, COL_INFO))
+						{
+							col_append_fstr(pinfo->cinfo, COL_INFO,
+							                ", Duplicate Request ID:%u",
+							                rhident);
+						}
+						if (tree)
+						{
+							proto_item* item;
+							proto_tree_add_uint_hidden(radius_tree, hf_radius_dup, tvb, 0,0, rhident);
+							item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rhident);
+							PROTO_ITEM_SET_GENERATED(item);
+						}
+					}
+				}
+				else
+				{
+					/* Prepare the value data.
+					   "req_num" and "rsp_num" are frame numbers;
+					   frame numbers are 1-origin, so we use 0
+					   to mean "we don't yet know in which frame
+					   the reply for this call appears". */
+					new_radius_call_key = g_mem_chunk_alloc(radius_call_info_key_chunk);
+					*new_radius_call_key = radius_call_key;
+					radius_call = g_mem_chunk_alloc(radius_call_info_value_chunk);
+					radius_call->req_num = pinfo->fd->num;
+					radius_call->rsp_num = 0;
+					radius_call->ident = rhident;
+					radius_call->code = rhcode;
+					radius_call->responded = FALSE;
+					radius_call->req_time=pinfo->fd->abs_ts;
+					radius_call->rspcode = 0;
+
+					/* Store it */
+					g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
+				}
+				if (radius_call && radius_call->rsp_num)
+				{
+					proto_item* item = proto_tree_add_uint_format(radius_tree, hf_radius_rsp_frame,
+					                                              tvb, 0, 0, radius_call->rsp_num,
+					                                              "The response to this request is in frame %u",
+					                                              radius_call->rsp_num);
+					PROTO_ITEM_SET_GENERATED(item);
+				}
+				break;
+			case RADIUS_ACCESS_ACCEPT:
+			case RADIUS_ACCESS_REJECT:
+			case RADIUS_ACCOUNTING_RESPONSE:
+			case RADIUS_ACCESS_PASSWORD_ACK:
+			case RADIUS_ACCESS_PASSWORD_REJECT:
+			case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
+			case RADIUS_DISCONNECT_REQUEST_ACK:
+			case RADIUS_DISCONNECT_REQUEST_NAK:
+			case RADIUS_CHANGE_FILTER_REQUEST_ACK:
+			case RADIUS_CHANGE_FILTER_REQUEST_NAK:
+				proto_tree_add_boolean_hidden(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
+				/* Check for RADIUS response.  A response must match a call that
+				 * we've seen, and the response must be sent to the same
+				 * port and address that the call came from.
+				 *
+				 * Because it is UDP and the reply can come from any IP
+				 * and port (not necessarly the request dest), we only 
+				 * track the source IP and port of the request to match 
+				 * the reply. 
+				 */
+
+				/* XXX - can we just use NO_ADDR_B?  Unfortunately,
+				 * you currently still have to pass a non-null
+				 * pointer for the second address argument even
+				 * if you do that.
+				 */
+				conversation = find_conversation(pinfo->fd->num, &null_address,
+					                                 &pinfo->dst, pinfo->ptype, pinfo->srcport,
+					                                 pinfo->destport, 0);
+				if (conversation != NULL)
+				{
+					/* Look only for matching request, if
+					   matching conversation is available. */
+					/* Prepare the key data */
+					radius_call_key.code = rhcode;
+					radius_call_key.ident = rhident;
+					radius_call_key.conversation = conversation;
+					radius_call_key.req_time = pinfo->fd->abs_ts;
+
+					radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
+					if (radius_call)
+					{
+						/* Indicate the frame to which this is a reply. */
+						if (radius_call->req_num)
+						{
+							proto_item* item;
+							rad_info.request_available = TRUE;
+							rad_info.req_num = radius_call->req_num;
+							radius_call->responded = TRUE;
+
+							item = proto_tree_add_uint_format(radius_tree, hf_radius_req_frame,
+							                                  tvb, 0, 0, radius_call->req_num,
+							                                  "This is a response to a request in frame %u",
+							                                  radius_call->req_num);
+							PROTO_ITEM_SET_GENERATED(item);
+							nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
+							item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
+							PROTO_ITEM_SET_GENERATED(item);
+						}
+
+						if (radius_call->rsp_num == 0)
+						{
+							/* We have not yet seen a response to that call, so
+							   this must be the first response; remember its
+							   frame number. */
+							radius_call->rsp_num = pinfo->fd->num;
+						}
+						else
+						{
+							/* We have seen a response to this call - but was it
+							   *this* response? (disregard provisional responses) */
+							if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rhcode) )
+							{
+								/* No, so it's a duplicate response. Mark it as such. */
+								rad_info.is_duplicate = TRUE;
+								if (check_col(pinfo->cinfo, COL_INFO))
+								{
+									col_append_fstr(pinfo->cinfo, COL_INFO,
+									                ", Duplicate Response ID:%u",
+									                rhident);
+								}
+								if (tree)
+								{
+									proto_item* item;
+									proto_tree_add_uint_hidden(radius_tree, hf_radius_dup, tvb, 0,0, rhident);
+									item = proto_tree_add_uint(radius_tree, hf_radius_rsp_dup,
+									                           tvb, 0, 0, rhident);
+									PROTO_ITEM_SET_GENERATED(item);
+								}
+							}
+						}
+						/* Now store the response code (after comparison above) */
+						radius_call->rspcode = rhcode;
+						rad_info.rspcode = rhcode;
+					}
+				}
+				break;
+			default:
+				break;
+		}
+		
+		if (radius_call)
+		{
+			rad_info.req_time.secs = radius_call->req_time.secs;
+			rad_info.req_time.nsecs = radius_call->req_time.nsecs;
+		}
+
+		if (avplength > 0) {
+			/* list the attribute value pairs */
+			avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
+							avplength, "Attribute Value Pairs");
+			avptree = proto_item_add_subtree(avptf, ett_radius_avp);
+				
+			dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
+							  avplength);
+		}
 	}
+
+	tap_queue_packet(radius_tap, pinfo, &rad_info);
 }	
 
 
@@ -1034,10 +1318,62 @@
 	}	
 }
 
+/* Discard and init any state we've saved */
+static void 
+radius_init_protocol(void)
+{
+	if (radius_calls != NULL)
+	{
+		g_hash_table_destroy(radius_calls);
+		radius_calls = NULL;
+	}
+	if (radius_call_info_key_chunk != NULL)
+	{
+		g_mem_chunk_destroy(radius_call_info_key_chunk);
+		radius_call_info_key_chunk = NULL;
+	}
+	if (radius_call_info_value_chunk != NULL)
+	{
+		g_mem_chunk_destroy(radius_call_info_value_chunk);
+		radius_call_info_value_chunk = NULL;
+	}
+
+	radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
+	radius_call_info_key_chunk = g_mem_chunk_new("call_info_key_chunk",
+	                                           sizeof(radius_call_info_key),
+	                                           200 * sizeof(radius_call_info_key),
+	                                           G_ALLOC_ONLY);
+	radius_call_info_value_chunk = g_mem_chunk_new("call_info_value_chunk",
+	                                             sizeof(radius_call_t),
+	                                             200 * sizeof(radius_call_t),
+	                                             G_ALLOC_ONLY);
+}
+
 void
 proto_register_radius(void)
 {
 	hf_register_info base_hf[] = {
+
+	{ &hf_radius_req,
+    { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+		"TRUE if RADIUS request", HFILL }},
+
+	{ &hf_radius_rsp,
+	{ "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+		"TRUE if RADIUS response", HFILL }},
+
+	{ &hf_radius_req_frame,
+	{ "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
+            "Request Frame", HFILL }},
+
+	{ &hf_radius_rsp_frame,
+	{ "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
+            "Response Frame", HFILL }},
+
+	{ &hf_radius_time,
+	{ "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
+			"Timedelta between Request and Response", HFILL }},
+
 	{ &hf_radius_code,
 	{ "Code","radius.code", FT_UINT8, BASE_DEC, VALS(radius_vals), 0x0,
 		"", HFILL }},
@@ -1082,7 +1418,18 @@
 	{ "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
 		"", HFILL }},
 
-		
+	{ &hf_radius_dup,
+	{ "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
+		"Duplicate Message", HFILL }},
+
+	{ &hf_radius_req_dup,
+	{ "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
+		"Duplicate Request", HFILL }},
+
+	{ &hf_radius_rsp_dup,
+	{ "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
+		"Duplicate Response", HFILL }},
+
 	};
 	
 	gint *base_ett[] = {
@@ -1154,6 +1501,8 @@
 	proto_register_field_array(proto_radius,(hf_register_info*)(ri.hf->data),ri.hf->len);
 	proto_register_subtree_array((gint**)(ri.ett->data), ri.ett->len);
 
+	register_init_routine(&radius_init_protocol);
+
 	g_array_free(ri.hf,FALSE);
 	g_array_free(ri.ett,FALSE);
 	g_array_free(ri.vend_vs,FALSE);
@@ -1170,6 +1519,7 @@
 
     no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
     
+	radius_tap = register_tap("radius");
 }
 
 void
Index: epan/dissectors/packet-radius.h
===================================================================
--- epan/dissectors/packet-radius.h	(revision 19652)
+++ epan/dissectors/packet-radius.h	(working copy)
@@ -23,6 +23,35 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#define RADIUS_ACCESS_REQUEST			1
+#define RADIUS_ACCESS_ACCEPT			2
+#define RADIUS_ACCESS_REJECT			3
+#define RADIUS_ACCOUNTING_REQUEST		4
+#define RADIUS_ACCOUNTING_RESPONSE		5
+#define RADIUS_ACCOUNTING_STATUS		6
+#define RADIUS_ACCESS_PASSWORD_REQUEST		7
+#define RADIUS_ACCESS_PASSWORD_ACK		8
+#define RADIUS_ACCESS_PASSWORD_REJECT		9
+#define RADIUS_ACCOUNTING_MESSAGE		10
+#define RADIUS_ACCESS_CHALLENGE			11
+#define RADIUS_STATUS_SERVER			12
+#define RADIUS_STATUS_CLIENT			13
+
+#define RADIUS_VENDOR_SPECIFIC_CODE		26
+#define RADIUS_ASCEND_ACCESS_NEXT_CODE		29
+#define RADIUS_ASCEND_ACCESS_NEW_PIN		30
+#define RADIUS_ASCEND_PASSWORD_EXPIRED		32
+#define RADIUS_ASCEND_ACCESS_EVENT_REQUEST	33
+#define RADIUS_ASCEND_ACCESS_EVENT_RESPONSE	34
+#define RADIUS_DISCONNECT_REQUEST		40
+#define RADIUS_DISCONNECT_REQUEST_ACK		41
+#define RADIUS_DISCONNECT_REQUEST_NAK		42
+#define RADIUS_CHANGE_FILTER_REQUEST		43
+#define RADIUS_CHANGE_FILTER_REQUEST_ACK	44
+#define RADIUS_CHANGE_FILTER_REQUEST_NAK	45
+#define RADIUS_EAP_MESSAGE_CODE				79
+#define RADIUS_RESERVED				255
+
 typedef struct _radius_vendor_info_t {
 	const gchar *name;
 	guint code;
@@ -71,3 +100,28 @@
 
 /* from radius_dict.l */
 radius_dictionary_t* radius_load_dictionary (gchar* directory, const gchar* filename, gchar** err_str);
+
+/* Item of request list */
+typedef struct _radius_call_t
+{
+	guint code;
+	guint ident;
+
+	guint32 req_num; /* frame number request seen */
+	guint32 rsp_num; /* frame number response seen */
+	guint32 rspcode;
+	nstime_t req_time;
+	gboolean responded;
+} radius_call_t;
+
+/* Container for tapping relevant data */
+typedef struct _radius_info_t
+{
+	guint code;
+	guint ident;
+	nstime_t req_time;
+	gboolean is_duplicate;
+	gboolean request_available;
+	guint32 req_num; /* frame number request seen */
+	guint32 rspcode;
+} radius_info_t;
\ No newline at end of file
Index: gtk/Makefile.common
===================================================================
--- gtk/Makefile.common	(revision 19652)
+++ gtk/Makefile.common	(working copy)
@@ -164,6 +164,7 @@
 	mtp3_stat.c	\
 	mtp3_summary.c	\
 	ncp_stat.c  \
+	radius_stat.c	\
 	rpc_progs.c	\
 	rpc_stat.c	\
 	rtp_analysis.c	\
Index: gtk/radius_stat.c
===================================================================
--- gtk/radius_stat.c	(revision 0)
+++ gtk/radius_stat.c	(revision 0)
@@ -0,0 +1,373 @@
+/* radius_stat.c
+ * radius-statistics for Wireshark
+ * Copyright 2006 Alejandro Vaquero <alejandrovaquero@xxxxxxxxx>
+ *
+ * 
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+
+#include <epan/tap.h>
+#include "../register.h"
+#include <epan/dissectors/packet-radius.h>
+#include "../timestats.h"
+#include "gui_stat_util.h"
+#include "compat_macros.h"
+#include "../simple_dialog.h"
+#include "dlg_utils.h"
+#include "../file.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+#include "../tap_dfilter_dlg.h"
+#include "gui_utils.h"
+
+
+#define NUM_TIMESTATS 8
+#define NUM_COLUMNS 11
+
+/* Summary of response-time calculations*/
+typedef struct _radius_rtd_t {
+	guint32 open_req_num;
+	guint32 disc_rsp_num;
+	guint32 req_dup_num;
+	guint32 rsp_dup_num;
+	timestat_t stats;
+} radius_rtd_t;
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _radiusstat_t {
+	GtkWidget *win;
+	GtkWidget *vbox;
+	char *filter;
+	GtkWidget *scrolled_window;
+	GtkCList *table;
+	radius_rtd_t radius_rtd[NUM_TIMESTATS];
+} radiusstat_t;
+
+static const value_string radius_message_code[] = {
+  {  0,	"Overall"},
+  {  1,	"Access"},
+  {  2,	"Accounting"},
+  {  3,	"Access Password"},
+  {  4, "Ascend Access Event"},
+  {  5, "Diconnect"},
+  {  6, "Change Filter"},
+  {  7, "Other"},
+};
+
+typedef enum _radius_category {
+	OVERALL,
+	ACCESS,
+	ACCOUNTING,
+	ACCESS_PASSWORD,
+	ASCEND_ACCESS_EVENT,
+	DISCONNECT,
+	CHANGE_FILTER,
+	OTHERS
+}radius_category;
+
+static void
+radiusstat_reset(void *prs)
+{
+	radiusstat_t *rs=(radiusstat_t *)prs;
+	int i;
+
+
+	for(i=0;i<NUM_TIMESTATS;i++) {
+		rs->radius_rtd[i].stats.num=0;
+		rs->radius_rtd[i].stats.min_num=0;
+		rs->radius_rtd[i].stats.max_num=0;
+		rs->radius_rtd[i].stats.min.secs=0;
+        rs->radius_rtd[i].stats.min.nsecs=0;
+        rs->radius_rtd[i].stats.max.secs=0;
+        rs->radius_rtd[i].stats.max.nsecs=0;
+        rs->radius_rtd[i].stats.tot.secs=0;
+        rs->radius_rtd[i].stats.tot.nsecs=0;
+		rs->radius_rtd[i].open_req_num = 0;
+		rs->radius_rtd[i].disc_rsp_num = 0;
+		rs->radius_rtd[i].req_dup_num = 0;
+		rs->radius_rtd[i].rsp_dup_num = 0;
+	}
+
+}
+
+
+static int
+radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
+{
+	radiusstat_t *rs=(radiusstat_t *)prs;
+	const radius_info_t *ri=pri;
+	nstime_t delta;
+	radius_category radius_cat = OTHERS;
+
+	switch (ri->code) { 
+		case RADIUS_ACCESS_REQUEST:
+		case RADIUS_ACCESS_ACCEPT:
+		case RADIUS_ACCESS_REJECT:
+			radius_cat = ACCESS;
+			break;
+		case RADIUS_ACCOUNTING_REQUEST:
+		case RADIUS_ACCOUNTING_RESPONSE:
+			radius_cat = ACCOUNTING;
+			break;
+		case RADIUS_ACCESS_PASSWORD_REQUEST:
+		case RADIUS_ACCESS_PASSWORD_ACK:
+		case RADIUS_ACCESS_PASSWORD_REJECT:
+			radius_cat = ACCESS_PASSWORD;
+			break;
+		case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
+		case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
+			radius_cat = ASCEND_ACCESS_EVENT;
+			break;
+		case RADIUS_DISCONNECT_REQUEST:
+		case RADIUS_DISCONNECT_REQUEST_ACK:
+		case RADIUS_DISCONNECT_REQUEST_NAK:
+			radius_cat = DISCONNECT;
+			break;
+		case RADIUS_CHANGE_FILTER_REQUEST:
+		case RADIUS_CHANGE_FILTER_REQUEST_ACK:
+		case RADIUS_CHANGE_FILTER_REQUEST_NAK:
+			radius_cat = CHANGE_FILTER;
+			break;
+	}
+
+	switch (ri->code) {
+
+	case RADIUS_ACCESS_REQUEST:
+	case RADIUS_ACCOUNTING_REQUEST:
+	case RADIUS_ACCESS_PASSWORD_REQUEST:
+	case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
+	case RADIUS_DISCONNECT_REQUEST:
+	case RADIUS_CHANGE_FILTER_REQUEST:
+		if(ri->is_duplicate){
+			/* Duplicate is ignored */
+			rs->radius_rtd[OVERALL].req_dup_num++;
+			rs->radius_rtd[radius_cat].req_dup_num++;
+			return 0;
+		}
+		else {
+			rs->radius_rtd[OVERALL].open_req_num++;
+			rs->radius_rtd[radius_cat].open_req_num++;
+			return 0;
+		}
+	break;
+
+	case RADIUS_ACCESS_ACCEPT:
+	case RADIUS_ACCESS_REJECT:
+	case RADIUS_ACCOUNTING_RESPONSE:
+	case RADIUS_ACCESS_PASSWORD_ACK:
+	case RADIUS_ACCESS_PASSWORD_REJECT:
+	case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
+	case RADIUS_DISCONNECT_REQUEST_ACK:
+	case RADIUS_DISCONNECT_REQUEST_NAK:
+	case RADIUS_CHANGE_FILTER_REQUEST_ACK:
+	case RADIUS_CHANGE_FILTER_REQUEST_NAK:
+		if(ri->is_duplicate){
+			/* Duplicate is ignored */
+			rs->radius_rtd[OVERALL].rsp_dup_num++;
+			rs->radius_rtd[radius_cat].rsp_dup_num++;
+			return 0;
+		}
+		else if (!ri->request_available) {
+			/* no request was seen */
+			rs->radius_rtd[OVERALL].disc_rsp_num++;
+			rs->radius_rtd[radius_cat].disc_rsp_num++;
+			return 0;
+		}
+		else {
+			rs->radius_rtd[OVERALL].open_req_num--;
+			rs->radius_rtd[radius_cat].open_req_num--;
+			/* calculate time delta between request and response */
+			nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
+
+			time_stat_update(&(rs->radius_rtd[OVERALL].stats),&delta, pinfo);
+			time_stat_update(&(rs->radius_rtd[radius_cat].stats),&delta, pinfo);
+
+			return 1;
+		}
+	break;
+
+	default:
+		return 0;
+	break;
+	}
+}
+
+static void
+radiusstat_draw(void *prs)
+{
+	radiusstat_t *rs=(radiusstat_t *)prs;
+	int i;
+	/* gtk1 using a scrollable clist*/
+	char *str[NUM_COLUMNS];
+
+	for(i=0;i<NUM_COLUMNS;i++) {
+		str[i]=g_malloc(sizeof(char[256]));
+	}
+
+	/* clear list before printing */
+	gtk_clist_clear(rs->table);
+
+	for(i=0;i<NUM_TIMESTATS;i++) {
+		/* nothing seen, nothing to do */
+		if(rs->radius_rtd[i].stats.num==0){
+			continue;
+		}
+
+		g_snprintf(str[0], sizeof(char[256]), "%s", val_to_str(i,radius_message_code,"Other"));
+		g_snprintf(str[1], sizeof(char[256]), "%d", rs->radius_rtd[i].stats.num);
+		g_snprintf(str[2], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.min)));
+		g_snprintf(str[3], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.max)));
+		g_snprintf(str[4], sizeof(char[256]), "%8.2f msec", get_average(&(rs->radius_rtd[i].stats.tot), rs->radius_rtd[i].stats.num));
+		g_snprintf(str[5], sizeof(char[256]), "%6u", rs->radius_rtd[i].stats.min_num);
+		g_snprintf(str[6], sizeof(char[256]), "%6u", rs->radius_rtd[i].stats.max_num);
+		g_snprintf(str[7], sizeof(char[256]), "%4u", rs->radius_rtd[i].open_req_num);
+		g_snprintf(str[8], sizeof(char[256]), "%4u", rs->radius_rtd[i].disc_rsp_num);
+		g_snprintf(str[9], sizeof(char[256]), "%4u (%4.2f%%)", rs->radius_rtd[i].req_dup_num, 
+			rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].req_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
+		g_snprintf(str[10], sizeof(char[256]), "%4u (%4.2f%%)", rs->radius_rtd[i].rsp_dup_num, 
+			rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].rsp_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
+
+		gtk_clist_append(rs->table, str);
+	}
+
+	gtk_widget_show(GTK_WIDGET(rs->table));
+	for(i=0;i<NUM_COLUMNS;i++) {
+		g_free(str[i]);
+	}
+}
+
+void protect_thread_critical_region(void);
+void unprotect_thread_critical_region(void);
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+	radiusstat_t *rs=(radiusstat_t *)data;
+
+	protect_thread_critical_region();
+	remove_tap_listener(rs);
+	unprotect_thread_critical_region();
+
+	if(rs->filter){
+		g_free(rs->filter);
+		rs->filter=NULL;
+	}
+	g_free(rs);
+}
+
+static const gchar *titles[]={
+			"Type",
+			"Messages",
+			"Min SRT",
+			"Max SRT",
+			"Avg SRT",
+			"Min in Frame",
+			"Max in Frame",
+			"Open Requests",
+			"Discarded Responses",
+			"Repeated Requests",
+			"Repeated Responses" };
+
+static void
+gtk_radiusstat_init(const char *optarg, void *userdata _U_)
+{
+	radiusstat_t *rs;
+	const char *filter=NULL;
+	GString *error_string;
+	GtkWidget *bt_close;
+	GtkWidget *bbox;
+
+	if(strncmp(optarg,"radius,srt,",11) == 0){
+		filter=optarg+11;
+	} else {
+		filter="";
+	}
+
+	rs=g_malloc(sizeof(radiusstat_t));
+	rs->filter=g_strdup(filter);
+
+	radiusstat_reset(rs);
+
+	rs->win=window_new(GTK_WINDOW_TOPLEVEL, "RADIUS SRT");
+	gtk_window_set_default_size(GTK_WINDOW(rs->win), 600, 150);
+
+	rs->vbox=gtk_vbox_new(FALSE, 3);
+
+	init_main_stat_window(rs->win, rs->vbox, "RADIUS Service Response Time (SRT) Statistics", filter);
+
+	/* GTK1 using a scrollable clist*/
+        /* init a scrolled window*/
+	rs->scrolled_window = scrolled_window_new(NULL, NULL);
+
+	rs->table = create_stat_table(rs->scrolled_window, rs->vbox, NUM_COLUMNS, titles);
+
+	error_string=register_tap_listener("radius", rs, filter, radiusstat_reset, radiusstat_packet, radiusstat_draw);
+	if(error_string){
+		simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
+		g_string_free(error_string, TRUE);
+		g_free(rs->filter);
+		g_free(rs);
+		return;
+	}
+
+	/* Button row. */
+	bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+	gtk_box_pack_start(GTK_BOX(rs->vbox), bbox, FALSE, FALSE, 0);
+
+	bt_close = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
+	window_set_cancel_button(rs->win, bt_close, window_cancel_button_cb);
+
+	SIGNAL_CONNECT(rs->win, "delete_event", window_delete_event_cb, NULL);
+	SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
+
+	gtk_widget_show_all(rs->win);
+	window_present(rs->win);
+	
+	cf_retap_packets(&cfile, FALSE);
+}
+
+static tap_dfilter_dlg radius_srt_dlg = {
+	"RADIUS Service Response Time (SRT) Statistics",
+	"radius,srt",
+	gtk_radiusstat_init,
+	-1
+};
+
+void
+register_tap_listener_gtkradiusstat(void)
+{
+	register_dfilter_stat(&radius_srt_dlg, "RADIUS",
+		    REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
Index: Makefile.common
===================================================================
--- Makefile.common	(revision 19652)
+++ Makefile.common	(working copy)
@@ -110,6 +110,7 @@
 	tap-mgcpstat.c	\
 	tap-protocolinfo.c	\
 	tap-protohierstat.c	\
+	tap-radiusstat.c	\
 	tap-rpcstat.c	\
 	tap-rpcprogs.c	\
 	tap-sctpchunkstat.c	\
Index: tap-radiusstat.c
===================================================================
--- tap-radiusstat.c	(revision 0)
+++ tap-radiusstat.c	(revision 0)
@@ -0,0 +1,226 @@
+/* tap-radiusstat.c
+ * Copyright 2006 Alejandro Vaquero <alejandrovaquero@xxxxxxxxx>
+ *
+ * 
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+#include "epan/packet_info.h"
+#include <epan/tap.h>
+#include <epan/stat_cmd_args.h>
+#include "epan/value_string.h"
+#include "register.h"
+#include <epan/dissectors/packet-radius.h>
+#include "timestats.h"
+
+#define NUM_TIMESTATS 8
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _radiusstat_t {
+	char *filter;
+	timestat_t rtd[NUM_TIMESTATS];
+	guint32 open_req_num;
+	guint32 disc_rsp_num;
+	guint32 req_dup_num;
+	guint32 rsp_dup_num;
+} radiusstat_t;
+
+static const value_string radius_message_code[] = {
+  {  0,	"Overall       "},
+  {  1,	"Access        "},
+  {  2,	"Accounting    "},
+  {  3,	"Access Passw  "},
+  {  4, "Ascend Acce Ev"},
+  {  5, "Diconnect     "},
+  {  6, "Change Filter "},
+  {  7, "Other         "},
+};
+
+static int
+radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
+{
+	radiusstat_t *rs=(radiusstat_t *)prs;
+	const radius_info_t *ri=pri;
+	nstime_t delta;
+
+	switch (ri->code) {
+
+	case RADIUS_ACCESS_REQUEST:
+	case RADIUS_ACCOUNTING_REQUEST:
+	case RADIUS_ACCESS_PASSWORD_REQUEST:
+	case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
+	case RADIUS_DISCONNECT_REQUEST:
+	case RADIUS_CHANGE_FILTER_REQUEST:
+		if(ri->is_duplicate){
+			/* Duplicate is ignored */
+			rs->req_dup_num++;
+			return 0;
+		}
+		else {
+			rs->open_req_num++;
+			return 0;
+		}
+	break;
+
+	case RADIUS_ACCESS_ACCEPT:
+	case RADIUS_ACCESS_REJECT:
+	case RADIUS_ACCOUNTING_RESPONSE:
+	case RADIUS_ACCESS_PASSWORD_ACK:
+	case RADIUS_ACCESS_PASSWORD_REJECT:
+	case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
+	case RADIUS_DISCONNECT_REQUEST_ACK:
+	case RADIUS_DISCONNECT_REQUEST_NAK:
+	case RADIUS_CHANGE_FILTER_REQUEST_ACK:
+	case RADIUS_CHANGE_FILTER_REQUEST_NAK:
+		if(ri->is_duplicate){
+			/* Duplicate is ignored */
+			rs->rsp_dup_num++;
+			return 0;
+		}
+		else if (!ri->request_available) {
+			/* no request was seen */
+			rs->disc_rsp_num++;
+			return 0;
+		}
+		else {
+			rs->open_req_num--;
+			/* calculate time delta between request and response */
+			nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
+
+			time_stat_update(&(rs->rtd[0]),&delta, pinfo);
+			if (ri->code == RADIUS_ACCESS_ACCEPT || ri->code == RADIUS_ACCESS_REJECT) {
+				time_stat_update(&(rs->rtd[1]),&delta, pinfo);
+			}
+			else if (ri->code == RADIUS_ACCOUNTING_RESPONSE) {
+				time_stat_update(&(rs->rtd[2]),&delta, pinfo);
+			}
+
+
+
+			else {
+				time_stat_update(&(rs->rtd[7]),&delta, pinfo);
+			}
+
+			return 1;
+		}
+	break;
+
+	default:
+		return 0;
+	break;
+	}
+}
+
+static void
+radiusstat_draw(void *prs)
+{
+	radiusstat_t *rs=(radiusstat_t *)prs;
+	int i;
+
+	/* printing results */
+	printf("\n");
+	printf("===========================================================================================================\n");
+	printf("RADIUS Response Time Delay (RTD) Statistics:\n");
+	printf("Filter for statistics: %s\n",rs->filter?rs->filter:"");
+        printf("Duplicate requests: %u\n",rs->req_dup_num);
+        printf("Duplicate responses: %u\n",rs->rsp_dup_num);
+        printf("Open requests: %u\n",rs->open_req_num);
+        printf("Discarded responses: %u\n",rs->disc_rsp_num);
+        printf("Type           | Messages   |    Min RTD    |    Max RTD    |    Avg RTD    | Min in Frame | Max in Frame |\n");
+        for(i=0;i<NUM_TIMESTATS;i++) {
+        	if(rs->rtd[i].num) {
+        		printf("%s | %7u    | %8.2f msec | %8.2f msec | %8.2f msec |  %10u  |  %10u  |\n",
+        			val_to_str(i,radius_message_code,"Other  "),rs->rtd[i].num,
+				nstime_to_msec(&(rs->rtd[i].min)), nstime_to_msec(&(rs->rtd[i].max)),
+				get_average(&(rs->rtd[i].tot), rs->rtd[i].num),
+				rs->rtd[i].min_num, rs->rtd[i].max_num
+			);
+		}
+	}
+        printf("===========================================================================================================\n");
+}
+
+
+static void
+radiusstat_init(const char *optarg, void* userdata _U_)
+{
+	radiusstat_t *rs;
+	int i;
+	const char *filter=NULL;
+	GString *error_string;
+
+	if(!strncmp(optarg,"radius,rtd,",11)){
+		filter=optarg+11;
+	} else {
+		filter="";
+	}
+
+	rs=g_malloc(sizeof(radiusstat_t));
+	rs->filter=g_malloc(strlen(filter)+1);
+	strcpy(rs->filter, filter);
+
+	for(i=0;i<NUM_TIMESTATS;i++) {
+		rs->rtd[i].num=0;
+		rs->rtd[i].min_num=0;
+		rs->rtd[i].max_num=0;
+		rs->rtd[i].min.secs=0;
+        rs->rtd[i].min.nsecs=0;
+        rs->rtd[i].max.secs=0;
+        rs->rtd[i].max.nsecs=0;
+        rs->rtd[i].tot.secs=0;
+        rs->rtd[i].tot.nsecs=0;
+	}
+
+	rs->open_req_num=0;
+	rs->disc_rsp_num=0;
+	rs->req_dup_num=0;
+	rs->rsp_dup_num=0;
+
+	error_string=register_tap_listener("radius", rs, filter, NULL, radiusstat_packet, radiusstat_draw);
+	if(error_string){
+		/* error, we failed to attach to the tap. clean up */
+		g_free(rs->filter);
+		g_free(rs);
+
+		fprintf(stderr, "tshark: Couldn't register radius,rtd tap: %s\n",
+		    error_string->str);
+		g_string_free(error_string, TRUE);
+		exit(1);
+	}
+}
+
+
+void
+register_tap_listener_radiusstat(void)
+{
+	register_stat_cmd_arg("radius,rtd", radiusstat_init, NULL);
+}
+