/* * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2012 Giuliano Grassi * Copyright (C) 2012 Ralf Sager * 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 . * * 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 "ipsec_event_relay.h" #include #include #include #include #include #include typedef struct private_ipsec_event_relay_t private_ipsec_event_relay_t; /** * Private additions to ipsec_event_relay_t. */ struct private_ipsec_event_relay_t { /** * Public members */ ipsec_event_relay_t public; /** * Registered listeners */ linked_list_t *listeners; /** * Lock to safely access the list of listeners */ rwlock_t *lock; /** * Blocking queue for events */ blocking_queue_t *queue; }; /** * Helper struct used to manage events in a queue */ typedef struct { /** * Type of the event */ enum { IPSEC_EVENT_EXPIRE, } type; /** * Protocol of the SA */ uint8_t protocol; /** * SPI of the SA, if any */ uint32_t spi; /** * SA destination address */ host_t *dst; /** * Additional data for specific event types */ union { struct { /** TRUE in case of a hard expire */ bool hard; } expire; } data; } ipsec_event_t; /** * Destroy IPsec event data */ static void ipsec_event_destroy(ipsec_event_t *event) { event->dst->destroy(event->dst); free(event); } /** * Dequeue events and relay them to listeners */ static job_requeue_t handle_events(private_ipsec_event_relay_t *this) { enumerator_t *enumerator; ipsec_event_listener_t *current; ipsec_event_t *event; event = this->queue->dequeue(this->queue); this->lock->read_lock(this->lock); enumerator = this->listeners->create_enumerator(this->listeners); while (enumerator->enumerate(enumerator, (void**)¤t)) { switch (event->type) { case IPSEC_EVENT_EXPIRE: if (current->expire) { current->expire(event->protocol, event->spi, event->dst, event->data.expire.hard); } break; } } enumerator->destroy(enumerator); this->lock->unlock(this->lock); ipsec_event_destroy(event); return JOB_REQUEUE_DIRECT; } METHOD(ipsec_event_relay_t, expire, void, private_ipsec_event_relay_t *this, uint8_t protocol, uint32_t spi, host_t *dst, bool hard) { ipsec_event_t *event; INIT(event, .type = IPSEC_EVENT_EXPIRE, .protocol = protocol, .spi = spi, .dst = dst->clone(dst), .data = { .expire = { .hard = hard, }, }, ); this->queue->enqueue(this->queue, event); } METHOD(ipsec_event_relay_t, register_listener, void, private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener) { this->lock->write_lock(this->lock); this->listeners->insert_last(this->listeners, listener); this->lock->unlock(this->lock); } METHOD(ipsec_event_relay_t, unregister_listener, void, private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener) { this->lock->write_lock(this->lock); this->listeners->remove(this->listeners, listener, NULL); this->lock->unlock(this->lock); } METHOD(ipsec_event_relay_t, destroy, void, private_ipsec_event_relay_t *this) { this->queue->destroy_function(this->queue, free); this->listeners->destroy(this->listeners); this->lock->destroy(this->lock); free(this); } /** * Described in header. */ ipsec_event_relay_t *ipsec_event_relay_create() { private_ipsec_event_relay_t *this; INIT(this, .public = { .expire = _expire, .register_listener = _register_listener, .unregister_listener = _unregister_listener, .destroy = _destroy, }, .listeners = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .queue = blocking_queue_create(), ); lib->processor->queue_job(lib->processor, (job_t*)callback_job_create((callback_job_cb_t)handle_events, this, NULL, (callback_job_cancel_t)return_false)); return &this->public; }