From: Alan T. DeKok Date: Thu, 21 May 2009 13:58:44 +0000 (+0200) Subject: Allow src_ipaddr to be specified for home servers X-Git-Tag: release_2_1_7~134 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=788b2eb52472135d0786e101804fc1f62120afd8;p=thirdparty%2Ffreeradius-server.git Allow src_ipaddr to be specified for home servers --- diff --git a/raddb/proxy.conf b/raddb/proxy.conf index 9f5f8b70d44..4839ba22618 100644 --- a/raddb/proxy.conf +++ b/raddb/proxy.conf @@ -192,6 +192,22 @@ home_server localhost { # ############################################################ + # + # You can optionally specify the source IP address used when + # proxying requests to this home server. When setting the + # src_ipaddr, you MUST also configure a listener section of + # type "proxy", using the same IP address. Failure to do so + # means that the server cannot proxy any requests to this + # home server. + # + # If you specify this field for one home server, you will + # likely need to specify it for ALL home servers. + # + # If you don't care about the source IP address, leave this + # entry commented. + # +# src_ipaddr = 127.0.0.1 + # RFC 5080 suggests that all clients SHOULD include it in an # Access-Request. The configuration item below tells the # proxying server (i.e. this one) whether or not the home diff --git a/raddb/radiusd.conf.in b/raddb/radiusd.conf.in index 0dd0eab0f7d..9d23c6ec87d 100644 --- a/raddb/radiusd.conf.in +++ b/raddb/radiusd.conf.in @@ -256,10 +256,15 @@ listen { # Note: "type = proxy" lets you control the source IP used for # proxying packets, with some limitations: # - # * Only ONE proxy listener can be defined. # * A proxy listener CANNOT be used in a virtual server section. # * You should probably set "port = 0". # * Any "clients" configuration will be ignored. + # + # See also proxy.conf, and the "src_ipaddr" configuration entry + # in the sample "home_server" section. It is possible to specify + # the source IP address for packets sent to a home server. In + # that situation, you MUST have a proxy listener defined with + # that IP address. # IP address on which to listen. # Allowed values are: diff --git a/src/include/radiusd.h b/src/include/radiusd.h index 850752a3e10..563d3ae6ac4 100644 --- a/src/include/radiusd.h +++ b/src/include/radiusd.h @@ -619,7 +619,7 @@ void fr_suid_down_permanent(void); /* listen.c */ void listen_free(rad_listen_t **head); int listen_init(CONF_SECTION *cs, rad_listen_t **head); -rad_listen_t *proxy_new_listener(void); +rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr); RADCLIENT *client_listener_find(const rad_listen_t *listener, const fr_ipaddr_t *ipaddr, int src_port); #ifdef WITH_STATS diff --git a/src/include/realms.h b/src/include/realms.h index 2729104bc83..ef4524e8b8f 100644 --- a/src/include/realms.h +++ b/src/include/realms.h @@ -78,6 +78,8 @@ typedef struct home_server { #ifdef WITH_STATS int number; + fr_ipaddr_t src_ipaddr; /* preferred source IP address */ + fr_stats_t stats; fr_stats_ema_t ema; diff --git a/src/lib/packet.c b/src/lib/packet.c index 57410eba397..5eb1f19a874 100644 --- a/src/lib/packet.c +++ b/src/lib/packet.c @@ -607,7 +607,7 @@ int fr_packet_list_num_elements(fr_packet_list_t *pl) int fr_packet_list_id_alloc(fr_packet_list_t *pl, RADIUS_PACKET *request) { - int i, id, start; + int i, id, start, fd; uint32_t free_mask; fr_packet_dst2id_t my_pd, *pd; fr_packet_socket_t *ps; @@ -647,6 +647,7 @@ int fr_packet_list_id_alloc(fr_packet_list_t *pl, id = start = (int) fr_rand() & 0xff; while (pd->id[id] == pl->mask) { /* all sockets are using this ID */ + redo: id++; id &= 0xff; if (id == start) return 0; @@ -654,20 +655,48 @@ int fr_packet_list_id_alloc(fr_packet_list_t *pl, free_mask = ~((~pd->id[id]) & pl->mask); - start = -1; + /* + * This ID has at least one socket free. Check the sockets + * to see if they are satisfactory for the caller. + */ + fd = -1; for (i = 0; i < MAX_SOCKETS; i++) { if (pl->sockets[i].sockfd == -1) continue; /* paranoia */ - if ((free_mask & (1 << i)) == 0) { - start = i; - break; - } + /* + * This ID is allocated. + */ + if ((free_mask & (1 << i)) != 0) continue; + + /* + * If the caller cares about the source address, + * try to re-use that. This means that the + * requested source address is set, AND this + * socket wasn't bound to "*", AND the requested + * source address is the same as this socket + * address. + */ + if ((request->src_ipaddr.af != AF_UNSPEC) && + !pl->sockets[i].inaddr_any && + (fr_ipaddr_cmp(&request->src_ipaddr, &pl->sockets[i].ipaddr) != 0)) continue; + + /* + * They asked for a specific address, and this socket + * is bound to a wildcard address. Ignore this one, too. + */ + if ((request->src_ipaddr.af != AF_UNSPEC) && + pl->sockets[i].inaddr_any) continue; + + fd = i; + break; } - if (start < 0) return 0; /* bad error */ + if (fd < 0) { + goto redo; /* keep searching IDs */ + } - pd->id[id] |= (1 << start); - ps = &pl->sockets[start]; + pd->id[id] |= (1 << fd); + ps = &pl->sockets[fd]; ps->num_outgoing++; pl->num_outgoing++; diff --git a/src/main/listen.c b/src/main/listen.c index 5623bea21b0..fe81ef82fbf 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -1646,7 +1646,7 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type) * Not thread-safe, but all calls to it are protected by the * proxy mutex in request_list.c */ -rad_listen_t *proxy_new_listener() +rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr) { int last_proxy_port, port; rad_listen_t *this, *tmp, **last; @@ -1656,35 +1656,38 @@ rad_listen_t *proxy_new_listener() /* * Find an existing proxy socket to copy. - * - * FIXME: Make it per-realm, or per-home server! */ last_proxy_port = 0; old = NULL; last = &mainconfig.listen; for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) { - if (tmp->type == RAD_LISTEN_PROXY) { - sock = tmp->data; - if (sock->port > last_proxy_port) { - last_proxy_port = sock->port + 1; - } - if (!old) old = sock; + /* + * Not proxy, ignore it. + */ + if (tmp->type != RAD_LISTEN_PROXY) continue; + + sock = tmp->data; + + /* + * If we were asked to copy a specific one, do + * so. + */ + if ((ipaddr->af != AF_UNSPEC) && + (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) continue; + + if (sock->port > last_proxy_port) { + last_proxy_port = sock->port + 1; } + if (!old) old = sock; last = &(tmp->next); } - if (!old) { + if (!old) { /* This is a serious error. */ listen_free(&this); - return NULL; /* This is a serious error. */ + return NULL; } - /* - * FIXME: find a new IP address to listen on? - * - * This could likely be done in the "home server" - * configuration, to have per-home-server source IP's. - */ sock = this->data; memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr)); diff --git a/src/main/radclient.c b/src/main/radclient.c index 5805455cab0..e31a1e2d0d4 100644 --- a/src/main/radclient.c +++ b/src/main/radclient.c @@ -490,6 +490,7 @@ static int send_one_packet(radclient_t *radclient) * this packet. */ retry: + radclient->request->src_ipaddr.af = AF_UNSPEC; rcode = fr_packet_list_id_alloc(pl, radclient->request); if (rcode < 0) { int mysockfd; diff --git a/src/main/realms.c b/src/main/realms.c index abccdbe4aae..65c6dc708ec 100644 --- a/src/main/realms.c +++ b/src/main/realms.c @@ -297,6 +297,9 @@ static CONF_PARSER home_server_config[] = { { "secret", PW_TYPE_STRING_PTR, offsetof(home_server,secret), NULL, NULL}, + { "src_ipaddr", PW_TYPE_IPADDR, + offsetof(home_server,src_ipaddr), NULL, NULL }, + { "response_window", PW_TYPE_INTEGER, offsetof(home_server,response_window), NULL, "30" }, { "max_outstanding", PW_TYPE_INTEGER, @@ -1967,6 +1970,11 @@ home_server *home_server_ldb(const char *realmname, * the 'hints' file. */ request->proxy->vps = paircopy(request->packet->vps); + + /* + * Set the source IP address for proxying. + */ + request->proxy->src_ipaddr = found->src_ipaddr; } /*