46d5c5b0fe75138ec9357b2c79ad56fb81e48d16
[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 #define USEC (1000000)
33 #define PREC (USEC * EMA_SCALE)
34
35 #define F_EMA_SCALE (1000000)
36
37 struct timeval  radius_start_time;
38 struct timeval  radius_hup_time;
39
40 fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
41 #ifdef WITH_ACCOUNTING
42 fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
43 #endif
44
45 #ifdef WITH_PROXY
46 fr_stats_t proxy_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
47 #ifdef WITH_ACCOUNTING
48 fr_stats_t proxy_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
49 #endif
50 #endif
51
52 void request_stats_final(REQUEST *request)
53 {
54         if (request->master_state == REQUEST_COUNTED) return;
55
56         if ((request->listener->type != RAD_LISTEN_NONE) &&
57             (request->listener->type != RAD_LISTEN_AUTH) &&
58             (request->listener->type != RAD_LISTEN_ACCT)) return;
59
60 #undef INC_AUTH
61 #define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
62
63
64 #undef INC_ACCT
65 #define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
66
67         /*
68          *      Update the statistics.
69          *
70          *      Note that we do NOT do this in a child thread.
71          *      Instead, we update the stats when a request is
72          *      deleted, because only the main server thread calls
73          *      this function, which makes it thread-safe.
74          */
75         if (request->reply) switch (request->reply->code) {
76         case PW_AUTHENTICATION_ACK:
77                 INC_AUTH(total_responses);
78                 INC_AUTH(total_access_accepts);
79                 break;
80
81         case PW_AUTHENTICATION_REJECT:
82                 INC_AUTH(total_responses);
83                 INC_AUTH(total_access_rejects);
84                 break;
85
86         case PW_ACCESS_CHALLENGE:
87                 INC_AUTH(total_responses);
88                 INC_AUTH(total_access_challenges);
89                 break;
90
91 #ifdef WITH_ACCOUNTING
92         case PW_ACCOUNTING_RESPONSE:
93                 INC_ACCT(total_responses);
94                 break;
95 #endif
96
97                 /*
98                  *      No response, it must have been a bad
99                  *      authenticator.
100                  */
101         case 0:
102                 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
103                         if (request->reply->offset == -2) {
104                                 INC_AUTH(total_bad_authenticators);
105                         } else {
106                                 INC_AUTH(total_packets_dropped);
107                         }
108                 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
109                         if (request->reply->offset == -2) {
110                                 INC_ACCT(total_bad_authenticators);
111                         } else {
112                                 INC_ACCT(total_packets_dropped);
113                         }
114                 }
115                 break;
116
117         default:
118                 break;
119         }
120
121 #ifdef WITH_PROXY
122         if (!request->proxy || !request->proxy_listener) goto done;     /* simplifies formatting */
123
124         switch (request->proxy->code) {
125         case PW_AUTHENTICATION_REQUEST:
126                 proxy_auth_stats.total_requests += request->num_proxied_requests;
127                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
128                 request->home_server->stats.total_requests += request->num_proxied_requests;
129                 break;
130
131 #ifdef WITH_ACCOUNTING
132         case PW_ACCOUNTING_REQUEST:
133                 proxy_acct_stats.total_requests++;
134                 request->proxy_listener->stats.total_requests += request->num_proxied_requests;
135                 request->home_server->stats.total_requests += request->num_proxied_requests;
136                 break;
137 #endif
138
139         default:
140                 break;
141         }
142
143         if (!request->proxy_reply) goto done;   /* simplifies formatting */
144
145 #undef INC
146 #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;
147
148         switch (request->proxy_reply->code) {
149         case PW_AUTHENTICATION_ACK:
150                 INC(total_responses);
151                 INC(total_access_accepts);
152                 break;
153
154         case PW_AUTHENTICATION_REJECT:
155                 INC(total_responses);
156                 INC(total_access_rejects);
157                 break;
158
159         case PW_ACCESS_CHALLENGE:
160                 INC(total_responses);
161                 INC(total_access_challenges);
162                 break;
163
164 #ifdef WITH_ACCOUNTING
165         case PW_ACCOUNTING_RESPONSE:
166                 radius_acct_stats.total_responses++;
167                 request->proxy_listener->stats.total_responses++;
168                 request->home_server->stats.total_responses++;
169                 break;
170 #endif
171
172         default:
173                 proxy_auth_stats.total_unknown_types++;
174                 request->proxy_listener->stats.total_unknown_types++;
175                 request->home_server->stats.total_unknown_types++;
176                 break;
177         }
178
179  done:
180 #endif /* WITH_PROXY */
181
182         request->master_state = REQUEST_COUNTED;
183 }
184
185 void radius_stats_init(int flag)
186 {
187         if (!flag) {
188                 gettimeofday(&radius_start_time, NULL);
189                 radius_hup_time = radius_start_time; /* it's just nicer this way */
190         } else {
191                 gettimeofday(&radius_hup_time, NULL);
192         }
193 }
194
195 void radius_stats_ema(fr_stats_ema_t *ema,
196                       struct timeval *start, struct timeval *end)
197 {
198         int micro;
199         time_t tdiff;
200 #ifdef WITH_STATS_DEBUG
201         static int n = 0;
202 #endif
203         if (ema->window == 0) return;
204
205         rad_assert(start->tv_sec >= end->tv_sec);
206
207         /*
208          *      Initialize it.
209          */
210         if (ema->f1 == 0) {
211                 if (ema->window > 10000) ema->window = 10000;
212                 
213                 ema->f1 =  (2 * F_EMA_SCALE) / (ema->window + 1);
214                 ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
215         }
216
217
218         tdiff = start->tv_sec;
219         tdiff -= end->tv_sec;
220         
221         micro = (int) tdiff;
222         if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
223         micro *= USEC;
224         micro += start->tv_usec;
225         micro -= end->tv_usec;
226         
227         micro *= EMA_SCALE;
228
229         if (ema->ema1 == 0) {
230                 ema->ema1 = micro;
231                 ema->ema10 = micro;
232         } else {
233                 int diff;
234                 
235                 diff = ema->f1 * (micro - ema->ema1);
236                 ema->ema1 += (diff / 1000000);
237                 
238                 diff = ema->f10 * (micro - ema->ema10);
239                 ema->ema10 += (diff / 1000000);
240         }
241         
242         
243 #ifdef WITH_STATS_DEBUG
244         DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
245               n, micro / PREC, (micro / EMA_SCALE) % USEC,
246               ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
247               ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
248         n++;
249 #endif  
250 }
251
252 #endif /* WITH_STATS */