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++;
77
request->client->auth->total_responses++;
81
case PW_AUTHENTICATION_REJECT:
82
radius_auth_stats.total_responses++;
83
radius_auth_stats.total_access_rejects++;
84
request->listener->stats.total_responses++;
85
request->listener->stats.total_access_rejects++;
86
if (request->client && request->client->auth) {
87
request->client->auth->total_access_rejects++;
88
request->client->auth->total_responses++;
92
case PW_ACCESS_CHALLENGE:
93
radius_auth_stats.total_responses++;
94
radius_auth_stats.total_access_challenges++;
95
request->listener->stats.total_responses++;
96
request->listener->stats.total_access_challenges++;
97
if (request->client && request->client->auth) {
98
request->client->auth->total_access_challenges++;
99
request->client->auth->total_responses++;
103
#ifdef WITH_ACCOUNTING
104
case PW_ACCOUNTING_RESPONSE:
105
radius_acct_stats.total_responses++;
106
request->listener->stats.total_responses++;
107
if (request->client && request->client->acct) {
108
request->client->acct->total_responses++;
114
* No response, it must have been a bad
118
if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
119
radius_auth_stats.total_bad_authenticators++;
120
request->listener->stats.total_bad_authenticators++;
121
if (request->client && request->client->auth) {
122
request->client->auth->total_bad_authenticators++;
132
if (!request->proxy || !request->proxy_listener) goto done; /* simplifies formatting */
134
switch (request->proxy->code) {
135
case PW_AUTHENTICATION_REQUEST:
136
proxy_auth_stats.total_requests += request->num_proxied_requests;
137
request->proxy_listener->stats.total_requests += request->num_proxied_requests;
138
request->home_server->stats.total_requests += request->num_proxied_requests;
141
#ifdef WITH_ACCOUNTING
142
case PW_ACCOUNTING_REQUEST:
143
proxy_acct_stats.total_requests++;
144
request->proxy_listener->stats.total_requests += request->num_proxied_requests;
145
request->home_server->stats.total_requests += request->num_proxied_requests;
153
if (!request->proxy_reply) goto done; /* simplifies formatting */
156
#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;
158
switch (request->proxy_reply->code) {
159
case PW_AUTHENTICATION_ACK:
160
INC(total_responses);
161
INC(total_access_accepts);
164
case PW_AUTHENTICATION_REJECT:
165
INC(total_responses);
166
INC(total_access_rejects);
169
case PW_ACCESS_CHALLENGE:
170
INC(total_responses);
171
INC(total_access_challenges);
174
#ifdef WITH_ACCOUNTING
175
case PW_ACCOUNTING_RESPONSE:
176
radius_acct_stats.total_responses++;
177
request->proxy_listener->stats.total_responses++;
178
request->home_server->stats.total_responses++;
183
proxy_auth_stats.total_unknown_types++;
184
request->proxy_listener->stats.total_unknown_types++;
185
request->home_server->stats.total_unknown_types++;
190
#endif /* WITH_PROXY */
192
request->master_state = REQUEST_COUNTED;
195
typedef struct fr_stats2vp {
203
static fr_stats2vp authvp[] = {
204
{ 128, offsetof(fr_stats_t, total_requests) },
205
{ 129, offsetof(fr_stats_t, total_access_accepts) },
206
{ 130, offsetof(fr_stats_t, total_access_rejects) },
207
{ 131, offsetof(fr_stats_t, total_access_challenges) },
208
{ 132, offsetof(fr_stats_t, total_responses) },
209
{ 133, offsetof(fr_stats_t, total_dup_requests) },
210
{ 134, offsetof(fr_stats_t, total_malformed_requests) },
211
{ 135, offsetof(fr_stats_t, total_bad_authenticators) },
212
{ 136, offsetof(fr_stats_t, total_packets_dropped) },
213
{ 137, offsetof(fr_stats_t, total_unknown_types) },
220
* Proxied authentication requests.
222
static fr_stats2vp proxy_authvp[] = {
223
{ 138, offsetof(fr_stats_t, total_requests) },
224
{ 139, offsetof(fr_stats_t, total_access_accepts) },
225
{ 140, offsetof(fr_stats_t, total_access_rejects) },
226
{ 141, offsetof(fr_stats_t, total_access_challenges) },
227
{ 142, offsetof(fr_stats_t, total_responses) },
228
{ 143, offsetof(fr_stats_t, total_dup_requests) },
229
{ 144, offsetof(fr_stats_t, total_malformed_requests) },
230
{ 145, offsetof(fr_stats_t, total_bad_authenticators) },
231
{ 146, offsetof(fr_stats_t, total_packets_dropped) },
232
{ 147, offsetof(fr_stats_t, total_unknown_types) },
238
#ifdef WITH_ACCOUNTING
242
static fr_stats2vp acctvp[] = {
243
{ 148, offsetof(fr_stats_t, total_requests) },
244
{ 149, offsetof(fr_stats_t, total_responses) },
245
{ 150, offsetof(fr_stats_t, total_dup_requests) },
246
{ 151, offsetof(fr_stats_t, total_malformed_requests) },
247
{ 152, offsetof(fr_stats_t, total_bad_authenticators) },
248
{ 153, offsetof(fr_stats_t, total_packets_dropped) },
249
{ 154, offsetof(fr_stats_t, total_unknown_types) },
254
static fr_stats2vp proxy_acctvp[] = {
255
{ 155, offsetof(fr_stats_t, total_requests) },
256
{ 156, offsetof(fr_stats_t, total_responses) },
257
{ 157, offsetof(fr_stats_t, total_dup_requests) },
258
{ 158, offsetof(fr_stats_t, total_malformed_requests) },
259
{ 159, offsetof(fr_stats_t, total_bad_authenticators) },
260
{ 160, offsetof(fr_stats_t, total_packets_dropped) },
261
{ 161, offsetof(fr_stats_t, total_unknown_types) },
267
static fr_stats2vp client_authvp[] = {
268
{ 128, offsetof(fr_stats_t, total_requests) },
269
{ 129, offsetof(fr_stats_t, total_access_accepts) },
270
{ 130, offsetof(fr_stats_t, total_access_rejects) },
271
{ 131, offsetof(fr_stats_t, total_access_challenges) },
272
{ 132, offsetof(fr_stats_t, total_responses) },
273
{ 133, offsetof(fr_stats_t, total_dup_requests) },
274
{ 134, offsetof(fr_stats_t, total_malformed_requests) },
275
{ 135, offsetof(fr_stats_t, total_bad_authenticators) },
276
{ 136, offsetof(fr_stats_t, total_packets_dropped) },
277
{ 137, offsetof(fr_stats_t, total_unknown_types) },
281
#ifdef WITH_ACCOUNTING
282
static fr_stats2vp client_acctvp[] = {
283
{ 148, offsetof(fr_stats_t, total_requests) },
284
{ 149, offsetof(fr_stats_t, total_responses) },
285
{ 150, offsetof(fr_stats_t, total_dup_requests) },
286
{ 151, offsetof(fr_stats_t, total_malformed_requests) },
287
{ 152, offsetof(fr_stats_t, total_bad_authenticators) },
288
{ 153, offsetof(fr_stats_t, total_packets_dropped) },
289
{ 154, offsetof(fr_stats_t, total_unknown_types) },
294
#define FR2ATTR(x) ((11344 << 16) | (x))
296
static void request_stats_addvp(REQUEST *request,
297
fr_stats2vp *table, fr_stats_t *stats)
302
for (i = 0; table[i].attribute != 0; i++) {
303
vp = radius_paircreate(request, &request->reply->vps,
304
FR2ATTR(table[i].attribute),
308
vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
313
void request_stats_reply(REQUEST *request)
315
VALUE_PAIR *flag, *vp;
318
* Statistics are available ONLY on a "status" port.
320
rad_assert(request->packet->code == PW_STATUS_SERVER);
321
rad_assert(request->listener->type == RAD_LISTEN_NONE);
323
flag = pairfind(request->packet->vps, FR2ATTR(127));
324
if (!flag || (flag->vp_integer == 0)) return;
329
if (((flag->vp_integer & 0x01) != 0) &&
330
((flag->vp_integer & 0xc0) == 0)) {
331
request_stats_addvp(request, authvp, &radius_auth_stats);
334
#ifdef WITH_ACCOUNTING
338
if (((flag->vp_integer & 0x02) != 0) &&
339
((flag->vp_integer & 0xc0) == 0)) {
340
request_stats_addvp(request, acctvp, &radius_acct_stats);
346
* Proxied authentication requests.
348
if (((flag->vp_integer & 0x04) != 0) &&
349
((flag->vp_integer & 0x20) == 0)) {
350
request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
353
#ifdef WITH_ACCOUNTING
355
* Proxied accounting requests.
357
if (((flag->vp_integer & 0x08) != 0) &&
358
((flag->vp_integer & 0x20) == 0)) {
359
request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
365
* Internal server statistics
367
if ((flag->vp_integer & 0x10) != 0) {
368
vp = radius_paircreate(request, &request->reply->vps,
369
FR2ATTR(176), PW_TYPE_DATE);
370
if (vp) vp->vp_date = start_time.tv_sec;
371
vp = radius_paircreate(request, &request->reply->vps,
372
FR2ATTR(177), PW_TYPE_DATE);
373
if (vp) vp->vp_date = hup_time.tv_sec;
375
#ifdef HAVE_PTHREAD_H
376
int i, array[RAD_LISTEN_MAX];
378
thread_pool_queue_stats(array);
380
for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
381
vp = radius_paircreate(request, &request->reply->vps,
386
vp->vp_integer = array[i];
392
* For a particular client.
394
if ((flag->vp_integer & 0x20) != 0) {
396
VALUE_PAIR *server_ip, *server_port = NULL;
397
RADCLIENT *client = NULL;
398
RADCLIENT_LIST *cl = NULL;
401
* See if we need to look up the client by server
404
server_ip = pairfind(request->packet->vps, FR2ATTR(170));
406
server_port = pairfind(request->packet->vps,
411
ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
412
cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
415
* Not found: don't do anything
422
vp = pairfind(request->packet->vps, FR2ATTR(167));
425
ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
426
client = client_find(cl, &ipaddr);
429
* Else look it up by number.
431
} else if ((vp = pairfind(request->packet->vps,
432
FR2ATTR(168))) != NULL) {
433
client = client_findbynumber(cl, vp->vp_integer);
438
* If found, echo it back, along with
439
* the requested statistics.
441
pairadd(&request->reply->vps, paircopyvp(vp));
444
* When retrieving client by number, also
445
* echo back it's IP address.
447
if ((vp->type == PW_TYPE_INTEGER) &&
448
(client->ipaddr.af == AF_INET)) {
449
vp = radius_paircreate(request,
450
&request->reply->vps,
454
vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
457
if (client->prefix != 32) {
458
vp = radius_paircreate(request,
459
&request->reply->vps,
463
vp->vp_integer = client->prefix;
469
pairadd(&request->reply->vps,
470
paircopyvp(server_ip));
471
pairadd(&request->reply->vps,
472
paircopyvp(server_port));
476
((flag->vp_integer & 0x01) != 0)) {
477
request_stats_addvp(request, client_authvp,
480
#ifdef WITH_ACCOUNTING
482
((flag->vp_integer & 0x01) != 0)) {
483
request_stats_addvp(request, client_acctvp,
487
} /* else client wasn't found, don't echo it back */
491
* For a particular "listen" socket.
493
if (((flag->vp_integer & 0x40) != 0) &&
494
((flag->vp_integer & 0x03) != 0)) {
496
VALUE_PAIR *server_ip, *server_port;
500
* See if we need to look up the server by socket
503
server_ip = pairfind(request->packet->vps, FR2ATTR(170));
504
if (!server_ip) return;
506
server_port = pairfind(request->packet->vps,
508
if (!server_port) return;
511
ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
512
this = listener_find_byipaddr(&ipaddr,
513
server_port->vp_integer);
516
* Not found: don't do anything
520
pairadd(&request->reply->vps,
521
paircopyvp(server_ip));
522
pairadd(&request->reply->vps,
523
paircopyvp(server_port));
525
if (((flag->vp_integer & 0x01) != 0) &&
526
((request->listener->type == RAD_LISTEN_AUTH) ||
527
(request->listener->type == RAD_LISTEN_NONE))) {
528
request_stats_addvp(request, authvp, &this->stats);
531
#ifdef WITH_ACCOUNTING
532
if (((flag->vp_integer & 0x02) != 0) &&
533
((request->listener->type == RAD_LISTEN_ACCT) ||
534
(request->listener->type == RAD_LISTEN_NONE))) {
535
request_stats_addvp(request, acctvp, &this->stats);
543
if (((flag->vp_integer & 0x80) != 0) &&
544
((flag->vp_integer & 0x03) != 0)) {
546
VALUE_PAIR *server_ip, *server_port;
550
* See if we need to look up the server by socket
553
server_ip = pairfind(request->packet->vps, FR2ATTR(170));
554
if (!server_ip) return;
556
server_port = pairfind(request->packet->vps,
558
if (!server_port) return;
561
ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
562
home = home_server_find(&ipaddr, server_port->vp_integer);
565
* Not found: don't do anything
569
pairadd(&request->reply->vps,
570
paircopyvp(server_ip));
571
pairadd(&request->reply->vps,
572
paircopyvp(server_port));
574
vp = radius_paircreate(request, &request->reply->vps,
575
FR2ATTR(172), PW_TYPE_INTEGER);
576
if (vp) vp->vp_integer = home->currently_outstanding;
578
vp = radius_paircreate(request, &request->reply->vps,
579
FR2ATTR(173), PW_TYPE_INTEGER);
580
if (vp) vp->vp_integer = home->state;
582
if ((home->state == HOME_STATE_ALIVE) &&
583
(home->revive_time.tv_sec != 0)) {
584
vp = radius_paircreate(request, &request->reply->vps,
585
FR2ATTR(175), PW_TYPE_DATE);
586
if (vp) vp->vp_date = home->revive_time.tv_sec;
589
if ((home->state == HOME_STATE_ALIVE) &&
590
(home->ema.window > 0)) {
591
vp = radius_paircreate(request,
592
&request->reply->vps,
595
if (vp) vp->vp_integer = home->ema.window;
596
vp = radius_paircreate(request,
597
&request->reply->vps,
600
if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
601
vp = radius_paircreate(request,
602
&request->reply->vps,
605
if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
609
if (home->state == HOME_STATE_IS_DEAD) {
610
vp = radius_paircreate(request, &request->reply->vps,
611
FR2ATTR(174), PW_TYPE_DATE);
612
if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
615
if (((flag->vp_integer & 0x01) != 0) &&
616
(home->type == HOME_TYPE_AUTH)) {
617
request_stats_addvp(request, proxy_authvp,
621
#ifdef WITH_ACCOUNTING
622
if (((flag->vp_integer & 0x02) != 0) &&
623
(home->type == HOME_TYPE_ACCT)) {
624
request_stats_addvp(request, proxy_acctvp,
631
void radius_stats_init(int flag)
634
gettimeofday(&start_time, NULL);
635
hup_time = start_time; /* it's just nicer this way */
637
gettimeofday(&hup_time, NULL);
641
void radius_stats_ema(fr_stats_ema_t *ema,
642
struct timeval *start, struct timeval *end)
646
#ifdef WITH_STATS_DEBUG
649
if (ema->window == 0) return;
651
rad_assert(start->tv_sec >= end->tv_sec);
657
if (ema->window > 10000) ema->window = 10000;
659
ema->f1 = (2 * F_EMA_SCALE) / (ema->window + 1);
660
ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
664
tdiff = start->tv_sec;
665
tdiff -= end->tv_sec;
668
if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
670
micro += start->tv_usec;
671
micro -= end->tv_usec;
675
if (ema->ema1 == 0) {
681
diff = ema->f1 * (micro - ema->ema1);
682
ema->ema1 += (diff / 1000000);
684
diff = ema->f10 * (micro - ema->ema10);
685
ema->ema10 += (diff / 1000000);
689
#ifdef WITH_STATS_DEBUG
690
DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
691
n, micro / PREC, (micro / EMA_SCALE) % USEC,
692
ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
693
ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
698
#endif /* WITH_STATS */