diff options
Diffstat (limited to 'src/libcharon/bus')
-rw-r--r-- | src/libcharon/bus/bus.c | 727 | ||||
-rw-r--r-- | src/libcharon/bus/bus.h | 286 | ||||
-rw-r--r-- | src/libcharon/bus/listeners/file_logger.c | 125 | ||||
-rw-r--r-- | src/libcharon/bus/listeners/file_logger.h | 60 | ||||
-rw-r--r-- | src/libcharon/bus/listeners/listener.h | 178 | ||||
-rw-r--r-- | src/libcharon/bus/listeners/sys_logger.c | 121 | ||||
-rw-r--r-- | src/libcharon/bus/listeners/sys_logger.h | 62 |
7 files changed, 1559 insertions, 0 deletions
diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c new file mode 100644 index 000000000..764744a41 --- /dev/null +++ b/src/libcharon/bus/bus.c @@ -0,0 +1,727 @@ +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 "bus.h" + +#include <stdint.h> + +#include <daemon.h> +#include <threading/thread.h> +#include <threading/thread_value.h> +#include <threading/condvar.h> +#include <threading/mutex.h> + +typedef struct private_bus_t private_bus_t; + +/** + * Private data of a bus_t object. + */ +struct private_bus_t { + /** + * Public part of a bus_t object. + */ + bus_t public; + + /** + * List of registered listeners as entry_t's + */ + linked_list_t *listeners; + + /** + * mutex to synchronize active listeners, recursively + */ + mutex_t *mutex; + + /** + * Thread local storage the threads IKE_SA + */ + thread_value_t *thread_sa; +}; + +typedef struct entry_t entry_t; + +/** + * a listener entry, either active or passive + */ +struct entry_t { + + /** + * registered listener interface + */ + listener_t *listener; + + /** + * is this a active listen() call with a blocking thread + */ + bool blocker; + + /** + * are we currently calling this listener + */ + int calling; + + /** + * condvar where active listeners wait + */ + condvar_t *condvar; +}; + +/** + * create a listener entry + */ +static entry_t *entry_create(listener_t *listener, bool blocker) +{ + entry_t *this = malloc_thing(entry_t); + + this->listener = listener; + this->blocker = blocker; + this->calling = 0; + this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + + return this; +} + +/** + * destroy an entry_t + */ +static void entry_destroy(entry_t *entry) +{ + entry->condvar->destroy(entry->condvar); + free(entry); +} + +/** + * Implementation of bus_t.add_listener. + */ +static void add_listener(private_bus_t *this, listener_t *listener) +{ + this->mutex->lock(this->mutex); + this->listeners->insert_last(this->listeners, entry_create(listener, FALSE)); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.remove_listener. + */ +static void remove_listener(private_bus_t *this, listener_t *listener) +{ + enumerator_t *enumerator; + entry_t *entry; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->listener == listener) + { + this->listeners->remove_at(this->listeners, enumerator); + entry_destroy(entry); + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +typedef struct cleanup_data_t cleanup_data_t; + +/** + * data to remove a listener using thread_cleanup_t handler + */ +struct cleanup_data_t { + /** bus instance */ + private_bus_t *this; + /** listener entry */ + entry_t *entry; +}; + +/** + * thread_cleanup_t handler to remove a listener + */ +static void listener_cleanup(cleanup_data_t *data) +{ + data->this->listeners->remove(data->this->listeners, data->entry, NULL); + entry_destroy(data->entry); +} + +/** + * Implementation of bus_t.listen. + */ +static void listen_(private_bus_t *this, listener_t *listener, job_t *job) +{ + bool old; + cleanup_data_t data; + + data.this = this; + data.entry = entry_create(listener, TRUE); + + this->mutex->lock(this->mutex); + this->listeners->insert_last(this->listeners, data.entry); + charon->processor->queue_job(charon->processor, job); + thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex); + thread_cleanup_push((thread_cleanup_t)listener_cleanup, &data); + old = thread_cancelability(TRUE); + while (data.entry->blocker) + { + data.entry->condvar->wait(data.entry->condvar, this->mutex); + } + thread_cancelability(old); + thread_cleanup_pop(FALSE); + /* unlock mutex */ + thread_cleanup_pop(TRUE); + entry_destroy(data.entry); +} + +/** + * Implementation of bus_t.set_sa. + */ +static void set_sa(private_bus_t *this, ike_sa_t *ike_sa) +{ + this->thread_sa->set(this->thread_sa, ike_sa); +} + +/** + * Implementation of bus_t.get_sa + */ +static ike_sa_t* get_sa(private_bus_t *this) +{ + return this->thread_sa->get(this->thread_sa); +} + +/** + * data associated to a signal, passed to callback + */ +typedef struct { + /** associated IKE_SA */ + ike_sa_t *ike_sa; + /** invoking thread */ + long thread; + /** debug group */ + debug_t group; + /** debug level */ + level_t level; + /** format string */ + char *format; + /** argument list */ + va_list args; +} log_data_t; + +/** + * listener->log() invocation as a list remove callback + */ +static bool log_cb(entry_t *entry, log_data_t *data) +{ + va_list args; + + if (entry->calling || !entry->listener->log) + { /* avoid recursive calls */ + return FALSE; + } + entry->calling++; + va_copy(args, data->args); + if (!entry->listener->log(entry->listener, data->group, data->level, + data->thread, data->ike_sa, data->format, args)) + { + if (entry->blocker) + { + entry->blocker = FALSE; + entry->condvar->signal(entry->condvar); + } + else + { + entry_destroy(entry); + } + va_end(args); + entry->calling--; + return TRUE; + } + va_end(args); + entry->calling--; + return FALSE; +} + +/** + * Implementation of bus_t.vlog. + */ +static void vlog(private_bus_t *this, debug_t group, level_t level, + char* format, va_list args) +{ + log_data_t data; + + data.ike_sa = this->thread_sa->get(this->thread_sa); + data.thread = thread_current_id(); + data.group = group; + data.level = level; + data.format = format; + va_copy(data.args, args); + + this->mutex->lock(this->mutex); + /* We use the remove() method to invoke all listeners. This is cheap and + * does not require an allocation for this performance critical function. */ + this->listeners->remove(this->listeners, &data, (void*)log_cb); + this->mutex->unlock(this->mutex); + + va_end(data.args); +} + +/** + * Implementation of bus_t.log. + */ +static void log_(private_bus_t *this, debug_t group, level_t level, + char* format, ...) +{ + va_list args; + + va_start(args, format); + vlog(this, group, level, format, args); + va_end(args); +} + +/** + * unregister a listener + */ +static void unregister_listener(private_bus_t *this, entry_t *entry, + enumerator_t *enumerator) +{ + if (entry->blocker) + { + entry->blocker = FALSE; + entry->condvar->signal(entry->condvar); + } + else + { + entry_destroy(entry); + } + this->listeners->remove_at(this->listeners, enumerator); +} + +/** + * Implementation of bus_t.alert + */ +static void alert(private_bus_t *this, alert_t alert, ...) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + va_list args; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->alert) + { + continue; + } + entry->calling++; + va_start(args, alert); + keep = entry->listener->alert(entry->listener, ike_sa, alert, args); + va_end(args); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.ike_state_change + */ +static void ike_state_change(private_bus_t *this, ike_sa_t *ike_sa, + ike_sa_state_t state) +{ + enumerator_t *enumerator; + entry_t *entry; + bool keep; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->ike_state_change) + { + continue; + } + entry->calling++; + keep = entry->listener->ike_state_change(entry->listener, ike_sa, state); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.child_state_change + */ +static void child_state_change(private_bus_t *this, child_sa_t *child_sa, + child_sa_state_t state) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->child_state_change) + { + continue; + } + entry->calling++; + keep = entry->listener->child_state_change(entry->listener, ike_sa, + child_sa, state); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.message + */ +static void message(private_bus_t *this, message_t *message, bool incoming) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->message) + { + continue; + } + entry->calling++; + keep = entry->listener->message(entry->listener, ike_sa, + message, incoming); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.ike_keys + */ +static void ike_keys(private_bus_t *this, ike_sa_t *ike_sa, + diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, + ike_sa_t *rekey) +{ + enumerator_t *enumerator; + entry_t *entry; + bool keep; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->ike_keys) + { + continue; + } + entry->calling++; + keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, + nonce_i, nonce_r, rekey); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.child_keys + */ +static void child_keys(private_bus_t *this, child_sa_t *child_sa, + diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->child_keys) + { + continue; + } + entry->calling++; + keep = entry->listener->child_keys(entry->listener, ike_sa, child_sa, + dh, nonce_i, nonce_r); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.child_updown + */ +static void child_updown(private_bus_t *this, child_sa_t *child_sa, bool up) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->child_updown) + { + continue; + } + entry->calling++; + keep = entry->listener->child_updown(entry->listener, + ike_sa, child_sa, up); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.child_rekey + */ +static void child_rekey(private_bus_t *this, child_sa_t *old, child_sa_t *new) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->child_rekey) + { + continue; + } + entry->calling++; + keep = entry->listener->child_rekey(entry->listener, ike_sa, old, new); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.ike_updown + */ +static void ike_updown(private_bus_t *this, ike_sa_t *ike_sa, bool up) +{ + enumerator_t *enumerator; + entry_t *entry; + bool keep; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->ike_updown) + { + continue; + } + entry->calling++; + keep = entry->listener->ike_updown(entry->listener, ike_sa, up); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + /* a down event for IKE_SA implicitly downs all CHILD_SAs */ + if (!up) + { + iterator_t *iterator; + child_sa_t *child_sa; + + iterator = ike_sa->create_child_sa_iterator(ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_updown(this, child_sa, FALSE); + } + iterator->destroy(iterator); + } +} + +/** + * Implementation of bus_t.ike_rekey + */ +static void ike_rekey(private_bus_t *this, ike_sa_t *old, ike_sa_t *new) +{ + enumerator_t *enumerator; + entry_t *entry; + bool keep; + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->ike_rekey) + { + continue; + } + entry->calling++; + keep = entry->listener->ike_rekey(entry->listener, old, new); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + +/** + * Implementation of bus_t.authorize + */ +static bool authorize(private_bus_t *this, bool final) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep, success = TRUE; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->authorize) + { + continue; + } + entry->calling++; + keep = entry->listener->authorize(entry->listener, ike_sa, + final, &success); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + if (!success) + { + break; + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + return success; +} + +/** + * Implementation of bus_t.destroy. + */ +static void destroy(private_bus_t *this) +{ + this->thread_sa->destroy(this->thread_sa); + this->mutex->destroy(this->mutex); + this->listeners->destroy_function(this->listeners, (void*)entry_destroy); + free(this); +} + +/* + * Described in header. + */ +bus_t *bus_create() +{ + private_bus_t *this = malloc_thing(private_bus_t); + + this->public.add_listener = (void(*)(bus_t*,listener_t*))add_listener; + this->public.remove_listener = (void(*)(bus_t*,listener_t*))remove_listener; + this->public.listen = (void(*)(bus_t*, listener_t *listener, job_t *job))listen_; + this->public.set_sa = (void(*)(bus_t*,ike_sa_t*))set_sa; + this->public.get_sa = (ike_sa_t*(*)(bus_t*))get_sa; + this->public.log = (void(*)(bus_t*,debug_t,level_t,char*,...))log_; + this->public.vlog = (void(*)(bus_t*,debug_t,level_t,char*,va_list))vlog; + this->public.alert = (void(*)(bus_t*, alert_t alert, ...))alert; + this->public.ike_state_change = (void(*)(bus_t*,ike_sa_t*,ike_sa_state_t))ike_state_change; + this->public.child_state_change = (void(*)(bus_t*,child_sa_t*,child_sa_state_t))child_state_change; + this->public.message = (void(*)(bus_t*, message_t *message, bool incoming))message; + this->public.ike_keys = (void(*)(bus_t*, ike_sa_t *ike_sa, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey))ike_keys; + this->public.child_keys = (void(*)(bus_t*, child_sa_t *child_sa, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r))child_keys; + this->public.ike_updown = (void(*)(bus_t*, ike_sa_t *ike_sa, bool up))ike_updown; + this->public.ike_rekey = (void(*)(bus_t*, ike_sa_t *old, ike_sa_t *new))ike_rekey; + this->public.child_updown = (void(*)(bus_t*, child_sa_t *child_sa, bool up))child_updown; + this->public.child_rekey = (void(*)(bus_t*, child_sa_t *old, child_sa_t *new))child_rekey; + this->public.authorize = (bool(*)(bus_t*, bool final))authorize; + this->public.destroy = (void(*)(bus_t*)) destroy; + + this->listeners = linked_list_create(); + this->mutex = mutex_create(MUTEX_TYPE_RECURSIVE); + this->thread_sa = thread_value_create(NULL); + + return &this->public; +} + diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h new file mode 100644 index 000000000..8cf392eae --- /dev/null +++ b/src/libcharon/bus/bus.h @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2006-2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 bus bus + * @{ @ingroup libcharon + */ + +#ifndef BUS_H_ +#define BUS_H_ + +typedef enum alert_t alert_t; +typedef struct bus_t bus_t; + +#include <stdarg.h> + +#include <debug.h> +#include <sa/ike_sa.h> +#include <sa/child_sa.h> +#include <processing/jobs/job.h> +#include <bus/listeners/listener.h> + +/* undefine the definitions from libstrongswan */ +#undef DBG0 +#undef DBG1 +#undef DBG2 +#undef DBG3 +#undef DBG4 + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 4 +#endif /* DEBUG_LEVEL */ + +#if DEBUG_LEVEL >= 0 +#define DBG0(group, format, ...) charon->bus->log(charon->bus, group, 0, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL >= 0 */ +#if DEBUG_LEVEL >= 1 +#define DBG1(group, format, ...) charon->bus->log(charon->bus, group, 1, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL >= 1 */ +#if DEBUG_LEVEL >= 2 +#define DBG2(group, format, ...) charon->bus->log(charon->bus, group, 2, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL >= 2 */ +#if DEBUG_LEVEL >= 3 +#define DBG3(group, format, ...) charon->bus->log(charon->bus, group, 3, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL >= 3 */ +#if DEBUG_LEVEL >= 4 +#define DBG4(group, format, ...) charon->bus->log(charon->bus, group, 4, format, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL >= 4 */ + +#ifndef DBG0 +# define DBG0(...) {} +#endif /* DBG0 */ +#ifndef DBG1 +# define DBG1(...) {} +#endif /* DBG1 */ +#ifndef DBG2 +# define DBG2(...) {} +#endif /* DBG2 */ +#ifndef DBG3 +# define DBG3(...) {} +#endif /* DBG3 */ +#ifndef DBG4 +# define DBG4(...) {} +#endif /* DBG4 */ + +/** + * Kind of alerts to raise. + */ +enum alert_t { + /* a RADIUS server did not respond, no additional arguments */ + ALERT_RADIUS_NOT_RESPONDING, + /* a shutdown signal has been received, argument is a int with the signal */ + ALERT_SHUTDOWN_SIGNAL, +}; + +/** + * The bus receives events and sends them to all registered listeners. + * + * Any events sent to are delivered to all registered listeners. Threads + * may wait actively to events using the blocking listen() call. + */ +struct bus_t { + + /** + * Register a listener to the bus. + * + * A registered listener receives all events which are sent to the bus. + * The listener is passive; the thread which emitted the event + * processes the listener routine. + * + * @param listener listener to register. + */ + void (*add_listener) (bus_t *this, listener_t *listener); + + /** + * Unregister a listener from the bus. + * + * @param listener listener to unregister. + */ + void (*remove_listener) (bus_t *this, listener_t *listener); + + /** + * Register a listener and block the calling thread. + * + * This call registers a listener and blocks the calling thread until + * its listeners function returns FALSE. This allows to wait for certain + * events. The associated job is executed after the listener has been + * registered: This allows to listen on events we initiate with the job, + * without missing any events to job may fire. + * + * @param listener listener to register + * @param job job to execute asynchronously when registered, or NULL + */ + void (*listen)(bus_t *this, listener_t *listener, job_t *job); + + /** + * Set the IKE_SA the calling thread is using. + * + * To associate an received log message to an IKE_SA without passing it as + * parameter each time, the thread registers the currenlty used IKE_SA + * during check-out. Before check-in, the thread unregisters the IKE_SA. + * This IKE_SA is stored per-thread, so each thread has its own IKE_SA + * registered. + * + * @param ike_sa ike_sa to register, or NULL to unregister + */ + void (*set_sa) (bus_t *this, ike_sa_t *ike_sa); + + /** + * Get the IKE_SA the calling thread is currently using. + * + * If a thread currently does not know what IKE_SA it is processing, + * it can call get_sa() to look up the SA set during checkout via set_sa(). + * + * @return registered ike_sa, NULL if none registered + */ + ike_sa_t* (*get_sa)(bus_t *this); + + /** + * Send a log message to the bus. + * + * The signal specifies the type of the event occured. The format string + * specifies an additional informational or error message with a + * printf() like variable argument list. + * Use the DBG() macros. + * + * @param group debugging group + * @param level verbosity level of the signal + * @param format printf() style format string + * @param ... printf() style argument list + */ + void (*log)(bus_t *this, debug_t group, level_t level, char* format, ...); + + /** + * Send a log message to the bus using va_list arguments. + * + * Same as bus_t.signal(), but uses va_list argument list. + * + * @param group kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param format printf() style format string + * @param args va_list arguments + */ + void (*vlog)(bus_t *this, debug_t group, level_t level, + char* format, va_list args); + + /** + * Raise an alert over the bus. + * + * @param alert kind of alert + * @param ... alert specific attributes + */ + void (*alert)(bus_t *this, alert_t alert, ...); + + /** + * Send a IKE_SA state change event to the bus. + * + * @param ike_sa IKE_SA which changes its state + * @param state new state IKE_SA changes to + */ + void (*ike_state_change)(bus_t *this, ike_sa_t *ike_sa, + ike_sa_state_t state); + /** + * Send a CHILD_SA state change event to the bus. + * + * @param child_sa CHILD_SA which changes its state + * @param state new state CHILD_SA changes to + */ + void (*child_state_change)(bus_t *this, child_sa_t *child_sa, + child_sa_state_t state); + /** + * Message send/receive hook. + * + * @param message message to send/receive + * @param incoming TRUE for incoming messages, FALSE for outgoing + */ + void (*message)(bus_t *this, message_t *message, bool incoming); + + /** + * IKE_SA authorization hook. + * + * @param final TRUE if this is the final invocation + * @return TRUE to establish IKE_SA, FALSE to send AUTH_FAILED + */ + bool (*authorize)(bus_t *this, bool final); + + /** + * IKE_SA keymat hook. + * + * @param ike_sa IKE_SA this keymat belongs to + * @param dh diffie hellman shared secret + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + * @param rekey IKE_SA we are rekeying, if any + */ + void (*ike_keys)(bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, + chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey); + /** + * CHILD_SA keymat hook. + * + * @param child_sa CHILD_SA this keymat is used for + * @param dh diffie hellman shared secret + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + */ + void (*child_keys)(bus_t *this, child_sa_t *child_sa, diffie_hellman_t *dh, + chunk_t nonce_i, chunk_t nonce_r); + + /** + * IKE_SA up/down hook. + * + * @param ike_sa IKE_SA coming up/going down + * @param up TRUE for an up event, FALSE for a down event + */ + void (*ike_updown)(bus_t *this, ike_sa_t *ike_sa, bool up); + + /** + * IKE_SA rekeying hook. + * + * @param old rekeyed and obsolete IKE_SA + * @param new new IKE_SA replacing old + */ + void (*ike_rekey)(bus_t *this, ike_sa_t *old, ike_sa_t *new); + + /** + * CHILD_SA up/down hook. + * + * @param child_sa CHILD_SA coming up/going down + * @param up TRUE for an up event, FALSE for a down event + */ + void (*child_updown)(bus_t *this, child_sa_t *child_sa, bool up); + + /** + * CHILD_SA rekeying hook. + * + * @param old rekeyed and obsolete CHILD_SA + * @param new new CHILD_SA replacing old + */ + void (*child_rekey)(bus_t *this, child_sa_t *old, child_sa_t *new); + + /** + * Destroy the event bus. + */ + void (*destroy) (bus_t *this); +}; + +/** + * Create the event bus which forwards events to its listeners. + * + * @return event bus instance + */ +bus_t *bus_create(); + +#endif /** BUS_H_ @}*/ diff --git a/src/libcharon/bus/listeners/file_logger.c b/src/libcharon/bus/listeners/file_logger.c new file mode 100644 index 000000000..12587deaf --- /dev/null +++ b/src/libcharon/bus/listeners/file_logger.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 <stdio.h> +#include <string.h> + +#include "file_logger.h" + + +typedef struct private_file_logger_t private_file_logger_t; + +/** + * Private data of a file_logger_t object + */ +struct private_file_logger_t { + + /** + * Public data. + */ + file_logger_t public; + + /** + * output file + */ + FILE *out; + + /** + * Maximum level to log, for each group + */ + level_t levels[DBG_MAX]; +}; + +/** + * Implementation of bus_listener_t.log. + */ +static bool log_(private_file_logger_t *this, debug_t group, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->levels[group]) + { + char buffer[8192]; + char *current = buffer, *next; + + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), format, args); + + /* prepend a prefix in front of every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + fprintf(this->out, "%.2d[%N] %s\n", + thread, debug_names, group, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +/** + * Implementation of file_logger_t.set_level. + */ +static void set_level(private_file_logger_t *this, debug_t group, level_t level) +{ + if (group < DBG_ANY) + { + this->levels[group] = level; + } + else + { + for (group = 0; group < DBG_MAX; group++) + { + this->levels[group] = level; + } + } +} + +/** + * Implementation of file_logger_t.destroy. + */ +static void destroy(private_file_logger_t *this) +{ + if (this->out != stdout && this->out != stderr) + { + fclose(this->out); + } + free(this); +} + +/* + * Described in header. + */ +file_logger_t *file_logger_create(FILE *out) +{ + private_file_logger_t *this = malloc_thing(private_file_logger_t); + + /* public functions */ + memset(&this->public.listener, 0, sizeof(listener_t)); + this->public.listener.log = (bool(*)(listener_t*,debug_t,level_t,int,ike_sa_t*,char*,va_list))log_; + this->public.set_level = (void(*)(file_logger_t*,debug_t,level_t))set_level; + this->public.destroy = (void(*)(file_logger_t*))destroy; + + /* private variables */ + this->out = out; + set_level(this, DBG_ANY, LEVEL_SILENT); + + return &this->public; +} + diff --git a/src/libcharon/bus/listeners/file_logger.h b/src/libcharon/bus/listeners/file_logger.h new file mode 100644 index 000000000..bd443fdb8 --- /dev/null +++ b/src/libcharon/bus/listeners/file_logger.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 file_logger file_logger + * @{ @ingroup listeners + */ + +#ifndef FILE_LOGGER_H_ +#define FILE_LOGGER_H_ + +#include <bus/listeners/listener.h> + +typedef struct file_logger_t file_logger_t; + +/** + * Logger to files which implements listener_t. + */ +struct file_logger_t { + + /** + * Implements the listener_t interface. + */ + listener_t listener; + + /** + * Set the loglevel for a debug group. + * + * @param group debug group to set + * @param level max level to log (0..4) + */ + void (*set_level) (file_logger_t *this, debug_t group, level_t level); + + /** + * Destroys a file_logger_t object. + */ + void (*destroy) (file_logger_t *this); +}; + +/** + * Constructor to create a file_logger_t object. + * + * @param out FILE to write to + * @return file_logger_t object + */ +file_logger_t *file_logger_create(FILE *out); + +#endif /** FILE_LOGGER_H_ @}*/ diff --git a/src/libcharon/bus/listeners/listener.h b/src/libcharon/bus/listeners/listener.h new file mode 100644 index 000000000..9a51a2ef4 --- /dev/null +++ b/src/libcharon/bus/listeners/listener.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 listener listener + * @{ @ingroup listeners + */ + +#ifndef LISTENER_H_ +#define LISTENER_H_ + +typedef struct listener_t listener_t; + +#include <bus/bus.h> + +/** + * Listener interface, listens to events if registered to the bus. + */ +struct listener_t { + + /** + * Log a debugging message. + * + * The implementing signal function returns TRUE to stay registered + * to the bus, or FALSE to unregister itself. + * Calling bus_t.log() inside of a registered listener is possible, + * but the bus does not invoke listeners recursively. + * + * @param group kind of the signal (up, down, rekeyed, ...) + * @param level verbosity level of the signal + * @param thread ID of the thread raised this signal + * @param ike_sa IKE_SA associated to the event + * @param format printf() style format string + * @param args vprintf() style va_list argument list + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*log)(listener_t *this, debug_t group, level_t level, int thread, + ike_sa_t *ike_sa, char* format, va_list args); + + /** + * Hook called if a critical alert is risen. + * + * @param ike_sa IKE_SA associated to the alert, if any + * @param alert kind of alert + * @param ... alert specific argument list + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*alert)(listener_t *this, ike_sa_t *ike_sa, + alert_t alert, va_list args); + + /** + * Handle state changes in an IKE_SA. + * + * @param ike_sa IKE_SA which changes its state + * @param state new IKE_SA state this IKE_SA changes to + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*ike_state_change)(listener_t *this, ike_sa_t *ike_sa, + ike_sa_state_t state); + + /** + * Handle state changes in a CHILD_SA. + * + * @param ike_sa IKE_SA containing the affected CHILD_SA + * @param child_sa CHILD_SA which changes its state + * @param state new CHILD_SA state this CHILD_SA changes to + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*child_state_change)(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, child_sa_state_t state); + + /** + * Hook called for received/sent messages of an IKE_SA. + * + * @param ike_sa IKE_SA sending/receving a message + * @param message message object + * @param incoming TRUE for incoming messages, FALSE for outgoing + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*message)(listener_t *this, ike_sa_t *ike_sa, message_t *message, + bool incoming); + + /** + * Hook called with IKE_SA key material. + * + * @param ike_sa IKE_SA this keymat belongs to + * @param dh diffie hellman shared secret + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + * @param rekey IKE_SA we are rekeying, if any + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*ike_keys)(listener_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, + chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey); + + /** + * Hook called with CHILD_SA key material. + * + * @param ike_sa IKE_SA the child sa belongs to + * @param child_sa CHILD_SA this keymat is used for + * @param dh diffie hellman shared secret + * @param nonce_i initiators nonce + * @param nonce_r responders nonce + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*child_keys)(listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, + diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r); + + /** + * Hook called if an IKE_SA gets up or down. + * + * @param ike_sa IKE_SA coming up/going down + * @param up TRUE for an up event, FALSE for a down event + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*ike_updown)(listener_t *this, ike_sa_t *ike_sa, bool up); + + /** + * Hook called when an IKE_SA gets rekeyed. + * + * @param old rekeyed IKE_SA getting obsolete + * @param new new IKE_SA replacing old + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*ike_rekey)(listener_t *this, ike_sa_t *old, ike_sa_t *new); + + /** + * Hook called when a CHILD_SA gets up or down. + * + * @param ike_sa IKE_SA containing the handled CHILD_SA + * @param child_sa CHILD_SA coming up/going down + * @param up TRUE for an up event, FALSE for a down event + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*child_updown)(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool up); + + /** + * Hook called when an CHILD_SA gets rekeyed. + * + * @param ike_sa IKE_SA containing the rekeyed CHILD_SA + * @param old rekeyed CHILD_SA getting obsolete + * @param new new CHILD_SA replacing old + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*child_rekey)(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *old, child_sa_t *new); + + /** + * Hook called to invoke additional authorization rules. + * + * An authorization hook gets invoked several times: After each + * authentication round, the hook gets invoked with with final = FALSE. + * After authentication is complete and the peer configuration is selected, + * it is invoked again, but with final = TRUE. + * + * @param ike_sa IKE_SA to authorize + * @param final TRUE if this is the final hook invocation + * @param success set to TRUE to complete IKE_SA, FALSE abort + * @return TRUE to stay registered, FALSE to unregister + */ + bool (*authorize)(listener_t *this, ike_sa_t *ike_sa, + bool final, bool *success); +}; + +#endif /** LISTENER_H_ @}*/ diff --git a/src/libcharon/bus/listeners/sys_logger.c b/src/libcharon/bus/listeners/sys_logger.c new file mode 100644 index 000000000..11421ad05 --- /dev/null +++ b/src/libcharon/bus/listeners/sys_logger.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 <stdio.h> +#include <string.h> + +#include "sys_logger.h" + + +typedef struct private_sys_logger_t private_sys_logger_t; + +/** + * Private data of a sys_logger_t object + */ +struct private_sys_logger_t { + + /** + * Public data. + */ + sys_logger_t public; + + /** + * syslog facility to use + */ + int facility; + + /** + * Maximum level to log, for each group + */ + level_t levels[DBG_MAX]; +}; + +/** + * Implementation of listener_t.log. + */ +static bool log_(private_sys_logger_t *this, debug_t group, level_t level, + int thread, ike_sa_t* ike_sa, char *format, va_list args) +{ + if (level <= this->levels[group]) + { + char buffer[8192]; + char *current = buffer, *next; + + /* write in memory buffer first */ + vsnprintf(buffer, sizeof(buffer), format, args); + + /* do a syslog with every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + syslog(this->facility|LOG_INFO, "%.2d[%N] %s\n", + thread, debug_names, group, current); + current = next; + } + } + /* always stay registered */ + return TRUE; +} + +/** + * Implementation of sys_logger_t.set_level. + */ +static void set_level(private_sys_logger_t *this, debug_t group, level_t level) +{ + if (group < DBG_ANY) + { + this->levels[group] = level; + } + else + { + for (group = 0; group < DBG_MAX; group++) + { + this->levels[group] = level; + } + } +} + +/** + * Implementation of sys_logger_t.destroy. + */ +static void destroy(private_sys_logger_t *this) +{ + closelog(); + free(this); +} + +/* + * Described in header. + */ +sys_logger_t *sys_logger_create(int facility) +{ + private_sys_logger_t *this = malloc_thing(private_sys_logger_t); + + /* public functions */ + memset(&this->public.listener, 0, sizeof(listener_t)); + this->public.listener.log = (bool(*)(listener_t*,debug_t,level_t,int,ike_sa_t*,char*,va_list))log_; + this->public.set_level = (void(*)(sys_logger_t*,debug_t,level_t))set_level; + this->public.destroy = (void(*)(sys_logger_t*))destroy; + + /* private variables */ + this->facility = facility; + set_level(this, DBG_ANY, LEVEL_SILENT); + + return &this->public; +} diff --git a/src/libcharon/bus/listeners/sys_logger.h b/src/libcharon/bus/listeners/sys_logger.h new file mode 100644 index 000000000..730890d68 --- /dev/null +++ b/src/libcharon/bus/listeners/sys_logger.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 sys_logger sys_logger + * @{ @ingroup listeners + */ + +#ifndef SYS_LOGGER_H_ +#define SYS_LOGGER_H_ + +#include <syslog.h> + +#include <bus/listeners/listener.h> + +typedef struct sys_logger_t sys_logger_t; + +/** + * Logger for syslog which implements listener_t. + */ +struct sys_logger_t { + + /** + * Implements the listener_t interface. + */ + listener_t listener; + + /** + * Set the loglevel for a debug group. + * + * @param group debug group to set + * @param level max level to log (0..4) + */ + void (*set_level) (sys_logger_t *this, debug_t group, level_t level); + + /** + * Destroys a sys_logger_t object. + */ + void (*destroy) (sys_logger_t *this); +}; + +/** + * Constructor to create a sys_logger_t object. + * + * @param facility syslog facility to use + * @return sys_logger_t object + */ +sys_logger_t *sys_logger_create(int facility); + +#endif /** SYS_LOGGER_H_ @}*/ |