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