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