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