Author: sem (ports committer)
Date: Thu Dec  6 19:00:37 2012
New Revision: 243956
URL: http://svnweb.freebsd.org/changeset/base/243956

Log:
  - Rewrite radius servers traversal algorithm.
  - Add functions for working with IPv6 attributes.
  
  Approved by:  ae

Modified:
  head/lib/libradius/Makefile
  head/lib/libradius/libradius.3
  head/lib/libradius/radius.conf.5
  head/lib/libradius/radlib.c
  head/lib/libradius/radlib.h
  head/lib/libradius/radlib_private.h
  head/lib/libradius/radlib_vs.h

Modified: head/lib/libradius/Makefile
==============================================================================
--- head/lib/libradius/Makefile Thu Dec  6 18:51:18 2012        (r243955)
+++ head/lib/libradius/Makefile Thu Dec  6 19:00:37 2012        (r243956)
@@ -35,6 +35,7 @@ MAN=          libradius.3 radius.conf.5
 
 MLINKS+=libradius.3 rad_acct_open.3 \
        libradius.3 rad_add_server.3 \
+       libradius.3 rad_add_server_ex.3 \
        libradius.3 rad_auth_open.3 \
        libradius.3 rad_bind_to.3 \
        libradius.3 rad_close.3 \

Modified: head/lib/libradius/libradius.3
==============================================================================
--- head/lib/libradius/libradius.3      Thu Dec  6 18:51:18 2012        
(r243955)
+++ head/lib/libradius/libradius.3      Thu Dec  6 19:00:37 2012        
(r243956)
@@ -37,6 +37,8 @@
 .Fn rad_acct_open "void"
 .Ft int
 .Fn rad_add_server "struct rad_handle *h" "const char *host" "int port" "const 
char *secret" "int timeout" "int max_tries"
+.Ft int
+.Fn rad_add_server_ex "struct rad_handle *h" "const char *host" "int port" 
"const char *secret" "int timeout" "int max_tries" "int dead_time" "struct 
in_addr bindto"
 .Ft "struct rad_handle *"
 .Fn rad_auth_open "void"
 .Ft void
@@ -153,7 +155,12 @@ is used.
 returns 0 on success, or \-1 if an error occurs.
 .Pp
 The library can also be configured programmatically by calls to
-.Fn rad_add_server .
+.Fn rad_add_server
+or
+.Fn rad_add_server_ex .
+.Fn rad_add_server
+is a backward compatible function, implemented via
+.Fn rad_add_server_ex .
 The
 .Fa host
 parameter specifies the server host, either as a fully qualified
@@ -188,11 +195,20 @@ The maximum number of repeated
 requests to make before giving up is passed into the
 .Fa max_tries
 parameter.
+Time interval in seconds when the server will not be requested
+if it is marked as dead (did not answer on the last try) set with
+.Fa dead_time
+parameter.
+.Fa bindto
+parameter is an IP address on the multihomed host that is used as
+a source address for all requests.
 .Fn rad_add_server
 returns 0 on success, or \-1 if an error occurs.
 .Pp
 .Fn rad_add_server
-may be called multiple times, and it may be used together with
+or
+.Fn rad_add_server_ex
+may be called multiple times, and they may be used together with
 .Fn rad_config .
 At most 10 servers may be specified.
 When multiple servers are given, they are tried in round-robin

Modified: head/lib/libradius/radius.conf.5
==============================================================================
--- head/lib/libradius/radius.conf.5    Thu Dec  6 18:51:18 2012        
(r243955)
+++ head/lib/libradius/radius.conf.5    Thu Dec  6 19:00:37 2012        
(r243956)
@@ -44,7 +44,7 @@ Leading
 white space is ignored, as are empty lines and lines containing
 only comments.
 .Pp
-A RADIUS server is described by three to five fields on a line:
+A RADIUS server is described by three to seven fields on a line:
 .Pp
 .Bl -item -offset indent -compact
 .It
@@ -57,6 +57,10 @@ Shared secret
 Timeout
 .It
 Retries
+.It
+Dead time
+.It
+Bind address
 .El
 .Pp
 The fields are separated by white space.
@@ -139,6 +143,13 @@ If omitted, it defaults to 3 attempts.
 Note,
 this is the total number of attempts and not the number of retries.
 .Pp
