eap-radius: Use Framed-IPv6-Address attributes to send IPv6 VIPs in accounting messages
[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 message->add(message, RAT_FRAMED_IPV6_ADDRESS,
451 vip->get_address(vip));
452 break;
453 default:
454 break;
455 }
456 }
457 enumerator->destroy(enumerator);
458 }
459
460 /**
461 * Get an existing or create a new entry from the locked session table
462 */
463 static entry_t* get_or_create_entry(private_eap_radius_accounting_t *this,
464 ike_sa_id_t *id, u_int32_t unique)
465 {
466 entry_t *entry;
467 time_t now;
468
469 entry = this->sessions->get(this->sessions, id);
470 if (!entry)
471 {
472 now = time_monotonic(NULL);
473
474 INIT(entry,
475 .id = id->clone(id),
476 .created = now,
477 .interim = {
478 .last = now,
479 },
480 /* default terminate cause, if none other catched */
481 .cause = ACCT_CAUSE_USER_REQUEST,
482 );
483 snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, unique);
484 this->sessions->put(this->sessions, entry->id, entry);
485 }
486 return entry;
487 }
488
489 /* forward declaration */
490 static void schedule_interim(private_eap_radius_accounting_t *this,
491 entry_t *entry);
492
493 /**
494 * Data passed to send_interim() using callback job
495 */
496 typedef struct {
497 /** reference to radius accounting */
498 private_eap_radius_accounting_t *this;
499 /** IKE_SA identifier to send interim update to */
500 ike_sa_id_t *id;
501 } interim_data_t;
502
503 /**
504 * Clean up interim data
505 */
506 void destroy_interim_data(interim_data_t *this)
507 {
508 this->id->destroy(this->id);
509 free(this);
510 }
511
512 /**
513 * Send an interim update for entry of given IKE_SA identifier
514 */
515 static job_requeue_t send_interim(interim_data_t *data)
516 {
517 private_eap_radius_accounting_t *this = data->this;
518 usage_t usage;
519 radius_message_t *message = NULL;
520 enumerator_t *enumerator;
521 ike_sa_t *ike_sa;
522 entry_t *entry;
523 u_int32_t value;
524 array_t *stats;
525 sa_entry_t *sa, *found;
526
527 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id);
528 if (!ike_sa)
529 {
530 return JOB_REQUEUE_NONE;
531 }
532 stats = collect_stats(ike_sa, &usage);
533 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
534
535 /* avoid any races by returning IKE_SA before acquiring lock */
536
537 this->mutex->lock(this->mutex);
538 entry = this->sessions->get(this->sessions, data->id);
539 if (entry)
540 {
541 entry->interim.last = time_monotonic(NULL);
542
543 enumerator = array_create_enumerator(entry->cached);
544 while (enumerator->enumerate(enumerator, &sa))
545 {
546 if (array_bsearch(stats, sa, sa_find, &found) != -1)
547 {
548 /* SA is still around, update stats (e.g. for IKEv1 where
549 * SA might get used even after rekeying) */
550 sa->usage = found->usage;
551 }
552 else
553 {
554 /* SA is gone, add its last stats to the total for this IKE_SA
555 * and remove the cache entry */
556 add_usage(&entry->usage, sa->usage);
557 array_remove_at(entry->cached, enumerator);
558 free(sa);
559 }
560 }
561 enumerator->destroy(enumerator);
562
563 enumerator = array_create_enumerator(entry->migrated);
564 while (enumerator->enumerate(enumerator, &sa))
565 {
566 if (array_bsearch(stats, sa, sa_find, &found) != -1)
567 {
568 /* SA is still around, but we have to compensate */
569 sub_usage(&usage, sa->usage);
570 }
571 else
572 {
573 /* SA is gone, subtract stats from the total for this IKE_SA */
574 sub_usage(&entry->usage, sa->usage);
575 array_remove_at(entry->migrated, enumerator);
576 free(sa);
577 }
578 }
579 enumerator->destroy(enumerator);
580
581 add_usage(&usage, entry->usage);
582
583 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
584 value = htonl(ACCT_STATUS_INTERIM_UPDATE);
585 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
586 message->add(message, RAT_ACCT_SESSION_ID,
587 chunk_create(entry->sid, strlen(entry->sid)));
588 add_ike_sa_parameters(this, message, ike_sa);
589
590 value = htonl(usage.bytes.sent);
591 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
592 value = htonl(usage.bytes.sent >> 32);
593 if (value)
594 {
595 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
596 chunk_from_thing(value));
597 }
598 value = htonl(usage.packets.sent);
599 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
600
601 value = htonl(usage.bytes.received);
602 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
603 value = htonl(usage.bytes.received >> 32);
604 if (value)
605 {
606 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
607 chunk_from_thing(value));
608 }
609 value = htonl(usage.packets.received);
610 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
611
612 value = htonl(entry->interim.last - entry->created);
613 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
614
615 schedule_interim(this, entry);
616 }
617 this->mutex->unlock(this->mutex);
618 array_destroy_function(stats, (void*)free, NULL);
619
620 if (message)
621 {
622 if (!send_message(this, message))
623 {
624 if (lib->settings->get_bool(lib->settings,
625 "%s.plugins.eap-radius.accounting_close_on_timeout",
626 TRUE, lib->ns))
627 {
628 eap_radius_handle_timeout(data->id);
629 }
630 }
631 message->destroy(message);
632 }
633 return JOB_REQUEUE_NONE;
634 }
635
636 /**
637 * Schedule interim update for given entry
638 */
639 static void schedule_interim(private_eap_radius_accounting_t *this,
640 entry_t *entry)
641 {
642 if (entry->interim.interval)
643 {
644 interim_data_t *data;
645 timeval_t tv = {
646 .tv_sec = entry->interim.last + entry->interim.interval,
647 };
648
649 INIT(data,
650 .this = this,
651 .id = entry->id->clone(entry->id),
652 );
653 lib->scheduler->schedule_job_tv(lib->scheduler,
654 (job_t*)callback_job_create_with_prio(
655 (callback_job_cb_t)send_interim,
656 data, (void*)destroy_interim_data,
657 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL), tv);
658 }
659 }
660
661 /**
662 * Check if an IKE_SA has assigned a virtual IP (to peer)
663 */
664 static bool has_vip(ike_sa_t *ike_sa)
665 {
666 enumerator_t *enumerator;
667 host_t *host;
668 bool found;
669
670 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
671 found = enumerator->enumerate(enumerator, &host);
672 enumerator->destroy(enumerator);
673
674 return found;
675 }
676
677 /**
678 * Send an accounting start message
679 */
680 static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
681 {
682 radius_message_t *message;
683 entry_t *entry;
684 u_int32_t value;
685
686 if (this->acct_req_vip && !has_vip(ike_sa))
687 {
688 return;
689 }
690
691 this->mutex->lock(this->mutex);
692
693 entry = get_or_create_entry(this, ike_sa->get_id(ike_sa),
694 ike_sa->get_unique_id(ike_sa));
695 if (entry->start_sent)
696 {
697 this->mutex->unlock(this->mutex);
698 return;
699 }
700 entry->start_sent = TRUE;
701
702 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
703 value = htonl(ACCT_STATUS_START);
704 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
705 message->add(message, RAT_ACCT_SESSION_ID,
706 chunk_create(entry->sid, strlen(entry->sid)));
707
708 if (!entry->interim.interval)
709 {
710 entry->interim.interval = lib->settings->get_time(lib->settings,
711 "%s.plugins.eap-radius.accounting_interval", 0, lib->ns);
712 if (entry->interim.interval)
713 {
714 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us",
715 entry->interim.interval);
716 }
717 }
718 schedule_interim(this, entry);
719 this->mutex->unlock(this->mutex);
720
721 add_ike_sa_parameters(this, message, ike_sa);
722 if (!send_message(this, message))
723 {
724 eap_radius_handle_timeout(ike_sa->get_id(ike_sa));
725 }
726 message->destroy(message);
727 }
728
729 /**
730 * Send an account stop message
731 */
732 static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
733 {
734 radius_message_t *message;
735 enumerator_t *enumerator;
736 entry_t *entry;
737 sa_entry_t *sa;
738 u_int32_t value;
739
740 this->mutex->lock(this->mutex);
741 entry = this->sessions->remove(this->sessions, ike_sa->get_id(ike_sa));
742 this->mutex->unlock(this->mutex);
743 if (entry)
744 {
745 if (!entry->start_sent)
746 { /* we tried to authenticate this peer, but never sent a start */
747 destroy_entry(entry);
748 return;
749 }
750 enumerator = array_create_enumerator(entry->cached);
751 while (enumerator->enumerate(enumerator, &sa))
752 {
753 add_usage(&entry->usage, sa->usage);
754 }
755 enumerator->destroy(enumerator);
756
757 enumerator = array_create_enumerator(entry->migrated);
758 while (enumerator->enumerate(enumerator, &sa))
759 {
760 sub_usage(&entry->usage, sa->usage);
761 }
762 enumerator->destroy(enumerator);
763
764 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
765 value = htonl(ACCT_STATUS_STOP);
766 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
767 message->add(message, RAT_ACCT_SESSION_ID,
768 chunk_create(entry->sid, strlen(entry->sid)));
769 add_ike_sa_parameters(this, message, ike_sa);
770
771 value = htonl(entry->usage.bytes.sent);
772 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
773 value = htonl(entry->usage.bytes.sent >> 32);
774 if (value)
775 {
776 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
777 chunk_from_thing(value));
778 }
779 value = htonl(entry->usage.packets.sent);
780 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
781
782 value = htonl(entry->usage.bytes.received);
783 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
784 value = htonl(entry->usage.bytes.received >> 32);
785 if (value)
786 {
787 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
788 chunk_from_thing(value));
789 }
790 value = htonl(entry->usage.packets.received);
791 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
792
793 value = htonl(time_monotonic(NULL) - entry->created);
794 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
795
796
797 value = htonl(entry->cause);
798 message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value));
799
800 if (!send_message(this, message))
801 {
802 eap_radius_handle_timeout(NULL);
803 }
804 message->destroy(message);
805 destroy_entry(entry);
806 }
807 }
808
809 METHOD(listener_t, alert, bool,
810 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, alert_t alert,
811 va_list args)
812 {
813 radius_acct_terminate_cause_t cause;
814 entry_t *entry;
815
816 switch (alert)
817 {
818 case ALERT_IKE_SA_EXPIRED:
819 cause = ACCT_CAUSE_SESSION_TIMEOUT;
820 break;
821 case ALERT_RETRANSMIT_SEND_TIMEOUT:
822 cause = ACCT_CAUSE_LOST_SERVICE;
823 break;
824 default:
825 return TRUE;
826 }
827 this->mutex->lock(this->mutex);
828 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
829 if (entry)
830 {
831 entry->cause = cause;
832 }
833 this->mutex->unlock(this->mutex);
834 return TRUE;
835 }
836
837 METHOD(listener_t, ike_updown, bool,
838 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
839 {
840 if (!up)
841 {
842 enumerator_t *enumerator;
843 child_sa_t *child_sa;
844
845 /* update usage for all children just before sending stop */
846 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
847 while (enumerator->enumerate(enumerator, &child_sa))
848 {
849 update_usage(this, ike_sa, child_sa);
850 }
851 enumerator->destroy(enumerator);
852
853 send_stop(this, ike_sa);
854 }
855 return TRUE;
856 }
857
858 METHOD(listener_t, message_hook, bool,
859 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
860 message_t *message, bool incoming, bool plain)
861 {
862 /* start accounting here, virtual IP now is set */
863 if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
864 !incoming && !message->get_request(message))
865 {
866 if (ike_sa->get_version(ike_sa) == IKEV2 &&
867 message->get_exchange_type(message) == IKE_AUTH)
868 {
869 send_start(this, ike_sa);
870 }
871 }
872 return TRUE;
873 }
874
875 METHOD(listener_t, assign_vips, bool,
876 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool assign)
877 {
878 /* start accounting as soon as the virtual IP is set */
879 if (assign && ike_sa->get_version(ike_sa) == IKEV1)
880 {
881 send_start(this, ike_sa);
882 }
883 return TRUE;
884 }
885
886 METHOD(listener_t, ike_rekey, bool,
887 private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new)
888 {
889 entry_t *entry;
890
891 this->mutex->lock(this->mutex);
892 entry = this->sessions->remove(this->sessions, old->get_id(old));
893 if (entry)
894 {
895 /* update IKE_SA identifier */
896 entry->id->destroy(entry->id);
897 entry->id = new->get_id(new);
898 entry->id = entry->id->clone(entry->id);
899 /* fire new interim update job, old gets invalid */
900 schedule_interim(this, entry);
901
902 cleanup_sas(this, new, entry);
903
904 entry = this->sessions->put(this->sessions, entry->id, entry);
905 if (entry)
906 {
907 destroy_entry(entry);
908 }
909 }
910 this->mutex->unlock(this->mutex);
911
912 return TRUE;
913 }
914
915 METHOD(listener_t, child_rekey, bool,
916 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
917 child_sa_t *old, child_sa_t *new)
918 {
919 entry_t *entry;
920
921 update_usage(this, ike_sa, old);
922 this->mutex->lock(this->mutex);
923 entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
924 if (entry)
925 {
926 cleanup_sas(this, ike_sa, entry);
927 }
928 this->mutex->unlock(this->mutex);
929 return TRUE;
930 }
931
932 METHOD(listener_t, children_migrate, bool,
933 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, ike_sa_id_t *new,
934 u_int32_t unique)
935 {
936 enumerator_t *enumerator;
937 sa_entry_t *sa, *sa_new, *cached;
938 entry_t *entry_old, *entry_new;
939 array_t *stats;
940
941 if (!new)
942 {
943 return TRUE;
944 }
945 stats = collect_stats(ike_sa, NULL);
946 this->mutex->lock(this->mutex);
947 entry_old = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
948 if (entry_old)
949 {
950 entry_new = get_or_create_entry(this, new, unique);
951 enumerator = array_create_enumerator(stats);
952 while (enumerator->enumerate(enumerator, &sa))
953 {
954 /* if the SA was already rekeyed/cached we cache it too on the new
955 * SA to track it properly until it's finally gone */
956 if (array_bsearch(entry_old->cached, sa, sa_find, &cached) != -1)
957 {
958 sa_new = clone_sa(sa);
959 array_insert_create(&entry_new->cached, ARRAY_TAIL, sa_new);
960 array_sort(entry_new->cached, sa_sort, NULL);
961 }
962 /* if the SA was used, we store it to compensate on the new SA */
963 if (sa->usage.bytes.sent || sa->usage.bytes.received ||
964 sa->usage.packets.sent || sa->usage.packets.received)
965 {
966 sa_new = clone_sa(sa);
967 array_insert_create(&entry_new->migrated, ARRAY_TAIL, sa_new);
968 array_sort(entry_new->migrated, sa_sort, NULL);
969 /* store/update latest stats on old SA to report in Stop */
970 update_sa(entry_old, sa->id, sa->usage);
971 }
972 }
973 enumerator->destroy(enumerator);
974 }
975 this->mutex->unlock(this->mutex);
976 array_destroy_function(stats, (void*)free, NULL);
977 return TRUE;
978 }
979
980 METHOD(listener_t, child_updown, bool,
981 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
982 child_sa_t *child_sa, bool up)
983 {
984 if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
985 {
986 update_usage(this, ike_sa, child_sa);
987 }
988 return TRUE;
989 }
990
991 METHOD(eap_radius_accounting_t, destroy, void,
992 private_eap_radius_accounting_t *this)
993 {
994 charon->bus->remove_listener(charon->bus, &this->public.listener);
995 singleton = NULL;
996 this->mutex->destroy(this->mutex);
997 this->sessions->destroy(this->sessions);
998 free(this);
999 }
1000
1001 /**
1002 * See header
1003 */
1004 eap_radius_accounting_t *eap_radius_accounting_create()
1005 {
1006 private_eap_radius_accounting_t *this;
1007
1008 INIT(this,
1009 .public = {
1010 .listener = {
1011 .alert = _alert,
1012 .ike_updown = _ike_updown,
1013 .ike_rekey = _ike_rekey,
1014 .message = _message_hook,
1015 .assign_vips = _assign_vips,
1016 .child_updown = _child_updown,
1017 .child_rekey = _child_rekey,
1018 .children_migrate = _children_migrate,
1019 },
1020 .destroy = _destroy,
1021 },
1022 /* use system time as Session ID prefix */
1023 .prefix = (u_int32_t)time(NULL),
1024 .sessions = hashtable_create((hashtable_hash_t)hash,
1025 (hashtable_equals_t)equals, 32),
1026 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
1027 );
1028 if (lib->settings->get_bool(lib->settings,
1029 "%s.plugins.eap-radius.station_id_with_port", TRUE, lib->ns))
1030 {
1031 this->station_id_fmt = "%#H";
1032 }
1033 else
1034 {
1035 this->station_id_fmt = "%H";
1036 }
1037 if (lib->settings->get_bool(lib->settings,
1038 "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
1039 {
1040 singleton = this;
1041 charon->bus->add_listener(charon->bus, &this->public.listener);
1042 }
1043 this->acct_req_vip = lib->settings->get_bool(lib->settings,
1044 "%s.plugins.eap-radius.accounting_requires_vip",
1045 FALSE, lib->ns);
1046
1047 return &this->public;
1048 }
1049
1050 /**
1051 * See header
1052 */
1053 void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, u_int32_t interval)
1054 {
1055 if (singleton)
1056 {
1057 entry_t *entry;
1058
1059 DBG1(DBG_CFG, "scheduling RADIUS Interim-Updates every %us", interval);
1060 singleton->mutex->lock(singleton->mutex);
1061 entry = get_or_create_entry(singleton, ike_sa->get_id(ike_sa),
1062 ike_sa->get_unique_id(ike_sa));
1063 entry->interim.interval = interval;
1064 singleton->mutex->unlock(singleton->mutex);
1065 }
1066 }