]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add libfreeradius-radius-stats.la along with framework
authorAlan T. DeKok <aland@freeradius.org>
Tue, 13 Jan 2026 12:55:21 +0000 (07:55 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 13 Jan 2026 18:01:05 +0000 (13:01 -0500)
src/all.mk
src/bin/radict.c
src/lib/util/stats.h
src/stats/all.mk [new file with mode: 0644]
src/stats/radius/all.mk [new file with mode: 0644]
src/stats/radius/auth_serv_da_autoload.c [new file with mode: 0644]
src/stats/radius/auth_serv_da_def.c [new file with mode: 0644]
src/stats/radius/auth_serv_stats.c [new file with mode: 0644]
src/stats/radius/auth_serv_stats.h [new file with mode: 0644]
src/stats/radius/base.c [new file with mode: 0644]
src/stats/radius/stats.h [new file with mode: 0644]

index 716cd4592763c2d8c51311a8f85aff4a0be475df..a108ab3de25e21a81ff98be97f87ad1c755f2728 100644 (file)
@@ -7,7 +7,8 @@ SUBMAKEFILES := include/all.mk \
        listen/all.mk \
        process/all.mk \
        modules/all.mk \
-       bin/all.mk
+       bin/all.mk \
+       stats/all.mk
 
 #
 #  The default is to just build the source code.  We skip running the
index e13deea0babe756adeb267c6adf849cec9c19b71..bbfd5928108aa8412359c2b528f3ce4a9f36ddda 100644 (file)
@@ -44,7 +44,6 @@ typedef enum {
        RADICT_OUT_DICT,
        RADICT_OUT_STRUCT,
        RADICT_OUT_STATS_LINK,
-       RADICT_OUT_ATTRS_H,
        RADICT_OUT_BASE_C_DA_DEF,
        RADICT_OUT_ATTR_AUTOLOAD,
        RADICT_OUT_STATS_H,
@@ -502,7 +501,7 @@ static void da_print_struct(FILE *fp, fr_dict_attr_t const *parent)
        fprintf(fp, "_t;\n");
 }
 
-static void da_print_attr_def(FILE *fp, fr_dict_attr_t const *parent, char const *prefix)
+static void da_print_base_c_da_def(FILE *fp, fr_dict_attr_t const *parent)
 {
        int i;
        fr_dict_attr_t const *da;
@@ -512,10 +511,10 @@ static void da_print_attr_def(FILE *fp, fr_dict_attr_t const *parent, char const
 
        da_normalize_name(parent, parent_name);
 
-       fprintf(fp, "%sfr_dict_attr_t const *attr_%s;\n", prefix, parent_name);
+       fprintf(fp, "static fr_dict_attr_t const *attr_%s;\n", parent_name);
 
        for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) {
-               fprintf(fp, "%sfr_dict_attr_t const *attr_%s_", prefix, parent_name);
+               fprintf(fp, "static fr_dict_attr_t const *attr_%s_", parent_name);
                da_print_name(fp, da);
                fprintf(fp, ";\n");
        }
@@ -523,15 +522,6 @@ static void da_print_attr_def(FILE *fp, fr_dict_attr_t const *parent, char const
        fprintf(fp, "\n\n");
 }
 
-static void da_print_attrs_h(FILE *fp, fr_dict_attr_t const *parent)
-{
-       da_print_attr_def(fp, parent, "extern HIDDEN ");
-}
-
-static void da_print_base_c_da_def(FILE *fp, fr_dict_attr_t const *parent)
-{
-       da_print_attr_def(fp, parent, "");
-}
 
 /** Map data types to enum names representing those types
  */
@@ -606,7 +596,7 @@ static void da_print_stats_link(FILE *fp, fr_dict_attr_t const *parent)
        }
        fprintf(fp, "\t.num_elements = %d,\n", num_elements);
 
-       fprintf(fp, "\t.entry = (fr_stats_link_entry_t[]) { /* -Wgnu-flexible-array-initializer */\n");
+       fprintf(fp, "\t.entry = {\n");
 
        /*
         *      For locality, also print out data type and size.  That way we _can_ dereference the da, but we
@@ -678,6 +668,8 @@ static void da_print_stats_h(FILE *fp, fr_dict_attr_t const *parent)
 
        fprintf(fp, "\n");
 
+       fprintf(fp, "FR_STATS_TYPEDEF(%s_%s);\n\n", dict_name, parent_name);
+
        fprintf(fp, "extern fr_stats_link_t const fr_stats_link_%s_%s;\n\n", dict_name, parent_name);
 }
 
@@ -738,7 +730,6 @@ static fr_table_num_ordered_t const format_table[] = {
        { L("dict"),            RADICT_OUT_DICT },
        { L("struct"),          RADICT_OUT_STRUCT },
        { L("stats_link"),      RADICT_OUT_STATS_LINK },
-       { L("attrs.h"),         RADICT_OUT_ATTRS_H },
        { L("da_def"),          RADICT_OUT_BASE_C_DA_DEF },
        { L("attr_autoload"),   RADICT_OUT_ATTR_AUTOLOAD },
        { L("stats.h"),         RADICT_OUT_STATS_H },
@@ -751,7 +742,6 @@ static fr_table_ptr_ordered_t const function_table[] = {
        { L("dict"),            NULL },
        { L("struct"),          (void *) da_print_struct },
        { L("stats_link"),      (void *) da_print_stats_link },
-       { L("attrs.h"),         (void *) da_print_attrs_h },
        { L("da_def"),          (void *) da_print_base_c_da_def },
        { L("attr_autoload"),   (void *) da_print_attr_autoload },
        { L("stats.h"),         (void *) da_print_stats_h },
index ab482eed32e1a9e5d30752b75efe68bd36f77a62..6ee2d7cdd208adf8f505f88ad9c027670161b98c 100644 (file)
@@ -68,46 +68,76 @@ typedef struct {
        uint8_t                 stats[];        //!< generic statistics data
 } fr_stats_instance_t;
 
-/** Macro to define a typedef for a particular kind of statistics
+/** Iterator for a statistics structure.
  *
+ *  This is used internally, and there's no real need for code outside of the statistics library to use it.
  */
-#define FR_STATS_TYPEDEF(_name, _type) \
+typedef struct {
+       fr_stats_instance_t const *inst;
+       unsigned int              current;
+} fr_stats_iter_t;
+
+
+/** Macro to define a typedef for a particular instance of statistics
+ *
+ *  Defines fr_stats_name_instance_t which contains an instance of the statistics for fr_stats_name_t, and
+ *  which points to the linking structure fr_stats_link_name_t.
+ *
+ *  Note that nothing needs to refer to the base statistics structure: fr_stats_name_t.  All of that is
+ *  wrapped in an instance definition.
+ *
+ *  This is used internally, and there's no real need for code outside of the statistics library to use it.
+ */
+#define FR_STATS_TYPEDEF(_name) \
        typedef struct {                \
                STATS_HEADER_COMMON;    \
-               _type   stats;          \
-       } fr_stats_## _name ## _instance_t
+               fr_stats_ ## _name ## _t        stats;   \
+       } fr_stats_ ## _name ## _instance_t
 
