Handle initiation of not supported IKE versions properly
[strongswan.git] / src / libcharon / sa / trap_manager.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "trap_manager.h"
17
18 #include <hydra.h>
19 #include <daemon.h>
20 #include <threading/rwlock.h>
21 #include <utils/linked_list.h>
22
23
24 typedef struct private_trap_manager_t private_trap_manager_t;
25 typedef struct trap_listener_t trap_listener_t;
26
27 /**
28 * listener to track acquires
29 */
30 struct trap_listener_t {
31
32 /**
33 * Implements listener interface
34 */
35 listener_t listener;
36
37 /**
38 * points to trap_manager
39 */
40 private_trap_manager_t *traps;
41 };
42
43 /**
44 * Private data of an trap_manager_t object.
45 */
46 struct private_trap_manager_t {
47
48 /**
49 * Public trap_manager_t interface.
50 */
51 trap_manager_t public;
52
53 /**
54 * Installed traps, as entry_t
55 */
56 linked_list_t *traps;
57
58 /**
59 * read write lock for traps list
60 */
61 rwlock_t *lock;
62
63 /**
64 * listener to track acquiring IKE_SAs
65 */
66 trap_listener_t listener;
67 };
68
69 /**
70 * A installed trap entry
71 */
72 typedef struct {
73 /** ref to peer_cfg to initiate */
74 peer_cfg_t *peer_cfg;
75 /** ref to instanciated CHILD_SA */
76 child_sa_t *child_sa;
77 /** pending IKE_SA connecting upon acquire */
78 ike_sa_t *pending;
79 } entry_t;
80
81 /**
82 * actually uninstall and destroy an installed entry
83 */
84 static void destroy_entry(entry_t *entry)
85 {
86 entry->child_sa->destroy(entry->child_sa);
87 entry->peer_cfg->destroy(entry->peer_cfg);
88 free(entry);
89 }
90
91 METHOD(trap_manager_t, install, u_int32_t,
92 private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child)
93 {
94 entry_t *entry;
95 ike_cfg_t *ike_cfg;
96 child_sa_t *child_sa;
97 host_t *me, *other;
98 linked_list_t *my_ts, *other_ts;
99 enumerator_t *enumerator;
100 bool found = FALSE;
101 status_t status;
102 u_int32_t reqid;
103
104 /* check if not already done */
105 this->lock->read_lock(this->lock);
106 enumerator = this->traps->create_enumerator(this->traps);
107 while (enumerator->enumerate(enumerator, &entry))
108 {
109 if (streq(entry->child_sa->get_name(entry->child_sa),
110 child->get_name(child)))
111 {
112 found = TRUE;
113 break;
114 }
115 }
116 enumerator->destroy(enumerator);
117 this->lock->unlock(this->lock);
118 if (found)
119 {
120 DBG1(DBG_CFG, "CHILD_SA named '%s' already routed",
121 child->get_name(child));
122 return 0;
123 }
124
125 /* try to resolve addresses */
126 ike_cfg = peer->get_ike_cfg(peer);
127 other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg),
128 0, ike_cfg->get_other_port(ike_cfg));
129 if (!other || other->is_anyaddr(other))
130 {
131 DBG1(DBG_CFG, "installing trap failed, remote address unknown");
132 return 0;
133 }
134 me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg),
135 other->get_family(other), ike_cfg->get_my_port(ike_cfg));
136 if (!me || me->is_anyaddr(me))
137 {
138 DESTROY_IF(me);
139 me = hydra->kernel_interface->get_source_addr(
140 hydra->kernel_interface, other, NULL);
141 if (!me)
142 {
143 DBG1(DBG_CFG, "installing trap failed, local address unknown");
144 other->destroy(other);
145 return 0;
146 }
147 me->set_port(me, ike_cfg->get_my_port(ike_cfg));
148 }
149
150 /* create and route CHILD_SA */
151 child_sa = child_sa_create(me, other, child, 0, FALSE);
152 my_ts = child->get_traffic_selectors(child, TRUE, NULL, me);
153 other_ts = child->get_traffic_selectors(child, FALSE, NULL, other);
154 me->destroy(me);
155 other->destroy(other);
156
157 /* while we don't know the finally negotiated protocol (ESP|AH), we
158 * could iterate all proposals for a best guess (TODO). But as we
159 * support ESP only for now, we set it here. */
160 child_sa->set_protocol(child_sa, PROTO_ESP);
161 child_sa->set_mode(child_sa, child->get_mode(child));
162 status = child_sa->add_policies(child_sa, my_ts, other_ts);
163 my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
164 other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
165 if (status != SUCCESS)
166 {
167 child_sa->destroy(child_sa);
168 DBG1(DBG_CFG, "installing trap failed");
169 return 0;
170 }
171
172 reqid = child_sa->get_reqid(child_sa);
173 entry = malloc_thing(entry_t);
174 entry->child_sa = child_sa;
175 entry->peer_cfg = peer->get_ref(peer);
176 entry->pending = NULL;
177
178 this->lock->write_lock(this->lock);
179 this->traps->insert_last(this->traps, entry);
180 this->lock->unlock(this->lock);
181
182 return reqid;
183 }
184
185 METHOD(trap_manager_t, uninstall, bool,
186 private_trap_manager_t *this, u_int32_t reqid)
187 {
188 enumerator_t *enumerator;
189 entry_t *entry, *found = NULL;
190
191 this->lock->write_lock(this->lock);
192 enumerator = this->traps->create_enumerator(this->traps);
193 while (enumerator->enumerate(enumerator, &entry))
194 {
195 if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
196 {
197 this->traps->remove_at(this->traps, enumerator);
198 found = entry;
199 break;
200 }
201 }
202 enumerator->destroy(enumerator);
203 this->lock->unlock(this->lock);
204
205 if (!found)
206 {
207 DBG1(DBG_CFG, "trap %d not found to uninstall", reqid);
208 return FALSE;
209 }
210
211 destroy_entry(found);
212 return TRUE;
213 }
214
215 /**
216 * convert enumerated entries to peer_cfg, child_sa
217 */
218 static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg,
219 void *none, child_sa_t **child_sa)
220 {
221 if (peer_cfg)
222 {
223 *peer_cfg = (*entry)->peer_cfg;
224 }
225 if (child_sa)
226 {
227 *child_sa = (*entry)->child_sa;
228 }
229 return TRUE;
230 }
231
232 METHOD(trap_manager_t, create_enumerator, enumerator_t*,
233 private_trap_manager_t *this)
234 {
235 this->lock->read_lock(this->lock);
236 return enumerator_create_filter(this->traps->create_enumerator(this->traps),
237 (void*)trap_filter, this->lock,
238 (void*)this->lock->unlock);
239 }
240
241 METHOD(trap_manager_t, acquire, void,
242 private_trap_manager_t *this, u_int32_t reqid,
243 traffic_selector_t *src, traffic_selector_t *dst)
244 {
245 enumerator_t *enumerator;
246 entry_t *entry, *found = NULL;
247 peer_cfg_t *peer;
248 child_cfg_t *child;
249 ike_sa_t *ike_sa;
250
251 this->lock->read_lock(this->lock);
252 enumerator = this->traps->create_enumerator(this->traps);
253 while (enumerator->enumerate(enumerator, &entry))
254 {
255 if (entry->child_sa->get_reqid(entry->child_sa) == reqid)
256 {
257 found = entry;
258 break;
259 }
260 }
261 enumerator->destroy(enumerator);
262
263 if (!found)
264 {
265 DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
266 }
267 else if (found->pending)
268 {
269 DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
270 }
271 else
272 {
273 child = found->child_sa->get_config(found->child_sa);
274 peer = found->peer_cfg;
275 ike_sa = charon->ike_sa_manager->checkout_by_config(
276 charon->ike_sa_manager, peer);
277 if (ike_sa)
278 {
279 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
280 {
281 ike_sa->set_peer_cfg(ike_sa, peer);
282 }
283 child->get_ref(child);
284 reqid = found->child_sa->get_reqid(found->child_sa);
285 if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
286 {
287 found->pending = ike_sa;
288 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
289 }
290 else
291 {
292 charon->ike_sa_manager->checkin_and_destroy(
293 charon->ike_sa_manager, ike_sa);
294 }
295 }
296 }
297 this->lock->unlock(this->lock);
298 }
299
300 /**
301 * Complete the acquire, if successful or failed
302 */
303 static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
304 child_sa_t *child_sa)
305 {
306 enumerator_t *enumerator;
307 entry_t *entry;
308
309 this->lock->read_lock(this->lock);
310 enumerator = this->traps->create_enumerator(this->traps);
311 while (enumerator->enumerate(enumerator, &entry))
312 {
313 if (entry->pending != ike_sa)
314 {
315 continue;
316 }
317 if (child_sa && child_sa->get_reqid(child_sa) !=
318 entry->child_sa->get_reqid(entry->child_sa))
319 {
320 continue;
321 }
322 entry->pending = NULL;
323 }
324 enumerator->destroy(enumerator);
325 this->lock->unlock(this->lock);
326 }
327
328 METHOD(listener_t, ike_state_change, bool,
329 trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state)
330 {
331 switch (state)
332 {
333 case IKE_DESTROYING:
334 complete(listener->traps, ike_sa, NULL);
335 return TRUE;
336 default:
337 return TRUE;
338 }
339 }
340
341 METHOD(listener_t, child_state_change, bool,
342 trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa,
343 child_sa_state_t state)
344 {
345 switch (state)
346 {
347 case CHILD_INSTALLED:
348 case CHILD_DESTROYING:
349 complete(listener->traps, ike_sa, child_sa);
350 return TRUE;
351 default:
352 return TRUE;
353 }
354 }
355
356 METHOD(trap_manager_t, flush, void,
357 private_trap_manager_t *this)
358 {
359 this->traps->invoke_function(this->traps, (void*)destroy_entry);
360 }
361
362 METHOD(trap_manager_t, destroy, void,
363 private_trap_manager_t *this)
364 {
365 charon->bus->remove_listener(charon->bus, &this->listener.listener);
366 this->traps->invoke_function(this->traps, (void*)destroy_entry);
367 this->traps->destroy(this->traps);
368 this->lock->destroy(this->lock);
369 free(this);
370 }
371
372 /**
373 * See header
374 */
375 trap_manager_t *trap_manager_create(void)
376 {
377 private_trap_manager_t *this;
378
379 INIT(this,
380 .public = {
381 .install = _install,
382 .uninstall = _uninstall,
383 .create_enumerator = _create_enumerator,
384 .acquire = _acquire,
385 .flush = _flush,
386 .destroy = _destroy,
387 },
388 .listener = {
389 .traps = this,
390 .listener = {
391 .ike_state_change = _ike_state_change,
392 .child_state_change = _child_state_change,
393 },
394 },
395 .traps = linked_list_create(),
396 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
397 );
398 charon->bus->add_listener(charon->bus, &this->listener.listener);
399
400 return &this->public;
401 }
402