blob: 2a04560660a112dbaa600d7395547bf4c14899c6 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * hostapd / RADIUS Accounting
3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "utils/includes.h"
10
11#include "utils/common.h"
12#include "utils/eloop.h"
13#include "drivers/driver.h"
14#include "radius/radius.h"
15#include "radius/radius_client.h"
16#include "hostapd.h"
17#include "ieee802_1x.h"
18#include "ap_config.h"
19#include "sta_info.h"
20#include "ap_drv_ops.h"
21#include "accounting.h"
22
23
24/* Default interval in seconds for polling TX/RX octets from the driver if
25 * STA is not using interim accounting. This detects wrap arounds for
26 * input/output octets and updates Acct-{Input,Output}-Gigawords. */
27#define ACCT_DEFAULT_UPDATE_INTERVAL 300
28
29static void accounting_sta_get_id(struct hostapd_data *hapd,
30 struct sta_info *sta);
31
32
33static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
34 struct sta_info *sta,
35 int status_type)
36{
37 struct radius_msg *msg;
38 char buf[128];
39 u8 *val;
40 size_t len;
41 int i;
42
43 msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
44 radius_client_get_id(hapd->radius));
45 if (msg == NULL) {
46 printf("Could not create net RADIUS packet\n");
47 return NULL;
48 }
49
50 if (sta) {
51 radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
52
53 os_snprintf(buf, sizeof(buf), "%08X-%08X",
54 sta->acct_session_id_hi, sta->acct_session_id_lo);
55 if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
56 (u8 *) buf, os_strlen(buf))) {
57 printf("Could not add Acct-Session-Id\n");
58 goto fail;
59 }
60 } else {
61 radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
62 }
63
64 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
65 status_type)) {
66 printf("Could not add Acct-Status-Type\n");
67 goto fail;
68 }
69
70 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
71 hapd->conf->ieee802_1x ?
72 RADIUS_ACCT_AUTHENTIC_RADIUS :
73 RADIUS_ACCT_AUTHENTIC_LOCAL)) {
74 printf("Could not add Acct-Authentic\n");
75 goto fail;
76 }
77
78 if (sta) {
79 val = ieee802_1x_get_identity(sta->eapol_sm, &len);
80 if (!val) {
81 os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
82 MAC2STR(sta->addr));
83 val = (u8 *) buf;
84 len = os_strlen(buf);
85 }
86
87 if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
88 len)) {
89 printf("Could not add User-Name\n");
90 goto fail;
91 }
92 }
93
94 if (hapd->conf->own_ip_addr.af == AF_INET &&
95 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
96 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
97 printf("Could not add NAS-IP-Address\n");
98 goto fail;
99 }
100
101#ifdef CONFIG_IPV6
102 if (hapd->conf->own_ip_addr.af == AF_INET6 &&
103 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
104 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
105 printf("Could not add NAS-IPv6-Address\n");
106 goto fail;
107 }
108#endif /* CONFIG_IPV6 */
109
110 if (hapd->conf->nas_identifier &&
111 !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
112 (u8 *) hapd->conf->nas_identifier,
113 os_strlen(hapd->conf->nas_identifier))) {
114 printf("Could not add NAS-Identifier\n");
115 goto fail;
116 }
117
118 if (sta &&
119 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
120 printf("Could not add NAS-Port\n");
121 goto fail;
122 }
123
124 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
125 MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
126 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
127 (u8 *) buf, os_strlen(buf))) {
128 printf("Could not add Called-Station-Id\n");
129 goto fail;
130 }
131
132 if (sta) {
133 os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
134 MAC2STR(sta->addr));
135 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
136 (u8 *) buf, os_strlen(buf))) {
137 printf("Could not add Calling-Station-Id\n");
138 goto fail;
139 }
140
141 if (!radius_msg_add_attr_int32(
142 msg, RADIUS_ATTR_NAS_PORT_TYPE,
143 RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
144 printf("Could not add NAS-Port-Type\n");
145 goto fail;
146 }
147
148 os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
149 radius_sta_rate(hapd, sta) / 2,
150 (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
151 radius_mode_txt(hapd));
152 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
153 (u8 *) buf, os_strlen(buf))) {
154 printf("Could not add Connect-Info\n");
155 goto fail;
156 }
157
158 for (i = 0; ; i++) {
159 val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
160 i);
161 if (val == NULL)
162 break;
163
164 if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
165 val, len)) {
166 printf("Could not add Class\n");
167 goto fail;
168 }
169 }
170 }
171
172 return msg;
173
174 fail:
175 radius_msg_free(msg);
176 return NULL;
177}
178
179
180static int accounting_sta_update_stats(struct hostapd_data *hapd,
181 struct sta_info *sta,
182 struct hostap_sta_driver_data *data)
183{
184 if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
185 return -1;
186
187 if (sta->last_rx_bytes > data->rx_bytes)
188 sta->acct_input_gigawords++;
189 if (sta->last_tx_bytes > data->tx_bytes)
190 sta->acct_output_gigawords++;
191 sta->last_rx_bytes = data->rx_bytes;
192 sta->last_tx_bytes = data->tx_bytes;
193
194 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
195 HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
196 "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
197 "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
198 sta->last_rx_bytes, sta->acct_input_gigawords,
199 sta->last_tx_bytes, sta->acct_output_gigawords);
200
201 return 0;
202}
203
204
205static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
206{
207 struct hostapd_data *hapd = eloop_ctx;
208 struct sta_info *sta = timeout_ctx;
209 int interval;
210
211 if (sta->acct_interim_interval) {
212 accounting_sta_interim(hapd, sta);
213 interval = sta->acct_interim_interval;
214 } else {
215 struct hostap_sta_driver_data data;
216 accounting_sta_update_stats(hapd, sta, &data);
217 interval = ACCT_DEFAULT_UPDATE_INTERVAL;
218 }
219
220 eloop_register_timeout(interval, 0, accounting_interim_update,
221 hapd, sta);
222}
223
224
225/**
226 * accounting_sta_start - Start STA accounting
227 * @hapd: hostapd BSS data
228 * @sta: The station
229 */
230void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
231{
232 struct radius_msg *msg;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800233 struct os_time t;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700234 int interval;
235
236 if (sta->acct_session_started)
237 return;
238
239 accounting_sta_get_id(hapd, sta);
240 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
241 HOSTAPD_LEVEL_INFO,
242 "starting accounting session %08X-%08X",
243 sta->acct_session_id_hi, sta->acct_session_id_lo);
244
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800245 os_get_time(&t);
246 sta->acct_session_start = t.sec;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700247 sta->last_rx_bytes = sta->last_tx_bytes = 0;
248 sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
249 hostapd_drv_sta_clear_stats(hapd, sta->addr);
250
251 if (!hapd->conf->radius->acct_server)
252 return;
253
254 if (sta->acct_interim_interval)
255 interval = sta->acct_interim_interval;
256 else
257 interval = ACCT_DEFAULT_UPDATE_INTERVAL;
258 eloop_register_timeout(interval, 0, accounting_interim_update,
259 hapd, sta);
260
261 msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
262 if (msg)
263 radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
264
265 sta->acct_session_started = 1;
266}
267
268
269static void accounting_sta_report(struct hostapd_data *hapd,
270 struct sta_info *sta, int stop)
271{
272 struct radius_msg *msg;
273 int cause = sta->acct_terminate_cause;
274 struct hostap_sta_driver_data data;
275 struct os_time now;
276 u32 gigawords;
277
278 if (!hapd->conf->radius->acct_server)
279 return;
280
281 msg = accounting_msg(hapd, sta,
282 stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
283 RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
284 if (!msg) {
285 printf("Could not create RADIUS Accounting message\n");
286 return;
287 }
288
289 os_get_time(&now);
290 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
291 now.sec - sta->acct_session_start)) {
292 printf("Could not add Acct-Session-Time\n");
293 goto fail;
294 }
295
296 if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
297 if (!radius_msg_add_attr_int32(msg,
298 RADIUS_ATTR_ACCT_INPUT_PACKETS,
299 data.rx_packets)) {
300 printf("Could not add Acct-Input-Packets\n");
301 goto fail;
302 }
303 if (!radius_msg_add_attr_int32(msg,
304 RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
305 data.tx_packets)) {
306 printf("Could not add Acct-Output-Packets\n");
307 goto fail;
308 }
309 if (!radius_msg_add_attr_int32(msg,
310 RADIUS_ATTR_ACCT_INPUT_OCTETS,
311 data.rx_bytes)) {
312 printf("Could not add Acct-Input-Octets\n");
313 goto fail;
314 }
315 gigawords = sta->acct_input_gigawords;
316#if __WORDSIZE == 64
317 gigawords += data.rx_bytes >> 32;
318#endif
319 if (gigawords &&
320 !radius_msg_add_attr_int32(
321 msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
322 gigawords)) {
323 printf("Could not add Acct-Input-Gigawords\n");
324 goto fail;
325 }
326 if (!radius_msg_add_attr_int32(msg,
327 RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
328 data.tx_bytes)) {
329 printf("Could not add Acct-Output-Octets\n");
330 goto fail;
331 }
332 gigawords = sta->acct_output_gigawords;
333#if __WORDSIZE == 64
334 gigawords += data.tx_bytes >> 32;
335#endif
336 if (gigawords &&
337 !radius_msg_add_attr_int32(
338 msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
339 gigawords)) {
340 printf("Could not add Acct-Output-Gigawords\n");
341 goto fail;
342 }
343 }
344
345 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
346 now.sec)) {
347 printf("Could not add Event-Timestamp\n");
348 goto fail;
349 }
350
351 if (eloop_terminated())
352 cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
353
354 if (stop && cause &&
355 !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
356 cause)) {
357 printf("Could not add Acct-Terminate-Cause\n");
358 goto fail;
359 }
360
361 radius_client_send(hapd->radius, msg,
362 stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
363 sta->addr);
364 return;
365
366 fail:
367 radius_msg_free(msg);
368}
369
370
371/**
372 * accounting_sta_interim - Send a interim STA accounting report
373 * @hapd: hostapd BSS data
374 * @sta: The station
375 */
376void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
377{
378 if (sta->acct_session_started)
379 accounting_sta_report(hapd, sta, 0);
380}
381
382
383/**
384 * accounting_sta_stop - Stop STA accounting
385 * @hapd: hostapd BSS data
386 * @sta: The station
387 */
388void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
389{
390 if (sta->acct_session_started) {
391 accounting_sta_report(hapd, sta, 1);
392 eloop_cancel_timeout(accounting_interim_update, hapd, sta);
393 hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
394 HOSTAPD_LEVEL_INFO,
395 "stopped accounting session %08X-%08X",
396 sta->acct_session_id_hi,
397 sta->acct_session_id_lo);
398 sta->acct_session_started = 0;
399 }
400}
401
402
403static void accounting_sta_get_id(struct hostapd_data *hapd,
404 struct sta_info *sta)
405{
406 sta->acct_session_id_lo = hapd->acct_session_id_lo++;
407 if (hapd->acct_session_id_lo == 0) {
408 hapd->acct_session_id_hi++;
409 }
410 sta->acct_session_id_hi = hapd->acct_session_id_hi;
411}
412
413
414/**
415 * accounting_receive - Process the RADIUS frames from Accounting Server
416 * @msg: RADIUS response message
417 * @req: RADIUS request message
418 * @shared_secret: RADIUS shared secret
419 * @shared_secret_len: Length of shared_secret in octets
420 * @data: Context data (struct hostapd_data *)
421 * Returns: Processing status
422 */
423static RadiusRxResult
424accounting_receive(struct radius_msg *msg, struct radius_msg *req,
425 const u8 *shared_secret, size_t shared_secret_len,
426 void *data)
427{
428 if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
429 printf("Unknown RADIUS message code\n");
430 return RADIUS_RX_UNKNOWN;
431 }
432
433 if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
434 printf("Incoming RADIUS packet did not have correct "
435 "Authenticator - dropped\n");
436 return RADIUS_RX_INVALID_AUTHENTICATOR;
437 }
438
439 return RADIUS_RX_PROCESSED;
440}
441
442
443static void accounting_report_state(struct hostapd_data *hapd, int on)
444{
445 struct radius_msg *msg;
446
447 if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
448 return;
449
450 /* Inform RADIUS server that accounting will start/stop so that the
451 * server can close old accounting sessions. */
452 msg = accounting_msg(hapd, NULL,
453 on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
454 RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
455 if (!msg)
456 return;
457
458 if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
459 RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
460 {
461 printf("Could not add Acct-Terminate-Cause\n");
462 radius_msg_free(msg);
463 return;
464 }
465
466 radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
467}
468
469
470/**
471 * accounting_init: Initialize accounting
472 * @hapd: hostapd BSS data
473 * Returns: 0 on success, -1 on failure
474 */
475int accounting_init(struct hostapd_data *hapd)
476{
477 struct os_time now;
478
479 /* Acct-Session-Id should be unique over reboots. If reliable clock is
480 * not available, this could be replaced with reboot counter, etc. */
481 os_get_time(&now);
482 hapd->acct_session_id_hi = now.sec;
483
484 if (radius_client_register(hapd->radius, RADIUS_ACCT,
485 accounting_receive, hapd))
486 return -1;
487
488 accounting_report_state(hapd, 1);
489
490 return 0;
491}
492
493
494/**
495 * accounting_deinit: Deinitilize accounting
496 * @hapd: hostapd BSS data
497 */
498void accounting_deinit(struct hostapd_data *hapd)
499{
500 accounting_report_state(hapd, 0);
501}