+The sixth field contains a decimal integer specifying a time interval
+in seconds when the server will not requested if it was inaccessible
+on the last try. 0 means ask always.
+.Pp
+The seventh field contains an IP address on multihomed host. All
+requests will be binded to this IP.
+.Pp
 Up to 10 RADIUS servers may be specified for each service type.
 The servers are tried in
 round-robin fashion, until a valid response is received or the
@@ -161,6 +172,9 @@ acct  radius1.domain.com  OurLittleSecre
 # timeout and maximum tries:
 auth  auth.domain.com:1645  "I can't see you"  5  4
 
+# As above but set dead time and bind address
+auth  auth.domain.com:1645  "I can't see you"  5  4  60  192.168.1.8
+
 # A server specified by its IP address:
 auth  192.168.27.81  $X*#..38947ax-+=
 .Ed

Modified: head/lib/libradius/radlib.c
==============================================================================
--- head/lib/libradius/radlib.c Thu Dec  6 18:51:18 2012        (r243955)
+++ head/lib/libradius/radlib.c Thu Dec  6 19:00:37 2012        (r243956)
@@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
 #include <md5.h>
 #endif
 
+#define        MAX_FIELDS      7
+
 /* We need the MPPE_KEY_LEN define */
 #include <netgraph/ng_mppc.h>
 
