eap-radius: Add cache for usage stats of expired/rekeyed SAs
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius_accounting.c
1 /*
2 * Copyright (C) 2015 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2012 Martin Willi
6 * Copyright (C) 2012 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "eap_radius_accounting.h"
20 #include "eap_radius_plugin.h"
21
22 #include <time.h>
23
24 #include <radius_message.h>
25 #include <radius_client.h>
26 #include <daemon.h>
27 #include <collections/array.h>
28 #include <collections/hashtable.h>
29 #include <threading/mutex.h>
30 #include <processing/jobs/callback_job.h>
31
32 typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t;
33
34 /**
35 * Private data of an eap_radius_accounting_t object.
36 */
37 struct private_eap_radius_accounting_t {
38
39 /**
40 * Public eap_radius_accounting_t interface.
41 */
42 eap_radius_accounting_t public;
43
44 /**
45 * Hashtable with sessions, ike_sa_id_t => entry_t
46 */
47 hashtable_t *sessions;
48
49 /**
50 * Mutex to lock sessions
51 */
52 mutex_t *mutex;
53
54 /**
55 * Session ID prefix
56 */
57 u_int32_t prefix;
58
59 /**
60 * Format string we use for Called/Calling-Station-Id for a host
61 */
62 char *station_id_fmt;
63
64 /**
65 * Disable accounting unless IKE_SA has at least one virtual IP
66 */
67 bool acct_req_vip;
68 };
69
70 /**
71 * Singleton instance of accounting
72 */
73 static private_eap_radius_accounting_t *singleton = NULL;
74
75 /**
76 * Acct-Terminate-Cause
77 */
78 typedef enum {
79 ACCT_CAUSE_USER_REQUEST = 1,
80 ACCT_CAUSE_LOST_CARRIER = 2,
81 ACCT_CAUSE_LOST_SERVICE = 3,
82 ACCT_CAUSE_IDLE_TIMEOUT = 4,
83 ACCT_CAUSE_SESSION_TIMEOUT = 5,
84 ACCT_CAUSE_ADMIN_RESET = 6,
85 ACCT_CAUSE_ADMIN_REBOOT = 7,
86 ACCT_CAUSE_PORT_ERROR = 8,
87 ACCT_CAUSE_NAS_ERROR = 9,
88 ACCT_CAUSE_NAS_REQUEST = 10,
89 ACCT_CAUSE_NAS_REBOOT = 11,
90 ACCT_CAUSE_PORT_UNNEEDED = 12,
91 ACCT_CAUSE_PORT_PREEMPTED = 13,
92 ACCT_CAUSE_PORT_SUSPENDED = 14,
93 ACCT_CAUSE_SERVICE_UNAVAILABLE = 15,
94 ACCT_CAUSE_CALLBACK = 16,
95 ACCT_CAUSE_USER_ERROR = 17,
96 ACCT_CAUSE_HOST_REQUEST = 18,
97 } radius_acct_terminate_cause_t;
98
99 /**
100 * Usage stats for a cached SAs
101 */
102 typedef struct {
103 /** unique CHILD_SA identifier */
104 u_int32_t id;
105 /** usage stats for this SA */
106 struct {
107 u_int64_t sent;
108 u_int64_t received;
109 } bytes, packets;
110 } sa_entry_t;
111
112 /**
113 * Hashtable entry with usage stats
114 */
115 typedef struct {
116 /** IKE_SA identifier this entry is stored under */
117 ike_sa_id_t *id;
118 /** RADIUS accounting session ID */
119 char sid[24];
120 /** number of sent/received octets/packets for expired SAs */
121 struct {
122 u_int64_t sent;
123 u_int64_t received;
124 } bytes, packets;
125 /** list of cached SAs, sa_entry_t (sorted by their unique ID) */
126 array_t *cached;
127 /** session creation time */
128 time_t created;
129 /** terminate cause */
130 radius_acct_terminate_cause_t cause;
131 /* interim interval and timestamp of last update */
132 struct {
133 u_int32_t interval;
134 time_t last;
135 } interim;
136 /** did we send Accounting-Start */
137 bool start_sent;
138 } entry_t;
139
140 /**
141 * Destroy an entry_t
142 */
143 static void destroy_entry(entry_t *this)
144 {
145 array_destroy_function(this->cached, (void*)free, NULL);
146 this->id->destroy(this->id);
147 free(this);
148 }
149
150 /**
151 * Accounting message status types
152 */
153 typedef enum {
154 ACCT_STATUS_START = 1,
155 ACCT_STATUS_STOP = 2,
156 ACCT_STATUS_INTERIM_UPDATE = 3,
157 ACCT_STATUS_ACCOUNTING_ON = 7,
158 ACCT_STATUS_ACCOUNTING_OFF = 8,
159 } radius_acct_status_t;
160
161 /**
162 * Hashtable hash function
163 */
164 static u_int hash(ike_sa_id_t *key)
165 {
166 return key->get_responder_spi(key);
167 }
168
169 /**
170 * Hashtable equals function
171 */
172 static bool equals(ike_sa_id_t *a, ike_sa_id_t *b)
173 {
174 return a->equals(a, b);
175 }
176
177 /**
178 * Sort cached SAs
179 */
180 static int sa_sort(const void *a, const void *b, void *user)
181 {
182 const sa_entry_t *ra = a, *rb = b;
183 return ra->id - rb->id;
184 }
185
186 /**
187 * Find a cached SA
188 */
189 static int sa_find(const void *a, const void *b)
190 {
191 return sa_sort(a, b, NULL);
192 }
193
194 /**
195 * Update usage counter when a CHILD_SA rekeys/goes down
196 */
197 static void update_usage(private_eap_radius_accounting_t *this,
198 ike_sa_t *ike_sa, child_sa_t *child_sa)
199 {
200 u_int64_t bytes_in, bytes_out, packets_in, packets_out;
201 entry_t *entry;
202 sa_entry_t *sa, lookup;
203
204 child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, &packets_out);
205 child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, &packets_in);
206
207 this->mutex->lock(this->mutex);
208 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
209 if (entry)
210 {
211 lookup.id = child_sa->get_unique_id(child_sa);
212 if (array_bsearch(entry->cached, &lookup, sa_find, &sa) == -1)
213 {
214 INIT(sa,
215 .id = lookup.id,
216 );
217 array_insert_create(&entry->cached, ARRAY_TAIL, sa);
218 array_sort(entry->cached, sa_sort, NULL);
219 }
220 sa->bytes.sent = bytes_out;
221 sa->bytes.received = bytes_in;
222 sa->packets.sent = packets_out;
223 sa->packets.received = packets_in;
224 }
225 this->mutex->unlock(this->mutex);
226 }
227
228 /**
229 * Send a RADIUS message, wait for response
230 */
231 static bool send_message(private_eap_radius_accounting_t *this,
232 radius_message_t *request)
233 {
234 radius_message_t *response;
235 radius_client_t *client;
236 bool ack = FALSE;
237
238 client = eap_radius_create_client();
239 if (client)
240 {
241 response = client->request(client, request);
242 if (response)
243 {
244 ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
245 response->destroy(response);
246 }
247 client->destroy(client);
248 }
249 return ack;
250 }
251
252 /**
253 * Add common IKE_SA parameters to RADIUS account message
254 */
255 static void add_ike_sa_parameters(private_eap_radius_accounting_t *this,
256 radius_message_t *message, ike_sa_t *ike_sa)
257 {
258 enumerator_t *enumerator;
259 host_t *vip, *host;
260 char buf[MAX_RADIUS_ATTRIBUTE_SIZE + 1];
261 chunk_t data;
262 u_int32_t value;
263
264 /* virtual NAS-Port-Type */
265 value = htonl(5);
266 message->add(message, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
267 /* framed ServiceType */
268 value = htonl(2);
269 message->add(message, RAT_SERVICE_TYPE, chunk_from_thing(value));
270
271 value = htonl(ike_sa->get_unique_id(ike_sa));
272 message->add(message, RAT_NAS_PORT, chunk_from_thing(value));
273 message->add(message, RAT_NAS_PORT_ID,
274 chunk_from_str(ike_sa->get_name(ike_sa)));
275
276 host = ike_sa->get_my_host(ike_sa);
277 data = host->get_address(host);
278 switch (host->get_family(host))
279 {
280 case AF_INET:
281 message->add(message, RAT_NAS_IP_ADDRESS, data);
282 break;
283 case AF_INET6:
284 message->add(message, RAT_NAS_IPV6_ADDRESS, data);
285 default:
286 break;
287 }
288 snprintf(buf, sizeof(buf), this->station_id_fmt, host);
289 message->add(message, RAT_CALLED_STATION_ID, chunk_from_str(buf));
290 host = ike_sa->get_other_host(ike_sa);
291 snprintf(buf, sizeof(buf), this->station_id_fmt, host);
292 message->add(message, RAT_CALLING_STATION_ID, chunk_from_str(buf));
293
294 snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
295 message->add(message, RAT_USER_NAME, chunk_from_str(buf));
296
297 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
298 while (enumerator->enumerate(enumerator, &vip))
299 {
300 switch (vip->get_family(vip))
301 {
302 case AF_INET:
303 message->add(message, RAT_FRAMED_IP_ADDRESS,
304 vip->get_address(vip));
305 break;
306 case AF_INET6:
307 /* we currently assign /128 prefixes, only (reserved, length) */
308 data = chunk_from_chars(0, 128);
309 data = chunk_cata("cc", data, vip->get_address(vip));
310 message->add(message, RAT_FRAMED_IPV6_PREFIX, data);
311 break;
312 default:
313 break;
314 }
315 }
316 enumerator->destroy(enumerator);
317 }
318
319 /**
320 * Get an existing or create a new entry from the locked session table
321 */
322 static entry_t* get_or_create_entry(private_eap_radius_accounting_t *this,
323 ike_sa_t *ike_sa)
324 {
325 ike_sa_id_t *id;
326 entry_t *entry;
327 time_t now;
328
329 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
330 if (!entry)
331 {
332 now = time_monotonic(NULL);
333 id = ike_sa->get_id(ike_sa);
334
335 INIT(entry,
336 .id = id->clone(id),
337 .created = now,
338 .interim = {
339 .last = now,
340 },
341 /* default terminate cause, if none other catched */
342 .cause = ACCT_CAUSE_USER_REQUEST,
343 );
344 snprintf(entry->sid, sizeof(entry->sid), "%u-%u",
345 this->prefix, ike_sa->get_unique_id(ike_sa));
346 this->sessions->put(this->sessions, entry->id, entry);
347 }
348 return entry;
349 }
350
351 /* forward declaration */
352 static void schedule_interim(private_eap_radius_accounting_t *this,
353 entry_t *entry);
354
355 /**
356 * Data passed to send_interim() using callback job
357 */
358 typedef struct {
359 /** reference to radius accounting */
360 private_eap_radius_accounting_t *this;
361 /** IKE_SA identifier to send interim update to */
362 ike_sa_id_t *id;
363 } interim_data_t;
364
365 /**
366 * Clean up interim data
367 */
368 void destroy_interim_data(interim_data_t *this)
369 {
370 this->id->destroy(this->id);
371 free(this);
372 }
373
374 /**
375 * Send an interim update for entry of given IKE_SA identifier
376 */
377 static job_requeue_t send_interim(interim_data_t *data)
378 {
379 private_eap_radius_accounting_t *this = data->this;
380 u_int64_t bytes_in = 0, bytes_out = 0, packets_in = 0, packets_out = 0;
381 u_int64_t bytes, packets;
382 radius_message_t *message = NULL;
383 enumerator_t *enumerator;
384 child_sa_t *child_sa;
385 ike_sa_t *ike_sa;
386 entry_t *entry;
387 u_int32_t value;
388 array_t *stats;
389 sa_entry_t *sa, *found;
390
391 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id);
392 if (!ike_sa)
393 {
394 return JOB_REQUEUE_NONE;
395 }
396 stats = array_create(0, 0);
397 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
398 while (enumerator->enumerate(enumerator, &child_sa))
399 {
400 INIT(sa,
401 .id = child_sa->get_unique_id(child_sa),
402 );
403 array_insert(stats, ARRAY_TAIL, sa);
404 array_sort(stats, sa_sort, NULL);
405
406 child_sa->get_usestats(child_sa, FALSE, NULL, &bytes, &packets);
407 sa->bytes.sent = bytes;
408 sa->packets.sent = packets;
409 bytes_out += bytes;
410 packets_out += packets;
411 child_sa->get_usestats(child_sa, TRUE, NULL, &bytes, &packets);
412 sa->bytes.received = bytes;
413 sa->packets.received = packets;
414 bytes_in += bytes;
415 packets_in += packets;
416 }
417 enumerator->destroy(enumerator);
418 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
419
420 /* avoid any races by returning IKE_SA before acquiring lock */
421
422 this->mutex->lock(this->mutex);
423 entry = this->sessions->get(this->sessions, data->id);
424 if (entry)
425 {
426 entry->interim.last = time_monotonic(NULL);
427
428 enumerator = array_create_enumerator(entry->cached);
429 while (enumerator->enumerate(enumerator, &sa))
430 {
431 if (array_bsearch(stats, sa, sa_find, &found) != -1)
432 {
433 /* SA is still around, update stats (e.g. for IKEv1 where
434 * SA might get used even after rekeying) */
435 sa->bytes = found->bytes;
436 sa->packets = found->packets;
437 }
438 else
439 {
440 /* SA is gone, add its latest stats to the total for this IKE_SA
441 * and remove the cache entry */
442 entry->bytes.sent += sa->bytes.sent;
443 entry->bytes.received += sa->bytes.received;
444 entry->packets.sent += sa->packets.sent;
445 entry->packets.received += sa->packets.received;
446 array_remove_at(entry->cached, enumerator);
447 free(sa);
448 }
449 }
450 enumerator->destroy(enumerator);
451
452 bytes_in += entry->bytes.received;
453 bytes_out += entry->bytes.sent;
454 packets_in += entry->packets.received;
455 packets_out += entry->packets.sent;
456
457 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
458 value = htonl(ACCT_STATUS_INTERIM_UPDATE);
459 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
460 message->add(message, RAT_ACCT_SESSION_ID,
461 chunk_create(entry->sid, strlen(entry->sid)));
462 add_ike_sa_parameters(this, message, ike_sa);
463
464 value = htonl(bytes_out);
465 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
466 value = htonl(bytes_out >> 32);
467 if (value)
468 {
469 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
470 chunk_from_thing(value));
471 }
472 value = htonl(packets_out);
473 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
474
475 value = htonl(bytes_in);
476 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
477 value = htonl(bytes_in >> 32);
478 if (value)
479 {
480 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
481 chunk_from_thing(value));
482 }
483 value = htonl(packets_in);
484 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
485
486 value = htonl(entry->interim.last - entry->created);
487 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
488
489 schedule_interim(this, entry);
490 }
491 this->mutex->unlock(this->mutex);
492 array_destroy_function(stats, (void*)free, NULL);
493
494 if (message)
495 {
496 if (!send_message(this, message))
497 {
498 if (lib->settings->get_bool(lib->settings,
499 "%s.plugins.eap-radius.accounting_close_on_timeout",
500 TRUE, lib->ns))
501 {
502 eap_radius_handle_timeout(data->id);
503 }
504 }
505 message->destroy(message);
506 }
507 return JOB_REQUEUE_NONE;
508 }
509
510 /**
511 * Schedule interim update for given entry
512 */
513 static void schedule_interim(private_eap_radius_accounting_t *this,
514 entry_t *entry)
515 {
516 if (entry->interim.interval)
517 {
518 interim_data_t *data;
519 timeval_t tv = {
520 .tv_sec = entry->interim.last + entry->interim.interval,
521 };
522
523 INIT(data,
524 .this = this,
525 .id = entry->id->clone(entry->id),
526 );
527 lib->scheduler->schedule_job_tv(lib->scheduler,
528 (job_t*)callback_job_create_with_prio(
529 (callback_job_cb_t)send_interim,
530 data, (void*)destroy_interim_data,
531 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL), tv);
532 }
533 }
534
535 /**
536 * Check if an IKE_SA has assigned a virtual IP (to peer)
537 */
538 static bool has_vip(ike_sa_t *ike_sa)
539 {
540 enumerator_t *enumerator;
541 host_t *host;
542 bool found;
543
544 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
545 found = enumerator->enumerate(enumerator, &host);
546 enumerator->destroy(enumerator);
547
548 return found;
549 }
550
551 /**
552 * Send an accounting start message
553 */
554 static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
555 {
556 radius_message_t *message;
557 entry_t *entry;
558 u_int32_t value;
559
560 if (this->acct_req_vip && !has_vip(ike_sa))
561 {
562 return;
563 }
564
565 this->mutex->lock(this->mutex);
566
567 entry = get_or_create_entry(this, ike_sa);
568 entry->start_sent = TRUE;
569
570 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
571 value = htonl(ACCT_STATUS_START);
572 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
573 message->add(message, RAT_ACCT_SESSION_ID,
574 chunk_create(entry->sid, strlen(entry->sid)));
575
576 if (!entry->interim.interval)
577 {
578 entry->interim.interval = lib->settings->get_time(lib->settings,
579 "%s.plugins.eap-radius.accounting_interval", 0, lib->ns);
580 if (entry->interim.interval)
581 {
582 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us",
583 entry->interim.interval);
584 }
585 }
586 schedule_interim(this, entry);
587 this->mutex->unlock(this->mutex);
588
589 add_ike_sa_parameters(this, message, ike_sa);
590 if (!send_message(this, message))
591 {
592 eap_radius_handle_timeout(ike_sa->get_id(ike_sa));
593 }
594 message->destroy(message);
595 }
596
597 /**
598 * Send an account stop message
599 */
600 static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
601 {
602 radius_message_t *message;
603 enumerator_t *enumerator;
604 entry_t *entry;
605 sa_entry_t *sa;
606 u_int32_t value;
607
608 this->mutex->lock(this->mutex);
609 entry = this->sessions->remove(this->sessions, ike_sa->get_id(ike_sa));
610 this->mutex->unlock(this->mutex);
611 if (entry)
612 {
613 if (!entry->start_sent)
614 { /* we tried to authenticate this peer, but never sent a start */
615 destroy_entry(entry);
616 return;
617 }
618 enumerator = array_create_enumerator(entry->cached);
619 while (enumerator->enumerate(enumerator, &sa))
620 {
621 entry->bytes.sent += sa->bytes.sent;
622 entry->bytes.received += sa->bytes.received;
623 entry->packets.sent += sa->packets.sent;
624 entry->packets.received += sa->packets.received;
625 }
626 enumerator->destroy(enumerator);
627
628 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
629 value = htonl(ACCT_STATUS_STOP);
630 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
631 message->add(message, RAT_ACCT_SESSION_ID,
632 chunk_create(entry->sid, strlen(entry->sid)));
633 add_ike_sa_parameters(this, message, ike_sa);
634
635 value = htonl(entry->bytes.sent);
636 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
637 value = htonl(entry->bytes.sent >> 32);
638 if (value)
639 {
640 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
641 chunk_from_thing(value));
642 }
643 value = htonl(entry->packets.sent);
644 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
645
646 value = htonl(entry->bytes.received);
647 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
648 value = htonl(entry->bytes.received >> 32);
649 if (value)
650 {
651 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
652 chunk_from_thing(value));
653 }
654 value = htonl(entry->packets.received);
655 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
656
657 value = htonl(time_monotonic(NULL) - entry->created);
658 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
659
660
661 value = htonl(entry->cause);
662 message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value));
663
664 if (!send_message(this, message))
665 {
666 eap_radius_handle_timeout(NULL);
667 }
668 message->destroy(message);
669 destroy_entry(entry);
670 }
671 }
672
673 METHOD(listener_t, alert, bool,
674 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, alert_t alert,
675 va_list args)
676 {
677 radius_acct_terminate_cause_t cause;
678 entry_t *entry;
679
680 switch (alert)
681 {
682 case ALERT_IKE_SA_EXPIRED:
683 cause = ACCT_CAUSE_SESSION_TIMEOUT;
684 break;
685 case ALERT_RETRANSMIT_SEND_TIMEOUT:
686 cause = ACCT_CAUSE_LOST_SERVICE;
687 break;
688 default:
689 return TRUE;
690 }
691 this->mutex->lock(this->mutex);
692 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
693 if (entry)
694 {
695 entry->cause = cause;
696 }
697 this->mutex->unlock(this->mutex);
698 return TRUE;
699 }
700
701 METHOD(listener_t, ike_updown, bool,
702 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
703 {
704 if (!up)
705 {
706 enumerator_t *enumerator;
707 child_sa_t *child_sa;
708
709 /* update usage for all children just before sending stop */
710 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
711 while (enumerator->enumerate(enumerator, &child_sa))
712 {
713 update_usage(this, ike_sa, child_sa);
714 }
715 enumerator->destroy(enumerator);
716
717 send_stop(this, ike_sa);
718 }
719 return TRUE;
720 }
721
722 METHOD(listener_t, message_hook, bool,
723 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
724 message_t *message, bool incoming, bool plain)
725 {
726 /* start accounting here, virtual IP now is set */
727 if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
728 !incoming && !message->get_request(message))
729 {
730 if (ike_sa->get_version(ike_sa) == IKEV1 &&
731 message->get_exchange_type(message) == TRANSACTION)
732 {
733 send_start(this, ike_sa);
734 }
735 if (ike_sa->get_version(ike_sa) == IKEV2 &&
736 message->get_exchange_type(message) == IKE_AUTH)
737 {
738 send_start(this, ike_sa);
739 }
740 }
741 return TRUE;
742 }
743
744 METHOD(listener_t, ike_rekey, bool,
745 private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new)
746 {
747 entry_t *entry;
748
749 this->mutex->lock(this->mutex);
750 entry = this->sessions->remove(this->sessions, old->get_id(old));
751 if (entry)
752 {
753 /* update IKE_SA identifier */
754 entry->id->destroy(entry->id);
755 entry->id = new->get_id(new);
756 entry->id = entry->id->clone(entry->id);
757 /* fire new interim update job, old gets invalid */
758 schedule_interim(this, entry);
759
760 entry = this->sessions->put(this->sessions, entry->id, entry);
761 if (entry)
762 {
763 destroy_entry(entry);
764 }
765 }
766 this->mutex->unlock(this->mutex);
767
768 return TRUE;
769 }
770
771 METHOD(listener_t, child_rekey, bool,
772 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
773 child_sa_t *old, child_sa_t *new)
774 {
775 update_usage(this, ike_sa, old);
776 return TRUE;
777 }
778
779 METHOD(listener_t, child_updown, bool,
780 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
781 child_sa_t *child_sa, bool up)
782 {
783 if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
784 {
785 update_usage(this, ike_sa, child_sa);
786 }
787 return TRUE;
788 }
789
790 METHOD(eap_radius_accounting_t, destroy, void,
791 private_eap_radius_accounting_t *this)
792 {
793 charon->bus->remove_listener(charon->bus, &this->public.listener);
794 singleton = NULL;
795 this->mutex->destroy(this->mutex);
796 this->sessions->destroy(this->sessions);
797 free(this);
798 }
799
800 /**
801 * See header
802 */
803 eap_radius_accounting_t *eap_radius_accounting_create()
804 {
805 private_eap_radius_accounting_t *this;
806
807 INIT(this,
808 .public = {
809 .listener = {
810 .alert = _alert,
811 .ike_updown = _ike_updown,
812 .ike_rekey = _ike_rekey,
813 .message = _message_hook,
814 .child_updown = _child_updown,
815 .child_rekey = _child_rekey,
816 },
817 .destroy = _destroy,
818 },
819 /* use system time as Session ID prefix */
820 .prefix = (u_int32_t)time(NULL),
821 .sessions = hashtable_create((hashtable_hash_t)hash,
822 (hashtable_equals_t)equals, 32),
823 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
824 );
825 if (lib->settings->get_bool(lib->settings,
826 "%s.plugins.eap-radius.station_id_with_port", TRUE, lib->ns))
827 {
828 this->station_id_fmt = "%#H";
829 }
830 else
831 {
832 this->station_id_fmt = "%H";
833 }
834 if (lib->settings->get_bool(lib->settings,
835 "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
836 {
837 singleton = this;
838 charon->bus->add_listener(charon->bus, &this->public.listener);
839 }
840 this->acct_req_vip = lib->settings->get_bool(lib->settings,
841 "%s.plugins.eap-radius.accounting_requires_vip",
842 FALSE, lib->ns);
843
844 return &this->public;
845 }
846
847 /**
848 * See header
849 */
850 void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, u_int32_t interval)
851 {
852 if (singleton)
853 {
854 entry_t *entry;
855
856 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us", interval);
857 singleton->mutex->lock(singleton->mutex);
858 entry = get_or_create_entry(singleton, ike_sa);
859 entry->interim.interval = interval;
860 singleton->mutex->unlock(singleton->mutex);
861 }
862 }