| /* |
| * Copyright (C) 2011-2020 Tobias Brunner |
| * Copyright (C) 2006 Martin Willi |
| * HSR 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 <threading/thread.h> |
| #include <threading/thread_value.h> |
| #include <threading/mutex.h> |
| #include <threading/rwlock.h> |
| |
| /** |
| * These operations allow us to speed up the log level checks on some platforms. |
| * In particular if acquiring the read lock is expensive even in the absence of |
| * any writers. |
| * |
| * Note that while holding the read/write lock the read does not have to be |
| * atomic as the write lock must be held to set the level. |
| */ |
| #ifdef HAVE_GCC_ATOMIC_OPERATIONS |
| |
| #define skip_level(ptr, level) (__atomic_load_n(ptr, __ATOMIC_RELAXED) < level) |
| #define set_level(ptr, val) __atomic_store_n(ptr, val, __ATOMIC_RELAXED) |
| |
| #elif defined(HAVE_GCC_SYNC_OPERATIONS) |
| |
| #define skip_level(ptr, level) (__sync_fetch_and_add(ptr, 0) < level) |
| #define set_level(ptr, val) __sync_bool_compare_and_swap(ptr, *ptr, val) |
| |
| #else |
| |
| #define skip_level(ptr, level) FALSE |
| #define set_level(ptr, val) ({ *ptr = val; }) |
| |
| #endif |
| |
| 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. |
| */ |
| linked_list_t *listeners; |
| |
| /** |
| * List of registered loggers for each log group as log_entry_t. |
| * Loggers are ordered by descending log level. |
| * The extra list stores all loggers so we can properly unregister them. |
| */ |
| linked_list_t *loggers[DBG_MAX + 1]; |
| |
| /** |
| * Maximum log level of any registered logger for each log group. |
| * This allows to check quickly if a log message has to be logged at all. |
| */ |
| level_t max_level[DBG_MAX + 1]; |
| |
| /** |
| * Same as max level, but for loggers using the vlog() method. |
| */ |
| level_t max_vlevel[DBG_MAX + 1]; |
| |
| /** |
| * Mutex for the list of listeners, recursively. |
| */ |
| mutex_t *mutex; |
| |
| /** |
| * Read-write lock for the list of loggers. |
| */ |
| rwlock_t *log_lock; |
| |
| /** |
| * Thread local storage the threads IKE_SA |
| */ |
| thread_value_t *thread_sa; |
| }; |
| |
| typedef struct entry_t entry_t; |
| |
| /** |
| * a listener entry |
| */ |
| struct entry_t { |
| |
| /** |
| * registered listener interface |
| */ |
| listener_t *listener; |
| |
| /** |
| * are we currently calling this listener |
| */ |
| int calling; |
| |
| }; |
| |
| typedef struct log_entry_t log_entry_t; |
| |
| /** |
| * a logger entry |
| */ |
| struct log_entry_t { |
| |
| /** |
| * registered logger interface |
| */ |
| logger_t *logger; |
| |
| /** |
| * registered log levels per group |
| */ |
| level_t levels[DBG_MAX]; |
| |
| }; |
| |
| METHOD(bus_t, add_listener, void, |
| private_bus_t *this, listener_t *listener) |
| { |
| entry_t *entry; |
| |
| INIT(entry, |
| .listener = listener, |
| ); |
| |
| this->mutex->lock(this->mutex); |
| this->listeners->insert_last(this->listeners, entry); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, remove_listener, void, |
| 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); |
| free(entry); |
| break; |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| /** |
| * Register a logger on the given log group according to the requested level |
| */ |
| static inline void register_logger(private_bus_t *this, debug_t group, |
| log_entry_t *entry) |
| { |
| enumerator_t *enumerator; |
| linked_list_t *loggers; |
| log_entry_t *current; |
| level_t level; |
| |
| loggers = this->loggers[group]; |
| level = entry->levels[group]; |
| |
| enumerator = loggers->create_enumerator(loggers); |
| while (enumerator->enumerate(enumerator, (void**)¤t)) |
| { |
| if (current->levels[group] <= level) |
| { |
| break; |
| } |
| } |
| loggers->insert_before(loggers, enumerator, entry); |
| enumerator->destroy(enumerator); |
| |
| if (entry->logger->log) |
| { |
| set_level(&this->max_level[group], max(this->max_level[group], level)); |
| } |
| if (entry->logger->vlog) |
| { |
| set_level(&this->max_vlevel[group], |
| max(this->max_vlevel[group], level)); |
| } |
| } |
| |
| CALLBACK(find_max_levels, bool, |
| log_entry_t *entry, va_list args) |
| { |
| level_t *level, *vlevel; |
| debug_t group; |
| |
| VA_ARGS_VGET(args, group, level, vlevel); |
| if (entry->logger->log && *level == LEVEL_SILENT) |
| { |
| *level = entry->levels[group]; |
| } |
| if (entry->logger->vlog && *vlevel == LEVEL_SILENT) |
| { |
| *vlevel = entry->levels[group]; |
| } |
| return *level > LEVEL_SILENT && *vlevel > LEVEL_SILENT; |
| } |
| |
| /** |
| * Unregister a logger from all log groups (destroys the log_entry_t) |
| */ |
| static inline void unregister_logger(private_bus_t *this, logger_t *logger) |
| { |
| enumerator_t *enumerator; |
| linked_list_t *loggers; |
| log_entry_t *entry, *found = NULL; |
| debug_t group; |
| |
| loggers = this->loggers[DBG_MAX]; |
| enumerator = loggers->create_enumerator(loggers); |
| while (enumerator->enumerate(enumerator, &entry)) |
| { |
| if (entry->logger == logger) |
| { |
| loggers->remove_at(loggers, enumerator); |
| found = entry; |
| break; |
| } |
| } |
| enumerator->destroy(enumerator); |
| |
| if (found) |
| { |
| for (group = 0; group < DBG_MAX; group++) |
| { |
| if (found->levels[group] > LEVEL_SILENT) |
| { |
| level_t level = LEVEL_SILENT, vlevel = LEVEL_SILENT; |
| |
| loggers = this->loggers[group]; |
| loggers->remove(loggers, found, NULL); |
| loggers->find_first(loggers, find_max_levels, NULL, group, |
| &level, &vlevel); |
| set_level(&this->max_level[group], level); |
| set_level(&this->max_vlevel[group], vlevel); |
| } |
| } |
| free(found); |
| } |
| } |
| |
| METHOD(bus_t, add_logger, void, |
| private_bus_t *this, logger_t *logger) |
| { |
| log_entry_t *entry; |
| debug_t group; |
| |
| INIT(entry, |
| .logger = logger, |
| ); |
| |
| this->log_lock->write_lock(this->log_lock); |
| unregister_logger(this, logger); |
| for (group = 0; group < DBG_MAX; group++) |
| { |
| entry->levels[group] = logger->get_level(logger, group); |
| if (entry->levels[group] > LEVEL_SILENT) |
| { |
| register_logger(this, group, entry); |
| } |
| } |
| this->loggers[DBG_MAX]->insert_last(this->loggers[DBG_MAX], entry); |
| this->log_lock->unlock(this->log_lock); |
| } |
| |
| METHOD(bus_t, remove_logger, void, |
| private_bus_t *this, logger_t *logger) |
| { |
| this->log_lock->write_lock(this->log_lock); |
| unregister_logger(this, logger); |
| this->log_lock->unlock(this->log_lock); |
| } |
| |
| METHOD(bus_t, set_sa, void, |
| private_bus_t *this, ike_sa_t *ike_sa) |
| { |
| this->thread_sa->set(this->thread_sa, ike_sa); |
| } |
| |
| METHOD(bus_t, get_sa, ike_sa_t*, |
| 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; |
| /** message/fmt */ |
| char *message; |
| /** argument list if message is a format string for vlog() */ |
| va_list args; |
| } log_data_t; |
| |
| CALLBACK(log_cb, void, |
| log_entry_t *entry, va_list args) |
| { |
| log_data_t *data; |
| |
| VA_ARGS_VGET(args, data); |
| if (entry->logger->log && entry->levels[data->group] >= data->level) |
| { |
| entry->logger->log(entry->logger, data->group, data->level, |
| data->thread, data->ike_sa, data->message); |
| } |
| } |
| |
| CALLBACK(vlog_cb, void, |
| log_entry_t *entry, va_list args) |
| { |
| log_data_t *data; |
| |
| VA_ARGS_VGET(args, data); |
| if (entry->logger->vlog && entry->levels[data->group] >= data->level) |
| { |
| va_list copy; |
| |
| va_copy(copy, data->args); |
| entry->logger->vlog(entry->logger, data->group, data->level, |
| data->thread, data->ike_sa, data->message, copy); |
| va_end(copy); |
| } |
| } |
| |
| METHOD(bus_t, vlog, void, |
| private_bus_t *this, debug_t group, level_t level, |
| char* format, va_list args) |
| { |
| linked_list_t *loggers; |
| log_data_t data; |
| |
| /* NOTE: This is not 100% thread-safe and done here only because it is |
| * performance critical. We therefore ignore the following two issues for |
| * this particular case: 1) We might miss some log messages if another |
| * thread concurrently increases the log level or registers a new logger. |
| * 2) We might have to acquire the read lock below even if it wouldn't be |
| * necessary anymore due to another thread concurrently unregistering a |
| * logger or reducing the level. */ |
| if (skip_level(&this->max_level[group], level) && |
| skip_level(&this->max_vlevel[group], level)) |
| { |
| return; |
| } |
| |
| this->log_lock->read_lock(this->log_lock); |
| loggers = this->loggers[group]; |
| |
| if (this->max_level[group] >= level) |
| { |
| char buf[1024]; |
| ssize_t len; |
| |
| data.ike_sa = this->thread_sa->get(this->thread_sa); |
| data.thread = thread_current_id(); |
| data.group = group; |
| data.level = level; |
| data.message = buf; |
| |
| va_copy(data.args, args); |
| len = vsnprintf(data.message, sizeof(buf), format, data.args); |
| va_end(data.args); |
| if (len >= sizeof(buf)) |
| { |
| len++; |
| data.message = malloc(len); |
| va_copy(data.args, args); |
| len = vsnprintf(data.message, len, format, data.args); |
| va_end(data.args); |
| } |
| if (len > 0) |
| { |
| loggers->invoke_function(loggers, log_cb, &data); |
| } |
| if (data.message != buf) |
| { |
| free(data.message); |
| } |
| } |
| if (this->max_vlevel[group] >= level) |
| { |
| data.ike_sa = this->thread_sa->get(this->thread_sa); |
| data.thread = thread_current_id(); |
| data.group = group; |
| data.level = level; |
| data.message = format; |
| |
| va_copy(data.args, args); |
| loggers->invoke_function(loggers, vlog_cb, &data); |
| va_end(data.args); |
| } |
| |
| this->log_lock->unlock(this->log_lock); |
| } |
| |
| METHOD(bus_t, log_, void, |
| 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 inline void unregister_listener(private_bus_t *this, entry_t *entry, |
| enumerator_t *enumerator) |
| { |
| this->listeners->remove_at(this->listeners, enumerator); |
| free(entry); |
| } |
| |
| METHOD(bus_t, alert, void, |
| 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); |
| } |
| |
| METHOD(bus_t, ike_state_change, void, |
| 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); |
| } |
| |
| METHOD(bus_t, child_state_change, void, |
| 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); |
| } |
| |
| METHOD(bus_t, message, void, |
| private_bus_t *this, message_t *message, bool incoming, bool plain) |
| { |
| 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, plain); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, ike_keys, void, |
| private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, |
| chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, |
| ike_sa_t *rekey, shared_key_t *shared, auth_method_t method) |
| { |
| 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, dh_other, |
| nonce_i, nonce_r, rekey, shared, |
| method); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, ike_derived_keys, void, |
| private_bus_t *this, chunk_t sk_d, chunk_t sk_ai, chunk_t sk_ar, |
| chunk_t sk_ei, chunk_t sk_er, chunk_t sk_pi, chunk_t sk_pr) |
| { |
| 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->ike_derived_keys) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->ike_derived_keys(entry->listener, ike_sa, sk_d, |
| sk_ai, sk_ar, sk_ei, sk_er, |
| sk_pi, sk_pr); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, child_keys, void, |
| private_bus_t *this, child_sa_t *child_sa, bool initiator, |
| 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, initiator, dh, nonce_i, nonce_r); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, child_derived_keys, void, |
| private_bus_t *this, child_sa_t *child_sa, bool initiator, |
| chunk_t encr_i, chunk_t encr_r, chunk_t integ_i, chunk_t integ_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_derived_keys) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->child_derived_keys(entry->listener, ike_sa, |
| child_sa, initiator, encr_i, encr_r, |
| integ_i, integ_r); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, child_updown, void, |
| 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); |
| } |
| |
| METHOD(bus_t, child_rekey, void, |
| 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); |
| } |
| |
| METHOD(bus_t, children_migrate, void, |
| private_bus_t *this, ike_sa_id_t *new, uint32_t unique) |
| { |
| 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->children_migrate) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->children_migrate(entry->listener, ike_sa, new, |
| unique); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, ike_updown, void, |
| 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) |
| { |
| enumerator_t *enumerator; |
| child_sa_t *child_sa; |
| |
| enumerator = ike_sa->create_child_sa_enumerator(ike_sa); |
| while (enumerator->enumerate(enumerator, (void**)&child_sa)) |
| { |
| if (child_sa->get_state(child_sa) != CHILD_REKEYED && |
| child_sa->get_state(child_sa) != CHILD_DELETED) |
| { |
| child_updown(this, child_sa, FALSE); |
| } |
| } |
| enumerator->destroy(enumerator); |
| } |
| } |
| |
| METHOD(bus_t, ike_rekey, void, |
| 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); |
| } |
| |
| METHOD(bus_t, ike_update, void, |
| private_bus_t *this, ike_sa_t *ike_sa, host_t *local, host_t *remote) |
| { |
| 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_update) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->ike_update(entry->listener, ike_sa, local, |
| remote); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, ike_reestablish_pre, void, |
| 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_reestablish_pre) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->ike_reestablish_pre(entry->listener, old, new); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, ike_reestablish_post, void, |
| private_bus_t *this, ike_sa_t *old, ike_sa_t *new, bool initiated) |
| { |
| 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_reestablish_post) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->ike_reestablish_post(entry->listener, old, new, |
| initiated); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, authorize, bool, |
| 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); |
| if (!success) |
| { |
| alert(this, ALERT_AUTHORIZATION_FAILED); |
| } |
| return success; |
| } |
| |
| METHOD(bus_t, narrow, void, |
| private_bus_t *this, child_sa_t *child_sa, narrow_hook_t type, |
| linked_list_t *local, linked_list_t *remote) |
| { |
| 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->narrow) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->narrow(entry->listener, ike_sa, child_sa, |
| type, local, remote); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, assign_vips, void, |
| private_bus_t *this, ike_sa_t *ike_sa, bool assign) |
| { |
| 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->assign_vips) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->assign_vips(entry->listener, ike_sa, assign); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| METHOD(bus_t, handle_vips, void, |
| private_bus_t *this, ike_sa_t *ike_sa, bool handle) |
| { |
| 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->handle_vips) |
| { |
| continue; |
| } |
| entry->calling++; |
| keep = entry->listener->handle_vips(entry->listener, ike_sa, handle); |
| entry->calling--; |
| if (!keep) |
| { |
| unregister_listener(this, entry, enumerator); |
| } |
| } |
| enumerator->destroy(enumerator); |
| this->mutex->unlock(this->mutex); |
| } |
| |
| /** |
| * Credential manager hook function to forward bus alerts |
| */ |
| static void hook_creds(private_bus_t *this, credential_hook_type_t type, |
| certificate_t *cert) |
| { |
| switch (type) |
| { |
| case CRED_HOOK_EXPIRED: |
| return alert(this, ALERT_CERT_EXPIRED, cert); |
| case CRED_HOOK_REVOKED: |
| return alert(this, ALERT_CERT_REVOKED, cert); |
| case CRED_HOOK_VALIDATION_FAILED: |
| return alert(this, ALERT_CERT_VALIDATION_FAILED, cert); |
| case CRED_HOOK_NO_ISSUER: |
| return alert(this, ALERT_CERT_NO_ISSUER, cert); |
| case CRED_HOOK_UNTRUSTED_ROOT: |
| return alert(this, ALERT_CERT_UNTRUSTED_ROOT, cert); |
| case CRED_HOOK_EXCEEDED_PATH_LEN: |
| return alert(this, ALERT_CERT_EXCEEDED_PATH_LEN, cert); |
| case CRED_HOOK_POLICY_VIOLATION: |
| return alert(this, ALERT_CERT_POLICY_VIOLATION, cert); |
| } |
| } |
| |
| METHOD(bus_t, destroy, void, |
| private_bus_t *this) |
| { |
| debug_t group; |
| |
| lib->credmgr->set_hook(lib->credmgr, NULL, NULL); |
| for (group = 0; group < DBG_MAX; group++) |
| { |
| this->loggers[group]->destroy(this->loggers[group]); |
| } |
| this->loggers[DBG_MAX]->destroy_function(this->loggers[DBG_MAX], |
| (void*)free); |
| this->listeners->destroy_function(this->listeners, (void*)free); |
| this->thread_sa->destroy(this->thread_sa); |
| this->log_lock->destroy(this->log_lock); |
| this->mutex->destroy(this->mutex); |
| free(this); |
| } |
| |
| /* |
| * Described in header. |
| */ |
| bus_t *bus_create() |
| { |
| private_bus_t *this; |
| debug_t group; |
| |
| INIT(this, |
| .public = { |
| .add_listener = _add_listener, |
| .remove_listener = _remove_listener, |
| .add_logger = _add_logger, |
| .remove_logger = _remove_logger, |
| .set_sa = _set_sa, |
| .get_sa = _get_sa, |
| .log = _log_, |
| .vlog = _vlog, |
| .alert = _alert, |
| .ike_state_change = _ike_state_change, |
| .child_state_change = _child_state_change, |
| .message = _message, |
| .ike_keys = _ike_keys, |
| .ike_derived_keys = _ike_derived_keys, |
| .child_keys = _child_keys, |
| .child_derived_keys = _child_derived_keys, |
| .ike_updown = _ike_updown, |
| .ike_rekey = _ike_rekey, |
| .ike_update = _ike_update, |
| .ike_reestablish_pre = _ike_reestablish_pre, |
| .ike_reestablish_post = _ike_reestablish_post, |
| .child_updown = _child_updown, |
| .child_rekey = _child_rekey, |
| .children_migrate = _children_migrate, |
| .authorize = _authorize, |
| .narrow = _narrow, |
| .assign_vips = _assign_vips, |
| .handle_vips = _handle_vips, |
| .destroy = _destroy, |
| }, |
| .listeners = linked_list_create(), |
| .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), |
| .log_lock = rwlock_create(RWLOCK_TYPE_DEFAULT), |
| .thread_sa = thread_value_create(NULL), |
| ); |
| |
| for (group = 0; group <= DBG_MAX; group++) |
| { |
| this->loggers[group] = linked_list_create(); |
| this->max_level[group] = LEVEL_SILENT; |
| this->max_vlevel[group] = LEVEL_SILENT; |
| } |
| |
| lib->credmgr->set_hook(lib->credmgr, (credential_hook_t)hook_creds, this); |
| |
| return &this->public; |
| } |