xref: /freebsd/contrib/wpa/src/ap/accounting.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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 
30 static void accounting_sta_interim(struct hostapd_data *hapd,
31 				   struct sta_info *sta);
32 
33 
34 static 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 
170 static 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 
201 static 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  */
226 void 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 
265 static 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  */
368 static 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  */
381 void 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 
395 int 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  */
411 static RadiusRxResult
412 accounting_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 
430 static 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 
460 static 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  */
522 int 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  */
544 void accounting_deinit(struct hostapd_data *hapd)
545 {
546 	accounting_report_state(hapd, 0);
547 }
548