Example #1
0
static void radius_client_list_del(struct radius_client_data *radius,
                                   RadiusType msg_type, u8 *addr)
{
    struct hostapd_data *hapd = radius->hapd;
    struct radius_msg_list *entry, *prev, *tmp;

    if (addr == NULL)
        return;

    entry = radius->msgs;
    prev = NULL;
    while (entry) {
        if (entry->msg_type == msg_type &&
                memcmp(entry->addr, addr, ETH_ALEN) == 0) {
            if (prev)
                prev->next = entry->next;
            else
                radius->msgs = entry->next;
            tmp = entry;
            entry = entry->next;
            HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
                          "Removing matching RADIUS message for "
                          MACSTR "\n", MAC2STR(addr));
            radius_client_msg_free(tmp);
            radius->num_msgs--;
            continue;
        }
        prev = entry;
        entry = entry->next;
    }
}
Example #2
0
/**
 * radius_client_flush - Flush all pending RADIUS client messages
 * @radius: RADIUS client context from radius_client_init()
 * @only_auth: Whether only authentication messages are removed
 */
void radius_client_flush(struct radius_client_data *radius, int only_auth)
{
	struct radius_msg_list *entry, *prev, *tmp;

	if (!radius)
		return;

	prev = NULL;
	entry = radius->msgs;

	while (entry) {
		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
			if (prev)
				prev->next = entry->next;
			else
				radius->msgs = entry->next;

			tmp = entry;
			entry = entry->next;
			radius_client_msg_free(tmp);
			radius->num_msgs--;
		} else {
			prev = entry;
			entry = entry->next;
		}
	}

	if (radius->msgs == NULL)
		eloop_cancel_timeout(radius_client_timer, radius, NULL);
}
Example #3
0
/**
 * radius_client_get_id - Get an identifier for a new RADIUS message
 * @radius: RADIUS client context from radius_client_init()
 * Returns: Allocated identifier
 *
 * This function is used to fetch a unique (among pending requests) identifier
 * for a new RADIUS message.
 */
u8 radius_client_get_id(struct radius_client_data *radius)
{
	struct radius_msg_list *entry, *prev, *_remove;
	u8 id = radius->next_radius_identifier++;

	/* remove entries with matching id from retransmit list to avoid
	 * using new reply from the RADIUS server with an old request */
	entry = radius->msgs;
	prev = NULL;
	while (entry) {
		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
			hostapd_logger(radius->ctx, entry->addr,
				       HOSTAPD_MODULE_RADIUS,
				       HOSTAPD_LEVEL_DEBUG,
				       "Removing pending RADIUS message, "
				       "since its id (%d) is reused", id);
			if (prev)
				prev->next = entry->next;
			else
				radius->msgs = entry->next;
			_remove = entry;
		} else {
			_remove = NULL;
			prev = entry;
		}
		entry = entry->next;

		if (_remove)
			radius_client_msg_free(_remove);
	}

	return id;
}
static void radius_client_list_del(struct radius_client_data *radius,
				   RadiusType msg_type, const u8 *addr)
{
	struct radius_msg_list *entry, *prev, *tmp;

	if (addr == NULL)
		return;

	entry = radius->msgs;
	prev = NULL;
	while (entry) {
		if (entry->msg_type == msg_type &&
		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
			if (prev)
				prev->next = entry->next;
			else
				radius->msgs = entry->next;
			tmp = entry;
			entry = entry->next;
			hostapd_logger(radius->ctx, addr,
				       HOSTAPD_MODULE_RADIUS,
				       HOSTAPD_LEVEL_DEBUG,
				       "Removing matching RADIUS message");
			radius_client_msg_free(tmp);
			radius->num_msgs--;
			continue;
		}
		prev = entry;
		entry = entry->next;
	}
}
Example #5
0
/**
 * radius_client_flush_auth - Flush pending RADIUS messages for an address
 * @radius: RADIUS client context from radius_client_init()
 * @addr: MAC address of the related device
 *
 * This function can be used to remove pending RADIUS authentication messages
 * that are related to a specific device. The addr parameter is matched with
 * the one used in radius_client_send() call that was used to transmit the
 * authentication request.
 */
