Merge from vendor branch GCC:
[dragonfly.git] / contrib / hostapd-0.4.9 / accounting.c
1 /*
2  * Host AP (software wireless LAN access point) user space daemon for
3  * Host AP kernel driver / Accounting
4  * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Alternatively, this software may be distributed under the terms of BSD
11  * license.
12  *
13  * See README and COPYING for more details.
14  */
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <netinet/in.h>
20 #include <string.h>
21 #include <sys/ioctl.h>
22 #include <signal.h>
23 #include <assert.h>
24 #include <time.h>
25 #include <sys/time.h>
26 #include <sys/socket.h>
27
28
29 #include "hostapd.h"
30 #include "radius.h"
31 #include "radius_client.h"
32 #include "eloop.h"
33 #include "accounting.h"
34 #include "ieee802_1x.h"
35 #include "driver.h"
36
37
38 /* Default interval in seconds for polling TX/RX octets from the driver if
39  * STA is not using interim accounting. This detects wrap arounds for
40  * input/output octets and updates Acct-{Input,Output}-Gigawords. */
41 #define ACCT_DEFAULT_UPDATE_INTERVAL 300
42
43 static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta,
44                                           int status_type)
45 {
46         struct radius_msg *msg;
47         char buf[128];
48         u8 *val;
49         size_t len;
50         int i;
51
52         msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
53                              radius_client_get_id(hapd->radius));
54         if (msg == NULL) {
55                 printf("Could not create net RADIUS packet\n");
56                 return NULL;
57         }
58
59         if (sta) {
60                 radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
61
62                 snprintf(buf, sizeof(buf), "%08X-%08X",
63                          sta->acct_session_id_hi, sta->acct_session_id_lo);
64                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
65                                          (u8 *) buf, strlen(buf))) {
66                         printf("Could not add Acct-Session-Id\n");
67                         goto fail;
68                 }
69         } else {
70                 radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
71         }
72
73         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
74                                        status_type)) {
75                 printf("Could not add Acct-Status-Type\n");
76                 goto fail;
77         }
78
79         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
80                                        hapd->conf->ieee802_1x ?
81                                        RADIUS_ACCT_AUTHENTIC_RADIUS :
82                                        RADIUS_ACCT_AUTHENTIC_LOCAL)) {
83                 printf("Could not add Acct-Authentic\n");
84                 goto fail;
85         }
86
87         if (sta) {
88                 val = ieee802_1x_get_identity(sta->eapol_sm, &len);
89                 if (!val) {
90                         snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
91                                  MAC2STR(sta->addr));
92                         val = (u8 *) buf;
93                         len = strlen(buf);
94                 }
95
96                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
97                                          len)) {
98                         printf("Could not add User-Name\n");
99                         goto fail;
100                 }
101         }
102
103         if (hapd->conf->own_ip_addr.af == AF_INET &&
104             !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
105                                  (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
106                 printf("Could not add NAS-IP-Address\n");
107                 goto fail;
108         }
109
110 #ifdef CONFIG_IPV6
111         if (hapd->conf->own_ip_addr.af == AF_INET6 &&
112             !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
113                                  (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
114                 printf("Could not add NAS-IPv6-Address\n");
115                 goto fail;
116         }
117 #endif /* CONFIG_IPV6 */
118
119         if (hapd->conf->nas_identifier &&
120             !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
121                                  (u8 *) hapd->conf->nas_identifier,
122                                  strlen(hapd->conf->nas_identifier))) {
123                 printf("Could not add NAS-Identifier\n");
124                 goto fail;
125         }
126
127         if (sta &&
128             !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
129                 printf("Could not add NAS-Port\n");
130                 goto fail;
131         }
132
133         snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
134                  MAC2STR(hapd->own_addr), hapd->conf->ssid);
135         if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
136                                  (u8 *) buf, strlen(buf))) {
137                 printf("Could not add Called-Station-Id\n");
138                 goto fail;
139         }
140
141         if (sta) {
142                 snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
143                          MAC2STR(sta->addr));
144                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
145                                          (u8 *) buf, strlen(buf))) {
146                         printf("Could not add Calling-Station-Id\n");
147                         goto fail;
148                 }
149
150                 if (!radius_msg_add_attr_int32(
151                             msg, RADIUS_ATTR_NAS_PORT_TYPE,
152                             RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
153                         printf("Could not add NAS-Port-Type\n");
154                         goto fail;
155                 }
156
157                 snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
158                 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
159                                          (u8 *) buf, strlen(buf))) {
160                         printf("Could not add Connect-Info\n");
161                         goto fail;
162                 }
163
164                 for (i = 0; ; i++) {
165                         val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
166                                                           i);
167                         if (val == NULL)
168                                 break;
169
170                         if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
171                                                  val, len)) {
172                                 printf("Could not add Class\n");
173                                 goto fail;
174                         }
175                 }
176         }
177
178         return msg;
179
180  fail:
181         radius_msg_free(msg);
182         free(msg);
183         return NULL;
184 }
185
186
187 static int accounting_sta_update_stats(struct hostapd_data *hapd,
188                                        struct sta_info *sta,
189                                        struct hostap_sta_driver_data *data)
190 {
191         if (hostapd_read_sta_data(hapd, data, sta->addr))
192                 return -1;
193
194         if (sta->last_rx_bytes > data->rx_bytes)
195                 sta->acct_input_gigawords++;
196         if (sta->last_tx_bytes > data->tx_bytes)
197                 sta->acct_output_gigawords++;
198         sta->last_rx_bytes = data->rx_bytes;
199         sta->last_tx_bytes = data->tx_bytes;
200
201         hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
202                        HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
203                        "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
204                        "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
205                        sta->last_rx_bytes, sta->acct_input_gigawords,
206                        sta->last_tx_bytes, sta->acct_output_gigawords);
207
208         return 0;
209 }
210
211
212 static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
213 {
214         hostapd *hapd = eloop_ctx;
215         struct sta_info *sta = timeout_ctx;
216         int interval;
217
218         if (sta->acct_interim_interval) {
219                 accounting_sta_interim(hapd, sta);
220                 interval = sta->acct_interim_interval;
221         } else {
222                 struct hostap_sta_driver_data data;
223                 accounting_sta_update_stats(hapd, sta, &data);
224                 interval = ACCT_DEFAULT_UPDATE_INTERVAL;
225         }
226
227         eloop_register_timeout(interval, 0, accounting_interim_update,
228                                hapd, sta);
229 }
230
231
232 void accounting_sta_start(hostapd *hapd, struct sta_info *sta)
233 {
234         struct radius_msg *msg;
235         int interval;
236         
237         if (sta->acct_session_started)
238                 return;
239
240         time(&sta->acct_session_start);
241         sta->last_rx_bytes = sta->last_tx_bytes = 0;
242         sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
243         hostapd_sta_clear_stats(hapd, sta->addr);
244
245         if (!hapd->conf->radius->acct_server)
246                 return;
247
248         if (sta->acct_interim_interval)
249                 interval = sta->acct_interim_interval;
250         else
251                 interval = ACCT_DEFAULT_UPDATE_INTERVAL;
252         eloop_register_timeout(interval, 0, accounting_interim_update,
253                                hapd, sta);
254
255         msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
256         if (msg)
257                 radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
258
259         sta->acct_session_started = 1;
260 }
261
262
263 void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop)
264 {
265         struct radius_msg *msg;
266         int cause = sta->acct_terminate_cause;
267         struct hostap_sta_driver_data data;
268         u32 gigawords;
269
270         if (!hapd->conf->radius->acct_server)
271                 return;
272
273         msg = accounting_msg(hapd, sta,
274                              stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
275                              RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
276         if (!msg) {
277                 printf("Could not create RADIUS Accounting message\n");
278                 return;
279         }
280
281         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
282                                        time(NULL) - sta->acct_session_start)) {
283                 printf("Could not add Acct-Session-Time\n");
284                 goto fail;
285         }
286
287         if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
288                 if (!radius_msg_add_attr_int32(msg,
289                                                RADIUS_ATTR_ACCT_INPUT_PACKETS,
290                                                data.rx_packets)) {
291                         printf("Could not add Acct-Input-Packets\n");
292                         goto fail;
293                 }
294                 if (!radius_msg_add_attr_int32(msg,
295                                                RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
296                                                data.tx_packets)) {
297                         printf("Could not add Acct-Output-Packets\n");
298                         goto fail;
299                 }
300                 if (!radius_msg_add_attr_int32(msg,
301                                                RADIUS_ATTR_ACCT_INPUT_OCTETS,
302                                                data.rx_bytes)) {
303                         printf("Could not add Acct-Input-Octets\n");
304                         goto fail;
305                 }
306                 gigawords = sta->acct_input_gigawords;
307 #if __WORDSIZE == 64
308                 gigawords += data.rx_bytes >> 32;
309 #endif
310                 if (gigawords &&
311                     !radius_msg_add_attr_int32(
312                             msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
313                             gigawords)) {
314                         printf("Could not add Acct-Input-Gigawords\n");
315                         goto fail;
316                 }
317                 if (!radius_msg_add_attr_int32(msg,
318                                                RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
319                                                data.tx_bytes)) {
320                         printf("Could not add Acct-Output-Octets\n");
321                         goto fail;
322                 }
323                 gigawords = sta->acct_output_gigawords;
324 #if __WORDSIZE == 64
325                 gigawords += data.tx_bytes >> 32;
326 #endif
327                 if (gigawords &&
328                     !radius_msg_add_attr_int32(
329                             msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
330                             gigawords)) {
331                         printf("Could not add Acct-Output-Gigawords\n");
332                         goto fail;
333                 }
334         }
335
336         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
337                                        time(NULL))) {
338                 printf("Could not add Event-Timestamp\n");
339                 goto fail;
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                 printf("Could not add Acct-Terminate-Cause\n");
349                 goto fail;
350         }
351
352         radius_client_send(hapd->radius, msg,
353                            stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
354                            sta->addr);
355         return;
356
357  fail:
358         radius_msg_free(msg);
359         free(msg);
360 }
361
362
363 void accounting_sta_interim(hostapd *hapd, struct sta_info *sta)
364 {
365         if (sta->acct_session_started)
366                 accounting_sta_report(hapd, sta, 0);
367 }
368
369
370 void accounting_sta_stop(hostapd *hapd, struct sta_info *sta)
371 {
372         if (sta->acct_session_started) {
373                 accounting_sta_report(hapd, sta, 1);
374                 eloop_cancel_timeout(accounting_interim_update, hapd, sta);
375                 sta->acct_session_started = 0;
376         }
377 }
378
379
380 void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
381 {
382         sta->acct_session_id_lo = hapd->acct_session_id_lo++;
383         if (hapd->acct_session_id_lo == 0) {
384                 hapd->acct_session_id_hi++;
385         }
386         sta->acct_session_id_hi = hapd->acct_session_id_hi;
387 }
388
389
390 /* Process the RADIUS frames from Accounting Server */
391 static RadiusRxResult
392 accounting_receive(struct radius_msg *msg, struct radius_msg *req,
393                    u8 *shared_secret, size_t shared_secret_len, void *data)
394 {
395         if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
396                 printf("Unknown RADIUS message code\n");
397                 return RADIUS_RX_UNKNOWN;
398         }
399
400         if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
401                 printf("Incoming RADIUS packet did not have correct "
402                        "Authenticator - dropped\n");
403                 return RADIUS_RX_INVALID_AUTHENTICATOR;
404         }
405
406         return RADIUS_RX_PROCESSED;
407 }
408
409
410 static void accounting_report_state(struct hostapd_data *hapd, int on)
411 {
412         struct radius_msg *msg;
413
414         if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
415                 return;
416
417         /* Inform RADIUS server that accounting will start/stop so that the
418          * server can close old accounting sessions. */
419         msg = accounting_msg(hapd, NULL,
420                              on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
421                              RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
422         if (!msg)
423                 return;
424
425         if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
426                                        RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
427         {
428                 printf("Could not add Acct-Terminate-Cause\n");
429                 radius_msg_free(msg);
430                 free(msg);
431                 return;
432         }
433
434         radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
435 }
436
437
438 int accounting_init(hostapd *hapd)
439 {
440         /* Acct-Session-Id should be unique over reboots. If reliable clock is
441          * not available, this could be replaced with reboot counter, etc. */
442         hapd->acct_session_id_hi = time(NULL);
443
444         if (radius_client_register(hapd->radius, RADIUS_ACCT,
445                                    accounting_receive, hapd))
446                 return -1;
447
448         accounting_report_state(hapd, 1);
449
450         return 0;
451 }
452
453
454 void accounting_deinit(hostapd *hapd)
455 {
456         accounting_report_state(hapd, 0);
457 }