Coverage Report

Created: 2026-03-19 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hostap/src/ap/accounting.c
Line
Count
Source
1
/*
2
 * hostapd / RADIUS Accounting
3
 * Copyright (c) 2002-2009, 2012-2015, 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
0
#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
static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
35
            struct sta_info *sta,
36
            int status_type)
37
0
{
38
0
  struct radius_msg *msg;
39
0
  char buf[128];
40
0
  u8 *val;
41
0
  size_t len;
42
0
  int i;
43
0
  struct wpabuf *b;
44
0
  struct os_time now;
45
46
0
  msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
47
0
           radius_client_get_id(hapd->radius));
48
0
  if (msg == NULL) {
49
0
    wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
50
0
    return NULL;
51
0
  }
52
53
0
  if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
54
0
               status_type)) {
55
0
    wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
56
0
    goto fail;
57
0
  }
58
59
0
  if (sta) {
60
0
    if (!hostapd_config_get_radius_attr(
61
0
          hapd->conf->radius_acct_req_attr,
62
0
          RADIUS_ATTR_ACCT_AUTHENTIC) &&
63
0
        !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
64
0
                 hapd->conf->ieee802_1x ?
65
0
                 RADIUS_ACCT_AUTHENTIC_RADIUS :
66
0
                 RADIUS_ACCT_AUTHENTIC_LOCAL)) {
67
0
      wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
68
0
      goto fail;
69
0
    }
70
71
    /* Use 802.1X identity if available */
72
0
    val = ieee802_1x_get_identity(sta->eapol_sm, &len);
73
74
    /* Use RADIUS ACL identity if 802.1X provides no identity */
75
0
    if (!val && sta->identity) {
76
0
      val = (u8 *) sta->identity;
77
0
      len = os_strlen(sta->identity);
78
0
    }
79
80
    /* Use STA MAC if neither 802.1X nor RADIUS ACL provided
81
     * identity */
82
0
    if (!val) {
83
0
      os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
84
0
            MAC2STR(sta->addr));
85
0
      val = (u8 *) buf;
86
0
      len = os_strlen(buf);
87
0
    }
88
89
0
    if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
90
0
           len)) {
91
0
      wpa_printf(MSG_INFO, "Could not add User-Name");
92
0
      goto fail;
93
0
    }
94
0
  }
95
96
0
  if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
97
0
           msg) < 0)
98
0
    goto fail;
99
100
0
  if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
101
0
    goto fail;
102
103
0
  if (sta) {
104
0
    for (i = 0; ; i++) {
105
0
      val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
106
0
                i);
107
0
      if (val == NULL)
108
0
        break;
109
110
0
      if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
111
0
             val, len)) {
112
0
        wpa_printf(MSG_INFO, "Could not add Class");
113
0
        goto fail;
114
0
      }
115
0
    }
116
117
0
    b = ieee802_1x_get_radius_cui(sta->eapol_sm);
118
0
    if (b &&
119
0
        !radius_msg_add_attr(msg,
120
0
           RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
121
0
           wpabuf_head(b), wpabuf_len(b))) {
122
0
      wpa_printf(MSG_ERROR, "Could not add CUI");
123
0
      goto fail;
124
0
    }
125
126
0
    if (!b && sta->radius_cui &&
127
0
        !radius_msg_add_attr(msg,
128
0
           RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
129
0
           (u8 *) sta->radius_cui,
130
0
           os_strlen(sta->radius_cui))) {
131
0
      wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
132
0
      goto fail;
133
0
    }
134
135
0
    if (sta->ipaddr &&
136
0
        !radius_msg_add_attr_int32(msg,
137
0
                 RADIUS_ATTR_FRAMED_IP_ADDRESS,
138
0
                 be_to_host32(sta->ipaddr))) {
139
0
      wpa_printf(MSG_ERROR,
140
0
           "Could not add Framed-IP-Address");
141
0
      goto fail;
142
0
    }
143
0
  }
144
145
0
  os_get_time(&now);
146
0
  if (now.sec > 1000000000 &&
147
0
      !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
148
0
               now.sec)) {
149
0
    wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
150
0
    goto fail;
151
0
  }
152
153
  /*
154
   * Add Acct-Delay-Time with zero value for the first transmission. This
155
   * will be updated within radius_client.c when retransmitting the frame.
156
   */
157
0
  if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
158
0
    wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
159
0
    goto fail;
160
0
  }
161
162
0
  return msg;
163
164
0
 fail:
