Have the server automatically create proxy listeners
authorAlan T. DeKok <aland@freeradius.org>
Sat, 23 May 2009 07:08:43 +0000 (09:08 +0200)
committerAlan T. DeKok <aland@freeradius.org>
Sat, 23 May 2009 07:08:43 +0000 (09:08 +0200)
When src_ipaddr is set.

Also fix a bug in parsing the src IP address.  For now, it's not
IPv6 capable (sorry)

raddb/proxy.conf
raddb/radiusd.conf.in
src/include/radiusd.h
src/include/realms.h
src/main/event.c
src/main/listen.c
src/main/realms.c

index 4839ba2..4f49b7a 100644 (file)
@@ -194,11 +194,9 @@ 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.
+       #  proxying requests to this home server.  When the src_ipaddr
+       #  it set, the server will automatically create a proxy
+       #  listener for that IP address.
        #
        #  If you specify this field for one home server, you will
        #  likely need to specify it for ALL home servers.
index 9d23c6e..0bfbc6b 100644 (file)
@@ -261,10 +261,9 @@ listen {
        #    * 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.
+       #  in the sample "home_server" section.  When you specify the
+       #  source IP address for packets sent to a home server, the
+       #  proxy listeners are automatically created.
 
        #  IP address on which to listen.
        #  Allowed values are:
index 563d3ae..6a9dadf 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(fr_ipaddr_t *ipaddr);
+rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists);
 RADCLIENT *client_listener_find(const rad_listen_t *listener,
                                const fr_ipaddr_t *ipaddr, int src_port);
 #ifdef WITH_STATS
index ef4524e..3baf2b3 100644 (file)
@@ -129,6 +129,7 @@ REALM *realm_find2(const char *name); /* ... with name taken from realm_find */
 
 home_server *home_server_ldb(const char *realmname, home_pool_t *pool, REQUEST *request);
 home_server *home_server_find(fr_ipaddr_t *ipaddr, int port);
+int    home_server_create_listeners(void *head);
 #ifdef WITH_COA
 home_server *home_server_byname(const char *name);
 #endif
index ceea19a..523a505 100644 (file)
@@ -287,7 +287,7 @@ static int proxy_id_alloc(REQUEST *request, RADIUS_PACKET *packet)
         *      it to the tail of the list of listeners.  With
         *      some care, this can be thread-safe.
         */
-       proxy_listener = proxy_new_listener(&packet->src_ipaddr);
+       proxy_listener = proxy_new_listener(&packet->src_ipaddr, FALSE);
        if (!proxy_listener) {
                RDEBUG2("ERROR: Failed to create a new socket for proxying requests.");
                return 0;
@@ -764,7 +764,6 @@ static void ping_home_server(void *ctx)
        VALUE_PAIR *vp;
 
        if (home->state == HOME_STATE_ALIVE) {
-               radlog(L_INFO, "Suspicious proxy state... continuing");
                return;
        }
 
@@ -3018,6 +3017,11 @@ REQUEST *received_proxy_response(RADIUS_PACKET *packet)
         *      Having it here means that late or duplicate proxy
         *      replies no longer get the home server marked as
         *      "alive".  This might be good for stability, though.
+        *
+        *      FIXME: Do we really want to do this whenever we
+        *      receive a packet?  Setting this here means that we
+        *      mark it alive on *any* packet, even if it's lost all
+        *      of the *other* packets in the last 10s.
         */
        request->home_server->state = HOME_STATE_ALIVE;
        
index fe81ef8..3c9d0ef 100644 (file)
@@ -1641,19 +1641,15 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
 /*
  *     Externally visible function for creating a new proxy LISTENER.
  *
- *     For now, don't take ipaddr or port.
- *
  *     Not thread-safe, but all calls to it are protected by the
- *     proxy mutex in request_list.c
+ *     proxy mutex in event.c
  */
-rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr)
+rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
 {
        int last_proxy_port, port;
        rad_listen_t *this, *tmp, **last;
        listen_socket_t *sock, *old;
 
-       this = listen_alloc(RAD_LISTEN_PROXY);
-
        /*
         *      Find an existing proxy socket to copy.
         */
@@ -1670,10 +1666,15 @@ rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr)
 
                /*
                 *      If we were asked to copy a specific one, do
-                *      so.
+                *      so.  If we're just finding one that already
+                *      exists, return a pointer to it.  Otherwise,
+                *      create ANOTHER one with the same IP address.
                 */
                if ((ipaddr->af != AF_UNSPEC) &&
-                   (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) continue;
+                   (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) {
+                       if (exists) return tmp;
+                       continue;
+               }
                
                if (sock->port > last_proxy_port) {
                        last_proxy_port = sock->port + 1;
@@ -1683,27 +1684,48 @@ rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr)
                last = &(tmp->next);
        }
 
