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