1 /* 2 * hostapd / RADIUS Accounting 3 * Copyright (c) 2002-2009, Jouni Malinen <j (at) 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 35 static void accounting_sta_get_id(struct hostapd_data *hapd, 36 struct sta_info *sta); 37 38 39 static 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 186 static 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 211 static 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 */ 236 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) 237 { 238 struct radius_msg *msg; 239 int interval; 240 241 if (sta->acct_session_started) 242 return; 243 244 accounting_sta_get_id(hapd, sta); 245 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 246 HOSTAPD_LEVEL_INFO, 247 "starting accounting session %08X-%08X", 248 sta->acct_session_id_hi, sta->acct_session_id_lo); 249 250 time(&sta->acct_session_start); 251 sta->last_rx_bytes = sta->last_tx_bytes = 0; 252 sta->acct_input_gigawords = sta->acct_output_gigawords = 0; 253 hostapd_drv_sta_clear_stats(hapd, sta->addr); 254 255 if (!hapd->conf->radius->acct_server) 256 return; 257 258 if (sta->acct_interim_interval) 259 interval = sta->acct_interim_interval; 260 else 261 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 262 eloop_register_timeout(interval, 0, accounting_interim_update, 263 hapd, sta); 264 265 msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); 266 if (msg) 267 radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); 268 269 sta->acct_session_started = 1; 270 } 271 272 273 static void accounting_sta_report(struct hostapd_data *hapd, 274 struct sta_info *sta, int stop) 275 { 276 struct radius_msg *msg; 277 int cause = sta->acct_terminate_cause; 278 struct hostap_sta_driver_data data; 279 struct os_time now; 280 u32 gigawords; 281 282 if (!hapd->conf->radius->acct_server) 283 return; 284 285 msg = accounting_msg(hapd, sta, 286 stop ? RADIUS_ACCT_STATUS_TYPE_STOP : 287 RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); 288 if (!msg) { 289 printf("Could not create RADIUS Accounting message\n"); 290 return; 291 } 292 293 os_get_time(&now); 294 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, 295 now.sec - sta->acct_session_start)) { 296 printf("Could not add Acct-Session-Time\n"); 297 goto fail; 298 } 299 300 if (accounting_sta_update_stats(hapd, sta, &data) == 0) { 301 if (!radius_msg_add_attr_int32(msg, 302 RADIUS_ATTR_ACCT_INPUT_PACKETS, 303 data.rx_packets)) { 304 printf("Could not add Acct-Input-Packets\n"); 305 goto fail; 306 } 307 if (!radius_msg_add_attr_int32(msg, 308 RADIUS_ATTR_ACCT_OUTPUT_PACKETS, 309 data.tx_packets)) { 310 printf("Could not add Acct-Output-Packets\n"); 311 goto fail; 312 } 313 if (!radius_msg_add_attr_int32(msg, 314 RADIUS_ATTR_ACCT_INPUT_OCTETS, 315 data.rx_bytes)) { 316 printf("Could not add Acct-Input-Octets\n"); 317 goto fail; 318 } 319 gigawords = sta->acct_input_gigawords; 320 #if __WORDSIZE == 64 321 gigawords += data.rx_bytes >> 32; 322 #endif 323 if (gigawords && 324 !radius_msg_add_attr_int32( 325 msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, 326 gigawords)) { 327 printf("Could not add Acct-Input-Gigawords\n"); 328 goto fail; 329 } 330 if (!radius_msg_add_attr_int32(msg, 331 RADIUS_ATTR_ACCT_OUTPUT_OCTETS, 332 data.tx_bytes)) { 333 printf("Could not add Acct-Output-Octets\n"); 334 goto fail; 335 } 336 gigawords = sta->acct_output_gigawords; 337 #if __WORDSIZE == 64 338 gigawords += data.tx_bytes >> 32; 339 #endif 340 if (gigawords && 341 !radius_msg_add_attr_int32( 342 msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, 343 gigawords)) { 344 printf("Could not add Acct-Output-Gigawords\n"); 345 goto fail; 346 } 347 } 348 349 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 350 now.sec)) { 351 printf("Could not add Event-Timestamp\n"); 352 goto fail; 353 } 354 355 if (eloop_terminated()) 356 cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; 357 358 if (stop && cause && 359 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 360 cause)) { 361 printf("Could not add Acct-Terminate-Cause\n"); 362 goto fail; 363 } 364 365 radius_client_send(hapd->radius, msg, 366 stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, 367 sta->addr); 368 return; 369 370 fail: 371 radius_msg_free(msg); 372 } 373 374 375 /** 376 * accounting_sta_interim - Send a interim STA accounting report 377 * @hapd: hostapd BSS data 378 * @sta: The station 379 */ 380 void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) 381 { 382 if (sta->acct_session_started) 383 accounting_sta_report(hapd, sta, 0); 384 } 385 386 387 /** 388 * accounting_sta_stop - Stop STA accounting 389 * @hapd: hostapd BSS data 390 * @sta: The station 391 */ 392 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) 393 { 394 if (sta->acct_session_started) { 395 accounting_sta_report(hapd, sta, 1); 396 eloop_cancel_timeout(accounting_interim_update, hapd, sta); 397 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 398 HOSTAPD_LEVEL_INFO, 399 "stopped accounting session %08X-%08X", 400 sta->acct_session_id_hi, 401 sta->acct_session_id_lo); 402 sta->acct_session_started = 0; 403 } 404 } 405 406 407 static void accounting_sta_get_id(struct hostapd_data *hapd, 408 struct sta_info *sta) 409 { 410 sta->acct_session_id_lo = hapd->acct_session_id_lo++; 411 if (hapd->acct_session_id_lo == 0) { 412 hapd->acct_session_id_hi++; 413 } 414 sta->acct_session_id_hi = hapd->acct_session_id_hi; 415 } 416 417 418 /** 419 * accounting_receive - Process the RADIUS frames from Accounting Server 420 * @msg: RADIUS response message 421 * @req: RADIUS request message 422 * @shared_secret: RADIUS shared secret 423 * @shared_secret_len: Length of shared_secret in octets 424 * @data: Context data (struct hostapd_data *) 425 * Returns: Processing status 426 */ 427 static RadiusRxResult 428 accounting_receive(struct radius_msg *msg, struct radius_msg *req, 429 const u8 *shared_secret, size_t shared_secret_len, 430 void *data) 431 { 432 if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { 433 printf("Unknown RADIUS message code\n"); 434 return RADIUS_RX_UNKNOWN; 435 } 436 437 if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { 438 printf("Incoming RADIUS packet did not have correct " 439 "Authenticator - dropped\n"); 440 return RADIUS_RX_INVALID_AUTHENTICATOR; 441 } 442 443 return RADIUS_RX_PROCESSED; 444 } 445 446 447 static void accounting_report_state(struct hostapd_data *hapd, int on) 448 { 449 struct radius_msg *msg; 450 451 if (!hapd->conf->radius->acct_server || hapd->radius == NULL) 452 return; 453 454 /* Inform RADIUS server that accounting will start/stop so that the 455 * server can close old accounting sessions. */ 456 msg = accounting_msg(hapd, NULL, 457 on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : 458 RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); 459 if (!msg) 460 return; 461 462 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 463 RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) 464 { 465 printf("Could not add Acct-Terminate-Cause\n"); 466 radius_msg_free(msg); 467 return; 468 } 469 470 radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); 471 } 472 473 474 /** 475 * accounting_init: Initialize accounting 476 * @hapd: hostapd BSS data 477 * Returns: 0 on success, -1 on failure 478 */ 479 int accounting_init(struct hostapd_data *hapd) 480 { 481 struct os_time now; 482 483 /* Acct-Session-Id should be unique over reboots. If reliable clock is 484 * not available, this could be replaced with reboot counter, etc. */ 485 os_get_time(&now); 486 hapd->acct_session_id_hi = now.sec; 487 488 if (radius_client_register(hapd->radius, RADIUS_ACCT, 489 accounting_receive, hapd)) 490 return -1; 491 492 accounting_report_state(hapd, 1); 493 494 return 0; 495 } 496 497 498 /** 499 * accounting_deinit: Deinitilize accounting 500 * @hapd: hostapd BSS data 501 */ 502 void accounting_deinit(struct hostapd_data *hapd) 503 { 504 accounting_report_state(hapd, 0); 505 } 506