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