-/** Macro used when declaring a variable which contains the statistics
+/** Macro used when referencing a linking structure
+ *
+ *  .def = FR_STATS_LINK_NAME(radius_auth_serv),
  *
+ *  This is used internally, and there's no real need for code outside of the statistics library to use it.
  */
-#define FR_STATS_ENTRY_DECL(_name, _var) fr_stats_## _name ## _instance_t _var;
+#define FR_STATS_LINK_NAME(_name) fr_stats_link_ ## _name ## _t
 
-/** Macro used when initializing a variable which contains the statistics
+/** Macro used when declaring a variable which contains an instance of the statistics
+ *
+ *  Defines "fr_stats_name_instance_t var", which can be used in a structure
  *
+ *  Code which needs to use some statistics can use this macro to declare a variable which contains stats for
+ *  the local module / etc.
+ */
+#define FR_STATS_ENTRY_DECL(_name, _var) fr_stats_ ## _name ## _instance_t _var
+
+/** Macro used when initializing a variable which contains an instance of the statistics
+ *
+ *  var = .... initializer for the stats instance ...
+ *
+ *  Code which needs to use some statistics can use this macro to initialize a variable which contains stats
+ *  for the local module / etc.
  */
 #define FR_STATS_ENTRY_INIT(_name, _var, _mib)         \
