Added statistics gathering via RADIUS packets, to replace
authoraland <aland>
Thu, 19 Jun 2008 13:03:09 +0000 (13:03 +0000)
committeraland <aland>
Thu, 19 Jun 2008 13:03:09 +0000 (13:03 +0000)
SNMP code that doesn't work.

src/include/radius_snmp.h
src/include/radiusd.h
src/include/stats.h [new file with mode: 0644]
src/main/event.c
src/main/radius_snmp.c
src/main/stats.c [new file with mode: 0644]

index 038252a..c3d0523 100644 (file)
@@ -1,5 +1,8 @@
 #ifndef _RADIUS_SNMP_H
 #define _RADIUS_SNMP_H
+
+#include <freeradius-devel/stats.h>
+
 /*
  * Version:    $Id$
  */
@@ -9,6 +12,10 @@ RCSIDH(radius_snmp_h, "$Id$")
 
 #ifdef WITH_SNMP
 
+#ifndef WITH_STATS
+#error WITH_SNMP needs WITH_STATS
+#endif
+
 typedef enum smux_event_t {
   SMUX_NONE, SMUX_CONNECT, SMUX_READ
 } smux_event_t;
@@ -28,18 +35,7 @@ typedef struct rad_snmp_server_t {
        time_t          last_reset_time;
        int32_t         reset_time;
        int32_t         config_reset;
-       int32_t         total_requests;
-       int32_t         total_invalid_requests;
-       int32_t         total_dup_requests;
-       int32_t         total_responses;
-       int32_t         total_access_accepts;
-       int32_t         total_access_rejects;
-       int32_t         total_access_challenges;
-       int32_t         total_malformed_requests;
-       int32_t         total_bad_authenticators;
-       int32_t         total_packets_dropped;
-       int32_t         total_no_records;
-       int32_t         total_unknown_types;
+       fr_stats_t      stats;
 } rad_snmp_server_t;
 
 typedef struct rad_snmp_t {
@@ -55,34 +51,15 @@ typedef struct rad_snmp_t {
        int               smux_max_failures;
 } rad_snmp_t;
 
-/*
- *  Taken from RFC 2619 and RFC 2621
- */
-struct rad_snmp_client_entry_t {
-       int             index;
-       /* IP address */
-       /* Client ID (string ) */
-       uint32_t        requests;
-       uint32_t        dup_requests;
-       uint32_t        responses;
-       uint32_t        accepts;
-       uint32_t        rejects;
-       uint32_t        challenges;
-       uint32_t        malformed_requests;
-       uint32_t        bad_authenticators;
-       uint32_t        packets_dropped;
-       uint32_t        unknown_types;
-};
-
 extern rad_snmp_t      rad_snmp;
 
 #define RAD_SNMP_INC(_x) if (mainconfig.do_snmp) _x++
 #ifdef WITH_ACCOUNTING
 #define RAD_SNMP_TYPE_INC(_listener, _x) if (mainconfig.do_snmp) { \
                                      if (_listener->type == RAD_LISTEN_AUTH) { \
-                                       rad_snmp.auth._x++; \
+                                       rad_snmp.auth.stats._x++; \
                                     } else { if (_listener->type == RAD_LISTEN_ACCT) \
-                                       rad_snmp.acct._x++; } }
+                                       rad_snmp.acct.stats._x++; } }
 
 #define RAD_SNMP_CLIENT_INC(_listener, _client, _x) if (mainconfig.do_snmp) { \
                                      if (_listener->type == RAD_LISTEN_AUTH) { \
@@ -93,7 +70,7 @@ extern rad_snmp_t     rad_snmp;
 #else  /* WITH_ACCOUNTING */
 
 #define RAD_SNMP_TYPE_INC(_listener, _x) if (mainconfig.do_snmp) { \
-                                     rad_snmp.auth._x++; }
+                                     rad_snmp.auth.stats._x++; }
 
 #define RAD_SNMP_CLIENT_INC(_listener, _client, _x) if (mainconfig.do_snmp) { \
                                      _client->auth->_x++; }
index 0b3a7de..8b60b71 100644 (file)
@@ -86,13 +86,17 @@ typedef pthread_t child_pid_t;
 #define WITH_DYNAMIC_CLIENTS (1)
 #endif
 
+#ifndef WITHOUT_STATS
+#define WITH_STATS
+#endif
+#include <freeradius-devel/stats.h>
+
+
 /*
  *     See util.c
  */
 typedef struct request_data_t request_data_t;
 
