trap-manager: Make sure a config is not trapped twice
[strongswan.git] / src / libcharon / sa / trap_manager.c
1 /*
2 * Copyright (C) 2011-2013 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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 "trap_manager.h"
18
19 #include <hydra.h>
20 #include <daemon.h>
21 #include <threading/rwlock.h>
22 #include <threading/thread_value.h>
23 #include <collections/linked_list.h>
24
25
26 typedef struct private_trap_manager_t private_trap_manager_t;
27 typedef struct trap_listener_t trap_listener_t;
28
29 /**
30 * listener to track acquires
31 */
32 struct trap_listener_t {
33
34 /**
35 * Implements listener interface
36 */
37 listener_t listener;
38
39 /**
40 * points to trap_manager
41 */
42 private_trap_manager_t *traps;
43 };
44
45 /**
46 * Private data of an trap_manager_t object.
47 */
48 struct private_trap_manager_t {
49
50 /**
51 * Public trap_manager_t interface.
52 */
53 trap_manager_t public;
54
55 /**
56 * Installed traps, as entry_t
57 */
58 linked_list_t *traps;
59
60 /**
61 * read write lock for traps list
62 */
63 rwlock_t *lock;
64
65 /**
66 * track if the current thread is installing a trap policy
67 */
68 thread_value_t *installing;
69
70 /**
71 * listener to track acquiring IKE_SAs
72 */
73 trap_listener_t listener;
74 };
75
76 /**
77 * A installed trap entry
78 */
79 typedef struct {
80 /** ref to peer_cfg to initiate */
81 peer_cfg_t *peer_cfg;
82 /** ref to instanciated CHILD_SA */
83 child_sa_t *child_sa;
84 /** TRUE if an acquire is pending */
85 bool pending;
86 /** pending IKE_SA connecting upon acquire */
87 ike_sa_t *ike_sa;
88 } entry_t;
89
90 /**
91 * actually uninstall and destroy an installed entry
92 */
93 static void destroy_entry(entry_t *entry)
94 {
95 entry->child_sa->destroy(entry->child_sa);
96 entry->peer_cfg->destroy(entry->peer_cfg);
97 free(entry);
98 }
99
100 METHOD(trap_manager_t, install, u_int32_t,
101 private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child,
102 u_int32_t reqid)
103 {
104 entry_t *entry, *found = NULL;
105 ike_cfg_t *ike_cfg;
106 child_sa_t *child_sa;
107 host_t *me, *other;
108 linked_list_t *my_ts, *other_ts, *list;
109 enumerator_t *enumerator;
110 status_t status;
111 linked_list_t *proposals;
112 proposal_t *proposal;
113 protocol_id_t proto = PROTO_ESP;
114
115 /* try to resolve addresses */
116 ike_cfg = peer->get_ike_cfg(peer);
117 other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
118 if (!other || other->is_anyaddr(other))
119 {
120 DESTROY_IF(other);
121 DBG1(DBG_CFG, "installing trap failed, remote address unknown");
122 return 0;
123 }
124 me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
125 if (!me || me->is_anyaddr(me))
126 {
127 DESTROY_IF(me);
128 me = hydra->kernel_interface->get_source_addr(
129 hydra->kernel_interface, other, NULL);
130 if (!me)
131 {
132 DBG1(DBG_CFG, "installing trap failed, local address unknown");
133 other->destroy(other);
134 return 0;
135 }
136 me->set_port(me, ike_cfg->get_my_port(ike_cfg));
137 }
138
139 this->lock->write_lock(this->lock);
140 this->installing->set(this->installing, this);
141 enumerator = this->traps->create_enumerator(this->traps);
142 while (enumerator->enumerate(enumerator, &entry))
143 {
144 if (streq(entry->child_sa->get_name(entry->child_sa),
145 child->get_name(child)))
146 {
147 this->traps->remove_at(this->traps, enumerator);
148 found = entry;
149 break;
150 }
151 }
152 enumerator->destroy(enumerator);
153
154 if (found)
155 { /* config might have changed so update everything */
156 DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'",
157 child->get_name(child));
158 reqid = found->child_sa->get_reqid(found->child_sa);
159 }
160
161 /* create and route CHILD_SA */
162 child_sa = child_sa_create(me, other, child, reqid, FALSE);
163
164 list = linked_list_create_with_items(me, NULL);
165 my_ts = child->get_traffic_selectors(child, TRUE, NULL, list);
166 list->destroy_offset(list, offsetof(host_t, destroy));
167
168 list = linked_list_create_with_items(other, NULL);
169 other_ts = child->get_traffic_selectors(child, FALSE, NULL, list);
170 list->destroy_offset(list, offsetof(host_t, destroy));
171
172 /* We don't know the finally negotiated protocol (ESP|AH), we install
173 * the SA with the protocol of the first proposal */
174 proposals = child->get_proposals(child, TRUE);
175 if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
176 {
177 proto = proposal->get_protocol(proposal);
178 }
179 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
180 child_sa->set_protocol(child_sa, proto);
181 child_sa->set_mode(child_sa, child->get_mode(child));
182 status = child_sa->add_policies(child_sa, my_ts, other_ts);
183 my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
184 other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
185 if (status != SUCCESS)
186 {
187 DBG1(DBG_CFG, "installing trap failed");
188 reqid = 0;
189 /* hold off destroying the CHILD_SA until we released the lock */
190 }
191 else
192 {
193 INIT(entry,
194 .child_sa = child_sa,
195 .peer_cfg = peer->get_ref(peer),
196 );
197 this->traps->insert_last(this->traps, entry);
198 reqid = child_sa->get_reqid(child_sa);
199 }
200 this->installing->set(this->installing, NULL);
201 this->lock->unlock(this->lock);
202
203 if (status != SUCCESS)
204 {
205 child_sa->destroy(child_sa);
206 }
207 if (found)
208 {
209 destroy_entry(found);
210 }
211 return reqid;
212 }
213
214 METHOD(trap_manager_t, uninstall, bool,
215 private_trap_manager_t *this, u_int32_t reqid)
216 {
217 enumerator_t *enumerator;
218 entry_t *entry, *found = NULL;
219
220 this->lock->write_lock(this->lock);
221 enumerator = this->traps->create_enumerator(this->traps);
222 while (enumerator->enumerate(enumerator, &entry))
223 {
224 if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
225 {
226 this->traps->remove_at(this->traps, enumerator);
227 found = entry;
228 break;
229 }
230 }
231 enumerator->destroy(enumerator);
232 this->lock->unlock(this->lock);
233
234 if (!found)
235 {
236 DBG1(DBG_CFG, "trap %d not found to uninstall", reqid);
237 return FALSE;
238 }
239
240 destroy_entry(found);
241 return TRUE;
242 }
243
244 /**
245 * convert enumerated entries to peer_cfg, child_sa
246 */
247 static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg,
248 void *none, child_sa_t **child_sa)
249 {
250 if (peer_cfg)
251 {
252 *peer_cfg = (*entry)->peer_cfg;
253 }
254 if (child_sa)
255 {
256 *child_sa = (*entry)->child_sa;
257 }
258 return TRUE;
259 }
260
261 METHOD(trap_manager_t, create_enumerator, enumerator_t*,
262 private_trap_manager_t *this)
263 {
264 this->lock->read_lock(this->lock);
265 return enumerator_create_filter(this->traps->create_enumerator(this->traps),
266 (void*)trap_filter, this->lock,
267 (void*)this->lock->unlock);
268 }
269
270 METHOD(trap_manager_t, find_reqid, u_int32_t,
271 private_trap_manager_t *this, child_cfg_t *child)
272 {
273 enumerator_t *enumerator;
274 child_cfg_t *current;
275 entry_t *entry;
276 u_int32_t reqid = 0;
277
278 if (this->installing->get(this->installing))
279 { /* current thread holds the lock */
280 return reqid;
281 }
282 this->lock->read_lock(this->lock);
283 enumerator = this->traps->create_enumerator(this->traps);
284 while (enumerator->enumerate(enumerator, &entry))
285 {
286 current = entry->child_sa->get_config(entry->child_sa);
287 if (streq(current->get_name(current), child->get_name(child)))
288 {
289 reqid = entry->child_sa->get_reqid(entry->child_sa);
290 break;
291 }
292 }
293 enumerator->destroy(enumerator);
294 this->lock->unlock(this->lock);
295
296 return reqid;
297 }
298
299 METHOD(trap_manager_t, acquire, void,
300 private_trap_manager_t *this, u_int32_t reqid,
301 traffic_selector_t *src, traffic_selector_t *dst)
302 {
303 enumerator_t *enumerator;
304 entry_t *entry, *found = NULL;
305 peer_cfg_t *peer;
306 child_cfg_t *child;
307 ike_sa_t *ike_sa;
308
309 this->lock->read_lock(this->lock);
310 enumerator = this->traps->create_enumerator(this->traps);
311 while (enumerator->enumerate(enumerator, &entry))
312 {
313 if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
314 {
315 found = entry;
316 break;
317 }
318 }
319 enumerator->destroy(enumerator);
320
321 if (!found)
322 {
323 DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
324 this->lock->unlock(this->lock);
325 return;
326 }
327 if (!cas_bool(&found->pending, FALSE, TRUE))
328 {
329 DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
330 this->lock->unlock(this->lock);
331 return;
332 }
333 peer = found->peer_cfg->get_ref(found->peer_cfg);
334 child = found->child_sa->get_config(found->child_sa);
335 child = child->get_ref(child);
336 reqid = found->child_sa->get_reqid(found->child_sa);
337 /* don't hold the lock while checking out the IKE_SA */
338 this->lock->unlock(this->lock);
339
340 ike_sa = charon->ike_sa_manager->checkout_by_config(
341 charon->ike_sa_manager, peer);
342 if (ike_sa)
343 {
344 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
345 {
346 ike_sa->set_peer_cfg(ike_sa, peer);
347 }
348 if (ike_sa->get_version(ike_sa) == IKEV1)
349 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
350 * have a single TS that we can establish in a Quick Mode. */
351 src = dst = NULL;
352 }
353 if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
354 {
355 /* make sure the entry is still there */
356 this->lock->read_lock(this->lock);
357 if (this->traps->find_first(this->traps, NULL,
358 (void**)&found) == SUCCESS)
359 {
360 found->ike_sa = ike_sa;
361 }
362 this->lock->unlock(this->lock);
363 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
364 }
365 else
366 {
367 ike_sa->destroy(ike_sa);
368 }
369 }
370 peer->destroy(peer);
371 }
372
373 /**
374 * Complete the acquire, if successful or failed
375 */
376 static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
377 child_sa_t *child_sa)
378 {
379 enumerator_t *enumerator;
380 entry_t *entry;
381
382 this->lock->read_lock(this->lock);
383 enumerator = this->traps->create_enumerator(this->traps);
384 while (enumerator->enumerate(enumerator, &entry))
385 {
386 if (entry->ike_sa != ike_sa)
387 {
388 continue;
389 }
390 if (child_sa && child_sa->get_reqid(child_sa) !=
391 entry->child_sa->get_reqid(entry->child_sa))
392 {
393 continue;
394 }
395 entry->ike_sa = NULL;
396 entry->pending = FALSE;
397 }
398 enumerator->destroy(enumerator);
399 this->lock->unlock(this->lock);
400 }
401
402 METHOD(listener_t, ike_state_change, bool,
403 trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state)
404 {
405 switch (state)
406 {
407 case IKE_DESTROYING:
408 complete(listener->traps, ike_sa, NULL);
409 return TRUE;
410 default:
411 return TRUE;
412 }
413 }
414
415 METHOD(listener_t, child_state_change, bool,
416 trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa,
417 child_sa_state_t state)
418 {
419 switch (state)
420 {
421 case CHILD_INSTALLED:
422 case CHILD_DESTROYING:
423 complete(listener->traps, ike_sa, child_sa);
424 return TRUE;
425 default:
426 return TRUE;
427 }
428 }
429
430 METHOD(trap_manager_t, flush, void,
431 private_trap_manager_t *this)
432 {
433 linked_list_t *traps;
434 /* since destroying the CHILD_SA results in events which require a read
435 * lock we cannot destroy the list while holding the write lock */
436 this->lock->write_lock(this->lock);
437 traps = this->traps;
438 this->traps = linked_list_create();
439 this->lock->unlock(this->lock);
440 traps->destroy_function(traps, (void*)destroy_entry);
441 }
442
443 METHOD(trap_manager_t, destroy, void,
444 private_trap_manager_t *this)
445 {
446 charon->bus->remove_listener(charon->bus, &this->listener.listener);
447 this->traps->destroy_function(this->traps, (void*)destroy_entry);
448 this->installing->destroy(this->installing);
449 this->lock->destroy(this->lock);
450 free(this);
451 }
452
453 /**
454 * See header
455 */
456 trap_manager_t *trap_manager_create(void)
457 {
458 private_trap_manager_t *this;
459
460 INIT(this,
461 .public = {
462 .install = _install,
463 .uninstall = _uninstall,
464 .create_enumerator = _create_enumerator,
465 .find_reqid = _find_reqid,
466 .acquire = _acquire,
467 .flush = _flush,
468 .destroy = _destroy,
469 },
470 .listener = {
471 .traps = this,
472 .listener = {
473 .ike_state_change = _ike_state_change,
474 .child_state_change = _child_state_change,
475 },
476 },
477 .traps = linked_list_create(),
478 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
479 .installing = thread_value_create(NULL),
480 );
481 charon->bus->add_listener(charon->bus, &this->listener.listener);
482
483 return &this->public;
484 }