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 "accounting.h" 27 28 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 33 34 static void accounting_sta_get_id(struct hostapd_data *hapd, 35 struct sta_info *sta); 36 37 38 static struct radius_msg * accounting_msg(struct hostapd_data *hapd, 39 struct sta_info *sta, 40 int status_type) 41 { 42 struct radius_msg *msg; 43 char buf[128]; 44 u8 *val; 45 size_t len; 46 int i; 47 48 msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, 49 radius_client_get_id(hapd->radius)); 50 if (msg == NULL) { 51 printf("Could not create net RADIUS packet\n"); 52 return NULL; 53 } 54 55 if (sta) { 56 radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); 57 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"); 63 goto fail; 64 } 65 } else { 66 radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); 67 } 68 69 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, 70 status_type)) { 71 printf("Could not add Acct-Status-Type\n"); 72 goto fail; 73 } 74 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"); 80 goto fail; 81 } 82 83 if (sta) { 84 val = ieee802_1x_get_identity(sta->eapol_sm, &len); 85 if (!val) { 86 os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, 87 MAC2STR(sta->addr)); 88 val = (u8 *) buf; 89 len = os_strlen(buf); 90 } 91 92 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, 93 len)) { 94 printf("Could not add User-Name\n"); 95 goto fail; 96 } 97 } 98 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"); 103 goto fail; 104 } 105 106 #ifdef CONFIG_IPV6 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"); 111 goto fail; 112 } 113 #endif /* CONFIG_IPV6 */ 114 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"); 120 goto fail; 121 } 122 123 if (sta && 124 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { 125 printf("Could not add NAS-Port\n"); 126 goto fail; 127 } 128 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"); 134 goto fail; 135 } 136 137 if (sta) { 138 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, 139 MAC2STR(sta->addr)); 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"); 143 goto fail; 144 } 145 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"); 150 goto fail; 151 } 152 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"); 160 goto fail; 161 } 162 163 for (i = 0; ; i++) { 164 val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, 165 i); 166 if (val == NULL) 167 break; 168 169 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, 170 val, len)) { 171 printf("Could not add Class\n"); 172 goto fail; 173 } 174 } 175 } 176 177 return msg; 178 179 fail: 180 radius_msg_free(msg); 181 return NULL; 182 } 183 184 185 static int accounting_sta_update_stats(struct hostapd_data *hapd, 186 struct sta_info *sta, 187 struct hostap_sta_driver_data *data) 188 { 189 if (hapd->drv.read_sta_data(hapd, data, sta->addr)) 190 return -1; 191 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; 198 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); 205 206 return 0; 207 } 208 209 210 static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) 211 { 212 struct hostapd_data *hapd = eloop_ctx; 213 struct sta_info *sta = timeout_ctx; 214 int interval; 215 216 if (sta->acct_interim_interval) { 217 accounting_sta_interim(hapd, sta); 218 interval = sta->acct_interim_interval; 219 } else { 220 struct hostap_sta_driver_data data; 221 accounting_sta_update_stats(hapd, sta, &data); 222 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 223 } 224 225 eloop_register_timeout(interval, 0, accounting_interim_update, 226 hapd, sta); 227 } 228 229 230 /** 231 * accounting_sta_start - Start STA accounting 232 * @hapd: hostapd BSS data 233 * @sta: The station 234 */ 235 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) 236 { 237 struct radius_msg *msg; 238 int interval; 239 240 if (sta->acct_session_started) 241 return; 242 243 accounting_sta_get_id(hapd, sta); 244 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 245 HOSTAPD_LEVEL_INFO, 246 "starting accounting session %08X-%08X", 247 sta->acct_session_id_hi, sta->acct_session_id_lo); 248 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); 253 254 if (!hapd->conf->radius->acct_server) 255 return; 256 257 if (sta->acct_interim_interval) 258 interval = sta->acct_interim_interval; 259 else 260 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 261 eloop_register_timeout(interval, 0, accounting_interim_update, 262 hapd, sta); 263 264 msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); 265 if (msg) 266 radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); 267 268 sta->acct_session_started = 1; 269 } 270 271 272 static void accounting_sta_report(struct hostapd_data *hapd, 273 struct sta_info *sta, int stop) 274 { 275 struct radius_msg *msg; 276 int cause = sta->acct_terminate_cause; 277 struct hostap_sta_driver_data data; 278 u32 gigawords; 279 280 if (!hapd->conf->radius->acct_server) 281 return; 282 283 msg = accounting_msg(hapd, sta, 284 stop ? RADIUS_ACCT_STATUS_TYPE_STOP : 285 RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); 286 if (!msg) { 287 printf("Could not create RADIUS Accounting message\n"); 288 return; 289 } 290 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"); 294 goto fail; 295 } 296 297 if (accounting_sta_update_stats(hapd, sta, &data) == 0) { 298 if (!radius_msg_add_attr_int32(msg, 299 RADIUS_ATTR_ACCT_INPUT_PACKETS, 300 data.rx_packets)) { 301 printf("Could not add Acct-Input-Packets\n"); 302 goto fail; 303 } 304 if (!radius_msg_add_attr_int32(msg, 305 RADIUS_ATTR_ACCT_OUTPUT_PACKETS, 306 data.tx_packets)) { 307 printf("Could not add Acct-Output-Packets\n"); 308 goto fail; 309 } 310 if (!radius_msg_add_attr_int32(msg, 311 RADIUS_ATTR_ACCT_INPUT_OCTETS, 312 data.rx_bytes)) { 313 printf("Could not add Acct-Input-Octets\n"); 314 goto fail; 315 } 316 gigawords = sta->acct_input_gigawords; 317 #if __WORDSIZE == 64 318 gigawords += data.rx_bytes >> 32; 319 #endif 320 if (gigawords && 321 !radius_msg_add_attr_int32( 322 msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, 323 gigawords)) { 324 printf("Could not add Acct-Input-Gigawords\n"); 325 goto fail; 326 } 327 if (!radius_msg_add_attr_int32(msg, 328 RADIUS_ATTR_ACCT_OUTPUT_OCTETS, 329 data.tx_bytes)) { 330 printf("Could not add Acct-Output-Octets\n"); 331 goto fail; 332 } 333 gigawords = sta->acct_output_gigawords; 334 #if __WORDSIZE == 64 335 gigawords += data.tx_bytes >> 32; 336 #endif 337 if (gigawords && 338 !radius_msg_add_attr_int32( 339 msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, 340 gigawords)) { 341 printf("Could not add Acct-Output-Gigawords\n"); 342 goto fail; 343 } 344 } 345 346 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 347 time(NULL))) { 348 printf("Could not add Event-Timestamp\n"); 349 goto fail; 350 } 351 352 if (eloop_terminated()) 353 cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; 354 355 if (stop && cause && 356 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 357 cause)) { 358 printf("Could not add Acct-Terminate-Cause\n"); 359 goto fail; 360 } 361 362 radius_client_send(hapd->radius, msg, 363 stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, 364 sta->addr); 365 return; 366 367 fail: 368 radius_msg_free(msg); 369 } 370 371 372 /** 373 * accounting_sta_interim - Send a interim STA accounting report 374 * @hapd: hostapd BSS data 375 * @sta: The station 376 */ 377 void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) 378 { 379 if (sta->acct_session_started) 380 accounting_sta_report(hapd, sta, 0); 381 } 382 383 384 /** 385 * accounting_sta_stop - Stop STA accounting 386 * @hapd: hostapd BSS data 387 * @sta: The station 388 */ 389 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) 390 { 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, 395 HOSTAPD_LEVEL_INFO, 396 "stopped accounting session %08X-%08X", 397 sta->acct_session_id_hi, 398 sta->acct_session_id_lo); 399 sta->acct_session_started = 0; 400 } 401 } 402 403 404 static void accounting_sta_get_id(struct hostapd_data *hapd, 405 struct sta_info *sta) 406 { 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++; 410 } 411 sta->acct_session_id_hi = hapd->acct_session_id_hi; 412 } 413 414 415 /** 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 423 */ 424 static RadiusRxResult 425 accounting_receive(struct radius_msg *msg, struct radius_msg *req, 426 const u8 *shared_secret, size_t shared_secret_len, 427 void *data) 428 { 429 if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { 430 printf("Unknown RADIUS message code\n"); 431 return RADIUS_RX_UNKNOWN; 432 } 433 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; 438 } 439 440 return RADIUS_RX_PROCESSED; 441 } 442 443 444 static void accounting_report_state(struct hostapd_data *hapd, int on) 445 { 446 struct radius_msg *msg; 447 448 if (!hapd->conf->radius->acct_server || hapd->radius == NULL) 449 return; 450 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); 456 if (!msg) 457 return; 458 459 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 460 RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) 461 { 462 printf("Could not add Acct-Terminate-Cause\n"); 463 radius_msg_free(msg); 464 return; 465 } 466 467 radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); 468 } 469 470 471 /** 472 * accounting_init: Initialize accounting 473 * @hapd: hostapd BSS data 474 * Returns: 0 on success, -1 on failure 475 */ 476 int accounting_init(struct hostapd_data *hapd) 477 { 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); 481 482 if (radius_client_register(hapd->radius, RADIUS_ACCT, 483 accounting_receive, hapd)) 484 return -1; 485 486 accounting_report_state(hapd, 1); 487 488 return 0; 489 } 490 491 492 /** 493 * accounting_deinit: Deinitilize accounting 494 * @hapd: hostapd BSS data 495 */ 496 void accounting_deinit(struct hostapd_data *hapd) 497 { 498 accounting_report_state(hapd, 0); 499 } 500