165
0
  radius_msg_free(msg);
166
0
  return NULL;
167
0
}
168
169
170
static int accounting_sta_update_stats(struct hostapd_data *hapd,
171
               struct sta_info *sta,
172
               struct hostap_sta_driver_data *data)
173
0
{
174
0
  if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
175
0
    return -1;
176
177
0
  if (!data->bytes_64bit) {
178
    /* Extend 32-bit counters from the driver to 64-bit counters */
179
0
    if (sta->last_rx_bytes_lo > data->rx_bytes)
180
0
      sta->last_rx_bytes_hi++;
181
0
    sta->last_rx_bytes_lo = data->rx_bytes;
182
183
0
    if (sta->last_tx_bytes_lo > data->tx_bytes)
184
0
      sta->last_tx_bytes_hi++;
185
0
    sta->last_tx_bytes_lo = data->tx_bytes;
186
0
  }
187
188
0
  hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
189
0
           HOSTAPD_LEVEL_DEBUG,
190
0
           "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
191
0
           data->rx_bytes, sta->last_rx_bytes_hi,
192
0
           sta->last_rx_bytes_lo,
193
0
           data->tx_bytes, sta->last_tx_bytes_hi,
194
0
           sta->last_tx_bytes_lo,
195
0
           data->bytes_64bit);
196
197
0
  return 0;
198
0
}
199
200
201
static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
202
0
{
203
0
  struct hostapd_data *hapd = eloop_ctx;
204
0
  struct sta_info *sta = timeout_ctx;
205
0
  int interval;
206
207
0
  if (sta->acct_interim_interval) {
208
0
    accounting_sta_interim(hapd, sta);
209
0
    interval = sta->acct_interim_interval;
210
0
  } else {
211
0
    struct hostap_sta_driver_data data;
212
0
    accounting_sta_update_stats(hapd, sta, &data);
213
0
    interval = ACCT_DEFAULT_UPDATE_INTERVAL;
214
0
  }
215
216
0
  eloop_register_timeout(interval, 0, accounting_interim_update,
217
0
             hapd, sta);
218
0
}
219
220
221
/**
222
 * accounting_sta_start - Start STA accounting
223
 * @hapd: hostapd BSS data
224
 * @sta: The station
225
 */
226
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
227
0
{
228
0
  struct radius_msg *msg;
229
0
  int interval;
230
231
0
  if (sta->acct_session_started)
232
0
    return;
233
234
0
  hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
235
0
           HOSTAPD_LEVEL_INFO,
236
0
           "starting accounting session %016llX",
237
0
           (unsigned long long) sta->acct_session_id);
238
239
0
  os_get_reltime(&sta->acct_session_start);
240
0
  sta->last_rx_bytes_hi = 0;
241
0
  sta->last_rx_bytes_lo = 0;
242
0
  sta->last_tx_bytes_hi = 0;
243
0
  sta->last_tx_bytes_lo = 0;
244
0
  hostapd_drv_sta_clear_stats(hapd, sta->addr);
245
246
0
  if (!hapd->conf->radius->acct_server)
247
0
    return;
248
249
0
  if (sta->acct_interim_interval)
250
0
    interval = sta->acct_interim_interval;
251
0
  else
252
0
    interval = ACCT_DEFAULT_UPDATE_INTERVAL;
253
0
  eloop_register_timeout(interval, 0, accounting_interim_update,
254
0
             hapd, sta);
255
256
0
  msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
257
0
  if (msg &&
258
0
      radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
259
0
    radius_msg_free(msg);
260
261
0
  sta->acct_session_started = 1;
262
0
}
263
264
265
static void accounting_sta_report(struct hostapd_data *hapd,
266
          struct sta_info *sta, int stop)
267
0
{
268
0
  struct radius_msg *msg;
269
0
  int cause = sta->acct_terminate_cause;
270
0
  struct hostap_sta_driver_data data;
271
0
  struct os_reltime now_r, diff;
272
0
  u64 bytes;
273
274
0
  if (!hapd->conf->radius->acct_server)
275
0
    return;
276
277
0
  msg = accounting_msg(hapd, sta,
278
0
           stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
279
0
           RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
280
0
  if (!msg) {
281
0
    wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
282
0
    return;
283
0
  }
284
285
0
  os_get_reltime(&now_r);
286
0
  os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
287
0
  if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
288
0
               diff.sec)) {
289
0
    wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
290
0
    goto fail;
291
0
  }
