1189251Ssam/*
2214734Srpaulo * RADIUS client
3214734Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12189251Ssam#include "radius.h"
13189251Ssam#include "radius_client.h"
14189251Ssam#include "eloop.h"
15189251Ssam
16189251Ssam/* Defaults for RADIUS retransmit values (exponential backoff) */
17189251Ssam
18214734Srpaulo/**
19214734Srpaulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
20214734Srpaulo */
21214734Srpaulo#define RADIUS_CLIENT_FIRST_WAIT 3
22189251Ssam
23214734Srpaulo/**
24214734Srpaulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
25214734Srpaulo */
26214734Srpaulo#define RADIUS_CLIENT_MAX_WAIT 120
27214734Srpaulo
28214734Srpaulo/**
29214734Srpaulo * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
30214734Srpaulo *
31214734Srpaulo * Maximum number of retransmit attempts before the entry is removed from
32214734Srpaulo * retransmit list.
33214734Srpaulo */
34214734Srpaulo#define RADIUS_CLIENT_MAX_RETRIES 10
35214734Srpaulo
36214734Srpaulo/**
37214734Srpaulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
38214734Srpaulo *
39214734Srpaulo * Maximum number of entries in retransmit list (oldest entries will be
40214734Srpaulo * removed, if this limit is exceeded).
41214734Srpaulo */
42214734Srpaulo#define RADIUS_CLIENT_MAX_ENTRIES 30
43214734Srpaulo
44214734Srpaulo/**
45214734Srpaulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
46214734Srpaulo *
47214734Srpaulo * The number of failed retry attempts after which the RADIUS server will be
48214734Srpaulo * changed (if one of more backup servers are configured).
49214734Srpaulo */
50214734Srpaulo#define RADIUS_CLIENT_NUM_FAILOVER 4
51214734Srpaulo
52214734Srpaulo
53214734Srpaulo/**
54214734Srpaulo * struct radius_rx_handler - RADIUS client RX handler
55214734Srpaulo *
56214734Srpaulo * This data structure is used internally inside the RADIUS client module to
57214734Srpaulo * store registered RX handlers. These handlers are registered by calls to
58214734Srpaulo * radius_client_register() and unregistered when the RADIUS client is
59214734Srpaulo * deinitialized with a call to radius_client_deinit().
60214734Srpaulo */
61189251Ssamstruct radius_rx_handler {
62214734Srpaulo	/**
63214734Srpaulo	 * handler - Received RADIUS message handler
64214734Srpaulo	 */
65189251Ssam	RadiusRxResult (*handler)(struct radius_msg *msg,
66189251Ssam				  struct radius_msg *req,
67189251Ssam				  const u8 *shared_secret,
68189251Ssam				  size_t shared_secret_len,
69189251Ssam				  void *data);
70214734Srpaulo
71214734Srpaulo	/**
72214734Srpaulo	 * data - Context data for the handler
73214734Srpaulo	 */
74189251Ssam	void *data;
75189251Ssam};
76189251Ssam
77189251Ssam
78214734Srpaulo/**
79214734Srpaulo * struct radius_msg_list - RADIUS client message retransmit list
80214734Srpaulo *
81214734Srpaulo * This data structure is used internally inside the RADIUS client module to
82214734Srpaulo * store pending RADIUS requests that may still need to be retransmitted.
83214734Srpaulo */
84189251Ssamstruct radius_msg_list {
85214734Srpaulo	/**
86214734Srpaulo	 * addr - STA/client address
87214734Srpaulo	 *
88214734Srpaulo	 * This is used to find RADIUS messages for the same STA.
89214734Srpaulo	 */
90214734Srpaulo	u8 addr[ETH_ALEN];
91214734Srpaulo
92214734Srpaulo	/**
93214734Srpaulo	 * msg - RADIUS message
94214734Srpaulo	 */
95189251Ssam	struct radius_msg *msg;
96214734Srpaulo
97214734Srpaulo	/**
98214734Srpaulo	 * msg_type - Message type
99214734Srpaulo	 */
100189251Ssam	RadiusType msg_type;
101214734Srpaulo
102214734Srpaulo	/**
103214734Srpaulo	 * first_try - Time of the first transmission attempt
104214734Srpaulo	 */
105189251Ssam	os_time_t first_try;
106214734Srpaulo
107214734Srpaulo	/**
108214734Srpaulo	 * next_try - Time for the next transmission attempt
109214734Srpaulo	 */
110189251Ssam	os_time_t next_try;
111214734Srpaulo
112214734Srpaulo	/**
113214734Srpaulo	 * attempts - Number of transmission attempts
114214734Srpaulo	 */
115189251Ssam	int attempts;
116214734Srpaulo
117214734Srpaulo	/**
118214734Srpaulo	 * next_wait - Next retransmission wait time in seconds
119214734Srpaulo	 */
120189251Ssam	int next_wait;
121214734Srpaulo
122214734Srpaulo	/**
123214734Srpaulo	 * last_attempt - Time of the last transmission attempt
124214734Srpaulo	 */
125189251Ssam	struct os_time last_attempt;
126189251Ssam
127214734Srpaulo	/**
128214734Srpaulo	 * shared_secret - Shared secret with the target RADIUS server
129214734Srpaulo	 */
130214734Srpaulo	const u8 *shared_secret;
131214734Srpaulo
132214734Srpaulo	/**
133214734Srpaulo	 * shared_secret_len - shared_secret length in octets
134214734Srpaulo	 */
135189251Ssam	size_t shared_secret_len;
136189251Ssam
137189251Ssam	/* TODO: server config with failover to backup server(s) */
138189251Ssam
139214734Srpaulo	/**
140214734Srpaulo	 * next - Next message in the list
141214734Srpaulo	 */
142189251Ssam	struct radius_msg_list *next;
143189251Ssam};
144189251Ssam
145189251Ssam
146214734Srpaulo/**
147214734Srpaulo * struct radius_client_data - Internal RADIUS client data
148214734Srpaulo *
149214734Srpaulo * This data structure is used internally inside the RADIUS client module.
150214734Srpaulo * External users allocate this by calling radius_client_init() and free it by
151214734Srpaulo * calling radius_client_deinit(). The pointer to this opaque data is used in
152214734Srpaulo * calls to other functions as an identifier for the RADIUS client instance.
153214734Srpaulo */
154189251Ssamstruct radius_client_data {
155214734Srpaulo	/**
156214734Srpaulo	 * ctx - Context pointer for hostapd_logger() callbacks
157214734Srpaulo	 */
158189251Ssam	void *ctx;
159214734Srpaulo
160214734Srpaulo	/**
161214734Srpaulo	 * conf - RADIUS client configuration (list of RADIUS servers to use)
162214734Srpaulo	 */
163189251Ssam	struct hostapd_radius_servers *conf;
164189251Ssam
165214734Srpaulo	/**
166214734Srpaulo	 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
167214734Srpaulo	 */
168214734Srpaulo	int auth_serv_sock;
169214734Srpaulo
170214734Srpaulo	/**
171214734Srpaulo	 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
172214734Srpaulo	 */
173214734Srpaulo	int acct_serv_sock;
174214734Srpaulo
175214734Srpaulo	/**
176214734Srpaulo	 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
177214734Srpaulo	 */
178189251Ssam	int auth_serv_sock6;
179214734Srpaulo
180214734Srpaulo	/**
181214734Srpaulo	 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
182214734Srpaulo	 */
183189251Ssam	int acct_serv_sock6;
184189251Ssam
185214734Srpaulo	/**
186214734Srpaulo	 * auth_sock - Currently used socket for RADIUS authentication server
187214734Srpaulo	 */
188214734Srpaulo	int auth_sock;
189214734Srpaulo
190214734Srpaulo	/**
191214734Srpaulo	 * acct_sock - Currently used socket for RADIUS accounting server
192214734Srpaulo	 */
193214734Srpaulo	int acct_sock;
194214734Srpaulo
195214734Srpaulo	/**
196214734Srpaulo	 * auth_handlers - Authentication message handlers
197214734Srpaulo	 */
198189251Ssam	struct radius_rx_handler *auth_handlers;
199214734Srpaulo
200214734Srpaulo	/**
201214734Srpaulo	 * num_auth_handlers - Number of handlers in auth_handlers
202214734Srpaulo	 */
203189251Ssam	size_t num_auth_handlers;
204214734Srpaulo
205214734Srpaulo	/**
206214734Srpaulo	 * acct_handlers - Accounting message handlers
207214734Srpaulo	 */
208189251Ssam	struct radius_rx_handler *acct_handlers;
209214734Srpaulo
210214734Srpaulo	/**
211214734Srpaulo	 * num_acct_handlers - Number of handlers in acct_handlers
212214734Srpaulo	 */
213189251Ssam	size_t num_acct_handlers;
214189251Ssam
215214734Srpaulo	/**
216214734Srpaulo	 * msgs - Pending outgoing RADIUS messages
217214734Srpaulo	 */
218189251Ssam	struct radius_msg_list *msgs;
219214734Srpaulo
220214734Srpaulo	/**
221214734Srpaulo	 * num_msgs - Number of pending messages in the msgs list
222214734Srpaulo	 */
223189251Ssam	size_t num_msgs;
224189251Ssam
225214734Srpaulo	/**
226214734Srpaulo	 * next_radius_identifier - Next RADIUS message identifier to use
227214734Srpaulo	 */
228189251Ssam	u8 next_radius_identifier;
229189251Ssam};
230189251Ssam
231189251Ssam
232189251Ssamstatic int
233189251Ssamradius_change_server(struct radius_client_data *radius,
234189251Ssam		     struct hostapd_radius_server *nserv,
235189251Ssam		     struct hostapd_radius_server *oserv,
236189251Ssam		     int sock, int sock6, int auth);
237189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius);
238189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius);
239189251Ssam
240189251Ssam
241189251Ssamstatic void radius_client_msg_free(struct radius_msg_list *req)
242189251Ssam{
243189251Ssam	radius_msg_free(req->msg);
244189251Ssam	os_free(req);
245189251Ssam}
246189251Ssam
247189251Ssam
248214734Srpaulo/**
249214734Srpaulo * radius_client_register - Register a RADIUS client RX handler
250214734Srpaulo * @radius: RADIUS client context from radius_client_init()
251214734Srpaulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
252214734Srpaulo * @handler: Handler for received RADIUS messages
253214734Srpaulo * @data: Context pointer for handler callbacks
254214734Srpaulo * Returns: 0 on success, -1 on failure
255214734Srpaulo *
256214734Srpaulo * This function is used to register a handler for processing received RADIUS
257214734Srpaulo * authentication and accounting messages. The handler() callback function will
258214734Srpaulo * be called whenever a RADIUS message is received from the active server.
259214734Srpaulo *
260214734Srpaulo * There can be multiple registered RADIUS message handlers. The handlers will
261214734Srpaulo * be called in order until one of them indicates that it has processed or
262214734Srpaulo * queued the message.
263214734Srpaulo */
264189251Ssamint radius_client_register(struct radius_client_data *radius,
265189251Ssam			   RadiusType msg_type,
266189251Ssam			   RadiusRxResult (*handler)(struct radius_msg *msg,
267189251Ssam						     struct radius_msg *req,
268189251Ssam						     const u8 *shared_secret,
269189251Ssam						     size_t shared_secret_len,
270189251Ssam						     void *data),
271189251Ssam			   void *data)
272189251Ssam{
273189251Ssam	struct radius_rx_handler **handlers, *newh;
274189251Ssam	size_t *num;
275189251Ssam
276189251Ssam	if (msg_type == RADIUS_ACCT) {
277189251Ssam		handlers = &radius->acct_handlers;
278189251Ssam		num = &radius->num_acct_handlers;
279189251Ssam	} else {
280189251Ssam		handlers = &radius->auth_handlers;
281189251Ssam		num = &radius->num_auth_handlers;
282189251Ssam	}
283189251Ssam
284252726Srpaulo	newh = os_realloc_array(*handlers, *num + 1,
285252726Srpaulo				sizeof(struct radius_rx_handler));
286189251Ssam	if (newh == NULL)
287189251Ssam		return -1;
288189251Ssam
289189251Ssam	newh[*num].handler = handler;
290189251Ssam	newh[*num].data = data;
291189251Ssam	(*num)++;
292189251Ssam	*handlers = newh;
293189251Ssam
294189251Ssam	return 0;
295189251Ssam}
296189251Ssam
297189251Ssam
298189251Ssamstatic void radius_client_handle_send_error(struct radius_client_data *radius,
299189251Ssam					    int s, RadiusType msg_type)
300189251Ssam{
301189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
302189251Ssam	int _errno = errno;
303189251Ssam	perror("send[RADIUS]");
304189251Ssam	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
305189251Ssam	    _errno == EBADF) {
306189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
307189251Ssam			       HOSTAPD_LEVEL_INFO,
308189251Ssam			       "Send failed - maybe interface status changed -"
309189251Ssam			       " try to connect again");
310189251Ssam		eloop_unregister_read_sock(s);
311189251Ssam		close(s);
312189251Ssam		if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
313189251Ssam			radius_client_init_acct(radius);
314189251Ssam		else
315189251Ssam			radius_client_init_auth(radius);
316189251Ssam	}
317189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
318189251Ssam}
319189251Ssam
320189251Ssam
321189251Ssamstatic int radius_client_retransmit(struct radius_client_data *radius,
322189251Ssam				    struct radius_msg_list *entry,
323189251Ssam				    os_time_t now)
324189251Ssam{
325189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
326189251Ssam	int s;
327214734Srpaulo	struct wpabuf *buf;
328189251Ssam
329189251Ssam	if (entry->msg_type == RADIUS_ACCT ||
330189251Ssam	    entry->msg_type == RADIUS_ACCT_INTERIM) {
331189251Ssam		s = radius->acct_sock;
332189251Ssam		if (entry->attempts == 0)
333189251Ssam			conf->acct_server->requests++;
334189251Ssam		else {
335189251Ssam			conf->acct_server->timeouts++;
336189251Ssam			conf->acct_server->retransmissions++;
337189251Ssam		}
338189251Ssam	} else {
339189251Ssam		s = radius->auth_sock;
340189251Ssam		if (entry->attempts == 0)
341189251Ssam			conf->auth_server->requests++;
342189251Ssam		else {
343189251Ssam			conf->auth_server->timeouts++;
344189251Ssam			conf->auth_server->retransmissions++;
345189251Ssam		}
346189251Ssam	}
347189251Ssam
348189251Ssam	/* retransmit; remove entry if too many attempts */
349189251Ssam	entry->attempts++;
350189251Ssam	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
351189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
352214734Srpaulo		       radius_msg_get_hdr(entry->msg)->identifier);
353189251Ssam
354189251Ssam	os_get_time(&entry->last_attempt);
355214734Srpaulo	buf = radius_msg_get_buf(entry->msg);
356214734Srpaulo	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
357189251Ssam		radius_client_handle_send_error(radius, s, entry->msg_type);
358189251Ssam
359189251Ssam	entry->next_try = now + entry->next_wait;
360189251Ssam	entry->next_wait *= 2;
361189251Ssam	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
362189251Ssam		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
363189251Ssam	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
364189251Ssam		printf("Removing un-ACKed RADIUS message due to too many "
365189251Ssam		       "failed retransmit attempts\n");
366189251Ssam		return 1;
367189251Ssam	}
368189251Ssam
369189251Ssam	return 0;
370189251Ssam}
371189251Ssam
372189251Ssam
373189251Ssamstatic void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
374189251Ssam{
375189251Ssam	struct radius_client_data *radius = eloop_ctx;
376189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
377189251Ssam	struct os_time now;
378189251Ssam	os_time_t first;
379189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
380189251Ssam	int auth_failover = 0, acct_failover = 0;
381189251Ssam	char abuf[50];
382189251Ssam
383189251Ssam	entry = radius->msgs;
384189251Ssam	if (!entry)
385189251Ssam		return;
386189251Ssam
387189251Ssam	os_get_time(&now);
388189251Ssam	first = 0;
389189251Ssam
390189251Ssam	prev = NULL;
391189251Ssam	while (entry) {
392189251Ssam		if (now.sec >= entry->next_try &&
393189251Ssam		    radius_client_retransmit(radius, entry, now.sec)) {
394189251Ssam			if (prev)
395189251Ssam				prev->next = entry->next;
396189251Ssam			else
397189251Ssam				radius->msgs = entry->next;
398189251Ssam
399189251Ssam			tmp = entry;
400189251Ssam			entry = entry->next;
401189251Ssam			radius_client_msg_free(tmp);
402189251Ssam			radius->num_msgs--;
403189251Ssam			continue;
404189251Ssam		}
405189251Ssam
406189251Ssam		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
407189251Ssam			if (entry->msg_type == RADIUS_ACCT ||
408189251Ssam			    entry->msg_type == RADIUS_ACCT_INTERIM)
409189251Ssam				acct_failover++;
410189251Ssam			else
411189251Ssam				auth_failover++;
412189251Ssam		}
413189251Ssam
414189251Ssam		if (first == 0 || entry->next_try < first)
415189251Ssam			first = entry->next_try;
416189251Ssam
417189251Ssam		prev = entry;
418189251Ssam		entry = entry->next;
419189251Ssam	}
420189251Ssam
421189251Ssam	if (radius->msgs) {
422189251Ssam		if (first < now.sec)
423189251Ssam			first = now.sec;
424189251Ssam		eloop_register_timeout(first - now.sec, 0,
425189251Ssam				       radius_client_timer, radius, NULL);
426189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
427189251Ssam			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
428189251Ssam			       "retransmit in %ld seconds",
429189251Ssam			       (long int) (first - now.sec));
430189251Ssam	}
431189251Ssam
432189251Ssam	if (auth_failover && conf->num_auth_servers > 1) {
433189251Ssam		struct hostapd_radius_server *next, *old;
434189251Ssam		old = conf->auth_server;
435189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
436189251Ssam			       HOSTAPD_LEVEL_NOTICE,
437189251Ssam			       "No response from Authentication server "
438189251Ssam			       "%s:%d - failover",
439189251Ssam			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
440189251Ssam			       old->port);
441189251Ssam
442189251Ssam		for (entry = radius->msgs; entry; entry = entry->next) {
443189251Ssam			if (entry->msg_type == RADIUS_AUTH)
444189251Ssam				old->timeouts++;
445189251Ssam		}
446189251Ssam
447189251Ssam		next = old + 1;
448189251Ssam		if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
449189251Ssam			next = conf->auth_servers;
450189251Ssam		conf->auth_server = next;
451189251Ssam		radius_change_server(radius, next, old,
452189251Ssam				     radius->auth_serv_sock,
453189251Ssam				     radius->auth_serv_sock6, 1);
454189251Ssam	}
455189251Ssam
456189251Ssam	if (acct_failover && conf->num_acct_servers > 1) {
457189251Ssam		struct hostapd_radius_server *next, *old;
458189251Ssam		old = conf->acct_server;
459189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
460189251Ssam			       HOSTAPD_LEVEL_NOTICE,
461189251Ssam			       "No response from Accounting server "
462189251Ssam			       "%s:%d - failover",
463189251Ssam			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
464189251Ssam			       old->port);
465189251Ssam
466189251Ssam		for (entry = radius->msgs; entry; entry = entry->next) {
467189251Ssam			if (entry->msg_type == RADIUS_ACCT ||
468189251Ssam			    entry->msg_type == RADIUS_ACCT_INTERIM)
469189251Ssam				old->timeouts++;
470189251Ssam		}
471189251Ssam
472189251Ssam		next = old + 1;
473189251Ssam		if (next > &conf->acct_servers[conf->num_acct_servers - 1])
474189251Ssam			next = conf->acct_servers;
475189251Ssam		conf->acct_server = next;
476189251Ssam		radius_change_server(radius, next, old,
477189251Ssam				     radius->acct_serv_sock,
478189251Ssam				     radius->acct_serv_sock6, 0);
479189251Ssam	}
480189251Ssam}
481189251Ssam
482189251Ssam
483189251Ssamstatic void radius_client_update_timeout(struct radius_client_data *radius)
484189251Ssam{
485189251Ssam	struct os_time now;
486189251Ssam	os_time_t first;
487189251Ssam	struct radius_msg_list *entry;
488189251Ssam
489189251Ssam	eloop_cancel_timeout(radius_client_timer, radius, NULL);
490189251Ssam
491189251Ssam	if (radius->msgs == NULL) {
492189251Ssam		return;
493189251Ssam	}
494189251Ssam
495189251Ssam	first = 0;
496189251Ssam	for (entry = radius->msgs; entry; entry = entry->next) {
497189251Ssam		if (first == 0 || entry->next_try < first)
498189251Ssam			first = entry->next_try;
499189251Ssam	}
500189251Ssam
501189251Ssam	os_get_time(&now);
502189251Ssam	if (first < now.sec)
503189251Ssam		first = now.sec;
504189251Ssam	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
505189251Ssam			       NULL);
506189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
507189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
508252726Srpaulo		       " %ld seconds", (long int) (first - now.sec));
509189251Ssam}
510189251Ssam
511189251Ssam
512189251Ssamstatic void radius_client_list_add(struct radius_client_data *radius,
513189251Ssam				   struct radius_msg *msg,
514214734Srpaulo				   RadiusType msg_type,
515214734Srpaulo				   const u8 *shared_secret,
516189251Ssam				   size_t shared_secret_len, const u8 *addr)
517189251Ssam{
518189251Ssam	struct radius_msg_list *entry, *prev;
519189251Ssam
520189251Ssam	if (eloop_terminated()) {
521189251Ssam		/* No point in adding entries to retransmit queue since event
522189251Ssam		 * loop has already been terminated. */
523189251Ssam		radius_msg_free(msg);
524189251Ssam		return;
525189251Ssam	}
526189251Ssam
527189251Ssam	entry = os_zalloc(sizeof(*entry));
528189251Ssam	if (entry == NULL) {
529189251Ssam		printf("Failed to add RADIUS packet into retransmit list\n");
530189251Ssam		radius_msg_free(msg);
531189251Ssam		return;
532189251Ssam	}
533189251Ssam
534189251Ssam	if (addr)
535189251Ssam		os_memcpy(entry->addr, addr, ETH_ALEN);
536189251Ssam	entry->msg = msg;
537189251Ssam	entry->msg_type = msg_type;
538189251Ssam	entry->shared_secret = shared_secret;
539189251Ssam	entry->shared_secret_len = shared_secret_len;
540189251Ssam	os_get_time(&entry->last_attempt);
541189251Ssam	entry->first_try = entry->last_attempt.sec;
542189251Ssam	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
543189251Ssam	entry->attempts = 1;
544189251Ssam	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
545189251Ssam	entry->next = radius->msgs;
546189251Ssam	radius->msgs = entry;
547189251Ssam	radius_client_update_timeout(radius);
548189251Ssam
549189251Ssam	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
550189251Ssam		printf("Removing the oldest un-ACKed RADIUS packet due to "
551189251Ssam		       "retransmit list limits.\n");
552189251Ssam		prev = NULL;
553189251Ssam		while (entry->next) {
554189251Ssam			prev = entry;
555189251Ssam			entry = entry->next;
556189251Ssam		}
557189251Ssam		if (prev) {
558189251Ssam			prev->next = NULL;
559189251Ssam			radius_client_msg_free(entry);
560189251Ssam		}
561189251Ssam	} else
562189251Ssam		radius->num_msgs++;
563189251Ssam}
564189251Ssam
565189251Ssam
566189251Ssamstatic void radius_client_list_del(struct radius_client_data *radius,
567189251Ssam				   RadiusType msg_type, const u8 *addr)
568189251Ssam{
569189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
570189251Ssam
571189251Ssam	if (addr == NULL)
572189251Ssam		return;
573189251Ssam
574189251Ssam	entry = radius->msgs;
575189251Ssam	prev = NULL;
576189251Ssam	while (entry) {
577189251Ssam		if (entry->msg_type == msg_type &&
578189251Ssam		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
579189251Ssam			if (prev)
580189251Ssam				prev->next = entry->next;
581189251Ssam			else
582189251Ssam				radius->msgs = entry->next;
583189251Ssam			tmp = entry;
584189251Ssam			entry = entry->next;
585189251Ssam			hostapd_logger(radius->ctx, addr,
586189251Ssam				       HOSTAPD_MODULE_RADIUS,
587189251Ssam				       HOSTAPD_LEVEL_DEBUG,
588189251Ssam				       "Removing matching RADIUS message");
589189251Ssam			radius_client_msg_free(tmp);
590189251Ssam			radius->num_msgs--;
591189251Ssam			continue;
592189251Ssam		}
593189251Ssam		prev = entry;
594189251Ssam		entry = entry->next;
595189251Ssam	}
596189251Ssam}
597189251Ssam
598189251Ssam
599214734Srpaulo/**
600214734Srpaulo * radius_client_send - Send a RADIUS request
601214734Srpaulo * @radius: RADIUS client context from radius_client_init()
602214734Srpaulo * @msg: RADIUS message to be sent
603214734Srpaulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
604214734Srpaulo * @addr: MAC address of the device related to this message or %NULL
605214734Srpaulo * Returns: 0 on success, -1 on failure
606214734Srpaulo *
607214734Srpaulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
608214734Srpaulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
609214734Srpaulo * between accounting and interim accounting messages is that the interim
610214734Srpaulo * message will override any pending interim accounting updates while a new
611214734Srpaulo * accounting message does not remove any pending messages.
612214734Srpaulo *
613214734Srpaulo * The message is added on the retransmission queue and will be retransmitted
614214734Srpaulo * automatically until a response is received or maximum number of retries
615214734Srpaulo * (RADIUS_CLIENT_MAX_RETRIES) is reached.
616214734Srpaulo *
617214734Srpaulo * The related device MAC address can be used to identify pending messages that
618214734Srpaulo * can be removed with radius_client_flush_auth() or with interim accounting
619214734Srpaulo * updates.
620214734Srpaulo */
621189251Ssamint radius_client_send(struct radius_client_data *radius,
622189251Ssam		       struct radius_msg *msg, RadiusType msg_type,
623189251Ssam		       const u8 *addr)
624189251Ssam{
625189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
626214734Srpaulo	const u8 *shared_secret;
627189251Ssam	size_t shared_secret_len;
628189251Ssam	char *name;
629189251Ssam	int s, res;
630214734Srpaulo	struct wpabuf *buf;
631189251Ssam
632189251Ssam	if (msg_type == RADIUS_ACCT_INTERIM) {
633189251Ssam		/* Remove any pending interim acct update for the same STA. */
634189251Ssam		radius_client_list_del(radius, msg_type, addr);
635189251Ssam	}
636189251Ssam
637189251Ssam	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
638189251Ssam		if (conf->acct_server == NULL) {
639189251Ssam			hostapd_logger(radius->ctx, NULL,
640189251Ssam				       HOSTAPD_MODULE_RADIUS,
641189251Ssam				       HOSTAPD_LEVEL_INFO,
642189251Ssam				       "No accounting server configured");
643189251Ssam			return -1;
644189251Ssam		}
645189251Ssam		shared_secret = conf->acct_server->shared_secret;
646189251Ssam		shared_secret_len = conf->acct_server->shared_secret_len;
647189251Ssam		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
648189251Ssam		name = "accounting";
649189251Ssam		s = radius->acct_sock;
650189251Ssam		conf->acct_server->requests++;
651189251Ssam	} else {
652189251Ssam		if (conf->auth_server == NULL) {
653189251Ssam			hostapd_logger(radius->ctx, NULL,
654189251Ssam				       HOSTAPD_MODULE_RADIUS,
655189251Ssam				       HOSTAPD_LEVEL_INFO,
656189251Ssam				       "No authentication server configured");
657189251Ssam			return -1;
658189251Ssam		}
659189251Ssam		shared_secret = conf->auth_server->shared_secret;
660189251Ssam		shared_secret_len = conf->auth_server->shared_secret_len;
661189251Ssam		radius_msg_finish(msg, shared_secret, shared_secret_len);
662189251Ssam		name = "authentication";
663189251Ssam		s = radius->auth_sock;
664189251Ssam		conf->auth_server->requests++;
665189251Ssam	}
666189251Ssam
667189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
668189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
669189251Ssam		       "server", name);
670189251Ssam	if (conf->msg_dumps)
671189251Ssam		radius_msg_dump(msg);
672189251Ssam
673214734Srpaulo	buf = radius_msg_get_buf(msg);
674214734Srpaulo	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
675189251Ssam	if (res < 0)
676189251Ssam		radius_client_handle_send_error(radius, s, msg_type);
677189251Ssam
678189251Ssam	radius_client_list_add(radius, msg, msg_type, shared_secret,
679189251Ssam			       shared_secret_len, addr);
680189251Ssam
681252726Srpaulo	return 0;
682189251Ssam}
683189251Ssam
684189251Ssam
685189251Ssamstatic void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
686189251Ssam{
687189251Ssam	struct radius_client_data *radius = eloop_ctx;
688189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
689189251Ssam	RadiusType msg_type = (RadiusType) sock_ctx;
690189251Ssam	int len, roundtrip;
691189251Ssam	unsigned char buf[3000];
692189251Ssam	struct radius_msg *msg;
693214734Srpaulo	struct radius_hdr *hdr;
694189251Ssam	struct radius_rx_handler *handlers;
695189251Ssam	size_t num_handlers, i;
696189251Ssam	struct radius_msg_list *req, *prev_req;
697189251Ssam	struct os_time now;
698189251Ssam	struct hostapd_radius_server *rconf;
699189251Ssam	int invalid_authenticator = 0;
700189251Ssam
701189251Ssam	if (msg_type == RADIUS_ACCT) {
702189251Ssam		handlers = radius->acct_handlers;
703189251Ssam		num_handlers = radius->num_acct_handlers;
704189251Ssam		rconf = conf->acct_server;
705189251Ssam	} else {
706189251Ssam		handlers = radius->auth_handlers;
707189251Ssam		num_handlers = radius->num_auth_handlers;
708189251Ssam		rconf = conf->auth_server;
709189251Ssam	}
710189251Ssam
711189251Ssam	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
712189251Ssam	if (len < 0) {
713189251Ssam		perror("recv[RADIUS]");
714189251Ssam		return;
715189251Ssam	}
716189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
717189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
718189251Ssam		       "server", len);
719189251Ssam	if (len == sizeof(buf)) {
720189251Ssam		printf("Possibly too long UDP frame for our buffer - "
721189251Ssam		       "dropping it\n");
722189251Ssam		return;
723189251Ssam	}
724189251Ssam
725189251Ssam	msg = radius_msg_parse(buf, len);
726189251Ssam	if (msg == NULL) {
727189251Ssam		printf("Parsing incoming RADIUS frame failed\n");
728189251Ssam		rconf->malformed_responses++;
729189251Ssam		return;
730189251Ssam	}
731214734Srpaulo	hdr = radius_msg_get_hdr(msg);
732189251Ssam
733189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
734189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
735189251Ssam	if (conf->msg_dumps)
736189251Ssam		radius_msg_dump(msg);
737189251Ssam
738214734Srpaulo	switch (hdr->code) {
739189251Ssam	case RADIUS_CODE_ACCESS_ACCEPT:
740189251Ssam		rconf->access_accepts++;
741189251Ssam		break;
742189251Ssam	case RADIUS_CODE_ACCESS_REJECT:
743189251Ssam		rconf->access_rejects++;
744189251Ssam		break;
745189251Ssam	case RADIUS_CODE_ACCESS_CHALLENGE:
746189251Ssam		rconf->access_challenges++;
747189251Ssam		break;
748189251Ssam	case RADIUS_CODE_ACCOUNTING_RESPONSE:
749189251Ssam		rconf->responses++;
750189251Ssam		break;
751189251Ssam	}
752189251Ssam
753189251Ssam	prev_req = NULL;
754189251Ssam	req = radius->msgs;
755189251Ssam	while (req) {
756189251Ssam		/* TODO: also match by src addr:port of the packet when using
757189251Ssam		 * alternative RADIUS servers (?) */
758189251Ssam		if ((req->msg_type == msg_type ||
759189251Ssam		     (req->msg_type == RADIUS_ACCT_INTERIM &&
760189251Ssam		      msg_type == RADIUS_ACCT)) &&
761214734Srpaulo		    radius_msg_get_hdr(req->msg)->identifier ==
762214734Srpaulo		    hdr->identifier)
763189251Ssam			break;
764189251Ssam
765189251Ssam		prev_req = req;
766189251Ssam		req = req->next;
767189251Ssam	}
768189251Ssam
769189251Ssam	if (req == NULL) {
770189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
771189251Ssam			       HOSTAPD_LEVEL_DEBUG,
772189251Ssam			       "No matching RADIUS request found (type=%d "
773189251Ssam			       "id=%d) - dropping packet",
774214734Srpaulo			       msg_type, hdr->identifier);
775189251Ssam		goto fail;
776189251Ssam	}
777189251Ssam
778189251Ssam	os_get_time(&now);
779189251Ssam	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
780189251Ssam		(now.usec - req->last_attempt.usec) / 10000;
781189251Ssam	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
782189251Ssam		       HOSTAPD_LEVEL_DEBUG,
783189251Ssam		       "Received RADIUS packet matched with a pending "
784189251Ssam		       "request, round trip time %d.%02d sec",
785189251Ssam		       roundtrip / 100, roundtrip % 100);
786189251Ssam	rconf->round_trip_time = roundtrip;
787189251Ssam
788189251Ssam	/* Remove ACKed RADIUS packet from retransmit list */
789189251Ssam	if (prev_req)
790189251Ssam		prev_req->next = req->next;
791189251Ssam	else
792189251Ssam		radius->msgs = req->next;
793189251Ssam	radius->num_msgs--;
794189251Ssam
795189251Ssam	for (i = 0; i < num_handlers; i++) {
796189251Ssam		RadiusRxResult res;
797189251Ssam		res = handlers[i].handler(msg, req->msg, req->shared_secret,
798189251Ssam					  req->shared_secret_len,
799189251Ssam					  handlers[i].data);
800189251Ssam		switch (res) {
801189251Ssam		case RADIUS_RX_PROCESSED:
802189251Ssam			radius_msg_free(msg);
803189251Ssam			/* continue */
804189251Ssam		case RADIUS_RX_QUEUED:
805189251Ssam			radius_client_msg_free(req);
806189251Ssam			return;
807189251Ssam		case RADIUS_RX_INVALID_AUTHENTICATOR:
808189251Ssam			invalid_authenticator++;
809189251Ssam			/* continue */
810189251Ssam		case RADIUS_RX_UNKNOWN:
811189251Ssam			/* continue with next handler */
812189251Ssam			break;
813189251Ssam		}
814189251Ssam	}
815189251Ssam
816189251Ssam	if (invalid_authenticator)
817189251Ssam		rconf->bad_authenticators++;
818189251Ssam	else
819189251Ssam		rconf->unknown_types++;
820189251Ssam	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
821189251Ssam		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
822189251Ssam		       "(type=%d code=%d id=%d)%s - dropping packet",
823214734Srpaulo		       msg_type, hdr->code, hdr->identifier,
824189251Ssam		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
825189251Ssam		       "");
826189251Ssam	radius_client_msg_free(req);
827189251Ssam
828189251Ssam fail:
829189251Ssam	radius_msg_free(msg);
830189251Ssam}
831189251Ssam
832189251Ssam
833214734Srpaulo/**
834214734Srpaulo * radius_client_get_id - Get an identifier for a new RADIUS message
835214734Srpaulo * @radius: RADIUS client context from radius_client_init()
836214734Srpaulo * Returns: Allocated identifier
837214734Srpaulo *
838214734Srpaulo * This function is used to fetch a unique (among pending requests) identifier
839214734Srpaulo * for a new RADIUS message.
840214734Srpaulo */
841189251Ssamu8 radius_client_get_id(struct radius_client_data *radius)
842189251Ssam{
843189251Ssam	struct radius_msg_list *entry, *prev, *_remove;
844189251Ssam	u8 id = radius->next_radius_identifier++;
845189251Ssam
846189251Ssam	/* remove entries with matching id from retransmit list to avoid
847189251Ssam	 * using new reply from the RADIUS server with an old request */
848189251Ssam	entry = radius->msgs;
849189251Ssam	prev = NULL;
850189251Ssam	while (entry) {
851214734Srpaulo		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
852189251Ssam			hostapd_logger(radius->ctx, entry->addr,
853189251Ssam				       HOSTAPD_MODULE_RADIUS,
854189251Ssam				       HOSTAPD_LEVEL_DEBUG,
855189251Ssam				       "Removing pending RADIUS message, "
856189251Ssam				       "since its id (%d) is reused", id);
857189251Ssam			if (prev)
858189251Ssam				prev->next = entry->next;
859189251Ssam			else
860189251Ssam				radius->msgs = entry->next;
861189251Ssam			_remove = entry;
862189251Ssam		} else {
863189251Ssam			_remove = NULL;
864189251Ssam			prev = entry;
865189251Ssam		}
866189251Ssam		entry = entry->next;
867189251Ssam
868189251Ssam		if (_remove)
869189251Ssam			radius_client_msg_free(_remove);
870189251Ssam	}
871189251Ssam
872189251Ssam	return id;
873189251Ssam}
874189251Ssam
875189251Ssam
876214734Srpaulo/**
877214734Srpaulo * radius_client_flush - Flush all pending RADIUS client messages
878214734Srpaulo * @radius: RADIUS client context from radius_client_init()
879214734Srpaulo * @only_auth: Whether only authentication messages are removed
880214734Srpaulo */
881189251Ssamvoid radius_client_flush(struct radius_client_data *radius, int only_auth)
882189251Ssam{
883189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
884189251Ssam
885189251Ssam	if (!radius)
886189251Ssam		return;
887189251Ssam
888189251Ssam	prev = NULL;
889189251Ssam	entry = radius->msgs;
890189251Ssam
891189251Ssam	while (entry) {
892189251Ssam		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
893189251Ssam			if (prev)
894189251Ssam				prev->next = entry->next;
895189251Ssam			else
896189251Ssam				radius->msgs = entry->next;
897189251Ssam
898189251Ssam			tmp = entry;
899189251Ssam			entry = entry->next;
900189251Ssam			radius_client_msg_free(tmp);
901189251Ssam			radius->num_msgs--;
902189251Ssam		} else {
903189251Ssam			prev = entry;
904189251Ssam			entry = entry->next;
905189251Ssam		}
906189251Ssam	}
907189251Ssam
908189251Ssam	if (radius->msgs == NULL)
909189251Ssam		eloop_cancel_timeout(radius_client_timer, radius, NULL);
910189251Ssam}
911189251Ssam
912189251Ssam
913189251Ssamstatic void radius_client_update_acct_msgs(struct radius_client_data *radius,
914214734Srpaulo					   const u8 *shared_secret,
915189251Ssam					   size_t shared_secret_len)
916189251Ssam{
917189251Ssam	struct radius_msg_list *entry;
918189251Ssam
919189251Ssam	if (!radius)
920189251Ssam		return;
921189251Ssam
922189251Ssam	for (entry = radius->msgs; entry; entry = entry->next) {
923189251Ssam		if (entry->msg_type == RADIUS_ACCT) {
924189251Ssam			entry->shared_secret = shared_secret;
925189251Ssam			entry->shared_secret_len = shared_secret_len;
926189251Ssam			radius_msg_finish_acct(entry->msg, shared_secret,
927189251Ssam					       shared_secret_len);
928189251Ssam		}
929189251Ssam	}
930189251Ssam}
931189251Ssam
932189251Ssam
933189251Ssamstatic int
934189251Ssamradius_change_server(struct radius_client_data *radius,
935189251Ssam		     struct hostapd_radius_server *nserv,
936189251Ssam		     struct hostapd_radius_server *oserv,
937189251Ssam		     int sock, int sock6, int auth)
938189251Ssam{
939189251Ssam	struct sockaddr_in serv, claddr;
940189251Ssam#ifdef CONFIG_IPV6
941189251Ssam	struct sockaddr_in6 serv6, claddr6;
942189251Ssam#endif /* CONFIG_IPV6 */
943189251Ssam	struct sockaddr *addr, *cl_addr;
944189251Ssam	socklen_t addrlen, claddrlen;
945189251Ssam	char abuf[50];
946189251Ssam	int sel_sock;
947189251Ssam	struct radius_msg_list *entry;
948189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
949189251Ssam
950189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
951189251Ssam		       HOSTAPD_LEVEL_INFO,
952189251Ssam		       "%s server %s:%d",
953189251Ssam		       auth ? "Authentication" : "Accounting",
954189251Ssam		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
955189251Ssam		       nserv->port);
956189251Ssam
957189251Ssam	if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
958189251Ssam	    os_memcmp(nserv->shared_secret, oserv->shared_secret,
959189251Ssam		      nserv->shared_secret_len) != 0) {
960189251Ssam		/* Pending RADIUS packets used different shared secret, so
961189251Ssam		 * they need to be modified. Update accounting message
962189251Ssam		 * authenticators here. Authentication messages are removed
963189251Ssam		 * since they would require more changes and the new RADIUS
964189251Ssam		 * server may not be prepared to receive them anyway due to
965189251Ssam		 * missing state information. Client will likely retry
966189251Ssam		 * authentication, so this should not be an issue. */
967189251Ssam		if (auth)
968189251Ssam			radius_client_flush(radius, 1);
969189251Ssam		else {
970189251Ssam			radius_client_update_acct_msgs(
971189251Ssam				radius, nserv->shared_secret,
972189251Ssam				nserv->shared_secret_len);
973189251Ssam		}
974189251Ssam	}
975189251Ssam
976189251Ssam	/* Reset retry counters for the new server */
977189251Ssam	for (entry = radius->msgs; entry; entry = entry->next) {
978189251Ssam		if ((auth && entry->msg_type != RADIUS_AUTH) ||
979189251Ssam		    (!auth && entry->msg_type != RADIUS_ACCT))
980189251Ssam			continue;
981189251Ssam		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
982189251Ssam		entry->attempts = 0;
983189251Ssam		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
984189251Ssam	}
985189251Ssam
986189251Ssam	if (radius->msgs) {
987189251Ssam		eloop_cancel_timeout(radius_client_timer, radius, NULL);
988189251Ssam		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
989189251Ssam				       radius_client_timer, radius, NULL);
990189251Ssam	}
991189251Ssam
992189251Ssam	switch (nserv->addr.af) {
993189251Ssam	case AF_INET:
994189251Ssam		os_memset(&serv, 0, sizeof(serv));
995189251Ssam		serv.sin_family = AF_INET;
996189251Ssam		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
997189251Ssam		serv.sin_port = htons(nserv->port);
998189251Ssam		addr = (struct sockaddr *) &serv;
999189251Ssam		addrlen = sizeof(serv);
1000189251Ssam		sel_sock = sock;
1001189251Ssam		break;
1002189251Ssam#ifdef CONFIG_IPV6
1003189251Ssam	case AF_INET6:
1004189251Ssam		os_memset(&serv6, 0, sizeof(serv6));
1005189251Ssam		serv6.sin6_family = AF_INET6;
1006189251Ssam		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
1007189251Ssam			  sizeof(struct in6_addr));
1008189251Ssam		serv6.sin6_port = htons(nserv->port);
1009189251Ssam		addr = (struct sockaddr *) &serv6;
1010189251Ssam		addrlen = sizeof(serv6);
1011189251Ssam		sel_sock = sock6;
1012189251Ssam		break;
1013189251Ssam#endif /* CONFIG_IPV6 */
1014189251Ssam	default:
1015189251Ssam		return -1;
1016189251Ssam	}
1017189251Ssam
1018189251Ssam	if (conf->force_client_addr) {
1019189251Ssam		switch (conf->client_addr.af) {
1020189251Ssam		case AF_INET:
1021189251Ssam			os_memset(&claddr, 0, sizeof(claddr));
1022189251Ssam			claddr.sin_family = AF_INET;
1023189251Ssam			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
1024189251Ssam			claddr.sin_port = htons(0);
1025189251Ssam			cl_addr = (struct sockaddr *) &claddr;
1026189251Ssam			claddrlen = sizeof(claddr);
1027189251Ssam			break;
1028189251Ssam#ifdef CONFIG_IPV6
1029189251Ssam		case AF_INET6:
1030189251Ssam			os_memset(&claddr6, 0, sizeof(claddr6));
1031189251Ssam			claddr6.sin6_family = AF_INET6;
1032189251Ssam			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
1033189251Ssam				  sizeof(struct in6_addr));
1034189251Ssam			claddr6.sin6_port = htons(0);
1035189251Ssam			cl_addr = (struct sockaddr *) &claddr6;
1036189251Ssam			claddrlen = sizeof(claddr6);
1037189251Ssam			break;
1038189251Ssam#endif /* CONFIG_IPV6 */
1039189251Ssam		default:
1040189251Ssam			return -1;
1041189251Ssam		}
1042189251Ssam
1043189251Ssam		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
1044189251Ssam			perror("bind[radius]");
1045189251Ssam			return -1;
1046189251Ssam		}
1047189251Ssam	}
1048189251Ssam
1049189251Ssam	if (connect(sel_sock, addr, addrlen) < 0) {
1050189251Ssam		perror("connect[radius]");
1051189251Ssam		return -1;
1052189251Ssam	}
1053189251Ssam
1054189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
1055189251Ssam	switch (nserv->addr.af) {
1056189251Ssam	case AF_INET:
1057189251Ssam		claddrlen = sizeof(claddr);
1058189251Ssam		getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
1059189251Ssam		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1060189251Ssam			   inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
1061189251Ssam		break;
1062189251Ssam#ifdef CONFIG_IPV6
1063189251Ssam	case AF_INET6: {
1064189251Ssam		claddrlen = sizeof(claddr6);
1065189251Ssam		getsockname(sel_sock, (struct sockaddr *) &claddr6,
1066189251Ssam			    &claddrlen);
1067189251Ssam		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1068189251Ssam			   inet_ntop(AF_INET6, &claddr6.sin6_addr,
1069189251Ssam				     abuf, sizeof(abuf)),
1070189251Ssam			   ntohs(claddr6.sin6_port));
1071189251Ssam		break;
1072189251Ssam	}
1073189251Ssam#endif /* CONFIG_IPV6 */
1074189251Ssam	}
1075189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1076189251Ssam
1077189251Ssam	if (auth)
1078189251Ssam		radius->auth_sock = sel_sock;
1079189251Ssam	else
1080189251Ssam		radius->acct_sock = sel_sock;
1081189251Ssam
1082189251Ssam	return 0;
1083189251Ssam}
1084189251Ssam
1085189251Ssam
1086189251Ssamstatic void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
1087189251Ssam{
1088189251Ssam	struct radius_client_data *radius = eloop_ctx;
1089189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1090189251Ssam	struct hostapd_radius_server *oserv;
1091189251Ssam
1092189251Ssam	if (radius->auth_sock >= 0 && conf->auth_servers &&
1093189251Ssam	    conf->auth_server != conf->auth_servers) {
1094189251Ssam		oserv = conf->auth_server;
1095189251Ssam		conf->auth_server = conf->auth_servers;
1096189251Ssam		radius_change_server(radius, conf->auth_server, oserv,
1097189251Ssam				     radius->auth_serv_sock,
1098189251Ssam				     radius->auth_serv_sock6, 1);
1099189251Ssam	}
1100189251Ssam
1101189251Ssam	if (radius->acct_sock >= 0 && conf->acct_servers &&
1102189251Ssam	    conf->acct_server != conf->acct_servers) {
1103189251Ssam		oserv = conf->acct_server;
1104189251Ssam		conf->acct_server = conf->acct_servers;
1105189251Ssam		radius_change_server(radius, conf->acct_server, oserv,
1106189251Ssam				     radius->acct_serv_sock,
1107189251Ssam				     radius->acct_serv_sock6, 0);
1108189251Ssam	}
1109189251Ssam
1110189251Ssam	if (conf->retry_primary_interval)
1111189251Ssam		eloop_register_timeout(conf->retry_primary_interval, 0,
1112189251Ssam				       radius_retry_primary_timer, radius,
1113189251Ssam				       NULL);
1114189251Ssam}
1115189251Ssam
1116189251Ssam
1117209158Srpaulostatic int radius_client_disable_pmtu_discovery(int s)
1118209158Srpaulo{
1119209158Srpaulo	int r = -1;
1120209158Srpaulo#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1121209158Srpaulo	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
1122209158Srpaulo	int action = IP_PMTUDISC_DONT;
1123209158Srpaulo	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
1124209158Srpaulo		       sizeof(action));
1125209158Srpaulo	if (r == -1)
1126209158Srpaulo		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
1127209158Srpaulo			   "%s", strerror(errno));
1128209158Srpaulo#endif
1129209158Srpaulo	return r;
1130209158Srpaulo}
1131209158Srpaulo
1132209158Srpaulo
1133189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius)
1134189251Ssam{
1135189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1136189251Ssam	int ok = 0;
1137189251Ssam
1138189251Ssam	radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1139189251Ssam	if (radius->auth_serv_sock < 0)
1140189251Ssam		perror("socket[PF_INET,SOCK_DGRAM]");
1141209158Srpaulo	else {
1142209158Srpaulo		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
1143189251Ssam		ok++;
1144209158Srpaulo	}
1145189251Ssam
1146189251Ssam#ifdef CONFIG_IPV6
1147189251Ssam	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1148189251Ssam	if (radius->auth_serv_sock6 < 0)
1149189251Ssam		perror("socket[PF_INET6,SOCK_DGRAM]");
1150189251Ssam	else
1151189251Ssam		ok++;
1152189251Ssam#endif /* CONFIG_IPV6 */
1153189251Ssam
1154189251Ssam	if (ok == 0)
1155189251Ssam		return -1;
1156189251Ssam
1157189251Ssam	radius_change_server(radius, conf->auth_server, NULL,
1158189251Ssam			     radius->auth_serv_sock, radius->auth_serv_sock6,
1159189251Ssam			     1);
1160189251Ssam
1161189251Ssam	if (radius->auth_serv_sock >= 0 &&
1162189251Ssam	    eloop_register_read_sock(radius->auth_serv_sock,
1163189251Ssam				     radius_client_receive, radius,
1164189251Ssam				     (void *) RADIUS_AUTH)) {
1165189251Ssam		printf("Could not register read socket for authentication "
1166189251Ssam		       "server\n");
1167189251Ssam		return -1;
1168189251Ssam	}
1169189251Ssam
1170189251Ssam#ifdef CONFIG_IPV6
1171189251Ssam	if (radius->auth_serv_sock6 >= 0 &&
1172189251Ssam	    eloop_register_read_sock(radius->auth_serv_sock6,
1173189251Ssam				     radius_client_receive, radius,
1174189251Ssam				     (void *) RADIUS_AUTH)) {
1175189251Ssam		printf("Could not register read socket for authentication "
1176189251Ssam		       "server\n");
1177189251Ssam		return -1;
1178189251Ssam	}
1179189251Ssam#endif /* CONFIG_IPV6 */
1180189251Ssam
1181189251Ssam	return 0;
1182189251Ssam}
1183189251Ssam
1184189251Ssam
1185189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius)
1186189251Ssam{
1187189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1188189251Ssam	int ok = 0;
1189189251Ssam
1190189251Ssam	radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1191189251Ssam	if (radius->acct_serv_sock < 0)
1192189251Ssam		perror("socket[PF_INET,SOCK_DGRAM]");
1193209158Srpaulo	else {
1194209158Srpaulo		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
1195189251Ssam		ok++;
1196209158Srpaulo	}
1197189251Ssam
1198189251Ssam#ifdef CONFIG_IPV6
1199189251Ssam	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1200189251Ssam	if (radius->acct_serv_sock6 < 0)
1201189251Ssam		perror("socket[PF_INET6,SOCK_DGRAM]");
1202189251Ssam	else
1203189251Ssam		ok++;
1204189251Ssam#endif /* CONFIG_IPV6 */
1205189251Ssam
1206189251Ssam	if (ok == 0)
1207189251Ssam		return -1;
1208189251Ssam
1209189251Ssam	radius_change_server(radius, conf->acct_server, NULL,
1210189251Ssam			     radius->acct_serv_sock, radius->acct_serv_sock6,
1211189251Ssam			     0);
1212189251Ssam
1213189251Ssam	if (radius->acct_serv_sock >= 0 &&
1214189251Ssam	    eloop_register_read_sock(radius->acct_serv_sock,
1215189251Ssam				     radius_client_receive, radius,
1216189251Ssam				     (void *) RADIUS_ACCT)) {
1217189251Ssam		printf("Could not register read socket for accounting "
1218189251Ssam		       "server\n");
1219189251Ssam		return -1;
1220189251Ssam	}
1221189251Ssam
1222189251Ssam#ifdef CONFIG_IPV6
1223189251Ssam	if (radius->acct_serv_sock6 >= 0 &&
1224189251Ssam	    eloop_register_read_sock(radius->acct_serv_sock6,
1225189251Ssam				     radius_client_receive, radius,
1226189251Ssam				     (void *) RADIUS_ACCT)) {
1227189251Ssam		printf("Could not register read socket for accounting "
1228189251Ssam		       "server\n");
1229189251Ssam		return -1;
1230189251Ssam	}
1231189251Ssam#endif /* CONFIG_IPV6 */
1232189251Ssam
1233189251Ssam	return 0;
1234189251Ssam}
1235189251Ssam
1236189251Ssam
1237214734Srpaulo/**
1238214734Srpaulo * radius_client_init - Initialize RADIUS client
1239214734Srpaulo * @ctx: Callback context to be used in hostapd_logger() calls
1240214734Srpaulo * @conf: RADIUS client configuration (RADIUS servers)
1241214734Srpaulo * Returns: Pointer to private RADIUS client context or %NULL on failure
1242214734Srpaulo *
1243214734Srpaulo * The caller is responsible for keeping the configuration data available for
1244214734Srpaulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1245214734Srpaulo * called for the returned context pointer.
1246214734Srpaulo */
1247189251Ssamstruct radius_client_data *
1248189251Ssamradius_client_init(void *ctx, struct hostapd_radius_servers *conf)
1249189251Ssam{
1250189251Ssam	struct radius_client_data *radius;
1251189251Ssam
1252189251Ssam	radius = os_zalloc(sizeof(struct radius_client_data));
1253189251Ssam	if (radius == NULL)
1254189251Ssam		return NULL;
1255189251Ssam
1256189251Ssam	radius->ctx = ctx;
1257189251Ssam	radius->conf = conf;
1258189251Ssam	radius->auth_serv_sock = radius->acct_serv_sock =
1259189251Ssam		radius->auth_serv_sock6 = radius->acct_serv_sock6 =
1260189251Ssam		radius->auth_sock = radius->acct_sock = -1;
1261189251Ssam
1262189251Ssam	if (conf->auth_server && radius_client_init_auth(radius)) {
1263189251Ssam		radius_client_deinit(radius);
1264189251Ssam		return NULL;
1265189251Ssam	}
1266189251Ssam
1267189251Ssam	if (conf->acct_server && radius_client_init_acct(radius)) {
1268189251Ssam		radius_client_deinit(radius);
1269189251Ssam		return NULL;
1270189251Ssam	}
1271189251Ssam
1272189251Ssam	if (conf->retry_primary_interval)
1273189251Ssam		eloop_register_timeout(conf->retry_primary_interval, 0,
1274189251Ssam				       radius_retry_primary_timer, radius,
1275189251Ssam				       NULL);
1276189251Ssam
1277189251Ssam	return radius;
1278189251Ssam}
1279189251Ssam
1280189251Ssam
1281214734Srpaulo/**
1282214734Srpaulo * radius_client_deinit - Deinitialize RADIUS client
1283214734Srpaulo * @radius: RADIUS client context from radius_client_init()
1284214734Srpaulo */
1285189251Ssamvoid radius_client_deinit(struct radius_client_data *radius)
1286189251Ssam{
1287189251Ssam	if (!radius)
1288189251Ssam		return;
1289189251Ssam
1290189251Ssam	if (radius->auth_serv_sock >= 0)
1291189251Ssam		eloop_unregister_read_sock(radius->auth_serv_sock);
1292189251Ssam	if (radius->acct_serv_sock >= 0)
1293189251Ssam		eloop_unregister_read_sock(radius->acct_serv_sock);
1294209158Srpaulo#ifdef CONFIG_IPV6
1295209158Srpaulo	if (radius->auth_serv_sock6 >= 0)
1296209158Srpaulo		eloop_unregister_read_sock(radius->auth_serv_sock6);
1297209158Srpaulo	if (radius->acct_serv_sock6 >= 0)
1298209158Srpaulo		eloop_unregister_read_sock(radius->acct_serv_sock6);
1299209158Srpaulo#endif /* CONFIG_IPV6 */
1300189251Ssam
1301189251Ssam	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
1302189251Ssam
1303189251Ssam	radius_client_flush(radius, 0);
1304189251Ssam	os_free(radius->auth_handlers);
1305189251Ssam	os_free(radius->acct_handlers);
1306189251Ssam	os_free(radius);
1307189251Ssam}
1308189251Ssam
1309189251Ssam
1310214734Srpaulo/**
1311214734Srpaulo * radius_client_flush_auth - Flush pending RADIUS messages for an address
1312214734Srpaulo * @radius: RADIUS client context from radius_client_init()
1313214734Srpaulo * @addr: MAC address of the related device
1314214734Srpaulo *
1315214734Srpaulo * This function can be used to remove pending RADIUS authentication messages
1316214734Srpaulo * that are related to a specific device. The addr parameter is matched with
1317214734Srpaulo * the one used in radius_client_send() call that was used to transmit the
1318214734Srpaulo * authentication request.
1319214734Srpaulo */
1320214734Srpaulovoid radius_client_flush_auth(struct radius_client_data *radius,
1321214734Srpaulo			      const u8 *addr)
1322189251Ssam{
1323189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
1324189251Ssam
1325189251Ssam	prev = NULL;
1326189251Ssam	entry = radius->msgs;
1327189251Ssam	while (entry) {
1328189251Ssam		if (entry->msg_type == RADIUS_AUTH &&
1329189251Ssam		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
1330189251Ssam			hostapd_logger(radius->ctx, addr,
1331189251Ssam				       HOSTAPD_MODULE_RADIUS,
1332189251Ssam				       HOSTAPD_LEVEL_DEBUG,
1333189251Ssam				       "Removing pending RADIUS authentication"
1334189251Ssam				       " message for removed client");
1335189251Ssam
1336189251Ssam			if (prev)
1337189251Ssam				prev->next = entry->next;
1338189251Ssam			else
1339189251Ssam				radius->msgs = entry->next;
1340189251Ssam
1341189251Ssam			tmp = entry;
1342189251Ssam			entry = entry->next;
1343189251Ssam			radius_client_msg_free(tmp);
1344189251Ssam			radius->num_msgs--;
1345189251Ssam			continue;
1346189251Ssam		}
1347189251Ssam
1348189251Ssam		prev = entry;
1349189251Ssam		entry = entry->next;
1350189251Ssam	}
1351189251Ssam}
1352189251Ssam
1353189251Ssam
1354189251Ssamstatic int radius_client_dump_auth_server(char *buf, size_t buflen,
1355189251Ssam					  struct hostapd_radius_server *serv,
1356189251Ssam					  struct radius_client_data *cli)
1357189251Ssam{
1358189251Ssam	int pending = 0;
1359189251Ssam	struct radius_msg_list *msg;
1360189251Ssam	char abuf[50];
1361189251Ssam
1362189251Ssam	if (cli) {
1363189251Ssam		for (msg = cli->msgs; msg; msg = msg->next) {
1364189251Ssam			if (msg->msg_type == RADIUS_AUTH)
1365189251Ssam				pending++;
1366189251Ssam		}
1367189251Ssam	}
1368189251Ssam
1369189251Ssam	return os_snprintf(buf, buflen,
1370189251Ssam			   "radiusAuthServerIndex=%d\n"
1371189251Ssam			   "radiusAuthServerAddress=%s\n"
1372189251Ssam			   "radiusAuthClientServerPortNumber=%d\n"
1373189251Ssam			   "radiusAuthClientRoundTripTime=%d\n"
1374189251Ssam			   "radiusAuthClientAccessRequests=%u\n"
1375189251Ssam			   "radiusAuthClientAccessRetransmissions=%u\n"
1376189251Ssam			   "radiusAuthClientAccessAccepts=%u\n"
1377189251Ssam			   "radiusAuthClientAccessRejects=%u\n"
1378189251Ssam			   "radiusAuthClientAccessChallenges=%u\n"
1379189251Ssam			   "radiusAuthClientMalformedAccessResponses=%u\n"
1380189251Ssam			   "radiusAuthClientBadAuthenticators=%u\n"
1381189251Ssam			   "radiusAuthClientPendingRequests=%u\n"
1382189251Ssam			   "radiusAuthClientTimeouts=%u\n"
1383189251Ssam			   "radiusAuthClientUnknownTypes=%u\n"
1384189251Ssam			   "radiusAuthClientPacketsDropped=%u\n",
1385189251Ssam			   serv->index,
1386189251Ssam			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1387189251Ssam			   serv->port,
1388189251Ssam			   serv->round_trip_time,
1389189251Ssam			   serv->requests,
1390189251Ssam			   serv->retransmissions,
1391189251Ssam			   serv->access_accepts,
1392189251Ssam			   serv->access_rejects,
1393189251Ssam			   serv->access_challenges,
1394189251Ssam			   serv->malformed_responses,
1395189251Ssam			   serv->bad_authenticators,
1396189251Ssam			   pending,
1397189251Ssam			   serv->timeouts,
1398189251Ssam			   serv->unknown_types,
1399189251Ssam			   serv->packets_dropped);
1400189251Ssam}
1401189251Ssam
1402189251Ssam
1403189251Ssamstatic int radius_client_dump_acct_server(char *buf, size_t buflen,
1404189251Ssam					  struct hostapd_radius_server *serv,
1405189251Ssam					  struct radius_client_data *cli)
1406189251Ssam{
1407189251Ssam	int pending = 0;
1408189251Ssam	struct radius_msg_list *msg;
1409189251Ssam	char abuf[50];
1410189251Ssam
1411189251Ssam	if (cli) {
1412189251Ssam		for (msg = cli->msgs; msg; msg = msg->next) {
1413189251Ssam			if (msg->msg_type == RADIUS_ACCT ||
1414189251Ssam			    msg->msg_type == RADIUS_ACCT_INTERIM)
1415189251Ssam				pending++;
1416189251Ssam		}
1417189251Ssam	}
1418189251Ssam
1419189251Ssam	return os_snprintf(buf, buflen,
1420189251Ssam			   "radiusAccServerIndex=%d\n"
1421189251Ssam			   "radiusAccServerAddress=%s\n"
1422189251Ssam			   "radiusAccClientServerPortNumber=%d\n"
1423189251Ssam			   "radiusAccClientRoundTripTime=%d\n"
1424189251Ssam			   "radiusAccClientRequests=%u\n"
1425189251Ssam			   "radiusAccClientRetransmissions=%u\n"
1426189251Ssam			   "radiusAccClientResponses=%u\n"
1427189251Ssam			   "radiusAccClientMalformedResponses=%u\n"
1428189251Ssam			   "radiusAccClientBadAuthenticators=%u\n"
1429189251Ssam			   "radiusAccClientPendingRequests=%u\n"
1430189251Ssam			   "radiusAccClientTimeouts=%u\n"
1431189251Ssam			   "radiusAccClientUnknownTypes=%u\n"
1432189251Ssam			   "radiusAccClientPacketsDropped=%u\n",
1433189251Ssam			   serv->index,
1434189251Ssam			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1435189251Ssam			   serv->port,
1436189251Ssam			   serv->round_trip_time,
1437189251Ssam			   serv->requests,
1438189251Ssam			   serv->retransmissions,
1439189251Ssam			   serv->responses,
1440189251Ssam			   serv->malformed_responses,
1441189251Ssam			   serv->bad_authenticators,
1442189251Ssam			   pending,
1443189251Ssam			   serv->timeouts,
1444189251Ssam			   serv->unknown_types,
1445189251Ssam			   serv->packets_dropped);
1446189251Ssam}
1447189251Ssam
1448189251Ssam
1449214734Srpaulo/**
1450214734Srpaulo * radius_client_get_mib - Get RADIUS client MIB information
1451214734Srpaulo * @radius: RADIUS client context from radius_client_init()
1452214734Srpaulo * @buf: Buffer for returning MIB data in text format
1453214734Srpaulo * @buflen: Maximum buf length in octets
1454214734Srpaulo * Returns: Number of octets written into the buffer
1455214734Srpaulo */
1456189251Ssamint radius_client_get_mib(struct radius_client_data *radius, char *buf,
1457189251Ssam			  size_t buflen)
1458189251Ssam{
1459189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1460189251Ssam	int i;
1461189251Ssam	struct hostapd_radius_server *serv;
1462189251Ssam	int count = 0;
1463189251Ssam
1464189251Ssam	if (conf->auth_servers) {
1465189251Ssam		for (i = 0; i < conf->num_auth_servers; i++) {
1466189251Ssam			serv = &conf->auth_servers[i];
1467189251Ssam			count += radius_client_dump_auth_server(
1468189251Ssam				buf + count, buflen - count, serv,
1469189251Ssam				serv == conf->auth_server ?
1470189251Ssam				radius : NULL);
1471189251Ssam		}
1472189251Ssam	}
1473189251Ssam
1474189251Ssam	if (conf->acct_servers) {
1475189251Ssam		for (i = 0; i < conf->num_acct_servers; i++) {
1476189251Ssam			serv = &conf->acct_servers[i];
1477189251Ssam			count += radius_client_dump_acct_server(
1478189251Ssam				buf + count, buflen - count, serv,
1479189251Ssam				serv == conf->acct_server ?
1480189251Ssam				radius : NULL);
1481189251Ssam		}
1482189251Ssam	}
1483189251Ssam
1484189251Ssam	return count;
1485189251Ssam}
1486252726Srpaulo
1487252726Srpaulo
1488252726Srpaulovoid radius_client_reconfig(struct radius_client_data *radius,
1489252726Srpaulo			    struct hostapd_radius_servers *conf)
1490252726Srpaulo{
1491252726Srpaulo	if (radius)
1492252726Srpaulo		radius->conf = conf;
1493252726Srpaulo}
1494