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 "radius/radius.h" 14 #include "radius/radius_client.h" 15 #include "hostapd.h" 16 #include "ieee802_1x.h" 17 #include "ap_config.h" 18 #include "sta_info.h" 19 #include "ap_drv_ops.h" 20 #include "accounting.h" 21 22 23 /* Default interval in seconds for polling TX/RX octets from the driver if 24 * STA is not using interim accounting. This detects wrap arounds for 25 * input/output octets and updates Acct-{Input,Output}-Gigawords. */ 26 #define ACCT_DEFAULT_UPDATE_INTERVAL 300 27 28 static void accounting_sta_interim(struct hostapd_data *hapd, 29 struct sta_info *sta); 30 31 32 static struct radius_msg * accounting_msg(struct hostapd_data *hapd, 33 struct sta_info *sta, 34 int status_type) 35 { 36 struct radius_msg *msg; 37 char buf[128]; 38 u8 *val; 39 size_t len; 40 int i; 41 struct wpabuf *b; 42 43 msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, 44 radius_client_get_id(hapd->radius)); 45 if (msg == NULL) { 46 wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); 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 wpa_printf(MSG_INFO, "Could not add Acct-Session-Id"); 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 wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); 67 goto fail; 68 } 69 70 if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, 71 RADIUS_ATTR_ACCT_AUTHENTIC) && 72 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, 73 hapd->conf->ieee802_1x ? 74 RADIUS_ACCT_AUTHENTIC_RADIUS : 75 RADIUS_ACCT_AUTHENTIC_LOCAL)) { 76 wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); 77 goto fail; 78 } 79 80 if (sta) { 81 /* Use 802.1X identity if available */ 82 val = ieee802_1x_get_identity(sta->eapol_sm, &len); 83 84 /* Use RADIUS ACL identity if 802.1X provides no identity */ 85 if (!val && sta->identity) { 86 val = (u8 *) sta->identity; 87 len = os_strlen(sta->identity); 88 } 89 90 /* Use STA MAC if neither 802.1X nor RADIUS ACL provided 91 * identity */ 92 if (!val) { 93 os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, 94 MAC2STR(sta->addr)); 95 val = (u8 *) buf; 96 len = os_strlen(buf); 97 } 98 99 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, 100 len)) { 101 wpa_printf(MSG_INFO, "Could not add User-Name"); 102 goto fail; 103 } 104 } 105 106 if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, 107 msg) < 0) 108 goto fail; 109 110 if (sta) { 111 for (i = 0; ; i++) { 112 val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, 113 i); 114 if (val == NULL) 115 break; 116 117 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, 118 val, len)) { 119 wpa_printf(MSG_INFO, "Could not add Class"); 120 goto fail; 121 } 122 } 123 124 b = ieee802_1x_get_radius_cui(sta->eapol_sm); 125 if (b && 126 !radius_msg_add_attr(msg, 127 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 128 wpabuf_head(b), wpabuf_len(b))) { 129 wpa_printf(MSG_ERROR, "Could not add CUI"); 130 goto fail; 131 } 132 133 if (!b && sta->radius_cui && 134 !radius_msg_add_attr(msg, 135 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 136 (u8 *) sta->radius_cui, 137 os_strlen(sta->radius_cui))) { 138 wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); 139 goto fail; 140 } 141 } 142 143 return msg; 144 145 fail: 146 radius_msg_free(msg); 147 return NULL; 148 } 149 150 151 static int accounting_sta_update_stats(struct hostapd_data *hapd, 152 struct sta_info *sta, 153 struct hostap_sta_driver_data *data) 154 { 155 if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) 156 return -1; 157 158 if (sta->last_rx_bytes > data->rx_bytes) 159 sta->acct_input_gigawords++; 160 if (sta->last_tx_bytes > data->tx_bytes) 161 sta->acct_output_gigawords++; 162 sta->last_rx_bytes = data->rx_bytes; 163 sta->last_tx_bytes = data->tx_bytes; 164 165 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 166 HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " 167 "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " 168 "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", 169 sta->last_rx_bytes, sta->acct_input_gigawords, 170 sta->last_tx_bytes, sta->acct_output_gigawords); 171 172 return 0; 173 } 174 175 176 static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) 177 { 178 struct hostapd_data *hapd = eloop_ctx; 179 struct sta_info *sta = timeout_ctx; 180 int interval; 181 182 if (sta->acct_interim_interval) { 183 accounting_sta_interim(hapd, sta); 184 interval = sta->acct_interim_interval; 185 } else { 186 struct hostap_sta_driver_data data; 187 accounting_sta_update_stats(hapd, sta, &data); 188 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 189 } 190 191 eloop_register_timeout(interval, 0, accounting_interim_update, 192 hapd, sta); 193 } 194 195 196 /** 197 * accounting_sta_start - Start STA accounting 198 * @hapd: hostapd BSS data 199 * @sta: The station 200 */ 201 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) 202 { 203 struct radius_msg *msg; 204 int interval; 205 206 if (sta->acct_session_started) 207 return; 208 209 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 210 HOSTAPD_LEVEL_INFO, 211 "starting accounting session %08X-%08X", 212 sta->acct_session_id_hi, sta->acct_session_id_lo); 213 214 os_get_reltime(&sta->acct_session_start); 215 sta->last_rx_bytes = sta->last_tx_bytes = 0; 216 sta->acct_input_gigawords = sta->acct_output_gigawords = 0; 217 hostapd_drv_sta_clear_stats(hapd, sta->addr); 218 219 if (!hapd->conf->radius->acct_server) 220 return; 221 222 if (sta->acct_interim_interval) 223 interval = sta->acct_interim_interval; 224 else 225 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 226 eloop_register_timeout(interval, 0, accounting_interim_update, 227 hapd, sta); 228 229 msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); 230 if (msg && 231 radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) 232 radius_msg_free(msg); 233 234 sta->acct_session_started = 1; 235 } 236 237 238 static void accounting_sta_report(struct hostapd_data *hapd, 239 struct sta_info *sta, int stop) 240 { 241 struct radius_msg *msg; 242 int cause = sta->acct_terminate_cause; 243 struct hostap_sta_driver_data data; 244 struct os_reltime now_r, diff; 245 struct os_time now; 246 u32 gigawords; 247 248 if (!hapd->conf->radius->acct_server) 249 return; 250 251 msg = accounting_msg(hapd, sta, 252 stop ? RADIUS_ACCT_STATUS_TYPE_STOP : 253 RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); 254 if (!msg) { 255 wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); 256 return; 257 } 258 259 os_get_reltime(&now_r); 260 os_get_time(&now); 261 os_reltime_sub(&now_r, &sta->acct_session_start, &diff); 262 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, 263 diff.sec)) { 264 wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); 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 wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets"); 273 goto fail; 274 } 275 if (!radius_msg_add_attr_int32(msg, 276 RADIUS_ATTR_ACCT_OUTPUT_PACKETS, 277 data.tx_packets)) { 278 wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); 279 goto fail; 280 } 281 if (!radius_msg_add_attr_int32(msg, 282 RADIUS_ATTR_ACCT_INPUT_OCTETS, 283 data.rx_bytes)) { 284 wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); 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 wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); 296 goto fail; 297 } 298 if (!radius_msg_add_attr_int32(msg, 299 RADIUS_ATTR_ACCT_OUTPUT_OCTETS, 300 data.tx_bytes)) { 301 wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); 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 wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); 313 goto fail; 314 } 315 } 316 317 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 318 now.sec)) { 319 wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); 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 wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); 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 wpa_printf(MSG_INFO, "Unknown RADIUS message code"); 404 return RADIUS_RX_UNKNOWN; 405 } 406 407 if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { 408 wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); 409 return RADIUS_RX_INVALID_AUTHENTICATOR; 410 } 411 412 return RADIUS_RX_PROCESSED; 413 } 414 415 416 static void accounting_report_state(struct hostapd_data *hapd, int on) 417 { 418 struct radius_msg *msg; 419 420 if (!hapd->conf->radius->acct_server || hapd->radius == NULL) 421 return; 422 423 /* Inform RADIUS server that accounting will start/stop so that the 424 * server can close old accounting sessions. */ 425 msg = accounting_msg(hapd, NULL, 426 on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : 427 RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); 428 if (!msg) 429 return; 430 431 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 432 RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) 433 { 434 wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); 435 radius_msg_free(msg); 436 return; 437 } 438 439 if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) 440 radius_msg_free(msg); 441 } 442 443 444 /** 445 * accounting_init: Initialize accounting 446 * @hapd: hostapd BSS data 447 * Returns: 0 on success, -1 on failure 448 */ 449 int accounting_init(struct hostapd_data *hapd) 450 { 451 struct os_time now; 452 453 /* Acct-Session-Id should be unique over reboots. If reliable clock is 454 * not available, this could be replaced with reboot counter, etc. */ 455 os_get_time(&now); 456 hapd->acct_session_id_hi = now.sec; 457 458 if (radius_client_register(hapd->radius, RADIUS_ACCT, 459 accounting_receive, hapd)) 460 return -1; 461 462 accounting_report_state(hapd, 1); 463 464 return 0; 465 } 466 467 468 /** 469 * accounting_deinit: Deinitilize accounting 470 * @hapd: hostapd BSS data 471 */ 472 void accounting_deinit(struct hostapd_data *hapd) 473 { 474 accounting_report_state(hapd, 0); 475 } 476