2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
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>.
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
18 #include <utils/hashtable.h>
19 #include <utils/linked_list.h>
20 #include <threading/mutex.h>
21 #include <processing/jobs/callback_job.h>
23 typedef struct private_ha_cache_t private_ha_cache_t
;
26 * Private data of an ha_cache_t object.
28 struct private_ha_cache_t
{
31 * Public ha_cache_t interface.
36 * Kernel helper functions
41 * Socket to send sync messages over
46 * Total number of segments
51 * cached entries (ike_sa_t, entry_t)
62 * Hashtable hash function
64 static u_int
hash(void *key
)
66 return (uintptr_t)key
;
70 * Hashtable equals function
72 static bool equals(void *a
, void *b
)
78 * Cache entry for an IKE_SA
81 /* segment this entry is associate to */
85 /* list of updates UPDATE message */
86 linked_list_t
*updates
;
87 /* last initiator mid */
89 /* last responder mid */
94 * Create a entry with an add message
96 static entry_t
*entry_create(ha_message_t
*add
)
102 .updates
= linked_list_create(),
110 static void entry_destroy(entry_t
*entry
)
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
);
120 METHOD(ha_cache_t
, cache
, void,
121 private_ha_cache_t
*this, ike_sa_t
*ike_sa
, ha_message_t
*message
)
125 this->mutex
->lock(this->mutex
);
126 switch (message
->get_type(message
))
129 entry
= entry_create(message
);
130 entry
= this->cache
->put(this->cache
, ike_sa
, entry
);
133 entry_destroy(entry
);
137 entry
= this->cache
->get(this->cache
, ike_sa
);
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
);
145 message
->destroy(message
);
147 case HA_IKE_MID_INITIATOR
:
148 entry
= this->cache
->get(this->cache
, ike_sa
);
151 DESTROY_IF(entry
->midi
);
152 entry
->midi
= message
;
155 message
->destroy(message
);
157 case HA_IKE_MID_RESPONDER
:
158 entry
= this->cache
->get(this->cache
, ike_sa
);
161 DESTROY_IF(entry
->midr
);
162 entry
->midr
= message
;
165 message
->destroy(message
);
168 entry
= this->cache
->remove(this->cache
, ike_sa
);
171 entry_destroy(entry
);
173 message
->destroy(message
);
176 message
->destroy(message
);
179 this->mutex
->unlock(this->mutex
);
182 METHOD(ha_cache_t
, delete_
, void,
183 private_ha_cache_t
*this, ike_sa_t
*ike_sa
)
187 entry
= this->cache
->remove(this->cache
, ike_sa
);
190 entry_destroy(entry
);
195 * Rekey all children of an IKE_SA
197 static status_t
rekey_children(ike_sa_t
*ike_sa
)
199 enumerator_t
*enumerator
;
200 child_sa_t
*child_sa
;
201 status_t status
= SUCCESS
;
203 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
204 while (enumerator
->enumerate(enumerator
, (void**)&child_sa
))
206 if (ike_sa
->supports_extension(ike_sa
, EXT_MS_WINDOWS
) &&
207 ike_sa
->has_condition(ike_sa
, COND_NAT_THERE
))
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
));
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
));
224 if (status
== DESTROY_ME
)
229 enumerator
->destroy(enumerator
);
234 * Trigger rekeying of CHILD_SA in segment
236 static void rekey_segment(private_ha_cache_t
*this, u_int segment
)
239 enumerator_t
*enumerator
;
243 list
= linked_list_create();
245 enumerator
= charon
->ike_sa_manager
->create_enumerator(
246 charon
->ike_sa_manager
, TRUE
);
247 while (enumerator
->enumerate(enumerator
, &ike_sa
))
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
)
253 id
= ike_sa
->get_id(ike_sa
);
254 list
->insert_last(list
, id
->clone(id
));
257 enumerator
->destroy(enumerator
);
259 while (list
->remove_last(list
, (void**)&id
) == SUCCESS
)
261 ike_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
, id
);
264 if (rekey_children(ike_sa
) != DESTROY_ME
)
266 charon
->ike_sa_manager
->checkin(
267 charon
->ike_sa_manager
, ike_sa
);
271 charon
->ike_sa_manager
->checkin_and_destroy(
272 charon
->ike_sa_manager
, ike_sa
);
280 METHOD(ha_cache_t
, resync
, void,
281 private_ha_cache_t
*this, u_int segment
)
283 enumerator_t
*enumerator
, *updates
;
286 ha_message_t
*message
;
288 DBG1(DBG_CFG
, "resyncing HA segment %d", segment
);
290 this->mutex
->lock(this->mutex
);
291 enumerator
= this->cache
->create_enumerator(this->cache
);
292 while (enumerator
->enumerate(enumerator
, &ike_sa
, &entry
))
294 if (entry
->segment
== segment
)
296 this->socket
->push(this->socket
, entry
->add
);
297 updates
= entry
->updates
->create_enumerator(entry
->updates
);
298 while (updates
->enumerate(updates
, &message
))
300 this->socket
->push(this->socket
, message
);
302 updates
->destroy(updates
);
305 this->socket
->push(this->socket
, entry
->midi
);
309 this->socket
->push(this->socket
, entry
->midr
);
313 enumerator
->destroy(enumerator
);
314 this->mutex
->unlock(this->mutex
);
316 rekey_segment(this, segment
);
320 * Request a resync of all segments
322 static job_requeue_t
request_resync(private_ha_cache_t
*this)
324 ha_message_t
*message
;
327 DBG1(DBG_CFG
, "requesting HA resynchronization");
329 message
= ha_message_create(HA_RESYNC
);
330 for (i
= 1; i
<= this->count
; i
++)
332 message
->add_attribute(message
, HA_SEGMENT
, i
);
334 this->socket
->push(this->socket
, message
);
335 message
->destroy(message
);
336 return JOB_REQUEUE_NONE
;
339 METHOD(ha_cache_t
, destroy
, void,
340 private_ha_cache_t
*this)
342 this->cache
->destroy(this->cache
);
343 this->mutex
->destroy(this->mutex
);
350 ha_cache_t
*ha_cache_create(ha_kernel_t
*kernel
, ha_socket_t
*socket
,
351 bool sync
, u_int count
)
353 private_ha_cache_t
*this;
365 .cache
= hashtable_create(hash
, equals
, 8),
366 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
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);
376 return &this->public;