From: Alan T. DeKok Date: Mon, 30 Oct 2017 18:29:15 +0000 (-0400) Subject: first pass at having statistics in v4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d298f9ef6cec1c2f867ba6326539cae48ebe25ed;p=thirdparty%2Ffreeradius-server.git first pass at having statistics in v4 --- diff --git a/raddb/all.mk b/raddb/all.mk index 586cb718af3..6d44df4c4db 100644 --- a/raddb/all.mk +++ b/raddb/all.mk @@ -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 index 00000000000..fabeec32a53 --- /dev/null +++ b/raddb/mods-available/stats @@ -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 { + +} diff --git a/share/dictionary.freeradius b/share/dictionary.freeradius index 5de613e4ef8..f68631bffad 100644 --- a/share/dictionary.freeradius +++ b/share/dictionary.freeradius @@ -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 index 00000000000..9459938ded7 --- /dev/null +++ b/src/modules/rlm_stats/all.mk @@ -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 index 00000000000..d4d66b082f5 --- /dev/null +++ b/src/modules/rlm_stats/rlm_stats.c @@ -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 + */ +RCSID("$Id$") + +#include +#include +#include +#include + +#ifdef HAVE_PTHREAD_H +#include +#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, + }, +};