2 * Copyright (C) 2011-2012 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"
21 #include <threading/rwlock.h>
22 #include <collections/linked_list.h>
25 typedef struct private_trap_manager_t private_trap_manager_t
;
26 typedef struct trap_listener_t trap_listener_t
;
29 * listener to track acquires
31 struct trap_listener_t
{
34 * Implements listener interface
39 * points to trap_manager
41 private_trap_manager_t
*traps
;
45 * Private data of an trap_manager_t object.
47 struct private_trap_manager_t
{
50 * Public trap_manager_t interface.
52 trap_manager_t
public;
55 * Installed traps, as entry_t
60 * read write lock for traps list
65 * listener to track acquiring IKE_SAs
67 trap_listener_t listener
;
71 * A installed trap entry
74 /** ref to peer_cfg to initiate */
76 /** ref to instanciated CHILD_SA */
78 /** TRUE if an acquire is pending */
80 /** pending IKE_SA connecting upon acquire */
85 * actually uninstall and destroy an installed entry
87 static void destroy_entry(entry_t
*entry
)
89 entry
->child_sa
->destroy(entry
->child_sa
);
90 entry
->peer_cfg
->destroy(entry
->peer_cfg
);
94 METHOD(trap_manager_t
, install
, u_int32_t
,
95 private_trap_manager_t
*this, peer_cfg_t
*peer
, child_cfg_t
*child
)
97 entry_t
*entry
, *found
= NULL
;
101 linked_list_t
*my_ts
, *other_ts
, *list
;
102 enumerator_t
*enumerator
;
106 /* try to resolve addresses */
107 ike_cfg
= peer
->get_ike_cfg(peer
);
108 other
= host_create_from_dns(ike_cfg
->get_other_addr(ike_cfg
, NULL
),
109 0, ike_cfg
->get_other_port(ike_cfg
));
110 if (!other
|| other
->is_anyaddr(other
))
112 DBG1(DBG_CFG
, "installing trap failed, remote address unknown");
115 me
= host_create_from_dns(ike_cfg
->get_my_addr(ike_cfg
, NULL
),
116 other
->get_family(other
), ike_cfg
->get_my_port(ike_cfg
));
117 if (!me
|| me
->is_anyaddr(me
))
120 me
= hydra
->kernel_interface
->get_source_addr(
121 hydra
->kernel_interface
, other
, NULL
);
124 DBG1(DBG_CFG
, "installing trap failed, local address unknown");
125 other
->destroy(other
);
128 me
->set_port(me
, ike_cfg
->get_my_port(ike_cfg
));
131 this->lock
->write_lock(this->lock
);
132 enumerator
= this->traps
->create_enumerator(this->traps
);
133 while (enumerator
->enumerate(enumerator
, &entry
))
135 if (streq(entry
->child_sa
->get_name(entry
->child_sa
),
136 child
->get_name(child
)))
138 this->traps
->remove_at(this->traps
, enumerator
);
143 enumerator
->destroy(enumerator
);
145 { /* config might have changed so update everything */
146 DBG1(DBG_CFG
, "updating already routed CHILD_SA '%s'",
147 child
->get_name(child
));
148 reqid
= found
->child_sa
->get_reqid(found
->child_sa
);
151 /* create and route CHILD_SA */
152 child_sa
= child_sa_create(me
, other
, child
, reqid
, FALSE
);
154 list
= linked_list_create_with_items(me
, NULL
);
155 my_ts
= child
->get_traffic_selectors(child
, TRUE
, NULL
, list
);
156 list
->destroy_offset(list
, offsetof(host_t
, destroy
));
158 list
= linked_list_create_with_items(other
, NULL
);
159 other_ts
= child
->get_traffic_selectors(child
, FALSE
, NULL
, list
);
160 list
->destroy_offset(list
, offsetof(host_t
, destroy
));
162 /* while we don't know the finally negotiated protocol (ESP|AH), we
163 * could iterate all proposals for a best guess (TODO). But as we
164 * support ESP only for now, we set it here. */
165 child_sa
->set_protocol(child_sa
, PROTO_ESP
);
166 child_sa
->set_mode(child_sa
, child
->get_mode(child
));
167 status
= child_sa
->add_policies(child_sa
, my_ts
, other_ts
);
168 my_ts
->destroy_offset(my_ts
, offsetof(traffic_selector_t
, destroy
));
169 other_ts
->destroy_offset(other_ts
, offsetof(traffic_selector_t
, destroy
));
170 if (status
!= SUCCESS
)
172 DBG1(DBG_CFG
, "installing trap failed");
174 /* hold off destroying the CHILD_SA until we released the lock */
179 .child_sa
= child_sa
,
180 .peer_cfg
= peer
->get_ref(peer
),
182 this->traps
->insert_last(this->traps
, entry
);
183 reqid
= child_sa
->get_reqid(child_sa
);
185 this->lock
->unlock(this->lock
);
187 if (status
!= SUCCESS
)
189 child_sa
->destroy(child_sa
);
193 destroy_entry(found
);
198 METHOD(trap_manager_t
, uninstall
, bool,
199 private_trap_manager_t
*this, u_int32_t reqid
)
201 enumerator_t
*enumerator
;
202 entry_t
*entry
, *found
= NULL
;
204 this->lock
->write_lock(this->lock
);
205 enumerator
= this->traps
->create_enumerator(this->traps
);
206 while (enumerator
->enumerate(enumerator
, &entry
))
208 if (entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
210 this->traps
->remove_at(this->traps
, enumerator
);
215 enumerator
->destroy(enumerator
);
216 this->lock
->unlock(this->lock
);
220 DBG1(DBG_CFG
, "trap %d not found to uninstall", reqid
);
224 destroy_entry(found
);
229 * convert enumerated entries to peer_cfg, child_sa
231 static bool trap_filter(rwlock_t
*lock
, entry_t
**entry
, peer_cfg_t
**peer_cfg
,
232 void *none
, child_sa_t
**child_sa
)
236 *peer_cfg
= (*entry
)->peer_cfg
;
240 *child_sa
= (*entry
)->child_sa
;
245 METHOD(trap_manager_t
, create_enumerator
, enumerator_t
*,
246 private_trap_manager_t
*this)
248 this->lock
->read_lock(this->lock
);
249 return enumerator_create_filter(this->traps
->create_enumerator(this->traps
),
250 (void*)trap_filter
, this->lock
,
251 (void*)this->lock
->unlock
);
254 METHOD(trap_manager_t
, acquire
, void,
255 private_trap_manager_t
*this, u_int32_t reqid
,
256 traffic_selector_t
*src
, traffic_selector_t
*dst
)
258 enumerator_t
*enumerator
;
259 entry_t
*entry
, *found
= NULL
;
264 this->lock
->read_lock(this->lock
);
265 enumerator
= this->traps
->create_enumerator(this->traps
);
266 while (enumerator
->enumerate(enumerator
, &entry
))
268 if (entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
274 enumerator
->destroy(enumerator
);
278 DBG1(DBG_CFG
, "trap not found, unable to acquire reqid %d",reqid
);
279 this->lock
->unlock(this->lock
);
282 if (!cas_bool(&found
->pending
, FALSE
, TRUE
))
284 DBG1(DBG_CFG
, "ignoring acquire, connection attempt pending");
285 this->lock
->unlock(this->lock
);
288 peer
= found
->peer_cfg
->get_ref(found
->peer_cfg
);
289 child
= found
->child_sa
->get_config(found
->child_sa
);
290 child
= child
->get_ref(child
);
291 reqid
= found
->child_sa
->get_reqid(found
->child_sa
);
292 /* don't hold the lock while checking out the IKE_SA */
293 this->lock
->unlock(this->lock
);
295 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(
296 charon
->ike_sa_manager
, peer
);
299 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
301 ike_sa
->set_peer_cfg(ike_sa
, peer
);
303 if (ike_sa
->get_version(ike_sa
) == IKEV1
)
304 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
305 * have a single TS that we can establish in a Quick Mode. */
308 if (ike_sa
->initiate(ike_sa
, child
, reqid
, src
, dst
) != DESTROY_ME
)
310 /* make sure the entry is still there */
311 this->lock
->read_lock(this->lock
);
312 if (this->traps
->find_first(this->traps
, NULL
,
313 (void**)&found
) == SUCCESS
)
315 found
->ike_sa
= ike_sa
;
317 this->lock
->unlock(this->lock
);
318 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
322 charon
->ike_sa_manager
->checkin_and_destroy(
323 charon
->ike_sa_manager
, ike_sa
);
330 * Complete the acquire, if successful or failed
332 static void complete(private_trap_manager_t
*this, ike_sa_t
*ike_sa
,
333 child_sa_t
*child_sa
)
335 enumerator_t
*enumerator
;
338 this->lock
->read_lock(this->lock
);
339 enumerator
= this->traps
->create_enumerator(this->traps
);
340 while (enumerator
->enumerate(enumerator
, &entry
))
342 if (entry
->ike_sa
!= ike_sa
)
346 if (child_sa
&& child_sa
->get_reqid(child_sa
) !=
347 entry
->child_sa
->get_reqid(entry
->child_sa
))
351 entry
->ike_sa
= NULL
;
352 entry
->pending
= FALSE
;
354 enumerator
->destroy(enumerator
);
355 this->lock
->unlock(this->lock
);
358 METHOD(listener_t
, ike_state_change
, bool,
359 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
364 complete(listener
->traps
, ike_sa
, NULL
);
371 METHOD(listener_t
, child_state_change
, bool,
372 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
373 child_sa_state_t state
)
377 case CHILD_INSTALLED
:
378 case CHILD_DESTROYING
:
379 complete(listener
->traps
, ike_sa
, child_sa
);
386 METHOD(trap_manager_t
, flush
, void,
387 private_trap_manager_t
*this)
389 linked_list_t
*traps
;
390 /* since destroying the CHILD_SA results in events which require a read
391 * lock we cannot destroy the list while holding the write lock */
392 this->lock
->write_lock(this->lock
);
394 this->traps
= linked_list_create();
395 this->lock
->unlock(this->lock
);
396 traps
->destroy_function(traps
, (void*)destroy_entry
);
399 METHOD(trap_manager_t
, destroy
, void,
400 private_trap_manager_t
*this)
402 charon
->bus
->remove_listener(charon
->bus
, &this->listener
.listener
);
403 this->traps
->destroy_function(this->traps
, (void*)destroy_entry
);
404 this->lock
->destroy(this->lock
);
411 trap_manager_t
*trap_manager_create(void)
413 private_trap_manager_t
*this;
418 .uninstall
= _uninstall
,
419 .create_enumerator
= _create_enumerator
,
427 .ike_state_change
= _ike_state_change
,
428 .child_state_change
= _child_state_change
,
431 .traps
= linked_list_create(),
432 .lock
= rwlock_create(RWLOCK_TYPE_DEFAULT
),
434 charon
->bus
->add_listener(charon
->bus
, &this->listener
.listener
);
436 return &this->public;