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