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