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

Generated by: LCOV version 1.10