RADIUS client: Re-try connection if socket is closed on retransmit

Previously, send() was called with invalid fd = -1 in some error cases
for retransmission and this could even result in a loop of multiple such
attempts. This is obviously not going to work, so drop such attempts and
instead, try to reconnect a socket to the server if the current socket
is not valid.

In addition, initiate server failover immediately if the current socket
is not valid instead of waiting for a timeout.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2015-02-28 15:43:26 +02:00
parent abeea374a4
commit 347c55e216

View file

@ -1,6 +1,6 @@
/* /*
* RADIUS client * RADIUS client
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
* *
* This software may be distributed under the terms of the BSD license. * This software may be distributed under the terms of the BSD license.
* See README for more details. * See README for more details.
@ -236,6 +236,8 @@ radius_change_server(struct radius_client_data *radius,
int sock, int sock6, int auth); int sock, int sock6, int auth);
static int radius_client_init_acct(struct radius_client_data *radius); static int radius_client_init_acct(struct radius_client_data *radius);
static int radius_client_init_auth(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) static void radius_client_msg_free(struct radius_msg_list *req)
@ -304,7 +306,7 @@ static int radius_client_handle_send_error(struct radius_client_data *radius,
{ {
#ifndef CONFIG_NATIVE_WINDOWS #ifndef CONFIG_NATIVE_WINDOWS
int _errno = errno; int _errno = errno;
wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno)); wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
_errno == EBADF || _errno == ENETUNREACH) { _errno == EBADF || _errno == ENETUNREACH) {
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
@ -336,6 +338,10 @@ static int radius_client_retransmit(struct radius_client_data *radius,
if (entry->msg_type == RADIUS_ACCT || if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM) { 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)
radius_client_auth_failover(radius);
s = radius->acct_sock; s = radius->acct_sock;
if (entry->attempts == 0) if (entry->attempts == 0)
conf->acct_server->requests++; conf->acct_server->requests++;
@ -344,6 +350,10 @@ static int radius_client_retransmit(struct radius_client_data *radius,
conf->acct_server->retransmissions++; conf->acct_server->retransmissions++;
} }
} else { } else {
if (radius->auth_sock < 0)
radius_client_init_auth(radius);
if (radius->auth_sock < 0 && conf->num_auth_servers > 1)
radius_client_auth_failover(radius);
s = radius->auth_sock; s = radius->auth_sock;
if (entry->attempts == 0) if (entry->attempts == 0)
conf->auth_server->requests++; conf->auth_server->requests++;
@ -352,6 +362,11 @@ static int radius_client_retransmit(struct radius_client_data *radius,
conf->auth_server->retransmissions++; 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 */ /* retransmit; remove entry if too many attempts */
entry->attempts++; entry->attempts++;
@ -388,7 +403,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
os_time_t first; os_time_t first;
struct radius_msg_list *entry, *prev, *tmp; struct radius_msg_list *entry, *prev, *tmp;
int auth_failover = 0, acct_failover = 0; int auth_failover = 0, acct_failover = 0;
char abuf[50];
size_t prev_num_msgs; size_t prev_num_msgs;
int s; int s;
@ -453,54 +467,70 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
(long int) (first - now.sec)); (long int) (first - now.sec));
} }
if (auth_failover && conf->num_auth_servers > 1) { if (auth_failover && conf->num_auth_servers > 1)
struct hostapd_radius_server *next, *old; radius_client_auth_failover(radius);
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 (acct_failover && conf->num_acct_servers > 1)
if (entry->msg_type == RADIUS_AUTH) radius_client_acct_failover(radius);
old->timeouts++; }
}
next = old + 1;
if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) static void radius_client_auth_failover(struct radius_client_data *radius)
next = conf->auth_servers; {
conf->auth_server = next; struct hostapd_radius_servers *conf = radius->conf;
radius_change_server(radius, next, old, struct hostapd_radius_server *next, *old;
radius->auth_serv_sock, struct radius_msg_list *entry;
radius->auth_serv_sock6, 1); 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) { next = old + 1;
struct hostapd_radius_server *next, *old; if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
old = conf->acct_server; next = conf->auth_servers;
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, conf->auth_server = next;
HOSTAPD_LEVEL_NOTICE, radius_change_server(radius, next, old,
"No response from Accounting server " radius->auth_serv_sock,
"%s:%d - failover", radius->auth_serv_sock6, 1);
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; static void radius_client_acct_failover(struct radius_client_data *radius)
if (next > &conf->acct_servers[conf->num_acct_servers - 1]) {
next = conf->acct_servers; struct hostapd_radius_servers *conf = radius->conf;
conf->acct_server = next; struct hostapd_radius_server *next, *old;
radius_change_server(radius, next, old, struct radius_msg_list *entry;
radius->acct_serv_sock, char abuf[50];
radius->acct_serv_sock6, 0);
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);
} }