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