2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Cache values and merge them back into future requests.
21 * @copyright 2012-2014 The FreeRADIUS server project
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27 #include <freeradius-devel/modpriv.h>
28 #include <freeradius-devel/modcall.h>
29 #include <freeradius-devel/rad_assert.h>
31 #include "rlm_cache.h"
34 * A mapping of configuration file names to internal variables.
36 * Note that the string is dynamically allocated, so it MUST
37 * be freed. When the configuration file parse re-reads the string,
38 * it free's the old one, and strdup's the new one, placing the pointer
39 * to the strdup'd string into 'config.string'. This gets around
42 static const CONF_PARSER module_config[] = {
43 { "driver", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_cache_t, driver_name), "rlm_cache_rbtree" },
44 { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_cache_t, key), NULL },
45 { "ttl", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_cache_t, ttl), "500" },
46 { "max_entries", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_cache_t, max_entries), "0" },
48 /* Should be a type which matches time_t, @fixme before 2038 */
49 { "epoch", FR_CONF_OFFSET(PW_TYPE_SIGNED, rlm_cache_t, epoch), "0" },
50 { "add_stats", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_cache_t, stats), "no" },
52 { NULL, -1, 0, NULL, NULL } /* end the list */
55 static int cache_acquire(rlm_cache_handle_t **out, rlm_cache_t *inst, REQUEST *request)
57 if (!inst->module->acquire) return 0;
59 return inst->module->acquire(out, inst, request);
62 static void cache_release(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle)
64 if (!inst->module->release) return;
67 inst->module->release(inst, request, handle);
70 static int cache_reconnect(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle)
72 rad_assert(inst->module->reconnect);
74 return inst->module->reconnect(inst, request, handle);
77 /** Allocate a cache entry
79 * This is used so that drivers may use their own allocation functions
80 * to allocate structures larger than the normal rlm_cache_entry_t.
82 * If the driver doesn't specify a custom allocation function, the cache
83 * entry is talloced in the NULL ctx.
85 static rlm_cache_entry_t *cache_alloc(rlm_cache_t *inst, REQUEST *request)
87 if (inst->module->alloc) return inst->module->alloc(inst, request);
89 return talloc_zero(NULL, rlm_cache_entry_t);
92 /** Free memory associated with a cache entry
94 * This does not necessarily remove the entry from the cache, cache_expire
95 * should be used for that.
97 * This function should be called when an entry that is known to have been
98 * retrieved or inserted into a data store successfully, is no longer needed.
100 * Some drivers (like rlm_cache_rbtree) don't register a free function.
101 * This means that the cache entry never needs to be explicitly freed.
103 * @param c Cache entry to free.
104 * @param inst Module instance.
106 static void cache_free(rlm_cache_t *inst, rlm_cache_entry_t **c)
108 if (!*c || !inst->module->free) return;
110 inst->module->free(*c);
115 * Merge a cached entry into a REQUEST.
117 static void CC_HINT(nonnull) cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c)
121 vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
122 if (vp && (vp->vp_integer == 0)) {
123 RDEBUG2("Told not to merge entry into request");
127 RDEBUG2("Merging cache entry into request");
130 rdebug_pair_list(L_DBG_LVL_2, request, c->control, "&control:");
131 radius_pairmove(request, &request->config_items, paircopy(request, c->control), false);
134 if (c->packet && request->packet) {
135 rdebug_pair_list(L_DBG_LVL_2, request, c->packet, "&request:");
136 radius_pairmove(request, &request->packet->vps, paircopy(request->packet, c->packet), false);
139 if (c->reply && request->reply) {
140 rdebug_pair_list(L_DBG_LVL_2, request, c->reply, "&reply:");
141 radius_pairmove(request, &request->reply->vps, paircopy(request->reply, c->reply), false);
145 rad_assert(request->packet != NULL);
146 vp = pairfind(request->packet->vps, PW_CACHE_ENTRY_HITS, 0, TAG_ANY);
148 vp = paircreate(request->packet, PW_CACHE_ENTRY_HITS, 0);
149 rad_assert(vp != NULL);
150 pairadd(&request->packet->vps, vp);
152 vp->vp_integer = c->hits;
157 /** Find a cached entry.
159 * @return RLM_MODULE_OK on success, RLM_MODULE_FAIL on failure, RLM_MODULE_NOTFOUND if notfound.
161 static rlm_rcode_t cache_find(rlm_cache_entry_t **out, rlm_cache_t *inst, REQUEST *request,
162 rlm_cache_handle_t **handle, char const *key)
166 rlm_cache_entry_t *c;
171 ret = inst->module->find(&c, inst, request, handle, key);
173 case CACHE_RECONNECT:
174 RDEBUG("Reconnecting...");
175 if (cache_reconnect(inst, request, handle) == 0) continue;
176 return RLM_MODULE_FAIL;
182 RDEBUG("No cache entry found for \"%s\"", key);
183 return RLM_MODULE_NOTFOUND;
187 return RLM_MODULE_FAIL;
195 * Yes, but it expired, OR the "forget all" epoch has
196 * passed. Delete it, and pretend it doesn't exist.
198 if ((c->expires < request->timestamp) || (c->created < inst->epoch)) {
199 RDEBUG("Removing expired entry");
201 inst->module->expire(inst, request, handle, c);
202 cache_free(inst, &c);
203 return RLM_MODULE_NOTFOUND; /* Couldn't find a non-expired entry */
206 RDEBUG("Found entry for \"%s\"", key);
211 return RLM_MODULE_OK;
214 /** Expire a cache entry (removing it from the datastore)
217 static void cache_expire(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, rlm_cache_entry_t **c)
221 for (;;) switch (inst->module->expire(inst, request, handle, *c)) {
222 case CACHE_RECONNECT:
223 if (cache_reconnect(inst, request, handle) == 0) continue;
233 /** Create and insert a cache entry.
235 * @return RLM_MODULE_OK on success, RLM_MODULE_UPDATED if we merged the cache entry and RLM_MODULE_FAIL on failure.
237 static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle,
238 char const *key, int ttl)
240 VALUE_PAIR *vp, *to_cache;
241 vp_cursor_t src_list, cached_request, cached_reply, cached_control;
243 value_pair_map_t const *map;
246 rlm_cache_entry_t *c;
248 if ((inst->max_entries > 0) && inst->module->count &&
249 (inst->module->count(inst, request, handle) > inst->max_entries)) {
250 RWDEBUG("Cache is full: %d entries", inst->max_entries);
251 return RLM_MODULE_FAIL;
254 c = cache_alloc(inst, request);
255 if (!c) return RLM_MODULE_FAIL;
257 c->key = talloc_typed_strdup(c, key);
258 c->created = c->expires = request->timestamp;
261 RDEBUG("Creating new cache entry");
263 fr_cursor_init(&cached_request, &c->packet);
264 fr_cursor_init(&cached_reply, &c->reply);
265 fr_cursor_init(&cached_control, &c->control);
267 for (map = inst->maps; map != NULL; map = map->next) {
268 rad_assert(map->lhs && map->rhs);
270 if (map_to_vp(&to_cache, request, map, NULL) < 0) {
271 RDEBUG("Skipping %s", map->rhs->name);
276 * Reparent the VPs map_to_vp may return multiple.
278 for (vp = fr_cursor_init(&src_list, &to_cache);
280 vp = fr_cursor_next(&src_list)) {
284 * Prevent people from accidentally caching
285 * cache control attributes.
287 if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) {
289 case PW_CACHE_STATUS_ONLY:
290 case PW_CACHE_READ_ONLY:
292 case PW_CACHE_ENTRY_HITS:
293 RDEBUG2("Skipping %s", vp->da->name);
301 if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
303 (void) talloc_steal(c, vp);
307 switch (map->lhs->tmpl_list) {
308 case PAIR_LIST_REQUEST:
309 fr_cursor_insert(&cached_request, vp);
312 case PAIR_LIST_REPLY:
313 fr_cursor_insert(&cached_reply, vp);
316 case PAIR_LIST_CONTROL:
317 fr_cursor_insert(&cached_control, vp);
321 rad_assert(0); /* should have been caught by validation */
327 * Check to see if we need to merge the entry into the request
329 vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
330 if (vp && (vp->vp_integer == 0)) merge = false;
332 if (merge) cache_merge(inst, request, c);
337 ret = inst->module->insert(inst, request, handle, c);
339 case CACHE_RECONNECT:
340 if (cache_reconnect(inst, request, handle) == 0) continue;
341 return RLM_MODULE_FAIL;
344 RDEBUG("Commited entry, TTL %d seconds", ttl);
345 cache_free(inst, &c);
346 return RLM_MODULE_UPDATED;
349 talloc_free(c); /* Failed insertion - use talloc_free not the driver free */
350 return RLM_MODULE_FAIL;
355 /** Verify that a map in the cache section makes sense
358 static int cache_verify(value_pair_map_t *map, UNUSED void *ctx)
360 if (modcall_fixup_update(map, ctx) < 0) return -1;
362 if ((map->lhs->type != TMPL_TYPE_ATTR) &&
363 (map->lhs->type != TMPL_TYPE_LIST)) {
364 cf_log_err(map->ci, "Left operand must be an attribute ref or a list");
368 switch (map->rhs->type) {
370 cf_log_err(map->ci, "Exec values are not allowed");
373 * Only =, :=, += and -= operators are supported for
376 case TMPL_TYPE_LITERAL:
387 cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values",
388 fr_int2str(fr_tokens, map->op, "<INVALID>"),
389 fr_int2str(tmpl_types, map->rhs->type, "<INVALID>"));
400 * Do caching checks. Since we can update ANY VP list, we do
401 * exactly the same thing for all sections (autz / auth / etc.)
403 * If you want to cache something different in different sections,
404 * configure another cache module.
406 static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *request)
408 rlm_cache_entry_t *c;
409 rlm_cache_t *inst = instance;
411 rlm_cache_handle_t *handle;
420 if (radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL) < 0) return RLM_MODULE_FAIL;
422 if (buffer[0] == '\0') {
423 REDEBUG("Zero length key string is invalid");
424 return RLM_MODULE_INVALID;
427 if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL;
429 rcode = cache_find(&c, inst, request, &handle, buffer);
430 if (rcode == RLM_MODULE_FAIL) goto finish;
434 * If Cache-Status-Only == yes, only return whether we found a
437 vp = pairfind(request->config_items, PW_CACHE_STATUS_ONLY, 0, TAG_ANY);
438 if (vp && vp->vp_integer) {
439 rcode = c ? RLM_MODULE_OK:
445 * Update the expiry time based on the TTL.
446 * A TTL of 0 means "delete from the cache".
447 * A TTL < 0 means "delete from the cache and recreate the entry".
449 vp = pairfind(request->config_items, PW_CACHE_TTL, 0, TAG_ANY);
450 if (vp) ttl = vp->vp_signed;
453 * If there's no existing cache entry, go and create a new one.
456 if (ttl <= 0) ttl = inst->ttl;
461 * Expire the entry if requested to do so
465 cache_expire(inst, request, &handle, &c);
466 RDEBUG("Forcing expiry of entry");
467 rcode = RLM_MODULE_OK;
472 RDEBUG("Forcing expiry of existing entry");
473 cache_expire(inst, request, &handle, &c);
477 c->expires = request->timestamp + ttl;
478 RDEBUG("Setting TTL to %d", ttl);
482 * Cache entry was still valid, so we merge it into the request
483 * and return. No need to add a new entry.
485 cache_merge(inst, request, c);
486 rcode = RLM_MODULE_OK;
492 * If Cache-Read-Only == yes, then we only allow already cached entries
493 * to be merged into the request
495 vp = pairfind(request->config_items, PW_CACHE_READ_ONLY, 0, TAG_ANY);
496 if (vp && vp->vp_integer) {
497 rcode = RLM_MODULE_NOTFOUND;
502 * Create a new entry.
504 rcode = cache_insert(inst, request, &handle, buffer, ttl);
508 cache_free(inst, &c);
509 cache_release(inst, request, &handle);
512 * Clear control attributes
514 for (vp = fr_cursor_init(&cursor, &request->config_items);
516 vp = fr_cursor_next(&cursor)) {
517 if (vp->da->vendor == 0) switch (vp->da->attr) {
519 case PW_CACHE_STATUS_ONLY:
520 case PW_CACHE_READ_ONLY:
522 vp = fr_cursor_remove(&cursor);
532 * Allow single attribute values to be retrieved from the cache.
534 static ssize_t cache_xlat(void *instance, REQUEST *request,
535 char const *fmt, char *out, size_t freespace)
537 rlm_cache_entry_t *c;
538 rlm_cache_t *inst = instance;
539 rlm_cache_handle_t *handle;
541 VALUE_PAIR *vp, *vps;
543 DICT_ATTR const *target;
548 p += radius_list_name(&list, p, PAIR_LIST_REQUEST);
549 if (list == PAIR_LIST_UNKNOWN) {
550 REDEBUG("Unknown list qualifier in \"%s\"", fmt);
555 target = dict_attrbyname(p);
557 REDEBUG("Unknown attribute \"%s\"", p);
561 if (cache_acquire(&handle, inst, request) < 0) return -1;
563 switch (cache_find(&c, inst, request, handle, fmt)) {
564 case RLM_MODULE_OK: /* found */
567 case RLM_MODULE_NOTFOUND: /* not found */
576 case PAIR_LIST_REQUEST:
580 case PAIR_LIST_REPLY:
584 case PAIR_LIST_CONTROL:
588 case PAIR_LIST_UNKNOWN:
591 REDEBUG("Unsupported list \"%s\"", fr_int2str(pair_lists, list, "<UNKNOWN>"));
596 vp = pairfind(vps, target->attr, target->vendor, TAG_ANY);
598 RDEBUG("No instance of this attribute has been cached");
603 len = vp_prints_value(out, freespace, vp, 0);
604 if (is_truncated(len, freespace)) {
605 REDEBUG("Insufficient buffer space to write cached value");
611 cache_free(inst, &c);
612 cache_release(inst, request, &handle);
618 * Only free memory we allocated. The strings allocated via
619 * cf_section_parse() do not need to be freed.
621 static int mod_detach(void *instance)
623 rlm_cache_t *inst = instance;
625 talloc_free(inst->maps);
628 * We need to explicitly free all children, so if the driver
629 * parented any memory off the instance, their destructors
630 * run before we unload the bytecode for them.
632 * If we don't do this, we get a SEGV deep inside the talloc code
633 * when it tries to call a destructor that no longer exists.
635 talloc_free_children(inst);
638 * Decrements the reference count. The driver object won't be unloaded
639 * until all instances of rlm_cache that use it have been destroyed.
641 if (inst->handle) dlclose(inst->handle);
647 * Instantiate the module.
649 static int mod_instantiate(CONF_SECTION *conf, void *instance)
651 rlm_cache_t *inst = instance;
655 inst->xlat_name = cf_section_name2(conf);
656 if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
659 * Register the cache xlat function
661 xlat_register(inst->xlat_name, cache_xlat, NULL, inst);
664 * Sanity check for crazy people.
666 if (strncmp(inst->driver_name, "rlm_cache_", 8) != 0) {
667 ERROR("rlm_cache (%s): \"%s\" is NOT an Cache driver!", inst->xlat_name, inst->driver_name);
672 * Load the appropriate driver for our database
674 inst->handle = lt_dlopenext(inst->driver_name);
676 ERROR("rlm_cache (%s): Could not link driver %s: %s", inst->xlat_name, inst->driver_name, dlerror());
677 ERROR("rlm_cache (%s): Make sure it (and all its dependent libraries!) are in the search path"
678 "of your system's ld", inst->xlat_name);
682 inst->module = (cache_module_t *) dlsym(inst->handle, inst->driver_name);
684 ERROR("rlm_cache (%s): Could not link symbol %s: %s", inst->xlat_name, inst->driver_name, dlerror());
688 INFO("rlm_cahe (%s): Driver %s (module %s) loaded and linked", inst->xlat_name,
689 inst->driver_name, inst->module->name);
692 * Non optional fields and callbacks
694 rad_assert(inst->module->name);
695 rad_assert(inst->module->find);
696 rad_assert(inst->module->insert);
697 rad_assert(inst->module->expire);
699 if (inst->module->mod_instantiate) {
703 name = strrchr(inst->driver_name, '_');
705 name = inst->driver_name;
710 cs = cf_section_sub_find(conf, name);
712 cs = cf_section_alloc(conf, name, NULL);
717 * It's up to the driver to register a destructor (using talloc)
719 * Should write its instance data in inst->driver,
720 * and parent it off of inst.
722 if (inst->module->mod_instantiate(cs, inst) < 0) return -1;
725 rad_assert(inst->key && *inst->key);
727 if (inst->ttl == 0) {
728 cf_log_err_cs(conf, "Must set 'ttl' to non-zero");
732 if (inst->epoch != 0) {
733 cf_log_err_cs(conf, "Must not set 'epoch' in the configuration files");
738 * Make sure the users don't screw up too badly.
740 if (map_afrom_cs(&inst->maps, cf_section_sub_find(inst->cs, "update"),
741 PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, cache_verify, NULL, MAX_ATTRMAP) < 0) {
746 cf_log_err_cs(inst->cs, "Cache config must contain an update section, and "
747 "that section must not be empty");
755 * The module name should be the only globally exported symbol.
756 * That is, everything else should be 'static'.
758 * If the module needs to temporarily modify it's instantiation
759 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
760 * The server will then take care of ensuring that the module
761 * is single-threaded.
763 module_t rlm_cache = {
769 mod_instantiate, /* instantiation */
770 mod_detach, /* detach */
772 NULL, /* authentication */
773 mod_cache_it, /* authorization */
774 mod_cache_it, /* preaccounting */
775 mod_cache_it, /* accounting */
776 NULL, /* checksimul */
777 mod_cache_it, /* pre-proxy */
778 mod_cache_it, /* post-proxy */
779 mod_cache_it, /* post-auth */