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 <utils/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
);
106 child_sa
->get_usestats(child_sa
, TRUE
, NULL
, &received
);
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
)
156 snprintf(buf
, sizeof(buf
), "%Y", ike_sa
->get_other_eap_id(ike_sa
));
157 message
->add(message
, RAT_USER_NAME
, chunk_create(buf
, strlen(buf
)));
158 snprintf(buf
, sizeof(buf
), "%#H", ike_sa
->get_other_host(ike_sa
));
159 message
->add(message
, RAT_CALLING_STATION_ID
, chunk_create(buf
, strlen(buf
)));
160 vip
= ike_sa
->get_virtual_ip(ike_sa
, FALSE
);
161 if (vip
&& vip
->get_family(vip
) == AF_INET
)
163 message
->add(message
, RAT_FRAMED_IP_ADDRESS
, vip
->get_address(vip
));
165 if (vip
&& vip
->get_family(vip
) == AF_INET6
)
167 /* we currently assign /128 prefixes, only (reserved, length) */
168 data
= chunk_from_chars(0, 128);
169 data
= chunk_cata("cc", data
, vip
->get_address(vip
));
170 message
->add(message
, RAT_FRAMED_IPV6_PREFIX
, data
);
175 * Send an accounting start message
177 static void send_start(private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
)
179 radius_message_t
*message
;
183 id
= ike_sa
->get_unique_id(ike_sa
);
185 .created
= time_monotonic(NULL
),
187 snprintf(entry
->sid
, sizeof(entry
->sid
), "%u-%u", this->prefix
, id
);
189 message
= radius_message_create(RMC_ACCOUNTING_REQUEST
);
190 value
= htonl(ACCT_STATUS_START
);
191 message
->add(message
, RAT_ACCT_STATUS_TYPE
, chunk_from_thing(value
));
192 message
->add(message
, RAT_ACCT_SESSION_ID
,
193 chunk_create(entry
->sid
, strlen(entry
->sid
)));
194 add_ike_sa_parameters(message
, ike_sa
);
195 if (send_message(this, message
))
197 this->mutex
->lock(this->mutex
);
198 entry
= this->sessions
->put(this->sessions
, (void*)(uintptr_t)id
, entry
);
199 this->mutex
->unlock(this->mutex
);
202 message
->destroy(message
);
206 * Send an account stop message
208 static void send_stop(private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
)
210 radius_message_t
*message
;
214 id
= ike_sa
->get_unique_id(ike_sa
);
215 this->mutex
->lock(this->mutex
);
216 entry
= this->sessions
->remove(this->sessions
, (void*)(uintptr_t)id
);
217 this->mutex
->unlock(this->mutex
);
220 message
= radius_message_create(RMC_ACCOUNTING_REQUEST
);
221 value
= htonl(ACCT_STATUS_STOP
);
222 message
->add(message
, RAT_ACCT_STATUS_TYPE
, chunk_from_thing(value
));
223 message
->add(message
, RAT_ACCT_SESSION_ID
,
224 chunk_create(entry
->sid
, strlen(entry
->sid
)));
225 add_ike_sa_parameters(message
, ike_sa
);
226 value
= htonl(entry
->sent
);
227 message
->add(message
, RAT_ACCT_OUTPUT_OCTETS
, chunk_from_thing(value
));
228 value
= htonl(entry
->sent
>> 32);
231 message
->add(message
, RAT_ACCT_OUTPUT_GIGAWORDS
,
232 chunk_from_thing(value
));
234 value
= htonl(entry
->received
);
235 message
->add(message
, RAT_ACCT_INPUT_OCTETS
, chunk_from_thing(value
));
236 value
= htonl(entry
->received
>> 32);
239 message
->add(message
, RAT_ACCT_INPUT_GIGAWORDS
,
240 chunk_from_thing(value
));
242 value
= htonl(time_monotonic(NULL
) - entry
->created
);
243 message
->add(message
, RAT_ACCT_SESSION_TIME
, chunk_from_thing(value
));
245 send_message(this, message
);
246 message
->destroy(message
);
251 METHOD(listener_t
, ike_updown
, bool,
252 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
, bool up
)
256 enumerator_t
*enumerator
;
257 child_sa_t
*child_sa
;
259 /* update usage for all children just before sending stop */
260 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
261 while (enumerator
->enumerate(enumerator
, &child_sa
))
263 update_usage(this, ike_sa
, child_sa
);
265 enumerator
->destroy(enumerator
);
267 send_stop(this, ike_sa
);
272 METHOD(listener_t
, message_hook
, bool,
273 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
274 message_t
*message
, bool incoming
, bool plain
)
276 /* start accounting here, virtual IP now is set */
277 if (plain
&& ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
&&
278 message
->get_exchange_type(message
) == IKE_AUTH
&&
279 !incoming
&& !message
->get_request(message
))
281 send_start(this, ike_sa
);
286 METHOD(listener_t
, child_rekey
, bool,
287 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
288 child_sa_t
*old
, child_sa_t
*new)
290 update_usage(this, ike_sa
, old
);
295 METHOD(listener_t
, child_updown
, bool,
296 private_eap_radius_accounting_t
*this, ike_sa_t
*ike_sa
,
297 child_sa_t
*child_sa
, bool up
)
299 if (!up
&& ike_sa
->get_state(ike_sa
) == IKE_ESTABLISHED
)
301 update_usage(this, ike_sa
, child_sa
);
306 METHOD(eap_radius_accounting_t
, destroy
, void,
307 private_eap_radius_accounting_t
*this)
309 this->mutex
->destroy(this->mutex
);
310 this->sessions
->destroy(this->sessions
);
317 eap_radius_accounting_t
*eap_radius_accounting_create()
319 private_eap_radius_accounting_t
*this;
324 .ike_updown
= _ike_updown
,
325 .message
= _message_hook
,
326 .child_updown
= _child_updown
,
327 .child_rekey
= _child_rekey
,
331 /* use system time as Session ID prefix */
332 .prefix
= (u_int32_t
)time(NULL
),
333 .sessions
= hashtable_create((hashtable_hash_t
)hash
,
334 (hashtable_equals_t
)equals
, 32),
335 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
338 return &this->public;