-       if (!old) {             /* This is a serious error. */
-               listen_free(&this);
-               return NULL;
-       }
+       if (!old) {
+               /*
+                *      The socket MUST already exist if we're binding
+                *      to an address while proxying.
+                *
+                *      If we're initializing the server, it's OK for the
+                *      socket to NOT exist.
+                */
+               if (!exists) return NULL;
 
-       sock = this->data;
-       memcpy(&sock->ipaddr, &old->ipaddr, sizeof(sock->ipaddr));
+               this = listen_alloc(RAD_LISTEN_PROXY);
+
+               sock = this->data;
+               sock->ipaddr = *ipaddr;
+
+       } else {
+               this = listen_alloc(RAD_LISTEN_PROXY);
+               
+               sock = this->data;
+               sock->ipaddr = old->ipaddr;
+       }
 
        /*
         *      Keep going until we find an unused port.
         */
        for (port = last_proxy_port; port < 64000; port++) {
+               int rcode;
+
                sock->port = port;
-               if (listen_bind(this) == 0) {
-                       /*
-                        *      Add the new listener to the list of
-                        *      listeners.
-                        */
-                       *last = this;
-                       return this;
+
+               rcode = listen_bind(this);
+               if (rcode < 0) {
+                       listen_free(&this);
+                       return NULL;
                }
+               
+               /*
+                *      Add the new listener to the list of
+                *      listeners.
+                */
+               *last = this;
+               return this;
        }
 
        listen_free(&this);
@@ -2061,6 +2083,17 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                 */
                if (!*head) return -1;
 
+               /*
+                *      Create *additional* proxy listeners, based
+                *      on their src_ipaddr.
+                */
+               if (home_server_create_listeners(*head) != 0) return -1;
+
+               /*
+                *
+                */
+               while (*last) last = &((*last)->next);
+                               
                if (defined_proxy) goto done;
 
                /*
index 65c6dc7..1e6d524 100644 (file)
@@ -275,6 +275,7 @@ void realms_free(void)
 
 #ifdef WITH_PROXY
 static struct in_addr hs_ip4addr;
+static struct in_addr hs_srcip4addr;
 static struct in6_addr hs_ip6addr;
 static char *hs_type = NULL;
 static char *hs_check = NULL;
@@ -298,7 +299,7 @@ static CONF_PARSER home_server_config[] = {
          offsetof(home_server,secret), NULL,  NULL},
 
        { "src_ipaddr",  PW_TYPE_IPADDR,
-         offsetof(home_server,src_ipaddr), NULL,  NULL },
+         0, &hs_srcip4addr,  NULL },
 
        { "response_window", PW_TYPE_INTEGER,
          offsetof(home_server,response_window), NULL,   "30" },
@@ -384,6 +385,7 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
 
        memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
        memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
+       memset(&hs_srcip4addr, 0, sizeof(hs_srcip4addr));
        if (cf_section_parse(cs, home, home_server_config) < 0) {
                free(home);
                return 0;
@@ -505,6 +507,14 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs, int pool_type)
        free(hs_type);
        hs_type = NULL;
 
+       /*
+        *      FIXME: Add support for IPv6 source addresses
+        */
+       if (hs_srcip4addr.s_addr != htonl(INADDR_ANY)) {
+               home->src_ipaddr.af = AF_INET;
+               home->src_ipaddr.ipaddr.ip4addr = hs_srcip4addr;
+       }
+
        if (!hs_check || (strcasecmp(hs_check, "none") == 0)) {
                home->ping_check = HOME_PING_CHECK_NONE;
 
@@ -2103,3 +2113,54 @@ home_pool_t *home_pool_byname(const char *name, int type)
 }
 
 #endif
+
+#ifdef WITH_PROXY
+static int home_server_create_callback(void *ctx, void *data)
+{
+       rad_listen_t *head = ctx;
+       home_server *home = data;
+       rad_listen_t *this;
+
+       /*
+        *      If there WAS a src address defined, ensure that a
+        *      proxy listener has been defined.
+        */
+       if (home->src_ipaddr.af != AF_UNSPEC) {
+               this = proxy_new_listener(&home->src_ipaddr, TRUE);
+
+               /*
+                *      Failed to create it: Die
+                */
+               if (!this) return 1;
+
+               this->next = head->next;
+               head->next = this;
+       }
+
+       return 0;
+}
+
+/*
+ *     Taking a void* here solves some header issues.
+ */
+int home_server_create_listeners(void *ctx)
+{
+       rad_listen_t *head = ctx;
+
+       if (!home_servers_byaddr) return 0;
+
+       rad_assert(head != NULL);
+
+       /*
+        *      Add the listeners to the TAIL of the list.
+        */
+       while (head->next) head = head->next;
+
+       if (rbtree_walk(home_servers_byaddr, InOrder,
+                       home_server_create_callback, head) != 0) {
+               return -1;
+       }
+
+       return 0;
+}
+#endif