Skip to content

Commit 9f78912

Browse files
committed
add and document src_port_start and src_port_end
which currently only work for mode=dynamic-proxy
1 parent c400136 commit 9f78912

File tree

4 files changed

+182
-31
lines changed

4 files changed

+182
-31
lines changed

raddb/mods-available/radius

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,34 @@ radius proxy {
792792
#
793793
src_ipaddr = *
794794

795+
#
796+
# src_port_start:: Start of source port range.
797+
#
798+
# The outgoing proxy normally uses random source
799+
# ports. When all of the RADIUS IDs are used for one
800+
# connection, it opens up another random source port.
801+
#
802+
# However, it is sometimes useful to restrict the
803+
# range of source ports to known values. The
804+
# `src_port_start` and `src_port_end` configuration
805+
# flags allow the port range to be controlled. The
806+
# module will then restrict the source ports it is
807+
# using to be within this range.
808+
#
809+
# When all ports in this range are used, the module
810+
# will not be able to open any more outgoing
811+
# connections.x
812+
#
813+
# These two configuratiuon items can only be used for
814+
# UDP sockets.
815+
#
816+
src_port_start = 10000
817+
818+
#
819+
# src_port_end:: End of source port range.
820+
#
821+
src_port_end = 11000
822+
795823
#
796824
# `src_port` cannot be used. If it is used here, the
797825
# module will refuse to start. Instead, the module

src/modules/rlm_radius/bio.c

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
//#include "rlm_radius.h"
3838
#include "track.h"
3939

40+
extern int fr_schedule_worker_id(void);
41+
4042
/*
4143
* Macro to simplify checking packets before calling decode(), so that
4244
* it gets a known valid length and no longer calls fr_radius_ok() itself.
@@ -49,14 +51,13 @@ typedef struct {
4951
rlm_radius_t const *inst; //!< our instance
5052
fr_event_list_t *el; //!< Event list.
5153
trunk_t *trunk; //!< trunk handler
52-
fr_bio_fd_config_t *fd_config; //!< for threads or sockets
54+
fr_bio_fd_config_t fd_config; //!< for threads or sockets
5355
fr_bio_fd_info_t const *fd_info; //!< status of the FD.
5456
fr_radius_ctx_t radius_ctx;
5557
} bio_handle_ctx_t;
5658

5759
typedef struct {
58-
bio_handle_ctx_t ctx; //!< for copying to bio_handle_t
59-
60+
bio_handle_ctx_t ctx; //!< common struct for home servers and BIO handles
6061

6162
struct {
6263
fr_bio_t *fd; //!< writing
@@ -71,7 +72,7 @@ typedef struct bio_request_s bio_request_t;
7172
*
7273
*/
7374
typedef struct {
74-
bio_handle_ctx_t ctx; //! from thread or home server
75+
bio_handle_ctx_t ctx; //!< common struct for home servers and BIO handles
7576

7677
int fd; //!< File descriptor.
7778

@@ -140,8 +141,6 @@ struct bio_request_s {
140141
typedef struct {
141142
bio_handle_ctx_t ctx; //!< for copying to bio_handle_t
142143

143-
fr_bio_fd_config_t fd_config; //!< fil descriptor configuration
144-
145144
fr_rb_expire_node_t expire;
146145
} home_server_t;
147146

@@ -747,7 +746,14 @@ static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx
747746

748747
MEM(h->tt = radius_track_alloc(h));
749748

750-
h->bio.fd = fr_bio_fd_alloc(h, h->ctx.fd_config, 0);
749+
/*
750+
* Limit the source port to the given range.
751+
*/
752+
if (h->ctx.inst->src_port_start) {
753+
DEBUG("WARNING - src_port_start and src_port_end not currently supported. A random source port will be chosen");
754+
}
755+
756+
h->bio.fd = fr_bio_fd_alloc(h, &h->ctx.fd_config, 0);
751757
if (!h->bio.fd) {
752758
PERROR("%s - failed opening socket", h->ctx.module_name);
753759
fail:
@@ -774,7 +780,7 @@ static connection_state_t conn_init(void **h_out, connection_t *conn, void *uctx
774780
* way we don't need a memory BIO for UDP sockets, but we can still add a verification layer for
775781
* UDP sockets?
776782
*/
777-
if (h->ctx.fd_config->socket_type == SOCK_STREAM) {
783+
if (h->ctx.fd_config.socket_type == SOCK_STREAM) {
778784
h->bio.mem = fr_bio_mem_alloc(h, 8192, 0, h->bio.fd);
779785
if (!h->bio.mem) {
780786
PERROR("%s - Failed allocating memory buffer - ", h->ctx.module_name);
@@ -1411,7 +1417,7 @@ static void mod_dup(request_t *request, bio_request_t *u)
14111417

14121418
h = talloc_get_type_abort(u->treq->tconn->conn->h, bio_handle_t);
14131419

1414-
if (h->ctx.fd_config->socket_type != SOCK_DGRAM) {
1420+
if (h->ctx.fd_config.socket_type != SOCK_DGRAM) {
14151421
RDEBUG("Using stream sockets - suppressing retransmission");
14161422
return;
14171423
}
@@ -1735,7 +1741,7 @@ static void mod_write(request_t *request, trunk_request_t *treq, bio_handle_t *h
17351741
/*
17361742
* We don't retransmit over TCP.
17371743
*/
1738-
if (h->ctx.fd_config->socket_type != SOCK_DGRAM) return;
1744+
if (h->ctx.fd_config.socket_type != SOCK_DGRAM) return;
17391745

17401746
/*
17411747
* If we only send one datagram packet, then don't bother saving it.
@@ -2427,7 +2433,6 @@ static const trunk_io_funcs_t io_funcs = {
24272433
.request_cancel = request_cancel,
24282434
};
24292435

2430-
24312436
/** Instantiate thread data for the submodule.
24322437
*
24332438
*/
@@ -2438,7 +2443,7 @@ static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
24382443

24392444
thread->ctx.el = mctx->el;
24402445
thread->ctx.inst = inst;
2441-
thread->ctx.fd_config = &inst->fd_config;
2446+
thread->ctx.fd_config = inst->fd_config;
24422447
thread->ctx.radius_ctx = inst->common_ctx;
24432448

24442449
if ((inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) &&
@@ -2449,10 +2454,20 @@ static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
24492454
return 0;
24502455
}
24512456

2457+
/*
2458+
* If we have a port range, allocate the source IP based
2459+
* on the range start, plus the thread ID. This means
2460+
* that we can avoid "hunt and peck" attempts to open up
2461+
* the source port.
2462+
*/
2463+
if (thread->ctx.inst->src_port_start) {
2464+
thread->ctx.fd_config.src_port = thread->ctx.inst->src_port_start + fr_schedule_worker_id();
2465+
}
2466+
24522467
/*
24532468
* Allocate the unconnected replication socket.
24542469
*/
2455-
thread->bio.fd = fr_bio_fd_alloc(thread, &thread->ctx.inst->fd_config, 0);
2470+
thread->bio.fd = fr_bio_fd_alloc(thread, &thread->ctx.fd_config, 0);
24562471
if (!thread->bio.fd) {
24572472
PERROR("%s - failed opening socket", inst->name);
24582473
return CONNECTION_STATE_FAILED;
@@ -2597,10 +2612,10 @@ static int8_t home_server_cmp(void const *one, void const *two)
25972612
home_server_t const *b = two;
25982613
int8_t rcode;
25992614

2600-
rcode = fr_ipaddr_cmp(&a->fd_config.dst_ipaddr, &b->fd_config.dst_ipaddr);
2615+
rcode = fr_ipaddr_cmp(&a->ctx.fd_config.dst_ipaddr, &b->ctx.fd_config.dst_ipaddr);
26012616
if (rcode != 0) return rcode;
26022617

2603-
return CMP(a->fd_config.dst_port, b->fd_config.dst_port);
2618+
return CMP(a->ctx.fd_config.dst_port, b->ctx.fd_config.dst_port);
26042619
}
26052620

26062621
static xlat_action_t xlat_sendto_resume(TALLOC_CTX *ctx, fr_dcursor_t *out,
@@ -2665,9 +2680,11 @@ static xlat_action_t xlat_radius_client(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcurso
26652680
}
26662681

26672682
home = fr_rb_find(&thread->bio.expires.tree, &(home_server_t) {
2668-
.fd_config = (fr_bio_fd_config_t) {
2669-
.dst_ipaddr = ipaddr->vb_ip,
2670-
.dst_port = port->vb_uint16,
2683+
.ctx = {
2684+
.fd_config = (fr_bio_fd_config_t) {
2685+
.dst_ipaddr = ipaddr->vb_ip,
2686+
.dst_port = port->vb_uint16,
2687+
},
26712688
},
26722689
});
26732690
if (!home) {
@@ -2681,12 +2698,14 @@ static xlat_action_t xlat_radius_client(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcurso
26812698
},
26822699
};
26832700

2684-
home->fd_config = inst->fd_config;
2685-
home->ctx.fd_config = &home->fd_config;
2686-
2687-
home->fd_config.type = FR_BIO_FD_CONNECTED;
2688-
home->fd_config.dst_ipaddr = ipaddr->vb_ip;
2689-
home->fd_config.dst_port = port->vb_uint32;
2701+
/*
2702+
* Copy the home server configuration from the root configuration. Then update it with
2703+
* the needs of the home server.
2704+
*/
2705+
home->ctx.fd_config = inst->fd_config;
2706+
home->ctx.fd_config.type = FR_BIO_FD_CONNECTED;
2707+
home->ctx.fd_config.dst_ipaddr = ipaddr->vb_ip;
2708+
home->ctx.fd_config.dst_port = port->vb_uint32;
26902709

26912710
home->ctx.radius_ctx = (fr_radius_ctx_t) {
26922711
.secret = talloc_strdup(home, secret->vb_strvalue),

src/modules/rlm_radius/rlm_radius.c

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ static conf_parser_t const transport_config[] = {
103103
CONF_PARSER_TERMINATOR
104104
};
105105

106+
static conf_parser_t const transport_unconnected_config[] = {
107+
{ FR_CONF_OFFSET("src_port_start", rlm_radius_t, src_port_start) },
108+
{ FR_CONF_OFFSET("src_port_end", rlm_radius_t, src_port_end) },
109+
110+
CONF_PARSER_TERMINATOR
111+
};
112+
106113
/*
107114
* We only parse the pool options if we're connected.
108115
*/
@@ -118,6 +125,14 @@ static conf_parser_t const connected_config[] = {
118125
CONF_PARSER_TERMINATOR
119126
};
120127

128+
static conf_parser_t const unconnected_config[] = {
129+
{ FR_CONF_POINTER("udp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_unconnected_config },
130+
131+
{ FR_CONF_POINTER("tcp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_unconnected_config },
132+
133+
CONF_PARSER_TERMINATOR
134+
};
135+
121136
/*
122137
* We only parse the pool options if we're connected.
123138
*/
@@ -126,6 +141,10 @@ static conf_parser_t const pool_config[] = {
126141

127142
{ FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_radius_t, trunk_conf, trunk_config ) },
128143

144+
{ FR_CONF_POINTER("udp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_unconnected_config },
145+
146+
{ FR_CONF_POINTER("tcp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_unconnected_config },
147+
129148
CONF_PARSER_TERMINATOR
130149
};
131150

@@ -279,6 +298,7 @@ static int mode_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent,
279298

280299
case RLM_RADIUS_MODE_UNCONNECTED_REPLICATE:
281300
inst->fd_config.type = FR_BIO_FD_UNCONNECTED;
301+
if (cf_section_rules_push(cs, unconnected_config) < 0) return -1;
282302
break;
283303
}
284304

@@ -656,23 +676,104 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
656676
FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 64);
657677
FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535);
658678

659-
if (inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) {
679+
/*
680+
* Check invalid configurations.
681+
*/
682+
switch (inst->mode) {
683+
default:
660684
/*
661-
* These limits are specific to RADIUS, and cannot be over-ridden
685+
* Filenames are write-only, and cannot get response packets.
686+
*/
687+
if (inst->fd_config.filename) {
688+
cf_log_err(conf, "Cannot set 'filename' here - it is only supported for 'mode=replicate'.");
689+
return -1;
690+
}
691+
692+
/*
693+
* For normal proxying or originating client packets, we need to be able to open multiple
694+
* source ports. So the admin can't force a particular source port.
695+
*/
696+
if (inst->fd_config.src_port && (inst->mode != RLM_RADIUS_MODE_XLAT_PROXY)) {
697+
cf_log_err(conf, "Cannot set 'src_port' when sending packets to a static destination");
698+
return -1;
699+
}
700+
701+
/*
702+
* No src_port range, we don't need to check any other settings.
703+
*/
704+
if (!inst->src_port_start && !inst->src_port_end) break;
705+
706+
if (inst->fd_config.path) {
707+
cf_log_err(conf, "Cannot set 'src_port_start' or 'src_port_end' for outgoing Unix sockets");
708+
return -1;
709+
}
710+
711+
/*
712+
* We can only have one method of allocating source ports.
713+
*/
714+
if (inst->fd_config.src_port) {
715+
cf_log_err(conf, "Cannot set 'src_port' and 'src_port_start' or 'src_port_end'");
716+
return -1;
717+
}
718+
719+
/*
720+
* Cross-check src_port, src_port_start, and src_port_end.
721+
*/
722+
if (inst->src_port_start) {
723+
if (!inst->src_port_end) {
724+
cf_log_err(conf, "Range has 'src_port_start', but is missing 'src_port_end'");
725+
return -1;
726+
}
727+
728+
if (inst->src_port_start >= inst->src_port_end) {
729+
cf_log_err(conf, "Range has invalid values for 'src_port_start' ... 'src_port_end'");
730+
return -1;
731+
}
732+
733+
} else if (inst->src_port_end) {
734+
cf_log_err(conf, "Range has 'src_port_end', but is missing 'src_port_start'");
735+
return -1;
736+
}
737+
738+
/*
739+
* Encorce limits per trunk, due to the 8-bit ID space.
662740
*/
663741
FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, >=, 2);
664742
FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, <=, 255);
665743
FR_INTEGER_BOUND_CHECK("trunk.per_connection_target", inst->trunk_conf.target_req_per_conn, <=, inst->trunk_conf.max_req_per_conn / 2);
666-
}
744+
break;
667745

668-
if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) ||
669-
(inst->mode == RLM_RADIUS_MODE_XLAT_PROXY)) {
670-
if (inst->fd_config.src_port != 0) {
671-
cf_log_err(conf, "Cannot set 'src_port' when using this 'mode'");
746+
case RLM_RADIUS_MODE_UNCONNECTED_REPLICATE:
747+
/*
748+
* Replication to dynamic filenames or dynamic unix sockets isn't supported.
749+
*/
750+
if (inst->fd_config.filename || inst->fd_config.path) {
751+
cf_log_err(conf, "Cannot set 'filename' or 'path' when using 'mode=unconnected-replicate'");
752+
return -1;
753+
}
754+
FALL_THROUGH;
755+
756+
case RLM_RADIUS_MODE_REPLICATE:
757+
/*
758+
* We can force the source port, but then we have to set SO_REUSEPORT.
759+
*/
760+
inst->fd_config.reuse_port = (inst->fd_config.src_port != 0);
761+
762+
/*
763+
* Files and unix sockets are OK. The src_port can be set (or not), and that's fine.
764+
*/
765+
if (inst->src_port_start || inst->src_port_end) {
766+
cf_log_err(conf, "Cannot set 'src_port_start' or 'src_port_end' when replicating packets");
672767
return -1;
673768
}
769+
break;
674770
}
675771

772+
/*
773+
* We allow what may otherwise be conflicting configurations, because the BIO code will pick one
774+
* path, and the conflicts won't affect anything else. Only the src_port range is special.
775+
*/
776+
676777
FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, >=, fr_time_delta_from_sec(1));
677778
FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, <=, fr_time_delta_from_sec(120));
678779

src/modules/rlm_radius/rlm_radius.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ struct rlm_radius_s {
6464
uint32_t max_packet_size; //!< Maximum packet size.
6565
uint16_t max_send_coalesce; //!< Maximum number of packets to coalesce into one mmsg call.
6666

67+
uint16_t src_port_start; //!< control the source port range for dynamic destinations
68+
uint16_t src_port_end;
69+
6770
fr_radius_ctx_t common_ctx;
6871

6972
bool replicate; //!< Ignore responses.

0 commit comments

Comments
 (0)