aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2012-01-30 19:16:49 +0100
committerMartin Willi <martin@revosec.ch>2012-01-30 19:16:49 +0100
commit0399edef71f5d0c3b1fbf1b2adfe5e9e985484e7 (patch)
treee34a2a78002f334b1abc0e025e22cc5874bc6546 /src
parent8e5b4aa023accbf7b3406050b3b7175b9bd0b6b7 (diff)
downloadstrongswan-0399edef71f5d0c3b1fbf1b2adfe5e9e985484e7.tar.bz2
strongswan-0399edef71f5d0c3b1fbf1b2adfe5e9e985484e7.tar.xz
Support RADIUS accounting messages containing Framed-IP and Inbound/Outbound-Octets
Diffstat (limited to 'src')
-rw-r--r--src/libcharon/plugins/eap_radius/Makefile.am1
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_accounting.c312
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_accounting.h49
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_plugin.c14
4 files changed, 376 insertions, 0 deletions
diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am
index afc50bced..c07f9ab95 100644
--- a/src/libcharon/plugins/eap_radius/Makefile.am
+++ b/src/libcharon/plugins/eap_radius/Makefile.am
@@ -13,6 +13,7 @@ endif
libstrongswan_eap_radius_la_SOURCES = \
eap_radius_plugin.h eap_radius_plugin.c \
eap_radius.h eap_radius.c \
+ eap_radius_accounting.h eap_radius_accounting.c \
radius_server.h radius_server.c \
radius_socket.h radius_socket.c \
radius_client.h radius_client.c \
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c
new file mode 100644
index 000000000..da9d91c3a
--- /dev/null
+++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "eap_radius_accounting.h"
+
+#include <time.h>
+
+#include "radius_message.h"
+#include "radius_client.h"
+#include <daemon.h>
+#include <utils/hashtable.h>
+#include <threading/mutex.h>
+
+typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t;
+
+/**
+ * Private data of an eap_radius_accounting_t object.
+ */
+struct private_eap_radius_accounting_t {
+
+ /**
+ * Public eap_radius_accounting_t interface.
+ */
+ eap_radius_accounting_t public;
+
+ /**
+ * Hashtable with sessions, IKE_SA unique id => entry_t
+ */
+ hashtable_t *sessions;
+
+ /**
+ * Mutex to lock sessions
+ */
+ mutex_t *mutex;
+
+ /**
+ * Session ID prefix
+ */
+ u_int32_t prefix;
+};
+
+/**
+ * Hashtable entry with usage stats
+ */
+typedef struct {
+ /** RADIUS accounting session ID */
+ char sid[16];
+ /** number of octets sent */
+ u_int64_t sent;
+ /** number of octets received */
+ u_int64_t received;
+ /** session creation time */
+ time_t created;
+} entry_t;
+
+/**
+ * Accounting message status types
+ */
+typedef enum {
+ ACCT_STATUS_START = 1,
+ ACCT_STATUS_STOP = 2,
+ ACCT_STATUS_INTERIM_UPDATE = 3,
+ ACCT_STATUS_ACCOUNTING_ON = 7,
+ ACCT_STATUS_ACCOUNTING_OFF = 8,
+} radius_acct_status_t;
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(uintptr_t key)
+{
+ return key;
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(uintptr_t a, uintptr_t b)
+{
+ return a == b;
+}
+
+/**
+ * Update usage counter when a CHILD_SA rekeys/goes down
+ */
+static void update_usage(private_eap_radius_accounting_t *this,
+ ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ u_int64_t sent, received;
+ entry_t *entry;
+
+ child_sa->get_usestats(child_sa, FALSE, NULL, &sent);
+ child_sa->get_usestats(child_sa, TRUE, NULL, &received);
+
+ this->mutex->lock(this->mutex);
+ entry = this->sessions->get(this->sessions,
+ (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
+ if (entry)
+ {
+ entry->sent += sent;
+ entry->received += received;
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Send a RADIUS message, wait for response
+ */
+static bool send_message(private_eap_radius_accounting_t *this,
+ radius_message_t *request)
+{
+ radius_message_t *response;
+ radius_client_t *client;
+ bool ack = FALSE;
+
+ client = radius_client_create();
+ if (client)
+ {
+ response = client->request(client, request);
+ if (response)
+ {
+ ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
+ response->destroy(response);
+ }
+ client->destroy(client);
+ }
+ return ack;
+}
+
+/**
+ * Send an accounting start message
+ */
+static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
+{
+ char user[32];
+ radius_message_t *message;
+ host_t *vip;
+ entry_t *entry;
+ u_int32_t id, value;
+
+ id = ike_sa->get_unique_id(ike_sa);
+ INIT(entry,
+ .created = time_monotonic(NULL),
+ );
+ snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id);
+
+ message = radius_message_create_request(RMC_ACCOUNTING_REQUEST);
+ value = htonl(ACCT_STATUS_START);
+ message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
+ message->add(message, RAT_ACCT_SESSION_ID,
+ chunk_create(entry->sid, strlen(entry->sid)));
+ snprintf(user, sizeof(user), "%Y", ike_sa->get_other_eap_id(ike_sa));
+ message->add(message, RAT_USER_NAME, chunk_create(user, strlen(user)));
+ vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
+ if (vip)
+ {
+ message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip));
+ }
+ if (send_message(this, message))
+ {
+ this->mutex->lock(this->mutex);
+ entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry);
+ this->mutex->unlock(this->mutex);
+ free(entry);
+ }
+ message->destroy(message);
+}
+
+/**
+ * Send an account stop message
+ */
+static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
+{
+ radius_message_t *message;
+ entry_t *entry;
+ u_int32_t id, value;
+ host_t *vip;
+ char user[32];
+
+ id = ike_sa->get_unique_id(ike_sa);
+ this->mutex->lock(this->mutex);
+ entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id);
+ this->mutex->unlock(this->mutex);
+ if (entry)
+ {
+ message = radius_message_create_request(RMC_ACCOUNTING_REQUEST);
+ value = htonl(ACCT_STATUS_STOP);
+ message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
+ message->add(message, RAT_ACCT_SESSION_ID,
+ chunk_create(entry->sid, strlen(entry->sid)));
+ snprintf(user, sizeof(user), "%Y", ike_sa->get_other_eap_id(ike_sa));
+ message->add(message, RAT_USER_NAME, chunk_create(user, strlen(user)));
+ vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
+ if (vip)
+ {
+ message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip));
+ }
+ value = htonl(entry->sent);
+ message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
+ value = htonl(entry->sent >> 32);
+ if (value)
+ {
+ message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
+ chunk_from_thing(value));
+ }
+ value = htonl(entry->received);
+ message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
+ value = htonl(entry->received >> 32);
+ if (value)
+ {
+ message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
+ chunk_from_thing(value));
+ }
+ value = htonl(time_monotonic(NULL) - entry->created);
+ message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
+
+ send_message(this, message);
+ message->destroy(message);
+ free(entry);
+ }
+}
+
+METHOD(listener_t, ike_updown, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
+{
+ if (!up)
+ {
+ send_stop(this, ike_sa);
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, message_hook, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
+ message_t *message, bool incoming)
+{
+ /* start accounting here, virtual IP now is set */
+ if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
+ message->get_exchange_type(message) == IKE_AUTH &&
+ !incoming && !message->get_request(message))
+ {
+ send_start(this, ike_sa);
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, child_rekey, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
+ child_sa_t *old, child_sa_t *new)
+{
+ update_usage(this, ike_sa, old);
+
+ return TRUE;
+}
+
+METHOD(listener_t, child_updown, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
+ child_sa_t *child_sa, bool up)
+{
+ if (!up)
+ {
+ update_usage(this, ike_sa, child_sa);
+ }
+ return TRUE;
+}
+
+METHOD(eap_radius_accounting_t, destroy, void,
+ private_eap_radius_accounting_t *this)
+{
+ this->mutex->destroy(this->mutex);
+ this->sessions->destroy(this->sessions);
+ free(this);
+}
+
+/**
+ * See header
+ */
+eap_radius_accounting_t *eap_radius_accounting_create()
+{
+ private_eap_radius_accounting_t *this;
+
+ INIT(this,
+ .public = {
+ .listener = {
+ .ike_updown = _ike_updown,
+ .message = _message_hook,
+ .child_updown = _child_updown,
+ .child_rekey = _child_rekey,
+ },
+ .destroy = _destroy,
+ },
+ /* use system time as Session ID prefix */
+ .prefix = (u_int32_t)time(NULL),
+ .sessions = hashtable_create((hashtable_hash_t)hash,
+ (hashtable_equals_t)equals, 32),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h
new file mode 100644
index 000000000..a634e2d70
--- /dev/null
+++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup eap_radius_accounting eap_radius_accounting
+ * @{ @ingroup
+ */
+
+#ifndef EAP_RADIUS_ACCOUNTING_H_
+#define EAP_RADIUS_ACCOUNTING_H_
+
+#include <bus/listeners/listener.h>
+
+typedef struct eap_radius_accounting_t eap_radius_accounting_t;
+
+/**
+ * RADIUS accounting for IKE/IPsec.
+ */
+struct eap_radius_accounting_t {
+
+ /**
+ * Implements listener_t.
+ */
+ listener_t listener;
+
+ /**
+ * Destroy a eap_radius_accounting_t.
+ */
+ void (*destroy)(eap_radius_accounting_t *this);
+};
+
+/**
+ * Create a eap_radius_accounting instance.
+ */
+eap_radius_accounting_t *eap_radius_accounting_create();
+
+#endif /** EAP_RADIUS_ACCOUNTING_H_ @}*/
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c
index c38ebb9be..546736c94 100644
--- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c
+++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c
@@ -16,6 +16,7 @@
#include "eap_radius_plugin.h"
#include "eap_radius.h"
+#include "eap_radius_accounting.h"
#include "radius_client.h"
#include "radius_server.h"
@@ -53,6 +54,11 @@ struct private_eap_radius_plugin_t {
* Lock for server list
*/
rwlock_t *lock;
+
+ /**
+ * RADIUS sessions for accounting
+ */
+ eap_radius_accounting_t *accounting;
};
/**
@@ -185,6 +191,8 @@ METHOD(plugin_t, destroy, void,
this->servers->destroy_offset(this->servers,
offsetof(radius_server_t, destroy));
this->lock->destroy(this->lock);
+ charon->bus->remove_listener(charon->bus, &this->accounting->listener);
+ this->accounting->destroy(this->accounting);
free(this);
instance = NULL;
}
@@ -207,11 +215,17 @@ plugin_t *eap_radius_plugin_create()
},
.servers = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .accounting = eap_radius_accounting_create(),
);
load_servers(this);
instance = this;
+ if (lib->settings->get_bool(lib->settings,
+ "charon.plugins.eap-radius.accounting", FALSE))
+ {
+ charon->bus->add_listener(charon->bus, &this->accounting->listener);
+ }
return &this->public.plugin;
}