Added a non-blocking, skipping variant of IKE_SA enumerator
[strongswan.git] / src / libcharon / plugins / ha / ha_cache.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "ha_cache.h"
17
18 #include <utils/hashtable.h>
19 #include <utils/linked_list.h>
20 #include <threading/mutex.h>
21 #include <processing/jobs/callback_job.h>
22
23 typedef struct private_ha_cache_t private_ha_cache_t;
24
25 /**
26 * Private data of an ha_cache_t object.
27 */
28 struct private_ha_cache_t {
29
30 /**
31 * Public ha_cache_t interface.
32 */
33 ha_cache_t public;
34
35 /**
36 * Kernel helper functions
37 */
38 ha_kernel_t *kernel;
39
40 /**
41 * Socket to send sync messages over
42 */
43 ha_socket_t *socket;
44
45 /**
46 * Total number of segments
47 */
48 u_int count;
49
50 /**
51 * cached entries (ike_sa_t, entry_t)
52 */
53 hashtable_t *cache;
54
55 /**
56 * Mutex to lock cache
57 */
58 mutex_t *mutex;
59 };
60
61 /**
62 * Hashtable hash function
63 */
64 static u_int hash(void *key)
65 {
66 return (uintptr_t)key;
67 }
68
69 /**
70 * Hashtable equals function
71 */
72 static bool equals(void *a, void *b)
73 {
74 return a == b;
75 }
76
77 /**
78 * Cache entry for an IKE_SA
79 */
80 typedef struct {
81 /* segment this entry is associate to */
82 u_int segment;
83 /* ADD message */
84 ha_message_t *add;
85 /* list of updates UPDATE message */
86 linked_list_t *updates;
87 /* last initiator mid */
88 ha_message_t *midi;
89 /* last responder mid */
90 ha_message_t *midr;
91 } entry_t;
92
93 /**
94 * Create a entry with an add message
95 */
96 static entry_t *entry_create(ha_message_t *add)
97 {
98 entry_t *entry;
99
100 INIT(entry,
101 .add = add,
102 .updates = linked_list_create(),
103 );
104 return entry;
105 }
106
107 /**
108 * clean up a entry
109 */
110 static void entry_destroy(entry_t *entry)
111 {
112 entry->updates->destroy_offset(entry->updates,
113 offsetof(ha_message_t, destroy));
114 entry->add->destroy(entry->add);
115 DESTROY_IF(entry->midi);
116 DESTROY_IF(entry->midr);
117 free(entry);
118 }
119
120 METHOD(ha_cache_t, cache, void,
121 private_ha_cache_t *this, ike_sa_t *ike_sa, ha_message_t *message)
122 {
123 entry_t *entry;
124
125 this->mutex->lock(this->mutex);
126 switch (message->get_type(message))
127 {
128 case HA_IKE_ADD:
129 entry = entry_create(message);
130 entry = this->cache->put(this->cache, ike_sa, entry);
131 if (entry)
132 {
133 entry_destroy(entry);
134 }
135 break;
136 case HA_IKE_UPDATE:
137 entry = this->cache->get(this->cache, ike_sa);
138 if (entry)
139 {
140 entry->segment = this->kernel->get_segment(this->kernel,
141 ike_sa->get_other_host(ike_sa));
142 entry->updates->insert_last(entry->updates, message);
143 break;
144 }
145 message->destroy(message);
146 break;
147 case HA_IKE_MID_INITIATOR:
148 entry = this->cache->get(this->cache, ike_sa);
149 if (entry)
150 {
151 DESTROY_IF(entry->midi);
152 entry->midi = message;
153 break;
154 }
155 message->destroy(message);
156 break;
157 case HA_IKE_MID_RESPONDER:
158 entry = this->cache->get(this->cache, ike_sa);
159 if (entry)
160 {
161 DESTROY_IF(entry->midr);
162 entry->midr = message;
163 break;
164 }
165 message->destroy(message);
166 break;
167 case HA_IKE_DELETE:
168 entry = this->cache->remove(this->cache, ike_sa);
169 if (entry)
170 {
171 entry_destroy(entry);
172 }
173 message->destroy(message);
174 break;
175 default:
176 message->destroy(message);
177 break;
178 }
179 this->mutex->unlock(this->mutex);
180 }
181
182 METHOD(ha_cache_t, delete_, void,
183 private_ha_cache_t *this, ike_sa_t *ike_sa)
184 {
185 entry_t *entry;
186
187 entry = this->cache->remove(this->cache, ike_sa);
188 if (entry)
189 {
190 entry_destroy(entry);
191 }
192 }
193
194 /**
195 * Rekey all children of an IKE_SA
196 */
197 static status_t rekey_children(ike_sa_t *ike_sa)
198 {
199 iterator_t *iterator;
200 child_sa_t *child_sa;
201 status_t status = SUCCESS;
202
203 iterator = ike_sa->create_child_sa_iterator(ike_sa);
204 while (iterator->iterate(iterator, (void**)&child_sa))
205 {
206 DBG1(DBG_CFG, "resyncing CHILD_SA");
207 status = ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa),
208 child_sa->get_spi(child_sa, TRUE));
209 if (status == DESTROY_ME)
210 {
211 break;
212 }
213 }
214 iterator->destroy(iterator);
215 return status;
216 }
217
218 /**
219 * Trigger rekeying of CHILD_SA in segment
220 */
221 static void rekey_segment(private_ha_cache_t *this, u_int segment)
222 {
223 ike_sa_t *ike_sa;
224 enumerator_t *enumerator;
225 linked_list_t *list;
226 ike_sa_id_t *id;
227
228 list = linked_list_create();
229
230 enumerator = charon->ike_sa_manager->create_enumerator(
231 charon->ike_sa_manager, TRUE);
232 while (enumerator->enumerate(enumerator, &ike_sa))
233 {
234 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
235 this->kernel->get_segment(this->kernel,
236 ike_sa->get_other_host(ike_sa)) == segment)
237 {
238 id = ike_sa->get_id(ike_sa);
239 list->insert_last(list, id->clone(id));
240 }
241 }
242 enumerator->destroy(enumerator);
243
244 while (list->remove_last(list, (void**)&id) == SUCCESS)
245 {
246 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
247 if (ike_sa)
248 {
249 if (rekey_children(ike_sa) != DESTROY_ME)
250 {
251 charon->ike_sa_manager->checkin(
252 charon->ike_sa_manager, ike_sa);
253 }
254 else
255 {
256 charon->ike_sa_manager->checkin_and_destroy(
257 charon->ike_sa_manager, ike_sa);
258 }
259 }
260 id->destroy(id);
261 }
262 list->destroy(list);
263 }
264
265 METHOD(ha_cache_t, resync, void,
266 private_ha_cache_t *this, u_int segment)
267 {
268 enumerator_t *enumerator, *updates;
269 ike_sa_t *ike_sa;
270 entry_t *entry;
271 ha_message_t *message;
272
273 DBG1(DBG_CFG, "resyncing HA segment %d", segment);
274
275 this->mutex->lock(this->mutex);
276 enumerator = this->cache->create_enumerator(this->cache);
277 while (enumerator->enumerate(enumerator, &ike_sa, &entry))
278 {
279 if (entry->segment == segment)
280 {
281 this->socket->push(this->socket, entry->add);
282 updates = entry->updates->create_enumerator(entry->updates);
283 while (updates->enumerate(updates, &message))
284 {
285 this->socket->push(this->socket, message);
286 }
287 updates->destroy(updates);
288 if (entry->midi)
289 {
290 this->socket->push(this->socket, entry->midi);
291 }
292 if (entry->midr)
293 {
294 this->socket->push(this->socket, entry->midr);
295 }
296 }
297 }
298 enumerator->destroy(enumerator);
299 this->mutex->unlock(this->mutex);
300
301 rekey_segment(this, segment);
302 }
303
304 /**
305 * Request a resync of all segments
306 */
307 static job_requeue_t request_resync(private_ha_cache_t *this)
308 {
309 ha_message_t *message;
310 int i;
311
312 DBG1(DBG_CFG, "requesting HA resynchronization");
313
314 message = ha_message_create(HA_RESYNC);
315 for (i = 1; i <= this->count; i++)
316 {
317 message->add_attribute(message, HA_SEGMENT, i);
318 }
319 this->socket->push(this->socket, message);
320 message->destroy(message);
321 return JOB_REQUEUE_NONE;
322 }
323
324 METHOD(ha_cache_t, destroy, void,
325 private_ha_cache_t *this)
326 {
327 this->cache->destroy(this->cache);
328 this->mutex->destroy(this->mutex);
329 free(this);
330 }
331
332 /**
333 * See header
334 */
335 ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket,
336 bool sync, u_int count)
337 {
338 private_ha_cache_t *this;
339
340 INIT(this,
341 .public = {
342 .cache = _cache,
343 .delete = _delete_,
344 .resync = _resync,
345 .destroy = _destroy,
346 },
347 .count = count,
348 .kernel = kernel,
349 .socket = socket,
350 .cache = hashtable_create(hash, equals, 8),
351 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
352 );
353
354 if (sync)
355 {
356 /* request a resync as soon as we are up */
357 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
358 callback_job_create((callback_job_cb_t)request_resync,
359 this, NULL, NULL), 1);
360 }
361 return &this->public;
362 }