Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / radius / radius_client.c
index 171af29..693f61e 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * RADIUS client
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -128,7 +122,7 @@ struct radius_msg_list {
        /**
         * last_attempt - Time of the last transmission attempt
         */
-       struct os_time last_attempt;
+       struct os_reltime last_attempt;
 
        /**
         * shared_secret - Shared secret with the target RADIUS server
@@ -242,6 +236,8 @@ radius_change_server(struct radius_client_data *radius,
                     int sock, int sock6, int auth);
 static int radius_client_init_acct(struct radius_client_data *radius);
 static int radius_client_init_auth(struct radius_client_data *radius);
+static void radius_client_auth_failover(struct radius_client_data *radius);
+static void radius_client_acct_failover(struct radius_client_data *radius);
 
 
 static void radius_client_msg_free(struct radius_msg_list *req)
@@ -287,8 +283,8 @@ int radius_client_register(struct radius_client_data *radius,
                num = &radius->num_auth_handlers;
        }
 
-       newh = os_realloc(*handlers,
-                         (*num + 1) * sizeof(struct radius_rx_handler));
+       newh = os_realloc_array(*handlers, *num + 1,
+                               sizeof(struct radius_rx_handler));
        if (newh == NULL)
                return -1;
 
@@ -301,26 +297,34 @@ int radius_client_register(struct radius_client_data *radius,
 }
 
 
-static void radius_client_handle_send_error(struct radius_client_data *radius,
-                                           int s, RadiusType msg_type)
+/*
+ * Returns >0 if message queue was flushed (i.e., the message that triggered
+ * the error is not available anymore)
+ */
+static int radius_client_handle_send_error(struct radius_client_data *radius,
+                                          int s, RadiusType msg_type)
 {
 #ifndef CONFIG_NATIVE_WINDOWS
        int _errno = errno;
-       perror("send[RADIUS]");
+       wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
        if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
-           _errno == EBADF) {
+           _errno == EBADF || _errno == ENETUNREACH) {
                hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
                               HOSTAPD_LEVEL_INFO,
                               "Send failed - maybe interface status changed -"
                               " try to connect again");
-               eloop_unregister_read_sock(s);
-               close(s);
-               if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
+               if (msg_type == RADIUS_ACCT ||
+                   msg_type == RADIUS_ACCT_INTERIM) {
                        radius_client_init_acct(radius);
-               else
+                       return 0;
+               } else {
                        radius_client_init_auth(radius);
+                       return 1;
+               }
        }
 #endif /* CONFIG_NATIVE_WINDOWS */
+
+       return 0;
 }
 
 
@@ -331,9 +335,18 @@ static int radius_client_retransmit(struct radius_client_data *radius,
        struct hostapd_radius_servers *conf = radius->conf;
        int s;
        struct wpabuf *buf;
+       size_t prev_num_msgs;
 
        if (entry->msg_type == RADIUS_ACCT ||
            entry->msg_type == RADIUS_ACCT_INTERIM) {
+               if (radius->acct_sock < 0)
+                       radius_client_init_acct(radius);
+               if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
+                       prev_num_msgs = radius->num_msgs;
+                       radius_client_acct_failover(radius);
+                       if (prev_num_msgs != radius->num_msgs)
+                               return 0;
+               }
                s = radius->acct_sock;
                if (entry->attempts == 0)
                        conf->acct_server->requests++;
@@ -342,6 +355,14 @@ static int radius_client_retransmit(struct radius_client_data *radius,
                        conf->acct_server->retransmissions++;
                }
        } else {
+               if (radius->auth_sock < 0)
+                       radius_client_init_auth(radius);
+               if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
+                       prev_num_msgs = radius->num_msgs;
+                       radius_client_auth_failover(radius);
+                       if (prev_num_msgs != radius->num_msgs)
+                               return 0;
+               }
                s = radius->auth_sock;
                if (entry->attempts == 0)
                        conf->auth_server->requests++;
@@ -350,6 +371,11 @@ static int radius_client_retransmit(struct radius_client_data *radius,
                        conf->auth_server->retransmissions++;
                }
        }
