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