static int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, os_time_t now) { struct hostapd_radius_servers *conf = radius->conf; int s; struct wpabuf *buf; size_t prev_num_msgs; u8 *acct_delay_time; size_t acct_delay_time_len; 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++; else { conf->acct_server->timeouts++; 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++; else { conf->auth_server->timeouts++; conf->auth_server->retransmissions++; } } if (entry->msg_type == RADIUS_ACCT_INTERIM) { wpa_printf(MSG_DEBUG, "RADIUS: Failed to transmit interim accounting update to " MACSTR " - drop message and request a new update", MAC2STR(entry->addr)); if (radius->interim_error_cb) radius->interim_error_cb(entry->addr, radius->interim_error_cb_ctx); return 1; } if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: No valid socket for retransmission"); return 1; } if (entry->msg_type == RADIUS_ACCT && radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, &acct_delay_time, &acct_delay_time_len, NULL) == 0 && acct_delay_time_len == 4) { struct radius_hdr *hdr; u32 delay_time; /* * Need to assign a new identifier since attribute contents * changes. */ hdr = radius_msg_get_hdr(entry->msg); hdr->identifier = radius_client_get_id(radius); /* Update Acct-Delay-Time to show wait time in queue */ delay_time = now - entry->first_try; WPA_PUT_BE32(acct_delay_time, delay_time); wpa_printf(MSG_DEBUG, "RADIUS: Updated Acct-Delay-Time to %u for retransmission", delay_time); radius_msg_finish_acct(entry->msg, entry->shared_secret, entry->shared_secret_len); if (radius->conf->msg_dumps) radius_msg_dump(entry->msg); } /* retransmit; remove entry if too many attempts */ entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); 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) { wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } return 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 int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, os_time_t now) { 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++; else { conf->acct_server->timeouts++; 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++; else { conf->auth_server->timeouts++; 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++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); 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) { wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } return 0; }