+       if (s < 0) {
+               wpa_printf(MSG_INFO,
+                          "RADIUS: No valid socket for retransmission");
+               return 1;
+       }
 
        /* retransmit; remove entry if too many attempts */
        entry->attempts++;
@@ -357,18 +383,20 @@ static int radius_client_retransmit(struct radius_client_data *radius,
                       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
                       radius_msg_get_hdr(entry->msg)->identifier);
 
-       os_get_time(&entry->last_attempt);
+       os_get_reltime(&entry->last_attempt);
        buf = radius_msg_get_buf(entry->msg);
-       if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
-               radius_client_handle_send_error(radius, s, entry->msg_type);
+       if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+               if (radius_client_handle_send_error(radius, s, entry->msg_type)
+                   > 0)
+                       return 0;
+       }
 
        entry->next_try = now + entry->next_wait;
        entry->next_wait *= 2;
        if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
                entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
        if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
-               printf("Removing un-ACKed RADIUS message due to too many "
-                      "failed retransmit attempts\n");
+               wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
                return 1;
        }
 
@@ -380,21 +408,23 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
 {
        struct radius_client_data *radius = eloop_ctx;
        struct hostapd_radius_servers *conf = radius->conf;
-       struct os_time now;
+       struct os_reltime now;
        os_time_t first;
        struct radius_msg_list *entry, *prev, *tmp;
        int auth_failover = 0, acct_failover = 0;
-       char abuf[50];
+       size_t prev_num_msgs;
+       int s;
 
        entry = radius->msgs;
        if (!entry)
                return;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        first = 0;
 
        prev = NULL;
        while (entry) {
+               prev_num_msgs = radius->num_msgs;
                if (now.sec >= entry->next_try &&
                    radius_client_retransmit(radius, entry, now.sec)) {
                        if (prev)
@@ -409,7 +439,18 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
                        continue;
                }
 
-               if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+               if (prev_num_msgs != radius->num_msgs) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS: Message removed from queue - restart from beginning");
+                       entry = radius->msgs;
+                       prev = NULL;
+                       continue;
+               }
+
+               s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
+                       radius->acct_sock;
+               if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
+                   (s < 0 && entry->attempts > 0)) {
                        if (entry->msg_type == RADIUS_ACCT ||
                            entry->msg_type == RADIUS_ACCT_INTERIM)
                                acct_failover++;
@@ -435,60 +476,76 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
                               (long int) (first - now.sec));
        }
 
-       if (auth_failover && conf->num_auth_servers > 1) {
-               struct hostapd_radius_server *next, *old;
-               old = conf->auth_server;
-               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
-                              HOSTAPD_LEVEL_NOTICE,
-                              "No response from Authentication server "
-                              "%s:%d - failover",
-                              hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
-                              old->port);
-
-               for (entry = radius->msgs; entry; entry = entry->next) {
-                       if (entry->msg_type == RADIUS_AUTH)
-                               old->timeouts++;
-               }
+       if (auth_failover && conf->num_auth_servers > 1)
+               radius_client_auth_failover(radius);
+
+       if (acct_failover && conf->num_acct_servers > 1)
+               radius_client_acct_failover(radius);
+}
 
-               next = old + 1;
-               if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
-                       next = conf->auth_servers;
-               conf->auth_server = next;
-               radius_change_server(radius, next, old,
-                                    radius->auth_serv_sock,
-                                    radius->auth_serv_sock6, 1);
+
+static void radius_client_auth_failover(struct radius_client_data *radius)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       struct hostapd_radius_server *next, *old;
+       struct radius_msg_list *entry;
+       char abuf[50];
+
+       old = conf->auth_server;
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_NOTICE,
+                      "No response from Authentication server %s:%d - failover",
+                      hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+                      old->port);
+
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if (entry->msg_type == RADIUS_AUTH)
+                       old->timeouts++;
        }
 
