Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_radius/eap_radius_accounting.c, revision 1.1.1.1
1.1 misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>