radius_client.c revision 214734
1189251Ssam/*
2214734Srpaulo * RADIUS client
3214734Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#include "common.h"
18189251Ssam#include "radius.h"
19189251Ssam#include "radius_client.h"
20189251Ssam#include "eloop.h"
21189251Ssam
22189251Ssam/* Defaults for RADIUS retransmit values (exponential backoff) */
23189251Ssam
24214734Srpaulo/**
25214734Srpaulo * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
26214734Srpaulo */
27214734Srpaulo#define RADIUS_CLIENT_FIRST_WAIT 3
28189251Ssam
29214734Srpaulo/**
30214734Srpaulo * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
31214734Srpaulo */
32214734Srpaulo#define RADIUS_CLIENT_MAX_WAIT 120
33214734Srpaulo
34214734Srpaulo/**
35214734Srpaulo * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
36214734Srpaulo *
37214734Srpaulo * Maximum number of retransmit attempts before the entry is removed from
38214734Srpaulo * retransmit list.
39214734Srpaulo */
40214734Srpaulo#define RADIUS_CLIENT_MAX_RETRIES 10
41214734Srpaulo
42214734Srpaulo/**
43214734Srpaulo * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
44214734Srpaulo *
45214734Srpaulo * Maximum number of entries in retransmit list (oldest entries will be
46214734Srpaulo * removed, if this limit is exceeded).
47214734Srpaulo */
48214734Srpaulo#define RADIUS_CLIENT_MAX_ENTRIES 30
49214734Srpaulo
50214734Srpaulo/**
51214734Srpaulo * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
52214734Srpaulo *
53214734Srpaulo * The number of failed retry attempts after which the RADIUS server will be
54214734Srpaulo * changed (if one of more backup servers are configured).
55214734Srpaulo */
56214734Srpaulo#define RADIUS_CLIENT_NUM_FAILOVER 4
57214734Srpaulo
58214734Srpaulo
59214734Srpaulo/**
60214734Srpaulo * struct radius_rx_handler - RADIUS client RX handler
61214734Srpaulo *
62214734Srpaulo * This data structure is used internally inside the RADIUS client module to
63214734Srpaulo * store registered RX handlers. These handlers are registered by calls to
64214734Srpaulo * radius_client_register() and unregistered when the RADIUS client is
65214734Srpaulo * deinitialized with a call to radius_client_deinit().
66214734Srpaulo */
67189251Ssamstruct radius_rx_handler {
68214734Srpaulo	/**
69214734Srpaulo	 * handler - Received RADIUS message handler
70214734Srpaulo	 */
71189251Ssam	RadiusRxResult (*handler)(struct radius_msg *msg,
72189251Ssam				  struct radius_msg *req,
73189251Ssam				  const u8 *shared_secret,
74189251Ssam				  size_t shared_secret_len,
75189251Ssam				  void *data);
76214734Srpaulo
77214734Srpaulo	/**
78214734Srpaulo	 * data - Context data for the handler
79214734Srpaulo	 */
80189251Ssam	void *data;
81189251Ssam};
82189251Ssam
83189251Ssam
84214734Srpaulo/**
85214734Srpaulo * struct radius_msg_list - RADIUS client message retransmit list
86214734Srpaulo *
87214734Srpaulo * This data structure is used internally inside the RADIUS client module to
88214734Srpaulo * store pending RADIUS requests that may still need to be retransmitted.
89214734Srpaulo */
90189251Ssamstruct radius_msg_list {
91214734Srpaulo	/**
92214734Srpaulo	 * addr - STA/client address
93214734Srpaulo	 *
94214734Srpaulo	 * This is used to find RADIUS messages for the same STA.
95214734Srpaulo	 */
96214734Srpaulo	u8 addr[ETH_ALEN];
97214734Srpaulo
98214734Srpaulo	/**
99214734Srpaulo	 * msg - RADIUS message
100214734Srpaulo	 */
101189251Ssam	struct radius_msg *msg;
102214734Srpaulo
103214734Srpaulo	/**
104214734Srpaulo	 * msg_type - Message type
105214734Srpaulo	 */
106189251Ssam	RadiusType msg_type;
107214734Srpaulo
108214734Srpaulo	/**
109214734Srpaulo	 * first_try - Time of the first transmission attempt
110214734Srpaulo	 */
111189251Ssam	os_time_t first_try;
112214734Srpaulo
113214734Srpaulo	/**
114214734Srpaulo	 * next_try - Time for the next transmission attempt
115214734Srpaulo	 */
116189251Ssam	os_time_t next_try;
117214734Srpaulo
118214734Srpaulo	/**
119214734Srpaulo	 * attempts - Number of transmission attempts
120214734Srpaulo	 */
121189251Ssam	int attempts;
122214734Srpaulo
123214734Srpaulo	/**
124214734Srpaulo	 * next_wait - Next retransmission wait time in seconds
125214734Srpaulo	 */
126189251Ssam	int next_wait;
127214734Srpaulo
128214734Srpaulo	/**
129214734Srpaulo	 * last_attempt - Time of the last transmission attempt
130214734Srpaulo	 */
131189251Ssam	struct os_time last_attempt;
132189251Ssam
133214734Srpaulo	/**
134214734Srpaulo	 * shared_secret - Shared secret with the target RADIUS server
135214734Srpaulo	 */
136214734Srpaulo	const u8 *shared_secret;
137214734Srpaulo
138214734Srpaulo	/**
139214734Srpaulo	 * shared_secret_len - shared_secret length in octets
140214734Srpaulo	 */
141189251Ssam	size_t shared_secret_len;
142189251Ssam
143189251Ssam	/* TODO: server config with failover to backup server(s) */
144189251Ssam
145214734Srpaulo	/**
146214734Srpaulo	 * next - Next message in the list
147214734Srpaulo	 */
148189251Ssam	struct radius_msg_list *next;
149189251Ssam};
150189251Ssam
151189251Ssam
152214734Srpaulo/**
153214734Srpaulo * struct radius_client_data - Internal RADIUS client data
154214734Srpaulo *
155214734Srpaulo * This data structure is used internally inside the RADIUS client module.
156214734Srpaulo * External users allocate this by calling radius_client_init() and free it by
157214734Srpaulo * calling radius_client_deinit(). The pointer to this opaque data is used in
158214734Srpaulo * calls to other functions as an identifier for the RADIUS client instance.
159214734Srpaulo */
160189251Ssamstruct radius_client_data {
161214734Srpaulo	/**
162214734Srpaulo	 * ctx - Context pointer for hostapd_logger() callbacks
163214734Srpaulo	 */
164189251Ssam	void *ctx;
165214734Srpaulo
166214734Srpaulo	/**
167214734Srpaulo	 * conf - RADIUS client configuration (list of RADIUS servers to use)
168214734Srpaulo	 */
169189251Ssam	struct hostapd_radius_servers *conf;
170189251Ssam
171214734Srpaulo	/**
172214734Srpaulo	 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
173214734Srpaulo	 */
174214734Srpaulo	int auth_serv_sock;
175214734Srpaulo
176214734Srpaulo	/**
177214734Srpaulo	 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
178214734Srpaulo	 */
179214734Srpaulo	int acct_serv_sock;
180214734Srpaulo
181214734Srpaulo	/**
182214734Srpaulo	 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
183214734Srpaulo	 */
184189251Ssam	int auth_serv_sock6;
185214734Srpaulo
186214734Srpaulo	/**
187214734Srpaulo	 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
188214734Srpaulo	 */
189189251Ssam	int acct_serv_sock6;
190189251Ssam
191214734Srpaulo	/**
192214734Srpaulo	 * auth_sock - Currently used socket for RADIUS authentication server
193214734Srpaulo	 */
194214734Srpaulo	int auth_sock;
195214734Srpaulo
196214734Srpaulo	/**
197214734Srpaulo	 * acct_sock - Currently used socket for RADIUS accounting server
198214734Srpaulo	 */
199214734Srpaulo	int acct_sock;
200214734Srpaulo
201214734Srpaulo	/**
202214734Srpaulo	 * auth_handlers - Authentication message handlers
203214734Srpaulo	 */
204189251Ssam	struct radius_rx_handler *auth_handlers;
205214734Srpaulo
206214734Srpaulo	/**
207214734Srpaulo	 * num_auth_handlers - Number of handlers in auth_handlers
208214734Srpaulo	 */
209189251Ssam	size_t num_auth_handlers;
210214734Srpaulo
211214734Srpaulo	/**
212214734Srpaulo	 * acct_handlers - Accounting message handlers
213214734Srpaulo	 */
214189251Ssam	struct radius_rx_handler *acct_handlers;
215214734Srpaulo
216214734Srpaulo	/**
217214734Srpaulo	 * num_acct_handlers - Number of handlers in acct_handlers
218214734Srpaulo	 */
219189251Ssam	size_t num_acct_handlers;
220189251Ssam
221214734Srpaulo	/**
222214734Srpaulo	 * msgs - Pending outgoing RADIUS messages
223214734Srpaulo	 */
224189251Ssam	struct radius_msg_list *msgs;
225214734Srpaulo
226214734Srpaulo	/**
227214734Srpaulo	 * num_msgs - Number of pending messages in the msgs list
228214734Srpaulo	 */
229189251Ssam	size_t num_msgs;
230189251Ssam
231214734Srpaulo	/**
232214734Srpaulo	 * next_radius_identifier - Next RADIUS message identifier to use
233214734Srpaulo	 */
234189251Ssam	u8 next_radius_identifier;
235189251Ssam};
236189251Ssam
237189251Ssam
238189251Ssamstatic int
239189251Ssamradius_change_server(struct radius_client_data *radius,
240189251Ssam		     struct hostapd_radius_server *nserv,
241189251Ssam		     struct hostapd_radius_server *oserv,
242189251Ssam		     int sock, int sock6, int auth);
243189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius);
244189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius);
245189251Ssam
246189251Ssam
247189251Ssamstatic void radius_client_msg_free(struct radius_msg_list *req)
248189251Ssam{
249189251Ssam	radius_msg_free(req->msg);
250189251Ssam	os_free(req);
251189251Ssam}
252189251Ssam
253189251Ssam
254214734Srpaulo/**
255214734Srpaulo * radius_client_register - Register a RADIUS client RX handler
256214734Srpaulo * @radius: RADIUS client context from radius_client_init()
257214734Srpaulo * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
258214734Srpaulo * @handler: Handler for received RADIUS messages
259214734Srpaulo * @data: Context pointer for handler callbacks
260214734Srpaulo * Returns: 0 on success, -1 on failure
261214734Srpaulo *
262214734Srpaulo * This function is used to register a handler for processing received RADIUS
263214734Srpaulo * authentication and accounting messages. The handler() callback function will
264214734Srpaulo * be called whenever a RADIUS message is received from the active server.
265214734Srpaulo *
266214734Srpaulo * There can be multiple registered RADIUS message handlers. The handlers will
267214734Srpaulo * be called in order until one of them indicates that it has processed or
268214734Srpaulo * queued the message.
269214734Srpaulo */
270189251Ssamint radius_client_register(struct radius_client_data *radius,
271189251Ssam			   RadiusType msg_type,
272189251Ssam			   RadiusRxResult (*handler)(struct radius_msg *msg,
273189251Ssam						     struct radius_msg *req,
274189251Ssam						     const u8 *shared_secret,
275189251Ssam						     size_t shared_secret_len,
276189251Ssam						     void *data),
277189251Ssam			   void *data)
278189251Ssam{
279189251Ssam	struct radius_rx_handler **handlers, *newh;
280189251Ssam	size_t *num;
281189251Ssam
282189251Ssam	if (msg_type == RADIUS_ACCT) {
283189251Ssam		handlers = &radius->acct_handlers;
284189251Ssam		num = &radius->num_acct_handlers;
285189251Ssam	} else {
286189251Ssam		handlers = &radius->auth_handlers;
287189251Ssam		num = &radius->num_auth_handlers;
288189251Ssam	}
289189251Ssam
290189251Ssam	newh = os_realloc(*handlers,
291189251Ssam			  (*num + 1) * sizeof(struct radius_rx_handler));
292189251Ssam	if (newh == NULL)
293189251Ssam		return -1;
294189251Ssam
295189251Ssam	newh[*num].handler = handler;
296189251Ssam	newh[*num].data = data;
297189251Ssam	(*num)++;
298189251Ssam	*handlers = newh;
299189251Ssam
300189251Ssam	return 0;
301189251Ssam}
302189251Ssam
303189251Ssam
304189251Ssamstatic void radius_client_handle_send_error(struct radius_client_data *radius,
305189251Ssam					    int s, RadiusType msg_type)
306189251Ssam{
307189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
308189251Ssam	int _errno = errno;
309189251Ssam	perror("send[RADIUS]");
310189251Ssam	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
311189251Ssam	    _errno == EBADF) {
312189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
313189251Ssam			       HOSTAPD_LEVEL_INFO,
314189251Ssam			       "Send failed - maybe interface status changed -"
315189251Ssam			       " try to connect again");
316189251Ssam		eloop_unregister_read_sock(s);
317189251Ssam		close(s);
318189251Ssam		if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
319189251Ssam			radius_client_init_acct(radius);
320189251Ssam		else
321189251Ssam			radius_client_init_auth(radius);
322189251Ssam	}
323189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
324189251Ssam}
325189251Ssam
326189251Ssam
327189251Ssamstatic int radius_client_retransmit(struct radius_client_data *radius,
328189251Ssam				    struct radius_msg_list *entry,
329189251Ssam				    os_time_t now)
330189251Ssam{
331189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
332189251Ssam	int s;
333214734Srpaulo	struct wpabuf *buf;
334189251Ssam
335189251Ssam	if (entry->msg_type == RADIUS_ACCT ||
336189251Ssam	    entry->msg_type == RADIUS_ACCT_INTERIM) {
337189251Ssam		s = radius->acct_sock;
338189251Ssam		if (entry->attempts == 0)
339189251Ssam			conf->acct_server->requests++;
340189251Ssam		else {
341189251Ssam			conf->acct_server->timeouts++;
342189251Ssam			conf->acct_server->retransmissions++;
343189251Ssam		}
344189251Ssam	} else {
345189251Ssam		s = radius->auth_sock;
346189251Ssam		if (entry->attempts == 0)
347189251Ssam			conf->auth_server->requests++;
348189251Ssam		else {
349189251Ssam			conf->auth_server->timeouts++;
350189251Ssam			conf->auth_server->retransmissions++;
351189251Ssam		}
352189251Ssam	}
353189251Ssam
354189251Ssam	/* retransmit; remove entry if too many attempts */
355189251Ssam	entry->attempts++;
356189251Ssam	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
357189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
358214734Srpaulo		       radius_msg_get_hdr(entry->msg)->identifier);
359189251Ssam
360189251Ssam	os_get_time(&entry->last_attempt);
361214734Srpaulo	buf = radius_msg_get_buf(entry->msg);
362214734Srpaulo	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
363189251Ssam		radius_client_handle_send_error(radius, s, entry->msg_type);
364189251Ssam
365189251Ssam	entry->next_try = now + entry->next_wait;
366189251Ssam	entry->next_wait *= 2;
367189251Ssam	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
368189251Ssam		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
369189251Ssam	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
370189251Ssam		printf("Removing un-ACKed RADIUS message due to too many "
371189251Ssam		       "failed retransmit attempts\n");
372189251Ssam		return 1;
373189251Ssam	}
374189251Ssam
375189251Ssam	return 0;
376189251Ssam}
377189251Ssam
378189251Ssam
379189251Ssamstatic void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
380189251Ssam{
381189251Ssam	struct radius_client_data *radius = eloop_ctx;
382189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
383189251Ssam	struct os_time now;
384189251Ssam	os_time_t first;
385189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
386189251Ssam	int auth_failover = 0, acct_failover = 0;
387189251Ssam	char abuf[50];
388189251Ssam
389189251Ssam	entry = radius->msgs;
390189251Ssam	if (!entry)
391189251Ssam		return;
392189251Ssam
393189251Ssam	os_get_time(&now);
394189251Ssam	first = 0;
395189251Ssam
396189251Ssam	prev = NULL;
397189251Ssam	while (entry) {
398189251Ssam		if (now.sec >= entry->next_try &&
399189251Ssam		    radius_client_retransmit(radius, entry, now.sec)) {
400189251Ssam			if (prev)
401189251Ssam				prev->next = entry->next;
402189251Ssam			else
403189251Ssam				radius->msgs = entry->next;
404189251Ssam
405189251Ssam			tmp = entry;
406189251Ssam			entry = entry->next;
407189251Ssam			radius_client_msg_free(tmp);
408189251Ssam			radius->num_msgs--;
409189251Ssam			continue;
410189251Ssam		}
411189251Ssam
412189251Ssam		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
413189251Ssam			if (entry->msg_type == RADIUS_ACCT ||
414189251Ssam			    entry->msg_type == RADIUS_ACCT_INTERIM)
415189251Ssam				acct_failover++;
416189251Ssam			else
417189251Ssam				auth_failover++;
418189251Ssam		}
419189251Ssam
420189251Ssam		if (first == 0 || entry->next_try < first)
421189251Ssam			first = entry->next_try;
422189251Ssam
423189251Ssam		prev = entry;
424189251Ssam		entry = entry->next;
425189251Ssam	}
426189251Ssam
427189251Ssam	if (radius->msgs) {
428189251Ssam		if (first < now.sec)
429189251Ssam			first = now.sec;
430189251Ssam		eloop_register_timeout(first - now.sec, 0,
431189251Ssam				       radius_client_timer, radius, NULL);
432189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
433189251Ssam			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
434189251Ssam			       "retransmit in %ld seconds",
435189251Ssam			       (long int) (first - now.sec));
436189251Ssam	}
437189251Ssam
438189251Ssam	if (auth_failover && conf->num_auth_servers > 1) {
439189251Ssam		struct hostapd_radius_server *next, *old;
440189251Ssam		old = conf->auth_server;
441189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
442189251Ssam			       HOSTAPD_LEVEL_NOTICE,
443189251Ssam			       "No response from Authentication server "
444189251Ssam			       "%s:%d - failover",
445189251Ssam			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
446189251Ssam			       old->port);
447189251Ssam
448189251Ssam		for (entry = radius->msgs; entry; entry = entry->next) {
449189251Ssam			if (entry->msg_type == RADIUS_AUTH)
450189251Ssam				old->timeouts++;
451189251Ssam		}
452189251Ssam
453189251Ssam		next = old + 1;
454189251Ssam		if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
455189251Ssam			next = conf->auth_servers;
456189251Ssam		conf->auth_server = next;
457189251Ssam		radius_change_server(radius, next, old,
458189251Ssam				     radius->auth_serv_sock,
459189251Ssam				     radius->auth_serv_sock6, 1);
460189251Ssam	}
461189251Ssam
462189251Ssam	if (acct_failover && conf->num_acct_servers > 1) {
463189251Ssam		struct hostapd_radius_server *next, *old;
464189251Ssam		old = conf->acct_server;
465189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
466189251Ssam			       HOSTAPD_LEVEL_NOTICE,
467189251Ssam			       "No response from Accounting server "
468189251Ssam			       "%s:%d - failover",
469189251Ssam			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
470189251Ssam			       old->port);
471189251Ssam
472189251Ssam		for (entry = radius->msgs; entry; entry = entry->next) {
473189251Ssam			if (entry->msg_type == RADIUS_ACCT ||
474189251Ssam			    entry->msg_type == RADIUS_ACCT_INTERIM)
475189251Ssam				old->timeouts++;
476189251Ssam		}
477189251Ssam
478189251Ssam		next = old + 1;
479189251Ssam		if (next > &conf->acct_servers[conf->num_acct_servers - 1])
480189251Ssam			next = conf->acct_servers;
481189251Ssam		conf->acct_server = next;
482189251Ssam		radius_change_server(radius, next, old,
483189251Ssam				     radius->acct_serv_sock,
484189251Ssam				     radius->acct_serv_sock6, 0);
485189251Ssam	}
486189251Ssam}
487189251Ssam
488189251Ssam
489189251Ssamstatic void radius_client_update_timeout(struct radius_client_data *radius)
490189251Ssam{
491189251Ssam	struct os_time now;
492189251Ssam	os_time_t first;
493189251Ssam	struct radius_msg_list *entry;
494189251Ssam
495189251Ssam	eloop_cancel_timeout(radius_client_timer, radius, NULL);
496189251Ssam
497189251Ssam	if (radius->msgs == NULL) {
498189251Ssam		return;
499189251Ssam	}
500189251Ssam
501189251Ssam	first = 0;
502189251Ssam	for (entry = radius->msgs; entry; entry = entry->next) {
503189251Ssam		if (first == 0 || entry->next_try < first)
504189251Ssam			first = entry->next_try;
505189251Ssam	}
506189251Ssam
507189251Ssam	os_get_time(&now);
508189251Ssam	if (first < now.sec)
509189251Ssam		first = now.sec;
510189251Ssam	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
511189251Ssam			       NULL);
512189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
513189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
514189251Ssam		       " %ld seconds\n", (long int) (first - now.sec));
515189251Ssam}
516189251Ssam
517189251Ssam
518189251Ssamstatic void radius_client_list_add(struct radius_client_data *radius,
519189251Ssam				   struct radius_msg *msg,
520214734Srpaulo				   RadiusType msg_type,
521214734Srpaulo				   const u8 *shared_secret,
522189251Ssam				   size_t shared_secret_len, const u8 *addr)
523189251Ssam{
524189251Ssam	struct radius_msg_list *entry, *prev;
525189251Ssam
526189251Ssam	if (eloop_terminated()) {
527189251Ssam		/* No point in adding entries to retransmit queue since event
528189251Ssam		 * loop has already been terminated. */
529189251Ssam		radius_msg_free(msg);
530189251Ssam		return;
531189251Ssam	}
532189251Ssam
533189251Ssam	entry = os_zalloc(sizeof(*entry));
534189251Ssam	if (entry == NULL) {
535189251Ssam		printf("Failed to add RADIUS packet into retransmit list\n");
536189251Ssam		radius_msg_free(msg);
537189251Ssam		return;
538189251Ssam	}
539189251Ssam
540189251Ssam	if (addr)
541189251Ssam		os_memcpy(entry->addr, addr, ETH_ALEN);
542189251Ssam	entry->msg = msg;
543189251Ssam	entry->msg_type = msg_type;
544189251Ssam	entry->shared_secret = shared_secret;
545189251Ssam	entry->shared_secret_len = shared_secret_len;
546189251Ssam	os_get_time(&entry->last_attempt);
547189251Ssam	entry->first_try = entry->last_attempt.sec;
548189251Ssam	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
549189251Ssam	entry->attempts = 1;
550189251Ssam	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
551189251Ssam	entry->next = radius->msgs;
552189251Ssam	radius->msgs = entry;
553189251Ssam	radius_client_update_timeout(radius);
554189251Ssam
555189251Ssam	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
556189251Ssam		printf("Removing the oldest un-ACKed RADIUS packet due to "
557189251Ssam		       "retransmit list limits.\n");
558189251Ssam		prev = NULL;
559189251Ssam		while (entry->next) {
560189251Ssam			prev = entry;
561189251Ssam			entry = entry->next;
562189251Ssam		}
563189251Ssam		if (prev) {
564189251Ssam			prev->next = NULL;
565189251Ssam			radius_client_msg_free(entry);
566189251Ssam		}
567189251Ssam	} else
568189251Ssam		radius->num_msgs++;
569189251Ssam}
570189251Ssam
571189251Ssam
572189251Ssamstatic void radius_client_list_del(struct radius_client_data *radius,
573189251Ssam				   RadiusType msg_type, const u8 *addr)
574189251Ssam{
575189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
576189251Ssam
577189251Ssam	if (addr == NULL)
578189251Ssam		return;
579189251Ssam
580189251Ssam	entry = radius->msgs;
581189251Ssam	prev = NULL;
582189251Ssam	while (entry) {
583189251Ssam		if (entry->msg_type == msg_type &&
584189251Ssam		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
585189251Ssam			if (prev)
586189251Ssam				prev->next = entry->next;
587189251Ssam			else
588189251Ssam				radius->msgs = entry->next;
589189251Ssam			tmp = entry;
590189251Ssam			entry = entry->next;
591189251Ssam			hostapd_logger(radius->ctx, addr,
592189251Ssam				       HOSTAPD_MODULE_RADIUS,
593189251Ssam				       HOSTAPD_LEVEL_DEBUG,
594189251Ssam				       "Removing matching RADIUS message");
595189251Ssam			radius_client_msg_free(tmp);
596189251Ssam			radius->num_msgs--;
597189251Ssam			continue;
598189251Ssam		}
599189251Ssam		prev = entry;
600189251Ssam		entry = entry->next;
601189251Ssam	}
602189251Ssam}
603189251Ssam
604189251Ssam
605214734Srpaulo/**
606214734Srpaulo * radius_client_send - Send a RADIUS request
607214734Srpaulo * @radius: RADIUS client context from radius_client_init()
608214734Srpaulo * @msg: RADIUS message to be sent
609214734Srpaulo * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
610214734Srpaulo * @addr: MAC address of the device related to this message or %NULL
611214734Srpaulo * Returns: 0 on success, -1 on failure
612214734Srpaulo *
613214734Srpaulo * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
614214734Srpaulo * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
615214734Srpaulo * between accounting and interim accounting messages is that the interim
616214734Srpaulo * message will override any pending interim accounting updates while a new
617214734Srpaulo * accounting message does not remove any pending messages.
618214734Srpaulo *
619214734Srpaulo * The message is added on the retransmission queue and will be retransmitted
620214734Srpaulo * automatically until a response is received or maximum number of retries
621214734Srpaulo * (RADIUS_CLIENT_MAX_RETRIES) is reached.
622214734Srpaulo *
623214734Srpaulo * The related device MAC address can be used to identify pending messages that
624214734Srpaulo * can be removed with radius_client_flush_auth() or with interim accounting
625214734Srpaulo * updates.
626214734Srpaulo */
627189251Ssamint radius_client_send(struct radius_client_data *radius,
628189251Ssam		       struct radius_msg *msg, RadiusType msg_type,
629189251Ssam		       const u8 *addr)
630189251Ssam{
631189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
632214734Srpaulo	const u8 *shared_secret;
633189251Ssam	size_t shared_secret_len;
634189251Ssam	char *name;
635189251Ssam	int s, res;
636214734Srpaulo	struct wpabuf *buf;
637189251Ssam
638189251Ssam	if (msg_type == RADIUS_ACCT_INTERIM) {
639189251Ssam		/* Remove any pending interim acct update for the same STA. */
640189251Ssam		radius_client_list_del(radius, msg_type, addr);
641189251Ssam	}
642189251Ssam
643189251Ssam	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
644189251Ssam		if (conf->acct_server == NULL) {
645189251Ssam			hostapd_logger(radius->ctx, NULL,
646189251Ssam				       HOSTAPD_MODULE_RADIUS,
647189251Ssam				       HOSTAPD_LEVEL_INFO,
648189251Ssam				       "No accounting server configured");
649189251Ssam			return -1;
650189251Ssam		}
651189251Ssam		shared_secret = conf->acct_server->shared_secret;
652189251Ssam		shared_secret_len = conf->acct_server->shared_secret_len;
653189251Ssam		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
654189251Ssam		name = "accounting";
655189251Ssam		s = radius->acct_sock;
656189251Ssam		conf->acct_server->requests++;
657189251Ssam	} else {
658189251Ssam		if (conf->auth_server == NULL) {
659189251Ssam			hostapd_logger(radius->ctx, NULL,
660189251Ssam				       HOSTAPD_MODULE_RADIUS,
661189251Ssam				       HOSTAPD_LEVEL_INFO,
662189251Ssam				       "No authentication server configured");
663189251Ssam			return -1;
664189251Ssam		}
665189251Ssam		shared_secret = conf->auth_server->shared_secret;
666189251Ssam		shared_secret_len = conf->auth_server->shared_secret_len;
667189251Ssam		radius_msg_finish(msg, shared_secret, shared_secret_len);
668189251Ssam		name = "authentication";
669189251Ssam		s = radius->auth_sock;
670189251Ssam		conf->auth_server->requests++;
671189251Ssam	}
672189251Ssam
673189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
674189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
675189251Ssam		       "server", name);
676189251Ssam	if (conf->msg_dumps)
677189251Ssam		radius_msg_dump(msg);
678189251Ssam
679214734Srpaulo	buf = radius_msg_get_buf(msg);
680214734Srpaulo	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
681189251Ssam	if (res < 0)
682189251Ssam		radius_client_handle_send_error(radius, s, msg_type);
683189251Ssam
684189251Ssam	radius_client_list_add(radius, msg, msg_type, shared_secret,
685189251Ssam			       shared_secret_len, addr);
686189251Ssam
687189251Ssam	return res;
688189251Ssam}
689189251Ssam
690189251Ssam
691189251Ssamstatic void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
692189251Ssam{
693189251Ssam	struct radius_client_data *radius = eloop_ctx;
694189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
695189251Ssam	RadiusType msg_type = (RadiusType) sock_ctx;
696189251Ssam	int len, roundtrip;
697189251Ssam	unsigned char buf[3000];
698189251Ssam	struct radius_msg *msg;
699214734Srpaulo	struct radius_hdr *hdr;
700189251Ssam	struct radius_rx_handler *handlers;
701189251Ssam	size_t num_handlers, i;
702189251Ssam	struct radius_msg_list *req, *prev_req;
703189251Ssam	struct os_time now;
704189251Ssam	struct hostapd_radius_server *rconf;
705189251Ssam	int invalid_authenticator = 0;
706189251Ssam
707189251Ssam	if (msg_type == RADIUS_ACCT) {
708189251Ssam		handlers = radius->acct_handlers;
709189251Ssam		num_handlers = radius->num_acct_handlers;
710189251Ssam		rconf = conf->acct_server;
711189251Ssam	} else {
712189251Ssam		handlers = radius->auth_handlers;
713189251Ssam		num_handlers = radius->num_auth_handlers;
714189251Ssam		rconf = conf->auth_server;
715189251Ssam	}
716189251Ssam
717189251Ssam	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
718189251Ssam	if (len < 0) {
719189251Ssam		perror("recv[RADIUS]");
720189251Ssam		return;
721189251Ssam	}
722189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
723189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
724189251Ssam		       "server", len);
725189251Ssam	if (len == sizeof(buf)) {
726189251Ssam		printf("Possibly too long UDP frame for our buffer - "
727189251Ssam		       "dropping it\n");
728189251Ssam		return;
729189251Ssam	}
730189251Ssam
731189251Ssam	msg = radius_msg_parse(buf, len);
732189251Ssam	if (msg == NULL) {
733189251Ssam		printf("Parsing incoming RADIUS frame failed\n");
734189251Ssam		rconf->malformed_responses++;
735189251Ssam		return;
736189251Ssam	}
737214734Srpaulo	hdr = radius_msg_get_hdr(msg);
738189251Ssam
739189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
740189251Ssam		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
741189251Ssam	if (conf->msg_dumps)
742189251Ssam		radius_msg_dump(msg);
743189251Ssam
744214734Srpaulo	switch (hdr->code) {
745189251Ssam	case RADIUS_CODE_ACCESS_ACCEPT:
746189251Ssam		rconf->access_accepts++;
747189251Ssam		break;
748189251Ssam	case RADIUS_CODE_ACCESS_REJECT:
749189251Ssam		rconf->access_rejects++;
750189251Ssam		break;
751189251Ssam	case RADIUS_CODE_ACCESS_CHALLENGE:
752189251Ssam		rconf->access_challenges++;
753189251Ssam		break;
754189251Ssam	case RADIUS_CODE_ACCOUNTING_RESPONSE:
755189251Ssam		rconf->responses++;
756189251Ssam		break;
757189251Ssam	}
758189251Ssam
759189251Ssam	prev_req = NULL;
760189251Ssam	req = radius->msgs;
761189251Ssam	while (req) {
762189251Ssam		/* TODO: also match by src addr:port of the packet when using
763189251Ssam		 * alternative RADIUS servers (?) */
764189251Ssam		if ((req->msg_type == msg_type ||
765189251Ssam		     (req->msg_type == RADIUS_ACCT_INTERIM &&
766189251Ssam		      msg_type == RADIUS_ACCT)) &&
767214734Srpaulo		    radius_msg_get_hdr(req->msg)->identifier ==
768214734Srpaulo		    hdr->identifier)
769189251Ssam			break;
770189251Ssam
771189251Ssam		prev_req = req;
772189251Ssam		req = req->next;
773189251Ssam	}
774189251Ssam
775189251Ssam	if (req == NULL) {
776189251Ssam		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
777189251Ssam			       HOSTAPD_LEVEL_DEBUG,
778189251Ssam			       "No matching RADIUS request found (type=%d "
779189251Ssam			       "id=%d) - dropping packet",
780214734Srpaulo			       msg_type, hdr->identifier);
781189251Ssam		goto fail;
782189251Ssam	}
783189251Ssam
784189251Ssam	os_get_time(&now);
785189251Ssam	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
786189251Ssam		(now.usec - req->last_attempt.usec) / 10000;
787189251Ssam	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
788189251Ssam		       HOSTAPD_LEVEL_DEBUG,
789189251Ssam		       "Received RADIUS packet matched with a pending "
790189251Ssam		       "request, round trip time %d.%02d sec",
791189251Ssam		       roundtrip / 100, roundtrip % 100);
792189251Ssam	rconf->round_trip_time = roundtrip;
793189251Ssam
794189251Ssam	/* Remove ACKed RADIUS packet from retransmit list */
795189251Ssam	if (prev_req)
796189251Ssam		prev_req->next = req->next;
797189251Ssam	else
798189251Ssam		radius->msgs = req->next;
799189251Ssam	radius->num_msgs--;
800189251Ssam
801189251Ssam	for (i = 0; i < num_handlers; i++) {
802189251Ssam		RadiusRxResult res;
803189251Ssam		res = handlers[i].handler(msg, req->msg, req->shared_secret,
804189251Ssam					  req->shared_secret_len,
805189251Ssam					  handlers[i].data);
806189251Ssam		switch (res) {
807189251Ssam		case RADIUS_RX_PROCESSED:
808189251Ssam			radius_msg_free(msg);
809189251Ssam			/* continue */
810189251Ssam		case RADIUS_RX_QUEUED:
811189251Ssam			radius_client_msg_free(req);
812189251Ssam			return;
813189251Ssam		case RADIUS_RX_INVALID_AUTHENTICATOR:
814189251Ssam			invalid_authenticator++;
815189251Ssam			/* continue */
816189251Ssam		case RADIUS_RX_UNKNOWN:
817189251Ssam			/* continue with next handler */
818189251Ssam			break;
819189251Ssam		}
820189251Ssam	}
821189251Ssam
822189251Ssam	if (invalid_authenticator)
823189251Ssam		rconf->bad_authenticators++;
824189251Ssam	else
825189251Ssam		rconf->unknown_types++;
826189251Ssam	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
827189251Ssam		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
828189251Ssam		       "(type=%d code=%d id=%d)%s - dropping packet",
829214734Srpaulo		       msg_type, hdr->code, hdr->identifier,
830189251Ssam		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
831189251Ssam		       "");
832189251Ssam	radius_client_msg_free(req);
833189251Ssam
834189251Ssam fail:
835189251Ssam	radius_msg_free(msg);
836189251Ssam}
837189251Ssam
838189251Ssam
839214734Srpaulo/**
840214734Srpaulo * radius_client_get_id - Get an identifier for a new RADIUS message
841214734Srpaulo * @radius: RADIUS client context from radius_client_init()
842214734Srpaulo * Returns: Allocated identifier
843214734Srpaulo *
844214734Srpaulo * This function is used to fetch a unique (among pending requests) identifier
845214734Srpaulo * for a new RADIUS message.
846214734Srpaulo */
847189251Ssamu8 radius_client_get_id(struct radius_client_data *radius)
848189251Ssam{
849189251Ssam	struct radius_msg_list *entry, *prev, *_remove;
850189251Ssam	u8 id = radius->next_radius_identifier++;
851189251Ssam
852189251Ssam	/* remove entries with matching id from retransmit list to avoid
853189251Ssam	 * using new reply from the RADIUS server with an old request */
854189251Ssam	entry = radius->msgs;
855189251Ssam	prev = NULL;
856189251Ssam	while (entry) {
857214734Srpaulo		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
858189251Ssam			hostapd_logger(radius->ctx, entry->addr,
859189251Ssam				       HOSTAPD_MODULE_RADIUS,
860189251Ssam				       HOSTAPD_LEVEL_DEBUG,
861189251Ssam				       "Removing pending RADIUS message, "
862189251Ssam				       "since its id (%d) is reused", id);
863189251Ssam			if (prev)
864189251Ssam				prev->next = entry->next;
865189251Ssam			else
866189251Ssam				radius->msgs = entry->next;
867189251Ssam			_remove = entry;
868189251Ssam		} else {
869189251Ssam			_remove = NULL;
870189251Ssam			prev = entry;
871189251Ssam		}
872189251Ssam		entry = entry->next;
873189251Ssam
874189251Ssam		if (_remove)
875189251Ssam			radius_client_msg_free(_remove);
876189251Ssam	}
877189251Ssam
878189251Ssam	return id;
879189251Ssam}
880189251Ssam
881189251Ssam
882214734Srpaulo/**
883214734Srpaulo * radius_client_flush - Flush all pending RADIUS client messages
884214734Srpaulo * @radius: RADIUS client context from radius_client_init()
885214734Srpaulo * @only_auth: Whether only authentication messages are removed
886214734Srpaulo */
887189251Ssamvoid radius_client_flush(struct radius_client_data *radius, int only_auth)
888189251Ssam{
889189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
890189251Ssam
891189251Ssam	if (!radius)
892189251Ssam		return;
893189251Ssam
894189251Ssam	prev = NULL;
895189251Ssam	entry = radius->msgs;
896189251Ssam
897189251Ssam	while (entry) {
898189251Ssam		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
899189251Ssam			if (prev)
900189251Ssam				prev->next = entry->next;
901189251Ssam			else
902189251Ssam				radius->msgs = entry->next;
903189251Ssam
904189251Ssam			tmp = entry;
905189251Ssam			entry = entry->next;
906189251Ssam			radius_client_msg_free(tmp);
907189251Ssam			radius->num_msgs--;
908189251Ssam		} else {
909189251Ssam			prev = entry;
910189251Ssam			entry = entry->next;
911189251Ssam		}
912189251Ssam	}
913189251Ssam
914189251Ssam	if (radius->msgs == NULL)
915189251Ssam		eloop_cancel_timeout(radius_client_timer, radius, NULL);
916189251Ssam}
917189251Ssam
918189251Ssam
919189251Ssamstatic void radius_client_update_acct_msgs(struct radius_client_data *radius,
920214734Srpaulo					   const u8 *shared_secret,
921189251Ssam					   size_t shared_secret_len)
922189251Ssam{
923189251Ssam	struct radius_msg_list *entry;
924189251Ssam
925189251Ssam	if (!radius)
926189251Ssam		return;
927189251Ssam
928189251Ssam	for (entry = radius->msgs; entry; entry = entry->next) {
929189251Ssam		if (entry->msg_type == RADIUS_ACCT) {
930189251Ssam			entry->shared_secret = shared_secret;
931189251Ssam			entry->shared_secret_len = shared_secret_len;
932189251Ssam			radius_msg_finish_acct(entry->msg, shared_secret,
933189251Ssam					       shared_secret_len);
934189251Ssam		}
935189251Ssam	}
936189251Ssam}
937189251Ssam
938189251Ssam
939189251Ssamstatic int
940189251Ssamradius_change_server(struct radius_client_data *radius,
941189251Ssam		     struct hostapd_radius_server *nserv,
942189251Ssam		     struct hostapd_radius_server *oserv,
943189251Ssam		     int sock, int sock6, int auth)
944189251Ssam{
945189251Ssam	struct sockaddr_in serv, claddr;
946189251Ssam#ifdef CONFIG_IPV6
947189251Ssam	struct sockaddr_in6 serv6, claddr6;
948189251Ssam#endif /* CONFIG_IPV6 */
949189251Ssam	struct sockaddr *addr, *cl_addr;
950189251Ssam	socklen_t addrlen, claddrlen;
951189251Ssam	char abuf[50];
952189251Ssam	int sel_sock;
953189251Ssam	struct radius_msg_list *entry;
954189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
955189251Ssam
956189251Ssam	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
957189251Ssam		       HOSTAPD_LEVEL_INFO,
958189251Ssam		       "%s server %s:%d",
959189251Ssam		       auth ? "Authentication" : "Accounting",
960189251Ssam		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
961189251Ssam		       nserv->port);
962189251Ssam
963189251Ssam	if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
964189251Ssam	    os_memcmp(nserv->shared_secret, oserv->shared_secret,
965189251Ssam		      nserv->shared_secret_len) != 0) {
966189251Ssam		/* Pending RADIUS packets used different shared secret, so
967189251Ssam		 * they need to be modified. Update accounting message
968189251Ssam		 * authenticators here. Authentication messages are removed
969189251Ssam		 * since they would require more changes and the new RADIUS
970189251Ssam		 * server may not be prepared to receive them anyway due to
971189251Ssam		 * missing state information. Client will likely retry
972189251Ssam		 * authentication, so this should not be an issue. */
973189251Ssam		if (auth)
974189251Ssam			radius_client_flush(radius, 1);
975189251Ssam		else {
976189251Ssam			radius_client_update_acct_msgs(
977189251Ssam				radius, nserv->shared_secret,
978189251Ssam				nserv->shared_secret_len);
979189251Ssam		}
980189251Ssam	}
981189251Ssam
982189251Ssam	/* Reset retry counters for the new server */
983189251Ssam	for (entry = radius->msgs; entry; entry = entry->next) {
984189251Ssam		if ((auth && entry->msg_type != RADIUS_AUTH) ||
985189251Ssam		    (!auth && entry->msg_type != RADIUS_ACCT))
986189251Ssam			continue;
987189251Ssam		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
988189251Ssam		entry->attempts = 0;
989189251Ssam		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
990189251Ssam	}
991189251Ssam
992189251Ssam	if (radius->msgs) {
993189251Ssam		eloop_cancel_timeout(radius_client_timer, radius, NULL);
994189251Ssam		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
995189251Ssam				       radius_client_timer, radius, NULL);
996189251Ssam	}
997189251Ssam
998189251Ssam	switch (nserv->addr.af) {
999189251Ssam	case AF_INET:
1000189251Ssam		os_memset(&serv, 0, sizeof(serv));
1001189251Ssam		serv.sin_family = AF_INET;
1002189251Ssam		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
1003189251Ssam		serv.sin_port = htons(nserv->port);
1004189251Ssam		addr = (struct sockaddr *) &serv;
1005189251Ssam		addrlen = sizeof(serv);
1006189251Ssam		sel_sock = sock;
1007189251Ssam		break;
1008189251Ssam#ifdef CONFIG_IPV6
1009189251Ssam	case AF_INET6:
1010189251Ssam		os_memset(&serv6, 0, sizeof(serv6));
1011189251Ssam		serv6.sin6_family = AF_INET6;
1012189251Ssam		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
1013189251Ssam			  sizeof(struct in6_addr));
1014189251Ssam		serv6.sin6_port = htons(nserv->port);
1015189251Ssam		addr = (struct sockaddr *) &serv6;
1016189251Ssam		addrlen = sizeof(serv6);
1017189251Ssam		sel_sock = sock6;
1018189251Ssam		break;
1019189251Ssam#endif /* CONFIG_IPV6 */
1020189251Ssam	default:
1021189251Ssam		return -1;
1022189251Ssam	}
1023189251Ssam
1024189251Ssam	if (conf->force_client_addr) {
1025189251Ssam		switch (conf->client_addr.af) {
1026189251Ssam		case AF_INET:
1027189251Ssam			os_memset(&claddr, 0, sizeof(claddr));
1028189251Ssam			claddr.sin_family = AF_INET;
1029189251Ssam			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
1030189251Ssam			claddr.sin_port = htons(0);
1031189251Ssam			cl_addr = (struct sockaddr *) &claddr;
1032189251Ssam			claddrlen = sizeof(claddr);
1033189251Ssam			break;
1034189251Ssam#ifdef CONFIG_IPV6
1035189251Ssam		case AF_INET6:
1036189251Ssam			os_memset(&claddr6, 0, sizeof(claddr6));
1037189251Ssam			claddr6.sin6_family = AF_INET6;
1038189251Ssam			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
1039189251Ssam				  sizeof(struct in6_addr));
1040189251Ssam			claddr6.sin6_port = htons(0);
1041189251Ssam			cl_addr = (struct sockaddr *) &claddr6;
1042189251Ssam			claddrlen = sizeof(claddr6);
1043189251Ssam			break;
1044189251Ssam#endif /* CONFIG_IPV6 */
1045189251Ssam		default:
1046189251Ssam			return -1;
1047189251Ssam		}
1048189251Ssam
1049189251Ssam		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
1050189251Ssam			perror("bind[radius]");
1051189251Ssam			return -1;
1052189251Ssam		}
1053189251Ssam	}
1054189251Ssam
1055189251Ssam	if (connect(sel_sock, addr, addrlen) < 0) {
1056189251Ssam		perror("connect[radius]");
1057189251Ssam		return -1;
1058189251Ssam	}
1059189251Ssam
1060189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
1061189251Ssam	switch (nserv->addr.af) {
1062189251Ssam	case AF_INET:
1063189251Ssam		claddrlen = sizeof(claddr);
1064189251Ssam		getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
1065189251Ssam		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1066189251Ssam			   inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
1067189251Ssam		break;
1068189251Ssam#ifdef CONFIG_IPV6
1069189251Ssam	case AF_INET6: {
1070189251Ssam		claddrlen = sizeof(claddr6);
1071189251Ssam		getsockname(sel_sock, (struct sockaddr *) &claddr6,
1072189251Ssam			    &claddrlen);
1073189251Ssam		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1074189251Ssam			   inet_ntop(AF_INET6, &claddr6.sin6_addr,
1075189251Ssam				     abuf, sizeof(abuf)),
1076189251Ssam			   ntohs(claddr6.sin6_port));
1077189251Ssam		break;
1078189251Ssam	}
1079189251Ssam#endif /* CONFIG_IPV6 */
1080189251Ssam	}
1081189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1082189251Ssam
1083189251Ssam	if (auth)
1084189251Ssam		radius->auth_sock = sel_sock;
1085189251Ssam	else
1086189251Ssam		radius->acct_sock = sel_sock;
1087189251Ssam
1088189251Ssam	return 0;
1089189251Ssam}
1090189251Ssam
1091189251Ssam
1092189251Ssamstatic void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
1093189251Ssam{
1094189251Ssam	struct radius_client_data *radius = eloop_ctx;
1095189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1096189251Ssam	struct hostapd_radius_server *oserv;
1097189251Ssam
1098189251Ssam	if (radius->auth_sock >= 0 && conf->auth_servers &&
1099189251Ssam	    conf->auth_server != conf->auth_servers) {
1100189251Ssam		oserv = conf->auth_server;
1101189251Ssam		conf->auth_server = conf->auth_servers;
1102189251Ssam		radius_change_server(radius, conf->auth_server, oserv,
1103189251Ssam				     radius->auth_serv_sock,
1104189251Ssam				     radius->auth_serv_sock6, 1);
1105189251Ssam	}
1106189251Ssam
1107189251Ssam	if (radius->acct_sock >= 0 && conf->acct_servers &&
1108189251Ssam	    conf->acct_server != conf->acct_servers) {
1109189251Ssam		oserv = conf->acct_server;
1110189251Ssam		conf->acct_server = conf->acct_servers;
1111189251Ssam		radius_change_server(radius, conf->acct_server, oserv,
1112189251Ssam				     radius->acct_serv_sock,
1113189251Ssam				     radius->acct_serv_sock6, 0);
1114189251Ssam	}
1115189251Ssam
1116189251Ssam	if (conf->retry_primary_interval)
1117189251Ssam		eloop_register_timeout(conf->retry_primary_interval, 0,
1118189251Ssam				       radius_retry_primary_timer, radius,
1119189251Ssam				       NULL);
1120189251Ssam}
1121189251Ssam
1122189251Ssam
1123209158Srpaulostatic int radius_client_disable_pmtu_discovery(int s)
1124209158Srpaulo{
1125209158Srpaulo	int r = -1;
1126209158Srpaulo#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1127209158Srpaulo	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
1128209158Srpaulo	int action = IP_PMTUDISC_DONT;
1129209158Srpaulo	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
1130209158Srpaulo		       sizeof(action));
1131209158Srpaulo	if (r == -1)
1132209158Srpaulo		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
1133209158Srpaulo			   "%s", strerror(errno));
1134209158Srpaulo#endif
1135209158Srpaulo	return r;
1136209158Srpaulo}
1137209158Srpaulo
1138209158Srpaulo
1139189251Ssamstatic int radius_client_init_auth(struct radius_client_data *radius)
1140189251Ssam{
1141189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1142189251Ssam	int ok = 0;
1143189251Ssam
1144189251Ssam	radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1145189251Ssam	if (radius->auth_serv_sock < 0)
1146189251Ssam		perror("socket[PF_INET,SOCK_DGRAM]");
1147209158Srpaulo	else {
1148209158Srpaulo		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
1149189251Ssam		ok++;
1150209158Srpaulo	}
1151189251Ssam
1152189251Ssam#ifdef CONFIG_IPV6
1153189251Ssam	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1154189251Ssam	if (radius->auth_serv_sock6 < 0)
1155189251Ssam		perror("socket[PF_INET6,SOCK_DGRAM]");
1156189251Ssam	else
1157189251Ssam		ok++;
1158189251Ssam#endif /* CONFIG_IPV6 */
1159189251Ssam
1160189251Ssam	if (ok == 0)
1161189251Ssam		return -1;
1162189251Ssam
1163189251Ssam	radius_change_server(radius, conf->auth_server, NULL,
1164189251Ssam			     radius->auth_serv_sock, radius->auth_serv_sock6,
1165189251Ssam			     1);
1166189251Ssam
1167189251Ssam	if (radius->auth_serv_sock >= 0 &&
1168189251Ssam	    eloop_register_read_sock(radius->auth_serv_sock,
1169189251Ssam				     radius_client_receive, radius,
1170189251Ssam				     (void *) RADIUS_AUTH)) {
1171189251Ssam		printf("Could not register read socket for authentication "
1172189251Ssam		       "server\n");
1173189251Ssam		return -1;
1174189251Ssam	}
1175189251Ssam
1176189251Ssam#ifdef CONFIG_IPV6
1177189251Ssam	if (radius->auth_serv_sock6 >= 0 &&
1178189251Ssam	    eloop_register_read_sock(radius->auth_serv_sock6,
1179189251Ssam				     radius_client_receive, radius,
1180189251Ssam				     (void *) RADIUS_AUTH)) {
1181189251Ssam		printf("Could not register read socket for authentication "
1182189251Ssam		       "server\n");
1183189251Ssam		return -1;
1184189251Ssam	}
1185189251Ssam#endif /* CONFIG_IPV6 */
1186189251Ssam
1187189251Ssam	return 0;
1188189251Ssam}
1189189251Ssam
1190189251Ssam
1191189251Ssamstatic int radius_client_init_acct(struct radius_client_data *radius)
1192189251Ssam{
1193189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1194189251Ssam	int ok = 0;
1195189251Ssam
1196189251Ssam	radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1197189251Ssam	if (radius->acct_serv_sock < 0)
1198189251Ssam		perror("socket[PF_INET,SOCK_DGRAM]");
1199209158Srpaulo	else {
1200209158Srpaulo		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
1201189251Ssam		ok++;
1202209158Srpaulo	}
1203189251Ssam
1204189251Ssam#ifdef CONFIG_IPV6
1205189251Ssam	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1206189251Ssam	if (radius->acct_serv_sock6 < 0)
1207189251Ssam		perror("socket[PF_INET6,SOCK_DGRAM]");
1208189251Ssam	else
1209189251Ssam		ok++;
1210189251Ssam#endif /* CONFIG_IPV6 */
1211189251Ssam
1212189251Ssam	if (ok == 0)
1213189251Ssam		return -1;
1214189251Ssam
1215189251Ssam	radius_change_server(radius, conf->acct_server, NULL,
1216189251Ssam			     radius->acct_serv_sock, radius->acct_serv_sock6,
1217189251Ssam			     0);
1218189251Ssam
1219189251Ssam	if (radius->acct_serv_sock >= 0 &&
1220189251Ssam	    eloop_register_read_sock(radius->acct_serv_sock,
1221189251Ssam				     radius_client_receive, radius,
1222189251Ssam				     (void *) RADIUS_ACCT)) {
1223189251Ssam		printf("Could not register read socket for accounting "
1224189251Ssam		       "server\n");
1225189251Ssam		return -1;
1226189251Ssam	}
1227189251Ssam
1228189251Ssam#ifdef CONFIG_IPV6
1229189251Ssam	if (radius->acct_serv_sock6 >= 0 &&
1230189251Ssam	    eloop_register_read_sock(radius->acct_serv_sock6,
1231189251Ssam				     radius_client_receive, radius,
1232189251Ssam				     (void *) RADIUS_ACCT)) {
1233189251Ssam		printf("Could not register read socket for accounting "
1234189251Ssam		       "server\n");
1235189251Ssam		return -1;
1236189251Ssam	}
1237189251Ssam#endif /* CONFIG_IPV6 */
1238189251Ssam
1239189251Ssam	return 0;
1240189251Ssam}
1241189251Ssam
1242189251Ssam
1243214734Srpaulo/**
1244214734Srpaulo * radius_client_init - Initialize RADIUS client
1245214734Srpaulo * @ctx: Callback context to be used in hostapd_logger() calls
1246214734Srpaulo * @conf: RADIUS client configuration (RADIUS servers)
1247214734Srpaulo * Returns: Pointer to private RADIUS client context or %NULL on failure
1248214734Srpaulo *
1249214734Srpaulo * The caller is responsible for keeping the configuration data available for
1250214734Srpaulo * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1251214734Srpaulo * called for the returned context pointer.
1252214734Srpaulo */
1253189251Ssamstruct radius_client_data *
1254189251Ssamradius_client_init(void *ctx, struct hostapd_radius_servers *conf)
1255189251Ssam{
1256189251Ssam	struct radius_client_data *radius;
1257189251Ssam
1258189251Ssam	radius = os_zalloc(sizeof(struct radius_client_data));
1259189251Ssam	if (radius == NULL)
1260189251Ssam		return NULL;
1261189251Ssam
1262189251Ssam	radius->ctx = ctx;
1263189251Ssam	radius->conf = conf;
1264189251Ssam	radius->auth_serv_sock = radius->acct_serv_sock =
1265189251Ssam		radius->auth_serv_sock6 = radius->acct_serv_sock6 =
1266189251Ssam		radius->auth_sock = radius->acct_sock = -1;
1267189251Ssam
1268189251Ssam	if (conf->auth_server && radius_client_init_auth(radius)) {
1269189251Ssam		radius_client_deinit(radius);
1270189251Ssam		return NULL;
1271189251Ssam	}
1272189251Ssam
1273189251Ssam	if (conf->acct_server && radius_client_init_acct(radius)) {
1274189251Ssam		radius_client_deinit(radius);
1275189251Ssam		return NULL;
1276189251Ssam	}
1277189251Ssam
1278189251Ssam	if (conf->retry_primary_interval)
1279189251Ssam		eloop_register_timeout(conf->retry_primary_interval, 0,
1280189251Ssam				       radius_retry_primary_timer, radius,
1281189251Ssam				       NULL);
1282189251Ssam
1283189251Ssam	return radius;
1284189251Ssam}
1285189251Ssam
1286189251Ssam
1287214734Srpaulo/**
1288214734Srpaulo * radius_client_deinit - Deinitialize RADIUS client
1289214734Srpaulo * @radius: RADIUS client context from radius_client_init()
1290214734Srpaulo */
1291189251Ssamvoid radius_client_deinit(struct radius_client_data *radius)
1292189251Ssam{
1293189251Ssam	if (!radius)
1294189251Ssam		return;
1295189251Ssam
1296189251Ssam	if (radius->auth_serv_sock >= 0)
1297189251Ssam		eloop_unregister_read_sock(radius->auth_serv_sock);
1298189251Ssam	if (radius->acct_serv_sock >= 0)
1299189251Ssam		eloop_unregister_read_sock(radius->acct_serv_sock);
1300209158Srpaulo#ifdef CONFIG_IPV6
1301209158Srpaulo	if (radius->auth_serv_sock6 >= 0)
1302209158Srpaulo		eloop_unregister_read_sock(radius->auth_serv_sock6);
1303209158Srpaulo	if (radius->acct_serv_sock6 >= 0)
1304209158Srpaulo		eloop_unregister_read_sock(radius->acct_serv_sock6);
1305209158Srpaulo#endif /* CONFIG_IPV6 */
1306189251Ssam
1307189251Ssam	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
1308189251Ssam
1309189251Ssam	radius_client_flush(radius, 0);
1310189251Ssam	os_free(radius->auth_handlers);
1311189251Ssam	os_free(radius->acct_handlers);
1312189251Ssam	os_free(radius);
1313189251Ssam}
1314189251Ssam
1315189251Ssam
1316214734Srpaulo/**
1317214734Srpaulo * radius_client_flush_auth - Flush pending RADIUS messages for an address
1318214734Srpaulo * @radius: RADIUS client context from radius_client_init()
1319214734Srpaulo * @addr: MAC address of the related device
1320214734Srpaulo *
1321214734Srpaulo * This function can be used to remove pending RADIUS authentication messages
1322214734Srpaulo * that are related to a specific device. The addr parameter is matched with
1323214734Srpaulo * the one used in radius_client_send() call that was used to transmit the
1324214734Srpaulo * authentication request.
1325214734Srpaulo */
1326214734Srpaulovoid radius_client_flush_auth(struct radius_client_data *radius,
1327214734Srpaulo			      const u8 *addr)
1328189251Ssam{
1329189251Ssam	struct radius_msg_list *entry, *prev, *tmp;
1330189251Ssam
1331189251Ssam	prev = NULL;
1332189251Ssam	entry = radius->msgs;
1333189251Ssam	while (entry) {
1334189251Ssam		if (entry->msg_type == RADIUS_AUTH &&
1335189251Ssam		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
1336189251Ssam			hostapd_logger(radius->ctx, addr,
1337189251Ssam				       HOSTAPD_MODULE_RADIUS,
1338189251Ssam				       HOSTAPD_LEVEL_DEBUG,
1339189251Ssam				       "Removing pending RADIUS authentication"
1340189251Ssam				       " message for removed client");
1341189251Ssam
1342189251Ssam			if (prev)
1343189251Ssam				prev->next = entry->next;
1344189251Ssam			else
1345189251Ssam				radius->msgs = entry->next;
1346189251Ssam
1347189251Ssam			tmp = entry;
1348189251Ssam			entry = entry->next;
1349189251Ssam			radius_client_msg_free(tmp);
1350189251Ssam			radius->num_msgs--;
1351189251Ssam			continue;
1352189251Ssam		}
1353189251Ssam
1354189251Ssam		prev = entry;
1355189251Ssam		entry = entry->next;
1356189251Ssam	}
1357189251Ssam}
1358189251Ssam
1359189251Ssam
1360189251Ssamstatic int radius_client_dump_auth_server(char *buf, size_t buflen,
1361189251Ssam					  struct hostapd_radius_server *serv,
1362189251Ssam					  struct radius_client_data *cli)
1363189251Ssam{
1364189251Ssam	int pending = 0;
1365189251Ssam	struct radius_msg_list *msg;
1366189251Ssam	char abuf[50];
1367189251Ssam
1368189251Ssam	if (cli) {
1369189251Ssam		for (msg = cli->msgs; msg; msg = msg->next) {
1370189251Ssam			if (msg->msg_type == RADIUS_AUTH)
1371189251Ssam				pending++;
1372189251Ssam		}
1373189251Ssam	}
1374189251Ssam
1375189251Ssam	return os_snprintf(buf, buflen,
1376189251Ssam			   "radiusAuthServerIndex=%d\n"
1377189251Ssam			   "radiusAuthServerAddress=%s\n"
1378189251Ssam			   "radiusAuthClientServerPortNumber=%d\n"
1379189251Ssam			   "radiusAuthClientRoundTripTime=%d\n"
1380189251Ssam			   "radiusAuthClientAccessRequests=%u\n"
1381189251Ssam			   "radiusAuthClientAccessRetransmissions=%u\n"
1382189251Ssam			   "radiusAuthClientAccessAccepts=%u\n"
1383189251Ssam			   "radiusAuthClientAccessRejects=%u\n"
1384189251Ssam			   "radiusAuthClientAccessChallenges=%u\n"
1385189251Ssam			   "radiusAuthClientMalformedAccessResponses=%u\n"
1386189251Ssam			   "radiusAuthClientBadAuthenticators=%u\n"
1387189251Ssam			   "radiusAuthClientPendingRequests=%u\n"
1388189251Ssam			   "radiusAuthClientTimeouts=%u\n"
1389189251Ssam			   "radiusAuthClientUnknownTypes=%u\n"
1390189251Ssam			   "radiusAuthClientPacketsDropped=%u\n",
1391189251Ssam			   serv->index,
1392189251Ssam			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1393189251Ssam			   serv->port,
1394189251Ssam			   serv->round_trip_time,
1395189251Ssam			   serv->requests,
1396189251Ssam			   serv->retransmissions,
1397189251Ssam			   serv->access_accepts,
1398189251Ssam			   serv->access_rejects,
1399189251Ssam			   serv->access_challenges,
1400189251Ssam			   serv->malformed_responses,
1401189251Ssam			   serv->bad_authenticators,
1402189251Ssam			   pending,
1403189251Ssam			   serv->timeouts,
1404189251Ssam			   serv->unknown_types,
1405189251Ssam			   serv->packets_dropped);
1406189251Ssam}
1407189251Ssam
1408189251Ssam
1409189251Ssamstatic int radius_client_dump_acct_server(char *buf, size_t buflen,
1410189251Ssam					  struct hostapd_radius_server *serv,
1411189251Ssam					  struct radius_client_data *cli)
1412189251Ssam{
1413189251Ssam	int pending = 0;
1414189251Ssam	struct radius_msg_list *msg;
1415189251Ssam	char abuf[50];
1416189251Ssam
1417189251Ssam	if (cli) {
1418189251Ssam		for (msg = cli->msgs; msg; msg = msg->next) {
1419189251Ssam			if (msg->msg_type == RADIUS_ACCT ||
1420189251Ssam			    msg->msg_type == RADIUS_ACCT_INTERIM)
1421189251Ssam				pending++;
1422189251Ssam		}
1423189251Ssam	}
1424189251Ssam
1425189251Ssam	return os_snprintf(buf, buflen,
1426189251Ssam			   "radiusAccServerIndex=%d\n"
1427189251Ssam			   "radiusAccServerAddress=%s\n"
1428189251Ssam			   "radiusAccClientServerPortNumber=%d\n"
1429189251Ssam			   "radiusAccClientRoundTripTime=%d\n"
1430189251Ssam			   "radiusAccClientRequests=%u\n"
1431189251Ssam			   "radiusAccClientRetransmissions=%u\n"
1432189251Ssam			   "radiusAccClientResponses=%u\n"
1433189251Ssam			   "radiusAccClientMalformedResponses=%u\n"
1434189251Ssam			   "radiusAccClientBadAuthenticators=%u\n"
1435189251Ssam			   "radiusAccClientPendingRequests=%u\n"
1436189251Ssam			   "radiusAccClientTimeouts=%u\n"
1437189251Ssam			   "radiusAccClientUnknownTypes=%u\n"
1438189251Ssam			   "radiusAccClientPacketsDropped=%u\n",
1439189251Ssam			   serv->index,
1440189251Ssam			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1441189251Ssam			   serv->port,
1442189251Ssam			   serv->round_trip_time,
1443189251Ssam			   serv->requests,
1444189251Ssam			   serv->retransmissions,
1445189251Ssam			   serv->responses,
1446189251Ssam			   serv->malformed_responses,
1447189251Ssam			   serv->bad_authenticators,
1448189251Ssam			   pending,
1449189251Ssam			   serv->timeouts,
1450189251Ssam			   serv->unknown_types,
1451189251Ssam			   serv->packets_dropped);
1452189251Ssam}
1453189251Ssam
1454189251Ssam
1455214734Srpaulo/**
1456214734Srpaulo * radius_client_get_mib - Get RADIUS client MIB information
1457214734Srpaulo * @radius: RADIUS client context from radius_client_init()
1458214734Srpaulo * @buf: Buffer for returning MIB data in text format
1459214734Srpaulo * @buflen: Maximum buf length in octets
1460214734Srpaulo * Returns: Number of octets written into the buffer
1461214734Srpaulo */
1462189251Ssamint radius_client_get_mib(struct radius_client_data *radius, char *buf,
1463189251Ssam			  size_t buflen)
1464189251Ssam{
1465189251Ssam	struct hostapd_radius_servers *conf = radius->conf;
1466189251Ssam	int i;
1467189251Ssam	struct hostapd_radius_server *serv;
1468189251Ssam	int count = 0;
1469189251Ssam
1470189251Ssam	if (conf->auth_servers) {
1471189251Ssam		for (i = 0; i < conf->num_auth_servers; i++) {
1472189251Ssam			serv = &conf->auth_servers[i];
1473189251Ssam			count += radius_client_dump_auth_server(
1474189251Ssam				buf + count, buflen - count, serv,
1475189251Ssam				serv == conf->auth_server ?
1476189251Ssam				radius : NULL);
1477189251Ssam		}
1478189251Ssam	}
1479189251Ssam
1480189251Ssam	if (conf->acct_servers) {
1481189251Ssam		for (i = 0; i < conf->num_acct_servers; i++) {
1482189251Ssam			serv = &conf->acct_servers[i];
1483189251Ssam			count += radius_client_dump_acct_server(
1484189251Ssam				buf + count, buflen - count, serv,
1485189251Ssam				serv == conf->acct_server ?
1486189251Ssam				radius : NULL);
1487189251Ssam		}
1488189251Ssam	}
1489189251Ssam
1490189251Ssam	return count;
1491189251Ssam}
1492