Do not query CHILD_SA during delete if they already expired
[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 enumerator_t *enumerator;
200 child_sa_t *child_sa;
201 status_t status = SUCCESS;
202
203 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
204 while (enumerator->enumerate(enumerator, (void**)&child_sa))
205 {
206 if (ike_sa->supports_extension(ike_sa, EXT_MS_WINDOWS) &&
207 ike_sa->has_condition(ike_sa, COND_NAT_THERE))
208 {
209 /* NATed Windows clients don't accept CHILD_SA rekeying, but fail
210 * with an "invalid situation" error. We just close the CHILD_SA,
211 * Windows will reestablish it immediately if required. */
212 DBG1(DBG_CFG, "resyncing CHILD_SA using a delete");
213 status = ike_sa->delete_child_sa(ike_sa,
214 child_sa->get_protocol(child_sa),
215 child_sa->get_spi(child_sa, TRUE),
216 FALSE);
217 }
218 else
219 {
220 DBG1(DBG_CFG, "resyncing CHILD_SA using a rekey");
221 status = ike_sa->rekey_child_sa(ike_sa,
222 child_sa->get_protocol(child_sa),
223 child_sa->get_spi(child_sa, TRUE));
224 }
225 if (status == DESTROY_ME)
226 {
227 break;
228 }
229 }
230 enumerator->destroy(enumerator);
231 return status;
232 }
233
234 /**
235 * Trigger rekeying of CHILD_SA in segment
236 */
237 static void rekey_segment(private_ha_cache_t *this, u_int segment)
238 {
239 ike_sa_t *ike_sa;
240 enumerator_t *enumerator;
241 linked_list_t *list;
242 ike_sa_id_t *id;
243
244 list = linked_list_create();
245
246 enumerator = charon->ike_sa_manager->create_enumerator(
247 charon->ike_sa_manager, TRUE);
248 while (enumerator->enumerate(enumerator, &ike_sa))
249 {
250 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
251 this->kernel->get_segment(this->kernel,
252 ike_sa->get_other_host(ike_sa)) == segment)
253 {
254 id = ike_sa->get_id(ike_sa);
255 list->insert_last(list, id->clone(id));
256 }
257 }
258 enumerator->destroy(enumerator);
259
260 while (list->remove_last(list, (void**)&id) == SUCCESS)
261 {
262 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
263 if (ike_sa)
264 {
265 if (rekey_children(ike_sa) != DESTROY_ME)
266 {
267 charon->ike_sa_manager->checkin(
268 charon->ike_sa_manager, ike_sa);
269 }
270 else
271 {
272 charon->ike_sa_manager->checkin_and_destroy(
273 charon->ike_sa_manager, ike_sa);
274 }
275 }
276 id->destroy(id);
277 }
278 list->destroy(list);
279 }
280
281 METHOD(ha_cache_t, resync, void,
282 private_ha_cache_t *this, u_int segment)
283 {
284 enumerator_t *enumerator, *updates;
285 ike_sa_t *ike_sa;
286 entry_t *entry;
287 ha_message_t *message;
288
289 DBG1(DBG_CFG, "resyncing HA segment %d", segment);
290
291 this->mutex->lock(this->mutex);
292 enumerator = this->cache->create_enumerator(this->cache);
293 while (enumerator->enumerate(enumerator, &ike_sa, &entry))
294 {
295 if (entry->segment == segment)
296 {
297 this->socket->push(this->socket, entry->add);
298 updates = entry->updates->create_enumerator(entry->updates);
299 while (updates->enumerate(updates, &message))
300 {
301 this->socket->push(this->socket, message);
302 }
303 updates->destroy(updates);
304 if (entry->midi)
305 {
306 this->socket->push(this->socket, entry->midi);
307 }
308 if (entry->midr)
309 {
310 this->socket->push(this->socket, entry->midr);
311 }
312 }
313 }
314 enumerator->destroy(enumerator);
315 this->mutex->unlock(this->mutex);
316
317 rekey_segment(this, segment);
318 }
319
320 /**
321 * Request a resync of all segments
322 */
323 static job_requeue_t request_resync(private_ha_cache_t *this)
324 {
325 ha_message_t *message;
326 int i;
327
328 DBG1(DBG_CFG, "requesting HA resynchronization");
329
330 message = ha_message_create(HA_RESYNC);
331 for (i = 1; i <= this->count; i++)
332 {
333 message->add_attribute(message, HA_SEGMENT, i);
334 }
335 this->socket->push(this->socket, message);
336 message->destroy(message);
337 return JOB_REQUEUE_NONE;
338 }
339
340 METHOD(ha_cache_t, destroy, void,
341 private_ha_cache_t *this)
342 {
343 this->cache->destroy(this->cache);
344 this->mutex->destroy(this->mutex);
345 free(this);
346 }
347
348 /**
349 * See header
350 */
351 ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket,
352 bool sync, u_int count)
353 {
354 private_ha_cache_t *this;
355
356 INIT(this,
357 .public = {
358 .cache = _cache,
359 .delete = _delete_,
360 .resync = _resync,
361 .destroy = _destroy,
362 },
363 .count = count,
364 .kernel = kernel,
365 .socket = socket,
366 .cache = hashtable_create(hash, equals, 8),
367 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
368 );
369
370 if (sync)
371 {
372 /* request a resync as soon as we are up */
373 lib->scheduler->schedule_job(lib->scheduler, (job_t*)
374 callback_job_create_with_prio((callback_job_cb_t)request_resync,
375 this, NULL, NULL, JOB_PRIO_CRITICAL), 1);
376 }
377 return &this->public;
378 }