void radius_client_flush_auth(struct radius_client_data *radius,
			      const u8 *addr)
{
	struct radius_msg_list *entry, *prev, *tmp;

	prev = NULL;
	entry = radius->msgs;
	while (entry) {
		if (entry->msg_type == RADIUS_AUTH &&
		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
			hostapd_logger(radius->ctx, addr,
				       HOSTAPD_MODULE_RADIUS,
				       HOSTAPD_LEVEL_DEBUG,
				       "Removing pending RADIUS authentication"
				       " message for removed client");

			if (prev)
				prev->next = entry->next;
			else
				radius->msgs = entry->next;

			tmp = entry;
			entry = entry->next;
			radius_client_msg_free(tmp);
			radius->num_msgs--;
			continue;
		}

		prev = entry;
		entry = entry->next;
	}
}
Example #6
0
u8 radius_client_get_id(struct radius_client_data *radius)
{
    struct hostapd_data *hapd = radius->hapd;
    struct radius_msg_list *entry, *prev, *remove;
    u8 id = radius->next_radius_identifier++;

    /* remove entries with matching id from retransmit list to avoid
     * using new reply from the RADIUS server with an old request */
    entry = radius->msgs;
    prev = NULL;
    while (entry) {
        if (entry->msg->hdr->identifier == id) {
            HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
                          "Removing pending RADIUS message, since "
                          "its id (%d) is reused\n", id);
            if (prev)
                prev->next = entry->next;
            else
                radius->msgs = entry->next;
            remove = entry;
        } else
            remove = NULL;
        prev = entry;
        entry = entry->next;

        if (remove)
            radius_client_msg_free(remove);
    }

    return id;
}
static void radius_client_list_add(struct radius_client_data *radius,
				   struct radius_msg *msg,
				   RadiusType msg_type, u8 *shared_secret,
				   size_t shared_secret_len, u8 *addr)
{
	struct radius_msg_list *entry, *prev;

	if (eloop_terminated()) {
		/* No point in adding entries to retransmit queue since event
		 * loop has already been terminated. */
		radius_msg_free(msg);
		free(msg);
		return;
	}

	entry = malloc(sizeof(*entry));
	if (entry == NULL) {
		wpa_printf(MSG_ERROR,
			"Failed to add RADIUS packet into retransmit list\n");
		radius_msg_free(msg);
		free(msg);
		return;
	}

	memset(entry, 0, sizeof(*entry));
	if (addr)
		memcpy(entry->addr, addr, ETH_ALEN);
	entry->msg = msg;
	entry->msg_type = msg_type;
	entry->shared_secret = shared_secret;
	entry->shared_secret_len = shared_secret_len;
	time(&entry->first_try);
	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
	entry->attempts = 1;
	gettimeofday(&entry->last_attempt, NULL);
	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
	entry->next = radius->msgs;
	radius->msgs = entry;
	radius_client_update_timeout(radius);

	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
		wpa_printf(MSG_ERROR,
			"Removing the oldest un-ACKed RADIUS packet due to "
		       "retransmit list limits.\n");
		prev = NULL;
		while (entry->next) {
			prev = entry;
			entry = entry->next;
		}
		if (prev) {
			prev->next = NULL;
			radius_client_msg_free(entry);
		}
	} else
		radius->num_msgs++;
}
static void radius_client_list_add(hostapd *hapd, struct radius_msg *msg,
				   RadiusType msg_type, u8 *shared_secret,
				   size_t shared_secret_len)
{
	struct radius_msg_list *entry, *prev;

	if (eloop_terminated()) {
		/* No point in adding entries to retransmit queue since event
		 * loop has already been terminated. */
		radius_msg_free(msg);
		free(msg);
		return;
	}

	entry = malloc(sizeof(*entry));
	if (entry == NULL) {
		printf("Failed to add RADIUS packet into retransmit list\n");
		radius_msg_free(msg);
		free(msg);
		return;
	}

	memset(entry, 0, sizeof(*entry));
	entry->msg = msg;
	entry->msg_type = msg_type;
	entry->shared_secret = shared_secret;
	entry->shared_secret_len = shared_secret_len;
	time(&entry->first_try);
	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
	entry->attempts = 1;
	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;

	if (!hapd->radius->msgs)
		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
				       radius_client_timer, hapd, NULL);

	entry->next = hapd->radius->msgs;
	hapd->radius->msgs = entry;

	if (hapd->radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
		printf("Removing the oldest un-ACKed RADIUS packet due to "
		       "retransmit list limits.\n");
		prev = NULL;
		while (entry->next) {
			prev = entry;
			entry = entry->next;
		}
		if (prev) {
			prev->next = NULL;
			radius_client_msg_free(entry);
		}
	} else
		hapd->radius->num_msgs++;
}
void radius_client_flush(struct radius_client_data *radius)
{
	struct radius_msg_list *entry, *prev;

	if (!radius)
		return;

	eloop_cancel_timeout(radius_client_timer, radius, NULL);
	entry = radius->msgs;
	radius->msgs = NULL;
	radius->num_msgs = 0;
	while (entry) {
		prev = entry;
		entry = entry->next;
		radius_client_msg_free(prev);
	}
}
Example #10
0
void radius_client_flush(struct wpa_supplicant *wpa_s)
{
	struct radius_msg_list *entry, *prev;

	if (!wpa_s->radius)
		return;

	eloop_cancel_timeout(radius_client_timer, wpa_s, NULL);

	entry = wpa_s->radius->msgs;
	wpa_s->radius->msgs = NULL;
	wpa_s->radius->num_msgs = 0;
	while (entry) {
		prev = entry;
		entry = entry->next;
		radius_client_msg_free(prev);
	}
}
void radius_client_flush(hostapd *hapd)
{
	struct radius_msg_list *entry, *prev;

	if (!hapd->radius)
		return;

	eloop_cancel_timeout(radius_client_timer, hapd, NULL);

	entry = hapd->radius->msgs;
	hapd->radius->msgs = NULL;
	hapd->radius->num_msgs = 0;
	while (entry) {
		prev = entry;
		entry = entry->next;
		radius_client_msg_free(prev);
	}
}
Example #12
0
static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
	struct wpa_supplicant *wpa_s = eloop_ctx;
	time_t now, first;
	struct radius_msg_list *entry, *prev, *tmp;
	int auth_failover = 0, acct_failover = 0;

	entry = wpa_s->radius->msgs;
	if (!entry)
		return;

	time(&now);
	first = 0;

	prev = NULL;
	while (entry) {
		if (now >= entry->next_try &&
		    radius_client_retransmit(wpa_s, entry, now)) {
			if (prev)
				prev->next = entry->next;
			else
				wpa_s->radius->msgs = entry->next;

			tmp = entry;
			entry = entry->next;
			radius_client_msg_free(tmp);
			wpa_s->radius->num_msgs--;
			continue;
		}

		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
			if (entry->msg_type == RADIUS_ACCT ||
			    entry->msg_type == RADIUS_ACCT_INTERIM)
				acct_failover++;
			else
				auth_failover++;
		}

		if (first == 0 || entry->next_try < first)
			first = entry->next_try;

		prev = entry;
		entry = entry->next;
	}

	if (wpa_s->radius->msgs) {
		if (first < now)
			first = now;
		eloop_register_timeout(first - now, 0,
				       radius_client_timer, wpa_s, NULL);
	}

	if (auth_failover && wpa_s->num_auth_servers > 1) {
		struct hostapd_radius_server *next, *old;
		old = wpa_s->auth_server;
		hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS,
			       HOSTAPD_LEVEL_NOTICE,
			       "No response from Authentication server "
			       "%s:%d - failover",
			       inet_ntoa(old->addr), old->port);

		next = old + 1;
		if (next > &(wpa_s->auth_servers
			     [wpa_s->num_auth_servers - 1]))
			next = wpa_s->auth_servers;
		wpa_s->auth_server = next;
		radius_change_server(wpa_s, next, old,
				     wpa_s->radius->auth_serv_sock, 1);
	}

	if (acct_failover && wpa_s->num_acct_servers > 1) {
		struct hostapd_radius_server *next, *old;
		old = wpa_s->acct_server;
		hostapd_logger(wpa_s, NULL, HOSTAPD_MODULE_RADIUS,
			       HOSTAPD_LEVEL_NOTICE,
			       "No response from Accounting server "
			       "%s:%d - failover",
			       inet_ntoa(old->addr), old->port);
		next = old + 1;
		if (next > &wpa_s->acct_servers
		    [wpa_s->num_acct_servers - 1])
			next = wpa_s->acct_servers;
		wpa_s->acct_server = next;
		radius_change_server(wpa_s, next, old,
				     wpa_s->radius->acct_serv_sock, 0);
	}
}
Example #13
0
static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
	struct radius_client_data *radius = eloop_ctx;
	struct hostapd_radius_servers *conf = radius->conf;
	RadiusType msg_type = (RadiusType) sock_ctx;
	int len, roundtrip;
	unsigned char buf[3000];
	struct radius_msg *msg;
	struct radius_hdr *hdr;
	struct radius_rx_handler *handlers;
	size_t num_handlers, i;
	struct radius_msg_list *req, *prev_req;
	struct os_reltime now;
	struct hostapd_radius_server *rconf;
	int invalid_authenticator = 0;

	if (msg_type == RADIUS_ACCT) {
		handlers = radius->acct_handlers;
		num_handlers = radius->num_acct_handlers;
		rconf = conf->acct_server;
	} else {
		handlers = radius->auth_handlers;
		num_handlers = radius->num_auth_handlers;
		rconf = conf->auth_server;
	}

	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
	if (len < 0) {
		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)) {
		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) {
		wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
		rconf->malformed_responses++;
		return;
	}
	hdr = radius_msg_get_hdr(msg);

	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
	if (conf->msg_dumps)
		radius_msg_dump(msg);

	switch (hdr->code) {
	case RADIUS_CODE_ACCESS_ACCEPT:
		rconf->access_accepts++;
		break;
	case RADIUS_CODE_ACCESS_REJECT:
		rconf->access_rejects++;
		break;
	case RADIUS_CODE_ACCESS_CHALLENGE:
		rconf->access_challenges++;
		break;
	case RADIUS_CODE_ACCOUNTING_RESPONSE:
		rconf->responses++;
		break;
	}

	prev_req = NULL;
	req = radius->msgs;
	while (req) {
		/* TODO: also match by src addr:port of the packet when using
		 * alternative RADIUS servers (?) */
		if ((req->msg_type == msg_type ||
		     (req->msg_type == RADIUS_ACCT_INTERIM &&
		      msg_type == RADIUS_ACCT)) &&
		    radius_msg_get_hdr(req->msg)->identifier ==
		    hdr->identifier)
			break;

		prev_req = req;
		req = req->next;
	}

	if (req == NULL) {
		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
			       HOSTAPD_LEVEL_DEBUG,
			       "No matching RADIUS request found (type=%d "
			       "id=%d) - dropping packet",
			       msg_type, hdr->identifier);
		goto fail;
	}

	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,
		       HOSTAPD_LEVEL_DEBUG,
		       "Received RADIUS packet matched with a pending "
		       "request, round trip time %d.%02d sec",
		       roundtrip / 100, roundtrip % 100);
	rconf->round_trip_time = roundtrip;

	/* Remove ACKed RADIUS packet from retransmit list */
	if (prev_req)
		prev_req->next = req->next;
	else
		radius->msgs = req->next;
	radius->num_msgs--;

	for (i = 0; i < num_handlers; i++) {
		RadiusRxResult res;
		res = handlers[i].handler(msg, req->msg, req->shared_secret,
					  req->shared_secret_len,
					  handlers[i].data);
		switch (res) {
		case RADIUS_RX_PROCESSED:
			radius_msg_free(msg);
			/* continue */
		case RADIUS_RX_QUEUED:
			radius_client_msg_free(req);
			return;
		case RADIUS_RX_INVALID_AUTHENTICATOR:
			invalid_authenticator++;
			/* continue */
		case RADIUS_RX_UNKNOWN:
			/* continue with next handler */
			break;
		}
	}

	if (invalid_authenticator)
		rconf->bad_authenticators++;
	else
		rconf->unknown_types++;
	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
		       "(type=%d code=%d id=%d)%s - dropping packet",
		       msg_type, hdr->code, hdr->identifier,
		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
		       "");
	radius_client_msg_free(req);

 fail:
	radius_msg_free(msg);
}
Example #14
0
static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
	struct radius_client_data *radius = eloop_ctx;
	struct os_reltime now;
	os_time_t first;
	struct radius_msg_list *entry, *prev, *tmp;
	int auth_failover = 0, acct_failover = 0;
	size_t prev_num_msgs;
	int s;

	entry = radius->msgs;
	if (!entry)
		return;

	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)
				prev->next = entry->next;
			else
				radius->msgs = entry->next;

			tmp = entry;
			entry = entry->next;
			radius_client_msg_free(tmp);
			radius->num_msgs--;
			continue;
		}

		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++;
			else
				auth_failover++;
		}

		if (first == 0 || entry->next_try < first)
			first = entry->next_try;

		prev = entry;
		entry = entry->next;
	}

	if (radius->msgs) {
		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",
			       (long int) (first - now.sec));
	}

	if (auth_failover)
		radius_client_auth_failover(radius);

	if (acct_failover)
		radius_client_acct_failover(radius);
}
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;
	os_time_t first;
	struct radius_msg_list *entry, *prev, *tmp;
	int auth_failover = 0, acct_failover = 0;
	char abuf[50];

	entry = radius->msgs;
	if (!entry)
		return;

	os_get_time(&now);
	first = 0;

	prev = NULL;
	while (entry) {
		if (now.sec >= entry->next_try &&
		    radius_client_retransmit(radius, entry, now.sec)) {
			if (prev)
				prev->next = entry->next;
			else
				radius->msgs = entry->next;

			tmp = entry;
			entry = entry->next;
			radius_client_msg_free(tmp);
			radius->num_msgs--;
			continue;
		}

		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
			if (entry->msg_type == RADIUS_ACCT ||
			    entry->msg_type == RADIUS_ACCT_INTERIM)
				acct_failover++;
			else
				auth_failover++;
		}

		if (first == 0 || entry->next_try < first)
			first = entry->next_try;

		prev = entry;
		entry = entry->next;
	}

	if (radius->msgs) {
		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",
			       (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++;
		}

		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);
	}

	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);

		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);
	}
}
Example #16
0
static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
    struct radius_client_data *radius = eloop_ctx;
    struct hostapd_data *hapd = radius->hapd;
    time_t now, first;
    struct radius_msg_list *entry, *prev, *tmp;
    int auth_failover = 0, acct_failover = 0;

    entry = radius->msgs;
    if (!entry)
        return;

    time(&now);
    first = 0;

    prev = NULL;
    while (entry) {
        if (now >= entry->next_try &&
                radius_client_retransmit(radius, entry, now)) {
            if (prev)
                prev->next = entry->next;
            else
                radius->msgs = entry->next;

            tmp = entry;
            entry = entry->next;
            radius_client_msg_free(tmp);
            radius->num_msgs--;
            continue;
        }

        if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
            if (entry->msg_type == RADIUS_ACCT ||
                    entry->msg_type == RADIUS_ACCT_INTERIM)
                acct_failover++;
            else
                auth_failover++;
        }

        if (first == 0 || entry->next_try < first)
            first = entry->next_try;

        prev = entry;
        entry = entry->next;
    }

    if (radius->msgs) {
        if (first < now)
            first = now;
        eloop_register_timeout(first - now, 0,
                               radius_client_timer, radius, NULL);
        HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Next RADIUS client "
                      "retransmit in %ld seconds\n",
                      (long int) (first - now));

    }

    if (auth_failover && hapd->conf->num_auth_servers > 1) {
        struct hostapd_radius_server *next, *old;
        old = hapd->conf->auth_server;
        hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS,
                       HOSTAPD_LEVEL_NOTICE,
                       "No response from Authentication server "
                       "%s:%d - failover",
                       inet_ntoa(old->addr), old->port);

        for (entry = radius->msgs; entry; entry = entry->next) {
            if (entry->msg_type == RADIUS_AUTH)
                old->timeouts++;
        }

        next = old + 1;
        if (next > &(hapd->conf->auth_servers
                     [hapd->conf->num_auth_servers - 1]))
            next = hapd->conf->auth_servers;
        hapd->conf->auth_server = next;
        radius_change_server(radius, next, old,
                             radius->auth_serv_sock, 1);
    }

    if (acct_failover && hapd->conf->num_acct_servers > 1) {
        struct hostapd_radius_server *next, *old;
        old = hapd->conf->acct_server;
        hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS,
                       HOSTAPD_LEVEL_NOTICE,
                       "No response from Accounting server "
                       "%s:%d - failover",
                       inet_ntoa(old->addr), 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 > &hapd->conf->acct_servers
                [hapd->conf->num_acct_servers - 1])
            next = hapd->conf->acct_servers;
        hapd->conf->acct_server = next;
        radius_change_server(radius, next, old,
                             radius->acct_serv_sock, 0);
    }
}
static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
	hostapd *hapd = (hostapd *) eloop_ctx;
	RadiusType msg_type = (RadiusType) sock_ctx;
	int len, i;
	unsigned char buf[3000];
	struct radius_msg *msg;
	struct radius_rx_handler *handlers;
	size_t num_handlers;
	struct radius_msg_list *req, *prev_req;

	len = recv(sock, buf, sizeof(buf), 0);
	if (len < 0) {
		perror("recv[RADIUS]");
		return;
	}
	HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
		      "Received %d bytes from RADIUS server\n", len);
	if (len == sizeof(buf)) {
		printf("Possibly too long UDP frame for our buffer - "
		       "dropping it\n");
		return;
	}

	msg = radius_msg_parse(buf, len);
	if (msg == NULL) {
		printf("Parsing incoming RADIUS frame failed\n");
		return;
	}

	HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
		      "Received RADIUS message\n");
	if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS))
		radius_msg_dump(msg);

	if (msg_type == RADIUS_ACCT) {
		handlers = hapd->radius->acct_handlers;
		num_handlers = hapd->radius->num_acct_handlers;
	} else {
		handlers = hapd->radius->auth_handlers;
		num_handlers = hapd->radius->num_auth_handlers;
	}

	prev_req = NULL;
	req = hapd->radius->msgs;
	while (req) {
		/* TODO: also match by src addr:port of the packet when using
		 * alternative RADIUS servers (?) */
		if (req->msg_type == msg_type &&
		    req->msg->hdr->identifier == msg->hdr->identifier)
			break;

		prev_req = req;
		req = req->next;
	}

	if (req == NULL) {
		HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
			      "No maching RADIUS request found (type=%d id=%d)"
			      " - dropping packet\n",
			      msg_type, msg->hdr->identifier);
		goto fail;
	}

	/* Remove ACKed RADIUS packet from retransmit list */
	if (prev_req)
		prev_req->next = req->next;
	else
		hapd->radius->msgs = req->next;
	hapd->radius->num_msgs--;

	for (i = 0; i < num_handlers; i++) {
		RadiusRxResult res;
		res = handlers[i].handler(hapd, msg, req->msg,
					  req->shared_secret,
					  req->shared_secret_len,
					  handlers[i].data);
		switch (res) {
		case RADIUS_RX_PROCESSED:
			radius_msg_free(msg);
			free(msg);
			/* continue */
		case RADIUS_RX_QUEUED:
			radius_client_msg_free(req);
			return;
		case RADIUS_RX_UNKNOWN:
			/* continue with next handler */
			break;
		}
	}

	printf("No RADIUS RX handler found (type=%d code=%d id=%d) - dropping "
	       "packet\n", msg_type, msg->hdr->code, msg->hdr->identifier);
	radius_client_msg_free(req);

 fail:
	radius_msg_free(msg);
	free(msg);
}