1 |
|
/* |
2 |
|
* hostapd / RADIUS Accounting |
3 |
< |
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> |
3 |
> |
* Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi> |
4 |
|
* |
5 |
< |
* This program is free software; you can redistribute it and/or modify |
6 |
< |
* it under the terms of the GNU General Public License version 2 as |
7 |
< |
* published by the Free Software Foundation. |
8 |
< |
* |
9 |
< |
* Alternatively, this software may be distributed under the terms of BSD |
10 |
< |
* license. |
11 |
< |
* |
12 |
< |
* See README and COPYING for more details. |
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" |
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 |
|
|
26 |
|
* input/output octets and updates Acct-{Input,Output}-Gigawords. */ |
27 |
|
#define ACCT_DEFAULT_UPDATE_INTERVAL 300 |
28 |
|
|
29 |
< |
static void accounting_sta_get_id(struct hostapd_data *hapd, |
30 |
< |
struct sta_info *sta); |
29 |
> |
static void accounting_sta_interim(struct hostapd_data *hapd, |
30 |
> |
struct sta_info *sta); |
31 |
|
|
32 |
|
|
33 |
|
static struct radius_msg * accounting_msg(struct hostapd_data *hapd, |
39 |
|
u8 *val; |
40 |
|
size_t len; |
41 |
|
int i; |
42 |
+ |
struct wpabuf *b; |
43 |
|
|
44 |
|
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, |
45 |
|
radius_client_get_id(hapd->radius)); |
68 |
|
goto fail; |
69 |
|
} |
70 |
|
|
71 |
< |
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, |
71 |
> |
if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, |
72 |
> |
RADIUS_ATTR_ACCT_AUTHENTIC) && |
73 |
> |
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, |
74 |
|
hapd->conf->ieee802_1x ? |
75 |
|
RADIUS_ACCT_AUTHENTIC_RADIUS : |
76 |
|
RADIUS_ACCT_AUTHENTIC_LOCAL)) { |
79 |
|
} |
80 |
|
|
81 |
|
if (sta) { |
82 |
+ |
/* Use 802.1X identity if available */ |
83 |
|
val = ieee802_1x_get_identity(sta->eapol_sm, &len); |
84 |
+ |
|
85 |
+ |
/* Use RADIUS ACL identity if 802.1X provides no identity */ |
86 |
+ |
if (!val && sta->identity) { |
87 |
+ |
val = (u8 *) sta->identity; |
88 |
+ |
len = os_strlen(sta->identity); |
89 |
+ |
} |
90 |
+ |
|
91 |
+ |
/* Use STA MAC if neither 802.1X nor RADIUS ACL provided |
92 |
+ |
* identity */ |
93 |
|
if (!val) { |
94 |
|
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, |
95 |
|
MAC2STR(sta->addr)); |
104 |
|
} |
105 |
|
} |
106 |
|
|
107 |
< |
if (hapd->conf->own_ip_addr.af == AF_INET && |
108 |
< |
!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, |
101 |
< |
(u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { |
102 |
< |
printf("Could not add NAS-IP-Address\n"); |
107 |
> |
if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, |
108 |
> |
msg) < 0) |
109 |
|
goto fail; |
104 |
– |
} |
110 |
|
|
106 |
– |
#ifdef CONFIG_IPV6 |
107 |
– |
if (hapd->conf->own_ip_addr.af == AF_INET6 && |
108 |
– |
!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, |
109 |
– |
(u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { |
110 |
– |
printf("Could not add NAS-IPv6-Address\n"); |
111 |
– |
goto fail; |
112 |
– |
} |
113 |
– |
#endif /* CONFIG_IPV6 */ |
114 |
– |
|
115 |
– |
if (hapd->conf->nas_identifier && |
116 |
– |
!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, |
117 |
– |
(u8 *) hapd->conf->nas_identifier, |
118 |
– |
os_strlen(hapd->conf->nas_identifier))) { |
119 |
– |
printf("Could not add NAS-Identifier\n"); |
120 |
– |
goto fail; |
121 |
– |
} |
122 |
– |
|
123 |
– |
if (sta && |
124 |
– |
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { |
125 |
– |
printf("Could not add NAS-Port\n"); |
126 |
– |
goto fail; |
127 |
– |
} |
128 |
– |
|
129 |
– |
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", |
130 |
– |
MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); |
131 |
– |
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, |
132 |
– |
(u8 *) buf, os_strlen(buf))) { |
133 |
– |
printf("Could not add Called-Station-Id\n"); |
134 |
– |
goto fail; |
135 |
– |
} |
136 |
– |
|
111 |
|
if (sta) { |
138 |
– |
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, |
139 |
– |
MAC2STR(sta->addr)); |
140 |
– |
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, |
141 |
– |
(u8 *) buf, os_strlen(buf))) { |
142 |
– |
printf("Could not add Calling-Station-Id\n"); |
143 |
– |
goto fail; |
144 |
– |
} |
145 |
– |
|
146 |
– |
if (!radius_msg_add_attr_int32( |
147 |
– |
msg, RADIUS_ATTR_NAS_PORT_TYPE, |
148 |
– |
RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { |
149 |
– |
printf("Could not add NAS-Port-Type\n"); |
150 |
– |
goto fail; |
151 |
– |
} |
152 |
– |
|
153 |
– |
os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", |
154 |
– |
radius_sta_rate(hapd, sta) / 2, |
155 |
– |
(radius_sta_rate(hapd, sta) & 1) ? ".5" : "", |
156 |
– |
radius_mode_txt(hapd)); |
157 |
– |
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, |
158 |
– |
(u8 *) buf, os_strlen(buf))) { |
159 |
– |
printf("Could not add Connect-Info\n"); |
160 |
– |
goto fail; |
161 |
– |
} |
162 |
– |
|
112 |
|
for (i = 0; ; i++) { |
113 |
|
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, |
114 |
|
i); |
121 |
|
goto fail; |
122 |
|
} |
123 |
|
} |
124 |
+ |
|
125 |
+ |
b = ieee802_1x_get_radius_cui(sta->eapol_sm); |
126 |
+ |
if (b && |
127 |
+ |
!radius_msg_add_attr(msg, |
128 |
+ |
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, |
129 |
+ |
wpabuf_head(b), wpabuf_len(b))) { |
130 |
+ |
wpa_printf(MSG_ERROR, "Could not add CUI"); |
131 |
+ |
goto fail; |
132 |
+ |
} |
133 |
+ |
|
134 |
+ |
if (!b && sta->radius_cui && |
135 |
+ |
!radius_msg_add_attr(msg, |
136 |
+ |
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, |
137 |
+ |
(u8 *) sta->radius_cui, |
138 |
+ |
os_strlen(sta->radius_cui))) { |
139 |
+ |
wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); |
140 |
+ |
goto fail; |
141 |
+ |
} |
142 |
|
} |
143 |
|
|
144 |
|
return msg; |
153 |
|
struct sta_info *sta, |
154 |
|
struct hostap_sta_driver_data *data) |
155 |
|
{ |
156 |
< |
if (hapd->drv.read_sta_data(hapd, data, sta->addr)) |
156 |
> |
if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) |
157 |
|
return -1; |
158 |
|
|
159 |
|
if (sta->last_rx_bytes > data->rx_bytes) |
202 |
|
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) |
203 |
|
{ |
204 |
|
struct radius_msg *msg; |
205 |
+ |
struct os_time t; |
206 |
|
int interval; |
207 |
|
|
208 |
|
if (sta->acct_session_started) |
209 |
|
return; |
210 |
|
|
243 |
– |
accounting_sta_get_id(hapd, sta); |
211 |
|
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, |
212 |
|
HOSTAPD_LEVEL_INFO, |
213 |
|
"starting accounting session %08X-%08X", |
214 |
|
sta->acct_session_id_hi, sta->acct_session_id_lo); |
215 |
|
|
216 |
< |
time(&sta->acct_session_start); |
216 |
> |
os_get_time(&t); |
217 |
> |
sta->acct_session_start = t.sec; |
218 |
|
sta->last_rx_bytes = sta->last_tx_bytes = 0; |
219 |
|
sta->acct_input_gigawords = sta->acct_output_gigawords = 0; |
220 |
< |
hapd->drv.sta_clear_stats(hapd, sta->addr); |
220 |
> |
hostapd_drv_sta_clear_stats(hapd, sta->addr); |
221 |
|
|
222 |
|
if (!hapd->conf->radius->acct_server) |
223 |
|
return; |
230 |
|
hapd, sta); |
231 |
|
|
232 |
|
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); |
233 |
< |
if (msg) |
234 |
< |
radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); |
233 |
> |
if (msg && |
234 |
> |
radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) |
235 |
> |
radius_msg_free(msg); |
236 |
|
|
237 |
|
sta->acct_session_started = 1; |
238 |
|
} |
244 |
|
struct radius_msg *msg; |
245 |
|
int cause = sta->acct_terminate_cause; |
246 |
|
struct hostap_sta_driver_data data; |
247 |
+ |
struct os_time now; |
248 |
|
u32 gigawords; |
249 |
|
|
250 |
|
if (!hapd->conf->radius->acct_server) |
258 |
|
return; |
259 |
|
} |
260 |
|
|
261 |
+ |
os_get_time(&now); |
262 |
|
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, |
263 |
< |
time(NULL) - sta->acct_session_start)) { |
263 |
> |
now.sec - sta->acct_session_start)) { |
264 |
|
printf("Could not add Acct-Session-Time\n"); |
265 |
|
goto fail; |
266 |
|
} |
315 |
|
} |
316 |
|
|
317 |
|
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, |
318 |
< |
time(NULL))) { |
318 |
> |
now.sec)) { |
319 |
|
printf("Could not add Event-Timestamp\n"); |
320 |
|
goto fail; |
321 |
|
} |
330 |
|
goto fail; |
331 |
|
} |
332 |
|
|
333 |
< |
radius_client_send(hapd->radius, msg, |
334 |
< |
stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, |
335 |
< |
sta->addr); |
333 |
> |
if (radius_client_send(hapd->radius, msg, |
334 |
> |
stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, |
335 |
> |
sta->addr) < 0) |
336 |
> |
goto fail; |
337 |
|
return; |
338 |
|
|
339 |
|
fail: |
346 |
|
* @hapd: hostapd BSS data |
347 |
|
* @sta: The station |
348 |
|
*/ |
349 |
< |
void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) |
349 |
> |
static void accounting_sta_interim(struct hostapd_data *hapd, |
350 |
> |
struct sta_info *sta) |
351 |
|
{ |
352 |
|
if (sta->acct_session_started) |
353 |
|
accounting_sta_report(hapd, sta, 0); |
374 |
|
} |
375 |
|
|
376 |
|
|
377 |
< |
static void accounting_sta_get_id(struct hostapd_data *hapd, |
377 |
> |
void accounting_sta_get_id(struct hostapd_data *hapd, |
378 |
|
struct sta_info *sta) |
379 |
|
{ |
380 |
|
sta->acct_session_id_lo = hapd->acct_session_id_lo++; |
437 |
|
return; |
438 |
|
} |
439 |
|
|
440 |
< |
radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); |
440 |
> |
if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) |
441 |
> |
radius_msg_free(msg); |
442 |
|
} |
443 |
|
|
444 |
|
|
449 |
|
*/ |
450 |
|
int accounting_init(struct hostapd_data *hapd) |
451 |
|
{ |
452 |
+ |
struct os_time now; |
453 |
+ |
|
454 |
|
/* Acct-Session-Id should be unique over reboots. If reliable clock is |
455 |
|
* not available, this could be replaced with reboot counter, etc. */ |
456 |
< |
hapd->acct_session_id_hi = time(NULL); |
456 |
> |
os_get_time(&now); |
457 |
> |
hapd->acct_session_id_hi = now.sec; |
458 |
|
|
459 |
|
if (radius_client_register(hapd->radius, RADIUS_ACCT, |
460 |
|
accounting_receive, hapd)) |