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