aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.common1
-rw-r--r--epan/dissectors/packet-radius.c428
-rw-r--r--epan/dissectors/packet-radius.h54
-rw-r--r--gtk/Makefile.common3
-rw-r--r--gtk/radius_stat.c373
-rw-r--r--tap-radiusstat.c226
6 files changed, 1045 insertions, 40 deletions
diff --git a/Makefile.common b/Makefile.common
index 21b07b7fc4..78b425907c 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -110,6 +110,7 @@ TSHARK_TAP_SRC = \
tap-mgcpstat.c \
tap-protocolinfo.c \
tap-protohierstat.c \
+ tap-radiusstat.c \
tap-rpcstat.c \
tap-rpcprogs.c \
tap-sctpchunkstat.c \
diff --git a/epan/dissectors/packet-radius.c b/epan/dissectors/packet-radius.c
index 4bc07797bf..4300b09187 100644
--- a/epan/dissectors/packet-radius.c
+++ b/epan/dissectors/packet-radius.c
@@ -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@gmail.com> - use FreeRADIUS' dictionary
+ * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - 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 @@ typedef struct {
#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
-
-#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 radius_dictionary_t* dict = NULL;
static int proto_radius = -1;
+
+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 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 = -1;
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 guint8 authenticator[AUTHENTICATOR_LENGTH];
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 @@ static const value_string radius_vals[] =
{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 @@ static void dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
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 @@ static void dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
* 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 @@ static void dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
}
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 @@ static void reinit_radius(void) {
}
}
+/* 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 @@ proto_register_radius(void)
{ "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_radius(void)
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 @@ proto_register_radius(void)
no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
+ radius_tap = register_tap("radius");
}
void
diff --git a/epan/dissectors/packet-radius.h b/epan/dissectors/packet-radius.h
index f35e95d048..c300cf7b53 100644
--- a/epan/dissectors/packet-radius.h
+++ b/epan/dissectors/packet-radius.h
@@ -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 @@ extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_i
/* 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
diff --git a/gtk/Makefile.common b/gtk/Makefile.common
index e3ffafba0d..280bc73ffa 100644
--- a/gtk/Makefile.common
+++ b/gtk/Makefile.common
@@ -163,7 +163,8 @@ WIRESHARK_TAP_SRC = \
mgcp_stat.c \
mtp3_stat.c \
mtp3_summary.c \
- ncp_stat.c \
+ ncp_stat.c \
+ radius_stat.c \
rpc_progs.c \
rpc_stat.c \
rtp_analysis.c \
diff --git a/gtk/radius_stat.c b/gtk/radius_stat.c
new file mode 100644
index 0000000000..3c1ba8a2a4
--- /dev/null
+++ b/gtk/radius_stat.c
@@ -0,0 +1,373 @@
+/* radius_stat.c
+ * radius-statistics for Wireshark
+ * Copyright 2006 Alejandro Vaquero <alejandrovaquero@yahoo.com>
+ *
+ *
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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);
+}
diff --git a/tap-radiusstat.c b/tap-radiusstat.c
new file mode 100644
index 0000000000..56f5979202
--- /dev/null
+++ b/tap-radiusstat.c
@@ -0,0 +1,226 @@
+/* tap-radiusstat.c
+ * Copyright 2006 Alejandro Vaquero <alejandrovaquero@yahoo.com>
+ *
+ *
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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);
+}
+