47RCSID(
"$Id: 011ad1d65b96835e85061cc0fef30d68ee2995b8 $")
 
   49#include <freeradius-devel/server/request.h> 
   50#include <freeradius-devel/server/request_data.h> 
   51#include <freeradius-devel/server/state.h> 
   53#include <freeradius-devel/io/listen.h> 
   55#include <freeradius-devel/util/debug.h> 
   56#include <freeradius-devel/util/md5.h> 
   57#include <freeradius-devel/util/rand.h> 
   99                uint8_t         state[
sizeof(
struct state_comp)];       
 
 
 
  164#define PTHREAD_MUTEX_LOCK if (state->thread_safe) pthread_mutex_lock 
  165#define PTHREAD_MUTEX_UNLOCK if (state->thread_safe) pthread_mutex_unlock 
  177        ret = memcmp(a->state, b->state, 
sizeof(a->state));
 
 
  190        DEBUG4(
"Freeing state tree %p", state);
 
  193                DEBUG4(
"Freeing state entry %p (%"PRIu64
")", entry, entry->
id);
 
 
  227        if (!state) 
return 0;
 
  241        if (thread_safe && (pthread_mutex_init(&state->
mutex, NULL) != 0)) {
 
 
  272static inline CC_HINT(always_inline)
 
  283        DEBUG4(
"State ID %" PRIu64 
" unlinked", entry->id);
 
 
  291#ifdef WITH_VERIFY_PTR 
  317        if (entry->
ctx) TALLOC_FREE(entry->
ctx);
 
  319        DEBUG4(
"State ID %" PRIu64 
" freed", entry->
id);
 
 
  339        uint8_t                 old_state[
sizeof(old->state)];
 
  341        uint64_t                timed_out = 0;
 
  342        bool                    too_many = 
false;
 
  363                if (entry == old) 
continue;
 
  383                memset(old_state, 0, 
sizeof(old_state));
 
  385                old_tries = old->
tries;
 
  386                memcpy(old_state, old->state, 
sizeof(old_state));
 
  391        if (timed_out > 0) 
RWDEBUG(
"Cleaning up %"PRIu64
" timed out state entries", timed_out);
 
  414                RERROR(
"Failed inserting state entry - At maximum ongoing session limit (%u)",
 
  434                talloc_free_children(old);
 
  435                memset(old, 0, 
sizeof(*old));
 
  443        entry->
id = state->
id++;
 
  462                        WARN(
"State too long, will be truncated.  Expected <= %zd bytes, got %zu bytes",
 
  463                             sizeof(entry->state), 
vp->vp_length);
 
  469                if (
vp->vp_length == 
sizeof(entry->state)) {
 
  470                        memcpy(entry->state, 
vp->vp_octets, 
sizeof(entry->state));
 
  476                } 
else if (
vp->vp_length > 
sizeof(entry->state)) {
 
  484                        memcpy(entry->state, 
vp->vp_octets, 
vp->vp_length);
 
  485                        memset(&entry->state[
vp->vp_length], 0, 
sizeof(entry->state) - 
vp->vp_length);
 
  492                        memcpy(entry->state, old_state, 
sizeof(entry->state));
 
  493                        entry->
tries = old_tries + 1;
 
  499                        for (i = 0; i < 
sizeof(entry->state) / 
sizeof(x); i++) {
 
  501                                memcpy(entry->state + (i * 4), &x, 
sizeof(x));
 
  507                entry->state_comp.tx = entry->state_comp.
tries ^ entry->
tries;
 
  509                entry->state_comp.vx_0 = entry->state_comp.r_0 ^
 
  511                entry->state_comp.vx_1 = entry->state_comp.r_0 ^
 
  513                entry->state_comp.vx_2 = entry->state_comp.r_0 ^
 
  515                entry->state_comp.vx_3 = entry->state_comp.r_0 ^
 
  522                entry->state_comp.server_id = state->
server_id;
 
  529        DEBUG4(
"State ID %" PRIu64 
" created, value 0x%pH, expires %pV",
 
  544                RERROR(
"Failed inserting state entry - Insertion into state tree failed");
 
 
  569        if (vb->vb_length == 
sizeof(my_entry.state)) {
 
  570                memcpy(my_entry.state, vb->vb_octets, 
sizeof(my_entry.state));
 
  576        } 
else if (vb->vb_length > 
sizeof(my_entry.state)) {
 
  577                fr_md5_calc(my_entry.state, vb->vb_octets, vb->vb_length);
 
  584                memcpy(my_entry.state, vb->vb_octets, vb->vb_length);
 
  585                memset(&my_entry.state[vb->vb_length], 0, 
sizeof(my_entry.state) - vb->vb_length);
 
  591        my_entry.state_comp.context_id ^= state->
context_id;
 
 
  639        RDEBUG3(
"%s - discarded", state->
da->name);
 
 
  669                RDEBUG3(
"No request.%s attribute, can't restore session-state", state->
da->name);
 
  670                if (request->seq_start == 0) request->seq_start = request->number;      
 
  678                RDEBUG2(
"No state entry matching request.%pP found", 
vp);
 
  685                RERROR(
"State entry has already been thawed by a request %"PRIu64, entry->
thawed->number);
 
  714                RDEBUG2(
"Restored session-state");
 
  718        RDEBUG3(
"%s - restored", state->
da->name);
 
  723        request->sequence = entry->
tries;
 
 
  749                RDEBUG2(
"Saving session-state");
 
  752#ifdef WITH_VERIFY_PTR 
  758                fr_pair_list_verify(__FILE__, __LINE__, request->session_state_ctx, &request->session_state_pairs);
 
  771                RERROR(
"Creating state entry failed");
 
  782        entry->
ctx = state_ctx;
 
 
  817                                "Child request must have request->parent set when storing state")) 
return;
 
  819        RDEBUG3(
"Storing subrequest state in request %s", child->parent->name);
 
 
  863                                "Child request must have request->parent set when restoring state")) 
return;
 
  868                RDEBUG3(
"No child state found in parent %s", child->parent->name);
 
  877                                "Child state entry already thawed by %s - %p",
 
  878                                child_entry->
thawed->name, child_entry->
thawed)) 
return;
 
  880        RDEBUG3(
"Restoring subrequest state from request %s", child->parent->name);
 
  887        child_entry->
