The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_kv.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 *
16 */
17
18/**
19 * $Id: b095d598276c0cb09d804e6f6acd3c1ba1e7358f $
20 * @file rlm_kv.c
21 * @brief Provide an ephemeral, in-memory kv store.
22 *
23 * This module will use infinite memory if asked, as it doesn't track
24 * or expire old entries.
25 *
26 * This module uses cross-thread mutex locks, so if used a lot, it
27 * will cause all threads to synchronise, and will kill performance.
28 *
29 * @copyright 2026 Network RADIUS SAS (legal@networkradus.com)
30 */
31RCSID("$Id: b095d598276c0cb09d804e6f6acd3c1ba1e7358f $")
32
33#define LOG_PREFIX mctx->mi->name
34
35#include <freeradius-devel/server/base.h>
36#include <freeradius-devel/server/module.h>
37#include <freeradius-devel/server/module_rlm.h>
38#include <freeradius-devel/unlang/xlat_func.h>
39
40#include <freeradius-devel/util/htrie.h>
41
43
44FR_DLIST_TYPES(rlm_kv_list)
45FR_DLIST_TYPEDEFS(rlm_kv_list, rlm_kv_list_t, rlm_kv_entry_t)
46
47/** KV structure
48 *
49 * The "key" field MUST be first, so that we can do lookups by giving
50 * the htrie code a "fr_value_box_t*", which is the key.
51 */
53 fr_value_box_t key; //!< indexed key
54 fr_value_box_t value; //!< value to store
55 rlm_kv_entry_t entry; //!< for expiration
56};
57
59
60/** Mutable data structure which is shared across all threads.
61 *
62 */
63typedef struct {
64 fr_htrie_t *tree; //!< for kv stores.
65 rlm_kv_list_t list; //!< for expiring old entries
66 pthread_mutex_t mutex; //!< for thread locking.
68
69typedef struct {
72 char const *key_type; //!< data type of the key
75} rlm_kv_t;
76
77/*
78 * A mapping of configuration file names to internal variables.
79 */
80static const conf_parser_t module_config[] = {
81 { FR_CONF_OFFSET("key_type", rlm_kv_t, key_type), .dflt = "string" },
82
83 { FR_CONF_OFFSET("max_entries", rlm_kv_t, max_entries), .dflt = "8192" },
84
86};
87
89 { .required = true, .single = true, .type = FR_TYPE_VOID },
90 { .required = true, .single = true, .type = FR_TYPE_VOID },
92};
93
95 { .required = true, .single = true, .type = FR_TYPE_VOID },
97};
98
99/** Write an entry to the KV
100 *
101 * %kv.write(key, value)
102 */
104 xlat_ctx_t const *xctx,
105 UNUSED request_t *request, fr_value_box_list_t *args)
106{
107 rlm_kv_t const *in = talloc_get_type_abort(xctx->mctx->mi->data, rlm_kv_t);
108 rlm_kv_mutable_t *inst = talloc_get_type_abort(in->mutable, rlm_kv_mutable_t);
109 fr_value_box_t *key, *value;
110 rlm_kv_data_t *data, *old = NULL;
111
112 XLAT_ARGS(args, &key, &value);
113
114 if (key->type != in->type) {
115 RWDEBUG("Invalid key data type %s - expected %s",
116 fr_type_to_str(key->type), fr_type_to_str(in->type));
117 return XLAT_ACTION_FAIL;
118 }
119
120 MEM(data = talloc_zero(inst, rlm_kv_data_t));
121 if (fr_value_box_copy(data, &data->key, key) < 0) {
123 return XLAT_ACTION_FAIL;
124 }
125 if (fr_value_box_copy(data, &data->value, value) < 0) {
127 return XLAT_ACTION_FAIL;
128 }
129
130 pthread_mutex_lock(&inst->mutex);
131
132 if (fr_htrie_replace((void **) &old, inst->tree, data) < 0) {
133 pthread_mutex_unlock(&inst->mutex);
135 REDEBUG("Failed inserting (key=%pV, value=%pV)", key, value);
136 return XLAT_ACTION_DONE;
137 }
138
139 /*
140 * This is now the newest entry, as it has been recently written.
141 */
142 (void) rlm_kv_list_insert_head(&inst->list, data);
143
144 /*
145 * We've removed the old box from the tree. Unlink it.
146 * And since we removed an old box, we don't have to
147 * worry about the htrie being too full.
148 */
149 if (old) {
150 (void) rlm_kv_list_remove(&inst->list, old);
151 talloc_free(old);
152
153 /*
154 * We've inserted a brand new entry. If the list
155 * is full, delete an old entry.
156 */
157 } else if (rlm_kv_list_num_elements(&inst->list) >= in->max_entries) {
158 old = rlm_kv_list_pop_tail(&inst->list);
159 fr_assert(old != NULL);
160
161 talloc_free(old);
162 }
163
164 pthread_mutex_unlock(&inst->mutex);
165
166 return XLAT_ACTION_DONE;
167
168}
169
170/** Read an entry from the KV
171 *
172 * %kv.read(key)
173 */
175 xlat_ctx_t const *xctx,
176 UNUSED request_t *request, fr_value_box_list_t *args)
177{
178 rlm_kv_t const *in = talloc_get_type_abort(xctx->mctx->mi->data, rlm_kv_t);
179 rlm_kv_mutable_t *inst = talloc_get_type_abort(in->mutable, rlm_kv_mutable_t);
180 fr_value_box_t *key, *dst;
182
183 XLAT_ARGS(args, &key);
184
185 if (key->type != in->type) {
186 RWDEBUG("Invalid key data type %s - expected %s",
187 fr_type_to_str(key->type), fr_type_to_str(in->type));
188 return XLAT_ACTION_FAIL;
189 }
190
191 pthread_mutex_lock(&inst->mutex);
192 data = fr_htrie_find(inst->tree, key);
193 if (!data) {
194 pthread_mutex_unlock(&inst->mutex);
195 RDEBUG("Failed to find entry for key %pV", key);
196 return XLAT_ACTION_DONE;
197 }
198
199 MEM(dst = fr_value_box_acopy(ctx, &data->value));
200
201 /*
202 * This item was recently accessed. It's therefore now
203 * the newest entry.
204 */
205 (void) rlm_kv_list_remove(&inst->list, data);
206 (void) rlm_kv_list_insert_head(&inst->list, data);
207
208 pthread_mutex_unlock(&inst->mutex);
209
211
212 return XLAT_ACTION_DONE;
213
214}
215
216/** Delete an entry from the KV
217 *
218 * Returns the deleted entry, if one exists. Otherwise returns nothing.
219 *
220 * %kv.delete(key)
221 */
223 xlat_ctx_t const *xctx,
224 UNUSED request_t *request, fr_value_box_list_t *args)
225{
226 rlm_kv_t const *in = talloc_get_type_abort(xctx->mctx->mi->data, rlm_kv_t);
227 rlm_kv_mutable_t *inst = talloc_get_type_abort(in->mutable, rlm_kv_mutable_t);
228 fr_value_box_t *key, *dst;
230
231 XLAT_ARGS(args, &key);
232
233 if (key->type != in->type) {
234 RWDEBUG("Invalid key data type %s - expected %s",
235 fr_type_to_str(key->type), fr_type_to_str(in->type));
236 return XLAT_ACTION_FAIL;
237 }
238
239 /*
240 * @todo - if the key is a string, allow wildcards in the
241 * deletion path. In which case we need to be able to
242 * walk over the entire htrie. And the htrie API doesn't
243 * support that yet.
244 */
245
246 pthread_mutex_lock(&inst->mutex);
247 data = fr_htrie_remove(inst->tree, key);
248 if (!data) {
249 pthread_mutex_unlock(&inst->mutex);
250 return XLAT_ACTION_DONE;
251 }
252 (void) rlm_kv_list_remove(&inst->list, data);
253
254 pthread_mutex_unlock(&inst->mutex);
255
256 MEM(dst = fr_value_box_acopy(ctx, &data->value));
258
260
261 return XLAT_ACTION_DONE;
262
263}
264
266{
267 pthread_mutex_destroy(&mutable->mutex);
268 return 0;
269}
270
271static int mod_detach(module_detach_ctx_t const *mctx)
272{
273 rlm_kv_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_kv_t);
274
275 return talloc_free(inst->mutable);
276}
277
278static int mod_instantiate(module_inst_ctx_t const *mctx)
279{
280 rlm_kv_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_kv_t);
281
282 /*
283 * Get the data type, and convert it to an htrie type.
284 */
285 inst->type = fr_type_from_str(inst->key_type);
286 if (inst->type == FR_TYPE_NULL) {
287 cf_log_err(mctx->mi->conf, "Unknown data type '%s'", inst->key_type);
288 return -1;
289 }
290
291 inst->htype = fr_htrie_hint(inst->type);
292 if (inst->htype == FR_HTRIE_INVALID) {
293 cf_log_err(mctx->mi->conf, "Invalid data type '%s' for KV store", inst->key_type);
294 return -1;
295 }
296
297 MEM(inst->mutable = talloc_zero(NULL, rlm_kv_mutable_t));
298
299 inst->mutable->tree = fr_htrie_alloc(inst->mutable, inst->htype,
303 if (!inst->mutable->tree) return -1;
304
305 pthread_mutex_init(&inst->mutable->mutex, NULL);
306 talloc_set_destructor(inst->mutable, mod_mutable_free);
307
308 rlm_kv_list_init(&inst->mutable->list);
309
310 FR_INTEGER_BOUND_CHECK("max_entries", inst->max_entries, >=, 1024);
311 FR_INTEGER_BOUND_CHECK("max_entries", inst->max_entries, <, (1 << 22)); /* 4M should be enough */
312
313 return 0;
314}
315
316static int mod_bootstrap(module_inst_ctx_t const *mctx)
317{
318 xlat_t *xlat;
319
320 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "write", kv_write_xlat, FR_TYPE_NULL);
321 xlat_func_args_set(xlat, kv_write_xlat_args); /* path, value */
322
323 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "read", kv_read_xlat, FR_TYPE_VOID);
324 xlat_func_args_set(xlat, kv_read_xlat_args); /* path */
325
326 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "delete", kv_delete_xlat, FR_TYPE_VOID);
327 xlat_func_args_set(xlat, kv_read_xlat_args); /* path */
328
329 return 0;
330}
331
332extern module_rlm_t rlm_kv;
334 .common = {
335 .magic = MODULE_MAGIC_INIT,
336 .name = "kv",
337 .inst_size = sizeof(rlm_kv_t),
339 .bootstrap = mod_bootstrap,
340 .instantiate = mod_instantiate,
341 .detach = mod_detach,
342 },
343};
va_list args
Definition acutest.h:772
#define RCSID(id)
Definition build.h:487
#define UNUSED
Definition build.h:317
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:660
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:520
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:283
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:597
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:288
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:408
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:884
Test enumeration values.
Definition dict_test.h:92
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define FR_DLIST_TYPES(_name)
Define type specific wrapper structs for dlists.
Definition dlist.h:1111
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
Definition dlist.h:1134
#define FR_DLIST_TYPEDEFS(_name, _head, _entry)
Define friendly names for type specific dlist head and entry structures.
Definition dlist.h:1121
uint32_t(* fr_hash_t)(void const *)
Definition hash.h:36
talloc_free(hp)
fr_htrie_t * fr_htrie_alloc(TALLOC_CTX *ctx, fr_htrie_type_t type, fr_hash_t hash_data, fr_cmp_t cmp_data, fr_trie_key_t get_key, fr_free_t free_data)
An abstraction over our internal hashes, rb trees, and prefix tries.
Definition htrie.c:95
fr_htrie_type_t
Definition htrie.h:58
@ FR_HTRIE_INVALID
Definition htrie.h:59
static fr_htrie_type_t fr_htrie_hint(fr_type_t type)
Definition htrie.h:180
static int fr_htrie_replace(void **old, fr_htrie_t *ht, void const *data)
Replace data in a htrie, freeing previous data if free_data cb was passed to fr_htrie_alloc.
Definition htrie.h:131
static void * fr_htrie_find(fr_htrie_t *ht, void const *data)
Find data in a htrie.
Definition htrie.h:115
static void * fr_htrie_remove(fr_htrie_t *ht, void const *data)
Remove data from a htrie without freeing it.
Definition htrie.h:139
A hash/rb/prefix trie abstraction.
Definition htrie.h:91
#define RWDEBUG(fmt,...)
Definition log.h:361
fr_type_t
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VOID
User data.
unsigned int uint32_t
int8_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:38
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:247
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:172
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG(fmt,...)
Definition radclient.h:53
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_kv.c:271
static xlat_action_t kv_delete_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Delete an entry from the KV.
Definition rlm_kv.c:222
char const * key_type
data type of the key
Definition rlm_kv.c:72
static xlat_arg_parser_t const kv_write_xlat_args[]
Definition rlm_kv.c:88
fr_type_t type
Definition rlm_kv.c:73
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_kv.c:316
rlm_kv_entry_t entry
for expiration
Definition rlm_kv.c:55
rlm_kv_list_t list
for expiring old entries
Definition rlm_kv.c:65
static int mod_mutable_free(rlm_kv_mutable_t *mutable)
Definition rlm_kv.c:265
module_rlm_t rlm_kv
Definition rlm_kv.c:333
static xlat_arg_parser_t const kv_read_xlat_args[]
Definition rlm_kv.c:94
fr_htrie_t * tree
for kv stores.
Definition rlm_kv.c:64
fr_htrie_type_t htype
Definition rlm_kv.c:71
pthread_mutex_t mutex
for thread locking.
Definition rlm_kv.c:66
fr_value_box_t key
indexed key
Definition rlm_kv.c:53
static xlat_action_t kv_read_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Read an entry from the KV.
Definition rlm_kv.c:174
fr_value_box_t value
value to store
Definition rlm_kv.c:54
static const conf_parser_t module_config[]
Definition rlm_kv.c:80
uint32_t max_entries
Definition rlm_kv.c:70
static xlat_action_t kv_write_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, UNUSED request_t *request, fr_value_box_list_t *args)
Write an entry to the KV.
Definition rlm_kv.c:103
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_kv.c:278
KV structure.
Definition rlm_kv.c:52
Mutable data structure which is shared across all threads.
Definition rlm_kv.c:63
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
void * boot
Data allocated during the boostrap phase.
Definition module.h:294
eap_aka_sim_process_conf_t * inst
eap_type_t type
The preferred EAP-Type of this instance of the EAP-SIM/AKA/AKA' state machine.
int(* fr_trie_key_t)(uint8_t **out, size_t *outlen, void const *data)
Definition trie.h:56
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
static fr_type_t fr_type_from_str(char const *type)
Return the constant value representing a type.
Definition types.h:465
uint32_t fr_value_box_hash(fr_value_box_t const *vb)
Hash the contents of a value box.
Definition value.c:6982
int8_t fr_value_box_cmp(fr_value_box_t const *a, fr_value_box_t const *b)
Compare two values.
Definition value.c:749
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4411
int fr_value_box_to_key(uint8_t **out, size_t *outlen, fr_value_box_t const *value)
Get a key from a value box.
Definition value.c:2516
static fr_slen_t data
Definition value.h:1334
static fr_value_box_t * fr_value_box_acopy(TALLOC_CTX *ctx, fr_value_box_t const *src)
Copy an existing box, allocating a new box to hold its contents.
Definition value.h:738
static size_t char ** out
Definition value.h:1024
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363