2 * stats.c Internal statistics handling.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008 Alan DeKok <aland@deployingradius.com>
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
32 #define USEC (1000000)
33 #define EMA_SCALE (100)
34 #define PREC (USEC * EMA_SCALE)
36 #define F_EMA_SCALE (1000000)
38 static struct timeval start_time;
39 static struct timeval hup_time;
41 fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
42 #ifdef WITH_ACCOUNTING
43 fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
47 fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
48 #ifdef WITH_ACCOUNTING
49 fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
53 void request_stats_final(REQUEST *request)
55 if (request->master_state == REQUEST_COUNTED) return;
57 if ((request->listener->type != RAD_LISTEN_NONE) &&
58 (request->listener->type != RAD_LISTEN_AUTH) &&
59 (request->listener->type != RAD_LISTEN_ACCT)) return;
62 * Update the statistics.
64 * Note that we do NOT do this in a child thread.
65 * Instead, we update the stats when a request is
66 * deleted, because only the main server thread calls
67 * this function, which makes it thread-safe.
69 switch (request->reply->code) {
70 case PW_AUTHENTICATION_ACK:
71 radius_auth_stats.total_responses++;
72 radius_auth_stats.total_access_accepts++;
73 request->listener->stats.total_responses++;
74 request->listener->stats.total_access_accepts++;
75 if (request->client && request->client->auth) {
76 request->client->auth->total_access_accepts++;
80 case PW_AUTHENTICATION_REJECT:
81 radius_auth_stats.total_responses++;
82 radius_auth_stats.total_access_rejects++;
83 request->listener->stats.total_responses++;
84 request->listener->stats.total_access_rejects++;
85 if (request->client && request->client->auth) {
86 request->client->auth->total_access_rejects++;
90 case PW_ACCESS_CHALLENGE:
91 radius_auth_stats.total_responses++;
92 radius_auth_stats.total_access_challenges++;
93 request->listener->stats.total_responses++;
94 request->listener->stats.total_access_challenges++;
95 if (request->client && request->client->auth) {
96 request->client->auth->total_access_challenges++;
100 #ifdef WITH_ACCOUNTING
101 case PW_ACCOUNTING_RESPONSE:
102 radius_acct_stats.total_responses++;
103 request->listener->stats.total_responses++;
104 if (request->client && request->client->acct) {
105 request->client->acct->total_responses++;
111 * No response, it must have been a bad
115 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
116 radius_auth_stats.total_bad_authenticators++;
117 request->listener->stats.total_bad_authenticators++;
118 if (request->client && request->client->auth) {
119 request->client->auth->total_bad_authenticators++;
129 if (!request->proxy || !request->proxy_listener) goto done; /* simplifies formatting */
131 switch (request->proxy->code) {
132 case PW_AUTHENTICATION_REQUEST:
133 proxy_auth_stats.total_requests += request->num_proxied_requests;
134 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
135 request->home_server->stats.total_requests += request->num_proxied_requests;
138 #ifdef WITH_ACCOUNTING
139 case PW_ACCOUNTING_REQUEST:
140 proxy_acct_stats.total_requests++;
141 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
142 request->home_server->stats.total_requests += request->num_proxied_requests;
150 if (!request->proxy_reply) goto done; /* simplifies formatting */
153 #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;
155 switch (request->proxy_reply->code) {
156 case PW_AUTHENTICATION_ACK:
157 INC(total_responses);
158 INC(total_access_accepts);
161 case PW_AUTHENTICATION_REJECT:
162 INC(total_responses);
163 INC(total_access_rejects);
166 case PW_ACCESS_CHALLENGE:
167 INC(total_responses);
168 INC(total_access_challenges);
171 #ifdef WITH_ACCOUNTING
172 case PW_ACCOUNTING_RESPONSE:
173 radius_acct_stats.total_responses++;
174 request->proxy_listener->stats.total_responses++;
175 request->home_server->stats.total_responses++;
180 proxy_auth_stats.total_unknown_types++;
181 request->proxy_listener->stats.total_unknown_types++;
182 request->home_server->stats.total_unknown_types++;
187 #endif /* WITH_PROXY */
189 request->master_state = REQUEST_COUNTED;
192 typedef struct fr_stats2vp {
200 static fr_stats2vp authvp[] = {
201 { 128, offsetof(fr_stats_t, total_requests) },
202 { 129, offsetof(fr_stats_t, total_access_accepts) },
203 { 130, offsetof(fr_stats_t, total_access_rejects) },
204 { 131, offsetof(fr_stats_t, total_access_challenges) },
205 { 132, offsetof(fr_stats_t, total_responses) },
206 { 133, offsetof(fr_stats_t, total_dup_requests) },
207 { 134, offsetof(fr_stats_t, total_malformed_requests) },
208 { 135, offsetof(fr_stats_t, total_bad_authenticators) },
209 { 136, offsetof(fr_stats_t, total_packets_dropped) },
210 { 137, offsetof(fr_stats_t, total_unknown_types) },
217 * Proxied authentication requests.
219 static fr_stats2vp proxy_authvp[] = {
220 { 138, offsetof(fr_stats_t, total_requests) },
221 { 139, offsetof(fr_stats_t, total_access_accepts) },
222 { 140, offsetof(fr_stats_t, total_access_rejects) },
223 { 141, offsetof(fr_stats_t, total_access_challenges) },
224 { 142, offsetof(fr_stats_t, total_responses) },
225 { 143, offsetof(fr_stats_t, total_dup_requests) },
226 { 144, offsetof(fr_stats_t, total_malformed_requests) },
227 { 145, offsetof(fr_stats_t, total_bad_authenticators) },
228 { 146, offsetof(fr_stats_t, total_packets_dropped) },
229 { 147, offsetof(fr_stats_t, total_unknown_types) },
235 #ifdef WITH_ACCOUNTING
239 static fr_stats2vp acctvp[] = {
240 { 148, offsetof(fr_stats_t, total_requests) },
241 { 149, offsetof(fr_stats_t, total_responses) },
242 { 150, offsetof(fr_stats_t, total_dup_requests) },
243 { 151, offsetof(fr_stats_t, total_malformed_requests) },
244 { 152, offsetof(fr_stats_t, total_bad_authenticators) },
245 { 153, offsetof(fr_stats_t, total_packets_dropped) },
246 { 154, offsetof(fr_stats_t, total_unknown_types) },
251 static fr_stats2vp proxy_acctvp[] = {
252 { 155, offsetof(fr_stats_t, total_requests) },
253 { 156, offsetof(fr_stats_t, total_responses) },
254 { 157, offsetof(fr_stats_t, total_dup_requests) },
255 { 158, offsetof(fr_stats_t, total_malformed_requests) },
256 { 159, offsetof(fr_stats_t, total_bad_authenticators) },
257 { 160, offsetof(fr_stats_t, total_packets_dropped) },
258 { 161, offsetof(fr_stats_t, total_unknown_types) },
264 static fr_stats2vp client_authvp[] = {
265 { 128, offsetof(fr_stats_t, total_requests) },
266 { 129, offsetof(fr_stats_t, total_access_accepts) },
267 { 130, offsetof(fr_stats_t, total_access_rejects) },
268 { 131, offsetof(fr_stats_t, total_access_challenges) },
269 { 132, offsetof(fr_stats_t, total_responses) },
270 { 133, offsetof(fr_stats_t, total_dup_requests) },
271 { 134, offsetof(fr_stats_t, total_malformed_requests) },
272 { 135, offsetof(fr_stats_t, total_bad_authenticators) },
273 { 136, offsetof(fr_stats_t, total_packets_dropped) },
274 { 137, offsetof(fr_stats_t, total_unknown_types) },
278 #ifdef WITH_ACCOUNTING
279 static fr_stats2vp client_acctvp[] = {
280 { 155, offsetof(fr_stats_t, total_requests) },
281 { 156, offsetof(fr_stats_t, total_responses) },
282 { 157, offsetof(fr_stats_t, total_dup_requests) },
283 { 158, offsetof(fr_stats_t, total_malformed_requests) },
284 { 159, offsetof(fr_stats_t, total_bad_authenticators) },
285 { 160, offsetof(fr_stats_t, total_packets_dropped) },
286 { 161, offsetof(fr_stats_t, total_unknown_types) },
291 #define FR2ATTR(x) ((11344 << 16) | (x))
293 static void request_stats_addvp(REQUEST *request,
294 fr_stats2vp *table, fr_stats_t *stats)
299 for (i = 0; table[i].attribute != 0; i++) {
300 vp = radius_paircreate(request, &request->reply->vps,
301 FR2ATTR(table[i].attribute),
305 vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
310 void request_stats_reply(REQUEST *request)
312 VALUE_PAIR *flag, *vp;
315 * Statistics are available ONLY on a "status" port.
317 rad_assert(request->packet->code == PW_STATUS_SERVER);
318 rad_assert(request->listener->type == RAD_LISTEN_NONE);
320 flag = pairfind(request->packet->vps, FR2ATTR(127));
321 if (!flag || (flag->vp_integer == 0)) return;
326 if (((flag->vp_integer & 0x01) != 0) &&
327 ((flag->vp_integer & 0xc0) == 0)) {
328 request_stats_addvp(request, authvp, &radius_auth_stats);
331 #ifdef WITH_ACCOUNTING
335 if (((flag->vp_integer & 0x02) != 0) &&
336 ((flag->vp_integer & 0xc0) == 0)) {
337 request_stats_addvp(request, acctvp, &radius_acct_stats);
343 * Proxied authentication requests.
345 if (((flag->vp_integer & 0x04) != 0) &&
346 ((flag->vp_integer & 0x20) == 0)) {
347 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
350 #ifdef WITH_ACCOUNTING
352 * Proxied accounting requests.
354 if (((flag->vp_integer & 0x08) != 0) &&
355 ((flag->vp_integer & 0x20) == 0)) {
356 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
362 * Internal server statistics
364 if ((flag->vp_integer & 0x10) != 0) {
365 vp = radius_paircreate(request, &request->reply->vps,
366 FR2ATTR(176), PW_TYPE_DATE);
367 if (vp) vp->vp_date = start_time.tv_sec;
368 vp = radius_paircreate(request, &request->reply->vps,
369 FR2ATTR(177), PW_TYPE_DATE);
370 if (vp) vp->vp_date = hup_time.tv_sec;
372 #ifdef HAVE_PTHREAD_H
373 int i, array[RAD_LISTEN_MAX];
375 thread_pool_queue_stats(array);
377 for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
378 vp = radius_paircreate(request, &request->reply->vps,
383 vp->vp_integer = array[i];
389 * For a particular client.
391 if ((flag->vp_integer & 0x20) != 0) {
393 VALUE_PAIR *server_ip, *server_port = NULL;
394 RADCLIENT *client = NULL;
395 RADCLIENT_LIST *cl = NULL;
398 * See if we need to look up the client by server
401 server_ip = pairfind(request->packet->vps, FR2ATTR(170));
403 server_port = pairfind(request->packet->vps,
408 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
409 cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
412 * Not found: don't do anything
419 vp = pairfind(request->packet->vps, FR2ATTR(167));
422 ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
423 client = client_find(cl, &ipaddr);
426 * Else look it up by number.
428 } else if ((vp = pairfind(request->packet->vps,
429 FR2ATTR(168))) != NULL) {
430 client = client_findbynumber(cl, vp->vp_integer);
435 * If found, echo it back, along with
436 * the requested statistics.
438 pairadd(&request->reply->vps, paircopyvp(vp));
441 * When retrieving client by number, also
442 * echo back it's IP address.
444 if ((vp->type == PW_TYPE_INTEGER) &&
445 (client->ipaddr.af == AF_INET)) {
446 vp = radius_paircreate(request,
447 &request->reply->vps,
451 vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
454 if (client->prefix != 32) {
455 vp = radius_paircreate(request,
456 &request->reply->vps,
460 vp->vp_integer = client->prefix;
466 pairadd(&request->reply->vps,
467 paircopyvp(server_ip));
468 pairadd(&request->reply->vps,
469 paircopyvp(server_port));
473 ((flag->vp_integer & 0x01) != 0)) {
474 request_stats_addvp(request, client_authvp,
477 #ifdef WITH_ACCOUNTING
479 ((flag->vp_integer & 0x01) != 0)) {
480 request_stats_addvp(request, client_acctvp,
484 } /* else client wasn't found, don't echo it back */
488 * For a particular "listen" socket.
490 if (((flag->vp_integer & 0x40) != 0) &&
491 ((flag->vp_integer & 0x03) != 0)) {
493 VALUE_PAIR *server_ip, *server_port;
497 * See if we need to look up the server by socket
500 server_ip = pairfind(request->packet->vps, FR2ATTR(170));
501 if (!server_ip) return;
503 server_port = pairfind(request->packet->vps,
505 if (!server_port) return;
508 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
509 this = listener_find_byipaddr(&ipaddr,
510 server_port->vp_integer);
513 * Not found: don't do anything
517 pairadd(&request->reply->vps,
518 paircopyvp(server_ip));
519 pairadd(&request->reply->vps,
520 paircopyvp(server_port));
522 if (((flag->vp_integer & 0x01) != 0) &&
523 ((request->listener->type == RAD_LISTEN_AUTH) ||
524 (request->listener->type == RAD_LISTEN_NONE))) {
525 request_stats_addvp(request, authvp, &this->stats);
528 #ifdef WITH_ACCOUNTING
529 if (((flag->vp_integer & 0x02) != 0) &&
530 ((request->listener->type == RAD_LISTEN_ACCT) ||
531 (request->listener->type == RAD_LISTEN_NONE))) {
532 request_stats_addvp(request, acctvp, &this->stats);
540 if (((flag->vp_integer & 0x80) != 0) &&
541 ((flag->vp_integer & 0x03) != 0)) {
543 VALUE_PAIR *server_ip, *server_port;
547 * See if we need to look up the server by socket
550 server_ip = pairfind(request->packet->vps, FR2ATTR(170));
551 if (!server_ip) return;
553 server_port = pairfind(request->packet->vps,
555 if (!server_port) return;
558 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
559 home = home_server_find(&ipaddr, server_port->vp_integer);
562 * Not found: don't do anything
566 pairadd(&request->reply->vps,
567 paircopyvp(server_ip));
568 pairadd(&request->reply->vps,
569 paircopyvp(server_port));
571 vp = radius_paircreate(request, &request->reply->vps,
572 FR2ATTR(172), PW_TYPE_INTEGER);
573 if (vp) vp->vp_integer = home->currently_outstanding;
575 vp = radius_paircreate(request, &request->reply->vps,
576 FR2ATTR(173), PW_TYPE_INTEGER);
577 if (vp) vp->vp_integer = home->state;
579 if ((home->state == HOME_STATE_ALIVE) &&
580 (home->revive_time.tv_sec != 0)) {
581 vp = radius_paircreate(request, &request->reply->vps,
582 FR2ATTR(175), PW_TYPE_DATE);
583 if (vp) vp->vp_date = home->revive_time.tv_sec;
586 if ((home->state == HOME_STATE_ALIVE) &&
587 (home->ema.window > 0)) {
588 vp = radius_paircreate(request,
589 &request->reply->vps,
592 if (vp) vp->vp_integer = home->ema.window;
593 vp = radius_paircreate(request,
594 &request->reply->vps,
597 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
598 vp = radius_paircreate(request,
599 &request->reply->vps,
602 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
606 if (home->state == HOME_STATE_IS_DEAD) {
607 vp = radius_paircreate(request, &request->reply->vps,
608 FR2ATTR(174), PW_TYPE_DATE);
609 if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
612 if (((flag->vp_integer & 0x01) != 0) &&
613 (home->type == HOME_TYPE_AUTH)) {
614 request_stats_addvp(request, proxy_authvp,
618 #ifdef WITH_ACCOUNTING
619 if (((flag->vp_integer & 0x02) != 0) &&
620 (home->type == HOME_TYPE_ACCT)) {
621 request_stats_addvp(request, proxy_acctvp,
628 void radius_stats_init(int flag)
631 gettimeofday(&start_time, NULL);
632 hup_time = start_time; /* it's just nicer this way */
634 gettimeofday(&hup_time, NULL);
638 void radius_stats_ema(fr_stats_ema_t *ema,
639 struct timeval *start, struct timeval *end)
643 #ifdef WITH_STATS_DEBUG
646 if (ema->window == 0) return;
648 rad_assert(start->tv_sec >= end->tv_sec);
654 if (ema->window > 10000) ema->window = 10000;
656 ema->f1 = (2 * F_EMA_SCALE) / (ema->window + 1);
657 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
661 tdiff = start->tv_sec;
662 tdiff -= end->tv_sec;
665 if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
667 micro += start->tv_usec;
668 micro -= end->tv_usec;
672 if (ema->ema1 == 0) {
678 diff = ema->f1 * (micro - ema->ema1);
679 ema->ema1 += (diff / 1000000);
681 diff = ema->f10 * (micro - ema->ema10);
682 ema->ema10 += (diff / 1000000);
686 #ifdef WITH_STATS_DEBUG
687 DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
688 n, micro / PREC, (micro / EMA_SCALE) % USEC,
689 ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
690 ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
695 #endif /* WITH_STATS */