eap-radius: Remove cache entries for expired SAs during ike/child_rekey
[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 * Cleanup cached SAs
230 */
231 static void cleanup_sas(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
232 entry_t *entry)
233 {
234 enumerator_t *enumerator;
235 child_sa_t *child_sa;
236 sa_entry_t *sa, *found;
237 array_t *sas;
238
239 sas = array_create(0, 0);
240 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
241 while (enumerator->enumerate(enumerator, &child_sa))
242 {
243 INIT(sa,
244 .id = child_sa->get_unique_id(child_sa),
245 );
246 array_insert(sas, ARRAY_TAIL, sa);
247 array_sort(sas, sa_sort, NULL);
248 }
249 enumerator->destroy(enumerator);
250
251 enumerator = array_create_enumerator(entry->cached);
252 while (enumerator->enumerate(enumerator, &sa))
253 {
254 if (array_bsearch(sas, sa, sa_find, &found) == -1)
255 {
256 /* SA is gone, add its latest stats to the total for this IKE_SA
257 * and remove the cache entry */
258 entry->bytes.sent += sa->bytes.sent;
259 entry->bytes.received += sa->bytes.received;
260 entry->packets.sent += sa->packets.sent;
261 entry->packets.received += sa->packets.received;
262 array_remove_at(entry->cached, enumerator);
263 free(sa);
264 }
265 }
266 enumerator->destroy(enumerator);
267 array_destroy_function(sas, (void*)free, NULL);
268 }
269
270 /**
271 * Send a RADIUS message, wait for response
272 */
273 static bool send_message(private_eap_radius_accounting_t *this,
274 radius_message_t *request)
275 {
276 radius_message_t *response;
277 radius_client_t *client;
278 bool ack = FALSE;
279
280 client = eap_radius_create_client();
281 if (client)
282 {
283 response = client->request(client, request);
284 if (response)
285 {
286 ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
287 response->destroy(response);
288 }
289 client->destroy(client);
290 }
291 return ack;
292 }
293
294 /**
295 * Add common IKE_SA parameters to RADIUS account message
296 */
297 static void add_ike_sa_parameters(private_eap_radius_accounting_t *this,
298 radius_message_t *message, ike_sa_t *ike_sa)
299 {
300 enumerator_t *enumerator;
301 host_t *vip, *host;
302 char buf[MAX_RADIUS_ATTRIBUTE_SIZE + 1];
303 chunk_t data;
304 u_int32_t value;
305
306 /* virtual NAS-Port-Type */
307 value = htonl(5);
308 message->add(message, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
309 /* framed ServiceType */
310 value = htonl(2);
311 message->add(message, RAT_SERVICE_TYPE, chunk_from_thing(value));
312
313 value = htonl(ike_sa->get_unique_id(ike_sa));
314 message->add(message, RAT_NAS_PORT, chunk_from_thing(value));
315 message->add(message, RAT_NAS_PORT_ID,
316 chunk_from_str(ike_sa->get_name(ike_sa)));
317
318 host = ike_sa->get_my_host(ike_sa);
319 data = host->get_address(host);
320 switch (host->get_family(host))
321 {
322 case AF_INET:
323 message->add(message, RAT_NAS_IP_ADDRESS, data);
324 break;
325 case AF_INET6:
326 message->add(message, RAT_NAS_IPV6_ADDRESS, data);
327 default:
328 break;
329 }
330 snprintf(buf, sizeof(buf), this->station_id_fmt, host);
331 message->add(message, RAT_CALLED_STATION_ID, chunk_from_str(buf));
332 host = ike_sa->get_other_host(ike_sa);
333 snprintf(buf, sizeof(buf), this->station_id_fmt, host);
334 message->add(message, RAT_CALLING_STATION_ID, chunk_from_str(buf));
335
336 snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
337 message->add(message, RAT_USER_NAME, chunk_from_str(buf));
338
339 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
340 while (enumerator->enumerate(enumerator, &vip))
341 {
342 switch (vip->get_family(vip))
343 {
344 case AF_INET:
345 message->add(message, RAT_FRAMED_IP_ADDRESS,
346 vip->get_address(vip));
347 break;
348 case AF_INET6:
349 /* we currently assign /128 prefixes, only (reserved, length) */
350 data = chunk_from_chars(0, 128);
351 data = chunk_cata("cc", data, vip->get_address(vip));
352 message->add(message, RAT_FRAMED_IPV6_PREFIX, data);
353 break;
354 default:
355 break;
356 }
357 }
358 enumerator->destroy(enumerator);
359 }
360
361 /**
362 * Get an existing or create a new entry from the locked session table
363 */
364 static entry_t* get_or_create_entry(private_eap_radius_accounting_t *this,
365 ike_sa_t *ike_sa)
366 {
367 ike_sa_id_t *id;
368 entry_t *entry;
369 time_t now;
370
371 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
372 if (!entry)
373 {
374 now = time_monotonic(NULL);
375 id = ike_sa->get_id(ike_sa);
376
377 INIT(entry,
378 .id = id->clone(id),
379 .created = now,
380 .interim = {
381 .last = now,
382 },
383 /* default terminate cause, if none other catched */
384 .cause = ACCT_CAUSE_USER_REQUEST,
385 );
386 snprintf(entry->sid, sizeof(entry->sid), "%u-%u",
387 this->prefix, ike_sa->get_unique_id(ike_sa));
388 this->sessions->put(this->sessions, entry->id, entry);
389 }
390 return entry;
391 }
392
393 /* forward declaration */
394 static void schedule_interim(private_eap_radius_accounting_t *this,
395 entry_t *entry);
396
397 /**
398 * Data passed to send_interim() using callback job
399 */
400 typedef struct {
401 /** reference to radius accounting */
402 private_eap_radius_accounting_t *this;
403 /** IKE_SA identifier to send interim update to */
404 ike_sa_id_t *id;
405 } interim_data_t;
406
407 /**
408 * Clean up interim data
409 */
410 void destroy_interim_data(interim_data_t *this)
411 {
412 this->id->destroy(this->id);
413 free(this);
414 }
415
416 /**
417 * Send an interim update for entry of given IKE_SA identifier
418 */
419 static job_requeue_t send_interim(interim_data_t *data)
420 {
421 private_eap_radius_accounting_t *this = data->this;
422 u_int64_t bytes_in = 0, bytes_out = 0, packets_in = 0, packets_out = 0;
423 u_int64_t bytes, packets;
424 radius_message_t *message = NULL;
425 enumerator_t *enumerator;
426 child_sa_t *child_sa;
427 ike_sa_t *ike_sa;
428 entry_t *entry;
429 u_int32_t value;
430 array_t *stats;
431 sa_entry_t *sa, *found;
432
433 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id);
434 if (!ike_sa)
435 {
436 return JOB_REQUEUE_NONE;
437 }
438 stats = array_create(0, 0);
439 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
440 while (enumerator->enumerate(enumerator, &child_sa))
441 {
442 INIT(sa,
443 .id = child_sa->get_unique_id(child_sa),
444 );
445 array_insert(stats, ARRAY_TAIL, sa);
446 array_sort(stats, sa_sort, NULL);
447
448 child_sa->get_usestats(child_sa, FALSE, NULL, &bytes, &packets);
449 sa->bytes.sent = bytes;
450 sa->packets.sent = packets;
451 bytes_out += bytes;
452 packets_out += packets;
453 child_sa->get_usestats(child_sa, TRUE, NULL, &bytes, &packets);
454 sa->bytes.received = bytes;
455 sa->packets.received = packets;
456 bytes_in += bytes;
457 packets_in += packets;
458 }
459 enumerator->destroy(enumerator);
460 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
461
462 /* avoid any races by returning IKE_SA before acquiring lock */
463
464 this->mutex->lock(this->mutex);
465 entry = this->sessions->get(this->sessions, data->id);
466 if (entry)
467 {
468 entry->interim.last = time_monotonic(NULL);
469
470 enumerator = array_create_enumerator(entry->cached);
471 while (enumerator->enumerate(enumerator, &sa))
472 {
473 if (array_bsearch(stats, sa, sa_find, &found) != -1)
474 {
475 /* SA is still around, update stats (e.g. for IKEv1 where
476 * SA might get used even after rekeying) */
477 sa->bytes = found->bytes;
478 sa->packets = found->packets;
479 }
480 else
481 {
482 /* SA is gone, add its latest stats to the total for this IKE_SA
483 * and remove the cache entry */
484 entry->bytes.sent += sa->bytes.sent;
485 entry->bytes.received += sa->bytes.received;
486 entry->packets.sent += sa->packets.sent;
487 entry->packets.received += sa->packets.received;
488 array_remove_at(entry->cached, enumerator);
489 free(sa);
490 }
491 }
492 enumerator->destroy(enumerator);
493
494 bytes_in += entry->bytes.received;
495 bytes_out += entry->bytes.sent;
496 packets_in += entry->packets.received;
497 packets_out += entry->packets.sent;
498
499 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
500 value = htonl(ACCT_STATUS_INTERIM_UPDATE);
501 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
502 message->add(message, RAT_ACCT_SESSION_ID,
503 chunk_create(entry->sid, strlen(entry->sid)));
504 add_ike_sa_parameters(this, message, ike_sa);
505
506 value = htonl(bytes_out);
507 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
508 value = htonl(bytes_out >> 32);
509 if (value)
510 {
511 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
512 chunk_from_thing(value));
513 }
514 value = htonl(packets_out);
515 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
516
517 value = htonl(bytes_in);
518 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
519 value = htonl(bytes_in >> 32);
520 if (value)
521 {
522 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
523 chunk_from_thing(value));
524 }
525 value = htonl(packets_in);
526 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
527
528 value = htonl(entry->interim.last - entry->created);
529 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
530
531 schedule_interim(this, entry);
532 }
533 this->mutex->unlock(this->mutex);
534 array_destroy_function(stats, (void*)free, NULL);
535
536 if (message)
537 {
538 if (!send_message(this, message))
539 {
540 if (lib->settings->get_bool(lib->settings,
541 "%s.plugins.eap-radius.accounting_close_on_timeout",
542 TRUE, lib->ns))
543 {
544 eap_radius_handle_timeout(data->id);
545 }
546 }
547 message->destroy(message);
548 }
549 return JOB_REQUEUE_NONE;
550 }
551
552 /**
553 * Schedule interim update for given entry
554 */
555 static void schedule_interim(private_eap_radius_accounting_t *this,
556 entry_t *entry)
557 {
558 if (entry->interim.interval)
559 {
560 interim_data_t *data;
561 timeval_t tv = {
562 .tv_sec = entry->interim.last + entry->interim.interval,
563 };
564
565 INIT(data,
566 .this = this,
567 .id = entry->id->clone(entry->id),
568 );
569 lib->scheduler->schedule_job_tv(lib->scheduler,
570 (job_t*)callback_job_create_with_prio(
571 (callback_job_cb_t)send_interim,
572 data, (void*)destroy_interim_data,
573 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL), tv);
574 }
575 }
576
577 /**
578 * Check if an IKE_SA has assigned a virtual IP (to peer)
579 */
580 static bool has_vip(ike_sa_t *ike_sa)
581 {
582 enumerator_t *enumerator;
583 host_t *host;
584 bool found;
585
586 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
587 found = enumerator->enumerate(enumerator, &host);
588 enumerator->destroy(enumerator);
589
590 return found;
591 }
592
593 /**
594 * Send an accounting start message
595 */
596 static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
597 {
598 radius_message_t *message;
599 entry_t *entry;
600 u_int32_t value;
601
602 if (this->acct_req_vip && !has_vip(ike_sa))
603 {
604 return;
605 }
606
607 this->mutex->lock(this->mutex);
608
609 entry = get_or_create_entry(this, ike_sa);
610 entry->start_sent = TRUE;
611
612 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
613 value = htonl(ACCT_STATUS_START);
614 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
615 message->add(message, RAT_ACCT_SESSION_ID,
616 chunk_create(entry->sid, strlen(entry->sid)));
617
618 if (!entry->interim.interval)
619 {
620 entry->interim.interval = lib->settings->get_time(lib->settings,
621 "%s.plugins.eap-radius.accounting_interval", 0, lib->ns);
622 if (entry->interim.interval)
623 {
624 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us",
625 entry->interim.interval);
626 }
627 }
628 schedule_interim(this, entry);
629 this->mutex->unlock(this->mutex);
630
631 add_ike_sa_parameters(this, message, ike_sa);
632 if (!send_message(this, message))
633 {
634 eap_radius_handle_timeout(ike_sa->get_id(ike_sa));
635 }
636 message->destroy(message);
637 }
638
639 /**
640 * Send an account stop message
641 */
642 static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
643 {
644 radius_message_t *message;
645 enumerator_t *enumerator;
646 entry_t *entry;
647 sa_entry_t *sa;
648 u_int32_t value;
649
650 this->mutex->lock(this->mutex);
651 entry = this->sessions->remove(this->sessions, ike_sa->get_id(ike_sa));
652 this->mutex->unlock(this->mutex);
653 if (entry)
654 {
655 if (!entry->start_sent)
656 { /* we tried to authenticate this peer, but never sent a start */
657 destroy_entry(entry);
658 return;
659 }
660 enumerator = array_create_enumerator(entry->cached);
661 while (enumerator->enumerate(enumerator, &sa))
662 {
663 entry->bytes.sent += sa->bytes.sent;
664 entry->bytes.received += sa->bytes.received;
665 entry->packets.sent += sa->packets.sent;
666 entry->packets.received += sa->packets.received;
667 }
668 enumerator->destroy(enumerator);
669
670 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
671 value = htonl(ACCT_STATUS_STOP);
672 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
673 message->add(message, RAT_ACCT_SESSION_ID,
674 chunk_create(entry->sid, strlen(entry->sid)));
675 add_ike_sa_parameters(this, message, ike_sa);
676
677 value = htonl(entry->bytes.sent);
678 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
679 value = htonl(entry->bytes.sent >> 32);
680 if (value)
681 {
682 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
683 chunk_from_thing(value));
684 }
685 value = htonl(entry->packets.sent);
686 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
687
688 value = htonl(entry->bytes.received);
689 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
690 value = htonl(entry->bytes.received >> 32);
691 if (value)
692 {
693 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
694 chunk_from_thing(value));
695 }
696 value = htonl(entry->packets.received);
697 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
698
699 value = htonl(time_monotonic(NULL) - entry->created);
700 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
701
702
703 value = htonl(entry->cause);
704 message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value));
705
706 if (!send_message(this, message))
707 {
708 eap_radius_handle_timeout(NULL);
709 }
710 message->destroy(message);
711 destroy_entry(entry);
712 }
713 }
714
715 METHOD(listener_t, alert, bool,
716 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, alert_t alert,
717 va_list args)
718 {
719 radius_acct_terminate_cause_t cause;
720 entry_t *entry;
721
722 switch (alert)
723 {
724 case ALERT_IKE_SA_EXPIRED:
725 cause = ACCT_CAUSE_SESSION_TIMEOUT;
726 break;
727 case ALERT_RETRANSMIT_SEND_TIMEOUT:
728 cause = ACCT_CAUSE_LOST_SERVICE;
729 break;
730 default:
731 return TRUE;
732 }
733 this->mutex->lock(this->mutex);
734 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
735 if (entry)
736 {
737 entry->cause = cause;
738 }
739 this->mutex->unlock(this->mutex);
740 return TRUE;
741 }
742
743 METHOD(listener_t, ike_updown, bool,
744 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
745 {
746 if (!up)
747 {
748 enumerator_t *enumerator;
749 child_sa_t *child_sa;
750
751 /* update usage for all children just before sending stop */
752 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
753 while (enumerator->enumerate(enumerator, &child_sa))
754 {
755 update_usage(this, ike_sa, child_sa);
756 }
757 enumerator->destroy(enumerator);
758
759 send_stop(this, ike_sa);
760 }
761 return TRUE;
762 }
763
764 METHOD(listener_t, message_hook, bool,
765 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
766 message_t *message, bool incoming, bool plain)
767 {
768 /* start accounting here, virtual IP now is set */
769 if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
770 !incoming && !message->get_request(message))
771 {
772 if (ike_sa->get_version(ike_sa) == IKEV1 &&
773 message->get_exchange_type(message) == TRANSACTION)
774 {
775 send_start(this, ike_sa);
776 }
777 if (ike_sa->get_version(ike_sa) == IKEV2 &&
778 message->get_exchange_type(message) == IKE_AUTH)
779 {
780 send_start(this, ike_sa);
781 }
782 }
783 return TRUE;
784 }
785
786 METHOD(listener_t, ike_rekey, bool,
787 private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new)
788 {
789 entry_t *entry;
790
791 this->mutex->lock(this->mutex);
792 entry = this->sessions->remove(this->sessions, old->get_id(old));
793 if (entry)
794 {
795 /* update IKE_SA identifier */
796 entry->id->destroy(entry->id);
797 entry->id = new->get_id(new);
798 entry->id = entry->id->clone(entry->id);
799 /* fire new interim update job, old gets invalid */
800 schedule_interim(this, entry);
801
802 cleanup_sas(this, new, entry);
803
804 entry = this->sessions->put(this->sessions, entry->id, entry);
805 if (entry)
806 {
807 destroy_entry(entry);
808 }
809 }
810 this->mutex->unlock(this->mutex);
811
812 return TRUE;
813 }
814
815 METHOD(listener_t, child_rekey, bool,
816 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
817 child_sa_t *old, child_sa_t *new)
818 {
819 entry_t *entry;
820
821 update_usage(this, ike_sa, old);
822 this->mutex->lock(this->mutex);
823 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
824 if (entry)
825 {
826 cleanup_sas(this, ike_sa, entry);
827 }
828 this->mutex->unlock(this->mutex);
829 return TRUE;
830 }
831
832 METHOD(listener_t, child_updown, bool,
833 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
834 child_sa_t *child_sa, bool up)
835 {
836 if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
837 {
838 update_usage(this, ike_sa, child_sa);
839 }
840 return TRUE;
841 }
842
843 METHOD(eap_radius_accounting_t, destroy, void,
844 private_eap_radius_accounting_t *this)
845 {
846 charon->bus->remove_listener(charon->bus, &this->public.listener);
847 singleton = NULL;
848 this->mutex->destroy(this->mutex);
849 this->sessions->destroy(this->sessions);
850 free(this);
851 }
852
853 /**
854 * See header
855 */
856 eap_radius_accounting_t *eap_radius_accounting_create()
857 {
858 private_eap_radius_accounting_t *this;
859
860 INIT(this,
861 .public = {
862 .listener = {
863 .alert = _alert,
864 .ike_updown = _ike_updown,
865 .ike_rekey = _ike_rekey,
866 .message = _message_hook,
867 .child_updown = _child_updown,
868 .child_rekey = _child_rekey,
869 },
870 .destroy = _destroy,
871 },
872 /* use system time as Session ID prefix */
873 .prefix = (u_int32_t)time(NULL),
874 .sessions = hashtable_create((hashtable_hash_t)hash,
875 (hashtable_equals_t)equals, 32),
876 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
877 );
878 if (lib->settings->get_bool(lib->settings,
879 "%s.plugins.eap-radius.station_id_with_port", TRUE, lib->ns))
880 {
881 this->station_id_fmt = "%#H";
882 }
883 else
884 {
885 this->station_id_fmt = "%H";
886 }
887 if (lib->settings->get_bool(lib->settings,
888 "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
889 {
890 singleton = this;
891 charon->bus->add_listener(charon->bus, &this->public.listener);
892 }
893 this->acct_req_vip = lib->settings->get_bool(lib->settings,
894 "%s.plugins.eap-radius.accounting_requires_vip",
895 FALSE, lib->ns);
896
897 return &this->public;
898 }
899
900 /**
901 * See header
902 */
903 void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, u_int32_t interval)
904 {
905 if (singleton)
906 {
907 entry_t *entry;
908
909 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us", interval);
910 singleton->mutex->lock(singleton->mutex);
911 entry = get_or_create_entry(singleton, ike_sa);
912 entry->interim.interval = interval;
913 singleton->mutex->unlock(singleton->mutex);
914 }
915 }