]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
first pass at having statistics in v4
authorAlan T. DeKok <aland@freeradius.org>
Mon, 30 Oct 2017 18:29:15 +0000 (14:29 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 30 Oct 2017 19:01:19 +0000 (15:01 -0400)
raddb/all.mk
raddb/mods-available/stats [new file with mode: 0644]
share/dictionary.freeradius
src/modules/rlm_stats/all.mk [new file with mode: 0644]
src/modules/rlm_stats/rlm_stats.c [new file with mode: 0644]

index 586cb718af394d1bd7b4e06301ecce25b51bc897..6d44df4c4db4f46964f36905d548420730870c28 100644 (file)
@@ -11,7 +11,7 @@ DEFAULT_MODULES :=    always attr_filter cache_eap chap client \
                        detail detail.log digest dhcpv4 eap \
                        eap_inner echo exec expiration expr files linelog logintime \
                        mschap ntlm_auth pam pap passwd radius radutmp \
-                       soh sradutmp unix unpack utf8
+                       soh sradutmp stats unix unpack utf8
 
 LOCAL_MODULES :=       $(addprefix raddb/mods-enabled/,$(DEFAULT_MODULES))
 
diff --git a/raddb/mods-available/stats b/raddb/mods-available/stats
new file mode 100644 (file)
index 0000000..fabeec3
--- /dev/null
@@ -0,0 +1,17 @@
+#
+#  The "stats" module tracks RADIUS statistics.
+#
+#  It should be listed LAST in any "send ... { }" section.
+#
+#  When listed in a "send" section, it will increment statistics for
+#  input / output packets.
+#
+#  When listed in a "recv Status-Server" section, it will add global
+#  server statistics to the packet.  See "dictionary.freeradius", and
+#  the "FreeRADIUS-Stats4" attributes, for a list of which attributes
+#  it adds.
+#
+#
+stats {
+
+}
index 5de613e4ef863872ea86de2d31b2afabef485756..f68631bffad4688eea59a0a68970d356083d5a22 100644 (file)
@@ -17,6 +17,63 @@ BEGIN-VENDOR FreeRADIUS
 ATTRIBUTE      FreeRADIUS-Proxied-To                   1       ipaddr
 ATTRIBUTE      FreeRADIUS-Acct-Session-Start-Time      2       date
 
+#
+#  FreeRADIUS v4 produces statistics in its own TLV
+#
+ATTRIBUTE      FreeRADIUS-Stats4-TLV                   15      tlv
+ATTRIBUTE      FreeRADIUS-Stats4-Type                  15.1    integer
+ATTRIBUTE      FreeRADIUS-Stats4-Name                  15.2    string
+ATTRIBUTE      FreeRADIUS-Stats4-Number                15.3    string
+ATTRIBUTE      FreeRADIUS-Stats4-IPv4-Address          15.4    ipaddr
+ATTRIBUTE      FreeRADIUS-Stats4-IPv6-Address          15.5    ipv6addr
+ATTRIBUTE      FreeRADIUS-Stats4-Port                  15.6    integer
+
+#
+#  All of the counters are in a sub-TLV
+#
+ATTRIBUTE      FreeRADIUS-Stats4-Counters              15.8    TLV
+ATTRIBUTE      FreeRADIUS-Stats4-Requests              15.8.1  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Responses             15.8.2  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Invalid-Requests      15.8.3  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Dup-Requests          15.8.4  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Malformed-Requests    15.8.5  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Packets-Dropped       15.8.6  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Bad-Authenticators    15.8.7  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Unknown-Types         15.8.8  integer64
+
+#
+#  These count request and response packets for each type of packet.
+#  There is just one attribute for all of these counters.  We trust
+#  that the reader is knowledgable enough to know which ones are
+#  requests, and which ones are responses.
+#
+ATTRIBUTE      FreeRADIUS-Stats4-Packet-Counters       15.9    TLV
+
+#
+#  The attribute number for the counters is taken from the packet codes.
+#
+ATTRIBUTE      FreeRADIUS-Stats4-Access-Request        15.9.1  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Access-Accept         15.9.2  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Access-Reject         15.9.3  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Accounting-Request    15.9.4  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Accounting-Response   15.9.5  integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Access-Challenge      15.9.11 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Status-Server         15.9.12 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Disconnect-Request    15.9.40 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Disconnect-ACK        15.9.41 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Disconnect-NAK        15.9.42 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-CoA-Request           15.9.43 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-CoA-ACK               15.9.44 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-CoA-NAK               15.9.45 integer64
+ATTRIBUTE      FreeRADIUS-Stats4-Protocol-Error        15.9.52 integer64
+
+
+#
+#  Attributes 127 through 187 are for statistics produced by
+#  FreeRADIUS from version 2 to version 3.  Version 4 produces
+#  entirely different statistics.
+#
+
 #
 #  This attribute is really a bitmask.
 #
diff --git a/src/modules/rlm_stats/all.mk b/src/modules/rlm_stats/all.mk
new file mode 100644 (file)
index 0000000..9459938
--- /dev/null
@@ -0,0 +1,7 @@
+TARGETNAME     := rlm_stats
+
+ifneq "$(TARGETNAME)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := rlm_stats.c
diff --git a/src/modules/rlm_stats/rlm_stats.c b/src/modules/rlm_stats/rlm_stats.c
new file mode 100644 (file)
index 0000000..d4d66b0
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or (at
+ *   your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file rlm_stats.c
+ * @brief Keep RADIUS statistics. Eventually, also non-RADIUS statistics
+ *
+ * @copyright 2017 Network RADIUS SARL <license@networkradius.com>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/io/listen.h>
+#include <freeradius-devel/rad_assert.h>
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#define PTHREAD_MUTEX_LOCK   pthread_mutex_lock
+#define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
+
+#else
+#define PTHREAD_MUTEX_LOCK
+#define PTHREAD_MUTEX_UNLOCK
+#endif
+
+typedef struct rlm_stats_t {
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_t mutex;
+#endif
+
+       uint64_t                stats[FR_MAX_PACKET_CODE];
+} rlm_stats_t;
+
+typedef struct rlm_stats_thread_t {
+       rlm_stats_t             *inst;
+
+       fr_time_t               last_update;
+
+       uint64_t                stats[FR_MAX_PACKET_CODE];
+} rlm_stats_thread_t;
+
+static const CONF_PARSER module_config[] = {
+       CONF_PARSER_TERMINATOR
+};
+
+
+/*
+ *     Do the statistics
+ */
+static rlm_rcode_t CC_HINT(nonnull) mod_stats(void *instance, void *thread, REQUEST *request)
+{
+       int i, code;
+       rlm_stats_thread_t *t = thread;
+       rlm_stats_t *inst = instance;
+       VALUE_PAIR *vp;
+       vp_cursor_t cursor;
+       char buffer[64];
+       uint64_t stats[sizeof(inst->stats) / sizeof(inst->stats[0])];
+
+       /*
+        *      Increment counters only in "send foo" sections.
+        *
+        *      i.e. only when we have a reply to send.
+        */
+       if (request->request_state == REQUEST_SEND) {
+               code = request->packet->code;
+               if ((code > 0) && (code < FR_MAX_PACKET_CODE)) t->stats[code]++;
+
+               code = request->reply->code;
+               if ((code > 0) && (code < FR_MAX_PACKET_CODE)) t->stats[code]++;
+
+               if ((t->last_update + NANOSEC) > request->async->recv_time) {
+                       return RLM_MODULE_UPDATED;
+               }
+
+               t->last_update = request->async->recv_time;
+
+               PTHREAD_MUTEX_LOCK(&inst->mutex);
+               for (i = 0; i < FR_MAX_PACKET_CODE; i++) {
+                       inst->stats[i] += t->stats[i];
+                       t->stats[i] = 0;
+               }
+               PTHREAD_MUTEX_UNLOCK(&inst->mutex);
+
+               return RLM_MODULE_UPDATED;
+       }
+
+       /*
+        *      Ignore "authenticate" and anything other than Status-Server
+        */
+       if ((request->request_state != REQUEST_RECV) ||
+           (request->packet->code != FR_CODE_STATUS_SERVER)) {
+               return RLM_MODULE_NOOP;
+       }
+
+       /*
+        *      Merge our stats with the global stats, and then copy
+        *      the global stats to a thread-local variable.
+        *
+        *      The copy helps minimize mutex contention.
+        */
+       PTHREAD_MUTEX_LOCK(&inst->mutex);
+       for (i = 0; i < FR_MAX_PACKET_CODE; i++) {
+               inst->stats[i] += t->stats[i];
+               t->stats[i] = 0;
+       }
+       memcpy(&stats, inst->stats, sizeof(inst->stats));
+       PTHREAD_MUTEX_UNLOCK(&inst->mutex);
+
+       /*
+        *      Create attributes based on the statistics.
+        */
+       fr_pair_cursor_init(&cursor, &request->reply->vps);
+       vp = pair_make_reply("FreeRADIUS-Stats4-Name", "global", T_OP_EQ);
+       if (!vp) return RLM_MODULE_FAIL;
+
+       strcpy(buffer, "FreeRADIUS-Stats4-");
+
+       for (i = 0; i < FR_MAX_PACKET_CODE; i++) {
+               fr_dict_attr_t const *da;
+
+               if (!stats[i]) continue;
+
+               strcpy(buffer + 18, fr_packet_codes[i]);
+               da = fr_dict_attr_by_name(NULL, buffer);
+               if (!da) continue;
+
+               vp = fr_pair_afrom_da(request->reply, da);
+               if (!vp) return RLM_MODULE_FAIL;
+
+               vp->vp_uint64 = stats[i];
+
+               fr_pair_cursor_append(&cursor, vp);
+               (void) fr_pair_cursor_last(&cursor);
+       }
+
+       return RLM_MODULE_OK;
+}
+
+
+/** Instantiate thread data for the submodule.
+ *
+ */
+static int mod_thread_instantiate(UNUSED CONF_SECTION const *cs, void *instance, UNUSED fr_event_list_t *el, void *thread)
+{
+       rlm_stats_t *inst = talloc_get_type_abort(instance, rlm_stats_t);
+       rlm_stats_thread_t *t = thread;
+
+       (void) talloc_set_type(t, rlm_radius_thread_t);
+
+       t->inst = inst;
+
+       return 0;
+}
+
+
+/** Destroy thread data for the submodule.
+ *
+ */
+static int mod_thread_detach(void *thread)
+{
+       rlm_stats_thread_t *t = talloc_get_type_abort(thread, rlm_stats_thread_t);
+       rlm_stats_t *inst = t->inst;
+
+
+       PTHREAD_MUTEX_LOCK(&inst->mutex);
+       // @todo - merge all of the stats in
+       PTHREAD_MUTEX_UNLOCK(&inst->mutex);
+
+       return 0;
+}
+
+
+static int mod_instantiate(UNUSED void *instance, UNUSED CONF_SECTION *conf)
+{
+       rlm_stats_t     *inst = instance;
+
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_init(&inst->mutex, NULL);
+#endif
+
+       return 0;
+}
+
+/*
+ *     Only free memory we allocated.  The strings allocated via
+ *     cf_section_parse() do not need to be freed.
+ */
+static int mod_detach(void *instance)
+{
+       rlm_stats_t *inst = talloc_get_type_abort(instance, rlm_stats_t);
+
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_destroy(&inst->mutex);
+#endif
+
+       /* free things here */
+       return 0;
+}
+
+/*
+ *     The module name should be the only globally exported symbol.
+ *     That is, everything else should be 'static'.
+ *
+ *     If the module needs to temporarily modify it's instantiation
+ *     data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
+ *     The server will then take care of ensuring that the module
+ *     is single-threaded.
+ */
+extern rad_module_t rlm_stats;
+
+rad_module_t rlm_stats = {
+       .magic                  = RLM_MODULE_INIT,
+       .name                   = "stats",
+       .inst_size              = sizeof(rlm_stats_t),
+       .thread_inst_size       = sizeof(rlm_stats_thread_t),
+       .config                 = module_config,
+       .instantiate            = mod_instantiate,
+       .detach                 = mod_detach,
+       .thread_instantiate     = mod_thread_instantiate,
+       .thread_detach          = mod_thread_detach,
+       .methods = {
+               [MOD_AUTHORIZE]         = mod_stats, /* @mod_stats_query */
+               [MOD_POST_AUTH]         = mod_stats,
+               [MOD_ACCOUNTING]        = mod_stats,
+               [MOD_SEND_COA]          = mod_stats,
+       },
+};