Home | History | Annotate | Download | only in ap
      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 "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