]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add sample configuration, and update docs to match
authorAlan T. DeKok <aland@freeradius.org>
Fri, 16 Jan 2026 13:49:43 +0000 (08:49 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 16 Jan 2026 19:56:36 +0000 (14:56 -0500)
doc/antora/modules/reference/nav.adoc
doc/antora/modules/reference/pages/raddb/mods-available/kv.adoc [new file with mode: 0644]
raddb/mods-available/kv [new file with mode: 0644]
src/modules/rlm_kv/rlm_kv.c
src/tests/keywords/all.mk
src/tests/keywords/radius.conf

index 1c59aa3d5c93bf8c5876f5e00b7944851c97ee53..bb7a50fcb6a7bdda62484515f46f4ab1e97e6184 100644 (file)
 **** xref:raddb/mods-available/files.adoc[Files]
 ***** xref:raddb/mods-config/files/users.adoc[File Format]
 **** xref:raddb/mods-available/ftp.adoc[FTP]
+**** xref:raddb/mods-available/kv.adoc[KV]
 **** xref:raddb/mods-available/ldap.adoc[LDAP]
 **** xref:raddb/mods-available/opendirectory.adoc[OpenDirectory]
 **** xref:raddb/mods-available/passwd.adoc[Passwd]
diff --git a/doc/antora/modules/reference/pages/raddb/mods-available/kv.adoc b/doc/antora/modules/reference/pages/raddb/mods-available/kv.adoc
new file mode 100644 (file)
index 0000000..b481dfa
--- /dev/null
@@ -0,0 +1,104 @@
+
+
+
+
+= KV Module
+
+In-memory key-value store.
+
+This module allows policies to associate "values" with "keys".  The
+data is store _only_ in memory, and is not persisted to disk, or to
+a database.  As such, all data in the key-value store is lost when
+the server restarts.
+
+The module creates a _global_ key-value store.  That is, one which is
+shared across multiple threads.  This design allows a value to be set
+at any time, and in any thread; then examined by a later thread.
+
+The caveat to the global / cross-thread store is that all access to
+the key-value store are protected by a mutex.  The mutex serializes
+access to the key-value store.  Which means that using this module
+can effectively force the server to become single-threaded, and
+therefore destroy all performance.
+
+We therefore recommend that this module be used only when no
+alternative exists.
+
+If you need to store multiple different types of data in a
+key-value store, then you should create multiple instances of the
+module.  Then, use one instance per type of data, or per use-case.
+This approach causes the module to use multiple mutexes (one for
+each instance), which reduces mutex contention and can improve
+performance.
+
+## Differences from the Cache module
+
+The `cache` module also provides an in-memory key-value store.
+However, the cache module stores _attributes_, and _lists of
+attributes_.
+
+In contrast, the `kv` module stores _values_.  e.g. ipv4addr,
+uint32, etc.  The use-case for the `kv` module is to store a small
+number of simple values that can be shared across multiple threads.
+
+## Functions
+
+The `kv` module exports a number of functions to interact with the
+key-value store.
+
+### %kv.write(key, value)
+
+Writes _value_ at _key_.  Any pre-existing value for _key_ is
+discarded.
+
+This function returns nothing.
+
+### %kv.read(key)
+
+Reads a value at _key_.  If the value exists, it is returned.
+Otherwise, nothing is returned.
+
+### %kv.delete(key)
+
+Deletes the value found at _key_.  If a value was deleted, it is
+returned.  Otherwise, nothing is returned.
+
+## Configuration Settings
+
+
+key_type:: Data type of the key
+
+Should be 'string', 'octets', 'ipv4addr', etc.
+
+The module will automatically choose a data structure based
+on the data type. It will be a hash table, rbtree or
+patricia trie store depending on the data type of the key.
+
+
+
+max_entries:: Maximum entries allowed.
+
+The value must be larger than zero.
+
+Keys are ordered by "last access" time.  Keys which have
+not been used for a while are not automatically removed.
+Keys which are being continuously used will stick around
+forever.
+
+Keys are removed automatically when the "max_entries" limit
+is reached.  When that limit is reached, the olded used (or
+unused) key is deleted every time a new key is inserted.
+
+
+
+== Default Configuration
+
+```
+kv {
+       key_type = string
+#      max_entries = 0
+}
+```
+
+// Copyright (C) 2026 Network RADIUS SAS.  Licenced under CC-by-NC 4.0.
+// This documentation was developed by Network RADIUS SAS.
diff --git a/raddb/mods-available/kv b/raddb/mods-available/kv
new file mode 100644 (file)
index 0000000..2b47526
--- /dev/null
@@ -0,0 +1,103 @@
+#  -*- text -*-
+#
+#
+#  $Id$
+
+#######################################################################
+#
+#  = KV Module
+#
+#  In-memory key-value store.
+#
+#  This module allows policies to associate "values" with "keys".  The
+#  data is store _only_ in memory, and is not persisted to disk, or to
+#  a database.  As such, all data in the key-value store is lost when
+#  the server restarts.
+#
+#  The module creates a _global_ key-value store.  That is, one which is
+#  shared across multiple threads.  This design allows a value to be set
+#  at any time, and in any thread; then examined by a later thread.
+#
+#  The caveat to the global / cross-thread store is that all access to
+#  the key-value store are protected by a mutex.  The mutex serializes
+#  access to the key-value store.  Which means that using this module
+#  can effectively force the server to become single-threaded, and
+#  therefore destroy all performance.
+#
+#  We therefore recommend that this module be used only when no
+#  alternative exists.
+#
+#  If you need to store multiple different types of data in a
+#  key-value store, then you should create multiple instances of the
+#  module.  Then, use one instance per type of data, or per use-case.
+#  This approach causes the module to use multiple mutexes (one for
+#  each instance), which reduces mutex contention and can improve
+#  performance.
+#
+#  ## Differences from the Cache module
+#
+#  The `cache` module also provides an in-memory key-value store.
+#  However, the cache module stores _attributes_, and _lists of
+#  attributes_.
+#
+#  In contrast, the `kv` module stores _values_.  e.g. ipv4addr,
+#  uint32, etc.  The use-case for the `kv` module is to store a small
+#  number of simple values that can be shared across multiple threads.
+#
+#  ## Functions
+#
+#  The `kv` module exports a number of functions to interact with the
+#  key-value store.
+#
+#  ### %kv.write(key, value)
+#
+#  Writes _value_ at _key_.  Any pre-existing value for _key_ is
+#  discarded.
+#
+#  This function returns nothing.
+#
+#  ### %kv.read(key)
+#
+#  Reads a value at _key_.  If the value exists, it is returned.
+#  Otherwise, nothing is returned.
+#
+#  ### %kv.delete(key)
+#
+#  Deletes the value found at _key_.  If a value was deleted, it is
+#  returned.  Otherwise, nothing is returned.
+#
+#  ## Configuration Settings
+#
+kv {
+       #
+       #  key_type:: Data type of the key
+       #
+       #  Should be 'string', 'octets', 'ipv4addr', etc.
+       #
+       #  The module will automatically choose a data structure based
+       #  on the data type. It will be a hash table, rbtree or
+       #  patricia trie store depending on the data type of the key.
+       #
+       #  Any `key` which is passed to the `kv` functions
+       #  (`%kv.write()`, `%kv.read()`, or `%kv.delete()`) must be
+       #  the same data type as is given in `key_type`.
+       #
+       key_type = string
+
+       #
+       #  max_entries:: Maximum entries allowed.
+       #
+       #  The value must be larger than zero.
+       #
+       #  Keys are ordered by "last access" time.  Keys which have
+       #  not been used for a while are not automatically removed.
+       #  Keys which are being continuously used will stick around
+       #  forever.
+       #
+       #  Keys are removed automatically when the "max_entries" limit
+       #  is reached.  When that limit is reached, the olded used (or
+       #  unused) key is deleted every time a new key is inserted.
+       #
+#      max_entries = 1024
+
+}
index 1ff8c9b62b39e055b54b8789359cc01c76cd0995..ec118522b79f259f979d2de56e8939b3df5a5db3 100644 (file)
@@ -70,6 +70,7 @@ typedef struct {
        uint32_t                max_entries;
        fr_htrie_type_t         htype;
        char const              *key_type;      //!< data type of the key
+       fr_type_t               type;
        rlm_kv_mutable_t        *mutable;
 } rlm_kv_t;
 
@@ -110,6 +111,12 @@ static xlat_action_t kv_write_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *
 
        XLAT_ARGS(args, &key, &value);
 
+       if (key->type != in->type) {
+               RWDEBUG("Invalid key data type %s - expected %s",
+                       fr_type_to_str(key->type), fr_type_to_str(in->type));
+               return XLAT_ACTION_FAIL;
+       }
+
        MEM(data = talloc_zero(NULL, rlm_kv_data_t));
        if (fr_value_box_copy(data, &data->key, key) < 0) {
                talloc_free(data);
@@ -175,6 +182,12 @@ static xlat_action_t kv_read_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
 
        XLAT_ARGS(args, &key);
 
+       if (key->type != in->type) {
+               RWDEBUG("Invalid key data type %s - expected %s",
+                       fr_type_to_str(key->type), fr_type_to_str(in->type));
+               return XLAT_ACTION_FAIL;
+       }
+
        pthread_mutex_lock(&inst->mutex);
        data = fr_htrie_find(inst->tree, key);
        if (!data) {
@@ -217,6 +230,12 @@ static xlat_action_t kv_delete_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
 
        XLAT_ARGS(args, &key);
 
+       if (key->type != in->type) {
+               RWDEBUG("Invalid key data type %s - expected %s",
+                       fr_type_to_str(key->type), fr_type_to_str(in->type));
+               return XLAT_ACTION_FAIL;
+       }
+
        /*
         *      @todo - if the key is a string, allow wildcards in the
         *      deletion path.  In which case we need to be able to
@@ -253,18 +272,17 @@ static int mod_mutable_free(rlm_kv_mutable_t *mutable)
 static int mod_instantiate(module_inst_ctx_t const *mctx)
 {
        rlm_kv_t        *inst = talloc_get_type_abort(mctx->mi->data, rlm_kv_t);
-       fr_type_t       type;
        
        /*
         *      Get the data type, and convert it to an htrie type.
         */
-       type = fr_type_from_str(inst->key_type);
-       if (type == FR_TYPE_NULL) {
+       inst->type = fr_type_from_str(inst->key_type);
+       if (inst->type == FR_TYPE_NULL) {
                cf_log_err(mctx->mi->conf, "Unknown data type '%s'", inst->key_type);
                return -1;
        }
 
-       inst->htype = fr_htrie_hint(type);
+       inst->htype = fr_htrie_hint(inst->type);
        if (inst->htype == FR_HTRIE_INVALID) {
                cf_log_err(mctx->mi->conf, "Invalid data type '%s' for KV store", inst->key_type);
                return -1;
index e49b598f6a817ff344682bbc150a82edf624d5ea..592b9ed25acf5cf36f246b77159f05546c4aa43b 100644 (file)
@@ -57,6 +57,10 @@ ifeq "${1}" "mschap"
 $(OUTPUT)/${1}: $(BUILD_DIR)/lib/local/rlm_mschap.la $(BUILD_DIR)/lib/rlm_mschap.la
 endif
 
+ifeq "${1}" "kv"
+$(OUTPUT)/${1}: $(BUILD_DIR)/lib/local/rlm_kv.la $(BUILD_DIR)/lib/rlm_kv.la
+endif
+
 ifeq "${1}" "xlat-dhcpv4"
 $(OUTPUT)/${1}: $(BUILD_DIR)/lib/local/libfreeradius-dhcpv4.la $(BUILD_DIR)/lib/libfreeradius-dhcpv4.la
 endif
index 44444764d6508f8e740ed2b8e8ac34fe85ae855e..34b2a354492097bfec3ba7d9f30840ccc1174e48 100644 (file)
@@ -123,7 +123,7 @@ modules {
 
        # Key-value store
        kv {
-
+               key_type = 'string'
        }
 }