-       _var = (fr_stats_## _name ## _instance_t) {     \
+       _var = (fr_stats_ ## _name ## _instance_t) {    \
                .def = fr_stats_link_ ## _name ## _t,   \
                .mib = _mib,                            \
-               fr_stats_## _name ## _t stats = {},     \
+               fr_stats_ ## _name ## _t stats = {},    \
               }
-       
 
-/** Macro used when referencing a linking structure
- *
- * .def = FR_STATS_LINK_NAME(radius_auth_serv),
- */
-#define FR_STATS_LINK_NAME(_name) fr_stats_link_ ## _name ## _t
 
 /** Macro used to increment one field in the statistics structure
  *
+ *  Code which needs to update some statistics can use this macro to increment a variable which contains
+ *  stats for the local module / etc.
  */
-#define FR_STATS_INC(_var, _field) _var->stats._field++
+#define FR_STATS_INC(_var, _field) ((_var)->stats.(_field))++
 
-typedef struct {
-       fr_stats_instance_t const *inst;
-       unsigned int              current;
-} fr_stats_iter_t;
+/** Macro used to reference a field in the statistics structure
+ *
+ *  Code which needs to mangle a field of the statistics can use this macro to get the correct field name.
+ */
+#define FR_STATS_FIELD(_var, _field) (_var)->stats.(_field)
 
 int    fr_stats_to_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_stats_instance_t const *in) CC_HINT(nonnull);
 int    fr_stats_from_pairs(TALLOC_CTX *ctx, fr_stats_instance_t *out, fr_pair_list_t *in) CC_HINT(nonnull);
