1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * RADIUS client
3e5b75505Sopenharmony_ci * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci */
8e5b75505Sopenharmony_ci
9e5b75505Sopenharmony_ci#include "includes.h"
10e5b75505Sopenharmony_ci
11e5b75505Sopenharmony_ci#include "common.h"
12e5b75505Sopenharmony_ci#include "radius.h"
13e5b75505Sopenharmony_ci#include "radius_client.h"
14e5b75505Sopenharmony_ci#include "eloop.h"
15e5b75505Sopenharmony_ci
16e5b75505Sopenharmony_ci/* Defaults for RADIUS retransmit values (exponential backoff) */
17e5b75505Sopenharmony_ci
18e5b75505Sopenharmony_ci/**
19e5b75505Sopenharmony_ci * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
20e5b75505Sopenharmony_ci */
21e5b75505Sopenharmony_ci#define RADIUS_CLIENT_FIRST_WAIT 3
22e5b75505Sopenharmony_ci
23e5b75505Sopenharmony_ci/**
24e5b75505Sopenharmony_ci * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
25e5b75505Sopenharmony_ci */
26e5b75505Sopenharmony_ci#define RADIUS_CLIENT_MAX_WAIT 120
27e5b75505Sopenharmony_ci
28e5b75505Sopenharmony_ci/**
29e5b75505Sopenharmony_ci * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries
30e5b75505Sopenharmony_ci *
31e5b75505Sopenharmony_ci * Maximum number of server failovers before the entry is removed from
32e5b75505Sopenharmony_ci * retransmit list.
33e5b75505Sopenharmony_ci */
34e5b75505Sopenharmony_ci#define RADIUS_CLIENT_MAX_FAILOVER 3
35e5b75505Sopenharmony_ci
36e5b75505Sopenharmony_ci/**
37e5b75505Sopenharmony_ci * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
38e5b75505Sopenharmony_ci *
39e5b75505Sopenharmony_ci * Maximum number of entries in retransmit list (oldest entries will be
40e5b75505Sopenharmony_ci * removed, if this limit is exceeded).
41e5b75505Sopenharmony_ci */
42e5b75505Sopenharmony_ci#define RADIUS_CLIENT_MAX_ENTRIES 30
43e5b75505Sopenharmony_ci
44e5b75505Sopenharmony_ci/**
45e5b75505Sopenharmony_ci * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
46e5b75505Sopenharmony_ci *
47e5b75505Sopenharmony_ci * The number of failed retry attempts after which the RADIUS server will be
48e5b75505Sopenharmony_ci * changed (if one of more backup servers are configured).
49e5b75505Sopenharmony_ci */
50e5b75505Sopenharmony_ci#define RADIUS_CLIENT_NUM_FAILOVER 4
51e5b75505Sopenharmony_ci
52e5b75505Sopenharmony_ci
53e5b75505Sopenharmony_ci/**
54e5b75505Sopenharmony_ci * struct radius_rx_handler - RADIUS client RX handler
55e5b75505Sopenharmony_ci *
56e5b75505Sopenharmony_ci * This data structure is used internally inside the RADIUS client module to
57e5b75505Sopenharmony_ci * store registered RX handlers. These handlers are registered by calls to
58e5b75505Sopenharmony_ci * radius_client_register() and unregistered when the RADIUS client is
59e5b75505Sopenharmony_ci * deinitialized with a call to radius_client_deinit().
60e5b75505Sopenharmony_ci */
61e5b75505Sopenharmony_cistruct radius_rx_handler {
62e5b75505Sopenharmony_ci	/**
63e5b75505Sopenharmony_ci	 * handler - Received RADIUS message handler
64e5b75505Sopenharmony_ci	 */
65e5b75505Sopenharmony_ci	RadiusRxResult (*handler)(struct radius_msg *msg,
66e5b75505Sopenharmony_ci				  struct radius_msg *req,
67e5b75505Sopenharmony_ci				  const u8 *shared_secret,
68e5b75505Sopenharmony_ci				  size_t shared_secret_len,
69e5b75505Sopenharmony_ci				  void *data);
70e5b75505Sopenharmony_ci
71e5b75505Sopenharmony_ci	/**
72e5b75505Sopenharmony_ci	 * data - Context data for the handler
73e5b75505Sopenharmony_ci	 */
74e5b75505Sopenharmony_ci	void *data;
75e5b75505Sopenharmony_ci};
76e5b75505Sopenharmony_ci
77e5b75505Sopenharmony_ci
78e5b75505Sopenharmony_ci/**
79e5b75505Sopenharmony_ci * struct radius_msg_list - RADIUS client message retransmit list
80e5b75505Sopenharmony_ci *
81e5b75505Sopenharmony_ci * This data structure is used internally inside the RADIUS client module to
82e5b75505Sopenharmony_ci * store pending RADIUS requests that may still need to be retransmitted.
83e5b75505Sopenharmony_ci */
84e5b75505Sopenharmony_cistruct radius_msg_list {
85e5b75505Sopenharmony_ci	/**
86e5b75505Sopenharmony_ci	 * addr - STA/client address
87e5b75505Sopenharmony_ci	 *
88e5b75505Sopenharmony_ci	 * This is used to find RADIUS messages for the same STA.
89e5b75505Sopenharmony_ci	 */
90e5b75505Sopenharmony_ci	u8 addr[ETH_ALEN];
91e5b75505Sopenharmony_ci
92e5b75505Sopenharmony_ci	/**
93e5b75505Sopenharmony_ci	 * msg - RADIUS message
94e5b75505Sopenharmony_ci	 */
95e5b75505Sopenharmony_ci	struct radius_msg *msg;
96e5b75505Sopenharmony_ci
97e5b75505Sopenharmony_ci	/**
98e5b75505Sopenharmony_ci	 * msg_type - Message type
99e5b75505Sopenharmony_ci	 */
100e5b75505Sopenharmony_ci	RadiusType msg_type;
101e5b75505Sopenharmony_ci
102e5b75505Sopenharmony_ci	/**
103e5b75505Sopenharmony_ci	 * first_try - Time of the first transmission attempt
104e5b75505Sopenharmony_ci	 */
105e5b75505Sopenharmony_ci	os_time_t first_try;
106e5b75505Sopenharmony_ci
107e5b75505Sopenharmony_ci	/**
108e5b75505Sopenharmony_ci	 * next_try - Time for the next transmission attempt
109e5b75505Sopenharmony_ci	 */
110e5b75505Sopenharmony_ci	os_time_t next_try;
111e5b75505Sopenharmony_ci
112e5b75505Sopenharmony_ci	/**
113e5b75505Sopenharmony_ci	 * attempts - Number of transmission attempts for one server
114e5b75505Sopenharmony_ci	 */
115e5b75505Sopenharmony_ci	int attempts;
116e5b75505Sopenharmony_ci
117e5b75505Sopenharmony_ci	/**
118e5b75505Sopenharmony_ci	 * accu_attempts - Number of accumulated attempts
119e5b75505Sopenharmony_ci	 */
120e5b75505Sopenharmony_ci	int accu_attempts;
121e5b75505Sopenharmony_ci
122e5b75505Sopenharmony_ci	/**
123e5b75505Sopenharmony_ci	 * next_wait - Next retransmission wait time in seconds
124e5b75505Sopenharmony_ci	 */
125e5b75505Sopenharmony_ci	int next_wait;
126e5b75505Sopenharmony_ci
127e5b75505Sopenharmony_ci	/**
128e5b75505Sopenharmony_ci	 * last_attempt - Time of the last transmission attempt
129e5b75505Sopenharmony_ci	 */
130e5b75505Sopenharmony_ci	struct os_reltime last_attempt;
131e5b75505Sopenharmony_ci
132e5b75505Sopenharmony_ci	/**
133e5b75505Sopenharmony_ci	 * shared_secret - Shared secret with the target RADIUS server
134e5b75505Sopenharmony_ci	 */
135e5b75505Sopenharmony_ci	const u8 *shared_secret;
136e5b75505Sopenharmony_ci
137e5b75505Sopenharmony_ci	/**
138e5b75505Sopenharmony_ci	 * shared_secret_len - shared_secret length in octets
139e5b75505Sopenharmony_ci	 */
140e5b75505Sopenharmony_ci	size_t shared_secret_len;
141e5b75505Sopenharmony_ci
142e5b75505Sopenharmony_ci	/* TODO: server config with failover to backup server(s) */
143e5b75505Sopenharmony_ci
144e5b75505Sopenharmony_ci	/**
145e5b75505Sopenharmony_ci	 * next - Next message in the list
146e5b75505Sopenharmony_ci	 */
147e5b75505Sopenharmony_ci	struct radius_msg_list *next;
148e5b75505Sopenharmony_ci};
149e5b75505Sopenharmony_ci
150e5b75505Sopenharmony_ci
151e5b75505Sopenharmony_ci/**
152e5b75505Sopenharmony_ci * struct radius_client_data - Internal RADIUS client data
153e5b75505Sopenharmony_ci *
154e5b75505Sopenharmony_ci * This data structure is used internally inside the RADIUS client module.
155e5b75505Sopenharmony_ci * External users allocate this by calling radius_client_init() and free it by
156e5b75505Sopenharmony_ci * calling radius_client_deinit(). The pointer to this opaque data is used in
157e5b75505Sopenharmony_ci * calls to other functions as an identifier for the RADIUS client instance.
158e5b75505Sopenharmony_ci */
159e5b75505Sopenharmony_cistruct radius_client_data {
160e5b75505Sopenharmony_ci	/**
161e5b75505Sopenharmony_ci	 * ctx - Context pointer for hostapd_logger() callbacks
162e5b75505Sopenharmony_ci	 */
163e5b75505Sopenharmony_ci	void *ctx;
164e5b75505Sopenharmony_ci
165e5b75505Sopenharmony_ci	/**
166e5b75505Sopenharmony_ci	 * conf - RADIUS client configuration (list of RADIUS servers to use)
167e5b75505Sopenharmony_ci	 */
168e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf;
169e5b75505Sopenharmony_ci
170e5b75505Sopenharmony_ci	/**
171e5b75505Sopenharmony_ci	 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
172e5b75505Sopenharmony_ci	 */
173e5b75505Sopenharmony_ci	int auth_serv_sock;
174e5b75505Sopenharmony_ci
175e5b75505Sopenharmony_ci	/**
176e5b75505Sopenharmony_ci	 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
177e5b75505Sopenharmony_ci	 */
178e5b75505Sopenharmony_ci	int acct_serv_sock;
179e5b75505Sopenharmony_ci
180e5b75505Sopenharmony_ci	/**
181e5b75505Sopenharmony_ci	 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
182e5b75505Sopenharmony_ci	 */
183e5b75505Sopenharmony_ci	int auth_serv_sock6;
184e5b75505Sopenharmony_ci
185e5b75505Sopenharmony_ci	/**
186e5b75505Sopenharmony_ci	 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
187e5b75505Sopenharmony_ci	 */
188e5b75505Sopenharmony_ci	int acct_serv_sock6;
189e5b75505Sopenharmony_ci
190e5b75505Sopenharmony_ci	/**
191e5b75505Sopenharmony_ci	 * auth_sock - Currently used socket for RADIUS authentication server
192e5b75505Sopenharmony_ci	 */
193e5b75505Sopenharmony_ci	int auth_sock;
194e5b75505Sopenharmony_ci
195e5b75505Sopenharmony_ci	/**
196e5b75505Sopenharmony_ci	 * acct_sock - Currently used socket for RADIUS accounting server
197e5b75505Sopenharmony_ci	 */
198e5b75505Sopenharmony_ci	int acct_sock;
199e5b75505Sopenharmony_ci
200e5b75505Sopenharmony_ci	/**
201e5b75505Sopenharmony_ci	 * auth_handlers - Authentication message handlers
202e5b75505Sopenharmony_ci	 */
203e5b75505Sopenharmony_ci	struct radius_rx_handler *auth_handlers;
204e5b75505Sopenharmony_ci
205e5b75505Sopenharmony_ci	/**
206e5b75505Sopenharmony_ci	 * num_auth_handlers - Number of handlers in auth_handlers
207e5b75505Sopenharmony_ci	 */
208e5b75505Sopenharmony_ci	size_t num_auth_handlers;
209e5b75505Sopenharmony_ci
210e5b75505Sopenharmony_ci	/**
211e5b75505Sopenharmony_ci	 * acct_handlers - Accounting message handlers
212e5b75505Sopenharmony_ci	 */
213e5b75505Sopenharmony_ci	struct radius_rx_handler *acct_handlers;
214e5b75505Sopenharmony_ci
215e5b75505Sopenharmony_ci	/**
216e5b75505Sopenharmony_ci	 * num_acct_handlers - Number of handlers in acct_handlers
217e5b75505Sopenharmony_ci	 */
218e5b75505Sopenharmony_ci	size_t num_acct_handlers;
219e5b75505Sopenharmony_ci
220e5b75505Sopenharmony_ci	/**
221e5b75505Sopenharmony_ci	 * msgs - Pending outgoing RADIUS messages
222e5b75505Sopenharmony_ci	 */
223e5b75505Sopenharmony_ci	struct radius_msg_list *msgs;
224e5b75505Sopenharmony_ci
225e5b75505Sopenharmony_ci	/**
226e5b75505Sopenharmony_ci	 * num_msgs - Number of pending messages in the msgs list
227e5b75505Sopenharmony_ci	 */
228e5b75505Sopenharmony_ci	size_t num_msgs;
229e5b75505Sopenharmony_ci
230e5b75505Sopenharmony_ci	/**
231e5b75505Sopenharmony_ci	 * next_radius_identifier - Next RADIUS message identifier to use
232e5b75505Sopenharmony_ci	 */
233e5b75505Sopenharmony_ci	u8 next_radius_identifier;
234e5b75505Sopenharmony_ci
235e5b75505Sopenharmony_ci	/**
236e5b75505Sopenharmony_ci	 * interim_error_cb - Interim accounting error callback
237e5b75505Sopenharmony_ci	 */
238e5b75505Sopenharmony_ci	void (*interim_error_cb)(const u8 *addr, void *ctx);
239e5b75505Sopenharmony_ci
240e5b75505Sopenharmony_ci	/**
241e5b75505Sopenharmony_ci	 * interim_error_cb_ctx - interim_error_cb() context data
242e5b75505Sopenharmony_ci	 */
243e5b75505Sopenharmony_ci	void *interim_error_cb_ctx;
244e5b75505Sopenharmony_ci};
245e5b75505Sopenharmony_ci
246e5b75505Sopenharmony_ci
247e5b75505Sopenharmony_cistatic int
248e5b75505Sopenharmony_ciradius_change_server(struct radius_client_data *radius,
249e5b75505Sopenharmony_ci		     struct hostapd_radius_server *nserv,
250e5b75505Sopenharmony_ci		     struct hostapd_radius_server *oserv,
251e5b75505Sopenharmony_ci		     int sock, int sock6, int auth);
252e5b75505Sopenharmony_cistatic int radius_client_init_acct(struct radius_client_data *radius);
253e5b75505Sopenharmony_cistatic int radius_client_init_auth(struct radius_client_data *radius);
254e5b75505Sopenharmony_cistatic void radius_client_auth_failover(struct radius_client_data *radius);
255e5b75505Sopenharmony_cistatic void radius_client_acct_failover(struct radius_client_data *radius);
256e5b75505Sopenharmony_ci
257e5b75505Sopenharmony_ci
258e5b75505Sopenharmony_cistatic void radius_client_msg_free(struct radius_msg_list *req)
259e5b75505Sopenharmony_ci{
260e5b75505Sopenharmony_ci	radius_msg_free(req->msg);
261e5b75505Sopenharmony_ci	os_free(req);
262e5b75505Sopenharmony_ci}
263e5b75505Sopenharmony_ci
264e5b75505Sopenharmony_ci
265e5b75505Sopenharmony_ci/**
266e5b75505Sopenharmony_ci * radius_client_register - Register a RADIUS client RX handler
267e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
268e5b75505Sopenharmony_ci * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
269e5b75505Sopenharmony_ci * @handler: Handler for received RADIUS messages
270e5b75505Sopenharmony_ci * @data: Context pointer for handler callbacks
271e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
272e5b75505Sopenharmony_ci *
273e5b75505Sopenharmony_ci * This function is used to register a handler for processing received RADIUS
274e5b75505Sopenharmony_ci * authentication and accounting messages. The handler() callback function will
275e5b75505Sopenharmony_ci * be called whenever a RADIUS message is received from the active server.
276e5b75505Sopenharmony_ci *
277e5b75505Sopenharmony_ci * There can be multiple registered RADIUS message handlers. The handlers will
278e5b75505Sopenharmony_ci * be called in order until one of them indicates that it has processed or
279e5b75505Sopenharmony_ci * queued the message.
280e5b75505Sopenharmony_ci */
281e5b75505Sopenharmony_ciint radius_client_register(struct radius_client_data *radius,
282e5b75505Sopenharmony_ci			   RadiusType msg_type,
283e5b75505Sopenharmony_ci			   RadiusRxResult (*handler)(struct radius_msg *msg,
284e5b75505Sopenharmony_ci						     struct radius_msg *req,
285e5b75505Sopenharmony_ci						     const u8 *shared_secret,
286e5b75505Sopenharmony_ci						     size_t shared_secret_len,
287e5b75505Sopenharmony_ci						     void *data),
288e5b75505Sopenharmony_ci			   void *data)
289e5b75505Sopenharmony_ci{
290e5b75505Sopenharmony_ci	struct radius_rx_handler **handlers, *newh;
291e5b75505Sopenharmony_ci	size_t *num;
292e5b75505Sopenharmony_ci
293e5b75505Sopenharmony_ci	if (msg_type == RADIUS_ACCT) {
294e5b75505Sopenharmony_ci		handlers = &radius->acct_handlers;
295e5b75505Sopenharmony_ci		num = &radius->num_acct_handlers;
296e5b75505Sopenharmony_ci	} else {
297e5b75505Sopenharmony_ci		handlers = &radius->auth_handlers;
298e5b75505Sopenharmony_ci		num = &radius->num_auth_handlers;
299e5b75505Sopenharmony_ci	}
300e5b75505Sopenharmony_ci
301e5b75505Sopenharmony_ci	newh = os_realloc_array(*handlers, *num + 1,
302e5b75505Sopenharmony_ci				sizeof(struct radius_rx_handler));
303e5b75505Sopenharmony_ci	if (newh == NULL)
304e5b75505Sopenharmony_ci		return -1;
305e5b75505Sopenharmony_ci
306e5b75505Sopenharmony_ci	newh[*num].handler = handler;
307e5b75505Sopenharmony_ci	newh[*num].data = data;
308e5b75505Sopenharmony_ci	(*num)++;
309e5b75505Sopenharmony_ci	*handlers = newh;
310e5b75505Sopenharmony_ci
311e5b75505Sopenharmony_ci	return 0;
312e5b75505Sopenharmony_ci}
313e5b75505Sopenharmony_ci
314e5b75505Sopenharmony_ci
315e5b75505Sopenharmony_ci/**
316e5b75505Sopenharmony_ci * radius_client_set_interim_erro_cb - Register an interim acct error callback
317e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
318e5b75505Sopenharmony_ci * @addr: Station address from the failed message
319e5b75505Sopenharmony_ci * @cb: Handler for interim accounting errors
320e5b75505Sopenharmony_ci * @ctx: Context pointer for handler callbacks
321e5b75505Sopenharmony_ci *
322e5b75505Sopenharmony_ci * This function is used to register a handler for processing failed
323e5b75505Sopenharmony_ci * transmission attempts of interim accounting update messages.
324e5b75505Sopenharmony_ci */
325e5b75505Sopenharmony_civoid radius_client_set_interim_error_cb(struct radius_client_data *radius,
326e5b75505Sopenharmony_ci					void (*cb)(const u8 *addr, void *ctx),
327e5b75505Sopenharmony_ci					void *ctx)
328e5b75505Sopenharmony_ci{
329e5b75505Sopenharmony_ci	radius->interim_error_cb = cb;
330e5b75505Sopenharmony_ci	radius->interim_error_cb_ctx = ctx;
331e5b75505Sopenharmony_ci}
332e5b75505Sopenharmony_ci
333e5b75505Sopenharmony_ci
334e5b75505Sopenharmony_ci/*
335e5b75505Sopenharmony_ci * Returns >0 if message queue was flushed (i.e., the message that triggered
336e5b75505Sopenharmony_ci * the error is not available anymore)
337e5b75505Sopenharmony_ci */
338e5b75505Sopenharmony_cistatic int radius_client_handle_send_error(struct radius_client_data *radius,
339e5b75505Sopenharmony_ci					   int s, RadiusType msg_type)
340e5b75505Sopenharmony_ci{
341e5b75505Sopenharmony_ci#ifndef CONFIG_NATIVE_WINDOWS
342e5b75505Sopenharmony_ci	int _errno = errno;
343e5b75505Sopenharmony_ci	wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
344e5b75505Sopenharmony_ci	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
345e5b75505Sopenharmony_ci	    _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) {
346e5b75505Sopenharmony_ci		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
347e5b75505Sopenharmony_ci			       HOSTAPD_LEVEL_INFO,
348e5b75505Sopenharmony_ci			       "Send failed - maybe interface status changed -"
349e5b75505Sopenharmony_ci			       " try to connect again");
350e5b75505Sopenharmony_ci		if (msg_type == RADIUS_ACCT ||
351e5b75505Sopenharmony_ci		    msg_type == RADIUS_ACCT_INTERIM) {
352e5b75505Sopenharmony_ci			radius_client_init_acct(radius);
353e5b75505Sopenharmony_ci			return 0;
354e5b75505Sopenharmony_ci		} else {
355e5b75505Sopenharmony_ci			radius_client_init_auth(radius);
356e5b75505Sopenharmony_ci			return 1;
357e5b75505Sopenharmony_ci		}
358e5b75505Sopenharmony_ci	}
359e5b75505Sopenharmony_ci#endif /* CONFIG_NATIVE_WINDOWS */
360e5b75505Sopenharmony_ci
361e5b75505Sopenharmony_ci	return 0;
362e5b75505Sopenharmony_ci}
363e5b75505Sopenharmony_ci
364e5b75505Sopenharmony_ci
365e5b75505Sopenharmony_cistatic int radius_client_retransmit(struct radius_client_data *radius,
366e5b75505Sopenharmony_ci				    struct radius_msg_list *entry,
367e5b75505Sopenharmony_ci				    os_time_t now)
368e5b75505Sopenharmony_ci{
369e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
370e5b75505Sopenharmony_ci	int s;
371e5b75505Sopenharmony_ci	struct wpabuf *buf;
372e5b75505Sopenharmony_ci	size_t prev_num_msgs;
373e5b75505Sopenharmony_ci	u8 *acct_delay_time;
374e5b75505Sopenharmony_ci	size_t acct_delay_time_len;
375e5b75505Sopenharmony_ci	int num_servers;
376e5b75505Sopenharmony_ci
377e5b75505Sopenharmony_ci	if (entry->msg_type == RADIUS_ACCT ||
378e5b75505Sopenharmony_ci	    entry->msg_type == RADIUS_ACCT_INTERIM) {
379e5b75505Sopenharmony_ci		num_servers = conf->num_acct_servers;
380e5b75505Sopenharmony_ci		if (radius->acct_sock < 0)
381e5b75505Sopenharmony_ci			radius_client_init_acct(radius);
382e5b75505Sopenharmony_ci		if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
383e5b75505Sopenharmony_ci			prev_num_msgs = radius->num_msgs;
384e5b75505Sopenharmony_ci			radius_client_acct_failover(radius);
385e5b75505Sopenharmony_ci			if (prev_num_msgs != radius->num_msgs)
386e5b75505Sopenharmony_ci				return 0;
387e5b75505Sopenharmony_ci		}
388e5b75505Sopenharmony_ci		s = radius->acct_sock;
389e5b75505Sopenharmony_ci		if (entry->attempts == 0)
390e5b75505Sopenharmony_ci			conf->acct_server->requests++;
391e5b75505Sopenharmony_ci		else {
392e5b75505Sopenharmony_ci			conf->acct_server->timeouts++;
393e5b75505Sopenharmony_ci			conf->acct_server->retransmissions++;
394e5b75505Sopenharmony_ci		}
395e5b75505Sopenharmony_ci	} else {
396e5b75505Sopenharmony_ci		num_servers = conf->num_auth_servers;
397e5b75505Sopenharmony_ci		if (radius->auth_sock < 0)
398e5b75505Sopenharmony_ci			radius_client_init_auth(radius);
399e5b75505Sopenharmony_ci		if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
400e5b75505Sopenharmony_ci			prev_num_msgs = radius->num_msgs;
401e5b75505Sopenharmony_ci			radius_client_auth_failover(radius);
402e5b75505Sopenharmony_ci			if (prev_num_msgs != radius->num_msgs)
403e5b75505Sopenharmony_ci				return 0;
404e5b75505Sopenharmony_ci		}
405e5b75505Sopenharmony_ci		s = radius->auth_sock;
406e5b75505Sopenharmony_ci		if (entry->attempts == 0)
407e5b75505Sopenharmony_ci			conf->auth_server->requests++;
408e5b75505Sopenharmony_ci		else {
409e5b75505Sopenharmony_ci			conf->auth_server->timeouts++;
410e5b75505Sopenharmony_ci			conf->auth_server->retransmissions++;
411e5b75505Sopenharmony_ci		}
412e5b75505Sopenharmony_ci	}
413e5b75505Sopenharmony_ci
414e5b75505Sopenharmony_ci	if (entry->msg_type == RADIUS_ACCT_INTERIM) {
415e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
416e5b75505Sopenharmony_ci			   "RADIUS: Failed to transmit interim accounting update to "
417e5b75505Sopenharmony_ci			   MACSTR " - drop message and request a new update",
418e5b75505Sopenharmony_ci			   MAC2STR(entry->addr));
419e5b75505Sopenharmony_ci		if (radius->interim_error_cb)
420e5b75505Sopenharmony_ci			radius->interim_error_cb(entry->addr,
421e5b75505Sopenharmony_ci						 radius->interim_error_cb_ctx);
422e5b75505Sopenharmony_ci		return 1;
423e5b75505Sopenharmony_ci	}
424e5b75505Sopenharmony_ci
425e5b75505Sopenharmony_ci	if (s < 0) {
426e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO,
427e5b75505Sopenharmony_ci			   "RADIUS: No valid socket for retransmission");
428e5b75505Sopenharmony_ci		return 1;
429e5b75505Sopenharmony_ci	}
430e5b75505Sopenharmony_ci
431e5b75505Sopenharmony_ci	if (entry->msg_type == RADIUS_ACCT &&
432e5b75505Sopenharmony_ci	    radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
433e5b75505Sopenharmony_ci				    &acct_delay_time, &acct_delay_time_len,
434e5b75505Sopenharmony_ci				    NULL) == 0 &&
435e5b75505Sopenharmony_ci	    acct_delay_time_len == 4) {
436e5b75505Sopenharmony_ci		struct radius_hdr *hdr;
437e5b75505Sopenharmony_ci		u32 delay_time;
438e5b75505Sopenharmony_ci
439e5b75505Sopenharmony_ci		/*
440e5b75505Sopenharmony_ci		 * Need to assign a new identifier since attribute contents
441e5b75505Sopenharmony_ci		 * changes.
442e5b75505Sopenharmony_ci		 */
443e5b75505Sopenharmony_ci		hdr = radius_msg_get_hdr(entry->msg);
444e5b75505Sopenharmony_ci		hdr->identifier = radius_client_get_id(radius);
445e5b75505Sopenharmony_ci
446e5b75505Sopenharmony_ci		/* Update Acct-Delay-Time to show wait time in queue */
447e5b75505Sopenharmony_ci		delay_time = now - entry->first_try;
448e5b75505Sopenharmony_ci		WPA_PUT_BE32(acct_delay_time, delay_time);
449e5b75505Sopenharmony_ci
450e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
451e5b75505Sopenharmony_ci			   "RADIUS: Updated Acct-Delay-Time to %u for retransmission",
452e5b75505Sopenharmony_ci			   delay_time);
453e5b75505Sopenharmony_ci		radius_msg_finish_acct(entry->msg, entry->shared_secret,
454e5b75505Sopenharmony_ci				       entry->shared_secret_len);
455e5b75505Sopenharmony_ci		if (radius->conf->msg_dumps)
456e5b75505Sopenharmony_ci			radius_msg_dump(entry->msg);
457e5b75505Sopenharmony_ci	}
458e5b75505Sopenharmony_ci
459e5b75505Sopenharmony_ci	/* retransmit; remove entry if too many attempts */
460e5b75505Sopenharmony_ci	if (entry->accu_attempts > RADIUS_CLIENT_MAX_FAILOVER *
461e5b75505Sopenharmony_ci	    RADIUS_CLIENT_NUM_FAILOVER * num_servers) {
462e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO,
463e5b75505Sopenharmony_ci			   "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
464e5b75505Sopenharmony_ci		return 1;
465e5b75505Sopenharmony_ci	}
466e5b75505Sopenharmony_ci
467e5b75505Sopenharmony_ci	entry->attempts++;
468e5b75505Sopenharmony_ci	entry->accu_attempts++;
469e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
470e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
471e5b75505Sopenharmony_ci		       radius_msg_get_hdr(entry->msg)->identifier);
472e5b75505Sopenharmony_ci
473e5b75505Sopenharmony_ci	os_get_reltime(&entry->last_attempt);
474e5b75505Sopenharmony_ci	buf = radius_msg_get_buf(entry->msg);
475e5b75505Sopenharmony_ci	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
476e5b75505Sopenharmony_ci		if (radius_client_handle_send_error(radius, s, entry->msg_type)
477e5b75505Sopenharmony_ci		    > 0)
478e5b75505Sopenharmony_ci			return 0;
479e5b75505Sopenharmony_ci	}
480e5b75505Sopenharmony_ci
481e5b75505Sopenharmony_ci	entry->next_try = now + entry->next_wait;
482e5b75505Sopenharmony_ci	entry->next_wait *= 2;
483e5b75505Sopenharmony_ci	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
484e5b75505Sopenharmony_ci		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
485e5b75505Sopenharmony_ci
486e5b75505Sopenharmony_ci	return 0;
487e5b75505Sopenharmony_ci}
488e5b75505Sopenharmony_ci
489e5b75505Sopenharmony_ci
490e5b75505Sopenharmony_cistatic void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
491e5b75505Sopenharmony_ci{
492e5b75505Sopenharmony_ci	struct radius_client_data *radius = eloop_ctx;
493e5b75505Sopenharmony_ci	struct os_reltime now;
494e5b75505Sopenharmony_ci	os_time_t first;
495e5b75505Sopenharmony_ci	struct radius_msg_list *entry, *prev, *tmp;
496e5b75505Sopenharmony_ci	int auth_failover = 0, acct_failover = 0;
497e5b75505Sopenharmony_ci	size_t prev_num_msgs;
498e5b75505Sopenharmony_ci	int s;
499e5b75505Sopenharmony_ci
500e5b75505Sopenharmony_ci	entry = radius->msgs;
501e5b75505Sopenharmony_ci	if (!entry)
502e5b75505Sopenharmony_ci		return;
503e5b75505Sopenharmony_ci
504e5b75505Sopenharmony_ci	os_get_reltime(&now);
505e5b75505Sopenharmony_ci
506e5b75505Sopenharmony_ci	while (entry) {
507e5b75505Sopenharmony_ci		if (now.sec >= entry->next_try) {
508e5b75505Sopenharmony_ci			s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
509e5b75505Sopenharmony_ci				radius->acct_sock;
510e5b75505Sopenharmony_ci			if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
511e5b75505Sopenharmony_ci			    (s < 0 && entry->attempts > 0)) {
512e5b75505Sopenharmony_ci				if (entry->msg_type == RADIUS_ACCT ||
513e5b75505Sopenharmony_ci				    entry->msg_type == RADIUS_ACCT_INTERIM)
514e5b75505Sopenharmony_ci					acct_failover++;
515e5b75505Sopenharmony_ci				else
516e5b75505Sopenharmony_ci					auth_failover++;
517e5b75505Sopenharmony_ci			}
518e5b75505Sopenharmony_ci		}
519e5b75505Sopenharmony_ci		entry = entry->next;
520e5b75505Sopenharmony_ci	}
521e5b75505Sopenharmony_ci
522e5b75505Sopenharmony_ci	if (auth_failover)
523e5b75505Sopenharmony_ci		radius_client_auth_failover(radius);
524e5b75505Sopenharmony_ci
525e5b75505Sopenharmony_ci	if (acct_failover)
526e5b75505Sopenharmony_ci		radius_client_acct_failover(radius);
527e5b75505Sopenharmony_ci
528e5b75505Sopenharmony_ci	entry = radius->msgs;
529e5b75505Sopenharmony_ci	first = 0;
530e5b75505Sopenharmony_ci
531e5b75505Sopenharmony_ci	prev = NULL;
532e5b75505Sopenharmony_ci	while (entry) {
533e5b75505Sopenharmony_ci		prev_num_msgs = radius->num_msgs;
534e5b75505Sopenharmony_ci		if (now.sec >= entry->next_try &&
535e5b75505Sopenharmony_ci		    radius_client_retransmit(radius, entry, now.sec)) {
536e5b75505Sopenharmony_ci			if (prev)
537e5b75505Sopenharmony_ci				prev->next = entry->next;
538e5b75505Sopenharmony_ci			else
539e5b75505Sopenharmony_ci				radius->msgs = entry->next;
540e5b75505Sopenharmony_ci
541e5b75505Sopenharmony_ci			tmp = entry;
542e5b75505Sopenharmony_ci			entry = entry->next;
543e5b75505Sopenharmony_ci			radius_client_msg_free(tmp);
544e5b75505Sopenharmony_ci			radius->num_msgs--;
545e5b75505Sopenharmony_ci			continue;
546e5b75505Sopenharmony_ci		}
547e5b75505Sopenharmony_ci
548e5b75505Sopenharmony_ci		if (prev_num_msgs != radius->num_msgs) {
549e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG,
550e5b75505Sopenharmony_ci				   "RADIUS: Message removed from queue - restart from beginning");
551e5b75505Sopenharmony_ci			entry = radius->msgs;
552e5b75505Sopenharmony_ci			prev = NULL;
553e5b75505Sopenharmony_ci			continue;
554e5b75505Sopenharmony_ci		}
555e5b75505Sopenharmony_ci
556e5b75505Sopenharmony_ci		if (first == 0 || entry->next_try < first)
557e5b75505Sopenharmony_ci			first = entry->next_try;
558e5b75505Sopenharmony_ci
559e5b75505Sopenharmony_ci		prev = entry;
560e5b75505Sopenharmony_ci		entry = entry->next;
561e5b75505Sopenharmony_ci	}
562e5b75505Sopenharmony_ci
563e5b75505Sopenharmony_ci	if (radius->msgs) {
564e5b75505Sopenharmony_ci		if (first < now.sec)
565e5b75505Sopenharmony_ci			first = now.sec;
566e5b75505Sopenharmony_ci		eloop_cancel_timeout(radius_client_timer, radius, NULL);
567e5b75505Sopenharmony_ci		eloop_register_timeout(first - now.sec, 0,
568e5b75505Sopenharmony_ci				       radius_client_timer, radius, NULL);
569e5b75505Sopenharmony_ci		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
570e5b75505Sopenharmony_ci			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
571e5b75505Sopenharmony_ci			       "retransmit in %ld seconds",
572e5b75505Sopenharmony_ci			       (long int) (first - now.sec));
573e5b75505Sopenharmony_ci	}
574e5b75505Sopenharmony_ci}
575e5b75505Sopenharmony_ci
576e5b75505Sopenharmony_ci
577e5b75505Sopenharmony_cistatic void radius_client_auth_failover(struct radius_client_data *radius)
578e5b75505Sopenharmony_ci{
579e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
580e5b75505Sopenharmony_ci	struct hostapd_radius_server *next, *old;
581e5b75505Sopenharmony_ci	struct radius_msg_list *entry;
582e5b75505Sopenharmony_ci	char abuf[50];
583e5b75505Sopenharmony_ci
584e5b75505Sopenharmony_ci	old = conf->auth_server;
585e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
586e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_NOTICE,
587e5b75505Sopenharmony_ci		       "No response from Authentication server %s:%d - failover",
588e5b75505Sopenharmony_ci		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
589e5b75505Sopenharmony_ci		       old->port);
590e5b75505Sopenharmony_ci
591e5b75505Sopenharmony_ci	for (entry = radius->msgs; entry; entry = entry->next) {
592e5b75505Sopenharmony_ci		if (entry->msg_type == RADIUS_AUTH)
593e5b75505Sopenharmony_ci			old->timeouts++;
594e5b75505Sopenharmony_ci	}
595e5b75505Sopenharmony_ci
596e5b75505Sopenharmony_ci	next = old + 1;
597e5b75505Sopenharmony_ci	if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
598e5b75505Sopenharmony_ci		next = conf->auth_servers;
599e5b75505Sopenharmony_ci	conf->auth_server = next;
600e5b75505Sopenharmony_ci	radius_change_server(radius, next, old,
601e5b75505Sopenharmony_ci			     radius->auth_serv_sock,
602e5b75505Sopenharmony_ci			     radius->auth_serv_sock6, 1);
603e5b75505Sopenharmony_ci}
604e5b75505Sopenharmony_ci
605e5b75505Sopenharmony_ci
606e5b75505Sopenharmony_cistatic void radius_client_acct_failover(struct radius_client_data *radius)
607e5b75505Sopenharmony_ci{
608e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
609e5b75505Sopenharmony_ci	struct hostapd_radius_server *next, *old;
610e5b75505Sopenharmony_ci	struct radius_msg_list *entry;
611e5b75505Sopenharmony_ci	char abuf[50];
612e5b75505Sopenharmony_ci
613e5b75505Sopenharmony_ci	old = conf->acct_server;
614e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
615e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_NOTICE,
616e5b75505Sopenharmony_ci		       "No response from Accounting server %s:%d - failover",
617e5b75505Sopenharmony_ci		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
618e5b75505Sopenharmony_ci		       old->port);
619e5b75505Sopenharmony_ci
620e5b75505Sopenharmony_ci	for (entry = radius->msgs; entry; entry = entry->next) {
621e5b75505Sopenharmony_ci		if (entry->msg_type == RADIUS_ACCT ||
622e5b75505Sopenharmony_ci		    entry->msg_type == RADIUS_ACCT_INTERIM)
623e5b75505Sopenharmony_ci			old->timeouts++;
624e5b75505Sopenharmony_ci	}
625e5b75505Sopenharmony_ci
626e5b75505Sopenharmony_ci	next = old + 1;
627e5b75505Sopenharmony_ci	if (next > &conf->acct_servers[conf->num_acct_servers - 1])
628e5b75505Sopenharmony_ci		next = conf->acct_servers;
629e5b75505Sopenharmony_ci	conf->acct_server = next;
630e5b75505Sopenharmony_ci	radius_change_server(radius, next, old,
631e5b75505Sopenharmony_ci			     radius->acct_serv_sock,
632e5b75505Sopenharmony_ci			     radius->acct_serv_sock6, 0);
633e5b75505Sopenharmony_ci}
634e5b75505Sopenharmony_ci
635e5b75505Sopenharmony_ci
636e5b75505Sopenharmony_cistatic void radius_client_update_timeout(struct radius_client_data *radius)
637e5b75505Sopenharmony_ci{
638e5b75505Sopenharmony_ci	struct os_reltime now;
639e5b75505Sopenharmony_ci	os_time_t first;
640e5b75505Sopenharmony_ci	struct radius_msg_list *entry;
641e5b75505Sopenharmony_ci
642e5b75505Sopenharmony_ci	eloop_cancel_timeout(radius_client_timer, radius, NULL);
643e5b75505Sopenharmony_ci
644e5b75505Sopenharmony_ci	if (radius->msgs == NULL) {
645e5b75505Sopenharmony_ci		return;
646e5b75505Sopenharmony_ci	}
647e5b75505Sopenharmony_ci
648e5b75505Sopenharmony_ci	first = 0;
649e5b75505Sopenharmony_ci	for (entry = radius->msgs; entry; entry = entry->next) {
650e5b75505Sopenharmony_ci		if (first == 0 || entry->next_try < first)
651e5b75505Sopenharmony_ci			first = entry->next_try;
652e5b75505Sopenharmony_ci	}
653e5b75505Sopenharmony_ci
654e5b75505Sopenharmony_ci	os_get_reltime(&now);
655e5b75505Sopenharmony_ci	if (first < now.sec)
656e5b75505Sopenharmony_ci		first = now.sec;
657e5b75505Sopenharmony_ci	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
658e5b75505Sopenharmony_ci			       NULL);
659e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
660e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
661e5b75505Sopenharmony_ci		       " %ld seconds", (long int) (first - now.sec));
662e5b75505Sopenharmony_ci}
663e5b75505Sopenharmony_ci
664e5b75505Sopenharmony_ci
665e5b75505Sopenharmony_cistatic void radius_client_list_add(struct radius_client_data *radius,
666e5b75505Sopenharmony_ci				   struct radius_msg *msg,
667e5b75505Sopenharmony_ci				   RadiusType msg_type,
668e5b75505Sopenharmony_ci				   const u8 *shared_secret,
669e5b75505Sopenharmony_ci				   size_t shared_secret_len, const u8 *addr)
670e5b75505Sopenharmony_ci{
671e5b75505Sopenharmony_ci	struct radius_msg_list *entry, *prev;
672e5b75505Sopenharmony_ci
673e5b75505Sopenharmony_ci	if (eloop_terminated()) {
674e5b75505Sopenharmony_ci		/* No point in adding entries to retransmit queue since event
675e5b75505Sopenharmony_ci		 * loop has already been terminated. */
676e5b75505Sopenharmony_ci		radius_msg_free(msg);
677e5b75505Sopenharmony_ci		return;
678e5b75505Sopenharmony_ci	}
679e5b75505Sopenharmony_ci
680e5b75505Sopenharmony_ci	entry = os_zalloc(sizeof(*entry));
681e5b75505Sopenharmony_ci	if (entry == NULL) {
682e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
683e5b75505Sopenharmony_ci		radius_msg_free(msg);
684e5b75505Sopenharmony_ci		return;
685e5b75505Sopenharmony_ci	}
686e5b75505Sopenharmony_ci
687e5b75505Sopenharmony_ci	if (addr)
688e5b75505Sopenharmony_ci		os_memcpy(entry->addr, addr, ETH_ALEN);
689e5b75505Sopenharmony_ci	entry->msg = msg;
690e5b75505Sopenharmony_ci	entry->msg_type = msg_type;
691e5b75505Sopenharmony_ci	entry->shared_secret = shared_secret;
692e5b75505Sopenharmony_ci	entry->shared_secret_len = shared_secret_len;
693e5b75505Sopenharmony_ci	os_get_reltime(&entry->last_attempt);
694e5b75505Sopenharmony_ci	entry->first_try = entry->last_attempt.sec;
695e5b75505Sopenharmony_ci	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
696e5b75505Sopenharmony_ci	entry->attempts = 1;
697e5b75505Sopenharmony_ci	entry->accu_attempts = 1;
698e5b75505Sopenharmony_ci	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
699e5b75505Sopenharmony_ci	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
700e5b75505Sopenharmony_ci		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
701e5b75505Sopenharmony_ci	entry->next = radius->msgs;
702e5b75505Sopenharmony_ci	radius->msgs = entry;
703e5b75505Sopenharmony_ci	radius_client_update_timeout(radius);
704e5b75505Sopenharmony_ci
705e5b75505Sopenharmony_ci	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
706e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
707e5b75505Sopenharmony_ci		prev = NULL;
708e5b75505Sopenharmony_ci		while (entry->next) {
709e5b75505Sopenharmony_ci			prev = entry;
710e5b75505Sopenharmony_ci			entry = entry->next;
711e5b75505Sopenharmony_ci		}
712e5b75505Sopenharmony_ci		if (prev) {
713e5b75505Sopenharmony_ci			prev->next = NULL;
714e5b75505Sopenharmony_ci			radius_client_msg_free(entry);
715e5b75505Sopenharmony_ci		}
716e5b75505Sopenharmony_ci	} else
717e5b75505Sopenharmony_ci		radius->num_msgs++;
718e5b75505Sopenharmony_ci}
719e5b75505Sopenharmony_ci
720e5b75505Sopenharmony_ci
721e5b75505Sopenharmony_ci/**
722e5b75505Sopenharmony_ci * radius_client_send - Send a RADIUS request
723e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
724e5b75505Sopenharmony_ci * @msg: RADIUS message to be sent
725e5b75505Sopenharmony_ci * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
726e5b75505Sopenharmony_ci * @addr: MAC address of the device related to this message or %NULL
727e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
728e5b75505Sopenharmony_ci *
729e5b75505Sopenharmony_ci * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
730e5b75505Sopenharmony_ci * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
731e5b75505Sopenharmony_ci * between accounting and interim accounting messages is that the interim
732e5b75505Sopenharmony_ci * message will not be retransmitted. Instead, a callback is used to indicate
733e5b75505Sopenharmony_ci * that the transmission failed for the specific station @addr so that a new
734e5b75505Sopenharmony_ci * interim accounting update message can be generated with up-to-date session
735e5b75505Sopenharmony_ci * data instead of trying to resend old information.
736e5b75505Sopenharmony_ci *
737e5b75505Sopenharmony_ci * The message is added on the retransmission queue and will be retransmitted
738e5b75505Sopenharmony_ci * automatically until a response is received or maximum number of retries
739e5b75505Sopenharmony_ci * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No
740e5b75505Sopenharmony_ci * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message
741e5b75505Sopenharmony_ci * is removed from the queue automatically on transmission failure.
742e5b75505Sopenharmony_ci *
743e5b75505Sopenharmony_ci * The related device MAC address can be used to identify pending messages that
744e5b75505Sopenharmony_ci * can be removed with radius_client_flush_auth().
745e5b75505Sopenharmony_ci */
746e5b75505Sopenharmony_ciint radius_client_send(struct radius_client_data *radius,
747e5b75505Sopenharmony_ci		       struct radius_msg *msg, RadiusType msg_type,
748e5b75505Sopenharmony_ci		       const u8 *addr)
749e5b75505Sopenharmony_ci{
750e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
751e5b75505Sopenharmony_ci	const u8 *shared_secret;
752e5b75505Sopenharmony_ci	size_t shared_secret_len;
753e5b75505Sopenharmony_ci	char *name;
754e5b75505Sopenharmony_ci	int s, res;
755e5b75505Sopenharmony_ci	struct wpabuf *buf;
756e5b75505Sopenharmony_ci
757e5b75505Sopenharmony_ci	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
758e5b75505Sopenharmony_ci		if (conf->acct_server && radius->acct_sock < 0)
759e5b75505Sopenharmony_ci			radius_client_init_acct(radius);
760e5b75505Sopenharmony_ci
761e5b75505Sopenharmony_ci		if (conf->acct_server == NULL || radius->acct_sock < 0 ||
762e5b75505Sopenharmony_ci		    conf->acct_server->shared_secret == NULL) {
763e5b75505Sopenharmony_ci			hostapd_logger(radius->ctx, NULL,
764e5b75505Sopenharmony_ci				       HOSTAPD_MODULE_RADIUS,
765e5b75505Sopenharmony_ci				       HOSTAPD_LEVEL_INFO,
766e5b75505Sopenharmony_ci				       "No accounting server configured");
767e5b75505Sopenharmony_ci			return -1;
768e5b75505Sopenharmony_ci		}
769e5b75505Sopenharmony_ci		shared_secret = conf->acct_server->shared_secret;
770e5b75505Sopenharmony_ci		shared_secret_len = conf->acct_server->shared_secret_len;
771e5b75505Sopenharmony_ci		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
772e5b75505Sopenharmony_ci		name = "accounting";
773e5b75505Sopenharmony_ci		s = radius->acct_sock;
774e5b75505Sopenharmony_ci		conf->acct_server->requests++;
775e5b75505Sopenharmony_ci	} else {
776e5b75505Sopenharmony_ci		if (conf->auth_server && radius->auth_sock < 0)
777e5b75505Sopenharmony_ci			radius_client_init_auth(radius);
778e5b75505Sopenharmony_ci
779e5b75505Sopenharmony_ci		if (conf->auth_server == NULL || radius->auth_sock < 0 ||
780e5b75505Sopenharmony_ci		    conf->auth_server->shared_secret == NULL) {
781e5b75505Sopenharmony_ci			hostapd_logger(radius->ctx, NULL,
782e5b75505Sopenharmony_ci				       HOSTAPD_MODULE_RADIUS,
783e5b75505Sopenharmony_ci				       HOSTAPD_LEVEL_INFO,
784e5b75505Sopenharmony_ci				       "No authentication server configured");
785e5b75505Sopenharmony_ci			return -1;
786e5b75505Sopenharmony_ci		}
787e5b75505Sopenharmony_ci		shared_secret = conf->auth_server->shared_secret;
788e5b75505Sopenharmony_ci		shared_secret_len = conf->auth_server->shared_secret_len;
789e5b75505Sopenharmony_ci		radius_msg_finish(msg, shared_secret, shared_secret_len);
790e5b75505Sopenharmony_ci		name = "authentication";
791e5b75505Sopenharmony_ci		s = radius->auth_sock;
792e5b75505Sopenharmony_ci		conf->auth_server->requests++;
793e5b75505Sopenharmony_ci	}
794e5b75505Sopenharmony_ci
795e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
796e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
797e5b75505Sopenharmony_ci		       "server", name);
798e5b75505Sopenharmony_ci	if (conf->msg_dumps)
799e5b75505Sopenharmony_ci		radius_msg_dump(msg);
800e5b75505Sopenharmony_ci
801e5b75505Sopenharmony_ci	buf = radius_msg_get_buf(msg);
802e5b75505Sopenharmony_ci	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
803e5b75505Sopenharmony_ci	if (res < 0)
804e5b75505Sopenharmony_ci		radius_client_handle_send_error(radius, s, msg_type);
805e5b75505Sopenharmony_ci
806e5b75505Sopenharmony_ci	radius_client_list_add(radius, msg, msg_type, shared_secret,
807e5b75505Sopenharmony_ci			       shared_secret_len, addr);
808e5b75505Sopenharmony_ci
809e5b75505Sopenharmony_ci	return 0;
810e5b75505Sopenharmony_ci}
811e5b75505Sopenharmony_ci
812e5b75505Sopenharmony_ci
813e5b75505Sopenharmony_cistatic void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
814e5b75505Sopenharmony_ci{
815e5b75505Sopenharmony_ci	struct radius_client_data *radius = eloop_ctx;
816e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
817e5b75505Sopenharmony_ci	RadiusType msg_type = (RadiusType) sock_ctx;
818e5b75505Sopenharmony_ci	int len, roundtrip;
819e5b75505Sopenharmony_ci	unsigned char buf[3000];
820e5b75505Sopenharmony_ci	struct radius_msg *msg;
821e5b75505Sopenharmony_ci	struct radius_hdr *hdr;
822e5b75505Sopenharmony_ci	struct radius_rx_handler *handlers;
823e5b75505Sopenharmony_ci	size_t num_handlers, i;
824e5b75505Sopenharmony_ci	struct radius_msg_list *req, *prev_req;
825e5b75505Sopenharmony_ci	struct os_reltime now;
826e5b75505Sopenharmony_ci	struct hostapd_radius_server *rconf;
827e5b75505Sopenharmony_ci	int invalid_authenticator = 0;
828e5b75505Sopenharmony_ci
829e5b75505Sopenharmony_ci	if (msg_type == RADIUS_ACCT) {
830e5b75505Sopenharmony_ci		handlers = radius->acct_handlers;
831e5b75505Sopenharmony_ci		num_handlers = radius->num_acct_handlers;
832e5b75505Sopenharmony_ci		rconf = conf->acct_server;
833e5b75505Sopenharmony_ci	} else {
834e5b75505Sopenharmony_ci		handlers = radius->auth_handlers;
835e5b75505Sopenharmony_ci		num_handlers = radius->num_auth_handlers;
836e5b75505Sopenharmony_ci		rconf = conf->auth_server;
837e5b75505Sopenharmony_ci	}
838e5b75505Sopenharmony_ci
839e5b75505Sopenharmony_ci	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
840e5b75505Sopenharmony_ci	if (len < 0) {
841e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno));
842e5b75505Sopenharmony_ci		return;
843e5b75505Sopenharmony_ci	}
844e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
845e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
846e5b75505Sopenharmony_ci		       "server", len);
847e5b75505Sopenharmony_ci	if (len == sizeof(buf)) {
848e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
849e5b75505Sopenharmony_ci		return;
850e5b75505Sopenharmony_ci	}
851e5b75505Sopenharmony_ci
852e5b75505Sopenharmony_ci	msg = radius_msg_parse(buf, len);
853e5b75505Sopenharmony_ci	if (msg == NULL) {
854e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
855e5b75505Sopenharmony_ci		rconf->malformed_responses++;
856e5b75505Sopenharmony_ci		return;
857e5b75505Sopenharmony_ci	}
858e5b75505Sopenharmony_ci	hdr = radius_msg_get_hdr(msg);
859e5b75505Sopenharmony_ci
860e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
861e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
862e5b75505Sopenharmony_ci	if (conf->msg_dumps)
863e5b75505Sopenharmony_ci		radius_msg_dump(msg);
864e5b75505Sopenharmony_ci
865e5b75505Sopenharmony_ci	switch (hdr->code) {
866e5b75505Sopenharmony_ci	case RADIUS_CODE_ACCESS_ACCEPT:
867e5b75505Sopenharmony_ci		rconf->access_accepts++;
868e5b75505Sopenharmony_ci		break;
869e5b75505Sopenharmony_ci	case RADIUS_CODE_ACCESS_REJECT:
870e5b75505Sopenharmony_ci		rconf->access_rejects++;
871e5b75505Sopenharmony_ci		break;
872e5b75505Sopenharmony_ci	case RADIUS_CODE_ACCESS_CHALLENGE:
873e5b75505Sopenharmony_ci		rconf->access_challenges++;
874e5b75505Sopenharmony_ci		break;
875e5b75505Sopenharmony_ci	case RADIUS_CODE_ACCOUNTING_RESPONSE:
876e5b75505Sopenharmony_ci		rconf->responses++;
877e5b75505Sopenharmony_ci		break;
878e5b75505Sopenharmony_ci	}
879e5b75505Sopenharmony_ci
880e5b75505Sopenharmony_ci	prev_req = NULL;
881e5b75505Sopenharmony_ci	req = radius->msgs;
882e5b75505Sopenharmony_ci	while (req) {
883e5b75505Sopenharmony_ci		/* TODO: also match by src addr:port of the packet when using
884e5b75505Sopenharmony_ci		 * alternative RADIUS servers (?) */
885e5b75505Sopenharmony_ci		if ((req->msg_type == msg_type ||
886e5b75505Sopenharmony_ci		     (req->msg_type == RADIUS_ACCT_INTERIM &&
887e5b75505Sopenharmony_ci		      msg_type == RADIUS_ACCT)) &&
888e5b75505Sopenharmony_ci		    radius_msg_get_hdr(req->msg)->identifier ==
889e5b75505Sopenharmony_ci		    hdr->identifier)
890e5b75505Sopenharmony_ci			break;
891e5b75505Sopenharmony_ci
892e5b75505Sopenharmony_ci		prev_req = req;
893e5b75505Sopenharmony_ci		req = req->next;
894e5b75505Sopenharmony_ci	}
895e5b75505Sopenharmony_ci
896e5b75505Sopenharmony_ci	if (req == NULL) {
897e5b75505Sopenharmony_ci		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
898e5b75505Sopenharmony_ci			       HOSTAPD_LEVEL_DEBUG,
899e5b75505Sopenharmony_ci			       "No matching RADIUS request found (type=%d "
900e5b75505Sopenharmony_ci			       "id=%d) - dropping packet",
901e5b75505Sopenharmony_ci			       msg_type, hdr->identifier);
902e5b75505Sopenharmony_ci		goto fail;
903e5b75505Sopenharmony_ci	}
904e5b75505Sopenharmony_ci
905e5b75505Sopenharmony_ci	os_get_reltime(&now);
906e5b75505Sopenharmony_ci	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
907e5b75505Sopenharmony_ci		(now.usec - req->last_attempt.usec) / 10000;
908e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
909e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG,
910e5b75505Sopenharmony_ci		       "Received RADIUS packet matched with a pending "
911e5b75505Sopenharmony_ci		       "request, round trip time %d.%02d sec",
912e5b75505Sopenharmony_ci		       roundtrip / 100, roundtrip % 100);
913e5b75505Sopenharmony_ci	rconf->round_trip_time = roundtrip;
914e5b75505Sopenharmony_ci
915e5b75505Sopenharmony_ci	/* Remove ACKed RADIUS packet from retransmit list */
916e5b75505Sopenharmony_ci	if (prev_req)
917e5b75505Sopenharmony_ci		prev_req->next = req->next;
918e5b75505Sopenharmony_ci	else
919e5b75505Sopenharmony_ci		radius->msgs = req->next;
920e5b75505Sopenharmony_ci	radius->num_msgs--;
921e5b75505Sopenharmony_ci
922e5b75505Sopenharmony_ci	for (i = 0; i < num_handlers; i++) {
923e5b75505Sopenharmony_ci		RadiusRxResult res;
924e5b75505Sopenharmony_ci		res = handlers[i].handler(msg, req->msg, req->shared_secret,
925e5b75505Sopenharmony_ci					  req->shared_secret_len,
926e5b75505Sopenharmony_ci					  handlers[i].data);
927e5b75505Sopenharmony_ci		switch (res) {
928e5b75505Sopenharmony_ci		case RADIUS_RX_PROCESSED:
929e5b75505Sopenharmony_ci			radius_msg_free(msg);
930e5b75505Sopenharmony_ci			/* fall through */
931e5b75505Sopenharmony_ci		case RADIUS_RX_QUEUED:
932e5b75505Sopenharmony_ci			radius_client_msg_free(req);
933e5b75505Sopenharmony_ci			return;
934e5b75505Sopenharmony_ci		case RADIUS_RX_INVALID_AUTHENTICATOR:
935e5b75505Sopenharmony_ci			invalid_authenticator++;
936e5b75505Sopenharmony_ci			/* fall through */
937e5b75505Sopenharmony_ci		case RADIUS_RX_UNKNOWN:
938e5b75505Sopenharmony_ci			/* continue with next handler */
939e5b75505Sopenharmony_ci			break;
940e5b75505Sopenharmony_ci		}
941e5b75505Sopenharmony_ci	}
942e5b75505Sopenharmony_ci
943e5b75505Sopenharmony_ci	if (invalid_authenticator)
944e5b75505Sopenharmony_ci		rconf->bad_authenticators++;
945e5b75505Sopenharmony_ci	else
946e5b75505Sopenharmony_ci		rconf->unknown_types++;
947e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
948e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
949e5b75505Sopenharmony_ci		       "(type=%d code=%d id=%d)%s - dropping packet",
950e5b75505Sopenharmony_ci		       msg_type, hdr->code, hdr->identifier,
951e5b75505Sopenharmony_ci		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
952e5b75505Sopenharmony_ci		       "");
953e5b75505Sopenharmony_ci	radius_client_msg_free(req);
954e5b75505Sopenharmony_ci
955e5b75505Sopenharmony_ci fail:
956e5b75505Sopenharmony_ci	radius_msg_free(msg);
957e5b75505Sopenharmony_ci}
958e5b75505Sopenharmony_ci
959e5b75505Sopenharmony_ci
960e5b75505Sopenharmony_ci/**
961e5b75505Sopenharmony_ci * radius_client_get_id - Get an identifier for a new RADIUS message
962e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
963e5b75505Sopenharmony_ci * Returns: Allocated identifier
964e5b75505Sopenharmony_ci *
965e5b75505Sopenharmony_ci * This function is used to fetch a unique (among pending requests) identifier
966e5b75505Sopenharmony_ci * for a new RADIUS message.
967e5b75505Sopenharmony_ci */
968e5b75505Sopenharmony_ciu8 radius_client_get_id(struct radius_client_data *radius)
969e5b75505Sopenharmony_ci{
970e5b75505Sopenharmony_ci	struct radius_msg_list *entry, *prev, *_remove;
971e5b75505Sopenharmony_ci	u8 id = radius->next_radius_identifier++;
972e5b75505Sopenharmony_ci
973e5b75505Sopenharmony_ci	/* remove entries with matching id from retransmit list to avoid
974e5b75505Sopenharmony_ci	 * using new reply from the RADIUS server with an old request */
975e5b75505Sopenharmony_ci	entry = radius->msgs;
976e5b75505Sopenharmony_ci	prev = NULL;
977e5b75505Sopenharmony_ci	while (entry) {
978e5b75505Sopenharmony_ci		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
979e5b75505Sopenharmony_ci			hostapd_logger(radius->ctx, entry->addr,
980e5b75505Sopenharmony_ci				       HOSTAPD_MODULE_RADIUS,
981e5b75505Sopenharmony_ci				       HOSTAPD_LEVEL_DEBUG,
982e5b75505Sopenharmony_ci				       "Removing pending RADIUS message, "
983e5b75505Sopenharmony_ci				       "since its id (%d) is reused", id);
984e5b75505Sopenharmony_ci			if (prev)
985e5b75505Sopenharmony_ci				prev->next = entry->next;
986e5b75505Sopenharmony_ci			else
987e5b75505Sopenharmony_ci				radius->msgs = entry->next;
988e5b75505Sopenharmony_ci			_remove = entry;
989e5b75505Sopenharmony_ci		} else {
990e5b75505Sopenharmony_ci			_remove = NULL;
991e5b75505Sopenharmony_ci			prev = entry;
992e5b75505Sopenharmony_ci		}
993e5b75505Sopenharmony_ci		entry = entry->next;
994e5b75505Sopenharmony_ci
995e5b75505Sopenharmony_ci		if (_remove)
996e5b75505Sopenharmony_ci			radius_client_msg_free(_remove);
997e5b75505Sopenharmony_ci	}
998e5b75505Sopenharmony_ci
999e5b75505Sopenharmony_ci	return id;
1000e5b75505Sopenharmony_ci}
1001e5b75505Sopenharmony_ci
1002e5b75505Sopenharmony_ci
1003e5b75505Sopenharmony_ci/**
1004e5b75505Sopenharmony_ci * radius_client_flush - Flush all pending RADIUS client messages
1005e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
1006e5b75505Sopenharmony_ci * @only_auth: Whether only authentication messages are removed
1007e5b75505Sopenharmony_ci */
1008e5b75505Sopenharmony_civoid radius_client_flush(struct radius_client_data *radius, int only_auth)
1009e5b75505Sopenharmony_ci{
1010e5b75505Sopenharmony_ci	struct radius_msg_list *entry, *prev, *tmp;
1011e5b75505Sopenharmony_ci
1012e5b75505Sopenharmony_ci	if (!radius)
1013e5b75505Sopenharmony_ci		return;
1014e5b75505Sopenharmony_ci
1015e5b75505Sopenharmony_ci	prev = NULL;
1016e5b75505Sopenharmony_ci	entry = radius->msgs;
1017e5b75505Sopenharmony_ci
1018e5b75505Sopenharmony_ci	while (entry) {
1019e5b75505Sopenharmony_ci		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
1020e5b75505Sopenharmony_ci			if (prev)
1021e5b75505Sopenharmony_ci				prev->next = entry->next;
1022e5b75505Sopenharmony_ci			else
1023e5b75505Sopenharmony_ci				radius->msgs = entry->next;
1024e5b75505Sopenharmony_ci
1025e5b75505Sopenharmony_ci			tmp = entry;
1026e5b75505Sopenharmony_ci			entry = entry->next;
1027e5b75505Sopenharmony_ci			radius_client_msg_free(tmp);
1028e5b75505Sopenharmony_ci			radius->num_msgs--;
1029e5b75505Sopenharmony_ci		} else {
1030e5b75505Sopenharmony_ci			prev = entry;
1031e5b75505Sopenharmony_ci			entry = entry->next;
1032e5b75505Sopenharmony_ci		}
1033e5b75505Sopenharmony_ci	}
1034e5b75505Sopenharmony_ci
1035e5b75505Sopenharmony_ci	if (radius->msgs == NULL)
1036e5b75505Sopenharmony_ci		eloop_cancel_timeout(radius_client_timer, radius, NULL);
1037e5b75505Sopenharmony_ci}
1038e5b75505Sopenharmony_ci
1039e5b75505Sopenharmony_ci
1040e5b75505Sopenharmony_cistatic void radius_client_update_acct_msgs(struct radius_client_data *radius,
1041e5b75505Sopenharmony_ci					   const u8 *shared_secret,
1042e5b75505Sopenharmony_ci					   size_t shared_secret_len)
1043e5b75505Sopenharmony_ci{
1044e5b75505Sopenharmony_ci	struct radius_msg_list *entry;
1045e5b75505Sopenharmony_ci
1046e5b75505Sopenharmony_ci	if (!radius)
1047e5b75505Sopenharmony_ci		return;
1048e5b75505Sopenharmony_ci
1049e5b75505Sopenharmony_ci	for (entry = radius->msgs; entry; entry = entry->next) {
1050e5b75505Sopenharmony_ci		if (entry->msg_type == RADIUS_ACCT) {
1051e5b75505Sopenharmony_ci			entry->shared_secret = shared_secret;
1052e5b75505Sopenharmony_ci			entry->shared_secret_len = shared_secret_len;
1053e5b75505Sopenharmony_ci			radius_msg_finish_acct(entry->msg, shared_secret,
1054e5b75505Sopenharmony_ci					       shared_secret_len);
1055e5b75505Sopenharmony_ci		}
1056e5b75505Sopenharmony_ci	}
1057e5b75505Sopenharmony_ci}
1058e5b75505Sopenharmony_ci
1059e5b75505Sopenharmony_ci
1060e5b75505Sopenharmony_cistatic int
1061e5b75505Sopenharmony_ciradius_change_server(struct radius_client_data *radius,
1062e5b75505Sopenharmony_ci		     struct hostapd_radius_server *nserv,
1063e5b75505Sopenharmony_ci		     struct hostapd_radius_server *oserv,
1064e5b75505Sopenharmony_ci		     int sock, int sock6, int auth)
1065e5b75505Sopenharmony_ci{
1066e5b75505Sopenharmony_ci	struct sockaddr_in serv, claddr;
1067e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1068e5b75505Sopenharmony_ci	struct sockaddr_in6 serv6, claddr6;
1069e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1070e5b75505Sopenharmony_ci	struct sockaddr *addr, *cl_addr;
1071e5b75505Sopenharmony_ci	socklen_t addrlen, claddrlen;
1072e5b75505Sopenharmony_ci	char abuf[50];
1073e5b75505Sopenharmony_ci	int sel_sock;
1074e5b75505Sopenharmony_ci	struct radius_msg_list *entry;
1075e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
1076e5b75505Sopenharmony_ci	struct sockaddr_in disconnect_addr = {
1077e5b75505Sopenharmony_ci		.sin_family = AF_UNSPEC,
1078e5b75505Sopenharmony_ci	};
1079e5b75505Sopenharmony_ci
1080e5b75505Sopenharmony_ci	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
1081e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_INFO,
1082e5b75505Sopenharmony_ci		       "%s server %s:%d",
1083e5b75505Sopenharmony_ci		       auth ? "Authentication" : "Accounting",
1084e5b75505Sopenharmony_ci		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
1085e5b75505Sopenharmony_ci		       nserv->port);
1086e5b75505Sopenharmony_ci
1087e5b75505Sopenharmony_ci	if (oserv && oserv == nserv) {
1088e5b75505Sopenharmony_ci		/* Reconnect to same server, flush */
1089e5b75505Sopenharmony_ci		if (auth)
1090e5b75505Sopenharmony_ci			radius_client_flush(radius, 1);
1091e5b75505Sopenharmony_ci	}
1092e5b75505Sopenharmony_ci
1093e5b75505Sopenharmony_ci	if (oserv && oserv != nserv &&
1094e5b75505Sopenharmony_ci	    (nserv->shared_secret_len != oserv->shared_secret_len ||
1095e5b75505Sopenharmony_ci	     os_memcmp(nserv->shared_secret, oserv->shared_secret,
1096e5b75505Sopenharmony_ci		       nserv->shared_secret_len) != 0)) {
1097e5b75505Sopenharmony_ci		/* Pending RADIUS packets used different shared secret, so
1098e5b75505Sopenharmony_ci		 * they need to be modified. Update accounting message
1099e5b75505Sopenharmony_ci		 * authenticators here. Authentication messages are removed
1100e5b75505Sopenharmony_ci		 * since they would require more changes and the new RADIUS
1101e5b75505Sopenharmony_ci		 * server may not be prepared to receive them anyway due to
1102e5b75505Sopenharmony_ci		 * missing state information. Client will likely retry
1103e5b75505Sopenharmony_ci		 * authentication, so this should not be an issue. */
1104e5b75505Sopenharmony_ci		if (auth)
1105e5b75505Sopenharmony_ci			radius_client_flush(radius, 1);
1106e5b75505Sopenharmony_ci		else {
1107e5b75505Sopenharmony_ci			radius_client_update_acct_msgs(
1108e5b75505Sopenharmony_ci				radius, nserv->shared_secret,
1109e5b75505Sopenharmony_ci				nserv->shared_secret_len);
1110e5b75505Sopenharmony_ci		}
1111e5b75505Sopenharmony_ci	}
1112e5b75505Sopenharmony_ci
1113e5b75505Sopenharmony_ci	/* Reset retry counters */
1114e5b75505Sopenharmony_ci	for (entry = radius->msgs; oserv && entry; entry = entry->next) {
1115e5b75505Sopenharmony_ci		if ((auth && entry->msg_type != RADIUS_AUTH) ||
1116e5b75505Sopenharmony_ci		    (!auth && entry->msg_type != RADIUS_ACCT))
1117e5b75505Sopenharmony_ci			continue;
1118e5b75505Sopenharmony_ci		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
1119e5b75505Sopenharmony_ci		entry->attempts = 1;
1120e5b75505Sopenharmony_ci		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
1121e5b75505Sopenharmony_ci	}
1122e5b75505Sopenharmony_ci
1123e5b75505Sopenharmony_ci	if (radius->msgs) {
1124e5b75505Sopenharmony_ci		eloop_cancel_timeout(radius_client_timer, radius, NULL);
1125e5b75505Sopenharmony_ci		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
1126e5b75505Sopenharmony_ci				       radius_client_timer, radius, NULL);
1127e5b75505Sopenharmony_ci	}
1128e5b75505Sopenharmony_ci
1129e5b75505Sopenharmony_ci	switch (nserv->addr.af) {
1130e5b75505Sopenharmony_ci	case AF_INET:
1131e5b75505Sopenharmony_ci		os_memset(&serv, 0, sizeof(serv));
1132e5b75505Sopenharmony_ci		serv.sin_family = AF_INET;
1133e5b75505Sopenharmony_ci		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
1134e5b75505Sopenharmony_ci		serv.sin_port = htons(nserv->port);
1135e5b75505Sopenharmony_ci		addr = (struct sockaddr *) &serv;
1136e5b75505Sopenharmony_ci		addrlen = sizeof(serv);
1137e5b75505Sopenharmony_ci		sel_sock = sock;
1138e5b75505Sopenharmony_ci		break;
1139e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1140e5b75505Sopenharmony_ci	case AF_INET6:
1141e5b75505Sopenharmony_ci		os_memset(&serv6, 0, sizeof(serv6));
1142e5b75505Sopenharmony_ci		serv6.sin6_family = AF_INET6;
1143e5b75505Sopenharmony_ci		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
1144e5b75505Sopenharmony_ci			  sizeof(struct in6_addr));
1145e5b75505Sopenharmony_ci		serv6.sin6_port = htons(nserv->port);
1146e5b75505Sopenharmony_ci		addr = (struct sockaddr *) &serv6;
1147e5b75505Sopenharmony_ci		addrlen = sizeof(serv6);
1148e5b75505Sopenharmony_ci		sel_sock = sock6;
1149e5b75505Sopenharmony_ci		break;
1150e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1151e5b75505Sopenharmony_ci	default:
1152e5b75505Sopenharmony_ci		return -1;
1153e5b75505Sopenharmony_ci	}
1154e5b75505Sopenharmony_ci
1155e5b75505Sopenharmony_ci	if (sel_sock < 0) {
1156e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO,
1157e5b75505Sopenharmony_ci			   "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
1158e5b75505Sopenharmony_ci			   nserv->addr.af, sock, sock6, auth);
1159e5b75505Sopenharmony_ci		return -1;
1160e5b75505Sopenharmony_ci	}
1161e5b75505Sopenharmony_ci
1162e5b75505Sopenharmony_ci	if (conf->force_client_addr) {
1163e5b75505Sopenharmony_ci		switch (conf->client_addr.af) {
1164e5b75505Sopenharmony_ci		case AF_INET:
1165e5b75505Sopenharmony_ci			os_memset(&claddr, 0, sizeof(claddr));
1166e5b75505Sopenharmony_ci			claddr.sin_family = AF_INET;
1167e5b75505Sopenharmony_ci			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
1168e5b75505Sopenharmony_ci			claddr.sin_port = htons(0);
1169e5b75505Sopenharmony_ci			cl_addr = (struct sockaddr *) &claddr;
1170e5b75505Sopenharmony_ci			claddrlen = sizeof(claddr);
1171e5b75505Sopenharmony_ci			break;
1172e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1173e5b75505Sopenharmony_ci		case AF_INET6:
1174e5b75505Sopenharmony_ci			os_memset(&claddr6, 0, sizeof(claddr6));
1175e5b75505Sopenharmony_ci			claddr6.sin6_family = AF_INET6;
1176e5b75505Sopenharmony_ci			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
1177e5b75505Sopenharmony_ci				  sizeof(struct in6_addr));
1178e5b75505Sopenharmony_ci			claddr6.sin6_port = htons(0);
1179e5b75505Sopenharmony_ci			cl_addr = (struct sockaddr *) &claddr6;
1180e5b75505Sopenharmony_ci			claddrlen = sizeof(claddr6);
1181e5b75505Sopenharmony_ci			break;
1182e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1183e5b75505Sopenharmony_ci		default:
1184e5b75505Sopenharmony_ci			return -1;
1185e5b75505Sopenharmony_ci		}
1186e5b75505Sopenharmony_ci
1187e5b75505Sopenharmony_ci		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
1188e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "bind[radius]: %s",
1189e5b75505Sopenharmony_ci				   strerror(errno));
1190e5b75505Sopenharmony_ci			return -1;
1191e5b75505Sopenharmony_ci		}
1192e5b75505Sopenharmony_ci	}
1193e5b75505Sopenharmony_ci
1194e5b75505Sopenharmony_ci	/* Force a reconnect by disconnecting the socket first */
1195e5b75505Sopenharmony_ci	if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
1196e5b75505Sopenharmony_ci		    sizeof(disconnect_addr)) < 0)
1197e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
1198e5b75505Sopenharmony_ci
1199e5b75505Sopenharmony_ci	if (connect(sel_sock, addr, addrlen) < 0) {
1200e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
1201e5b75505Sopenharmony_ci		return -1;
1202e5b75505Sopenharmony_ci	}
1203e5b75505Sopenharmony_ci
1204e5b75505Sopenharmony_ci#ifndef CONFIG_NATIVE_WINDOWS
1205e5b75505Sopenharmony_ci	switch (nserv->addr.af) {
1206e5b75505Sopenharmony_ci	case AF_INET:
1207e5b75505Sopenharmony_ci		claddrlen = sizeof(claddr);
1208e5b75505Sopenharmony_ci		if (getsockname(sel_sock, (struct sockaddr *) &claddr,
1209e5b75505Sopenharmony_ci				&claddrlen) == 0) {
1210e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1211e5b75505Sopenharmony_ci				   inet_ntoa(claddr.sin_addr),
1212e5b75505Sopenharmony_ci				   ntohs(claddr.sin_port));
1213e5b75505Sopenharmony_ci		}
1214e5b75505Sopenharmony_ci		break;
1215e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1216e5b75505Sopenharmony_ci	case AF_INET6: {
1217e5b75505Sopenharmony_ci		claddrlen = sizeof(claddr6);
1218e5b75505Sopenharmony_ci		if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
1219e5b75505Sopenharmony_ci				&claddrlen) == 0) {
1220e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1221e5b75505Sopenharmony_ci				   inet_ntop(AF_INET6, &claddr6.sin6_addr,
1222e5b75505Sopenharmony_ci					     abuf, sizeof(abuf)),
1223e5b75505Sopenharmony_ci				   ntohs(claddr6.sin6_port));
1224e5b75505Sopenharmony_ci		}
1225e5b75505Sopenharmony_ci		break;
1226e5b75505Sopenharmony_ci	}
1227e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1228e5b75505Sopenharmony_ci	}
1229e5b75505Sopenharmony_ci#endif /* CONFIG_NATIVE_WINDOWS */
1230e5b75505Sopenharmony_ci
1231e5b75505Sopenharmony_ci	if (auth)
1232e5b75505Sopenharmony_ci		radius->auth_sock = sel_sock;
1233e5b75505Sopenharmony_ci	else
1234e5b75505Sopenharmony_ci		radius->acct_sock = sel_sock;
1235e5b75505Sopenharmony_ci
1236e5b75505Sopenharmony_ci	return 0;
1237e5b75505Sopenharmony_ci}
1238e5b75505Sopenharmony_ci
1239e5b75505Sopenharmony_ci
1240e5b75505Sopenharmony_cistatic void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
1241e5b75505Sopenharmony_ci{
1242e5b75505Sopenharmony_ci	struct radius_client_data *radius = eloop_ctx;
1243e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
1244e5b75505Sopenharmony_ci	struct hostapd_radius_server *oserv;
1245e5b75505Sopenharmony_ci
1246e5b75505Sopenharmony_ci	if (radius->auth_sock >= 0 && conf->auth_servers &&
1247e5b75505Sopenharmony_ci	    conf->auth_server != conf->auth_servers) {
1248e5b75505Sopenharmony_ci		oserv = conf->auth_server;
1249e5b75505Sopenharmony_ci		conf->auth_server = conf->auth_servers;
1250e5b75505Sopenharmony_ci		if (radius_change_server(radius, conf->auth_server, oserv,
1251e5b75505Sopenharmony_ci					 radius->auth_serv_sock,
1252e5b75505Sopenharmony_ci					 radius->auth_serv_sock6, 1) < 0) {
1253e5b75505Sopenharmony_ci			conf->auth_server = oserv;
1254e5b75505Sopenharmony_ci			radius_change_server(radius, oserv, conf->auth_server,
1255e5b75505Sopenharmony_ci					     radius->auth_serv_sock,
1256e5b75505Sopenharmony_ci					     radius->auth_serv_sock6, 1);
1257e5b75505Sopenharmony_ci		}
1258e5b75505Sopenharmony_ci	}
1259e5b75505Sopenharmony_ci
1260e5b75505Sopenharmony_ci	if (radius->acct_sock >= 0 && conf->acct_servers &&
1261e5b75505Sopenharmony_ci	    conf->acct_server != conf->acct_servers) {
1262e5b75505Sopenharmony_ci		oserv = conf->acct_server;
1263e5b75505Sopenharmony_ci		conf->acct_server = conf->acct_servers;
1264e5b75505Sopenharmony_ci		if (radius_change_server(radius, conf->acct_server, oserv,
1265e5b75505Sopenharmony_ci					 radius->acct_serv_sock,
1266e5b75505Sopenharmony_ci					 radius->acct_serv_sock6, 0) < 0) {
1267e5b75505Sopenharmony_ci			conf->acct_server = oserv;
1268e5b75505Sopenharmony_ci			radius_change_server(radius, oserv, conf->acct_server,
1269e5b75505Sopenharmony_ci					     radius->acct_serv_sock,
1270e5b75505Sopenharmony_ci					     radius->acct_serv_sock6, 0);
1271e5b75505Sopenharmony_ci		}
1272e5b75505Sopenharmony_ci	}
1273e5b75505Sopenharmony_ci
1274e5b75505Sopenharmony_ci	if (conf->retry_primary_interval)
1275e5b75505Sopenharmony_ci		eloop_register_timeout(conf->retry_primary_interval, 0,
1276e5b75505Sopenharmony_ci				       radius_retry_primary_timer, radius,
1277e5b75505Sopenharmony_ci				       NULL);
1278e5b75505Sopenharmony_ci}
1279e5b75505Sopenharmony_ci
1280e5b75505Sopenharmony_ci
1281e5b75505Sopenharmony_cistatic int radius_client_disable_pmtu_discovery(int s)
1282e5b75505Sopenharmony_ci{
1283e5b75505Sopenharmony_ci	int r = -1;
1284e5b75505Sopenharmony_ci#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1285e5b75505Sopenharmony_ci	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
1286e5b75505Sopenharmony_ci	int action = IP_PMTUDISC_DONT;
1287e5b75505Sopenharmony_ci	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
1288e5b75505Sopenharmony_ci		       sizeof(action));
1289e5b75505Sopenharmony_ci	if (r == -1)
1290e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
1291e5b75505Sopenharmony_ci			   strerror(errno));
1292e5b75505Sopenharmony_ci#endif
1293e5b75505Sopenharmony_ci	return r;
1294e5b75505Sopenharmony_ci}
1295e5b75505Sopenharmony_ci
1296e5b75505Sopenharmony_ci
1297e5b75505Sopenharmony_cistatic void radius_close_auth_sockets(struct radius_client_data *radius)
1298e5b75505Sopenharmony_ci{
1299e5b75505Sopenharmony_ci	radius->auth_sock = -1;
1300e5b75505Sopenharmony_ci
1301e5b75505Sopenharmony_ci	if (radius->auth_serv_sock >= 0) {
1302e5b75505Sopenharmony_ci		eloop_unregister_read_sock(radius->auth_serv_sock);
1303e5b75505Sopenharmony_ci		close(radius->auth_serv_sock);
1304e5b75505Sopenharmony_ci		radius->auth_serv_sock = -1;
1305e5b75505Sopenharmony_ci	}
1306e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1307e5b75505Sopenharmony_ci	if (radius->auth_serv_sock6 >= 0) {
1308e5b75505Sopenharmony_ci		eloop_unregister_read_sock(radius->auth_serv_sock6);
1309e5b75505Sopenharmony_ci		close(radius->auth_serv_sock6);
1310e5b75505Sopenharmony_ci		radius->auth_serv_sock6 = -1;
1311e5b75505Sopenharmony_ci	}
1312e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1313e5b75505Sopenharmony_ci}
1314e5b75505Sopenharmony_ci
1315e5b75505Sopenharmony_ci
1316e5b75505Sopenharmony_cistatic void radius_close_acct_sockets(struct radius_client_data *radius)
1317e5b75505Sopenharmony_ci{
1318e5b75505Sopenharmony_ci	radius->acct_sock = -1;
1319e5b75505Sopenharmony_ci
1320e5b75505Sopenharmony_ci	if (radius->acct_serv_sock >= 0) {
1321e5b75505Sopenharmony_ci		eloop_unregister_read_sock(radius->acct_serv_sock);
1322e5b75505Sopenharmony_ci		close(radius->acct_serv_sock);
1323e5b75505Sopenharmony_ci		radius->acct_serv_sock = -1;
1324e5b75505Sopenharmony_ci	}
1325e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1326e5b75505Sopenharmony_ci	if (radius->acct_serv_sock6 >= 0) {
1327e5b75505Sopenharmony_ci		eloop_unregister_read_sock(radius->acct_serv_sock6);
1328e5b75505Sopenharmony_ci		close(radius->acct_serv_sock6);
1329e5b75505Sopenharmony_ci		radius->acct_serv_sock6 = -1;
1330e5b75505Sopenharmony_ci	}
1331e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1332e5b75505Sopenharmony_ci}
1333e5b75505Sopenharmony_ci
1334e5b75505Sopenharmony_ci
1335e5b75505Sopenharmony_cistatic int radius_client_init_auth(struct radius_client_data *radius)
1336e5b75505Sopenharmony_ci{
1337e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
1338e5b75505Sopenharmony_ci	int ok = 0;
1339e5b75505Sopenharmony_ci
1340e5b75505Sopenharmony_ci	radius_close_auth_sockets(radius);
1341e5b75505Sopenharmony_ci
1342e5b75505Sopenharmony_ci	radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1343e5b75505Sopenharmony_ci	if (radius->auth_serv_sock < 0)
1344e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
1345e5b75505Sopenharmony_ci			   strerror(errno));
1346e5b75505Sopenharmony_ci	else {
1347e5b75505Sopenharmony_ci		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
1348e5b75505Sopenharmony_ci		ok++;
1349e5b75505Sopenharmony_ci	}
1350e5b75505Sopenharmony_ci
1351e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1352e5b75505Sopenharmony_ci	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1353e5b75505Sopenharmony_ci	if (radius->auth_serv_sock6 < 0)
1354e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
1355e5b75505Sopenharmony_ci			   strerror(errno));
1356e5b75505Sopenharmony_ci	else
1357e5b75505Sopenharmony_ci		ok++;
1358e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1359e5b75505Sopenharmony_ci
1360e5b75505Sopenharmony_ci	if (ok == 0)
1361e5b75505Sopenharmony_ci		return -1;
1362e5b75505Sopenharmony_ci
1363e5b75505Sopenharmony_ci	radius_change_server(radius, conf->auth_server, NULL,
1364e5b75505Sopenharmony_ci			     radius->auth_serv_sock, radius->auth_serv_sock6,
1365e5b75505Sopenharmony_ci			     1);
1366e5b75505Sopenharmony_ci
1367e5b75505Sopenharmony_ci	if (radius->auth_serv_sock >= 0 &&
1368e5b75505Sopenharmony_ci	    eloop_register_read_sock(radius->auth_serv_sock,
1369e5b75505Sopenharmony_ci				     radius_client_receive, radius,
1370e5b75505Sopenharmony_ci				     (void *) RADIUS_AUTH)) {
1371e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
1372e5b75505Sopenharmony_ci		radius_close_auth_sockets(radius);
1373e5b75505Sopenharmony_ci		return -1;
1374e5b75505Sopenharmony_ci	}
1375e5b75505Sopenharmony_ci
1376e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1377e5b75505Sopenharmony_ci	if (radius->auth_serv_sock6 >= 0 &&
1378e5b75505Sopenharmony_ci	    eloop_register_read_sock(radius->auth_serv_sock6,
1379e5b75505Sopenharmony_ci				     radius_client_receive, radius,
1380e5b75505Sopenharmony_ci				     (void *) RADIUS_AUTH)) {
1381e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
1382e5b75505Sopenharmony_ci		radius_close_auth_sockets(radius);
1383e5b75505Sopenharmony_ci		return -1;
1384e5b75505Sopenharmony_ci	}
1385e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1386e5b75505Sopenharmony_ci
1387e5b75505Sopenharmony_ci	return 0;
1388e5b75505Sopenharmony_ci}
1389e5b75505Sopenharmony_ci
1390e5b75505Sopenharmony_ci
1391e5b75505Sopenharmony_cistatic int radius_client_init_acct(struct radius_client_data *radius)
1392e5b75505Sopenharmony_ci{
1393e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf = radius->conf;
1394e5b75505Sopenharmony_ci	int ok = 0;
1395e5b75505Sopenharmony_ci
1396e5b75505Sopenharmony_ci	radius_close_acct_sockets(radius);
1397e5b75505Sopenharmony_ci
1398e5b75505Sopenharmony_ci	radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1399e5b75505Sopenharmony_ci	if (radius->acct_serv_sock < 0)
1400e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
1401e5b75505Sopenharmony_ci			   strerror(errno));
1402e5b75505Sopenharmony_ci	else {
1403e5b75505Sopenharmony_ci		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
1404e5b75505Sopenharmony_ci		ok++;
1405e5b75505Sopenharmony_ci	}
1406e5b75505Sopenharmony_ci
1407e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1408e5b75505Sopenharmony_ci	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1409e5b75505Sopenharmony_ci	if (radius->acct_serv_sock6 < 0)
1410e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
1411e5b75505Sopenharmony_ci			   strerror(errno));
1412e5b75505Sopenharmony_ci	else
1413e5b75505Sopenharmony_ci		ok++;
1414e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1415e5b75505Sopenharmony_ci
1416e5b75505Sopenharmony_ci	if (ok == 0)
1417e5b75505Sopenharmony_ci		return -1;
1418e5b75505Sopenharmony_ci
1419e5b75505Sopenharmony_ci	radius_change_server(radius, conf->acct_server, NULL,
1420e5b75505Sopenharmony_ci			     radius->acct_serv_sock, radius->acct_serv_sock6,
1421e5b75505Sopenharmony_ci			     0);
1422e5b75505Sopenharmony_ci
1423e5b75505Sopenharmony_ci	if (radius->acct_serv_sock >= 0 &&
1424e5b75505Sopenharmony_ci	    eloop_register_read_sock(radius->acct_serv_sock,
1425e5b75505Sopenharmony_ci				     radius_client_receive, radius,
1426e5b75505Sopenharmony_ci				     (void *) RADIUS_ACCT)) {
1427e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
1428e5b75505Sopenharmony_ci		radius_close_acct_sockets(radius);
1429e5b75505Sopenharmony_ci		return -1;
1430e5b75505Sopenharmony_ci	}
1431e5b75505Sopenharmony_ci
1432e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6
1433e5b75505Sopenharmony_ci	if (radius->acct_serv_sock6 >= 0 &&
1434e5b75505Sopenharmony_ci	    eloop_register_read_sock(radius->acct_serv_sock6,
1435e5b75505Sopenharmony_ci				     radius_client_receive, radius,
1436e5b75505Sopenharmony_ci				     (void *) RADIUS_ACCT)) {
1437e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
1438e5b75505Sopenharmony_ci		radius_close_acct_sockets(radius);
1439e5b75505Sopenharmony_ci		return -1;
1440e5b75505Sopenharmony_ci	}
1441e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */
1442e5b75505Sopenharmony_ci
1443e5b75505Sopenharmony_ci	return 0;
1444e5b75505Sopenharmony_ci}
1445e5b75505Sopenharmony_ci
1446e5b75505Sopenharmony_ci
1447e5b75505Sopenharmony_ci/**
1448e5b75505Sopenharmony_ci * radius_client_init - Initialize RADIUS client
1449e5b75505Sopenharmony_ci * @ctx: Callback context to be used in hostapd_logger() calls
1450e5b75505Sopenharmony_ci * @conf: RADIUS client configuration (RADIUS servers)
1451e5b75505Sopenharmony_ci * Returns: Pointer to private RADIUS client context or %NULL on failure
1452e5b75505Sopenharmony_ci *
1453e5b75505Sopenharmony_ci * The caller is responsible for keeping the configuration data available for
1454e5b75505Sopenharmony_ci * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1455e5b75505Sopenharmony_ci * called for the returned context pointer.
1456e5b75505Sopenharmony_ci */
1457e5b75505Sopenharmony_cistruct radius_client_data *
1458e5b75505Sopenharmony_ciradius_client_init(void *ctx, struct hostapd_radius_servers *conf)
1459e5b75505Sopenharmony_ci{
1460e5b75505Sopenharmony_ci	struct radius_client_data *radius;
1461e5b75505Sopenharmony_ci
1462e5b75505Sopenharmony_ci	radius = os_zalloc(sizeof(struct radius_client_data));
1463e5b75505Sopenharmony_ci	if (radius == NULL)
1464e5b75505Sopenharmony_ci		return NULL;
1465e5b75505Sopenharmony_ci
1466e5b75505Sopenharmony_ci	radius->ctx = ctx;
1467e5b75505Sopenharmony_ci	radius->conf = conf;
1468e5b75505Sopenharmony_ci	radius->auth_serv_sock = radius->acct_serv_sock =
1469e5b75505Sopenharmony_ci		radius->auth_serv_sock6 = radius->acct_serv_sock6 =
1470e5b75505Sopenharmony_ci		radius->auth_sock = radius->acct_sock = -1;
1471e5b75505Sopenharmony_ci
1472e5b75505Sopenharmony_ci	if (conf->auth_server && radius_client_init_auth(radius)) {
1473e5b75505Sopenharmony_ci		radius_client_deinit(radius);
1474e5b75505Sopenharmony_ci		return NULL;
1475e5b75505Sopenharmony_ci	}
1476e5b75505Sopenharmony_ci
1477e5b75505Sopenharmony_ci	if (conf->acct_server && radius_client_init_acct(radius)) {
1478e5b75505Sopenharmony_ci		radius_client_deinit(radius);
1479e5b75505Sopenharmony_ci		return NULL;
1480e5b75505Sopenharmony_ci	}
1481e5b75505Sopenharmony_ci
1482e5b75505Sopenharmony_ci	if (conf->retry_primary_interval)
1483e5b75505Sopenharmony_ci		eloop_register_timeout(conf->retry_primary_interval, 0,
1484e5b75505Sopenharmony_ci				       radius_retry_primary_timer, radius,
1485e5b75505Sopenharmony_ci				       NULL);
1486e5b75505Sopenharmony_ci
1487e5b75505Sopenharmony_ci	return radius;
1488e5b75505Sopenharmony_ci}
1489e5b75505Sopenharmony_ci
1490e5b75505Sopenharmony_ci
1491e5b75505Sopenharmony_ci/**
1492e5b75505Sopenharmony_ci * radius_client_deinit - Deinitialize RADIUS client
1493e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
1494e5b75505Sopenharmony_ci */
1495e5b75505Sopenharmony_civoid radius_client_deinit(struct radius_client_data *radius)
1496e5b75505Sopenharmony_ci{
1497e5b75505Sopenharmony_ci	if (!radius)
1498e5b75505Sopenharmony_ci		return;
1499e5b75505Sopenharmony_ci
1500e5b75505Sopenharmony_ci	radius_close_auth_sockets(radius);
1501e5b75505Sopenharmony_ci	radius_close_acct_sockets(radius);
1502e5b75505Sopenharmony_ci
1503e5b75505Sopenharmony_ci	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
1504e5b75505Sopenharmony_ci
1505e5b75505Sopenharmony_ci	radius_client_flush(radius, 0);
1506e5b75505Sopenharmony_ci	os_free(radius->auth_handlers);
1507e5b75505Sopenharmony_ci	os_free(radius->acct_handlers);
1508e5b75505Sopenharmony_ci	os_free(radius);
1509e5b75505Sopenharmony_ci}
1510e5b75505Sopenharmony_ci
1511e5b75505Sopenharmony_ci
1512e5b75505Sopenharmony_ci/**
1513e5b75505Sopenharmony_ci * radius_client_flush_auth - Flush pending RADIUS messages for an address
1514e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
1515e5b75505Sopenharmony_ci * @addr: MAC address of the related device
1516e5b75505Sopenharmony_ci *
1517e5b75505Sopenharmony_ci * This function can be used to remove pending RADIUS authentication messages
1518e5b75505Sopenharmony_ci * that are related to a specific device. The addr parameter is matched with
1519e5b75505Sopenharmony_ci * the one used in radius_client_send() call that was used to transmit the
1520e5b75505Sopenharmony_ci * authentication request.
1521e5b75505Sopenharmony_ci */
1522e5b75505Sopenharmony_civoid radius_client_flush_auth(struct radius_client_data *radius,
1523e5b75505Sopenharmony_ci			      const u8 *addr)
1524e5b75505Sopenharmony_ci{
1525e5b75505Sopenharmony_ci	struct radius_msg_list *entry, *prev, *tmp;
1526e5b75505Sopenharmony_ci
1527e5b75505Sopenharmony_ci	prev = NULL;
1528e5b75505Sopenharmony_ci	entry = radius->msgs;
1529e5b75505Sopenharmony_ci	while (entry) {
1530e5b75505Sopenharmony_ci		if (entry->msg_type == RADIUS_AUTH &&
1531e5b75505Sopenharmony_ci		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
1532e5b75505Sopenharmony_ci			hostapd_logger(radius->ctx, addr,
1533e5b75505Sopenharmony_ci				       HOSTAPD_MODULE_RADIUS,
1534e5b75505Sopenharmony_ci				       HOSTAPD_LEVEL_DEBUG,
1535e5b75505Sopenharmony_ci				       "Removing pending RADIUS authentication"
1536e5b75505Sopenharmony_ci				       " message for removed client");
1537e5b75505Sopenharmony_ci
1538e5b75505Sopenharmony_ci			if (prev)
1539e5b75505Sopenharmony_ci				prev->next = entry->next;
1540e5b75505Sopenharmony_ci			else
1541e5b75505Sopenharmony_ci				radius->msgs = entry->next;
1542e5b75505Sopenharmony_ci
1543e5b75505Sopenharmony_ci			tmp = entry;
1544e5b75505Sopenharmony_ci			entry = entry->next;
1545e5b75505Sopenharmony_ci			radius_client_msg_free(tmp);
1546e5b75505Sopenharmony_ci			radius->num_msgs--;
1547e5b75505Sopenharmony_ci			continue;
1548e5b75505Sopenharmony_ci		}
1549e5b75505Sopenharmony_ci
1550e5b75505Sopenharmony_ci		prev = entry;
1551e5b75505Sopenharmony_ci		entry = entry->next;
1552e5b75505Sopenharmony_ci	}
1553e5b75505Sopenharmony_ci}
1554e5b75505Sopenharmony_ci
1555e5b75505Sopenharmony_ci
1556e5b75505Sopenharmony_cistatic int radius_client_dump_auth_server(char *buf, size_t buflen,
1557e5b75505Sopenharmony_ci					  struct hostapd_radius_server *serv,
1558e5b75505Sopenharmony_ci					  struct radius_client_data *cli)
1559e5b75505Sopenharmony_ci{
1560e5b75505Sopenharmony_ci	int pending = 0;
1561e5b75505Sopenharmony_ci	struct radius_msg_list *msg;
1562e5b75505Sopenharmony_ci	char abuf[50];
1563e5b75505Sopenharmony_ci
1564e5b75505Sopenharmony_ci	if (cli) {
1565e5b75505Sopenharmony_ci		for (msg = cli->msgs; msg; msg = msg->next) {
1566e5b75505Sopenharmony_ci			if (msg->msg_type == RADIUS_AUTH)
1567e5b75505Sopenharmony_ci				pending++;
1568e5b75505Sopenharmony_ci		}
1569e5b75505Sopenharmony_ci	}
1570e5b75505Sopenharmony_ci
1571e5b75505Sopenharmony_ci	return os_snprintf(buf, buflen,
1572e5b75505Sopenharmony_ci			   "radiusAuthServerIndex=%d\n"
1573e5b75505Sopenharmony_ci			   "radiusAuthServerAddress=%s\n"
1574e5b75505Sopenharmony_ci			   "radiusAuthClientServerPortNumber=%d\n"
1575e5b75505Sopenharmony_ci			   "radiusAuthClientRoundTripTime=%d\n"
1576e5b75505Sopenharmony_ci			   "radiusAuthClientAccessRequests=%u\n"
1577e5b75505Sopenharmony_ci			   "radiusAuthClientAccessRetransmissions=%u\n"
1578e5b75505Sopenharmony_ci			   "radiusAuthClientAccessAccepts=%u\n"
1579e5b75505Sopenharmony_ci			   "radiusAuthClientAccessRejects=%u\n"
1580e5b75505Sopenharmony_ci			   "radiusAuthClientAccessChallenges=%u\n"
1581e5b75505Sopenharmony_ci			   "radiusAuthClientMalformedAccessResponses=%u\n"
1582e5b75505Sopenharmony_ci			   "radiusAuthClientBadAuthenticators=%u\n"
1583e5b75505Sopenharmony_ci			   "radiusAuthClientPendingRequests=%u\n"
1584e5b75505Sopenharmony_ci			   "radiusAuthClientTimeouts=%u\n"
1585e5b75505Sopenharmony_ci			   "radiusAuthClientUnknownTypes=%u\n"
1586e5b75505Sopenharmony_ci			   "radiusAuthClientPacketsDropped=%u\n",
1587e5b75505Sopenharmony_ci			   serv->index,
1588e5b75505Sopenharmony_ci			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1589e5b75505Sopenharmony_ci			   serv->port,
1590e5b75505Sopenharmony_ci			   serv->round_trip_time,
1591e5b75505Sopenharmony_ci			   serv->requests,
1592e5b75505Sopenharmony_ci			   serv->retransmissions,
1593e5b75505Sopenharmony_ci			   serv->access_accepts,
1594e5b75505Sopenharmony_ci			   serv->access_rejects,
1595e5b75505Sopenharmony_ci			   serv->access_challenges,
1596e5b75505Sopenharmony_ci			   serv->malformed_responses,
1597e5b75505Sopenharmony_ci			   serv->bad_authenticators,
1598e5b75505Sopenharmony_ci			   pending,
1599e5b75505Sopenharmony_ci			   serv->timeouts,
1600e5b75505Sopenharmony_ci			   serv->unknown_types,
1601e5b75505Sopenharmony_ci			   serv->packets_dropped);
1602e5b75505Sopenharmony_ci}
1603e5b75505Sopenharmony_ci
1604e5b75505Sopenharmony_ci
1605e5b75505Sopenharmony_cistatic int radius_client_dump_acct_server(char *buf, size_t buflen,
1606e5b75505Sopenharmony_ci					  struct hostapd_radius_server *serv,
1607e5b75505Sopenharmony_ci					  struct radius_client_data *cli)
1608e5b75505Sopenharmony_ci{
1609e5b75505Sopenharmony_ci	int pending = 0;
1610e5b75505Sopenharmony_ci	struct radius_msg_list *msg;
1611e5b75505Sopenharmony_ci	char abuf[50];
1612e5b75505Sopenharmony_ci
1613e5b75505Sopenharmony_ci	if (cli) {
1614e5b75505Sopenharmony_ci		for (msg = cli->msgs; msg; msg = msg->next) {
1615e5b75505Sopenharmony_ci			if (msg->msg_type == RADIUS_ACCT ||
1616e5b75505Sopenharmony_ci			    msg->msg_type == RADIUS_ACCT_INTERIM)
1617e5b75505Sopenharmony_ci				pending++;
1618e5b75505Sopenharmony_ci		}
1619e5b75505Sopenharmony_ci	}
1620e5b75505Sopenharmony_ci
1621e5b75505Sopenharmony_ci	return os_snprintf(buf, buflen,
1622e5b75505Sopenharmony_ci			   "radiusAccServerIndex=%d\n"
1623e5b75505Sopenharmony_ci			   "radiusAccServerAddress=%s\n"
1624e5b75505Sopenharmony_ci			   "radiusAccClientServerPortNumber=%d\n"
1625e5b75505Sopenharmony_ci			   "radiusAccClientRoundTripTime=%d\n"
1626e5b75505Sopenharmony_ci			   "radiusAccClientRequests=%u\n"
1627e5b75505Sopenharmony_ci			   "radiusAccClientRetransmissions=%u\n"
1628e5b75505Sopenharmony_ci			   "radiusAccClientResponses=%u\n"
1629e5b75505Sopenharmony_ci			   "radiusAccClientMalformedResponses=%u\n"
1630e5b75505Sopenharmony_ci			   "radiusAccClientBadAuthenticators=%u\n"
1631e5b75505Sopenharmony_ci			   "radiusAccClientPendingRequests=%u\n"
1632e5b75505Sopenharmony_ci			   "radiusAccClientTimeouts=%u\n"
1633e5b75505Sopenharmony_ci			   "radiusAccClientUnknownTypes=%u\n"
1634e5b75505Sopenharmony_ci			   "radiusAccClientPacketsDropped=%u\n",
1635e5b75505Sopenharmony_ci			   serv->index,
1636e5b75505Sopenharmony_ci			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1637e5b75505Sopenharmony_ci			   serv->port,
1638e5b75505Sopenharmony_ci			   serv->round_trip_time,
1639e5b75505Sopenharmony_ci			   serv->requests,
1640e5b75505Sopenharmony_ci			   serv->retransmissions,
1641e5b75505Sopenharmony_ci			   serv->responses,
1642e5b75505Sopenharmony_ci			   serv->malformed_responses,
1643e5b75505Sopenharmony_ci			   serv->bad_authenticators,
1644e5b75505Sopenharmony_ci			   pending,
1645e5b75505Sopenharmony_ci			   serv->timeouts,
1646e5b75505Sopenharmony_ci			   serv->unknown_types,
1647e5b75505Sopenharmony_ci			   serv->packets_dropped);
1648e5b75505Sopenharmony_ci}
1649e5b75505Sopenharmony_ci
1650e5b75505Sopenharmony_ci
1651e5b75505Sopenharmony_ci/**
1652e5b75505Sopenharmony_ci * radius_client_get_mib - Get RADIUS client MIB information
1653e5b75505Sopenharmony_ci * @radius: RADIUS client context from radius_client_init()
1654e5b75505Sopenharmony_ci * @buf: Buffer for returning MIB data in text format
1655e5b75505Sopenharmony_ci * @buflen: Maximum buf length in octets
1656e5b75505Sopenharmony_ci * Returns: Number of octets written into the buffer
1657e5b75505Sopenharmony_ci */
1658e5b75505Sopenharmony_ciint radius_client_get_mib(struct radius_client_data *radius, char *buf,
1659e5b75505Sopenharmony_ci			  size_t buflen)
1660e5b75505Sopenharmony_ci{
1661e5b75505Sopenharmony_ci	struct hostapd_radius_servers *conf;
1662e5b75505Sopenharmony_ci	int i;
1663e5b75505Sopenharmony_ci	struct hostapd_radius_server *serv;
1664e5b75505Sopenharmony_ci	int count = 0;
1665e5b75505Sopenharmony_ci
1666e5b75505Sopenharmony_ci	if (!radius)
1667e5b75505Sopenharmony_ci		return 0;
1668e5b75505Sopenharmony_ci
1669e5b75505Sopenharmony_ci	conf = radius->conf;
1670e5b75505Sopenharmony_ci
1671e5b75505Sopenharmony_ci	if (conf->auth_servers) {
1672e5b75505Sopenharmony_ci		for (i = 0; i < conf->num_auth_servers; i++) {
1673e5b75505Sopenharmony_ci			serv = &conf->auth_servers[i];
1674e5b75505Sopenharmony_ci			count += radius_client_dump_auth_server(
1675e5b75505Sopenharmony_ci				buf + count, buflen - count, serv,
1676e5b75505Sopenharmony_ci				serv == conf->auth_server ?
1677e5b75505Sopenharmony_ci				radius : NULL);
1678e5b75505Sopenharmony_ci		}
1679e5b75505Sopenharmony_ci	}
1680e5b75505Sopenharmony_ci
1681e5b75505Sopenharmony_ci	if (conf->acct_servers) {
1682e5b75505Sopenharmony_ci		for (i = 0; i < conf->num_acct_servers; i++) {
1683e5b75505Sopenharmony_ci			serv = &conf->acct_servers[i];
1684e5b75505Sopenharmony_ci			count += radius_client_dump_acct_server(
1685e5b75505Sopenharmony_ci				buf + count, buflen - count, serv,
1686e5b75505Sopenharmony_ci				serv == conf->acct_server ?
1687e5b75505Sopenharmony_ci				radius : NULL);
1688e5b75505Sopenharmony_ci		}
1689e5b75505Sopenharmony_ci	}
1690e5b75505Sopenharmony_ci
1691e5b75505Sopenharmony_ci	return count;
1692e5b75505Sopenharmony_ci}
1693e5b75505Sopenharmony_ci
1694e5b75505Sopenharmony_ci
1695e5b75505Sopenharmony_civoid radius_client_reconfig(struct radius_client_data *radius,
1696e5b75505Sopenharmony_ci			    struct hostapd_radius_servers *conf)
1697e5b75505Sopenharmony_ci{
1698e5b75505Sopenharmony_ci	if (radius)
1699e5b75505Sopenharmony_ci		radius->conf = conf;
1700e5b75505Sopenharmony_ci}
1701