add "client-balance", which is load-balancing to home servers
authoraland <aland>
Sat, 7 Apr 2007 11:12:05 +0000 (11:12 +0000)
committeraland <aland>
Sat, 7 Apr 2007 11:12:05 +0000 (11:12 +0000)
by hashing the client source IP

src/include/radiusd.h
src/include/realms.h
src/main/event.c
src/main/realms.c

index 0800a6e..5770978 100644 (file)
@@ -15,6 +15,10 @@ RCSIDH(radiusd_h, "$Id$")
 #include <freeradius-devel/radpaths.h>
 #include <freeradius-devel/conf.h>
 #include <freeradius-devel/conffile.h>
+#include <freeradius-devel/event.h>
+
+typedef struct auth_req REQUEST;
+
 #include <freeradius-devel/realms.h>
 
 #ifdef HAVE_PTHREAD_H
@@ -45,7 +49,7 @@ typedef struct rad_listen_t rad_listen_t;
 #define REQUEST_DATA_REGEX (0xadbeef00)
 #define REQUEST_MAX_REGEX (8)
 
-typedef struct auth_req {
+struct auth_req {
 #ifndef NDEBUG
        uint32_t                magic; /* for debugging only */
 #endif
@@ -82,7 +86,7 @@ typedef struct auth_req {
        int                     child_state;
 
        struct timeval          next_when;
-       void                    *next_callback;
+       lrad_event_callback_t   next_callback;
 
        int                     in_request_hash;
        int                     in_proxy_hash;
@@ -94,7 +98,7 @@ typedef struct auth_req {
        int                     num_proxied_requests;
        int                     num_proxied_responses;
 
-} REQUEST;
+};                             /* REQUEST typedef */
 
 #define RAD_REQUEST_OPTION_NONE            (0)
 
index 12f57ce..0fedb18 100644 (file)
@@ -66,7 +66,8 @@ typedef struct home_server {
 typedef enum home_pool_type_t {
        HOME_POOL_INVALID = 0,
        HOME_POOL_LOAD_BALANCE,
-       HOME_POOL_FAIL_OVER
+       HOME_POOL_FAIL_OVER,
+       HOME_POOL_CLIENT_BALANCE
 } home_pool_type_t;
 
 
@@ -95,7 +96,7 @@ void realms_free(void);
 int realm_add(const char *filename, CONF_SECTION *cs);
 REALM *realm_find(const char *name);
 
-home_server *home_server_ldb(REALM *realm, int code);
+home_server *home_server_ldb(REALM *realm, REQUEST *request);
 home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port);
 
 #endif /* REALMS_H */
index 50ddc92..22c858d 100644 (file)
@@ -372,7 +372,8 @@ static void wait_for_child_to_die(void *ctx)
        remove_from_request_hash(request);
 
        if (request->proxy) {
-               return wait_for_proxy_id_to_expire(request);
+               wait_for_proxy_id_to_expire(request);
+               return;
        }
 
        request_free(&request);
@@ -389,7 +390,8 @@ static void cleanup_delay(void *ctx)
        remove_from_request_hash(request);
 
        if (request->proxy) {
-               return wait_for_proxy_id_to_expire(request);
+               wait_for_proxy_id_to_expire(request);
+               return;
        }
 
        DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
@@ -744,7 +746,8 @@ static void wait_a_bit(void *ctx)
        case REQUEST_DONE:
                request->child_pid = NO_SUCH_CHILD_PID;
                if (request->proxy) {
-                       return wait_for_proxy_id_to_expire(request);
+                       wait_for_proxy_id_to_expire(request);
+                       return;
                }
                return;
 
@@ -939,7 +942,7 @@ static int successfully_proxied_request(REQUEST *request)
                return 0;
        }
        
-       home = home_server_ldb(realm, request->packet->code);
+       home = home_server_ldb(realm, request);
        if (!home) {
                DEBUG2("ERROR: Failed to find live home server for realm %s",
                       realmname);
index 6acae67..3637a1e 100644 (file)
@@ -444,6 +444,15 @@ static int server_pool_add(const char *filename, CONF_SECTION *cs)
 
        cp = cf_pair_find(cs, "type");
        if (cp) {
+               static LRAD_NAME_NUMBER pool_types[] = {
+                       { "load-balance", HOME_POOL_LOAD_BALANCE },
+                       { "fail-over", HOME_POOL_FAIL_OVER },
+                       { "round_robin", HOME_POOL_LOAD_BALANCE },
+                       { "fail_over", HOME_POOL_FAIL_OVER },
+                       { "client-balance", HOME_POOL_CLIENT_BALANCE },
+                       { NULL, 0 }
+               };
+
                value = cf_pair_value(cp);
                if (!value) {
                        radlog(L_ERR, "%s[%d]: No value given for type.",
@@ -452,13 +461,8 @@ static int server_pool_add(const char *filename, CONF_SECTION *cs)
                        return 0;
                }
 
-               if (strcmp(value, "load-balance") == 0) {
-                       pool->type = HOME_POOL_LOAD_BALANCE;
-
-               } else if (strcmp(value, "fail-over") == 0) {
-                       pool->type = HOME_POOL_FAIL_OVER;
-
-               } else {
+               pool->type = lrad_str2int(pool_types, value, 0);
+               if (!pool->type) {
                        radlog(L_ERR, "%s[%d]: Unknown type \"%s\".",
                               filename, cf_pair_lineno(cp), value);
                        free(pool);
@@ -995,16 +999,16 @@ REALM *realm_find(const char *name)
 }
 
 
-home_server *home_server_ldb(REALM *r, int code)
+home_server *home_server_ldb(REALM *r, REQUEST *request)
 {
-       uint32_t        count;
-       home_server     *lb;
+       int             start;
+       int             count;
        home_pool_t     *pool;
 
-       if (code == PW_AUTHENTICATION_REQUEST) {
+       if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
                pool = r->auth_pool;
 
-       } else if (code == PW_ACCOUNTING_REQUEST) {
+       } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
                pool = r->acct_pool;
 
        } else {
@@ -1017,57 +1021,75 @@ home_server *home_server_ldb(REALM *r, int code)
                return NULL;
        }
 
-       lb = NULL;
-
-       for (count = 0; count < pool->num_home_servers; count++) {
-               home_server *home = pool->servers[count];
+       start = 0;
 
-               if (home->state == HOME_STATE_IS_DEAD) {
-                       continue;
-               }
+       /*
+        *      Determine how to pick choose the home server.
+        */
+       switch (pool->type) {
+               uint32_t hash;
 
                /*
-                *      This home server is too busy.  Choose another one.
+                *      Load balancing.  Pick one at random.
                 */
-               if (home->currently_outstanding >= home->max_outstanding) {
-                       continue;
-               }
-               
+       case HOME_POOL_LOAD_BALANCE:
+               hash = lrad_rand();
+               start = hash % pool->num_home_servers;
+               break;
+
                /*
-                *      Fail-over, pick the first one that matches.
+                *      For load-balancing by client IP address, we
+                *      pick a home server by hashing the client IP.
+                *
+                *      This isn't as even a load distribution as
+                *      tracking the State attribute, but it's better
+                *      than nothing.
                 */
-               if (pool->type == HOME_POOL_FAIL_OVER) {
-                       return home;
+       case HOME_POOL_CLIENT_BALANCE:
+               switch (request->packet->src_ipaddr.af) {
+               case AF_INET:
+                       hash = lrad_hash(&request->packet->src_ipaddr.ipaddr.ip4addr,
+                                        sizeof(request->packet->src_ipaddr.ipaddr.ip4addr));
+                       break;
+               case AF_INET6:
+                       hash = lrad_hash(&request->packet->src_ipaddr.ipaddr.ip6addr,
+                                        sizeof(request->packet->src_ipaddr.ipaddr.ip6addr));
+                       break;
+               default:
+                       hash = 0;
+                       break;
                }
+               start = hash % pool->num_home_servers;
+               break;
 
-               /*
-                *      FUTURE: If we're well into "zombie_period",
-                *      then we probably want to think about
-                *      load-balancing the packet somewhere else, as
-                *      the home server is probably dead.
-                */
+       default:
+               start = 0;
+               break;
+       }
 
-               /*
-                *      We're doing load-balancing.  Pick a random
-                *      number, which will be used to determine which
-                *      home server is chosen.
-                */
-               if (!lb) {
-                       lb = home;
+       /*
+        *      Starting with the home server we chose, loop through
+        *      all home servers.  If the current one is dead, skip
+        *      it.  If it is too busy, skip it.
+        *
+        *      Otherwise, use it.
+        */
+       for (count = 0; count < pool->num_home_servers; count++) {
+               home_server *home = pool->servers[(start + count) % pool->num_home_servers];
+
+               if (home->state == HOME_STATE_IS_DEAD) {
                        continue;
                }
 
                /*
-                *      See the "camel book" for why this works.
-                *
-                *      If (rand(0..n) < 1), pick the current server.
-                *      We add a scale factor of 65536, to avoid
-                *      floating point.
+                *      This home server is too busy.  Choose another one.
                 */
-               if (((count + 1) * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
-                       lb = home;
+               if (home->currently_outstanding >= home->max_outstanding) {
+                       continue;
                }
-       } /* loop over the realms */
+
+               return home;
+       } /* loop over the home servers */
 
        /*
         *      No live match found, and no fallback to the "DEFAULT"
@@ -1076,9 +1098,10 @@ home_server *home_server_ldb(REALM *r, int code)
         *      "pings", as they will be marked live when they
         *      actually are live.
         */
-       if (!lb &&
-           !mainconfig.proxy_fallback &&
+       if (!mainconfig.proxy_fallback &&
            mainconfig.wake_all_if_all_dead) {
+               home_server *lb = NULL;
+
                for (count = 0; count < pool->num_home_servers; count++) {
                        home_server *home = pool->servers[count];
 
@@ -1088,28 +1111,29 @@ home_server *home_server_ldb(REALM *r, int code)
                                if (!lb) lb = home;
                        }
                }
+
+               if (lb) return lb;
        }
 
        /*
         *      Still nothing.  Look up the DEFAULT realm, but only
         *      if we weren't looking up the NULL or DEFAULT realms.
         */
-       if (!lb &&
-           mainconfig.proxy_fallback &&
+       if (mainconfig.proxy_fallback &&
            (strcmp(r->name, "NULL") != 0) &&
            (strcmp(r->name, "DEFAULT") != 0)) {
                REALM *rd = realm_find("DEFAULT");
 
                if (rd) {
                        DEBUG2("  Realm %s has no live home servers.  Falling back to the DEFAULT realm.", r->name);
-                       return home_server_ldb(rd, code);
+                       return home_server_ldb(rd, request);
                }
        }
 
        /*
-        *      Return the appropriate home server.
+        *      Still haven't found anything.  Oh well.
         */
-       return lb;
+       return NULL;
 }