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