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 octets sent */
63 /** number of octets received */
65 /** session creation time */
70 * Accounting message status types
73 ACCT_STATUS_START
= 1,
75 ACCT_STATUS_INTERIM_UPDATE
= 3,
76 ACCT_STATUS_ACCOUNTING_ON
= 7,
77 ACCT_STATUS_ACCOUNTING_OFF
= 8,
78 } radius_acct_status_t
;
81 * Hashtable hash function
83 static u_int
hash(uintptr_t key
)
89 * Hashtable equals function
91 static bool equals(uintptr_t a
, uintptr_t b
)
97 * Update usage counter when a CHILD_SA rekeys/goes down
99 static void update_usage(private_eap_radius_accounting_t
*this,
100 ike_sa_t
*ike_sa
, child_sa_t
*child_sa
)
102 u_int64_t sent
, received
;
105 child_sa
->get_usestats(child_sa
, FALSE
, NULL
, &sent
, NULL
);
106 child_sa
->get_usestats(child_sa
, TRUE
, NULL
, &received
, NULL
);
108 this->mutex
->lock(this->mutex
);
109 entry
= this->sessions
->get(this->sessions
,
110 (void*)(uintptr_t)ike_sa
->get_unique_id(ike_sa
));
114 entry
->received
+= received
;
116 this->mutex
->unlock(this->mutex
);
120 * Send a RADIUS message, wait for response
122 static bool send_message(private_eap_radius_accounting_t
*this,
123 radius_message_t
*request
)
125 radius_message_t
*response
;
126 radius_client_t
*client
;
129 client
= eap_radius_create_client();
132 response
= client
->request(client
, request
);
135 ack
= response
->get_code(response
) == RMC_ACCOUNTING_RESPONSE
;
136 response
->destroy(response
);
140 charon
->bus
->alert(charon
->bus
, ALERT_RADIUS_NOT_RESPONDING
);
142 client
->destroy(client
);
148 * Add common IKE_SA parameters to RADIUS account message
150 static void add_ike_sa_parameters(radius_message_t
*message
, ike_sa_t
*ike_sa
)
152 enumerator_t
*enumerator
;
157 snprintf(buf
, sizeof(buf
), "%Y", ike_sa
->get_other_eap_id(ike_sa
));
158 message
->add(message
, RAT_USER_NAME
, chunk_create(buf
, strlen(buf
)));
159 snprintf(buf
, sizeof(buf
), "%#H", ike_sa
->get_other_host(ike_sa
));
160 message
->add(message
, RAT_CALLING_STATION_ID
, chunk_create(buf
, strlen(buf
)));
162 enumerator
= ike_sa
->create_virtual_ip_enumerator(ike_sa
, FALSE
);
163 while (enumerator
->enumerate(enumerator
, &vip
))
165 switch (vip
->get_family(vip
))
168 message
->add(message
, RAT_FRAMED_IP_ADDRESS
,
169 vip
->get_address(vip
));
172 /* we currently assign /128 prefixes, only (reserved, length) */
173 data
= chunk_from_chars(0, 128);
174 data
= chunk_cata("cc", data
, vip
->get_address(vip
));
175 message
->add(message
, RAT_FRAMED_IPV6_PREFIX
, data
);
181 enumerator
->destroy(enumerator
);
185 * Send an accounting start message
187 static void send_start(private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
)
189 radius_message_t
*message
;
193 id
= ike_sa
->get_unique_id(ike_sa
);
195 .created
= time_monotonic(NULL
),
197 snprintf(entry
->sid
, sizeof(entry
->sid
), "%u-%u", this->prefix
, id
);
199 message
= radius_message_create(RMC_ACCOUNTING_REQUEST
);
200 value
= htonl(ACCT_STATUS_START
);
201 message
->add(message
, RAT_ACCT_STATUS_TYPE
, chunk_from_thing(value
));
202 message
->add(message
, RAT_ACCT_SESSION_ID
,
203 chunk_create(entry
->sid
, strlen(entry
->sid
)));
204 add_ike_sa_parameters(message
, ike_sa
);
205 if (send_message(this, message
))
207 this->mutex
->lock(this->mutex
);
208 entry
= this->sessions
->put(this->sessions
, (void*)(uintptr_t)id
, entry
);
209 this->mutex
->unlock(this->mutex
);
211 message
->destroy(message
);
216 * Send an account stop message
218 static void send_stop(private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
)
220 radius_message_t
*message
;
224 id
= ike_sa
->get_unique_id(ike_sa
);
225 this->mutex
->lock(this->mutex
);
226 entry
= this->sessions
->remove(this->sessions
, (void*)(uintptr_t)id
);
227 this->mutex
->unlock(this->mutex
);
230 message
= radius_message_create(RMC_ACCOUNTING_REQUEST
);
231 value
= htonl(ACCT_STATUS_STOP
);
232 message
->add(message
, RAT_ACCT_STATUS_TYPE
, chunk_from_thing(value
));
233 message
->add(message
, RAT_ACCT_SESSION_ID
,
234 chunk_create(entry
->sid
, strlen(entry
->sid
)));
235 add_ike_sa_parameters(message
, ike_sa
);
236 value
= htonl(entry
->sent
);
237 message
->add(message
, RAT_ACCT_OUTPUT_OCTETS
, chunk_from_thing(value
));
238 value
= htonl(entry
->sent
>> 32);
241 message
->add(message
, RAT_ACCT_OUTPUT_GIGAWORDS
,
242 chunk_from_thing(value
));
244 value
= htonl(entry
->received
);
245 message
->add(message
, RAT_ACCT_INPUT_OCTETS
, chunk_from_thing(value
));
246 value
= htonl(entry
->received
>> 32);
249 message
->add(message
, RAT_ACCT_INPUT_GIGAWORDS
,
250 chunk_from_thing(value
));
252 value
= htonl(time_monotonic(NULL
) - entry
->created
);
253 message
->add(message
, RAT_ACCT_SESSION_TIME
, chunk_from_thing(value
));
255 send_message(this, message
);
256 message
->destroy(message
);
261 METHOD(listener_t
, ike_updown
, bool,
262 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
, bool up
)
266 enumerator_t
*enumerator
;
267 child_sa_t
*child_sa
;
269 /* update usage for all children just before sending stop */
270 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
271 while (enumerator
->enumerate(enumerator
, &child_sa
))
273 update_usage(this, ike_sa
, child_sa
);
275 enumerator
->destroy(enumerator
);
277 send_stop(this, ike_sa
);
282 METHOD(listener_t
, message_hook
, bool,
283 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
284 message_t
*message
, bool incoming
, bool plain
)
286 /* start accounting here, virtual IP now is set */
287 if (plain
&& ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
&&
288 !incoming
&& !message
->get_request(message
))
290 if (ike_sa
->get_version(ike_sa
) == IKEV1
&&
291 message
->get_exchange_type(message
) == TRANSACTION
)
293 send_start(this, ike_sa
);
295 if (ike_sa
->get_version(ike_sa
) == IKEV2
&&
296 message
->get_exchange_type(message
) == IKE_AUTH
)
298 send_start(this, ike_sa
);
304 METHOD(listener_t
, ike_rekey
, bool,
305 private_eap_radius_accounting_t
*this, ike_sa_t
*old
, ike_sa_t
*new)
309 this->mutex
->lock(this->mutex
);
310 entry
= this->sessions
->remove(this->sessions
,
311 (void*)(uintptr_t)old
->get_unique_id(old
));
314 entry
= this->sessions
->put(this->sessions
,
315 (void*)(uintptr_t)new->get_unique_id(new), entry
);
321 this->mutex
->unlock(this->mutex
);
326 METHOD(listener_t
, child_rekey
, bool,
327 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
328 child_sa_t
*old
, child_sa_t
*new)
330 update_usage(this, ike_sa
, old
);
335 METHOD(listener_t
, child_updown
, bool,
336 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
337 child_sa_t
*child_sa
, bool up
)
339 if (!up
&& ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
)
341 update_usage(this, ike_sa
, child_sa
);
346 METHOD(eap_radius_accounting_t
, destroy
, void,
347 private_eap_radius_accounting_t
*this)
349 this->mutex
->destroy(this->mutex
);
350 this->sessions
->destroy(this->sessions
);
357 eap_radius_accounting_t
*eap_radius_accounting_create()
359 private_eap_radius_accounting_t
*this;
364 .ike_updown
= _ike_updown
,
365 .ike_rekey
= _ike_rekey
,
366 .message
= _message_hook
,
367 .child_updown
= _child_updown
,
368 .child_rekey
= _child_rekey
,
372 /* use system time as Session ID prefix */
373 .prefix
= (u_int32_t
)time(NULL
),
374 .sessions
= hashtable_create((hashtable_hash_t
)hash
,
375 (hashtable_equals_t
)equals
, 32),
376 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
379 return &this->public;