1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * hostapd / RADIUS Accounting 3e5b75505Sopenharmony_ci * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi> 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci */ 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ci#include "utils/includes.h" 10e5b75505Sopenharmony_ci 11e5b75505Sopenharmony_ci#include "utils/common.h" 12e5b75505Sopenharmony_ci#include "utils/eloop.h" 13e5b75505Sopenharmony_ci#include "eapol_auth/eapol_auth_sm.h" 14e5b75505Sopenharmony_ci#include "eapol_auth/eapol_auth_sm_i.h" 15e5b75505Sopenharmony_ci#include "radius/radius.h" 16e5b75505Sopenharmony_ci#include "radius/radius_client.h" 17e5b75505Sopenharmony_ci#include "hostapd.h" 18e5b75505Sopenharmony_ci#include "ieee802_1x.h" 19e5b75505Sopenharmony_ci#include "ap_config.h" 20e5b75505Sopenharmony_ci#include "sta_info.h" 21e5b75505Sopenharmony_ci#include "ap_drv_ops.h" 22e5b75505Sopenharmony_ci#include "accounting.h" 23e5b75505Sopenharmony_ci 24e5b75505Sopenharmony_ci 25e5b75505Sopenharmony_ci/* Default interval in seconds for polling TX/RX octets from the driver if 26e5b75505Sopenharmony_ci * STA is not using interim accounting. This detects wrap arounds for 27e5b75505Sopenharmony_ci * input/output octets and updates Acct-{Input,Output}-Gigawords. */ 28e5b75505Sopenharmony_ci#define ACCT_DEFAULT_UPDATE_INTERVAL 300 29e5b75505Sopenharmony_ci 30e5b75505Sopenharmony_cistatic void accounting_sta_interim(struct hostapd_data *hapd, 31e5b75505Sopenharmony_ci struct sta_info *sta); 32e5b75505Sopenharmony_ci 33e5b75505Sopenharmony_ci 34e5b75505Sopenharmony_cistatic struct radius_msg * accounting_msg(struct hostapd_data *hapd, 35e5b75505Sopenharmony_ci struct sta_info *sta, 36e5b75505Sopenharmony_ci int status_type) 37e5b75505Sopenharmony_ci{ 38e5b75505Sopenharmony_ci struct radius_msg *msg; 39e5b75505Sopenharmony_ci char buf[128]; 40e5b75505Sopenharmony_ci u8 *val; 41e5b75505Sopenharmony_ci size_t len; 42e5b75505Sopenharmony_ci int i; 43e5b75505Sopenharmony_ci struct wpabuf *b; 44e5b75505Sopenharmony_ci struct os_time now; 45e5b75505Sopenharmony_ci 46e5b75505Sopenharmony_ci msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, 47e5b75505Sopenharmony_ci radius_client_get_id(hapd->radius)); 48e5b75505Sopenharmony_ci if (msg == NULL) { 49e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); 50e5b75505Sopenharmony_ci return NULL; 51e5b75505Sopenharmony_ci } 52e5b75505Sopenharmony_ci 53e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, 54e5b75505Sopenharmony_ci status_type)) { 55e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); 56e5b75505Sopenharmony_ci goto fail; 57e5b75505Sopenharmony_ci } 58e5b75505Sopenharmony_ci 59e5b75505Sopenharmony_ci if (sta) { 60e5b75505Sopenharmony_ci if (!hostapd_config_get_radius_attr( 61e5b75505Sopenharmony_ci hapd->conf->radius_acct_req_attr, 62e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_AUTHENTIC) && 63e5b75505Sopenharmony_ci !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, 64e5b75505Sopenharmony_ci hapd->conf->ieee802_1x ? 65e5b75505Sopenharmony_ci RADIUS_ACCT_AUTHENTIC_RADIUS : 66e5b75505Sopenharmony_ci RADIUS_ACCT_AUTHENTIC_LOCAL)) { 67e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); 68e5b75505Sopenharmony_ci goto fail; 69e5b75505Sopenharmony_ci } 70e5b75505Sopenharmony_ci 71e5b75505Sopenharmony_ci /* Use 802.1X identity if available */ 72e5b75505Sopenharmony_ci val = ieee802_1x_get_identity(sta->eapol_sm, &len); 73e5b75505Sopenharmony_ci 74e5b75505Sopenharmony_ci /* Use RADIUS ACL identity if 802.1X provides no identity */ 75e5b75505Sopenharmony_ci if (!val && sta->identity) { 76e5b75505Sopenharmony_ci val = (u8 *) sta->identity; 77e5b75505Sopenharmony_ci len = os_strlen(sta->identity); 78e5b75505Sopenharmony_ci } 79e5b75505Sopenharmony_ci 80e5b75505Sopenharmony_ci /* Use STA MAC if neither 802.1X nor RADIUS ACL provided 81e5b75505Sopenharmony_ci * identity */ 82e5b75505Sopenharmony_ci if (!val) { 83e5b75505Sopenharmony_ci os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, 84e5b75505Sopenharmony_ci MAC2STR(sta->addr)); 85e5b75505Sopenharmony_ci val = (u8 *) buf; 86e5b75505Sopenharmony_ci len = os_strlen(buf); 87e5b75505Sopenharmony_ci } 88e5b75505Sopenharmony_ci 89e5b75505Sopenharmony_ci if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, 90e5b75505Sopenharmony_ci len)) { 91e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add User-Name"); 92e5b75505Sopenharmony_ci goto fail; 93e5b75505Sopenharmony_ci } 94e5b75505Sopenharmony_ci } 95e5b75505Sopenharmony_ci 96e5b75505Sopenharmony_ci if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, 97e5b75505Sopenharmony_ci msg) < 0) 98e5b75505Sopenharmony_ci goto fail; 99e5b75505Sopenharmony_ci 100e5b75505Sopenharmony_ci if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0) 101e5b75505Sopenharmony_ci goto fail; 102e5b75505Sopenharmony_ci 103e5b75505Sopenharmony_ci if (sta) { 104e5b75505Sopenharmony_ci for (i = 0; ; i++) { 105e5b75505Sopenharmony_ci val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, 106e5b75505Sopenharmony_ci i); 107e5b75505Sopenharmony_ci if (val == NULL) 108e5b75505Sopenharmony_ci break; 109e5b75505Sopenharmony_ci 110e5b75505Sopenharmony_ci if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, 111e5b75505Sopenharmony_ci val, len)) { 112e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Class"); 113e5b75505Sopenharmony_ci goto fail; 114e5b75505Sopenharmony_ci } 115e5b75505Sopenharmony_ci } 116e5b75505Sopenharmony_ci 117e5b75505Sopenharmony_ci b = ieee802_1x_get_radius_cui(sta->eapol_sm); 118e5b75505Sopenharmony_ci if (b && 119e5b75505Sopenharmony_ci !radius_msg_add_attr(msg, 120e5b75505Sopenharmony_ci RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 121e5b75505Sopenharmony_ci wpabuf_head(b), wpabuf_len(b))) { 122e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Could not add CUI"); 123e5b75505Sopenharmony_ci goto fail; 124e5b75505Sopenharmony_ci } 125e5b75505Sopenharmony_ci 126e5b75505Sopenharmony_ci if (!b && sta->radius_cui && 127e5b75505Sopenharmony_ci !radius_msg_add_attr(msg, 128e5b75505Sopenharmony_ci RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 129e5b75505Sopenharmony_ci (u8 *) sta->radius_cui, 130e5b75505Sopenharmony_ci os_strlen(sta->radius_cui))) { 131e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); 132e5b75505Sopenharmony_ci goto fail; 133e5b75505Sopenharmony_ci } 134e5b75505Sopenharmony_ci 135e5b75505Sopenharmony_ci if (sta->ipaddr && 136e5b75505Sopenharmony_ci !radius_msg_add_attr_int32(msg, 137e5b75505Sopenharmony_ci RADIUS_ATTR_FRAMED_IP_ADDRESS, 138e5b75505Sopenharmony_ci be_to_host32(sta->ipaddr))) { 139e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 140e5b75505Sopenharmony_ci "Could not add Framed-IP-Address"); 141e5b75505Sopenharmony_ci goto fail; 142e5b75505Sopenharmony_ci } 143e5b75505Sopenharmony_ci } 144e5b75505Sopenharmony_ci 145e5b75505Sopenharmony_ci os_get_time(&now); 146e5b75505Sopenharmony_ci if (now.sec > 1000000000 && 147e5b75505Sopenharmony_ci !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 148e5b75505Sopenharmony_ci now.sec)) { 149e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); 150e5b75505Sopenharmony_ci goto fail; 151e5b75505Sopenharmony_ci } 152e5b75505Sopenharmony_ci 153e5b75505Sopenharmony_ci /* 154e5b75505Sopenharmony_ci * Add Acct-Delay-Time with zero value for the first transmission. This 155e5b75505Sopenharmony_ci * will be updated within radius_client.c when retransmitting the frame. 156e5b75505Sopenharmony_ci */ 157e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) { 158e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time"); 159e5b75505Sopenharmony_ci goto fail; 160e5b75505Sopenharmony_ci } 161e5b75505Sopenharmony_ci 162e5b75505Sopenharmony_ci return msg; 163e5b75505Sopenharmony_ci 164e5b75505Sopenharmony_ci fail: 165e5b75505Sopenharmony_ci radius_msg_free(msg); 166e5b75505Sopenharmony_ci return NULL; 167e5b75505Sopenharmony_ci} 168e5b75505Sopenharmony_ci 169e5b75505Sopenharmony_ci 170e5b75505Sopenharmony_cistatic int accounting_sta_update_stats(struct hostapd_data *hapd, 171e5b75505Sopenharmony_ci struct sta_info *sta, 172e5b75505Sopenharmony_ci struct hostap_sta_driver_data *data) 173e5b75505Sopenharmony_ci{ 174e5b75505Sopenharmony_ci if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) 175e5b75505Sopenharmony_ci return -1; 176e5b75505Sopenharmony_ci 177e5b75505Sopenharmony_ci if (!data->bytes_64bit) { 178e5b75505Sopenharmony_ci /* Extend 32-bit counters from the driver to 64-bit counters */ 179e5b75505Sopenharmony_ci if (sta->last_rx_bytes_lo > data->rx_bytes) 180e5b75505Sopenharmony_ci sta->last_rx_bytes_hi++; 181e5b75505Sopenharmony_ci sta->last_rx_bytes_lo = data->rx_bytes; 182e5b75505Sopenharmony_ci 183e5b75505Sopenharmony_ci if (sta->last_tx_bytes_lo > data->tx_bytes) 184e5b75505Sopenharmony_ci sta->last_tx_bytes_hi++; 185e5b75505Sopenharmony_ci sta->last_tx_bytes_lo = data->tx_bytes; 186e5b75505Sopenharmony_ci } 187e5b75505Sopenharmony_ci 188e5b75505Sopenharmony_ci hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 189e5b75505Sopenharmony_ci HOSTAPD_LEVEL_DEBUG, 190e5b75505Sopenharmony_ci "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d", 191e5b75505Sopenharmony_ci data->rx_bytes, sta->last_rx_bytes_hi, 192e5b75505Sopenharmony_ci sta->last_rx_bytes_lo, 193e5b75505Sopenharmony_ci data->tx_bytes, sta->last_tx_bytes_hi, 194e5b75505Sopenharmony_ci sta->last_tx_bytes_lo, 195e5b75505Sopenharmony_ci data->bytes_64bit); 196e5b75505Sopenharmony_ci 197e5b75505Sopenharmony_ci return 0; 198e5b75505Sopenharmony_ci} 199e5b75505Sopenharmony_ci 200e5b75505Sopenharmony_ci 201e5b75505Sopenharmony_cistatic void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) 202e5b75505Sopenharmony_ci{ 203e5b75505Sopenharmony_ci struct hostapd_data *hapd = eloop_ctx; 204e5b75505Sopenharmony_ci struct sta_info *sta = timeout_ctx; 205e5b75505Sopenharmony_ci int interval; 206e5b75505Sopenharmony_ci 207e5b75505Sopenharmony_ci if (sta->acct_interim_interval) { 208e5b75505Sopenharmony_ci accounting_sta_interim(hapd, sta); 209e5b75505Sopenharmony_ci interval = sta->acct_interim_interval; 210e5b75505Sopenharmony_ci } else { 211e5b75505Sopenharmony_ci struct hostap_sta_driver_data data; 212e5b75505Sopenharmony_ci accounting_sta_update_stats(hapd, sta, &data); 213e5b75505Sopenharmony_ci interval = ACCT_DEFAULT_UPDATE_INTERVAL; 214e5b75505Sopenharmony_ci } 215e5b75505Sopenharmony_ci 216e5b75505Sopenharmony_ci eloop_register_timeout(interval, 0, accounting_interim_update, 217e5b75505Sopenharmony_ci hapd, sta); 218e5b75505Sopenharmony_ci} 219e5b75505Sopenharmony_ci 220e5b75505Sopenharmony_ci 221e5b75505Sopenharmony_ci/** 222e5b75505Sopenharmony_ci * accounting_sta_start - Start STA accounting 223e5b75505Sopenharmony_ci * @hapd: hostapd BSS data 224e5b75505Sopenharmony_ci * @sta: The station 225e5b75505Sopenharmony_ci */ 226e5b75505Sopenharmony_civoid accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) 227e5b75505Sopenharmony_ci{ 228e5b75505Sopenharmony_ci struct radius_msg *msg; 229e5b75505Sopenharmony_ci int interval; 230e5b75505Sopenharmony_ci 231e5b75505Sopenharmony_ci if (sta->acct_session_started) 232e5b75505Sopenharmony_ci return; 233e5b75505Sopenharmony_ci 234e5b75505Sopenharmony_ci hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 235e5b75505Sopenharmony_ci HOSTAPD_LEVEL_INFO, 236e5b75505Sopenharmony_ci "starting accounting session %016llX", 237e5b75505Sopenharmony_ci (unsigned long long) sta->acct_session_id); 238e5b75505Sopenharmony_ci 239e5b75505Sopenharmony_ci os_get_reltime(&sta->acct_session_start); 240e5b75505Sopenharmony_ci sta->last_rx_bytes_hi = 0; 241e5b75505Sopenharmony_ci sta->last_rx_bytes_lo = 0; 242e5b75505Sopenharmony_ci sta->last_tx_bytes_hi = 0; 243e5b75505Sopenharmony_ci sta->last_tx_bytes_lo = 0; 244e5b75505Sopenharmony_ci hostapd_drv_sta_clear_stats(hapd, sta->addr); 245e5b75505Sopenharmony_ci 246e5b75505Sopenharmony_ci if (!hapd->conf->radius->acct_server) 247e5b75505Sopenharmony_ci return; 248e5b75505Sopenharmony_ci 249e5b75505Sopenharmony_ci if (sta->acct_interim_interval) 250e5b75505Sopenharmony_ci interval = sta->acct_interim_interval; 251e5b75505Sopenharmony_ci else 252e5b75505Sopenharmony_ci interval = ACCT_DEFAULT_UPDATE_INTERVAL; 253e5b75505Sopenharmony_ci eloop_register_timeout(interval, 0, accounting_interim_update, 254e5b75505Sopenharmony_ci hapd, sta); 255e5b75505Sopenharmony_ci 256e5b75505Sopenharmony_ci msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); 257e5b75505Sopenharmony_ci if (msg && 258e5b75505Sopenharmony_ci radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) 259e5b75505Sopenharmony_ci radius_msg_free(msg); 260e5b75505Sopenharmony_ci 261e5b75505Sopenharmony_ci sta->acct_session_started = 1; 262e5b75505Sopenharmony_ci} 263e5b75505Sopenharmony_ci 264e5b75505Sopenharmony_ci 265e5b75505Sopenharmony_cistatic void accounting_sta_report(struct hostapd_data *hapd, 266e5b75505Sopenharmony_ci struct sta_info *sta, int stop) 267e5b75505Sopenharmony_ci{ 268e5b75505Sopenharmony_ci struct radius_msg *msg; 269e5b75505Sopenharmony_ci int cause = sta->acct_terminate_cause; 270e5b75505Sopenharmony_ci struct hostap_sta_driver_data data; 271e5b75505Sopenharmony_ci struct os_reltime now_r, diff; 272e5b75505Sopenharmony_ci u64 bytes; 273e5b75505Sopenharmony_ci 274e5b75505Sopenharmony_ci if (!hapd->conf->radius->acct_server) 275e5b75505Sopenharmony_ci return; 276e5b75505Sopenharmony_ci 277e5b75505Sopenharmony_ci msg = accounting_msg(hapd, sta, 278e5b75505Sopenharmony_ci stop ? RADIUS_ACCT_STATUS_TYPE_STOP : 279e5b75505Sopenharmony_ci RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); 280e5b75505Sopenharmony_ci if (!msg) { 281e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); 282e5b75505Sopenharmony_ci return; 283e5b75505Sopenharmony_ci } 284e5b75505Sopenharmony_ci 285e5b75505Sopenharmony_ci os_get_reltime(&now_r); 286e5b75505Sopenharmony_ci os_reltime_sub(&now_r, &sta->acct_session_start, &diff); 287e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, 288e5b75505Sopenharmony_ci diff.sec)) { 289e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); 290e5b75505Sopenharmony_ci goto fail; 291e5b75505Sopenharmony_ci } 292e5b75505Sopenharmony_ci 293e5b75505Sopenharmony_ci if (accounting_sta_update_stats(hapd, sta, &data) == 0) { 294e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, 295e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_INPUT_PACKETS, 296e5b75505Sopenharmony_ci data.rx_packets)) { 297e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets"); 298e5b75505Sopenharmony_ci goto fail; 299e5b75505Sopenharmony_ci } 300e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, 301e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_OUTPUT_PACKETS, 302e5b75505Sopenharmony_ci data.tx_packets)) { 303e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); 304e5b75505Sopenharmony_ci goto fail; 305e5b75505Sopenharmony_ci } 306e5b75505Sopenharmony_ci if (data.bytes_64bit) 307e5b75505Sopenharmony_ci bytes = data.rx_bytes; 308e5b75505Sopenharmony_ci else 309e5b75505Sopenharmony_ci bytes = ((u64) sta->last_rx_bytes_hi << 32) | 310e5b75505Sopenharmony_ci sta->last_rx_bytes_lo; 311e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, 312e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_INPUT_OCTETS, 313e5b75505Sopenharmony_ci (u32) bytes)) { 314e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); 315e5b75505Sopenharmony_ci goto fail; 316e5b75505Sopenharmony_ci } 317e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, 318e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, 319e5b75505Sopenharmony_ci (u32) (bytes >> 32))) { 320e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); 321e5b75505Sopenharmony_ci goto fail; 322e5b75505Sopenharmony_ci } 323e5b75505Sopenharmony_ci if (data.bytes_64bit) 324e5b75505Sopenharmony_ci bytes = data.tx_bytes; 325e5b75505Sopenharmony_ci else 326e5b75505Sopenharmony_ci bytes = ((u64) sta->last_tx_bytes_hi << 32) | 327e5b75505Sopenharmony_ci sta->last_tx_bytes_lo; 328e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, 329e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_OUTPUT_OCTETS, 330e5b75505Sopenharmony_ci (u32) bytes)) { 331e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); 332e5b75505Sopenharmony_ci goto fail; 333e5b75505Sopenharmony_ci } 334e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(msg, 335e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, 336e5b75505Sopenharmony_ci (u32) (bytes >> 32))) { 337e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); 338e5b75505Sopenharmony_ci goto fail; 339e5b75505Sopenharmony_ci } 340e5b75505Sopenharmony_ci } 341e5b75505Sopenharmony_ci 342e5b75505Sopenharmony_ci if (eloop_terminated()) 343e5b75505Sopenharmony_ci cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; 344e5b75505Sopenharmony_ci 345e5b75505Sopenharmony_ci if (stop && cause && 346e5b75505Sopenharmony_ci !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 347e5b75505Sopenharmony_ci cause)) { 348e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); 349e5b75505Sopenharmony_ci goto fail; 350e5b75505Sopenharmony_ci } 351e5b75505Sopenharmony_ci 352e5b75505Sopenharmony_ci if (radius_client_send(hapd->radius, msg, 353e5b75505Sopenharmony_ci stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, 354e5b75505Sopenharmony_ci sta->addr) < 0) 355e5b75505Sopenharmony_ci goto fail; 356e5b75505Sopenharmony_ci return; 357e5b75505Sopenharmony_ci 358e5b75505Sopenharmony_ci fail: 359e5b75505Sopenharmony_ci radius_msg_free(msg); 360e5b75505Sopenharmony_ci} 361e5b75505Sopenharmony_ci 362e5b75505Sopenharmony_ci 363e5b75505Sopenharmony_ci/** 364e5b75505Sopenharmony_ci * accounting_sta_interim - Send a interim STA accounting report 365e5b75505Sopenharmony_ci * @hapd: hostapd BSS data 366e5b75505Sopenharmony_ci * @sta: The station 367e5b75505Sopenharmony_ci */ 368e5b75505Sopenharmony_cistatic void accounting_sta_interim(struct hostapd_data *hapd, 369e5b75505Sopenharmony_ci struct sta_info *sta) 370e5b75505Sopenharmony_ci{ 371e5b75505Sopenharmony_ci if (sta->acct_session_started) 372e5b75505Sopenharmony_ci accounting_sta_report(hapd, sta, 0); 373e5b75505Sopenharmony_ci} 374e5b75505Sopenharmony_ci 375e5b75505Sopenharmony_ci 376e5b75505Sopenharmony_ci/** 377e5b75505Sopenharmony_ci * accounting_sta_stop - Stop STA accounting 378e5b75505Sopenharmony_ci * @hapd: hostapd BSS data 379e5b75505Sopenharmony_ci * @sta: The station 380e5b75505Sopenharmony_ci */ 381e5b75505Sopenharmony_civoid accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) 382e5b75505Sopenharmony_ci{ 383e5b75505Sopenharmony_ci if (sta->acct_session_started) { 384e5b75505Sopenharmony_ci accounting_sta_report(hapd, sta, 1); 385e5b75505Sopenharmony_ci eloop_cancel_timeout(accounting_interim_update, hapd, sta); 386e5b75505Sopenharmony_ci hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 387e5b75505Sopenharmony_ci HOSTAPD_LEVEL_INFO, 388e5b75505Sopenharmony_ci "stopped accounting session %016llX", 389e5b75505Sopenharmony_ci (unsigned long long) sta->acct_session_id); 390e5b75505Sopenharmony_ci sta->acct_session_started = 0; 391e5b75505Sopenharmony_ci } 392e5b75505Sopenharmony_ci} 393e5b75505Sopenharmony_ci 394e5b75505Sopenharmony_ci 395e5b75505Sopenharmony_ciint accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) 396e5b75505Sopenharmony_ci{ 397e5b75505Sopenharmony_ci return radius_gen_session_id((u8 *) &sta->acct_session_id, 398e5b75505Sopenharmony_ci sizeof(sta->acct_session_id)); 399e5b75505Sopenharmony_ci} 400e5b75505Sopenharmony_ci 401e5b75505Sopenharmony_ci 402e5b75505Sopenharmony_ci/** 403e5b75505Sopenharmony_ci * accounting_receive - Process the RADIUS frames from Accounting Server 404e5b75505Sopenharmony_ci * @msg: RADIUS response message 405e5b75505Sopenharmony_ci * @req: RADIUS request message 406e5b75505Sopenharmony_ci * @shared_secret: RADIUS shared secret 407e5b75505Sopenharmony_ci * @shared_secret_len: Length of shared_secret in octets 408e5b75505Sopenharmony_ci * @data: Context data (struct hostapd_data *) 409e5b75505Sopenharmony_ci * Returns: Processing status 410e5b75505Sopenharmony_ci */ 411e5b75505Sopenharmony_cistatic RadiusRxResult 412e5b75505Sopenharmony_ciaccounting_receive(struct radius_msg *msg, struct radius_msg *req, 413e5b75505Sopenharmony_ci const u8 *shared_secret, size_t shared_secret_len, 414e5b75505Sopenharmony_ci void *data) 415e5b75505Sopenharmony_ci{ 416e5b75505Sopenharmony_ci if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { 417e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Unknown RADIUS message code"); 418e5b75505Sopenharmony_ci return RADIUS_RX_UNKNOWN; 419e5b75505Sopenharmony_ci } 420e5b75505Sopenharmony_ci 421e5b75505Sopenharmony_ci if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { 422e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); 423e5b75505Sopenharmony_ci return RADIUS_RX_INVALID_AUTHENTICATOR; 424e5b75505Sopenharmony_ci } 425e5b75505Sopenharmony_ci 426e5b75505Sopenharmony_ci return RADIUS_RX_PROCESSED; 427e5b75505Sopenharmony_ci} 428e5b75505Sopenharmony_ci 429e5b75505Sopenharmony_ci 430e5b75505Sopenharmony_cistatic void accounting_report_state(struct hostapd_data *hapd, int on) 431e5b75505Sopenharmony_ci{ 432e5b75505Sopenharmony_ci struct radius_msg *msg; 433e5b75505Sopenharmony_ci 434e5b75505Sopenharmony_ci if (!hapd->conf->radius->acct_server || hapd->radius == NULL) 435e5b75505Sopenharmony_ci return; 436e5b75505Sopenharmony_ci 437e5b75505Sopenharmony_ci /* Inform RADIUS server that accounting will start/stop so that the 438e5b75505Sopenharmony_ci * server can close old accounting sessions. */ 439e5b75505Sopenharmony_ci msg = accounting_msg(hapd, NULL, 440e5b75505Sopenharmony_ci on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : 441e5b75505Sopenharmony_ci RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); 442e5b75505Sopenharmony_ci if (!msg) 443e5b75505Sopenharmony_ci return; 444e5b75505Sopenharmony_ci 445e5b75505Sopenharmony_ci if (hapd->acct_session_id) { 446e5b75505Sopenharmony_ci char buf[20]; 447e5b75505Sopenharmony_ci 448e5b75505Sopenharmony_ci os_snprintf(buf, sizeof(buf), "%016llX", 449e5b75505Sopenharmony_ci (unsigned long long) hapd->acct_session_id); 450e5b75505Sopenharmony_ci if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, 451e5b75505Sopenharmony_ci (u8 *) buf, os_strlen(buf))) 452e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); 453e5b75505Sopenharmony_ci } 454e5b75505Sopenharmony_ci 455e5b75505Sopenharmony_ci if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) 456e5b75505Sopenharmony_ci radius_msg_free(msg); 457e5b75505Sopenharmony_ci} 458e5b75505Sopenharmony_ci 459e5b75505Sopenharmony_ci 460e5b75505Sopenharmony_cistatic void accounting_interim_error_cb(const u8 *addr, void *ctx) 461e5b75505Sopenharmony_ci{ 462e5b75505Sopenharmony_ci struct hostapd_data *hapd = ctx; 463e5b75505Sopenharmony_ci struct sta_info *sta; 464e5b75505Sopenharmony_ci unsigned int i, wait_time; 465e5b75505Sopenharmony_ci int res; 466e5b75505Sopenharmony_ci 467e5b75505Sopenharmony_ci sta = ap_get_sta(hapd, addr); 468e5b75505Sopenharmony_ci if (!sta) 469e5b75505Sopenharmony_ci return; 470e5b75505Sopenharmony_ci sta->acct_interim_errors++; 471e5b75505Sopenharmony_ci if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) { 472e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 473e5b75505Sopenharmony_ci "Interim RADIUS accounting update failed for " MACSTR 474e5b75505Sopenharmony_ci " - too many errors, abandon this interim accounting update", 475e5b75505Sopenharmony_ci MAC2STR(addr)); 476e5b75505Sopenharmony_ci sta->acct_interim_errors = 0; 477e5b75505Sopenharmony_ci /* Next update will be tried after normal update interval */ 478e5b75505Sopenharmony_ci return; 479e5b75505Sopenharmony_ci } 480e5b75505Sopenharmony_ci 481e5b75505Sopenharmony_ci /* 482e5b75505Sopenharmony_ci * Use a shorter update interval as an improved retransmission mechanism 483e5b75505Sopenharmony_ci * for failed interim accounting updates. This allows the statistics to 484e5b75505Sopenharmony_ci * be updated for each retransmission. 485e5b75505Sopenharmony_ci * 486e5b75505Sopenharmony_ci * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT. 487e5b75505Sopenharmony_ci * Schedule the first retry attempt immediately and every following one 488e5b75505Sopenharmony_ci * with exponential backoff. 489e5b75505Sopenharmony_ci */ 490e5b75505Sopenharmony_ci if (sta->acct_interim_errors == 1) { 491e5b75505Sopenharmony_ci wait_time = 0; 492e5b75505Sopenharmony_ci } else { 493e5b75505Sopenharmony_ci wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */ 494e5b75505Sopenharmony_ci for (i = 1; i < sta->acct_interim_errors; i++) 495e5b75505Sopenharmony_ci wait_time *= 2; 496e5b75505Sopenharmony_ci } 497e5b75505Sopenharmony_ci res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update, 498e5b75505Sopenharmony_ci hapd, sta); 499e5b75505Sopenharmony_ci if (res == 1) 500e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 501e5b75505Sopenharmony_ci "Interim RADIUS accounting update failed for " MACSTR 502e5b75505Sopenharmony_ci " (error count: %u) - schedule next update in %u seconds", 503e5b75505Sopenharmony_ci MAC2STR(addr), sta->acct_interim_errors, wait_time); 504e5b75505Sopenharmony_ci else if (res == 0) 505e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 506e5b75505Sopenharmony_ci "Interim RADIUS accounting update failed for " MACSTR 507e5b75505Sopenharmony_ci " (error count: %u)", MAC2STR(addr), 508e5b75505Sopenharmony_ci sta->acct_interim_errors); 509e5b75505Sopenharmony_ci else 510e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 511e5b75505Sopenharmony_ci "Interim RADIUS accounting update failed for " MACSTR 512e5b75505Sopenharmony_ci " (error count: %u) - no timer found", MAC2STR(addr), 513e5b75505Sopenharmony_ci sta->acct_interim_errors); 514e5b75505Sopenharmony_ci} 515e5b75505Sopenharmony_ci 516e5b75505Sopenharmony_ci 517e5b75505Sopenharmony_ci/** 518e5b75505Sopenharmony_ci * accounting_init: Initialize accounting 519e5b75505Sopenharmony_ci * @hapd: hostapd BSS data 520e5b75505Sopenharmony_ci * Returns: 0 on success, -1 on failure 521e5b75505Sopenharmony_ci */ 522e5b75505Sopenharmony_ciint accounting_init(struct hostapd_data *hapd) 523e5b75505Sopenharmony_ci{ 524e5b75505Sopenharmony_ci if (radius_gen_session_id((u8 *) &hapd->acct_session_id, 525e5b75505Sopenharmony_ci sizeof(hapd->acct_session_id)) < 0) 526e5b75505Sopenharmony_ci return -1; 527e5b75505Sopenharmony_ci 528e5b75505Sopenharmony_ci if (radius_client_register(hapd->radius, RADIUS_ACCT, 529e5b75505Sopenharmony_ci accounting_receive, hapd)) 530e5b75505Sopenharmony_ci return -1; 531e5b75505Sopenharmony_ci radius_client_set_interim_error_cb(hapd->radius, 532e5b75505Sopenharmony_ci accounting_interim_error_cb, hapd); 533e5b75505Sopenharmony_ci 534e5b75505Sopenharmony_ci accounting_report_state(hapd, 1); 535e5b75505Sopenharmony_ci 536e5b75505Sopenharmony_ci return 0; 537e5b75505Sopenharmony_ci} 538e5b75505Sopenharmony_ci 539e5b75505Sopenharmony_ci 540e5b75505Sopenharmony_ci/** 541e5b75505Sopenharmony_ci * accounting_deinit: Deinitialize accounting 542e5b75505Sopenharmony_ci * @hapd: hostapd BSS data 543e5b75505Sopenharmony_ci */ 544e5b75505Sopenharmony_civoid accounting_deinit(struct hostapd_data *hapd) 545e5b75505Sopenharmony_ci{ 546e5b75505Sopenharmony_ci accounting_report_state(hapd, 0); 547e5b75505Sopenharmony_ci} 548