-       if (acct_failover && conf->num_acct_servers > 1) {
-               struct hostapd_radius_server *next, *old;
-               old = conf->acct_server;
-               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
-                              HOSTAPD_LEVEL_NOTICE,
-                              "No response from Accounting server "
-                              "%s:%d - failover",
-                              hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
-                              old->port);
+       next = old + 1;
+       if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
+               next = conf->auth_servers;
+       conf->auth_server = next;
+       radius_change_server(radius, next, old,
+                            radius->auth_serv_sock,
+                            radius->auth_serv_sock6, 1);
+}
 
-               for (entry = radius->msgs; entry; entry = entry->next) {
-                       if (entry->msg_type == RADIUS_ACCT ||
-                           entry->msg_type == RADIUS_ACCT_INTERIM)
-                               old->timeouts++;
-               }
 
-               next = old + 1;
-               if (next > &conf->acct_servers[conf->num_acct_servers - 1])
-                       next = conf->acct_servers;
-               conf->acct_server = next;
-               radius_change_server(radius, next, old,
-                                    radius->acct_serv_sock,
-                                    radius->acct_serv_sock6, 0);
+static void radius_client_acct_failover(struct radius_client_data *radius)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       struct hostapd_radius_server *next, *old;
+       struct radius_msg_list *entry;
+       char abuf[50];
+
+       old = conf->acct_server;
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_NOTICE,
+                      "No response from Accounting server %s:%d - failover",
+                      hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+                      old->port);
+
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if (entry->msg_type == RADIUS_ACCT ||
+                   entry->msg_type == RADIUS_ACCT_INTERIM)
+                       old->timeouts++;
        }
+
+       next = old + 1;
+       if (next > &conf->acct_servers[conf->num_acct_servers - 1])
+               next = conf->acct_servers;
+       conf->acct_server = next;
+       radius_change_server(radius, next, old,
+                            radius->acct_serv_sock,
+                            radius->acct_serv_sock6, 0);
 }
 
 
 static void radius_client_update_timeout(struct radius_client_data *radius)
 {
-       struct os_time now;
+       struct os_reltime now;
        os_time_t first;
        struct radius_msg_list *entry;
 
@@ -504,14 +561,14 @@ static void radius_client_update_timeout(struct radius_client_data *radius)
                        first = entry->next_try;
        }
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        if (first < now.sec)
                first = now.sec;
        eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
                               NULL);
        hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
                       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
-                      " %ld seconds\n", (long int) (first - now.sec));
+                      " %ld seconds", (long int) (first - now.sec));
 }
 
 
