diff options
author | Martin Willi <martin@revosec.ch> | 2012-01-30 19:16:49 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2012-01-30 19:16:49 +0100 |
commit | 0399edef71f5d0c3b1fbf1b2adfe5e9e985484e7 (patch) | |
tree | e34a2a78002f334b1abc0e025e22cc5874bc6546 /src | |
parent | 8e5b4aa023accbf7b3406050b3b7175b9bd0b6b7 (diff) | |
download | strongswan-0399edef71f5d0c3b1fbf1b2adfe5e9e985484e7.tar.bz2 strongswan-0399edef71f5d0c3b1fbf1b2adfe5e9e985484e7.tar.xz |
Support RADIUS accounting messages containing Framed-IP and Inbound/Outbound-Octets
Diffstat (limited to 'src')
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; } |