From: Alan T. DeKok Date: Thu, 13 Jul 2017 16:48:42 +0000 (-0400) Subject: added tracking and allocation for outbound RADIUS packets X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7af11e2b8a628ae9d22ba0348c49d9b5b075cf0c;p=thirdparty%2Ffreeradius-server.git added tracking and allocation for outbound RADIUS packets --- diff --git a/src/modules/rlm_radius/rlm_radius.mk b/src/modules/rlm_radius/rlm_radius.mk index 96f9a844def..0819339e743 100644 --- a/src/modules/rlm_radius/rlm_radius.mk +++ b/src/modules/rlm_radius/rlm_radius.mk @@ -1,3 +1,3 @@ TARGET := rlm_radius.a -SOURCES := rlm_radius.c +SOURCES := rlm_radius.c track.c diff --git a/src/modules/rlm_radius/track.c b/src/modules/rlm_radius/track.c new file mode 100644 index 00000000000..71be277d126 --- /dev/null +++ b/src/modules/rlm_radius/track.c @@ -0,0 +1,343 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file rlm_radius/track.c + * @brief Tracking RADUS client packets + * + * @copyright 2017 Network RADIUS SARL + */ +RCSID("$Id$") + +#include +#include +#include +#include + +#include "track.h" + +/** Free an rlm_radius_id_t + * + */ +static int rr_track_free(rlm_radius_id_t *id) +{ + int i; + + for (i = 0; i < 256; i++) { + if (!id->id[i].request) continue; + + talloc_const_free(id->id[i].ev); + } + + return 0; +} + + +/** Create an rlm_radius_id_t + * + * @param ctx the talloc ctx + * @return + * - NULL on error + * - rlm_radius_id_t on success + */ +rlm_radius_id_t *rr_track_create(TALLOC_CTX *ctx) +{ + int i; + rlm_radius_id_t *id; + + id = talloc_zero(ctx, rlm_radius_id_t); + if (!id) return NULL; + + FR_DLIST_INIT(id->free_list); + + for (i = 0; i < 256; i++) { + id->id[i].id = i; + fr_dlist_insert_tail(&id->free_list, &id->id[i].entry); + } + + talloc_set_destructor(id, rr_track_free); + + id->next_id = fr_rand() & 0xff; + + return id; +} + + +/** Compare two rlm_radius_request_t + * + */ +static int rr_cmp(void const *one, void const *two) +{ + rlm_radius_request_t const *a = one; + rlm_radius_request_t const *b = two; + + return memcmp(a->vector, b->vector, sizeof(a->vector)); +} + +/** Allocate a tracking entry. + * + * @param id The rlm_radius_id_t tracking table + * @param request The request which will send the proxied packet + * @return + * - NULL on error + * - rlm_radius_request_t on success + */ +rlm_radius_request_t *rr_track_alloc(rlm_radius_id_t *id, REQUEST *request) +{ + fr_dlist_t *entry; + rlm_radius_request_t *rr; + +retry: + entry = FR_DLIST_FIRST(id->free_list); + if (entry) { + rad_assert(id->num_free > 0); + + rr = fr_ptr_to_type(rlm_radius_request_t, entry, entry); + + rad_assert(rr->request == NULL); + + /* + * Mark it as used, and remove it from the free list. + */ + fr_dlist_remove(&rr->entry); + id->num_free--; + + /* + * We've transitioned from "use it", to "oops, + * don't use it". Ensure that we only return IDs + * which are in the static array. + */ + if (!id->use_authenticator && + (rr != &id->id[rr->id])) { + talloc_free(rr); + goto retry; + } + + rr->request = request; + id->num_requests++; + return rr; + } + + /* + * There are no free entries, and we can't use the + * Request Authenticator. Oh well... + */ + if (!id->use_authenticator) return NULL; + + /* + * Get a new ID. It's value doesn't matter at this + * point. + */ + id->next_id++; + id->next_id &= 0xff; + + /* + * If needed, allocate a subtree. + */ + if (!id->subtree[id->next_id]) { + id->subtree[id->next_id] = rbtree_create(id, rr_cmp, NULL, RBTREE_FLAG_NONE); + if (!id->subtree[id->next_id]) return NULL; + } + + /* + * Allocate a new one, and insert it into the appropriate subtree. + */ + rr = talloc_zero(id, rlm_radius_request_t); + FR_DLIST_INIT(rr->entry); + rr->id = id->next_id; + + rr->request = request; + id->num_requests++; + return rr; +} + +/** Update a tracking entry with the authentication vector + * + * @param id The rlm_radius_id_t tracking table + * @param rr The rlm_radius_request_t, via rr_track_alloc() + * @param vector The authentication vector for the packet we're sending + * @return + * - <0 on error + * - 0 on success + */ +int rr_track_update(rlm_radius_id_t *id, rlm_radius_request_t *rr, uint8_t *vector) +{ + memcpy(rr->vector, vector, sizeof(rr->vector)); + + /* + * If we're not using the Request Authenticator, the + * tracking entry must be in the static array. + * + * @todo - gracefully handle fallback if the server screws up. + */ + if (!id->use_authenticator) { + rad_assert(rr == &id->id[rr->id]); + return 0; + } + + /* + * Insert it into the tree of authenticators + * + * We do this even if it was allocated from the static + * array. That way if the server responds with + * Original-Request-Authenticator, we can easily find it. + */ + if (!rbtree_insert(id->subtree[rr->id], rr)) { + return -1; + } + + return 0; +} + + +/** Delete a tracking entry + * + * @param id The rlm_radius_id_t tracking table + * @param rr The rlm_radius_request_t, via rr_track_alloc() + * @return + * - <0 on error + * - 0 on success + */ +int rr_track_delete(rlm_radius_id_t *id, rlm_radius_request_t *rr) +{ + (void) talloc_get_type_abort(id, rlm_radius_id_t); + + rr->request = NULL; + if (rr->ev) talloc_const_free(rr->ev); + + rad_assert(id->num_requests > 0); + id->num_requests--; + + /* + * We're freeing a static ID, just go do that... + */ + if (rr == &id->id[rr->id]) { + /* + * This entry MAY be in a subtree. If so, delete + * it. + */ + if (id->subtree[rr->id]) (void) rbtree_deletebydata(id->subtree[rr->id], rr); + + goto done; + } + + /* + * At this point, it MUST be talloc'd. + */ + (void) talloc_get_type_abort(rr, rlm_radius_request_t); + + /* + * Delete it from the tracking subtree. + */ + rad_assert(id->subtree[rr->id] != NULL); + (void) rbtree_deletebydata(id->subtree[rr->id], rr); + + /* + * Try to free memory if the system gets idle. If the + * system is busy, we will try to keep entries in the + * free list. If the system becomes completely idle, we + * will clear the free list. + */ + if (id->num_free > id->num_requests) { + talloc_free(rr); + return 0; + } + + /* + * Otherwise put it back on the free list. + */ +done: + fr_dlist_insert_tail(&id->free_list, &rr->entry); + id->num_free++; + + return 0; +} + + +/** Find a tracking entry from a request authenticator + * + * @param id The rlm_radius_id_t tracking table + * @param packet_id The ID from the RADIUS header + * @param vector The Request Authenticator (may be NULL) + * @return + * - NULL on "not found" + * - rlm_radius_request_t on success + */ +rlm_radius_request_t *rr_track_find(rlm_radius_id_t *id, int packet_id, uint8_t *vector) +{ + rlm_radius_request_t my_rr, *rr; + + (void) talloc_get_type_abort(id, rlm_radius_id_t); + + /* + * Screw you guys, I'm going home! + */ + if (packet_id > 255) return NULL; + + /* + * Just use the static array. + */ + if (!id->use_authenticator || !vector) { + rr = &id->id[packet_id]; + + /* + * Not in use, die. + */ + if (!rr->request) return NULL; + + /* + * Ignore the Request Authenticator, as the + * caller doesn't have it. + */ + return rr; + } + + /* + * The entry MAY be in the subtree! + */ + memcpy(&my_rr.vector, vector, sizeof(my_rr.vector)); + + rr = rbtree_finddata(id->subtree[packet_id], &my_rr); + + /* + * Not found, the packet MAY have been allocated in the + * old-style method prior to negotiation of + * Original-Request-Identifier. + */ + if (!rr) { + rr = &id->id[packet_id]; + + /* + * Not in use, die. + */ + if (!rr->request) return NULL; + + /* + * We have the vector, so we need to check it. + */ + if (memcmp(rr->vector, vector, sizeof(rr->vector)) != 0) { + return NULL; + } + + return rr; + } + + (void) talloc_get_type_abort(rr, rlm_radius_request_t); + rad_assert(rr->request != NULL); + + return rr; +} diff --git a/src/modules/rlm_radius/track.h b/src/modules/rlm_radius/track.h new file mode 100644 index 00000000000..9b7b42c642b --- /dev/null +++ b/src/modules/rlm_radius/track.h @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef _RLM_RADIUS_TRACK_H +#define _RLM_RADIUS_TRACK_H + +/* + * $Id$ + * + * @file track.h + * @brief RADIUS client packet tracking + * + * @copyright 2017 Alan DeKok + */ + +/** Track one request to a response + * + */ +typedef struct rlm_radius_request_t { + REQUEST *request; //!< the original request + + fr_event_timer_t const *ev; //!< timer event associated with this request + + int id; //!< our ID + struct timeval start; //!< when we started sending the packet + uint32_t count; //!< how many times we sent this packet + uint32_t rt; //!< retransmit timer (microseconds) + + union { + fr_dlist_t entry; //!< for free chain + uint8_t vector[16]; //!< copy of the authentication vector + }; +} rlm_radius_request_t; + +typedef struct rlm_radius_id_t { + int num_requests; //!< number of requests in the allocation + int num_free; //!< number of entries in the free list + + fr_dlist_t free_list; //!< so we allocate by least recently used + + bool use_authenticator; //!< whether to use the request authenticator as an ID + int next_id; //!< next ID to allocate + + rlm_radius_request_t id[256]; //!< which ID was used + + rbtree_t *subtree[256]; //!< for Original-Request-Authenticator +} rlm_radius_id_t; + +rlm_radius_id_t *rr_track_create(TALLOC_CTX *ctx); +rlm_radius_request_t *rr_track_alloc(rlm_radius_id_t *id, REQUEST *request) CC_HINT(nonnull); +int rr_track_update(rlm_radius_id_t *id, rlm_radius_request_t *rr, uint8_t *vector) CC_HINT(nonnull); +rlm_radius_request_t *rr_track_find(rlm_radius_id_t *id, int packet_id, uint8_t *vector) CC_HINT(nonnull(1)); +int rr_track_delete(rlm_radius_id_t *id, rlm_radius_request_t *rr); + +#endif /* _RLM_RADIUS_TRACK_H */