#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/radius_snmp.h>
#ifdef WITH_STATS
+#define USEC (1000000)
+#define EMA_SCALE (100)
+#define PREC (USEC * EMA_SCALE)
+
+#define F_EMA_SCALE (1000000)
+
+static struct timeval start_time;
+static struct timeval hup_time;
+
+fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#ifdef WITH_ACCOUNTING
+fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
#ifdef WITH_PROXY
fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#ifdef WITH_ACCOUNTING
fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
+#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;
+ if ((request->listener->type != RAD_LISTEN_NONE) &&
+#ifdef WITH_ACCOUNTING
+ (request->listener->type != RAD_LISTEN_ACCT) &&
+#endif
+ (request->listener->type != RAD_LISTEN_AUTH)) return;
+
+#undef INC_AUTH
+#define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
+
+
+#undef INC_ACCT
+#ifdef WITH_ACCOUNTING
+#define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
+#else
+#define INC_ACCT(_x)
+#endif
/*
* Update the statistics.
* deleted, because only the main server thread calls
* this function, which makes it thread-safe.
*/
- switch (request->reply->code) {
+ if (request->reply) 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++;
- }
+ INC_AUTH(total_responses);
+ INC_AUTH(total_access_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++;
- }
+ INC_AUTH(total_responses);
+ INC_AUTH(total_access_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++;
- }
+ INC_AUTH(total_responses);
+ INC_AUTH(total_access_challenges);
break;
-#ifdef WITH_ACCOUNTINGxu
+#ifdef WITH_ACCOUNTING
case PW_ACCOUNTING_RESPONSE:
- rad_snmp.acct.stats.total_responses++;
- if (request->client && request->client->acct) {
- request->client->acct->responses++;
- }
+ INC_ACCT(total_responses);
break;
#endif
*/
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++;
+ if (request->reply->offset == -2) {
+ INC_AUTH(total_bad_authenticators);
+ } else {
+ INC_AUTH(total_packets_dropped);
+ }
+ } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+ if (request->reply->offset == -2) {
+ INC_ACCT(total_bad_authenticators);
+ } else {
+ INC_ACCT(total_packets_dropped);
}
}
break;
}
#ifdef WITH_PROXY
- if (!request->proxy) goto done; /* simplifies formatting */
-
- switch (request->proxy_reply->code) {
+ if (!request->proxy || !request->proxy_listener) goto done; /* simplifies formatting */
+
+ switch (request->proxy->code) {
case PW_AUTHENTICATION_REQUEST:
proxy_auth_stats.total_requests += request->num_proxied_requests;
+ request->proxy_listener->stats.total_requests += request->num_proxied_requests;
+ request->home_server->stats.total_requests += request->num_proxied_requests;
break;
#ifdef WITH_ACCOUNTING
case PW_ACCOUNTING_REQUEST:
proxy_acct_stats.total_requests++;
+ request->proxy_listener->stats.total_requests += request->num_proxied_requests;
+ request->home_server->stats.total_requests += request->num_proxied_requests;
break;
#endif
if (!request->proxy_reply) goto done; /* simplifies formatting */
+#undef INC
+#define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->proxy_listener->stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
+
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;
+ INC(total_responses);
+ INC(total_access_accepts);
break;
case PW_AUTHENTICATION_REJECT:
- proxy_auth_stats.total_responses += request->num_proxied_responses;
- proxy_auth_stats.total_access_rejects += request->num_proxied_responses;
+ INC(total_responses);
+ INC(total_access_rejects);
break;
case PW_ACCESS_CHALLENGE:
- proxy_auth_stats.total_responses += request->num_proxied_responses;
- proxy_auth_stats.total_access_challenges += request->num_proxied_responses;
+ INC(total_responses);
+ INC(total_access_challenges);
break;
#ifdef WITH_ACCOUNTING
case PW_ACCOUNTING_RESPONSE:
- rad_snmp.acct.stats.total_responses++;
+ proxy_acct_stats.total_responses++;
+ request->proxy_listener->stats.total_responses++;
+ request->home_server->stats.total_responses++;
break;
#endif
default:
proxy_auth_stats.total_unknown_types++;
+ request->proxy_listener->stats.total_unknown_types++;
+ request->home_server->stats.total_unknown_types++;
break;
}
{ 0, 0 }
};
+
#ifdef WITH_PROXY
/*
* Proxied authentication requests.
};
#endif
-#define FR2ATTR(x) ((11344 << 16) | (x))
-void request_stats_reply(REQUEST *request)
+#ifdef WITH_ACCOUNTING
+/*
+ * Accounting
+ */
+static fr_stats2vp acctvp[] = {
+ { 148, offsetof(fr_stats_t, total_requests) },
+ { 149, offsetof(fr_stats_t, total_responses) },
+ { 150, offsetof(fr_stats_t, total_dup_requests) },
+ { 151, offsetof(fr_stats_t, total_malformed_requests) },
+ { 152, offsetof(fr_stats_t, total_bad_authenticators) },
+ { 153, offsetof(fr_stats_t, total_packets_dropped) },
+ { 154, offsetof(fr_stats_t, total_unknown_types) },
+ { 0, 0 }
+};
+
+#ifdef WITH_PROXY
+static fr_stats2vp proxy_acctvp[] = {
+ { 155, offsetof(fr_stats_t, total_requests) },
+ { 156, offsetof(fr_stats_t, total_responses) },
+ { 157, offsetof(fr_stats_t, total_dup_requests) },
+ { 158, offsetof(fr_stats_t, total_malformed_requests) },
+ { 159, offsetof(fr_stats_t, total_bad_authenticators) },
+ { 160, offsetof(fr_stats_t, total_packets_dropped) },
+ { 161, offsetof(fr_stats_t, total_unknown_types) },
+ { 0, 0 }
+};
+#endif
+#endif
+
+static fr_stats2vp client_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_ACCOUNTING
+static fr_stats2vp client_acctvp[] = {
+ { 148, offsetof(fr_stats_t, total_requests) },
+ { 149, offsetof(fr_stats_t, total_responses) },
+ { 150, offsetof(fr_stats_t, total_dup_requests) },
+ { 151, offsetof(fr_stats_t, total_malformed_requests) },
+ { 152, offsetof(fr_stats_t, total_bad_authenticators) },
+ { 153, offsetof(fr_stats_t, total_packets_dropped) },
+ { 154, offsetof(fr_stats_t, total_unknown_types) },
+ { 0, 0 }
+};
+#endif
+
+static void request_stats_addvp(REQUEST *request,
+ fr_stats2vp *table, fr_stats_t *stats)
{
int i;
VALUE_PAIR *vp;
- if (request->packet->code != PW_STATUS_SERVER) return;
+ for (i = 0; table[i].attribute != 0; i++) {
+ vp = radius_paircreate(request, &request->reply->vps,
+ table[i].attribute, VENDORPEC_FREERADIUS,
+ PW_TYPE_INTEGER);
+ if (!vp) continue;
+
+ vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
+ }
+}
+
+
+void request_stats_reply(REQUEST *request)
+{
+ VALUE_PAIR *flag, *vp;
+
+ /*
+ * Statistics are available ONLY on a "status" port.
+ */
+ rad_assert(request->packet->code == PW_STATUS_SERVER);
+ rad_assert(request->listener->type == RAD_LISTEN_NONE);
+
+ flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS);
+ if (!flag || (flag->vp_integer == 0)) return;
+
+ /*
+ * Authentication.
+ */
+ if (((flag->vp_integer & 0x01) != 0) &&
+ ((flag->vp_integer & 0xc0) == 0)) {
+ request_stats_addvp(request, authvp, &radius_auth_stats);
+ }
+
+#ifdef WITH_ACCOUNTING
+ /*
+ * Accounting
+ */
+ if (((flag->vp_integer & 0x02) != 0) &&
+ ((flag->vp_integer & 0xc0) == 0)) {
+ request_stats_addvp(request, acctvp, &radius_acct_stats);
+ }
+#endif
- if ((request->packet->src_ipaddr.af != AF_INET) ||
- (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_LOOPBACK))) return;
+#ifdef WITH_PROXY
+ /*
+ * Proxied authentication requests.
+ */
+ if (((flag->vp_integer & 0x04) != 0) &&
+ ((flag->vp_integer & 0x20) == 0)) {
+ request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
+ }
-#if 0
- vp = pairfind(request->packet->vps, PW_CLASS);
- if (!vp || (strcmp(vp->vp_strvalue, "Statistics") != 0)) return;
+#ifdef WITH_ACCOUNTING
+ /*
+ * Proxied accounting requests.
+ */
+ if (((flag->vp_integer & 0x08) != 0) &&
+ ((flag->vp_integer & 0x20) == 0)) {
+ request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
+ }
+#endif
#endif
- for (i = 0; authvp[i].attribute != 0; i++) {
+ /*
+ * Internal server statistics
+ */
+ if ((flag->vp_integer & 0x10) != 0) {
vp = radius_paircreate(request, &request->reply->vps,
- FR2ATTR(authvp[i].attribute),
- PW_TYPE_INTEGER);
- if (!vp) continue;
+ 176, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
+ if (vp) vp->vp_date = start_time.tv_sec;
+ vp = radius_paircreate(request, &request->reply->vps,
+ 177, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
+ if (vp) vp->vp_date = hup_time.tv_sec;
+
+#ifdef HAVE_PTHREAD_H
+ int i, array[RAD_LISTEN_MAX];
+
+ thread_pool_queue_stats(array);
+
+#ifdef WITH_DETAIL
+ for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
+ vp = radius_paircreate(request, &request->reply->vps,
+ 162 + i, VENDORPEC_FREERADIUS,
+ PW_TYPE_INTEGER);
+
+ if (!vp) continue;
+ vp->vp_integer = array[i];
+ }
+#endif
+#endif
+ }
+
+ /*
+ * For a particular client.
+ */
+ if ((flag->vp_integer & 0x20) != 0) {
+ fr_ipaddr_t ipaddr;
+ VALUE_PAIR *server_ip, *server_port = NULL;
+ RADCLIENT *client = NULL;
+ RADCLIENT_LIST *cl = NULL;
+
+ /*
+ * See if we need to look up the client by server
+ * socket.
+ */
+ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
+ if (server_ip) {
+ server_port = pairfind(request->packet->vps,
+ 171, VENDORPEC_FREERADIUS);
+
+ if (server_port) {
+ ipaddr.af = AF_INET;
+ ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
+ cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
+
+ /*
+ * Not found: don't do anything
+ */
+ if (!cl) return;
+ }
+ }
+
+
+ vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS);
+ if (vp) {
+ ipaddr.af = AF_INET;
+ ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+ client = client_find(cl, &ipaddr, IPPROTO_UDP);
+#ifdef WITH_TCP
+ if (!client) {
+ client = client_find(cl, &ipaddr, IPPROTO_TCP);
+ }
+#endif
+
+ /*
+ * Else look it up by number.
+ */
+ } else if ((vp = pairfind(request->packet->vps,
+ 168, VENDORPEC_FREERADIUS)) != NULL) {
+ client = client_findbynumber(cl, vp->vp_integer);
+ }
+
+ if (client) {
+ /*
+ * If found, echo it back, along with
+ * the requested statistics.
+ */
+ pairadd(&request->reply->vps, paircopyvp(vp));
+
+ /*
+ * When retrieving client by number, also
+ * echo back it's IP address.
+ */
+ if ((vp->type == PW_TYPE_INTEGER) &&
+ (client->ipaddr.af == AF_INET)) {
+ vp = radius_paircreate(request,
+ &request->reply->vps,
+ 167, VENDORPEC_FREERADIUS,
+ PW_TYPE_IPADDR);
+ if (vp) {
+ vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
+ }
+
+ if (client->prefix != 32) {
+ vp = radius_paircreate(request,
+ &request->reply->vps,
+ 169, VENDORPEC_FREERADIUS,
+ PW_TYPE_INTEGER);
+ if (vp) {
+ vp->vp_integer = client->prefix;
+ }
+ }
+ }
+
+ if (server_ip) {
+ pairadd(&request->reply->vps,
+ paircopyvp(server_ip));
+ pairadd(&request->reply->vps,
+ paircopyvp(server_port));
+ }
+
+ if (client->auth &&
+ ((flag->vp_integer & 0x01) != 0)) {
+ request_stats_addvp(request, client_authvp,
+ client->auth);
+ }
+#ifdef WITH_ACCOUNTING
+ if (client->acct &&
+ ((flag->vp_integer & 0x01) != 0)) {
+ request_stats_addvp(request, client_acctvp,
+ client->acct);
+ }
+#endif
+ } /* else client wasn't found, don't echo it back */
+ }
+
+ /*
+ * For a particular "listen" socket.
+ */
+ if (((flag->vp_integer & 0x40) != 0) &&
+ ((flag->vp_integer & 0x03) != 0)) {
+ rad_listen_t *this;
+ VALUE_PAIR *server_ip, *server_port;
+ fr_ipaddr_t ipaddr;
- vp->vp_integer = *(int *)(((char *) &rad_snmp.auth.stats) +
- authvp[i].offset);
+ /*
+ * See if we need to look up the server by socket
+ * socket.
+ */
+ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
+ if (!server_ip) return;
+
+ server_port = pairfind(request->packet->vps,
+ 171, VENDORPEC_FREERADIUS);
+ if (!server_port) return;
+
+ ipaddr.af = AF_INET;
+ ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
+ this = listener_find_byipaddr(&ipaddr,
+ server_port->vp_integer);
+
+ /*
+ * Not found: don't do anything
+ */
+ if (!this) return;
+
+ pairadd(&request->reply->vps,
+ paircopyvp(server_ip));
+ pairadd(&request->reply->vps,
+ paircopyvp(server_port));
+
+ if (((flag->vp_integer & 0x01) != 0) &&
+ ((request->listener->type == RAD_LISTEN_AUTH) ||
+ (request->listener->type == RAD_LISTEN_NONE))) {
+ request_stats_addvp(request, authvp, &this->stats);
+ }
+
+#ifdef WITH_ACCOUNTING
+ if (((flag->vp_integer & 0x02) != 0) &&
+ ((request->listener->type == RAD_LISTEN_ACCT) ||
+ (request->listener->type == RAD_LISTEN_NONE))) {
+ request_stats_addvp(request, acctvp, &this->stats);
+ }
+#endif
}
#ifdef WITH_PROXY
- for (i = 0; proxy_authvp[i].attribute != 0; i++) {
+ /*
+ * Home servers.
+ */
+ if (((flag->vp_integer & 0x80) != 0) &&
+ ((flag->vp_integer & 0x03) != 0)) {
+ home_server *home;
+ VALUE_PAIR *server_ip, *server_port;
+ fr_ipaddr_t ipaddr;
+
+ /*
+ * See if we need to look up the server by socket
+ * socket.
+ */
+ server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
+ if (!server_ip) return;
+
+ server_port = pairfind(request->packet->vps,
+ 171, VENDORPEC_FREERADIUS);
+ if (!server_port) return;
+
+ ipaddr.af = AF_INET;
+ ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
+ home = home_server_find(&ipaddr, server_port->vp_integer,
+ IPPROTO_UDP);
+
+ /*
+ * Not found: don't do anything
+ */
+ if (!home) return;
+
+ pairadd(&request->reply->vps,
+ paircopyvp(server_ip));
+ pairadd(&request->reply->vps,
+ paircopyvp(server_port));
+
vp = radius_paircreate(request, &request->reply->vps,
- FR2ATTR(proxy_authvp[i].attribute),
- PW_TYPE_INTEGER);
- if (!vp) continue;
+ 172, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
+ if (vp) vp->vp_integer = home->currently_outstanding;
+
+ vp = radius_paircreate(request, &request->reply->vps,
+ 173, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
+ if (vp) vp->vp_integer = home->state;
+
+ if ((home->state == HOME_STATE_ALIVE) &&
+ (home->revive_time.tv_sec != 0)) {
+ vp = radius_paircreate(request, &request->reply->vps,
+ 175, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
+ if (vp) vp->vp_date = home->revive_time.tv_sec;
+ }
+
+ if ((home->state == HOME_STATE_ALIVE) &&
+ (home->ema.window > 0)) {
+ vp = radius_paircreate(request,
+ &request->reply->vps,
+ 178, VENDORPEC_FREERADIUS,
+ PW_TYPE_INTEGER);
+ if (vp) vp->vp_integer = home->ema.window;
+ vp = radius_paircreate(request,
+ &request->reply->vps,
+ 179, VENDORPEC_FREERADIUS,
+ PW_TYPE_INTEGER);
+ if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
+ vp = radius_paircreate(request,
+ &request->reply->vps,
+ 180, VENDORPEC_FREERADIUS,
+ PW_TYPE_INTEGER);
+ if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
+
+ }
+
+ if (home->state == HOME_STATE_IS_DEAD) {
+ vp = radius_paircreate(request, &request->reply->vps,
+ 174, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
+ if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
+ }
- vp->vp_integer = *(int *)(((char *) &proxy_auth_stats) +
- proxy_authvp[i].offset);
+ if (((flag->vp_integer & 0x01) != 0) &&
+ (home->type == HOME_TYPE_AUTH)) {
+ request_stats_addvp(request, proxy_authvp,
+ &home->stats);
+ }
+
+#ifdef WITH_ACCOUNTING
+ if (((flag->vp_integer & 0x02) != 0) &&
+ (home->type == HOME_TYPE_ACCT)) {
+ request_stats_addvp(request, proxy_acctvp,
+ &home->stats);
+ }
+#endif
+ }
+#endif /* WITH_PROXY */
+}
+
+void radius_stats_init(int flag)
+{
+ if (!flag) {
+ gettimeofday(&start_time, NULL);
+ hup_time = start_time; /* it's just nicer this way */
+ } else {
+ gettimeofday(&hup_time, NULL);
}
+}
+
+void radius_stats_ema(fr_stats_ema_t *ema,
+ struct timeval *start, struct timeval *end)
+{
+ int micro;
+ time_t tdiff;
+#ifdef WITH_STATS_DEBUG
+ static int n = 0;
#endif
+ if (ema->window == 0) return;
+
+ rad_assert(start->tv_sec >= end->tv_sec);
+
+ /*
+ * Initialize it.
+ */
+ if (ema->f1 == 0) {
+ if (ema->window > 10000) ema->window = 10000;
+
+ ema->f1 = (2 * F_EMA_SCALE) / (ema->window + 1);
+ ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
+ }
+
+
+ tdiff = start->tv_sec;
+ tdiff -= end->tv_sec;
+
+ micro = (int) tdiff;
+ if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
+ micro *= USEC;
+ micro += start->tv_usec;
+ micro -= end->tv_usec;
+
+ micro *= EMA_SCALE;
+
+ if (ema->ema1 == 0) {
+ ema->ema1 = micro;
+ ema->ema10 = micro;
+ } else {
+ int diff;
+
+ diff = ema->f1 * (micro - ema->ema1);
+ ema->ema1 += (diff / 1000000);
+
+ diff = ema->f10 * (micro - ema->ema10);
+ ema->ema10 += (diff / 1000000);
+ }
+
+
+#ifdef WITH_STATS_DEBUG
+ DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
+ n, micro / PREC, (micro / EMA_SCALE) % USEC,
+ ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
+ ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
+ n++;
+#endif
}
#endif /* WITH_STATS */