xref: /freebsd/contrib/wpa/src/ap/accounting.c (revision d184218c)
1 /*
2  * hostapd / RADIUS Accounting
3  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14 
15 #include "utils/includes.h"
16 
17 #include "utils/common.h"
18 #include "utils/eloop.h"
19 #include "drivers/driver.h"
20 #include "radius/radius.h"
21 #include "radius/radius_client.h"
22 #include "hostapd.h"
23 #include "ieee802_1x.h"
24 #include "ap_config.h"
25 #include "sta_info.h"
26 #include "accounting.h"
27 
28 
29 /* Default interval in seconds for polling TX/RX octets from the driver if
30  * STA is not using interim accounting. This detects wrap arounds for
31  * input/output octets and updates Acct-{Input,Output}-Gigawords. */
32 #define ACCT_DEFAULT_UPDATE_INTERVAL 300
33 
34 static void accounting_sta_get_id(struct hostapd_data *hapd,
35 				  struct sta_info *sta);
36 
37 
38 static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
39 					  struct sta_info *sta,
40 					  int status_type)
41 {
42 	struct radius_msg *msg;
43 	char buf[128];
44 	u8 *val;
45 	size_t len;
46 	int i;
47 
48 	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
49 			     radius_client_get_id(hapd->radius));
50 	if (msg == NULL) {
51 		printf("Could not create net RADIUS packet\n");
52 		return NULL;
53 	}
54 
55 	if (sta) {
56 		radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
57 
58 		os_snprintf(buf, sizeof(buf), "%08X-%08X",
59 			    sta->acct_session_id_hi, sta->acct_session_id_lo);
60 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
61 					 (u8 *) buf, os_strlen(buf))) {
62 			printf("Could not add Acct-Session-Id\n");
63 			goto fail;
64 		}
65 	} else {
66 		radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
67 	}
68 
69 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
70 				       status_type)) {
71 		printf("Could not add Acct-Status-Type\n");
72 		goto fail;
73 	}
74 
75 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
76 				       hapd->conf->ieee802_1x ?
77 				       RADIUS_ACCT_AUTHENTIC_RADIUS :
78 				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
79 		printf("Could not add Acct-Authentic\n");
80 		goto fail;
81 	}
82 
83 	if (sta) {
84 		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
85 		if (!val) {
86 			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
87 				    MAC2STR(sta->addr));
88 			val = (u8 *) buf;
89 			len = os_strlen(buf);
90 		}
91 
92 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
93 					 len)) {
94 			printf("Could not add User-Name\n");
95 			goto fail;
96 		}
97 	}
98 
99 	if (hapd->conf->own_ip_addr.af == AF_INET &&
100 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
101 				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
102 		printf("Could not add NAS-IP-Address\n");
103 		goto fail;
104 	}
105 
106 #ifdef CONFIG_IPV6
107 	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
108 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
109 				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
110 		printf("Could not add NAS-IPv6-Address\n");
111 		goto fail;
112 	}
113 #endif /* CONFIG_IPV6 */
114 
115 	if (hapd->conf->nas_identifier &&
116 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
117 				 (u8 *) hapd->conf->nas_identifier,
118 				 os_strlen(hapd->conf->nas_identifier))) {
119 		printf("Could not add NAS-Identifier\n");
120 		goto fail;
121 	}
122 
123 	if (sta &&
124 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
125 		printf("Could not add NAS-Port\n");
126 		goto fail;
127 	}
128 
129 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
130 		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
131 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
132 				 (u8 *) buf, os_strlen(buf))) {
133 		printf("Could not add Called-Station-Id\n");
134 		goto fail;
135 	}
136 
137 	if (sta) {
138 		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
139 			    MAC2STR(sta->addr));
140 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
141 					 (u8 *) buf, os_strlen(buf))) {
142 			printf("Could not add Calling-Station-Id\n");
143 			goto fail;
144 		}
145 
146 		if (!radius_msg_add_attr_int32(
147 			    msg, RADIUS_ATTR_NAS_PORT_TYPE,
148 			    RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
149 			printf("Could not add NAS-Port-Type\n");
150 			goto fail;
151 		}
152 
153 		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
154 			    radius_sta_rate(hapd, sta) / 2,
155 			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
156 			    radius_mode_txt(hapd));
157 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
158 					 (u8 *) buf, os_strlen(buf))) {
159 			printf("Could not add Connect-Info\n");
160 			goto fail;
161 		}
162 
163 		for (i = 0; ; i++) {
164 			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
165 							  i);
166 			if (val == NULL)
167 				break;
168 
169 			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
170 						 val, len)) {
171 				printf("Could not add Class\n");
172 				goto fail;
173 			}
174 		}
175 	}
176 
177 	return msg;
178 
179  fail:
180 	radius_msg_free(msg);
181 	return NULL;
182 }
183 
184 
185 static int accounting_sta_update_stats(struct hostapd_data *hapd,
186 				       struct sta_info *sta,
187 				       struct hostap_sta_driver_data *data)
188 {
189 	if (hapd->drv.read_sta_data(hapd, data, sta->addr))
190 		return -1;
191 
192 	if (sta->last_rx_bytes > data->rx_bytes)
193 		sta->acct_input_gigawords++;
194 	if (sta->last_tx_bytes > data->tx_bytes)
195 		sta->acct_output_gigawords++;
196 	sta->last_rx_bytes = data->rx_bytes;
197 	sta->last_tx_bytes = data->tx_bytes;
198 
199 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
200 		       HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
201 		       "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
202 		       "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
203 		       sta->last_rx_bytes, sta->acct_input_gigawords,
204 		       sta->last_tx_bytes, sta->acct_output_gigawords);
205 
206 	return 0;
207 }
208 
209 
210 static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
211 {
212 	struct hostapd_data *hapd = eloop_ctx;
213 	struct sta_info *sta = timeout_ctx;
214 	int interval;
215 
216 	if (sta->acct_interim_interval) {
217 		accounting_sta_interim(hapd, sta);
218 		interval = sta->acct_interim_interval;
219 	} else {
220 		struct hostap_sta_driver_data data;
221 		accounting_sta_update_stats(hapd, sta, &data);
222 		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
223 	}
224 
225 	eloop_register_timeout(interval, 0, accounting_interim_update,
226 			       hapd, sta);
227 }
228 
229 
230 /**
231  * accounting_sta_start - Start STA accounting
232  * @hapd: hostapd BSS data
233  * @sta: The station
234  */
235 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
236 {
237 	struct radius_msg *msg;
238 	int interval;
239 
240 	if (sta->acct_session_started)
241 		return;
242 
243 	accounting_sta_get_id(hapd, sta);
244 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
245 		       HOSTAPD_LEVEL_INFO,
246 		       "starting accounting session %08X-%08X",
247 		       sta->acct_session_id_hi, sta->acct_session_id_lo);
248 
249 	time(&sta->acct_session_start);
250 	sta->last_rx_bytes = sta->last_tx_bytes = 0;
251 	sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
252 	hapd->drv.sta_clear_stats(hapd, sta->addr);
253 
254 	if (!hapd->conf->radius->acct_server)
255 		return;
256 
257 	if (sta->acct_interim_interval)
258 		interval = sta->acct_interim_interval;
259 	else
260 		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
261 	eloop_register_timeout(interval, 0, accounting_interim_update,
262 			       hapd, sta);
263 
264 	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
265 	if (msg)
266 		radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
267 
268 	sta->acct_session_started = 1;
269 }
270 
271 
272 static void accounting_sta_report(struct hostapd_data *hapd,
273 				  struct sta_info *sta, int stop)
274 {
275 	struct radius_msg *msg;
276 	int cause = sta->acct_terminate_cause;
277 	struct hostap_sta_driver_data data;
278 	u32 gigawords;
279 
280 	if (!hapd->conf->radius->acct_server)
281 		return;
282 
283 	msg = accounting_msg(hapd, sta,
284 			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
285 			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
286 	if (!msg) {
287 		printf("Could not create RADIUS Accounting message\n");
288 		return;
289 	}
290 
291 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
292 				       time(NULL) - sta->acct_session_start)) {
293 		printf("Could not add Acct-Session-Time\n");
294 		goto fail;
295 	}
296 
297 	if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
298 		if (!radius_msg_add_attr_int32(msg,
299 					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
300 					       data.rx_packets)) {
301 			printf("Could not add Acct-Input-Packets\n");
302 			goto fail;
303 		}
304 		if (!radius_msg_add_attr_int32(msg,
305 					       RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
306 					       data.tx_packets)) {
307 			printf("Could not add Acct-Output-Packets\n");
308 			goto fail;
309 		}
310 		if (!radius_msg_add_attr_int32(msg,
311 					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
312 					       data.rx_bytes)) {
313 			printf("Could not add Acct-Input-Octets\n");
314 			goto fail;
315 		}
316 		gigawords = sta->acct_input_gigawords;
317 #if __WORDSIZE == 64
318 		gigawords += data.rx_bytes >> 32;
319 #endif
320 		if (gigawords &&
321 		    !radius_msg_add_attr_int32(
322 			    msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
323 			    gigawords)) {
324 			printf("Could not add Acct-Input-Gigawords\n");
325 			goto fail;
326 		}
327 		if (!radius_msg_add_attr_int32(msg,
328 					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
329 					       data.tx_bytes)) {
330 			printf("Could not add Acct-Output-Octets\n");
331 			goto fail;
332 		}
333 		gigawords = sta->acct_output_gigawords;
334 #if __WORDSIZE == 64
335 		gigawords += data.tx_bytes >> 32;
336 #endif
337 		if (gigawords &&
338 		    !radius_msg_add_attr_int32(
339 			    msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
340 			    gigawords)) {
341 			printf("Could not add Acct-Output-Gigawords\n");
342 			goto fail;
343 		}
344 	}
345 
346 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
347 				       time(NULL))) {
348 		printf("Could not add Event-Timestamp\n");
349 		goto fail;
350 	}
351 
352 	if (eloop_terminated())
353 		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
354 
355 	if (stop && cause &&
356 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
357 				       cause)) {
358 		printf("Could not add Acct-Terminate-Cause\n");
359 		goto fail;
360 	}
361 
362 	radius_client_send(hapd->radius, msg,
363 			   stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
364 			   sta->addr);
365 	return;
366 
367  fail:
368 	radius_msg_free(msg);
369 }
370 
371 
372 /**
373  * accounting_sta_interim - Send a interim STA accounting report
374  * @hapd: hostapd BSS data
375  * @sta: The station
376  */
377 void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
378 {
379 	if (sta->acct_session_started)
380 		accounting_sta_report(hapd, sta, 0);
381 }
382 
383 
384 /**
385  * accounting_sta_stop - Stop STA accounting
386  * @hapd: hostapd BSS data
387  * @sta: The station
388  */
389 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
390 {
391 	if (sta->acct_session_started) {
392 		accounting_sta_report(hapd, sta, 1);
393 		eloop_cancel_timeout(accounting_interim_update, hapd, sta);
394 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
395 			       HOSTAPD_LEVEL_INFO,
396 			       "stopped accounting session %08X-%08X",
397 			       sta->acct_session_id_hi,
398 			       sta->acct_session_id_lo);
399 		sta->acct_session_started = 0;
400 	}
401 }
402 
403 
404 static void accounting_sta_get_id(struct hostapd_data *hapd,
405 				  struct sta_info *sta)
406 {
407 	sta->acct_session_id_lo = hapd->acct_session_id_lo++;
408 	if (hapd->acct_session_id_lo == 0) {
409 		hapd->acct_session_id_hi++;
410 	}
411 	sta->acct_session_id_hi = hapd->acct_session_id_hi;
412 }
413 
414 
415 /**
416  * accounting_receive - Process the RADIUS frames from Accounting Server
417  * @msg: RADIUS response message
418  * @req: RADIUS request message
419  * @shared_secret: RADIUS shared secret
420  * @shared_secret_len: Length of shared_secret in octets
421  * @data: Context data (struct hostapd_data *)
422  * Returns: Processing status
423  */
424 static RadiusRxResult
425 accounting_receive(struct radius_msg *msg, struct radius_msg *req,
426 		   const u8 *shared_secret, size_t shared_secret_len,
427 		   void *data)
428 {
429 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
430 		printf("Unknown RADIUS message code\n");
431 		return RADIUS_RX_UNKNOWN;
432 	}
433 
434 	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
435 		printf("Incoming RADIUS packet did not have correct "
436 		       "Authenticator - dropped\n");
437 		return RADIUS_RX_INVALID_AUTHENTICATOR;
438 	}
439 
440 	return RADIUS_RX_PROCESSED;
441 }
442 
443 
444 static void accounting_report_state(struct hostapd_data *hapd, int on)
445 {
446 	struct radius_msg *msg;
447 
448 	if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
449 		return;
450 
451 	/* Inform RADIUS server that accounting will start/stop so that the
452 	 * server can close old accounting sessions. */
453 	msg = accounting_msg(hapd, NULL,
454 			     on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
455 			     RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
456 	if (!msg)
457 		return;
458 
459 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
460 				       RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
461 	{
462 		printf("Could not add Acct-Terminate-Cause\n");
463 		radius_msg_free(msg);
464 		return;
465 	}
466 
467 	radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
468 }
469 
470 
471 /**
472  * accounting_init: Initialize accounting
473  * @hapd: hostapd BSS data
474  * Returns: 0 on success, -1 on failure
475  */
476 int accounting_init(struct hostapd_data *hapd)
477 {
478 	/* Acct-Session-Id should be unique over reboots. If reliable clock is
479 	 * not available, this could be replaced with reboot counter, etc. */
480 	hapd->acct_session_id_hi = time(NULL);
481 
482 	if (radius_client_register(hapd->radius, RADIUS_ACCT,
483 				   accounting_receive, hapd))
484 		return -1;
485 
486 	accounting_report_state(hapd, 1);
487 
488 	return 0;
489 }
490 
491 
492 /**
493  * accounting_deinit: Deinitilize accounting
494  * @hapd: hostapd BSS data
495  */
496 void accounting_deinit(struct hostapd_data *hapd)
497 {
498 	accounting_report_state(hapd, 0);
499 }
500