]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
first attempt at limiting the source port for %radius.sendto.ipaddr() developer/alandekok master
authorAlan T. DeKok <aland@freeradius.org>
Mon, 4 Aug 2025 21:52:37 +0000 (17:52 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 5 Aug 2025 01:30:25 +0000 (21:30 -0400)
the code is commented out for now, as it is a change of behavior

src/modules/rlm_radius/bio.c
src/modules/rlm_radius/rlm_radius.c

index 5bb48c46b035df729a024439454855f61590547e..01e0ab3e7ea18967a27e3ae440e48ce624f9e657 100644 (file)
@@ -44,7 +44,10 @@ typedef struct {
        trunk_t                 *trunk;         //!< trunk handler
        fr_bio_fd_config_t      fd_config;      //!< for threads or sockets
        fr_bio_fd_info_t const  *fd_info;       //!< status of the FD.
-       fr_radius_ctx_t         radius_ctx;
+       fr_radius_ctx_t         radius_ctx;     //!< for signing packets
+#ifdef REUSE_CONN
+       bool                    limit_source_ports;
+#endif
 } bio_handle_ctx_t;
 
 typedef struct {
@@ -133,8 +136,14 @@ typedef struct {
        bio_handle_ctx_t        ctx;            //!< for copying to bio_handle_t
 
        fr_rb_expire_node_t     expire;
+
+#ifdef REUSE_CONN
+       int                     num_ports;
+       connection_t            *connections[]; //!< for tracking outbound connections
+#endif
 } home_server_t;
 
+
 /** Turn a reply code into a module rcode;
  *
  */
@@ -713,6 +722,9 @@ static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx
        int                     fd;
        bio_handle_t            *h;
        bio_handle_ctx_t        *ctx = uctx; /* thread or home server */
+#ifdef REUSE_CONN
+       connection_t            **to_save = NULL;
+#endif
 
        MEM(h = talloc_zero(conn, bio_handle_t));
        h->ctx = *ctx;
@@ -725,6 +737,37 @@ static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx
 
        MEM(h->tt = radius_track_alloc(h));
 
+#ifdef REUSE_CONN
+       /*
+        *      We are proxying to multiple home servers, but using a limited port range.  We must track the
+        *      source port for each home server, so that we only can select the right unused source port for
+        *      this home server.
+        */
+       if (ctx->limit_source_ports) {
+               int i;
+               home_server_t *home = talloc_get_type_abort(ctx, home_server_t);
+
+               for (i = 0; i < home->num_ports; i++) {
+                       if (!home->connections[i]) {
+                               to_save = &home->connections[i];
+
+                               /*
+                                *      Set the source port, but also leave the src_port_start and
+                                *      src_port_end alone.
+                                */
+                               h->ctx.fd_config.src_port = h->ctx.fd_config.src_port_start + i;
+                               break;
+                       }
+               }
+
+               if (!to_save) {
+                       ERROR("%s - Failed opening socket to home server %pV:%u - source port range is full",
+                             h->ctx.module_name, fr_box_ipaddr(h->ctx.fd_config.dst_ipaddr), h->ctx.fd_config.dst_port);
+                       goto fail;
+               }
+       }
+#endif
+
        h->bio.fd = fr_bio_fd_alloc(h, &h->ctx.fd_config, 0);
        if (!h->bio.fd) {
                PERROR("%s - failed opening socket", h->ctx.module_name);
@@ -823,13 +866,21 @@ static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx
 
        *h_out = h;
 
+#ifdef REUSE_CONN
+       if (to_save) *to_save = conn;
+#endif
+
        return CONNECTION_STATE_CONNECTING;
 }
 
 /** Shutdown/close a file descriptor
  *
  */
-static void conn_close(UNUSED fr_event_list_t *el, void *handle, UNUSED void *uctx)
+static void conn_close(UNUSED fr_event_list_t *el, void *handle,
+#ifndef REUSE_CONN
+  UNUSED
+#endif
+  void *uctx)
 {
        bio_handle_t *h = talloc_get_type_abort(handle, bio_handle_t);
 
@@ -847,6 +898,27 @@ static void conn_close(UNUSED fr_event_list_t *el, void *handle, UNUSED void *uc
 
        fr_bio_shutdown(h->bio.mem);
 
+       /*
+        *      We have opened a limited number of outbound source ports.  This means that when we close a
+        *      port, we have to mark it unused.
+        */
+#ifdef REUSE_CONN
+       if (h->ctx.limit_source_ports) {
+               int offset;
+               home_server_t *home = talloc_get_type_abort(uctx, home_server_t);
+
+               fr_assert(h->ctx.fd_config.src_port >= h->ctx.fd_config.src_port_start);
+               fr_assert(h->ctx.fd_config.src_port < h->ctx.fd_config.src_port_end);
+
+               offset = h->ctx.fd_config.src_port - h->ctx.fd_config.src_port_start;
+               fr_assert(offset < home->num_ports);
+
+               fr_assert(home->connections[offset] == h->conn);
+
+               home->connections[offset] = NULL;
+       }
+#endif
+
        DEBUG4("Freeing handle %p", handle);
 
        talloc_free(h);
@@ -2588,7 +2660,7 @@ static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
        }
 
        /*
-        *      If we have a port range, allocate the source IP based
+        *      If we have a port range, allocate the source port based
         *      on the range start, plus the thread ID.  This means
         *      that we can avoid "hunt and peck" attempts to open up
         *      the source port.
@@ -2807,14 +2879,35 @@ static xlat_action_t xlat_radius_client(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcurso
                        },
                });
        if (!home) {
+#ifdef REUSE_CONN
+               size_t num_ports = 0;
+
+               /*
+                *      Track which connections are made to this home server from which open ports.
+                */
+               if ((inst->fd_config.src_port_start > 0) && (inst->fd_config.src_port_end > 0)) {
+                       num_ports = inst->fd_config.src_port_end - inst->fd_config.src_port_start;
+               }
+
+               MEM(home = (home_server_t *) talloc_zero_array(thread, uint8_t, sizeof(home_server_t) + sizeof(connection_t *) * num_ports));
+               talloc_set_type(home, home_server_t);
+#else
                MEM(home = talloc(thread, home_server_t));
+#endif
 
                *home = (home_server_t) {
                        .ctx = (bio_handle_ctx_t) {
                                .el = unlang_interpret_event_list(request),
                                .module_name = inst->name,
                                .inst = inst,
+#ifdef REUSE_CONN
+                               .limit_source_ports = (num_ports > 0),
+#endif
                        },
+
+#ifdef REUSE_CONN
+                       .num_ports = num_ports,
+#endif
                };
 
                /*
index 5dabe03abe595202857815440a258dfa39c3cc3d..6c92b7fc004e089ac3397108afc99c233e63c03b 100644 (file)
@@ -703,6 +703,27 @@ check_others:
                inst->fd_config.flags = O_RDWR;
        }
 
+       /*
+        *      When using %radius.sendto.ipaddr(), we can then often re-use the source port, as we connect()
+        *      the outbound sockets.
+        */
+       if (inst->mode == RLM_RADIUS_MODE_XLAT_PROXY) {
+               if (inst->fd_config.src_port != 0) {
+                       cf_log_err(conf, "Cannot set a fixed source port for mode=dynamic-proxy");
+                       return -1;
+               }
+
+#ifdef REUSE_CONN
+               /*
+                *      If there is a limited source port range, then set the reuse port flag.  This lets us
+                *      bind multiple sockets to the same port before we connect() them.
+                */
+               if ((inst->fd_config.src_port_start > 0) && (inst->fd_config.src_port_end > 0)) {
+                       inst->fd_config.reuse_port = 1;
+               }
+#endif
+       }
+
        if (fr_bio_fd_check_config(&inst->fd_config) < 0) {
                cf_log_perr(conf, "Invalid configuration");
                return -1;