-typedef struct rad_snmp_client_entry_t rad_snmp_client_entry_t;
-
 typedef struct radclient {
        fr_ipaddr_t             ipaddr;
        int                     prefix;
@@ -106,10 +110,10 @@ typedef struct radclient {
        char                    *server;
        int                     number; /* internal use only */
        const CONF_SECTION      *cs;
-#ifdef WITH_SNMP
-       rad_snmp_client_entry_t *auth;
+#ifdef WITH_STATS
+       fr_client_stats_t       *auth;
 #ifdef WITH_ACCOUNTING
-       rad_snmp_client_entry_t *acct;
+       fr_client_stats_t       *acct;
 #endif
 #endif
 
diff --git a/src/include/stats.h b/src/include/stats.h
new file mode 100644 (file)
index 0000000..1b067a5
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef FR_STATS_H
+#define FR_STATS_H
+
+/*
+ * stats.h     Structures and functions for statistics.
+ *
+ * Version:    $Id$
+ *
+ *   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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2005,2006,2007,2008  The FreeRADIUS server project
+ */
+
+#include <freeradius-devel/ident.h>
+RCSIDH(stats_h, "$Id$")
+
+#ifdef WITH_STATS_64BIT
+typedef uint64_t fr_uint_t;
+#else
+typedef uint32_t fr_uint_t;
+#endif
+
+#ifdef WITH_STATS
+typedef struct fr_stats_t {
+       fr_uint_t               total_requests;
+       fr_uint_t               total_invalid_requests;
+       fr_uint_t               total_dup_requests;
+       fr_uint_t               total_responses;
+       fr_uint_t               total_access_accepts;
+       fr_uint_t               total_access_rejects;
+       fr_uint_t               total_access_challenges;
+       fr_uint_t               total_malformed_requests;
+       fr_uint_t               total_bad_authenticators;
+       fr_uint_t               total_packets_dropped;
+       fr_uint_t               total_no_records;
+       fr_uint_t               total_unknown_types;
+} fr_stats_t;
+
+/*
+ *  Taken from RFC 2619 and RFC 2621
+ */
+typedef struct fr_client_stats_t {
+       /* IP address */
+       /* Client ID (string ) */
+       fr_uint_t               requests;
+       fr_uint_t       dup_requests;
+       fr_uint_t       responses;
+       fr_uint_t       accepts;
+       fr_uint_t       rejects;
+       fr_uint_t       challenges;
+       fr_uint_t       malformed_requests;
+       fr_uint_t       bad_authenticators;
+       fr_uint_t       packets_dropped;
+       fr_uint_t       unknown_types;
+} fr_client_stats_t;
+
+
+void request_stats_final(REQUEST *request);
+
+
+#else
+#define request_stats_final(_x)
+#endif
+
+#endif /* FR_STATS_H */
index d223faf..14a4f4f 100644 (file)
@@ -125,82 +125,6 @@ static void tv_add(struct timeval *tv, int usec_delay)
        }
 }
 
-#ifdef WITH_SNMP
-static void snmp_inc_counters(REQUEST *request)
-{
-       if (!request->root->do_snmp) return;
-
-       if (request->master_state == REQUEST_COUNTED) return;
-
-       if ((request->listener->type != RAD_LISTEN_AUTH) &&
-           (request->listener->type != RAD_LISTEN_ACCT)) return;
-
-       /*
-        *      Update the SNMP statistics.
-        *
-        *      Note that we do NOT do this in a child thread.
-        *      Instead, we update the stats when a request is
-        *      deleted, because only the main server thread calls
-        *      this function, which makes it thread-safe.
-        */
-       switch (request->reply->code) {
-       case PW_AUTHENTICATION_ACK:
-               rad_snmp.auth.total_responses++;
-               rad_snmp.auth.total_access_accepts++;
-               if (request->client && request->client->auth) {
-                       request->client->auth->accepts++;
-               }
-               break;
-
-       case PW_AUTHENTICATION_REJECT:
-               rad_snmp.auth.total_responses++;
-               rad_snmp.auth.total_access_rejects++;
-               if (request->client && request->client->auth) {
-                       request->client->auth->rejects++;
-               }
-               break;
-
-       case PW_ACCESS_CHALLENGE:
-               rad_snmp.auth.total_responses++;
-               rad_snmp.auth.total_access_challenges++;
-               if (request->client && request->client->auth) {
-                       request->client->auth->challenges++;
-               }
-               break;
-
-#ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_RESPONSE:
-               rad_snmp.acct.total_responses++;
-               if (request->client && request->client->acct) {
-                       request->client->acct->responses++;
-               }
-               break;
-#endif
-
-               /*
-                *      No response, it must have been a bad
-                *      authenticator.
-                */
-       case 0:
-               if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
-                       rad_snmp.auth.total_bad_authenticators++;
-                       if (request->client && request->client->auth) {
-                               request->client->auth->bad_authenticators++;
-                       }
-               }
-               break;
-
-       default:
-               break;
-       }
-
-       request->master_state = REQUEST_COUNTED;
-}
-#else
-#define snmp_inc_counters(_x)
-#endif
-
-
 static void remove_from_request_hash(REQUEST *request)
 {
        if (!request->in_request_hash) return;
@@ -208,7 +132,7 @@ static void remove_from_request_hash(REQUEST *request)
        fr_packet_list_yank(pl, request->packet);
        request->in_request_hash = FALSE;
 
-       snmp_inc_counters(request);
+       request_stats_final(request);
 }
 
 
