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