kernel-netlink: Check return value of both halfs when installing default route in...
[strongswan.git] / src / libipsec / ipsec_event_relay.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "ipsec_event_relay.h"
19
20 #include <library.h>
21 #include <utils/debug.h>
22 #include <threading/rwlock.h>
23 #include <collections/linked_list.h>
24 #include <collections/blocking_queue.h>
25 #include <processing/jobs/callback_job.h>
26
27 typedef struct private_ipsec_event_relay_t private_ipsec_event_relay_t;
28
29 /**
30 * Private additions to ipsec_event_relay_t.
31 */
32 struct private_ipsec_event_relay_t {
33
34 /**
35 * Public members
36 */
37 ipsec_event_relay_t public;
38
39 /**
40 * Registered listeners
41 */
42 linked_list_t *listeners;
43
44 /**
45 * Lock to safely access the list of listeners
46 */
47 rwlock_t *lock;
48
49 /**
50 * Blocking queue for events
51 */
52 blocking_queue_t *queue;
53 };
54
55 /**
56 * Helper struct used to manage events in a queue
57 */
58 typedef struct {
59
60 /**
61 * Type of the event
62 */
63 enum {
64 IPSEC_EVENT_EXPIRE,
65 } type;
66
67 /**
68 * Protocol of the SA
69 */
70 uint8_t protocol;
71
72 /**
73 * SPI of the SA, if any
74 */
75 uint32_t spi;
76
77 /**
78 * SA destination address
79 */
80 host_t *dst;
81
82 /**
83 * Additional data for specific event types
84 */
85 union {
86
87 struct {
88 /** TRUE in case of a hard expire */
89 bool hard;
90 } expire;
91
92 } data;
93
94 } ipsec_event_t;
95
96 /**
97 * Destroy IPsec event data
98 */
99 static void ipsec_event_destroy(ipsec_event_t *event)
100 {
101 event->dst->destroy(event->dst);
102 free(event);
103 }
104
105 /**
106 * Dequeue events and relay them to listeners
107 */
108 static job_requeue_t handle_events(private_ipsec_event_relay_t *this)
109 {
110 enumerator_t *enumerator;
111 ipsec_event_listener_t *current;
112 ipsec_event_t *event;
113
114 event = this->queue->dequeue(this->queue);
115
116 this->lock->read_lock(this->lock);
117 enumerator = this->listeners->create_enumerator(this->listeners);
118 while (enumerator->enumerate(enumerator, (void**)&current))
119 {
120 switch (event->type)
121 {
122 case IPSEC_EVENT_EXPIRE:
123 if (current->expire)
124 {
125 current->expire(event->protocol, event->spi, event->dst,
126 event->data.expire.hard);
127 }
128 break;
129 }
130 }
131 enumerator->destroy(enumerator);
132 this->lock->unlock(this->lock);
133 ipsec_event_destroy(event);
134 return JOB_REQUEUE_DIRECT;
135 }
136
137 METHOD(ipsec_event_relay_t, expire, void,
138 private_ipsec_event_relay_t *this, uint8_t protocol, uint32_t spi,
139 host_t *dst, bool hard)
140 {
141 ipsec_event_t *event;
142
143 INIT(event,
144 .type = IPSEC_EVENT_EXPIRE,
145 .protocol = protocol,
146 .spi = spi,
147 .dst = dst->clone(dst),
148 .data = {
149 .expire = {
150 .hard = hard,
151 },
152 },
153 );
154 this->queue->enqueue(this->queue, event);
155 }
156
157 METHOD(ipsec_event_relay_t, register_listener, void,
158 private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
159 {
160 this->lock->write_lock(this->lock);
161 this->listeners->insert_last(this->listeners, listener);
162 this->lock->unlock(this->lock);
163 }
164
165 METHOD(ipsec_event_relay_t, unregister_listener, void,
166 private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
167 {
168 this->lock->write_lock(this->lock);
169 this->listeners->remove(this->listeners, listener, NULL);
170 this->lock->unlock(this->lock);
171 }
172
173 METHOD(ipsec_event_relay_t, destroy, void,
174 private_ipsec_event_relay_t *this)
175 {
176 this->queue->destroy_function(this->queue, free);
177 this->listeners->destroy(this->listeners);
178 this->lock->destroy(this->lock);
179 free(this);
180 }
181
182 /**
183 * Described in header.
184 */
185 ipsec_event_relay_t *ipsec_event_relay_create()
186 {
187 private_ipsec_event_relay_t *this;
188
189 INIT(this,
190 .public = {
191 .expire = _expire,
192 .register_listener = _register_listener,
193 .unregister_listener = _unregister_listener,
194 .destroy = _destroy,
195 },
196 .listeners = linked_list_create(),
197 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
198 .queue = blocking_queue_create(),
199 );
200
201 lib->processor->queue_job(lib->processor,
202 (job_t*)callback_job_create((callback_job_cb_t)handle_events, this,
203 NULL, (callback_job_cancel_t)return_false));
204
205 return &this->public;
206 }