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