From: Alan T. DeKok Date: Thu, 26 Dec 2024 16:56:22 +0000 (-0500) Subject: add support for dynamic proxying X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7d4173fefdeebaf0419ecc2510089e43fc16de28;p=thirdparty%2Ffreeradius-server.git add support for dynamic proxying The home servers are never cleaned up or timed out. The home servers also can't have their secrets changed. --- diff --git a/raddb/mods-available/radius b/raddb/mods-available/radius index 1eb1fc2f500..db42f18a64b 100644 --- a/raddb/mods-available/radius +++ b/raddb/mods-available/radius @@ -101,12 +101,11 @@ radius { # - You CANNOT use the module "in place" as with normal proxying. # - It is only supported via the function %replicate.sendto.ipaddr(ipaddr, port, secret) # - # unconnected-proxy - proxy packets to dynamic destinations - # - For unconnected UDP sockets only. - # - It MUST have `transport = udp` + # dynamic-proxy - proxy packets to dynamic destinations + # - RFC 7585 dynamic DNS lookups are not supported # - It MUST have `src_ipaddr = *` and no `src_port` # - You CANNOT use the module "in place" as with normal proxying. - # - It is only supported via the function %proxy.sendto.ipaddr(ipaddr, port, secret) + # - Proxying is only supported via the function %proxy.sendto.ipaddr(ipaddr, port, secret) # # The server can still be used to create (i.e. originate) # packets via this module when `mode = proxy` is set. The @@ -732,3 +731,182 @@ radius replicate { # along with all per-packet timeouts. # } + +# +# A dynamic proxy module +# +radius proxy { + type = Access-Request + + # + # We are not opening a socket from our server to their + # server. We are replicating packets. + # + mode = dynamic-proxy + + # + # Both UDP and TCP are supported. + # + transport = udp + + # + # ### UDP Transport + # + udp { + # + # src_ipaddr:: The source IP address used by the module. + # + src_ipaddr = * + + # + # `src_port` cannot be used. If it is used here, the + # module will refuse to start. Instead, the module + # will open a unique source port per thread. + # + # `secret` cannot be used. If it is used, the value + # will be ignored. + # + } + + # + # ## Connection trunking + # + # See above for documentation on connection trunking. + # + pool { + # + # start:: Connections to create during module instantiation. + # + # If the server cannot create specified number of connections during instantiation + # it will exit. + # + # Set to `0` to allow the server to start without the database being available. + # + start = 0 + + # + # min:: Minimum number of connections to keep open. + # + min = 1 + + # + # max:: Maximum number of connections. + # + # If these connections are all in use and a new one is requested, the request + # will NOT get a connection. + # + max = 8 + + # + # connecting:: Maximum number of sockets to have in the "connecting" state. + # + # If a home server goes down, the module will close + # old / broken connections, and try to open new ones. + # In order to avoid flooding the home server with + # connection attempts, set the `connecting` value to + # a small number. + # + connecting = 1 + + # + # uses:: number of packets which will use the connection. + # + # After `uses` packets have been sent the connection + # will be closed, and a new one opened. For no + # limits, set `uses = 0`. + # + uses = 0 + + # + # lifetime:: lifetime of a connection, in seconds. + # + # After `lifetime` seconds have passed, no new + # packets will be sent on the connection. When all + # replies have been received, the connection will be + # closed. + # + # For no limits, set `lifetime = 0`. + # + # It is possible to use precise times, such as + # `lifetime = 1.023`, or even qualifiers such as + # `lifetime = 400ms`. + # + lifetime = 0 + + # + # open_delay:: How long (in seconds) a connection + # must be above `per_connection_target` before a new + # connection is opened. + # + # Parsing of this field is the same as for + # `lifetime`. + # + open_delay = 0.2 + + # + # close_delay:: How long (in seconds) a connection + # must be below `per_connection_target` before a + # connection is closed. + # + close_delay = 1.0 + + # + # manage_interval:: How often (in seconds) the + # connections are checked for limits, in order to + # open / close connections. + # + manage_interval = 0.2 + + # + # connection { ... }:: Per-connection configuration. + # + connection { + # + # connection_timeout:: How long to wait + # before giving up on a connection which is + # being opened. + # + connection_timeout = 3.0 + + # + # reconnect_delay:: If opening a connection + # fails, or an open connection fails, + # we wait `reconnect_delay` seconds before + # attempting to open another + # connection. + # + reconnect_delay = 5 + } + + # + # request { ... }:: Per-request configuration. + # + request { + # + # per_connection_max:: The maximum number of requests + # which are "live" on a particular connection. + # + per_connection_max = 255 + + # + # per_connection_target:: The target number + # of requests which are "live" on a + # particular connection. + # + # There can be a balance between overloading + # a connection, and under-utilizing it. The + # default is to fill each connection before + # opening a new one. + # + per_connection_target = 255 + + # + # free_delay:: How long to wait before + # freeing internal resources associated with + # the connection. + # + free_delay = 10 + } + + } +} diff --git a/src/modules/rlm_radius/bio.c b/src/modules/rlm_radius/bio.c index ff6dad986e7..c913325e9e0 100644 --- a/src/modules/rlm_radius/bio.c +++ b/src/modules/rlm_radius/bio.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -60,6 +61,7 @@ typedef struct { fr_bio_t *fd; //!< writing fr_bio_fd_info_t const *info; //!< for replication uint32_t id; //!< for replication + fr_rb_expire_t expires; //!< for proxying / client sending } bio; } bio_thread_t; @@ -136,6 +138,14 @@ struct bio_request_s { fr_retry_t retry; //!< retransmission timers }; +typedef struct { + bio_handle_ctx_t ctx; //!< for copying to bio_handle_t + + fr_bio_fd_config_t fd_config; //!< fil descriptor configuration + + fr_rb_expire_node_t expire; +} home_server_t; + /** Turn a reply code into a module rcode; * */ @@ -167,12 +177,12 @@ static fr_radius_decode_fail_t decode(TALLOC_CTX *ctx, fr_pair_list_t *reply, ui static void protocol_error_reply(bio_request_t *u, bio_handle_t *h); -static unlang_action_t mod_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, UNUSED request_t *request); -static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action); static void mod_write(request_t *request, trunk_request_t *treq, bio_handle_t *h); static int _bio_request_free(bio_request_t *u); +static int8_t home_server_cmp(void const *one, void const *two); + #ifndef NDEBUG /** Log additional information about a tracking entry * @@ -609,22 +619,9 @@ static int _bio_handle_free(bio_handle_t *h) if (h->status_u) fr_event_timer_delete(&h->status_u->ev); - fr_event_fd_delete(h->ctx.el, h->fd, FR_EVENT_FILTER_IO); - - if (shutdown(h->fd, SHUT_RDWR) < 0) { - DEBUG3("%s - Failed shutting down connection %s: %s", - h->ctx.module_name, h->fd_info->name, fr_syserror(errno)); - } - /* - * @todo - free the BIOs? + * The connection code will take care of deleting the FD from the event loop. */ - if (close(h->fd) < 0) { - DEBUG3("%s - Failed closing connection %s: %s", - h->ctx.module_name, h->fd_info->name, fr_syserror(errno)); - } - - h->fd = -1; DEBUG("%s - Connection closed - %s", h->ctx.module_name, h->fd_info->name); @@ -2142,11 +2139,19 @@ static unlang_action_t mod_resume(rlm_rcode_t *p_result, module_ctx_t const *mct RETURN_MODULE_RCODE(rcode); } +static void do_signal(rlm_radius_t const *inst, bio_request_t *u, request_t *request, fr_signal_t action); + static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_signal_t action) { rlm_radius_t const *inst = talloc_get_type_abort_const(mctx->mi->data, rlm_radius_t); bio_request_t *u = talloc_get_type_abort(mctx->rctx, bio_request_t); + + do_signal(inst, u, request, action); +} + +static void do_signal(rlm_radius_t const *inst, bio_request_t *u, UNUSED request_t *request, fr_signal_t action) +{ bio_handle_t *h; /* @@ -2166,10 +2171,7 @@ static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_s * unlang_request_is_scheduled will return false * (don't use it). */ - if (!u->treq) { - talloc_free(u); - return; - } + if (!u->treq) return; switch (action) { /* @@ -2179,7 +2181,6 @@ static void mod_signal(module_ctx_t const *mctx, UNUSED request_t *request, fr_s case FR_SIGNAL_CANCEL: trunk_request_signal_cancel(u->treq); u->treq = NULL; - talloc_free(u); /* Should be freed soon anyway, but better to be explicit */ return; /* @@ -2234,9 +2235,9 @@ static int _bio_request_free(bio_request_t *u) return 0; } -static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *inst, void *thread, request_t *request) +static int mod_enqueue(bio_request_t **p_u, fr_retry_config_t const **p_retry_config, + rlm_radius_t const *inst, trunk_t *trunk, request_t *request) { - bio_thread_t *t = talloc_get_type_abort(thread, bio_thread_t); bio_request_t *u; trunk_request_t *treq; fr_retry_config_t const *retry_config; @@ -2246,11 +2247,14 @@ static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *in if (request->packet->code == FR_RADIUS_CODE_STATUS_SERVER) { RWDEBUG("Status-Server is reserved for internal use, and cannot be sent manually."); - RETURN_MODULE_NOOP; + return 0; } - treq = trunk_request_alloc(t->ctx.trunk, request); - if (!treq) RETURN_MODULE_FAIL; + treq = trunk_request_alloc(trunk, request); + if (!treq) { + REDEBUG("Failed allocating handler for request"); + return -1; + } MEM(u = talloc_zero(request, bio_request_t)); talloc_set_destructor(u, _bio_request_free); @@ -2267,7 +2271,7 @@ static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *in u->rcode = RLM_MODULE_FAIL; - switch(trunk_request_enqueue(&treq, t->ctx.trunk, request, u, u)) { + switch(trunk_request_enqueue(&treq, trunk, request, u, u)) { case TRUNK_ENQUEUE_OK: case TRUNK_ENQUEUE_IN_BACKLOG: break; @@ -2278,7 +2282,7 @@ static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *in fr_assert(!u->rr && !u->packet); /* Should not have been fed to the muxer */ trunk_request_free(&treq); /* Return to the free list */ talloc_free(u); - RETURN_MODULE_FAIL; + return -1; case TRUNK_ENQUEUE_DST_UNAVAILABLE: REDEBUG("All destinations are down - cannot send packet"); @@ -2299,8 +2303,8 @@ static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *in switch (inst->mode) { case RLM_RADIUS_MODE_INVALID: case RLM_RADIUS_MODE_UNCONNECTED_REPLICATE: /* unconnected sockets are UDP, and bypass the trunk */ - case RLM_RADIUS_MODE_UNCONNECTED_PROXY: /* unconnected sockets are UDP, and bypass the trunk */ - RETURN_MODULE_FAIL; + REDEBUG("Internal sanity check failed - connection trunking cannot be used for replication"); + return -1; /* * We originate this packet if it was taken from the detail module, which doesn't have a @@ -2313,6 +2317,7 @@ static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *in * packet code. This lets us receive Accounting-Request, and originate * Disconnect-Request. */ + case RLM_RADIUS_MODE_XLAT_PROXY: case RLM_RADIUS_MODE_PROXY: if (!request->parent) { u->proxied = (request->client->cs != NULL); @@ -2355,9 +2360,25 @@ static unlang_action_t mod_enqueue(rlm_rcode_t *p_result, rlm_radius_t const *in * The event loop will take care of demux && sending the * packet, along with any retransmissions. */ - return unlang_module_yield_to_retry(request, mod_resume, mod_retry, mod_signal, 0, u, retry_config); + *p_u = u; + *p_retry_config = retry_config; + + return 1; } +static const trunk_io_funcs_t io_funcs = { + .connection_alloc = thread_conn_alloc, + .connection_notify = thread_conn_notify, + .request_prioritise = request_prioritise, + .request_mux = request_mux, + .request_demux = request_demux, + .request_conn_release = request_conn_release, + .request_complete = request_complete, + .request_fail = request_fail, + .request_cancel = request_cancel, +}; + + /** Instantiate thread data for the submodule. * */ @@ -2366,31 +2387,22 @@ static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx) rlm_radius_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); bio_thread_t *thread = talloc_get_type_abort(mctx->thread, bio_thread_t); - static trunk_io_funcs_t io_funcs = { - .connection_alloc = thread_conn_alloc, - .connection_notify = thread_conn_notify, - .request_prioritise = request_prioritise, - .request_mux = request_mux, - .request_demux = request_demux, - .request_conn_release = request_conn_release, - .request_complete = request_complete, - .request_fail = request_fail, - .request_cancel = request_cancel, - }; - thread->ctx.el = mctx->el; thread->ctx.inst = inst; thread->ctx.fd_config = &inst->fd_config; thread->ctx.radius_ctx = inst->common_ctx; if ((inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && - (inst->mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + (inst->mode != RLM_RADIUS_MODE_XLAT_PROXY)) { thread->ctx.trunk = trunk_alloc(thread, mctx->el, &io_funcs, &inst->trunk_conf, inst->name, thread, false); if (!thread->ctx.trunk) return -1; return 0; } + /* + * Allocate the unconnected replication socket. + */ thread->bio.fd = fr_bio_fd_alloc(thread, &thread->ctx.inst->fd_config, 0); if (!thread->bio.fd) { PERROR("%s - failed opening socket - ", inst->name); @@ -2407,10 +2419,17 @@ static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx) (void) fr_bio_fd_write_only(thread->bio.fd); DEBUG("%s - Opened unconnected replication socket %s", inst->name, thread->bio.info->name); - } else { - DEBUG("%s - Opened unconnected proxy socket %s", inst->name, thread->bio.info->name); + return 0; } + DEBUG("%s - Opened unconnected proxy socket %s", inst->name, thread->bio.info->name); + + /* + * @todo - make lifetime configurable? + */ + fr_rb_expire_inline_talloc_init(&thread->bio.expires, home_server_t, expire, home_server_cmp, NULL, + fr_time_delta_from_sec(60)); + return 0; } @@ -2516,3 +2535,161 @@ static xlat_action_t xlat_radius_replicate(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcu */ return XLAT_ACTION_DONE; } + +// ********************************************************************** + +/** Dynamic home server code + * + */ + +static int8_t home_server_cmp(void const *one, void const *two) +{ + home_server_t const *a = one; + home_server_t const *b = two; + int8_t rcode; + + rcode = fr_ipaddr_cmp(&a->fd_config.dst_ipaddr, &b->fd_config.dst_ipaddr); + if (rcode != 0) return rcode; + + return CMP(a->fd_config.dst_port, b->fd_config.dst_port); +} + +static xlat_action_t xlat_sendto_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, + xlat_ctx_t const *xctx, + UNUSED request_t *request, UNUSED fr_value_box_list_t *in) +{ + bio_request_t *u = talloc_get_type_abort(xctx->rctx, bio_request_t); + fr_value_box_t *dst; + + if (u->rcode == RLM_MODULE_FAIL) return XLAT_ACTION_FAIL; + + MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_UINT32, NULL)); /* @todo - attr for rcode? */ + dst->vb_uint32 = u->rcode; + + fr_dcursor_append(out, dst); + + return XLAT_ACTION_DONE; +} + +static void xlat_sendto_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action) +{ + rlm_radius_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_radius_t); + bio_request_t *u = talloc_get_type_abort(xctx->rctx, bio_request_t); + + do_signal(inst, u, request, action); +} + +/* + * @todo - change this to mod_retry + */ +static void xlat_sendto_timeout(xlat_ctx_t const *xctx, request_t *request, UNUSED fr_time_t fired) +{ +// rlm_radius_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_radius_t); + bio_request_t *u = talloc_get_type_abort(xctx->rctx, bio_request_t); + + RDEBUG2("XXX Timeout sending packet"); + + fr_assert(u->treq != NULL); + + u->rcode = RLM_MODULE_FAIL; + trunk_request_signal_fail(u->treq); + + unlang_interpret_mark_runnable(request); +} + +/* + * %proxy.sendto.ipaddr(ipaddr, port, secret) + */ +static xlat_action_t xlat_radius_client(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, + xlat_ctx_t const *xctx, + request_t *request, fr_value_box_list_t *args) +{ + rlm_radius_t const *inst = talloc_get_type_abort(xctx->mctx->mi->data, rlm_radius_t); + bio_thread_t *thread = talloc_get_type_abort(xctx->mctx->thread, bio_thread_t); + fr_value_box_t *ipaddr, *port, *secret; + home_server_t *home; + bio_request_t *u = NULL; + fr_retry_config_t const *retry_config = NULL; + fr_time_t timeout; + int rcode; + + XLAT_ARGS(args, &ipaddr, &port, &secret); + + /* + * Can't change IP address families. + */ + if (ipaddr->vb_ip.af != thread->bio.info->socket.af) { + RDEBUG("Invalid destination IP address family in %pV", ipaddr); + return -1; + } + + home = fr_rb_find(&thread->bio.expires.tree, &(home_server_t) { + .fd_config = (fr_bio_fd_config_t) { + .dst_ipaddr = ipaddr->vb_ip, + .dst_port = port->vb_uint16, + }, + }); + if (!home) { + MEM(home = talloc(thread, home_server_t)); + + *home = (home_server_t) { + .ctx = (bio_handle_ctx_t) { + .el = unlang_interpret_event_list(request), + .module_name = inst->name, + .inst = inst, + }, + }; + + home->fd_config = inst->fd_config; + home->ctx.fd_config = &home->fd_config; + + home->fd_config.type = FR_BIO_FD_CONNECTED; + home->fd_config.dst_ipaddr = ipaddr->vb_ip; + home->fd_config.dst_port = port->vb_uint32; + + home->ctx.radius_ctx = (fr_radius_ctx_t) { + .secret = talloc_strdup(home, secret->vb_strvalue), + .secret_length = secret->vb_length, + .proxy_state = inst->common_ctx.proxy_state, + }; + + /* + * Allocate the trunk and start it up. + */ + home->ctx.trunk = trunk_alloc(thread, unlang_interpret_event_list(request), &io_funcs, + &inst->trunk_conf, inst->name, home, false); + if (!home->ctx.trunk) { + fail: + talloc_free(home); + return XLAT_ACTION_FAIL; + } + + if (!fr_rb_expire_insert(&thread->bio.expires, home, fr_time())) goto fail; + } else { + fr_rb_expire_update(&thread->bio.expires, home, fr_time()); + + /* + * @todo - expire old entries, but only if they're not used? + */ + } + + /* + * Enqueue the packet on the per-home-server trunk. + */ + rcode = mod_enqueue(&u, &retry_config, inst, home->ctx.trunk, request); + if (rcode <= 0) { + REDEBUG("Failed enqueuing packet"); + return XLAT_ACTION_FAIL; + } + fr_assert(u != NULL); + fr_assert(retry_config != NULL); + + timeout = fr_time_add(fr_time(), fr_time_delta_from_sec(2)); + + if (unlang_xlat_timeout_add(request, xlat_sendto_timeout, u, timeout) < 0) { + RPEDEBUG("Adding event failed"); + return XLAT_ACTION_FAIL; + } + + return unlang_xlat_yield(request, xlat_sendto_resume, xlat_sendto_signal, ~(FR_SIGNAL_CANCEL | FR_SIGNAL_DUP), u); +} diff --git a/src/modules/rlm_radius/rlm_radius.c b/src/modules/rlm_radius/rlm_radius.c index 2036ae5ec8b..d564673b5ca 100644 --- a/src/modules/rlm_radius/rlm_radius.c +++ b/src/modules/rlm_radius/rlm_radius.c @@ -117,6 +117,17 @@ static conf_parser_t const connected_config[] = { CONF_PARSER_TERMINATOR }; +/* + * We only parse the pool options if we're connected. + */ +static conf_parser_t const pool_config[] = { + { FR_CONF_POINTER("status_check", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) status_check_config }, + + { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_radius_t, trunk_conf, trunk_config ) }, + + CONF_PARSER_TERMINATOR +}; + /* * A mapping of configuration file names to internal variables. */ @@ -209,10 +220,10 @@ fr_dict_attr_autoload_t rlm_radius_dict_attr[] = { static fr_table_num_sorted_t mode_names[] = { { L("client"), RLM_RADIUS_MODE_CLIENT }, + { L("dynamic-proxy"), RLM_RADIUS_MODE_XLAT_PROXY }, { L("proxy"), RLM_RADIUS_MODE_PROXY }, { L("replicate"), RLM_RADIUS_MODE_REPLICATE }, { L("unconnected-replicate"), RLM_RADIUS_MODE_UNCONNECTED_REPLICATE }, -// { L("unconnected-proxy"), RLM_RADIUS_MODE_UNCONNECTED_PROXY }, }; static size_t mode_names_len = NUM_ELEMENTS(mode_names); @@ -234,6 +245,7 @@ static int mode_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, char const *name = cf_pair_value(cf_item_to_pair(ci)); rlm_radius_mode_t mode; rlm_radius_t *inst = talloc_get_type_abort(parent, rlm_radius_t); + CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); mode = fr_table_value_by_str(mode_names, name, RLM_RADIUS_MODE_INVALID); @@ -248,18 +260,25 @@ static int mode_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, *(rlm_radius_mode_t *) out = mode; /* - * Normally we want connected sockets, in which case we push additional configuration for connected sockets. + * Normally we want connected sockets, in which case we push additional configuration for + * connected sockets. */ - if ((mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && - (mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { - CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); - + switch (mode) { + default: inst->fd_config.type = FR_BIO_FD_CONNECTED; if (cf_section_rules_push(cs, connected_config) < 0) return -1; + break; - } else { + case RLM_RADIUS_MODE_XLAT_PROXY: + inst->fd_config.type = FR_BIO_FD_UNCONNECTED; /* reset later when the home server is allocated */ + + if (cf_section_rules_push(cs, pool_config) < 0) return -1; + break; + + case RLM_RADIUS_MODE_UNCONNECTED_REPLICATE: inst->fd_config.type = FR_BIO_FD_UNCONNECTED; + break; } return 0; @@ -486,8 +505,11 @@ static void radius_fixups(rlm_radius_t const *inst, request_t *request) static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) { rlm_radius_t const *inst = talloc_get_type_abort_const(mctx->mi->data, rlm_radius_t); - rlm_rcode_t rcode; + bio_thread_t *thread = talloc_get_type_abort(mctx->thread, bio_thread_t); fr_client_t *client; + int rcode; + bio_request_t *u = NULL; + fr_retry_config_t const *retry_config = NULL; if (!request->packet->code) { REDEBUG("You MUST specify a packet code"); @@ -514,7 +536,7 @@ static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, modul * or %radius.sendto(ip, port, secret) */ if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || - (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + (inst->mode == RLM_RADIUS_MODE_XLAT_PROXY)) { REDEBUG("When using 'mode = unconnected-*', this module cannot be used in-place. Instead, it must be called via a function call"); RETURN_MODULE_FAIL; } @@ -545,8 +567,11 @@ static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, modul * return another code which indicates what happened to * the request... */ - return mod_enqueue(&rcode, inst, - module_thread(mctx->mi)->data, request); + rcode = mod_enqueue(&u, &retry_config, inst, thread->ctx.trunk, request); + if (rcode == 0) RETURN_MODULE_NOOP; + if (rcode < 0) RETURN_MODULE_FAIL; + + return unlang_module_yield_to_retry(request, mod_resume, mod_retry, mod_signal, 0, u, retry_config); } @@ -629,17 +654,19 @@ check_others: FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 64); FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535); - if ((inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && - (inst->mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + if (inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) { /* * These limits are specific to RADIUS, and cannot be over-ridden */ FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, >=, 2); FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, <=, 255); FR_INTEGER_BOUND_CHECK("trunk.per_connection_target", inst->trunk_conf.target_req_per_conn, <=, inst->trunk_conf.max_req_per_conn / 2); - } else { + } + + if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || + (inst->mode == RLM_RADIUS_MODE_XLAT_PROXY)) { if (inst->fd_config.src_port != 0) { - cf_log_err(conf, "Cannot set 'src_port' when using 'mode = unconnected'"); + cf_log_err(conf, "Cannot set 'src_port' when using this 'mode'"); return -1; } } @@ -695,8 +722,8 @@ check_others: inst->status_check = false; } else if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || - (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { - cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'mode = unconnected-*'", + (inst->mode == RLM_RADIUS_MODE_XLAT_PROXY)) { + cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'mode' setting", fr_radius_packet_name[inst->status_check]); inst->status_check = false; } @@ -842,8 +869,9 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx) xlat_func_args_set(xlat, xlat_radius_send_args); break; - case RLM_RADIUS_MODE_UNCONNECTED_PROXY: - fr_assert(0); /* not implemented */ + case RLM_RADIUS_MODE_XLAT_PROXY: + xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "sendto.ipaddr", xlat_radius_client, FR_TYPE_UINT32); + xlat_func_args_set(xlat, xlat_radius_send_args); break; default: diff --git a/src/modules/rlm_radius/rlm_radius.h b/src/modules/rlm_radius/rlm_radius.h index 13697630e9b..8d6a2f9195f 100644 --- a/src/modules/rlm_radius/rlm_radius.h +++ b/src/modules/rlm_radius/rlm_radius.h @@ -44,7 +44,7 @@ typedef enum { RLM_RADIUS_MODE_CLIENT, RLM_RADIUS_MODE_REPLICATE, RLM_RADIUS_MODE_UNCONNECTED_REPLICATE, - RLM_RADIUS_MODE_UNCONNECTED_PROXY, + RLM_RADIUS_MODE_XLAT_PROXY, } rlm_radius_mode_t; /*