]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
added tracking and allocation for outbound RADIUS packets
authorAlan T. DeKok <aland@freeradius.org>
Thu, 13 Jul 2017 16:48:42 +0000 (12:48 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 13 Jul 2017 17:49:13 +0000 (13:49 -0400)
src/modules/rlm_radius/rlm_radius.mk
src/modules/rlm_radius/track.c [new file with mode: 0644]
src/modules/rlm_radius/track.h [new file with mode: 0644]

index 96f9a844def4c3305312cf09aa6d6e6a290e9363..0819339e743f4661ebd58b15da1276d43a8bc6e5 100644 (file)
@@ -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 (file)
index 0000000..71be277
--- /dev/null
@@ -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 <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rbtree.h>
+#include <freeradius-devel/io/application.h>
+#include <freeradius-devel/rad_assert.h>
+
+#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 (file)
index 0000000..9b7b42c
--- /dev/null
@@ -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 <aland@freeradius.org>
+ */
+
+/** 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 */