292
293
0
  if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
294
0
    if (!radius_msg_add_attr_int32(msg,
295
0
                 RADIUS_ATTR_ACCT_INPUT_PACKETS,
296
0
                 data.rx_packets)) {
297
0
      wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
298
0
      goto fail;
299
0
    }
300
0
    if (!radius_msg_add_attr_int32(msg,
301
0
                 RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
302
0
                 data.tx_packets)) {
303
0
      wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
304
0
      goto fail;
305
0
    }
306
0
    if (data.bytes_64bit)
307
0
      bytes = data.rx_bytes;
308
0
    else
309
0
      bytes = ((u64) sta->last_rx_bytes_hi << 32) |
310
0
        sta->last_rx_bytes_lo;
311
0
    if (!radius_msg_add_attr_int32(msg,
312
0
                 RADIUS_ATTR_ACCT_INPUT_OCTETS,
313
0
                 (u32) bytes)) {
314
0
      wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
315
0
      goto fail;
316
0
    }
317
0
    if (!radius_msg_add_attr_int32(msg,
318
0
                 RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
319
0
                 (u32) (bytes >> 32))) {
320
0
      wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
321
0
      goto fail;
322
0
    }
323
0
    if (data.bytes_64bit)
324
0
      bytes = data.tx_bytes;
325
0
    else
326
0
      bytes = ((u64) sta->last_tx_bytes_hi << 32) |
327
0
        sta->last_tx_bytes_lo;
328
0
    if (!radius_msg_add_attr_int32(msg,
329
0
                 RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
330
0
                 (u32) bytes)) {
331
0
      wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
332
0
      goto fail;
333
0
    }
334
0
    if (!radius_msg_add_attr_int32(msg,
335
0
                 RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
336
0
                 (u32) (bytes >> 32))) {
337
0
      wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
338
0
      goto fail;
339
0
    }
340
0
  }
341
342
0
  if (eloop_terminated())
343
0
    cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
344
345
0
  if (stop && cause &&
346
0
      !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
347
0
               cause)) {
348
0
    wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
349
0
    goto fail;
350
0
  }
351
352
0
  if (radius_client_send(hapd->radius, msg,
353
0
             stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
354
0
             sta->addr) < 0)
355
0
    goto fail;
356
0
  return;
357
358
0
 fail:
359
0
  radius_msg_free(msg);
360
0
}
361
362
363
/**
364
 * accounting_sta_interim - Send a interim STA accounting report
365
 * @hapd: hostapd BSS data
366
 * @sta: The station
367
 */
368
static void accounting_sta_interim(struct hostapd_data *hapd,
369
           struct sta_info *sta)
370
0
{
371
0
  if (sta->acct_session_started)
372
0
    accounting_sta_report(hapd, sta, 0);
373
0
}
374
375
376
/**
377
 * accounting_sta_stop - Stop STA accounting
378
 * @hapd: hostapd BSS data
379
 * @sta: The station
380
 */
381
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
382
2.29k
{
383
2.29k
  if (sta->acct_session_started) {
384
0
    accounting_sta_report(hapd, sta, 1);
385
0
    eloop_cancel_timeout(accounting_interim_update, hapd, sta);
386
0
    hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
387
0
             HOSTAPD_LEVEL_INFO,
388
0
             "stopped accounting session %016llX",
389
0
             (unsigned long long) sta->acct_session_id);
390
0
    sta->acct_session_started = 0;
391
0
  }
392
2.29k
}
393
394
395
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
396
2.29k
{
397
2.29k
  return radius_gen_session_id((u8 *) &sta->acct_session_id,
398
2.29k
             sizeof(sta->acct_session_id));
399
2.29k
}
400
401
402
/**
403
 * accounting_receive - Process the RADIUS frames from Accounting Server
404
 * @msg: RADIUS response message
405
 * @req: RADIUS request message
406
 * @shared_secret: RADIUS shared secret
407
 * @shared_secret_len: Length of shared_secret in octets
408
 * @data: Context data (struct hostapd_data *)
409
 * Returns: Processing status
410
 */
411
static RadiusRxResult
412
accounting_receive(struct radius_msg *msg, struct radius_msg *req,
413
       const u8 *shared_secret, size_t shared_secret_len,
414
       void *data)
415
0
{
416
0
  if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
417
0
    wpa_printf(MSG_INFO, "Unknown RADIUS message code");
418
0
    return RADIUS_RX_UNKNOWN;
419
0
  }
