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