Add start && hup time
[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 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29
30 #ifdef WITH_STATS
31
32 static struct timeval   start_time;
33 static struct timeval   hup_time;
34
35 fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
36 #ifdef WITH_ACCOUNTING
37 fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
38 #endif
39
40 #ifdef WITH_PROXY
41 fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
42 #ifdef WITH_ACCOUNTING
43 fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
44 #endif
45 #endif
46
47 void request_stats_final(REQUEST *request)
48 {
49         if (request->master_state == REQUEST_COUNTED) return;
50
51         if ((request->listener->type != RAD_LISTEN_NONE) &&
52             (request->listener->type != RAD_LISTEN_AUTH) &&
53             (request->listener->type != RAD_LISTEN_ACCT)) return;
54
55         /*
56          *      Update the statistics.
57          *
58          *      Note that we do NOT do this in a child thread.
59          *      Instead, we update the stats when a request is
60          *      deleted, because only the main server thread calls
61          *      this function, which makes it thread-safe.
62          */
63         switch (request->reply->code) {
64         case PW_AUTHENTICATION_ACK:
65                 radius_auth_stats.total_responses++;
66                 radius_auth_stats.total_access_accepts++;
67                 request->listener->stats.total_responses++;
68                 request->listener->stats.total_access_accepts++;
69                 if (request->client && request->client->auth) {
70                         request->client->auth->total_access_accepts++;
71                 }
72                 break;
73
74         case PW_AUTHENTICATION_REJECT:
75                 radius_auth_stats.total_responses++;
76                 radius_auth_stats.total_access_rejects++;
77                 request->listener->stats.total_responses++;
78                 request->listener->stats.total_access_rejects++;
79                 if (request->client && request->client->auth) {
80                         request->client->auth->total_access_rejects++;
81                 }
82                 break;
83
84         case PW_ACCESS_CHALLENGE:
85                 radius_auth_stats.total_responses++;
86                 radius_auth_stats.total_access_challenges++;
87                 request->listener->stats.total_responses++;
88                 request->listener->stats.total_access_challenges++;
89                 if (request->client && request->client->auth) {
90                         request->client->auth->total_access_challenges++;
91                 }
92                 break;
93
94 #ifdef WITH_ACCOUNTING
95         case PW_ACCOUNTING_RESPONSE:
96                 radius_acct_stats.total_responses++;
97                 request->listener->stats.total_responses++;
98                 if (request->client && request->client->acct) {
99                         request->client->acct->total_responses++;
100                 }
101                 break;
102 #endif
103
104                 /*
105                  *      No response, it must have been a bad
106                  *      authenticator.
107                  */
108         case 0:
109                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
110                         radius_auth_stats.total_bad_authenticators++;
111                         request->listener->stats.total_bad_authenticators++;
112                         if (request->client && request->client->auth) {
113                                 request->client->auth->total_bad_authenticators++;
114                         }
115                 }
116                 break;
117
118         default:
119                 break;
120         }
121
122 #ifdef WITH_PROXY
123         if (!request->proxy) goto done; /* simplifies formatting */
124                 
125         switch (request->proxy_reply->code) {
126         case PW_AUTHENTICATION_REQUEST:
127                 proxy_auth_stats.total_requests += request->num_proxied_requests;
128                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
129                 request->home_server->stats.total_requests += request->num_proxied_requests;
130                 break;
131
132 #ifdef WITH_ACCOUNTING
133         case PW_ACCOUNTING_REQUEST:
134                 proxy_acct_stats.total_requests++;
135                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
136                 request->home_server->stats.total_requests += request->num_proxied_requests;
137                 break;
138 #endif
139
140         default:
141                 break;
142         }
143
144         if (!request->proxy_reply) goto done;   /* simplifies formatting */
145
146         switch (request->proxy_reply->code) {
147         case PW_AUTHENTICATION_ACK:
148                 proxy_auth_stats.total_responses += request->num_proxied_responses;
149                 proxy_auth_stats.total_access_accepts += request->num_proxied_responses;
150                 request->proxy_listener->stats.total_responses += request->num_proxied_responses;
151                 request->home_server->stats.total_access_accepts += request->num_proxied_responses;
152                 request->home_server->stats.total_responses += request->num_proxied_responses;
153                 request->home_server->stats.total_access_accepts += request->num_proxied_responses;
154                 break;
155
156         case PW_AUTHENTICATION_REJECT:
157                 proxy_auth_stats.total_responses += request->num_proxied_responses;
158                 proxy_auth_stats.total_access_rejects += request->num_proxied_responses;
159                 request->proxy_listener->stats.total_responses += request->num_proxied_responses;
160                 request->proxy_listener->stats.total_access_rejects += request->num_proxied_responses;
161                 request->home_server->stats.total_responses += request->num_proxied_responses;
162                 request->home_server->stats.total_access_rejects += request->num_proxied_responses;
163                 break;
164
165         case PW_ACCESS_CHALLENGE:
166                 proxy_auth_stats.total_responses += request->num_proxied_responses;
167                 proxy_auth_stats.total_access_challenges += request->num_proxied_responses;
168                 request->proxy_listener->stats.total_responses += request->num_proxied_responses;
169                 request->proxy_listener->stats.total_access_challenges += request->num_proxied_responses;
170                 request->home_server->stats.total_responses += request->num_proxied_responses;
171                 request->home_server->stats.total_access_challenges += request->num_proxied_responses;
172                 break;
173
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++;
179                 break;
180 #endif
181
182         default:
183                 proxy_auth_stats.total_unknown_types++;
184                 request->proxy_listener->stats.total_unknown_types++;
185                 request->home_server->stats.total_unknown_types++;
186                 break;
187         }
188
189  done:
190 #endif /* WITH_PROXY */
191
192         request->master_state = REQUEST_COUNTED;
193 }
194
195 typedef struct fr_stats2vp {
196         int     attribute;
197         size_t  offset;
198 } fr_stats2vp;
199
200 /*
201  *      Authentication
202  */
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) },
214         { 0, 0 }
215 };
216
217
218 #ifdef WITH_PROXY
219 /*
220  *      Proxied authentication requests.
221  */
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) },
233         { 0, 0 }
234 };
235 #endif
236
237
238 #ifdef WITH_ACCOUNTING
239 /*
240  *      Accounting
241  */
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) },
250         { 0, 0 }
251 };
252
253 #ifdef WITH_PROXY
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) },
262         { 0, 0 }
263 };
264 #endif
265 #endif
266
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) },
278         { 0, 0 }
279 };
280
281 #ifdef WITH_ACCOUNTING
282 static fr_stats2vp client_acctvp[] = {
283         { 155, offsetof(fr_stats_t, total_requests) },
284         { 156, offsetof(fr_stats_t, total_responses) },
285         { 157, offsetof(fr_stats_t, total_dup_requests) },
286         { 158, offsetof(fr_stats_t, total_malformed_requests) },
287         { 159, offsetof(fr_stats_t, total_bad_authenticators) },
288         { 160, offsetof(fr_stats_t, total_packets_dropped) },
289         { 161, offsetof(fr_stats_t, total_unknown_types) },
290         { 0, 0 }
291 };
292 #endif
293
294 #define FR2ATTR(x) ((11344 << 16) | (x))
295
296 static void request_stats_addvp(REQUEST *request,
297                                 fr_stats2vp *table, fr_stats_t *stats)
298 {
299         int i;
300         VALUE_PAIR *vp;
301
302         for (i = 0; table[i].attribute != 0; i++) {
303                 vp = radius_paircreate(request, &request->reply->vps,
304                                        FR2ATTR(table[i].attribute),
305                                        PW_TYPE_INTEGER);
306                 if (!vp) continue;
307
308                 vp->vp_integer = *(int *)(((char *) stats) + table[i].offset);
309         }
310 }
311
312
313 void request_stats_reply(REQUEST *request)
314 {
315         VALUE_PAIR *flag, *vp;
316
317         /*
318          *      Statistics are available ONLY on a "status" port.
319          */
320         rad_assert(request->packet->code == PW_STATUS_SERVER);
321         rad_assert(request->listener->type == RAD_LISTEN_NONE);
322                 
323         flag = pairfind(request->packet->vps, FR2ATTR(127));
324         if (!flag || (flag->vp_integer == 0)) return;
325
326         if (((flag->vp_integer & 0x01) != 0) &&
327             ((flag->vp_integer & 0xc0) == 0)) {
328                 request_stats_addvp(request, authvp, &radius_auth_stats);
329         }
330                 
331 #ifdef WITH_ACCOUNTING
332         if (((flag->vp_integer & 0x02) != 0) &&
333             ((flag->vp_integer & 0xc0) == 0)) {
334                 request_stats_addvp(request, acctvp, &radius_acct_stats);
335         }
336 #endif
337
338 #ifdef WITH_PROXY
339         if (((flag->vp_integer & 0x04) != 0) &&
340             ((flag->vp_integer & 0x20) == 0)) {
341                 request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
342         }
343
344 #ifdef WITH_ACCOUNTING
345         if (((flag->vp_integer & 0x08) != 0) &&
346             ((flag->vp_integer & 0x20) == 0)) {
347                 request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
348         }
349 #endif
350 #endif
351
352         if ((flag->vp_integer & 0x10) != 0) {
353                 vp = radius_paircreate(request, &request->reply->vps,
354                                        FR2ATTR(176), PW_TYPE_DATE);
355                 if (vp) vp->vp_date = start_time.tv_sec;
356                 vp = radius_paircreate(request, &request->reply->vps,
357                                        FR2ATTR(177), PW_TYPE_DATE);
358                 if (vp) vp->vp_date = hup_time.tv_sec;
359                 
360 #ifdef HAVE_PTHREAD_H
361                 int i, array[RAD_LISTEN_MAX];
362
363                 thread_pool_queue_stats(array);
364
365                 for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
366                         vp = radius_paircreate(request, &request->reply->vps,
367                                                FR2ATTR(162 + i),
368                                                PW_TYPE_INTEGER);
369                         
370                         if (!vp) continue;
371                         vp->vp_integer = array[i];
372                 }
373 #endif
374         }
375
376         if ((flag->vp_integer & 0x20) != 0) {
377                 fr_ipaddr_t ipaddr;
378                 VALUE_PAIR *server_ip, *server_port;
379                 RADCLIENT *client = NULL;
380                 RADCLIENT_LIST *cl = NULL;
381
382                 /*
383                  *      See if we need to look up the client by server
384                  *      socket.
385                  */
386                 server_ip = pairfind(request->packet->vps, FR2ATTR(170));
387                 if (server_ip) {
388                         server_port = pairfind(request->packet->vps,
389                                                FR2ATTR(171));
390
391                         if (server_port) {
392                                 ipaddr.af = AF_INET;
393                                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
394                                 cl = listener_find_client_list(&ipaddr, server_port->vp_integer);
395                                                                
396                                 /*
397                                  *      Not found: don't do anything
398                                  */
399                                 if (!cl) return;
400                         }
401                 }
402
403
404                 vp = pairfind(request->packet->vps, FR2ATTR(167));
405                 if (vp) {
406                         ipaddr.af = AF_INET;
407                         ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
408                         client = client_find(cl, &ipaddr);
409
410                         /*
411                          *      Else look it up by number.
412                          */
413                 } else if ((vp = pairfind(request->packet->vps,
414                                            FR2ATTR(168))) != NULL) {
415                         client = client_findbynumber(cl, vp->vp_integer);
416                 }
417
418                 if (client) {
419                         /*
420                          *      If found, echo it back, along with
421                          *      the requested statistics.
422                          */
423                         pairadd(&request->reply->vps, paircopyvp(vp));
424
425                         /*
426                          *      When retrieving client by number, also
427                          *      echo back it's IP address.
428                          */
429                         if ((vp->type == PW_TYPE_INTEGER) &&
430                             (client->ipaddr.af == AF_INET)) {
431                                 vp = radius_paircreate(request,
432                                                        &request->reply->vps,
433                                                        FR2ATTR(167),
434                                                        PW_TYPE_IPADDR);
435                                 if (vp) {
436                                         vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
437                                 }
438
439                                 if (client->prefix != 32) {
440                                         vp = radius_paircreate(request,
441                                                                &request->reply->vps,
442                                                                FR2ATTR(169),
443                                                                PW_TYPE_INTEGER);
444                                         if (vp) {
445                                                 vp->vp_integer = client->prefix;
446                                         }
447                                 }
448                         }
449                         
450                         if (server_ip) {
451                                 pairadd(&request->reply->vps,
452                                         paircopyvp(server_ip));
453                                 pairadd(&request->reply->vps,
454                                         paircopyvp(server_port));
455                         }
456
457                         if (client->auth &&
458                             ((flag->vp_integer & 0x01) != 0)) {
459                                 request_stats_addvp(request, client_authvp,
460                                                     client->auth);
461                         }
462 #ifdef WITH_ACCOUNTING
463                         if (client->acct &&
464                             ((flag->vp_integer & 0x01) != 0)) {
465                                 request_stats_addvp(request, client_acctvp,
466                                                     client->acct);
467                         }
468 #endif
469                 } /* else client wasn't found, don't echo it back */
470         }
471
472         if (((flag->vp_integer & 0x40) != 0) &&
473             ((flag->vp_integer & 0x03) != 0)) {
474                 rad_listen_t *this;
475                 VALUE_PAIR *server_ip, *server_port;
476                 fr_ipaddr_t ipaddr;
477
478                 /*
479                  *      See if we need to look up the server by socket
480                  *      socket.
481                  */
482                 server_ip = pairfind(request->packet->vps, FR2ATTR(170));
483                 if (!server_ip) return;
484
485                 server_port = pairfind(request->packet->vps,
486                                        FR2ATTR(171));
487                 if (!server_port) return;
488                 
489                 ipaddr.af = AF_INET;
490                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
491                 this = listener_find_byipaddr(&ipaddr,
492                                               server_port->vp_integer);
493                 
494                 /*
495                  *      Not found: don't do anything
496                  */
497                 if (!this) return;
498                 
499                 pairadd(&request->reply->vps,
500                         paircopyvp(server_ip));
501                 pairadd(&request->reply->vps,
502                         paircopyvp(server_port));
503
504                 if (((flag->vp_integer & 0x01) != 0) &&
505                     ((request->listener->type == RAD_LISTEN_AUTH) ||
506                      (request->listener->type == RAD_LISTEN_NONE))) {
507                         request_stats_addvp(request, authvp, &this->stats);
508                 }
509                 
510 #ifdef WITH_ACCOUNTING
511                 if (((flag->vp_integer & 0x02) != 0) &&
512                     ((request->listener->type == RAD_LISTEN_ACCT) ||
513                      (request->listener->type == RAD_LISTEN_NONE))) {
514                         request_stats_addvp(request, acctvp, &this->stats);
515                 }
516 #endif
517         }
518
519         /*
520          *      Home servers.
521          */
522         if (((flag->vp_integer & 0x80) != 0) &&
523             ((flag->vp_integer & 0x03) != 0)) {
524                 home_server *home;
525                 VALUE_PAIR *server_ip, *server_port;
526                 fr_ipaddr_t ipaddr;
527
528                 /*
529                  *      See if we need to look up the server by socket
530                  *      socket.
531                  */
532                 server_ip = pairfind(request->packet->vps, FR2ATTR(170));
533                 if (!server_ip) return;
534
535                 server_port = pairfind(request->packet->vps,
536                                        FR2ATTR(171));
537                 if (!server_port) return;
538                 
539                 ipaddr.af = AF_INET;
540                 ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
541                 home = home_server_find(&ipaddr, server_port->vp_integer);
542
543                 /*
544                  *      Not found: don't do anything
545                  */
546                 if (!home) return;
547                 
548                 pairadd(&request->reply->vps,
549                         paircopyvp(server_ip));
550                 pairadd(&request->reply->vps,
551                         paircopyvp(server_port));
552
553                 vp = radius_paircreate(request, &request->reply->vps,
554                                        FR2ATTR(172), PW_TYPE_INTEGER);
555                 if (vp) vp->vp_integer = home->currently_outstanding;
556
557                 vp = radius_paircreate(request, &request->reply->vps,
558                                        FR2ATTR(173), PW_TYPE_INTEGER);
559                 if (vp) vp->vp_integer = home->state;
560
561                 if ((home->state == HOME_STATE_ALIVE) &&
562                     (home->revive_time.tv_sec != 0)) {
563                         vp = radius_paircreate(request, &request->reply->vps,
564                                                FR2ATTR(175), PW_TYPE_DATE);
565                         if (vp) vp->vp_date = home->revive_time.tv_sec;
566                 }
567
568                 if (home->state == HOME_STATE_IS_DEAD) {
569                         vp = radius_paircreate(request, &request->reply->vps,
570                                                FR2ATTR(174), PW_TYPE_DATE);
571                         if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
572                 }
573
574                 if (((flag->vp_integer & 0x01) != 0) &&
575                     ((request->listener->type == RAD_LISTEN_AUTH) ||
576                      (request->listener->type == RAD_LISTEN_NONE))) {
577                         request_stats_addvp(request, proxy_authvp,
578                                             &home->stats);
579                 }
580
581 #ifdef WITH_ACCOUNTING
582                 if (((flag->vp_integer & 0x02) != 0) &&
583                     ((request->listener->type == RAD_LISTEN_ACCT) ||
584                      (request->listener->type == RAD_LISTEN_NONE))) {
585                         request_stats_addvp(request, proxy_acctvp,
586                                             &home->stats);
587                 }
588 #endif
589         }
590 }
591
592 void radius_stats_init(int flag)
593 {
594         if (!flag) {
595                 gettimeofday(&start_time, NULL);
596                 hup_time = start_time; /* it's just nicer this way */
597         } else {
598                 gettimeofday(&hup_time, NULL);
599         }
600 }
601
602 #endif /* WITH_STATS */