eap-radius: Add ability to configure RADIUS retransmission behavior
authorThom Troy <ttroy50@gmail.com>
Sat, 7 Nov 2015 17:53:50 +0000 (17:53 +0000)
committerTobias Brunner <tobias@strongswan.org>
Tue, 17 Nov 2015 13:25:08 +0000 (14:25 +0100)
Closes strongswan/strongswan#19.

conf/plugins/eap-radius.opt
src/libcharon/plugins/eap_radius/eap_radius_plugin.c
src/libradius/Makefile.am
src/libradius/radius_config.c
src/libradius/radius_config.h
src/libradius/radius_socket.c
src/libradius/radius_socket.h

index 929931a..c3668ec 100644 (file)
@@ -80,6 +80,15 @@ charon.plugins.eap-radius.secret =
 charon.plugins.eap-radius.server =
        IP/Hostname of RADIUS server.
 
+charon.plugins.eap-radius.retransmit_base = 1.4
+       Base to use for calculating exponential back off.
+
+charon.plugins.eap-radius.retransmit_timeout = 2.0
+       Timeout in seconds before sending first retransmit.
+
+charon.plugins.eap-radius.retransmit_tries = 4
+       Number of times to retransmit a packet before giving up.
+
 charon.plugins.eap-radius.servers {}
        Section to specify multiple RADIUS servers.
 
@@ -88,7 +97,9 @@ charon.plugins.eap-radius.servers {}
        specified for each server. A server's IP/Hostname can be configured using
        the **address** option. The **acct_port** [1813] option can be used to
        specify the port used for RADIUS accounting. For each RADIUS server a
-       priority can be specified using the **preference** [0] option.
+       priority can be specified using the **preference** [0] option. The
+       retransmission time for each server can set set using **retransmit_base**,
+       **retransmit_timeout** and **retransmit_tries**.
 
 charon.plugins.eap-radius.sockets = 1
        Number of sockets (ports) to use, increase for high load.
index 6a4a038..4fe9828 100644 (file)
  * for more details.
  */
 
+/*
+ * Copyright (C) 2015 Thom Troy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #include "eap_radius_plugin.h"
 
 #include "eap_radius.h"
@@ -98,6 +120,8 @@ static void load_configs(private_eap_radius_plugin_t *this)
        radius_config_t *config;
        char *nas_identifier, *secret, *address, *section;
        int auth_port, acct_port, sockets, preference;
+       u_int retransmit_tries;
+       double retransmit_timeout, retransmit_base;
 
        address = lib->settings->get_str(lib->settings,
                                                                "%s.plugins.eap-radius.server", NULL, lib->ns);
@@ -117,8 +141,18 @@ static void load_configs(private_eap_radius_plugin_t *this)
                                                "%s.plugins.eap-radius.port", AUTH_PORT, lib->ns);
                sockets = lib->settings->get_int(lib->settings,
                                                "%s.plugins.eap-radius.sockets", 1, lib->ns);
+
+               retransmit_tries = lib->settings->get_int(lib->settings,
+                                               "%s.plugins.eap-radius.retransmit_tries", 4, lib->ns);
+               retransmit_timeout = lib->settings->get_double(lib->settings,
+                                               "%s.plugins.eap-radius.retransmit_timeout", 2, lib->ns);
+               retransmit_base = lib->settings->get_double(lib->settings,
+                                               "%s.plugins.eap-radius.retransmit_base", 1.4, lib->ns);
+
                config = radius_config_create(address, address, auth_port, ACCT_PORT,
-                                                                         nas_identifier, secret, sockets, 0);
+                                                                         nas_identifier, secret, sockets, 0,
+                                                                         retransmit_tries, retransmit_timeout,
+                                                                         retransmit_base);
                if (!config)
                {
                        DBG1(DBG_CFG, "no RADUIS server defined");
@@ -170,11 +204,33 @@ static void load_configs(private_eap_radius_plugin_t *this)
                                        lib->settings->get_int(lib->settings,
                                                "%s.plugins.eap-radius.sockets", 1, lib->ns),
                                lib->ns, section);
+
+               retransmit_tries = lib->settings->get_int(lib->settings,
+                               "%s.plugins.eap-radius.servers.%s.retransmit_tries",
+                                       lib->settings->get_int(lib->settings,
+                                               "%s.plugins.eap-radius.retransmit_tries", 4, lib->ns),
+                               lib->ns, section);
+
+               retransmit_timeout = lib->settings->get_double(lib->settings,
+                               "%s.plugins.eap-radius.servers.%s.retransmit_timeout",
+                                       lib->settings->get_double(lib->settings,
+                                               "%s.plugins.eap-radius.retransmit_timeout", 2, lib->ns),
+                               lib->ns, section);
+
+               retransmit_base = lib->settings->get_double(lib->settings,
+                               "%s.plugins.eap-radius.servers.%s.retransmit_base",
+                                       lib->settings->get_double(lib->settings,
+                                               "%s.plugins.eap-radius.retransmit_base", 1.4, lib->ns),
+                               lib->ns, section);
+
                preference = lib->settings->get_int(lib->settings,
                                "%s.plugins.eap-radius.servers.%s.preference", 0,
                                lib->ns, section);
+
                config = radius_config_create(section, address, auth_port, acct_port,
-                                                               nas_identifier, secret, sockets, preference);
+                                                               nas_identifier, secret, sockets, preference,
+                                                               retransmit_tries, retransmit_timeout,
+                                                               retransmit_base);
                if (!config)
                {
                        DBG1(DBG_CFG, "loading RADIUS server '%s' failed, skipped", section);
index 4706f27..4761532 100644 (file)
@@ -7,7 +7,7 @@ AM_LDFLAGS = \
 ipseclib_LTLIBRARIES = libradius.la
 
 libradius_la_LIBADD = \
-       $(top_builddir)/src/libstrongswan/libstrongswan.la
+       $(top_builddir)/src/libstrongswan/libstrongswan.la -lm
 
 libradius_la_SOURCES = \
        radius_message.h radius_message.c \
index 5dbd1d7..6631734 100644 (file)
  * for more details.
  */
 
