2 * Copyright (C) 2011-2015 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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>.
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
17 #include "trap_manager.h"
20 #include <threading/mutex.h>
21 #include <threading/rwlock.h>
22 #include <threading/rwlock_condvar.h>
23 #include <collections/linked_list.h>
25 #define INSTALL_DISABLED ((u_int)~0)
27 typedef struct private_trap_manager_t private_trap_manager_t
;
28 typedef struct trap_listener_t trap_listener_t
;
31 * listener to track acquires
33 struct trap_listener_t
{
36 * Implements listener interface
41 * points to trap_manager
43 private_trap_manager_t
*traps
;
47 * Private data of an trap_manager_t object.
49 struct private_trap_manager_t
{
52 * Public trap_manager_t interface.
54 trap_manager_t
public;
57 * Installed traps, as entry_t
62 * read write lock for traps list
67 * listener to track acquiring IKE_SAs
69 trap_listener_t listener
;
72 * list of acquires we currently handle
74 linked_list_t
*acquires
;
77 * mutex for list of acquires
82 * number of threads currently installing trap policies, or INSTALL_DISABLED
87 * condvar to signal trap policy installation
89 rwlock_condvar_t
*condvar
;
92 * Whether to ignore traffic selectors from acquires
94 bool ignore_acquire_ts
;
98 * A installed trap entry
101 /** name of the trapped CHILD_SA */
103 /** ref to peer_cfg to initiate */
104 peer_cfg_t
*peer_cfg
;
105 /** ref to instantiated CHILD_SA (i.e the trap policy) */
106 child_sa_t
*child_sa
;
107 /** TRUE in case of wildcard Transport Mode SA */
115 /** pending IKE_SA connecting upon acquire */
117 /** reqid of pending trap policy */
119 /** destination address (wildcard case) */
124 * actually uninstall and destroy an installed entry
126 static void destroy_entry(entry_t
*this)
128 this->child_sa
->destroy(this->child_sa
);
129 this->peer_cfg
->destroy(this->peer_cfg
);
135 * destroy a cached acquire entry
137 static void destroy_acquire(acquire_t
*this)
139 DESTROY_IF(this->dst
);
143 CALLBACK(acquire_by_reqid
, bool,
144 acquire_t
*this, va_list args
)
148 VA_ARGS_VGET(args
, reqid
);
149 return this->reqid
== reqid
;
152 CALLBACK(acquire_by_dst
, bool,
153 acquire_t
*this, va_list args
)
157 VA_ARGS_VGET(args
, dst
);
158 return this->dst
&& this->dst
->ip_equals(this->dst
, dst
);
162 * Check if any remote TS are dynamic
164 static bool dynamic_remote_ts(child_cfg_t
*child
)
166 enumerator_t
*enumerator
;
167 linked_list_t
*other_ts
;
168 traffic_selector_t
*ts
;
171 other_ts
= child
->get_traffic_selectors(child
, FALSE
, NULL
, NULL
);
172 enumerator
= other_ts
->create_enumerator(other_ts
);
173 while (enumerator
->enumerate(enumerator
, &ts
))
175 if (ts
->is_dynamic(ts
))
181 enumerator
->destroy(enumerator
);
182 other_ts
->destroy_offset(other_ts
, offsetof(traffic_selector_t
, destroy
));
186 METHOD(trap_manager_t
, install
, uint32_t,
187 private_trap_manager_t
*this, peer_cfg_t
*peer
, child_cfg_t
*child
,
190 entry_t
*entry
, *found
= NULL
;
192 child_sa_t
*child_sa
;
194 linked_list_t
*my_ts
, *other_ts
, *list
;
195 enumerator_t
*enumerator
;
197 linked_list_t
*proposals
;
198 proposal_t
*proposal
;
199 protocol_id_t proto
= PROTO_ESP
;
200 bool wildcard
= FALSE
;
202 /* try to resolve addresses */
203 ike_cfg
= peer
->get_ike_cfg(peer
);
204 other
= ike_cfg
->resolve_other(ike_cfg
, AF_UNSPEC
);
205 if (other
&& other
->is_anyaddr(other
) &&
206 child
->get_mode(child
) == MODE_TRANSPORT
)
208 /* allow wildcard for Transport Mode SAs */
209 me
= host_create_any(other
->get_family(other
));
212 else if (other
&& other
->is_anyaddr(other
))
214 other
->destroy(other
);
215 DBG1(DBG_CFG
, "installing trap failed, remote address unknown");
219 { /* depending on the traffic selectors we don't really need a remote
220 * host yet, but we might fail later if no IP can be resolved */
221 if (!other
&& dynamic_remote_ts(child
))
222 { /* with dynamic TS we do need a host, otherwise 0.0.0.0/0 is used,
223 * which is probably not what users expect*/
224 DBG1(DBG_CFG
, "installing trap failed, remote address unknown with "
225 "dynamic traffic selector");
228 me
= ike_cfg
->resolve_me(ike_cfg
, other
? other
->get_family(other
)
232 other
= host_create_any(me
? me
->get_family(me
) : AF_INET
);
234 other
->set_port(other
, ike_cfg
->get_other_port(ike_cfg
));
235 if ((!me
|| me
->is_anyaddr(me
)) && !other
->is_anyaddr(other
))
238 me
= charon
->kernel
->get_source_addr(charon
->kernel
, other
, NULL
);
242 me
= host_create_any(other
->get_family(other
));
244 me
->set_port(me
, ike_cfg
->get_my_port(ike_cfg
));
247 this->lock
->write_lock(this->lock
);
248 if (this->installing
== INSTALL_DISABLED
)
249 { /* flush() has been called */
250 this->lock
->unlock(this->lock
);
251 other
->destroy(other
);
255 enumerator
= this->traps
->create_enumerator(this->traps
);
256 while (enumerator
->enumerate(enumerator
, &entry
))
258 if (streq(entry
->name
, child
->get_name(child
)))
262 { /* replace it with an updated version, if already installed */
263 this->traps
->remove_at(this->traps
, enumerator
);
268 enumerator
->destroy(enumerator
);
272 if (!found
->child_sa
)
274 DBG1(DBG_CFG
, "CHILD_SA '%s' is already being routed", found
->name
);
275 this->lock
->unlock(this->lock
);
276 other
->destroy(other
);
280 /* config might have changed so update everything */
281 DBG1(DBG_CFG
, "updating already routed CHILD_SA '%s'", found
->name
);
282 reqid
= found
->child_sa
->get_reqid(found
->child_sa
);
286 .name
= strdup(child
->get_name(child
)),
287 .peer_cfg
= peer
->get_ref(peer
),
288 .wildcard
= wildcard
,
290 this->traps
->insert_first(this->traps
, entry
);
292 /* don't hold lock while creating CHILD_SA and installing policies */
293 this->lock
->unlock(this->lock
);
295 /* create and route CHILD_SA */
296 child_sa
= child_sa_create(me
, other
, child
, reqid
, FALSE
, 0, 0);
298 list
= linked_list_create_with_items(me
, NULL
);
299 my_ts
= child
->get_traffic_selectors(child
, TRUE
, NULL
, list
);
300 list
->destroy_offset(list
, offsetof(host_t
, destroy
));
302 list
= linked_list_create_with_items(other
, NULL
);
303 other_ts
= child
->get_traffic_selectors(child
, FALSE
, NULL
, list
);
304 list
->destroy_offset(list
, offsetof(host_t
, destroy
));
306 /* We don't know the finally negotiated protocol (ESP|AH), we install
307 * the SA with the protocol of the first proposal */
308 proposals
= child
->get_proposals(child
, TRUE
);
309 if (proposals
->get_first(proposals
, (void**)&proposal
) == SUCCESS
)
311 proto
= proposal
->get_protocol(proposal
);
313 proposals
->destroy_offset(proposals
, offsetof(proposal_t
, destroy
));
314 child_sa
->set_protocol(child_sa
, proto
);
315 child_sa
->set_mode(child_sa
, child
->get_mode(child
));
316 child_sa
->set_policies(child_sa
, my_ts
, other_ts
);
317 status
= child_sa
->install_policies(child_sa
);
318 my_ts
->destroy_offset(my_ts
, offsetof(traffic_selector_t
, destroy
));
319 other_ts
->destroy_offset(other_ts
, offsetof(traffic_selector_t
, destroy
));
320 if (status
!= SUCCESS
)
322 DBG1(DBG_CFG
, "installing trap failed");
323 this->lock
->write_lock(this->lock
);
324 this->traps
->remove(this->traps
, entry
, NULL
);
325 this->lock
->unlock(this->lock
);
326 entry
->child_sa
= child_sa
;
327 destroy_entry(entry
);
332 reqid
= child_sa
->get_reqid(child_sa
);
333 this->lock
->write_lock(this->lock
);
334 entry
->child_sa
= child_sa
;
335 this->lock
->unlock(this->lock
);
339 destroy_entry(found
);
341 this->lock
->write_lock(this->lock
);
342 /* do this at the end, so entries created temporarily are also destroyed */
344 this->condvar
->signal(this->condvar
);
345 this->lock
->unlock(this->lock
);
349 METHOD(trap_manager_t
, uninstall
, bool,
350 private_trap_manager_t
*this, uint32_t reqid
)
352 enumerator_t
*enumerator
;
353 entry_t
*entry
, *found
= NULL
;
355 this->lock
->write_lock(this->lock
);
356 enumerator
= this->traps
->create_enumerator(this->traps
);
357 while (enumerator
->enumerate(enumerator
, &entry
))
359 if (entry
->child_sa
&&
360 entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
362 this->traps
->remove_at(this->traps
, enumerator
);
367 enumerator
->destroy(enumerator
);
368 this->lock
->unlock(this->lock
);
372 DBG1(DBG_CFG
, "trap %d not found to uninstall", reqid
);
375 destroy_entry(found
);
379 CALLBACK(trap_filter
, bool,
380 rwlock_t
*lock
, enumerator_t
*orig
, va_list args
)
383 peer_cfg_t
**peer_cfg
;
384 child_sa_t
**child_sa
;
386 VA_ARGS_VGET(args
, peer_cfg
, child_sa
);
388 while (orig
->enumerate(orig
, &entry
))
390 if (!entry
->child_sa
)
391 { /* skip entries that are currently being installed */
396 *peer_cfg
= entry
->peer_cfg
;
400 *child_sa
= entry
->child_sa
;
407 METHOD(trap_manager_t
, create_enumerator
, enumerator_t
*,
408 private_trap_manager_t
*this)
410 this->lock
->read_lock(this->lock
);
411 return enumerator_create_filter(this->traps
->create_enumerator(this->traps
),
412 trap_filter
, this->lock
,
413 (void*)this->lock
->unlock
);
416 METHOD(trap_manager_t
, find_reqid
, uint32_t,
417 private_trap_manager_t
*this, child_cfg_t
*child
)
419 enumerator_t
*enumerator
;
423 this->lock
->read_lock(this->lock
);
424 enumerator
= this->traps
->create_enumerator(this->traps
);
425 while (enumerator
->enumerate(enumerator
, &entry
))
427 if (streq(entry
->name
, child
->get_name(child
)))
431 reqid
= entry
->child_sa
->get_reqid(entry
->child_sa
);
436 enumerator
->destroy(enumerator
);
437 this->lock
->unlock(this->lock
);
441 METHOD(trap_manager_t
, acquire
, void,
442 private_trap_manager_t
*this, uint32_t reqid
,
443 traffic_selector_t
*src
, traffic_selector_t
*dst
)
445 enumerator_t
*enumerator
;
446 entry_t
*entry
, *found
= NULL
;
452 bool wildcard
, ignore
= FALSE
;
454 this->lock
->read_lock(this->lock
);
455 enumerator
= this->traps
->create_enumerator(this->traps
);
456 while (enumerator
->enumerate(enumerator
, &entry
))
458 if (entry
->child_sa
&&
459 entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
465 enumerator
->destroy(enumerator
);
469 DBG1(DBG_CFG
, "trap not found, unable to acquire reqid %d", reqid
);
470 this->lock
->unlock(this->lock
);
473 reqid
= found
->child_sa
->get_reqid(found
->child_sa
);
474 wildcard
= found
->wildcard
;
476 this->mutex
->lock(this->mutex
);
478 { /* for wildcard acquires we check that we don't have a pending acquire
479 * with the same peer */
482 dst
->to_subnet(dst
, &host
, &mask
);
483 if (this->acquires
->find_first(this->acquires
, acquire_by_dst
,
484 (void**)&acquire
, host
))
495 this->acquires
->insert_last(this->acquires
, acquire
);
500 if (this->acquires
->find_first(this->acquires
, acquire_by_reqid
,
501 (void**)&acquire
, reqid
))
510 this->acquires
->insert_last(this->acquires
, acquire
);
513 this->mutex
->unlock(this->mutex
);
516 DBG1(DBG_CFG
, "ignoring acquire, connection attempt pending");
517 this->lock
->unlock(this->lock
);
520 peer
= found
->peer_cfg
->get_ref(found
->peer_cfg
);
521 child
= found
->child_sa
->get_config(found
->child_sa
);
522 child
= child
->get_ref(child
);
523 /* don't hold the lock while checking out the IKE_SA */
524 this->lock
->unlock(this->lock
);
527 { /* the peer config would match IKE_SAs with other peers */
528 ike_sa
= charon
->ike_sa_manager
->checkout_new(charon
->ike_sa_manager
,
529 peer
->get_ike_version(peer
), TRUE
);
536 ike_sa
->set_peer_cfg(ike_sa
, peer
);
537 ike_cfg
= ike_sa
->get_ike_cfg(ike_sa
);
539 port
= ike_cfg
->get_other_port(ike_cfg
);
540 dst
->to_subnet(dst
, &host
, &mask
);
541 host
->set_port(host
, port
);
542 ike_sa
->set_other_host(ike_sa
, host
);
544 port
= ike_cfg
->get_my_port(ike_cfg
);
545 src
->to_subnet(src
, &host
, &mask
);
546 host
->set_port(host
, port
);
547 ike_sa
->set_my_host(ike_sa
, host
);
549 charon
->bus
->set_sa(charon
->bus
, ike_sa
);
554 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(
555 charon
->ike_sa_manager
, peer
);
559 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
561 ike_sa
->set_peer_cfg(ike_sa
, peer
);
563 if (this->ignore_acquire_ts
|| ike_sa
->get_version(ike_sa
) == IKEV1
)
564 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
565 * have a single TS that we can establish in a Quick Mode. */
569 this->mutex
->lock(this->mutex
);
570 acquire
->ike_sa
= ike_sa
;
571 this->mutex
->unlock(this->mutex
);
573 if (ike_sa
->initiate(ike_sa
, child
, reqid
, src
, dst
) != DESTROY_ME
)
575 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
579 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
585 this->mutex
->lock(this->mutex
);
586 this->acquires
->remove(this->acquires
, acquire
, NULL
);
587 this->mutex
->unlock(this->mutex
);
588 destroy_acquire(acquire
);
589 child
->destroy(child
);
595 * Complete the acquire, if successful or failed
597 static void complete(private_trap_manager_t
*this, ike_sa_t
*ike_sa
,
598 child_sa_t
*child_sa
)
600 enumerator_t
*enumerator
;
603 this->mutex
->lock(this->mutex
);
604 enumerator
= this->acquires
->create_enumerator(this->acquires
);
605 while (enumerator
->enumerate(enumerator
, &acquire
))
607 if (!acquire
->ike_sa
|| acquire
->ike_sa
!= ike_sa
)
615 /* since every wildcard acquire results in a separate IKE_SA
616 * there is no need to compare the destination address */
618 else if (child_sa
->get_reqid(child_sa
) != acquire
->reqid
)
623 this->acquires
->remove_at(this->acquires
, enumerator
);
624 destroy_acquire(acquire
);
626 enumerator
->destroy(enumerator
);
627 this->mutex
->unlock(this->mutex
);
630 METHOD(listener_t
, ike_state_change
, bool,
631 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
636 complete(listener
->traps
, ike_sa
, NULL
);
643 METHOD(listener_t
, child_state_change
, bool,
644 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
645 child_sa_state_t state
)
649 case CHILD_INSTALLED
:
650 case CHILD_DESTROYING
:
651 complete(listener
->traps
, ike_sa
, child_sa
);
658 METHOD(trap_manager_t
, flush
, void,
659 private_trap_manager_t
*this)
661 this->lock
->write_lock(this->lock
);
662 while (this->installing
)
664 this->condvar
->wait(this->condvar
, this->lock
);
666 this->traps
->destroy_function(this->traps
, (void*)destroy_entry
);
667 this->traps
= linked_list_create();
668 this->installing
= INSTALL_DISABLED
;
669 this->lock
->unlock(this->lock
);
672 METHOD(trap_manager_t
, destroy
, void,
673 private_trap_manager_t
*this)
675 charon
->bus
->remove_listener(charon
->bus
, &this->listener
.listener
);
676 this->traps
->destroy_function(this->traps
, (void*)destroy_entry
);
677 this->acquires
->destroy_function(this->acquires
, (void*)destroy_acquire
);
678 this->condvar
->destroy(this->condvar
);
679 this->mutex
->destroy(this->mutex
);
680 this->lock
->destroy(this->lock
);
687 trap_manager_t
*trap_manager_create(void)
689 private_trap_manager_t
*this;
694 .uninstall
= _uninstall
,
695 .create_enumerator
= _create_enumerator
,
696 .find_reqid
= _find_reqid
,
704 .ike_state_change
= _ike_state_change
,
705 .child_state_change
= _child_state_change
,
708 .traps
= linked_list_create(),
709 .acquires
= linked_list_create(),
710 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
711 .lock
= rwlock_create(RWLOCK_TYPE_DEFAULT
),
712 .condvar
= rwlock_condvar_create(),
713 .ignore_acquire_ts
= lib
->settings
->get_bool(lib
->settings
,
714 "%s.ignore_acquire_ts", FALSE
, lib
->ns
),
716 charon
->bus
->add_listener(charon
->bus
, &this->listener
.listener
);
718 return &this->public;