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