@@ -1034,7 +958,7 @@ static void wait_a_bit(void *ctx)
 #ifdef HAVE_PTHREAD_H
                request->child_pid = NO_SUCH_CHILD_PID;
 #endif
-               snmp_inc_counters(request);
+               request_stats_final(request);
                cleanup_delay(request);
                return;
 
@@ -1043,7 +967,7 @@ static void wait_a_bit(void *ctx)
 #ifdef HAVE_PTHREAD_H
                request->child_pid = NO_SUCH_CHILD_PID;
 #endif
-               snmp_inc_counters(request);
+               request_stats_final(request);
 
        case REQUEST_PROXIED:
                rad_assert(request->next_callback != NULL);
index 0ef77cc..26e3183 100644 (file)
@@ -289,39 +289,39 @@ radAccServ(struct variable *vp, oid *name, size_t *length, int exact,
 
                case RADIUSACCSERVTOTALREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_requests;
+                       return (u_char *) &rad_snmp.acct.stats.total_requests;
 
                case RADIUSACCSERVTOTALINVALIDREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_invalid_requests;
+                       return (u_char *) &rad_snmp.acct.stats.total_invalid_requests;
 
                case RADIUSACCSERVTOTALDUPREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_dup_requests;
+                       return (u_char *) &rad_snmp.acct.stats.total_dup_requests;
 
                case RADIUSACCSERVTOTALRESPONSES:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_responses;
+                       return (u_char *) &rad_snmp.acct.stats.total_responses;
 
                case RADIUSACCSERVTOTALMALFORMEDREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_malformed_requests;
+                       return (u_char *) &rad_snmp.acct.stats.total_malformed_requests;
 
                case RADIUSACCSERVTOTALBADAUTHENTICATORS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_bad_authenticators;
+                       return (u_char *) &rad_snmp.acct.stats.total_bad_authenticators;
 
                case RADIUSACCSERVTOTALPACKETSDROPPED:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_packets_dropped;
+                       return (u_char *) &rad_snmp.acct.stats.total_packets_dropped;
 
                case RADIUSACCSERVTOTALNORECORDS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_no_records;
+                       return (u_char *) &rad_snmp.acct.stats.total_no_records;
 
                case RADIUSACCSERVTOTALUNKNOWNTYPES:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.acct.total_unknown_types;
+                       return (u_char *) &rad_snmp.acct.stats.total_unknown_types;
 
        }
 
