**** 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]
--- /dev/null
+
+
+
+
+= 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.
--- /dev/null
+# -*- 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
+
+}
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;
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);
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) {
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
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;
$(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
# Key-value store
kv {
-
+ key_type = 'string'
}
}