+/*
+ * Copyright (C) 2015 Thom Troy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #include "radius_config.h"
 
 #include <threading/mutex.h>
@@ -180,7 +202,8 @@ METHOD(radius_config_t, destroy, void,
 radius_config_t *radius_config_create(char *name, char *address,
                                                                          u_int16_t auth_port, u_int16_t acct_port,
                                                                          char *nas_identifier, char *secret,
-                                                                         int sockets, int preference)
+                                                                         int sockets, int preference,
+                                                                         u_int tries, double timeout, double base)
 {
        private_radius_config_t *this;
        radius_socket_t *socket;
@@ -209,7 +232,8 @@ radius_config_t *radius_config_create(char *name, char *address,
        while (sockets--)
        {
                socket = radius_socket_create(address, auth_port, acct_port,
-                                                                         chunk_create(secret, strlen(secret)));
+                                                                         chunk_create(secret, strlen(secret)),
+                                                                         tries, timeout, base);
                if (!socket)
                {
                        destroy(this);
index 40ed619..c0ff057 100644 (file)
  * for more details.
  */
 
+/*
+ * Copyright (C) 2015 Thom Troy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 /**
  * @defgroup radius_config radius_config
  * @{ @ingroup libradius
@@ -91,10 +113,14 @@ struct radius_config_t {
  * @param secret                       secret to use with this server
  * @param sockets                      number of sockets to create in pool
  * @param preference           preference boost for this server
+ * @param tries                                number of times we retransmit messages
+ * @param timeout                      retransmission timeout
+ * @param base                         base to calculate retransmission timeout
  */
 radius_config_t *radius_config_create(char *name, char *address,
                                                                          u_int16_t auth_port, u_int16_t acct_port,
                                                                          char *nas_identifier, char *secret,
-                                                                         int sockets, int preference);
+                                                                         int sockets, int preference,
+                                                                         u_int tries, double timeout, double base);
 
 #endif /** RADIUS_CONFIG_H_ @}*/
index ad5daa5..065f264 100644 (file)
  * for more details.
  */
 
