]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
track sent / replied timers
authorAlan T. DeKok <aland@freeradius.org>
Tue, 21 May 2024 19:23:38 +0000 (15:23 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 21 May 2024 22:20:58 +0000 (18:20 -0400)
in preparation for adding application-layer watchdogs

src/lib/bio/retry.c
src/lib/bio/retry.h
src/protocols/radius/client.c
src/protocols/radius/client.h

index ecb49797dda4c8796c67e863159892b833e615ca..21d8207cf2c6efaba1d1f0767af9916a137595cf 100644 (file)
@@ -66,6 +66,8 @@ struct fr_bio_retry_s {
        fr_event_list_t         *el;
        fr_rb_tree_t            rb;
 
+       fr_bio_retry_info_t     info;
+
        fr_retry_config_t       retry_config;
 
        ssize_t                 error;
@@ -205,6 +207,16 @@ static int fr_bio_retry_write_item(fr_bio_retry_t *my, fr_bio_retry_entry_t *ite
                return 1;
        }
 
+       /*
+        *      Track when we last sent a NEW packet.  Also track when we first sent a packet after becoming
+        *      active again.
+        */
+       if ((item->retry.count == 1) && fr_time_lt(my->info.last_sent, now)) {
+               my->info.last_sent = now;
+
+               if (fr_time_lteq(my->info.first_sent, my->info.last_idle)) my->info.first_sent = now;
+       }
+
        /*
         *      Write out the packet.  On failure release this item.
         *
@@ -332,6 +344,7 @@ static ssize_t fr_bio_retry_write_partial(fr_bio_t *bio, void *packet_ctx, const
         */
        fr_bio_buf_reset(&my->buffer);
        my->partial = NULL;
+       my->info.write_blocked = false;
 
        /*
         *      The item was cancelled.  It's still in the tree, so we remove it, and reset its fields.
@@ -357,8 +370,8 @@ static ssize_t fr_bio_retry_write_partial(fr_bio_t *bio, void *packet_ctx, const
         *      Note that the retried packets are sent _before_ the new one.  If the caller doesn't want this
         *      behavior, he can cancel the old ones.
         *
-        *      @todo - have a way to prioritize packets?  i.e. to insert a packet at the _head_ of the list,
-        *      and write it _now_, as with Status-Server.
+        *      We don't need to prioritize outgoing packets (e.g. Status-Server).  A call to write() will
+        *      write them now, in advance of any pending retries.
         */
        item = fr_rb_first(&my->rb);
        if (item) {
@@ -413,6 +426,7 @@ static ssize_t fr_bio_retry_blocked(fr_bio_retry_t *my, fr_bio_retry_entry_t *it
        fr_bio_buf_write(&my->buffer, item->buffer + rcode, item->size - rcode);
 
        my->partial = item;
+       my->info.write_blocked = true;
 
        /*
         *      There's no timer, as the write is blocked, so we can't retry.
@@ -719,6 +733,28 @@ static ssize_t fr_bio_retry_read(fr_bio_t *bio, void *packet_ctx, void *buffer,
        fr_assert(item->retry.replies == 0);
        fr_assert(item != my->partial);
        
+       /*
+        *      Track when the "most recently sent" packet has a reply.  This metric is better than most
+        *      others for judging the liveliness of the destination.
+        */
+       if (fr_time_lt(my->info.mrs_time, item->retry.start)) my->info.mrs_time = item->retry.start;
+
+       /*
+        *      We have a new reply, remember when that happened.  Note that we don't update this timer for
+        *      duplicate replies, but perhaps we should?
+        */
+       my->info.last_reply = fr_time();
+
+       /*
+        *      There are no more packets to send, so this connection is idle.
+        *
+        *      Note that partial packets aren't tracked in the timer tree.  We can't do retransmits until the
+        *      socket is writable.
+        *
+        *      @todo - don't include application watchdog packets in the idle count?
+        */
+       if (!my->partial && (fr_rb_num_elements(&my->rb) == 1)) my->info.last_idle = my->info.last_reply;
+
        /*
         *      We have a new reply.  If we've received all of the replies (i.e. one), OR we don't have a
         *      maximum lifetime for this request, then release it immediately.
@@ -900,14 +936,23 @@ fr_bio_t *fr_bio_retry_alloc(TALLOC_CTX *ctx, size_t max_saved,
        my->release = release;
 
        my->el = cfg->el;
+       my->info.last_idle = fr_time();
        my->retry_config = cfg->retry_config;
 
        my->bio.write = fr_bio_retry_write;
        my->bio.read = fr_bio_retry_read;
 
+
        fr_bio_chain(&my->bio, next);
 
        talloc_set_destructor(my, fr_bio_retry_destructor);
 
        return (fr_bio_t *) my;
 }
+
+fr_bio_retry_info_t const *fr_bio_retry_info(fr_bio_t *bio)
+{
+       fr_bio_retry_t *my = talloc_get_type_abort(bio, fr_bio_retry_t);
+
+       return &my->info;
+}
index f2ed5ea934c3b90a2151f23f65d1c8b64c382f6a..a53afd49709cd70d03d67ded06f19ddf92818cb8 100644 (file)
@@ -38,6 +38,16 @@ typedef struct {
        fr_retry_config_t       retry_config;   //!< base retry config
 } fr_bio_retry_config_t;
 
+typedef struct {
+       fr_time_t               mrs_time;               //!< Most recent sent time which had a reply.
+       fr_time_t               last_reply;             //!< When we last received a reply.
+       fr_time_t               first_sent;             //!< first time we sent a packet since going idle
+       fr_time_t               last_sent;              //!< last time we sent a packet.
+       fr_time_t               last_idle;              //!< last time we had nothing to do
+
+       bool                    write_blocked;          //!< are writes blocked?
+} fr_bio_retry_info_t;
+
 typedef struct fr_bio_retry_entry_s fr_bio_retry_entry_t;
 
 typedef ssize_t        (*fr_bio_retry_rewrite_t)(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, const void *buffer, size_t size);
@@ -125,3 +135,5 @@ int         fr_bio_retry_entry_start(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, fr
 const fr_retry_t *fr_bio_retry_entry_info(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx) CC_HINT(nonnull);
 
 ssize_t                fr_bio_retry_rewrite(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, const void *buffer, size_t size) CC_HINT(nonnull(1,2));
+
+fr_bio_retry_info_t const *fr_bio_retry_info(fr_bio_t *bio) CC_HINT(nonnull);
index b6057ef9b9c4c09a06cab7168ae7c89d6e0dbe1d..7db0f53488963cb21d3fd76aa8d316e982886d68 100644 (file)
@@ -116,6 +116,9 @@ fr_radius_client_fd_bio_t *fr_radius_client_fd_bio_alloc(TALLOC_CTX *ctx, size_t
        if (!my->retry) goto fail;
        my->retry->uctx = my;
        
+       my->info.retry_info = fr_bio_retry_info(my->retry);
+       fr_assert(my->info.retry_info != NULL);
+
        my->cfg = *cfg;
 
        my->common.bio = my->retry;
@@ -325,6 +328,7 @@ static bool radius_client_retry_response(fr_bio_t *bio, fr_bio_retry_entry_t **r
 
                fr_assert(my->info.outstanding > 0);
                my->info.outstanding--;
+
                return true;
        }
 
index caae5860926f91aacbbf9429e59953d11cb78b80..b51e7c0daebcad5051d9e5f648eae8556bd2b1d9 100644 (file)
@@ -54,6 +54,8 @@ typedef struct {
        size_t                  outstanding;
 
        fr_bio_fd_info_t const  *fd_info;
+
+       fr_bio_retry_info_t const       *retry_info;
 } fr_radius_client_bio_info_t;
 
 fr_bio_packet_t *fr_radius_client_bio_alloc(TALLOC_CTX *ctx, fr_radius_client_config_t *cfg, fr_bio_fd_config_t const *fd_cfg) CC_HINT(nonnull);