The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
track.c
Go to the documentation of this file.
1 /*
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 as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * 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  * $Id: 80a69d6923beb24d2eb02f2eee7f3813bd8cedf4 $
19  * @file rlm_radius/track.c
20  * @brief Tracking RADUS client packets
21  *
22  * @copyright 2017 Network RADIUS SAS
23  */
24 RCSID("$Id: 80a69d6923beb24d2eb02f2eee7f3813bd8cedf4 $")
25 
26 #include <freeradius-devel/server/base.h>
27 #include <freeradius-devel/util/rb.h>
28 #include <freeradius-devel/io/application.h>
29 #include <freeradius-devel/util/dlist.h>
30 #include <freeradius-devel/util/debug.h>
31 
32 #include "track.h"
33 #include "rlm_radius.h"
34 
35 /** Create an radius_track_t
36  *
37  * @param ctx the talloc ctx
38  * @return
39  * - NULL on error
40  * - radius_track_t on success
41  */
43 {
44  int i;
45  radius_track_t *tt;
46 
47  MEM(tt = talloc_zero(ctx, radius_track_t));
48 
50 
51  for (i = 0; i < 256; i++) {
52  tt->id[i].id = i;
53 #ifndef NDEBUG
54  tt->id[i].file = __FILE__;
55  tt->id[i].line = __LINE__;
56 #endif
57  fr_dlist_insert_tail(&tt->free_list, &tt->id[i]);
58  }
59 
60  tt->next_id = fr_rand() & 0xff;
61 
62  return tt;
63 }
64 
65 
66 /** Compare two radius_track_entry_t
67  *
68  */
69 static int8_t te_cmp(void const *one, void const *two)
70 {
71  radius_track_entry_t const *a = one;
72  radius_track_entry_t const *b = two;
73  int ret;
74 
75  ret = memcmp(a->vector, b->vector, sizeof(a->vector));
76  return CMP(ret, 0);
77 }
78 
79 /** Ensures the entry is released when the ctx passed to radius_track_entry_reserve is freed
80  *
81  * @param[in] te_p Entry to release.
82  * @return 0
83  */
85 {
87 
88  return 0;
89 }
90 
91 /** Allocate a tracking entry.
92  *
93  * @param[in] file The allocation was made in.
94  * @param[in] line The allocation was made on.
95  * @param[out] te_out Where the tracking entry should be written.
96  * If ctx is not-null, then this pointer must
97  * remain valid for the lifetime of the ctx.
98  * @param[in] ctx If not-null, the tracking entry release will
99  * be bound to the lifetime of the talloc chunk.
100  * @param[in] tt The radius_track_t tracking table.
101  * @param[in] request The request which will send the proxied packet.
102  * @param[in] code Of the outbound request.
103  * @param[in] uctx The context to associate with the request
104  * @return
105  * - 0 on success.
106  * - -1 on failure.
107  */
108 #ifndef NDEBUG
109 int _radius_track_entry_reserve(char const *file, int line,
110 #else
112 #endif
113  radius_track_entry_t **te_out,
114  TALLOC_CTX *ctx, radius_track_t *tt, request_t *request, uint8_t code, void *uctx)
115 {
117 
118  if (!fr_cond_assert_msg(!*te_out, "Expected tracking entry to be NULL")) return -1;
119 
120 retry:
121  te = fr_dlist_head(&tt->free_list);
122  if (te) {
123  fr_assert(te->request == NULL);
124 
125  /*
126  * Mark it as used, and remove it from the free list.
127  */
128  fr_dlist_remove(&tt->free_list, te);
129 
130  /*
131  * We've transitioned from "use it", to "oops,
132  * don't use it". Ensure that we only return IDs
133  * which are in the static array.
134  */
135  if (!tt->use_authenticator && (te != &tt->id[te->id])) {
136  talloc_free(te);
137  goto retry;
138  }
139 
140  goto done;
141  }
142 
143  /*
144  * There are no free entries, and we can't use the
145  * Request Authenticator. Oh well...
146  */
147  if (!tt->use_authenticator) {
148  fr_strerror_const("No free entries");
149  return -1;
150  }
151 
152  /*
153  * Get a new ID. It's value doesn't matter at this
154  * point.
155  */
156  tt->next_id++;
157  tt->next_id &= 0xff;
158 
159  /*
160  * If needed, allocate a subtree.
161  */
162  if (!tt->subtree[tt->next_id]) {
164  te_cmp, NULL));
165  }
166 
167  /*
168  * Allocate a new one, and insert it into the appropriate subtree.
169  */
170  te = talloc_zero(tt, radius_track_entry_t);
171  te->id = tt->next_id;
172 
173 done:
174  te->tt = tt;
175  te->request = request;
176  te->uctx = uctx;
177  te->code = code;
178 #ifndef NDEBUG
179  te->operation = te->tt->operation++;
180  te->file = file;
181  te->line = line;
182 #endif
183  if (ctx) {
184  te->binding = talloc_zero(ctx, radius_track_entry_t **);
185  talloc_set_destructor(te->binding, _radius_track_entry_release_on_free);
186  *(te->binding) = te_out;
187  }
188 
189  /*
190  * te->id is already allocated
191  */
192  tt->num_requests++;
193 
194  *te_out = te;
195 
196  return 0;
197 }
198 
199 /** Release a tracking entry
200  *
201  * @param[in] file Allocation was released in.
202  * @param[in] line Allocation was released on.
203  * @param[in,out] te_to_free The #radius_track_entry_t allocated via #radius_track_entry_reserve.
204  * @return
205  * - <0 on error
206  * - 0 on success
207  */
208 #ifndef NDEBUG
209 int _radius_track_entry_release(char const *file, int line,
210 #else
212 #endif
213  radius_track_entry_t **te_to_free)
214 {
215  radius_track_entry_t *te = *te_to_free;
216  radius_track_t *tt;
217 
218  if (!te) return 0;
219 
220  tt = talloc_get_type_abort(te->tt, radius_track_t); /* Make sure table is still valid */
221 
222  if (te->binding) {
223  talloc_set_destructor(te->binding, NULL); /* Disarm the destructor */
224  talloc_free(te->binding);
225  }
226 
227 #ifndef NDEBUG
228  te->operation = te->tt->operation++;
229  te->file = file;
230  te->line = line;
231 #endif
232 
233  te->request = NULL;
234 
235  fr_assert(tt->num_requests > 0);
236  tt->num_requests--;
237 
238  /*
239  * We're freeing a static ID, just go do that...
240  */
241  if (te == &tt->id[te->id]) {
242  /*
243  * This entry MAY be in a subtree. If so, delete
244  * it.
245  */
246  if (tt->subtree[te->id]) (void) fr_rb_delete(tt->subtree[te->id], te);
247 
248  goto done;
249  }
250 
251  /*
252  * At this point, it MUST be talloc'd.
253  */
254  (void) talloc_get_type_abort(te, radius_track_entry_t);
255 
256  /*
257  * Delete it from the tracking subtree.
258  */
259  fr_assert(tt->subtree[te->id] != NULL);
260  (void) fr_rb_delete(tt->subtree[te->id], te);
261 
262  /*
263  * Try to free memory if the system gets idle. If the
264  * system is busy, we will try to keep entries in the
265  * free list. If the system becomes completely idle, we
266  * will clear the free list.
267  */
268  if (fr_dlist_num_elements(&tt->free_list) > tt->num_requests) {
269  talloc_free(te);
270  *te_to_free = NULL;
271  return 0;
272  }
273 
274  /*
275  * Otherwise put it back on the free list.
276  */
277 done:
279 
280  *te_to_free = NULL;
281 
282  return 0;
283 }
284 
285 /** Update a tracking entry with the authentication vector
286  *
287  * @param te The radius_track_entry_t, via radius_track_entry_reserve()
288  * @param vector The authentication vector for the packet we're sending
289  * @return
290  * - <0 on error
291  * - 0 on success
292  */
294 {
295  radius_track_t *tt = te->tt;
296 
297  fr_assert(tt);
298 
299  /*
300  * The authentication vector may have changed.
301  */
302  if (tt->subtree[te->id]) (void) fr_rb_delete(tt->subtree[te->id], te);
303 
304  memcpy(te->vector, vector, sizeof(te->vector));
305 
306  /*
307  * If we're not using the Request Authenticator, the
308  * tracking entry must be in the static array.
309  *
310  * @todo - gracefully handle fallback if the server screws up.
311  */
312  if (!tt->use_authenticator) {
313  fr_assert(te == &tt->id[te->id]);
314  return 0;
315  }
316 
317  /*
318  * Insert it into the tree of authenticators
319  *
320  * We do this even if it was allocated from the static
321  * array. That way if the server responds with
322  * Original-Request-Authenticator, we can easily find it.
323  */
324  if (!fr_rb_insert(tt->subtree[te->id], te)) return -1;
325 
326  return 0;
327 }
328 
329 /** Find a tracking entry from a request authenticator
330  *
331  * @param tt The radius_track_t tracking table
332  * @param packet_id The ID from the RADIUS header
333  * @param vector The Request Authenticator (may be NULL)
334  * @return
335  * - NULL on "not found"
336  * - radius_track_entry_t on success
337  */
339 {
340  radius_track_entry_t my_te, *te;
341 
342  (void) talloc_get_type_abort(tt, radius_track_t);
343 
344  /*
345  * Just use the static array.
346  */
347  if (!tt->use_authenticator || !vector) {
348  te = &tt->id[packet_id];
349 
350  /*
351  * Not in use, die.
352  */
353  if (!te->request) return NULL;
354 
355  /*
356  * Ignore the Request Authenticator, as the
357  * caller doesn't have it.
358  */
359  return te;
360  }
361 
362  /*
363  * The entry MAY be in the subtree!
364  */
365  memcpy(&my_te.vector, vector, sizeof(my_te.vector));
366 
367  te = fr_rb_find(tt->subtree[packet_id], &my_te);
368 
369  /*
370  * Not found, the packet MAY have been allocated in the
371  * old-style method prior to negotiation of
372  * Original-Request-Identifier.
373  */
374  if (!te) {
375  te = &tt->id[packet_id];
376 
377  /*
378  * Not in use, die.
379  */
380  if (!te->request) return NULL;
381 
382  // @todo - add a "generation" count for packets, so we can skip this after all outstanding packets
383  // are using the new method. Hmm... probably just a timer "last sent packet with old-style"
384  // and then compare it to te->start
385 
386  /*
387  * We have the vector, so we need to check it.
388  */
389  if (memcmp(te->vector, vector, sizeof(te->vector)) != 0) {
390  return NULL;
391  }
392 
393  return te;
394  }
395 
396  (void) talloc_get_type_abort(te, radius_track_entry_t);
397  fr_assert(te->request != NULL);
398 
399  return te;
400 }
401 
402 
403 /** Use Request Authenticator (or not) as an Identifier
404  *
405  * @param tt The radius_track_t tracking table
406  * @param flag Whether or not to use it.
407  */
409 {
410  (void) talloc_get_type_abort(tt, radius_track_t);
411 
412  tt->use_authenticator = flag;
413 }
414 
415 #ifndef NDEBUG
416 /** Print out the state of every tracking entry
417  *
418  * @param[in] log destination.
419  * @param[in] log_type Type of log message.
420  * @param[in] file this function was called in.
421  * @param[in] line this function was called on.
422  * @param[in] tt Table to print.
423  * @param[in] extra Callback function for printing extra detail.
424  */
425 void radius_track_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line,
427 {
428  size_t i;
429 
430  for (i = 0; i < NUM_ELEMENTS(tt->id); i++) {
431  radius_track_entry_t *entry;
432 
433  entry = &tt->id[i];
434 
435  if (entry->request) {
436  fr_log(log, log_type, file, line,
437  "[%zu] %"PRIu64 " - Allocated at %s:%u to request %p (%s), uctx %p",
438  i, entry->operation,
439  entry->file, entry->line, entry->request, entry->request->name, entry->uctx);
440  } else {
441  fr_log(log, log_type, file, line,
442  "[%zu] %"PRIu64 " - Freed at %s:%u",
443  i, entry->operation, entry->file, entry->line);
444  }
445 
446  if (extra) extra(log, log_type, file, line, entry);
447  }
448 }
449 #endif
int const char * file
Definition: acutest.h:702
int const char int line
Definition: acutest.h:702
#define RCSID(id)
Definition: build.h:481
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition: build.h:110
#define NUM_ELEMENTS(_t)
Definition: build.h:335
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:156
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition: dlist.h:939
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.
Definition: dlist.h:486
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
talloc_free(reap)
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
Definition: log.c:583
fr_log_type_t
Definition: log.h:54
unsigned char uint8_t
Definition: merged_model.c:30
static bool done
Definition: radclient.c:80
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
#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.
Definition: rb.h:246
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
Definition: log.h:96
void radius_track_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, radius_track_t *tt, radius_track_log_extra_t extra)
Print out the state of every tracking entry.
Definition: track.c:425
static int8_t te_cmp(void const *one, void const *two)
Compare two radius_track_entry_t.
Definition: track.c:69
int radius_track_entry_update(radius_track_entry_t *te, uint8_t const *vector)
Update a tracking entry with the authentication vector.
Definition: track.c:293
int _radius_track_entry_reserve(char const *file, int line, radius_track_entry_t **te_out, TALLOC_CTX *ctx, radius_track_t *tt, request_t *request, uint8_t code, void *uctx)
Allocate a tracking entry.
Definition: track.c:109
radius_track_t * radius_track_alloc(TALLOC_CTX *ctx)
Create an radius_track_t.
Definition: track.c:42
int _radius_track_entry_release(char const *file, int line, radius_track_entry_t **te_to_free)
Release a tracking entry.
Definition: track.c:209
static int _radius_track_entry_release_on_free(radius_track_entry_t ***te_p)
Ensures the entry is released when the ctx passed to radius_track_entry_reserve is freed.
Definition: track.c:84
void radius_track_use_authenticator(radius_track_t *tt, bool flag)
Use Request Authenticator (or not) as an Identifier.
Definition: track.c:408
radius_track_entry_t * radius_track_entry_find(radius_track_t *tt, uint8_t packet_id, uint8_t const *vector)
Find a tracking entry from a request authenticator.
Definition: track.c:338
char const * file
Where the entry was allocated.
Definition: track.h:59
fr_dlist_head_t free_list
so we allocate by least recently used
Definition: track.h:67
radius_track_t * tt
Definition: track.h:39
#define radius_track_entry_release(_te)
Definition: track.h:95
bool use_authenticator
whether to use the request authenticator as an ID
Definition: track.h:69
void * uctx
Result/resumption context.
Definition: track.h:47
fr_rb_tree_t * subtree[UINT8_MAX+1]
for Original-Request-Authenticator
Definition: track.h:74
int line
Where the entry was freed.
Definition: track.h:60
uint8_t id
our ID
Definition: track.h:50
uint64_t operation
Incremented each alloc and de-alloc.
Definition: track.h:77
unsigned int num_requests
number of requests in the allocation
Definition: track.h:65
uint8_t code
packet code (sigh)
Definition: track.h:49
radius_track_entry_t id[UINT8_MAX+1]
which ID was used
Definition: track.h:72
#define radius_track_entry_reserve(_te_out, _ctx, _tt, _request, _code, _uctx)
Definition: track.h:87
int next_id
next ID to allocate
Definition: track.h:70
void(* radius_track_log_extra_t)(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, radius_track_entry_t *te)
Definition: track.h:100
uint64_t operation
Used to give an idea of the alloc/free timeline.
Definition: track.h:58
request_t * request
as always...
Definition: track.h:45
radius_track_entry_t *** binding
Binding chunk we use to release the entry when its parent is freed.
Definition: track.h:41
Track one request to a response.
Definition: track.h:36
#define fr_strerror_const(_msg)
Definition: strerror.h:223