ctx = NULL;                                
 
  888        child_entry->
thawed = child;
 
 
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
static int fr_dlist_move(fr_dlist_head_t *list_dst, fr_dlist_head_t *list_src)
Merge two lists, inserting the source at the tail of the destination.
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Head of a doubly linked list.
Entry in a doubly linked list.
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a fr_pair_list_t.
#define DEBUG_ENABLED
True if global debug level 1 messages are enabled.
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Perform a single digest operation on a single input buffer.
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
uint32_t fr_rand(void)
Return a 32-bit random number.
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
static bool fr_rb_node_inline_in_tree(fr_rb_node_t const *node)
Check to see if an item is in a tree by examining its inline fr_rb_node_t.
The main red black tree structure.
fr_pair_t * request_state_replace(request_t *request, fr_pair_t *new_state)
Replace the session_state_ctx with a new one.
#define REQUEST_VERIFY(_x)
int request_data_by_persistance_count(request_t *request, bool persist)
Return how many request data entries exist of a given persistence.
int request_data_by_persistance(fr_dlist_head_t *out, request_t *request, bool persist)
Loop over all the request data, pulling out ones matching persist state.
void request_data_list_init(fr_dlist_head_t *data)
void request_data_restore(request_t *request, fr_dlist_head_t *in)
Add request data back to a request.
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
#define request_data_talloc_add(_request, _unique_ptr, _unique_int, _type, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
#define request_data_add(_request, _unique_ptr, _unique_int, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
uint64_t id
State number within state heap.
void fr_state_discard_child(request_t *parent, void const *unique_ptr, int unique_int)
Remove state from a child.
fr_state_tree_t * fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, bool thread_safe, uint32_t max_sessions, fr_time_delta_t timeout, uint8_t server_id, uint32_t context_id)
Initialise a new state tree.
uint64_t seq_start
Number of first request in this sequence.
fr_dlist_head_t data
Persistable request data, also parented by ctx.
void fr_state_discard(fr_state_tree_t *state, request_t *request)
Called when sending an Access-Accept/Access-Reject to discard state information.
uint32_t max_sessions
Maximum number of sessions we track.
#define PTHREAD_MUTEX_UNLOCK
#define PTHREAD_MUTEX_LOCK
int fr_request_to_state(fr_state_tree_t *state, request_t *request)
Transfer ownership of the state fr_pair_ts and ctx, back to a state entry.
uint8_t server_id
ID to use for load balancing.
fr_dict_attr_t const  * da
State attribute used.
int fr_state_to_request(fr_state_tree_t *state, request_t *request)
Copy a pointer to the head of the list of state fr_pair_ts (and their ctx) into the request.
fr_dlist_head_t data
Persistable request data, also parented by ctx.
fr_rb_node_t node
Entry in the state rbtree.
request_t * thawed
The request that thawed this entry.
fr_time_t cleanup
When this entry should be cleaned up.
static int _state_tree_free(fr_state_tree_t *state)
Free the state tree.
static int _state_entry_free(fr_state_entry_t *entry)
Frees any data associated with a state.
static int8_t state_entry_cmp(void const *one, void const *two)
Compare two fr_state_entry_t based on their state value i.e.
fr_pair_t * ctx
for all session specific data.
pthread_mutex_t mutex
Synchronisation mutex.
fr_pair_t * ctx
for all session specific data.
static void state_entry_unlink(fr_state_tree_t *state, fr_state_entry_t *entry)
Unlink an entry and remove if from the tree.
fr_rb_tree_t * tree
rbtree used to lookup state value.
uint64_t fr_state_entries_tracked(fr_state_tree_t *state)
Return number of entries we're currently tracking.
fr_dlist_head_t to_expire
Linked list of entries to free.
uint64_t fr_state_entries_created(fr_state_tree_t *state)
Return number of entries created.
static fr_state_entry_t * state_entry_find_and_unlink(fr_state_tree_t *state, fr_value_box_t const *vb)
Find the entry based on the State attribute and remove it from the state tree.
bool thread_safe
Whether we lock the tree whilst modifying it.
uint64_t fr_state_entries_timeout(fr_state_tree_t *state)
Return number of entries that timed out.
static int _free_child_data(state_child_entry_t *child_entry)
Free any subrequest request data if the dlist head is freed.
fr_time_delta_t timeout
How long to wait before cleaning up state entries.
void fr_state_restore_to_child(request_t *child, void const *unique_ptr, int unique_int)
Restore subrequest data from a parent request.
uint64_t timed_out
Number of states that were cleaned up due to timeout.
uint64_t id
Next ID to assign.
fr_state_tree_t * state_tree
Tree this entry belongs to.
request_t * thawed
The request that thawed this entry.
uint32_t used_sessions
How many sessions are currently in progress.
uint32_t context_id
ID binding state values to a context such as a virtual server.
void fr_state_store_in_parent(request_t *child, void const *unique_ptr, int unique_int)
Store subrequest's session-state list and persistable request data in its parent.
Holds a state value, and associated fr_pair_ts and data.
A child of a fr_state_entry_t.
#define fr_time()
Allow us to arbitrarily manipulate time.
static void state_entry_create(void)
Test functions that read from dbuffs.
Stores an attribute, a value and various bits of other data.
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
#define fr_time_add(_a, _b)
Add a time/time delta together.
#define fr_time_sub(_a, _b)
Subtract one time from another.
#define fr_time_lt(_a, _b)
A time delta, a difference in time measured in nanoseconds.
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
#define fr_box_time_delta(_val)
#define fr_box_octets(_val, _len)