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
;
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
),
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
),
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
);
155 my_ts
= child
->get_traffic_selectors(child
, TRUE
, NULL
, me
);
156 other_ts
= child
->get_traffic_selectors(child
, FALSE
, NULL
, other
);
158 other
->destroy(other
);
160 /* while we don't know the finally negotiated protocol (ESP|AH), we
161 * could iterate all proposals for a best guess (TODO). But as we
162 * support ESP only for now, we set it here. */
163 child_sa
->set_protocol(child_sa
, PROTO_ESP
);
164 child_sa
->set_mode(child_sa
, child
->get_mode(child
));
165 status
= child_sa
->add_policies(child_sa
, my_ts
, other_ts
);
166 my_ts
->destroy_offset(my_ts
, offsetof(traffic_selector_t
, destroy
));
167 other_ts
->destroy_offset(other_ts
, offsetof(traffic_selector_t
, destroy
));
168 if (status
!= SUCCESS
)
170 child_sa
->destroy(child_sa
);
171 DBG1(DBG_CFG
, "installing trap failed");
175 reqid
= child_sa
->get_reqid(child_sa
);
177 .child_sa
= child_sa
,
178 .peer_cfg
= peer
->get_ref(peer
),
181 this->lock
->write_lock(this->lock
);
182 this->traps
->insert_last(this->traps
, entry
);
183 this->lock
->unlock(this->lock
);
188 METHOD(trap_manager_t
, uninstall
, bool,
189 private_trap_manager_t
*this, u_int32_t reqid
)
191 enumerator_t
*enumerator
;
192 entry_t
*entry
, *found
= NULL
;
194 this->lock
->write_lock(this->lock
);
195 enumerator
= this->traps
->create_enumerator(this->traps
);
196 while (enumerator
->enumerate(enumerator
, &entry
))
198 if (entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
200 this->traps
->remove_at(this->traps
, enumerator
);
205 enumerator
->destroy(enumerator
);
206 this->lock
->unlock(this->lock
);
210 DBG1(DBG_CFG
, "trap %d not found to uninstall", reqid
);
214 destroy_entry(found
);
219 * convert enumerated entries to peer_cfg, child_sa
221 static bool trap_filter(rwlock_t
*lock
, entry_t
**entry
, peer_cfg_t
**peer_cfg
,
222 void *none
, child_sa_t
**child_sa
)
226 *peer_cfg
= (*entry
)->peer_cfg
;
230 *child_sa
= (*entry
)->child_sa
;
235 METHOD(trap_manager_t
, create_enumerator
, enumerator_t
*,
236 private_trap_manager_t
*this)
238 this->lock
->read_lock(this->lock
);
239 return enumerator_create_filter(this->traps
->create_enumerator(this->traps
),
240 (void*)trap_filter
, this->lock
,
241 (void*)this->lock
->unlock
);
244 METHOD(trap_manager_t
, acquire
, void,
245 private_trap_manager_t
*this, u_int32_t reqid
,
246 traffic_selector_t
*src
, traffic_selector_t
*dst
)
248 enumerator_t
*enumerator
;
249 entry_t
*entry
, *found
= NULL
;
254 this->lock
->read_lock(this->lock
);
255 enumerator
= this->traps
->create_enumerator(this->traps
);
256 while (enumerator
->enumerate(enumerator
, &entry
))
258 if (entry
->child_sa
->get_reqid(entry
->child_sa
) == reqid
)
264 enumerator
->destroy(enumerator
);
268 DBG1(DBG_CFG
, "trap not found, unable to acquire reqid %d",reqid
);
269 this->lock
->unlock(this->lock
);
272 if (!cas_bool(&found
->pending
, FALSE
, TRUE
))
274 DBG1(DBG_CFG
, "ignoring acquire, connection attempt pending");
275 this->lock
->unlock(this->lock
);
278 peer
= found
->peer_cfg
->get_ref(found
->peer_cfg
);
279 child
= found
->child_sa
->get_config(found
->child_sa
);
280 child
= child
->get_ref(child
);
281 reqid
= found
->child_sa
->get_reqid(found
->child_sa
);
282 /* don't hold the lock while checking out the IKE_SA */
283 this->lock
->unlock(this->lock
);
285 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(
286 charon
->ike_sa_manager
, peer
);
287 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
289 ike_sa
->set_peer_cfg(ike_sa
, peer
);
291 if (ike_sa
->initiate(ike_sa
, child
, reqid
, src
, dst
) != DESTROY_ME
)
293 /* make sure the entry is still there */
294 this->lock
->read_lock(this->lock
);
295 if (this->traps
->find_first(this->traps
, NULL
,
296 (void**)&found
) == SUCCESS
)
298 found
->ike_sa
= ike_sa
;
300 this->lock
->unlock(this->lock
);
301 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
305 charon
->ike_sa_manager
->checkin_and_destroy(
306 charon
->ike_sa_manager
, ike_sa
);
312 * Complete the acquire, if successful or failed
314 static void complete(private_trap_manager_t
*this, ike_sa_t
*ike_sa
,
315 child_sa_t
*child_sa
)
317 enumerator_t
*enumerator
;
320 this->lock
->read_lock(this->lock
);
321 enumerator
= this->traps
->create_enumerator(this->traps
);
322 while (enumerator
->enumerate(enumerator
, &entry
))
324 if (entry
->ike_sa
!= ike_sa
)
328 if (child_sa
&& child_sa
->get_reqid(child_sa
) !=
329 entry
->child_sa
->get_reqid(entry
->child_sa
))
333 entry
->ike_sa
= NULL
;
334 entry
->pending
= FALSE
;
336 enumerator
->destroy(enumerator
);
337 this->lock
->unlock(this->lock
);
340 METHOD(listener_t
, ike_state_change
, bool,
341 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
346 complete(listener
->traps
, ike_sa
, NULL
);
353 METHOD(listener_t
, child_state_change
, bool,
354 trap_listener_t
*listener
, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
355 child_sa_state_t state
)
359 case CHILD_INSTALLED
:
360 case CHILD_DESTROYING
:
361 complete(listener
->traps
, ike_sa
, child_sa
);
368 METHOD(trap_manager_t
, flush
, void,
369 private_trap_manager_t
*this)
371 linked_list_t
*traps
;
372 /* since destroying the CHILD_SA results in events which require a read
373 * lock we cannot destroy the list while holding the write lock */
374 this->lock
->write_lock(this->lock
);
376 this->traps
= linked_list_create();
377 this->lock
->unlock(this->lock
);
378 traps
->destroy_function(traps
, (void*)destroy_entry
);
381 METHOD(trap_manager_t
, destroy
, void,
382 private_trap_manager_t
*this)
384 charon
->bus
->remove_listener(charon
->bus
, &this->listener
.listener
);
385 this->traps
->destroy_function(this->traps
, (void*)destroy_entry
);
386 this->lock
->destroy(this->lock
);
393 trap_manager_t
*trap_manager_create(void)
395 private_trap_manager_t
*this;
400 .uninstall
= _uninstall
,
401 .create_enumerator
= _create_enumerator
,
409 .ike_state_change
= _ike_state_change
,
410 .child_state_change
= _child_state_change
,
413 .traps
= linked_list_create(),
414 .lock
= rwlock_create(RWLOCK_TYPE_DEFAULT
),
416 charon
->bus
->add_listener(charon
->bus
, &this->listener
.listener
);
418 return &this->public;