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