@@ -532,7 +589,7 @@ static void radius_client_list_add(struct radius_client_data *radius,
 
        entry = os_zalloc(sizeof(*entry));
        if (entry == NULL) {
-               printf("Failed to add RADIUS packet into retransmit list\n");
+               wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
                radius_msg_free(msg);
                return;
        }
@@ -543,7 +600,7 @@ static void radius_client_list_add(struct radius_client_data *radius,
        entry->msg_type = msg_type;
        entry->shared_secret = shared_secret;
        entry->shared_secret_len = shared_secret_len;
-       os_get_time(&entry->last_attempt);
+       os_get_reltime(&entry->last_attempt);
        entry->first_try = entry->last_attempt.sec;
        entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
        entry->attempts = 1;
@@ -553,8 +610,7 @@ static void radius_client_list_add(struct radius_client_data *radius,
        radius_client_update_timeout(radius);
 
        if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
-               printf("Removing the oldest un-ACKed RADIUS packet due to "
-                      "retransmit list limits.\n");
+               wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
                prev = NULL;
                while (entry->next) {
                        prev = entry;
@@ -641,7 +697,11 @@ int radius_client_send(struct radius_client_data *radius,
        }
 
        if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
-               if (conf->acct_server == NULL) {
+               if (conf->acct_server && radius->acct_sock < 0)
+                       radius_client_init_acct(radius);
+
+               if (conf->acct_server == NULL || radius->acct_sock < 0 ||
+                   conf->acct_server->shared_secret == NULL) {
                        hostapd_logger(radius->ctx, NULL,
                                       HOSTAPD_MODULE_RADIUS,
                                       HOSTAPD_LEVEL_INFO,
@@ -655,7 +715,11 @@ int radius_client_send(struct radius_client_data *radius,
                s = radius->acct_sock;
                conf->acct_server->requests++;
        } else {
-               if (conf->auth_server == NULL) {
+               if (conf->auth_server && radius->auth_sock < 0)
+                       radius_client_init_auth(radius);
+
+               if (conf->auth_server == NULL || radius->auth_sock < 0 ||
+                   conf->auth_server->shared_secret == NULL) {
                        hostapd_logger(radius->ctx, NULL,
                                       HOSTAPD_MODULE_RADIUS,
                                       HOSTAPD_LEVEL_INFO,
@@ -684,7 +748,7 @@ int radius_client_send(struct radius_client_data *radius,
        radius_client_list_add(radius, msg, msg_type, shared_secret,
                               shared_secret_len, addr);
 
-       return res;
+       return 0;
 }
 
 
@@ -700,7 +764,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
        struct radius_rx_handler *handlers;
        size_t num_handlers, i;
        struct radius_msg_list *req, *prev_req;
-       struct os_time now;
+       struct os_reltime now;
        struct hostapd_radius_server *rconf;
        int invalid_authenticator = 0;
 
@@ -716,21 +780,20 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
 
        len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
        if (len < 0) {
-               perror("recv[RADIUS]");
+               wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno));
                return;
        }
        hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
                       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
                       "server", len);
        if (len == sizeof(buf)) {
-               printf("Possibly too long UDP frame for our buffer - "
-                      "dropping it\n");
+               wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
                return;
        }
 
        msg = radius_msg_parse(buf, len);
        if (msg == NULL) {
-               printf("Parsing incoming RADIUS frame failed\n");
+               wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
                rconf->malformed_responses++;
                return;
        }
@@ -781,7 +844,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
                goto fail;
        }
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        roundtrip = (now.sec - req->last_attempt.sec) * 100 +
                (now.usec - req->last_attempt.usec) / 10000;
        hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
@@ -960,9 +1023,10 @@ radius_change_server(struct radius_client_data *radius,
                       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
                       nserv->port);
 
-       if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
-           os_memcmp(nserv->shared_secret, oserv->shared_secret,
-                     nserv->shared_secret_len) != 0) {
+       if (oserv && oserv != nserv &&
+           (nserv->shared_secret_len != oserv->shared_secret_len ||
+            os_memcmp(nserv->shared_secret, oserv->shared_secret,
+                      nserv->shared_secret_len) != 0)) {
                /* Pending RADIUS packets used different shared secret, so
                 * they need to be modified. Update accounting message
                 * authenticators here. Authentication messages are removed
@@ -980,7 +1044,8 @@ radius_change_server(struct radius_client_data *radius,
        }
 
        /* Reset retry counters for the new server */
-       for (entry = radius->msgs; entry; entry = entry->next) {
+       for (entry = radius->msgs; oserv && oserv != nserv && entry;
+            entry = entry->next) {
                if ((auth && entry->msg_type != RADIUS_AUTH) ||
                    (!auth && entry->msg_type != RADIUS_ACCT))
                        continue;
@@ -1021,6 +1086,13 @@ radius_change_server(struct radius_client_data *radius,
                return -1;
        }
 
+       if (sel_sock < 0) {
+               wpa_printf(MSG_INFO,
+                          "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
+                          nserv->addr.af, sock, sock6, auth);
+               return -1;
+       }
+
        if (conf->force_client_addr) {
                switch (conf->client_addr.af) {
                case AF_INET:
@@ -1047,13 +1119,14 @@ radius_change_server(struct radius_client_data *radius,
                }
 
                if (bind(sel_sock, cl_addr, claddrlen) < 0) {
-                       perror("bind[radius]");
+                       wpa_printf(MSG_INFO, "bind[radius]: %s",
+                                  strerror(errno));
                        return -1;
                }
        }
 
        if (connect(sel_sock, addr, addrlen) < 0) {
-               perror("connect[radius]");
+               wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
                return -1;
        }
 
@@ -1061,19 +1134,23 @@ radius_change_server(struct radius_client_data *radius,
        switch (nserv->addr.af) {
        case AF_INET:
                claddrlen = sizeof(claddr);
-               getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
-               wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
-                          inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
+               if (getsockname(sel_sock, (struct sockaddr *) &claddr,
+                               &claddrlen) == 0) {
+                       wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+                                  inet_ntoa(claddr.sin_addr),
+                                  ntohs(claddr.sin_port));
+               }
                break;
 #ifdef CONFIG_IPV6
        case AF_INET6: {
                claddrlen = sizeof(claddr6);
-               getsockname(sel_sock, (struct sockaddr *) &claddr6,
-                           &claddrlen);
-               wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
-                          inet_ntop(AF_INET6, &claddr6.sin6_addr,
-                                    abuf, sizeof(abuf)),
-                          ntohs(claddr6.sin6_port));
+               if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
+                               &claddrlen) == 0) {
+                       wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+                                  inet_ntop(AF_INET6, &claddr6.sin6_addr,
+                                            abuf, sizeof(abuf)),
+                                  ntohs(claddr6.sin6_port));
+               }
                break;
        }
 #endif /* CONFIG_IPV6 */
@@ -1099,18 +1176,28 @@ static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
            conf->auth_server != conf->auth_servers) {
                oserv = conf->auth_server;
                conf->auth_server = conf->auth_servers;
-               radius_change_server(radius, conf->auth_server, oserv,
-                                    radius->auth_serv_sock,
-                                    radius->auth_serv_sock6, 1);
+               if (radius_change_server(radius, conf->auth_server, oserv,
+                                        radius->auth_serv_sock,
+                                        radius->auth_serv_sock6, 1) < 0) {
+                       conf->auth_server = oserv;
+                       radius_change_server(radius, oserv, conf->auth_server,
+                                            radius->auth_serv_sock,
+                                            radius->auth_serv_sock6, 1);
+               }
        }
 
        if (radius->acct_sock >= 0 && conf->acct_servers &&
            conf->acct_server != conf->acct_servers) {
                oserv = conf->acct_server;
                conf->acct_server = conf->acct_servers;
-               radius_change_server(radius, conf->acct_server, oserv,
-                                    radius->acct_serv_sock,
-                                    radius->acct_serv_sock6, 0);
+               if (radius_change_server(radius, conf->acct_server, oserv,
+                                        radius->acct_serv_sock,
+                                        radius->acct_serv_sock6, 0) < 0) {
+                       conf->acct_server = oserv;
+                       radius_change_server(radius, oserv, conf->acct_server,
+                                            radius->acct_serv_sock,
+                                            radius->acct_serv_sock6, 0);
+               }
        }
 
        if (conf->retry_primary_interval)
@@ -1129,21 +1216,62 @@ static int radius_client_disable_pmtu_discovery(int s)
        r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
                       sizeof(action));
        if (r == -1)
-               wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
-                          "%s", strerror(errno));
+               wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
+                          strerror(errno));
 #endif
        return r;
 }
 
 
+static void radius_close_auth_sockets(struct radius_client_data *radius)
+{
+       radius->auth_sock = -1;
+
+       if (radius->auth_serv_sock >= 0) {
+               eloop_unregister_read_sock(radius->auth_serv_sock);
+               close(radius->auth_serv_sock);
+               radius->auth_serv_sock = -1;
+       }
+#ifdef CONFIG_IPV6
+       if (radius->auth_serv_sock6 >= 0) {
+               eloop_unregister_read_sock(radius->auth_serv_sock6);
+               close(radius->auth_serv_sock6);
+               radius->auth_serv_sock6 = -1;
+       }
+#endif /* CONFIG_IPV6 */
+}
+
+
+static void radius_close_acct_sockets(struct radius_client_data *radius)
+{
+       radius->acct_sock = -1;
+
+       if (radius->acct_serv_sock >= 0) {
+               eloop_unregister_read_sock(radius->acct_serv_sock);
+               close(radius->acct_serv_sock);
+               radius->acct_serv_sock = -1;
+       }
+#ifdef CONFIG_IPV6
+       if (radius->acct_serv_sock6 >= 0) {
+               eloop_unregister_read_sock(radius->acct_serv_sock6);
+               close(radius->acct_serv_sock6);
+               radius->acct_serv_sock6 = -1;
+       }
+#endif /* CONFIG_IPV6 */
+}
+
+
 static int radius_client_init_auth(struct radius_client_data *radius)
 {
        struct hostapd_radius_servers *conf = radius->conf;
        int ok = 0;
 
+       radius_close_auth_sockets(radius);
+
        radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (radius->auth_serv_sock < 0)
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else {
                radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
                ok++;
@@ -1152,7 +1280,8 @@ static int radius_client_init_auth(struct radius_client_data *radius)
 #ifdef CONFIG_IPV6
        radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
        if (radius->auth_serv_sock6 < 0)
-               perror("socket[PF_INET6,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else
                ok++;
 #endif /* CONFIG_IPV6 */
@@ -1168,8 +1297,8 @@ static int radius_client_init_auth(struct radius_client_data *radius)
            eloop_register_read_sock(radius->auth_serv_sock,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_AUTH)) {
-               printf("Could not register read socket for authentication "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+               radius_close_auth_sockets(radius);
                return -1;
        }
 
@@ -1178,8 +1307,8 @@ static int radius_client_init_auth(struct radius_client_data *radius)
            eloop_register_read_sock(radius->auth_serv_sock6,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_AUTH)) {
-               printf("Could not register read socket for authentication "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+               radius_close_auth_sockets(radius);
                return -1;
        }
 #endif /* CONFIG_IPV6 */
@@ -1193,9 +1322,12 @@ static int radius_client_init_acct(struct radius_client_data *radius)
        struct hostapd_radius_servers *conf = radius->conf;
        int ok = 0;
 
+       radius_close_acct_sockets(radius);
+
        radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (radius->acct_serv_sock < 0)
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else {
                radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
                ok++;
@@ -1204,7 +1336,8 @@ static int radius_client_init_acct(struct radius_client_data *radius)
 #ifdef CONFIG_IPV6
        radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
        if (radius->acct_serv_sock6 < 0)
-               perror("socket[PF_INET6,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else
                ok++;
 #endif /* CONFIG_IPV6 */
@@ -1220,8 +1353,8 @@ static int radius_client_init_acct(struct radius_client_data *radius)
            eloop_register_read_sock(radius->acct_serv_sock,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_ACCT)) {
-               printf("Could not register read socket for accounting "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+               radius_close_acct_sockets(radius);
                return -1;
        }
 
@@ -1230,8 +1363,8 @@ static int radius_client_init_acct(struct radius_client_data *radius)
            eloop_register_read_sock(radius->acct_serv_sock6,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_ACCT)) {
-               printf("Could not register read socket for accounting "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+               radius_close_acct_sockets(radius);
                return -1;
        }
 #endif /* CONFIG_IPV6 */
@@ -1293,16 +1426,8 @@ void radius_client_deinit(struct radius_client_data *radius)
        if (!radius)
                return;
 
-       if (radius->auth_serv_sock >= 0)
-               eloop_unregister_read_sock(radius->auth_serv_sock);
-       if (radius->acct_serv_sock >= 0)
-               eloop_unregister_read_sock(radius->acct_serv_sock);
-#ifdef CONFIG_IPV6
-       if (radius->auth_serv_sock6 >= 0)
-               eloop_unregister_read_sock(radius->auth_serv_sock6);
-       if (radius->acct_serv_sock6 >= 0)
-               eloop_unregister_read_sock(radius->acct_serv_sock6);
-#endif /* CONFIG_IPV6 */
+       radius_close_auth_sockets(radius);
+       radius_close_acct_sockets(radius);
 
        eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
 
@@ -1489,3 +1614,11 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf,
 
        return count;
 }
+
+
+void radius_client_reconfig(struct radius_client_data *radius,
+                           struct hostapd_radius_servers *conf)
+{
+       if (radius)
+               radius->conf = conf;
+}