1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * hostapd / RADIUS Accounting
3e5b75505Sopenharmony_ci * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci */
8e5b75505Sopenharmony_ci
9e5b75505Sopenharmony_ci#include "utils/includes.h"
10e5b75505Sopenharmony_ci
11e5b75505Sopenharmony_ci#include "utils/common.h"
12e5b75505Sopenharmony_ci#include "utils/eloop.h"
13e5b75505Sopenharmony_ci#include "eapol_auth/eapol_auth_sm.h"
14e5b75505Sopenharmony_ci#include "eapol_auth/eapol_auth_sm_i.h"
15e5b75505Sopenharmony_ci#include "radius/radius.h"
16e5b75505Sopenharmony_ci#include "radius/radius_client.h"
17e5b75505Sopenharmony_ci#include "hostapd.h"
18e5b75505Sopenharmony_ci#include "ieee802_1x.h"
19e5b75505Sopenharmony_ci#include "ap_config.h"
20e5b75505Sopenharmony_ci#include "sta_info.h"
21e5b75505Sopenharmony_ci#include "ap_drv_ops.h"
22e5b75505Sopenharmony_ci#include "accounting.h"
23e5b75505Sopenharmony_ci
24e5b75505Sopenharmony_ci
25e5b75505Sopenharmony_ci/* Default interval in seconds for polling TX/RX octets from the driver if
26e5b75505Sopenharmony_ci * STA is not using interim accounting. This detects wrap arounds for
27e5b75505Sopenharmony_ci * input/output octets and updates Acct-{Input,Output}-Gigawords. */
28e5b75505Sopenharmony_ci#define ACCT_DEFAULT_UPDATE_INTERVAL 300
29e5b75505Sopenharmony_ci
30e5b75505Sopenharmony_cistatic void accounting_sta_interim(struct hostapd_data *hapd,
31e5b75505Sopenharmony_ci				   struct sta_info *sta);
32e5b75505Sopenharmony_ci
33e5b75505Sopenharmony_ci
34e5b75505Sopenharmony_cistatic struct radius_msg * accounting_msg(struct hostapd_data *hapd,
35e5b75505Sopenharmony_ci					  struct sta_info *sta,
36e5b75505Sopenharmony_ci					  int status_type)
37e5b75505Sopenharmony_ci{
38e5b75505Sopenharmony_ci	struct radius_msg *msg;
39e5b75505Sopenharmony_ci	char buf[128];
40e5b75505Sopenharmony_ci	u8 *val;
41e5b75505Sopenharmony_ci	size_t len;
42e5b75505Sopenharmony_ci	int i;
43e5b75505Sopenharmony_ci	struct wpabuf *b;
44e5b75505Sopenharmony_ci	struct os_time now;
45e5b75505Sopenharmony_ci
46e5b75505Sopenharmony_ci	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
47e5b75505Sopenharmony_ci			     radius_client_get_id(hapd->radius));
48e5b75505Sopenharmony_ci	if (msg == NULL) {
49e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
50e5b75505Sopenharmony_ci		return NULL;
51e5b75505Sopenharmony_ci	}
52e5b75505Sopenharmony_ci
53e5b75505Sopenharmony_ci	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
54e5b75505Sopenharmony_ci				       status_type)) {
55e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
56e5b75505Sopenharmony_ci		goto fail;
57e5b75505Sopenharmony_ci	}
58e5b75505Sopenharmony_ci
59e5b75505Sopenharmony_ci	if (sta) {
60e5b75505Sopenharmony_ci		if (!hostapd_config_get_radius_attr(
61e5b75505Sopenharmony_ci			    hapd->conf->radius_acct_req_attr,
62e5b75505Sopenharmony_ci			    RADIUS_ATTR_ACCT_AUTHENTIC) &&
63e5b75505Sopenharmony_ci		    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
64e5b75505Sopenharmony_ci					       hapd->conf->ieee802_1x ?
65e5b75505Sopenharmony_ci					       RADIUS_ACCT_AUTHENTIC_RADIUS :
66e5b75505Sopenharmony_ci					       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
67e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
68e5b75505Sopenharmony_ci			goto fail;
69e5b75505Sopenharmony_ci		}
70e5b75505Sopenharmony_ci
71e5b75505Sopenharmony_ci		/* Use 802.1X identity if available */
72e5b75505Sopenharmony_ci		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
73e5b75505Sopenharmony_ci
74e5b75505Sopenharmony_ci		/* Use RADIUS ACL identity if 802.1X provides no identity */
75e5b75505Sopenharmony_ci		if (!val && sta->identity) {
76e5b75505Sopenharmony_ci			val = (u8 *) sta->identity;
77e5b75505Sopenharmony_ci			len = os_strlen(sta->identity);
78e5b75505Sopenharmony_ci		}
79e5b75505Sopenharmony_ci
80e5b75505Sopenharmony_ci		/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
81e5b75505Sopenharmony_ci		 * identity */
82e5b75505Sopenharmony_ci		if (!val) {
83e5b75505Sopenharmony_ci			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
84e5b75505Sopenharmony_ci				    MAC2STR(sta->addr));
85e5b75505Sopenharmony_ci			val = (u8 *) buf;
86e5b75505Sopenharmony_ci			len = os_strlen(buf);
87e5b75505Sopenharmony_ci		}
88e5b75505Sopenharmony_ci
89e5b75505Sopenharmony_ci		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
90e5b75505Sopenharmony_ci					 len)) {
91e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add User-Name");
92e5b75505Sopenharmony_ci			goto fail;
93e5b75505Sopenharmony_ci		}
94e5b75505Sopenharmony_ci	}
95e5b75505Sopenharmony_ci
96e5b75505Sopenharmony_ci	if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
97e5b75505Sopenharmony_ci				   msg) < 0)
98e5b75505Sopenharmony_ci		goto fail;
99e5b75505Sopenharmony_ci
100e5b75505Sopenharmony_ci	if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
101e5b75505Sopenharmony_ci		goto fail;
102e5b75505Sopenharmony_ci
103e5b75505Sopenharmony_ci	if (sta) {
104e5b75505Sopenharmony_ci		for (i = 0; ; i++) {
105e5b75505Sopenharmony_ci			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
106e5b75505Sopenharmony_ci							  i);
107e5b75505Sopenharmony_ci			if (val == NULL)
108e5b75505Sopenharmony_ci				break;
109e5b75505Sopenharmony_ci
110e5b75505Sopenharmony_ci			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
111e5b75505Sopenharmony_ci						 val, len)) {
112e5b75505Sopenharmony_ci				wpa_printf(MSG_INFO, "Could not add Class");
113e5b75505Sopenharmony_ci				goto fail;
114e5b75505Sopenharmony_ci			}
115e5b75505Sopenharmony_ci		}
116e5b75505Sopenharmony_ci
117e5b75505Sopenharmony_ci		b = ieee802_1x_get_radius_cui(sta->eapol_sm);
118e5b75505Sopenharmony_ci		if (b &&
119e5b75505Sopenharmony_ci		    !radius_msg_add_attr(msg,
120e5b75505Sopenharmony_ci					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
121e5b75505Sopenharmony_ci					 wpabuf_head(b), wpabuf_len(b))) {
122e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "Could not add CUI");
123e5b75505Sopenharmony_ci			goto fail;
124e5b75505Sopenharmony_ci		}
125e5b75505Sopenharmony_ci
126e5b75505Sopenharmony_ci		if (!b && sta->radius_cui &&
127e5b75505Sopenharmony_ci		    !radius_msg_add_attr(msg,
128e5b75505Sopenharmony_ci					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
129e5b75505Sopenharmony_ci					 (u8 *) sta->radius_cui,
130e5b75505Sopenharmony_ci					 os_strlen(sta->radius_cui))) {
131e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
132e5b75505Sopenharmony_ci			goto fail;
133e5b75505Sopenharmony_ci		}
134e5b75505Sopenharmony_ci
135e5b75505Sopenharmony_ci		if (sta->ipaddr &&
136e5b75505Sopenharmony_ci		    !radius_msg_add_attr_int32(msg,
137e5b75505Sopenharmony_ci					       RADIUS_ATTR_FRAMED_IP_ADDRESS,
138e5b75505Sopenharmony_ci					       be_to_host32(sta->ipaddr))) {
139e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR,
140e5b75505Sopenharmony_ci				   "Could not add Framed-IP-Address");
141e5b75505Sopenharmony_ci			goto fail;
142e5b75505Sopenharmony_ci		}
143e5b75505Sopenharmony_ci	}
144e5b75505Sopenharmony_ci
145e5b75505Sopenharmony_ci	os_get_time(&now);
146e5b75505Sopenharmony_ci	if (now.sec > 1000000000 &&
147e5b75505Sopenharmony_ci	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
148e5b75505Sopenharmony_ci				       now.sec)) {
149e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
150e5b75505Sopenharmony_ci		goto fail;
151e5b75505Sopenharmony_ci	}
152e5b75505Sopenharmony_ci
153e5b75505Sopenharmony_ci	/*
154e5b75505Sopenharmony_ci	 * Add Acct-Delay-Time with zero value for the first transmission. This
155e5b75505Sopenharmony_ci	 * will be updated within radius_client.c when retransmitting the frame.
156e5b75505Sopenharmony_ci	 */
157e5b75505Sopenharmony_ci	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
158e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
159e5b75505Sopenharmony_ci		goto fail;
160e5b75505Sopenharmony_ci	}
161e5b75505Sopenharmony_ci
162e5b75505Sopenharmony_ci	return msg;
163e5b75505Sopenharmony_ci
164e5b75505Sopenharmony_ci fail:
165e5b75505Sopenharmony_ci	radius_msg_free(msg);
166e5b75505Sopenharmony_ci	return NULL;
167e5b75505Sopenharmony_ci}
168e5b75505Sopenharmony_ci
169e5b75505Sopenharmony_ci
170e5b75505Sopenharmony_cistatic int accounting_sta_update_stats(struct hostapd_data *hapd,
171e5b75505Sopenharmony_ci				       struct sta_info *sta,
172e5b75505Sopenharmony_ci				       struct hostap_sta_driver_data *data)
173e5b75505Sopenharmony_ci{
174e5b75505Sopenharmony_ci	if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
175e5b75505Sopenharmony_ci		return -1;
176e5b75505Sopenharmony_ci
177e5b75505Sopenharmony_ci	if (!data->bytes_64bit) {
178e5b75505Sopenharmony_ci		/* Extend 32-bit counters from the driver to 64-bit counters */
179e5b75505Sopenharmony_ci		if (sta->last_rx_bytes_lo > data->rx_bytes)
180e5b75505Sopenharmony_ci			sta->last_rx_bytes_hi++;
181e5b75505Sopenharmony_ci		sta->last_rx_bytes_lo = data->rx_bytes;
182e5b75505Sopenharmony_ci
183e5b75505Sopenharmony_ci		if (sta->last_tx_bytes_lo > data->tx_bytes)
184e5b75505Sopenharmony_ci			sta->last_tx_bytes_hi++;
185e5b75505Sopenharmony_ci		sta->last_tx_bytes_lo = data->tx_bytes;
186e5b75505Sopenharmony_ci	}
187e5b75505Sopenharmony_ci
188e5b75505Sopenharmony_ci	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
189e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_DEBUG,
190e5b75505Sopenharmony_ci		       "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
191e5b75505Sopenharmony_ci		       data->rx_bytes, sta->last_rx_bytes_hi,
192e5b75505Sopenharmony_ci		       sta->last_rx_bytes_lo,
193e5b75505Sopenharmony_ci		       data->tx_bytes, sta->last_tx_bytes_hi,
194e5b75505Sopenharmony_ci		       sta->last_tx_bytes_lo,
195e5b75505Sopenharmony_ci		       data->bytes_64bit);
196e5b75505Sopenharmony_ci
197e5b75505Sopenharmony_ci	return 0;
198e5b75505Sopenharmony_ci}
199e5b75505Sopenharmony_ci
200e5b75505Sopenharmony_ci
201e5b75505Sopenharmony_cistatic void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
202e5b75505Sopenharmony_ci{
203e5b75505Sopenharmony_ci	struct hostapd_data *hapd = eloop_ctx;
204e5b75505Sopenharmony_ci	struct sta_info *sta = timeout_ctx;
205e5b75505Sopenharmony_ci	int interval;
206e5b75505Sopenharmony_ci
207e5b75505Sopenharmony_ci	if (sta->acct_interim_interval) {
208e5b75505Sopenharmony_ci		accounting_sta_interim(hapd, sta);
209e5b75505Sopenharmony_ci		interval = sta->acct_interim_interval;
210e5b75505Sopenharmony_ci	} else {
211e5b75505Sopenharmony_ci		struct hostap_sta_driver_data data;
212e5b75505Sopenharmony_ci		accounting_sta_update_stats(hapd, sta, &data);
213e5b75505Sopenharmony_ci		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
214e5b75505Sopenharmony_ci	}
215e5b75505Sopenharmony_ci
216e5b75505Sopenharmony_ci	eloop_register_timeout(interval, 0, accounting_interim_update,
217e5b75505Sopenharmony_ci			       hapd, sta);
218e5b75505Sopenharmony_ci}
219e5b75505Sopenharmony_ci
220e5b75505Sopenharmony_ci
221e5b75505Sopenharmony_ci/**
222e5b75505Sopenharmony_ci * accounting_sta_start - Start STA accounting
223e5b75505Sopenharmony_ci * @hapd: hostapd BSS data
224e5b75505Sopenharmony_ci * @sta: The station
225e5b75505Sopenharmony_ci */
226e5b75505Sopenharmony_civoid accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
227e5b75505Sopenharmony_ci{
228e5b75505Sopenharmony_ci	struct radius_msg *msg;
229e5b75505Sopenharmony_ci	int interval;
230e5b75505Sopenharmony_ci
231e5b75505Sopenharmony_ci	if (sta->acct_session_started)
232e5b75505Sopenharmony_ci		return;
233e5b75505Sopenharmony_ci
234e5b75505Sopenharmony_ci	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
235e5b75505Sopenharmony_ci		       HOSTAPD_LEVEL_INFO,
236e5b75505Sopenharmony_ci		       "starting accounting session %016llX",
237e5b75505Sopenharmony_ci		       (unsigned long long) sta->acct_session_id);
238e5b75505Sopenharmony_ci
239e5b75505Sopenharmony_ci	os_get_reltime(&sta->acct_session_start);
240e5b75505Sopenharmony_ci	sta->last_rx_bytes_hi = 0;
241e5b75505Sopenharmony_ci	sta->last_rx_bytes_lo = 0;
242e5b75505Sopenharmony_ci	sta->last_tx_bytes_hi = 0;
243e5b75505Sopenharmony_ci	sta->last_tx_bytes_lo = 0;
244e5b75505Sopenharmony_ci	hostapd_drv_sta_clear_stats(hapd, sta->addr);
245e5b75505Sopenharmony_ci
246e5b75505Sopenharmony_ci	if (!hapd->conf->radius->acct_server)
247e5b75505Sopenharmony_ci		return;
248e5b75505Sopenharmony_ci
249e5b75505Sopenharmony_ci	if (sta->acct_interim_interval)
250e5b75505Sopenharmony_ci		interval = sta->acct_interim_interval;
251e5b75505Sopenharmony_ci	else
252e5b75505Sopenharmony_ci		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
253e5b75505Sopenharmony_ci	eloop_register_timeout(interval, 0, accounting_interim_update,
254e5b75505Sopenharmony_ci			       hapd, sta);
255e5b75505Sopenharmony_ci
256e5b75505Sopenharmony_ci	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
257e5b75505Sopenharmony_ci	if (msg &&
258e5b75505Sopenharmony_ci	    radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
259e5b75505Sopenharmony_ci		radius_msg_free(msg);
260e5b75505Sopenharmony_ci
261e5b75505Sopenharmony_ci	sta->acct_session_started = 1;
262e5b75505Sopenharmony_ci}
263e5b75505Sopenharmony_ci
264e5b75505Sopenharmony_ci
265e5b75505Sopenharmony_cistatic void accounting_sta_report(struct hostapd_data *hapd,
266e5b75505Sopenharmony_ci				  struct sta_info *sta, int stop)
267e5b75505Sopenharmony_ci{
268e5b75505Sopenharmony_ci	struct radius_msg *msg;
269e5b75505Sopenharmony_ci	int cause = sta->acct_terminate_cause;
270e5b75505Sopenharmony_ci	struct hostap_sta_driver_data data;
271e5b75505Sopenharmony_ci	struct os_reltime now_r, diff;
272e5b75505Sopenharmony_ci	u64 bytes;
273e5b75505Sopenharmony_ci
274e5b75505Sopenharmony_ci	if (!hapd->conf->radius->acct_server)
275e5b75505Sopenharmony_ci		return;
276e5b75505Sopenharmony_ci
277e5b75505Sopenharmony_ci	msg = accounting_msg(hapd, sta,
278e5b75505Sopenharmony_ci			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
279e5b75505Sopenharmony_ci			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
280e5b75505Sopenharmony_ci	if (!msg) {
281e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
282e5b75505Sopenharmony_ci		return;
283e5b75505Sopenharmony_ci	}
284e5b75505Sopenharmony_ci
285e5b75505Sopenharmony_ci	os_get_reltime(&now_r);
286e5b75505Sopenharmony_ci	os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
287e5b75505Sopenharmony_ci	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
288e5b75505Sopenharmony_ci				       diff.sec)) {
289e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
290e5b75505Sopenharmony_ci		goto fail;
291e5b75505Sopenharmony_ci	}
292e5b75505Sopenharmony_ci
293e5b75505Sopenharmony_ci	if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
294e5b75505Sopenharmony_ci		if (!radius_msg_add_attr_int32(msg,
295e5b75505Sopenharmony_ci					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
296e5b75505Sopenharmony_ci					       data.rx_packets)) {
297e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
298e5b75505Sopenharmony_ci			goto fail;
299e5b75505Sopenharmony_ci		}
300e5b75505Sopenharmony_ci		if (!radius_msg_add_attr_int32(msg,
301e5b75505Sopenharmony_ci					       RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
302e5b75505Sopenharmony_ci					       data.tx_packets)) {
303e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
304e5b75505Sopenharmony_ci			goto fail;
305e5b75505Sopenharmony_ci		}
306e5b75505Sopenharmony_ci		if (data.bytes_64bit)
307e5b75505Sopenharmony_ci			bytes = data.rx_bytes;
308e5b75505Sopenharmony_ci		else
309e5b75505Sopenharmony_ci			bytes = ((u64) sta->last_rx_bytes_hi << 32) |
310e5b75505Sopenharmony_ci				sta->last_rx_bytes_lo;
311e5b75505Sopenharmony_ci		if (!radius_msg_add_attr_int32(msg,
312e5b75505Sopenharmony_ci					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
313e5b75505Sopenharmony_ci					       (u32) bytes)) {
314e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
315e5b75505Sopenharmony_ci			goto fail;
316e5b75505Sopenharmony_ci		}
317e5b75505Sopenharmony_ci		if (!radius_msg_add_attr_int32(msg,
318e5b75505Sopenharmony_ci					       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
319e5b75505Sopenharmony_ci					       (u32) (bytes >> 32))) {
320e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
321e5b75505Sopenharmony_ci			goto fail;
322e5b75505Sopenharmony_ci		}
323e5b75505Sopenharmony_ci		if (data.bytes_64bit)
324e5b75505Sopenharmony_ci			bytes = data.tx_bytes;
325e5b75505Sopenharmony_ci		else
326e5b75505Sopenharmony_ci			bytes = ((u64) sta->last_tx_bytes_hi << 32) |
327e5b75505Sopenharmony_ci				sta->last_tx_bytes_lo;
328e5b75505Sopenharmony_ci		if (!radius_msg_add_attr_int32(msg,
329e5b75505Sopenharmony_ci					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
330e5b75505Sopenharmony_ci					       (u32) bytes)) {
331e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
332e5b75505Sopenharmony_ci			goto fail;
333e5b75505Sopenharmony_ci		}
334e5b75505Sopenharmony_ci		if (!radius_msg_add_attr_int32(msg,
335e5b75505Sopenharmony_ci					       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
336e5b75505Sopenharmony_ci					       (u32) (bytes >> 32))) {
337e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
338e5b75505Sopenharmony_ci			goto fail;
339e5b75505Sopenharmony_ci		}
340e5b75505Sopenharmony_ci	}
341e5b75505Sopenharmony_ci
342e5b75505Sopenharmony_ci	if (eloop_terminated())
343e5b75505Sopenharmony_ci		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
344e5b75505Sopenharmony_ci
345e5b75505Sopenharmony_ci	if (stop && cause &&
346e5b75505Sopenharmony_ci	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
347e5b75505Sopenharmony_ci				       cause)) {
348e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
349e5b75505Sopenharmony_ci		goto fail;
350e5b75505Sopenharmony_ci	}
351e5b75505Sopenharmony_ci
352e5b75505Sopenharmony_ci	if (radius_client_send(hapd->radius, msg,
353e5b75505Sopenharmony_ci			       stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
354e5b75505Sopenharmony_ci			       sta->addr) < 0)
355e5b75505Sopenharmony_ci		goto fail;
356e5b75505Sopenharmony_ci	return;
357e5b75505Sopenharmony_ci
358e5b75505Sopenharmony_ci fail:
359e5b75505Sopenharmony_ci	radius_msg_free(msg);
360e5b75505Sopenharmony_ci}
361e5b75505Sopenharmony_ci
362e5b75505Sopenharmony_ci
363e5b75505Sopenharmony_ci/**
364e5b75505Sopenharmony_ci * accounting_sta_interim - Send a interim STA accounting report
365e5b75505Sopenharmony_ci * @hapd: hostapd BSS data
366e5b75505Sopenharmony_ci * @sta: The station
367e5b75505Sopenharmony_ci */
368e5b75505Sopenharmony_cistatic void accounting_sta_interim(struct hostapd_data *hapd,
369e5b75505Sopenharmony_ci				   struct sta_info *sta)
370e5b75505Sopenharmony_ci{
371e5b75505Sopenharmony_ci	if (sta->acct_session_started)
372e5b75505Sopenharmony_ci		accounting_sta_report(hapd, sta, 0);
373e5b75505Sopenharmony_ci}
374e5b75505Sopenharmony_ci
375e5b75505Sopenharmony_ci
376e5b75505Sopenharmony_ci/**
377e5b75505Sopenharmony_ci * accounting_sta_stop - Stop STA accounting
378e5b75505Sopenharmony_ci * @hapd: hostapd BSS data
379e5b75505Sopenharmony_ci * @sta: The station
380e5b75505Sopenharmony_ci */
381e5b75505Sopenharmony_civoid accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
382e5b75505Sopenharmony_ci{
383e5b75505Sopenharmony_ci	if (sta->acct_session_started) {
384e5b75505Sopenharmony_ci		accounting_sta_report(hapd, sta, 1);
385e5b75505Sopenharmony_ci		eloop_cancel_timeout(accounting_interim_update, hapd, sta);
386e5b75505Sopenharmony_ci		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
387e5b75505Sopenharmony_ci			       HOSTAPD_LEVEL_INFO,
388e5b75505Sopenharmony_ci			       "stopped accounting session %016llX",
389e5b75505Sopenharmony_ci			       (unsigned long long) sta->acct_session_id);
390e5b75505Sopenharmony_ci		sta->acct_session_started = 0;
391e5b75505Sopenharmony_ci	}
392e5b75505Sopenharmony_ci}
393e5b75505Sopenharmony_ci
394e5b75505Sopenharmony_ci
395e5b75505Sopenharmony_ciint accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
396e5b75505Sopenharmony_ci{
397e5b75505Sopenharmony_ci	return radius_gen_session_id((u8 *) &sta->acct_session_id,
398e5b75505Sopenharmony_ci				     sizeof(sta->acct_session_id));
399e5b75505Sopenharmony_ci}
400e5b75505Sopenharmony_ci
401e5b75505Sopenharmony_ci
402e5b75505Sopenharmony_ci/**
403e5b75505Sopenharmony_ci * accounting_receive - Process the RADIUS frames from Accounting Server
404e5b75505Sopenharmony_ci * @msg: RADIUS response message
405e5b75505Sopenharmony_ci * @req: RADIUS request message
406e5b75505Sopenharmony_ci * @shared_secret: RADIUS shared secret
407e5b75505Sopenharmony_ci * @shared_secret_len: Length of shared_secret in octets
408e5b75505Sopenharmony_ci * @data: Context data (struct hostapd_data *)
409e5b75505Sopenharmony_ci * Returns: Processing status
410e5b75505Sopenharmony_ci */
411e5b75505Sopenharmony_cistatic RadiusRxResult
412e5b75505Sopenharmony_ciaccounting_receive(struct radius_msg *msg, struct radius_msg *req,
413e5b75505Sopenharmony_ci		   const u8 *shared_secret, size_t shared_secret_len,
414e5b75505Sopenharmony_ci		   void *data)
415e5b75505Sopenharmony_ci{
416e5b75505Sopenharmony_ci	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
417e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
418e5b75505Sopenharmony_ci		return RADIUS_RX_UNKNOWN;
419e5b75505Sopenharmony_ci	}
420e5b75505Sopenharmony_ci
421e5b75505Sopenharmony_ci	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
422e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
423e5b75505Sopenharmony_ci		return RADIUS_RX_INVALID_AUTHENTICATOR;
424e5b75505Sopenharmony_ci	}
425e5b75505Sopenharmony_ci
426e5b75505Sopenharmony_ci	return RADIUS_RX_PROCESSED;
427e5b75505Sopenharmony_ci}
428e5b75505Sopenharmony_ci
429e5b75505Sopenharmony_ci
430e5b75505Sopenharmony_cistatic void accounting_report_state(struct hostapd_data *hapd, int on)
431e5b75505Sopenharmony_ci{
432e5b75505Sopenharmony_ci	struct radius_msg *msg;
433e5b75505Sopenharmony_ci
434e5b75505Sopenharmony_ci	if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
435e5b75505Sopenharmony_ci		return;
436e5b75505Sopenharmony_ci
437e5b75505Sopenharmony_ci	/* Inform RADIUS server that accounting will start/stop so that the
438e5b75505Sopenharmony_ci	 * server can close old accounting sessions. */
439e5b75505Sopenharmony_ci	msg = accounting_msg(hapd, NULL,
440e5b75505Sopenharmony_ci			     on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
441e5b75505Sopenharmony_ci			     RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
442e5b75505Sopenharmony_ci	if (!msg)
443e5b75505Sopenharmony_ci		return;
444e5b75505Sopenharmony_ci
445e5b75505Sopenharmony_ci	if (hapd->acct_session_id) {
446e5b75505Sopenharmony_ci		char buf[20];
447e5b75505Sopenharmony_ci
448e5b75505Sopenharmony_ci		os_snprintf(buf, sizeof(buf), "%016llX",
449e5b75505Sopenharmony_ci			    (unsigned long long) hapd->acct_session_id);
450e5b75505Sopenharmony_ci		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
451e5b75505Sopenharmony_ci					 (u8 *) buf, os_strlen(buf)))
452e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
453e5b75505Sopenharmony_ci	}
454e5b75505Sopenharmony_ci
455e5b75505Sopenharmony_ci	if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
456e5b75505Sopenharmony_ci		radius_msg_free(msg);
457e5b75505Sopenharmony_ci}
458e5b75505Sopenharmony_ci
459e5b75505Sopenharmony_ci
460e5b75505Sopenharmony_cistatic void accounting_interim_error_cb(const u8 *addr, void *ctx)
461e5b75505Sopenharmony_ci{
462e5b75505Sopenharmony_ci	struct hostapd_data *hapd = ctx;
463e5b75505Sopenharmony_ci	struct sta_info *sta;
464e5b75505Sopenharmony_ci	unsigned int i, wait_time;
465e5b75505Sopenharmony_ci	int res;
466e5b75505Sopenharmony_ci
467e5b75505Sopenharmony_ci	sta = ap_get_sta(hapd, addr);
468e5b75505Sopenharmony_ci	if (!sta)
469e5b75505Sopenharmony_ci		return;
470e5b75505Sopenharmony_ci	sta->acct_interim_errors++;
471e5b75505Sopenharmony_ci	if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
472e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
473e5b75505Sopenharmony_ci			   "Interim RADIUS accounting update failed for " MACSTR
474e5b75505Sopenharmony_ci			   " - too many errors, abandon this interim accounting update",
475e5b75505Sopenharmony_ci			   MAC2STR(addr));
476e5b75505Sopenharmony_ci		sta->acct_interim_errors = 0;
477e5b75505Sopenharmony_ci		/* Next update will be tried after normal update interval */
478e5b75505Sopenharmony_ci		return;
479e5b75505Sopenharmony_ci	}
480e5b75505Sopenharmony_ci
481e5b75505Sopenharmony_ci	/*
482e5b75505Sopenharmony_ci	 * Use a shorter update interval as an improved retransmission mechanism
483e5b75505Sopenharmony_ci	 * for failed interim accounting updates. This allows the statistics to
484e5b75505Sopenharmony_ci	 * be updated for each retransmission.
485e5b75505Sopenharmony_ci	 *
486e5b75505Sopenharmony_ci	 * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
487e5b75505Sopenharmony_ci	 * Schedule the first retry attempt immediately and every following one
488e5b75505Sopenharmony_ci	 * with exponential backoff.
489e5b75505Sopenharmony_ci	 */
490e5b75505Sopenharmony_ci	if (sta->acct_interim_errors == 1) {
491e5b75505Sopenharmony_ci		wait_time = 0;
492e5b75505Sopenharmony_ci	} else {
493e5b75505Sopenharmony_ci		wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
494e5b75505Sopenharmony_ci		for (i = 1; i < sta->acct_interim_errors; i++)
495e5b75505Sopenharmony_ci			wait_time *= 2;
496e5b75505Sopenharmony_ci	}
497e5b75505Sopenharmony_ci	res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
498e5b75505Sopenharmony_ci				    hapd, sta);
499e5b75505Sopenharmony_ci	if (res == 1)
500e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
501e5b75505Sopenharmony_ci			   "Interim RADIUS accounting update failed for " MACSTR
502e5b75505Sopenharmony_ci			   " (error count: %u) - schedule next update in %u seconds",
503e5b75505Sopenharmony_ci			   MAC2STR(addr), sta->acct_interim_errors, wait_time);
504e5b75505Sopenharmony_ci	else if (res == 0)
505e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
506e5b75505Sopenharmony_ci			   "Interim RADIUS accounting update failed for " MACSTR
507e5b75505Sopenharmony_ci			   " (error count: %u)", MAC2STR(addr),
508e5b75505Sopenharmony_ci			   sta->acct_interim_errors);
509e5b75505Sopenharmony_ci	else
510e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
511e5b75505Sopenharmony_ci			   "Interim RADIUS accounting update failed for " MACSTR
512e5b75505Sopenharmony_ci			   " (error count: %u) - no timer found", MAC2STR(addr),
513e5b75505Sopenharmony_ci			   sta->acct_interim_errors);
514e5b75505Sopenharmony_ci}
515e5b75505Sopenharmony_ci
516e5b75505Sopenharmony_ci
517e5b75505Sopenharmony_ci/**
518e5b75505Sopenharmony_ci * accounting_init: Initialize accounting
519e5b75505Sopenharmony_ci * @hapd: hostapd BSS data
520e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure
521e5b75505Sopenharmony_ci */
522e5b75505Sopenharmony_ciint accounting_init(struct hostapd_data *hapd)
523e5b75505Sopenharmony_ci{
524e5b75505Sopenharmony_ci	if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
525e5b75505Sopenharmony_ci				  sizeof(hapd->acct_session_id)) < 0)
526e5b75505Sopenharmony_ci		return -1;
527e5b75505Sopenharmony_ci
528e5b75505Sopenharmony_ci	if (radius_client_register(hapd->radius, RADIUS_ACCT,
529e5b75505Sopenharmony_ci				   accounting_receive, hapd))
530e5b75505Sopenharmony_ci		return -1;
531e5b75505Sopenharmony_ci	radius_client_set_interim_error_cb(hapd->radius,
532e5b75505Sopenharmony_ci					   accounting_interim_error_cb, hapd);
533e5b75505Sopenharmony_ci
534e5b75505Sopenharmony_ci	accounting_report_state(hapd, 1);
535e5b75505Sopenharmony_ci
536e5b75505Sopenharmony_ci	return 0;
537e5b75505Sopenharmony_ci}
538e5b75505Sopenharmony_ci
539e5b75505Sopenharmony_ci
540e5b75505Sopenharmony_ci/**
541e5b75505Sopenharmony_ci * accounting_deinit: Deinitialize accounting
542e5b75505Sopenharmony_ci * @hapd: hostapd BSS data
543e5b75505Sopenharmony_ci */
544e5b75505Sopenharmony_civoid accounting_deinit(struct hostapd_data *hapd)
545e5b75505Sopenharmony_ci{
546e5b75505Sopenharmony_ci	accounting_report_state(hapd, 0);
547e5b75505Sopenharmony_ci}
548