2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "eap_radius_accounting.h"
17 #include "eap_radius_plugin.h"
21 #include <radius_message.h>
22 #include <radius_client.h>
24 #include <collections/hashtable.h>
25 #include <threading/mutex.h>
27 typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t
;
30 * Private data of an eap_radius_accounting_t object.
32 struct private_eap_radius_accounting_t
{
35 * Public eap_radius_accounting_t interface.
37 eap_radius_accounting_t
public;
40 * Hashtable with sessions, IKE_SA unique id => entry_t
42 hashtable_t
*sessions
;
45 * Mutex to lock sessions
56 * Hashtable entry with usage stats
59 /** RADIUS accounting session ID */
61 /** number of sent/received octets/packets */
66 /** session creation time */
71 * Accounting message status types
74 ACCT_STATUS_START
= 1,
76 ACCT_STATUS_INTERIM_UPDATE
= 3,
77 ACCT_STATUS_ACCOUNTING_ON
= 7,
78 ACCT_STATUS_ACCOUNTING_OFF
= 8,
79 } radius_acct_status_t
;
82 * Hashtable hash function
84 static u_int
hash(uintptr_t key
)
90 * Hashtable equals function
92 static bool equals(uintptr_t a
, uintptr_t b
)
98 * Update usage counter when a CHILD_SA rekeys/goes down
100 static void update_usage(private_eap_radius_accounting_t
*this,
101 ike_sa_t
*ike_sa
, child_sa_t
*child_sa
)
103 u_int64_t bytes_in
, bytes_out
, packets_in
, packets_out
;
106 child_sa
->get_usestats(child_sa
, FALSE
, NULL
, &bytes_out
, &packets_out
);
107 child_sa
->get_usestats(child_sa
, TRUE
, NULL
, &bytes_in
, &packets_in
);
109 this->mutex
->lock(this->mutex
);
110 entry
= this->sessions
->get(this->sessions
,
111 (void*)(uintptr_t)ike_sa
->get_unique_id(ike_sa
));
114 entry
->bytes
.sent
+= bytes_out
;
115 entry
->bytes
.received
+= bytes_in
;
116 entry
->packets
.sent
+= packets_out
;
117 entry
->packets
.received
+= packets_in
;
119 this->mutex
->unlock(this->mutex
);
123 * Send a RADIUS message, wait for response
125 static bool send_message(private_eap_radius_accounting_t
*this,
126 radius_message_t
*request
)
128 radius_message_t
*response
;
129 radius_client_t
*client
;
132 client
= eap_radius_create_client();
135 response
= client
->request(client
, request
);
138 ack
= response
->get_code(response
) == RMC_ACCOUNTING_RESPONSE
;
139 response
->destroy(response
);
143 charon
->bus
->alert(charon
->bus
, ALERT_RADIUS_NOT_RESPONDING
);
145 client
->destroy(client
);
151 * Add common IKE_SA parameters to RADIUS account message
153 static void add_ike_sa_parameters(radius_message_t
*message
, ike_sa_t
*ike_sa
)
155 enumerator_t
*enumerator
;
160 snprintf(buf
, sizeof(buf
), "%Y", ike_sa
->get_other_eap_id(ike_sa
));
161 message
->add(message
, RAT_USER_NAME
, chunk_create(buf
, strlen(buf
)));
162 snprintf(buf
, sizeof(buf
), "%#H", ike_sa
->get_other_host(ike_sa
));
163 message
->add(message
, RAT_CALLING_STATION_ID
, chunk_create(buf
, strlen(buf
)));
165 enumerator
= ike_sa
->create_virtual_ip_enumerator(ike_sa
, FALSE
);
166 while (enumerator
->enumerate(enumerator
, &vip
))
168 switch (vip
->get_family(vip
))
171 message
->add(message
, RAT_FRAMED_IP_ADDRESS
,
172 vip
->get_address(vip
));
175 /* we currently assign /128 prefixes, only (reserved, length) */
176 data
= chunk_from_chars(0, 128);
177 data
= chunk_cata("cc", data
, vip
->get_address(vip
));
178 message
->add(message
, RAT_FRAMED_IPV6_PREFIX
, data
);
184 enumerator
->destroy(enumerator
);
188 * Send an accounting start message
190 static void send_start(private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
)
192 radius_message_t
*message
;
196 id
= ike_sa
->get_unique_id(ike_sa
);
198 .created
= time_monotonic(NULL
),
200 snprintf(entry
->sid
, sizeof(entry
->sid
), "%u-%u", this->prefix
, id
);
202 message
= radius_message_create(RMC_ACCOUNTING_REQUEST
);
203 value
= htonl(ACCT_STATUS_START
);
204 message
->add(message
, RAT_ACCT_STATUS_TYPE
, chunk_from_thing(value
));
205 message
->add(message
, RAT_ACCT_SESSION_ID
,
206 chunk_create(entry
->sid
, strlen(entry
->sid
)));
207 add_ike_sa_parameters(message
, ike_sa
);
208 if (send_message(this, message
))
210 this->mutex
->lock(this->mutex
);
211 entry
= this->sessions
->put(this->sessions
, (void*)(uintptr_t)id
, entry
);
212 this->mutex
->unlock(this->mutex
);
214 message
->destroy(message
);
219 * Send an account stop message
221 static void send_stop(private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
)
223 radius_message_t
*message
;
227 id
= ike_sa
->get_unique_id(ike_sa
);
228 this->mutex
->lock(this->mutex
);
229 entry
= this->sessions
->remove(this->sessions
, (void*)(uintptr_t)id
);
230 this->mutex
->unlock(this->mutex
);
233 message
= radius_message_create(RMC_ACCOUNTING_REQUEST
);
234 value
= htonl(ACCT_STATUS_STOP
);
235 message
->add(message
, RAT_ACCT_STATUS_TYPE
, chunk_from_thing(value
));
236 message
->add(message
, RAT_ACCT_SESSION_ID
,
237 chunk_create(entry
->sid
, strlen(entry
->sid
)));
238 add_ike_sa_parameters(message
, ike_sa
);
240 value
= htonl(entry
->bytes
.sent
);
241 message
->add(message
, RAT_ACCT_OUTPUT_OCTETS
, chunk_from_thing(value
));
242 value
= htonl(entry
->bytes
.sent
>> 32);
245 message
->add(message
, RAT_ACCT_OUTPUT_GIGAWORDS
,
246 chunk_from_thing(value
));
248 value
= htonl(entry
->packets
.sent
);
249 message
->add(message
, RAT_ACCT_OUTPUT_PACKETS
, chunk_from_thing(value
));
251 value
= htonl(entry
->bytes
.received
);
252 message
->add(message
, RAT_ACCT_INPUT_OCTETS
, chunk_from_thing(value
));
253 value
= htonl(entry
->bytes
.received
>> 32);
256 message
->add(message
, RAT_ACCT_INPUT_GIGAWORDS
,
257 chunk_from_thing(value
));
259 value
= htonl(entry
->packets
.received
);
260 message
->add(message
, RAT_ACCT_INPUT_PACKETS
, chunk_from_thing(value
));
262 value
= htonl(time_monotonic(NULL
) - entry
->created
);
263 message
->add(message
, RAT_ACCT_SESSION_TIME
, chunk_from_thing(value
));
265 send_message(this, message
);
266 message
->destroy(message
);
271 METHOD(listener_t
, ike_updown
, bool,
272 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
, bool up
)
276 enumerator_t
*enumerator
;
277 child_sa_t
*child_sa
;
279 /* update usage for all children just before sending stop */
280 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
281 while (enumerator
->enumerate(enumerator
, &child_sa
))
283 update_usage(this, ike_sa
, child_sa
);
285 enumerator
->destroy(enumerator
);
287 send_stop(this, ike_sa
);
292 METHOD(listener_t
, message_hook
, bool,
293 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
294 message_t
*message
, bool incoming
, bool plain
)
296 /* start accounting here, virtual IP now is set */
297 if (plain
&& ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
&&
298 !incoming
&& !message
->get_request(message
))
300 if (ike_sa
->get_version(ike_sa
) == IKEV1
&&
301 message
->get_exchange_type(message
) == TRANSACTION
)
303 send_start(this, ike_sa
);
305 if (ike_sa
->get_version(ike_sa
) == IKEV2
&&
306 message
->get_exchange_type(message
) == IKE_AUTH
)
308 send_start(this, ike_sa
);
314 METHOD(listener_t
, ike_rekey
, bool,
315 private_eap_radius_accounting_t
*this, ike_sa_t
*old
, ike_sa_t
*new)
319 this->mutex
->lock(this->mutex
);
320 entry
= this->sessions
->remove(this->sessions
,
321 (void*)(uintptr_t)old
->get_unique_id(old
));
324 entry
= this->sessions
->put(this->sessions
,
325 (void*)(uintptr_t)new->get_unique_id(new), entry
);
331 this->mutex
->unlock(this->mutex
);
336 METHOD(listener_t
, child_rekey
, bool,
337 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
338 child_sa_t
*old
, child_sa_t
*new)
340 update_usage(this, ike_sa
, old
);
345 METHOD(listener_t
, child_updown
, bool,
346 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
347 child_sa_t
*child_sa
, bool up
)
349 if (!up
&& ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
)
351 update_usage(this, ike_sa
, child_sa
);
356 METHOD(eap_radius_accounting_t
, destroy
, void,
357 private_eap_radius_accounting_t
*this)
359 this->mutex
->destroy(this->mutex
);
360 this->sessions
->destroy(this->sessions
);
367 eap_radius_accounting_t
*eap_radius_accounting_create()
369 private_eap_radius_accounting_t
*this;
374 .ike_updown
= _ike_updown
,
375 .ike_rekey
= _ike_rekey
,
376 .message
= _message_hook
,
377 .child_updown
= _child_updown
,
378 .child_rekey
= _child_rekey
,
382 /* use system time as Session ID prefix */
383 .prefix
= (u_int32_t
)time(NULL
),
384 .sessions
= hashtable_create((hashtable_hash_t
)hash
,
385 (hashtable_equals_t
)equals
, 32),
386 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
389 return &this->public;