| /* |
| * 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 <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 "ipsec_event_relay.h" |
| |
| #include <library.h> |
| #include <utils/debug.h> |
| #include <threading/rwlock.h> |
| #include <collections/linked_list.h> |
| #include <collections/blocking_queue.h> |
| #include <processing/jobs/callback_job.h> |
| |
| 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; |
| } |