139beb93cSSam Leffler /* 2e28a4053SRui Paulo * RADIUS client 35b9c547cSRui Paulo * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 939beb93cSSam Leffler #include "includes.h" 10c1d255d3SCy Schubert #include <net/if.h> 1139beb93cSSam Leffler 1239beb93cSSam Leffler #include "common.h" 1339beb93cSSam Leffler #include "radius.h" 1439beb93cSSam Leffler #include "radius_client.h" 1539beb93cSSam Leffler #include "eloop.h" 1639beb93cSSam Leffler 1739beb93cSSam Leffler /* Defaults for RADIUS retransmit values (exponential backoff) */ 18e28a4053SRui Paulo 19e28a4053SRui Paulo /** 20e28a4053SRui Paulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds 21e28a4053SRui Paulo */ 22e28a4053SRui Paulo #define RADIUS_CLIENT_FIRST_WAIT 3 23e28a4053SRui Paulo 24e28a4053SRui Paulo /** 25e28a4053SRui Paulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds 26e28a4053SRui Paulo */ 27e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_WAIT 120 28e28a4053SRui Paulo 29e28a4053SRui Paulo /** 304bc52338SCy Schubert * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries 31e28a4053SRui Paulo * 324bc52338SCy Schubert * Maximum number of server failovers before the entry is removed from 33e28a4053SRui Paulo * retransmit list. 34e28a4053SRui Paulo */ 354bc52338SCy Schubert #define RADIUS_CLIENT_MAX_FAILOVER 3 36e28a4053SRui Paulo 37e28a4053SRui Paulo /** 38e28a4053SRui Paulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages 39e28a4053SRui Paulo * 40e28a4053SRui Paulo * Maximum number of entries in retransmit list (oldest entries will be 41e28a4053SRui Paulo * removed, if this limit is exceeded). 42e28a4053SRui Paulo */ 43e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_ENTRIES 30 44e28a4053SRui Paulo 45e28a4053SRui Paulo /** 46e28a4053SRui Paulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point 47e28a4053SRui Paulo * 48e28a4053SRui Paulo * The number of failed retry attempts after which the RADIUS server will be 49e28a4053SRui Paulo * changed (if one of more backup servers are configured). 50e28a4053SRui Paulo */ 51e28a4053SRui Paulo #define RADIUS_CLIENT_NUM_FAILOVER 4 5239beb93cSSam Leffler 5339beb93cSSam Leffler 54e28a4053SRui Paulo /** 55e28a4053SRui Paulo * struct radius_rx_handler - RADIUS client RX handler 56e28a4053SRui Paulo * 57e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 58e28a4053SRui Paulo * store registered RX handlers. These handlers are registered by calls to 59e28a4053SRui Paulo * radius_client_register() and unregistered when the RADIUS client is 60e28a4053SRui Paulo * deinitialized with a call to radius_client_deinit(). 61e28a4053SRui Paulo */ 6239beb93cSSam Leffler struct radius_rx_handler { 63e28a4053SRui Paulo /** 64e28a4053SRui Paulo * handler - Received RADIUS message handler 65e28a4053SRui Paulo */ 6639beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 6739beb93cSSam Leffler struct radius_msg *req, 6839beb93cSSam Leffler const u8 *shared_secret, 6939beb93cSSam Leffler size_t shared_secret_len, 7039beb93cSSam Leffler void *data); 71e28a4053SRui Paulo 72e28a4053SRui Paulo /** 73e28a4053SRui Paulo * data - Context data for the handler 74e28a4053SRui Paulo */ 7539beb93cSSam Leffler void *data; 7639beb93cSSam Leffler }; 7739beb93cSSam Leffler 7839beb93cSSam Leffler 79e28a4053SRui Paulo /** 80e28a4053SRui Paulo * struct radius_msg_list - RADIUS client message retransmit list 81e28a4053SRui Paulo * 82e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 83e28a4053SRui Paulo * store pending RADIUS requests that may still need to be retransmitted. 84e28a4053SRui Paulo */ 8539beb93cSSam Leffler struct radius_msg_list { 86e28a4053SRui Paulo /** 87e28a4053SRui Paulo * addr - STA/client address 88e28a4053SRui Paulo * 89e28a4053SRui Paulo * This is used to find RADIUS messages for the same STA. 90e28a4053SRui Paulo */ 91e28a4053SRui Paulo u8 addr[ETH_ALEN]; 92e28a4053SRui Paulo 93e28a4053SRui Paulo /** 94e28a4053SRui Paulo * msg - RADIUS message 95e28a4053SRui Paulo */ 9639beb93cSSam Leffler struct radius_msg *msg; 97e28a4053SRui Paulo 98e28a4053SRui Paulo /** 99e28a4053SRui Paulo * msg_type - Message type 100e28a4053SRui Paulo */ 10139beb93cSSam Leffler RadiusType msg_type; 102e28a4053SRui Paulo 103e28a4053SRui Paulo /** 104e28a4053SRui Paulo * first_try - Time of the first transmission attempt 105e28a4053SRui Paulo */ 10639beb93cSSam Leffler os_time_t first_try; 107e28a4053SRui Paulo 108e28a4053SRui Paulo /** 109e28a4053SRui Paulo * next_try - Time for the next transmission attempt 110e28a4053SRui Paulo */ 11139beb93cSSam Leffler os_time_t next_try; 112e28a4053SRui Paulo 113e28a4053SRui Paulo /** 1144bc52338SCy Schubert * attempts - Number of transmission attempts for one server 115e28a4053SRui Paulo */ 11639beb93cSSam Leffler int attempts; 117e28a4053SRui Paulo 118e28a4053SRui Paulo /** 1194bc52338SCy Schubert * accu_attempts - Number of accumulated attempts 1204bc52338SCy Schubert */ 1214bc52338SCy Schubert int accu_attempts; 1224bc52338SCy Schubert 1234bc52338SCy Schubert /** 124e28a4053SRui Paulo * next_wait - Next retransmission wait time in seconds 125e28a4053SRui Paulo */ 12639beb93cSSam Leffler int next_wait; 127e28a4053SRui Paulo 128e28a4053SRui Paulo /** 129e28a4053SRui Paulo * last_attempt - Time of the last transmission attempt 130e28a4053SRui Paulo */ 1315b9c547cSRui Paulo struct os_reltime last_attempt; 13239beb93cSSam Leffler 133e28a4053SRui Paulo /** 134e28a4053SRui Paulo * shared_secret - Shared secret with the target RADIUS server 135e28a4053SRui Paulo */ 136e28a4053SRui Paulo const u8 *shared_secret; 137e28a4053SRui Paulo 138e28a4053SRui Paulo /** 139e28a4053SRui Paulo * shared_secret_len - shared_secret length in octets 140e28a4053SRui Paulo */ 14139beb93cSSam Leffler size_t shared_secret_len; 14239beb93cSSam Leffler 14339beb93cSSam Leffler /* TODO: server config with failover to backup server(s) */ 14439beb93cSSam Leffler 145e28a4053SRui Paulo /** 146e28a4053SRui Paulo * next - Next message in the list 147e28a4053SRui Paulo */ 14839beb93cSSam Leffler struct radius_msg_list *next; 14939beb93cSSam Leffler }; 15039beb93cSSam Leffler 15139beb93cSSam Leffler 152e28a4053SRui Paulo /** 153e28a4053SRui Paulo * struct radius_client_data - Internal RADIUS client data 154e28a4053SRui Paulo * 155e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module. 156e28a4053SRui Paulo * External users allocate this by calling radius_client_init() and free it by 157e28a4053SRui Paulo * calling radius_client_deinit(). The pointer to this opaque data is used in 158e28a4053SRui Paulo * calls to other functions as an identifier for the RADIUS client instance. 159e28a4053SRui Paulo */ 16039beb93cSSam Leffler struct radius_client_data { 161e28a4053SRui Paulo /** 162e28a4053SRui Paulo * ctx - Context pointer for hostapd_logger() callbacks 163e28a4053SRui Paulo */ 16439beb93cSSam Leffler void *ctx; 165e28a4053SRui Paulo 166e28a4053SRui Paulo /** 167e28a4053SRui Paulo * conf - RADIUS client configuration (list of RADIUS servers to use) 168e28a4053SRui Paulo */ 16939beb93cSSam Leffler struct hostapd_radius_servers *conf; 17039beb93cSSam Leffler 171e28a4053SRui Paulo /** 172e28a4053SRui Paulo * auth_serv_sock - IPv4 socket for RADIUS authentication messages 173e28a4053SRui Paulo */ 174e28a4053SRui Paulo int auth_serv_sock; 17539beb93cSSam Leffler 176e28a4053SRui Paulo /** 177e28a4053SRui Paulo * acct_serv_sock - IPv4 socket for RADIUS accounting messages 178e28a4053SRui Paulo */ 179e28a4053SRui Paulo int acct_serv_sock; 180e28a4053SRui Paulo 181e28a4053SRui Paulo /** 182e28a4053SRui Paulo * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages 183e28a4053SRui Paulo */ 184e28a4053SRui Paulo int auth_serv_sock6; 185e28a4053SRui Paulo 186e28a4053SRui Paulo /** 187e28a4053SRui Paulo * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages 188e28a4053SRui Paulo */ 189e28a4053SRui Paulo int acct_serv_sock6; 190e28a4053SRui Paulo 191e28a4053SRui Paulo /** 192e28a4053SRui Paulo * auth_sock - Currently used socket for RADIUS authentication server 193e28a4053SRui Paulo */ 194e28a4053SRui Paulo int auth_sock; 195e28a4053SRui Paulo 196e28a4053SRui Paulo /** 197e28a4053SRui Paulo * acct_sock - Currently used socket for RADIUS accounting server 198e28a4053SRui Paulo */ 199e28a4053SRui Paulo int acct_sock; 200e28a4053SRui Paulo 201e28a4053SRui Paulo /** 202e28a4053SRui Paulo * auth_handlers - Authentication message handlers 203e28a4053SRui Paulo */ 20439beb93cSSam Leffler struct radius_rx_handler *auth_handlers; 205e28a4053SRui Paulo 206e28a4053SRui Paulo /** 207e28a4053SRui Paulo * num_auth_handlers - Number of handlers in auth_handlers 208e28a4053SRui Paulo */ 20939beb93cSSam Leffler size_t num_auth_handlers; 210e28a4053SRui Paulo 211e28a4053SRui Paulo /** 212e28a4053SRui Paulo * acct_handlers - Accounting message handlers 213e28a4053SRui Paulo */ 21439beb93cSSam Leffler struct radius_rx_handler *acct_handlers; 215e28a4053SRui Paulo 216e28a4053SRui Paulo /** 217e28a4053SRui Paulo * num_acct_handlers - Number of handlers in acct_handlers 218e28a4053SRui Paulo */ 21939beb93cSSam Leffler size_t num_acct_handlers; 22039beb93cSSam Leffler 221e28a4053SRui Paulo /** 222e28a4053SRui Paulo * msgs - Pending outgoing RADIUS messages 223e28a4053SRui Paulo */ 22439beb93cSSam Leffler struct radius_msg_list *msgs; 225e28a4053SRui Paulo 226e28a4053SRui Paulo /** 227e28a4053SRui Paulo * num_msgs - Number of pending messages in the msgs list 228e28a4053SRui Paulo */ 22939beb93cSSam Leffler size_t num_msgs; 23039beb93cSSam Leffler 231e28a4053SRui Paulo /** 232e28a4053SRui Paulo * next_radius_identifier - Next RADIUS message identifier to use 233e28a4053SRui Paulo */ 23439beb93cSSam Leffler u8 next_radius_identifier; 235780fb4a2SCy Schubert 236780fb4a2SCy Schubert /** 237780fb4a2SCy Schubert * interim_error_cb - Interim accounting error callback 238780fb4a2SCy Schubert */ 239780fb4a2SCy Schubert void (*interim_error_cb)(const u8 *addr, void *ctx); 240780fb4a2SCy Schubert 241780fb4a2SCy Schubert /** 242780fb4a2SCy Schubert * interim_error_cb_ctx - interim_error_cb() context data 243780fb4a2SCy Schubert */ 244780fb4a2SCy Schubert void *interim_error_cb_ctx; 24539beb93cSSam Leffler }; 24639beb93cSSam Leffler 24739beb93cSSam Leffler 24839beb93cSSam Leffler static int 24939beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 25039beb93cSSam Leffler struct hostapd_radius_server *nserv, 25139beb93cSSam Leffler struct hostapd_radius_server *oserv, 25239beb93cSSam Leffler int sock, int sock6, int auth); 25339beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius); 25439beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius); 2555b9c547cSRui Paulo static void radius_client_auth_failover(struct radius_client_data *radius); 2565b9c547cSRui Paulo static void radius_client_acct_failover(struct radius_client_data *radius); 25739beb93cSSam Leffler 25839beb93cSSam Leffler 25939beb93cSSam Leffler static void radius_client_msg_free(struct radius_msg_list *req) 26039beb93cSSam Leffler { 26139beb93cSSam Leffler radius_msg_free(req->msg); 26239beb93cSSam Leffler os_free(req); 26339beb93cSSam Leffler } 26439beb93cSSam Leffler 26539beb93cSSam Leffler 266e28a4053SRui Paulo /** 267e28a4053SRui Paulo * radius_client_register - Register a RADIUS client RX handler 268e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 269e28a4053SRui Paulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) 270e28a4053SRui Paulo * @handler: Handler for received RADIUS messages 271e28a4053SRui Paulo * @data: Context pointer for handler callbacks 272e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 273e28a4053SRui Paulo * 274e28a4053SRui Paulo * This function is used to register a handler for processing received RADIUS 275e28a4053SRui Paulo * authentication and accounting messages. The handler() callback function will 276e28a4053SRui Paulo * be called whenever a RADIUS message is received from the active server. 277e28a4053SRui Paulo * 278e28a4053SRui Paulo * There can be multiple registered RADIUS message handlers. The handlers will 279e28a4053SRui Paulo * be called in order until one of them indicates that it has processed or 280e28a4053SRui Paulo * queued the message. 281e28a4053SRui Paulo */ 28239beb93cSSam Leffler int radius_client_register(struct radius_client_data *radius, 28339beb93cSSam Leffler RadiusType msg_type, 28439beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 28539beb93cSSam Leffler struct radius_msg *req, 28639beb93cSSam Leffler const u8 *shared_secret, 28739beb93cSSam Leffler size_t shared_secret_len, 28839beb93cSSam Leffler void *data), 28939beb93cSSam Leffler void *data) 29039beb93cSSam Leffler { 29139beb93cSSam Leffler struct radius_rx_handler **handlers, *newh; 29239beb93cSSam Leffler size_t *num; 29339beb93cSSam Leffler 29439beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 29539beb93cSSam Leffler handlers = &radius->acct_handlers; 29639beb93cSSam Leffler num = &radius->num_acct_handlers; 29739beb93cSSam Leffler } else { 29839beb93cSSam Leffler handlers = &radius->auth_handlers; 29939beb93cSSam Leffler num = &radius->num_auth_handlers; 30039beb93cSSam Leffler } 30139beb93cSSam Leffler 302f05cddf9SRui Paulo newh = os_realloc_array(*handlers, *num + 1, 303f05cddf9SRui Paulo sizeof(struct radius_rx_handler)); 30439beb93cSSam Leffler if (newh == NULL) 30539beb93cSSam Leffler return -1; 30639beb93cSSam Leffler 30739beb93cSSam Leffler newh[*num].handler = handler; 30839beb93cSSam Leffler newh[*num].data = data; 30939beb93cSSam Leffler (*num)++; 31039beb93cSSam Leffler *handlers = newh; 31139beb93cSSam Leffler 31239beb93cSSam Leffler return 0; 31339beb93cSSam Leffler } 31439beb93cSSam Leffler 31539beb93cSSam Leffler 316780fb4a2SCy Schubert /** 317780fb4a2SCy Schubert * radius_client_set_interim_erro_cb - Register an interim acct error callback 318780fb4a2SCy Schubert * @radius: RADIUS client context from radius_client_init() 319780fb4a2SCy Schubert * @addr: Station address from the failed message 320780fb4a2SCy Schubert * @cb: Handler for interim accounting errors 321780fb4a2SCy Schubert * @ctx: Context pointer for handler callbacks 322780fb4a2SCy Schubert * 323780fb4a2SCy Schubert * This function is used to register a handler for processing failed 324780fb4a2SCy Schubert * transmission attempts of interim accounting update messages. 325780fb4a2SCy Schubert */ 326780fb4a2SCy Schubert void radius_client_set_interim_error_cb(struct radius_client_data *radius, 327780fb4a2SCy Schubert void (*cb)(const u8 *addr, void *ctx), 328780fb4a2SCy Schubert void *ctx) 329780fb4a2SCy Schubert { 330780fb4a2SCy Schubert radius->interim_error_cb = cb; 331780fb4a2SCy Schubert radius->interim_error_cb_ctx = ctx; 332780fb4a2SCy Schubert } 333780fb4a2SCy Schubert 334780fb4a2SCy Schubert 3355b9c547cSRui Paulo /* 3365b9c547cSRui Paulo * Returns >0 if message queue was flushed (i.e., the message that triggered 3375b9c547cSRui Paulo * the error is not available anymore) 3385b9c547cSRui Paulo */ 3395b9c547cSRui Paulo static int radius_client_handle_send_error(struct radius_client_data *radius, 34039beb93cSSam Leffler int s, RadiusType msg_type) 34139beb93cSSam Leffler { 34239beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 34339beb93cSSam Leffler int _errno = errno; 3445b9c547cSRui Paulo wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); 34539beb93cSSam Leffler if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 346780fb4a2SCy Schubert _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { 34739beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 34839beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 34939beb93cSSam Leffler "Send failed - maybe interface status changed -" 35039beb93cSSam Leffler " try to connect again"); 3515b9c547cSRui Paulo if (msg_type == RADIUS_ACCT || 3525b9c547cSRui Paulo msg_type == RADIUS_ACCT_INTERIM) { 35339beb93cSSam Leffler radius_client_init_acct(radius); 3545b9c547cSRui Paulo return 0; 3555b9c547cSRui Paulo } else { 35639beb93cSSam Leffler radius_client_init_auth(radius); 3575b9c547cSRui Paulo return 1; 3585b9c547cSRui Paulo } 35939beb93cSSam Leffler } 36039beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 3615b9c547cSRui Paulo 3625b9c547cSRui Paulo return 0; 36339beb93cSSam Leffler } 36439beb93cSSam Leffler 36539beb93cSSam Leffler 36639beb93cSSam Leffler static int radius_client_retransmit(struct radius_client_data *radius, 36739beb93cSSam Leffler struct radius_msg_list *entry, 36839beb93cSSam Leffler os_time_t now) 36939beb93cSSam Leffler { 37039beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 37139beb93cSSam Leffler int s; 372e28a4053SRui Paulo struct wpabuf *buf; 3735b9c547cSRui Paulo size_t prev_num_msgs; 374780fb4a2SCy Schubert u8 *acct_delay_time; 375780fb4a2SCy Schubert size_t acct_delay_time_len; 3764bc52338SCy Schubert int num_servers; 37739beb93cSSam Leffler 37839beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 37939beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) { 3804bc52338SCy Schubert num_servers = conf->num_acct_servers; 3815b9c547cSRui Paulo if (radius->acct_sock < 0) 3825b9c547cSRui Paulo radius_client_init_acct(radius); 3835b9c547cSRui Paulo if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { 3845b9c547cSRui Paulo prev_num_msgs = radius->num_msgs; 3855b9c547cSRui Paulo radius_client_acct_failover(radius); 3865b9c547cSRui Paulo if (prev_num_msgs != radius->num_msgs) 3875b9c547cSRui Paulo return 0; 3885b9c547cSRui Paulo } 38939beb93cSSam Leffler s = radius->acct_sock; 39039beb93cSSam Leffler if (entry->attempts == 0) 39139beb93cSSam Leffler conf->acct_server->requests++; 39239beb93cSSam Leffler else { 39339beb93cSSam Leffler conf->acct_server->timeouts++; 39439beb93cSSam Leffler conf->acct_server->retransmissions++; 39539beb93cSSam Leffler } 39639beb93cSSam Leffler } else { 3974bc52338SCy Schubert num_servers = conf->num_auth_servers; 3985b9c547cSRui Paulo if (radius->auth_sock < 0) 3995b9c547cSRui Paulo radius_client_init_auth(radius); 4005b9c547cSRui Paulo if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { 4015b9c547cSRui Paulo prev_num_msgs = radius->num_msgs; 4025b9c547cSRui Paulo radius_client_auth_failover(radius); 4035b9c547cSRui Paulo if (prev_num_msgs != radius->num_msgs) 4045b9c547cSRui Paulo return 0; 4055b9c547cSRui Paulo } 40639beb93cSSam Leffler s = radius->auth_sock; 40739beb93cSSam Leffler if (entry->attempts == 0) 40839beb93cSSam Leffler conf->auth_server->requests++; 40939beb93cSSam Leffler else { 41039beb93cSSam Leffler conf->auth_server->timeouts++; 41139beb93cSSam Leffler conf->auth_server->retransmissions++; 41239beb93cSSam Leffler } 41339beb93cSSam Leffler } 414780fb4a2SCy Schubert 415780fb4a2SCy Schubert if (entry->msg_type == RADIUS_ACCT_INTERIM) { 416780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 417780fb4a2SCy Schubert "RADIUS: Failed to transmit interim accounting update to " 418780fb4a2SCy Schubert MACSTR " - drop message and request a new update", 419780fb4a2SCy Schubert MAC2STR(entry->addr)); 420780fb4a2SCy Schubert if (radius->interim_error_cb) 421780fb4a2SCy Schubert radius->interim_error_cb(entry->addr, 422780fb4a2SCy Schubert radius->interim_error_cb_ctx); 423780fb4a2SCy Schubert return 1; 424780fb4a2SCy Schubert } 425780fb4a2SCy Schubert 4265b9c547cSRui Paulo if (s < 0) { 4275b9c547cSRui Paulo wpa_printf(MSG_INFO, 4285b9c547cSRui Paulo "RADIUS: No valid socket for retransmission"); 4295b9c547cSRui Paulo return 1; 4305b9c547cSRui Paulo } 43139beb93cSSam Leffler 432780fb4a2SCy Schubert if (entry->msg_type == RADIUS_ACCT && 433780fb4a2SCy Schubert radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, 434780fb4a2SCy Schubert &acct_delay_time, &acct_delay_time_len, 435780fb4a2SCy Schubert NULL) == 0 && 436780fb4a2SCy Schubert acct_delay_time_len == 4) { 437780fb4a2SCy Schubert struct radius_hdr *hdr; 438780fb4a2SCy Schubert u32 delay_time; 439780fb4a2SCy Schubert 440780fb4a2SCy Schubert /* 441780fb4a2SCy Schubert * Need to assign a new identifier since attribute contents 442780fb4a2SCy Schubert * changes. 443780fb4a2SCy Schubert */ 444780fb4a2SCy Schubert hdr = radius_msg_get_hdr(entry->msg); 445780fb4a2SCy Schubert hdr->identifier = radius_client_get_id(radius); 446780fb4a2SCy Schubert 447780fb4a2SCy Schubert /* Update Acct-Delay-Time to show wait time in queue */ 448780fb4a2SCy Schubert delay_time = now - entry->first_try; 449780fb4a2SCy Schubert WPA_PUT_BE32(acct_delay_time, delay_time); 450780fb4a2SCy Schubert 451780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 452780fb4a2SCy Schubert "RADIUS: Updated Acct-Delay-Time to %u for retransmission", 453780fb4a2SCy Schubert delay_time); 454780fb4a2SCy Schubert radius_msg_finish_acct(entry->msg, entry->shared_secret, 455780fb4a2SCy Schubert entry->shared_secret_len); 456780fb4a2SCy Schubert if (radius->conf->msg_dumps) 457780fb4a2SCy Schubert radius_msg_dump(entry->msg); 458780fb4a2SCy Schubert } 459780fb4a2SCy Schubert 46039beb93cSSam Leffler /* retransmit; remove entry if too many attempts */ 461c1d255d3SCy Schubert if (entry->accu_attempts >= RADIUS_CLIENT_MAX_FAILOVER * 4624bc52338SCy Schubert RADIUS_CLIENT_NUM_FAILOVER * num_servers) { 4634bc52338SCy Schubert wpa_printf(MSG_INFO, 4644bc52338SCy Schubert "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); 4654bc52338SCy Schubert return 1; 4664bc52338SCy Schubert } 4674bc52338SCy Schubert 46839beb93cSSam Leffler entry->attempts++; 4694bc52338SCy Schubert entry->accu_attempts++; 47039beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, 47139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", 472e28a4053SRui Paulo radius_msg_get_hdr(entry->msg)->identifier); 47339beb93cSSam Leffler 4745b9c547cSRui Paulo os_get_reltime(&entry->last_attempt); 475e28a4053SRui Paulo buf = radius_msg_get_buf(entry->msg); 4765b9c547cSRui Paulo if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { 4775b9c547cSRui Paulo if (radius_client_handle_send_error(radius, s, entry->msg_type) 4785b9c547cSRui Paulo > 0) 4795b9c547cSRui Paulo return 0; 4805b9c547cSRui Paulo } 48139beb93cSSam Leffler 48239beb93cSSam Leffler entry->next_try = now + entry->next_wait; 48339beb93cSSam Leffler entry->next_wait *= 2; 48439beb93cSSam Leffler if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 48539beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 48639beb93cSSam Leffler 48739beb93cSSam Leffler return 0; 48839beb93cSSam Leffler } 48939beb93cSSam Leffler 49039beb93cSSam Leffler 49139beb93cSSam Leffler static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 49239beb93cSSam Leffler { 49339beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 4945b9c547cSRui Paulo struct os_reltime now; 49539beb93cSSam Leffler os_time_t first; 49639beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 49739beb93cSSam Leffler int auth_failover = 0, acct_failover = 0; 4985b9c547cSRui Paulo size_t prev_num_msgs; 4995b9c547cSRui Paulo int s; 50039beb93cSSam Leffler 50139beb93cSSam Leffler entry = radius->msgs; 50239beb93cSSam Leffler if (!entry) 50339beb93cSSam Leffler return; 50439beb93cSSam Leffler 5055b9c547cSRui Paulo os_get_reltime(&now); 5064bc52338SCy Schubert 5074bc52338SCy Schubert while (entry) { 5084bc52338SCy Schubert if (now.sec >= entry->next_try) { 5094bc52338SCy Schubert s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock : 5104bc52338SCy Schubert radius->acct_sock; 511c1d255d3SCy Schubert if (entry->attempts >= RADIUS_CLIENT_NUM_FAILOVER || 5124bc52338SCy Schubert (s < 0 && entry->attempts > 0)) { 5134bc52338SCy Schubert if (entry->msg_type == RADIUS_ACCT || 5144bc52338SCy Schubert entry->msg_type == RADIUS_ACCT_INTERIM) 5154bc52338SCy Schubert acct_failover++; 5164bc52338SCy Schubert else 5174bc52338SCy Schubert auth_failover++; 5184bc52338SCy Schubert } 5194bc52338SCy Schubert } 5204bc52338SCy Schubert entry = entry->next; 5214bc52338SCy Schubert } 5224bc52338SCy Schubert 5234bc52338SCy Schubert if (auth_failover) 5244bc52338SCy Schubert radius_client_auth_failover(radius); 5254bc52338SCy Schubert 5264bc52338SCy Schubert if (acct_failover) 5274bc52338SCy Schubert radius_client_acct_failover(radius); 5284bc52338SCy Schubert 5294bc52338SCy Schubert entry = radius->msgs; 53039beb93cSSam Leffler first = 0; 53139beb93cSSam Leffler 53239beb93cSSam Leffler prev = NULL; 53339beb93cSSam Leffler while (entry) { 5345b9c547cSRui Paulo prev_num_msgs = radius->num_msgs; 53539beb93cSSam Leffler if (now.sec >= entry->next_try && 53639beb93cSSam Leffler radius_client_retransmit(radius, entry, now.sec)) { 53739beb93cSSam Leffler if (prev) 53839beb93cSSam Leffler prev->next = entry->next; 53939beb93cSSam Leffler else 54039beb93cSSam Leffler radius->msgs = entry->next; 54139beb93cSSam Leffler 54239beb93cSSam Leffler tmp = entry; 54339beb93cSSam Leffler entry = entry->next; 54439beb93cSSam Leffler radius_client_msg_free(tmp); 54539beb93cSSam Leffler radius->num_msgs--; 54639beb93cSSam Leffler continue; 54739beb93cSSam Leffler } 54839beb93cSSam Leffler 5495b9c547cSRui Paulo if (prev_num_msgs != radius->num_msgs) { 5505b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 5515b9c547cSRui Paulo "RADIUS: Message removed from queue - restart from beginning"); 5525b9c547cSRui Paulo entry = radius->msgs; 5535b9c547cSRui Paulo prev = NULL; 5545b9c547cSRui Paulo continue; 5555b9c547cSRui Paulo } 5565b9c547cSRui Paulo 55739beb93cSSam Leffler if (first == 0 || entry->next_try < first) 55839beb93cSSam Leffler first = entry->next_try; 55939beb93cSSam Leffler 56039beb93cSSam Leffler prev = entry; 56139beb93cSSam Leffler entry = entry->next; 56239beb93cSSam Leffler } 56339beb93cSSam Leffler 56439beb93cSSam Leffler if (radius->msgs) { 56539beb93cSSam Leffler if (first < now.sec) 56639beb93cSSam Leffler first = now.sec; 5674bc52338SCy Schubert eloop_cancel_timeout(radius_client_timer, radius, NULL); 56839beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, 56939beb93cSSam Leffler radius_client_timer, radius, NULL); 57039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 57139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " 57239beb93cSSam Leffler "retransmit in %ld seconds", 57339beb93cSSam Leffler (long int) (first - now.sec)); 57439beb93cSSam Leffler } 5755b9c547cSRui Paulo } 5765b9c547cSRui Paulo 5775b9c547cSRui Paulo 5785b9c547cSRui Paulo static void radius_client_auth_failover(struct radius_client_data *radius) 5795b9c547cSRui Paulo { 5805b9c547cSRui Paulo struct hostapd_radius_servers *conf = radius->conf; 58139beb93cSSam Leffler struct hostapd_radius_server *next, *old; 5825b9c547cSRui Paulo struct radius_msg_list *entry; 5835b9c547cSRui Paulo char abuf[50]; 5845b9c547cSRui Paulo 58539beb93cSSam Leffler old = conf->auth_server; 58639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 58739beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 5885b9c547cSRui Paulo "No response from Authentication server %s:%d - failover", 58939beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 59039beb93cSSam Leffler old->port); 59139beb93cSSam Leffler 59239beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 59339beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH) 59439beb93cSSam Leffler old->timeouts++; 59539beb93cSSam Leffler } 59639beb93cSSam Leffler 59739beb93cSSam Leffler next = old + 1; 59839beb93cSSam Leffler if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) 59939beb93cSSam Leffler next = conf->auth_servers; 60039beb93cSSam Leffler conf->auth_server = next; 60139beb93cSSam Leffler radius_change_server(radius, next, old, 60239beb93cSSam Leffler radius->auth_serv_sock, 60339beb93cSSam Leffler radius->auth_serv_sock6, 1); 60439beb93cSSam Leffler } 60539beb93cSSam Leffler 6065b9c547cSRui Paulo 6075b9c547cSRui Paulo static void radius_client_acct_failover(struct radius_client_data *radius) 6085b9c547cSRui Paulo { 6095b9c547cSRui Paulo struct hostapd_radius_servers *conf = radius->conf; 61039beb93cSSam Leffler struct hostapd_radius_server *next, *old; 6115b9c547cSRui Paulo struct radius_msg_list *entry; 6125b9c547cSRui Paulo char abuf[50]; 6135b9c547cSRui Paulo 61439beb93cSSam Leffler old = conf->acct_server; 61539beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 61639beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 6175b9c547cSRui Paulo "No response from Accounting server %s:%d - failover", 61839beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 61939beb93cSSam Leffler old->port); 62039beb93cSSam Leffler 62139beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 62239beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 62339beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) 62439beb93cSSam Leffler old->timeouts++; 62539beb93cSSam Leffler } 62639beb93cSSam Leffler 62739beb93cSSam Leffler next = old + 1; 62839beb93cSSam Leffler if (next > &conf->acct_servers[conf->num_acct_servers - 1]) 62939beb93cSSam Leffler next = conf->acct_servers; 63039beb93cSSam Leffler conf->acct_server = next; 63139beb93cSSam Leffler radius_change_server(radius, next, old, 63239beb93cSSam Leffler radius->acct_serv_sock, 63339beb93cSSam Leffler radius->acct_serv_sock6, 0); 63439beb93cSSam Leffler } 63539beb93cSSam Leffler 63639beb93cSSam Leffler 63739beb93cSSam Leffler static void radius_client_update_timeout(struct radius_client_data *radius) 63839beb93cSSam Leffler { 6395b9c547cSRui Paulo struct os_reltime now; 64039beb93cSSam Leffler os_time_t first; 64139beb93cSSam Leffler struct radius_msg_list *entry; 64239beb93cSSam Leffler 64339beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 64439beb93cSSam Leffler 64539beb93cSSam Leffler if (radius->msgs == NULL) { 64639beb93cSSam Leffler return; 64739beb93cSSam Leffler } 64839beb93cSSam Leffler 64939beb93cSSam Leffler first = 0; 65039beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 65139beb93cSSam Leffler if (first == 0 || entry->next_try < first) 65239beb93cSSam Leffler first = entry->next_try; 65339beb93cSSam Leffler } 65439beb93cSSam Leffler 6555b9c547cSRui Paulo os_get_reltime(&now); 65639beb93cSSam Leffler if (first < now.sec) 65739beb93cSSam Leffler first = now.sec; 65839beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, 65939beb93cSSam Leffler NULL); 66039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 66139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" 662f05cddf9SRui Paulo " %ld seconds", (long int) (first - now.sec)); 66339beb93cSSam Leffler } 66439beb93cSSam Leffler 66539beb93cSSam Leffler 66639beb93cSSam Leffler static void radius_client_list_add(struct radius_client_data *radius, 66739beb93cSSam Leffler struct radius_msg *msg, 668e28a4053SRui Paulo RadiusType msg_type, 669e28a4053SRui Paulo const u8 *shared_secret, 67039beb93cSSam Leffler size_t shared_secret_len, const u8 *addr) 67139beb93cSSam Leffler { 67239beb93cSSam Leffler struct radius_msg_list *entry, *prev; 67339beb93cSSam Leffler 67439beb93cSSam Leffler if (eloop_terminated()) { 67539beb93cSSam Leffler /* No point in adding entries to retransmit queue since event 67639beb93cSSam Leffler * loop has already been terminated. */ 67739beb93cSSam Leffler radius_msg_free(msg); 67839beb93cSSam Leffler return; 67939beb93cSSam Leffler } 68039beb93cSSam Leffler 68139beb93cSSam Leffler entry = os_zalloc(sizeof(*entry)); 68239beb93cSSam Leffler if (entry == NULL) { 6835b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); 68439beb93cSSam Leffler radius_msg_free(msg); 68539beb93cSSam Leffler return; 68639beb93cSSam Leffler } 68739beb93cSSam Leffler 68839beb93cSSam Leffler if (addr) 68939beb93cSSam Leffler os_memcpy(entry->addr, addr, ETH_ALEN); 69039beb93cSSam Leffler entry->msg = msg; 69139beb93cSSam Leffler entry->msg_type = msg_type; 69239beb93cSSam Leffler entry->shared_secret = shared_secret; 69339beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 6945b9c547cSRui Paulo os_get_reltime(&entry->last_attempt); 69539beb93cSSam Leffler entry->first_try = entry->last_attempt.sec; 69639beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 69739beb93cSSam Leffler entry->attempts = 1; 6984bc52338SCy Schubert entry->accu_attempts = 1; 69939beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 7004bc52338SCy Schubert if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 7014bc52338SCy Schubert entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 70239beb93cSSam Leffler entry->next = radius->msgs; 70339beb93cSSam Leffler radius->msgs = entry; 70439beb93cSSam Leffler radius_client_update_timeout(radius); 70539beb93cSSam Leffler 70639beb93cSSam Leffler if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { 7075b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); 70839beb93cSSam Leffler prev = NULL; 70939beb93cSSam Leffler while (entry->next) { 71039beb93cSSam Leffler prev = entry; 71139beb93cSSam Leffler entry = entry->next; 71239beb93cSSam Leffler } 71339beb93cSSam Leffler if (prev) { 71439beb93cSSam Leffler prev->next = NULL; 71539beb93cSSam Leffler radius_client_msg_free(entry); 71639beb93cSSam Leffler } 71739beb93cSSam Leffler } else 71839beb93cSSam Leffler radius->num_msgs++; 71939beb93cSSam Leffler } 72039beb93cSSam Leffler 72139beb93cSSam Leffler 722e28a4053SRui Paulo /** 723e28a4053SRui Paulo * radius_client_send - Send a RADIUS request 724e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 725e28a4053SRui Paulo * @msg: RADIUS message to be sent 726e28a4053SRui Paulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) 727e28a4053SRui Paulo * @addr: MAC address of the device related to this message or %NULL 728e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 729e28a4053SRui Paulo * 730e28a4053SRui Paulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or 731e28a4053SRui Paulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference 732e28a4053SRui Paulo * between accounting and interim accounting messages is that the interim 733780fb4a2SCy Schubert * message will not be retransmitted. Instead, a callback is used to indicate 734780fb4a2SCy Schubert * that the transmission failed for the specific station @addr so that a new 735780fb4a2SCy Schubert * interim accounting update message can be generated with up-to-date session 736780fb4a2SCy Schubert * data instead of trying to resend old information. 737e28a4053SRui Paulo * 738e28a4053SRui Paulo * The message is added on the retransmission queue and will be retransmitted 739e28a4053SRui Paulo * automatically until a response is received or maximum number of retries 7404bc52338SCy Schubert * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No 7414bc52338SCy Schubert * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message 7424bc52338SCy Schubert * is removed from the queue automatically on transmission failure. 743e28a4053SRui Paulo * 744e28a4053SRui Paulo * The related device MAC address can be used to identify pending messages that 745780fb4a2SCy Schubert * can be removed with radius_client_flush_auth(). 746e28a4053SRui Paulo */ 74739beb93cSSam Leffler int radius_client_send(struct radius_client_data *radius, 74839beb93cSSam Leffler struct radius_msg *msg, RadiusType msg_type, 74939beb93cSSam Leffler const u8 *addr) 75039beb93cSSam Leffler { 75139beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 752e28a4053SRui Paulo const u8 *shared_secret; 75339beb93cSSam Leffler size_t shared_secret_len; 75439beb93cSSam Leffler char *name; 75539beb93cSSam Leffler int s, res; 756e28a4053SRui Paulo struct wpabuf *buf; 75739beb93cSSam Leffler 75839beb93cSSam Leffler if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { 7595b9c547cSRui Paulo if (conf->acct_server && radius->acct_sock < 0) 7605b9c547cSRui Paulo radius_client_init_acct(radius); 7615b9c547cSRui Paulo 7625b9c547cSRui Paulo if (conf->acct_server == NULL || radius->acct_sock < 0 || 7635b9c547cSRui Paulo conf->acct_server->shared_secret == NULL) { 76439beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 76539beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 76639beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 76739beb93cSSam Leffler "No accounting server configured"); 76839beb93cSSam Leffler return -1; 76939beb93cSSam Leffler } 77039beb93cSSam Leffler shared_secret = conf->acct_server->shared_secret; 77139beb93cSSam Leffler shared_secret_len = conf->acct_server->shared_secret_len; 77239beb93cSSam Leffler radius_msg_finish_acct(msg, shared_secret, shared_secret_len); 77339beb93cSSam Leffler name = "accounting"; 77439beb93cSSam Leffler s = radius->acct_sock; 77539beb93cSSam Leffler conf->acct_server->requests++; 77639beb93cSSam Leffler } else { 7775b9c547cSRui Paulo if (conf->auth_server && radius->auth_sock < 0) 7785b9c547cSRui Paulo radius_client_init_auth(radius); 7795b9c547cSRui Paulo 7805b9c547cSRui Paulo if (conf->auth_server == NULL || radius->auth_sock < 0 || 7815b9c547cSRui Paulo conf->auth_server->shared_secret == NULL) { 78239beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 78339beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 78439beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 78539beb93cSSam Leffler "No authentication server configured"); 78639beb93cSSam Leffler return -1; 78739beb93cSSam Leffler } 78839beb93cSSam Leffler shared_secret = conf->auth_server->shared_secret; 78939beb93cSSam Leffler shared_secret_len = conf->auth_server->shared_secret_len; 79039beb93cSSam Leffler radius_msg_finish(msg, shared_secret, shared_secret_len); 79139beb93cSSam Leffler name = "authentication"; 79239beb93cSSam Leffler s = radius->auth_sock; 79339beb93cSSam Leffler conf->auth_server->requests++; 79439beb93cSSam Leffler } 79539beb93cSSam Leffler 79639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 79739beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " 79839beb93cSSam Leffler "server", name); 79939beb93cSSam Leffler if (conf->msg_dumps) 80039beb93cSSam Leffler radius_msg_dump(msg); 80139beb93cSSam Leffler 802e28a4053SRui Paulo buf = radius_msg_get_buf(msg); 803e28a4053SRui Paulo res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); 80439beb93cSSam Leffler if (res < 0) 80539beb93cSSam Leffler radius_client_handle_send_error(radius, s, msg_type); 80639beb93cSSam Leffler 80739beb93cSSam Leffler radius_client_list_add(radius, msg, msg_type, shared_secret, 80839beb93cSSam Leffler shared_secret_len, addr); 80939beb93cSSam Leffler 810f05cddf9SRui Paulo return 0; 81139beb93cSSam Leffler } 81239beb93cSSam Leffler 81339beb93cSSam Leffler 81439beb93cSSam Leffler static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 81539beb93cSSam Leffler { 81639beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 81739beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 818*4b72b91aSCy Schubert RadiusType msg_type = (uintptr_t) sock_ctx; 81939beb93cSSam Leffler int len, roundtrip; 820c1d255d3SCy Schubert unsigned char buf[RADIUS_MAX_MSG_LEN]; 821c1d255d3SCy Schubert struct msghdr msghdr = {0}; 822c1d255d3SCy Schubert struct iovec iov; 82339beb93cSSam Leffler struct radius_msg *msg; 824e28a4053SRui Paulo struct radius_hdr *hdr; 82539beb93cSSam Leffler struct radius_rx_handler *handlers; 82639beb93cSSam Leffler size_t num_handlers, i; 82739beb93cSSam Leffler struct radius_msg_list *req, *prev_req; 8285b9c547cSRui Paulo struct os_reltime now; 82939beb93cSSam Leffler struct hostapd_radius_server *rconf; 83039beb93cSSam Leffler int invalid_authenticator = 0; 83139beb93cSSam Leffler 83239beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 83339beb93cSSam Leffler handlers = radius->acct_handlers; 83439beb93cSSam Leffler num_handlers = radius->num_acct_handlers; 83539beb93cSSam Leffler rconf = conf->acct_server; 83639beb93cSSam Leffler } else { 83739beb93cSSam Leffler handlers = radius->auth_handlers; 83839beb93cSSam Leffler num_handlers = radius->num_auth_handlers; 83939beb93cSSam Leffler rconf = conf->auth_server; 84039beb93cSSam Leffler } 84139beb93cSSam Leffler 842c1d255d3SCy Schubert iov.iov_base = buf; 843c1d255d3SCy Schubert iov.iov_len = RADIUS_MAX_MSG_LEN; 844c1d255d3SCy Schubert msghdr.msg_iov = &iov; 845c1d255d3SCy Schubert msghdr.msg_iovlen = 1; 846c1d255d3SCy Schubert msghdr.msg_flags = 0; 847c1d255d3SCy Schubert len = recvmsg(sock, &msghdr, MSG_DONTWAIT); 84839beb93cSSam Leffler if (len < 0) { 849c1d255d3SCy Schubert wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno)); 85039beb93cSSam Leffler return; 85139beb93cSSam Leffler } 852c1d255d3SCy Schubert 85339beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 85439beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " 85539beb93cSSam Leffler "server", len); 856c1d255d3SCy Schubert 857c1d255d3SCy Schubert if (msghdr.msg_flags & MSG_TRUNC) { 8585b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); 85939beb93cSSam Leffler return; 86039beb93cSSam Leffler } 86139beb93cSSam Leffler 86239beb93cSSam Leffler msg = radius_msg_parse(buf, len); 86339beb93cSSam Leffler if (msg == NULL) { 8645b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); 86539beb93cSSam Leffler rconf->malformed_responses++; 86639beb93cSSam Leffler return; 86739beb93cSSam Leffler } 868e28a4053SRui Paulo hdr = radius_msg_get_hdr(msg); 86939beb93cSSam Leffler 87039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 87139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); 87239beb93cSSam Leffler if (conf->msg_dumps) 87339beb93cSSam Leffler radius_msg_dump(msg); 87439beb93cSSam Leffler 875e28a4053SRui Paulo switch (hdr->code) { 87639beb93cSSam Leffler case RADIUS_CODE_ACCESS_ACCEPT: 87739beb93cSSam Leffler rconf->access_accepts++; 87839beb93cSSam Leffler break; 87939beb93cSSam Leffler case RADIUS_CODE_ACCESS_REJECT: 88039beb93cSSam Leffler rconf->access_rejects++; 88139beb93cSSam Leffler break; 88239beb93cSSam Leffler case RADIUS_CODE_ACCESS_CHALLENGE: 88339beb93cSSam Leffler rconf->access_challenges++; 88439beb93cSSam Leffler break; 88539beb93cSSam Leffler case RADIUS_CODE_ACCOUNTING_RESPONSE: 88639beb93cSSam Leffler rconf->responses++; 88739beb93cSSam Leffler break; 88839beb93cSSam Leffler } 88939beb93cSSam Leffler 89039beb93cSSam Leffler prev_req = NULL; 89139beb93cSSam Leffler req = radius->msgs; 89239beb93cSSam Leffler while (req) { 89339beb93cSSam Leffler /* TODO: also match by src addr:port of the packet when using 89439beb93cSSam Leffler * alternative RADIUS servers (?) */ 89539beb93cSSam Leffler if ((req->msg_type == msg_type || 89639beb93cSSam Leffler (req->msg_type == RADIUS_ACCT_INTERIM && 89739beb93cSSam Leffler msg_type == RADIUS_ACCT)) && 898e28a4053SRui Paulo radius_msg_get_hdr(req->msg)->identifier == 899e28a4053SRui Paulo hdr->identifier) 90039beb93cSSam Leffler break; 90139beb93cSSam Leffler 90239beb93cSSam Leffler prev_req = req; 90339beb93cSSam Leffler req = req->next; 90439beb93cSSam Leffler } 90539beb93cSSam Leffler 90639beb93cSSam Leffler if (req == NULL) { 90739beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 90839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 90939beb93cSSam Leffler "No matching RADIUS request found (type=%d " 91039beb93cSSam Leffler "id=%d) - dropping packet", 911e28a4053SRui Paulo msg_type, hdr->identifier); 91239beb93cSSam Leffler goto fail; 91339beb93cSSam Leffler } 91439beb93cSSam Leffler 9155b9c547cSRui Paulo os_get_reltime(&now); 91639beb93cSSam Leffler roundtrip = (now.sec - req->last_attempt.sec) * 100 + 91739beb93cSSam Leffler (now.usec - req->last_attempt.usec) / 10000; 91839beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 91939beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 92039beb93cSSam Leffler "Received RADIUS packet matched with a pending " 92139beb93cSSam Leffler "request, round trip time %d.%02d sec", 92239beb93cSSam Leffler roundtrip / 100, roundtrip % 100); 92339beb93cSSam Leffler rconf->round_trip_time = roundtrip; 92439beb93cSSam Leffler 92539beb93cSSam Leffler /* Remove ACKed RADIUS packet from retransmit list */ 92639beb93cSSam Leffler if (prev_req) 92739beb93cSSam Leffler prev_req->next = req->next; 92839beb93cSSam Leffler else 92939beb93cSSam Leffler radius->msgs = req->next; 93039beb93cSSam Leffler radius->num_msgs--; 93139beb93cSSam Leffler 93239beb93cSSam Leffler for (i = 0; i < num_handlers; i++) { 93339beb93cSSam Leffler RadiusRxResult res; 93439beb93cSSam Leffler res = handlers[i].handler(msg, req->msg, req->shared_secret, 93539beb93cSSam Leffler req->shared_secret_len, 93639beb93cSSam Leffler handlers[i].data); 93739beb93cSSam Leffler switch (res) { 93839beb93cSSam Leffler case RADIUS_RX_PROCESSED: 93939beb93cSSam Leffler radius_msg_free(msg); 94085732ac8SCy Schubert /* fall through */ 94139beb93cSSam Leffler case RADIUS_RX_QUEUED: 94239beb93cSSam Leffler radius_client_msg_free(req); 94339beb93cSSam Leffler return; 94439beb93cSSam Leffler case RADIUS_RX_INVALID_AUTHENTICATOR: 94539beb93cSSam Leffler invalid_authenticator++; 94685732ac8SCy Schubert /* fall through */ 94739beb93cSSam Leffler case RADIUS_RX_UNKNOWN: 94839beb93cSSam Leffler /* continue with next handler */ 94939beb93cSSam Leffler break; 95039beb93cSSam Leffler } 95139beb93cSSam Leffler } 95239beb93cSSam Leffler 95339beb93cSSam Leffler if (invalid_authenticator) 95439beb93cSSam Leffler rconf->bad_authenticators++; 95539beb93cSSam Leffler else 95639beb93cSSam Leffler rconf->unknown_types++; 95739beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 95839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " 95939beb93cSSam Leffler "(type=%d code=%d id=%d)%s - dropping packet", 960e28a4053SRui Paulo msg_type, hdr->code, hdr->identifier, 96139beb93cSSam Leffler invalid_authenticator ? " [INVALID AUTHENTICATOR]" : 96239beb93cSSam Leffler ""); 96339beb93cSSam Leffler radius_client_msg_free(req); 96439beb93cSSam Leffler 96539beb93cSSam Leffler fail: 96639beb93cSSam Leffler radius_msg_free(msg); 96739beb93cSSam Leffler } 96839beb93cSSam Leffler 96939beb93cSSam Leffler 970e28a4053SRui Paulo /** 971e28a4053SRui Paulo * radius_client_get_id - Get an identifier for a new RADIUS message 972e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 973e28a4053SRui Paulo * Returns: Allocated identifier 974e28a4053SRui Paulo * 975e28a4053SRui Paulo * This function is used to fetch a unique (among pending requests) identifier 976e28a4053SRui Paulo * for a new RADIUS message. 977e28a4053SRui Paulo */ 97839beb93cSSam Leffler u8 radius_client_get_id(struct radius_client_data *radius) 97939beb93cSSam Leffler { 98039beb93cSSam Leffler struct radius_msg_list *entry, *prev, *_remove; 98139beb93cSSam Leffler u8 id = radius->next_radius_identifier++; 98239beb93cSSam Leffler 98339beb93cSSam Leffler /* remove entries with matching id from retransmit list to avoid 98439beb93cSSam Leffler * using new reply from the RADIUS server with an old request */ 98539beb93cSSam Leffler entry = radius->msgs; 98639beb93cSSam Leffler prev = NULL; 98739beb93cSSam Leffler while (entry) { 988e28a4053SRui Paulo if (radius_msg_get_hdr(entry->msg)->identifier == id) { 98939beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, 99039beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 99139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 99239beb93cSSam Leffler "Removing pending RADIUS message, " 99339beb93cSSam Leffler "since its id (%d) is reused", id); 99439beb93cSSam Leffler if (prev) 99539beb93cSSam Leffler prev->next = entry->next; 99639beb93cSSam Leffler else 99739beb93cSSam Leffler radius->msgs = entry->next; 99839beb93cSSam Leffler _remove = entry; 99939beb93cSSam Leffler } else { 100039beb93cSSam Leffler _remove = NULL; 100139beb93cSSam Leffler prev = entry; 100239beb93cSSam Leffler } 100339beb93cSSam Leffler entry = entry->next; 100439beb93cSSam Leffler 100539beb93cSSam Leffler if (_remove) 100639beb93cSSam Leffler radius_client_msg_free(_remove); 100739beb93cSSam Leffler } 100839beb93cSSam Leffler 100939beb93cSSam Leffler return id; 101039beb93cSSam Leffler } 101139beb93cSSam Leffler 101239beb93cSSam Leffler 1013e28a4053SRui Paulo /** 1014e28a4053SRui Paulo * radius_client_flush - Flush all pending RADIUS client messages 1015e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1016e28a4053SRui Paulo * @only_auth: Whether only authentication messages are removed 1017e28a4053SRui Paulo */ 101839beb93cSSam Leffler void radius_client_flush(struct radius_client_data *radius, int only_auth) 101939beb93cSSam Leffler { 102039beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 102139beb93cSSam Leffler 102239beb93cSSam Leffler if (!radius) 102339beb93cSSam Leffler return; 102439beb93cSSam Leffler 102539beb93cSSam Leffler prev = NULL; 102639beb93cSSam Leffler entry = radius->msgs; 102739beb93cSSam Leffler 102839beb93cSSam Leffler while (entry) { 102939beb93cSSam Leffler if (!only_auth || entry->msg_type == RADIUS_AUTH) { 103039beb93cSSam Leffler if (prev) 103139beb93cSSam Leffler prev->next = entry->next; 103239beb93cSSam Leffler else 103339beb93cSSam Leffler radius->msgs = entry->next; 103439beb93cSSam Leffler 103539beb93cSSam Leffler tmp = entry; 103639beb93cSSam Leffler entry = entry->next; 103739beb93cSSam Leffler radius_client_msg_free(tmp); 103839beb93cSSam Leffler radius->num_msgs--; 103939beb93cSSam Leffler } else { 104039beb93cSSam Leffler prev = entry; 104139beb93cSSam Leffler entry = entry->next; 104239beb93cSSam Leffler } 104339beb93cSSam Leffler } 104439beb93cSSam Leffler 104539beb93cSSam Leffler if (radius->msgs == NULL) 104639beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 104739beb93cSSam Leffler } 104839beb93cSSam Leffler 104939beb93cSSam Leffler 105039beb93cSSam Leffler static void radius_client_update_acct_msgs(struct radius_client_data *radius, 1051e28a4053SRui Paulo const u8 *shared_secret, 105239beb93cSSam Leffler size_t shared_secret_len) 105339beb93cSSam Leffler { 105439beb93cSSam Leffler struct radius_msg_list *entry; 105539beb93cSSam Leffler 105639beb93cSSam Leffler if (!radius) 105739beb93cSSam Leffler return; 105839beb93cSSam Leffler 105939beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 106039beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT) { 106139beb93cSSam Leffler entry->shared_secret = shared_secret; 106239beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 106339beb93cSSam Leffler radius_msg_finish_acct(entry->msg, shared_secret, 106439beb93cSSam Leffler shared_secret_len); 106539beb93cSSam Leffler } 106639beb93cSSam Leffler } 106739beb93cSSam Leffler } 106839beb93cSSam Leffler 106939beb93cSSam Leffler 107039beb93cSSam Leffler static int 107139beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 107239beb93cSSam Leffler struct hostapd_radius_server *nserv, 107339beb93cSSam Leffler struct hostapd_radius_server *oserv, 107439beb93cSSam Leffler int sock, int sock6, int auth) 107539beb93cSSam Leffler { 107639beb93cSSam Leffler struct sockaddr_in serv, claddr; 107739beb93cSSam Leffler #ifdef CONFIG_IPV6 107839beb93cSSam Leffler struct sockaddr_in6 serv6, claddr6; 107939beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 108039beb93cSSam Leffler struct sockaddr *addr, *cl_addr; 108139beb93cSSam Leffler socklen_t addrlen, claddrlen; 108239beb93cSSam Leffler char abuf[50]; 108339beb93cSSam Leffler int sel_sock; 108439beb93cSSam Leffler struct radius_msg_list *entry; 108539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 1086780fb4a2SCy Schubert struct sockaddr_in disconnect_addr = { 1087780fb4a2SCy Schubert .sin_family = AF_UNSPEC, 1088780fb4a2SCy Schubert }; 108939beb93cSSam Leffler 109039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 109139beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 109239beb93cSSam Leffler "%s server %s:%d", 109339beb93cSSam Leffler auth ? "Authentication" : "Accounting", 109439beb93cSSam Leffler hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), 109539beb93cSSam Leffler nserv->port); 109639beb93cSSam Leffler 1097780fb4a2SCy Schubert if (oserv && oserv == nserv) { 1098780fb4a2SCy Schubert /* Reconnect to same server, flush */ 1099780fb4a2SCy Schubert if (auth) 1100780fb4a2SCy Schubert radius_client_flush(radius, 1); 1101780fb4a2SCy Schubert } 1102780fb4a2SCy Schubert 11035b9c547cSRui Paulo if (oserv && oserv != nserv && 11045b9c547cSRui Paulo (nserv->shared_secret_len != oserv->shared_secret_len || 110539beb93cSSam Leffler os_memcmp(nserv->shared_secret, oserv->shared_secret, 11065b9c547cSRui Paulo nserv->shared_secret_len) != 0)) { 110739beb93cSSam Leffler /* Pending RADIUS packets used different shared secret, so 110839beb93cSSam Leffler * they need to be modified. Update accounting message 110939beb93cSSam Leffler * authenticators here. Authentication messages are removed 111039beb93cSSam Leffler * since they would require more changes and the new RADIUS 111139beb93cSSam Leffler * server may not be prepared to receive them anyway due to 111239beb93cSSam Leffler * missing state information. Client will likely retry 111339beb93cSSam Leffler * authentication, so this should not be an issue. */ 111439beb93cSSam Leffler if (auth) 111539beb93cSSam Leffler radius_client_flush(radius, 1); 111639beb93cSSam Leffler else { 111739beb93cSSam Leffler radius_client_update_acct_msgs( 111839beb93cSSam Leffler radius, nserv->shared_secret, 111939beb93cSSam Leffler nserv->shared_secret_len); 112039beb93cSSam Leffler } 112139beb93cSSam Leffler } 112239beb93cSSam Leffler 11234bc52338SCy Schubert /* Reset retry counters */ 11244bc52338SCy Schubert for (entry = radius->msgs; oserv && entry; entry = entry->next) { 112539beb93cSSam Leffler if ((auth && entry->msg_type != RADIUS_AUTH) || 112639beb93cSSam Leffler (!auth && entry->msg_type != RADIUS_ACCT)) 112739beb93cSSam Leffler continue; 112839beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 1129c1d255d3SCy Schubert entry->attempts = 0; 113039beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 113139beb93cSSam Leffler } 113239beb93cSSam Leffler 113339beb93cSSam Leffler if (radius->msgs) { 113439beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 113539beb93cSSam Leffler eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, 113639beb93cSSam Leffler radius_client_timer, radius, NULL); 113739beb93cSSam Leffler } 113839beb93cSSam Leffler 113939beb93cSSam Leffler switch (nserv->addr.af) { 114039beb93cSSam Leffler case AF_INET: 114139beb93cSSam Leffler os_memset(&serv, 0, sizeof(serv)); 114239beb93cSSam Leffler serv.sin_family = AF_INET; 114339beb93cSSam Leffler serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; 114439beb93cSSam Leffler serv.sin_port = htons(nserv->port); 114539beb93cSSam Leffler addr = (struct sockaddr *) &serv; 114639beb93cSSam Leffler addrlen = sizeof(serv); 114739beb93cSSam Leffler sel_sock = sock; 114839beb93cSSam Leffler break; 114939beb93cSSam Leffler #ifdef CONFIG_IPV6 115039beb93cSSam Leffler case AF_INET6: 115139beb93cSSam Leffler os_memset(&serv6, 0, sizeof(serv6)); 115239beb93cSSam Leffler serv6.sin6_family = AF_INET6; 115339beb93cSSam Leffler os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, 115439beb93cSSam Leffler sizeof(struct in6_addr)); 115539beb93cSSam Leffler serv6.sin6_port = htons(nserv->port); 115639beb93cSSam Leffler addr = (struct sockaddr *) &serv6; 115739beb93cSSam Leffler addrlen = sizeof(serv6); 115839beb93cSSam Leffler sel_sock = sock6; 115939beb93cSSam Leffler break; 116039beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 116139beb93cSSam Leffler default: 116239beb93cSSam Leffler return -1; 116339beb93cSSam Leffler } 116439beb93cSSam Leffler 11655b9c547cSRui Paulo if (sel_sock < 0) { 11665b9c547cSRui Paulo wpa_printf(MSG_INFO, 11675b9c547cSRui Paulo "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d", 11685b9c547cSRui Paulo nserv->addr.af, sock, sock6, auth); 11695b9c547cSRui Paulo return -1; 11705b9c547cSRui Paulo } 11715b9c547cSRui Paulo 1172c1d255d3SCy Schubert /* Force a reconnect by disconnecting the socket first */ 1173c1d255d3SCy Schubert if (connect(sel_sock, (struct sockaddr *) &disconnect_addr, 1174c1d255d3SCy Schubert sizeof(disconnect_addr)) < 0) 1175c1d255d3SCy Schubert wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno)); 1176c1d255d3SCy Schubert 1177c1d255d3SCy Schubert #ifdef __linux__ 1178c1d255d3SCy Schubert if (conf->force_client_dev && conf->force_client_dev[0]) { 1179c1d255d3SCy Schubert if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE, 1180c1d255d3SCy Schubert conf->force_client_dev, 1181c1d255d3SCy Schubert os_strlen(conf->force_client_dev)) < 0) { 1182c1d255d3SCy Schubert wpa_printf(MSG_ERROR, 1183c1d255d3SCy Schubert "RADIUS: setsockopt[SO_BINDTODEVICE]: %s", 1184c1d255d3SCy Schubert strerror(errno)); 1185c1d255d3SCy Schubert /* Probably not a critical error; continue on and hope 1186c1d255d3SCy Schubert * for the best. */ 1187c1d255d3SCy Schubert } else { 1188c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1189c1d255d3SCy Schubert "RADIUS: Bound client socket to device: %s", 1190c1d255d3SCy Schubert conf->force_client_dev); 1191c1d255d3SCy Schubert } 1192c1d255d3SCy Schubert } 1193c1d255d3SCy Schubert #endif /* __linux__ */ 1194c1d255d3SCy Schubert 119539beb93cSSam Leffler if (conf->force_client_addr) { 119639beb93cSSam Leffler switch (conf->client_addr.af) { 119739beb93cSSam Leffler case AF_INET: 119839beb93cSSam Leffler os_memset(&claddr, 0, sizeof(claddr)); 119939beb93cSSam Leffler claddr.sin_family = AF_INET; 120039beb93cSSam Leffler claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; 120139beb93cSSam Leffler claddr.sin_port = htons(0); 120239beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr; 120339beb93cSSam Leffler claddrlen = sizeof(claddr); 120439beb93cSSam Leffler break; 120539beb93cSSam Leffler #ifdef CONFIG_IPV6 120639beb93cSSam Leffler case AF_INET6: 120739beb93cSSam Leffler os_memset(&claddr6, 0, sizeof(claddr6)); 120839beb93cSSam Leffler claddr6.sin6_family = AF_INET6; 120939beb93cSSam Leffler os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, 121039beb93cSSam Leffler sizeof(struct in6_addr)); 121139beb93cSSam Leffler claddr6.sin6_port = htons(0); 121239beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr6; 121339beb93cSSam Leffler claddrlen = sizeof(claddr6); 121439beb93cSSam Leffler break; 121539beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 121639beb93cSSam Leffler default: 121739beb93cSSam Leffler return -1; 121839beb93cSSam Leffler } 121939beb93cSSam Leffler 122039beb93cSSam Leffler if (bind(sel_sock, cl_addr, claddrlen) < 0) { 12215b9c547cSRui Paulo wpa_printf(MSG_INFO, "bind[radius]: %s", 12225b9c547cSRui Paulo strerror(errno)); 122339beb93cSSam Leffler return -1; 122439beb93cSSam Leffler } 122539beb93cSSam Leffler } 122639beb93cSSam Leffler 122739beb93cSSam Leffler if (connect(sel_sock, addr, addrlen) < 0) { 12285b9c547cSRui Paulo wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); 122939beb93cSSam Leffler return -1; 123039beb93cSSam Leffler } 123139beb93cSSam Leffler 123239beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 123339beb93cSSam Leffler switch (nserv->addr.af) { 123439beb93cSSam Leffler case AF_INET: 123539beb93cSSam Leffler claddrlen = sizeof(claddr); 12365b9c547cSRui Paulo if (getsockname(sel_sock, (struct sockaddr *) &claddr, 12375b9c547cSRui Paulo &claddrlen) == 0) { 123839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 12395b9c547cSRui Paulo inet_ntoa(claddr.sin_addr), 12405b9c547cSRui Paulo ntohs(claddr.sin_port)); 12415b9c547cSRui Paulo } 124239beb93cSSam Leffler break; 124339beb93cSSam Leffler #ifdef CONFIG_IPV6 124439beb93cSSam Leffler case AF_INET6: { 124539beb93cSSam Leffler claddrlen = sizeof(claddr6); 12465b9c547cSRui Paulo if (getsockname(sel_sock, (struct sockaddr *) &claddr6, 12475b9c547cSRui Paulo &claddrlen) == 0) { 124839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 124939beb93cSSam Leffler inet_ntop(AF_INET6, &claddr6.sin6_addr, 125039beb93cSSam Leffler abuf, sizeof(abuf)), 125139beb93cSSam Leffler ntohs(claddr6.sin6_port)); 12525b9c547cSRui Paulo } 125339beb93cSSam Leffler break; 125439beb93cSSam Leffler } 125539beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 125639beb93cSSam Leffler } 125739beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 125839beb93cSSam Leffler 125939beb93cSSam Leffler if (auth) 126039beb93cSSam Leffler radius->auth_sock = sel_sock; 126139beb93cSSam Leffler else 126239beb93cSSam Leffler radius->acct_sock = sel_sock; 126339beb93cSSam Leffler 126439beb93cSSam Leffler return 0; 126539beb93cSSam Leffler } 126639beb93cSSam Leffler 126739beb93cSSam Leffler 126839beb93cSSam Leffler static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) 126939beb93cSSam Leffler { 127039beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 127139beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 127239beb93cSSam Leffler struct hostapd_radius_server *oserv; 127339beb93cSSam Leffler 127439beb93cSSam Leffler if (radius->auth_sock >= 0 && conf->auth_servers && 127539beb93cSSam Leffler conf->auth_server != conf->auth_servers) { 127639beb93cSSam Leffler oserv = conf->auth_server; 127739beb93cSSam Leffler conf->auth_server = conf->auth_servers; 12785b9c547cSRui Paulo if (radius_change_server(radius, conf->auth_server, oserv, 12795b9c547cSRui Paulo radius->auth_serv_sock, 12805b9c547cSRui Paulo radius->auth_serv_sock6, 1) < 0) { 12815b9c547cSRui Paulo conf->auth_server = oserv; 12825b9c547cSRui Paulo radius_change_server(radius, oserv, conf->auth_server, 128339beb93cSSam Leffler radius->auth_serv_sock, 128439beb93cSSam Leffler radius->auth_serv_sock6, 1); 128539beb93cSSam Leffler } 12865b9c547cSRui Paulo } 128739beb93cSSam Leffler 128839beb93cSSam Leffler if (radius->acct_sock >= 0 && conf->acct_servers && 128939beb93cSSam Leffler conf->acct_server != conf->acct_servers) { 129039beb93cSSam Leffler oserv = conf->acct_server; 129139beb93cSSam Leffler conf->acct_server = conf->acct_servers; 12925b9c547cSRui Paulo if (radius_change_server(radius, conf->acct_server, oserv, 12935b9c547cSRui Paulo radius->acct_serv_sock, 12945b9c547cSRui Paulo radius->acct_serv_sock6, 0) < 0) { 12955b9c547cSRui Paulo conf->acct_server = oserv; 12965b9c547cSRui Paulo radius_change_server(radius, oserv, conf->acct_server, 129739beb93cSSam Leffler radius->acct_serv_sock, 129839beb93cSSam Leffler radius->acct_serv_sock6, 0); 129939beb93cSSam Leffler } 13005b9c547cSRui Paulo } 130139beb93cSSam Leffler 130239beb93cSSam Leffler if (conf->retry_primary_interval) 130339beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 130439beb93cSSam Leffler radius_retry_primary_timer, radius, 130539beb93cSSam Leffler NULL); 130639beb93cSSam Leffler } 130739beb93cSSam Leffler 130839beb93cSSam Leffler 13093157ba21SRui Paulo static int radius_client_disable_pmtu_discovery(int s) 13103157ba21SRui Paulo { 13113157ba21SRui Paulo int r = -1; 13123157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 13133157ba21SRui Paulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 13143157ba21SRui Paulo int action = IP_PMTUDISC_DONT; 13153157ba21SRui Paulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 13163157ba21SRui Paulo sizeof(action)); 13173157ba21SRui Paulo if (r == -1) 13185b9c547cSRui Paulo wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", 13195b9c547cSRui Paulo strerror(errno)); 13203157ba21SRui Paulo #endif 13213157ba21SRui Paulo return r; 13223157ba21SRui Paulo } 13233157ba21SRui Paulo 13243157ba21SRui Paulo 13255b9c547cSRui Paulo static void radius_close_auth_sockets(struct radius_client_data *radius) 13265b9c547cSRui Paulo { 13275b9c547cSRui Paulo radius->auth_sock = -1; 13285b9c547cSRui Paulo 13295b9c547cSRui Paulo if (radius->auth_serv_sock >= 0) { 13305b9c547cSRui Paulo eloop_unregister_read_sock(radius->auth_serv_sock); 13315b9c547cSRui Paulo close(radius->auth_serv_sock); 13325b9c547cSRui Paulo radius->auth_serv_sock = -1; 13335b9c547cSRui Paulo } 13345b9c547cSRui Paulo #ifdef CONFIG_IPV6 13355b9c547cSRui Paulo if (radius->auth_serv_sock6 >= 0) { 13365b9c547cSRui Paulo eloop_unregister_read_sock(radius->auth_serv_sock6); 13375b9c547cSRui Paulo close(radius->auth_serv_sock6); 13385b9c547cSRui Paulo radius->auth_serv_sock6 = -1; 13395b9c547cSRui Paulo } 13405b9c547cSRui Paulo #endif /* CONFIG_IPV6 */ 13415b9c547cSRui Paulo } 13425b9c547cSRui Paulo 13435b9c547cSRui Paulo 13445b9c547cSRui Paulo static void radius_close_acct_sockets(struct radius_client_data *radius) 13455b9c547cSRui Paulo { 13465b9c547cSRui Paulo radius->acct_sock = -1; 13475b9c547cSRui Paulo 13485b9c547cSRui Paulo if (radius->acct_serv_sock >= 0) { 13495b9c547cSRui Paulo eloop_unregister_read_sock(radius->acct_serv_sock); 13505b9c547cSRui Paulo close(radius->acct_serv_sock); 13515b9c547cSRui Paulo radius->acct_serv_sock = -1; 13525b9c547cSRui Paulo } 13535b9c547cSRui Paulo #ifdef CONFIG_IPV6 13545b9c547cSRui Paulo if (radius->acct_serv_sock6 >= 0) { 13555b9c547cSRui Paulo eloop_unregister_read_sock(radius->acct_serv_sock6); 13565b9c547cSRui Paulo close(radius->acct_serv_sock6); 13575b9c547cSRui Paulo radius->acct_serv_sock6 = -1; 13585b9c547cSRui Paulo } 13595b9c547cSRui Paulo #endif /* CONFIG_IPV6 */ 13605b9c547cSRui Paulo } 13615b9c547cSRui Paulo 13625b9c547cSRui Paulo 136339beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius) 136439beb93cSSam Leffler { 136539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 136639beb93cSSam Leffler int ok = 0; 136739beb93cSSam Leffler 13685b9c547cSRui Paulo radius_close_auth_sockets(radius); 13695b9c547cSRui Paulo 137039beb93cSSam Leffler radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 137139beb93cSSam Leffler if (radius->auth_serv_sock < 0) 13725b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", 13735b9c547cSRui Paulo strerror(errno)); 13743157ba21SRui Paulo else { 13753157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->auth_serv_sock); 137639beb93cSSam Leffler ok++; 13773157ba21SRui Paulo } 137839beb93cSSam Leffler 137939beb93cSSam Leffler #ifdef CONFIG_IPV6 138039beb93cSSam Leffler radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 138139beb93cSSam Leffler if (radius->auth_serv_sock6 < 0) 13825b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", 13835b9c547cSRui Paulo strerror(errno)); 138439beb93cSSam Leffler else 138539beb93cSSam Leffler ok++; 138639beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 138739beb93cSSam Leffler 138839beb93cSSam Leffler if (ok == 0) 138939beb93cSSam Leffler return -1; 139039beb93cSSam Leffler 139139beb93cSSam Leffler radius_change_server(radius, conf->auth_server, NULL, 139239beb93cSSam Leffler radius->auth_serv_sock, radius->auth_serv_sock6, 139339beb93cSSam Leffler 1); 139439beb93cSSam Leffler 139539beb93cSSam Leffler if (radius->auth_serv_sock >= 0 && 139639beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock, 139739beb93cSSam Leffler radius_client_receive, radius, 139839beb93cSSam Leffler (void *) RADIUS_AUTH)) { 13995b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); 14005b9c547cSRui Paulo radius_close_auth_sockets(radius); 140139beb93cSSam Leffler return -1; 140239beb93cSSam Leffler } 140339beb93cSSam Leffler 140439beb93cSSam Leffler #ifdef CONFIG_IPV6 140539beb93cSSam Leffler if (radius->auth_serv_sock6 >= 0 && 140639beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock6, 140739beb93cSSam Leffler radius_client_receive, radius, 140839beb93cSSam Leffler (void *) RADIUS_AUTH)) { 14095b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); 14105b9c547cSRui Paulo radius_close_auth_sockets(radius); 141139beb93cSSam Leffler return -1; 141239beb93cSSam Leffler } 141339beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 141439beb93cSSam Leffler 141539beb93cSSam Leffler return 0; 141639beb93cSSam Leffler } 141739beb93cSSam Leffler 141839beb93cSSam Leffler 141939beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius) 142039beb93cSSam Leffler { 142139beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 142239beb93cSSam Leffler int ok = 0; 142339beb93cSSam Leffler 14245b9c547cSRui Paulo radius_close_acct_sockets(radius); 14255b9c547cSRui Paulo 142639beb93cSSam Leffler radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 142739beb93cSSam Leffler if (radius->acct_serv_sock < 0) 14285b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", 14295b9c547cSRui Paulo strerror(errno)); 14303157ba21SRui Paulo else { 14313157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->acct_serv_sock); 143239beb93cSSam Leffler ok++; 14333157ba21SRui Paulo } 143439beb93cSSam Leffler 143539beb93cSSam Leffler #ifdef CONFIG_IPV6 143639beb93cSSam Leffler radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 143739beb93cSSam Leffler if (radius->acct_serv_sock6 < 0) 14385b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", 14395b9c547cSRui Paulo strerror(errno)); 144039beb93cSSam Leffler else 144139beb93cSSam Leffler ok++; 144239beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 144339beb93cSSam Leffler 144439beb93cSSam Leffler if (ok == 0) 144539beb93cSSam Leffler return -1; 144639beb93cSSam Leffler 144739beb93cSSam Leffler radius_change_server(radius, conf->acct_server, NULL, 144839beb93cSSam Leffler radius->acct_serv_sock, radius->acct_serv_sock6, 144939beb93cSSam Leffler 0); 145039beb93cSSam Leffler 145139beb93cSSam Leffler if (radius->acct_serv_sock >= 0 && 145239beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock, 145339beb93cSSam Leffler radius_client_receive, radius, 145439beb93cSSam Leffler (void *) RADIUS_ACCT)) { 14555b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); 14565b9c547cSRui Paulo radius_close_acct_sockets(radius); 145739beb93cSSam Leffler return -1; 145839beb93cSSam Leffler } 145939beb93cSSam Leffler 146039beb93cSSam Leffler #ifdef CONFIG_IPV6 146139beb93cSSam Leffler if (radius->acct_serv_sock6 >= 0 && 146239beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock6, 146339beb93cSSam Leffler radius_client_receive, radius, 146439beb93cSSam Leffler (void *) RADIUS_ACCT)) { 14655b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); 14665b9c547cSRui Paulo radius_close_acct_sockets(radius); 146739beb93cSSam Leffler return -1; 146839beb93cSSam Leffler } 146939beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 147039beb93cSSam Leffler 147139beb93cSSam Leffler return 0; 147239beb93cSSam Leffler } 147339beb93cSSam Leffler 147439beb93cSSam Leffler 1475e28a4053SRui Paulo /** 1476e28a4053SRui Paulo * radius_client_init - Initialize RADIUS client 1477e28a4053SRui Paulo * @ctx: Callback context to be used in hostapd_logger() calls 1478e28a4053SRui Paulo * @conf: RADIUS client configuration (RADIUS servers) 1479e28a4053SRui Paulo * Returns: Pointer to private RADIUS client context or %NULL on failure 1480e28a4053SRui Paulo * 1481e28a4053SRui Paulo * The caller is responsible for keeping the configuration data available for 1482e28a4053SRui Paulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is 1483e28a4053SRui Paulo * called for the returned context pointer. 1484e28a4053SRui Paulo */ 148539beb93cSSam Leffler struct radius_client_data * 148639beb93cSSam Leffler radius_client_init(void *ctx, struct hostapd_radius_servers *conf) 148739beb93cSSam Leffler { 148839beb93cSSam Leffler struct radius_client_data *radius; 148939beb93cSSam Leffler 149039beb93cSSam Leffler radius = os_zalloc(sizeof(struct radius_client_data)); 149139beb93cSSam Leffler if (radius == NULL) 149239beb93cSSam Leffler return NULL; 149339beb93cSSam Leffler 149439beb93cSSam Leffler radius->ctx = ctx; 149539beb93cSSam Leffler radius->conf = conf; 149639beb93cSSam Leffler radius->auth_serv_sock = radius->acct_serv_sock = 149739beb93cSSam Leffler radius->auth_serv_sock6 = radius->acct_serv_sock6 = 149839beb93cSSam Leffler radius->auth_sock = radius->acct_sock = -1; 149939beb93cSSam Leffler 150039beb93cSSam Leffler if (conf->auth_server && radius_client_init_auth(radius)) { 150139beb93cSSam Leffler radius_client_deinit(radius); 150239beb93cSSam Leffler return NULL; 150339beb93cSSam Leffler } 150439beb93cSSam Leffler 150539beb93cSSam Leffler if (conf->acct_server && radius_client_init_acct(radius)) { 150639beb93cSSam Leffler radius_client_deinit(radius); 150739beb93cSSam Leffler return NULL; 150839beb93cSSam Leffler } 150939beb93cSSam Leffler 151039beb93cSSam Leffler if (conf->retry_primary_interval) 151139beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 151239beb93cSSam Leffler radius_retry_primary_timer, radius, 151339beb93cSSam Leffler NULL); 151439beb93cSSam Leffler 151539beb93cSSam Leffler return radius; 151639beb93cSSam Leffler } 151739beb93cSSam Leffler 151839beb93cSSam Leffler 1519e28a4053SRui Paulo /** 1520e28a4053SRui Paulo * radius_client_deinit - Deinitialize RADIUS client 1521e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1522e28a4053SRui Paulo */ 152339beb93cSSam Leffler void radius_client_deinit(struct radius_client_data *radius) 152439beb93cSSam Leffler { 152539beb93cSSam Leffler if (!radius) 152639beb93cSSam Leffler return; 152739beb93cSSam Leffler 15285b9c547cSRui Paulo radius_close_auth_sockets(radius); 15295b9c547cSRui Paulo radius_close_acct_sockets(radius); 153039beb93cSSam Leffler 153139beb93cSSam Leffler eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); 153239beb93cSSam Leffler 153339beb93cSSam Leffler radius_client_flush(radius, 0); 153439beb93cSSam Leffler os_free(radius->auth_handlers); 153539beb93cSSam Leffler os_free(radius->acct_handlers); 153639beb93cSSam Leffler os_free(radius); 153739beb93cSSam Leffler } 153839beb93cSSam Leffler 153939beb93cSSam Leffler 1540e28a4053SRui Paulo /** 1541e28a4053SRui Paulo * radius_client_flush_auth - Flush pending RADIUS messages for an address 1542e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1543e28a4053SRui Paulo * @addr: MAC address of the related device 1544e28a4053SRui Paulo * 1545e28a4053SRui Paulo * This function can be used to remove pending RADIUS authentication messages 1546e28a4053SRui Paulo * that are related to a specific device. The addr parameter is matched with 1547e28a4053SRui Paulo * the one used in radius_client_send() call that was used to transmit the 1548e28a4053SRui Paulo * authentication request. 1549e28a4053SRui Paulo */ 1550e28a4053SRui Paulo void radius_client_flush_auth(struct radius_client_data *radius, 1551e28a4053SRui Paulo const u8 *addr) 155239beb93cSSam Leffler { 155339beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 155439beb93cSSam Leffler 155539beb93cSSam Leffler prev = NULL; 155639beb93cSSam Leffler entry = radius->msgs; 155739beb93cSSam Leffler while (entry) { 155839beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH && 155939beb93cSSam Leffler os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 156039beb93cSSam Leffler hostapd_logger(radius->ctx, addr, 156139beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 156239beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 156339beb93cSSam Leffler "Removing pending RADIUS authentication" 156439beb93cSSam Leffler " message for removed client"); 156539beb93cSSam Leffler 156639beb93cSSam Leffler if (prev) 156739beb93cSSam Leffler prev->next = entry->next; 156839beb93cSSam Leffler else 156939beb93cSSam Leffler radius->msgs = entry->next; 157039beb93cSSam Leffler 157139beb93cSSam Leffler tmp = entry; 157239beb93cSSam Leffler entry = entry->next; 157339beb93cSSam Leffler radius_client_msg_free(tmp); 157439beb93cSSam Leffler radius->num_msgs--; 157539beb93cSSam Leffler continue; 157639beb93cSSam Leffler } 157739beb93cSSam Leffler 157839beb93cSSam Leffler prev = entry; 157939beb93cSSam Leffler entry = entry->next; 158039beb93cSSam Leffler } 158139beb93cSSam Leffler } 158239beb93cSSam Leffler 158339beb93cSSam Leffler 158439beb93cSSam Leffler static int radius_client_dump_auth_server(char *buf, size_t buflen, 158539beb93cSSam Leffler struct hostapd_radius_server *serv, 158639beb93cSSam Leffler struct radius_client_data *cli) 158739beb93cSSam Leffler { 158839beb93cSSam Leffler int pending = 0; 158939beb93cSSam Leffler struct radius_msg_list *msg; 159039beb93cSSam Leffler char abuf[50]; 159139beb93cSSam Leffler 159239beb93cSSam Leffler if (cli) { 159339beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 159439beb93cSSam Leffler if (msg->msg_type == RADIUS_AUTH) 159539beb93cSSam Leffler pending++; 159639beb93cSSam Leffler } 159739beb93cSSam Leffler } 159839beb93cSSam Leffler 159939beb93cSSam Leffler return os_snprintf(buf, buflen, 160039beb93cSSam Leffler "radiusAuthServerIndex=%d\n" 160139beb93cSSam Leffler "radiusAuthServerAddress=%s\n" 160239beb93cSSam Leffler "radiusAuthClientServerPortNumber=%d\n" 160339beb93cSSam Leffler "radiusAuthClientRoundTripTime=%d\n" 160439beb93cSSam Leffler "radiusAuthClientAccessRequests=%u\n" 160539beb93cSSam Leffler "radiusAuthClientAccessRetransmissions=%u\n" 160639beb93cSSam Leffler "radiusAuthClientAccessAccepts=%u\n" 160739beb93cSSam Leffler "radiusAuthClientAccessRejects=%u\n" 160839beb93cSSam Leffler "radiusAuthClientAccessChallenges=%u\n" 160939beb93cSSam Leffler "radiusAuthClientMalformedAccessResponses=%u\n" 161039beb93cSSam Leffler "radiusAuthClientBadAuthenticators=%u\n" 161139beb93cSSam Leffler "radiusAuthClientPendingRequests=%u\n" 161239beb93cSSam Leffler "radiusAuthClientTimeouts=%u\n" 161339beb93cSSam Leffler "radiusAuthClientUnknownTypes=%u\n" 161439beb93cSSam Leffler "radiusAuthClientPacketsDropped=%u\n", 161539beb93cSSam Leffler serv->index, 161639beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 161739beb93cSSam Leffler serv->port, 161839beb93cSSam Leffler serv->round_trip_time, 161939beb93cSSam Leffler serv->requests, 162039beb93cSSam Leffler serv->retransmissions, 162139beb93cSSam Leffler serv->access_accepts, 162239beb93cSSam Leffler serv->access_rejects, 162339beb93cSSam Leffler serv->access_challenges, 162439beb93cSSam Leffler serv->malformed_responses, 162539beb93cSSam Leffler serv->bad_authenticators, 162639beb93cSSam Leffler pending, 162739beb93cSSam Leffler serv->timeouts, 162839beb93cSSam Leffler serv->unknown_types, 162939beb93cSSam Leffler serv->packets_dropped); 163039beb93cSSam Leffler } 163139beb93cSSam Leffler 163239beb93cSSam Leffler 163339beb93cSSam Leffler static int radius_client_dump_acct_server(char *buf, size_t buflen, 163439beb93cSSam Leffler struct hostapd_radius_server *serv, 163539beb93cSSam Leffler struct radius_client_data *cli) 163639beb93cSSam Leffler { 163739beb93cSSam Leffler int pending = 0; 163839beb93cSSam Leffler struct radius_msg_list *msg; 163939beb93cSSam Leffler char abuf[50]; 164039beb93cSSam Leffler 164139beb93cSSam Leffler if (cli) { 164239beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 164339beb93cSSam Leffler if (msg->msg_type == RADIUS_ACCT || 164439beb93cSSam Leffler msg->msg_type == RADIUS_ACCT_INTERIM) 164539beb93cSSam Leffler pending++; 164639beb93cSSam Leffler } 164739beb93cSSam Leffler } 164839beb93cSSam Leffler 164939beb93cSSam Leffler return os_snprintf(buf, buflen, 165039beb93cSSam Leffler "radiusAccServerIndex=%d\n" 165139beb93cSSam Leffler "radiusAccServerAddress=%s\n" 165239beb93cSSam Leffler "radiusAccClientServerPortNumber=%d\n" 165339beb93cSSam Leffler "radiusAccClientRoundTripTime=%d\n" 165439beb93cSSam Leffler "radiusAccClientRequests=%u\n" 165539beb93cSSam Leffler "radiusAccClientRetransmissions=%u\n" 165639beb93cSSam Leffler "radiusAccClientResponses=%u\n" 165739beb93cSSam Leffler "radiusAccClientMalformedResponses=%u\n" 165839beb93cSSam Leffler "radiusAccClientBadAuthenticators=%u\n" 165939beb93cSSam Leffler "radiusAccClientPendingRequests=%u\n" 166039beb93cSSam Leffler "radiusAccClientTimeouts=%u\n" 166139beb93cSSam Leffler "radiusAccClientUnknownTypes=%u\n" 166239beb93cSSam Leffler "radiusAccClientPacketsDropped=%u\n", 166339beb93cSSam Leffler serv->index, 166439beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 166539beb93cSSam Leffler serv->port, 166639beb93cSSam Leffler serv->round_trip_time, 166739beb93cSSam Leffler serv->requests, 166839beb93cSSam Leffler serv->retransmissions, 166939beb93cSSam Leffler serv->responses, 167039beb93cSSam Leffler serv->malformed_responses, 167139beb93cSSam Leffler serv->bad_authenticators, 167239beb93cSSam Leffler pending, 167339beb93cSSam Leffler serv->timeouts, 167439beb93cSSam Leffler serv->unknown_types, 167539beb93cSSam Leffler serv->packets_dropped); 167639beb93cSSam Leffler } 167739beb93cSSam Leffler 167839beb93cSSam Leffler 1679e28a4053SRui Paulo /** 1680e28a4053SRui Paulo * radius_client_get_mib - Get RADIUS client MIB information 1681e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1682e28a4053SRui Paulo * @buf: Buffer for returning MIB data in text format 1683e28a4053SRui Paulo * @buflen: Maximum buf length in octets 1684e28a4053SRui Paulo * Returns: Number of octets written into the buffer 1685e28a4053SRui Paulo */ 168639beb93cSSam Leffler int radius_client_get_mib(struct radius_client_data *radius, char *buf, 168739beb93cSSam Leffler size_t buflen) 168839beb93cSSam Leffler { 1689780fb4a2SCy Schubert struct hostapd_radius_servers *conf; 169039beb93cSSam Leffler int i; 169139beb93cSSam Leffler struct hostapd_radius_server *serv; 169239beb93cSSam Leffler int count = 0; 169339beb93cSSam Leffler 1694780fb4a2SCy Schubert if (!radius) 1695780fb4a2SCy Schubert return 0; 1696780fb4a2SCy Schubert 1697780fb4a2SCy Schubert conf = radius->conf; 1698780fb4a2SCy Schubert 169939beb93cSSam Leffler if (conf->auth_servers) { 170039beb93cSSam Leffler for (i = 0; i < conf->num_auth_servers; i++) { 170139beb93cSSam Leffler serv = &conf->auth_servers[i]; 170239beb93cSSam Leffler count += radius_client_dump_auth_server( 170339beb93cSSam Leffler buf + count, buflen - count, serv, 170439beb93cSSam Leffler serv == conf->auth_server ? 170539beb93cSSam Leffler radius : NULL); 170639beb93cSSam Leffler } 170739beb93cSSam Leffler } 170839beb93cSSam Leffler 170939beb93cSSam Leffler if (conf->acct_servers) { 171039beb93cSSam Leffler for (i = 0; i < conf->num_acct_servers; i++) { 171139beb93cSSam Leffler serv = &conf->acct_servers[i]; 171239beb93cSSam Leffler count += radius_client_dump_acct_server( 171339beb93cSSam Leffler buf + count, buflen - count, serv, 171439beb93cSSam Leffler serv == conf->acct_server ? 171539beb93cSSam Leffler radius : NULL); 171639beb93cSSam Leffler } 171739beb93cSSam Leffler } 171839beb93cSSam Leffler 171939beb93cSSam Leffler return count; 172039beb93cSSam Leffler } 1721f05cddf9SRui Paulo 1722f05cddf9SRui Paulo 1723f05cddf9SRui Paulo void radius_client_reconfig(struct radius_client_data *radius, 1724f05cddf9SRui Paulo struct hostapd_radius_servers *conf) 1725f05cddf9SRui Paulo { 1726f05cddf9SRui Paulo if (radius) 1727f05cddf9SRui Paulo radius->conf = conf; 1728f05cddf9SRui Paulo } 1729