Enable building WITHOUT_ACCOUNTING
[freeradius.git] / src / main / stats.c
index ed186ba..ced48bb 100644 (file)
@@ -29,6 +29,15 @@ RCSID("$Id$")
 
 #ifdef WITH_STATS
 
+#define USEC (1000000)
+#define EMA_SCALE (100)
+#define PREC (USEC * EMA_SCALE)
+
+#define F_EMA_SCALE (1000000)
+
+static struct timeval  start_time;
+static struct timeval  hup_time;
+
 fr_stats_t radius_auth_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 #ifdef WITH_ACCOUNTING
 fr_stats_t radius_acct_stats = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -46,8 +55,21 @@ void request_stats_final(REQUEST *request)
        if (request->master_state == REQUEST_COUNTED) return;
 
        if ((request->listener->type != RAD_LISTEN_NONE) &&
-           (request->listener->type != RAD_LISTEN_AUTH) &&
-           (request->listener->type != RAD_LISTEN_ACCT)) return;
+#ifdef WITH_ACCOUNTING
+           (request->listener->type != RAD_LISTEN_ACCT) &&
+#endif
+           (request->listener->type != RAD_LISTEN_AUTH)) return;
+
+#undef INC_AUTH
+#define INC_AUTH(_x) radius_auth_stats._x++;request->listener->stats._x++;if (request->client && request->client->auth) request->client->auth->_x++;
+
+
+#undef INC_ACCT
+#ifdef WITH_ACCOUNTING
+#define INC_ACCT(_x) radius_acct_stats._x++;request->listener->stats._x++;if (request->client && request->client->acct) request->client->acct->_x++
+#else
+#define INC_ACCT(_x)
+#endif
 
        /*
         *      Update the statistics.
@@ -57,44 +79,25 @@ void request_stats_final(REQUEST *request)
         *      deleted, because only the main server thread calls
         *      this function, which makes it thread-safe.
         */
-       switch (request->reply->code) {
+       if (request->reply) switch (request->reply->code) {
        case PW_AUTHENTICATION_ACK:
-               radius_auth_stats.total_responses++;
-               radius_auth_stats.total_access_accepts++;
-               request->listener->stats.total_responses++;
-               request->listener->stats.total_access_accepts++;
-               if (request->client && request->client->auth) {
-                       request->client->auth->accepts++;
-               }
+               INC_AUTH(total_responses);
+               INC_AUTH(total_access_accepts);
                break;
 
        case PW_AUTHENTICATION_REJECT:
-               radius_auth_stats.total_responses++;
-               radius_auth_stats.total_access_rejects++;
-               request->listener->stats.total_responses++;
-               request->listener->stats.total_access_rejects++;
-               if (request->client && request->client->auth) {
-                       request->client->auth->rejects++;
-               }
+               INC_AUTH(total_responses);
+               INC_AUTH(total_access_rejects);
                break;
 
        case PW_ACCESS_CHALLENGE:
-               radius_auth_stats.total_responses++;
-               radius_auth_stats.total_access_challenges++;
-               request->listener->stats.total_responses++;
-               request->listener->stats.total_access_challenges++;
-               if (request->client && request->client->auth) {
-                       request->client->auth->challenges++;
-               }
+               INC_AUTH(total_responses);
+               INC_AUTH(total_access_challenges);
                break;
 
 #ifdef WITH_ACCOUNTING
        case PW_ACCOUNTING_RESPONSE:
-               radius_acct_stats.total_responses++;
-               request->listener->stats.total_responses++;
-               if (request->client && request->client->acct) {
-                       request->client->acct->responses++;
-               }
+               INC_ACCT(total_responses);
                break;
 #endif
 
@@ -104,10 +107,16 @@ void request_stats_final(REQUEST *request)
                 */
        case 0:
                if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
-                       radius_auth_stats.total_bad_authenticators++;
-                       request->listener->stats.total_bad_authenticators++;
-                       if (request->client && request->client->auth) {
-                               request->client->auth->bad_authenticators++;
+                       if (request->reply->offset == -2) {
+                               INC_AUTH(total_bad_authenticators);
+                       } else {
+                               INC_AUTH(total_packets_dropped);
+                       }
+               } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+                       if (request->reply->offset == -2) {
+                               INC_ACCT(total_bad_authenticators);
+                       } else {
+                               INC_ACCT(total_packets_dropped);
                        }
                }
                break;
@@ -117,9 +126,9 @@ void request_stats_final(REQUEST *request)
        }
 
 #ifdef WITH_PROXY
