radius_client.c revision 61d9df3e62aaa0e87ad05452fcb95142159a17b6
1c3aae25116e66c177579b0b79182b09340b19753Chris Lattner/*
2ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman * RADIUS client
36fbcc26f1460eaee4e0eb8b426fc1ff0c7af11beJohn Criswell * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
46fbcc26f1460eaee4e0eb8b426fc1ff0c7af11beJohn Criswell *
57ed47a13356daed2a34cd2209a31f92552e3bdd8Chris Lattner * This software may be distributed under the terms of the BSD license.
67ed47a13356daed2a34cd2209a31f92552e3bdd8Chris Lattner * See README for more details.
7ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman */
86fbcc26f1460eaee4e0eb8b426fc1ff0c7af11beJohn Criswell
9ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman#include "includes.h"
10c3aae25116e66c177579b0b79182b09340b19753Chris Lattner
11c3aae25116e66c177579b0b79182b09340b19753Chris Lattner#include "common.h"
12ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman#include "radius.h"
13cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner#include "radius_client.h"
14cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner#include "eloop.h"
15cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner
16cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner/* Defaults for RADIUS retransmit values (exponential backoff) */
17cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner
18fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman/**
19583bd47f777fe3eb8305872fa0eadab31e833dffJim Laskey * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
204b84086e89d86fb16f562166d9fea8df37db6be7Dan Gohman */
21583bd47f777fe3eb8305872fa0eadab31e833dffJim Laskey#define RADIUS_CLIENT_FIRST_WAIT 3
22b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner
23acaf09dbe4a6781163857db1321bbd5795e7d410Dan Gohman/**
24109654fae9c5b8b96bd3a829824cdbceb27ced06Chris Lattner * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
255892d47a625638a90afeb31dd4f6f80a2f9bacdeChris Lattner */
26322812e603705e1c2037313633e72f689524b163Evan Cheng#define RADIUS_CLIENT_MAX_WAIT 120
27eb19e40efbd3cae80c908a30cdf4d33450733c45Chris Lattner
28d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke/**
29d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
30fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman *
31fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * Maximum number of retransmit attempts before the entry is removed from
32fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * retransmit list.
33fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman */
34fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman#define RADIUS_CLIENT_MAX_RETRIES 10
35fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
36fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman/**
37fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
38fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman *
39fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * Maximum number of entries in retransmit list (oldest entries will be
40fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * removed, if this limit is exceeded).
41fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman */
42fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman#define RADIUS_CLIENT_MAX_ENTRIES 30
43fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
44fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman/**
45fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
46fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman *
47fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * The number of failed retry attempts after which the RADIUS server will be
48fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * changed (if one of more backup servers are configured).
49fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman */
50fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman#define RADIUS_CLIENT_NUM_FAILOVER 4
51fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
52fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
53fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman/**
54fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * struct radius_rx_handler - RADIUS client RX handler
55fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman *
56fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * This data structure is used internally inside the RADIUS client module to
57fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * store registered RX handlers. These handlers are registered by calls to
58fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * radius_client_register() and unregistered when the RADIUS client is
59fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman * deinitialized with a call to radius_client_deinit().
60fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman */
61fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohmanstruct radius_rx_handler {
62c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	/**
63c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 * handler - Received RADIUS message handler
64c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 */
65c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	RadiusRxResult (*handler)(struct radius_msg *msg,
66c3aae25116e66c177579b0b79182b09340b19753Chris Lattner				  struct radius_msg *req,
67c3aae25116e66c177579b0b79182b09340b19753Chris Lattner				  const u8 *shared_secret,
68c3aae25116e66c177579b0b79182b09340b19753Chris Lattner				  size_t shared_secret_len,
69c3aae25116e66c177579b0b79182b09340b19753Chris Lattner				  void *data);
70c3aae25116e66c177579b0b79182b09340b19753Chris Lattner
71c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	/**
72c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 * data - Context data for the handler
73cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	 */
74cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	void *data;
75063287a76b5d1486f498fcf674a26d1155471a3fChris Lattner};
76c3aae25116e66c177579b0b79182b09340b19753Chris Lattner
77ead0d88ad7659dabd66cc3149af97d98256fca84Chris Lattner
7844c3b9fdd416c79f4b67cde1aecfced5921efd81Jim Laskey/**
79cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner * struct radius_msg_list - RADIUS client message retransmit list
80213a16c637926bfc38ba373d3aba6778e181e3ecChris Lattner *
81475871a144eb604ddaf37503397ba0941442e5fbDan Gohman * This data structure is used internally inside the RADIUS client module to
82cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner * store pending RADIUS requests that may still need to be retransmitted.
83213a16c637926bfc38ba373d3aba6778e181e3ecChris Lattner */
84fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohmanstruct radius_msg_list {
85fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	/**
86fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	 * addr - STA/client address
87fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	 *
88fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	 * This is used to find RADIUS messages for the same STA.
89fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	 */
90691ef2ba066dda14ae4ac0ad645054fbc967785aAndrew Lenharth	u8 addr[ETH_ALEN];
91213a16c637926bfc38ba373d3aba6778e181e3ecChris Lattner
92213a16c637926bfc38ba373d3aba6778e181e3ecChris Lattner	/**
93583bd47f777fe3eb8305872fa0eadab31e833dffJim Laskey	 * msg - RADIUS message
94213a16c637926bfc38ba373d3aba6778e181e3ecChris Lattner	 */
95e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	struct radius_msg *msg;
96e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman
97e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	/**
98e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	 * msg_type - Message type
99d038e04188047eca4749d025ef1f05f7ae660bcaDuncan Sands	 */
100d038e04188047eca4749d025ef1f05f7ae660bcaDuncan Sands	RadiusType msg_type;
101d038e04188047eca4749d025ef1f05f7ae660bcaDuncan Sands
102cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	/**
103ead0d88ad7659dabd66cc3149af97d98256fca84Chris Lattner	 * first_try - Time of the first transmission attempt
1040e5f1306b059b62d7725f324e087efbc8e7a782dDan Gohman	 */
105fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	os_time_t first_try;
106fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
107c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	/**
108c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 * next_try - Time for the next transmission attempt
109cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	 */
110cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	os_time_t next_try;
111c3aae25116e66c177579b0b79182b09340b19753Chris Lattner
112063287a76b5d1486f498fcf674a26d1155471a3fChris Lattner	/**
113063287a76b5d1486f498fcf674a26d1155471a3fChris Lattner	 * attempts - Number of transmission attempts
114ead0d88ad7659dabd66cc3149af97d98256fca84Chris Lattner	 */
11544c3b9fdd416c79f4b67cde1aecfced5921efd81Jim Laskey	int attempts;
116cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner
117ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	/**
1181080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner	 * next_wait - Next retransmission wait time in seconds
119462dc7f4960e5074ddf4769ec8b2ef1ba7a4d2c8Dan Gohman	 */
120462dc7f4960e5074ddf4769ec8b2ef1ba7a4d2c8Dan Gohman	int next_wait;
121ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey
122ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	/**
123ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 * last_attempt - Time of the last transmission attempt
124ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 */
1251080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner	struct os_time last_attempt;
126ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey
127ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	/**
128ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 * shared_secret - Shared secret with the target RADIUS server
129ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 */
130ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	const u8 *shared_secret;
131ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey
132ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	/**
133ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 * shared_secret_len - shared_secret length in octets
134ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 */
135ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	size_t shared_secret_len;
136ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey
137ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	/* TODO: server config with failover to backup server(s) */
138ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey
139ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	/**
140ec20402c90b605afeedbcf0e3aabe6f8054f23ddJim Laskey	 * next - Next message in the list
1411080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner	 */
142fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman	struct radius_msg_list *next;
143b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner};
144b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner
145fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
146b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner/**
147b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner * struct radius_client_data - Internal RADIUS client data
148fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman *
1490e5f1306b059b62d7725f324e087efbc8e7a782dDan Gohman * This data structure is used internally inside the RADIUS client module.
1500e5f1306b059b62d7725f324e087efbc8e7a782dDan Gohman * External users allocate this by calling radius_client_init() and free it by
151b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner * calling radius_client_deinit(). The pointer to this opaque data is used in
152c3aae25116e66c177579b0b79182b09340b19753Chris Lattner * calls to other functions as an identifier for the RADIUS client instance.
153cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner */
154475871a144eb604ddaf37503397ba0941442e5fbDan Gohmanstruct radius_client_data {
155cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	/**
156c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 * ctx - Context pointer for hostapd_logger() callbacks
157c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 */
158475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	void *ctx;
159cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner
160c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	/**
161cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	 * conf - RADIUS client configuration (list of RADIUS servers to use)
162475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	 */
163acaf09dbe4a6781163857db1321bbd5795e7d410Dan Gohman	struct hostapd_radius_servers *conf;
164acaf09dbe4a6781163857db1321bbd5795e7d410Dan Gohman
165acaf09dbe4a6781163857db1321bbd5795e7d410Dan Gohman	/**
166acaf09dbe4a6781163857db1321bbd5795e7d410Dan Gohman	 * auth_serv_sock - IPv4 socket for RADIUS authentication messages
167cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	 */
1681d4d41411190dd9e62764e56713753d4155764ddNate Begeman	int auth_serv_sock;
1691d4d41411190dd9e62764e56713753d4155764ddNate Begeman
1701d4d41411190dd9e62764e56713753d4155764ddNate Begeman	/**
1711d4d41411190dd9e62764e56713753d4155764ddNate Begeman	 * acct_serv_sock - IPv4 socket for RADIUS accounting messages
172c7c3f110eda0ff8040e4bd99e38d3112b910810fJim Laskey	 */
1731d4d41411190dd9e62764e56713753d4155764ddNate Begeman	int acct_serv_sock;
17401d029b82cb08367d81aa10cdc94d05360466649Chris Lattner
17501d029b82cb08367d81aa10cdc94d05360466649Chris Lattner	/**
17601d029b82cb08367d81aa10cdc94d05360466649Chris Lattner	 * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
17701d029b82cb08367d81aa10cdc94d05360466649Chris Lattner	 */
17801d029b82cb08367d81aa10cdc94d05360466649Chris Lattner	int auth_serv_sock6;
17901d029b82cb08367d81aa10cdc94d05360466649Chris Lattner
18001d029b82cb08367d81aa10cdc94d05360466649Chris Lattner	/**
181c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
182c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 */
183c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	int acct_serv_sock6;
184cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner
185c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	/**
186c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	 * auth_sock - Currently used socket for RADIUS authentication server
187063287a76b5d1486f498fcf674a26d1155471a3fChris Lattner	 */
188c3aae25116e66c177579b0b79182b09340b19753Chris Lattner	int auth_sock;
189d1fc96499b7619356c7542200d32da898b79f7c1Chris Lattner
190190a418bf6b49a4ef1c1980229a2f0d516e8a2cdChris Lattner	/**
191190a418bf6b49a4ef1c1980229a2f0d516e8a2cdChris Lattner	 * acct_sock - Currently used socket for RADIUS accounting server
192130a6471b90f66e99b1f9f42877fdf611c330ac6Evan Cheng	 */
193130a6471b90f66e99b1f9f42877fdf611c330ac6Evan Cheng	int acct_sock;
194130a6471b90f66e99b1f9f42877fdf611c330ac6Evan Cheng
195130a6471b90f66e99b1f9f42877fdf611c330ac6Evan Cheng	/**
196130a6471b90f66e99b1f9f42877fdf611c330ac6Evan Cheng	 * auth_handlers - Authentication message handlers
19770046e920fa37989a041af663ada2b2b646e258fChris Lattner	 */
19870046e920fa37989a041af663ada2b2b646e258fChris Lattner	struct radius_rx_handler *auth_handlers;
19983ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands
20083ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	/**
20183ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	 * num_auth_handlers - Number of handlers in auth_handlers
20283ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	 */
20370046e920fa37989a041af663ada2b2b646e258fChris Lattner	size_t num_auth_handlers;
20470046e920fa37989a041af663ada2b2b646e258fChris Lattner
20583ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	/**
20670046e920fa37989a041af663ada2b2b646e258fChris Lattner	 * acct_handlers - Accounting message handlers
20770046e920fa37989a041af663ada2b2b646e258fChris Lattner	 */
20883ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	struct radius_rx_handler *acct_handlers;
20970046e920fa37989a041af663ada2b2b646e258fChris Lattner
21070046e920fa37989a041af663ada2b2b646e258fChris Lattner	/**
21183ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	 * num_acct_handlers - Number of handlers in acct_handlers
21270046e920fa37989a041af663ada2b2b646e258fChris Lattner	 */
21370046e920fa37989a041af663ada2b2b646e258fChris Lattner	size_t num_acct_handlers;
214f877b735ad4987f26cafcbaf22aa4c2199458b5dDan Gohman
21513d57320bd212483463d4f8992d5787b29eda5dfBill Wendling	/**
21670046e920fa37989a041af663ada2b2b646e258fChris Lattner	 * msgs - Pending outgoing RADIUS messages
21770046e920fa37989a041af663ada2b2b646e258fChris Lattner	 */
21870046e920fa37989a041af663ada2b2b646e258fChris Lattner	struct radius_msg_list *msgs;
2191b1a49714ef26225a42199cf2930529f31868322Chris Lattner
22070046e920fa37989a041af663ada2b2b646e258fChris Lattner	/**
22170046e920fa37989a041af663ada2b2b646e258fChris Lattner	 * num_msgs - Number of pending messages in the msgs list
222475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	 */
223475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	size_t num_msgs;
224475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
225475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	/**
226cbea3045ce0bdd061c494a831d0ce2d5834211ccChris Lattner	 * next_radius_identifier - Next RADIUS message identifier to use
227cbea3045ce0bdd061c494a831d0ce2d5834211ccChris Lattner	 */
228475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	u8 next_radius_identifier;
2296394b099e836f56a937cdcc7332c9487b504ca68Dan Gohman};
2306394b099e836f56a937cdcc7332c9487b504ca68Dan Gohman
231475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
232475871a144eb604ddaf37503397ba0941442e5fbDan Gohmanstatic int
233475871a144eb604ddaf37503397ba0941442e5fbDan Gohmanradius_change_server(struct radius_client_data *radius,
234c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner		     struct hostapd_radius_server *nserv,
235c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner		     struct hostapd_radius_server *oserv,
236475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		     int sock, int sock6, int auth);
237f04afdbb48568ef09f11fd10ac03426101f2dbf8Dale Johannesenstatic int radius_client_init_acct(struct radius_client_data *radius);
238f04afdbb48568ef09f11fd10ac03426101f2dbf8Dale Johannesenstatic int radius_client_init_auth(struct radius_client_data *radius);
239475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
240cbea3045ce0bdd061c494a831d0ce2d5834211ccChris Lattner
241475871a144eb604ddaf37503397ba0941442e5fbDan Gohmanstatic void radius_client_msg_free(struct radius_msg_list *req)
242cbea3045ce0bdd061c494a831d0ce2d5834211ccChris Lattner{
243cbea3045ce0bdd061c494a831d0ce2d5834211ccChris Lattner	radius_msg_free(req->msg);
244cbea3045ce0bdd061c494a831d0ce2d5834211ccChris Lattner	os_free(req);
245475871a144eb604ddaf37503397ba0941442e5fbDan Gohman}
246475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
247c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner
248c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner/**
249475871a144eb604ddaf37503397ba0941442e5fbDan Gohman * radius_client_register - Register a RADIUS client RX handler
250475871a144eb604ddaf37503397ba0941442e5fbDan Gohman * @radius: RADIUS client context from radius_client_init()
251c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
252c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner * @handler: Handler for received RADIUS messages
253475871a144eb604ddaf37503397ba0941442e5fbDan Gohman * @data: Context pointer for handler callbacks
254c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner * Returns: 0 on success, -1 on failure
255475871a144eb604ddaf37503397ba0941442e5fbDan Gohman *
256c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner * This function is used to register a handler for processing received RADIUS
257c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner * authentication and accounting messages. The handler() callback function will
258c9f8f416800784ca6453222b307bc44ad24739b0Chris Lattner * be called whenever a RADIUS message is received from the active server.
259475871a144eb604ddaf37503397ba0941442e5fbDan Gohman *
260d6594ae54cfde4db4d30272192645c0a45fb9902Evan Cheng * There can be multiple registered RADIUS message handlers. The handlers will
261475871a144eb604ddaf37503397ba0941442e5fbDan Gohman * be called in order until one of them indicates that it has processed or
26283ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands * queued the message.
263d6594ae54cfde4db4d30272192645c0a45fb9902Evan Cheng */
264d6594ae54cfde4db4d30272192645c0a45fb9902Evan Chengint radius_client_register(struct radius_client_data *radius,
265d6594ae54cfde4db4d30272192645c0a45fb9902Evan Cheng			   RadiusType msg_type,
266475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			   RadiusRxResult (*handler)(struct radius_msg *msg,
267475871a144eb604ddaf37503397ba0941442e5fbDan Gohman						     struct radius_msg *req,
268475871a144eb604ddaf37503397ba0941442e5fbDan Gohman						     const u8 *shared_secret,
269475871a144eb604ddaf37503397ba0941442e5fbDan Gohman						     size_t shared_secret_len,
270475871a144eb604ddaf37503397ba0941442e5fbDan Gohman						     void *data),
271475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			   void *data)
272475871a144eb604ddaf37503397ba0941442e5fbDan Gohman{
2737f460203b0c5350e9b2c592f438e40f7a7de6e45Dan Gohman	struct radius_rx_handler **handlers, *newh;
274475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	size_t *num;
275c3aae25116e66c177579b0b79182b09340b19753Chris Lattner
276475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (msg_type == RADIUS_ACCT) {
277d5d0f9bd20d9df07d6b4d41b7e8ed6d33b6a649dChris Lattner		handlers = &radius->acct_handlers;
278d5d0f9bd20d9df07d6b4d41b7e8ed6d33b6a649dChris Lattner		num = &radius->num_acct_handlers;
279cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	} else {
280cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner		handlers = &radius->auth_handlers;
281e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner		num = &radius->num_auth_handlers;
282e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner	}
283e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner
284475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	newh = os_realloc_array(*handlers, *num + 1,
285475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				sizeof(struct radius_rx_handler));
28683ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	if (newh == NULL)
287475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		return -1;
2882fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner
289e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner	newh[*num].handler = handler;
29066a48bbc3565b40ea0e6f2d58cf5e3a8e64802efEvan Cheng	newh[*num].data = data;
291475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	(*num)++;
292475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	*handlers = newh;
293475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
29483ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	return 0;
295475871a144eb604ddaf37503397ba0941442e5fbDan Gohman}
2962fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner
29766a48bbc3565b40ea0e6f2d58cf5e3a8e64802efEvan Cheng
298e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattnerstatic void radius_client_handle_send_error(struct radius_client_data *radius,
299475871a144eb604ddaf37503397ba0941442e5fbDan Gohman					    int s, RadiusType msg_type)
30083ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands{
301475871a144eb604ddaf37503397ba0941442e5fbDan Gohman#ifndef CONFIG_NATIVE_WINDOWS
3022fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner	int _errno = errno;
30318c2f13e0f9d0e5d6227cf6d1881e9ee3d1b6109Chris Lattner	perror("send[RADIUS]");
304e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
305e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner	    _errno == EBADF) {
306e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
307e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner			       HOSTAPD_LEVEL_INFO,
308475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			       "Send failed - maybe interface status changed -"
309475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			       " try to connect again");
31083ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		eloop_unregister_read_sock(s);
311475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		close(s);
3122fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner		if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
313e3f1026683c38f6605ccaf698b7082f1b0a0f8c8Chris Lattner			radius_client_init_acct(radius);
31418c2f13e0f9d0e5d6227cf6d1881e9ee3d1b6109Chris Lattner		else
315475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			radius_client_init_auth(radius);
316cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	}
3171ccae666f596d5aeca5c9942995763600b622062Chris Lattner#endif /* CONFIG_NATIVE_WINDOWS */
3181ccae666f596d5aeca5c9942995763600b622062Chris Lattner}
319475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
3206a5b6d7633c96c72ca7d5f8ba0c855e4690ada04Chris Lattner
3216a5b6d7633c96c72ca7d5f8ba0c855e4690ada04Chris Lattnerstatic int radius_client_retransmit(struct radius_client_data *radius,
3226a5b6d7633c96c72ca7d5f8ba0c855e4690ada04Chris Lattner				    struct radius_msg_list *entry,
323475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				    os_time_t now)
32483ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands{
325475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	struct hostapd_radius_servers *conf = radius->conf;
3262fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner	int s;
3276a5b6d7633c96c72ca7d5f8ba0c855e4690ada04Chris Lattner	struct wpabuf *buf;
3281ccae666f596d5aeca5c9942995763600b622062Chris Lattner
3290f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling	if (entry->msg_type == RADIUS_ACCT ||
3300f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling	    entry->msg_type == RADIUS_ACCT_INTERIM) {
331475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		s = radius->acct_sock;
332475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		if (entry->attempts == 0)
3330f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling			conf->acct_server->requests++;
334475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		else {
3350f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling			conf->acct_server->timeouts++;
3360f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling			conf->acct_server->retransmissions++;
3370f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling		}
3380f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling	} else {
3390f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling		s = radius->auth_sock;
34034cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng		if (entry->attempts == 0)
3410f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling			conf->auth_server->requests++;
3420f8d9c04d9feef86cee35cf5fecfb348a6b3de50Bill Wendling		else {
343c3aae25116e66c177579b0b79182b09340b19753Chris Lattner			conf->auth_server->timeouts++;
344cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner			conf->auth_server->retransmissions++;
345475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		}
346475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
347475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
348475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	/* retransmit; remove entry if too many attempts */
349475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->attempts++;
350475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
351475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
352475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       radius_msg_get_hdr(entry->msg)->identifier);
353475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
354475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	os_get_time(&entry->last_attempt);
355475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	buf = radius_msg_get_buf(entry->msg);
356475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
357475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		radius_client_handle_send_error(radius, s, entry->msg_type);
3586d9cdd56173fb915a9e3a8f0f6b5a8ed9bed1098Dan Gohman
359475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->next_try = now + entry->next_wait;
360475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->next_wait *= 2;
361475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
362475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
363475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
364475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		printf("Removing un-ACKed RADIUS message due to too many "
365475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       "failed retransmit attempts\n");
366475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		return 1;
367475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
368475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
369475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	return 0;
370475871a144eb604ddaf37503397ba0941442e5fbDan Gohman}
371475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
372475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
373475871a144eb604ddaf37503397ba0941442e5fbDan Gohmanstatic void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
374475871a144eb604ddaf37503397ba0941442e5fbDan Gohman{
375475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	struct radius_client_data *radius = eloop_ctx;
376475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	struct hostapd_radius_servers *conf = radius->conf;
377475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	struct os_time now;
378707e0184233f27e0e9f9aee0309f2daab8cfe7f8Dan Gohman	os_time_t first;
3791f13c686df75ddbbe15b208606ece4846d7479a8Dan Gohman	struct radius_msg_list *entry, *prev, *tmp;
3801f13c686df75ddbbe15b208606ece4846d7479a8Dan Gohman	int auth_failover = 0, acct_failover = 0;
3815c0d6ed325417baa5d119af9c2b6790231d8565fRafael Espindola	char abuf[50];
382475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
383475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry = radius->msgs;
3841f13c686df75ddbbe15b208606ece4846d7479a8Dan Gohman	if (!entry)
3851f13c686df75ddbbe15b208606ece4846d7479a8Dan Gohman		return;
3865c0d6ed325417baa5d119af9c2b6790231d8565fRafael Espindola
387475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	os_get_time(&now);
388475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	first = 0;
3891f13c686df75ddbbe15b208606ece4846d7479a8Dan Gohman
3905c0d6ed325417baa5d119af9c2b6790231d8565fRafael Espindola	prev = NULL;
3917cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner	while (entry) {
392475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		if (now.sec >= entry->next_try &&
3937cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner		    radius_client_retransmit(radius, entry, now.sec)) {
394475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			if (prev)
3957cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner				prev->next = entry->next;
3967cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner			else
397b43e9c196542acc80c9e4643809661065710848fNate Begeman				radius->msgs = entry->next;
398b43e9c196542acc80c9e4643809661065710848fNate Begeman
399b43e9c196542acc80c9e4643809661065710848fNate Begeman			tmp = entry;
400475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			entry = entry->next;
401b43e9c196542acc80c9e4643809661065710848fNate Begeman			radius_client_msg_free(tmp);
402475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			radius->num_msgs--;
403b43e9c196542acc80c9e4643809661065710848fNate Begeman			continue;
404b43e9c196542acc80c9e4643809661065710848fNate Begeman		}
4057cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner
4069373a81e53ce5f9f2c06c4209b8b886605aece08Nate Begeman		if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
4079373a81e53ce5f9f2c06c4209b8b886605aece08Nate Begeman			if (entry->msg_type == RADIUS_ACCT ||
408475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			    entry->msg_type == RADIUS_ACCT_INTERIM)
4099373a81e53ce5f9f2c06c4209b8b886605aece08Nate Begeman				acct_failover++;
410475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			else
411475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				auth_failover++;
4122fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner		}
4132fa6d3b1fcadbde90eaee0e8e89aebd81630b662Chris Lattner
4149373a81e53ce5f9f2c06c4209b8b886605aece08Nate Begeman		if (first == 0 || entry->next_try < first)
4157cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner			first = entry->next_try;
416acc398c195a697795bff3245943d104eb19192b9Nate Begeman
417acc398c195a697795bff3245943d104eb19192b9Nate Begeman		prev = entry;
418475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		entry = entry->next;
419475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
4207cbd525ba85ebe440d15fa359ec940e404d14906Nate Begeman
421ab0b949e0e9de452f3b052b11634ab761e008b23Andrew Lenharth	if (radius->msgs) {
42228873106309db515d58889a4c4fa3e0a92d1b60eMon P Wang		if (first < now.sec)
423475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			first = now.sec;
424475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		eloop_register_timeout(first - now.sec, 0,
42528873106309db515d58889a4c4fa3e0a92d1b60eMon P Wang				       radius_client_timer, radius, NULL);
426ab0b949e0e9de452f3b052b11634ab761e008b23Andrew Lenharth		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
427ab0b949e0e9de452f3b052b11634ab761e008b23Andrew Lenharth			       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
42828873106309db515d58889a4c4fa3e0a92d1b60eMon P Wang			       "retransmit in %ld seconds",
429475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			       (long int) (first - now.sec));
430475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
43128873106309db515d58889a4c4fa3e0a92d1b60eMon P Wang
432ab0b949e0e9de452f3b052b11634ab761e008b23Andrew Lenharth	if (auth_failover && conf->num_auth_servers > 1) {
4334bdcb61af33399d4e01fdf3c47ca1f1f5356e370Duncan Sands		struct hostapd_radius_server *next, *old;
4344bdcb61af33399d4e01fdf3c47ca1f1f5356e370Duncan Sands		old = conf->auth_server;
435475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
4364bdcb61af33399d4e01fdf3c47ca1f1f5356e370Duncan Sands			       HOSTAPD_LEVEL_NOTICE,
4374bdcb61af33399d4e01fdf3c47ca1f1f5356e370Duncan Sands			       "No response from Authentication server "
438f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands			       "%s:%d - failover",
439f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
4404bdcb61af33399d4e01fdf3c47ca1f1f5356e370Duncan Sands			       old->port);
441475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
442f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands		for (entry = radius->msgs; entry; entry = entry->next) {
443f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands			if (entry->msg_type == RADIUS_AUTH)
444f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands				old->timeouts++;
445f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands		}
446f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands
447f9516208e57364ab1e7d8748af1f59a2ea5fb572Duncan Sands		next = old + 1;
448c3aae25116e66c177579b0b79182b09340b19753Chris Lattner		if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
449c3aae25116e66c177579b0b79182b09340b19753Chris Lattner			next = conf->auth_servers;
450cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner		conf->auth_server = next;
451475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		radius_change_server(radius, next, old,
45295c218a83ecf77590b9dc40c636720772d2b5cd7Christopher Lamb				     radius->auth_serv_sock,
45395c218a83ecf77590b9dc40c636720772d2b5cd7Christopher Lamb				     radius->auth_serv_sock6, 1);
454475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
455475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
45683ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	if (acct_failover && conf->num_acct_servers > 1) {
45795c218a83ecf77590b9dc40c636720772d2b5cd7Christopher Lamb		struct hostapd_radius_server *next, *old;
458475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		old = conf->acct_server;
459475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
460475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			       HOSTAPD_LEVEL_NOTICE,
461475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			       "No response from Accounting server "
462475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			       "%s:%d - failover",
46383ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
464e10efce22502d1a1855d25baf1458660f4ba6f33Duncan Sands			       old->port);
4652d86ea21dd76647cb054fd5d27df9e49efc672b6Andrew Lenharth
466ad071e1cd1a4b880019f1b2e827ee81867815f82Evan Cheng		for (entry = radius->msgs; entry; entry = entry->next) {
467ad071e1cd1a4b880019f1b2e827ee81867815f82Evan Cheng			if (entry->msg_type == RADIUS_ACCT ||
468475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			    entry->msg_type == RADIUS_ACCT_INTERIM)
46995c218a83ecf77590b9dc40c636720772d2b5cd7Christopher Lamb				old->timeouts++;
47095c218a83ecf77590b9dc40c636720772d2b5cd7Christopher Lamb		}
471475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
47283ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		next = old + 1;
47395c218a83ecf77590b9dc40c636720772d2b5cd7Christopher Lamb		if (next > &conf->acct_servers[conf->num_acct_servers - 1])
474475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			next = conf->acct_servers;
475475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		conf->acct_server = next;
476ad071e1cd1a4b880019f1b2e827ee81867815f82Evan Cheng		radius_change_server(radius, next, old,
47769de1932b350d7cdfc0ed1f4198d6f78c7822a02Dan Gohman				     radius->acct_serv_sock,
478475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				     radius->acct_serv_sock6, 0);
47969de1932b350d7cdfc0ed1f4198d6f78c7822a02Dan Gohman	}
48069de1932b350d7cdfc0ed1f4198d6f78c7822a02Dan Gohman}
48169de1932b350d7cdfc0ed1f4198d6f78c7822a02Dan Gohman
482475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
483cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattnerstatic void radius_client_update_timeout(struct radius_client_data *radius)
484b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner{
485b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner	struct os_time now;
486b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner	os_time_t first;
487b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner	struct radius_msg_list *entry;
488b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner
489b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner	eloop_cancel_timeout(radius_client_timer, radius, NULL);
490475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
491475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (radius->msgs == NULL) {
492475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		return;
493475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
494475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
495475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	first = 0;
496475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	for (entry = radius->msgs; entry; entry = entry->next) {
497475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		if (first == 0 || entry->next_try < first)
498475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			first = entry->next_try;
499475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
5001b95095857b78e12138c22e76c7936611c51355bChris Lattner
5011b95095857b78e12138c22e76c7936611c51355bChris Lattner	os_get_time(&now);
5021b95095857b78e12138c22e76c7936611c51355bChris Lattner	if (first < now.sec)
5031b95095857b78e12138c22e76c7936611c51355bChris Lattner		first = now.sec;
504e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
50583ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands			       NULL);
506475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
50783ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
508475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       " %ld seconds", (long int) (first - now.sec));
50983ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands}
510475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
51183ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands
512475871a144eb604ddaf37503397ba0941442e5fbDan Gohmanstatic void radius_client_list_add(struct radius_client_data *radius,
513cd920d9ecfcefff13c3619a32b58399cac2e3630Dan Gohman				   struct radius_msg *msg,
514cd920d9ecfcefff13c3619a32b58399cac2e3630Dan Gohman				   RadiusType msg_type,
515475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				   const u8 *shared_secret,
516cd920d9ecfcefff13c3619a32b58399cac2e3630Dan Gohman				   size_t shared_secret_len, const u8 *addr)
517475871a144eb604ddaf37503397ba0941442e5fbDan Gohman{
518cd920d9ecfcefff13c3619a32b58399cac2e3630Dan Gohman	struct radius_msg_list *entry, *prev;
519475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
52083ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	if (eloop_terminated()) {
521475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		/* No point in adding entries to retransmit queue since event
52283ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		 * loop has already been terminated. */
523475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		radius_msg_free(msg);
524cd920d9ecfcefff13c3619a32b58399cac2e3630Dan Gohman		return;
525475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
526694481ee01bfe507c6e37de0dc1c64cff455eefdEvan Cheng
527e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	entry = os_zalloc(sizeof(*entry));
528e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	if (entry == NULL) {
529e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman		printf("Failed to add RADIUS packet into retransmit list\n");
530475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		radius_msg_free(msg);
531e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman		return;
532475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
533e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman
534475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (addr)
535e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman		os_memcpy(entry->addr, addr, ETH_ALEN);
536475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->msg = msg;
537e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	entry->msg_type = msg_type;
538e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	entry->shared_secret = shared_secret;
539475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->shared_secret_len = shared_secret_len;
540e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	os_get_time(&entry->last_attempt);
541475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->first_try = entry->last_attempt.sec;
542e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
543475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->attempts = 1;
544e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
545475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	entry->next = radius->msgs;
546e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	radius->msgs = entry;
547475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	radius_client_update_timeout(radius);
548e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman
549475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
550753c8f20e45f6e4198c7cf4096ecc8948a029e9cChris Lattner		printf("Removing the oldest un-ACKed RADIUS packet due to "
5516ae46c4c8757237bca2b78b589c96c37015bc356Evan Cheng		       "retransmit list limits.\n");
5526ae46c4c8757237bca2b78b589c96c37015bc356Evan Cheng		prev = NULL;
5536ae46c4c8757237bca2b78b589c96c37015bc356Evan Cheng		while (entry->next) {
5546ae46c4c8757237bca2b78b589c96c37015bc356Evan Cheng			prev = entry;
5556ae46c4c8757237bca2b78b589c96c37015bc356Evan Cheng			entry = entry->next;
5566ae46c4c8757237bca2b78b589c96c37015bc356Evan Cheng		}
55783ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		if (prev) {
558475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			prev->next = NULL;
559475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			radius_client_msg_free(entry);
56083ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		}
561475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	} else
56283ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		radius->num_msgs++;
563475871a144eb604ddaf37503397ba0941442e5fbDan Gohman}
56483ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands
565475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
56683ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sandsstatic void radius_client_list_del(struct radius_client_data *radius,
567475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				   RadiusType msg_type, const u8 *addr)
56883ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands{
569475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	struct radius_msg_list *entry, *prev, *tmp;
57083ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands
571475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (addr == NULL)
57283ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		return;
573475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
57483ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	entry = radius->msgs;
575475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	prev = NULL;
57683ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands	while (entry) {
577475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		if (entry->msg_type == msg_type &&
57883ec4b6711980242ef3c55a4fa36b2d7a39c1bfbDuncan Sands		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
579475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			if (prev)
580f877b735ad4987f26cafcbaf22aa4c2199458b5dDan Gohman				prev->next = entry->next;
581475871a144eb604ddaf37503397ba0941442e5fbDan Gohman			else
58208b1173971a51eb89d7d6ee0992c39170c86994aEvan Cheng				radius->msgs = entry->next;
58308b1173971a51eb89d7d6ee0992c39170c86994aEvan Cheng			tmp = entry;
58408b1173971a51eb89d7d6ee0992c39170c86994aEvan Cheng			entry = entry->next;
58508b1173971a51eb89d7d6ee0992c39170c86994aEvan Cheng			hostapd_logger(radius->ctx, addr,
586475871a144eb604ddaf37503397ba0941442e5fbDan Gohman				       HOSTAPD_MODULE_RADIUS,
5876542d950609208de3e1cde704c5f89aad864c0d9Chris Lattner				       HOSTAPD_LEVEL_DEBUG,
588f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner				       "Removing matching RADIUS message");
589f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner			radius_client_msg_free(tmp);
590f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner			radius->num_msgs--;
591f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner			continue;
592f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner		}
593f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner		prev = entry;
594edfcf598faab9ce294712551ecf67093acd1c66eDuncan Sands		entry = entry->next;
595edfcf598faab9ce294712551ecf67093acd1c66eDuncan Sands	}
596edfcf598faab9ce294712551ecf67093acd1c66eDuncan Sands}
597edfcf598faab9ce294712551ecf67093acd1c66eDuncan Sands
598edfcf598faab9ce294712551ecf67093acd1c66eDuncan Sands
599edfcf598faab9ce294712551ecf67093acd1c66eDuncan Sands/**
600f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * radius_client_send - Send a RADIUS request
601f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * @radius: RADIUS client context from radius_client_init()
602f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * @msg: RADIUS message to be sent
603f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
604f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * @addr: MAC address of the device related to this message or %NULL
605f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * Returns: 0 on success, -1 on failure
606f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner *
607f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
6080fe9c6e7babb3c0731d9cb864ec498ec4184760fDan Gohman * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
6090fe9c6e7babb3c0731d9cb864ec498ec4184760fDan Gohman * between accounting and interim accounting messages is that the interim
6100fe9c6e7babb3c0731d9cb864ec498ec4184760fDan Gohman * message will override any pending interim accounting updates while a new
6110fe9c6e7babb3c0731d9cb864ec498ec4184760fDan Gohman * accounting message does not remove any pending messages.
6120fe9c6e7babb3c0731d9cb864ec498ec4184760fDan Gohman *
6136542d950609208de3e1cde704c5f89aad864c0d9Chris Lattner * The message is added on the retransmission queue and will be retransmitted
61426005b1b672aebd437edc561d381c5dd19a03ddbChris Lattner * automatically until a response is received or maximum number of retries
61526005b1b672aebd437edc561d381c5dd19a03ddbChris Lattner * (RADIUS_CLIENT_MAX_RETRIES) is reached.
61626005b1b672aebd437edc561d381c5dd19a03ddbChris Lattner *
6176542d950609208de3e1cde704c5f89aad864c0d9Chris Lattner * The related device MAC address can be used to identify pending messages that
618f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * can be removed with radius_client_flush_auth() or with interim accounting
619f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner * updates.
620f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner */
621fde3f3061d665babeb78443119a09876098fc35eChris Lattnerint radius_client_send(struct radius_client_data *radius,
622475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       struct radius_msg *msg, RadiusType msg_type,
623f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner		       const u8 *addr)
624fde3f3061d665babeb78443119a09876098fc35eChris Lattner{
625f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner	struct hostapd_radius_servers *conf = radius->conf;
626475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	const u8 *shared_secret;
627f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner	size_t shared_secret_len;
628fae9f1cb34d6d2c4dbd007f2d748a70b67776a82Evan Cheng	char *name;
62980274268b99e5a066825c8cc5aba58dbc5ad0a52Chris Lattner	int s, res;
630f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner	struct wpabuf *buf;
631475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
632f8dc0617baceeba8ccd67c8881eb88eb1be2902cChris Lattner	if (msg_type == RADIUS_ACCT_INTERIM) {
63380274268b99e5a066825c8cc5aba58dbc5ad0a52Chris Lattner		/* Remove any pending interim acct update for the same STA. */
634e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman		radius_client_list_del(radius, msg_type, addr);
635e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman	}
636e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman
637475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
638e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman		if (conf->acct_server == NULL) {
639e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman			hostapd_logger(radius->ctx, NULL,
640e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman				       HOSTAPD_MODULE_RADIUS,
641e6f35d8a5cc92d776cf460200e2b815e8c301b14Evan Cheng				       HOSTAPD_LEVEL_INFO,
64209fd736058ec3f69b856ae3ad65177bc31904a8cEvan Cheng				       "No accounting server configured");
64309fd736058ec3f69b856ae3ad65177bc31904a8cEvan Cheng			return -1;
64409fd736058ec3f69b856ae3ad65177bc31904a8cEvan Cheng		}
645e6f35d8a5cc92d776cf460200e2b815e8c301b14Evan Cheng		shared_secret = conf->acct_server->shared_secret;
6461efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		shared_secret_len = conf->acct_server->shared_secret_len;
6471efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
6481efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		name = "accounting";
6494ae9e0c5301126d7f2d4b2975eb86ed21f7b574dChris Lattner		s = radius->acct_sock;
6504ae9e0c5301126d7f2d4b2975eb86ed21f7b574dChris Lattner		conf->acct_server->requests++;
6511efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng	} else {
6521efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		if (conf->auth_server == NULL) {
6531efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng			hostapd_logger(radius->ctx, NULL,
6541efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng				       HOSTAPD_MODULE_RADIUS,
6551efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng				       HOSTAPD_LEVEL_INFO,
656b6c7437568f0472548ede2710458f52cfad4532eDan Gohman				       "No authentication server configured");
657b6c7437568f0472548ede2710458f52cfad4532eDan Gohman			return -1;
6581efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		}
6591efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		shared_secret = conf->auth_server->shared_secret;
6601efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		shared_secret_len = conf->auth_server->shared_secret_len;
6611efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		radius_msg_finish(msg, shared_secret, shared_secret_len);
6621efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		name = "authentication";
6631efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		s = radius->auth_sock;
6641efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		conf->auth_server->requests++;
6651efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng	}
6661efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng
6671efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
6681efba0ecb4d0b3807c48e6e0f74e3ce5c9fad809Evan Cheng		       HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
669cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner		       "server", name);
670d1fc96499b7619356c7542200d32da898b79f7c1Chris Lattner	if (conf->msg_dumps)
67137ce9df0da6cddc3b8bfef9b63d33d058a0f2f15Chris Lattner		radius_msg_dump(msg);
672364d73ddab43b699ab90240f11b7a2eb5cf69bd8Mon P Wang
673364d73ddab43b699ab90240f11b7a2eb5cf69bd8Mon P Wang	buf = radius_msg_get_buf(msg);
674475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
675364d73ddab43b699ab90240f11b7a2eb5cf69bd8Mon P Wang	if (res < 0)
67651dabfb28375be7bc5848806ae31cd068b6133f8Chris Lattner		radius_client_handle_send_error(radius, s, msg_type);
677475871a144eb604ddaf37503397ba0941442e5fbDan Gohman
678475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	radius_client_list_add(radius, msg, msg_type, shared_secret,
67951dabfb28375be7bc5848806ae31cd068b6133f8Chris Lattner			       shared_secret_len, addr);
6802e68b6f52d0979575b2f02ed29717d907ba0684cDan Gohman
6812e68b6f52d0979575b2f02ed29717d907ba0684cDan Gohman	return 0;
682475871a144eb604ddaf37503397ba0941442e5fbDan Gohman}
6832e68b6f52d0979575b2f02ed29717d907ba0684cDan Gohman
684ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman
685ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohmanstatic void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
686ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman{
687475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	struct radius_client_data *radius = eloop_ctx;
688ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	struct hostapd_radius_servers *conf = radius->conf;
689ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	RadiusType msg_type = (RadiusType) sock_ctx;
690ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	int len, roundtrip;
691ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	unsigned char buf[3000];
692ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	struct radius_msg *msg;
693ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	struct radius_hdr *hdr;
694ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	struct radius_rx_handler *handlers;
695475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	size_t num_handlers, i;
696fd29e0eb060ea8b4d490860329234d2ae5f5952eDan Gohman	struct radius_msg_list *req, *prev_req;
697fd29e0eb060ea8b4d490860329234d2ae5f5952eDan Gohman	struct os_time now;
698ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	struct hostapd_radius_server *rconf;
699ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	int invalid_authenticator = 0;
700ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman
701ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman	if (msg_type == RADIUS_ACCT) {
702ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman		handlers = radius->acct_handlers;
703ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman		num_handlers = radius->num_acct_handlers;
704ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman		rconf = conf->acct_server;
705475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	} else {
706a844bdeab31ef04221e7ef59a8467893584cc14dEvan Cheng		handlers = radius->auth_handlers;
707475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		num_handlers = radius->num_auth_handlers;
708a844bdeab31ef04221e7ef59a8467893584cc14dEvan Cheng		rconf = conf->auth_server;
709475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	}
71077f0b7a50a08614b5ffd58f1864b68a9a30d0cb0Evan Cheng
71177f0b7a50a08614b5ffd58f1864b68a9a30d0cb0Evan Cheng	len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
71277f0b7a50a08614b5ffd58f1864b68a9a30d0cb0Evan Cheng	if (len < 0) {
713475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		perror("recv[RADIUS]");
714ea859be53ca13a1547c4675549946b74dc3c6f41Dan Gohman		return;
715d1fc96499b7619356c7542200d32da898b79f7c1Chris Lattner	}
7161b95095857b78e12138c22e76c7936611c51355bChris Lattner	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
7176542d950609208de3e1cde704c5f89aad864c0d9Chris Lattner		       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
718475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       "server", len);
719475871a144eb604ddaf37503397ba0941442e5fbDan Gohman	if (len == sizeof(buf)) {
720a5682853b9921bbb0dd2ee175c9bd44142d4819eChris Lattner		printf("Possibly too long UDP frame for our buffer - "
721475871a144eb604ddaf37503397ba0941442e5fbDan Gohman		       "dropping it\n");
722a5682853b9921bbb0dd2ee175c9bd44142d4819eChris Lattner		return;
723b9aff659e82e4ec1a507e6e7fe7969379a431613Chris Lattner	}
724fde3f3061d665babeb78443119a09876098fc35eChris Lattner
7259c6e70eca9a49c146b26621cbcbb9464ceeac024Dan Gohman	msg = radius_msg_parse(buf, len);
7269c6e70eca9a49c146b26621cbcbb9464ceeac024Dan Gohman	if (msg == NULL) {
7277cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner		printf("Parsing incoming RADIUS frame failed\n");
728109654fae9c5b8b96bd3a829824cdbceb27ced06Chris Lattner		rconf->malformed_responses++;
729e8be6c63915e0389f1eef6b53c64300d13b2ce99Dan Gohman		return;
730109654fae9c5b8b96bd3a829824cdbceb27ced06Chris Lattner	}
7311cff05c7c216eea0e9173738c2a60b70c2b3c013Chris Lattner	hdr = radius_msg_get_hdr(msg);
7327cf7e3f33f25544d08492d47cc8a1cbba25dc8d7Chris Lattner
7331cff05c7c216eea0e9173738c2a60b70c2b3c013Chris Lattner	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
73415e4b01920d6a0ffbe35d3e5aa88a4b42970b6a7Chris Lattner		       HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
7358e4eb09b1e3571965f49edcdfb56b1375b1b7551Duncan Sands	if (conf->msg_dumps)
7364b84086e89d86fb16f562166d9fea8df37db6be7Dan Gohman		radius_msg_dump(msg);
7374b84086e89d86fb16f562166d9fea8df37db6be7Dan Gohman
738cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	switch (hdr->code) {
739cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	case RADIUS_CODE_ACCESS_ACCEPT:
7401080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		rconf->access_accepts++;
7411080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		break;
7421080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner	case RADIUS_CODE_ACCESS_REJECT:
7431080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		rconf->access_rejects++;
7441080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		break;
7451080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner	case RADIUS_CODE_ACCESS_CHALLENGE:
7461080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		rconf->access_challenges++;
7471080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		break;
7481080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner	case RADIUS_CODE_ACCOUNTING_RESPONSE:
7491080b9ee534579c67f7c99364cc6fa11edbcd919Chris Lattner		rconf->responses++;
750b80e2be8894db9f843f32ebaffb9b7fd6b57d206Chris Lattner		break;
751cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner	}
752cacf462915344c2af25eef1af1f3ee2c7280ff56Chris Lattner
753	prev_req = NULL;
754	req = radius->msgs;
755	while (req) {
756		/* TODO: also match by src addr:port of the packet when using
757		 * alternative RADIUS servers (?) */
758		if ((req->msg_type == msg_type ||
759		     (req->msg_type == RADIUS_ACCT_INTERIM &&
760		      msg_type == RADIUS_ACCT)) &&
761		    radius_msg_get_hdr(req->msg)->identifier ==
762		    hdr->identifier)
763			break;
764
765		prev_req = req;
766		req = req->next;
767	}
768
769	if (req == NULL) {
770		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
771			       HOSTAPD_LEVEL_DEBUG,
772			       "No matching RADIUS request found (type=%d "
773			       "id=%d) - dropping packet",
774			       msg_type, hdr->identifier);
775		goto fail;
776	}
777
778	os_get_time(&now);
779	roundtrip = (now.sec - req->last_attempt.sec) * 100 +
780		(now.usec - req->last_attempt.usec) / 10000;
781	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
782		       HOSTAPD_LEVEL_DEBUG,
783		       "Received RADIUS packet matched with a pending "
784		       "request, round trip time %d.%02d sec",
785		       roundtrip / 100, roundtrip % 100);
786	rconf->round_trip_time = roundtrip;
787
788	/* Remove ACKed RADIUS packet from retransmit list */
789	if (prev_req)
790		prev_req->next = req->next;
791	else
792		radius->msgs = req->next;
793	radius->num_msgs--;
794
795	for (i = 0; i < num_handlers; i++) {
796		RadiusRxResult res;
797		res = handlers[i].handler(msg, req->msg, req->shared_secret,
798					  req->shared_secret_len,
799					  handlers[i].data);
800		switch (res) {
801		case RADIUS_RX_PROCESSED:
802			radius_msg_free(msg);
803			/* continue */
804		case RADIUS_RX_QUEUED:
805			radius_client_msg_free(req);
806			return;
807		case RADIUS_RX_INVALID_AUTHENTICATOR:
808			invalid_authenticator++;
809			/* continue */
810		case RADIUS_RX_UNKNOWN:
811			/* continue with next handler */
812			break;
813		}
814	}
815
816	if (invalid_authenticator)
817		rconf->bad_authenticators++;
818	else
819		rconf->unknown_types++;
820	hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
821		       HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
822		       "(type=%d code=%d id=%d)%s - dropping packet",
823		       msg_type, hdr->code, hdr->identifier,
824		       invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
825		       "");
826	radius_client_msg_free(req);
827
828 fail:
829	radius_msg_free(msg);
830}
831
832
833/**
834 * radius_client_get_id - Get an identifier for a new RADIUS message
835 * @radius: RADIUS client context from radius_client_init()
836 * Returns: Allocated identifier
837 *
838 * This function is used to fetch a unique (among pending requests) identifier
839 * for a new RADIUS message.
840 */
841u8 radius_client_get_id(struct radius_client_data *radius)
842{
843	struct radius_msg_list *entry, *prev, *_remove;
844	u8 id = radius->next_radius_identifier++;
845
846	/* remove entries with matching id from retransmit list to avoid
847	 * using new reply from the RADIUS server with an old request */
848	entry = radius->msgs;
849	prev = NULL;
850	while (entry) {
851		if (radius_msg_get_hdr(entry->msg)->identifier == id) {
852			hostapd_logger(radius->ctx, entry->addr,
853				       HOSTAPD_MODULE_RADIUS,
854				       HOSTAPD_LEVEL_DEBUG,
855				       "Removing pending RADIUS message, "
856				       "since its id (%d) is reused", id);
857			if (prev)
858				prev->next = entry->next;
859			else
860				radius->msgs = entry->next;
861			_remove = entry;
862		} else {
863			_remove = NULL;
864			prev = entry;
865		}
866		entry = entry->next;
867
868		if (_remove)
869			radius_client_msg_free(_remove);
870	}
871
872	return id;
873}
874
875
876/**
877 * radius_client_flush - Flush all pending RADIUS client messages
878 * @radius: RADIUS client context from radius_client_init()
879 * @only_auth: Whether only authentication messages are removed
880 */
881void radius_client_flush(struct radius_client_data *radius, int only_auth)
882{
883	struct radius_msg_list *entry, *prev, *tmp;
884
885	if (!radius)
886		return;
887
888	prev = NULL;
889	entry = radius->msgs;
890
891	while (entry) {
892		if (!only_auth || entry->msg_type == RADIUS_AUTH) {
893			if (prev)
894				prev->next = entry->next;
895			else
896				radius->msgs = entry->next;
897
898			tmp = entry;
899			entry = entry->next;
900			radius_client_msg_free(tmp);
901			radius->num_msgs--;
902		} else {
903			prev = entry;
904			entry = entry->next;
905		}
906	}
907
908	if (radius->msgs == NULL)
909		eloop_cancel_timeout(radius_client_timer, radius, NULL);
910}
911
912
913static void radius_client_update_acct_msgs(struct radius_client_data *radius,
914					   const u8 *shared_secret,
915					   size_t shared_secret_len)
916{
917	struct radius_msg_list *entry;
918
919	if (!radius)
920		return;
921
922	for (entry = radius->msgs; entry; entry = entry->next) {
923		if (entry->msg_type == RADIUS_ACCT) {
924			entry->shared_secret = shared_secret;
925			entry->shared_secret_len = shared_secret_len;
926			radius_msg_finish_acct(entry->msg, shared_secret,
927					       shared_secret_len);
928		}
929	}
930}
931
932
933static int
934radius_change_server(struct radius_client_data *radius,
935		     struct hostapd_radius_server *nserv,
936		     struct hostapd_radius_server *oserv,
937		     int sock, int sock6, int auth)
938{
939	struct sockaddr_in serv, claddr;
940#ifdef CONFIG_IPV6
941	struct sockaddr_in6 serv6, claddr6;
942#endif /* CONFIG_IPV6 */
943	struct sockaddr *addr, *cl_addr;
944	socklen_t addrlen, claddrlen;
945	char abuf[50];
946	int sel_sock;
947	struct radius_msg_list *entry;
948	struct hostapd_radius_servers *conf = radius->conf;
949
950	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
951		       HOSTAPD_LEVEL_INFO,
952		       "%s server %s:%d",
953		       auth ? "Authentication" : "Accounting",
954		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
955		       nserv->port);
956
957	if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
958	    os_memcmp(nserv->shared_secret, oserv->shared_secret,
959		      nserv->shared_secret_len) != 0) {
960		/* Pending RADIUS packets used different shared secret, so
961		 * they need to be modified. Update accounting message
962		 * authenticators here. Authentication messages are removed
963		 * since they would require more changes and the new RADIUS
964		 * server may not be prepared to receive them anyway due to
965		 * missing state information. Client will likely retry
966		 * authentication, so this should not be an issue. */
967		if (auth)
968			radius_client_flush(radius, 1);
969		else {
970			radius_client_update_acct_msgs(
971				radius, nserv->shared_secret,
972				nserv->shared_secret_len);
973		}
974	}
975
976	/* Reset retry counters for the new server */
977	for (entry = radius->msgs; entry; entry = entry->next) {
978		if ((auth && entry->msg_type != RADIUS_AUTH) ||
979		    (!auth && entry->msg_type != RADIUS_ACCT))
980			continue;
981		entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
982		entry->attempts = 0;
983		entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
984	}
985
986	if (radius->msgs) {
987		eloop_cancel_timeout(radius_client_timer, radius, NULL);
988		eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
989				       radius_client_timer, radius, NULL);
990	}
991
992	switch (nserv->addr.af) {
993	case AF_INET:
994		os_memset(&serv, 0, sizeof(serv));
995		serv.sin_family = AF_INET;
996		serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
997		serv.sin_port = htons(nserv->port);
998		addr = (struct sockaddr *) &serv;
999		addrlen = sizeof(serv);
1000		sel_sock = sock;
1001		break;
1002#ifdef CONFIG_IPV6
1003	case AF_INET6:
1004		os_memset(&serv6, 0, sizeof(serv6));
1005		serv6.sin6_family = AF_INET6;
1006		os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
1007			  sizeof(struct in6_addr));
1008		serv6.sin6_port = htons(nserv->port);
1009		addr = (struct sockaddr *) &serv6;
1010		addrlen = sizeof(serv6);
1011		sel_sock = sock6;
1012		break;
1013#endif /* CONFIG_IPV6 */
1014	default:
1015		return -1;
1016	}
1017
1018	if (conf->force_client_addr) {
1019		switch (conf->client_addr.af) {
1020		case AF_INET:
1021			os_memset(&claddr, 0, sizeof(claddr));
1022			claddr.sin_family = AF_INET;
1023			claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
1024			claddr.sin_port = htons(0);
1025			cl_addr = (struct sockaddr *) &claddr;
1026			claddrlen = sizeof(claddr);
1027			break;
1028#ifdef CONFIG_IPV6
1029		case AF_INET6:
1030			os_memset(&claddr6, 0, sizeof(claddr6));
1031			claddr6.sin6_family = AF_INET6;
1032			os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
1033				  sizeof(struct in6_addr));
1034			claddr6.sin6_port = htons(0);
1035			cl_addr = (struct sockaddr *) &claddr6;
1036			claddrlen = sizeof(claddr6);
1037			break;
1038#endif /* CONFIG_IPV6 */
1039		default:
1040			return -1;
1041		}
1042
1043		if (bind(sel_sock, cl_addr, claddrlen) < 0) {
1044			perror("bind[radius]");
1045			return -1;
1046		}
1047	}
1048
1049	if (connect(sel_sock, addr, addrlen) < 0) {
1050		perror("connect[radius]");
1051		return -1;
1052	}
1053
1054#ifndef CONFIG_NATIVE_WINDOWS
1055	switch (nserv->addr.af) {
1056	case AF_INET:
1057		claddrlen = sizeof(claddr);
1058		getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
1059		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1060			   inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
1061		break;
1062#ifdef CONFIG_IPV6
1063	case AF_INET6: {
1064		claddrlen = sizeof(claddr6);
1065		getsockname(sel_sock, (struct sockaddr *) &claddr6,
1066			    &claddrlen);
1067		wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
1068			   inet_ntop(AF_INET6, &claddr6.sin6_addr,
1069				     abuf, sizeof(abuf)),
1070			   ntohs(claddr6.sin6_port));
1071		break;
1072	}
1073#endif /* CONFIG_IPV6 */
1074	}
1075#endif /* CONFIG_NATIVE_WINDOWS */
1076
1077	if (auth)
1078		radius->auth_sock = sel_sock;
1079	else
1080		radius->acct_sock = sel_sock;
1081
1082	return 0;
1083}
1084
1085
1086static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
1087{
1088	struct radius_client_data *radius = eloop_ctx;
1089	struct hostapd_radius_servers *conf = radius->conf;
1090	struct hostapd_radius_server *oserv;
1091
1092	if (radius->auth_sock >= 0 && conf->auth_servers &&
1093	    conf->auth_server != conf->auth_servers) {
1094		oserv = conf->auth_server;
1095		conf->auth_server = conf->auth_servers;
1096		radius_change_server(radius, conf->auth_server, oserv,
1097				     radius->auth_serv_sock,
1098				     radius->auth_serv_sock6, 1);
1099	}
1100
1101	if (radius->acct_sock >= 0 && conf->acct_servers &&
1102	    conf->acct_server != conf->acct_servers) {
1103		oserv = conf->acct_server;
1104		conf->acct_server = conf->acct_servers;
1105		radius_change_server(radius, conf->acct_server, oserv,
1106				     radius->acct_serv_sock,
1107				     radius->acct_serv_sock6, 0);
1108	}
1109
1110	if (conf->retry_primary_interval)
1111		eloop_register_timeout(conf->retry_primary_interval, 0,
1112				       radius_retry_primary_timer, radius,
1113				       NULL);
1114}
1115
1116
1117static int radius_client_disable_pmtu_discovery(int s)
1118{
1119	int r = -1;
1120#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
1121	/* Turn off Path MTU discovery on IPv4/UDP sockets. */
1122	int action = IP_PMTUDISC_DONT;
1123	r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
1124		       sizeof(action));
1125	if (r == -1)
1126		wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
1127			   "%s", strerror(errno));
1128#endif
1129	return r;
1130}
1131
1132
1133static int radius_client_init_auth(struct radius_client_data *radius)
1134{
1135	struct hostapd_radius_servers *conf = radius->conf;
1136	int ok = 0;
1137
1138	radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1139	if (radius->auth_serv_sock < 0)
1140		perror("socket[PF_INET,SOCK_DGRAM]");
1141	else {
1142		radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
1143		ok++;
1144	}
1145
1146#ifdef CONFIG_IPV6
1147	radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1148	if (radius->auth_serv_sock6 < 0)
1149		perror("socket[PF_INET6,SOCK_DGRAM]");
1150	else
1151		ok++;
1152#endif /* CONFIG_IPV6 */
1153
1154	if (ok == 0)
1155		return -1;
1156
1157	radius_change_server(radius, conf->auth_server, NULL,
1158			     radius->auth_serv_sock, radius->auth_serv_sock6,
1159			     1);
1160
1161	if (radius->auth_serv_sock >= 0 &&
1162	    eloop_register_read_sock(radius->auth_serv_sock,
1163				     radius_client_receive, radius,
1164				     (void *) RADIUS_AUTH)) {
1165		printf("Could not register read socket for authentication "
1166		       "server\n");
1167		return -1;
1168	}
1169
1170#ifdef CONFIG_IPV6
1171	if (radius->auth_serv_sock6 >= 0 &&
1172	    eloop_register_read_sock(radius->auth_serv_sock6,
1173				     radius_client_receive, radius,
1174				     (void *) RADIUS_AUTH)) {
1175		printf("Could not register read socket for authentication "
1176		       "server\n");
1177		return -1;
1178	}
1179#endif /* CONFIG_IPV6 */
1180
1181	return 0;
1182}
1183
1184
1185static int radius_client_init_acct(struct radius_client_data *radius)
1186{
1187	struct hostapd_radius_servers *conf = radius->conf;
1188	int ok = 0;
1189
1190	radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
1191	if (radius->acct_serv_sock < 0)
1192		perror("socket[PF_INET,SOCK_DGRAM]");
1193	else {
1194		radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
1195		ok++;
1196	}
1197
1198#ifdef CONFIG_IPV6
1199	radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
1200	if (radius->acct_serv_sock6 < 0)
1201		perror("socket[PF_INET6,SOCK_DGRAM]");
1202	else
1203		ok++;
1204#endif /* CONFIG_IPV6 */
1205
1206	if (ok == 0)
1207		return -1;
1208
1209	radius_change_server(radius, conf->acct_server, NULL,
1210			     radius->acct_serv_sock, radius->acct_serv_sock6,
1211			     0);
1212
1213	if (radius->acct_serv_sock >= 0 &&
1214	    eloop_register_read_sock(radius->acct_serv_sock,
1215				     radius_client_receive, radius,
1216				     (void *) RADIUS_ACCT)) {
1217		printf("Could not register read socket for accounting "
1218		       "server\n");
1219		return -1;
1220	}
1221
1222#ifdef CONFIG_IPV6
1223	if (radius->acct_serv_sock6 >= 0 &&
1224	    eloop_register_read_sock(radius->acct_serv_sock6,
1225				     radius_client_receive, radius,
1226				     (void *) RADIUS_ACCT)) {
1227		printf("Could not register read socket for accounting "
1228		       "server\n");
1229		return -1;
1230	}
1231#endif /* CONFIG_IPV6 */
1232
1233	return 0;
1234}
1235
1236
1237/**
1238 * radius_client_init - Initialize RADIUS client
1239 * @ctx: Callback context to be used in hostapd_logger() calls
1240 * @conf: RADIUS client configuration (RADIUS servers)
1241 * Returns: Pointer to private RADIUS client context or %NULL on failure
1242 *
1243 * The caller is responsible for keeping the configuration data available for
1244 * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
1245 * called for the returned context pointer.
1246 */
1247struct radius_client_data *
1248radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
1249{
1250	struct radius_client_data *radius;
1251
1252	radius = os_zalloc(sizeof(struct radius_client_data));
1253	if (radius == NULL)
1254		return NULL;
1255
1256	radius->ctx = ctx;
1257	radius->conf = conf;
1258	radius->auth_serv_sock = radius->acct_serv_sock =
1259		radius->auth_serv_sock6 = radius->acct_serv_sock6 =
1260		radius->auth_sock = radius->acct_sock = -1;
1261
1262	if (conf->auth_server && radius_client_init_auth(radius)) {
1263		radius_client_deinit(radius);
1264		return NULL;
1265	}
1266
1267	if (conf->acct_server && radius_client_init_acct(radius)) {
1268		radius_client_deinit(radius);
1269		return NULL;
1270	}
1271
1272	if (conf->retry_primary_interval)
1273		eloop_register_timeout(conf->retry_primary_interval, 0,
1274				       radius_retry_primary_timer, radius,
1275				       NULL);
1276
1277	return radius;
1278}
1279
1280
1281/**
1282 * radius_client_deinit - Deinitialize RADIUS client
1283 * @radius: RADIUS client context from radius_client_init()
1284 */
1285void radius_client_deinit(struct radius_client_data *radius)
1286{
1287	if (!radius)
1288		return;
1289
1290	if (radius->auth_serv_sock >= 0)
1291		eloop_unregister_read_sock(radius->auth_serv_sock);
1292	if (radius->acct_serv_sock >= 0)
1293		eloop_unregister_read_sock(radius->acct_serv_sock);
1294#ifdef CONFIG_IPV6
1295	if (radius->auth_serv_sock6 >= 0)
1296		eloop_unregister_read_sock(radius->auth_serv_sock6);
1297	if (radius->acct_serv_sock6 >= 0)
1298		eloop_unregister_read_sock(radius->acct_serv_sock6);
1299#endif /* CONFIG_IPV6 */
1300
1301	eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
1302
1303	radius_client_flush(radius, 0);
1304	os_free(radius->auth_handlers);
1305	os_free(radius->acct_handlers);
1306	os_free(radius);
1307}
1308
1309
1310/**
1311 * radius_client_flush_auth - Flush pending RADIUS messages for an address
1312 * @radius: RADIUS client context from radius_client_init()
1313 * @addr: MAC address of the related device
1314 *
1315 * This function can be used to remove pending RADIUS authentication messages
1316 * that are related to a specific device. The addr parameter is matched with
1317 * the one used in radius_client_send() call that was used to transmit the
1318 * authentication request.
1319 */
1320void radius_client_flush_auth(struct radius_client_data *radius,
1321			      const u8 *addr)
1322{
1323	struct radius_msg_list *entry, *prev, *tmp;
1324
1325	prev = NULL;
1326	entry = radius->msgs;
1327	while (entry) {
1328		if (entry->msg_type == RADIUS_AUTH &&
1329		    os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
1330			hostapd_logger(radius->ctx, addr,
1331				       HOSTAPD_MODULE_RADIUS,
1332				       HOSTAPD_LEVEL_DEBUG,
1333				       "Removing pending RADIUS authentication"
1334				       " message for removed client");
1335
1336			if (prev)
1337				prev->next = entry->next;
1338			else
1339				radius->msgs = entry->next;
1340
1341			tmp = entry;
1342			entry = entry->next;
1343			radius_client_msg_free(tmp);
1344			radius->num_msgs--;
1345			continue;
1346		}
1347
1348		prev = entry;
1349		entry = entry->next;
1350	}
1351}
1352
1353
1354static int radius_client_dump_auth_server(char *buf, size_t buflen,
1355					  struct hostapd_radius_server *serv,
1356					  struct radius_client_data *cli)
1357{
1358	int pending = 0;
1359	struct radius_msg_list *msg;
1360	char abuf[50];
1361
1362	if (cli) {
1363		for (msg = cli->msgs; msg; msg = msg->next) {
1364			if (msg->msg_type == RADIUS_AUTH)
1365				pending++;
1366		}
1367	}
1368
1369	return os_snprintf(buf, buflen,
1370			   "radiusAuthServerIndex=%d\n"
1371			   "radiusAuthServerAddress=%s\n"
1372			   "radiusAuthClientServerPortNumber=%d\n"
1373			   "radiusAuthClientRoundTripTime=%d\n"
1374			   "radiusAuthClientAccessRequests=%u\n"
1375			   "radiusAuthClientAccessRetransmissions=%u\n"
1376			   "radiusAuthClientAccessAccepts=%u\n"
1377			   "radiusAuthClientAccessRejects=%u\n"
1378			   "radiusAuthClientAccessChallenges=%u\n"
1379			   "radiusAuthClientMalformedAccessResponses=%u\n"
1380			   "radiusAuthClientBadAuthenticators=%u\n"
1381			   "radiusAuthClientPendingRequests=%u\n"
1382			   "radiusAuthClientTimeouts=%u\n"
1383			   "radiusAuthClientUnknownTypes=%u\n"
1384			   "radiusAuthClientPacketsDropped=%u\n",
1385			   serv->index,
1386			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1387			   serv->port,
1388			   serv->round_trip_time,
1389			   serv->requests,
1390			   serv->retransmissions,
1391			   serv->access_accepts,
1392			   serv->access_rejects,
1393			   serv->access_challenges,
1394			   serv->malformed_responses,
1395			   serv->bad_authenticators,
1396			   pending,
1397			   serv->timeouts,
1398			   serv->unknown_types,
1399			   serv->packets_dropped);
1400}
1401
1402
1403static int radius_client_dump_acct_server(char *buf, size_t buflen,
1404					  struct hostapd_radius_server *serv,
1405					  struct radius_client_data *cli)
1406{
1407	int pending = 0;
1408	struct radius_msg_list *msg;
1409	char abuf[50];
1410
1411	if (cli) {
1412		for (msg = cli->msgs; msg; msg = msg->next) {
1413			if (msg->msg_type == RADIUS_ACCT ||
1414			    msg->msg_type == RADIUS_ACCT_INTERIM)
1415				pending++;
1416		}
1417	}
1418
1419	return os_snprintf(buf, buflen,
1420			   "radiusAccServerIndex=%d\n"
1421			   "radiusAccServerAddress=%s\n"
1422			   "radiusAccClientServerPortNumber=%d\n"
1423			   "radiusAccClientRoundTripTime=%d\n"
1424			   "radiusAccClientRequests=%u\n"
1425			   "radiusAccClientRetransmissions=%u\n"
1426			   "radiusAccClientResponses=%u\n"
1427			   "radiusAccClientMalformedResponses=%u\n"
1428			   "radiusAccClientBadAuthenticators=%u\n"
1429			   "radiusAccClientPendingRequests=%u\n"
1430			   "radiusAccClientTimeouts=%u\n"
1431			   "radiusAccClientUnknownTypes=%u\n"
1432			   "radiusAccClientPacketsDropped=%u\n",
1433			   serv->index,
1434			   hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
1435			   serv->port,
1436			   serv->round_trip_time,
1437			   serv->requests,
1438			   serv->retransmissions,
1439			   serv->responses,
1440			   serv->malformed_responses,
1441			   serv->bad_authenticators,
1442			   pending,
1443			   serv->timeouts,
1444			   serv->unknown_types,
1445			   serv->packets_dropped);
1446}
1447
1448
1449/**
1450 * radius_client_get_mib - Get RADIUS client MIB information
1451 * @radius: RADIUS client context from radius_client_init()
1452 * @buf: Buffer for returning MIB data in text format
1453 * @buflen: Maximum buf length in octets
1454 * Returns: Number of octets written into the buffer
1455 */
1456int radius_client_get_mib(struct radius_client_data *radius, char *buf,
1457			  size_t buflen)
1458{
1459	struct hostapd_radius_servers *conf = radius->conf;
1460	int i;
1461	struct hostapd_radius_server *serv;
1462	int count = 0;
1463
1464	if (conf->auth_servers) {
1465		for (i = 0; i < conf->num_auth_servers; i++) {
1466			serv = &conf->auth_servers[i];
1467			count += radius_client_dump_auth_server(
1468				buf + count, buflen - count, serv,
1469				serv == conf->auth_server ?
1470				radius : NULL);
1471		}
1472	}
1473
1474	if (conf->acct_servers) {
1475		for (i = 0; i < conf->num_acct_servers; i++) {
1476			serv = &conf->acct_servers[i];
1477			count += radius_client_dump_acct_server(
1478				buf + count, buflen - count, serv,
1479				serv == conf->acct_server ?
1480				radius : NULL);
1481		}
1482	}
1483
1484	return count;
1485}
1486
1487
1488void radius_client_reconfig(struct radius_client_data *radius,
1489			    struct hostapd_radius_servers *conf)
1490{
1491	if (radius)
1492		radius->conf = conf;
1493}
1494