]>
Commit | Line | Data |
---|---|---|
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 | ||
24 | typedef struct private_ha_cache_t private_ha_cache_t; | |
25 | ||
26 | /** | |
27 | * Private data of an ha_cache_t object. | |
28 | */ | |
29 | struct 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 | */ | |
70 | typedef 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 | */ | |
88 | static 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 | */ | |
102 | static 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 | ||
113 | METHOD(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 | ||
185 | METHOD(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 | */ | |
202 | static 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 | */ | |
257 | static 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 | ||
305 | METHOD(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 | */ | |
351 | static 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 | ||
368 | METHOD(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 | */ | |
379 | ha_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 | } |