Home | History | Annotate | Download | only in ap
      1 /*
      2  * hostapd / RADIUS Accounting
      3  * Copyright (c) 2002-2009, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  */
     14 
     15 #include "utils/includes.h"
     16 
     17 #include "utils/common.h"
     18 #include "utils/eloop.h"
     19 #include "drivers/driver.h"
     20 #include "radius/radius.h"
     21 #include "radius/radius_client.h"
     22 #include "hostapd.h"
     23 #include "ieee802_1x.h"
     24 #include "ap_config.h"
     25 #include "sta_info.h"
     26 #include "ap_drv_ops.h"
     27 #include "accounting.h"
     28 
     29 
     30 /* Default interval in seconds for polling TX/RX octets from the driver if
     31  * STA is not using interim accounting. This detects wrap arounds for
     32  * input/output octets and updates Acct-{Input,Output}-Gigawords. */
     33 #define ACCT_DEFAULT_UPDATE_INTERVAL 300
     34 
     35 static void accounting_sta_get_id(struct hostapd_data *hapd,
     36 				  struct sta_info *sta);
     37 
     38 
     39 static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
     40 					  struct sta_info *sta,
     41 					  int status_type)
     42 {
     43 	struct radius_msg *msg;
     44 	char buf[128];
     45 	u8 *val;
     46 	size_t len;
     47 	int i;
     48 
     49 	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
     50 			     radius_client_get_id(hapd->radius));
     51 	if (msg == NULL) {
     52 		printf("Could not create net RADIUS packet\n");
     53 		return NULL;
     54 	}
     55 
     56 	if (sta) {
     57 		radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
     58 
     59 		os_snprintf(buf, sizeof(buf), "%08X-%08X",
     60 			    sta->acct_session_id_hi, sta->acct_session_id_lo);
     61 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
     62 					 (u8 *) buf, os_strlen(buf))) {
     63 			printf("Could not add Acct-Session-Id\n");
     64 			goto fail;
     65 		}
     66 	} else {
     67 		radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
     68 	}
     69 
     70 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
     71 				       status_type)) {
     72 		printf("Could not add Acct-Status-Type\n");
     73 		goto fail;
     74 	}
     75 
     76 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
     77 				       hapd->conf->ieee802_1x ?
     78 				       RADIUS_ACCT_AUTHENTIC_RADIUS :
     79 				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
     80 		printf("Could not add Acct-Authentic\n");
     81 		goto fail;
     82 	}
     83 
     84 	if (sta) {
     85 		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
     86 		if (!val) {
     87 			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
     88 				    MAC2STR(sta->addr));
     89 			val = (u8 *) buf;
     90 			len = os_strlen(buf);
     91 		}
     92 
     93 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
     94 					 len)) {
     95 			printf("Could not add User-Name\n");
     96 			goto fail;
     97 		}
     98 	}
     99 
    100 	if (hapd->conf->own_ip_addr.af == AF_INET &&
    101 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
    102 				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
    103 		printf("Could not add NAS-IP-Address\n");
    104 		goto fail;
    105 	}
    106 
    107 #ifdef CONFIG_IPV6
    108 	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
    109 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
    110 				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
    111 		printf("Could not add NAS-IPv6-Address\n");
    112 		goto fail;
    113 	}
    114 #endif /* CONFIG_IPV6 */
    115 
    116 	if (hapd->conf->nas_identifier &&
    117 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
    118 				 (u8 *) hapd->conf->nas_identifier,
    119 				 os_strlen(hapd->conf->nas_identifier))) {
    120 		printf("Could not add NAS-Identifier\n");
    121 		goto fail;
    122 	}
    123 
    124 	if (sta &&
    125 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
    126 		printf("Could not add NAS-Port\n");
    127 		goto fail;
    128 	}
    129 
    130 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
    131 		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
    132 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
    133 				 (u8 *) buf, os_strlen(buf))) {
    134 		printf("Could not add Called-Station-Id\n");
    135 		goto fail;
    136 	}
    137 
    138 	if (sta) {
    139 		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
    140 			    MAC2STR(sta->addr));
    141 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
    142 					 (u8 *) buf, os_strlen(buf))) {
    143 			printf("Could not add Calling-Station-Id\n");
    144 			goto fail;
    145 		}
    146 
    147 		if (!radius_msg_add_attr_int32(
    148 			    msg, RADIUS_ATTR_NAS_PORT_TYPE,
    149 			    RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
    150 			printf("Could not add NAS-Port-Type\n");
    151 			goto fail;
    152 		}
    153 
    154 		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
    155 			    radius_sta_rate(hapd, sta) / 2,
    156 			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
    157 			    radius_mode_txt(hapd));
    158 		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
    159 					 (u8 *) buf, os_strlen(buf))) {
    160 			printf("Could not add Connect-Info\n");
    161 			goto fail;
    162 		}
    163 
    164 		for (i = 0; ; i++) {
    165 			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
    166 							  i);
    167 			if (val == NULL)
    168 				break;
    169 
    170 			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
    171 						 val, len)) {
    172 				printf("Could not add Class\n");
    173 				goto fail;
    174 			}
    175 		}
    176 	}
    177 
    178 	return msg;
    179 
    180  fail:
    181 	radius_msg_free(msg);
    182 	return NULL;
    183 }
    184 
    185 
    186 static int accounting_sta_update_stats(struct hostapd_data *hapd,
    187 				       struct sta_info *sta,
    188 				       struct hostap_sta_driver_data *data)
    189 {
    190 	if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
    191 		return -1;
    192 
    193 	if (sta->last_rx_bytes > data->rx_bytes)
    194 		sta->acct_input_gigawords++;
    195 	if (sta->last_tx_bytes > data->tx_bytes)
    196 		sta->acct_output_gigawords++;
    197 	sta->last_rx_bytes = data->rx_bytes;
    198 	sta->last_tx_bytes = data->tx_bytes;
    199 
    200 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
    201 		       HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
    202 		       "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
    203 		       "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
    204 		       sta->last_rx_bytes, sta->acct_input_gigawords,
    205 		       sta->last_tx_bytes, sta->acct_output_gigawords);
    206 
    207 	return 0;
    208 }
    209 
    210 
    211 static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
    212 {
    213 	struct hostapd_data *hapd = eloop_ctx;
    214 	struct sta_info *sta = timeout_ctx;
    215 	int interval;
    216 
    217 	if (sta->acct_interim_interval) {
    218 		accounting_sta_interim(hapd, sta);
    219 		interval = sta->acct_interim_interval;
    220 	} else {
    221 		struct hostap_sta_driver_data data;
    222 		accounting_sta_update_stats(hapd, sta, &data);
    223 		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
    224 	}
    225 
    226 	eloop_register_timeout(interval, 0, accounting_interim_update,
    227 			       hapd, sta);
    228 }
    229 
    230 
    231 /**
    232  * accounting_sta_start - Start STA accounting
    233  * @hapd: hostapd BSS data
    234  * @sta: The station
    235  */
    236 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
    237 {
    238 	struct radius_msg *msg;
    239 	int interval;
    240 
    241 	if (sta->acct_session_started)
    242 		return;
    243 
    244 	accounting_sta_get_id(hapd, sta);
    245 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
    246 		       HOSTAPD_LEVEL_INFO,
    247 		       "starting accounting session %08X-%08X",
    248 		       sta->acct_session_id_hi, sta->acct_session_id_lo);
    249 
    250 	time(&sta->acct_session_start);
    251 	sta->last_rx_bytes = sta->last_tx_bytes = 0;
    252 	sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
    253 	hostapd_drv_sta_clear_stats(hapd, sta->addr);
    254 
    255 	if (!hapd->conf->radius->acct_server)
    256 		return;
    257 
    258 	if (sta->acct_interim_interval)
    259 		interval = sta->acct_interim_interval;
    260 	else
    261 		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
    262 	eloop_register_timeout(interval, 0, accounting_interim_update,
    263 			       hapd, sta);
    264 
    265 	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
    266 	if (msg)
    267 		radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
    268 
    269 	sta->acct_session_started = 1;
    270 }
    271 
    272 
    273 static void accounting_sta_report(struct hostapd_data *hapd,
    274 				  struct sta_info *sta, int stop)
    275 {
    276 	struct radius_msg *msg;
    277 	int cause = sta->acct_terminate_cause;
    278 	struct hostap_sta_driver_data data;
    279 	struct os_time now;
    280 	u32 gigawords;
    281 
    282 	if (!hapd->conf->radius->acct_server)
    283 		return;
    284 
    285 	msg = accounting_msg(hapd, sta,
    286 			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
    287 			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
    288 	if (!msg) {
    289 		printf("Could not create RADIUS Accounting message\n");
    290 		return;
    291 	}
    292 
    293 	os_get_time(&now);
    294 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
    295 				       now.sec - sta->acct_session_start)) {
    296 		printf("Could not add Acct-Session-Time\n");
    297 		goto fail;
    298 	}
    299 
    300 	if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
    301 		if (!radius_msg_add_attr_int32(msg,
    302 					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
    303 					       data.rx_packets)) {
    304 			printf("Could not add Acct-Input-Packets\n");
    305 			goto fail;
    306 		}
    307 		if (!radius_msg_add_attr_int32(msg,
    308 					       RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
    309 					       data.tx_packets)) {
    310 			printf("Could not add Acct-Output-Packets\n");
    311 			goto fail;
    312 		}
    313 		if (!radius_msg_add_attr_int32(msg,
    314 					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
    315 					       data.rx_bytes)) {
    316 			printf("Could not add Acct-Input-Octets\n");
    317 			goto fail;
    318 		}
    319 		gigawords = sta->acct_input_gigawords;
    320 #if __WORDSIZE == 64
    321 		gigawords += data.rx_bytes >> 32;
    322 #endif
    323 		if (gigawords &&
    324 		    !radius_msg_add_attr_int32(
    325 			    msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
    326 			    gigawords)) {
    327 			printf("Could not add Acct-Input-Gigawords\n");
    328 			goto fail;
    329 		}
    330 		if (!radius_msg_add_attr_int32(msg,
    331 					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
    332 					       data.tx_bytes)) {
    333 			printf("Could not add Acct-Output-Octets\n");
    334 			goto fail;
    335 		}
    336 		gigawords = sta->acct_output_gigawords;
    337 #if __WORDSIZE == 64
    338 		gigawords += data.tx_bytes >> 32;
    339 #endif
    340 		if (gigawords &&
    341 		    !radius_msg_add_attr_int32(
    342 			    msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
    343 			    gigawords)) {
    344 			printf("Could not add Acct-Output-Gigawords\n");
    345 			goto fail;
    346 		}
    347 	}
    348 
    349 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
    350 				       now.sec)) {
    351 		printf("Could not add Event-Timestamp\n");
    352 		goto fail;
    353 	}
    354 
    355 	if (eloop_terminated())
    356 		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
    357 
    358 	if (stop && cause &&
    359 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
    360 				       cause)) {
    361 		printf("Could not add Acct-Terminate-Cause\n");
    362 		goto fail;
    363 	}
    364 
    365 	radius_client_send(hapd->radius, msg,
    366 			   stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
    367 			   sta->addr);
    368 	return;
    369 
    370  fail:
    371 	radius_msg_free(msg);
    372 }
    373 
    374 
    375 /**
    376  * accounting_sta_interim - Send a interim STA accounting report
    377  * @hapd: hostapd BSS data
    378  * @sta: The station
    379  */
    380 void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
    381 {
    382 	if (sta->acct_session_started)
    383 		accounting_sta_report(hapd, sta, 0);
    384 }
    385 
    386 
    387 /**
    388  * accounting_sta_stop - Stop STA accounting
    389  * @hapd: hostapd BSS data
    390  * @sta: The station
    391  */
    392 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
    393 {
    394 	if (sta->acct_session_started) {
    395 		accounting_sta_report(hapd, sta, 1);
    396 		eloop_cancel_timeout(accounting_interim_update, hapd, sta);
    397 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
    398 			       HOSTAPD_LEVEL_INFO,
    399 			       "stopped accounting session %08X-%08X",
    400 			       sta->acct_session_id_hi,
    401 			       sta->acct_session_id_lo);
    402 		sta->acct_session_started = 0;
    403 	}
    404 }
    405 
    406 
    407 static void accounting_sta_get_id(struct hostapd_data *hapd,
    408 				  struct sta_info *sta)
    409 {
    410 	sta->acct_session_id_lo = hapd->acct_session_id_lo++;
    411 	if (hapd->acct_session_id_lo == 0) {
    412 		hapd->acct_session_id_hi++;
    413 	}
    414 	sta->acct_session_id_hi = hapd->acct_session_id_hi;
    415 }
    416 
    417 
    418 /**
    419  * accounting_receive - Process the RADIUS frames from Accounting Server
    420  * @msg: RADIUS response message
    421  * @req: RADIUS request message
    422  * @shared_secret: RADIUS shared secret
    423  * @shared_secret_len: Length of shared_secret in octets
    424  * @data: Context data (struct hostapd_data *)
    425  * Returns: Processing status
    426  */
    427 static RadiusRxResult
    428 accounting_receive(struct radius_msg *msg, struct radius_msg *req,
    429 		   const u8 *shared_secret, size_t shared_secret_len,
    430 		   void *data)
    431 {
    432 	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
    433 		printf("Unknown RADIUS message code\n");
    434 		return RADIUS_RX_UNKNOWN;
    435 	}
    436 
    437 	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
    438 		printf("Incoming RADIUS packet did not have correct "
    439 		       "Authenticator - dropped\n");
    440 		return RADIUS_RX_INVALID_AUTHENTICATOR;
    441 	}
    442 
    443 	return RADIUS_RX_PROCESSED;
    444 }
    445 
    446 
    447 static void accounting_report_state(struct hostapd_data *hapd, int on)
    448 {
    449 	struct radius_msg *msg;
    450 
    451 	if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
    452 		return;
    453 
    454 	/* Inform RADIUS server that accounting will start/stop so that the
    455 	 * server can close old accounting sessions. */
    456 	msg = accounting_msg(hapd, NULL,
    457 			     on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
    458 			     RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
    459 	if (!msg)
    460 		return;
    461 
    462 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
    463 				       RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
    464 	{
    465 		printf("Could not add Acct-Terminate-Cause\n");
    466 		radius_msg_free(msg);
    467 		return;
    468 	}
    469 
    470 	radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
    471 }
    472 
    473 
    474 /**
    475  * accounting_init: Initialize accounting
    476  * @hapd: hostapd BSS data
    477  * Returns: 0 on success, -1 on failure
    478  */
    479 int accounting_init(struct hostapd_data *hapd)
    480 {
    481 	struct os_time now;
    482 
    483 	/* Acct-Session-Id should be unique over reboots. If reliable clock is
    484 	 * not available, this could be replaced with reboot counter, etc. */
    485 	os_get_time(&now);
    486 	hapd->acct_session_id_hi = now.sec;
    487 
    488 	if (radius_client_register(hapd->radius, RADIUS_ACCT,
    489 				   accounting_receive, hapd))
    490 		return -1;
    491 
    492 	accounting_report_state(hapd, 1);
    493 
    494 	return 0;
    495 }
    496 
    497 
    498 /**
    499  * accounting_deinit: Deinitilize accounting
    500  * @hapd: hostapd BSS data
    501  */
    502 void accounting_deinit(struct hostapd_data *hapd)
    503 {
    504 	accounting_report_state(hapd, 0);
    505 }
    506