aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2015-03-24 17:38:49 +0100
committerTobias Brunner <tobias@strongswan.org>2015-05-21 15:38:25 +0200
commit7fbe79bce7a92c7ce5aac8eacdf418680e3ae198 (patch)
tree88a0dd318a9a5423e6eda343e63a99ea7dedcfe1
parentd079f6a4f7c643abaa3e6b4d094b4395e56e2ec8 (diff)
downloadstrongswan-7fbe79bce7a92c7ce5aac8eacdf418680e3ae198.tar.bz2
strongswan-7fbe79bce7a92c7ce5aac8eacdf418680e3ae198.tar.xz
eap-radius: Add cache for usage stats of expired/rekeyed SAs
There are several situations that the previous code didn't handle that well, for example, interim updates during rekeying (until the rekeyed SA was deleted the numbers were too high, then suddenly dropped afterwards), or rekeying for IKEv1 in general because rekeyed IPsec SAs stay installed until they expire (so if they were still around when the IKE_SA was terminated, the reported numbers in the Stop message were too high). If intermediate updates are not used the cache entries for rekeyed CHILD_SA will accumulate, we can't clean them up as we don't get child_updown() events for them.
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_accounting.c108
1 files changed, 102 insertions, 6 deletions
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c
index ac4ecfc86..80d01d456 100644
--- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c
+++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2012 Martin Willi
* Copyright (C) 2012 revosec AG
*
@@ -21,6 +24,7 @@
#include <radius_message.h>
#include <radius_client.h>
#include <daemon.h>
+#include <collections/array.h>
#include <collections/hashtable.h>
#include <threading/mutex.h>
#include <processing/jobs/callback_job.h>
@@ -93,6 +97,19 @@ typedef enum {
} radius_acct_terminate_cause_t;
/**
+ * Usage stats for a cached SAs
+ */
+typedef struct {
+ /** unique CHILD_SA identifier */
+ u_int32_t id;
+ /** usage stats for this SA */
+ struct {
+ u_int64_t sent;
+ u_int64_t received;
+ } bytes, packets;
+} sa_entry_t;
+
+/**
* Hashtable entry with usage stats
*/
typedef struct {
@@ -100,11 +117,13 @@ typedef struct {
ike_sa_id_t *id;
/** RADIUS accounting session ID */
char sid[24];
- /** number of sent/received octets/packets */
+ /** number of sent/received octets/packets for expired SAs */
struct {
u_int64_t sent;
u_int64_t received;
} bytes, packets;
+ /** list of cached SAs, sa_entry_t (sorted by their unique ID) */
+ array_t *cached;
/** session creation time */
time_t created;
/** terminate cause */
@@ -123,6 +142,7 @@ typedef struct {
*/
static void destroy_entry(entry_t *this)
{
+ array_destroy_function(this->cached, (void*)free, NULL);
this->id->destroy(this->id);
free(this);
}
@@ -155,6 +175,23 @@ static bool equals(ike_sa_id_t *a, ike_sa_id_t *b)
}
/**
+ * Sort cached SAs
+ */
+static int sa_sort(const void *a, const void *b, void *user)
+{
+ const sa_entry_t *ra = a, *rb = b;
+ return ra->id - rb->id;
+}
+
+/**
+ * Find a cached SA
+ */
+static int sa_find(const void *a, const void *b)
+{
+ return sa_sort(a, b, NULL);
+}
+
+/**
* Update usage counter when a CHILD_SA rekeys/goes down
*/
static void update_usage(private_eap_radius_accounting_t *this,
@@ -162,6 +199,7 @@ static void update_usage(private_eap_radius_accounting_t *this,
{
u_int64_t bytes_in, bytes_out, packets_in, packets_out;
entry_t *entry;
+ sa_entry_t *sa, lookup;
child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, &packets_out);
child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, &packets_in);
@@ -170,10 +208,19 @@ static void update_usage(private_eap_radius_accounting_t *this,
entry = this->sessions->get(this->sessions, ike_sa->get_id(ike_sa));
if (entry)
{
- entry->bytes.sent += bytes_out;
- entry->bytes.received += bytes_in;
- entry->packets.sent += packets_out;
- entry->packets.received += packets_in;
+ lookup.id = child_sa->get_unique_id(child_sa);
+ if (array_bsearch(entry->cached, &lookup, sa_find, &sa) == -1)
+ {
+ INIT(sa,
+ .id = lookup.id,
+ );
+ array_insert_create(&entry->cached, ARRAY_TAIL, sa);
+ array_sort(entry->cached, sa_sort, NULL);
+ }
+ sa->bytes.sent = bytes_out;
+ sa->bytes.received = bytes_in;
+ sa->packets.sent = packets_out;
+ sa->packets.received = packets_in;
}
this->mutex->unlock(this->mutex);
}
@@ -338,19 +385,32 @@ static job_requeue_t send_interim(interim_data_t *data)
ike_sa_t *ike_sa;
entry_t *entry;
u_int32_t value;
+ array_t *stats;
+ sa_entry_t *sa, *found;
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, data->id);
if (!ike_sa)
{
return JOB_REQUEUE_NONE;
}
+ stats = array_create(0, 0);
enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
while (enumerator->enumerate(enumerator, &child_sa))
{
+ INIT(sa,
+ .id = child_sa->get_unique_id(child_sa),
+ );
+ array_insert(stats, ARRAY_TAIL, sa);
+ array_sort(stats, sa_sort, NULL);
+
child_sa->get_usestats(child_sa, FALSE, NULL, &bytes, &packets);
+ sa->bytes.sent = bytes;
+ sa->packets.sent = packets;
bytes_out += bytes;
packets_out += packets;
child_sa->get_usestats(child_sa, TRUE, NULL, &bytes, &packets);
+ sa->bytes.received = bytes;
+ sa->packets.received = packets;
bytes_in += bytes;
packets_in += packets;
}
@@ -365,6 +425,30 @@ static job_requeue_t send_interim(interim_data_t *data)
{
entry->interim.last = time_monotonic(NULL);
+ enumerator = array_create_enumerator(entry->cached);
+ while (enumerator->enumerate(enumerator, &sa))
+ {
+ if (array_bsearch(stats, sa, sa_find, &found) != -1)
+ {
+ /* SA is still around, update stats (e.g. for IKEv1 where
+ * SA might get used even after rekeying) */
+ sa->bytes = found->bytes;
+ sa->packets = found->packets;
+ }
+ else
+ {
+ /* SA is gone, add its latest stats to the total for this IKE_SA
+ * and remove the cache entry */
+ entry->bytes.sent += sa->bytes.sent;
+ entry->bytes.received += sa->bytes.received;
+ entry->packets.sent += sa->packets.sent;
+ entry->packets.received += sa->packets.received;
+ array_remove_at(entry->cached, enumerator);
+ free(sa);
+ }
+ }
+ enumerator->destroy(enumerator);
+
bytes_in += entry->bytes.received;
bytes_out += entry->bytes.sent;
packets_in += entry->packets.received;
@@ -405,6 +489,7 @@ static job_requeue_t send_interim(interim_data_t *data)
schedule_interim(this, entry);
}
this->mutex->unlock(this->mutex);
+ array_destroy_function(stats, (void*)free, NULL);
if (message)
{
@@ -515,7 +600,9 @@ static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
{
radius_message_t *message;
+ enumerator_t *enumerator;
entry_t *entry;
+ sa_entry_t *sa;
u_int32_t value;
this->mutex->lock(this->mutex);
@@ -528,6 +615,16 @@ static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
destroy_entry(entry);
return;
}
+ enumerator = array_create_enumerator(entry->cached);
+ while (enumerator->enumerate(enumerator, &sa))
+ {
+ entry->bytes.sent += sa->bytes.sent;
+ entry->bytes.received += sa->bytes.received;
+ entry->packets.sent += sa->packets.sent;
+ entry->packets.received += sa->packets.received;
+ }
+ enumerator->destroy(enumerator);
+
message = radius_message_create(RMC_ACCOUNTING_REQUEST);
value = htonl(ACCT_STATUS_STOP);
message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
@@ -676,7 +773,6 @@ METHOD(listener_t, child_rekey, bool,
child_sa_t *old, child_sa_t *new)
{
update_usage(this, ike_sa, old);
-
return TRUE;
}