+/*
+ * Copyright (C) 2015 Thom Troy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #include "radius_socket.h"
 #include "radius_mppe.h"
 
 #include <errno.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <pen/pen.h>
 #include <utils/debug.h>
@@ -83,6 +106,21 @@ struct private_radius_socket_t {
         * RADIUS secret
         */
        chunk_t secret;
+
+       /**
+        * Number of times we retransmit messages before giving up
+        */
+       u_int retransmit_tries;
+
+       /**
+        * Retransmission timeout
+        */
+       double retransmit_timeout;
+
+       /**
+        * Base to calculate retransmission timeout
+        */
+       double retransmit_base;
 };
 
 /**
@@ -185,7 +223,7 @@ METHOD(radius_socket_t, request, radius_message_t*,
 {
        radius_message_t *response;
        chunk_t data;
-       int i, *fd, retransmit = 0;
+       int *fd, retransmit = 0, timeout;
        u_int16_t port;
        rng_t *rng = NULL;
 
@@ -218,21 +256,22 @@ METHOD(radius_socket_t, request, radius_message_t*,
        data = request->get_encoding(request);
        DBG3(DBG_CFG, "%B", &data);
 
-       /* timeout after 2, 3, 4, 5 seconds */
-       for (i = 2; i <= 5; i++)
+       while (retransmit < this->retransmit_tries)
        {
+               timeout = (int)(this->retransmit_timeout * 1000.0 *
+                                               pow(this->retransmit_base, retransmit));
                if (retransmit)
                {
-                       DBG1(DBG_CFG, "retransmitting RADIUS %N (attempt %d)",
-                                radius_message_code_names, request->get_code(request),
-                                retransmit);
+                       DBG1(DBG_CFG, "retransmit %d of RADIUS %N (timeout: %.1fs)",
+                                retransmit, radius_message_code_names,
+                                request->get_code(request), timeout/1000.0);
                }
                if (send(*fd, data.ptr, data.len, 0) != data.len)
                {
                        DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
                        return NULL;
                }
-               switch (receive_response(*fd, i*1000, request->get_identifier(request),
+               switch (receive_response(*fd, timeout, request->get_identifier(request),
                                                                 &response))
                {
                        case SUCCESS:
@@ -251,8 +290,9 @@ METHOD(radius_socket_t, request, radius_message_t*,
                response->destroy(response);
                return NULL;
        }
-       DBG1(DBG_CFG, "RADIUS %N timed out after %d retransmits",
-                radius_message_code_names, request->get_code(request), retransmit - 1);
+
+       DBG1(DBG_CFG, "RADIUS %N timed out after %d attempts",
+                radius_message_code_names, request->get_code(request), retransmit);
        return NULL;
 }
 
@@ -336,7 +376,8 @@ METHOD(radius_socket_t, destroy, void,
  * See header
  */
 radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
-                                                                         u_int16_t acct_port, chunk_t secret)
+                                                                         u_int16_t acct_port, chunk_t secret,
+                                                                         u_int tries, double timeout, double base)
 {
        private_radius_socket_t *this;
 
@@ -354,6 +395,9 @@ radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
                .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5),
                .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128),
                .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
+               .retransmit_tries = tries,
+               .retransmit_timeout = timeout,
+               .retransmit_base = base,
        );
 
        if (!this->hasher || !this->signer || !this->rng ||
index eb510ea..84b146a 100644 (file)
  * for more details.
  */
 
+/*
+ * Copyright (C) 2015 Thom Troy
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 /**
  * @defgroup radius_socket radius_socket
  * @{ @ingroup libradius
@@ -70,8 +92,12 @@ struct radius_socket_t {
  * @param auth_port    server port for authentication
  * @param acct_port    server port for accounting
  * @param secret       RADIUS secret
+ * @param tries                number of times we retransmit messages
+ * @param timeout      retransmission timeout
+ * @param base         base to calculate retransmission timeout
  */
 radius_socket_t *radius_socket_create(char *address, u_int16_t auth_port,
-                                                                         u_int16_t acct_port, chunk_t secret);
+                                                                         u_int16_t acct_port, chunk_t secret,
+                                                                         u_int tries, double timeout, double base);
 
 #endif /** RADIUS_SOCKET_H_ @}*/