2 * Copyright (C) 2011 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 <utils/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
)
101 linked_list_t
*my_ts
, *other_ts
, *list
;
102 enumerator_t
*enumerator
;
107 /* check if not already done */
108 this->lock
->read_lock(this->lock
);
109 enumerator
= this->traps
->create_enumerator(this->traps
);
110 while (enumerator
->enumerate(enumerator
, &entry
))
112 if (streq(entry
->child_sa
->get_name(entry
->child_sa
),
113 child
->get_name(child
)))
119 enumerator
->destroy(enumerator
);
120 this->lock
->unlock(this->lock
);
123 DBG1(DBG_CFG
, "CHILD_SA named '%s' already routed",
124 child
->get_name(child
));
128 /* try to resolve addresses */
129 ike_cfg
= peer
->get_ike_cfg(peer
);
130 other
= host_create_from_dns(ike_cfg
->get_other_addr(ike_cfg
, NULL
),
131 0, ike_cfg
->get_other_port(ike_cfg
));
132 if (!other
|| other
->is_anyaddr(other
))
134 DBG1(DBG_CFG
, "installing trap failed, remote address unknown");
137 me
= host_create_from_dns(ike_cfg
->get_my_addr(ike_cfg
, NULL
),
138 other
->get_family(other
), ike_cfg
->get_my_port(ike_cfg
));
139 if (!me
|| me
->is_anyaddr(me
))
142 me
= hydra
->kernel_interface
->get_source_addr(
143 hydra
->kernel_interface
, other
, NULL
);
146 DBG1(DBG_CFG
, "installing trap failed, local address unknown");
147 other
->destroy(other
);
150 me
->set_port(me
, ike_cfg
->get_my_port(ike_cfg
));
153 /* create and route CHILD_SA */
154 child_sa
= child_sa_create(me
, other
, child
, 0, FALSE
);
156 list
= linked_list_create_with_items(me
, NULL
);
157 my_ts
= child
->get_traffic_selectors(child
, TRUE
, NULL
, list
);
158 list
->destroy_offset(list
, offsetof(host_t
, destroy
));
160 list
= linked_list_create_with_items(other
, NULL
);
161 other_ts
= child
->get_traffic_selectors(child
, FALSE
, NULL
, list
);
162 list
->destroy_offset(list
, offsetof(host_t
, destroy
));
164 /* while we don't know the finally negotiated protocol (ESP|AH), we
165 * could iterate all proposals for a best guess (TODO). But as we
166 * support ESP only for now, we set it here. */
167 child_sa
->set_protocol(child_sa
, PROTO_ESP
);
168 child_sa
->set_mode(child_sa
, child
->get_mode(child
));
169 status
= child_sa
->add_policies(child_sa
, my_ts
, other_ts
);
170 my_ts
->destroy_offset(my_ts
, offsetof(traffic_selector_t
, destroy
));
171 other_ts
->destroy_offset(other_ts
, offsetof(traffic_selector_t
, destroy
));
172 if (status
!= SUCCESS
)
174 child_sa
->destroy(child_sa
);
175 DBG1(DBG_CFG
, "installing trap failed");
179 reqid
= child_sa
->get_reqid(child_sa
);
181 .child_sa
= child_sa
,
182 .peer_cfg
= peer
->get_ref(peer
),
185 this->lock
->write_lock(this->lock
);
186 this->traps
->insert_last(this->traps
, entry
);
187 this->lock
->unlock(this->lock
);
192 METHOD(trap_manager_t
, uninstall
, bool,
193 private_trap_manager_t
*this, u_int32_t reqid
)
195 enumerator_t
*enumerator
;
196 entry_t
*entry
, *found
= NULL
;
198 this->lock
->write_lock(this->lock
);
199 enumerator
= this->traps
->create_enumerator(this->traps
);
200 while (enumerator
->enumerate(enumerator
, &entry
))
202 if (entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
204 this->traps
->remove_at(this->traps
, enumerator
);
209 enumerator
->destroy(enumerator
);
210 this->lock
->unlock(this->lock
);
214 DBG1(DBG_CFG
, "trap %d not found to uninstall", reqid
);
218 destroy_entry(found
);
223 * convert enumerated entries to peer_cfg, child_sa
225 static bool trap_filter(rwlock_t
*lock
, entry_t
**entry
, peer_cfg_t
**peer_cfg
,
226 void *none
, child_sa_t
**child_sa
)
230 *peer_cfg
= (*entry
)->peer_cfg
;
234 *child_sa
= (*entry
)->child_sa
;
239 METHOD(trap_manager_t
, create_enumerator
, enumerator_t
*,
240 private_trap_manager_t
*this)
242 this->lock
->read_lock(this->lock
);
243 return enumerator_create_filter(this->traps
->create_enumerator(this->traps
),
244 (void*)trap_filter
, this->lock
,
245 (void*)this->lock
->unlock
);
248 METHOD(trap_manager_t
, acquire
, void,
249 private_trap_manager_t
*this, u_int32_t reqid
,
250 traffic_selector_t
*src
, traffic_selector_t
*dst
)
252 enumerator_t
*enumerator
;
253 entry_t
*entry
, *found
= NULL
;
258 this->lock
->read_lock(this->lock
);
259 enumerator
= this->traps
->create_enumerator(this->traps
);
260 while (enumerator
->enumerate(enumerator
, &entry
))
262 if (entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
268 enumerator
->destroy(enumerator
);
272 DBG1(DBG_CFG
, "trap not found, unable to acquire reqid %d",reqid
);
273 this->lock
->unlock(this->lock
);
276 if (!cas_bool(&found
->pending
, FALSE
, TRUE
))
278 DBG1(DBG_CFG
, "ignoring acquire, connection attempt pending");
279 this->lock
->unlock(this->lock
);
282 peer
= found
->peer_cfg
->get_ref(found
->peer_cfg
);
283 child
= found
->child_sa
->get_config(found
->child_sa
);
284 child
= child
->get_ref(child
);
285 reqid
= found
->child_sa
->get_reqid(found
->child_sa
);
286 /* don't hold the lock while checking out the IKE_SA */
287 this->lock
->unlock(this->lock
);
289 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(
290 charon
->ike_sa_manager
, peer
);
293 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
295 ike_sa
->set_peer_cfg(ike_sa
, peer
);
297 if (ike_sa
->get_version(ike_sa
) == IKEV1
)
298 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
299 * have a single TS that we can establish in a Quick Mode. */
302 if (ike_sa
->initiate(ike_sa
, child
, reqid
, src
, dst
) != DESTROY_ME
)
304 /* make sure the entry is still there */
305 this->lock
->read_lock(this->lock
);
306 if (this->traps
->find_first(this->traps
, NULL
,
307 (void**)&found
) == SUCCESS
)
309 found
->ike_sa
= ike_sa
;
311 this->lock
->unlock(this->lock
);
312 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
316 charon
->ike_sa_manager
->checkin_and_destroy(
317 charon
->ike_sa_manager
, ike_sa
);
324 * Complete the acquire, if successful or failed
326 static void complete(private_trap_manager_t
*this, ike_sa_t
*ike_sa
,
327 child_sa_t
*child_sa
)
329 enumerator_t
*enumerator
;
332 this->lock
->read_lock(this->lock
);
333 enumerator
= this->traps
->create_enumerator(this->traps
);
334 while (enumerator
->enumerate(enumerator
, &entry
))
336 if (entry
->ike_sa
!= ike_sa
)
340 if (child_sa
&& child_sa
->get_reqid(child_sa
) !=
341 entry
->child_sa
->get_reqid(entry
->child_sa
))
345 entry
->ike_sa
= NULL
;
346 entry
->pending
= FALSE
;
348 enumerator
->destroy(enumerator
);
349 this->lock
->unlock(this->lock
);
352 METHOD(listener_t
, ike_state_change
, bool,
353 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
358 complete(listener
->traps
, ike_sa
, NULL
);
365 METHOD(listener_t
, child_state_change
, bool,
366 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
367 child_sa_state_t state
)
371 case CHILD_INSTALLED
:
372 case CHILD_DESTROYING
:
373 complete(listener
->traps
, ike_sa
, child_sa
);
380 METHOD(trap_manager_t
, flush
, void,
381 private_trap_manager_t
*this)
383 linked_list_t
*traps
;
384 /* since destroying the CHILD_SA results in events which require a read
385 * lock we cannot destroy the list while holding the write lock */
386 this->lock
->write_lock(this->lock
);
388 this->traps
= linked_list_create();
389 this->lock
->unlock(this->lock
);
390 traps
->destroy_function(traps
, (void*)destroy_entry
);
393 METHOD(trap_manager_t
, destroy
, void,
394 private_trap_manager_t
*this)
396 charon
->bus
->remove_listener(charon
->bus
, &this->listener
.listener
);
397 this->traps
->destroy_function(this->traps
, (void*)destroy_entry
);
398 this->lock
->destroy(this->lock
);
405 trap_manager_t
*trap_manager_create(void)
407 private_trap_manager_t
*this;
412 .uninstall
= _uninstall
,
413 .create_enumerator
= _create_enumerator
,
421 .ike_state_change
= _ike_state_change
,
422 .child_state_change
= _child_state_change
,
425 .traps
= linked_list_create(),
426 .lock
= rwlock_create(RWLOCK_TYPE_DEFAULT
),
428 charon
->bus
->add_listener(charon
->bus
, &this->listener
.listener
);
430 return &this->public;