@@ -379,6 +381,18 @@ int
 rad_add_server(struct rad_handle *h, const char *host, int port,
     const char *secret, int timeout, int tries)
 {
+       struct in_addr bindto;
+       bindto.s_addr = INADDR_ANY;
+
+       return rad_add_server_ex(h, host, port, secret, timeout, tries,
+               DEAD_TIME, &bindto);
+}
+
+int
+rad_add_server_ex(struct rad_handle *h, const char *host, int port,
+    const char *secret, int timeout, int tries, int dead_time,
+    struct in_addr *bindto)
+{
        struct rad_server *srvp;
 
        if (h->num_servers >= MAXSERVERS) {
@@ -421,6 +435,10 @@ rad_add_server(struct rad_handle *h, con
        srvp->timeout = timeout;
        srvp->max_tries = tries;
        srvp->num_tries = 0;
+       srvp->is_dead = 0;
+       srvp->dead_time = dead_time;
+       srvp->next_probe = 0;
+       srvp->bindto = bindto->s_addr;
        h->num_servers++;
        return 0;
 }
@@ -441,6 +459,13 @@ rad_close(struct rad_handle *h)
        free(h);
 }
 
+void
+rad_bind_to(struct rad_handle *h, in_addr_t addr)
+{
+
+       h->bindto = addr;
+}
+
 int
 rad_config(struct rad_handle *h, const char *path)
 {
@@ -468,11 +493,15 @@ rad_config(struct rad_handle *h, const c
                char *secret;
                char *timeout_str;
                char *maxtries_str;
+               char *dead_time_str;
+               char *bindto_str;
                char *end;
                char *wanttype;
                unsigned long timeout;
                unsigned long maxtries;
+               unsigned long dead_time;
                int port;
+               struct in_addr bindto;
                int i;
 
                linenum++;
@@ -491,7 +520,7 @@ rad_config(struct rad_handle *h, const c
                buf[len - 1] = '\0';
 
                /* Extract the fields from the line. */
-               nfields = split(buf, fields, 5, msg, sizeof msg);
+               nfields = split(buf, fields, MAX_FIELDS, msg, sizeof msg);
                if (nfields == -1) {
                        generr(h, "%s:%d: %s", path, linenum, msg);
                        retval = -1;
@@ -507,7 +536,7 @@ rad_config(struct rad_handle *h, const c
                 */
                if (strcmp(fields[0], "auth") != 0 &&
                    strcmp(fields[0], "acct") != 0) {
-                       if (nfields >= 5) {
+                       if (nfields >= MAX_FIELDS) {
                                generr(h, "%s:%d: invalid service type", path,
                                    linenum);
                                retval = -1;
@@ -529,6 +558,8 @@ rad_config(struct rad_handle *h, const c
                secret = fields[2];
                timeout_str = fields[3];
                maxtries_str = fields[4];
+               dead_time_str = fields[5];
+               bindto_str = fields[6];
 
                /* Ignore the line if it is for the wrong service type. */
                wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
@@ -570,8 +601,30 @@ rad_config(struct rad_handle *h, const c
                } else
                        maxtries = MAXTRIES;
 
-               if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
-                   -1) {
+               if (dead_time_str != NULL) {
+                       dead_time = strtoul(dead_time_str, &end, 10);
+                       if (*end != '\0') {
+                               generr(h, "%s:%d: invalid dead_time", path,
+                                   linenum);
+                               retval = -1;
+                               break;
+                       }
+               } else
+                       dead_time = DEAD_TIME;
+
+               if (bindto_str != NULL) {
+                       bindto.s_addr = inet_addr(bindto_str);
+                       if (bindto.s_addr == INADDR_NONE) {
+                               generr(h, "%s:%d: invalid bindto", path,
+                                   linenum);
+                               retval = -1;
+                               break;
+                       }
+               } else
+                       bindto.s_addr = INADDR_ANY;
+
+               if (rad_add_server_ex(h, host, port, secret, timeout, maxtries,
+                           dead_time, &bindto) == -1) {
                        strcpy(msg, h->errmsg);
                        generr(h, "%s:%d: %s", path, linenum, msg);
                        retval = -1;
@@ -596,7 +649,9 @@ int
 rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
                           struct timeval *tv)
 {
-       int n;
+       int n, cur_srv;
+       time_t now;
+       struct sockaddr_in sin;
 
        if (h->type == RADIUS_SERVER) {
                generr(h, "denied function call");
@@ -621,19 +676,61 @@ rad_continue_send_request(struct rad_han
                }
        }
 
-       if (h->try == h->total_tries) {
-               generr(h, "No valid RADIUS responses received");
-               return -1;
-       }
-
        /*
          * Scan round-robin to the next server that has some
          * tries left.  There is guaranteed to be one, or we
          * would have exited this loop by now.
         */
-       while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
-               if (++h->srv >= h->num_servers)
-                       h->srv = 0;
+       cur_srv = h->srv;
+       now = time(NULL);
+       if (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries) {
+               /* Set next probe time for this server */
+               if (h->servers[h->srv].dead_time) {
+                       h->servers[h->srv].is_dead = 1;
+                       h->servers[h->srv].next_probe = now +
+                           h->servers[h->srv].dead_time;
+               }
+               do {
+                       h->srv++;
+                       if (h->srv >= h->num_servers)
+                               h->srv = 0;
+                       if (h->servers[h->srv].is_dead == 0)
+                               break;
+                       if (h->servers[h->srv].dead_time &&
+                           h->servers[h->srv].next_probe <= now) {
+                               h->servers[h->srv].is_dead = 0;
+                               h->servers[h->srv].num_tries = 0;
+                               break;
+                       }
+               } while (h->srv != cur_srv);
+
+               if (h->srv == cur_srv) {
+                       generr(h, "No valid RADIUS responses received");
+                       return (-1);
+               }
+       }
+
+       /* Rebind */
+       if (h->bindto != h->servers[h->srv].bindto) {
+               h->bindto = h->servers[h->srv].bindto;
+               close(h->fd);
+               if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+                       generr(h, "Cannot create socket: %s", strerror(errno));
+                       return -1;
+               }
+               memset(&sin, 0, sizeof sin);
+               sin.sin_len = sizeof sin;
+               sin.sin_family = AF_INET;
+               sin.sin_addr.s_addr = h->bindto;
+               sin.sin_port = 0;
+               if (bind(h->fd, (const struct sockaddr *)&sin,
+                   sizeof sin) == -1) {
+                       generr(h, "bind: %s", strerror(errno));
+                       close(h->fd);
+                       h->fd = -1;
+                       return (-1);
+               }
+       }
 
        if (h->out[POS_CODE] == RAD_ACCESS_REQUEST) {
                /* Insert the scrambled password into the request */
@@ -641,9 +738,11 @@ rad_continue_send_request(struct rad_han
                        insert_scrambled_password(h, h->srv);
        }
        insert_message_authenticator(h, 0);
+
        if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) {
                /* Insert the request authenticator into the request */
-               insert_request_authenticator(h, h->srv);
+               memset(&h->out[POS_AUTH], 0, LEN_AUTH);
+               insert_request_authenticator(h, 0);
        }
 
        /* Send the request */
@@ -654,7 +753,6 @@ rad_continue_send_request(struct rad_han
                tv->tv_sec = 1; /* Do not wait full timeout if send failed. */
        else
                tv->tv_sec = h->servers[h->srv].timeout;
-       h->try++;
        h->servers[h->srv].num_tries++;
        tv->tv_usec = 0;
        *fd = h->fd;
@@ -740,6 +838,10 @@ rad_create_request(struct rad_handle *h,
                generr(h, "denied function call");
                return (-1);
        }
+       if (h->num_servers == 0) {
+               generr(h, "No RADIUS servers specified");
+               return (-1);
+       }
        h->out[POS_CODE] = code;
        h->out[POS_IDENT] = ++h->ident;
        if (code == RAD_ACCESS_REQUEST) {
@@ -756,16 +858,9 @@ rad_create_request(struct rad_handle *h,
        clear_password(h);
        h->authentic_pos = 0;
        h->out_created = 1;
-       h->bindto = INADDR_ANY;
        return 0;
 }
 
-void
-rad_bind_to(struct rad_handle *h, in_addr_t addr)
-{
-    h->bindto = addr;
-}
-
 int
 rad_create_response(struct rad_handle *h, int code)
 {
@@ -793,6 +888,15 @@ rad_cvt_addr(const void *data)
        return value;
 }
 
+struct in6_addr
+rad_cvt_addr6(const void *data)
+{
+       struct in6_addr value;
+
+       memcpy(&value.s6_addr, data, sizeof value.s6_addr);
+       return value;
+}
+
 u_int32_t
 rad_cvt_int(const void *data)
 {
@@ -848,6 +952,8 @@ int
 rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
 {
        int srv;
+       time_t now;
+       struct sockaddr_in sin;
 
        if (h->type == RADIUS_SERVER) {
                generr(h, "denied function call");
@@ -855,8 +961,6 @@ rad_init_send_request(struct rad_handle 
        }
        /* Make sure we have a socket to use */
        if (h->fd == -1) {
-               struct sockaddr_in sin;
-
                if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
                        generr(h, "Cannot create socket: %s", strerror(errno));
                        return -1;
@@ -902,21 +1006,30 @@ rad_init_send_request(struct rad_handle 
        h->out[POS_LENGTH] = h->out_len >> 8;
        h->out[POS_LENGTH+1] = h->out_len;
 
-       /*
-        * Count the total number of tries we will make, and zero the
-        * counter for each server.
-        */
-       h->total_tries = 0;
-       for (srv = 0;  srv < h->num_servers;  srv++) {
-               h->total_tries += h->servers[srv].max_tries;
+       h->srv = 0;
+       now = time(NULL);
+       for (srv = 0;  srv < h->num_servers;  srv++)
                h->servers[srv].num_tries = 0;
-       }
-       if (h->total_tries == 0) {
-               generr(h, "No RADIUS servers specified");
-               return -1;
+       /* Find a first good server. */
+       for (srv = 0;  srv < h->num_servers;  srv++) {
+               if (h->servers[srv].is_dead == 0)
+                       break;
+               if (h->servers[srv].dead_time &&
+                   h->servers[srv].next_probe <= now) {
+                       h->servers[srv].is_dead = 0;
+                       break;
+               }
+               h->srv++;
        }
 
-       h->try = h->srv = 0;
+       /* If all servers was dead on the last probe, try from beginning */
+       if (h->srv == h->num_servers) {
+               for (srv = 0;  srv < h->num_servers;  srv++) {
+                       h->servers[srv].is_dead = 0;
+                       h->servers[srv].next_probe = 0;
+               }
+               h->srv = 0;
+       }
 
        return rad_continue_send_request(h, 0, fd, tv);
 }
@@ -946,6 +1059,7 @@ rad_auth_open(void)
                h->type = RADIUS_AUTH;
                h->out_created = 0;
                h->eap_msg = 0;
+               h->bindto = INADDR_ANY;
        }
        return h;
 }
@@ -987,6 +1101,13 @@ rad_put_addr(struct rad_handle *h, int t
 }
 
 int
+rad_put_addr6(struct rad_handle *h, int type, struct in6_addr addr)
+{
+
+       return rad_put_attr(h, type, &addr.s6_addr, sizeof addr.s6_addr);
+}
+
+int
 rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
 {
        int result;
@@ -1229,6 +1350,15 @@ rad_put_vendor_addr(struct rad_handle *h
 }
 
 int
+rad_put_vendor_addr6(struct rad_handle *h, int vendor, int type,
+    struct in6_addr addr)
+{
+
+       return (rad_put_vendor_attr(h, vendor, type, &addr.s6_addr,
+           sizeof addr.s6_addr));
+}
+
+int
 rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
     const void *value, size_t len)
 {

Modified: head/lib/libradius/radlib.h
==============================================================================
--- head/lib/libradius/radlib.h Thu Dec  6 18:51:18 2012        (r243955)
+++ head/lib/libradius/radlib.h Thu Dec  6 19:00:37 2012        (r243956)
@@ -194,6 +194,9 @@ __BEGIN_DECLS
 struct rad_handle      *rad_acct_open(void);
 int                     rad_add_server(struct rad_handle *,
                            const char *, int, const char *, int, int);
+int                     rad_add_server_ex(struct rad_handle *,
+                           const char *, int, const char *, int, int,
+                           int, struct in_addr *);
 struct rad_handle      *rad_auth_open(void);
 void                    rad_bind_to(struct rad_handle *, in_addr_t);
 void                    rad_close(struct rad_handle *);
@@ -203,6 +206,7 @@ int                  rad_continue_send_request(struct 
 int                     rad_create_request(struct rad_handle *, int);
 int                     rad_create_response(struct rad_handle *, int);
 struct in_addr          rad_cvt_addr(const void *);
+struct in6_addr                 rad_cvt_addr6(const void *);
 u_int32_t               rad_cvt_int(const void *);
 char                   *rad_cvt_string(const void *, size_t);
 int                     rad_get_attr(struct rad_handle *, const void **,
@@ -211,6 +215,7 @@ int                  rad_init_send_request(struct rad_
                            struct timeval *);
 struct rad_handle      *rad_open(void);  /* Deprecated, == rad_auth_open */
 int                     rad_put_addr(struct rad_handle *, int, struct in_addr);
+int                     rad_put_addr6(struct rad_handle *, int, struct 
in6_addr);
 int                     rad_put_attr(struct rad_handle *, int,
                            const void *, size_t);
 int                     rad_put_int(struct rad_handle *, int, u_int32_t);

Modified: head/lib/libradius/radlib_private.h
==============================================================================
--- head/lib/libradius/radlib_private.h Thu Dec  6 18:51:18 2012        
(r243955)
+++ head/lib/libradius/radlib_private.h Thu Dec  6 19:00:37 2012        
(r243956)
@@ -46,6 +46,7 @@
 #define RADIUS_PORT            1812
 #define RADACCT_PORT           1813
 #define TIMEOUT                        3       /* In seconds */
+#define        DEAD_TIME               0
 
 /* Limits */
 #define ERRSIZE                128             /* Maximum error message length 
*/
@@ -68,6 +69,10 @@ struct rad_server {
        int              timeout;       /* Timeout in seconds */
        int              max_tries;     /* Number of tries before giving up */
        int              num_tries;     /* Number of tries so far */
+       int              is_dead;       /* The server did not answer last time 
*/
+       time_t           dead_time;     /* Don't try this server for the time 
period if it is dead */
+       time_t           next_probe;    /* Time of a next probe after failure */
+       in_addr_t        bindto;        /* Bind to address */
 };
 
 struct rad_handle {
@@ -88,11 +93,9 @@ struct rad_handle {
        unsigned char    in[MSGSIZE];   /* Response received */
        int              in_len;        /* Length of response */
        int              in_pos;        /* Current position scanning attrs */
-       int              total_tries;   /* How many requests we'll send */
-       int              try;           /* How many requests we've sent */
        int              srv;           /* Server number we did last */
        int              type;          /* Handle type */
-       in_addr_t        bindto;        /* Bind to address */
+       in_addr_t        bindto;        /* Current bind address */
 };
 
 struct vendor_attribute {

Modified: head/lib/libradius/radlib_vs.h
==============================================================================
--- head/lib/libradius/radlib_vs.h      Thu Dec  6 18:51:18 2012        
(r243955)
+++ head/lib/libradius/radlib_vs.h      Thu Dec  6 19:00:37 2012        
(r243956)
@@ -73,6 +73,7 @@ struct rad_handle;
 __BEGIN_DECLS
 int     rad_get_vendor_attr(u_int32_t *, const void **, size_t *);
 int     rad_put_vendor_addr(struct rad_handle *, int, int, struct in_addr);
+int     rad_put_vendor_addr6(struct rad_handle *, int, int, struct in6_addr);
 int     rad_put_vendor_attr(struct rad_handle *, int, int, const void *,
            size_t);
 int     rad_put_vendor_int(struct rad_handle *, int, int, u_int32_t);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to