@@ -434,43 +434,43 @@ radAuthServ(struct variable *vp, oid *name, size_t *length, int exact,
 
                case RADIUSAUTHSERVTOTALACCESSREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_requests;
+                       return (u_char *) &rad_snmp.auth.stats.total_requests;
 
                case RADIUSAUTHSERVTOTALINVALIDREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_invalid_requests;
+                       return (u_char *) &rad_snmp.auth.stats.total_invalid_requests;
 
                case RADIUSAUTHSERVTOTALDUPACCESSREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_dup_requests;
+                       return (u_char *) &rad_snmp.auth.stats.total_dup_requests;
 
                case RADIUSAUTHSERVTOTALACCESSACCEPTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_access_accepts;
+                       return (u_char *) &rad_snmp.auth.stats.total_access_accepts;
 
                case RADIUSAUTHSERVTOTALACCESSREJECTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_access_rejects;
+                       return (u_char *) &rad_snmp.auth.stats.total_access_rejects;
 
                case RADIUSAUTHSERVTOTALACCESSCHALLENGES:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_access_challenges;
+                       return (u_char *) &rad_snmp.auth.stats.total_access_challenges;
 
                case RADIUSAUTHSERVTOTALMALFORMEDACCESSREQUESTS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_malformed_requests;
+                       return (u_char *) &rad_snmp.auth.stats.total_malformed_requests;
 
                case RADIUSAUTHSERVTOTALBADAUTHENTICATORS:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_bad_authenticators;
+                       return (u_char *) &rad_snmp.auth.stats.total_bad_authenticators;
 
                case RADIUSAUTHSERVTOTALPACKETSDROPPED:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_packets_dropped;
+                       return (u_char *) &rad_snmp.auth.stats.total_packets_dropped;
 
                case RADIUSAUTHSERVTOTALUNKNOWNTYPES:
                        *var_len = sizeof(int32_t);
-                       return (u_char *) &rad_snmp.auth.total_unknown_types;
+                       return (u_char *) &rad_snmp.auth.stats.total_unknown_types;
 
        }
 
