47#include <freeradius-devel/bio/bio_priv.h>
48#include <freeradius-devel/bio/null.h>
49#include <freeradius-devel/bio/buf.h>
50#include <freeradius-devel/util/rb.h>
51#include <freeradius-devel/util/dlist.h>
53#define _BIO_RETRY_PRIVATE
54#include <freeradius-devel/bio/retry.h>
126 item->cancelled =
true;
134 if (!
item->reserved) {
149 if (
my->partial ==
item)
return;
155 if (
item->reserved)
return;
162 fr_assert(fr_bio_retry_list_num_elements(&
my->free) == 0);
171 my->all_used =
false;
173 if (!
my->info.write_blocked &&
my->cb.write_resume) (void)
my->cb.write_resume(&
my->bio);
176 item->packet_ctx = NULL;
178 fr_bio_retry_list_insert_head(&
my->free,
item);
188 if (
my->info.write_blocked) {
201 my->info.write_blocked =
true;
238 my->info.last_sent = now;
266 if (rcode ==
fr_bio_error(IO_WOULD_BLOCK))
return rcode;
275 if ((
size_t) rcode < item->size) {
292 if (!
my->info.write_blocked)
return 1;
294 my->info.write_blocked =
false;
306 return !
my->info.write_blocked;
336 if (rcode <= 0)
return rcode;
338 my->buffer.read += rcode;
356 if (
item->cancelled) {
357 item->packet_ctx = NULL;
359 fr_bio_retry_list_insert_head(&
my->free,
item);
363 if (rcode <= 0)
return rcode;
383 if (!
my->buffer.start ||
436 if (
my->partial)
return 0;
458 if ((
size_t) rcode == size)
return rcode;
463 if (rcode == 0)
return 0;
472 if (rcode ==
fr_bio_error(IO_WOULD_BLOCK))
return rcode;
566 return next->
write(next, packet_ctx, NULL, size);
572 if (fr_bio_retry_list_num_elements(&
my->free) == 0) {
593 my->info.write_blocked =
true;
600 if (rcode < 0)
return rcode;
608 fr_assert(fr_bio_retry_list_num_elements(&
my->free) > 0);
618 rcode = next->
write(next, packet_ctx,
buffer, size);
619 if (rcode <= 0)
return rcode;
624 item = fr_bio_retry_list_pop_head(&
my->free);
631 .packet_ctx = packet_ctx,
659 if ((
size_t) rcode < size) {
682 rcode = next->
read(next, packet_ctx,
buffer, size);
683 if (rcode <= 0)
return rcode;
692 if (!
my->response(bio, &
item, packet_ctx,
buffer, size)) {
695 item->retry.replies++;
727 item->retry.replies++;
733 if (
item->reserved)
return rcode;
821 if (!
item->retry.replies)
return 0;
843 if (
item->retry.config)
return -1;
860 if (!
item->retry.config)
return NULL;
918 if (!max_saved)
return NULL;
919 if (max_saved > 65536)
return NULL;
922 if (!
my)
return NULL;
929 if (!items)
return NULL;
934 fr_bio_retry_list_init(&
my->free);
935 for (i = 0; i < max_saved; i++) {
937 fr_bio_retry_list_insert_tail(&
my->free, &items[i]);
951 if (!
my->expiry_tl) {
966 my->rewrite = rewrite;
968 my->response = response;
969 my->release = release;
972 my->info.el = cfg->
el;
1005 if (!
my->partial)
return num;
1010 return num + !
my->partial->cancelled;
1022 item = fr_bio_retry_list_pop_head(&
my->free);
1023 if (!
item)
return NULL;
static int const char char buffer[256]
fr_bio_write_t _CONST write
write to the underlying bio
fr_bio_read_t _CONST read
read from the underlying bio
static fr_bio_t * fr_bio_next(fr_bio_t *bio)
static ssize_t fr_bio_retry_read(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
static int8_t _expiry_cmp(void const *one, void const *two)
fr_bio_retry_release_t release
callback to release a request / response pair
int fr_bio_retry_entry_init(UNUSED fr_bio_t *bio, fr_bio_retry_entry_t *item, fr_retry_config_t const *cfg)
Set a per-packet retry config.
fr_bio_t * fr_bio_retry_alloc(TALLOC_CTX *ctx, size_t max_saved, fr_bio_retry_sent_t sent, fr_bio_retry_response_t response, fr_bio_retry_rewrite_t rewrite, fr_bio_retry_release_t release, fr_bio_retry_config_t const *cfg, fr_bio_t *next)
Allocate a fr_bio_retry_t.
static ssize_t fr_bio_retry_save_write(fr_bio_retry_t *my, fr_bio_retry_entry_t *item, ssize_t rcode)
Save a partial packet when the write becomes blocked.
fr_bio_buf_t buffer
to store partial packets
static void fr_bio_retry_release(fr_bio_retry_t *my, fr_bio_retry_entry_t *item, fr_bio_retry_release_reason_t reason)
Release an entry back to the free list.
size_t fr_bio_retry_outstanding(fr_bio_t *bio)
fr_timer_list_t * next_tl
when packets are retried next
static void fr_bio_retry_next_timer(UNUSED fr_timer_list_t *tl, fr_time_t now, void *uctx)
Run a timer event.
static int fr_bio_retry_destructor(fr_bio_retry_t *my)
Cancel all outstanding packets.
int fr_bio_retry_entry_cancel(fr_bio_t *bio, fr_bio_retry_entry_t *item)
Cancel one item.
fr_timer_list_t * expiry_tl
when packets expire, so that we expire packets when the socket is blocked.
struct fr_bio_retry_list_s fr_bio_retry_list_t
ssize_t fr_bio_retry_rewrite(fr_bio_t *bio, fr_bio_retry_entry_t *item, const void *buffer, size_t size)
Resend a packet.
static void fr_bio_retry_shutdown(fr_bio_t *bio)
Orderly shutdown.
static int8_t _next_retry_cmp(void const *one, void const *two)
static int fr_bio_retry_write_item(fr_bio_retry_t *my, fr_bio_retry_entry_t *item, fr_time_t now)
Write one item.
fr_retry_config_t retry_config
static void fr_bio_retry_expiry_timer(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Run an expiry timer event.
fr_bio_retry_entry_t * partial
for partial writes
const fr_retry_t * fr_bio_retry_entry_info(UNUSED fr_bio_t *bio, fr_bio_retry_entry_t *item)
Allow the callbacks / application to know when things are being retried.
fr_bio_retry_sent_t sent
callback for when we successfully sent a packet
fr_bio_retry_response_t response
callback to see if we got a valid response
static ssize_t fr_bio_retry_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
Write a request, and see if we have a reply.
static ssize_t fr_bio_retry_write_fatal(fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
A previous timer write had a fatal error, so we forbid further writes.
bool all_used
blocked due to no free entries
static int fr_bio_retry_write_blocked(fr_bio_t *bio)
Writes are blocked.
static ssize_t fr_bio_retry_write_partial(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size)
There's a partial packet written.
fr_bio_retry_rewrite_t rewrite
optional callback which can change a packet on retry
fr_bio_retry_entry_t * fr_bio_retry_item_reserve(fr_bio_t *bio)
Reserve an entry for later use with fr_bio_retry_rewrite()
fr_bio_retry_info_t const * fr_bio_retry_info(fr_bio_t *bio)
static int fr_bio_retry_write_resume(fr_bio_t *bio)
Resume writes.
void * rewrite_ctx
context specifically for rewriting this packet
fr_retry_config_t retry_config
base retry config
void(* fr_bio_retry_release_t)(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, fr_bio_retry_release_reason_t reason)
Callback on release the packet (timeout or have all replies)
fr_retry_t retry
retry timers and counters
fr_bio_retry_release_reason_t
@ FR_BIO_RETRY_WRITE_ERROR
struct fr_bio_retry_entry_s fr_bio_retry_entry_t
uint8_t const * buffer
cached copy of the packet to send
size_t size
size of the cached packet
bool reserved
for application-layer watchdog
void(* fr_bio_retry_sent_t)(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size, fr_bio_retry_entry_t *retry_ctx)
Callback for when a packet is sent.
ssize_t(* fr_bio_retry_rewrite_t)(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, const void *buffer, size_t size)
bool(* fr_bio_retry_response_t)(fr_bio_t *bio, fr_bio_retry_entry_t **item_p, void *packet_ctx, const void *buffer, size_t size)
Callback on read to see if a packet is a response.
fr_event_list_t * el
event list
void * packet_ctx
packet_ctx from the write() call
fr_rb_node_t expiry_node
for expiries
bool cancelled
was this item cancelled?
fr_bio_retry_t * my
so we can get to it from the event timer callback
fr_bio_retry_rewrite_t rewrite
per-packet rewrite callback
void * uctx
user-writable context
static void fr_bio_chain(fr_bio_t *first, fr_bio_t *second)
Chain one bio after another.
int fr_bio_buf_alloc(TALLOC_CTX *ctx, fr_bio_buf_t *bio_buf, size_t size)
ssize_t fr_bio_buf_write(fr_bio_buf_t *bio_buf, const void *buffer, size_t size)
static size_t fr_bio_buf_used(fr_bio_buf_t const *bio_buf)
static void fr_bio_buf_reset(fr_bio_buf_t *bio_buf)
static size_t fr_bio_buf_size(fr_bio_buf_t const *bio_buf)
#define FR_DLIST_TYPES(_name)
Define type specific wrapper structs for dlists.
#define FR_DLIST_ENTRY(_name)
Expands to the type name used for the entry wrapper structure.
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
#define FR_DLIST_HEAD(_name)
Expands to the type name used for the head wrapper structure.
int fr_bio_write_blocked(fr_bio_t *bio)
Internal BIO function to tell all BIOs that it's blocked.
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
ssize_t fr_bio_null_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
Always return 0 on write.
#define fr_time()
Allow us to arbitrarily manipulate time.
static fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b)
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
#define fr_time_lteq(_a, _b)
#define fr_time_delta_ispos(_a)
#define fr_time_gt(_a, _b)
#define fr_time_lt(_a, _b)
static int8_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
int fr_timer_list_disarm(fr_timer_list_t *tl)
Disarm a timer list.
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
int fr_timer_uctx_insert(fr_timer_list_t *tl, void *uctx)
Insert a uctx into a shared timer, and update the timer.
fr_timer_list_t * fr_timer_list_shared_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent, fr_cmp_t cmp, fr_timer_cb_t callback, size_t node_offset, size_t time_offset)
Allocate a new shared event timer list.
int fr_timer_uctx_remove(fr_timer_list_t *tl, void *uctx)
Remove a uctx from a shared timer.
void * fr_timer_uctx_peek(fr_timer_list_t *tl)
int fr_timer_list_arm(fr_timer_list_t *tl)
Arm (or re-arm) a timer list.
fr_retry_state_t fr_retry_next(fr_retry_t *r, fr_time_t now)
Initialize a retransmission counter.
void fr_retry_init(fr_retry_t *r, fr_time_t now, fr_retry_config_t const *config)
Initialize a retransmission counter.
fr_time_delta_t irt
Initial transmission time.
fr_time_t end
when we will end the retransmissions
fr_time_t next
when the next timer should be set