xref: /freebsd/contrib/wpa/src/radius/radius_client.c (revision e28a4053b110e06768631ac8401ed4a3c05e68a5)
139beb93cSSam Leffler /*
2*e28a4053SRui Paulo  * RADIUS client
3*e28a4053SRui Paulo  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
539beb93cSSam Leffler  * This program is free software; you can redistribute it and/or modify
639beb93cSSam Leffler  * it under the terms of the GNU General Public License version 2 as
739beb93cSSam Leffler  * published by the Free Software Foundation.
839beb93cSSam Leffler  *
939beb93cSSam Leffler  * Alternatively, this software may be distributed under the terms of BSD
1039beb93cSSam Leffler  * license.
1139beb93cSSam Leffler  *
1239beb93cSSam Leffler  * See README and COPYING for more details.
1339beb93cSSam Leffler  */
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "includes.h"
1639beb93cSSam Leffler 
1739beb93cSSam Leffler #include "common.h"
1839beb93cSSam Leffler #include "radius.h"
1939beb93cSSam Leffler #include "radius_client.h"
2039beb93cSSam Leffler #include "eloop.h"
2139beb93cSSam Leffler 
2239beb93cSSam Leffler /* Defaults for RADIUS retransmit values (exponential backoff) */
23*e28a4053SRui Paulo 
24*e28a4053SRui Paulo /**
25*e28a4053SRui Paulo  * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
26*e28a4053SRui Paulo  */
27*e28a4053SRui Paulo #define RADIUS_CLIENT_FIRST_WAIT 3
28*e28a4053SRui Paulo 
29*e28a4053SRui Paulo /**
30*e28a4053SRui Paulo  * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
31*e28a4053SRui Paulo  */
32*e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_WAIT 120
33*e28a4053SRui Paulo 
34*e28a4053SRui Paulo /**
35*e28a4053SRui Paulo  * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
36*e28a4053SRui Paulo  *
37*e28a4053SRui Paulo  * Maximum number of retransmit attempts before the entry is removed from
38*e28a4053SRui Paulo  * retransmit list.
39*e28a4053SRui Paulo  */
40*e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_RETRIES 10
41*e28a4053SRui Paulo 
42*e28a4053SRui Paulo /**
43*e28a4053SRui Paulo  * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
44*e28a4053SRui Paulo  *
45*e28a4053SRui Paulo  * Maximum number of entries in retransmit list (oldest entries will be
46*e28a4053SRui Paulo  * removed, if this limit is exceeded).
47*e28a4053SRui Paulo  */
48*e28a4053SRui Paulo #define RADIUS_CLIENT_MAX_ENTRIES 30
49*e28a4053SRui Paulo 
50*e28a4053SRui Paulo /**
51*e28a4053SRui Paulo  * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
52*e28a4053SRui Paulo  *
53*e28a4053SRui Paulo  * The number of failed retry attempts after which the RADIUS server will be
54*e28a4053SRui Paulo  * changed (if one of more backup servers are configured).
55*e28a4053SRui Paulo  */
56*e28a4053SRui Paulo #define RADIUS_CLIENT_NUM_FAILOVER 4
5739beb93cSSam Leffler 
5839beb93cSSam Leffler 
59*e28a4053SRui Paulo /**
60*e28a4053SRui Paulo  * struct radius_rx_handler - RADIUS client RX handler
61*e28a4053SRui Paulo  *
62*e28a4053SRui Paulo  * This data structure is used internally inside the RADIUS client module to
63*e28a4053SRui Paulo  * store registered RX handlers. These handlers are registered by calls to
64*e28a4053SRui Paulo  * radius_client_register() and unregistered when the RADIUS client is
65*e28a4053SRui Paulo  * deinitialized with a call to radius_client_deinit().
66*e28a4053SRui Paulo  */
6739beb93cSSam Leffler struct radius_rx_handler {
68*e28a4053SRui Paulo 	/**
69*e28a4053SRui Paulo 	 * handler - Received RADIUS message handler
70*e28a4053SRui Paulo 	 */
7139beb93cSSam Leffler 	RadiusRxResult (*handler)(struct radius_msg *msg,
7239beb93cSSam Leffler 				  struct radius_msg *req,
7339beb93cSSam Leffler 				  const u8 *shared_secret,
7439beb93cSSam Leffler 				  size_t shared_secret_len,
7539beb93cSSam Leffler 				  void *data);
76*e28a4053SRui Paulo 
77*e28a4053SRui Paulo 	/**
78*e28a4053SRui Paulo 	 * data - Context data for the handler
79*e28a4053SRui Paulo 	 */
8039beb93cSSam Leffler 	void *data;
8139beb93cSSam Leffler };
8239beb93cSSam Leffler 
8339beb93cSSam Leffler 
84*e28a4053SRui Paulo /**
85*e28a4053SRui Paulo  * struct radius_msg_list - RADIUS client message retransmit list
86*e28a4053SRui Paulo  *
87*e28a4053SRui Paulo  * This data structure is used internally inside the RADIUS client module to
88*e28a4053SRui Paulo  * store pending RADIUS requests that may still need to be retransmitted.
89*e28a4053SRui Paulo  */
9039beb93cSSam Leffler struct radius_msg_list {
91*e28a4053SRui Paulo 	/**
92*e28a4053SRui Paulo 	 * addr - STA/client address
93*e28a4053SRui Paulo 	 *
94*e28a4053SRui Paulo 	 * This is used to find RADIUS messages for the same STA.
95*e28a4053SRui Paulo 	 */
96*e28a4053SRui Paulo 	u8 addr[ETH_ALEN];
97*e28a4053SRui Paulo 
98*e28a4053SRui Paulo 	/**
99*e28a4053SRui Paulo 	 * msg - RADIUS message
100*e28a4053SRui Paulo 	 */
10139beb93cSSam Leffler 	struct radius_msg *msg;
102*e28a4053SRui Paulo 
103*e28a4053SRui Paulo 	/**
104*e28a4053SRui Paulo 	 * msg_type - Message type
105*e28a4053SRui Paulo 	 */
10639beb93cSSam Leffler 	RadiusType msg_type;
107*e28a4053SRui Paulo 
108*e28a4053SRui Paulo 	/**
109*e28a4053SRui Paulo 	 * first_try - Time of the first transmission attempt
110*e28a4053SRui Paulo 	 */
11139beb93cSSam Leffler 	os_time_t first_try;
112*e28a4053SRui Paulo 
113*e28a4053SRui Paulo 	/**
114*e28a4053SRui Paulo 	 * next_try - Time for the next transmission attempt
115*e28a4053SRui Paulo 	 */
11639beb93cSSam Leffler 	os_time_t next_try;
117*e28a4053SRui Paulo 
118*e28a4053SRui Paulo 	/**
119*e28a4053SRui Paulo 	 * attempts - Number of transmission attempts
120*e28a4053SRui Paulo 	 */
12139beb93cSSam Leffler 	int attempts;
122*e28a4053SRui Paulo 
123*e28a4053SRui Paulo 	/**
124*e28a4053SRui Paulo 	 * next_wait - Next retransmission wait time in seconds
125*e28a4053SRui Paulo 	 */
12639beb93cSSam Leffler 	int next_wait;
127*e28a4053SRui Paulo 
128*e28a4053SRui Paulo 	/**
129*e28a4053SRui Paulo 	 * last_attempt - Time of the last transmission attempt
130*e28a4053SRui Paulo 	 */
13139beb93cSSam Leffler 	struct os_time last_attempt;
13239beb93cSSam Leffler 
133*e28a4053SRui Paulo 	/**
134*e28a4053SRui Paulo 	 * shared_secret - Shared secret with the target RADIUS server
135*e28a4053SRui Paulo 	 */
136*e28a4053SRui Paulo 	const u8 *shared_secret;
137*e28a4053SRui Paulo 
138*e28a4053SRui Paulo 	/**
139*e28a4053SRui Paulo 	 * shared_secret_len - shared_secret length in octets
140*e28a4053SRui Paulo 	 */
14139beb93cSSam Leffler 	size_t shared_secret_len;
14239beb93cSSam Leffler 
14339beb93cSSam Leffler 	/* TODO: server config with failover to backup server(s) */
14439beb93cSSam Leffler 
145*e28a4053SRui Paulo 	/**
146*e28a4053SRui Paulo 	 * next - Next message in the list
147*e28a4053SRui Paulo 	 */
14839beb93cSSam Leffler 	struct radius_msg_list *next;
14939beb93cSSam Leffler };
15039beb93cSSam Leffler 
15139beb93cSSam Leffler 
152*e28a4053SRui Paulo /**
153*e28a4053SRui Paulo  * struct radius_client_data - Internal RADIUS client data
154*e28a4053SRui Paulo  *
155*e28a4053SRui Paulo  * This data structure is used internally inside the RADIUS client module.
156*e28a4053SRui Paulo  * External users allocate this by calling radius_client_init() and free it by
157*e28a4053SRui Paulo  * calling radius_client_deinit(). The pointer to this opaque data is used in
158*e28a4053SRui Paulo  * calls to other functions as an identifier for the RADIUS client instance.
159*e28a4053SRui Paulo  */
16039beb93cSSam Leffler struct radius_client_data {
161*e28a4053SRui Paulo 	/**
162*e28a4053SRui Paulo 	 * ctx - Context pointer for hostapd_logger() callbacks
163*e28a4053SRui Paulo 	 */
16439beb93cSSam Leffler 	void *ctx;
165*e28a4053SRui Paulo 
166*e28a4053SRui Paulo 	/**
167*e28a4053SRui Paulo 	 * conf - RADIUS client configuration (list of RADIUS servers to use)
168*e28a4053SRui Paulo 	 */
16939beb93cSSam Leffler 	struct hostapd_radius_servers *conf;
17039beb93cSSam Leffler 
171*e28a4053SRui Paulo 	/**
172*e28a4053SRui Paulo 	 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
173*e28a4053SRui Paulo 	 */
174*e28a4053SRui Paulo 	int auth_serv_sock;
17539beb93cSSam Leffler 
176*e28a4053SRui Paulo 	/**
177*e28a4053SRui Paulo 	 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
178*e28a4053SRui Paulo 	 */
179*e28a4053SRui Paulo 	int acct_serv_sock;
180*e28a4053SRui Paulo 
181*e28a4053SRui Paulo 	/**
182*e28a4053SRui Paulo 	 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
183*e28a4053SRui Paulo 	 */
184*e28a4053SRui Paulo 	int auth_serv_sock6;
185*e28a4053SRui Paulo 
186*e28a4053SRui Paulo 	/**
187*e28a4053SRui Paulo 	 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
188*e28a4053SRui Paulo 	 */
189*e28a4053SRui Paulo 	int acct_serv_sock6;
190*e28a4053SRui Paulo 
191*e28a4053SRui Paulo 	/**
192*e28a4053SRui Paulo 	 * auth_sock - Currently used socket for RADIUS authentication server
193*e28a4053SRui Paulo 	 */
194*e28a4053SRui Paulo 	int auth_sock;
195*e28a4053SRui Paulo 
196*e28a4053SRui Paulo 	/**
197*e28a4053SRui Paulo 	 * acct_sock - Currently used socket for RADIUS accounting server
198*e28a4053SRui Paulo 	 */
199*e28a4053SRui Paulo 	int acct_sock;
200*e28a4053SRui Paulo 
201*e28a4053SRui Paulo 	/**
202*e28a4053SRui Paulo 	 * auth_handlers - Authentication message handlers
203*e28a4053SRui Paulo 	 */
20439beb93cSSam Leffler 	struct radius_rx_handler *auth_handlers;
205*e28a4053SRui Paulo 
206*e28a4053SRui Paulo 	/**
207*e28a4053SRui Paulo 	 * num_auth_handlers - Number of handlers in auth_handlers
208*e28a4053SRui Paulo 	 */
20939beb93cSSam Leffler 	size_t num_auth_handlers;
210*e28a4053SRui Paulo 
211*e28a4053SRui Paulo 	/**
212*e28a4053SRui Paulo 	 * acct_handlers - Accounting message handlers
213*e28a4053SRui Paulo 	 */
21439beb93cSSam Leffler 	struct radius_rx_handler *acct_handlers;
215*e28a4053SRui Paulo 
216*e28a4053SRui Paulo 	/**
217*e28a4053SRui Paulo 	 * num_acct_handlers - Number of handlers in acct_handlers
218*e28a4053SRui Paulo 	 */
21939beb93cSSam Leffler 	size_t num_acct_handlers;
22039beb93cSSam Leffler 
221*e28a4053SRui Paulo 	/**
222*e28a4053SRui Paulo 	 * msgs - Pending outgoing RADIUS messages
223*e28a4053SRui Paulo 	 */
22439beb93cSSam Leffler 	struct radius_msg_list *msgs;
225*e28a4053SRui Paulo 
226*e28a4053SRui Paulo 	/**
227*e28a4053SRui Paulo 	 * num_msgs - Number of pending messages in the msgs list
228*e28a4053SRui Paulo 	 */
22939beb93cSSam Leffler 	size_t num_msgs;
23039beb93cSSam Leffler 
231*e28a4053SRui Paulo 	/**
232*e28a4053SRui Paulo 	 * next_radius_identifier - Next RADIUS message identifier to use
233*e28a4053SRui Paulo 	 */
23439beb93cSSam Leffler 	u8 next_radius_identifier;
23539beb93cSSam Leffler };
23639beb93cSSam Leffler 
23739beb93cSSam Leffler 
23839beb93cSSam Leffler static int
23939beb93cSSam Leffler radius_change_server(struct radius_client_data *radius,
24039beb93cSSam Leffler 		     struct hostapd_radius_server *nserv,
24139beb93cSSam Leffler 		     struct hostapd_radius_server *oserv,
24239beb93cSSam Leffler 		     int sock, int sock6, int auth);
24339beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius);
24439beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius);
24539beb93cSSam Leffler 
24639beb93cSSam Leffler 
24739beb93cSSam Leffler static void radius_client_msg_free(struct radius_msg_list *req)
24839beb93cSSam Leffler {
24939beb93cSSam Leffler 	radius_msg_free(req->msg);
25039beb93cSSam Leffler 	os_free(req);
25139beb93cSSam Leffler }
25239beb93cSSam Leffler 
25339beb93cSSam Leffler 
254*e28a4053SRui Paulo /**
255*e28a4053SRui Paulo  * radius_client_register - Register a RADIUS client RX handler
256*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
257*e28a4053SRui Paulo  * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
258*e28a4053SRui Paulo  * @handler: Handler for received RADIUS messages
259*e28a4053SRui Paulo  * @data: Context pointer for handler callbacks
260*e28a4053SRui Paulo  * Returns: 0 on success, -1 on failure
261*e28a4053SRui Paulo  *
262*e28a4053SRui Paulo  * This function is used to register a handler for processing received RADIUS
263*e28a4053SRui Paulo  * authentication and accounting messages. The handler() callback function will
264*e28a4053SRui Paulo  * be called whenever a RADIUS message is received from the active server.
265*e28a4053SRui Paulo  *
266*e28a4053SRui Paulo  * There can be multiple registered RADIUS message handlers. The handlers will
267*e28a4053SRui Paulo  * be called in order until one of them indicates that it has processed or
268*e28a4053SRui Paulo  * queued the message.
269*e28a4053SRui Paulo  */
27039beb93cSSam Leffler int radius_client_register(struct radius_client_data *radius,
27139beb93cSSam Leffler 			   RadiusType msg_type,
27239beb93cSSam Leffler 			   RadiusRxResult (*handler)(struct radius_msg *msg,
27339beb93cSSam Leffler 						     struct radius_msg *req,
27439beb93cSSam Leffler 						     const u8 *shared_secret,
27539beb93cSSam Leffler 						     size_t shared_secret_len,
27639beb93cSSam Leffler 						     void *data),
27739beb93cSSam Leffler 			   void *data)
27839beb93cSSam Leffler {
27939beb93cSSam Leffler 	struct radius_rx_handler **handlers, *newh;
28039beb93cSSam Leffler 	size_t *num;
28139beb93cSSam Leffler 
28239beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT) {
28339beb93cSSam Leffler 		handlers = &radius->acct_handlers;
28439beb93cSSam Leffler 		num = &radius->num_acct_handlers;
28539beb93cSSam Leffler 	} else {
28639beb93cSSam Leffler 		handlers = &radius->auth_handlers;
28739beb93cSSam Leffler 		num = &radius->num_auth_handlers;
28839beb93cSSam Leffler 	}
28939beb93cSSam Leffler 
29039beb93cSSam Leffler 	newh = os_realloc(*handlers,
29139beb93cSSam Leffler 			  (*num + 1) * sizeof(struct radius_rx_handler));
29239beb93cSSam Leffler 	if (newh == NULL)
29339beb93cSSam Leffler 		return -1;
29439beb93cSSam Leffler 
29539beb93cSSam Leffler 	newh[*num].handler = handler;
29639beb93cSSam Leffler 	newh[*num].data = data;
29739beb93cSSam Leffler 	(*num)++;
29839beb93cSSam Leffler 	*handlers = newh;
29939beb93cSSam Leffler 
30039beb93cSSam Leffler 	return 0;
30139beb93cSSam Leffler }
30239beb93cSSam Leffler 
30339beb93cSSam Leffler 
30439beb93cSSam Leffler static void radius_client_handle_send_error(struct radius_client_data *radius,
30539beb93cSSam Leffler 					    int s, RadiusType msg_type)
30639beb93cSSam Leffler {
30739beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
30839beb93cSSam Leffler 	int _errno = errno;
30939beb93cSSam Leffler 	perror("send[RADIUS]");
31039beb93cSSam Leffler 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
31139beb93cSSam Leffler 	    _errno == EBADF) {
31239beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
31339beb93cSSam Leffler 			       HOSTAPD_LEVEL_INFO,
31439beb93cSSam Leffler 			       "Send failed - maybe interface status changed -"
31539beb93cSSam Leffler 			       " try to connect again");
31639beb93cSSam Leffler 		eloop_unregister_read_sock(s);
31739beb93cSSam Leffler 		close(s);
31839beb93cSSam Leffler 		if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
31939beb93cSSam Leffler 			radius_client_init_acct(radius);
32039beb93cSSam Leffler 		else
32139beb93cSSam Leffler 			radius_client_init_auth(radius);
32239beb93cSSam Leffler 	}
32339beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
32439beb93cSSam Leffler }
32539beb93cSSam Leffler 
32639beb93cSSam Leffler 
32739beb93cSSam Leffler static int radius_client_retransmit(struct radius_client_data *radius,
32839beb93cSSam Leffler 				    struct radius_msg_list *entry,
32939beb93cSSam Leffler 				    os_time_t now)
33039beb93cSSam Leffler {
33139beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
33239beb93cSSam Leffler 	int s;
333*e28a4053SRui Paulo 	struct wpabuf *buf;
33439beb93cSSam Leffler 
33539beb93cSSam Leffler 	if (entry->msg_type == RADIUS_ACCT ||
33639beb93cSSam Leffler 	    entry->msg_type == RADIUS_ACCT_INTERIM) {
33739beb93cSSam Leffler 		s = radius->acct_sock;
33839beb93cSSam Leffler 		if (entry->attempts == 0)
33939beb93cSSam Leffler 			conf->acct_server->requests++;
34039beb93cSSam Leffler 		else {
34139beb93cSSam Leffler 			conf->acct_server->timeouts++;
34239beb93cSSam Leffler 			conf->acct_server->retransmissions++;
34339beb93cSSam Leffler 		}
34439beb93cSSam Leffler 	} else {
34539beb93cSSam Leffler 		s = radius->auth_sock;
34639beb93cSSam Leffler 		if (entry->attempts == 0)
34739beb93cSSam Leffler 			conf->auth_server->requests++;
34839beb93cSSam Leffler 		else {
34939beb93cSSam Leffler 			conf->auth_server->timeouts++;
35039beb93cSSam Leffler 			conf->auth_server->retransmissions++;
35139beb93cSSam Leffler 		}
35239beb93cSSam Leffler 	}
35339beb93cSSam Leffler 
35439beb93cSSam Leffler 	/* retransmit; remove entry if too many attempts */
35539beb93cSSam Leffler 	entry->attempts++;
35639beb93cSSam Leffler 	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
35739beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
358*e28a4053SRui Paulo 		       radius_msg_get_hdr(entry->msg)->identifier);
35939beb93cSSam Leffler 
36039beb93cSSam Leffler 	os_get_time(&entry->last_attempt);
361*e28a4053SRui Paulo 	buf = radius_msg_get_buf(entry->msg);
362*e28a4053SRui Paulo 	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
36339beb93cSSam Leffler 		radius_client_handle_send_error(radius, s, entry->msg_type);
36439beb93cSSam Leffler 
36539beb93cSSam Leffler 	entry->next_try = now + entry->next_wait;
36639beb93cSSam Leffler 	entry->next_wait *= 2;
36739beb93cSSam Leffler 	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
36839beb93cSSam Leffler 		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
36939beb93cSSam Leffler 	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
37039beb93cSSam Leffler 		printf("Removing un-ACKed RADIUS message due to too many "
37139beb93cSSam Leffler 		       "failed retransmit attempts\n");
37239beb93cSSam Leffler 		return 1;
37339beb93cSSam Leffler 	}
37439beb93cSSam Leffler 
37539beb93cSSam Leffler 	return 0;
37639beb93cSSam Leffler }
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 
37939beb93cSSam Leffler static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
38039beb93cSSam Leffler {
38139beb93cSSam Leffler 	struct radius_client_data *radius = eloop_ctx;
38239beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
38339beb93cSSam Leffler 	struct os_time now;
38439beb93cSSam Leffler 	os_time_t first;
38539beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
38639beb93cSSam Leffler 	int auth_failover = 0, acct_failover = 0;
38739beb93cSSam Leffler 	char abuf[50];
38839beb93cSSam Leffler 
38939beb93cSSam Leffler 	entry = radius->msgs;
39039beb93cSSam Leffler 	if (!entry)
39139beb93cSSam Leffler 		return;
39239beb93cSSam Leffler 
39339beb93cSSam Leffler 	os_get_time(&now);
39439beb93cSSam Leffler 	first = 0;
39539beb93cSSam Leffler 
39639beb93cSSam Leffler 	prev = NULL;
39739beb93cSSam Leffler 	while (entry) {
39839beb93cSSam Leffler 		if (now.sec >= entry->next_try &&
39939beb93cSSam Leffler 		    radius_client_retransmit(radius, entry, now.sec)) {
40039beb93cSSam Leffler 			if (prev)
40139beb93cSSam Leffler 				prev->next = entry->next;
40239beb93cSSam Leffler 			else
40339beb93cSSam Leffler 				radius->msgs = entry->next;
40439beb93cSSam Leffler 
40539beb93cSSam Leffler 			tmp = entry;
40639beb93cSSam Leffler 			entry = entry->next;
40739beb93cSSam Leffler 			radius_client_msg_free(tmp);
40839beb93cSSam Leffler 			radius->num_msgs--;
40939beb93cSSam Leffler 			continue;
41039beb93cSSam Leffler 		}
41139beb93cSSam Leffler 
41239beb93cSSam Leffler 		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
41339beb93cSSam Leffler 			if (entry->msg_type == RADIUS_ACCT ||
41439beb93cSSam Leffler 			    entry->msg_type == RADIUS_ACCT_INTERIM)
41539beb93cSSam Leffler 				acct_failover++;
41639beb93cSSam Leffler 			else
41739beb93cSSam Leffler 				auth_failover++;
41839beb93cSSam Leffler 		}
41939beb93cSSam Leffler 
42039beb93cSSam Leffler 		if (first == 0 || entry->next_try < first)
42139beb93cSSam Leffler 			first = entry->next_try;
42239beb93cSSam Leffler 
42339beb93cSSam Leffler 		prev = entry;
42439beb93cSSam Leffler 		entry = entry->next;
42539beb93cSSam Leffler 	}
42639beb93cSSam Leffler 
42739beb93cSSam Leffler 	if (radius->msgs) {
42839beb93cSSam Leffler 		if (first < now.sec)
42939beb93cSSam Leffler 			first = now.sec;
43039beb93cSSam Leffler 		eloop_register_timeout(first - now.sec, 0,
43139beb93cSSam Leffler 				       radius_client_timer, radius, NULL);
43239beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
43339beb93cSSam Leffler 			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
43439beb93cSSam Leffler 			       "retransmit in %ld seconds",
43539beb93cSSam Leffler 			       (long int) (first - now.sec));
43639beb93cSSam Leffler 	}
43739beb93cSSam Leffler 
43839beb93cSSam Leffler 	if (auth_failover && conf->num_auth_servers > 1) {
43939beb93cSSam Leffler 		struct hostapd_radius_server *next, *old;
44039beb93cSSam Leffler 		old = conf->auth_server;
44139beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
44239beb93cSSam Leffler 			       HOSTAPD_LEVEL_NOTICE,
44339beb93cSSam Leffler 			       "No response from Authentication server "
44439beb93cSSam Leffler 			       "%s:%d - failover",
44539beb93cSSam Leffler 			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
44639beb93cSSam Leffler 			       old->port);
44739beb93cSSam Leffler 
44839beb93cSSam Leffler 		for (entry = radius->msgs; entry; entry = entry->next) {
44939beb93cSSam Leffler 			if (entry->msg_type == RADIUS_AUTH)
45039beb93cSSam Leffler 				old->timeouts++;
45139beb93cSSam Leffler 		}
45239beb93cSSam Leffler 
45339beb93cSSam Leffler 		next = old + 1;
45439beb93cSSam Leffler 		if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
45539beb93cSSam Leffler 			next = conf->auth_servers;
45639beb93cSSam Leffler 		conf->auth_server = next;
45739beb93cSSam Leffler 		radius_change_server(radius, next, old,
45839beb93cSSam Leffler 				     radius->auth_serv_sock,
45939beb93cSSam Leffler 				     radius->auth_serv_sock6, 1);
46039beb93cSSam Leffler 	}
46139beb93cSSam Leffler 
46239beb93cSSam Leffler 	if (acct_failover && conf->num_acct_servers > 1) {
46339beb93cSSam Leffler 		struct hostapd_radius_server *next, *old;
46439beb93cSSam Leffler 		old = conf->acct_server;
46539beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
46639beb93cSSam Leffler 			       HOSTAPD_LEVEL_NOTICE,
46739beb93cSSam Leffler 			       "No response from Accounting server "
46839beb93cSSam Leffler 			       "%s:%d - failover",
46939beb93cSSam Leffler 			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
47039beb93cSSam Leffler 			       old->port);
47139beb93cSSam Leffler 
47239beb93cSSam Leffler 		for (entry = radius->msgs; entry; entry = entry->next) {
47339beb93cSSam Leffler 			if (entry->msg_type == RADIUS_ACCT ||
47439beb93cSSam Leffler 			    entry->msg_type == RADIUS_ACCT_INTERIM)
47539beb93cSSam Leffler 				old->timeouts++;
47639beb93cSSam Leffler 		}
47739beb93cSSam Leffler 
47839beb93cSSam Leffler 		next = old + 1;
47939beb93cSSam Leffler 		if (next > &conf->acct_servers[conf->num_acct_servers - 1])
48039beb93cSSam Leffler 			next = conf->acct_servers;
48139beb93cSSam Leffler 		conf->acct_server = next;
48239beb93cSSam Leffler 		radius_change_server(radius, next, old,
48339beb93cSSam Leffler 				     radius->acct_serv_sock,
48439beb93cSSam Leffler 				     radius->acct_serv_sock6, 0);
48539beb93cSSam Leffler 	}
48639beb93cSSam Leffler }
48739beb93cSSam Leffler 
48839beb93cSSam Leffler 
48939beb93cSSam Leffler static void radius_client_update_timeout(struct radius_client_data *radius)
49039beb93cSSam Leffler {
49139beb93cSSam Leffler 	struct os_time now;
49239beb93cSSam Leffler 	os_time_t first;
49339beb93cSSam Leffler 	struct radius_msg_list *entry;
49439beb93cSSam Leffler 
49539beb93cSSam Leffler 	eloop_cancel_timeout(radius_client_timer, radius, NULL);
49639beb93cSSam Leffler 
49739beb93cSSam Leffler 	if (radius->msgs == NULL) {
49839beb93cSSam Leffler 		return;
49939beb93cSSam Leffler 	}
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 	first = 0;
50239beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
50339beb93cSSam Leffler 		if (first == 0 || entry->next_try < first)
50439beb93cSSam Leffler 			first = entry->next_try;
50539beb93cSSam Leffler 	}
50639beb93cSSam Leffler 
50739beb93cSSam Leffler 	os_get_time(&now);
50839beb93cSSam Leffler 	if (first < now.sec)
50939beb93cSSam Leffler 		first = now.sec;
51039beb93cSSam Leffler 	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
51139beb93cSSam Leffler 			       NULL);
51239beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
51339beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
51439beb93cSSam Leffler 		       " %ld seconds\n", (long int) (first - now.sec));
51539beb93cSSam Leffler }
51639beb93cSSam Leffler 
51739beb93cSSam Leffler 
51839beb93cSSam Leffler static void radius_client_list_add(struct radius_client_data *radius,
51939beb93cSSam Leffler 				   struct radius_msg *msg,
520*e28a4053SRui Paulo 				   RadiusType msg_type,
521*e28a4053SRui Paulo 				   const u8 *shared_secret,
52239beb93cSSam Leffler 				   size_t shared_secret_len, const u8 *addr)
52339beb93cSSam Leffler {
52439beb93cSSam Leffler 	struct radius_msg_list *entry, *prev;
52539beb93cSSam Leffler 
52639beb93cSSam Leffler 	if (eloop_terminated()) {
52739beb93cSSam Leffler 		/* No point in adding entries to retransmit queue since event
52839beb93cSSam Leffler 		 * loop has already been terminated. */
52939beb93cSSam Leffler 		radius_msg_free(msg);
53039beb93cSSam Leffler 		return;
53139beb93cSSam Leffler 	}
53239beb93cSSam Leffler 
53339beb93cSSam Leffler 	entry = os_zalloc(sizeof(*entry));
53439beb93cSSam Leffler 	if (entry == NULL) {
53539beb93cSSam Leffler 		printf("Failed to add RADIUS packet into retransmit list\n");
53639beb93cSSam Leffler 		radius_msg_free(msg);
53739beb93cSSam Leffler 		return;
53839beb93cSSam Leffler 	}
53939beb93cSSam Leffler 
54039beb93cSSam Leffler 	if (addr)
54139beb93cSSam Leffler 		os_memcpy(entry->addr, addr, ETH_ALEN);
54239beb93cSSam Leffler 	entry->msg = msg;
54339beb93cSSam Leffler 	entry->msg_type = msg_type;
54439beb93cSSam Leffler 	entry->shared_secret = shared_secret;
54539beb93cSSam Leffler 	entry->shared_secret_len = shared_secret_len;
54639beb93cSSam Leffler 	os_get_time(&entry->last_attempt);
54739beb93cSSam Leffler 	entry->first_try = entry->last_attempt.sec;
54839beb93cSSam Leffler 	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
54939beb93cSSam Leffler 	entry->attempts = 1;
55039beb93cSSam Leffler 	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
55139beb93cSSam Leffler 	entry->next = radius->msgs;
55239beb93cSSam Leffler 	radius->msgs = entry;
55339beb93cSSam Leffler 	radius_client_update_timeout(radius);
55439beb93cSSam Leffler 
55539beb93cSSam Leffler 	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
55639beb93cSSam Leffler 		printf("Removing the oldest un-ACKed RADIUS packet due to "
55739beb93cSSam Leffler 		       "retransmit list limits.\n");
55839beb93cSSam Leffler 		prev = NULL;
55939beb93cSSam Leffler 		while (entry->next) {
56039beb93cSSam Leffler 			prev = entry;
56139beb93cSSam Leffler 			entry = entry->next;
56239beb93cSSam Leffler 		}
56339beb93cSSam Leffler 		if (prev) {
56439beb93cSSam Leffler 			prev->next = NULL;
56539beb93cSSam Leffler 			radius_client_msg_free(entry);
56639beb93cSSam Leffler 		}
56739beb93cSSam Leffler 	} else
56839beb93cSSam Leffler 		radius->num_msgs++;
56939beb93cSSam Leffler }
57039beb93cSSam Leffler 
57139beb93cSSam Leffler 
57239beb93cSSam Leffler static void radius_client_list_del(struct radius_client_data *radius,
57339beb93cSSam Leffler 				   RadiusType msg_type, const u8 *addr)
57439beb93cSSam Leffler {
57539beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
57639beb93cSSam Leffler 
57739beb93cSSam Leffler 	if (addr == NULL)
57839beb93cSSam Leffler 		return;
57939beb93cSSam Leffler 
58039beb93cSSam Leffler 	entry = radius->msgs;
58139beb93cSSam Leffler 	prev = NULL;
58239beb93cSSam Leffler 	while (entry) {
58339beb93cSSam Leffler 		if (entry->msg_type == msg_type &&
58439beb93cSSam Leffler 		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
58539beb93cSSam Leffler 			if (prev)
58639beb93cSSam Leffler 				prev->next = entry->next;
58739beb93cSSam Leffler 			else
58839beb93cSSam Leffler 				radius->msgs = entry->next;
58939beb93cSSam Leffler 			tmp = entry;
59039beb93cSSam Leffler 			entry = entry->next;
59139beb93cSSam Leffler 			hostapd_logger(radius->ctx, addr,
59239beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
59339beb93cSSam Leffler 				       HOSTAPD_LEVEL_DEBUG,
59439beb93cSSam Leffler 				       "Removing matching RADIUS message");
59539beb93cSSam Leffler 			radius_client_msg_free(tmp);
59639beb93cSSam Leffler 			radius->num_msgs--;
59739beb93cSSam Leffler 			continue;
59839beb93cSSam Leffler 		}
59939beb93cSSam Leffler 		prev = entry;
60039beb93cSSam Leffler 		entry = entry->next;
60139beb93cSSam Leffler 	}
60239beb93cSSam Leffler }
60339beb93cSSam Leffler 
60439beb93cSSam Leffler 
605*e28a4053SRui Paulo /**
606*e28a4053SRui Paulo  * radius_client_send - Send a RADIUS request
607*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
608*e28a4053SRui Paulo  * @msg: RADIUS message to be sent
609*e28a4053SRui Paulo  * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
610*e28a4053SRui Paulo  * @addr: MAC address of the device related to this message or %NULL
611*e28a4053SRui Paulo  * Returns: 0 on success, -1 on failure
612*e28a4053SRui Paulo  *
613*e28a4053SRui Paulo  * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
614*e28a4053SRui Paulo  * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
615*e28a4053SRui Paulo  * between accounting and interim accounting messages is that the interim
616*e28a4053SRui Paulo  * message will override any pending interim accounting updates while a new
617*e28a4053SRui Paulo  * accounting message does not remove any pending messages.
618*e28a4053SRui Paulo  *
619*e28a4053SRui Paulo  * The message is added on the retransmission queue and will be retransmitted
620*e28a4053SRui Paulo  * automatically until a response is received or maximum number of retries
621*e28a4053SRui Paulo  * (RADIUS_CLIENT_MAX_RETRIES) is reached.
622*e28a4053SRui Paulo  *
623*e28a4053SRui Paulo  * The related device MAC address can be used to identify pending messages that
624*e28a4053SRui Paulo  * can be removed with radius_client_flush_auth() or with interim accounting
625*e28a4053SRui Paulo  * updates.
626*e28a4053SRui Paulo  */
62739beb93cSSam Leffler int radius_client_send(struct radius_client_data *radius,
62839beb93cSSam Leffler 		       struct radius_msg *msg, RadiusType msg_type,
62939beb93cSSam Leffler 		       const u8 *addr)
63039beb93cSSam Leffler {
63139beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
632*e28a4053SRui Paulo 	const u8 *shared_secret;
63339beb93cSSam Leffler 	size_t shared_secret_len;
63439beb93cSSam Leffler 	char *name;
63539beb93cSSam Leffler 	int s, res;
636*e28a4053SRui Paulo 	struct wpabuf *buf;
63739beb93cSSam Leffler 
63839beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT_INTERIM) {
63939beb93cSSam Leffler 		/* Remove any pending interim acct update for the same STA. */
64039beb93cSSam Leffler 		radius_client_list_del(radius, msg_type, addr);
64139beb93cSSam Leffler 	}
64239beb93cSSam Leffler 
64339beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
64439beb93cSSam Leffler 		if (conf->acct_server == NULL) {
64539beb93cSSam Leffler 			hostapd_logger(radius->ctx, NULL,
64639beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
64739beb93cSSam Leffler 				       HOSTAPD_LEVEL_INFO,
64839beb93cSSam Leffler 				       "No accounting server configured");
64939beb93cSSam Leffler 			return -1;
65039beb93cSSam Leffler 		}
65139beb93cSSam Leffler 		shared_secret = conf->acct_server->shared_secret;
65239beb93cSSam Leffler 		shared_secret_len = conf->acct_server->shared_secret_len;
65339beb93cSSam Leffler 		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
65439beb93cSSam Leffler 		name = "accounting";
65539beb93cSSam Leffler 		s = radius->acct_sock;
65639beb93cSSam Leffler 		conf->acct_server->requests++;
65739beb93cSSam Leffler 	} else {
65839beb93cSSam Leffler 		if (conf->auth_server == NULL) {
65939beb93cSSam Leffler 			hostapd_logger(radius->ctx, NULL,
66039beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
66139beb93cSSam Leffler 				       HOSTAPD_LEVEL_INFO,
66239beb93cSSam Leffler 				       "No authentication server configured");
66339beb93cSSam Leffler 			return -1;
66439beb93cSSam Leffler 		}
66539beb93cSSam Leffler 		shared_secret = conf->auth_server->shared_secret;
66639beb93cSSam Leffler 		shared_secret_len = conf->auth_server->shared_secret_len;
66739beb93cSSam Leffler 		radius_msg_finish(msg, shared_secret, shared_secret_len);
66839beb93cSSam Leffler 		name = "authentication";
66939beb93cSSam Leffler 		s = radius->auth_sock;
67039beb93cSSam Leffler 		conf->auth_server->requests++;
67139beb93cSSam Leffler 	}
67239beb93cSSam Leffler 
67339beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
67439beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
67539beb93cSSam Leffler 		       "server", name);
67639beb93cSSam Leffler 	if (conf->msg_dumps)
67739beb93cSSam Leffler 		radius_msg_dump(msg);
67839beb93cSSam Leffler 
679*e28a4053SRui Paulo 	buf = radius_msg_get_buf(msg);
680*e28a4053SRui Paulo 	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
68139beb93cSSam Leffler 	if (res < 0)
68239beb93cSSam Leffler 		radius_client_handle_send_error(radius, s, msg_type);
68339beb93cSSam Leffler 
68439beb93cSSam Leffler 	radius_client_list_add(radius, msg, msg_type, shared_secret,
68539beb93cSSam Leffler 			       shared_secret_len, addr);
68639beb93cSSam Leffler 
68739beb93cSSam Leffler 	return res;
68839beb93cSSam Leffler }
68939beb93cSSam Leffler 
69039beb93cSSam Leffler 
69139beb93cSSam Leffler static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
69239beb93cSSam Leffler {
69339beb93cSSam Leffler 	struct radius_client_data *radius = eloop_ctx;
69439beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
69539beb93cSSam Leffler 	RadiusType msg_type = (RadiusType) sock_ctx;
69639beb93cSSam Leffler 	int len, roundtrip;
69739beb93cSSam Leffler 	unsigned char buf[3000];
69839beb93cSSam Leffler 	struct radius_msg *msg;
699*e28a4053SRui Paulo 	struct radius_hdr *hdr;
70039beb93cSSam Leffler 	struct radius_rx_handler *handlers;
70139beb93cSSam Leffler 	size_t num_handlers, i;
70239beb93cSSam Leffler 	struct radius_msg_list *req, *prev_req;
70339beb93cSSam Leffler 	struct os_time now;
70439beb93cSSam Leffler 	struct hostapd_radius_server *rconf;
70539beb93cSSam Leffler 	int invalid_authenticator = 0;
70639beb93cSSam Leffler 
70739beb93cSSam Leffler 	if (msg_type == RADIUS_ACCT) {
70839beb93cSSam Leffler 		handlers = radius->acct_handlers;
70939beb93cSSam Leffler 		num_handlers = radius->num_acct_handlers;
71039beb93cSSam Leffler 		rconf = conf->acct_server;
71139beb93cSSam Leffler 	} else {
71239beb93cSSam Leffler 		handlers = radius->auth_handlers;
71339beb93cSSam Leffler 		num_handlers = radius->num_auth_handlers;
71439beb93cSSam Leffler 		rconf = conf->auth_server;
71539beb93cSSam Leffler 	}
71639beb93cSSam Leffler 
71739beb93cSSam Leffler 	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
71839beb93cSSam Leffler 	if (len < 0) {
71939beb93cSSam Leffler 		perror("recv[RADIUS]");
72039beb93cSSam Leffler 		return;
72139beb93cSSam Leffler 	}
72239beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
72339beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
72439beb93cSSam Leffler 		       "server", len);
72539beb93cSSam Leffler 	if (len == sizeof(buf)) {
72639beb93cSSam Leffler 		printf("Possibly too long UDP frame for our buffer - "
72739beb93cSSam Leffler 		       "dropping it\n");
72839beb93cSSam Leffler 		return;
72939beb93cSSam Leffler 	}
73039beb93cSSam Leffler 
73139beb93cSSam Leffler 	msg = radius_msg_parse(buf, len);
73239beb93cSSam Leffler 	if (msg == NULL) {
73339beb93cSSam Leffler 		printf("Parsing incoming RADIUS frame failed\n");
73439beb93cSSam Leffler 		rconf->malformed_responses++;
73539beb93cSSam Leffler 		return;
73639beb93cSSam Leffler 	}
737*e28a4053SRui Paulo 	hdr = radius_msg_get_hdr(msg);
73839beb93cSSam Leffler 
73939beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
74039beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
74139beb93cSSam Leffler 	if (conf->msg_dumps)
74239beb93cSSam Leffler 		radius_msg_dump(msg);
74339beb93cSSam Leffler 
744*e28a4053SRui Paulo 	switch (hdr->code) {
74539beb93cSSam Leffler 	case RADIUS_CODE_ACCESS_ACCEPT:
74639beb93cSSam Leffler 		rconf->access_accepts++;
74739beb93cSSam Leffler 		break;
74839beb93cSSam Leffler 	case RADIUS_CODE_ACCESS_REJECT:
74939beb93cSSam Leffler 		rconf->access_rejects++;
75039beb93cSSam Leffler 		break;
75139beb93cSSam Leffler 	case RADIUS_CODE_ACCESS_CHALLENGE:
75239beb93cSSam Leffler 		rconf->access_challenges++;
75339beb93cSSam Leffler 		break;
75439beb93cSSam Leffler 	case RADIUS_CODE_ACCOUNTING_RESPONSE:
75539beb93cSSam Leffler 		rconf->responses++;
75639beb93cSSam Leffler 		break;
75739beb93cSSam Leffler 	}
75839beb93cSSam Leffler 
75939beb93cSSam Leffler 	prev_req = NULL;
76039beb93cSSam Leffler 	req = radius->msgs;
76139beb93cSSam Leffler 	while (req) {
76239beb93cSSam Leffler 		/* TODO: also match by src addr:port of the packet when using
76339beb93cSSam Leffler 		 * alternative RADIUS servers (?) */
76439beb93cSSam Leffler 		if ((req->msg_type == msg_type ||
76539beb93cSSam Leffler 		     (req->msg_type == RADIUS_ACCT_INTERIM &&
76639beb93cSSam Leffler 		      msg_type == RADIUS_ACCT)) &&
767*e28a4053SRui Paulo 		    radius_msg_get_hdr(req->msg)->identifier ==
768*e28a4053SRui Paulo 		    hdr->identifier)
76939beb93cSSam Leffler 			break;
77039beb93cSSam Leffler 
77139beb93cSSam Leffler 		prev_req = req;
77239beb93cSSam Leffler 		req = req->next;
77339beb93cSSam Leffler 	}
77439beb93cSSam Leffler 
77539beb93cSSam Leffler 	if (req == NULL) {
77639beb93cSSam Leffler 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
77739beb93cSSam Leffler 			       HOSTAPD_LEVEL_DEBUG,
77839beb93cSSam Leffler 			       "No matching RADIUS request found (type=%d "
77939beb93cSSam Leffler 			       "id=%d) - dropping packet",
780*e28a4053SRui Paulo 			       msg_type, hdr->identifier);
78139beb93cSSam Leffler 		goto fail;
78239beb93cSSam Leffler 	}
78339beb93cSSam Leffler 
78439beb93cSSam Leffler 	os_get_time(&now);
78539beb93cSSam Leffler 	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
78639beb93cSSam Leffler 		(now.usec - req->last_attempt.usec) / 10000;
78739beb93cSSam Leffler 	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
78839beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG,
78939beb93cSSam Leffler 		       "Received RADIUS packet matched with a pending "
79039beb93cSSam Leffler 		       "request, round trip time %d.%02d sec",
79139beb93cSSam Leffler 		       roundtrip / 100, roundtrip % 100);
79239beb93cSSam Leffler 	rconf->round_trip_time = roundtrip;
79339beb93cSSam Leffler 
79439beb93cSSam Leffler 	/* Remove ACKed RADIUS packet from retransmit list */
79539beb93cSSam Leffler 	if (prev_req)
79639beb93cSSam Leffler 		prev_req->next = req->next;
79739beb93cSSam Leffler 	else
79839beb93cSSam Leffler 		radius->msgs = req->next;
79939beb93cSSam Leffler 	radius->num_msgs--;
80039beb93cSSam Leffler 
80139beb93cSSam Leffler 	for (i = 0; i < num_handlers; i++) {
80239beb93cSSam Leffler 		RadiusRxResult res;
80339beb93cSSam Leffler 		res = handlers[i].handler(msg, req->msg, req->shared_secret,
80439beb93cSSam Leffler 					  req->shared_secret_len,
80539beb93cSSam Leffler 					  handlers[i].data);
80639beb93cSSam Leffler 		switch (res) {
80739beb93cSSam Leffler 		case RADIUS_RX_PROCESSED:
80839beb93cSSam Leffler 			radius_msg_free(msg);
80939beb93cSSam Leffler 			/* continue */
81039beb93cSSam Leffler 		case RADIUS_RX_QUEUED:
81139beb93cSSam Leffler 			radius_client_msg_free(req);
81239beb93cSSam Leffler 			return;
81339beb93cSSam Leffler 		case RADIUS_RX_INVALID_AUTHENTICATOR:
81439beb93cSSam Leffler 			invalid_authenticator++;
81539beb93cSSam Leffler 			/* continue */
81639beb93cSSam Leffler 		case RADIUS_RX_UNKNOWN:
81739beb93cSSam Leffler 			/* continue with next handler */
81839beb93cSSam Leffler 			break;
81939beb93cSSam Leffler 		}
82039beb93cSSam Leffler 	}
82139beb93cSSam Leffler 
82239beb93cSSam Leffler 	if (invalid_authenticator)
82339beb93cSSam Leffler 		rconf->bad_authenticators++;
82439beb93cSSam Leffler 	else
82539beb93cSSam Leffler 		rconf->unknown_types++;
82639beb93cSSam Leffler 	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
82739beb93cSSam Leffler 		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
82839beb93cSSam Leffler 		       "(type=%d code=%d id=%d)%s - dropping packet",
829*e28a4053SRui Paulo 		       msg_type, hdr->code, hdr->identifier,
83039beb93cSSam Leffler 		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
83139beb93cSSam Leffler 		       "");
83239beb93cSSam Leffler 	radius_client_msg_free(req);
83339beb93cSSam Leffler 
83439beb93cSSam Leffler  fail:
83539beb93cSSam Leffler 	radius_msg_free(msg);
83639beb93cSSam Leffler }
83739beb93cSSam Leffler 
83839beb93cSSam Leffler 
839*e28a4053SRui Paulo /**
840*e28a4053SRui Paulo  * radius_client_get_id - Get an identifier for a new RADIUS message
841*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
842*e28a4053SRui Paulo  * Returns: Allocated identifier
843*e28a4053SRui Paulo  *
844*e28a4053SRui Paulo  * This function is used to fetch a unique (among pending requests) identifier
845*e28a4053SRui Paulo  * for a new RADIUS message.
846*e28a4053SRui Paulo  */
84739beb93cSSam Leffler u8 radius_client_get_id(struct radius_client_data *radius)
84839beb93cSSam Leffler {
84939beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *_remove;
85039beb93cSSam Leffler 	u8 id = radius->next_radius_identifier++;
85139beb93cSSam Leffler 
85239beb93cSSam Leffler 	/* remove entries with matching id from retransmit list to avoid
85339beb93cSSam Leffler 	 * using new reply from the RADIUS server with an old request */
85439beb93cSSam Leffler 	entry = radius->msgs;
85539beb93cSSam Leffler 	prev = NULL;
85639beb93cSSam Leffler 	while (entry) {
857*e28a4053SRui Paulo 		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
85839beb93cSSam Leffler 			hostapd_logger(radius->ctx, entry->addr,
85939beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
86039beb93cSSam Leffler 				       HOSTAPD_LEVEL_DEBUG,
86139beb93cSSam Leffler 				       "Removing pending RADIUS message, "
86239beb93cSSam Leffler 				       "since its id (%d) is reused", id);
86339beb93cSSam Leffler 			if (prev)
86439beb93cSSam Leffler 				prev->next = entry->next;
86539beb93cSSam Leffler 			else
86639beb93cSSam Leffler 				radius->msgs = entry->next;
86739beb93cSSam Leffler 			_remove = entry;
86839beb93cSSam Leffler 		} else {
86939beb93cSSam Leffler 			_remove = NULL;
87039beb93cSSam Leffler 			prev = entry;
87139beb93cSSam Leffler 		}
87239beb93cSSam Leffler 		entry = entry->next;
87339beb93cSSam Leffler 
87439beb93cSSam Leffler 		if (_remove)
87539beb93cSSam Leffler 			radius_client_msg_free(_remove);
87639beb93cSSam Leffler 	}
87739beb93cSSam Leffler 
87839beb93cSSam Leffler 	return id;
87939beb93cSSam Leffler }
88039beb93cSSam Leffler 
88139beb93cSSam Leffler 
882*e28a4053SRui Paulo /**
883*e28a4053SRui Paulo  * radius_client_flush - Flush all pending RADIUS client messages
884*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
885*e28a4053SRui Paulo  * @only_auth: Whether only authentication messages are removed
886*e28a4053SRui Paulo  */
88739beb93cSSam Leffler void radius_client_flush(struct radius_client_data *radius, int only_auth)
88839beb93cSSam Leffler {
88939beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
89039beb93cSSam Leffler 
89139beb93cSSam Leffler 	if (!radius)
89239beb93cSSam Leffler 		return;
89339beb93cSSam Leffler 
89439beb93cSSam Leffler 	prev = NULL;
89539beb93cSSam Leffler 	entry = radius->msgs;
89639beb93cSSam Leffler 
89739beb93cSSam Leffler 	while (entry) {
89839beb93cSSam Leffler 		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
89939beb93cSSam Leffler 			if (prev)
90039beb93cSSam Leffler 				prev->next = entry->next;
90139beb93cSSam Leffler 			else
90239beb93cSSam Leffler 				radius->msgs = entry->next;
90339beb93cSSam Leffler 
90439beb93cSSam Leffler 			tmp = entry;
90539beb93cSSam Leffler 			entry = entry->next;
90639beb93cSSam Leffler 			radius_client_msg_free(tmp);
90739beb93cSSam Leffler 			radius->num_msgs--;
90839beb93cSSam Leffler 		} else {
90939beb93cSSam Leffler 			prev = entry;
91039beb93cSSam Leffler 			entry = entry->next;
91139beb93cSSam Leffler 		}
91239beb93cSSam Leffler 	}
91339beb93cSSam Leffler 
91439beb93cSSam Leffler 	if (radius->msgs == NULL)
91539beb93cSSam Leffler 		eloop_cancel_timeout(radius_client_timer, radius, NULL);
91639beb93cSSam Leffler }
91739beb93cSSam Leffler 
91839beb93cSSam Leffler 
91939beb93cSSam Leffler static void radius_client_update_acct_msgs(struct radius_client_data *radius,
920*e28a4053SRui Paulo 					   const u8 *shared_secret,
92139beb93cSSam Leffler 					   size_t shared_secret_len)
92239beb93cSSam Leffler {
92339beb93cSSam Leffler 	struct radius_msg_list *entry;
92439beb93cSSam Leffler 
92539beb93cSSam Leffler 	if (!radius)
92639beb93cSSam Leffler 		return;
92739beb93cSSam Leffler 
92839beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
92939beb93cSSam Leffler 		if (entry->msg_type == RADIUS_ACCT) {
93039beb93cSSam Leffler 			entry->shared_secret = shared_secret;
93139beb93cSSam Leffler 			entry->shared_secret_len = shared_secret_len;
93239beb93cSSam Leffler 			radius_msg_finish_acct(entry->msg, shared_secret,
93339beb93cSSam Leffler 					       shared_secret_len);
93439beb93cSSam Leffler 		}
93539beb93cSSam Leffler 	}
93639beb93cSSam Leffler }
93739beb93cSSam Leffler 
93839beb93cSSam Leffler 
93939beb93cSSam Leffler static int
94039beb93cSSam Leffler radius_change_server(struct radius_client_data *radius,
94139beb93cSSam Leffler 		     struct hostapd_radius_server *nserv,
94239beb93cSSam Leffler 		     struct hostapd_radius_server *oserv,
94339beb93cSSam Leffler 		     int sock, int sock6, int auth)
94439beb93cSSam Leffler {
94539beb93cSSam Leffler 	struct sockaddr_in serv, claddr;
94639beb93cSSam Leffler #ifdef CONFIG_IPV6
94739beb93cSSam Leffler 	struct sockaddr_in6 serv6, claddr6;
94839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
94939beb93cSSam Leffler 	struct sockaddr *addr, *cl_addr;
95039beb93cSSam Leffler 	socklen_t addrlen, claddrlen;
95139beb93cSSam Leffler 	char abuf[50];
95239beb93cSSam Leffler 	int sel_sock;
95339beb93cSSam Leffler 	struct radius_msg_list *entry;
95439beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
95539beb93cSSam Leffler 
95639beb93cSSam Leffler 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
95739beb93cSSam Leffler 		       HOSTAPD_LEVEL_INFO,
95839beb93cSSam Leffler 		       "%s server %s:%d",
95939beb93cSSam Leffler 		       auth ? "Authentication" : "Accounting",
96039beb93cSSam Leffler 		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
96139beb93cSSam Leffler 		       nserv->port);
96239beb93cSSam Leffler 
96339beb93cSSam Leffler 	if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
96439beb93cSSam Leffler 	    os_memcmp(nserv->shared_secret, oserv->shared_secret,
96539beb93cSSam Leffler 		      nserv->shared_secret_len) != 0) {
96639beb93cSSam Leffler 		/* Pending RADIUS packets used different shared secret, so
96739beb93cSSam Leffler 		 * they need to be modified. Update accounting message
96839beb93cSSam Leffler 		 * authenticators here. Authentication messages are removed
96939beb93cSSam Leffler 		 * since they would require more changes and the new RADIUS
97039beb93cSSam Leffler 		 * server may not be prepared to receive them anyway due to
97139beb93cSSam Leffler 		 * missing state information. Client will likely retry
97239beb93cSSam Leffler 		 * authentication, so this should not be an issue. */
97339beb93cSSam Leffler 		if (auth)
97439beb93cSSam Leffler 			radius_client_flush(radius, 1);
97539beb93cSSam Leffler 		else {
97639beb93cSSam Leffler 			radius_client_update_acct_msgs(
97739beb93cSSam Leffler 				radius, nserv->shared_secret,
97839beb93cSSam Leffler 				nserv->shared_secret_len);
97939beb93cSSam Leffler 		}
98039beb93cSSam Leffler 	}
98139beb93cSSam Leffler 
98239beb93cSSam Leffler 	/* Reset retry counters for the new server */
98339beb93cSSam Leffler 	for (entry = radius->msgs; entry; entry = entry->next) {
98439beb93cSSam Leffler 		if ((auth && entry->msg_type != RADIUS_AUTH) ||
98539beb93cSSam Leffler 		    (!auth && entry->msg_type != RADIUS_ACCT))
98639beb93cSSam Leffler 			continue;
98739beb93cSSam Leffler 		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
98839beb93cSSam Leffler 		entry->attempts = 0;
98939beb93cSSam Leffler 		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
99039beb93cSSam Leffler 	}
99139beb93cSSam Leffler 
99239beb93cSSam Leffler 	if (radius->msgs) {
99339beb93cSSam Leffler 		eloop_cancel_timeout(radius_client_timer, radius, NULL);
99439beb93cSSam Leffler 		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
99539beb93cSSam Leffler 				       radius_client_timer, radius, NULL);
99639beb93cSSam Leffler 	}
99739beb93cSSam Leffler 
99839beb93cSSam Leffler 	switch (nserv->addr.af) {
99939beb93cSSam Leffler 	case AF_INET:
100039beb93cSSam Leffler 		os_memset(&serv, 0, sizeof(serv));
100139beb93cSSam Leffler 		serv.sin_family = AF_INET;
100239beb93cSSam Leffler 		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
100339beb93cSSam Leffler 		serv.sin_port = htons(nserv->port);
100439beb93cSSam Leffler 		addr = (struct sockaddr *) &serv;
100539beb93cSSam Leffler 		addrlen = sizeof(serv);
100639beb93cSSam Leffler 		sel_sock = sock;
100739beb93cSSam Leffler 		break;
100839beb93cSSam Leffler #ifdef CONFIG_IPV6
100939beb93cSSam Leffler 	case AF_INET6:
101039beb93cSSam Leffler 		os_memset(&serv6, 0, sizeof(serv6));
101139beb93cSSam Leffler 		serv6.sin6_family = AF_INET6;
101239beb93cSSam Leffler 		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
101339beb93cSSam Leffler 			  sizeof(struct in6_addr));
101439beb93cSSam Leffler 		serv6.sin6_port = htons(nserv->port);
101539beb93cSSam Leffler 		addr = (struct sockaddr *) &serv6;
101639beb93cSSam Leffler 		addrlen = sizeof(serv6);
101739beb93cSSam Leffler 		sel_sock = sock6;
101839beb93cSSam Leffler 		break;
101939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
102039beb93cSSam Leffler 	default:
102139beb93cSSam Leffler 		return -1;
102239beb93cSSam Leffler 	}
102339beb93cSSam Leffler 
102439beb93cSSam Leffler 	if (conf->force_client_addr) {
102539beb93cSSam Leffler 		switch (conf->client_addr.af) {
102639beb93cSSam Leffler 		case AF_INET:
102739beb93cSSam Leffler 			os_memset(&claddr, 0, sizeof(claddr));
102839beb93cSSam Leffler 			claddr.sin_family = AF_INET;
102939beb93cSSam Leffler 			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
103039beb93cSSam Leffler 			claddr.sin_port = htons(0);
103139beb93cSSam Leffler 			cl_addr = (struct sockaddr *) &claddr;
103239beb93cSSam Leffler 			claddrlen = sizeof(claddr);
103339beb93cSSam Leffler 			break;
103439beb93cSSam Leffler #ifdef CONFIG_IPV6
103539beb93cSSam Leffler 		case AF_INET6:
103639beb93cSSam Leffler 			os_memset(&claddr6, 0, sizeof(claddr6));
103739beb93cSSam Leffler 			claddr6.sin6_family = AF_INET6;
103839beb93cSSam Leffler 			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
103939beb93cSSam Leffler 				  sizeof(struct in6_addr));
104039beb93cSSam Leffler 			claddr6.sin6_port = htons(0);
104139beb93cSSam Leffler 			cl_addr = (struct sockaddr *) &claddr6;
104239beb93cSSam Leffler 			claddrlen = sizeof(claddr6);
104339beb93cSSam Leffler 			break;
104439beb93cSSam Leffler #endif /* CONFIG_IPV6 */
104539beb93cSSam Leffler 		default:
104639beb93cSSam Leffler 			return -1;
104739beb93cSSam Leffler 		}
104839beb93cSSam Leffler 
104939beb93cSSam Leffler 		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
105039beb93cSSam Leffler 			perror("bind[radius]");
105139beb93cSSam Leffler 			return -1;
105239beb93cSSam Leffler 		}
105339beb93cSSam Leffler 	}
105439beb93cSSam Leffler 
105539beb93cSSam Leffler 	if (connect(sel_sock, addr, addrlen) < 0) {
105639beb93cSSam Leffler 		perror("connect[radius]");
105739beb93cSSam Leffler 		return -1;
105839beb93cSSam Leffler 	}
105939beb93cSSam Leffler 
106039beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
106139beb93cSSam Leffler 	switch (nserv->addr.af) {
106239beb93cSSam Leffler 	case AF_INET:
106339beb93cSSam Leffler 		claddrlen = sizeof(claddr);
106439beb93cSSam Leffler 		getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
106539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
106639beb93cSSam Leffler 			   inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
106739beb93cSSam Leffler 		break;
106839beb93cSSam Leffler #ifdef CONFIG_IPV6
106939beb93cSSam Leffler 	case AF_INET6: {
107039beb93cSSam Leffler 		claddrlen = sizeof(claddr6);
107139beb93cSSam Leffler 		getsockname(sel_sock, (struct sockaddr *) &claddr6,
107239beb93cSSam Leffler 			    &claddrlen);
107339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
107439beb93cSSam Leffler 			   inet_ntop(AF_INET6, &claddr6.sin6_addr,
107539beb93cSSam Leffler 				     abuf, sizeof(abuf)),
107639beb93cSSam Leffler 			   ntohs(claddr6.sin6_port));
107739beb93cSSam Leffler 		break;
107839beb93cSSam Leffler 	}
107939beb93cSSam Leffler #endif /* CONFIG_IPV6 */
108039beb93cSSam Leffler 	}
108139beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
108239beb93cSSam Leffler 
108339beb93cSSam Leffler 	if (auth)
108439beb93cSSam Leffler 		radius->auth_sock = sel_sock;
108539beb93cSSam Leffler 	else
108639beb93cSSam Leffler 		radius->acct_sock = sel_sock;
108739beb93cSSam Leffler 
108839beb93cSSam Leffler 	return 0;
108939beb93cSSam Leffler }
109039beb93cSSam Leffler 
109139beb93cSSam Leffler 
109239beb93cSSam Leffler static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
109339beb93cSSam Leffler {
109439beb93cSSam Leffler 	struct radius_client_data *radius = eloop_ctx;
109539beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
109639beb93cSSam Leffler 	struct hostapd_radius_server *oserv;
109739beb93cSSam Leffler 
109839beb93cSSam Leffler 	if (radius->auth_sock >= 0 && conf->auth_servers &&
109939beb93cSSam Leffler 	    conf->auth_server != conf->auth_servers) {
110039beb93cSSam Leffler 		oserv = conf->auth_server;
110139beb93cSSam Leffler 		conf->auth_server = conf->auth_servers;
110239beb93cSSam Leffler 		radius_change_server(radius, conf->auth_server, oserv,
110339beb93cSSam Leffler 				     radius->auth_serv_sock,
110439beb93cSSam Leffler 				     radius->auth_serv_sock6, 1);
110539beb93cSSam Leffler 	}
110639beb93cSSam Leffler 
110739beb93cSSam Leffler 	if (radius->acct_sock >= 0 && conf->acct_servers &&
110839beb93cSSam Leffler 	    conf->acct_server != conf->acct_servers) {
110939beb93cSSam Leffler 		oserv = conf->acct_server;
111039beb93cSSam Leffler 		conf->acct_server = conf->acct_servers;
111139beb93cSSam Leffler 		radius_change_server(radius, conf->acct_server, oserv,
111239beb93cSSam Leffler 				     radius->acct_serv_sock,
111339beb93cSSam Leffler 				     radius->acct_serv_sock6, 0);
111439beb93cSSam Leffler 	}
111539beb93cSSam Leffler 
111639beb93cSSam Leffler 	if (conf->retry_primary_interval)
111739beb93cSSam Leffler 		eloop_register_timeout(conf->retry_primary_interval, 0,
111839beb93cSSam Leffler 				       radius_retry_primary_timer, radius,
111939beb93cSSam Leffler 				       NULL);
112039beb93cSSam Leffler }
112139beb93cSSam Leffler 
112239beb93cSSam Leffler 
11233157ba21SRui Paulo static int radius_client_disable_pmtu_discovery(int s)
11243157ba21SRui Paulo {
11253157ba21SRui Paulo 	int r = -1;
11263157ba21SRui Paulo #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
11273157ba21SRui Paulo 	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
11283157ba21SRui Paulo 	int action = IP_PMTUDISC_DONT;
11293157ba21SRui Paulo 	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
11303157ba21SRui Paulo 		       sizeof(action));
11313157ba21SRui Paulo 	if (r == -1)
11323157ba21SRui Paulo 		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
11333157ba21SRui Paulo 			   "%s", strerror(errno));
11343157ba21SRui Paulo #endif
11353157ba21SRui Paulo 	return r;
11363157ba21SRui Paulo }
11373157ba21SRui Paulo 
11383157ba21SRui Paulo 
113939beb93cSSam Leffler static int radius_client_init_auth(struct radius_client_data *radius)
114039beb93cSSam Leffler {
114139beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
114239beb93cSSam Leffler 	int ok = 0;
114339beb93cSSam Leffler 
114439beb93cSSam Leffler 	radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
114539beb93cSSam Leffler 	if (radius->auth_serv_sock < 0)
114639beb93cSSam Leffler 		perror("socket[PF_INET,SOCK_DGRAM]");
11473157ba21SRui Paulo 	else {
11483157ba21SRui Paulo 		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
114939beb93cSSam Leffler 		ok++;
11503157ba21SRui Paulo 	}
115139beb93cSSam Leffler 
115239beb93cSSam Leffler #ifdef CONFIG_IPV6
115339beb93cSSam Leffler 	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
115439beb93cSSam Leffler 	if (radius->auth_serv_sock6 < 0)
115539beb93cSSam Leffler 		perror("socket[PF_INET6,SOCK_DGRAM]");
115639beb93cSSam Leffler 	else
115739beb93cSSam Leffler 		ok++;
115839beb93cSSam Leffler #endif /* CONFIG_IPV6 */
115939beb93cSSam Leffler 
116039beb93cSSam Leffler 	if (ok == 0)
116139beb93cSSam Leffler 		return -1;
116239beb93cSSam Leffler 
116339beb93cSSam Leffler 	radius_change_server(radius, conf->auth_server, NULL,
116439beb93cSSam Leffler 			     radius->auth_serv_sock, radius->auth_serv_sock6,
116539beb93cSSam Leffler 			     1);
116639beb93cSSam Leffler 
116739beb93cSSam Leffler 	if (radius->auth_serv_sock >= 0 &&
116839beb93cSSam Leffler 	    eloop_register_read_sock(radius->auth_serv_sock,
116939beb93cSSam Leffler 				     radius_client_receive, radius,
117039beb93cSSam Leffler 				     (void *) RADIUS_AUTH)) {
117139beb93cSSam Leffler 		printf("Could not register read socket for authentication "
117239beb93cSSam Leffler 		       "server\n");
117339beb93cSSam Leffler 		return -1;
117439beb93cSSam Leffler 	}
117539beb93cSSam Leffler 
117639beb93cSSam Leffler #ifdef CONFIG_IPV6
117739beb93cSSam Leffler 	if (radius->auth_serv_sock6 >= 0 &&
117839beb93cSSam Leffler 	    eloop_register_read_sock(radius->auth_serv_sock6,
117939beb93cSSam Leffler 				     radius_client_receive, radius,
118039beb93cSSam Leffler 				     (void *) RADIUS_AUTH)) {
118139beb93cSSam Leffler 		printf("Could not register read socket for authentication "
118239beb93cSSam Leffler 		       "server\n");
118339beb93cSSam Leffler 		return -1;
118439beb93cSSam Leffler 	}
118539beb93cSSam Leffler #endif /* CONFIG_IPV6 */
118639beb93cSSam Leffler 
118739beb93cSSam Leffler 	return 0;
118839beb93cSSam Leffler }
118939beb93cSSam Leffler 
119039beb93cSSam Leffler 
119139beb93cSSam Leffler static int radius_client_init_acct(struct radius_client_data *radius)
119239beb93cSSam Leffler {
119339beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
119439beb93cSSam Leffler 	int ok = 0;
119539beb93cSSam Leffler 
119639beb93cSSam Leffler 	radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
119739beb93cSSam Leffler 	if (radius->acct_serv_sock < 0)
119839beb93cSSam Leffler 		perror("socket[PF_INET,SOCK_DGRAM]");
11993157ba21SRui Paulo 	else {
12003157ba21SRui Paulo 		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
120139beb93cSSam Leffler 		ok++;
12023157ba21SRui Paulo 	}
120339beb93cSSam Leffler 
120439beb93cSSam Leffler #ifdef CONFIG_IPV6
120539beb93cSSam Leffler 	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
120639beb93cSSam Leffler 	if (radius->acct_serv_sock6 < 0)
120739beb93cSSam Leffler 		perror("socket[PF_INET6,SOCK_DGRAM]");
120839beb93cSSam Leffler 	else
120939beb93cSSam Leffler 		ok++;
121039beb93cSSam Leffler #endif /* CONFIG_IPV6 */
121139beb93cSSam Leffler 
121239beb93cSSam Leffler 	if (ok == 0)
121339beb93cSSam Leffler 		return -1;
121439beb93cSSam Leffler 
121539beb93cSSam Leffler 	radius_change_server(radius, conf->acct_server, NULL,
121639beb93cSSam Leffler 			     radius->acct_serv_sock, radius->acct_serv_sock6,
121739beb93cSSam Leffler 			     0);
121839beb93cSSam Leffler 
121939beb93cSSam Leffler 	if (radius->acct_serv_sock >= 0 &&
122039beb93cSSam Leffler 	    eloop_register_read_sock(radius->acct_serv_sock,
122139beb93cSSam Leffler 				     radius_client_receive, radius,
122239beb93cSSam Leffler 				     (void *) RADIUS_ACCT)) {
122339beb93cSSam Leffler 		printf("Could not register read socket for accounting "
122439beb93cSSam Leffler 		       "server\n");
122539beb93cSSam Leffler 		return -1;
122639beb93cSSam Leffler 	}
122739beb93cSSam Leffler 
122839beb93cSSam Leffler #ifdef CONFIG_IPV6
122939beb93cSSam Leffler 	if (radius->acct_serv_sock6 >= 0 &&
123039beb93cSSam Leffler 	    eloop_register_read_sock(radius->acct_serv_sock6,
123139beb93cSSam Leffler 				     radius_client_receive, radius,
123239beb93cSSam Leffler 				     (void *) RADIUS_ACCT)) {
123339beb93cSSam Leffler 		printf("Could not register read socket for accounting "
123439beb93cSSam Leffler 		       "server\n");
123539beb93cSSam Leffler 		return -1;
123639beb93cSSam Leffler 	}
123739beb93cSSam Leffler #endif /* CONFIG_IPV6 */
123839beb93cSSam Leffler 
123939beb93cSSam Leffler 	return 0;
124039beb93cSSam Leffler }
124139beb93cSSam Leffler 
124239beb93cSSam Leffler 
1243*e28a4053SRui Paulo /**
1244*e28a4053SRui Paulo  * radius_client_init - Initialize RADIUS client
1245*e28a4053SRui Paulo  * @ctx: Callback context to be used in hostapd_logger() calls
1246*e28a4053SRui Paulo  * @conf: RADIUS client configuration (RADIUS servers)
1247*e28a4053SRui Paulo  * Returns: Pointer to private RADIUS client context or %NULL on failure
1248*e28a4053SRui Paulo  *
1249*e28a4053SRui Paulo  * The caller is responsible for keeping the configuration data available for
1250*e28a4053SRui Paulo  * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1251*e28a4053SRui Paulo  * called for the returned context pointer.
1252*e28a4053SRui Paulo  */
125339beb93cSSam Leffler struct radius_client_data *
125439beb93cSSam Leffler radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
125539beb93cSSam Leffler {
125639beb93cSSam Leffler 	struct radius_client_data *radius;
125739beb93cSSam Leffler 
125839beb93cSSam Leffler 	radius = os_zalloc(sizeof(struct radius_client_data));
125939beb93cSSam Leffler 	if (radius == NULL)
126039beb93cSSam Leffler 		return NULL;
126139beb93cSSam Leffler 
126239beb93cSSam Leffler 	radius->ctx = ctx;
126339beb93cSSam Leffler 	radius->conf = conf;
126439beb93cSSam Leffler 	radius->auth_serv_sock = radius->acct_serv_sock =
126539beb93cSSam Leffler 		radius->auth_serv_sock6 = radius->acct_serv_sock6 =
126639beb93cSSam Leffler 		radius->auth_sock = radius->acct_sock = -1;
126739beb93cSSam Leffler 
126839beb93cSSam Leffler 	if (conf->auth_server && radius_client_init_auth(radius)) {
126939beb93cSSam Leffler 		radius_client_deinit(radius);
127039beb93cSSam Leffler 		return NULL;
127139beb93cSSam Leffler 	}
127239beb93cSSam Leffler 
127339beb93cSSam Leffler 	if (conf->acct_server && radius_client_init_acct(radius)) {
127439beb93cSSam Leffler 		radius_client_deinit(radius);
127539beb93cSSam Leffler 		return NULL;
127639beb93cSSam Leffler 	}
127739beb93cSSam Leffler 
127839beb93cSSam Leffler 	if (conf->retry_primary_interval)
127939beb93cSSam Leffler 		eloop_register_timeout(conf->retry_primary_interval, 0,
128039beb93cSSam Leffler 				       radius_retry_primary_timer, radius,
128139beb93cSSam Leffler 				       NULL);
128239beb93cSSam Leffler 
128339beb93cSSam Leffler 	return radius;
128439beb93cSSam Leffler }
128539beb93cSSam Leffler 
128639beb93cSSam Leffler 
1287*e28a4053SRui Paulo /**
1288*e28a4053SRui Paulo  * radius_client_deinit - Deinitialize RADIUS client
1289*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1290*e28a4053SRui Paulo  */
129139beb93cSSam Leffler void radius_client_deinit(struct radius_client_data *radius)
129239beb93cSSam Leffler {
129339beb93cSSam Leffler 	if (!radius)
129439beb93cSSam Leffler 		return;
129539beb93cSSam Leffler 
129639beb93cSSam Leffler 	if (radius->auth_serv_sock >= 0)
129739beb93cSSam Leffler 		eloop_unregister_read_sock(radius->auth_serv_sock);
129839beb93cSSam Leffler 	if (radius->acct_serv_sock >= 0)
129939beb93cSSam Leffler 		eloop_unregister_read_sock(radius->acct_serv_sock);
13003157ba21SRui Paulo #ifdef CONFIG_IPV6
13013157ba21SRui Paulo 	if (radius->auth_serv_sock6 >= 0)
13023157ba21SRui Paulo 		eloop_unregister_read_sock(radius->auth_serv_sock6);
13033157ba21SRui Paulo 	if (radius->acct_serv_sock6 >= 0)
13043157ba21SRui Paulo 		eloop_unregister_read_sock(radius->acct_serv_sock6);
13053157ba21SRui Paulo #endif /* CONFIG_IPV6 */
130639beb93cSSam Leffler 
130739beb93cSSam Leffler 	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
130839beb93cSSam Leffler 
130939beb93cSSam Leffler 	radius_client_flush(radius, 0);
131039beb93cSSam Leffler 	os_free(radius->auth_handlers);
131139beb93cSSam Leffler 	os_free(radius->acct_handlers);
131239beb93cSSam Leffler 	os_free(radius);
131339beb93cSSam Leffler }
131439beb93cSSam Leffler 
131539beb93cSSam Leffler 
1316*e28a4053SRui Paulo /**
1317*e28a4053SRui Paulo  * radius_client_flush_auth - Flush pending RADIUS messages for an address
1318*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1319*e28a4053SRui Paulo  * @addr: MAC address of the related device
1320*e28a4053SRui Paulo  *
1321*e28a4053SRui Paulo  * This function can be used to remove pending RADIUS authentication messages
1322*e28a4053SRui Paulo  * that are related to a specific device. The addr parameter is matched with
1323*e28a4053SRui Paulo  * the one used in radius_client_send() call that was used to transmit the
1324*e28a4053SRui Paulo  * authentication request.
1325*e28a4053SRui Paulo  */
1326*e28a4053SRui Paulo void radius_client_flush_auth(struct radius_client_data *radius,
1327*e28a4053SRui Paulo 			      const u8 *addr)
132839beb93cSSam Leffler {
132939beb93cSSam Leffler 	struct radius_msg_list *entry, *prev, *tmp;
133039beb93cSSam Leffler 
133139beb93cSSam Leffler 	prev = NULL;
133239beb93cSSam Leffler 	entry = radius->msgs;
133339beb93cSSam Leffler 	while (entry) {
133439beb93cSSam Leffler 		if (entry->msg_type == RADIUS_AUTH &&
133539beb93cSSam Leffler 		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
133639beb93cSSam Leffler 			hostapd_logger(radius->ctx, addr,
133739beb93cSSam Leffler 				       HOSTAPD_MODULE_RADIUS,
133839beb93cSSam Leffler 				       HOSTAPD_LEVEL_DEBUG,
133939beb93cSSam Leffler 				       "Removing pending RADIUS authentication"
134039beb93cSSam Leffler 				       " message for removed client");
134139beb93cSSam Leffler 
134239beb93cSSam Leffler 			if (prev)
134339beb93cSSam Leffler 				prev->next = entry->next;
134439beb93cSSam Leffler 			else
134539beb93cSSam Leffler 				radius->msgs = entry->next;
134639beb93cSSam Leffler 
134739beb93cSSam Leffler 			tmp = entry;
134839beb93cSSam Leffler 			entry = entry->next;
134939beb93cSSam Leffler 			radius_client_msg_free(tmp);
135039beb93cSSam Leffler 			radius->num_msgs--;
135139beb93cSSam Leffler 			continue;
135239beb93cSSam Leffler 		}
135339beb93cSSam Leffler 
135439beb93cSSam Leffler 		prev = entry;
135539beb93cSSam Leffler 		entry = entry->next;
135639beb93cSSam Leffler 	}
135739beb93cSSam Leffler }
135839beb93cSSam Leffler 
135939beb93cSSam Leffler 
136039beb93cSSam Leffler static int radius_client_dump_auth_server(char *buf, size_t buflen,
136139beb93cSSam Leffler 					  struct hostapd_radius_server *serv,
136239beb93cSSam Leffler 					  struct radius_client_data *cli)
136339beb93cSSam Leffler {
136439beb93cSSam Leffler 	int pending = 0;
136539beb93cSSam Leffler 	struct radius_msg_list *msg;
136639beb93cSSam Leffler 	char abuf[50];
136739beb93cSSam Leffler 
136839beb93cSSam Leffler 	if (cli) {
136939beb93cSSam Leffler 		for (msg = cli->msgs; msg; msg = msg->next) {
137039beb93cSSam Leffler 			if (msg->msg_type == RADIUS_AUTH)
137139beb93cSSam Leffler 				pending++;
137239beb93cSSam Leffler 		}
137339beb93cSSam Leffler 	}
137439beb93cSSam Leffler 
137539beb93cSSam Leffler 	return os_snprintf(buf, buflen,
137639beb93cSSam Leffler 			   "radiusAuthServerIndex=%d\n"
137739beb93cSSam Leffler 			   "radiusAuthServerAddress=%s\n"
137839beb93cSSam Leffler 			   "radiusAuthClientServerPortNumber=%d\n"
137939beb93cSSam Leffler 			   "radiusAuthClientRoundTripTime=%d\n"
138039beb93cSSam Leffler 			   "radiusAuthClientAccessRequests=%u\n"
138139beb93cSSam Leffler 			   "radiusAuthClientAccessRetransmissions=%u\n"
138239beb93cSSam Leffler 			   "radiusAuthClientAccessAccepts=%u\n"
138339beb93cSSam Leffler 			   "radiusAuthClientAccessRejects=%u\n"
138439beb93cSSam Leffler 			   "radiusAuthClientAccessChallenges=%u\n"
138539beb93cSSam Leffler 			   "radiusAuthClientMalformedAccessResponses=%u\n"
138639beb93cSSam Leffler 			   "radiusAuthClientBadAuthenticators=%u\n"
138739beb93cSSam Leffler 			   "radiusAuthClientPendingRequests=%u\n"
138839beb93cSSam Leffler 			   "radiusAuthClientTimeouts=%u\n"
138939beb93cSSam Leffler 			   "radiusAuthClientUnknownTypes=%u\n"
139039beb93cSSam Leffler 			   "radiusAuthClientPacketsDropped=%u\n",
139139beb93cSSam Leffler 			   serv->index,
139239beb93cSSam Leffler 			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
139339beb93cSSam Leffler 			   serv->port,
139439beb93cSSam Leffler 			   serv->round_trip_time,
139539beb93cSSam Leffler 			   serv->requests,
139639beb93cSSam Leffler 			   serv->retransmissions,
139739beb93cSSam Leffler 			   serv->access_accepts,
139839beb93cSSam Leffler 			   serv->access_rejects,
139939beb93cSSam Leffler 			   serv->access_challenges,
140039beb93cSSam Leffler 			   serv->malformed_responses,
140139beb93cSSam Leffler 			   serv->bad_authenticators,
140239beb93cSSam Leffler 			   pending,
140339beb93cSSam Leffler 			   serv->timeouts,
140439beb93cSSam Leffler 			   serv->unknown_types,
140539beb93cSSam Leffler 			   serv->packets_dropped);
140639beb93cSSam Leffler }
140739beb93cSSam Leffler 
140839beb93cSSam Leffler 
140939beb93cSSam Leffler static int radius_client_dump_acct_server(char *buf, size_t buflen,
141039beb93cSSam Leffler 					  struct hostapd_radius_server *serv,
141139beb93cSSam Leffler 					  struct radius_client_data *cli)
141239beb93cSSam Leffler {
141339beb93cSSam Leffler 	int pending = 0;
141439beb93cSSam Leffler 	struct radius_msg_list *msg;
141539beb93cSSam Leffler 	char abuf[50];
141639beb93cSSam Leffler 
141739beb93cSSam Leffler 	if (cli) {
141839beb93cSSam Leffler 		for (msg = cli->msgs; msg; msg = msg->next) {
141939beb93cSSam Leffler 			if (msg->msg_type == RADIUS_ACCT ||
142039beb93cSSam Leffler 			    msg->msg_type == RADIUS_ACCT_INTERIM)
142139beb93cSSam Leffler 				pending++;
142239beb93cSSam Leffler 		}
142339beb93cSSam Leffler 	}
142439beb93cSSam Leffler 
142539beb93cSSam Leffler 	return os_snprintf(buf, buflen,
142639beb93cSSam Leffler 			   "radiusAccServerIndex=%d\n"
142739beb93cSSam Leffler 			   "radiusAccServerAddress=%s\n"
142839beb93cSSam Leffler 			   "radiusAccClientServerPortNumber=%d\n"
142939beb93cSSam Leffler 			   "radiusAccClientRoundTripTime=%d\n"
143039beb93cSSam Leffler 			   "radiusAccClientRequests=%u\n"
143139beb93cSSam Leffler 			   "radiusAccClientRetransmissions=%u\n"
143239beb93cSSam Leffler 			   "radiusAccClientResponses=%u\n"
143339beb93cSSam Leffler 			   "radiusAccClientMalformedResponses=%u\n"
143439beb93cSSam Leffler 			   "radiusAccClientBadAuthenticators=%u\n"
143539beb93cSSam Leffler 			   "radiusAccClientPendingRequests=%u\n"
143639beb93cSSam Leffler 			   "radiusAccClientTimeouts=%u\n"
143739beb93cSSam Leffler 			   "radiusAccClientUnknownTypes=%u\n"
143839beb93cSSam Leffler 			   "radiusAccClientPacketsDropped=%u\n",
143939beb93cSSam Leffler 			   serv->index,
144039beb93cSSam Leffler 			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
144139beb93cSSam Leffler 			   serv->port,
144239beb93cSSam Leffler 			   serv->round_trip_time,
144339beb93cSSam Leffler 			   serv->requests,
144439beb93cSSam Leffler 			   serv->retransmissions,
144539beb93cSSam Leffler 			   serv->responses,
144639beb93cSSam Leffler 			   serv->malformed_responses,
144739beb93cSSam Leffler 			   serv->bad_authenticators,
144839beb93cSSam Leffler 			   pending,
144939beb93cSSam Leffler 			   serv->timeouts,
145039beb93cSSam Leffler 			   serv->unknown_types,
145139beb93cSSam Leffler 			   serv->packets_dropped);
145239beb93cSSam Leffler }
145339beb93cSSam Leffler 
145439beb93cSSam Leffler 
1455*e28a4053SRui Paulo /**
1456*e28a4053SRui Paulo  * radius_client_get_mib - Get RADIUS client MIB information
1457*e28a4053SRui Paulo  * @radius: RADIUS client context from radius_client_init()
1458*e28a4053SRui Paulo  * @buf: Buffer for returning MIB data in text format
1459*e28a4053SRui Paulo  * @buflen: Maximum buf length in octets
1460*e28a4053SRui Paulo  * Returns: Number of octets written into the buffer
1461*e28a4053SRui Paulo  */
146239beb93cSSam Leffler int radius_client_get_mib(struct radius_client_data *radius, char *buf,
146339beb93cSSam Leffler 			  size_t buflen)
146439beb93cSSam Leffler {
146539beb93cSSam Leffler 	struct hostapd_radius_servers *conf = radius->conf;
146639beb93cSSam Leffler 	int i;
146739beb93cSSam Leffler 	struct hostapd_radius_server *serv;
146839beb93cSSam Leffler 	int count = 0;
146939beb93cSSam Leffler 
147039beb93cSSam Leffler 	if (conf->auth_servers) {
147139beb93cSSam Leffler 		for (i = 0; i < conf->num_auth_servers; i++) {
147239beb93cSSam Leffler 			serv = &conf->auth_servers[i];
147339beb93cSSam Leffler 			count += radius_client_dump_auth_server(
147439beb93cSSam Leffler 				buf + count, buflen - count, serv,
147539beb93cSSam Leffler 				serv == conf->auth_server ?
147639beb93cSSam Leffler 				radius : NULL);
147739beb93cSSam Leffler 		}
147839beb93cSSam Leffler 	}
147939beb93cSSam Leffler 
148039beb93cSSam Leffler 	if (conf->acct_servers) {
148139beb93cSSam Leffler 		for (i = 0; i < conf->num_acct_servers; i++) {
148239beb93cSSam Leffler 			serv = &conf->acct_servers[i];
148339beb93cSSam Leffler 			count += radius_client_dump_acct_server(
148439beb93cSSam Leffler 				buf + count, buflen - count, serv,
148539beb93cSSam Leffler 				serv == conf->acct_server ?
148639beb93cSSam Leffler 				radius : NULL);
148739beb93cSSam Leffler 		}
148839beb93cSSam Leffler 	}
148939beb93cSSam Leffler 
149039beb93cSSam Leffler 	return count;
149139beb93cSSam Leffler }
1492