accounting.c revision c5ec7f57ead87efa365800228aa0b09a12d9e6c4
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 29static void accounting_sta_get_id(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 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 180static 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 205static 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 */ 230void 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 269static 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 */ 376void 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 */ 388void 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 403static 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 */ 423static RadiusRxResult 424accounting_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 443static 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 */ 475int 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 */ 498void accounting_deinit(struct hostapd_data *hapd) 499{ 500 accounting_report_state(hapd, 0); 501} 502