diff --git a/src/main/stats.c b/src/main/stats.c
new file mode 100644 (file)
index 0000000..b82a032
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * stats.c     Internal statistics handling.
+ *
+ * Version:    $Id$
+ *
+ *   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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2008  The FreeRADIUS server project
+ * Copyright 2008  Alan DeKok <aland@deployingradius.com>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/radius_snmp.h>
+
+#ifdef WITH_STATS
+
+#ifdef WITH_PROXY
+fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+void request_stats_final(REQUEST *request)
+{
+       if (request->master_state == REQUEST_COUNTED) return;
+
+       if ((request->listener->type != RAD_LISTEN_AUTH) &&
+           (request->listener->type != RAD_LISTEN_ACCT)) return;
+
+       /*
+        *      Update the statistics.
+        *
+        *      Note that we do NOT do this in a child thread.
+        *      Instead, we update the stats when a request is
+        *      deleted, because only the main server thread calls
+        *      this function, which makes it thread-safe.
+        */
+       switch (request->reply->code) {
+       case PW_AUTHENTICATION_ACK:
+               rad_snmp.auth.stats.total_responses++;
+               rad_snmp.auth.stats.total_access_accepts++;
+               if (request->client && request->client->auth) {
+                       request->client->auth->accepts++;
+               }
+               break;
+
+       case PW_AUTHENTICATION_REJECT:
+               rad_snmp.auth.stats.total_responses++;
+               rad_snmp.auth.stats.total_access_rejects++;
+               if (request->client && request->client->auth) {
+                       request->client->auth->rejects++;
+               }
+               break;
+
+       case PW_ACCESS_CHALLENGE:
+               rad_snmp.auth.stats.total_responses++;
+               rad_snmp.auth.stats.total_access_challenges++;
+               if (request->client && request->client->auth) {
+                       request->client->auth->challenges++;
+               }
+               break;
+
+#ifdef WITH_ACCOUNTINGxu
+       case PW_ACCOUNTING_RESPONSE:
+               rad_snmp.acct.stats.total_responses++;
+               if (request->client && request->client->acct) {
+                       request->client->acct->responses++;
+               }
+               break;
+#endif
+
+               /*
+                *      No response, it must have been a bad
+                *      authenticator.
+                */
+       case 0:
+               if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+                       rad_snmp.auth.stats.total_bad_authenticators++;
+                       if (request->client && request->client->auth) {
+                               request->client->auth->bad_authenticators++;
+                       }
+               }
+               break;
+
+       default:
+               break;
+       }
+
+#ifdef WITH_PROXY
+       if (!request->proxy) goto done; /* simplifies formatting */
+               
+       switch (request->proxy_reply->code) {
+       case PW_AUTHENTICATION_REQUEST:
+               proxy_auth_stats.total_requests += request->num_proxied_requests;
+               break;
+
+#ifdef WITH_ACCOUNTING
+       case PW_ACCOUNTING_REQUEST:
+               proxy_acct_stats.total_requests++;
+               break;
+#endif
+
+       default:
+               break;
+       }
+
+       if (!request->proxy_reply) goto done;   /* simplifies formatting */
+
+       switch (request->proxy_reply->code) {
+       case PW_AUTHENTICATION_ACK:
+               proxy_auth_stats.total_responses += request->num_proxied_responses;
+               proxy_auth_stats.total_access_accepts += request->num_proxied_responses;
+               break;
+
+       case PW_AUTHENTICATION_REJECT:
+               proxy_auth_stats.total_responses += request->num_proxied_responses;
+               proxy_auth_stats.total_access_rejects += request->num_proxied_responses;
+               break;
+
+       case PW_ACCESS_CHALLENGE:
+               proxy_auth_stats.total_responses += request->num_proxied_responses;
+               proxy_auth_stats.total_access_challenges += request->num_proxied_responses;
+               break;
+
+#ifdef WITH_ACCOUNTING
+       case PW_ACCOUNTING_RESPONSE:
+               rad_snmp.acct.stats.total_responses++;
+               break;
+#endif
+
+       default:
+               proxy_auth_stats.total_unknown_types++;
+               break;
+       }
+
+ done:
+#endif /* WITH_PROXY */
+
+       request->master_state = REQUEST_COUNTED;
+}
+
+typedef struct fr_stats2vp {
+       int     attribute;
+       size_t  offset;
+} fr_stats2vp;
+
+/*
+ *     Authentication
+ */
+static fr_stats2vp authvp[] = {
+       { 128, offsetof(fr_stats_t, total_requests) },
+       { 129, offsetof(fr_stats_t, total_access_accepts) },
+       { 130, offsetof(fr_stats_t, total_access_rejects) },
+       { 131, offsetof(fr_stats_t, total_access_challenges) },
+       { 132, offsetof(fr_stats_t, total_responses) },
+       { 133, offsetof(fr_stats_t, total_dup_requests) },
+       { 134, offsetof(fr_stats_t, total_malformed_requests) },
+       { 135, offsetof(fr_stats_t, total_bad_authenticators) },
+       { 136, offsetof(fr_stats_t, total_packets_dropped) },
+       { 137, offsetof(fr_stats_t, total_unknown_types) },
+       { 0, 0 }
+};
+
+#ifdef WITH_PROXY
+/*
+ *     Proxied authentication requests.
+ */
+static fr_stats2vp proxy_authvp[] = {
+       { 138, offsetof(fr_stats_t, total_requests) },
+       { 139, offsetof(fr_stats_t, total_access_accepts) },
+       { 140, offsetof(fr_stats_t, total_access_rejects) },
+       { 141, offsetof(fr_stats_t, total_access_challenges) },
+       { 142, offsetof(fr_stats_t, total_responses) },
+       { 143, offsetof(fr_stats_t, total_dup_requests) },
+       { 144, offsetof(fr_stats_t, total_malformed_requests) },
+       { 145, offsetof(fr_stats_t, total_bad_authenticators) },
+       { 146, offsetof(fr_stats_t, total_packets_dropped) },
+       { 147, offsetof(fr_stats_t, total_unknown_types) },
+       { 0, 0 }
+};
+#endif
+
+#define FR2ATTR(x) ((11344 << 16) | (x))
+
+void request_stats_reply(REQUEST *request)
+{
+       int i;
+       VALUE_PAIR *vp;
+
+       if (request->packet->code != PW_STATUS_SERVER) return;
+
+       if ((request->packet->src_ipaddr.af != AF_INET) ||
+           (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_LOOPBACK))) return;
+
+#if 0
+       vp = pairfind(request->packet->vps, PW_CLASS);
+       if (!vp || (strcmp(vp->vp_strvalue, "Statistics") != 0)) return;
+#endif
+
+       for (i = 0; authvp[i].attribute != 0; i++) {
+               vp = radius_paircreate(request, &request->reply->vps,
+                                      FR2ATTR(authvp[i].attribute),
+                                      PW_TYPE_INTEGER);
+               if (!vp) continue;
+
+               vp->vp_integer = *(int *)(((char *) &rad_snmp.auth.stats) + 
+                                         authvp[i].offset);
+       }
+
+#ifdef WITH_PROXY
+       for (i = 0; proxy_authvp[i].attribute != 0; i++) {
+               vp = radius_paircreate(request, &request->reply->vps,
+                                      FR2ATTR(proxy_authvp[i].attribute),
+                                      PW_TYPE_INTEGER);
+               if (!vp) continue;
+
+               vp->vp_integer = *(int *)(((char *) &proxy_auth_stats) + 
+                                         proxy_authvp[i].offset);
+       }
+#endif
+}
+
+#endif /* WITH_STATS */