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" 1039beb93cSSam Leffler 1139beb93cSSam Leffler #include "common.h" 1239beb93cSSam Leffler #include "radius.h" 1339beb93cSSam Leffler #include "radius_client.h" 1439beb93cSSam Leffler #include "eloop.h" 1539beb93cSSam Leffler 1639beb93cSSam Leffler /* Defaults for RADIUS retransmit values (exponential backoff) */ 17e28a4053SRui Paulo 18e28a4053SRui Paulo /** 19e28a4053SRui Paulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds 20e28a4053SRui Paulo */ 21e28a4053SRui Paulo #define RADIUS_CLIENT_FIRST_WAIT 3 22e28a4053SRui Paulo 23e28a4053SRui Paulo /** 24e28a4053SRui Paulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds 25e28a4053SRui Paulo */ 26e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_WAIT 120 27e28a4053SRui Paulo 28e28a4053SRui Paulo /** 29*4bc52338SCy Schubert * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries 30e28a4053SRui Paulo * 31*4bc52338SCy Schubert * Maximum number of server failovers before the entry is removed from 32e28a4053SRui Paulo * retransmit list. 33e28a4053SRui Paulo */ 34*4bc52338SCy Schubert #define RADIUS_CLIENT_MAX_FAILOVER 3 35e28a4053SRui Paulo 36e28a4053SRui Paulo /** 37e28a4053SRui Paulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages 38e28a4053SRui Paulo * 39e28a4053SRui Paulo * Maximum number of entries in retransmit list (oldest entries will be 40e28a4053SRui Paulo * removed, if this limit is exceeded). 41e28a4053SRui Paulo */ 42e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_ENTRIES 30 43e28a4053SRui Paulo 44e28a4053SRui Paulo /** 45e28a4053SRui Paulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point 46e28a4053SRui Paulo * 47e28a4053SRui Paulo * The number of failed retry attempts after which the RADIUS server will be 48e28a4053SRui Paulo * changed (if one of more backup servers are configured). 49e28a4053SRui Paulo */ 50e28a4053SRui Paulo #define RADIUS_CLIENT_NUM_FAILOVER 4 5139beb93cSSam Leffler 5239beb93cSSam Leffler 53e28a4053SRui Paulo /** 54e28a4053SRui Paulo * struct radius_rx_handler - RADIUS client RX handler 55e28a4053SRui Paulo * 56e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 57e28a4053SRui Paulo * store registered RX handlers. These handlers are registered by calls to 58e28a4053SRui Paulo * radius_client_register() and unregistered when the RADIUS client is 59e28a4053SRui Paulo * deinitialized with a call to radius_client_deinit(). 60e28a4053SRui Paulo */ 6139beb93cSSam Leffler struct radius_rx_handler { 62e28a4053SRui Paulo /** 63e28a4053SRui Paulo * handler - Received RADIUS message handler 64e28a4053SRui Paulo */ 6539beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 6639beb93cSSam Leffler struct radius_msg *req, 6739beb93cSSam Leffler const u8 *shared_secret, 6839beb93cSSam Leffler size_t shared_secret_len, 6939beb93cSSam Leffler void *data); 70e28a4053SRui Paulo 71e28a4053SRui Paulo /** 72e28a4053SRui Paulo * data - Context data for the handler 73e28a4053SRui Paulo */ 7439beb93cSSam Leffler void *data; 7539beb93cSSam Leffler }; 7639beb93cSSam Leffler 7739beb93cSSam Leffler 78e28a4053SRui Paulo /** 79e28a4053SRui Paulo * struct radius_msg_list - RADIUS client message retransmit list 80e28a4053SRui Paulo * 81e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module to 82e28a4053SRui Paulo * store pending RADIUS requests that may still need to be retransmitted. 83e28a4053SRui Paulo */ 8439beb93cSSam Leffler struct radius_msg_list { 85e28a4053SRui Paulo /** 86e28a4053SRui Paulo * addr - STA/client address 87e28a4053SRui Paulo * 88e28a4053SRui Paulo * This is used to find RADIUS messages for the same STA. 89e28a4053SRui Paulo */ 90e28a4053SRui Paulo u8 addr[ETH_ALEN]; 91e28a4053SRui Paulo 92e28a4053SRui Paulo /** 93e28a4053SRui Paulo * msg - RADIUS message 94e28a4053SRui Paulo */ 9539beb93cSSam Leffler struct radius_msg *msg; 96e28a4053SRui Paulo 97e28a4053SRui Paulo /** 98e28a4053SRui Paulo * msg_type - Message type 99e28a4053SRui Paulo */ 10039beb93cSSam Leffler RadiusType msg_type; 101e28a4053SRui Paulo 102e28a4053SRui Paulo /** 103e28a4053SRui Paulo * first_try - Time of the first transmission attempt 104e28a4053SRui Paulo */ 10539beb93cSSam Leffler os_time_t first_try; 106e28a4053SRui Paulo 107e28a4053SRui Paulo /** 108e28a4053SRui Paulo * next_try - Time for the next transmission attempt 109e28a4053SRui Paulo */ 11039beb93cSSam Leffler os_time_t next_try; 111e28a4053SRui Paulo 112e28a4053SRui Paulo /** 113*4bc52338SCy Schubert * attempts - Number of transmission attempts for one server 114e28a4053SRui Paulo */ 11539beb93cSSam Leffler int attempts; 116e28a4053SRui Paulo 117e28a4053SRui Paulo /** 118*4bc52338SCy Schubert * accu_attempts - Number of accumulated attempts 119*4bc52338SCy Schubert */ 120*4bc52338SCy Schubert int accu_attempts; 121*4bc52338SCy Schubert 122*4bc52338SCy Schubert /** 123e28a4053SRui Paulo * next_wait - Next retransmission wait time in seconds 124e28a4053SRui Paulo */ 12539beb93cSSam Leffler int next_wait; 126e28a4053SRui Paulo 127e28a4053SRui Paulo /** 128e28a4053SRui Paulo * last_attempt - Time of the last transmission attempt 129e28a4053SRui Paulo */ 1305b9c547cSRui Paulo struct os_reltime last_attempt; 13139beb93cSSam Leffler 132e28a4053SRui Paulo /** 133e28a4053SRui Paulo * shared_secret - Shared secret with the target RADIUS server 134e28a4053SRui Paulo */ 135e28a4053SRui Paulo const u8 *shared_secret; 136e28a4053SRui Paulo 137e28a4053SRui Paulo /** 138e28a4053SRui Paulo * shared_secret_len - shared_secret length in octets 139e28a4053SRui Paulo */ 14039beb93cSSam Leffler size_t shared_secret_len; 14139beb93cSSam Leffler 14239beb93cSSam Leffler /* TODO: server config with failover to backup server(s) */ 14339beb93cSSam Leffler 144e28a4053SRui Paulo /** 145e28a4053SRui Paulo * next - Next message in the list 146e28a4053SRui Paulo */ 14739beb93cSSam Leffler struct radius_msg_list *next; 14839beb93cSSam Leffler }; 14939beb93cSSam Leffler 15039beb93cSSam Leffler 151e28a4053SRui Paulo /** 152e28a4053SRui Paulo * struct radius_client_data - Internal RADIUS client data 153e28a4053SRui Paulo * 154e28a4053SRui Paulo * This data structure is used internally inside the RADIUS client module. 155e28a4053SRui Paulo * External users allocate this by calling radius_client_init() and free it by 156e28a4053SRui Paulo * calling radius_client_deinit(). The pointer to this opaque data is used in 157e28a4053SRui Paulo * calls to other functions as an identifier for the RADIUS client instance. 158e28a4053SRui Paulo */ 15939beb93cSSam Leffler struct radius_client_data { 160e28a4053SRui Paulo /** 161e28a4053SRui Paulo * ctx - Context pointer for hostapd_logger() callbacks 162e28a4053SRui Paulo */ 16339beb93cSSam Leffler void *ctx; 164e28a4053SRui Paulo 165e28a4053SRui Paulo /** 166e28a4053SRui Paulo * conf - RADIUS client configuration (list of RADIUS servers to use) 167e28a4053SRui Paulo */ 16839beb93cSSam Leffler struct hostapd_radius_servers *conf; 16939beb93cSSam Leffler 170e28a4053SRui Paulo /** 171e28a4053SRui Paulo * auth_serv_sock - IPv4 socket for RADIUS authentication messages 172e28a4053SRui Paulo */ 173e28a4053SRui Paulo int auth_serv_sock; 17439beb93cSSam Leffler 175e28a4053SRui Paulo /** 176e28a4053SRui Paulo * acct_serv_sock - IPv4 socket for RADIUS accounting messages 177e28a4053SRui Paulo */ 178e28a4053SRui Paulo int acct_serv_sock; 179e28a4053SRui Paulo 180e28a4053SRui Paulo /** 181e28a4053SRui Paulo * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages 182e28a4053SRui Paulo */ 183e28a4053SRui Paulo int auth_serv_sock6; 184e28a4053SRui Paulo 185e28a4053SRui Paulo /** 186e28a4053SRui Paulo * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages 187e28a4053SRui Paulo */ 188e28a4053SRui Paulo int acct_serv_sock6; 189e28a4053SRui Paulo 190e28a4053SRui Paulo /** 191e28a4053SRui Paulo * auth_sock - Currently used socket for RADIUS authentication server 192e28a4053SRui Paulo */ 193e28a4053SRui Paulo int auth_sock; 194e28a4053SRui Paulo 195e28a4053SRui Paulo /** 196e28a4053SRui Paulo * acct_sock - Currently used socket for RADIUS accounting server 197e28a4053SRui Paulo */ 198e28a4053SRui Paulo int acct_sock; 199e28a4053SRui Paulo 200e28a4053SRui Paulo /** 201e28a4053SRui Paulo * auth_handlers - Authentication message handlers 202e28a4053SRui Paulo */ 20339beb93cSSam Leffler struct radius_rx_handler *auth_handlers; 204e28a4053SRui Paulo 205e28a4053SRui Paulo /** 206e28a4053SRui Paulo * num_auth_handlers - Number of handlers in auth_handlers 207e28a4053SRui Paulo */ 20839beb93cSSam Leffler size_t num_auth_handlers; 209e28a4053SRui Paulo 210e28a4053SRui Paulo /** 211e28a4053SRui Paulo * acct_handlers - Accounting message handlers 212e28a4053SRui Paulo */ 21339beb93cSSam Leffler struct radius_rx_handler *acct_handlers; 214e28a4053SRui Paulo 215e28a4053SRui Paulo /** 216e28a4053SRui Paulo * num_acct_handlers - Number of handlers in acct_handlers 217e28a4053SRui Paulo */ 21839beb93cSSam Leffler size_t num_acct_handlers; 21939beb93cSSam Leffler 220e28a4053SRui Paulo /** 221e28a4053SRui Paulo * msgs - Pending outgoing RADIUS messages 222e28a4053SRui Paulo */ 22339beb93cSSam Leffler struct radius_msg_list *msgs; 224e28a4053SRui Paulo 225e28a4053SRui Paulo /** 226e28a4053SRui Paulo * num_msgs - Number of pending messages in the msgs list 227e28a4053SRui Paulo */ 22839beb93cSSam Leffler size_t num_msgs; 22939beb93cSSam Leffler 230e28a4053SRui Paulo /** 231e28a4053SRui Paulo * next_radius_identifier - Next RADIUS message identifier to use 232e28a4053SRui Paulo */ 23339beb93cSSam Leffler u8 next_radius_identifier; 234780fb4a2SCy Schubert 235780fb4a2SCy Schubert /** 236780fb4a2SCy Schubert * interim_error_cb - Interim accounting error callback 237780fb4a2SCy Schubert */ 238780fb4a2SCy Schubert void (*interim_error_cb)(const u8 *addr, void *ctx); 239780fb4a2SCy Schubert 240780fb4a2SCy Schubert /** 241780fb4a2SCy Schubert * interim_error_cb_ctx - interim_error_cb() context data 242780fb4a2SCy Schubert */ 243780fb4a2SCy Schubert void *interim_error_cb_ctx; 24439beb93cSSam Leffler }; 24539beb93cSSam Leffler 24639beb93cSSam Leffler 24739beb93cSSam Leffler static int 24839beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 24939beb93cSSam Leffler struct hostapd_radius_server *nserv, 25039beb93cSSam Leffler struct hostapd_radius_server *oserv, 25139beb93cSSam Leffler int sock, int sock6, int auth); 25239beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius); 25339beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius); 2545b9c547cSRui Paulo static void radius_client_auth_failover(struct radius_client_data *radius); 2555b9c547cSRui Paulo static void radius_client_acct_failover(struct radius_client_data *radius); 25639beb93cSSam Leffler 25739beb93cSSam Leffler 25839beb93cSSam Leffler static void radius_client_msg_free(struct radius_msg_list *req) 25939beb93cSSam Leffler { 26039beb93cSSam Leffler radius_msg_free(req->msg); 26139beb93cSSam Leffler os_free(req); 26239beb93cSSam Leffler } 26339beb93cSSam Leffler 26439beb93cSSam Leffler 265e28a4053SRui Paulo /** 266e28a4053SRui Paulo * radius_client_register - Register a RADIUS client RX handler 267e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 268e28a4053SRui Paulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) 269e28a4053SRui Paulo * @handler: Handler for received RADIUS messages 270e28a4053SRui Paulo * @data: Context pointer for handler callbacks 271e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 272e28a4053SRui Paulo * 273e28a4053SRui Paulo * This function is used to register a handler for processing received RADIUS 274e28a4053SRui Paulo * authentication and accounting messages. The handler() callback function will 275e28a4053SRui Paulo * be called whenever a RADIUS message is received from the active server. 276e28a4053SRui Paulo * 277e28a4053SRui Paulo * There can be multiple registered RADIUS message handlers. The handlers will 278e28a4053SRui Paulo * be called in order until one of them indicates that it has processed or 279e28a4053SRui Paulo * queued the message. 280e28a4053SRui Paulo */ 28139beb93cSSam Leffler int radius_client_register(struct radius_client_data *radius, 28239beb93cSSam Leffler RadiusType msg_type, 28339beb93cSSam Leffler RadiusRxResult (*handler)(struct radius_msg *msg, 28439beb93cSSam Leffler struct radius_msg *req, 28539beb93cSSam Leffler const u8 *shared_secret, 28639beb93cSSam Leffler size_t shared_secret_len, 28739beb93cSSam Leffler void *data), 28839beb93cSSam Leffler void *data) 28939beb93cSSam Leffler { 29039beb93cSSam Leffler struct radius_rx_handler **handlers, *newh; 29139beb93cSSam Leffler size_t *num; 29239beb93cSSam Leffler 29339beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 29439beb93cSSam Leffler handlers = &radius->acct_handlers; 29539beb93cSSam Leffler num = &radius->num_acct_handlers; 29639beb93cSSam Leffler } else { 29739beb93cSSam Leffler handlers = &radius->auth_handlers; 29839beb93cSSam Leffler num = &radius->num_auth_handlers; 29939beb93cSSam Leffler } 30039beb93cSSam Leffler 301f05cddf9SRui Paulo newh = os_realloc_array(*handlers, *num + 1, 302f05cddf9SRui Paulo sizeof(struct radius_rx_handler)); 30339beb93cSSam Leffler if (newh == NULL) 30439beb93cSSam Leffler return -1; 30539beb93cSSam Leffler 30639beb93cSSam Leffler newh[*num].handler = handler; 30739beb93cSSam Leffler newh[*num].data = data; 30839beb93cSSam Leffler (*num)++; 30939beb93cSSam Leffler *handlers = newh; 31039beb93cSSam Leffler 31139beb93cSSam Leffler return 0; 31239beb93cSSam Leffler } 31339beb93cSSam Leffler 31439beb93cSSam Leffler 315780fb4a2SCy Schubert /** 316780fb4a2SCy Schubert * radius_client_set_interim_erro_cb - Register an interim acct error callback 317780fb4a2SCy Schubert * @radius: RADIUS client context from radius_client_init() 318780fb4a2SCy Schubert * @addr: Station address from the failed message 319780fb4a2SCy Schubert * @cb: Handler for interim accounting errors 320780fb4a2SCy Schubert * @ctx: Context pointer for handler callbacks 321780fb4a2SCy Schubert * 322780fb4a2SCy Schubert * This function is used to register a handler for processing failed 323780fb4a2SCy Schubert * transmission attempts of interim accounting update messages. 324780fb4a2SCy Schubert */ 325780fb4a2SCy Schubert void radius_client_set_interim_error_cb(struct radius_client_data *radius, 326780fb4a2SCy Schubert void (*cb)(const u8 *addr, void *ctx), 327780fb4a2SCy Schubert void *ctx) 328780fb4a2SCy Schubert { 329780fb4a2SCy Schubert radius->interim_error_cb = cb; 330780fb4a2SCy Schubert radius->interim_error_cb_ctx = ctx; 331780fb4a2SCy Schubert } 332780fb4a2SCy Schubert 333780fb4a2SCy Schubert 3345b9c547cSRui Paulo /* 3355b9c547cSRui Paulo * Returns >0 if message queue was flushed (i.e., the message that triggered 3365b9c547cSRui Paulo * the error is not available anymore) 3375b9c547cSRui Paulo */ 3385b9c547cSRui Paulo static int radius_client_handle_send_error(struct radius_client_data *radius, 33939beb93cSSam Leffler int s, RadiusType msg_type) 34039beb93cSSam Leffler { 34139beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 34239beb93cSSam Leffler int _errno = errno; 3435b9c547cSRui Paulo wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); 34439beb93cSSam Leffler if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 345780fb4a2SCy Schubert _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { 34639beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 34739beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 34839beb93cSSam Leffler "Send failed - maybe interface status changed -" 34939beb93cSSam Leffler " try to connect again"); 3505b9c547cSRui Paulo if (msg_type == RADIUS_ACCT || 3515b9c547cSRui Paulo msg_type == RADIUS_ACCT_INTERIM) { 35239beb93cSSam Leffler radius_client_init_acct(radius); 3535b9c547cSRui Paulo return 0; 3545b9c547cSRui Paulo } else { 35539beb93cSSam Leffler radius_client_init_auth(radius); 3565b9c547cSRui Paulo return 1; 3575b9c547cSRui Paulo } 35839beb93cSSam Leffler } 35939beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 3605b9c547cSRui Paulo 3615b9c547cSRui Paulo return 0; 36239beb93cSSam Leffler } 36339beb93cSSam Leffler 36439beb93cSSam Leffler 36539beb93cSSam Leffler static int radius_client_retransmit(struct radius_client_data *radius, 36639beb93cSSam Leffler struct radius_msg_list *entry, 36739beb93cSSam Leffler os_time_t now) 36839beb93cSSam Leffler { 36939beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 37039beb93cSSam Leffler int s; 371e28a4053SRui Paulo struct wpabuf *buf; 3725b9c547cSRui Paulo size_t prev_num_msgs; 373780fb4a2SCy Schubert u8 *acct_delay_time; 374780fb4a2SCy Schubert size_t acct_delay_time_len; 375*4bc52338SCy Schubert int num_servers; 37639beb93cSSam Leffler 37739beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 37839beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) { 379*4bc52338SCy Schubert num_servers = conf->num_acct_servers; 3805b9c547cSRui Paulo if (radius->acct_sock < 0) 3815b9c547cSRui Paulo radius_client_init_acct(radius); 3825b9c547cSRui Paulo if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { 3835b9c547cSRui Paulo prev_num_msgs = radius->num_msgs; 3845b9c547cSRui Paulo radius_client_acct_failover(radius); 3855b9c547cSRui Paulo if (prev_num_msgs != radius->num_msgs) 3865b9c547cSRui Paulo return 0; 3875b9c547cSRui Paulo } 38839beb93cSSam Leffler s = radius->acct_sock; 38939beb93cSSam Leffler if (entry->attempts == 0) 39039beb93cSSam Leffler conf->acct_server->requests++; 39139beb93cSSam Leffler else { 39239beb93cSSam Leffler conf->acct_server->timeouts++; 39339beb93cSSam Leffler conf->acct_server->retransmissions++; 39439beb93cSSam Leffler } 39539beb93cSSam Leffler } else { 396*4bc52338SCy Schubert num_servers = conf->num_auth_servers; 3975b9c547cSRui Paulo if (radius->auth_sock < 0) 3985b9c547cSRui Paulo radius_client_init_auth(radius); 3995b9c547cSRui Paulo if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { 4005b9c547cSRui Paulo prev_num_msgs = radius->num_msgs; 4015b9c547cSRui Paulo radius_client_auth_failover(radius); 4025b9c547cSRui Paulo if (prev_num_msgs != radius->num_msgs) 4035b9c547cSRui Paulo return 0; 4045b9c547cSRui Paulo } 40539beb93cSSam Leffler s = radius->auth_sock; 40639beb93cSSam Leffler if (entry->attempts == 0) 40739beb93cSSam Leffler conf->auth_server->requests++; 40839beb93cSSam Leffler else { 40939beb93cSSam Leffler conf->auth_server->timeouts++; 41039beb93cSSam Leffler conf->auth_server->retransmissions++; 41139beb93cSSam Leffler } 41239beb93cSSam Leffler } 413780fb4a2SCy Schubert 414780fb4a2SCy Schubert if (entry->msg_type == RADIUS_ACCT_INTERIM) { 415780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 416780fb4a2SCy Schubert "RADIUS: Failed to transmit interim accounting update to " 417780fb4a2SCy Schubert MACSTR " - drop message and request a new update", 418780fb4a2SCy Schubert MAC2STR(entry->addr)); 419780fb4a2SCy Schubert if (radius->interim_error_cb) 420780fb4a2SCy Schubert radius->interim_error_cb(entry->addr, 421780fb4a2SCy Schubert radius->interim_error_cb_ctx); 422780fb4a2SCy Schubert return 1; 423780fb4a2SCy Schubert } 424780fb4a2SCy Schubert 4255b9c547cSRui Paulo if (s < 0) { 4265b9c547cSRui Paulo wpa_printf(MSG_INFO, 4275b9c547cSRui Paulo "RADIUS: No valid socket for retransmission"); 4285b9c547cSRui Paulo return 1; 4295b9c547cSRui Paulo } 43039beb93cSSam Leffler 431780fb4a2SCy Schubert if (entry->msg_type == RADIUS_ACCT && 432780fb4a2SCy Schubert radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, 433780fb4a2SCy Schubert &acct_delay_time, &acct_delay_time_len, 434780fb4a2SCy Schubert NULL) == 0 && 435780fb4a2SCy Schubert acct_delay_time_len == 4) { 436780fb4a2SCy Schubert struct radius_hdr *hdr; 437780fb4a2SCy Schubert u32 delay_time; 438780fb4a2SCy Schubert 439780fb4a2SCy Schubert /* 440780fb4a2SCy Schubert * Need to assign a new identifier since attribute contents 441780fb4a2SCy Schubert * changes. 442780fb4a2SCy Schubert */ 443780fb4a2SCy Schubert hdr = radius_msg_get_hdr(entry->msg); 444780fb4a2SCy Schubert hdr->identifier = radius_client_get_id(radius); 445780fb4a2SCy Schubert 446780fb4a2SCy Schubert /* Update Acct-Delay-Time to show wait time in queue */ 447780fb4a2SCy Schubert delay_time = now - entry->first_try; 448780fb4a2SCy Schubert WPA_PUT_BE32(acct_delay_time, delay_time); 449780fb4a2SCy Schubert 450780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, 451780fb4a2SCy Schubert "RADIUS: Updated Acct-Delay-Time to %u for retransmission", 452780fb4a2SCy Schubert delay_time); 453780fb4a2SCy Schubert radius_msg_finish_acct(entry->msg, entry->shared_secret, 454780fb4a2SCy Schubert entry->shared_secret_len); 455780fb4a2SCy Schubert if (radius->conf->msg_dumps) 456780fb4a2SCy Schubert radius_msg_dump(entry->msg); 457780fb4a2SCy Schubert } 458780fb4a2SCy Schubert 45939beb93cSSam Leffler /* retransmit; remove entry if too many attempts */ 460*4bc52338SCy Schubert if (entry->accu_attempts > RADIUS_CLIENT_MAX_FAILOVER * 461*4bc52338SCy Schubert RADIUS_CLIENT_NUM_FAILOVER * num_servers) { 462*4bc52338SCy Schubert wpa_printf(MSG_INFO, 463*4bc52338SCy Schubert "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); 464*4bc52338SCy Schubert return 1; 465*4bc52338SCy Schubert } 466*4bc52338SCy Schubert 46739beb93cSSam Leffler entry->attempts++; 468*4bc52338SCy Schubert entry->accu_attempts++; 46939beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, 47039beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", 471e28a4053SRui Paulo radius_msg_get_hdr(entry->msg)->identifier); 47239beb93cSSam Leffler 4735b9c547cSRui Paulo os_get_reltime(&entry->last_attempt); 474e28a4053SRui Paulo buf = radius_msg_get_buf(entry->msg); 4755b9c547cSRui Paulo if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { 4765b9c547cSRui Paulo if (radius_client_handle_send_error(radius, s, entry->msg_type) 4775b9c547cSRui Paulo > 0) 4785b9c547cSRui Paulo return 0; 4795b9c547cSRui Paulo } 48039beb93cSSam Leffler 48139beb93cSSam Leffler entry->next_try = now + entry->next_wait; 48239beb93cSSam Leffler entry->next_wait *= 2; 48339beb93cSSam Leffler if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 48439beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 48539beb93cSSam Leffler 48639beb93cSSam Leffler return 0; 48739beb93cSSam Leffler } 48839beb93cSSam Leffler 48939beb93cSSam Leffler 49039beb93cSSam Leffler static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 49139beb93cSSam Leffler { 49239beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 4935b9c547cSRui Paulo struct os_reltime now; 49439beb93cSSam Leffler os_time_t first; 49539beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 49639beb93cSSam Leffler int auth_failover = 0, acct_failover = 0; 4975b9c547cSRui Paulo size_t prev_num_msgs; 4985b9c547cSRui Paulo int s; 49939beb93cSSam Leffler 50039beb93cSSam Leffler entry = radius->msgs; 50139beb93cSSam Leffler if (!entry) 50239beb93cSSam Leffler return; 50339beb93cSSam Leffler 5045b9c547cSRui Paulo os_get_reltime(&now); 505*4bc52338SCy Schubert 506*4bc52338SCy Schubert while (entry) { 507*4bc52338SCy Schubert if (now.sec >= entry->next_try) { 508*4bc52338SCy Schubert s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock : 509*4bc52338SCy Schubert radius->acct_sock; 510*4bc52338SCy Schubert if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER || 511*4bc52338SCy Schubert (s < 0 && entry->attempts > 0)) { 512*4bc52338SCy Schubert if (entry->msg_type == RADIUS_ACCT || 513*4bc52338SCy Schubert entry->msg_type == RADIUS_ACCT_INTERIM) 514*4bc52338SCy Schubert acct_failover++; 515*4bc52338SCy Schubert else 516*4bc52338SCy Schubert auth_failover++; 517*4bc52338SCy Schubert } 518*4bc52338SCy Schubert } 519*4bc52338SCy Schubert entry = entry->next; 520*4bc52338SCy Schubert } 521*4bc52338SCy Schubert 522*4bc52338SCy Schubert if (auth_failover) 523*4bc52338SCy Schubert radius_client_auth_failover(radius); 524*4bc52338SCy Schubert 525*4bc52338SCy Schubert if (acct_failover) 526*4bc52338SCy Schubert radius_client_acct_failover(radius); 527*4bc52338SCy Schubert 528*4bc52338SCy Schubert entry = radius->msgs; 52939beb93cSSam Leffler first = 0; 53039beb93cSSam Leffler 53139beb93cSSam Leffler prev = NULL; 53239beb93cSSam Leffler while (entry) { 5335b9c547cSRui Paulo prev_num_msgs = radius->num_msgs; 53439beb93cSSam Leffler if (now.sec >= entry->next_try && 53539beb93cSSam Leffler radius_client_retransmit(radius, entry, now.sec)) { 53639beb93cSSam Leffler if (prev) 53739beb93cSSam Leffler prev->next = entry->next; 53839beb93cSSam Leffler else 53939beb93cSSam Leffler radius->msgs = entry->next; 54039beb93cSSam Leffler 54139beb93cSSam Leffler tmp = entry; 54239beb93cSSam Leffler entry = entry->next; 54339beb93cSSam Leffler radius_client_msg_free(tmp); 54439beb93cSSam Leffler radius->num_msgs--; 54539beb93cSSam Leffler continue; 54639beb93cSSam Leffler } 54739beb93cSSam Leffler 5485b9c547cSRui Paulo if (prev_num_msgs != radius->num_msgs) { 5495b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 5505b9c547cSRui Paulo "RADIUS: Message removed from queue - restart from beginning"); 5515b9c547cSRui Paulo entry = radius->msgs; 5525b9c547cSRui Paulo prev = NULL; 5535b9c547cSRui Paulo continue; 5545b9c547cSRui Paulo } 5555b9c547cSRui Paulo 55639beb93cSSam Leffler if (first == 0 || entry->next_try < first) 55739beb93cSSam Leffler first = entry->next_try; 55839beb93cSSam Leffler 55939beb93cSSam Leffler prev = entry; 56039beb93cSSam Leffler entry = entry->next; 56139beb93cSSam Leffler } 56239beb93cSSam Leffler 56339beb93cSSam Leffler if (radius->msgs) { 56439beb93cSSam Leffler if (first < now.sec) 56539beb93cSSam Leffler first = now.sec; 566*4bc52338SCy Schubert eloop_cancel_timeout(radius_client_timer, radius, NULL); 56739beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, 56839beb93cSSam Leffler radius_client_timer, radius, NULL); 56939beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 57039beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " 57139beb93cSSam Leffler "retransmit in %ld seconds", 57239beb93cSSam Leffler (long int) (first - now.sec)); 57339beb93cSSam Leffler } 5745b9c547cSRui Paulo } 5755b9c547cSRui Paulo 5765b9c547cSRui Paulo 5775b9c547cSRui Paulo static void radius_client_auth_failover(struct radius_client_data *radius) 5785b9c547cSRui Paulo { 5795b9c547cSRui Paulo struct hostapd_radius_servers *conf = radius->conf; 58039beb93cSSam Leffler struct hostapd_radius_server *next, *old; 5815b9c547cSRui Paulo struct radius_msg_list *entry; 5825b9c547cSRui Paulo char abuf[50]; 5835b9c547cSRui Paulo 58439beb93cSSam Leffler old = conf->auth_server; 58539beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 58639beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 5875b9c547cSRui Paulo "No response from Authentication server %s:%d - failover", 58839beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 58939beb93cSSam Leffler old->port); 59039beb93cSSam Leffler 59139beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 59239beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH) 59339beb93cSSam Leffler old->timeouts++; 59439beb93cSSam Leffler } 59539beb93cSSam Leffler 59639beb93cSSam Leffler next = old + 1; 59739beb93cSSam Leffler if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) 59839beb93cSSam Leffler next = conf->auth_servers; 59939beb93cSSam Leffler conf->auth_server = next; 60039beb93cSSam Leffler radius_change_server(radius, next, old, 60139beb93cSSam Leffler radius->auth_serv_sock, 60239beb93cSSam Leffler radius->auth_serv_sock6, 1); 60339beb93cSSam Leffler } 60439beb93cSSam Leffler 6055b9c547cSRui Paulo 6065b9c547cSRui Paulo static void radius_client_acct_failover(struct radius_client_data *radius) 6075b9c547cSRui Paulo { 6085b9c547cSRui Paulo struct hostapd_radius_servers *conf = radius->conf; 60939beb93cSSam Leffler struct hostapd_radius_server *next, *old; 6105b9c547cSRui Paulo struct radius_msg_list *entry; 6115b9c547cSRui Paulo char abuf[50]; 6125b9c547cSRui Paulo 61339beb93cSSam Leffler old = conf->acct_server; 61439beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 61539beb93cSSam Leffler HOSTAPD_LEVEL_NOTICE, 6165b9c547cSRui Paulo "No response from Accounting server %s:%d - failover", 61739beb93cSSam Leffler hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), 61839beb93cSSam Leffler old->port); 61939beb93cSSam Leffler 62039beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 62139beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT || 62239beb93cSSam Leffler entry->msg_type == RADIUS_ACCT_INTERIM) 62339beb93cSSam Leffler old->timeouts++; 62439beb93cSSam Leffler } 62539beb93cSSam Leffler 62639beb93cSSam Leffler next = old + 1; 62739beb93cSSam Leffler if (next > &conf->acct_servers[conf->num_acct_servers - 1]) 62839beb93cSSam Leffler next = conf->acct_servers; 62939beb93cSSam Leffler conf->acct_server = next; 63039beb93cSSam Leffler radius_change_server(radius, next, old, 63139beb93cSSam Leffler radius->acct_serv_sock, 63239beb93cSSam Leffler radius->acct_serv_sock6, 0); 63339beb93cSSam Leffler } 63439beb93cSSam Leffler 63539beb93cSSam Leffler 63639beb93cSSam Leffler static void radius_client_update_timeout(struct radius_client_data *radius) 63739beb93cSSam Leffler { 6385b9c547cSRui Paulo struct os_reltime now; 63939beb93cSSam Leffler os_time_t first; 64039beb93cSSam Leffler struct radius_msg_list *entry; 64139beb93cSSam Leffler 64239beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 64339beb93cSSam Leffler 64439beb93cSSam Leffler if (radius->msgs == NULL) { 64539beb93cSSam Leffler return; 64639beb93cSSam Leffler } 64739beb93cSSam Leffler 64839beb93cSSam Leffler first = 0; 64939beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 65039beb93cSSam Leffler if (first == 0 || entry->next_try < first) 65139beb93cSSam Leffler first = entry->next_try; 65239beb93cSSam Leffler } 65339beb93cSSam Leffler 6545b9c547cSRui Paulo os_get_reltime(&now); 65539beb93cSSam Leffler if (first < now.sec) 65639beb93cSSam Leffler first = now.sec; 65739beb93cSSam Leffler eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, 65839beb93cSSam Leffler NULL); 65939beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 66039beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" 661f05cddf9SRui Paulo " %ld seconds", (long int) (first - now.sec)); 66239beb93cSSam Leffler } 66339beb93cSSam Leffler 66439beb93cSSam Leffler 66539beb93cSSam Leffler static void radius_client_list_add(struct radius_client_data *radius, 66639beb93cSSam Leffler struct radius_msg *msg, 667e28a4053SRui Paulo RadiusType msg_type, 668e28a4053SRui Paulo const u8 *shared_secret, 66939beb93cSSam Leffler size_t shared_secret_len, const u8 *addr) 67039beb93cSSam Leffler { 67139beb93cSSam Leffler struct radius_msg_list *entry, *prev; 67239beb93cSSam Leffler 67339beb93cSSam Leffler if (eloop_terminated()) { 67439beb93cSSam Leffler /* No point in adding entries to retransmit queue since event 67539beb93cSSam Leffler * loop has already been terminated. */ 67639beb93cSSam Leffler radius_msg_free(msg); 67739beb93cSSam Leffler return; 67839beb93cSSam Leffler } 67939beb93cSSam Leffler 68039beb93cSSam Leffler entry = os_zalloc(sizeof(*entry)); 68139beb93cSSam Leffler if (entry == NULL) { 6825b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); 68339beb93cSSam Leffler radius_msg_free(msg); 68439beb93cSSam Leffler return; 68539beb93cSSam Leffler } 68639beb93cSSam Leffler 68739beb93cSSam Leffler if (addr) 68839beb93cSSam Leffler os_memcpy(entry->addr, addr, ETH_ALEN); 68939beb93cSSam Leffler entry->msg = msg; 69039beb93cSSam Leffler entry->msg_type = msg_type; 69139beb93cSSam Leffler entry->shared_secret = shared_secret; 69239beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 6935b9c547cSRui Paulo os_get_reltime(&entry->last_attempt); 69439beb93cSSam Leffler entry->first_try = entry->last_attempt.sec; 69539beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 69639beb93cSSam Leffler entry->attempts = 1; 697*4bc52338SCy Schubert entry->accu_attempts = 1; 69839beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 699*4bc52338SCy Schubert if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) 700*4bc52338SCy Schubert entry->next_wait = RADIUS_CLIENT_MAX_WAIT; 70139beb93cSSam Leffler entry->next = radius->msgs; 70239beb93cSSam Leffler radius->msgs = entry; 70339beb93cSSam Leffler radius_client_update_timeout(radius); 70439beb93cSSam Leffler 70539beb93cSSam Leffler if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { 7065b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); 70739beb93cSSam Leffler prev = NULL; 70839beb93cSSam Leffler while (entry->next) { 70939beb93cSSam Leffler prev = entry; 71039beb93cSSam Leffler entry = entry->next; 71139beb93cSSam Leffler } 71239beb93cSSam Leffler if (prev) { 71339beb93cSSam Leffler prev->next = NULL; 71439beb93cSSam Leffler radius_client_msg_free(entry); 71539beb93cSSam Leffler } 71639beb93cSSam Leffler } else 71739beb93cSSam Leffler radius->num_msgs++; 71839beb93cSSam Leffler } 71939beb93cSSam Leffler 72039beb93cSSam Leffler 721e28a4053SRui Paulo /** 722e28a4053SRui Paulo * radius_client_send - Send a RADIUS request 723e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 724e28a4053SRui Paulo * @msg: RADIUS message to be sent 725e28a4053SRui Paulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) 726e28a4053SRui Paulo * @addr: MAC address of the device related to this message or %NULL 727e28a4053SRui Paulo * Returns: 0 on success, -1 on failure 728e28a4053SRui Paulo * 729e28a4053SRui Paulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or 730e28a4053SRui Paulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference 731e28a4053SRui Paulo * between accounting and interim accounting messages is that the interim 732780fb4a2SCy Schubert * message will not be retransmitted. Instead, a callback is used to indicate 733780fb4a2SCy Schubert * that the transmission failed for the specific station @addr so that a new 734780fb4a2SCy Schubert * interim accounting update message can be generated with up-to-date session 735780fb4a2SCy Schubert * data instead of trying to resend old information. 736e28a4053SRui Paulo * 737e28a4053SRui Paulo * The message is added on the retransmission queue and will be retransmitted 738e28a4053SRui Paulo * automatically until a response is received or maximum number of retries 739*4bc52338SCy Schubert * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No 740*4bc52338SCy Schubert * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message 741*4bc52338SCy Schubert * is removed from the queue automatically on transmission failure. 742e28a4053SRui Paulo * 743e28a4053SRui Paulo * The related device MAC address can be used to identify pending messages that 744780fb4a2SCy Schubert * can be removed with radius_client_flush_auth(). 745e28a4053SRui Paulo */ 74639beb93cSSam Leffler int radius_client_send(struct radius_client_data *radius, 74739beb93cSSam Leffler struct radius_msg *msg, RadiusType msg_type, 74839beb93cSSam Leffler const u8 *addr) 74939beb93cSSam Leffler { 75039beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 751e28a4053SRui Paulo const u8 *shared_secret; 75239beb93cSSam Leffler size_t shared_secret_len; 75339beb93cSSam Leffler char *name; 75439beb93cSSam Leffler int s, res; 755e28a4053SRui Paulo struct wpabuf *buf; 75639beb93cSSam Leffler 75739beb93cSSam Leffler if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { 7585b9c547cSRui Paulo if (conf->acct_server && radius->acct_sock < 0) 7595b9c547cSRui Paulo radius_client_init_acct(radius); 7605b9c547cSRui Paulo 7615b9c547cSRui Paulo if (conf->acct_server == NULL || radius->acct_sock < 0 || 7625b9c547cSRui Paulo conf->acct_server->shared_secret == NULL) { 76339beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 76439beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 76539beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 76639beb93cSSam Leffler "No accounting server configured"); 76739beb93cSSam Leffler return -1; 76839beb93cSSam Leffler } 76939beb93cSSam Leffler shared_secret = conf->acct_server->shared_secret; 77039beb93cSSam Leffler shared_secret_len = conf->acct_server->shared_secret_len; 77139beb93cSSam Leffler radius_msg_finish_acct(msg, shared_secret, shared_secret_len); 77239beb93cSSam Leffler name = "accounting"; 77339beb93cSSam Leffler s = radius->acct_sock; 77439beb93cSSam Leffler conf->acct_server->requests++; 77539beb93cSSam Leffler } else { 7765b9c547cSRui Paulo if (conf->auth_server && radius->auth_sock < 0) 7775b9c547cSRui Paulo radius_client_init_auth(radius); 7785b9c547cSRui Paulo 7795b9c547cSRui Paulo if (conf->auth_server == NULL || radius->auth_sock < 0 || 7805b9c547cSRui Paulo conf->auth_server->shared_secret == NULL) { 78139beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, 78239beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 78339beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 78439beb93cSSam Leffler "No authentication server configured"); 78539beb93cSSam Leffler return -1; 78639beb93cSSam Leffler } 78739beb93cSSam Leffler shared_secret = conf->auth_server->shared_secret; 78839beb93cSSam Leffler shared_secret_len = conf->auth_server->shared_secret_len; 78939beb93cSSam Leffler radius_msg_finish(msg, shared_secret, shared_secret_len); 79039beb93cSSam Leffler name = "authentication"; 79139beb93cSSam Leffler s = radius->auth_sock; 79239beb93cSSam Leffler conf->auth_server->requests++; 79339beb93cSSam Leffler } 79439beb93cSSam Leffler 79539beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 79639beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " 79739beb93cSSam Leffler "server", name); 79839beb93cSSam Leffler if (conf->msg_dumps) 79939beb93cSSam Leffler radius_msg_dump(msg); 80039beb93cSSam Leffler 801e28a4053SRui Paulo buf = radius_msg_get_buf(msg); 802e28a4053SRui Paulo res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); 80339beb93cSSam Leffler if (res < 0) 80439beb93cSSam Leffler radius_client_handle_send_error(radius, s, msg_type); 80539beb93cSSam Leffler 80639beb93cSSam Leffler radius_client_list_add(radius, msg, msg_type, shared_secret, 80739beb93cSSam Leffler shared_secret_len, addr); 80839beb93cSSam Leffler 809f05cddf9SRui Paulo return 0; 81039beb93cSSam Leffler } 81139beb93cSSam Leffler 81239beb93cSSam Leffler 81339beb93cSSam Leffler static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 81439beb93cSSam Leffler { 81539beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 81639beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 81739beb93cSSam Leffler RadiusType msg_type = (RadiusType) sock_ctx; 81839beb93cSSam Leffler int len, roundtrip; 81939beb93cSSam Leffler unsigned char buf[3000]; 82039beb93cSSam Leffler struct radius_msg *msg; 821e28a4053SRui Paulo struct radius_hdr *hdr; 82239beb93cSSam Leffler struct radius_rx_handler *handlers; 82339beb93cSSam Leffler size_t num_handlers, i; 82439beb93cSSam Leffler struct radius_msg_list *req, *prev_req; 8255b9c547cSRui Paulo struct os_reltime now; 82639beb93cSSam Leffler struct hostapd_radius_server *rconf; 82739beb93cSSam Leffler int invalid_authenticator = 0; 82839beb93cSSam Leffler 82939beb93cSSam Leffler if (msg_type == RADIUS_ACCT) { 83039beb93cSSam Leffler handlers = radius->acct_handlers; 83139beb93cSSam Leffler num_handlers = radius->num_acct_handlers; 83239beb93cSSam Leffler rconf = conf->acct_server; 83339beb93cSSam Leffler } else { 83439beb93cSSam Leffler handlers = radius->auth_handlers; 83539beb93cSSam Leffler num_handlers = radius->num_auth_handlers; 83639beb93cSSam Leffler rconf = conf->auth_server; 83739beb93cSSam Leffler } 83839beb93cSSam Leffler 83939beb93cSSam Leffler len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); 84039beb93cSSam Leffler if (len < 0) { 8415b9c547cSRui Paulo wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno)); 84239beb93cSSam Leffler return; 84339beb93cSSam Leffler } 84439beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 84539beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " 84639beb93cSSam Leffler "server", len); 84739beb93cSSam Leffler if (len == sizeof(buf)) { 8485b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); 84939beb93cSSam Leffler return; 85039beb93cSSam Leffler } 85139beb93cSSam Leffler 85239beb93cSSam Leffler msg = radius_msg_parse(buf, len); 85339beb93cSSam Leffler if (msg == NULL) { 8545b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); 85539beb93cSSam Leffler rconf->malformed_responses++; 85639beb93cSSam Leffler return; 85739beb93cSSam Leffler } 858e28a4053SRui Paulo hdr = radius_msg_get_hdr(msg); 85939beb93cSSam Leffler 86039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 86139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); 86239beb93cSSam Leffler if (conf->msg_dumps) 86339beb93cSSam Leffler radius_msg_dump(msg); 86439beb93cSSam Leffler 865e28a4053SRui Paulo switch (hdr->code) { 86639beb93cSSam Leffler case RADIUS_CODE_ACCESS_ACCEPT: 86739beb93cSSam Leffler rconf->access_accepts++; 86839beb93cSSam Leffler break; 86939beb93cSSam Leffler case RADIUS_CODE_ACCESS_REJECT: 87039beb93cSSam Leffler rconf->access_rejects++; 87139beb93cSSam Leffler break; 87239beb93cSSam Leffler case RADIUS_CODE_ACCESS_CHALLENGE: 87339beb93cSSam Leffler rconf->access_challenges++; 87439beb93cSSam Leffler break; 87539beb93cSSam Leffler case RADIUS_CODE_ACCOUNTING_RESPONSE: 87639beb93cSSam Leffler rconf->responses++; 87739beb93cSSam Leffler break; 87839beb93cSSam Leffler } 87939beb93cSSam Leffler 88039beb93cSSam Leffler prev_req = NULL; 88139beb93cSSam Leffler req = radius->msgs; 88239beb93cSSam Leffler while (req) { 88339beb93cSSam Leffler /* TODO: also match by src addr:port of the packet when using 88439beb93cSSam Leffler * alternative RADIUS servers (?) */ 88539beb93cSSam Leffler if ((req->msg_type == msg_type || 88639beb93cSSam Leffler (req->msg_type == RADIUS_ACCT_INTERIM && 88739beb93cSSam Leffler msg_type == RADIUS_ACCT)) && 888e28a4053SRui Paulo radius_msg_get_hdr(req->msg)->identifier == 889e28a4053SRui Paulo hdr->identifier) 89039beb93cSSam Leffler break; 89139beb93cSSam Leffler 89239beb93cSSam Leffler prev_req = req; 89339beb93cSSam Leffler req = req->next; 89439beb93cSSam Leffler } 89539beb93cSSam Leffler 89639beb93cSSam Leffler if (req == NULL) { 89739beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 89839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 89939beb93cSSam Leffler "No matching RADIUS request found (type=%d " 90039beb93cSSam Leffler "id=%d) - dropping packet", 901e28a4053SRui Paulo msg_type, hdr->identifier); 90239beb93cSSam Leffler goto fail; 90339beb93cSSam Leffler } 90439beb93cSSam Leffler 9055b9c547cSRui Paulo os_get_reltime(&now); 90639beb93cSSam Leffler roundtrip = (now.sec - req->last_attempt.sec) * 100 + 90739beb93cSSam Leffler (now.usec - req->last_attempt.usec) / 10000; 90839beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 90939beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 91039beb93cSSam Leffler "Received RADIUS packet matched with a pending " 91139beb93cSSam Leffler "request, round trip time %d.%02d sec", 91239beb93cSSam Leffler roundtrip / 100, roundtrip % 100); 91339beb93cSSam Leffler rconf->round_trip_time = roundtrip; 91439beb93cSSam Leffler 91539beb93cSSam Leffler /* Remove ACKed RADIUS packet from retransmit list */ 91639beb93cSSam Leffler if (prev_req) 91739beb93cSSam Leffler prev_req->next = req->next; 91839beb93cSSam Leffler else 91939beb93cSSam Leffler radius->msgs = req->next; 92039beb93cSSam Leffler radius->num_msgs--; 92139beb93cSSam Leffler 92239beb93cSSam Leffler for (i = 0; i < num_handlers; i++) { 92339beb93cSSam Leffler RadiusRxResult res; 92439beb93cSSam Leffler res = handlers[i].handler(msg, req->msg, req->shared_secret, 92539beb93cSSam Leffler req->shared_secret_len, 92639beb93cSSam Leffler handlers[i].data); 92739beb93cSSam Leffler switch (res) { 92839beb93cSSam Leffler case RADIUS_RX_PROCESSED: 92939beb93cSSam Leffler radius_msg_free(msg); 93085732ac8SCy Schubert /* fall through */ 93139beb93cSSam Leffler case RADIUS_RX_QUEUED: 93239beb93cSSam Leffler radius_client_msg_free(req); 93339beb93cSSam Leffler return; 93439beb93cSSam Leffler case RADIUS_RX_INVALID_AUTHENTICATOR: 93539beb93cSSam Leffler invalid_authenticator++; 93685732ac8SCy Schubert /* fall through */ 93739beb93cSSam Leffler case RADIUS_RX_UNKNOWN: 93839beb93cSSam Leffler /* continue with next handler */ 93939beb93cSSam Leffler break; 94039beb93cSSam Leffler } 94139beb93cSSam Leffler } 94239beb93cSSam Leffler 94339beb93cSSam Leffler if (invalid_authenticator) 94439beb93cSSam Leffler rconf->bad_authenticators++; 94539beb93cSSam Leffler else 94639beb93cSSam Leffler rconf->unknown_types++; 94739beb93cSSam Leffler hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, 94839beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " 94939beb93cSSam Leffler "(type=%d code=%d id=%d)%s - dropping packet", 950e28a4053SRui Paulo msg_type, hdr->code, hdr->identifier, 95139beb93cSSam Leffler invalid_authenticator ? " [INVALID AUTHENTICATOR]" : 95239beb93cSSam Leffler ""); 95339beb93cSSam Leffler radius_client_msg_free(req); 95439beb93cSSam Leffler 95539beb93cSSam Leffler fail: 95639beb93cSSam Leffler radius_msg_free(msg); 95739beb93cSSam Leffler } 95839beb93cSSam Leffler 95939beb93cSSam Leffler 960e28a4053SRui Paulo /** 961e28a4053SRui Paulo * radius_client_get_id - Get an identifier for a new RADIUS message 962e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 963e28a4053SRui Paulo * Returns: Allocated identifier 964e28a4053SRui Paulo * 965e28a4053SRui Paulo * This function is used to fetch a unique (among pending requests) identifier 966e28a4053SRui Paulo * for a new RADIUS message. 967e28a4053SRui Paulo */ 96839beb93cSSam Leffler u8 radius_client_get_id(struct radius_client_data *radius) 96939beb93cSSam Leffler { 97039beb93cSSam Leffler struct radius_msg_list *entry, *prev, *_remove; 97139beb93cSSam Leffler u8 id = radius->next_radius_identifier++; 97239beb93cSSam Leffler 97339beb93cSSam Leffler /* remove entries with matching id from retransmit list to avoid 97439beb93cSSam Leffler * using new reply from the RADIUS server with an old request */ 97539beb93cSSam Leffler entry = radius->msgs; 97639beb93cSSam Leffler prev = NULL; 97739beb93cSSam Leffler while (entry) { 978e28a4053SRui Paulo if (radius_msg_get_hdr(entry->msg)->identifier == id) { 97939beb93cSSam Leffler hostapd_logger(radius->ctx, entry->addr, 98039beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 98139beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 98239beb93cSSam Leffler "Removing pending RADIUS message, " 98339beb93cSSam Leffler "since its id (%d) is reused", id); 98439beb93cSSam Leffler if (prev) 98539beb93cSSam Leffler prev->next = entry->next; 98639beb93cSSam Leffler else 98739beb93cSSam Leffler radius->msgs = entry->next; 98839beb93cSSam Leffler _remove = entry; 98939beb93cSSam Leffler } else { 99039beb93cSSam Leffler _remove = NULL; 99139beb93cSSam Leffler prev = entry; 99239beb93cSSam Leffler } 99339beb93cSSam Leffler entry = entry->next; 99439beb93cSSam Leffler 99539beb93cSSam Leffler if (_remove) 99639beb93cSSam Leffler radius_client_msg_free(_remove); 99739beb93cSSam Leffler } 99839beb93cSSam Leffler 99939beb93cSSam Leffler return id; 100039beb93cSSam Leffler } 100139beb93cSSam Leffler 100239beb93cSSam Leffler 1003e28a4053SRui Paulo /** 1004e28a4053SRui Paulo * radius_client_flush - Flush all pending RADIUS client messages 1005e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1006e28a4053SRui Paulo * @only_auth: Whether only authentication messages are removed 1007e28a4053SRui Paulo */ 100839beb93cSSam Leffler void radius_client_flush(struct radius_client_data *radius, int only_auth) 100939beb93cSSam Leffler { 101039beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 101139beb93cSSam Leffler 101239beb93cSSam Leffler if (!radius) 101339beb93cSSam Leffler return; 101439beb93cSSam Leffler 101539beb93cSSam Leffler prev = NULL; 101639beb93cSSam Leffler entry = radius->msgs; 101739beb93cSSam Leffler 101839beb93cSSam Leffler while (entry) { 101939beb93cSSam Leffler if (!only_auth || entry->msg_type == RADIUS_AUTH) { 102039beb93cSSam Leffler if (prev) 102139beb93cSSam Leffler prev->next = entry->next; 102239beb93cSSam Leffler else 102339beb93cSSam Leffler radius->msgs = entry->next; 102439beb93cSSam Leffler 102539beb93cSSam Leffler tmp = entry; 102639beb93cSSam Leffler entry = entry->next; 102739beb93cSSam Leffler radius_client_msg_free(tmp); 102839beb93cSSam Leffler radius->num_msgs--; 102939beb93cSSam Leffler } else { 103039beb93cSSam Leffler prev = entry; 103139beb93cSSam Leffler entry = entry->next; 103239beb93cSSam Leffler } 103339beb93cSSam Leffler } 103439beb93cSSam Leffler 103539beb93cSSam Leffler if (radius->msgs == NULL) 103639beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 103739beb93cSSam Leffler } 103839beb93cSSam Leffler 103939beb93cSSam Leffler 104039beb93cSSam Leffler static void radius_client_update_acct_msgs(struct radius_client_data *radius, 1041e28a4053SRui Paulo const u8 *shared_secret, 104239beb93cSSam Leffler size_t shared_secret_len) 104339beb93cSSam Leffler { 104439beb93cSSam Leffler struct radius_msg_list *entry; 104539beb93cSSam Leffler 104639beb93cSSam Leffler if (!radius) 104739beb93cSSam Leffler return; 104839beb93cSSam Leffler 104939beb93cSSam Leffler for (entry = radius->msgs; entry; entry = entry->next) { 105039beb93cSSam Leffler if (entry->msg_type == RADIUS_ACCT) { 105139beb93cSSam Leffler entry->shared_secret = shared_secret; 105239beb93cSSam Leffler entry->shared_secret_len = shared_secret_len; 105339beb93cSSam Leffler radius_msg_finish_acct(entry->msg, shared_secret, 105439beb93cSSam Leffler shared_secret_len); 105539beb93cSSam Leffler } 105639beb93cSSam Leffler } 105739beb93cSSam Leffler } 105839beb93cSSam Leffler 105939beb93cSSam Leffler 106039beb93cSSam Leffler static int 106139beb93cSSam Leffler radius_change_server(struct radius_client_data *radius, 106239beb93cSSam Leffler struct hostapd_radius_server *nserv, 106339beb93cSSam Leffler struct hostapd_radius_server *oserv, 106439beb93cSSam Leffler int sock, int sock6, int auth) 106539beb93cSSam Leffler { 106639beb93cSSam Leffler struct sockaddr_in serv, claddr; 106739beb93cSSam Leffler #ifdef CONFIG_IPV6 106839beb93cSSam Leffler struct sockaddr_in6 serv6, claddr6; 106939beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 107039beb93cSSam Leffler struct sockaddr *addr, *cl_addr; 107139beb93cSSam Leffler socklen_t addrlen, claddrlen; 107239beb93cSSam Leffler char abuf[50]; 107339beb93cSSam Leffler int sel_sock; 107439beb93cSSam Leffler struct radius_msg_list *entry; 107539beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 1076780fb4a2SCy Schubert struct sockaddr_in disconnect_addr = { 1077780fb4a2SCy Schubert .sin_family = AF_UNSPEC, 1078780fb4a2SCy Schubert }; 107939beb93cSSam Leffler 108039beb93cSSam Leffler hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, 108139beb93cSSam Leffler HOSTAPD_LEVEL_INFO, 108239beb93cSSam Leffler "%s server %s:%d", 108339beb93cSSam Leffler auth ? "Authentication" : "Accounting", 108439beb93cSSam Leffler hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), 108539beb93cSSam Leffler nserv->port); 108639beb93cSSam Leffler 1087780fb4a2SCy Schubert if (oserv && oserv == nserv) { 1088780fb4a2SCy Schubert /* Reconnect to same server, flush */ 1089780fb4a2SCy Schubert if (auth) 1090780fb4a2SCy Schubert radius_client_flush(radius, 1); 1091780fb4a2SCy Schubert } 1092780fb4a2SCy Schubert 10935b9c547cSRui Paulo if (oserv && oserv != nserv && 10945b9c547cSRui Paulo (nserv->shared_secret_len != oserv->shared_secret_len || 109539beb93cSSam Leffler os_memcmp(nserv->shared_secret, oserv->shared_secret, 10965b9c547cSRui Paulo nserv->shared_secret_len) != 0)) { 109739beb93cSSam Leffler /* Pending RADIUS packets used different shared secret, so 109839beb93cSSam Leffler * they need to be modified. Update accounting message 109939beb93cSSam Leffler * authenticators here. Authentication messages are removed 110039beb93cSSam Leffler * since they would require more changes and the new RADIUS 110139beb93cSSam Leffler * server may not be prepared to receive them anyway due to 110239beb93cSSam Leffler * missing state information. Client will likely retry 110339beb93cSSam Leffler * authentication, so this should not be an issue. */ 110439beb93cSSam Leffler if (auth) 110539beb93cSSam Leffler radius_client_flush(radius, 1); 110639beb93cSSam Leffler else { 110739beb93cSSam Leffler radius_client_update_acct_msgs( 110839beb93cSSam Leffler radius, nserv->shared_secret, 110939beb93cSSam Leffler nserv->shared_secret_len); 111039beb93cSSam Leffler } 111139beb93cSSam Leffler } 111239beb93cSSam Leffler 1113*4bc52338SCy Schubert /* Reset retry counters */ 1114*4bc52338SCy Schubert for (entry = radius->msgs; oserv && entry; entry = entry->next) { 111539beb93cSSam Leffler if ((auth && entry->msg_type != RADIUS_AUTH) || 111639beb93cSSam Leffler (!auth && entry->msg_type != RADIUS_ACCT)) 111739beb93cSSam Leffler continue; 111839beb93cSSam Leffler entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; 1119*4bc52338SCy Schubert entry->attempts = 1; 112039beb93cSSam Leffler entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; 112139beb93cSSam Leffler } 112239beb93cSSam Leffler 112339beb93cSSam Leffler if (radius->msgs) { 112439beb93cSSam Leffler eloop_cancel_timeout(radius_client_timer, radius, NULL); 112539beb93cSSam Leffler eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, 112639beb93cSSam Leffler radius_client_timer, radius, NULL); 112739beb93cSSam Leffler } 112839beb93cSSam Leffler 112939beb93cSSam Leffler switch (nserv->addr.af) { 113039beb93cSSam Leffler case AF_INET: 113139beb93cSSam Leffler os_memset(&serv, 0, sizeof(serv)); 113239beb93cSSam Leffler serv.sin_family = AF_INET; 113339beb93cSSam Leffler serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; 113439beb93cSSam Leffler serv.sin_port = htons(nserv->port); 113539beb93cSSam Leffler addr = (struct sockaddr *) &serv; 113639beb93cSSam Leffler addrlen = sizeof(serv); 113739beb93cSSam Leffler sel_sock = sock; 113839beb93cSSam Leffler break; 113939beb93cSSam Leffler #ifdef CONFIG_IPV6 114039beb93cSSam Leffler case AF_INET6: 114139beb93cSSam Leffler os_memset(&serv6, 0, sizeof(serv6)); 114239beb93cSSam Leffler serv6.sin6_family = AF_INET6; 114339beb93cSSam Leffler os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, 114439beb93cSSam Leffler sizeof(struct in6_addr)); 114539beb93cSSam Leffler serv6.sin6_port = htons(nserv->port); 114639beb93cSSam Leffler addr = (struct sockaddr *) &serv6; 114739beb93cSSam Leffler addrlen = sizeof(serv6); 114839beb93cSSam Leffler sel_sock = sock6; 114939beb93cSSam Leffler break; 115039beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 115139beb93cSSam Leffler default: 115239beb93cSSam Leffler return -1; 115339beb93cSSam Leffler } 115439beb93cSSam Leffler 11555b9c547cSRui Paulo if (sel_sock < 0) { 11565b9c547cSRui Paulo wpa_printf(MSG_INFO, 11575b9c547cSRui Paulo "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d", 11585b9c547cSRui Paulo nserv->addr.af, sock, sock6, auth); 11595b9c547cSRui Paulo return -1; 11605b9c547cSRui Paulo } 11615b9c547cSRui Paulo 116239beb93cSSam Leffler if (conf->force_client_addr) { 116339beb93cSSam Leffler switch (conf->client_addr.af) { 116439beb93cSSam Leffler case AF_INET: 116539beb93cSSam Leffler os_memset(&claddr, 0, sizeof(claddr)); 116639beb93cSSam Leffler claddr.sin_family = AF_INET; 116739beb93cSSam Leffler claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; 116839beb93cSSam Leffler claddr.sin_port = htons(0); 116939beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr; 117039beb93cSSam Leffler claddrlen = sizeof(claddr); 117139beb93cSSam Leffler break; 117239beb93cSSam Leffler #ifdef CONFIG_IPV6 117339beb93cSSam Leffler case AF_INET6: 117439beb93cSSam Leffler os_memset(&claddr6, 0, sizeof(claddr6)); 117539beb93cSSam Leffler claddr6.sin6_family = AF_INET6; 117639beb93cSSam Leffler os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, 117739beb93cSSam Leffler sizeof(struct in6_addr)); 117839beb93cSSam Leffler claddr6.sin6_port = htons(0); 117939beb93cSSam Leffler cl_addr = (struct sockaddr *) &claddr6; 118039beb93cSSam Leffler claddrlen = sizeof(claddr6); 118139beb93cSSam Leffler break; 118239beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 118339beb93cSSam Leffler default: 118439beb93cSSam Leffler return -1; 118539beb93cSSam Leffler } 118639beb93cSSam Leffler 118739beb93cSSam Leffler if (bind(sel_sock, cl_addr, claddrlen) < 0) { 11885b9c547cSRui Paulo wpa_printf(MSG_INFO, "bind[radius]: %s", 11895b9c547cSRui Paulo strerror(errno)); 119039beb93cSSam Leffler return -1; 119139beb93cSSam Leffler } 119239beb93cSSam Leffler } 119339beb93cSSam Leffler 1194780fb4a2SCy Schubert /* Force a reconnect by disconnecting the socket first */ 1195780fb4a2SCy Schubert if (connect(sel_sock, (struct sockaddr *) &disconnect_addr, 1196780fb4a2SCy Schubert sizeof(disconnect_addr)) < 0) 1197780fb4a2SCy Schubert wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno)); 1198780fb4a2SCy Schubert 119939beb93cSSam Leffler if (connect(sel_sock, addr, addrlen) < 0) { 12005b9c547cSRui Paulo wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); 120139beb93cSSam Leffler return -1; 120239beb93cSSam Leffler } 120339beb93cSSam Leffler 120439beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS 120539beb93cSSam Leffler switch (nserv->addr.af) { 120639beb93cSSam Leffler case AF_INET: 120739beb93cSSam Leffler claddrlen = sizeof(claddr); 12085b9c547cSRui Paulo if (getsockname(sel_sock, (struct sockaddr *) &claddr, 12095b9c547cSRui Paulo &claddrlen) == 0) { 121039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 12115b9c547cSRui Paulo inet_ntoa(claddr.sin_addr), 12125b9c547cSRui Paulo ntohs(claddr.sin_port)); 12135b9c547cSRui Paulo } 121439beb93cSSam Leffler break; 121539beb93cSSam Leffler #ifdef CONFIG_IPV6 121639beb93cSSam Leffler case AF_INET6: { 121739beb93cSSam Leffler claddrlen = sizeof(claddr6); 12185b9c547cSRui Paulo if (getsockname(sel_sock, (struct sockaddr *) &claddr6, 12195b9c547cSRui Paulo &claddrlen) == 0) { 122039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", 122139beb93cSSam Leffler inet_ntop(AF_INET6, &claddr6.sin6_addr, 122239beb93cSSam Leffler abuf, sizeof(abuf)), 122339beb93cSSam Leffler ntohs(claddr6.sin6_port)); 12245b9c547cSRui Paulo } 122539beb93cSSam Leffler break; 122639beb93cSSam Leffler } 122739beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 122839beb93cSSam Leffler } 122939beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */ 123039beb93cSSam Leffler 123139beb93cSSam Leffler if (auth) 123239beb93cSSam Leffler radius->auth_sock = sel_sock; 123339beb93cSSam Leffler else 123439beb93cSSam Leffler radius->acct_sock = sel_sock; 123539beb93cSSam Leffler 123639beb93cSSam Leffler return 0; 123739beb93cSSam Leffler } 123839beb93cSSam Leffler 123939beb93cSSam Leffler 124039beb93cSSam Leffler static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) 124139beb93cSSam Leffler { 124239beb93cSSam Leffler struct radius_client_data *radius = eloop_ctx; 124339beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 124439beb93cSSam Leffler struct hostapd_radius_server *oserv; 124539beb93cSSam Leffler 124639beb93cSSam Leffler if (radius->auth_sock >= 0 && conf->auth_servers && 124739beb93cSSam Leffler conf->auth_server != conf->auth_servers) { 124839beb93cSSam Leffler oserv = conf->auth_server; 124939beb93cSSam Leffler conf->auth_server = conf->auth_servers; 12505b9c547cSRui Paulo if (radius_change_server(radius, conf->auth_server, oserv, 12515b9c547cSRui Paulo radius->auth_serv_sock, 12525b9c547cSRui Paulo radius->auth_serv_sock6, 1) < 0) { 12535b9c547cSRui Paulo conf->auth_server = oserv; 12545b9c547cSRui Paulo radius_change_server(radius, oserv, conf->auth_server, 125539beb93cSSam Leffler radius->auth_serv_sock, 125639beb93cSSam Leffler radius->auth_serv_sock6, 1); 125739beb93cSSam Leffler } 12585b9c547cSRui Paulo } 125939beb93cSSam Leffler 126039beb93cSSam Leffler if (radius->acct_sock >= 0 && conf->acct_servers && 126139beb93cSSam Leffler conf->acct_server != conf->acct_servers) { 126239beb93cSSam Leffler oserv = conf->acct_server; 126339beb93cSSam Leffler conf->acct_server = conf->acct_servers; 12645b9c547cSRui Paulo if (radius_change_server(radius, conf->acct_server, oserv, 12655b9c547cSRui Paulo radius->acct_serv_sock, 12665b9c547cSRui Paulo radius->acct_serv_sock6, 0) < 0) { 12675b9c547cSRui Paulo conf->acct_server = oserv; 12685b9c547cSRui Paulo radius_change_server(radius, oserv, conf->acct_server, 126939beb93cSSam Leffler radius->acct_serv_sock, 127039beb93cSSam Leffler radius->acct_serv_sock6, 0); 127139beb93cSSam Leffler } 12725b9c547cSRui Paulo } 127339beb93cSSam Leffler 127439beb93cSSam Leffler if (conf->retry_primary_interval) 127539beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 127639beb93cSSam Leffler radius_retry_primary_timer, radius, 127739beb93cSSam Leffler NULL); 127839beb93cSSam Leffler } 127939beb93cSSam Leffler 128039beb93cSSam Leffler 12813157ba21SRui Paulo static int radius_client_disable_pmtu_discovery(int s) 12823157ba21SRui Paulo { 12833157ba21SRui Paulo int r = -1; 12843157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) 12853157ba21SRui Paulo /* Turn off Path MTU discovery on IPv4/UDP sockets. */ 12863157ba21SRui Paulo int action = IP_PMTUDISC_DONT; 12873157ba21SRui Paulo r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, 12883157ba21SRui Paulo sizeof(action)); 12893157ba21SRui Paulo if (r == -1) 12905b9c547cSRui Paulo wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", 12915b9c547cSRui Paulo strerror(errno)); 12923157ba21SRui Paulo #endif 12933157ba21SRui Paulo return r; 12943157ba21SRui Paulo } 12953157ba21SRui Paulo 12963157ba21SRui Paulo 12975b9c547cSRui Paulo static void radius_close_auth_sockets(struct radius_client_data *radius) 12985b9c547cSRui Paulo { 12995b9c547cSRui Paulo radius->auth_sock = -1; 13005b9c547cSRui Paulo 13015b9c547cSRui Paulo if (radius->auth_serv_sock >= 0) { 13025b9c547cSRui Paulo eloop_unregister_read_sock(radius->auth_serv_sock); 13035b9c547cSRui Paulo close(radius->auth_serv_sock); 13045b9c547cSRui Paulo radius->auth_serv_sock = -1; 13055b9c547cSRui Paulo } 13065b9c547cSRui Paulo #ifdef CONFIG_IPV6 13075b9c547cSRui Paulo if (radius->auth_serv_sock6 >= 0) { 13085b9c547cSRui Paulo eloop_unregister_read_sock(radius->auth_serv_sock6); 13095b9c547cSRui Paulo close(radius->auth_serv_sock6); 13105b9c547cSRui Paulo radius->auth_serv_sock6 = -1; 13115b9c547cSRui Paulo } 13125b9c547cSRui Paulo #endif /* CONFIG_IPV6 */ 13135b9c547cSRui Paulo } 13145b9c547cSRui Paulo 13155b9c547cSRui Paulo 13165b9c547cSRui Paulo static void radius_close_acct_sockets(struct radius_client_data *radius) 13175b9c547cSRui Paulo { 13185b9c547cSRui Paulo radius->acct_sock = -1; 13195b9c547cSRui Paulo 13205b9c547cSRui Paulo if (radius->acct_serv_sock >= 0) { 13215b9c547cSRui Paulo eloop_unregister_read_sock(radius->acct_serv_sock); 13225b9c547cSRui Paulo close(radius->acct_serv_sock); 13235b9c547cSRui Paulo radius->acct_serv_sock = -1; 13245b9c547cSRui Paulo } 13255b9c547cSRui Paulo #ifdef CONFIG_IPV6 13265b9c547cSRui Paulo if (radius->acct_serv_sock6 >= 0) { 13275b9c547cSRui Paulo eloop_unregister_read_sock(radius->acct_serv_sock6); 13285b9c547cSRui Paulo close(radius->acct_serv_sock6); 13295b9c547cSRui Paulo radius->acct_serv_sock6 = -1; 13305b9c547cSRui Paulo } 13315b9c547cSRui Paulo #endif /* CONFIG_IPV6 */ 13325b9c547cSRui Paulo } 13335b9c547cSRui Paulo 13345b9c547cSRui Paulo 133539beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius) 133639beb93cSSam Leffler { 133739beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 133839beb93cSSam Leffler int ok = 0; 133939beb93cSSam Leffler 13405b9c547cSRui Paulo radius_close_auth_sockets(radius); 13415b9c547cSRui Paulo 134239beb93cSSam Leffler radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 134339beb93cSSam Leffler if (radius->auth_serv_sock < 0) 13445b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", 13455b9c547cSRui Paulo strerror(errno)); 13463157ba21SRui Paulo else { 13473157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->auth_serv_sock); 134839beb93cSSam Leffler ok++; 13493157ba21SRui Paulo } 135039beb93cSSam Leffler 135139beb93cSSam Leffler #ifdef CONFIG_IPV6 135239beb93cSSam Leffler radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 135339beb93cSSam Leffler if (radius->auth_serv_sock6 < 0) 13545b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", 13555b9c547cSRui Paulo strerror(errno)); 135639beb93cSSam Leffler else 135739beb93cSSam Leffler ok++; 135839beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 135939beb93cSSam Leffler 136039beb93cSSam Leffler if (ok == 0) 136139beb93cSSam Leffler return -1; 136239beb93cSSam Leffler 136339beb93cSSam Leffler radius_change_server(radius, conf->auth_server, NULL, 136439beb93cSSam Leffler radius->auth_serv_sock, radius->auth_serv_sock6, 136539beb93cSSam Leffler 1); 136639beb93cSSam Leffler 136739beb93cSSam Leffler if (radius->auth_serv_sock >= 0 && 136839beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock, 136939beb93cSSam Leffler radius_client_receive, radius, 137039beb93cSSam Leffler (void *) RADIUS_AUTH)) { 13715b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); 13725b9c547cSRui Paulo radius_close_auth_sockets(radius); 137339beb93cSSam Leffler return -1; 137439beb93cSSam Leffler } 137539beb93cSSam Leffler 137639beb93cSSam Leffler #ifdef CONFIG_IPV6 137739beb93cSSam Leffler if (radius->auth_serv_sock6 >= 0 && 137839beb93cSSam Leffler eloop_register_read_sock(radius->auth_serv_sock6, 137939beb93cSSam Leffler radius_client_receive, radius, 138039beb93cSSam Leffler (void *) RADIUS_AUTH)) { 13815b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); 13825b9c547cSRui Paulo radius_close_auth_sockets(radius); 138339beb93cSSam Leffler return -1; 138439beb93cSSam Leffler } 138539beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 138639beb93cSSam Leffler 138739beb93cSSam Leffler return 0; 138839beb93cSSam Leffler } 138939beb93cSSam Leffler 139039beb93cSSam Leffler 139139beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius) 139239beb93cSSam Leffler { 139339beb93cSSam Leffler struct hostapd_radius_servers *conf = radius->conf; 139439beb93cSSam Leffler int ok = 0; 139539beb93cSSam Leffler 13965b9c547cSRui Paulo radius_close_acct_sockets(radius); 13975b9c547cSRui Paulo 139839beb93cSSam Leffler radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); 139939beb93cSSam Leffler if (radius->acct_serv_sock < 0) 14005b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", 14015b9c547cSRui Paulo strerror(errno)); 14023157ba21SRui Paulo else { 14033157ba21SRui Paulo radius_client_disable_pmtu_discovery(radius->acct_serv_sock); 140439beb93cSSam Leffler ok++; 14053157ba21SRui Paulo } 140639beb93cSSam Leffler 140739beb93cSSam Leffler #ifdef CONFIG_IPV6 140839beb93cSSam Leffler radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); 140939beb93cSSam Leffler if (radius->acct_serv_sock6 < 0) 14105b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", 14115b9c547cSRui Paulo strerror(errno)); 141239beb93cSSam Leffler else 141339beb93cSSam Leffler ok++; 141439beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 141539beb93cSSam Leffler 141639beb93cSSam Leffler if (ok == 0) 141739beb93cSSam Leffler return -1; 141839beb93cSSam Leffler 141939beb93cSSam Leffler radius_change_server(radius, conf->acct_server, NULL, 142039beb93cSSam Leffler radius->acct_serv_sock, radius->acct_serv_sock6, 142139beb93cSSam Leffler 0); 142239beb93cSSam Leffler 142339beb93cSSam Leffler if (radius->acct_serv_sock >= 0 && 142439beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock, 142539beb93cSSam Leffler radius_client_receive, radius, 142639beb93cSSam Leffler (void *) RADIUS_ACCT)) { 14275b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); 14285b9c547cSRui Paulo radius_close_acct_sockets(radius); 142939beb93cSSam Leffler return -1; 143039beb93cSSam Leffler } 143139beb93cSSam Leffler 143239beb93cSSam Leffler #ifdef CONFIG_IPV6 143339beb93cSSam Leffler if (radius->acct_serv_sock6 >= 0 && 143439beb93cSSam Leffler eloop_register_read_sock(radius->acct_serv_sock6, 143539beb93cSSam Leffler radius_client_receive, radius, 143639beb93cSSam Leffler (void *) RADIUS_ACCT)) { 14375b9c547cSRui Paulo wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); 14385b9c547cSRui Paulo radius_close_acct_sockets(radius); 143939beb93cSSam Leffler return -1; 144039beb93cSSam Leffler } 144139beb93cSSam Leffler #endif /* CONFIG_IPV6 */ 144239beb93cSSam Leffler 144339beb93cSSam Leffler return 0; 144439beb93cSSam Leffler } 144539beb93cSSam Leffler 144639beb93cSSam Leffler 1447e28a4053SRui Paulo /** 1448e28a4053SRui Paulo * radius_client_init - Initialize RADIUS client 1449e28a4053SRui Paulo * @ctx: Callback context to be used in hostapd_logger() calls 1450e28a4053SRui Paulo * @conf: RADIUS client configuration (RADIUS servers) 1451e28a4053SRui Paulo * Returns: Pointer to private RADIUS client context or %NULL on failure 1452e28a4053SRui Paulo * 1453e28a4053SRui Paulo * The caller is responsible for keeping the configuration data available for 1454e28a4053SRui Paulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is 1455e28a4053SRui Paulo * called for the returned context pointer. 1456e28a4053SRui Paulo */ 145739beb93cSSam Leffler struct radius_client_data * 145839beb93cSSam Leffler radius_client_init(void *ctx, struct hostapd_radius_servers *conf) 145939beb93cSSam Leffler { 146039beb93cSSam Leffler struct radius_client_data *radius; 146139beb93cSSam Leffler 146239beb93cSSam Leffler radius = os_zalloc(sizeof(struct radius_client_data)); 146339beb93cSSam Leffler if (radius == NULL) 146439beb93cSSam Leffler return NULL; 146539beb93cSSam Leffler 146639beb93cSSam Leffler radius->ctx = ctx; 146739beb93cSSam Leffler radius->conf = conf; 146839beb93cSSam Leffler radius->auth_serv_sock = radius->acct_serv_sock = 146939beb93cSSam Leffler radius->auth_serv_sock6 = radius->acct_serv_sock6 = 147039beb93cSSam Leffler radius->auth_sock = radius->acct_sock = -1; 147139beb93cSSam Leffler 147239beb93cSSam Leffler if (conf->auth_server && radius_client_init_auth(radius)) { 147339beb93cSSam Leffler radius_client_deinit(radius); 147439beb93cSSam Leffler return NULL; 147539beb93cSSam Leffler } 147639beb93cSSam Leffler 147739beb93cSSam Leffler if (conf->acct_server && radius_client_init_acct(radius)) { 147839beb93cSSam Leffler radius_client_deinit(radius); 147939beb93cSSam Leffler return NULL; 148039beb93cSSam Leffler } 148139beb93cSSam Leffler 148239beb93cSSam Leffler if (conf->retry_primary_interval) 148339beb93cSSam Leffler eloop_register_timeout(conf->retry_primary_interval, 0, 148439beb93cSSam Leffler radius_retry_primary_timer, radius, 148539beb93cSSam Leffler NULL); 148639beb93cSSam Leffler 148739beb93cSSam Leffler return radius; 148839beb93cSSam Leffler } 148939beb93cSSam Leffler 149039beb93cSSam Leffler 1491e28a4053SRui Paulo /** 1492e28a4053SRui Paulo * radius_client_deinit - Deinitialize RADIUS client 1493e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1494e28a4053SRui Paulo */ 149539beb93cSSam Leffler void radius_client_deinit(struct radius_client_data *radius) 149639beb93cSSam Leffler { 149739beb93cSSam Leffler if (!radius) 149839beb93cSSam Leffler return; 149939beb93cSSam Leffler 15005b9c547cSRui Paulo radius_close_auth_sockets(radius); 15015b9c547cSRui Paulo radius_close_acct_sockets(radius); 150239beb93cSSam Leffler 150339beb93cSSam Leffler eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); 150439beb93cSSam Leffler 150539beb93cSSam Leffler radius_client_flush(radius, 0); 150639beb93cSSam Leffler os_free(radius->auth_handlers); 150739beb93cSSam Leffler os_free(radius->acct_handlers); 150839beb93cSSam Leffler os_free(radius); 150939beb93cSSam Leffler } 151039beb93cSSam Leffler 151139beb93cSSam Leffler 1512e28a4053SRui Paulo /** 1513e28a4053SRui Paulo * radius_client_flush_auth - Flush pending RADIUS messages for an address 1514e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1515e28a4053SRui Paulo * @addr: MAC address of the related device 1516e28a4053SRui Paulo * 1517e28a4053SRui Paulo * This function can be used to remove pending RADIUS authentication messages 1518e28a4053SRui Paulo * that are related to a specific device. The addr parameter is matched with 1519e28a4053SRui Paulo * the one used in radius_client_send() call that was used to transmit the 1520e28a4053SRui Paulo * authentication request. 1521e28a4053SRui Paulo */ 1522e28a4053SRui Paulo void radius_client_flush_auth(struct radius_client_data *radius, 1523e28a4053SRui Paulo const u8 *addr) 152439beb93cSSam Leffler { 152539beb93cSSam Leffler struct radius_msg_list *entry, *prev, *tmp; 152639beb93cSSam Leffler 152739beb93cSSam Leffler prev = NULL; 152839beb93cSSam Leffler entry = radius->msgs; 152939beb93cSSam Leffler while (entry) { 153039beb93cSSam Leffler if (entry->msg_type == RADIUS_AUTH && 153139beb93cSSam Leffler os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { 153239beb93cSSam Leffler hostapd_logger(radius->ctx, addr, 153339beb93cSSam Leffler HOSTAPD_MODULE_RADIUS, 153439beb93cSSam Leffler HOSTAPD_LEVEL_DEBUG, 153539beb93cSSam Leffler "Removing pending RADIUS authentication" 153639beb93cSSam Leffler " message for removed client"); 153739beb93cSSam Leffler 153839beb93cSSam Leffler if (prev) 153939beb93cSSam Leffler prev->next = entry->next; 154039beb93cSSam Leffler else 154139beb93cSSam Leffler radius->msgs = entry->next; 154239beb93cSSam Leffler 154339beb93cSSam Leffler tmp = entry; 154439beb93cSSam Leffler entry = entry->next; 154539beb93cSSam Leffler radius_client_msg_free(tmp); 154639beb93cSSam Leffler radius->num_msgs--; 154739beb93cSSam Leffler continue; 154839beb93cSSam Leffler } 154939beb93cSSam Leffler 155039beb93cSSam Leffler prev = entry; 155139beb93cSSam Leffler entry = entry->next; 155239beb93cSSam Leffler } 155339beb93cSSam Leffler } 155439beb93cSSam Leffler 155539beb93cSSam Leffler 155639beb93cSSam Leffler static int radius_client_dump_auth_server(char *buf, size_t buflen, 155739beb93cSSam Leffler struct hostapd_radius_server *serv, 155839beb93cSSam Leffler struct radius_client_data *cli) 155939beb93cSSam Leffler { 156039beb93cSSam Leffler int pending = 0; 156139beb93cSSam Leffler struct radius_msg_list *msg; 156239beb93cSSam Leffler char abuf[50]; 156339beb93cSSam Leffler 156439beb93cSSam Leffler if (cli) { 156539beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 156639beb93cSSam Leffler if (msg->msg_type == RADIUS_AUTH) 156739beb93cSSam Leffler pending++; 156839beb93cSSam Leffler } 156939beb93cSSam Leffler } 157039beb93cSSam Leffler 157139beb93cSSam Leffler return os_snprintf(buf, buflen, 157239beb93cSSam Leffler "radiusAuthServerIndex=%d\n" 157339beb93cSSam Leffler "radiusAuthServerAddress=%s\n" 157439beb93cSSam Leffler "radiusAuthClientServerPortNumber=%d\n" 157539beb93cSSam Leffler "radiusAuthClientRoundTripTime=%d\n" 157639beb93cSSam Leffler "radiusAuthClientAccessRequests=%u\n" 157739beb93cSSam Leffler "radiusAuthClientAccessRetransmissions=%u\n" 157839beb93cSSam Leffler "radiusAuthClientAccessAccepts=%u\n" 157939beb93cSSam Leffler "radiusAuthClientAccessRejects=%u\n" 158039beb93cSSam Leffler "radiusAuthClientAccessChallenges=%u\n" 158139beb93cSSam Leffler "radiusAuthClientMalformedAccessResponses=%u\n" 158239beb93cSSam Leffler "radiusAuthClientBadAuthenticators=%u\n" 158339beb93cSSam Leffler "radiusAuthClientPendingRequests=%u\n" 158439beb93cSSam Leffler "radiusAuthClientTimeouts=%u\n" 158539beb93cSSam Leffler "radiusAuthClientUnknownTypes=%u\n" 158639beb93cSSam Leffler "radiusAuthClientPacketsDropped=%u\n", 158739beb93cSSam Leffler serv->index, 158839beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 158939beb93cSSam Leffler serv->port, 159039beb93cSSam Leffler serv->round_trip_time, 159139beb93cSSam Leffler serv->requests, 159239beb93cSSam Leffler serv->retransmissions, 159339beb93cSSam Leffler serv->access_accepts, 159439beb93cSSam Leffler serv->access_rejects, 159539beb93cSSam Leffler serv->access_challenges, 159639beb93cSSam Leffler serv->malformed_responses, 159739beb93cSSam Leffler serv->bad_authenticators, 159839beb93cSSam Leffler pending, 159939beb93cSSam Leffler serv->timeouts, 160039beb93cSSam Leffler serv->unknown_types, 160139beb93cSSam Leffler serv->packets_dropped); 160239beb93cSSam Leffler } 160339beb93cSSam Leffler 160439beb93cSSam Leffler 160539beb93cSSam Leffler static int radius_client_dump_acct_server(char *buf, size_t buflen, 160639beb93cSSam Leffler struct hostapd_radius_server *serv, 160739beb93cSSam Leffler struct radius_client_data *cli) 160839beb93cSSam Leffler { 160939beb93cSSam Leffler int pending = 0; 161039beb93cSSam Leffler struct radius_msg_list *msg; 161139beb93cSSam Leffler char abuf[50]; 161239beb93cSSam Leffler 161339beb93cSSam Leffler if (cli) { 161439beb93cSSam Leffler for (msg = cli->msgs; msg; msg = msg->next) { 161539beb93cSSam Leffler if (msg->msg_type == RADIUS_ACCT || 161639beb93cSSam Leffler msg->msg_type == RADIUS_ACCT_INTERIM) 161739beb93cSSam Leffler pending++; 161839beb93cSSam Leffler } 161939beb93cSSam Leffler } 162039beb93cSSam Leffler 162139beb93cSSam Leffler return os_snprintf(buf, buflen, 162239beb93cSSam Leffler "radiusAccServerIndex=%d\n" 162339beb93cSSam Leffler "radiusAccServerAddress=%s\n" 162439beb93cSSam Leffler "radiusAccClientServerPortNumber=%d\n" 162539beb93cSSam Leffler "radiusAccClientRoundTripTime=%d\n" 162639beb93cSSam Leffler "radiusAccClientRequests=%u\n" 162739beb93cSSam Leffler "radiusAccClientRetransmissions=%u\n" 162839beb93cSSam Leffler "radiusAccClientResponses=%u\n" 162939beb93cSSam Leffler "radiusAccClientMalformedResponses=%u\n" 163039beb93cSSam Leffler "radiusAccClientBadAuthenticators=%u\n" 163139beb93cSSam Leffler "radiusAccClientPendingRequests=%u\n" 163239beb93cSSam Leffler "radiusAccClientTimeouts=%u\n" 163339beb93cSSam Leffler "radiusAccClientUnknownTypes=%u\n" 163439beb93cSSam Leffler "radiusAccClientPacketsDropped=%u\n", 163539beb93cSSam Leffler serv->index, 163639beb93cSSam Leffler hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), 163739beb93cSSam Leffler serv->port, 163839beb93cSSam Leffler serv->round_trip_time, 163939beb93cSSam Leffler serv->requests, 164039beb93cSSam Leffler serv->retransmissions, 164139beb93cSSam Leffler serv->responses, 164239beb93cSSam Leffler serv->malformed_responses, 164339beb93cSSam Leffler serv->bad_authenticators, 164439beb93cSSam Leffler pending, 164539beb93cSSam Leffler serv->timeouts, 164639beb93cSSam Leffler serv->unknown_types, 164739beb93cSSam Leffler serv->packets_dropped); 164839beb93cSSam Leffler } 164939beb93cSSam Leffler 165039beb93cSSam Leffler 1651e28a4053SRui Paulo /** 1652e28a4053SRui Paulo * radius_client_get_mib - Get RADIUS client MIB information 1653e28a4053SRui Paulo * @radius: RADIUS client context from radius_client_init() 1654e28a4053SRui Paulo * @buf: Buffer for returning MIB data in text format 1655e28a4053SRui Paulo * @buflen: Maximum buf length in octets 1656e28a4053SRui Paulo * Returns: Number of octets written into the buffer 1657e28a4053SRui Paulo */ 165839beb93cSSam Leffler int radius_client_get_mib(struct radius_client_data *radius, char *buf, 165939beb93cSSam Leffler size_t buflen) 166039beb93cSSam Leffler { 1661780fb4a2SCy Schubert struct hostapd_radius_servers *conf; 166239beb93cSSam Leffler int i; 166339beb93cSSam Leffler struct hostapd_radius_server *serv; 166439beb93cSSam Leffler int count = 0; 166539beb93cSSam Leffler 1666780fb4a2SCy Schubert if (!radius) 1667780fb4a2SCy Schubert return 0; 1668780fb4a2SCy Schubert 1669780fb4a2SCy Schubert conf = radius->conf; 1670780fb4a2SCy Schubert 167139beb93cSSam Leffler if (conf->auth_servers) { 167239beb93cSSam Leffler for (i = 0; i < conf->num_auth_servers; i++) { 167339beb93cSSam Leffler serv = &conf->auth_servers[i]; 167439beb93cSSam Leffler count += radius_client_dump_auth_server( 167539beb93cSSam Leffler buf + count, buflen - count, serv, 167639beb93cSSam Leffler serv == conf->auth_server ? 167739beb93cSSam Leffler radius : NULL); 167839beb93cSSam Leffler } 167939beb93cSSam Leffler } 168039beb93cSSam Leffler 168139beb93cSSam Leffler if (conf->acct_servers) { 168239beb93cSSam Leffler for (i = 0; i < conf->num_acct_servers; i++) { 168339beb93cSSam Leffler serv = &conf->acct_servers[i]; 168439beb93cSSam Leffler count += radius_client_dump_acct_server( 168539beb93cSSam Leffler buf + count, buflen - count, serv, 168639beb93cSSam Leffler serv == conf->acct_server ? 168739beb93cSSam Leffler radius : NULL); 168839beb93cSSam Leffler } 168939beb93cSSam Leffler } 169039beb93cSSam Leffler 169139beb93cSSam Leffler return count; 169239beb93cSSam Leffler } 1693f05cddf9SRui Paulo 1694f05cddf9SRui Paulo 1695f05cddf9SRui Paulo void radius_client_reconfig(struct radius_client_data *radius, 1696f05cddf9SRui Paulo struct hostapd_radius_servers *conf) 1697f05cddf9SRui Paulo { 1698f05cddf9SRui Paulo if (radius) 1699f05cddf9SRui Paulo radius->conf = conf; 1700f05cddf9SRui Paulo } 1701