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 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
66 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
69 * Update the statistics.
71 * Note that we do NOT do this in a child thread.
72 * Instead, we update the stats when a request is
73 * deleted, because only the main server thread calls
74 * this function, which makes it thread-safe.
76 if (request->reply) switch (request->reply->code) {
77 case PW_AUTHENTICATION_ACK:
78 INC_AUTH(total_responses);
79 INC_AUTH(total_access_accepts);
82 case PW_AUTHENTICATION_REJECT:
83 INC_AUTH(total_responses);
84 INC_AUTH(total_access_rejects);
87 case PW_ACCESS_CHALLENGE:
88 INC_AUTH(total_responses);
89 INC_AUTH(total_access_challenges);
92 #ifdef WITH_ACCOUNTING
93 case PW_ACCOUNTING_RESPONSE:
94 INC_ACCT(total_responses);
99 * No response, it must have been a bad
103 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
104 if (request->reply->offset == -2) {
105 INC_AUTH(total_bad_authenticators);
107 INC_AUTH(total_packets_dropped);
109 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
110 if (request->reply->offset == -2) {
111 INC_ACCT(total_bad_authenticators);
113 INC_ACCT(total_packets_dropped);
123 if (!request->proxy || !request->proxy_listener) goto done; /* simplifies formatting */
125 switch (request->proxy->code) {
126 case PW_AUTHENTICATION_REQUEST:
127 proxy_auth_stats.total_requests += request->num_proxied_requests;
128 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
129 request->home_server->stats.total_requests += request->num_proxied_requests;
132 #ifdef WITH_ACCOUNTING
133 case PW_ACCOUNTING_REQUEST:
134 proxy_acct_stats.total_requests++;
135 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
136 request->home_server->stats.total_requests += request->num_proxied_requests;
144 if (!request->proxy_reply) goto done; /* simplifies formatting */
147 #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;
149 switch (request->proxy_reply->code) {
150 case PW_AUTHENTICATION_ACK:
151 INC(total_responses);
152 INC(total_access_accepts);
155 case PW_AUTHENTICATION_REJECT:
156 INC(total_responses);
157 INC(total_access_rejects);
160 case PW_ACCESS_CHALLENGE:
161 INC(total_responses);
162 INC(total_access_challenges);
165 #ifdef WITH_ACCOUNTING
166 case PW_ACCOUNTING_RESPONSE:
167 proxy_acct_stats.total_responses++;
168 request->proxy_listener->stats.total_responses++;
169 request->home_server->stats.total_responses++;
174 proxy_auth_stats.total_unknown_types++;
175 request->proxy_listener->stats.total_unknown_types++;
176 request->home_server->stats.total_unknown_types++;
181 #endif /* WITH_PROXY */
183 request->master_state = REQUEST_COUNTED;
186 typedef struct fr_stats2vp {
194 static fr_stats2vp authvp[] = {
195 { 128, offsetof(fr_stats_t, total_requests) },
196 { 129, offsetof(fr_stats_t, total_access_accepts) },
197 { 130, offsetof(fr_stats_t, total_access_rejects) },
198 { 131, offsetof(fr_stats_t, total_access_challenges) },
199 { 132, offsetof(fr_stats_t, total_responses) },
200 { 133, offsetof(fr_stats_t, total_dup_requests) },
201 { 134, offsetof(fr_stats_t, total_malformed_requests) },
202 { 135, offsetof(fr_stats_t, total_bad_authenticators) },
203 { 136, offsetof(fr_stats_t, total_packets_dropped) },
204 { 137, offsetof(fr_stats_t, total_unknown_types) },
211 * Proxied authentication requests.
213 static fr_stats2vp proxy_authvp[] = {
214 { 138, offsetof(fr_stats_t, total_requests) },
215 { 139, offsetof(fr_stats_t, total_access_accepts) },
216 { 140, offsetof(fr_stats_t, total_access_rejects) },
217 { 141, offsetof(fr_stats_t, total_access_challenges) },
218 { 142, offsetof(fr_stats_t, total_responses) },
219 { 143, offsetof(fr_stats_t, total_dup_requests) },
220 { 144, offsetof(fr_stats_t, total_malformed_requests) },
221 { 145, offsetof(fr_stats_t, total_bad_authenticators) },
222 { 146, offsetof(fr_stats_t, total_packets_dropped) },
223 { 147, offsetof(fr_stats_t, total_unknown_types) },
229 #ifdef WITH_ACCOUNTING
233 static fr_stats2vp acctvp[] = {
234 { 148, offsetof(fr_stats_t, total_requests) },
235 { 149, offsetof(fr_stats_t, total_responses) },
236 { 150, offsetof(fr_stats_t, total_dup_requests) },
237 { 151, offsetof(fr_stats_t, total_malformed_requests) },
238 { 152, offsetof(fr_stats_t, total_bad_authenticators) },
239 { 153, offsetof(fr_stats_t, total_packets_dropped) },
240 { 154, offsetof(fr_stats_t, total_unknown_types) },
245 static fr_stats2vp proxy_acctvp[] = {
246 { 155, offsetof(fr_stats_t, total_requests) },
247 { 156, offsetof(fr_stats_t, total_responses) },
248 { 157, offsetof(fr_stats_t, total_dup_requests) },
249 { 158, offsetof(fr_stats_t, total_malformed_requests) },
250 { 159, offsetof(fr_stats_t, total_bad_authenticators) },
251 { 160, offsetof(fr_stats_t, total_packets_dropped) },
252 { 161, offsetof(fr_stats_t, total_unknown_types) },
258 static fr_stats2vp client_authvp[] = {
259 { 128, offsetof(fr_stats_t, total_requests) },
260 { 129, offsetof(fr_stats_t, total_access_accepts) },
261 { 130, offsetof(fr_stats_t, total_access_rejects) },
262 { 131, offsetof(fr_stats_t, total_access_challenges) },
263 { 132, offsetof(fr_stats_t, total_responses) },
264 { 133, offsetof(fr_stats_t, total_dup_requests) },
265 { 134, offsetof(fr_stats_t, total_malformed_requests) },
266 { 135, offsetof(fr_stats_t, total_bad_authenticators) },
267 { 136, offsetof(fr_stats_t, total_packets_dropped) },
268 { 137, offsetof(fr_stats_t, total_unknown_types) },
272 #ifdef WITH_ACCOUNTING
273 static fr_stats2vp client_acctvp[] = {
274 { 148, offsetof(fr_stats_t, total_requests) },
275 { 149, offsetof(fr_stats_t, total_responses) },
276 { 150, offsetof(fr_stats_t, total_dup_requests) },
277 { 151, offsetof(fr_stats_t, total_malformed_requests) },
278 { 152, offsetof(fr_stats_t, total_bad_authenticators) },
279 { 153, offsetof(fr_stats_t, total_packets_dropped) },
280 { 154, offsetof(fr_stats_t, total_unknown_types) },
285 static void request_stats_addvp(REQUEST *request,
286 fr_stats2vp *table, fr_stats_t *stats)
291 for (i = 0; table[i].attribute != 0; i++) {
292 vp = radius_paircreate(request, &request->reply->vps,
293 table[i].attribute, VENDORPEC_FREERADIUS,
297 vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
302 void request_stats_reply(REQUEST *request)
304 VALUE_PAIR *flag, *vp;
307 * Statistics are available ONLY on a "status" port.
309 rad_assert(request->packet->code == PW_STATUS_SERVER);
310 rad_assert(request->listener->type == RAD_LISTEN_NONE);
312 flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS);
313 if (!flag || (flag->vp_integer == 0)) return;
318 if (((flag->vp_integer & 0x01) != 0) &&
319 ((flag->vp_integer & 0xc0) == 0)) {
320 request_stats_addvp(request, authvp, &radius_auth_stats);
323 #ifdef WITH_ACCOUNTING
327 if (((flag->vp_integer & 0x02) != 0) &&
328 ((flag->vp_integer & 0xc0) == 0)) {
329 request_stats_addvp(request, acctvp, &radius_acct_stats);
335 * Proxied authentication requests.
337 if (((flag->vp_integer & 0x04) != 0) &&
338 ((flag->vp_integer & 0x20) == 0)) {
339 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
342 #ifdef WITH_ACCOUNTING
344 * Proxied accounting requests.
346 if (((flag->vp_integer & 0x08) != 0) &&
347 ((flag->vp_integer & 0x20) == 0)) {
348 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
354 * Internal server statistics
356 if ((flag->vp_integer & 0x10) != 0) {
357 vp = radius_paircreate(request, &request->reply->vps,
358 176, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
359 if (vp) vp->vp_date = start_time.tv_sec;
360 vp = radius_paircreate(request, &request->reply->vps,
361 177, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
362 if (vp) vp->vp_date = hup_time.tv_sec;
364 #ifdef HAVE_PTHREAD_H
365 int i, array[RAD_LISTEN_MAX];
367 thread_pool_queue_stats(array);
369 for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
370 vp = radius_paircreate(request, &request->reply->vps,
371 162 + i, VENDORPEC_FREERADIUS,
375 vp->vp_integer = array[i];
381 * For a particular client.
383 if ((flag->vp_integer & 0x20) != 0) {
385 VALUE_PAIR *server_ip, *server_port = NULL;
386 RADCLIENT *client = NULL;
387 RADCLIENT_LIST *cl = NULL;
390 * See if we need to look up the client by server
393 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
395 server_port = pairfind(request->packet->vps,
396 171, VENDORPEC_FREERADIUS);
400 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
401 cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
404 * Not found: don't do anything
411 vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS);
414 ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
415 client = client_find(cl, &ipaddr, IPPROTO_UDP);
418 client = client_find(cl, &ipaddr, IPPROTO_TCP);
423 * Else look it up by number.
425 } else if ((vp = pairfind(request->packet->vps,
426 168, VENDORPEC_FREERADIUS)) != NULL) {
427 client = client_findbynumber(cl, vp->vp_integer);
432 * If found, echo it back, along with
433 * the requested statistics.
435 pairadd(&request->reply->vps, paircopyvp(vp));
438 * When retrieving client by number, also
439 * echo back it's IP address.
441 if ((vp->type == PW_TYPE_INTEGER) &&
442 (client->ipaddr.af == AF_INET)) {
443 vp = radius_paircreate(request,
444 &request->reply->vps,
445 167, VENDORPEC_FREERADIUS,
448 vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
451 if (client->prefix != 32) {
452 vp = radius_paircreate(request,
453 &request->reply->vps,
454 169, VENDORPEC_FREERADIUS,
457 vp->vp_integer = client->prefix;
463 pairadd(&request->reply->vps,
464 paircopyvp(server_ip));
465 pairadd(&request->reply->vps,
466 paircopyvp(server_port));
470 ((flag->vp_integer & 0x01) != 0)) {
471 request_stats_addvp(request, client_authvp,
474 #ifdef WITH_ACCOUNTING
476 ((flag->vp_integer & 0x01) != 0)) {
477 request_stats_addvp(request, client_acctvp,
481 } /* else client wasn't found, don't echo it back */
485 * For a particular "listen" socket.
487 if (((flag->vp_integer & 0x40) != 0) &&
488 ((flag->vp_integer & 0x03) != 0)) {
490 VALUE_PAIR *server_ip, *server_port;
494 * See if we need to look up the server by socket
497 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
498 if (!server_ip) return;
500 server_port = pairfind(request->packet->vps,
501 171, VENDORPEC_FREERADIUS);
502 if (!server_port) return;
505 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
506 this = listener_find_byipaddr(&ipaddr,
507 server_port->vp_integer);
510 * Not found: don't do anything
514 pairadd(&request->reply->vps,
515 paircopyvp(server_ip));
516 pairadd(&request->reply->vps,
517 paircopyvp(server_port));
519 if (((flag->vp_integer & 0x01) != 0) &&
520 ((request->listener->type == RAD_LISTEN_AUTH) ||
521 (request->listener->type == RAD_LISTEN_NONE))) {
522 request_stats_addvp(request, authvp, &this->stats);
525 #ifdef WITH_ACCOUNTING
526 if (((flag->vp_integer & 0x02) != 0) &&
527 ((request->listener->type == RAD_LISTEN_ACCT) ||
528 (request->listener->type == RAD_LISTEN_NONE))) {
529 request_stats_addvp(request, acctvp, &this->stats);
538 if (((flag->vp_integer & 0x80) != 0) &&
539 ((flag->vp_integer & 0x03) != 0)) {
541 VALUE_PAIR *server_ip, *server_port;
545 * See if we need to look up the server by socket
548 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
549 if (!server_ip) return;
551 server_port = pairfind(request->packet->vps,
552 171, VENDORPEC_FREERADIUS);
553 if (!server_port) return;
556 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
557 home = home_server_find(&ipaddr, server_port->vp_integer,
561 * Not found: don't do anything
565 pairadd(&request->reply->vps,
566 paircopyvp(server_ip));
567 pairadd(&request->reply->vps,
568 paircopyvp(server_port));
570 vp = radius_paircreate(request, &request->reply->vps,
571 172, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
572 if (vp) vp->vp_integer = home->currently_outstanding;
574 vp = radius_paircreate(request, &request->reply->vps,
575 173, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
576 if (vp) vp->vp_integer = home->state;
578 if ((home->state == HOME_STATE_ALIVE) &&
579 (home->revive_time.tv_sec != 0)) {
580 vp = radius_paircreate(request, &request->reply->vps,
581 175, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
582 if (vp) vp->vp_date = home->revive_time.tv_sec;
585 if ((home->state == HOME_STATE_ALIVE) &&
586 (home->ema.window > 0)) {
587 vp = radius_paircreate(request,
588 &request->reply->vps,
589 178, VENDORPEC_FREERADIUS,
591 if (vp) vp->vp_integer = home->ema.window;
592 vp = radius_paircreate(request,
593 &request->reply->vps,
594 179, VENDORPEC_FREERADIUS,
596 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
597 vp = radius_paircreate(request,
598 &request->reply->vps,
599 180, VENDORPEC_FREERADIUS,
601 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
605 if (home->state == HOME_STATE_IS_DEAD) {
606 vp = radius_paircreate(request, &request->reply->vps,
607 174, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
608 if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
611 if (((flag->vp_integer & 0x01) != 0) &&
612 (home->type == HOME_TYPE_AUTH)) {
613 request_stats_addvp(request, proxy_authvp,
617 #ifdef WITH_ACCOUNTING
618 if (((flag->vp_integer & 0x02) != 0) &&
619 (home->type == HOME_TYPE_ACCT)) {
620 request_stats_addvp(request, proxy_acctvp,
625 #endif /* WITH_PROXY */
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 */