From 0af5ab4593e30ff39c4d20be027b4d489f835986 Mon Sep 17 00:00:00 2001 From: "Alan T. DeKok" Date: Tue, 21 May 2024 15:23:38 -0400 Subject: [PATCH] track sent / replied timers in preparation for adding application-layer watchdogs --- src/lib/bio/retry.c | 49 +++++++++++++++++++++++++++++++++-- src/lib/bio/retry.h | 12 +++++++++ src/protocols/radius/client.c | 4 +++ src/protocols/radius/client.h | 2 ++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/lib/bio/retry.c b/src/lib/bio/retry.c index ecb49797dd..21d8207cf2 100644 --- a/src/lib/bio/retry.c +++ b/src/lib/bio/retry.c @@ -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; +} diff --git a/src/lib/bio/retry.h b/src/lib/bio/retry.h index f2ed5ea934..a53afd4970 100644 --- a/src/lib/bio/retry.h +++ b/src/lib/bio/retry.h @@ -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); diff --git a/src/protocols/radius/client.c b/src/protocols/radius/client.c index b6057ef9b9..7db0f53488 100644 --- a/src/protocols/radius/client.c +++ b/src/protocols/radius/client.c @@ -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; } diff --git a/src/protocols/radius/client.h b/src/protocols/radius/client.h index caae586092..b51e7c0dae 100644 --- a/src/protocols/radius/client.h +++ b/src/protocols/radius/client.h @@ -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); -- 2.47.3