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 #ifdef WITH_ACCOUNTING
59 (request->listener->type != RAD_LISTEN_ACCT) &&
61 (request->listener->type != RAD_LISTEN_AUTH)) return;
64 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
68 #ifdef WITH_ACCOUNTING
69 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
75 * Update the statistics.
77 * Note that we do NOT do this in a child thread.
78 * Instead, we update the stats when a request is
79 * deleted, because only the main server thread calls
80 * this function, which makes it thread-safe.
82 if (request->reply) switch (request->reply->code) {
83 case PW_AUTHENTICATION_ACK:
84 INC_AUTH(total_responses);
85 INC_AUTH(total_access_accepts);
88 case PW_AUTHENTICATION_REJECT:
89 INC_AUTH(total_responses);
90 INC_AUTH(total_access_rejects);
93 case PW_ACCESS_CHALLENGE:
94 INC_AUTH(total_responses);
95 INC_AUTH(total_access_challenges);
98 #ifdef WITH_ACCOUNTING
99 case PW_ACCOUNTING_RESPONSE:
100 INC_ACCT(total_responses);
105 * No response, it must have been a bad
109 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
110 if (request->reply->offset == -2) {
111 INC_AUTH(total_bad_authenticators);
113 INC_AUTH(total_packets_dropped);
115 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
116 if (request->reply->offset == -2) {
117 INC_ACCT(total_bad_authenticators);
119 INC_ACCT(total_packets_dropped);
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 proxy_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 { 148, offsetof(fr_stats_t, total_requests) },
281 { 149, offsetof(fr_stats_t, total_responses) },
282 { 150, offsetof(fr_stats_t, total_dup_requests) },
283 { 151, offsetof(fr_stats_t, total_malformed_requests) },
284 { 152, offsetof(fr_stats_t, total_bad_authenticators) },
285 { 153, offsetof(fr_stats_t, total_packets_dropped) },
286 { 154, offsetof(fr_stats_t, total_unknown_types) },
291 static void request_stats_addvp(REQUEST *request,
292 fr_stats2vp *table, fr_stats_t *stats)
297 for (i = 0; table[i].attribute != 0; i++) {
298 vp = radius_paircreate(request, &request->reply->vps,
299 table[i].attribute, VENDORPEC_FREERADIUS,
303 vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
308 void request_stats_reply(REQUEST *request)
310 VALUE_PAIR *flag, *vp;
313 * Statistics are available ONLY on a "status" port.
315 rad_assert(request->packet->code == PW_STATUS_SERVER);
316 rad_assert(request->listener->type == RAD_LISTEN_NONE);
318 flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS);
319 if (!flag || (flag->vp_integer == 0)) return;
324 if (((flag->vp_integer & 0x01) != 0) &&
325 ((flag->vp_integer & 0xc0) == 0)) {
326 request_stats_addvp(request, authvp, &radius_auth_stats);
329 #ifdef WITH_ACCOUNTING
333 if (((flag->vp_integer & 0x02) != 0) &&
334 ((flag->vp_integer & 0xc0) == 0)) {
335 request_stats_addvp(request, acctvp, &radius_acct_stats);
341 * Proxied authentication requests.
343 if (((flag->vp_integer & 0x04) != 0) &&
344 ((flag->vp_integer & 0x20) == 0)) {
345 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
348 #ifdef WITH_ACCOUNTING
350 * Proxied accounting requests.
352 if (((flag->vp_integer & 0x08) != 0) &&
353 ((flag->vp_integer & 0x20) == 0)) {
354 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
360 * Internal server statistics
362 if ((flag->vp_integer & 0x10) != 0) {
363 vp = radius_paircreate(request, &request->reply->vps,
364 176, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
365 if (vp) vp->vp_date = start_time.tv_sec;
366 vp = radius_paircreate(request, &request->reply->vps,
367 177, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
368 if (vp) vp->vp_date = hup_time.tv_sec;
370 #ifdef HAVE_PTHREAD_H
371 int i, array[RAD_LISTEN_MAX];
373 thread_pool_queue_stats(array);
376 for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
377 vp = radius_paircreate(request, &request->reply->vps,
378 162 + i, VENDORPEC_FREERADIUS,
382 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, 170, VENDORPEC_FREERADIUS);
403 server_port = pairfind(request->packet->vps,
404 171, VENDORPEC_FREERADIUS);
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, 167, VENDORPEC_FREERADIUS);
422 ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
423 client = client_find(cl, &ipaddr, IPPROTO_UDP);
426 client = client_find(cl, &ipaddr, IPPROTO_TCP);
431 * Else look it up by number.
433 } else if ((vp = pairfind(request->packet->vps,
434 168, VENDORPEC_FREERADIUS)) != NULL) {
435 client = client_findbynumber(cl, vp->vp_integer);
440 * If found, echo it back, along with
441 * the requested statistics.
443 pairadd(&request->reply->vps, paircopyvp(vp));
446 * When retrieving client by number, also
447 * echo back it's IP address.
449 if ((vp->type == PW_TYPE_INTEGER) &&
450 (client->ipaddr.af == AF_INET)) {
451 vp = radius_paircreate(request,
452 &request->reply->vps,
453 167, VENDORPEC_FREERADIUS,
456 vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
459 if (client->prefix != 32) {
460 vp = radius_paircreate(request,
461 &request->reply->vps,
462 169, VENDORPEC_FREERADIUS,
465 vp->vp_integer = client->prefix;
471 pairadd(&request->reply->vps,
472 paircopyvp(server_ip));
473 pairadd(&request->reply->vps,
474 paircopyvp(server_port));
478 ((flag->vp_integer & 0x01) != 0)) {
479 request_stats_addvp(request, client_authvp,
482 #ifdef WITH_ACCOUNTING
484 ((flag->vp_integer & 0x01) != 0)) {
485 request_stats_addvp(request, client_acctvp,
489 } /* else client wasn't found, don't echo it back */
493 * For a particular "listen" socket.
495 if (((flag->vp_integer & 0x40) != 0) &&
496 ((flag->vp_integer & 0x03) != 0)) {
498 VALUE_PAIR *server_ip, *server_port;
502 * See if we need to look up the server by socket
505 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
506 if (!server_ip) return;
508 server_port = pairfind(request->packet->vps,
509 171, VENDORPEC_FREERADIUS);
510 if (!server_port) return;
513 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
514 this = listener_find_byipaddr(&ipaddr,
515 server_port->vp_integer);
518 * Not found: don't do anything
522 pairadd(&request->reply->vps,
523 paircopyvp(server_ip));
524 pairadd(&request->reply->vps,
525 paircopyvp(server_port));
527 if (((flag->vp_integer & 0x01) != 0) &&
528 ((request->listener->type == RAD_LISTEN_AUTH) ||
529 (request->listener->type == RAD_LISTEN_NONE))) {
530 request_stats_addvp(request, authvp, &this->stats);
533 #ifdef WITH_ACCOUNTING
534 if (((flag->vp_integer & 0x02) != 0) &&
535 ((request->listener->type == RAD_LISTEN_ACCT) ||
536 (request->listener->type == RAD_LISTEN_NONE))) {
537 request_stats_addvp(request, acctvp, &this->stats);
546 if (((flag->vp_integer & 0x80) != 0) &&
547 ((flag->vp_integer & 0x03) != 0)) {
549 VALUE_PAIR *server_ip, *server_port;
553 * See if we need to look up the server by socket
556 server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
557 if (!server_ip) return;
559 server_port = pairfind(request->packet->vps,
560 171, VENDORPEC_FREERADIUS);
561 if (!server_port) return;
564 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
565 home = home_server_find(&ipaddr, server_port->vp_integer,
569 * Not found: don't do anything
573 pairadd(&request->reply->vps,
574 paircopyvp(server_ip));
575 pairadd(&request->reply->vps,
576 paircopyvp(server_port));
578 vp = radius_paircreate(request, &request->reply->vps,
579 172, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
580 if (vp) vp->vp_integer = home->currently_outstanding;
582 vp = radius_paircreate(request, &request->reply->vps,
583 173, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
584 if (vp) vp->vp_integer = home->state;
586 if ((home->state == HOME_STATE_ALIVE) &&
587 (home->revive_time.tv_sec != 0)) {
588 vp = radius_paircreate(request, &request->reply->vps,
589 175, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
590 if (vp) vp->vp_date = home->revive_time.tv_sec;
593 if ((home->state == HOME_STATE_ALIVE) &&
594 (home->ema.window > 0)) {
595 vp = radius_paircreate(request,
596 &request->reply->vps,
597 178, VENDORPEC_FREERADIUS,
599 if (vp) vp->vp_integer = home->ema.window;
600 vp = radius_paircreate(request,
601 &request->reply->vps,
602 179, VENDORPEC_FREERADIUS,
604 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
605 vp = radius_paircreate(request,
606 &request->reply->vps,
607 180, VENDORPEC_FREERADIUS,
609 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
613 if (home->state == HOME_STATE_IS_DEAD) {
614 vp = radius_paircreate(request, &request->reply->vps,
615 174, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
616 if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
619 if (((flag->vp_integer & 0x01) != 0) &&
620 (home->type == HOME_TYPE_AUTH)) {
621 request_stats_addvp(request, proxy_authvp,
625 #ifdef WITH_ACCOUNTING
626 if (((flag->vp_integer & 0x02) != 0) &&
627 (home->type == HOME_TYPE_ACCT)) {
628 request_stats_addvp(request, proxy_acctvp,
633 #endif /* WITH_PROXY */
636 void radius_stats_init(int flag)
639 gettimeofday(&start_time, NULL);
640 hup_time = start_time; /* it's just nicer this way */
642 gettimeofday(&hup_time, NULL);
646 void radius_stats_ema(fr_stats_ema_t *ema,
647 struct timeval *start, struct timeval *end)
651 #ifdef WITH_STATS_DEBUG
654 if (ema->window == 0) return;
656 rad_assert(start->tv_sec >= end->tv_sec);
662 if (ema->window > 10000) ema->window = 10000;
664 ema->f1 = (2 * F_EMA_SCALE) / (ema->window + 1);
665 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
669 tdiff = start->tv_sec;
670 tdiff -= end->tv_sec;
673 if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
675 micro += start->tv_usec;
676 micro -= end->tv_usec;
680 if (ema->ema1 == 0) {
686 diff = ema->f1 * (micro - ema->ema1);
687 ema->ema1 += (diff / 1000000);
689 diff = ema->f10 * (micro - ema->ema10);
690 ema->ema10 += (diff / 1000000);
694 #ifdef WITH_STATS_DEBUG
695 DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
696 n, micro / PREC, (micro / EMA_SCALE) % USEC,
697 ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
698 ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
703 #endif /* WITH_STATS */