bugfix stats request
[freeradius.git] / src / main / stats.c
1 /*
2  * stats.c      Internal statistics handling.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2008  The FreeRADIUS server project
21  * Copyright 2008  Alan DeKok <aland@deployingradius.com>
22  */
23
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/rad_assert.h>
28
29 #ifdef WITH_STATS
30
31 #define USEC (1000000)
32 #define EMA_SCALE (100)
33 #define F_EMA_SCALE (1000000)
34
35 static struct timeval   start_time;
36 static struct timeval   hup_time;
37
38 #define FR_STATS_INIT { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,       \
39                                  { 0, 0, 0, 0, 0, 0, 0, 0 }}
40
41 fr_stats_t radius_auth_stats = FR_STATS_INIT;
42 #ifdef WITH_ACCOUNTING
43 fr_stats_t radius_acct_stats = FR_STATS_INIT;
44 #endif
45 #ifdef WITH_COA
46 fr_stats_t radius_coa_stats = FR_STATS_INIT;
47 fr_stats_t radius_dsc_stats = FR_STATS_INIT;
48 #endif
49
50 #ifdef WITH_PROXY
51 fr_stats_t proxy_auth_stats = FR_STATS_INIT;
52 #ifdef WITH_ACCOUNTING
53 fr_stats_t proxy_acct_stats = FR_STATS_INIT;
54 #endif
55 #ifdef WITH_COA
56 fr_stats_t proxy_coa_stats = FR_STATS_INIT;
57 fr_stats_t proxy_dsc_stats = FR_STATS_INIT;
58 #endif
59 #endif
60
61 static void tv_sub(struct timeval *end, struct timeval *start,
62                    struct timeval *elapsed)
63 {
64         elapsed->tv_sec = end->tv_sec - start->tv_sec;
65         if (elapsed->tv_sec > 0) {
66                 elapsed->tv_sec--;
67                 elapsed->tv_usec = USEC;
68         } else {
69                 elapsed->tv_usec = 0;
70         }
71         elapsed->tv_usec += end->tv_usec;
72         elapsed->tv_usec -= start->tv_usec;
73
74         if (elapsed->tv_usec >= USEC) {
75                 elapsed->tv_usec -= USEC;
76                 elapsed->tv_sec++;
77         }
78 }
79
80 static void stats_time(fr_stats_t *stats, struct timeval *start,
81                        struct timeval *end)
82 {
83         struct timeval diff;
84         uint32_t delay;
85
86         if ((start->tv_sec == 0) || (end->tv_sec == 0) ||
87             (end->tv_sec < start->tv_sec)) return;
88
89         tv_sub(end, start, &diff);
90
91         if (diff.tv_sec >= 10) {
92                 stats->elapsed[7]++;
93         } else {
94                 int i;
95                 uint32_t cmp;
96
97                 delay = (diff.tv_sec * USEC) + diff.tv_usec;
98
99                 cmp = 10;
100                 for (i = 0; i < 7; i++) {
101                         if (delay < cmp) {
102                                 stats->elapsed[i]++;
103                                 break;
104                         }
105                         cmp *= 10;
106                 }
107         }
108 }
109
110 void request_stats_final(REQUEST *request)
111 {
112         if (request->master_state == REQUEST_COUNTED) return;
113
114         if (!request->listener) return;
115         if (!request->client) return;
116
117         if ((request->listener->type != RAD_LISTEN_NONE) &&
118 #ifdef WITH_ACCOUNTING
119             (request->listener->type != RAD_LISTEN_ACCT) &&
120 #endif
121 #ifdef WITH_COA
122             (request->listener->type != RAD_LISTEN_COA) &&
123 #endif
124             (request->listener->type != RAD_LISTEN_AUTH)) return;
125
126         /* don't count statistic requests */
127         if (request->packet->code == PW_CODE_STATUS_SERVER)
128                 return;
129
130 #undef INC_AUTH
131 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;request->client->auth._x++;
132
133 #undef INC_ACCT
134 #ifdef WITH_ACCOUNTING
135 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;request->client->acct._x++
136 #else
137 #define INC_ACCT(_x)
138 #endif
139
140 #undef INC_COA
141 #ifdef WITH_COA
142 #define INC_COA(_x) radius_coa_stats._x++;request->listener->stats._x++;request->client->coa._x++
143 #else
144 #define INC_COA(_x)
145 #endif
146
147 #undef INC_DSC
148 #ifdef WITH_DSC
149 #define INC_DSC(_x) radius_dsc_stats._x++;request->listener->stats._x++;request->client->dsc._x++
150 #else
151 #define INC_DSC(_x)
152 #endif
153
154         /*
155          *      Update the statistics.
156          *
157          *      Note that we do NOT do this in a child thread.
158          *      Instead, we update the stats when a request is
159          *      deleted, because only the main server thread calls
160          *      this function, which makes it thread-safe.
161          */
162         if (request->reply && (request->packet->code != PW_CODE_STATUS_SERVER)) switch (request->reply->code) {
163         case PW_CODE_ACCESS_ACCEPT:
164                 INC_AUTH(total_access_accepts);
165
166                 auth_stats:
167                 INC_AUTH(total_responses);
168
169                 /*
170                  *      FIXME: Do the time calculations once...
171                  */
172                 stats_time(&radius_auth_stats,
173                            &request->packet->timestamp,
174                            &request->reply->timestamp);
175                 stats_time(&request->client->auth,
176                            &request->packet->timestamp,
177                            &request->reply->timestamp);
178                 stats_time(&request->listener->stats,
179                            &request->packet->timestamp,
180                            &request->reply->timestamp);
181                 break;
182
183         case PW_CODE_ACCESS_REJECT:
184                 INC_AUTH(total_access_rejects);
185                 goto auth_stats;
186
187         case PW_CODE_ACCESS_CHALLENGE:
188                 INC_AUTH(total_access_challenges);
189                 goto auth_stats;
190
191 #ifdef WITH_ACCOUNTING
192         case PW_CODE_ACCOUNTING_RESPONSE:
193                 INC_ACCT(total_responses);
194                 stats_time(&radius_acct_stats,
195                            &request->packet->timestamp,
196                            &request->reply->timestamp);
197                 stats_time(&request->client->acct,
198                            &request->packet->timestamp,
199                            &request->reply->timestamp);
200                 break;
201 #endif
202
203 #ifdef WITH_COA
204         case PW_CODE_COA_ACK:
205                 INC_COA(total_access_accepts);
206           coa_stats:
207                 INC_COA(total_responses);
208                 stats_time(&request->client->coa,
209                            &request->packet->timestamp,
210                            &request->reply->timestamp);
211                 break;
212
213         case PW_CODE_COA_NAK:
214                 INC_COA(total_access_rejects);
215                 goto coa_stats;
216
217         case PW_CODE_DISCONNECT_ACK:
218                 INC_DSC(total_access_accepts);
219           dsc_stats:
220                 INC_DSC(total_responses);
221                 stats_time(&request->client->dsc,
222                            &request->packet->timestamp,
223                            &request->reply->timestamp);
224                 break;
225
226         case PW_CODE_DISCONNECT_NAK:
227                 INC_DSC(total_access_rejects);
228                 goto dsc_stats;
229 #endif
230
231                 /*
232                  *      No response, it must have been a bad
233                  *      authenticator.
234                  */
235         case 0:
236                 if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
237                         if (request->reply->offset == -2) {
238                                 INC_AUTH(total_bad_authenticators);
239                         } else {
240                                 INC_AUTH(total_packets_dropped);
241                         }
242                 } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
243                         if (request->reply->offset == -2) {
244                                 INC_ACCT(total_bad_authenticators);
245                         } else {
246                                 INC_ACCT(total_packets_dropped);
247                         }
248                 }
249                 break;
250
251         default:
252                 break;
253         }
254
255 #ifdef WITH_PROXY
256         if (!request->proxy || !request->home_server) goto done;        /* simplifies formatting */
257
258         switch (request->proxy->code) {
259         case PW_CODE_ACCESS_REQUEST:
260                 proxy_auth_stats.total_requests += request->num_proxied_requests;
261                 request->home_server->stats.total_requests += request->num_proxied_requests;
262                 break;
263
264 #ifdef WITH_ACCOUNTING
265         case PW_CODE_ACCOUNTING_REQUEST:
266                 proxy_acct_stats.total_requests += request->num_proxied_requests;
267                 request->home_server->stats.total_requests += request->num_proxied_requests;
268                 break;
269 #endif
270
271 #ifdef WITH_COA
272         case PW_CODE_COA_REQUEST:
273                 proxy_coa_stats.total_requests += request->num_proxied_requests;
274                 request->home_server->stats.total_requests += request->num_proxied_requests;
275                 break;
276
277         case PW_CODE_DISCONNECT_REQUEST:
278                 proxy_dsc_stats.total_requests += request->num_proxied_requests;
279                 request->home_server->stats.total_requests += request->num_proxied_requests;
280                 break;
281 #endif
282
283         default:
284                 break;
285         }
286
287         if (!request->proxy_reply) goto done;   /* simplifies formatting */
288
289 #undef INC
290 #define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
291
292         switch (request->proxy_reply->code) {
293         case PW_CODE_ACCESS_ACCEPT:
294                 INC(total_access_accepts);
295         proxy_stats:
296                 INC(total_responses);
297                 stats_time(&proxy_auth_stats,
298                            &request->proxy->timestamp,
299                            &request->proxy_reply->timestamp);
300                 stats_time(&request->home_server->stats,
301                            &request->proxy->timestamp,
302                            &request->proxy_reply->timestamp);
303                 break;
304
305         case PW_CODE_ACCESS_REJECT:
306                 INC(total_access_rejects);
307                 goto proxy_stats;
308
309         case PW_CODE_ACCESS_CHALLENGE:
310                 INC(total_access_challenges);
311                 goto proxy_stats;
312
313 #ifdef WITH_ACCOUNTING
314         case PW_CODE_ACCOUNTING_RESPONSE:
315                 proxy_acct_stats.total_responses++;
316                 request->home_server->stats.total_responses++;
317                 stats_time(&proxy_acct_stats,
318                            &request->proxy->timestamp,
319                            &request->proxy_reply->timestamp);
320                 stats_time(&request->home_server->stats,
321                            &request->proxy->timestamp,
322                            &request->proxy_reply->timestamp);
323                 break;
324 #endif
325
326 #ifdef WITH_COA
327         case PW_CODE_COA_ACK:
328         case PW_CODE_COA_NAK:
329                 proxy_coa_stats.total_responses++;
330                 request->home_server->stats.total_responses++;
331                 stats_time(&proxy_coa_stats,
332                            &request->proxy->timestamp,
333                            &request->proxy_reply->timestamp);
334                 stats_time(&request->home_server->stats,
335                            &request->proxy->timestamp,
336                            &request->proxy_reply->timestamp);
337                 break;
338
339         case PW_CODE_DISCONNECT_ACK:
340         case PW_CODE_DISCONNECT_NAK:
341                 proxy_dsc_stats.total_responses++;
342                 request->home_server->stats.total_responses++;
343                 stats_time(&proxy_dsc_stats,
344                            &request->proxy->timestamp,
345                            &request->proxy_reply->timestamp);
346                 stats_time(&request->home_server->stats,
347                            &request->proxy->timestamp,
348                            &request->proxy_reply->timestamp);
349                 break;
350 #endif
351
352         default:
353                 proxy_auth_stats.total_unknown_types++;
354                 request->home_server->stats.total_unknown_types++;
355                 break;
356         }
357
358  done:
359 #endif /* WITH_PROXY */
360
361         request->master_state = REQUEST_COUNTED;
362 }
363
364 typedef struct fr_stats2vp {
365         int     attribute;
366         size_t  offset;
367 } fr_stats2vp;
368
369 /*
370  *      Authentication
371  */
372 static fr_stats2vp authvp[] = {
373         { PW_FREERADIUS_TOTAL_ACCESS_REQUESTS, offsetof(fr_stats_t, total_requests) },
374         { PW_FREERADIUS_TOTAL_ACCESS_ACCEPTS, offsetof(fr_stats_t, total_access_accepts) },
375         { PW_FREERADIUS_TOTAL_ACCESS_REJECTS, offsetof(fr_stats_t, total_access_rejects) },
376         { PW_FREERADIUS_TOTAL_ACCESS_CHALLENGES, offsetof(fr_stats_t, total_access_challenges) },
377         { PW_FREERADIUS_TOTAL_AUTH_RESPONSES, offsetof(fr_stats_t, total_responses) },
378         { PW_FREERADIUS_TOTAL_AUTH_DUPLICATE_REQUESTS, offsetof(fr_stats_t, total_dup_requests) },
379         { PW_FREERADIUS_TOTAL_AUTH_MALFORMED_REQUESTS, offsetof(fr_stats_t, total_malformed_requests) },
380         { PW_FREERADIUS_TOTAL_AUTH_INVALID_REQUESTS, offsetof(fr_stats_t, total_bad_authenticators) },
381         { PW_FREERADIUS_TOTAL_AUTH_DROPPED_REQUESTS, offsetof(fr_stats_t, total_packets_dropped) },
382         { PW_FREERADIUS_TOTAL_AUTH_UNKNOWN_TYPES, offsetof(fr_stats_t, total_unknown_types) },
383         { 0, 0 }
384 };
385
386
387 #ifdef WITH_PROXY
388 /*
389  *      Proxied authentication requests.
390  */
391 static fr_stats2vp proxy_authvp[] = {
392         { PW_FREERADIUS_TOTAL_PROXY_ACCESS_REQUESTS, offsetof(fr_stats_t, total_requests) },
393         { PW_FREERADIUS_TOTAL_PROXY_ACCESS_ACCEPTS, offsetof(fr_stats_t, total_access_accepts) },
394         { PW_FREERADIUS_TOTAL_PROXY_ACCESS_REJECTS, offsetof(fr_stats_t, total_access_rejects) },
395         { PW_FREERADIUS_TOTAL_PROXY_ACCESS_CHALLENGES, offsetof(fr_stats_t, total_access_challenges) },
396         { PW_FREERADIUS_TOTAL_PROXY_AUTH_RESPONSES, offsetof(fr_stats_t, total_responses) },
397         { PW_FREERADIUS_TOTAL_PROXY_AUTH_DUPLICATE_REQUESTS, offsetof(fr_stats_t, total_dup_requests) },
398         { PW_FREERADIUS_TOTAL_PROXY_AUTH_MALFORMED_REQUESTS, offsetof(fr_stats_t, total_malformed_requests) },
399         { PW_FREERADIUS_TOTAL_PROXY_AUTH_INVALID_REQUESTS, offsetof(fr_stats_t, total_bad_authenticators) },
400         { PW_FREERADIUS_TOTAL_PROXY_AUTH_DROPPED_REQUESTS, offsetof(fr_stats_t, total_packets_dropped) },
401         { PW_FREERADIUS_TOTAL_PROXY_AUTH_UNKNOWN_TYPES, offsetof(fr_stats_t, total_unknown_types) },
402         { 0, 0 }
403 };
404 #endif
405
406
407 #ifdef WITH_ACCOUNTING
408 /*
409  *      Accounting
410  */
411 static fr_stats2vp acctvp[] = {
412         { PW_FREERADIUS_TOTAL_ACCOUNTING_REQUESTS, offsetof(fr_stats_t, total_requests) },
413         { PW_FREERADIUS_TOTAL_ACCOUNTING_RESPONSES, offsetof(fr_stats_t, total_responses) },
414         { PW_FREERADIUS_TOTAL_ACCT_DUPLICATE_REQUESTS, offsetof(fr_stats_t, total_dup_requests) },
415         { PW_FREERADIUS_TOTAL_ACCT_MALFORMED_REQUESTS, offsetof(fr_stats_t, total_malformed_requests) },
416         { PW_FREERADIUS_TOTAL_ACCT_INVALID_REQUESTS, offsetof(fr_stats_t, total_bad_authenticators) },
417         { PW_FREERADIUS_TOTAL_ACCT_DROPPED_REQUESTS, offsetof(fr_stats_t, total_packets_dropped) },
418         { PW_FREERADIUS_TOTAL_ACCT_UNKNOWN_TYPES, offsetof(fr_stats_t, total_unknown_types) },
419         { 0, 0 }
420 };
421
422 #ifdef WITH_PROXY
423 static fr_stats2vp proxy_acctvp[] = {
424         { PW_FREERADIUS_TOTAL_PROXY_ACCOUNTING_REQUESTS, offsetof(fr_stats_t, total_requests) },
425         { PW_FREERADIUS_TOTAL_PROXY_ACCOUNTING_RESPONSES, offsetof(fr_stats_t, total_responses) },
426         { PW_FREERADIUS_TOTAL_PROXY_ACCT_DUPLICATE_REQUESTS, offsetof(fr_stats_t, total_dup_requests) },
427         { PW_FREERADIUS_TOTAL_PROXY_ACCT_MALFORMED_REQUESTS, offsetof(fr_stats_t, total_malformed_requests) },
428         { PW_FREERADIUS_TOTAL_PROXY_ACCT_INVALID_REQUESTS, offsetof(fr_stats_t, total_bad_authenticators) },
429         { PW_FREERADIUS_TOTAL_PROXY_ACCT_DROPPED_REQUESTS, offsetof(fr_stats_t, total_packets_dropped) },
430         { PW_FREERADIUS_TOTAL_PROXY_ACCT_UNKNOWN_TYPES, offsetof(fr_stats_t, total_unknown_types) },
431         { 0, 0 }
432 };
433 #endif
434 #endif
435
436 static fr_stats2vp client_authvp[] = {
437         { PW_FREERADIUS_TOTAL_ACCESS_REQUESTS, offsetof(fr_stats_t, total_requests) },
438         { PW_FREERADIUS_TOTAL_ACCESS_ACCEPTS, offsetof(fr_stats_t, total_access_accepts) },
439         { PW_FREERADIUS_TOTAL_ACCESS_REJECTS, offsetof(fr_stats_t, total_access_rejects) },
440         { PW_FREERADIUS_TOTAL_ACCESS_CHALLENGES, offsetof(fr_stats_t, total_access_challenges) },
441         { PW_FREERADIUS_TOTAL_AUTH_RESPONSES, offsetof(fr_stats_t, total_responses) },
442         { PW_FREERADIUS_TOTAL_AUTH_DUPLICATE_REQUESTS, offsetof(fr_stats_t, total_dup_requests) },
443         { PW_FREERADIUS_TOTAL_AUTH_MALFORMED_REQUESTS, offsetof(fr_stats_t, total_malformed_requests) },
444         { PW_FREERADIUS_TOTAL_AUTH_INVALID_REQUESTS, offsetof(fr_stats_t, total_bad_authenticators) },
445         { PW_FREERADIUS_TOTAL_AUTH_DROPPED_REQUESTS, offsetof(fr_stats_t, total_packets_dropped) },
446         { PW_FREERADIUS_TOTAL_AUTH_UNKNOWN_TYPES, offsetof(fr_stats_t, total_unknown_types) },
447         { 0, 0 }
448 };
449
450 #ifdef WITH_ACCOUNTING
451 static fr_stats2vp client_acctvp[] = {
452         { PW_FREERADIUS_TOTAL_ACCOUNTING_REQUESTS, offsetof(fr_stats_t, total_requests) },
453         { PW_FREERADIUS_TOTAL_ACCOUNTING_RESPONSES, offsetof(fr_stats_t, total_responses) },
454         { PW_FREERADIUS_TOTAL_ACCT_DUPLICATE_REQUESTS, offsetof(fr_stats_t, total_dup_requests) },
455         { PW_FREERADIUS_TOTAL_ACCT_MALFORMED_REQUESTS, offsetof(fr_stats_t, total_malformed_requests) },
456         { PW_FREERADIUS_TOTAL_ACCT_INVALID_REQUESTS, offsetof(fr_stats_t, total_bad_authenticators) },
457         { PW_FREERADIUS_TOTAL_ACCT_DROPPED_REQUESTS, offsetof(fr_stats_t, total_packets_dropped) },
458         { PW_FREERADIUS_TOTAL_ACCT_UNKNOWN_TYPES, offsetof(fr_stats_t, total_unknown_types) },
459         { 0, 0 }
460 };
461 #endif
462
463 static void request_stats_addvp(REQUEST *request,
464                                 fr_stats2vp *table, fr_stats_t *stats)
465 {
466         int i;
467         fr_uint_t counter;
468         VALUE_PAIR *vp;
469
470         for (i = 0; table[i].attribute != 0; i++) {
471                 vp = radius_pair_create(request->reply, &request->reply->vps,
472                                        table[i].attribute, VENDORPEC_FREERADIUS);
473                 if (!vp) continue;
474
475                 counter = *(fr_uint_t *) (((uint8_t *) stats) + table[i].offset);
476                 vp->vp_integer = counter;
477         }
478 }
479
480
481 void request_stats_reply(REQUEST *request)
482 {
483         VALUE_PAIR *flag, *vp;
484
485         /*
486          *      Statistics are available ONLY on a "status" port.
487          */
488         rad_assert(request->packet->code == PW_CODE_STATUS_SERVER);
489         rad_assert(request->listener->type == RAD_LISTEN_NONE);
490
491         flag = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATISTICS_TYPE, VENDORPEC_FREERADIUS, TAG_ANY);
492         if (!flag || (flag->vp_integer == 0)) return;
493
494         /*
495          *      Authentication.
496          */
497         if (((flag->vp_integer & 0x01) != 0) &&
498             ((flag->vp_integer & 0xc0) == 0)) {
499                 request_stats_addvp(request, authvp, &radius_auth_stats);
500         }
501
502 #ifdef WITH_ACCOUNTING
503         /*
504          *      Accounting
505          */
506         if (((flag->vp_integer & 0x02) != 0) &&
507             ((flag->vp_integer & 0xc0) == 0)) {
508                 request_stats_addvp(request, acctvp, &radius_acct_stats);
509         }
510 #endif
511
512 #ifdef WITH_PROXY
513         /*
514          *      Proxied authentication requests.
515          */
516         if (((flag->vp_integer & 0x04) != 0) &&
517             ((flag->vp_integer & 0x20) == 0)) {
518                 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
519         }
520
521 #ifdef WITH_ACCOUNTING
522         /*
523          *      Proxied accounting requests.
524          */
525         if (((flag->vp_integer & 0x08) != 0) &&
526             ((flag->vp_integer & 0x20) == 0)) {
527                 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
528         }
529 #endif
530 #endif
531
532         /*
533          *      Internal server statistics
534          */
535         if ((flag->vp_integer & 0x10) != 0) {
536                 vp = radius_pair_create(request->reply, &request->reply->vps,
537                                        PW_FREERADIUS_STATS_START_TIME, VENDORPEC_FREERADIUS);
538                 if (vp) vp->vp_date = start_time.tv_sec;
539                 vp = radius_pair_create(request->reply, &request->reply->vps,
540                                        PW_FREERADIUS_STATS_HUP_TIME, VENDORPEC_FREERADIUS);
541                 if (vp) vp->vp_date = hup_time.tv_sec;
542
543 #ifdef HAVE_PTHREAD_H
544                 int i, array[RAD_LISTEN_MAX], pps[2];
545
546                 thread_pool_queue_stats(array, pps);
547
548                 for (i = 0; i <= 4; i++) {
549                         vp = radius_pair_create(request->reply, &request->reply->vps,
550                                                PW_FREERADIUS_QUEUE_LEN_INTERNAL + i, VENDORPEC_FREERADIUS);
551
552                         if (!vp) continue;
553                         vp->vp_integer = array[i];
554                 }
555
556                 for (i = 0; i < 2; i++) {
557                         vp = radius_pair_create(request->reply, &request->reply->vps,
558                                                PW_FREERADIUS_QUEUE_PPS_IN + i, VENDORPEC_FREERADIUS);
559
560                         if (!vp) continue;
561                         vp->vp_integer = pps[i];
562                 }
563 #endif
564         }
565
566         /*
567          *      For a particular client.
568          */
569         if ((flag->vp_integer & 0x20) != 0) {
570                 fr_ipaddr_t ipaddr;
571                 VALUE_PAIR *server_ip, *server_port = NULL;
572                 RADCLIENT *client = NULL;
573                 RADCLIENT_LIST *cl = NULL;
574
575                 /*
576                  *      See if we need to look up the client by server
577                  *      socket.
578                  */
579                 server_ip = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_SERVER_IP_ADDRESS, VENDORPEC_FREERADIUS, TAG_ANY);
580                 if (server_ip) {
581                         server_port = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_SERVER_PORT, VENDORPEC_FREERADIUS, TAG_ANY);
582
583                         if (server_port) {
584                                 ipaddr.af = AF_INET;
585                                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
586                                 cl = listener_find_client_list(&ipaddr, server_port->vp_integer, IPPROTO_UDP);
587
588                                 /*
589                                  *      Not found: don't do anything
590                                  */
591                                 if (!cl) return;
592                         }
593                 }
594
595
596                 vp = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_CLIENT_IP_ADDRESS, VENDORPEC_FREERADIUS, TAG_ANY);
597                 if (vp) {
598                         memset(&ipaddr, 0, sizeof(ipaddr));
599                         ipaddr.af = AF_INET;
600                         ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
601                         client = client_find(cl, &ipaddr, IPPROTO_UDP);
602 #ifdef WITH_TCP
603                         if (!client) {
604                                 client = client_find(cl, &ipaddr, IPPROTO_TCP);
605                         }
606 #endif
607
608                         /*
609                          *      Else look it up by number.
610                          */
611                 } else if ((vp = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_CLIENT_NUMBER, VENDORPEC_FREERADIUS, TAG_ANY)) != NULL) {
612                         client = client_findbynumber(cl, vp->vp_integer);
613                 }
614
615                 if (client) {
616                         /*
617                          *      If found, echo it back, along with
618                          *      the requested statistics.
619                          */
620                         fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
621
622                         /*
623                          *      When retrieving client by number, also
624                          *      echo back it's IP address.
625                          */
626                         if ((vp->da->type == PW_TYPE_INTEGER) &&
627                             (client->ipaddr.af == AF_INET)) {
628                                 vp = radius_pair_create(request->reply,
629                                                        &request->reply->vps,
630                                                        PW_FREERADIUS_STATS_CLIENT_IP_ADDRESS, VENDORPEC_FREERADIUS);
631                                 if (vp) {
632                                         vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
633                                 }
634
635                                 if (client->ipaddr.prefix != 32) {
636                                         vp = radius_pair_create(request->reply,
637                                                                &request->reply->vps,
638                                                                PW_FREERADIUS_STATS_CLIENT_NETMASK, VENDORPEC_FREERADIUS);
639                                         if (vp) {
640                                                 vp->vp_integer = client->ipaddr.prefix;
641                                         }
642                                 }
643                         }
644
645                         if (server_ip) {
646                                 fr_pair_add(&request->reply->vps,
647                                         fr_pair_copy(request->reply, server_ip));
648                         }
649                         if (server_port) {
650                                 fr_pair_add(&request->reply->vps,
651                                         fr_pair_copy(request->reply, server_port));
652                         }
653
654                         if ((flag->vp_integer & 0x01) != 0) {
655                                 request_stats_addvp(request, client_authvp,
656                                                     &client->auth);
657                         }
658 #ifdef WITH_ACCOUNTING
659                         if ((flag->vp_integer & 0x02) != 0) {
660                                 request_stats_addvp(request, client_acctvp,
661                                                     &client->acct);
662                         }
663 #endif
664                 } /* else client wasn't found, don't echo it back */
665         }
666
667         /*
668          *      For a particular "listen" socket.
669          */
670         if (((flag->vp_integer & 0x40) != 0) &&
671             ((flag->vp_integer & 0x03) != 0)) {
672                 rad_listen_t *this;
673                 VALUE_PAIR *server_ip, *server_port;
674                 fr_ipaddr_t ipaddr;
675
676                 /*
677                  *      See if we need to look up the server by socket
678                  *      socket.
679                  */
680                 server_ip = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_SERVER_IP_ADDRESS, VENDORPEC_FREERADIUS, TAG_ANY);
681                 if (!server_ip) return;
682
683                 server_port = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_SERVER_PORT, VENDORPEC_FREERADIUS, TAG_ANY);
684                 if (!server_port) return;
685
686                 ipaddr.af = AF_INET;
687                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
688                 this = listener_find_byipaddr(&ipaddr,
689                                               server_port->vp_integer,
690                                               IPPROTO_UDP);
691
692                 /*
693                  *      Not found: don't do anything
694                  */
695                 if (!this) return;
696
697                 fr_pair_add(&request->reply->vps,
698                         fr_pair_copy(request->reply, server_ip));
699                 fr_pair_add(&request->reply->vps,
700                         fr_pair_copy(request->reply, server_port));
701
702                 if (((flag->vp_integer & 0x01) != 0) &&
703                     ((request->listener->type == RAD_LISTEN_AUTH) ||
704                      (request->listener->type == RAD_LISTEN_NONE))) {
705                         request_stats_addvp(request, authvp, &this->stats);
706                 }
707
708 #ifdef WITH_ACCOUNTING
709                 if (((flag->vp_integer & 0x02) != 0) &&
710                     ((request->listener->type == RAD_LISTEN_ACCT) ||
711                      (request->listener->type == RAD_LISTEN_NONE))) {
712                         request_stats_addvp(request, acctvp, &this->stats);
713                 }
714 #endif
715         }
716
717 #ifdef WITH_PROXY
718         /*
719          *      Home servers.
720          */
721         if (((flag->vp_integer & 0x80) != 0) &&
722             ((flag->vp_integer & 0x03) != 0)) {
723                 home_server_t *home;
724                 VALUE_PAIR *server_ip, *server_port;
725                 fr_ipaddr_t ipaddr;
726
727                 /*
728                  *      See if we need to look up the server by socket
729                  *      socket.
730                  */
731                 server_ip = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_SERVER_IP_ADDRESS, VENDORPEC_FREERADIUS, TAG_ANY);
732                 if (!server_ip) return;
733
734                 server_port = fr_pair_find_by_num(request->packet->vps, PW_FREERADIUS_STATS_SERVER_PORT, VENDORPEC_FREERADIUS, TAG_ANY);
735                 if (!server_port) return;
736
737 #ifndef NDEBUG
738                 memset(&ipaddr, 0, sizeof(ipaddr));
739 #endif
740                 ipaddr.af = AF_INET;
741                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
742                 home = home_server_find(&ipaddr, server_port->vp_integer,
743                                         IPPROTO_UDP);
744
745                 /*
746                  *      Not found: don't do anything
747                  */
748                 if (!home) return;
749
750                 fr_pair_add(&request->reply->vps,
751                         fr_pair_copy(request->reply, server_ip));
752                 fr_pair_add(&request->reply->vps,
753                         fr_pair_copy(request->reply, server_port));
754
755                 vp = radius_pair_create(request->reply, &request->reply->vps,
756                                        PW_FREERADIUS_STATS_SERVER_OUTSTANDING_REQUESTS, VENDORPEC_FREERADIUS);
757                 if (vp) vp->vp_integer = home->currently_outstanding;
758
759                 vp = radius_pair_create(request->reply, &request->reply->vps,
760                                        PW_FREERADIUS_STATS_SERVER_STATE, VENDORPEC_FREERADIUS);
761                 if (vp) vp->vp_integer = home->state;
762
763                 if ((home->state == HOME_STATE_ALIVE) &&
764                     (home->revive_time.tv_sec != 0)) {
765                         vp = radius_pair_create(request->reply, &request->reply->vps,
766                                                PW_FREERADIUS_STATS_SERVER_TIME_OF_LIFE, VENDORPEC_FREERADIUS);
767                         if (vp) vp->vp_date = home->revive_time.tv_sec;
768                 }
769
770                 if ((home->state == HOME_STATE_ALIVE) &&
771                     (home->ema.window > 0)) {
772                                 vp = radius_pair_create(request->reply,
773                                                        &request->reply->vps,
774                                                        PW_FREERADIUS_SERVER_EMA_WINDOW, VENDORPEC_FREERADIUS);
775                                 if (vp) vp->vp_integer = home->ema.window;
776                                 vp = radius_pair_create(request->reply,
777                                                        &request->reply->vps,
778                                                        PW_FREERADIUS_SERVER_EMA_USEC_WINDOW_1, VENDORPEC_FREERADIUS);
779                                 if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
780                                 vp = radius_pair_create(request->reply,
781                                                        &request->reply->vps,
782                                                        PW_FREERADIUS_SERVER_EMA_USEC_WINDOW_10, VENDORPEC_FREERADIUS);
783                                 if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
784
785                 }
786
787                 if (home->state == HOME_STATE_IS_DEAD) {
788                         vp = radius_pair_create(request->reply, &request->reply->vps,
789                                                PW_FREERADIUS_STATS_SERVER_TIME_OF_DEATH, VENDORPEC_FREERADIUS);
790                         if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
791                 }
792
793                 /*
794                  *      Show more information...
795                  *
796                  *      FIXME: do this for clients, too!
797                  */
798                 vp = radius_pair_create(request->reply, &request->reply->vps,
799                                        PW_FREERADIUS_STATS_LAST_PACKET_RECV, VENDORPEC_FREERADIUS);
800                 if (vp) vp->vp_date = home->last_packet_recv;
801
802                 vp = radius_pair_create(request->reply, &request->reply->vps,
803                                        PW_FREERADIUS_STATS_LAST_PACKET_SENT, VENDORPEC_FREERADIUS);
804                 if (vp) vp->vp_date = home->last_packet_sent;
805
806                 if (((flag->vp_integer & 0x01) != 0) &&
807                     (home->type == HOME_TYPE_AUTH)) {
808                         request_stats_addvp(request, proxy_authvp,
809                                             &home->stats);
810                 }
811
812 #ifdef WITH_ACCOUNTING
813                 if (((flag->vp_integer & 0x02) != 0) &&
814                     (home->type == HOME_TYPE_ACCT)) {
815                         request_stats_addvp(request, proxy_acctvp,
816                                             &home->stats);
817                 }
818 #endif
819         }
820 #endif  /* WITH_PROXY */
821 }
822
823 void radius_stats_init(int flag)
824 {
825         if (!flag) {
826                 gettimeofday(&start_time, NULL);
827                 hup_time = start_time; /* it's just nicer this way */
828         } else {
829                 gettimeofday(&hup_time, NULL);
830         }
831 }
832
833 void radius_stats_ema(fr_stats_ema_t *ema,
834                       struct timeval *start, struct timeval *end)
835 {
836         int micro;
837         time_t tdiff;
838 #ifdef WITH_STATS_DEBUG
839         static int n = 0;
840 #endif
841         if (ema->window == 0) return;
842
843         rad_assert(start->tv_sec <= end->tv_sec);
844
845         /*
846          *      Initialize it.
847          */
848         if (ema->f1 == 0) {
849                 if (ema->window > 10000) ema->window = 10000;
850
851                 ema->f1 =  (2 * F_EMA_SCALE) / (ema->window + 1);
852                 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
853         }
854
855
856         tdiff = start->tv_sec;
857         tdiff -= end->tv_sec;
858
859         micro = (int) tdiff;
860         if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
861         micro *= USEC;
862         micro += start->tv_usec;
863         micro -= end->tv_usec;
864
865         micro *= EMA_SCALE;
866
867         if (ema->ema1 == 0) {
868                 ema->ema1 = micro;
869                 ema->ema10 = micro;
870         } else {
871                 int diff;
872
873                 diff = ema->f1 * (micro - ema->ema1);
874                 ema->ema1 += (diff / 1000000);
875
876                 diff = ema->f10 * (micro - ema->ema10);
877                 ema->ema10 += (diff / 1000000);
878         }
879
880
881 #ifdef WITH_STATS_DEBUG
882         DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
883               n, micro / PREC, (micro / EMA_SCALE) % USEC,
884               ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
885               ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
886         n++;
887 #endif
888 }
889
890 #endif /* WITH_STATS */