diff options
Diffstat (limited to 'src/radius/radius_client.c')
-rw-r--r-- | src/radius/radius_client.c | 150 |
1 files changed, 102 insertions, 48 deletions
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 693f61ea0455..06c804d132fd 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -226,6 +226,16 @@ struct radius_client_data { * next_radius_identifier - Next RADIUS message identifier to use */ u8 next_radius_identifier; + + /** + * interim_error_cb - Interim accounting error callback + */ + void (*interim_error_cb)(const u8 *addr, void *ctx); + + /** + * interim_error_cb_ctx - interim_error_cb() context data + */ + void *interim_error_cb_ctx; }; @@ -297,6 +307,25 @@ int radius_client_register(struct radius_client_data *radius, } +/** + * radius_client_set_interim_erro_cb - Register an interim acct error callback + * @radius: RADIUS client context from radius_client_init() + * @addr: Station address from the failed message + * @cb: Handler for interim accounting errors + * @ctx: Context pointer for handler callbacks + * + * This function is used to register a handler for processing failed + * transmission attempts of interim accounting update messages. + */ +void radius_client_set_interim_error_cb(struct radius_client_data *radius, + void (*cb)(const u8 *addr, void *ctx), + void *ctx) +{ + radius->interim_error_cb = cb; + radius->interim_error_cb_ctx = ctx; +} + + /* * Returns >0 if message queue was flushed (i.e., the message that triggered * the error is not available anymore) @@ -308,7 +337,7 @@ static int radius_client_handle_send_error(struct radius_client_data *radius, int _errno = errno; wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || - _errno == EBADF || _errno == ENETUNREACH) { + _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" @@ -336,6 +365,8 @@ static int radius_client_retransmit(struct radius_client_data *radius, 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) { @@ -371,12 +402,52 @@ static int radius_client_retransmit(struct radius_client_data *radius, 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, @@ -407,7 +478,6 @@ static int radius_client_retransmit(struct radius_client_data *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_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; @@ -476,10 +546,10 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) (long int) (first - now.sec)); } - if (auth_failover && conf->num_auth_servers > 1) + if (auth_failover) radius_client_auth_failover(radius); - if (acct_failover && conf->num_acct_servers > 1) + if (acct_failover) radius_client_acct_failover(radius); } @@ -625,39 +695,6 @@ static void radius_client_list_add(struct radius_client_data *radius, } -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; - } -} - - /** * radius_client_send - Send a RADIUS request * @radius: RADIUS client context from radius_client_init() @@ -669,16 +706,19 @@ static void radius_client_list_del(struct radius_client_data *radius, * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference * between accounting and interim accounting messages is that the interim - * message will override any pending interim accounting updates while a new - * accounting message does not remove any pending messages. + * message will not be retransmitted. Instead, a callback is used to indicate + * that the transmission failed for the specific station @addr so that a new + * interim accounting update message can be generated with up-to-date session + * data instead of trying to resend old information. * * The message is added on the retransmission queue and will be retransmitted * automatically until a response is received or maximum number of retries - * (RADIUS_CLIENT_MAX_RETRIES) is reached. + * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with + * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue + * automatically on transmission failure. * * The related device MAC address can be used to identify pending messages that - * can be removed with radius_client_flush_auth() or with interim accounting - * updates. + * can be removed with radius_client_flush_auth(). */ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, @@ -691,11 +731,6 @@ int radius_client_send(struct radius_client_data *radius, int s, res; struct wpabuf *buf; - if (msg_type == RADIUS_ACCT_INTERIM) { - /* Remove any pending interim acct update for the same STA. */ - radius_client_list_del(radius, msg_type, addr); - } - if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { if (conf->acct_server && radius->acct_sock < 0) radius_client_init_acct(radius); @@ -1015,6 +1050,9 @@ radius_change_server(struct radius_client_data *radius, int sel_sock; struct radius_msg_list *entry; struct hostapd_radius_servers *conf = radius->conf; + struct sockaddr_in disconnect_addr = { + .sin_family = AF_UNSPEC, + }; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1023,6 +1061,12 @@ radius_change_server(struct radius_client_data *radius, hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), nserv->port); + if (oserv && oserv == nserv) { + /* Reconnect to same server, flush */ + if (auth) + radius_client_flush(radius, 1); + } + if (oserv && oserv != nserv && (nserv->shared_secret_len != oserv->shared_secret_len || os_memcmp(nserv->shared_secret, oserv->shared_secret, @@ -1125,6 +1169,11 @@ radius_change_server(struct radius_client_data *radius, } } + /* Force a reconnect by disconnecting the socket first */ + if (connect(sel_sock, (struct sockaddr *) &disconnect_addr, + sizeof(disconnect_addr)) < 0) + wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno)); + if (connect(sel_sock, addr, addrlen) < 0) { wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; @@ -1587,11 +1636,16 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen) { - struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_servers *conf; int i; struct hostapd_radius_server *serv; int count = 0; + if (!radius) + return 0; + + conf = radius->conf; + if (conf->auth_servers) { for (i = 0; i < conf->num_auth_servers; i++) { serv = &conf->auth_servers[i]; |