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