2 * hostapd / RADIUS Accounting
3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
15 #include "utils/includes.h"
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"
23 #include "ieee802_1x.h"
24 #include "ap_config.h"
26 #include "accounting.h"
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
34 static void accounting_sta_get_id(struct hostapd_data
*hapd
,
35 struct sta_info
*sta
);
38 static struct radius_msg
* accounting_msg(struct hostapd_data
*hapd
,
42 struct radius_msg
*msg
;
48 msg
= radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST
,
49 radius_client_get_id(hapd
->radius
));
51 printf("Could not create net RADIUS packet\n");
56 radius_msg_make_authenticator(msg
, (u8
*) sta
, sizeof(*sta
));
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");
66 radius_msg_make_authenticator(msg
, (u8
*) hapd
, sizeof(*hapd
));
69 if (!radius_msg_add_attr_int32(msg
, RADIUS_ATTR_ACCT_STATUS_TYPE
,
71 printf("Could not add Acct-Status-Type\n");
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");
84 val
= ieee802_1x_get_identity(sta
->eapol_sm
, &len
);
86 os_snprintf(buf
, sizeof(buf
), RADIUS_ADDR_FORMAT
,
92 if (!radius_msg_add_attr(msg
, RADIUS_ATTR_USER_NAME
, val
,
94 printf("Could not add User-Name\n");
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");
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");
113 #endif /* CONFIG_IPV6 */
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");
124 !radius_msg_add_attr_int32(msg
, RADIUS_ATTR_NAS_PORT
, sta
->aid
)) {
125 printf("Could not add NAS-Port\n");
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");
138 os_snprintf(buf
, sizeof(buf
), RADIUS_802_1X_ADDR_FORMAT
,
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");
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");
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");
164 val
= ieee802_1x_get_radius_class(sta
->eapol_sm
, &len
,
169 if (!radius_msg_add_attr(msg
, RADIUS_ATTR_CLASS
,
171 printf("Could not add Class\n");
180 radius_msg_free(msg
);
185 static int accounting_sta_update_stats(struct hostapd_data
*hapd
,
186 struct sta_info
*sta
,
187 struct hostap_sta_driver_data
*data
)
189 if (hapd
->drv
.read_sta_data(hapd
, data
, sta
->addr
))
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
;
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
);
210 static void accounting_interim_update(void *eloop_ctx
, void *timeout_ctx
)
212 struct hostapd_data
*hapd
= eloop_ctx
;
213 struct sta_info
*sta
= timeout_ctx
;
216 if (sta
->acct_interim_interval
) {
217 accounting_sta_interim(hapd
, sta
);
218 interval
= sta
->acct_interim_interval
;
220 struct hostap_sta_driver_data data
;
221 accounting_sta_update_stats(hapd
, sta
, &data
);
222 interval
= ACCT_DEFAULT_UPDATE_INTERVAL
;
225 eloop_register_timeout(interval
, 0, accounting_interim_update
,
231 * accounting_sta_start - Start STA accounting
232 * @hapd: hostapd BSS data
235 void accounting_sta_start(struct hostapd_data
*hapd
, struct sta_info
*sta
)
237 struct radius_msg
*msg
;
240 if (sta
->acct_session_started
)
243 accounting_sta_get_id(hapd
, sta
);
244 hostapd_logger(hapd
, sta
->addr
, HOSTAPD_MODULE_RADIUS
,
246 "starting accounting session %08X-%08X",
247 sta
->acct_session_id_hi
, sta
->acct_session_id_lo
);
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
);
254 if (!hapd
->conf
->radius
->acct_server
)
257 if (sta
->acct_interim_interval
)
258 interval
= sta
->acct_interim_interval
;
260 interval
= ACCT_DEFAULT_UPDATE_INTERVAL
;
261 eloop_register_timeout(interval
, 0, accounting_interim_update
,
264 msg
= accounting_msg(hapd
, sta
, RADIUS_ACCT_STATUS_TYPE_START
);
266 radius_client_send(hapd
->radius
, msg
, RADIUS_ACCT
, sta
->addr
);
268 sta
->acct_session_started
= 1;
272 static void accounting_sta_report(struct hostapd_data
*hapd
,
273 struct sta_info
*sta
, int stop
)
275 struct radius_msg
*msg
;
276 int cause
= sta
->acct_terminate_cause
;
277 struct hostap_sta_driver_data data
;
280 if (!hapd
->conf
->radius
->acct_server
)
283 msg
= accounting_msg(hapd
, sta
,
284 stop
? RADIUS_ACCT_STATUS_TYPE_STOP
:
285 RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE
);
287 printf("Could not create RADIUS Accounting message\n");
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");
297 if (accounting_sta_update_stats(hapd
, sta
, &data
) == 0) {
298 if (!radius_msg_add_attr_int32(msg
,
299 RADIUS_ATTR_ACCT_INPUT_PACKETS
,
301 printf("Could not add Acct-Input-Packets\n");
304 if (!radius_msg_add_attr_int32(msg
,
305 RADIUS_ATTR_ACCT_OUTPUT_PACKETS
,
307 printf("Could not add Acct-Output-Packets\n");
310 if (!radius_msg_add_attr_int32(msg
,
311 RADIUS_ATTR_ACCT_INPUT_OCTETS
,
313 printf("Could not add Acct-Input-Octets\n");
316 gigawords
= sta
->acct_input_gigawords
;
318 gigawords
+= data
.rx_bytes
>> 32;
321 !radius_msg_add_attr_int32(
322 msg
, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS
,
324 printf("Could not add Acct-Input-Gigawords\n");
327 if (!radius_msg_add_attr_int32(msg
,
328 RADIUS_ATTR_ACCT_OUTPUT_OCTETS
,
330 printf("Could not add Acct-Output-Octets\n");
333 gigawords
= sta
->acct_output_gigawords
;
335 gigawords
+= data
.tx_bytes
>> 32;
338 !radius_msg_add_attr_int32(
339 msg
, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS
,
341 printf("Could not add Acct-Output-Gigawords\n");
346 if (!radius_msg_add_attr_int32(msg
, RADIUS_ATTR_EVENT_TIMESTAMP
,
348 printf("Could not add Event-Timestamp\n");
352 if (eloop_terminated())
353 cause
= RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT
;
356 !radius_msg_add_attr_int32(msg
, RADIUS_ATTR_ACCT_TERMINATE_CAUSE
,
358 printf("Could not add Acct-Terminate-Cause\n");
362 radius_client_send(hapd
->radius
, msg
,
363 stop
? RADIUS_ACCT
: RADIUS_ACCT_INTERIM
,
368 radius_msg_free(msg
);
373 * accounting_sta_interim - Send a interim STA accounting report
374 * @hapd: hostapd BSS data
377 void accounting_sta_interim(struct hostapd_data
*hapd
, struct sta_info
*sta
)
379 if (sta
->acct_session_started
)
380 accounting_sta_report(hapd
, sta
, 0);
385 * accounting_sta_stop - Stop STA accounting
386 * @hapd: hostapd BSS data
389 void accounting_sta_stop(struct hostapd_data
*hapd
, struct sta_info
*sta
)
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
,
396 "stopped accounting session %08X-%08X",
397 sta
->acct_session_id_hi
,
398 sta
->acct_session_id_lo
);
399 sta
->acct_session_started
= 0;
404 static void accounting_sta_get_id(struct hostapd_data
*hapd
,
405 struct sta_info
*sta
)
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
++;
411 sta
->acct_session_id_hi
= hapd
->acct_session_id_hi
;
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
424 static RadiusRxResult
425 accounting_receive(struct radius_msg
*msg
, struct radius_msg
*req
,
426 const u8
*shared_secret
, size_t shared_secret_len
,
429 if (radius_msg_get_hdr(msg
)->code
!= RADIUS_CODE_ACCOUNTING_RESPONSE
) {
430 printf("Unknown RADIUS message code\n");
431 return RADIUS_RX_UNKNOWN
;
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
;
440 return RADIUS_RX_PROCESSED
;
444 static void accounting_report_state(struct hostapd_data
*hapd
, int on
)
446 struct radius_msg
*msg
;
448 if (!hapd
->conf
->radius
->acct_server
|| hapd
->radius
== NULL
)
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
);
459 if (!radius_msg_add_attr_int32(msg
, RADIUS_ATTR_ACCT_TERMINATE_CAUSE
,
460 RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT
))
462 printf("Could not add Acct-Terminate-Cause\n");
463 radius_msg_free(msg
);
467 radius_client_send(hapd
->radius
, msg
, RADIUS_ACCT
, NULL
);
472 * accounting_init: Initialize accounting
473 * @hapd: hostapd BSS data
474 * Returns: 0 on success, -1 on failure
476 int accounting_init(struct hostapd_data
*hapd
)
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
);
482 if (radius_client_register(hapd
->radius
, RADIUS_ACCT
,
483 accounting_receive
, hapd
))
486 accounting_report_state(hapd
, 1);
493 * accounting_deinit: Deinitilize accounting
494 * @hapd: hostapd BSS data
496 void accounting_deinit(struct hostapd_data
*hapd
)
498 accounting_report_state(hapd
, 0);