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