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