diff --git a/src/stats/all.mk b/src/stats/all.mk
new file mode 100644 (file)
index 0000000..c5ee915
--- /dev/null
@@ -0,0 +1,56 @@
+RADICT := $(BUILD_DIR)/make/jlibtool --silent --mode=execute $(BUILD_DIR)/bin/local/radict -D share/dictionary
+
+#
+#  $(eval $(call DICT_STATS,radius,auth_serv,mib-2.radiusAuthServ,1.3.6.1.2.1.67.1.1.1.1))
+#
+#  We don't want the outputs to be updated if the build fails.  So we use an intermediate filename.
+#  And we use ".cache", because that's ignored by the top level ".gitignore"
+#
+define DICT_STATS
+
+#
+#  Re-build the files if the dictionary changes.
+#
+#  Ensure that the protocol library is built before the statistics
+#  library, as radict needs it.
+#
+#  Ensure that radict is built before the statistics library, as we
+#  run radict to generate the output files.
+#
+
+#
+#  Define the structures and declare the extern variables
+#
+src/stats/${1}/${2}_stats.h: share/dictionary/${1}/dictionary.stats $(BUILD_DIR)/lib/local/libfreeradius-${1}.la $(BUILD_DIR)/bin/local/radict$(E)
+       @echo STATS ${1} ${2} - structs
+       ${Q}$(RADICT) -F stats.h -M ${4} -p ${1} ${3} > $$@.cache && mv $$@.cache $$@
+
+#
+#  define the "attr_foo" definitions for the dictionary autoload to populate
+#
+src/stats/${1}/${2}_da_def.c: share/dictionary/${1}/dictionary.stats $(BUILD_DIR)/lib/local/libfreeradius-${1}.la $(BUILD_DIR)/bin/local/radict$(E)
+       @echo STATS ${1} ${2} - define variables
+       ${Q}$(RADICT) -F da_def -M ${4} -p ${1} ${3} > $$@.cache && mv $$@.cache $$@
+
+#
+#  define the autoload structures which point to the "attr_foo" defintions
+#
+src/stats/${1}/${2}_da_autoload.c: share/dictionary/${1}/dictionary.stats $(BUILD_DIR)/lib/local/libfreeradius-${1}.la $(BUILD_DIR)/bin/local/radict$(E)
+       @echo STATS ${1} ${2} - autoload
+       ${Q}$(RADICT) -F attr_autoload -M ${4} -p ${1} ${3} > $$@.cache && mv $$@.cache $$@
+
+#
+#  define the linking structure between the statistics structure and the DAs.
+#
+src/stats/${1}/${2}_stats.c: share/dictionary/${1}/dictionary.stats $(BUILD_DIR)/lib/local/libfreeradius-${1}.la $(BUILD_DIR)/bin/local/radict$(E)
+       @echo STATS ${1} ${2} - link
+       ${Q}$(RADICT) -F stats_link -M ${4} -p ${1} ${3} > $$@.cache && mv $$@.cache $$@
+
+#
+#  The output OBJ has to be rebuilt if any of the input files have changed.
+#
+$(BUILD_DIR)/objs/src/stats/${1}/base.${OBJ_EXT}: src/stats/${1}/${2}_stats.h src/stats/${1}/${2}_da_def.c src/stats/${1}/${2}_da_autoload.c src/stats/${1}/${2}_stats.c
+
+endef
+
+SUBMAKEFILES := $(wildcard ${top_srcdir}/src/stats/*/all.mk)
diff --git a/src/stats/radius/all.mk b/src/stats/radius/all.mk
new file mode 100644 (file)
index 0000000..9ffa551
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Makefile
+#
+# Version:      $Id$
+#
+TARGET         := libfreeradius-radius-stats$(L)
+
+SOURCES                := base.c
+
+TGT_PREREQS    := libfreeradius-util$(L) libfreeradius-radius$(L)
+
+TGT_POSTCLEAN  := $(wildcard src/stats/radius/*.cache)
+
+$(eval $(call DICT_STATS,radius,auth_serv,mib-2.radiusAuthServ,1.3.6.1.2.1.67.1.1.1.1))
diff --git a/src/stats/radius/auth_serv_da_autoload.c b/src/stats/radius/auth_serv_da_autoload.c
new file mode 100644 (file)
index 0000000..b3fa91e
--- /dev/null
@@ -0,0 +1,17 @@
+{ .out = &attr_auth_serv, .name = "mib-2.radiusAuthServ", .type = FR_TYPE_TLV, .dict = &dict_radius },
+{ .out = &attr_auth_serv_ident, .name = ".radiusAuthServIdent", .type = FR_TYPE_STRING, .dict = &dict_radius },
+{ .out = &attr_auth_serv_up_time, .name = ".radiusAuthServUpTime", .type = FR_TYPE_TIME_DELTA, .dict = &dict_radius },
+{ .out = &attr_auth_serv_reset_time, .name = ".radiusAuthServResetTime", .type = FR_TYPE_TIME_DELTA, .dict = &dict_radius },
+{ .out = &attr_auth_serv_config_reset, .name = ".radiusAuthServConfigReset", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_access_requests, .name = ".radiusAuthServTotalAccessRequests", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_invalid_requests, .name = ".radiusAuthServTotalInvalidRequests", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_dup_access_requests, .name = ".radiusAuthServTotalDupAccessRequests", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_access_accepts, .name = ".radiusAuthServTotalAccessAccepts", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_access_rejects, .name = ".radiusAuthServTotalAccessRejects", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_access_challenges, .name = ".radiusAuthServTotalAccessChallenges", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_malformed_access_requests, .name = ".radiusAuthServTotalMalformedAccessRequests", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_bad_authenticators, .name = ".radiusAuthServTotalBadAuthenticators", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_packets_dropped, .name = ".radiusAuthServTotalPacketsDropped", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+{ .out = &attr_auth_serv_total_unknown_types, .name = ".radiusAuthServTotalUnknownTypes", .type = FR_TYPE_UINT32, .dict = &dict_radius },
+
+
diff --git a/src/stats/radius/auth_serv_da_def.c b/src/stats/radius/auth_serv_da_def.c
new file mode 100644 (file)
index 0000000..c5d82dc
--- /dev/null
@@ -0,0 +1,17 @@
+static fr_dict_attr_t const *attr_auth_serv;
+static fr_dict_attr_t const *attr_auth_serv_ident;
+static fr_dict_attr_t const *attr_auth_serv_up_time;
+static fr_dict_attr_t const *attr_auth_serv_reset_time;
+static fr_dict_attr_t const *attr_auth_serv_config_reset;
+static fr_dict_attr_t const *attr_auth_serv_total_access_requests;
+static fr_dict_attr_t const *attr_auth_serv_total_invalid_requests;
+static fr_dict_attr_t const *attr_auth_serv_total_dup_access_requests;
+static fr_dict_attr_t const *attr_auth_serv_total_access_accepts;
+static fr_dict_attr_t const *attr_auth_serv_total_access_rejects;
+static fr_dict_attr_t const *attr_auth_serv_total_access_challenges;
+static fr_dict_attr_t const *attr_auth_serv_total_malformed_access_requests;
+static fr_dict_attr_t const *attr_auth_serv_total_bad_authenticators;
+static fr_dict_attr_t const *attr_auth_serv_total_packets_dropped;
+static fr_dict_attr_t const *attr_auth_serv_total_unknown_types;
+
+
diff --git a/src/stats/radius/auth_serv_stats.c b/src/stats/radius/auth_serv_stats.c
new file mode 100644 (file)
index 0000000..f124166
--- /dev/null
@@ -0,0 +1,94 @@
+fr_stats_link_t const fr_stats_link_radius_auth_serv = {
+       .name = "fr_stats_radius_auth_serv_t",
+       .root_p = &attr_auth_serv,
+       .mib = "1.3.6.1.2.1.67.1.1.1.1",
+       .size = sizeof(fr_stats_radius_auth_serv_t),
+       .num_elements = 14,
+       .entry = {
+               {
+                       .da_p = &attr_auth_serv_ident,
+                       .type = FR_TYPE_STRING,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, ident),
+                       .size = 0,
+               },
+               {
+                       .da_p = &attr_auth_serv_up_time,
+                       .type = FR_TYPE_TIME_DELTA,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, up_time),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_reset_time,
+                       .type = FR_TYPE_TIME_DELTA,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, reset_time),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_config_reset,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, config_reset),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_access_requests,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_access_requests),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_invalid_requests,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_invalid_requests),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_dup_access_requests,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_dup_access_requests),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_access_accepts,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_access_accepts),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_access_rejects,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_access_rejects),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_access_challenges,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_access_challenges),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_malformed_access_requests,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_malformed_access_requests),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_bad_authenticators,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_bad_authenticators),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_packets_dropped,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_packets_dropped),
+                       .size = 4,
+               },
+               {
+                       .da_p = &attr_auth_serv_total_unknown_types,
+                       .type = FR_TYPE_UINT32,
+                       .offset = offsetof(fr_stats_radius_auth_serv_t, total_unknown_types),
+                       .size = 4,
+               },
+       },
+};
+
diff --git a/src/stats/radius/auth_serv_stats.h b/src/stats/radius/auth_serv_stats.h
new file mode 100644 (file)
index 0000000..4005938
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *     radiusAuthServ
+ */
+typedef struct {
+       char    *ident;
+       fr_time_delta_t up_time;
+       fr_time_delta_t reset_time;
+       uint32_t        config_reset;
+       uint32_t        total_access_requests;
+       uint32_t        total_invalid_requests;
+       uint32_t        total_dup_access_requests;
+       uint32_t        total_access_accepts;
+       uint32_t        total_access_rejects;
+       uint32_t        total_access_challenges;
+       uint32_t        total_malformed_access_requests;
+       uint32_t        total_bad_authenticators;
+       uint32_t        total_packets_dropped;
+       uint32_t        total_unknown_types;
+} fr_stats_radius_auth_serv_t;
+
+FR_STATS_TYPEDEF(radius_auth_serv);
+
+extern fr_stats_link_t const fr_stats_link_radius_auth_serv;
+
diff --git a/src/stats/radius/base.c b/src/stats/radius/base.c
new file mode 100644 (file)
index 0000000..ee74183
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library 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
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/radius/stats/stats.c
+ * @brief Functions for RADIUS statistics
+ *
+ * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/util/dict.h>
+#include "stats.h"
+
+static fr_dict_t const *dict_radius;
+
+/*
+ *     static fr_dict_attr_t const *attr_foo;
+ */
+#include "auth_serv_da_def.c"
+
+extern fr_dict_autoload_t libfreeradius_radius_stats_dict[];
+fr_dict_autoload_t libfreeradius_radius_stats_dict[] = {
+       { .out = &dict_radius, .proto = "radius" },
+
+       DICT_AUTOLOAD_TERMINATOR
+};
+
+extern fr_dict_attr_autoload_t libfreeradius_radius_stats_dict_attr[];
+fr_dict_attr_autoload_t libfreeradius_radius_stats_dict_attr[] = {
+
+#include "auth_serv_da_autoload.c"
+
+       DICT_AUTOLOAD_TERMINATOR
+};
+
+/*
+ *     Clang accepts this DIAG, but complains about the code unless we have it.
+ *
+ *     GCC doesn't accept this DIAG, but doesn't complain about the code.
+ */
+#if defined(__clang__)
+DIAG_OFF(gnu-flexible-array-initializer)
+#endif
+
+/*
+ *     Define the fr_stats_link_t for the statistics data structures.
+ */
+#include "auth_serv_stats.c"
diff --git a/src/stats/radius/stats.h b/src/stats/radius/stats.h
new file mode 100644 (file)
index 0000000..9cb47ae
--- /dev/null
@@ -0,0 +1,38 @@
+#pragma once
+/*
+ *  This program 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 protocols/radius/stats/stats.h
+ * @brief Structures and prototypes for RADIUS statistics
+ *
+ * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
+ */
+#include <freeradius-devel/util/stats.h>
+
+/*
+ *     The included file defines:
+ *
+ *     fr_stats_radius_auth_serv_t - the base statistics structure
+ *
+ *     fr_stats_link_radius_auth_serv - the structure linking the base stats structure to the dictionary
+ *     attributes.
+ *
+ *     fr_stats_radius_auth_serv_t
+ */
+#include "auth_serv_stats.h"