LCOV - code coverage report
Current view: top level - src/ap - accounting.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1422976643 Lines: 156 212 73.6 %
Date: 2015-02-03 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /*
       2             :  * hostapd / RADIUS Accounting
       3             :  * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@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         484 : 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             : 
      45         484 :         msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
      46         484 :                              radius_client_get_id(hapd->radius));
      47         484 :         if (msg == NULL) {
      48           4 :                 wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
      49           4 :                 return NULL;
      50             :         }
      51             : 
      52         480 :         if (sta) {
      53         383 :                 radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
      54             : 
      55         760 :                 if ((hapd->conf->wpa & 2) &&
      56         754 :                     !hapd->conf->disable_pmksa_caching &&
      57         754 :                     sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) {
      58         754 :                         os_snprintf(buf, sizeof(buf), "%08X+%08X",
      59         377 :                                     sta->eapol_sm->acct_multi_session_id_hi,
      60         377 :                                     sta->eapol_sm->acct_multi_session_id_lo);
      61         377 :                         if (!radius_msg_add_attr(
      62             :                                     msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
      63             :                                     (u8 *) buf, os_strlen(buf))) {
      64           0 :                                 wpa_printf(MSG_INFO,
      65             :                                            "Could not add Acct-Multi-Session-Id");
      66           0 :                                 goto fail;
      67             :                         }
      68             :                 }
      69             :         } else {
      70          97 :                 radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
      71             :         }
      72             : 
      73         480 :         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
      74             :                                        status_type)) {
      75           0 :                 wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
      76           0 :                 goto fail;
      77             :         }
      78             : 
      79         480 :         if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
      80         480 :                                             RADIUS_ATTR_ACCT_AUTHENTIC) &&
      81         480 :             !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
      82         480 :                                        hapd->conf->ieee802_1x ?
      83             :                                        RADIUS_ACCT_AUTHENTIC_RADIUS :
      84             :                                        RADIUS_ACCT_AUTHENTIC_LOCAL)) {
      85           0 :                 wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
      86           0 :                 goto fail;
      87             :         }
      88             : 
      89         480 :         if (sta) {
      90             :                 /* Use 802.1X identity if available */
      91         383 :                 val = ieee802_1x_get_identity(sta->eapol_sm, &len);
      92             : 
      93             :                 /* Use RADIUS ACL identity if 802.1X provides no identity */
      94         383 :                 if (!val && sta->identity) {
      95           0 :                         val = (u8 *) sta->identity;
      96           0 :                         len = os_strlen(sta->identity);
      97             :                 }
      98             : 
      99             :                 /* Use STA MAC if neither 802.1X nor RADIUS ACL provided
     100             :                  * identity */
     101         383 :                 if (!val) {
     102          36 :                         os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
     103          36 :                                     MAC2STR(sta->addr));
     104           6 :                         val = (u8 *) buf;
     105           6 :                         len = os_strlen(buf);
     106             :                 }
     107             : 
     108         383 :                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
     109             :                                          len)) {
     110           0 :                         wpa_printf(MSG_INFO, "Could not add User-Name");
     111           0 :                         goto fail;
     112             :                 }
     113             :         }
     114             : 
     115         480 :         if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
     116             :                                    msg) < 0)
     117           0 :                 goto fail;
     118             : 
     119         480 :         if (sta) {
     120         395 :                 for (i = 0; ; i++) {
     121         395 :                         val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
     122             :                                                           i);
     123         395 :                         if (val == NULL)
     124         383 :                                 break;
     125             : 
     126          12 :                         if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
     127             :                                                  val, len)) {
     128           0 :                                 wpa_printf(MSG_INFO, "Could not add Class");
     129           0 :                                 goto fail;
     130             :                         }
     131          12 :                 }
     132             : 
     133         383 :                 b = ieee802_1x_get_radius_cui(sta->eapol_sm);
     134         389 :                 if (b &&
     135          12 :                     !radius_msg_add_attr(msg,
     136             :                                          RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
     137           6 :                                          wpabuf_head(b), wpabuf_len(b))) {
     138           0 :                         wpa_printf(MSG_ERROR, "Could not add CUI");
     139           0 :                         goto fail;
     140             :                 }
     141             : 
     142         387 :                 if (!b && sta->radius_cui &&
     143           8 :                     !radius_msg_add_attr(msg,
     144             :                                          RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
     145           4 :                                          (u8 *) sta->radius_cui,
     146           4 :                                          os_strlen(sta->radius_cui))) {
     147           0 :                         wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
     148           0 :                         goto fail;
     149             :                 }
     150             :         }
     151             : 
     152         480 :         return msg;
     153             : 
     154             :  fail:
     155           0 :         radius_msg_free(msg);
     156           0 :         return NULL;
     157             : }
     158             : 
     159             : 
     160         195 : static int accounting_sta_update_stats(struct hostapd_data *hapd,
     161             :                                        struct sta_info *sta,
     162             :                                        struct hostap_sta_driver_data *data)
     163             : {
     164         195 :         if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
     165           0 :                 return -1;
     166             : 
     167         195 :         if (sta->last_rx_bytes > data->rx_bytes)
     168           0 :                 sta->acct_input_gigawords++;
     169         195 :         if (sta->last_tx_bytes > data->tx_bytes)
     170           0 :                 sta->acct_output_gigawords++;
     171         195 :         sta->last_rx_bytes = data->rx_bytes;
     172         195 :         sta->last_tx_bytes = data->tx_bytes;
     173             : 
     174         195 :         hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
     175             :                        HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
     176             :                        "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
     177             :                        "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
     178             :                        sta->last_rx_bytes, sta->acct_input_gigawords,
     179             :                        sta->last_tx_bytes, sta->acct_output_gigawords);
     180             : 
     181         195 :         return 0;
     182             : }
     183             : 
     184             : 
     185           6 : static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
     186             : {
     187           6 :         struct hostapd_data *hapd = eloop_ctx;
     188           6 :         struct sta_info *sta = timeout_ctx;
     189             :         int interval;
     190             : 
     191           6 :         if (sta->acct_interim_interval) {
     192           6 :                 accounting_sta_interim(hapd, sta);
     193           6 :                 interval = sta->acct_interim_interval;
     194             :         } else {
     195             :                 struct hostap_sta_driver_data data;
     196           0 :                 accounting_sta_update_stats(hapd, sta, &data);
     197           0 :                 interval = ACCT_DEFAULT_UPDATE_INTERVAL;
     198             :         }
     199             : 
     200           6 :         eloop_register_timeout(interval, 0, accounting_interim_update,
     201             :                                hapd, sta);
     202           6 : }
     203             : 
     204             : 
     205             : /**
     206             :  * accounting_sta_start - Start STA accounting
     207             :  * @hapd: hostapd BSS data
     208             :  * @sta: The station
     209             :  */
     210        1421 : void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
     211             : {
     212             :         struct radius_msg *msg;
     213             :         int interval;
     214             : 
     215        1421 :         if (sta->acct_session_started)
     216           1 :                 return;
     217             : 
     218        1420 :         hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
     219             :                        HOSTAPD_LEVEL_INFO,
     220             :                        "starting accounting session %08X-%08X",
     221             :                        sta->acct_session_id_hi, sta->acct_session_id_lo);
     222             : 
     223        1420 :         os_get_reltime(&sta->acct_session_start);
     224        1420 :         sta->last_rx_bytes = sta->last_tx_bytes = 0;
     225        1420 :         sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
     226        1420 :         hostapd_drv_sta_clear_stats(hapd, sta->addr);
     227             : 
     228        1420 :         if (!hapd->conf->radius->acct_server)
     229        1231 :                 return;
     230             : 
     231         189 :         if (sta->acct_interim_interval)
     232           2 :                 interval = sta->acct_interim_interval;
     233             :         else
     234         187 :                 interval = ACCT_DEFAULT_UPDATE_INTERVAL;
     235         189 :         eloop_register_timeout(interval, 0, accounting_interim_update,
     236             :                                hapd, sta);
     237             : 
     238         189 :         msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
     239         377 :         if (msg &&
     240         188 :             radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
     241           0 :                 radius_msg_free(msg);
     242             : 
     243         189 :         sta->acct_session_started = 1;
     244             : }
     245             : 
     246             : 
     247         195 : static void accounting_sta_report(struct hostapd_data *hapd,
     248             :                                   struct sta_info *sta, int stop)
     249             : {
     250             :         struct radius_msg *msg;
     251         195 :         int cause = sta->acct_terminate_cause;
     252             :         struct hostap_sta_driver_data data;
     253             :         struct os_reltime now_r, diff;
     254             :         struct os_time now;
     255             :         u32 gigawords;
     256             : 
     257         195 :         if (!hapd->conf->radius->acct_server)
     258         194 :                 return;
     259             : 
     260         195 :         msg = accounting_msg(hapd, sta,
     261             :                              stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
     262             :                              RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
     263         195 :         if (!msg) {
     264           0 :                 wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
     265           0 :                 return;
     266             :         }
     267             : 
     268         195 :         os_get_reltime(&now_r);
     269         195 :         os_get_time(&now);
     270         195 :         os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
     271         195 :         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
     272         195 :                                        diff.sec)) {
     273           0 :                 wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
     274           0 :                 goto fail;
     275             :         }
     276             : 
     277         195 :         if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
     278         195 :                 if (!radius_msg_add_attr_int32(msg,
     279             :                                                RADIUS_ATTR_ACCT_INPUT_PACKETS,
     280         195 :                                                data.rx_packets)) {
     281           0 :                         wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
     282           0 :                         goto fail;
     283             :                 }
     284         195 :                 if (!radius_msg_add_attr_int32(msg,
     285             :                                                RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
     286         195 :                                                data.tx_packets)) {
     287           0 :                         wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
     288           0 :                         goto fail;
     289             :                 }
     290         195 :                 if (!radius_msg_add_attr_int32(msg,
     291             :                                                RADIUS_ATTR_ACCT_INPUT_OCTETS,
     292         195 :                                                data.rx_bytes)) {
     293           0 :                         wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
     294           0 :                         goto fail;
     295             :                 }
     296         195 :                 gigawords = sta->acct_input_gigawords;
     297             : #if __WORDSIZE == 64
     298         195 :                 gigawords += data.rx_bytes >> 32;
     299             : #endif
     300         195 :                 if (gigawords &&
     301           0 :                     !radius_msg_add_attr_int32(
     302             :                             msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
     303             :                             gigawords)) {
     304           0 :                         wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
     305           0 :                         goto fail;
     306             :                 }
     307         195 :                 if (!radius_msg_add_attr_int32(msg,
     308             :                                                RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
     309         195 :                                                data.tx_bytes)) {
     310           0 :                         wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
     311           0 :                         goto fail;
     312             :                 }
     313         195 :                 gigawords = sta->acct_output_gigawords;
     314             : #if __WORDSIZE == 64
     315         195 :                 gigawords += data.tx_bytes >> 32;
     316             : #endif
     317         195 :                 if (gigawords &&
     318           0 :                     !radius_msg_add_attr_int32(
     319             :                             msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
     320             :                             gigawords)) {
     321           0 :                         wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
     322           0 :                         goto fail;
     323             :                 }
     324             :         }
     325             : 
     326         195 :         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
     327         195 :                                        now.sec)) {
     328           0 :                 wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
     329           0 :                 goto fail;
     330             :         }
     331             : 
     332         195 :         if (eloop_terminated())
     333           0 :                 cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
     334             : 
     335         384 :         if (stop && cause &&
     336         189 :             !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
     337             :                                        cause)) {
     338           0 :                 wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
     339           0 :                 goto fail;
     340             :         }
     341             : 
     342         195 :         if (radius_client_send(hapd->radius, msg,
     343             :                                stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
     344         195 :                                sta->addr) < 0)
     345           1 :                 goto fail;
     346         194 :         return;
     347             : 
     348             :  fail:
     349           1 :         radius_msg_free(msg);
     350             : }
     351             : 
     352             : 
     353             : /**
     354             :  * accounting_sta_interim - Send a interim STA accounting report
     355             :  * @hapd: hostapd BSS data
     356             :  * @sta: The station
     357             :  */
     358           6 : static void accounting_sta_interim(struct hostapd_data *hapd,
     359             :                                    struct sta_info *sta)
     360             : {
     361           6 :         if (sta->acct_session_started)
     362           6 :                 accounting_sta_report(hapd, sta, 0);
     363           6 : }
     364             : 
     365             : 
     366             : /**
     367             :  * accounting_sta_stop - Stop STA accounting
     368             :  * @hapd: hostapd BSS data
     369             :  * @sta: The station
     370             :  */
     371        4152 : void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
     372             : {
     373        4152 :         if (sta->acct_session_started) {
     374         189 :                 accounting_sta_report(hapd, sta, 1);
     375         189 :                 eloop_cancel_timeout(accounting_interim_update, hapd, sta);
     376         189 :                 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
     377             :                                HOSTAPD_LEVEL_INFO,
     378             :                                "stopped accounting session %08X-%08X",
     379             :                                sta->acct_session_id_hi,
     380             :                                sta->acct_session_id_lo);
     381         189 :                 sta->acct_session_started = 0;
     382             :         }
     383        4152 : }
     384             : 
     385             : 
     386        1804 : void accounting_sta_get_id(struct hostapd_data *hapd,
     387             :                                   struct sta_info *sta)
     388             : {
     389        1804 :         sta->acct_session_id_lo = hapd->acct_session_id_lo++;
     390        1804 :         if (hapd->acct_session_id_lo == 0) {
     391           0 :                 hapd->acct_session_id_hi++;
     392             :         }
     393        1804 :         sta->acct_session_id_hi = hapd->acct_session_id_hi;
     394        1804 : }
     395             : 
     396             : 
     397             : /**
     398             :  * accounting_receive - Process the RADIUS frames from Accounting Server
     399             :  * @msg: RADIUS response message
     400             :  * @req: RADIUS request message
     401             :  * @shared_secret: RADIUS shared secret
     402             :  * @shared_secret_len: Length of shared_secret in octets
     403             :  * @data: Context data (struct hostapd_data *)
     404             :  * Returns: Processing status
     405             :  */
     406             : static RadiusRxResult
     407         408 : accounting_receive(struct radius_msg *msg, struct radius_msg *req,
     408             :                    const u8 *shared_secret, size_t shared_secret_len,
     409             :                    void *data)
     410             : {
     411         408 :         if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
     412           0 :                 wpa_printf(MSG_INFO, "Unknown RADIUS message code");
     413           0 :                 return RADIUS_RX_UNKNOWN;
     414             :         }
     415             : 
     416         408 :         if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
     417           0 :                 wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
     418           0 :                 return RADIUS_RX_INVALID_AUTHENTICATOR;
     419             :         }
     420             : 
     421         408 :         return RADIUS_RX_PROCESSED;
     422             : }
     423             : 
     424             : 
     425        1969 : static void accounting_report_state(struct hostapd_data *hapd, int on)
     426             : {
     427             :         struct radius_msg *msg;
     428             : 
     429        1969 :         if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
     430        1869 :                 return;
     431             : 
     432             :         /* Inform RADIUS server that accounting will start/stop so that the
     433             :          * server can close old accounting sessions. */
     434         100 :         msg = accounting_msg(hapd, NULL,
     435             :                              on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
     436             :                              RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
     437         100 :         if (!msg)
     438           3 :                 return;
     439             : 
     440          97 :         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
     441             :                                        RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
     442             :         {
     443           0 :                 wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
     444           0 :                 radius_msg_free(msg);
     445           0 :                 return;
     446             :         }
     447             : 
     448          97 :         if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
     449           4 :                 radius_msg_free(msg);
     450             : }
     451             : 
     452             : 
     453             : /**
     454             :  * accounting_init: Initialize accounting
     455             :  * @hapd: hostapd BSS data
     456             :  * Returns: 0 on success, -1 on failure
     457             :  */
     458         983 : int accounting_init(struct hostapd_data *hapd)
     459             : {
     460             :         struct os_time now;
     461             : 
     462             :         /* Acct-Session-Id should be unique over reboots. If reliable clock is
     463             :          * not available, this could be replaced with reboot counter, etc. */
     464         983 :         os_get_time(&now);
     465         983 :         hapd->acct_session_id_hi = now.sec;
     466             : 
     467         983 :         if (radius_client_register(hapd->radius, RADIUS_ACCT,
     468             :                                    accounting_receive, hapd))
     469           1 :                 return -1;
     470             : 
     471         982 :         accounting_report_state(hapd, 1);
     472             : 
     473         982 :         return 0;
     474             : }
     475             : 
     476             : 
     477             : /**
     478             :  * accounting_deinit: Deinitilize accounting
     479             :  * @hapd: hostapd BSS data
     480             :  */
     481         987 : void accounting_deinit(struct hostapd_data *hapd)
     482             : {
     483         987 :         accounting_report_state(hapd, 0);
     484         987 : }

Generated by: LCOV version 1.10