c6bbb737b62b93f534a85c0171580d3195561b76
[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 bytes and packets
101 */
102 typedef struct {
103 struct {
104 u_int64_t sent;
105 u_int64_t received;
106 } bytes, packets;
107 } usage_t;
108
109 /**
110 * Add usage stats (modifies a)
111 */
112 static inline void add_usage(usage_t *a, usage_t b)
113 {
114 a->bytes.sent += b.bytes.sent;
115 a->bytes.received += b.bytes.received;
116 a->packets.sent += b.packets.sent;
117 a->packets.received += b.packets.received;
118 }
119
120 /**
121 * Subtract usage stats (modifies a)
122 */
123 static inline void sub_usage(usage_t *a, usage_t b)
124 {
125 a->bytes.sent -= b.bytes.sent;
126 a->bytes.received -= b.bytes.received;
127 a->packets.sent -= b.packets.sent;
128 a->packets.received -= b.packets.received;
129 }
130
131 /**
132 * Usage stats for a cached/migrated SAs
133 */
134 typedef struct {
135 /** unique CHILD_SA identifier */
136 u_int32_t id;
137 /** usage stats for this SA */
138 usage_t usage;
139 } sa_entry_t;
140
141 /**
142 * Clone an sa_entry_t
143 */
144 static sa_entry_t *clone_sa(sa_entry_t *sa)
145 {
146 sa_entry_t *this;
147
148 INIT(this,
149 .id = sa->id,
150 .usage = sa->usage,
151 );
152 return this;
153 }
154
155 /**
156 * Hashtable entry with usage stats
157 */
158 typedef struct {
159 /** IKE_SA identifier this entry is stored under */
160 ike_sa_id_t *id;
161 /** RADIUS accounting session ID */
162 char sid[24];
163 /** number of sent/received octets/packets for expired SAs */
164 usage_t usage;
165 /** list of cached SAs, sa_entry_t (sorted by their unique ID) */
166 array_t *cached;
167 /** list of migrated SAs, sa_entry_t (sorted by their unique ID) */
168 array_t *migrated;
169 /** session creation time */
170 time_t created;
171 /** terminate cause */
172 radius_acct_terminate_cause_t cause;
173 /* interim interval and timestamp of last update */
174 struct {
175 u_int32_t interval;
176 time_t last;
177 } interim;
178 /** did we send Accounting-Start */
179 bool start_sent;
180 } entry_t;
181
182 /**
183 * Destroy an entry_t
184 */
185 static void destroy_entry(entry_t *this)
186 {
187 array_destroy_function(this->cached, (void*)free, NULL);
188 array_destroy_function(this->migrated, (void*)free, NULL);
189 this->id->destroy(this->id);
190 free(this);
191 }
192
193 /**
194 * Accounting message status types
195 */
196 typedef enum {
197 ACCT_STATUS_START = 1,
198 ACCT_STATUS_STOP = 2,
199 ACCT_STATUS_INTERIM_UPDATE = 3,
200 ACCT_STATUS_ACCOUNTING_ON = 7,
201 ACCT_STATUS_ACCOUNTING_OFF = 8,
202 } radius_acct_status_t;
203
204 /**
205 * Hashtable hash function
206 */
207 static u_int hash(ike_sa_id_t *key)
208 {
209 return key->get_responder_spi(key);
210 }
211
212 /**
213 * Hashtable equals function
214 */
215 static bool equals(ike_sa_id_t *a, ike_sa_id_t *b)
216 {
217 return a->equals(a, b);
218 }
219
220 /**
221 * Sort cached SAs
222 */
223 static int sa_sort(const void *a, const void *b, void *user)
224 {
225 const sa_entry_t *ra = a, *rb = b;
226 return ra->id - rb->id;
227 }
228
229 /**
230 * Find a cached SA
231 */
232 static int sa_find(const void *a, const void *b)
233 {
234 return sa_sort(a, b, NULL);
235 }
236
237 /**
238 * Update or create usage counters of a cached SA
239 */
240 static void update_sa(entry_t *entry, u_int32_t id, usage_t usage)
241 {
242 sa_entry_t *sa, lookup;
243
244 lookup.id = id;
245 if (array_bsearch(entry->cached, &lookup, sa_find, &sa) == -1)
246 {
247 INIT(sa,
248 .id = id,
249 );
250 array_insert_create(&entry->cached, ARRAY_TAIL, sa);
251 array_sort(entry->cached, sa_sort, NULL);
252 }
253 sa->usage = usage;
254 }
255
256 /**
257 * Update usage counter when a CHILD_SA rekeys/goes down
258 */
259 static void update_usage(private_eap_radius_accounting_t *this,
260 ike_sa_t *ike_sa, child_sa_t *child_sa)
261 {
262 usage_t usage;
263 entry_t *entry;
264
265 child_sa->get_usestats(child_sa, TRUE, NULL, &usage.bytes.received,
266 &usage.packets.received);
267 child_sa->get_usestats(child_sa, FALSE, NULL, &usage.bytes.sent,
268 &usage.packets.sent);
269
270 this->mutex->lock(this->mutex);
271 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
272 if (entry)
273 {
274 update_sa(entry, child_sa->get_unique_id(child_sa), usage);
275 }
276 this->mutex->unlock(this->mutex);
277 }
278
279 /**
280 * Collect usage stats for all CHILD_SAs of the given IKE_SA, optionally returns
281 * the total number of bytes and packets
282 */
283 static array_t *collect_stats(ike_sa_t *ike_sa, usage_t *total)
284 {
285 enumerator_t *enumerator;
286 child_sa_t *child_sa;
287 array_t *stats;
288 sa_entry_t *sa;
289 usage_t usage;
290
291 if (total)
292 {
293 *total = (usage_t){};
294 }
295
296 stats = array_create(0, 0);
297 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
298 while (enumerator->enumerate(enumerator, &child_sa))
299 {
300 INIT(sa,
301 .id = child_sa->get_unique_id(child_sa),
302 );
303 array_insert(stats, ARRAY_TAIL, sa);
304 array_sort(stats, sa_sort, NULL);
305
306 child_sa->get_usestats(child_sa, TRUE, NULL, &usage.bytes.received,
307 &usage.packets.received);
308 child_sa->get_usestats(child_sa, FALSE, NULL, &usage.bytes.sent,
309 &usage.packets.sent);
310 sa->usage = usage;
311 if (total)
312 {
313 add_usage(total, usage);
314 }
315 }
316 enumerator->destroy(enumerator);
317 return stats;
318 }
319
320 /**
321 * Cleanup cached SAs
322 */
323 static void cleanup_sas(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
324 entry_t *entry)
325 {
326 enumerator_t *enumerator;
327 child_sa_t *child_sa;
328 sa_entry_t *sa, *found;
329 array_t *sas;
330
331 sas = array_create(0, 0);
332 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
333 while (enumerator->enumerate(enumerator, &child_sa))
334 {
335 INIT(sa,
336 .id = child_sa->get_unique_id(child_sa),
337 );
338 array_insert(sas, ARRAY_TAIL, sa);
339 array_sort(sas, sa_sort, NULL);
340 }
341 enumerator->destroy(enumerator);
342
343 enumerator = array_create_enumerator(entry->cached);
344 while (enumerator->enumerate(enumerator, &sa))
345 {
346 if (array_bsearch(sas, sa, sa_find, &found) == -1)
347 {
348 /* SA is gone, add its latest stats to the total for this IKE_SA
349 * and remove the cache entry */
350 add_usage(&entry->usage, sa->usage);
351 array_remove_at(entry->cached, enumerator);
352 free(sa);
353 }
354 }
355 enumerator->destroy(enumerator);
356 enumerator = array_create_enumerator(entry->migrated);
357 while (enumerator->enumerate(enumerator, &sa))
358 {
359 if (array_bsearch(sas, sa, sa_find, &found) == -1)
360 {
361 /* SA is gone, subtract stats from the total for this IKE_SA */
362 sub_usage(&entry->usage, sa->usage);
363 array_remove_at(entry->migrated, enumerator);
364 free(sa);
365 }
366 }
367 enumerator->destroy(enumerator);
368 array_destroy_function(sas, (void*)free, NULL);
369 }
370
371 /**
372 * Send a RADIUS message, wait for response
373 */
374 static bool send_message(private_eap_radius_accounting_t *this,
375 radius_message_t *request)
376 {
377 radius_message_t *response;
378 radius_client_t *client;
379 bool ack = FALSE;
380
381 client = eap_radius_create_client();
382 if (client)
383 {
384 response = client->request(client, request);
385 if (response)
386 {
387 ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
388 response->destroy(response);
389 }
390 client->destroy(client);
391 }
392 return ack;
393 }
394
395 /**
396 * Add common IKE_SA parameters to RADIUS account message
397 */
398 static void add_ike_sa_parameters(private_eap_radius_accounting_t *this,
399 radius_message_t *message, ike_sa_t *ike_sa)
400 {
401 enumerator_t *enumerator;
402 host_t *vip, *host;
403 char buf[MAX_RADIUS_ATTRIBUTE_SIZE + 1];
404 chunk_t data;
405 u_int32_t value;
406
407 /* virtual NAS-Port-Type */
408 value = htonl(5);
409 message->add(message, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
410 /* framed ServiceType */
411 value = htonl(2);
412 message->add(message, RAT_SERVICE_TYPE, chunk_from_thing(value));
413
414 value = htonl(ike_sa->get_unique_id(ike_sa));
415 message->add(message, RAT_NAS_PORT, chunk_from_thing(value));
416 message->add(message, RAT_NAS_PORT_ID,
417 chunk_from_str(ike_sa->get_name(ike_sa)));
418
419 host = ike_sa->get_my_host(ike_sa);
420 data = host->get_address(host);
421 switch (host->get_family(host))
422 {
423 case AF_INET:
424 message->add(message, RAT_NAS_IP_ADDRESS, data);
425 break;
426 case AF_INET6:
427 message->add(message, RAT_NAS_IPV6_ADDRESS, data);
428 default:
429 break;
430 }
431 snprintf(buf, sizeof(buf), this->station_id_fmt, host);
432 message->add(message, RAT_CALLED_STATION_ID, chunk_from_str(buf));
433 host = ike_sa->get_other_host(ike_sa);
434 snprintf(buf, sizeof(buf), this->station_id_fmt, host);
435 message->add(message, RAT_CALLING_STATION_ID, chunk_from_str(buf));
436
437 snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
438 message->add(message, RAT_USER_NAME, chunk_from_str(buf));
439
440 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
441 while (enumerator->enumerate(enumerator, &vip))
442 {
443 switch (vip->get_family(vip))
444 {
445 case AF_INET:
446 message->add(message, RAT_FRAMED_IP_ADDRESS,
447 vip->get_address(vip));
448 break;
449 case AF_INET6:
450 /* we currently assign /128 prefixes, only (reserved, length) */
451 data = chunk_from_chars(0, 128);
452 data = chunk_cata("cc", data, vip->get_address(vip));
453 message->add(message, RAT_FRAMED_IPV6_PREFIX, data);
454 break;
455 default:
456 break;
457 }
458 }
459 enumerator->destroy(enumerator);
460 }
461
462 /**
463 * Get an existing or create a new entry from the locked session table
464 */
465 static entry_t* get_or_create_entry(private_eap_radius_accounting_t *this,
466 ike_sa_id_t *id, u_int32_t unique)
467 {
468 entry_t *entry;
469 time_t now;
470
471 entry = this->sessions->get(this->sessions, id);
472 if (!entry)
473 {
474 now = time_monotonic(NULL);
475
476 INIT(entry,
477 .id = id->clone(id),
478 .created = now,
479 .interim = {
480 .last = now,
481 },
482 /* default terminate cause, if none other catched */
483 .cause = ACCT_CAUSE_USER_REQUEST,
484 );
485 snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, unique);
486 this->sessions->put(this->sessions, entry->id, entry);
487 }
488 return entry;
489 }
490
491 /* forward declaration */
492 static void schedule_interim(private_eap_radius_accounting_t *this,
493 entry_t *entry);
494
495 /**
496 * Data passed to send_interim() using callback job
497 */
498 typedef struct {
499 /** reference to radius accounting */
500 private_eap_radius_accounting_t *this;
501 /** IKE_SA identifier to send interim update to */
502 ike_sa_id_t *id;
503 } interim_data_t;
504
505 /**
506 * Clean up interim data
507 */
508 void destroy_interim_data(interim_data_t *this)
509 {
510 this->id->destroy(this->id);
511 free(this);
512 }
513
514 /**
515 * Send an interim update for entry of given IKE_SA identifier
516 */
517 static job_requeue_t send_interim(interim_data_t *data)
518 {
519 private_eap_radius_accounting_t *this = data->this;
520 usage_t usage;
521 radius_message_t *message = NULL;
522 enumerator_t *enumerator;
523 ike_sa_t *ike_sa;
524 entry_t *entry;
525 u_int32_t value;
526 array_t *stats;
527 sa_entry_t *sa, *found;
528
529 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id);
530 if (!ike_sa)
531 {
532 return JOB_REQUEUE_NONE;
533 }
534 stats = collect_stats(ike_sa, &usage);
535 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
536
537 /* avoid any races by returning IKE_SA before acquiring lock */
538
539 this->mutex->lock(this->mutex);
540 entry = this->sessions->get(this->sessions, data->id);
541 if (entry)
542 {
543 entry->interim.last = time_monotonic(NULL);
544
545 enumerator = array_create_enumerator(entry->cached);
546 while (enumerator->enumerate(enumerator, &sa))
547 {
548 if (array_bsearch(stats, sa, sa_find, &found) != -1)
549 {
550 /* SA is still around, update stats (e.g. for IKEv1 where
551 * SA might get used even after rekeying) */
552 sa->usage = found->usage;
553 }
554 else
555 {
556 /* SA is gone, add its last stats to the total for this IKE_SA
557 * and remove the cache entry */
558 add_usage(&entry->usage, sa->usage);
559 array_remove_at(entry->cached, enumerator);
560 free(sa);
561 }
562 }
563 enumerator->destroy(enumerator);
564
565 enumerator = array_create_enumerator(entry->migrated);
566 while (enumerator->enumerate(enumerator, &sa))
567 {
568 if (array_bsearch(stats, sa, sa_find, &found) != -1)
569 {
570 /* SA is still around, but we have to compensate */
571 sub_usage(&usage, sa->usage);
572 }
573 else
574 {
575 /* SA is gone, subtract stats from the total for this IKE_SA */
576 sub_usage(&entry->usage, sa->usage);
577 array_remove_at(entry->migrated, enumerator);
578 free(sa);
579 }
580 }
581 enumerator->destroy(enumerator);
582
583 add_usage(&usage, entry->usage);
584
585 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
586 value = htonl(ACCT_STATUS_INTERIM_UPDATE);
587 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
588 message->add(message, RAT_ACCT_SESSION_ID,
589 chunk_create(entry->sid, strlen(entry->sid)));
590 add_ike_sa_parameters(this, message, ike_sa);
591
592 value = htonl(usage.bytes.sent);
593 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
594 value = htonl(usage.bytes.sent >> 32);
595 if (value)
596 {
597 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
598 chunk_from_thing(value));
599 }
600 value = htonl(usage.packets.sent);
601 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
602
603 value = htonl(usage.bytes.received);
604 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
605 value = htonl(usage.bytes.received >> 32);
606 if (value)
607 {
608 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
609 chunk_from_thing(value));
610 }
611 value = htonl(usage.packets.received);
612 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
613
614 value = htonl(entry->interim.last - entry->created);
615 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
616
617 schedule_interim(this, entry);
618 }
619 this->mutex->unlock(this->mutex);
620 array_destroy_function(stats, (void*)free, NULL);
621
622 if (message)
623 {
624 if (!send_message(this, message))
625 {
626 if (lib->settings->get_bool(lib->settings,
627 "%s.plugins.eap-radius.accounting_close_on_timeout",
628 TRUE, lib->ns))
629 {
630 eap_radius_handle_timeout(data->id);
631 }
632 }
633 message->destroy(message);
634 }
635 return JOB_REQUEUE_NONE;
636 }
637
638 /**
639 * Schedule interim update for given entry
640 */
641 static void schedule_interim(private_eap_radius_accounting_t *this,
642 entry_t *entry)
643 {
644 if (entry->interim.interval)
645 {
646 interim_data_t *data;
647 timeval_t tv = {
648 .tv_sec = entry->interim.last + entry->interim.interval,
649 };
650
651 INIT(data,
652 .this = this,
653 .id = entry->id->clone(entry->id),
654 );
655 lib->scheduler->schedule_job_tv(lib->scheduler,
656 (job_t*)callback_job_create_with_prio(
657 (callback_job_cb_t)send_interim,
658 data, (void*)destroy_interim_data,
659 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL), tv);
660 }
661 }
662
663 /**
664 * Check if an IKE_SA has assigned a virtual IP (to peer)
665 */
666 static bool has_vip(ike_sa_t *ike_sa)
667 {
668 enumerator_t *enumerator;
669 host_t *host;
670 bool found;
671
672 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
673 found = enumerator->enumerate(enumerator, &host);
674 enumerator->destroy(enumerator);
675
676 return found;
677 }
678
679 /**
680 * Send an accounting start message
681 */
682 static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
683 {
684 radius_message_t *message;
685 entry_t *entry;
686 u_int32_t value;
687
688 if (this->acct_req_vip && !has_vip(ike_sa))
689 {
690 return;
691 }
692
693 this->mutex->lock(this->mutex);
694
695 entry = get_or_create_entry(this, ike_sa->get_id(ike_sa),
696 ike_sa->get_unique_id(ike_sa));
697 if (entry->start_sent)
698 {
699 this->mutex->unlock(this->mutex);
700 return;
701 }
702 entry->start_sent = TRUE;
703
704 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
705 value = htonl(ACCT_STATUS_START);
706 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
707 message->add(message, RAT_ACCT_SESSION_ID,
708 chunk_create(entry->sid, strlen(entry->sid)));
709
710 if (!entry->interim.interval)
711 {
712 entry->interim.interval = lib->settings->get_time(lib->settings,
713 "%s.plugins.eap-radius.accounting_interval", 0, lib->ns);
714 if (entry->interim.interval)
715 {
716 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us",
717 entry->interim.interval);
718 }
719 }
720 schedule_interim(this, entry);
721 this->mutex->unlock(this->mutex);
722
723 add_ike_sa_parameters(this, message, ike_sa);
724 if (!send_message(this, message))
725 {
726 eap_radius_handle_timeout(ike_sa->get_id(ike_sa));
727 }
728 message->destroy(message);
729 }
730
731 /**
732 * Send an account stop message
733 */
734 static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
735 {
736 radius_message_t *message;
737 enumerator_t *enumerator;
738 entry_t *entry;
739 sa_entry_t *sa;
740 u_int32_t value;
741
742 this->mutex->lock(this->mutex);
743 entry = this->sessions->remove(this->sessions, ike_sa->get_id(ike_sa));
744 this->mutex->unlock(this->mutex);
745 if (entry)
746 {
747 if (!entry->start_sent)
748 { /* we tried to authenticate this peer, but never sent a start */
749 destroy_entry(entry);
750 return;
751 }
752 enumerator = array_create_enumerator(entry->cached);
753 while (enumerator->enumerate(enumerator, &sa))
754 {
755 add_usage(&entry->usage, sa->usage);
756 }
757 enumerator->destroy(enumerator);
758
759 enumerator = array_create_enumerator(entry->migrated);
760 while (enumerator->enumerate(enumerator, &sa))
761 {
762 sub_usage(&entry->usage, sa->usage);
763 }
764 enumerator->destroy(enumerator);
765
766 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
767 value = htonl(ACCT_STATUS_STOP);
768 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
769 message->add(message, RAT_ACCT_SESSION_ID,
770 chunk_create(entry->sid, strlen(entry->sid)));
771 add_ike_sa_parameters(this, message, ike_sa);
772
773 value = htonl(entry->usage.bytes.sent);
774 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
775 value = htonl(entry->usage.bytes.sent >> 32);
776 if (value)
777 {
778 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
779 chunk_from_thing(value));
780 }
781 value = htonl(entry->usage.packets.sent);
782 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
783
784 value = htonl(entry->usage.bytes.received);
785 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
786 value = htonl(entry->usage.bytes.received >> 32);
787 if (value)
788 {
789 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
790 chunk_from_thing(value));
791 }
792 value = htonl(entry->usage.packets.received);
793 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
794
795 value = htonl(time_monotonic(NULL) - entry->created);
796 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
797
798
799 value = htonl(entry->cause);
800 message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value));
801
802 if (!send_message(this, message))
803 {
804 eap_radius_handle_timeout(NULL);
805 }
806 message->destroy(message);
807 destroy_entry(entry);
808 }
809 }
810
811 METHOD(listener_t, alert, bool,
812 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, alert_t alert,
813 va_list args)
814 {
815 radius_acct_terminate_cause_t cause;
816 entry_t *entry;
817
818 switch (alert)
819 {
820 case ALERT_IKE_SA_EXPIRED:
821 cause = ACCT_CAUSE_SESSION_TIMEOUT;
822 break;
823 case ALERT_RETRANSMIT_SEND_TIMEOUT:
824 cause = ACCT_CAUSE_LOST_SERVICE;
825 break;
826 default:
827 return TRUE;
828 }
829 this->mutex->lock(this->mutex);
830 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
831 if (entry)
832 {
833 entry->cause = cause;
834 }
835 this->mutex->unlock(this->mutex);
836 return TRUE;
837 }
838
839 METHOD(listener_t, ike_updown, bool,
840 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
841 {
842 if (!up)
843 {
844 enumerator_t *enumerator;
845 child_sa_t *child_sa;
846
847 /* update usage for all children just before sending stop */
848 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
849 while (enumerator->enumerate(enumerator, &child_sa))
850 {
851 update_usage(this, ike_sa, child_sa);
852 }
853 enumerator->destroy(enumerator);
854
855 send_stop(this, ike_sa);
856 }
857 return TRUE;
858 }
859
860 METHOD(listener_t, message_hook, bool,
861 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
862 message_t *message, bool incoming, bool plain)
863 {
864 /* start accounting here, virtual IP now is set */
865 if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
866 !incoming && !message->get_request(message))
867 {
868 if (ike_sa->get_version(ike_sa) == IKEV2 &&
869 message->get_exchange_type(message) == IKE_AUTH)
870 {
871 send_start(this, ike_sa);
872 }
873 }
874 return TRUE;
875 }
876
877 METHOD(listener_t, assign_vips, bool,
878 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool assign)
879 {
880 /* start accounting as soon as the virtual IP is set */
881 if (assign && ike_sa->get_version(ike_sa) == IKEV1)
882 {
883 send_start(this, ike_sa);
884 }
885 return TRUE;
886 }
887
888 METHOD(listener_t, ike_rekey, bool,
889 private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new)
890 {
891 entry_t *entry;
892
893 this->mutex->lock(this->mutex);
894 entry = this->sessions->remove(this->sessions, old->get_id(old));
895 if (entry)
896 {
897 /* update IKE_SA identifier */
898 entry->id->destroy(entry->id);
899 entry->id = new->get_id(new);
900 entry->id = entry->id->clone(entry->id);
901 /* fire new interim update job, old gets invalid */
902 schedule_interim(this, entry);
903
904 cleanup_sas(this, new, entry);
905
906 entry = this->sessions->put(this->sessions, entry->id, entry);
907 if (entry)
908 {
909 destroy_entry(entry);
910 }
911 }
912 this->mutex->unlock(this->mutex);
913
914 return TRUE;
915 }
916
917 METHOD(listener_t, child_rekey, bool,
918 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
919 child_sa_t *old, child_sa_t *new)
920 {
921 entry_t *entry;
922
923 update_usage(this, ike_sa, old);
924 this->mutex->lock(this->mutex);
925 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
926 if (entry)
927 {
928 cleanup_sas(this, ike_sa, entry);
929 }
930 this->mutex->unlock(this->mutex);
931 return TRUE;
932 }
933
934 METHOD(listener_t, children_migrate, bool,
935 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, ike_sa_id_t *new,
936 u_int32_t unique)
937 {
938 enumerator_t *enumerator;
939 sa_entry_t *sa, *sa_new, *cached;
940 entry_t *entry_old, *entry_new;
941 array_t *stats;
942
943 if (!new)
944 {
945 return TRUE;
946 }
947 stats = collect_stats(ike_sa, NULL);
948 this->mutex->lock(this->mutex);
949 entry_old = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
950 if (entry_old)
951 {
952 entry_new = get_or_create_entry(this, new, unique);
953 enumerator = array_create_enumerator(stats);
954 while (enumerator->enumerate(enumerator, &sa))
955 {
956 /* if the SA was already rekeyed/cached we cache it too on the new
957 * SA to track it properly until it's finally gone */
958 if (array_bsearch(entry_old->cached, sa, sa_find, &cached) != -1)
959 {
960 sa_new = clone_sa(sa);
961 array_insert_create(&entry_new->cached, ARRAY_TAIL, sa_new);
962 array_sort(entry_new->cached, sa_sort, NULL);
963 }
964 /* if the SA was used, we store it to compensate on the new SA */
965 if (sa->usage.bytes.sent || sa->usage.bytes.received ||
966 sa->usage.packets.sent || sa->usage.packets.received)
967 {
968 sa_new = clone_sa(sa);
969 array_insert_create(&entry_new->migrated, ARRAY_TAIL, sa_new);
970 array_sort(entry_new->migrated, sa_sort, NULL);
971 /* store/update latest stats on old SA to report in Stop */
972 update_sa(entry_old, sa->id, sa->usage);
973 }
974 }
975 enumerator->destroy(enumerator);
976 }
977 this->mutex->unlock(this->mutex);
978 array_destroy_function(stats, (void*)free, NULL);
979 return TRUE;
980 }
981
982 METHOD(listener_t, child_updown, bool,
983 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
984 child_sa_t *child_sa, bool up)
985 {
986 if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
987 {
988 update_usage(this, ike_sa, child_sa);
989 }
990 return TRUE;
991 }
992
993 METHOD(eap_radius_accounting_t, destroy, void,
994 private_eap_radius_accounting_t *this)
995 {
996 charon->bus->remove_listener(charon->bus, &this->public.listener);
997 singleton = NULL;
998 this->mutex->destroy(this->mutex);
999 this->sessions->destroy(this->sessions);
1000 free(this);
1001 }
1002
1003 /**
1004 * See header
1005 */
1006 eap_radius_accounting_t *eap_radius_accounting_create()
1007 {
1008 private_eap_radius_accounting_t *this;
1009
1010 INIT(this,
1011 .public = {
1012 .listener = {
1013 .alert = _alert,
1014 .ike_updown = _ike_updown,
1015 .ike_rekey = _ike_rekey,
1016 .message = _message_hook,
1017 .assign_vips = _assign_vips,
1018 .child_updown = _child_updown,
1019 .child_rekey = _child_rekey,
1020 .children_migrate = _children_migrate,
1021 },
1022 .destroy = _destroy,
1023 },
1024 /* use system time as Session ID prefix */
1025 .prefix = (u_int32_t)time(NULL),
1026 .sessions = hashtable_create((hashtable_hash_t)hash,
1027 (hashtable_equals_t)equals, 32),
1028 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
1029 );
1030 if (lib->settings->get_bool(lib->settings,
1031 "%s.plugins.eap-radius.station_id_with_port", TRUE, lib->ns))
1032 {
1033 this->station_id_fmt = "%#H";
1034 }
1035 else
1036 {
1037 this->station_id_fmt = "%H";
1038 }
1039 if (lib->settings->get_bool(lib->settings,
1040 "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
1041 {
1042 singleton = this;
1043 charon->bus->add_listener(charon->bus, &this->public.listener);
1044 }
1045 this->acct_req_vip = lib->settings->get_bool(lib->settings,
1046 "%s.plugins.eap-radius.accounting_requires_vip",
1047 FALSE, lib->ns);
1048
1049 return &this->public;
1050 }
1051
1052 /**
1053 * See header
1054 */
1055 void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, u_int32_t interval)
1056 {
1057 if (singleton)
1058 {
1059 entry_t *entry;
1060
1061 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us", interval);
1062 singleton->mutex->lock(singleton->mutex);
1063 entry = get_or_create_entry(singleton, ike_sa->get_id(ike_sa),
1064 ike_sa->get_unique_id(ike_sa));
1065 entry->interim.interval = interval;
1066 singleton->mutex->unlock(singleton->mutex);
1067 }
1068 }