-       if (!request->proxy) goto done; /* simplifies formatting */
-               
-       switch (request->proxy_reply->code) {
+       if (!request->proxy || !request->proxy_listener) goto done;     /* simplifies formatting */
+
+       switch (request->proxy->code) {
        case PW_AUTHENTICATION_REQUEST:
                proxy_auth_stats.total_requests += request->num_proxied_requests;
                request->proxy_listener->stats.total_requests += request->num_proxied_requests;
@@ -140,37 +149,28 @@ void request_stats_final(REQUEST *request)
 
        if (!request->proxy_reply) goto done;   /* simplifies formatting */
 
+#undef INC
+#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;
+
        switch (request->proxy_reply->code) {
        case PW_AUTHENTICATION_ACK:
-               proxy_auth_stats.total_responses += request->num_proxied_responses;
-               proxy_auth_stats.total_access_accepts += request->num_proxied_responses;
-               request->proxy_listener->stats.total_responses += request->num_proxied_responses;
-               request->home_server->stats.total_access_accepts += request->num_proxied_responses;
-               request->home_server->stats.total_responses += request->num_proxied_responses;
-               request->home_server->stats.total_access_accepts += request->num_proxied_responses;
+               INC(total_responses);
+               INC(total_access_accepts);
                break;
 
        case PW_AUTHENTICATION_REJECT:
-               proxy_auth_stats.total_responses += request->num_proxied_responses;
-               proxy_auth_stats.total_access_rejects += request->num_proxied_responses;
-               request->proxy_listener->stats.total_responses += request->num_proxied_responses;
-               request->proxy_listener->stats.total_access_rejects += request->num_proxied_responses;
-               request->home_server->stats.total_responses += request->num_proxied_responses;
-               request->home_server->stats.total_access_rejects += request->num_proxied_responses;
+               INC(total_responses);
+               INC(total_access_rejects);
                break;
 
        case PW_ACCESS_CHALLENGE:
-               proxy_auth_stats.total_responses += request->num_proxied_responses;
-               proxy_auth_stats.total_access_challenges += request->num_proxied_responses;
-               request->proxy_listener->stats.total_responses += request->num_proxied_responses;
-               request->proxy_listener->stats.total_access_challenges += request->num_proxied_responses;
-               request->home_server->stats.total_responses += request->num_proxied_responses;
-               request->home_server->stats.total_access_challenges += request->num_proxied_responses;
+               INC(total_responses);
+               INC(total_access_challenges);
                break;
 
 #ifdef WITH_ACCOUNTING
        case PW_ACCOUNTING_RESPONSE:
-               radius_acct_stats.total_responses++;
+               proxy_acct_stats.total_responses++;
                request->proxy_listener->stats.total_responses++;
                request->home_server->stats.total_responses++;
                break;
@@ -262,43 +262,41 @@ static fr_stats2vp proxy_acctvp[] = {
 #endif
 
 static fr_stats2vp client_authvp[] = {
-       { 128, offsetof(fr_client_stats_t, requests) },
-       { 129, offsetof(fr_client_stats_t, accepts) },
-       { 130, offsetof(fr_client_stats_t, rejects) },
-       { 131, offsetof(fr_client_stats_t, challenges) },
-       { 132, offsetof(fr_client_stats_t, responses) },
-       { 133, offsetof(fr_client_stats_t, dup_requests) },
-       { 134, offsetof(fr_client_stats_t, malformed_requests) },
-       { 135, offsetof(fr_client_stats_t, bad_authenticators) },
-       { 136, offsetof(fr_client_stats_t, packets_dropped) },
-       { 137, offsetof(fr_client_stats_t, unknown_types) },
+       { 128, offsetof(fr_stats_t, total_requests) },
+       { 129, offsetof(fr_stats_t, total_access_accepts) },
+       { 130, offsetof(fr_stats_t, total_access_rejects) },
+       { 131, offsetof(fr_stats_t, total_access_challenges) },
+       { 132, offsetof(fr_stats_t, total_responses) },
+       { 133, offsetof(fr_stats_t, total_dup_requests) },
+       { 134, offsetof(fr_stats_t, total_malformed_requests) },
+       { 135, offsetof(fr_stats_t, total_bad_authenticators) },
+       { 136, offsetof(fr_stats_t, total_packets_dropped) },
+       { 137, offsetof(fr_stats_t, total_unknown_types) },
        { 0, 0 }
 };
 
 #ifdef WITH_ACCOUNTING
 static fr_stats2vp client_acctvp[] = {
-       { 155, offsetof(fr_client_stats_t, requests) },
-       { 156, offsetof(fr_client_stats_t, responses) },
-       { 157, offsetof(fr_client_stats_t, dup_requests) },
-       { 158, offsetof(fr_client_stats_t, malformed_requests) },
-       { 159, offsetof(fr_client_stats_t, bad_authenticators) },
-       { 160, offsetof(fr_client_stats_t, packets_dropped) },
-       { 161, offsetof(fr_client_stats_t, unknown_types) },
+       { 148, offsetof(fr_stats_t, total_requests) },
+       { 149, offsetof(fr_stats_t, total_responses) },
+       { 150, offsetof(fr_stats_t, total_dup_requests) },
+       { 151, offsetof(fr_stats_t, total_malformed_requests) },
+       { 152, offsetof(fr_stats_t, total_bad_authenticators) },
+       { 153, offsetof(fr_stats_t, total_packets_dropped) },
+       { 154, offsetof(fr_stats_t, total_unknown_types) },
        { 0, 0 }
 };
 #endif
 
-#define FR2ATTR(x) ((11344 << 16) | (x))
-
 static void request_stats_addvp(REQUEST *request,
-                               fr_stats2vp *table, void *stats)
+                               fr_stats2vp *table, fr_stats_t *stats)
 {
        int i;
        VALUE_PAIR *vp;
 
        for (i = 0; table[i].attribute != 0; i++) {
                vp = radius_paircreate(request, &request->reply->vps,
-                                      FR2ATTR(table[i].attribute),
+                                      table[i].attribute, VENDORPEC_FREERADIUS,
                                       PW_TYPE_INTEGER);
                if (!vp) continue;
 
@@ -311,29 +309,27 @@ void request_stats_reply(REQUEST *request)
 {
        VALUE_PAIR *flag, *vp;
 
-       if (request->packet->code != PW_STATUS_SERVER) return;
-
        /*
-        *      Status sockets can have any IP.  For other sockets, we
-        *      do respond with statistics only if they're from
-        *      localhost.
+        *      Statistics are available ONLY on a "status" port.
         */
-       if (request->listener->type != RAD_LISTEN_NONE) {
-               if ((request->packet->src_ipaddr.af != AF_INET) ||
-                   (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_LOOPBACK))) {
-                       return;
-               }
-       }
+       rad_assert(request->packet->code == PW_STATUS_SERVER);
+       rad_assert(request->listener->type == RAD_LISTEN_NONE);
                
-       flag = pairfind(request->packet->vps, FR2ATTR(127));
+       flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS);
        if (!flag || (flag->vp_integer == 0)) return;
 
+       /*
+        *      Authentication.
+        */
        if (((flag->vp_integer & 0x01) != 0) &&
            ((flag->vp_integer & 0xc0) == 0)) {
                request_stats_addvp(request, authvp, &radius_auth_stats);
        }
                
 #ifdef WITH_ACCOUNTING
+       /*
+        *      Accounting
+        */
        if (((flag->vp_integer & 0x02) != 0) &&
            ((flag->vp_integer & 0xc0) == 0)) {
                request_stats_addvp(request, acctvp, &radius_acct_stats);
@@ -341,12 +337,18 @@ void request_stats_reply(REQUEST *request)
 #endif
 
 #ifdef WITH_PROXY
+       /*
+        *      Proxied authentication requests.
+        */
        if (((flag->vp_integer & 0x04) != 0) &&
            ((flag->vp_integer & 0x20) == 0)) {
                request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
        }
 
 #ifdef WITH_ACCOUNTING
+       /*
+        *      Proxied accounting requests.
+        */
        if (((flag->vp_integer & 0x08) != 0) &&
            ((flag->vp_integer & 0x20) == 0)) {
                request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
@@ -354,26 +356,41 @@ void request_stats_reply(REQUEST *request)
 #endif
 #endif
 
-#ifdef HAVE_PTHREAD_H
+       /*
+        *      Internal server statistics
+        */
        if ((flag->vp_integer & 0x10) != 0) {
+               vp = radius_paircreate(request, &request->reply->vps,
+                                      176, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
+               if (vp) vp->vp_date = start_time.tv_sec;
+               vp = radius_paircreate(request, &request->reply->vps,
+                                      177, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
+               if (vp) vp->vp_date = hup_time.tv_sec;
+               
+#ifdef HAVE_PTHREAD_H
                int i, array[RAD_LISTEN_MAX];
 
                thread_pool_queue_stats(array);
 
+#ifdef WITH_DETAIL
                for (i = 0; i <= RAD_LISTEN_DETAIL; i++) {
                        vp = radius_paircreate(request, &request->reply->vps,
-                                              FR2ATTR(162 + i),
+                                              162 + i, VENDORPEC_FREERADIUS,
                                               PW_TYPE_INTEGER);
                        
                        if (!vp) continue;
                        vp->vp_integer = array[i];
                }
-       }
 #endif
+#endif
+       }
 
+       /*
+        *      For a particular client.
+        */
        if ((flag->vp_integer & 0x20) != 0) {
                fr_ipaddr_t ipaddr;
-               VALUE_PAIR *server_ip, *server_port;
+               VALUE_PAIR *server_ip, *server_port = NULL;
                RADCLIENT *client = NULL;
                RADCLIENT_LIST *cl = NULL;
 
@@ -381,10 +398,10 @@ void request_stats_reply(REQUEST *request)
                 *      See if we need to look up the client by server
                 *      socket.
                 */
-               server_ip = pairfind(request->packet->vps, FR2ATTR(170));
+               server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
                if (server_ip) {
                        server_port = pairfind(request->packet->vps,
-                                              FR2ATTR(171));
+                                              171, VENDORPEC_FREERADIUS);
 
                        if (server_port) {
                                ipaddr.af = AF_INET;
@@ -399,17 +416,22 @@ void request_stats_reply(REQUEST *request)
                }
 
 
-               vp = pairfind(request->packet->vps, FR2ATTR(167));
+               vp = pairfind(request->packet->vps, 167, VENDORPEC_FREERADIUS);
                if (vp) {
                        ipaddr.af = AF_INET;
                        ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
-                       client = client_find(cl, &ipaddr);
+                       client = client_find(cl, &ipaddr, IPPROTO_UDP);
+#ifdef WITH_TCP
+                       if (!client) {
+                               client = client_find(cl, &ipaddr, IPPROTO_TCP);
+                       }
+#endif
 
                        /*
                         *      Else look it up by number.
                         */
                } else if ((vp = pairfind(request->packet->vps,
-                                          FR2ATTR(168))) != NULL) {
+                                          168, VENDORPEC_FREERADIUS)) != NULL) {
                        client = client_findbynumber(cl, vp->vp_integer);
                }
 
@@ -428,7 +450,7 @@ void request_stats_reply(REQUEST *request)
                            (client->ipaddr.af == AF_INET)) {
                                vp = radius_paircreate(request,
                                                       &request->reply->vps,
-                                                      FR2ATTR(167),
+                                                      167, VENDORPEC_FREERADIUS,
                                                       PW_TYPE_IPADDR);
                                if (vp) {
                                        vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
@@ -437,7 +459,7 @@ void request_stats_reply(REQUEST *request)
                                if (client->prefix != 32) {
                                        vp = radius_paircreate(request,
                                                               &request->reply->vps,
-                                                              FR2ATTR(169),
+                                                              169, VENDORPEC_FREERADIUS,
                                                               PW_TYPE_INTEGER);
                                        if (vp) {
                                                vp->vp_integer = client->prefix;
@@ -467,6 +489,9 @@ void request_stats_reply(REQUEST *request)
                } /* else client wasn't found, don't echo it back */
        }
 
+       /*
+        *      For a particular "listen" socket.
+        */
        if (((flag->vp_integer & 0x40) != 0) &&
            ((flag->vp_integer & 0x03) != 0)) {
                rad_listen_t *this;
@@ -477,11 +502,11 @@ void request_stats_reply(REQUEST *request)
                 *      See if we need to look up the server by socket
                 *      socket.
                 */
-               server_ip = pairfind(request->packet->vps, FR2ATTR(170));
+               server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
                if (!server_ip) return;
 
                server_port = pairfind(request->packet->vps,
-                                      FR2ATTR(171));
+                                      171, VENDORPEC_FREERADIUS);
                if (!server_port) return;
                
                ipaddr.af = AF_INET;
@@ -514,6 +539,7 @@ void request_stats_reply(REQUEST *request)
 #endif
        }
 
+#ifdef WITH_PROXY
        /*
         *      Home servers.
         */
@@ -527,16 +553,17 @@ void request_stats_reply(REQUEST *request)
                 *      See if we need to look up the server by socket
                 *      socket.
                 */
-               server_ip = pairfind(request->packet->vps, FR2ATTR(170));
+               server_ip = pairfind(request->packet->vps, 170, VENDORPEC_FREERADIUS);
                if (!server_ip) return;
 
                server_port = pairfind(request->packet->vps,
-                                      FR2ATTR(171));
+                                      171, VENDORPEC_FREERADIUS);
                if (!server_port) return;
                
                ipaddr.af = AF_INET;
                ipaddr.ipaddr.ip4addr.s_addr = server_ip->vp_ipaddr;
-               home = home_server_find(&ipaddr, server_port->vp_integer);
+               home = home_server_find(&ipaddr, server_port->vp_integer,
+                                       IPPROTO_UDP);
 
                /*
                 *      Not found: don't do anything
@@ -549,42 +576,128 @@ void request_stats_reply(REQUEST *request)
                        paircopyvp(server_port));
 
                vp = radius_paircreate(request, &request->reply->vps,
-                                      FR2ATTR(172), PW_TYPE_INTEGER);
+                                      172, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
                if (vp) vp->vp_integer = home->currently_outstanding;
 
                vp = radius_paircreate(request, &request->reply->vps,
-                                      FR2ATTR(173), PW_TYPE_INTEGER);
+                                      173, VENDORPEC_FREERADIUS, PW_TYPE_INTEGER);
                if (vp) vp->vp_integer = home->state;
 
                if ((home->state == HOME_STATE_ALIVE) &&
                    (home->revive_time.tv_sec != 0)) {
                        vp = radius_paircreate(request, &request->reply->vps,
-                                              FR2ATTR(175), PW_TYPE_DATE);
+                                              175, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
                        if (vp) vp->vp_date = home->revive_time.tv_sec;
                }
 
+               if ((home->state == HOME_STATE_ALIVE) &&
+                   (home->ema.window > 0)) {
+                               vp = radius_paircreate(request,
+                                                      &request->reply->vps,
+                                                      178, VENDORPEC_FREERADIUS,
+                                                      PW_TYPE_INTEGER);
+                               if (vp) vp->vp_integer = home->ema.window;
+                               vp = radius_paircreate(request,
+                                                      &request->reply->vps,
+                                                      179, VENDORPEC_FREERADIUS,
+                                                      PW_TYPE_INTEGER);
+                               if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
+                               vp = radius_paircreate(request,
+                                                      &request->reply->vps,
+                                                      180, VENDORPEC_FREERADIUS,
+                                                      PW_TYPE_INTEGER);
+                               if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
+
+               }
+
                if (home->state == HOME_STATE_IS_DEAD) {
                        vp = radius_paircreate(request, &request->reply->vps,
-                                              FR2ATTR(174), PW_TYPE_DATE);
+                                              174, VENDORPEC_FREERADIUS, PW_TYPE_DATE);
                        if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
                }
 
                if (((flag->vp_integer & 0x01) != 0) &&
-                   ((request->listener->type == RAD_LISTEN_AUTH) ||
-                    (request->listener->type == RAD_LISTEN_NONE))) {
+                   (home->type == HOME_TYPE_AUTH)) {
                        request_stats_addvp(request, proxy_authvp,
                                            &home->stats);
                }
 
 #ifdef WITH_ACCOUNTING
                if (((flag->vp_integer & 0x02) != 0) &&
-                   ((request->listener->type == RAD_LISTEN_ACCT) ||
-                    (request->listener->type == RAD_LISTEN_NONE))) {
+                   (home->type == HOME_TYPE_ACCT)) {
                        request_stats_addvp(request, proxy_acctvp,
                                            &home->stats);
                }
 #endif
        }
+#endif /* WITH_PROXY */
+}
+
+void radius_stats_init(int flag)
+{
+       if (!flag) {
+               gettimeofday(&start_time, NULL);
+               hup_time = start_time; /* it's just nicer this way */
+       } else {
+               gettimeofday(&hup_time, NULL);
+       }
+}
+
+void radius_stats_ema(fr_stats_ema_t *ema,
+                     struct timeval *start, struct timeval *end)
+{
+       int micro;
+       time_t tdiff;
+#ifdef WITH_STATS_DEBUG
+       static int n = 0;
+#endif
+       if (ema->window == 0) return;
+
+       rad_assert(start->tv_sec >= end->tv_sec);
+
+       /*
+        *      Initialize it.
+        */
+       if (ema->f1 == 0) {
+               if (ema->window > 10000) ema->window = 10000;
+               
+               ema->f1 =  (2 * F_EMA_SCALE) / (ema->window + 1);
+               ema->f10 = (2 * F_EMA_SCALE) / ((10 * ema->window) + 1);
+       }
+
+
+       tdiff = start->tv_sec;
+       tdiff -= end->tv_sec;
+       
+       micro = (int) tdiff;
+       if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
+       micro *= USEC;
+       micro += start->tv_usec;
+       micro -= end->tv_usec;
+       
+       micro *= EMA_SCALE;
+
+       if (ema->ema1 == 0) {
+               ema->ema1 = micro;
+               ema->ema10 = micro;
+       } else {
+               int diff;
+               
+               diff = ema->f1 * (micro - ema->ema1);
+               ema->ema1 += (diff / 1000000);
+               
+               diff = ema->f10 * (micro - ema->ema10);
+               ema->ema10 += (diff / 1000000);
+       }
+       
+       
+#ifdef WITH_STATS_DEBUG
+       DEBUG("time %d %d.%06d\t%d.%06d\t%d.%06d\n",
+             n, micro / PREC, (micro / EMA_SCALE) % USEC,
+             ema->ema1 / PREC, (ema->ema1 / EMA_SCALE) % USEC,
+             ema->ema10 / PREC, (ema->ema10 / EMA_SCALE) % USEC);
+       n++;
+#endif 
 }
 
 #endif /* WITH_STATS */