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