$search
00001 /* 00002 * hostapd / RADIUS Accounting 00003 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "utils/includes.h" 00016 00017 #include "utils/common.h" 00018 #include "utils/eloop.h" 00019 #include "drivers/driver.h" 00020 #include "radius/radius.h" 00021 #include "radius/radius_client.h" 00022 #include "hostapd.h" 00023 #include "ieee802_1x.h" 00024 #include "ap_config.h" 00025 #include "sta_info.h" 00026 #include "accounting.h" 00027 00028 00029 /* Default interval in seconds for polling TX/RX octets from the driver if 00030 * STA is not using interim accounting. This detects wrap arounds for 00031 * input/output octets and updates Acct-{Input,Output}-Gigawords. */ 00032 #define ACCT_DEFAULT_UPDATE_INTERVAL 300 00033 00034 static void accounting_sta_get_id(struct hostapd_data *hapd, 00035 struct sta_info *sta); 00036 00037 00038 static struct radius_msg * accounting_msg(struct hostapd_data *hapd, 00039 struct sta_info *sta, 00040 int status_type) 00041 { 00042 struct radius_msg *msg; 00043 char buf[128]; 00044 u8 *val; 00045 size_t len; 00046 int i; 00047 00048 msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, 00049 radius_client_get_id(hapd->radius)); 00050 if (msg == NULL) { 00051 printf("Could not create net RADIUS packet\n"); 00052 return NULL; 00053 } 00054 00055 if (sta) { 00056 radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); 00057 00058 os_snprintf(buf, sizeof(buf), "%08X-%08X", 00059 sta->acct_session_id_hi, sta->acct_session_id_lo); 00060 if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, 00061 (u8 *) buf, os_strlen(buf))) { 00062 printf("Could not add Acct-Session-Id\n"); 00063 goto fail; 00064 } 00065 } else { 00066 radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); 00067 } 00068 00069 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, 00070 status_type)) { 00071 printf("Could not add Acct-Status-Type\n"); 00072 goto fail; 00073 } 00074 00075 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, 00076 hapd->conf->ieee802_1x ? 00077 RADIUS_ACCT_AUTHENTIC_RADIUS : 00078 RADIUS_ACCT_AUTHENTIC_LOCAL)) { 00079 printf("Could not add Acct-Authentic\n"); 00080 goto fail; 00081 } 00082 00083 if (sta) { 00084 val = ieee802_1x_get_identity(sta->eapol_sm, &len); 00085 if (!val) { 00086 os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, 00087 MAC2STR(sta->addr)); 00088 val = (u8 *) buf; 00089 len = os_strlen(buf); 00090 } 00091 00092 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, 00093 len)) { 00094 printf("Could not add User-Name\n"); 00095 goto fail; 00096 } 00097 } 00098 00099 if (hapd->conf->own_ip_addr.af == AF_INET && 00100 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, 00101 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { 00102 printf("Could not add NAS-IP-Address\n"); 00103 goto fail; 00104 } 00105 00106 #ifdef CONFIG_IPV6 00107 if (hapd->conf->own_ip_addr.af == AF_INET6 && 00108 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, 00109 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { 00110 printf("Could not add NAS-IPv6-Address\n"); 00111 goto fail; 00112 } 00113 #endif /* CONFIG_IPV6 */ 00114 00115 if (hapd->conf->nas_identifier && 00116 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, 00117 (u8 *) hapd->conf->nas_identifier, 00118 os_strlen(hapd->conf->nas_identifier))) { 00119 printf("Could not add NAS-Identifier\n"); 00120 goto fail; 00121 } 00122 00123 if (sta && 00124 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { 00125 printf("Could not add NAS-Port\n"); 00126 goto fail; 00127 } 00128 00129 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", 00130 MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); 00131 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, 00132 (u8 *) buf, os_strlen(buf))) { 00133 printf("Could not add Called-Station-Id\n"); 00134 goto fail; 00135 } 00136 00137 if (sta) { 00138 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, 00139 MAC2STR(sta->addr)); 00140 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, 00141 (u8 *) buf, os_strlen(buf))) { 00142 printf("Could not add Calling-Station-Id\n"); 00143 goto fail; 00144 } 00145 00146 if (!radius_msg_add_attr_int32( 00147 msg, RADIUS_ATTR_NAS_PORT_TYPE, 00148 RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { 00149 printf("Could not add NAS-Port-Type\n"); 00150 goto fail; 00151 } 00152 00153 os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", 00154 radius_sta_rate(hapd, sta) / 2, 00155 (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", 00156 radius_mode_txt(hapd)); 00157 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, 00158 (u8 *) buf, os_strlen(buf))) { 00159 printf("Could not add Connect-Info\n"); 00160 goto fail; 00161 } 00162 00163 for (i = 0; ; i++) { 00164 val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, 00165 i); 00166 if (val == NULL) 00167 break; 00168 00169 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, 00170 val, len)) { 00171 printf("Could not add Class\n"); 00172 goto fail; 00173 } 00174 } 00175 } 00176 00177 return msg; 00178 00179 fail: 00180 radius_msg_free(msg); 00181 return NULL; 00182 } 00183 00184 00185 static int accounting_sta_update_stats(struct hostapd_data *hapd, 00186 struct sta_info *sta, 00187 struct hostap_sta_driver_data *data) 00188 { 00189 if (hapd->drv.read_sta_data(hapd, data, sta->addr)) 00190 return -1; 00191 00192 if (sta->last_rx_bytes > data->rx_bytes) 00193 sta->acct_input_gigawords++; 00194 if (sta->last_tx_bytes > data->tx_bytes) 00195 sta->acct_output_gigawords++; 00196 sta->last_rx_bytes = data->rx_bytes; 00197 sta->last_tx_bytes = data->tx_bytes; 00198 00199 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 00200 HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " 00201 "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " 00202 "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", 00203 sta->last_rx_bytes, sta->acct_input_gigawords, 00204 sta->last_tx_bytes, sta->acct_output_gigawords); 00205 00206 return 0; 00207 } 00208 00209 00210 static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) 00211 { 00212 struct hostapd_data *hapd = eloop_ctx; 00213 struct sta_info *sta = timeout_ctx; 00214 int interval; 00215 00216 if (sta->acct_interim_interval) { 00217 accounting_sta_interim(hapd, sta); 00218 interval = sta->acct_interim_interval; 00219 } else { 00220 struct hostap_sta_driver_data data; 00221 accounting_sta_update_stats(hapd, sta, &data); 00222 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 00223 } 00224 00225 eloop_register_timeout(interval, 0, accounting_interim_update, 00226 hapd, sta); 00227 } 00228 00229 00235 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) 00236 { 00237 struct radius_msg *msg; 00238 int interval; 00239 00240 if (sta->acct_session_started) 00241 return; 00242 00243 accounting_sta_get_id(hapd, sta); 00244 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 00245 HOSTAPD_LEVEL_INFO, 00246 "starting accounting session %08X-%08X", 00247 sta->acct_session_id_hi, sta->acct_session_id_lo); 00248 00249 time(&sta->acct_session_start); 00250 sta->last_rx_bytes = sta->last_tx_bytes = 0; 00251 sta->acct_input_gigawords = sta->acct_output_gigawords = 0; 00252 hapd->drv.sta_clear_stats(hapd, sta->addr); 00253 00254 if (!hapd->conf->radius->acct_server) 00255 return; 00256 00257 if (sta->acct_interim_interval) 00258 interval = sta->acct_interim_interval; 00259 else 00260 interval = ACCT_DEFAULT_UPDATE_INTERVAL; 00261 eloop_register_timeout(interval, 0, accounting_interim_update, 00262 hapd, sta); 00263 00264 msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); 00265 if (msg) 00266 radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); 00267 00268 sta->acct_session_started = 1; 00269 } 00270 00271 00272 static void accounting_sta_report(struct hostapd_data *hapd, 00273 struct sta_info *sta, int stop) 00274 { 00275 struct radius_msg *msg; 00276 int cause = sta->acct_terminate_cause; 00277 struct hostap_sta_driver_data data; 00278 u32 gigawords; 00279 00280 if (!hapd->conf->radius->acct_server) 00281 return; 00282 00283 msg = accounting_msg(hapd, sta, 00284 stop ? RADIUS_ACCT_STATUS_TYPE_STOP : 00285 RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); 00286 if (!msg) { 00287 printf("Could not create RADIUS Accounting message\n"); 00288 return; 00289 } 00290 00291 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, 00292 time(NULL) - sta->acct_session_start)) { 00293 printf("Could not add Acct-Session-Time\n"); 00294 goto fail; 00295 } 00296 00297 if (accounting_sta_update_stats(hapd, sta, &data) == 0) { 00298 if (!radius_msg_add_attr_int32(msg, 00299 RADIUS_ATTR_ACCT_INPUT_PACKETS, 00300 data.rx_packets)) { 00301 printf("Could not add Acct-Input-Packets\n"); 00302 goto fail; 00303 } 00304 if (!radius_msg_add_attr_int32(msg, 00305 RADIUS_ATTR_ACCT_OUTPUT_PACKETS, 00306 data.tx_packets)) { 00307 printf("Could not add Acct-Output-Packets\n"); 00308 goto fail; 00309 } 00310 if (!radius_msg_add_attr_int32(msg, 00311 RADIUS_ATTR_ACCT_INPUT_OCTETS, 00312 data.rx_bytes)) { 00313 printf("Could not add Acct-Input-Octets\n"); 00314 goto fail; 00315 } 00316 gigawords = sta->acct_input_gigawords; 00317 #if __WORDSIZE == 64 00318 gigawords += data.rx_bytes >> 32; 00319 #endif 00320 if (gigawords && 00321 !radius_msg_add_attr_int32( 00322 msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, 00323 gigawords)) { 00324 printf("Could not add Acct-Input-Gigawords\n"); 00325 goto fail; 00326 } 00327 if (!radius_msg_add_attr_int32(msg, 00328 RADIUS_ATTR_ACCT_OUTPUT_OCTETS, 00329 data.tx_bytes)) { 00330 printf("Could not add Acct-Output-Octets\n"); 00331 goto fail; 00332 } 00333 gigawords = sta->acct_output_gigawords; 00334 #if __WORDSIZE == 64 00335 gigawords += data.tx_bytes >> 32; 00336 #endif 00337 if (gigawords && 00338 !radius_msg_add_attr_int32( 00339 msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, 00340 gigawords)) { 00341 printf("Could not add Acct-Output-Gigawords\n"); 00342 goto fail; 00343 } 00344 } 00345 00346 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 00347 time(NULL))) { 00348 printf("Could not add Event-Timestamp\n"); 00349 goto fail; 00350 } 00351 00352 if (eloop_terminated()) 00353 cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; 00354 00355 if (stop && cause && 00356 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 00357 cause)) { 00358 printf("Could not add Acct-Terminate-Cause\n"); 00359 goto fail; 00360 } 00361 00362 radius_client_send(hapd->radius, msg, 00363 stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, 00364 sta->addr); 00365 return; 00366 00367 fail: 00368 radius_msg_free(msg); 00369 } 00370 00371 00377 void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) 00378 { 00379 if (sta->acct_session_started) 00380 accounting_sta_report(hapd, sta, 0); 00381 } 00382 00383 00389 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) 00390 { 00391 if (sta->acct_session_started) { 00392 accounting_sta_report(hapd, sta, 1); 00393 eloop_cancel_timeout(accounting_interim_update, hapd, sta); 00394 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, 00395 HOSTAPD_LEVEL_INFO, 00396 "stopped accounting session %08X-%08X", 00397 sta->acct_session_id_hi, 00398 sta->acct_session_id_lo); 00399 sta->acct_session_started = 0; 00400 } 00401 } 00402 00403 00404 static void accounting_sta_get_id(struct hostapd_data *hapd, 00405 struct sta_info *sta) 00406 { 00407 sta->acct_session_id_lo = hapd->acct_session_id_lo++; 00408 if (hapd->acct_session_id_lo == 0) { 00409 hapd->acct_session_id_hi++; 00410 } 00411 sta->acct_session_id_hi = hapd->acct_session_id_hi; 00412 } 00413 00414 00424 static RadiusRxResult 00425 accounting_receive(struct radius_msg *msg, struct radius_msg *req, 00426 const u8 *shared_secret, size_t shared_secret_len, 00427 void *data) 00428 { 00429 if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { 00430 printf("Unknown RADIUS message code\n"); 00431 return RADIUS_RX_UNKNOWN; 00432 } 00433 00434 if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { 00435 printf("Incoming RADIUS packet did not have correct " 00436 "Authenticator - dropped\n"); 00437 return RADIUS_RX_INVALID_AUTHENTICATOR; 00438 } 00439 00440 return RADIUS_RX_PROCESSED; 00441 } 00442 00443 00444 static void accounting_report_state(struct hostapd_data *hapd, int on) 00445 { 00446 struct radius_msg *msg; 00447 00448 if (!hapd->conf->radius->acct_server || hapd->radius == NULL) 00449 return; 00450 00451 /* Inform RADIUS server that accounting will start/stop so that the 00452 * server can close old accounting sessions. */ 00453 msg = accounting_msg(hapd, NULL, 00454 on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : 00455 RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); 00456 if (!msg) 00457 return; 00458 00459 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, 00460 RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) 00461 { 00462 printf("Could not add Acct-Terminate-Cause\n"); 00463 radius_msg_free(msg); 00464 return; 00465 } 00466 00467 radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); 00468 } 00469 00470 00476 int accounting_init(struct hostapd_data *hapd) 00477 { 00478 /* Acct-Session-Id should be unique over reboots. If reliable clock is 00479 * not available, this could be replaced with reboot counter, etc. */ 00480 hapd->acct_session_id_hi = time(NULL); 00481 00482 if (radius_client_register(hapd->radius, RADIUS_ACCT, 00483 accounting_receive, hapd)) 00484 return -1; 00485 00486 accounting_report_state(hapd, 1); 00487 00488 return 0; 00489 } 00490 00491 00496 void accounting_deinit(struct hostapd_data *hapd) 00497 { 00498 accounting_report_state(hapd, 0); 00499 }