420
421
0
  if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
422
0
    wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
423
0
    return RADIUS_RX_INVALID_AUTHENTICATOR;
424
0
  }
425
426
0
  return RADIUS_RX_PROCESSED;
427
0
}
428
429
430
static void accounting_report_state(struct hostapd_data *hapd, int on)
431
0
{
432
0
  struct radius_msg *msg;
433
434
0
  if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
435
0
    return;
436
437
  /* Inform RADIUS server that accounting will start/stop so that the
438
   * server can close old accounting sessions. */
439
0
  msg = accounting_msg(hapd, NULL,
440
0
           on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
441
0
           RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
442
0
  if (!msg)
443
0
    return;
444
445
0
  if (hapd->acct_session_id) {
446
0
    char buf[20];
447
448
0
    os_snprintf(buf, sizeof(buf), "%016llX",
449
0
          (unsigned long long) hapd->acct_session_id);
450
0
    if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
451
0
           (u8 *) buf, os_strlen(buf)))
452
0
      wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
453
0
  }
454
455
0
  if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
456
0
    radius_msg_free(msg);
457
0
}
458
459
460
static void accounting_interim_error_cb(const u8 *addr, void *ctx)
461
0
{
462
0
  struct hostapd_data *hapd = ctx;
463
0
  struct sta_info *sta;
464
0
  unsigned int i, wait_time;
465
0
  int res;
466
467
0
  sta = ap_get_sta(hapd, addr);
468
0
  if (!sta)
469
0
    return;
470
0
  sta->acct_interim_errors++;
471
0
  if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
472
0
    wpa_printf(MSG_DEBUG,
473
0
         "Interim RADIUS accounting update failed for " MACSTR
474
0
         " - too many errors, abandon this interim accounting update",
475
0
         MAC2STR(addr));
476
0
    sta->acct_interim_errors = 0;
477
    /* Next update will be tried after normal update interval */
478
0
    return;
479
0
  }
480
481
  /*
482
   * Use a shorter update interval as an improved retransmission mechanism
483
   * for failed interim accounting updates. This allows the statistics to
484
   * be updated for each retransmission.
485
   *
486
   * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
487
   * Schedule the first retry attempt immediately and every following one
488
   * with exponential backoff.
489
   */
490
0
  if (sta->acct_interim_errors == 1) {
491
0
    wait_time = 0;
492
0
  } else {
493
0
    wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
494
0
    for (i = 1; i < sta->acct_interim_errors; i++)
495
0
      wait_time *= 2;
496
0
  }
497
0
  res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
498
0
            hapd, sta);
499
0
  if (res == 1)
500
0
    wpa_printf(MSG_DEBUG,
501
0
         "Interim RADIUS accounting update failed for " MACSTR
502
0
         " (error count: %u) - schedule next update in %u seconds",
503
0
         MAC2STR(addr), sta->acct_interim_errors, wait_time);
504
0
  else if (res == 0)
505
0
    wpa_printf(MSG_DEBUG,
506
0
         "Interim RADIUS accounting update failed for " MACSTR
507
0
         " (error count: %u)", MAC2STR(addr),
508
0
         sta->acct_interim_errors);
509
0
  else
510
0
    wpa_printf(MSG_DEBUG,
511
0
         "Interim RADIUS accounting update failed for " MACSTR
512
0
         " (error count: %u) - no timer found", MAC2STR(addr),
513
0
         sta->acct_interim_errors);
514
0
}
515
516
517
/**
518
 * accounting_init: Initialize accounting
519
 * @hapd: hostapd BSS data
520
 * Returns: 0 on success, -1 on failure
521
 */
522
int accounting_init(struct hostapd_data *hapd)
523
0
{
524
0
  if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
525
0
          sizeof(hapd->acct_session_id)) < 0)
526
0
    return -1;
527
528
0
  if (radius_client_register(hapd->radius, RADIUS_ACCT,
529
0
           accounting_receive, hapd))
530
0
    return -1;
531
0
  radius_client_set_interim_error_cb(hapd->radius,
532
0
             accounting_interim_error_cb, hapd);
533
534
0
  accounting_report_state(hapd, 1);
535
536
0
  return 0;
537
0
}
538
539
540
/**
541
 * accounting_deinit: Deinitialize accounting
542
 * @hapd: hostapd BSS data
543
 */
544
void accounting_deinit(struct hostapd_data *hapd)
545
0
{
546
0
  accounting_report_state(hapd, 0);
547
0
}