Moved data structures to new collections subfolder
[strongswan.git] / src / libcharon / sa / trap_manager.c
1 /*
2 * Copyright (C) 2011-2012 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 /**
71 * A installed trap entry
72 */
73 typedef struct {
74 /** ref to peer_cfg to initiate */
75 peer_cfg_t *peer_cfg;
76 /** ref to instanciated CHILD_SA */
77 child_sa_t *child_sa;
78 /** TRUE if an acquire is pending */
79 bool pending;
80 /** pending IKE_SA connecting upon acquire */
81 ike_sa_t *ike_sa;
82 } entry_t;
83
84 /**
85 * actually uninstall and destroy an installed entry
86 */
87 static void destroy_entry(entry_t *entry)
88 {
89 entry->child_sa->destroy(entry->child_sa);
90 entry->peer_cfg->destroy(entry->peer_cfg);
91 free(entry);
92 }
93
94 METHOD(trap_manager_t, install, u_int32_t,
95 private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child)
96 {
97 entry_t *entry, *found = NULL;
98 ike_cfg_t *ike_cfg;
99 child_sa_t *child_sa;
100 host_t *me, *other;
101 linked_list_t *my_ts, *other_ts, *list;
102 enumerator_t *enumerator;
103 status_t status;
104 u_int32_t reqid = 0;
105
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))
111 {
112 DBG1(DBG_CFG, "installing trap failed, remote address unknown");
113 return 0;
114 }
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))
118 {
119 DESTROY_IF(me);
120 me = hydra->kernel_interface->get_source_addr(
121 hydra->kernel_interface, other, NULL);
122 if (!me)
123 {
124 DBG1(DBG_CFG, "installing trap failed, local address unknown");
125 other->destroy(other);
126 return 0;
127 }
128 me->set_port(me, ike_cfg->get_my_port(ike_cfg));
129 }
130
131 this->lock->write_lock(this->lock);
132 enumerator = this->traps->create_enumerator(this->traps);
133 while (enumerator->enumerate(enumerator, &entry))
134 {
135 if (streq(entry->child_sa->get_name(entry->child_sa),
136 child->get_name(child)))
137 {
138 this->traps->remove_at(this->traps, enumerator);
139 found = entry;
140 break;
141 }
142 }
143 enumerator->destroy(enumerator);
144 if (found)
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);
149 }
150
151 /* create and route CHILD_SA */
152 child_sa = child_sa_create(me, other, child, reqid, FALSE);
153
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));
157
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));
161
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)
171 {
172 DBG1(DBG_CFG, "installing trap failed");
173 child_sa->destroy(child_sa);
174 reqid = 0;
175 }
176 else
177 {
178 INIT(entry,
179 .child_sa = child_sa,
180 .peer_cfg = peer->get_ref(peer),
181 );
182 this->traps->insert_last(this->traps, entry);
183 reqid = child_sa->get_reqid(child_sa);
184 }
185 this->lock->unlock(this->lock);
186
187 if (found)
188 {
189 destroy_entry(found);
190 }
191 return reqid;
192 }
193
194 METHOD(trap_manager_t, uninstall, bool,
195 private_trap_manager_t *this, u_int32_t reqid)
196 {
197 enumerator_t *enumerator;
198 entry_t *entry, *found = NULL;
199
200 this->lock->write_lock(this->lock);
201 enumerator = this->traps->create_enumerator(this->traps);
202 while (enumerator->enumerate(enumerator, &entry))
203 {
204 if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
205 {
206 this->traps->remove_at(this->traps, enumerator);
207 found = entry;
208 break;
209 }
210 }
211 enumerator->destroy(enumerator);
212 this->lock->unlock(this->lock);
213
214 if (!found)
215 {
216 DBG1(DBG_CFG, "trap %d not found to uninstall", reqid);
217 return FALSE;
218 }
219
220 destroy_entry(found);
221 return TRUE;
222 }
223
224 /**
225 * convert enumerated entries to peer_cfg, child_sa
226 */
227 static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg,
228 void *none, child_sa_t **child_sa)
229 {
230 if (peer_cfg)
231 {
232 *peer_cfg = (*entry)->peer_cfg;
233 }
234 if (child_sa)
235 {
236 *child_sa = (*entry)->child_sa;
237 }
238 return TRUE;
239 }
240
241 METHOD(trap_manager_t, create_enumerator, enumerator_t*,
242 private_trap_manager_t *this)
243 {
244 this->lock->read_lock(this->lock);
245 return enumerator_create_filter(this->traps->create_enumerator(this->traps),
246 (void*)trap_filter, this->lock,
247 (void*)this->lock->unlock);
248 }
249
250 METHOD(trap_manager_t, acquire, void,
251 private_trap_manager_t *this, u_int32_t reqid,
252 traffic_selector_t *src, traffic_selector_t *dst)
253 {
254 enumerator_t *enumerator;
255 entry_t *entry, *found = NULL;
256 peer_cfg_t *peer;
257 child_cfg_t *child;
258 ike_sa_t *ike_sa;
259
260 this->lock->read_lock(this->lock);
261 enumerator = this->traps->create_enumerator(this->traps);
262 while (enumerator->enumerate(enumerator, &entry))
263 {
264 if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
265 {
266 found = entry;
267 break;
268 }
269 }
270 enumerator->destroy(enumerator);
271
272 if (!found)
273 {
274 DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
275 this->lock->unlock(this->lock);
276 return;
277 }
278 if (!cas_bool(&found->pending, FALSE, TRUE))
279 {
280 DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
281 this->lock->unlock(this->lock);
282 return;
283 }
284 peer = found->peer_cfg->get_ref(found->peer_cfg);
285 child = found->child_sa->get_config(found->child_sa);
286 child = child->get_ref(child);
287 reqid = found->child_sa->get_reqid(found->child_sa);
288 /* don't hold the lock while checking out the IKE_SA */
289 this->lock->unlock(this->lock);
290
291 ike_sa = charon->ike_sa_manager->checkout_by_config(
292 charon->ike_sa_manager, peer);
293 if (ike_sa)
294 {
295 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
296 {
297 ike_sa->set_peer_cfg(ike_sa, peer);
298 }
299 if (ike_sa->get_version(ike_sa) == IKEV1)
300 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
301 * have a single TS that we can establish in a Quick Mode. */
302 src = dst = NULL;
303 }
304 if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
305 {
306 /* make sure the entry is still there */
307 this->lock->read_lock(this->lock);
308 if (this->traps->find_first(this->traps, NULL,
309 (void**)&found) == SUCCESS)
310 {
311 found->ike_sa = ike_sa;
312 }
313 this->lock->unlock(this->lock);
314 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
315 }
316 else
317 {
318 charon->ike_sa_manager->checkin_and_destroy(
319 charon->ike_sa_manager, ike_sa);
320 }
321 }
322 peer->destroy(peer);
323 }
324
325 /**
326 * Complete the acquire, if successful or failed
327 */
328 static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
329 child_sa_t *child_sa)
330 {
331 enumerator_t *enumerator;
332 entry_t *entry;
333
334 this->lock->read_lock(this->lock);
335 enumerator = this->traps->create_enumerator(this->traps);
336 while (enumerator->enumerate(enumerator, &entry))
337 {
338 if (entry->ike_sa != ike_sa)
339 {
340 continue;
341 }
342 if (child_sa && child_sa->get_reqid(child_sa) !=
343 entry->child_sa->get_reqid(entry->child_sa))
344 {
345 continue;
346 }
347 entry->ike_sa = NULL;
348 entry->pending = FALSE;
349 }
350 enumerator->destroy(enumerator);
351 this->lock->unlock(this->lock);
352 }
353
354 METHOD(listener_t, ike_state_change, bool,
355 trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state)
356 {
357 switch (state)
358 {
359 case IKE_DESTROYING:
360 complete(listener->traps, ike_sa, NULL);
361 return TRUE;
362 default:
363 return TRUE;
364 }
365 }
366
367 METHOD(listener_t, child_state_change, bool,
368 trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa,
369 child_sa_state_t state)
370 {
371 switch (state)
372 {
373 case CHILD_INSTALLED:
374 case CHILD_DESTROYING:
375 complete(listener->traps, ike_sa, child_sa);
376 return TRUE;
377 default:
378 return TRUE;
379 }
380 }
381
382 METHOD(trap_manager_t, flush, void,
383 private_trap_manager_t *this)
384 {
385 linked_list_t *traps;
386 /* since destroying the CHILD_SA results in events which require a read
387 * lock we cannot destroy the list while holding the write lock */
388 this->lock->write_lock(this->lock);
389 traps = this->traps;
390 this->traps = linked_list_create();
391 this->lock->unlock(this->lock);
392 traps->destroy_function(traps, (void*)destroy_entry);
393 }
394
395 METHOD(trap_manager_t, destroy, void,
396 private_trap_manager_t *this)
397 {
398 charon->bus->remove_listener(charon->bus, &this->listener.listener);
399 this->traps->destroy_function(this->traps, (void*)destroy_entry);
400 this->lock->destroy(this->lock);
401 free(this);
402 }
403
404 /**
405 * See header
406 */
407 trap_manager_t *trap_manager_create(void)
408 {
409 private_trap_manager_t *this;
410
411 INIT(this,
412 .public = {
413 .install = _install,
414 .uninstall = _uninstall,
415 .create_enumerator = _create_enumerator,
416 .acquire = _acquire,
417 .flush = _flush,
418 .destroy = _destroy,
419 },
420 .listener = {
421 .traps = this,
422 .listener = {
423 .ike_state_change = _ike_state_change,
424 .child_state_change = _child_state_change,
425 },
426 },
427 .traps = linked_list_create(),
428 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
429 );
430 charon->bus->add_listener(charon->bus, &this->listener.listener);
431
432 return &this->public;
433 }
434