Allow src_ipaddr to be specified for home servers
authorAlan T. DeKok <aland@freeradius.org>
Thu, 21 May 2009 13:58:44 +0000 (15:58 +0200)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 21 May 2009 13:58:44 +0000 (15:58 +0200)
raddb/proxy.conf
raddb/radiusd.conf.in
src/include/radiusd.h
src/include/realms.h
src/lib/packet.c
src/main/listen.c
src/main/radclient.c
src/main/realms.c

index 9f5f8b7..4839ba2 100644 (file)
@@ -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
index 0dd0eab..9d23c6e 100644 (file)
@@ -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:
index 850752a..563d3ae 100644 (file)
@@ -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
index 2729104..ef4524e 100644 (file)
@@ -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;
index 57410eb..5eb1f19 100644 (file)
@@ -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++;
index 5623bea..fe81ef8 100644 (file)
@@ -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));
 
index 5805455..e31a1e2 100644 (file)
@@ -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;
index abccdbe..65c6dc7 100644 (file)
@@ -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;
                }
 
                /*