]>
Commit | Line | Data |
---|---|---|
eb8ed130 | 1 | /* |
ca213e19 | 2 | * Copyright (C) 2011-2017 Tobias Brunner |
eb8ed130 | 3 | * Copyright (C) 2009 Martin Willi |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
eb8ed130 MW |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
eb8ed130 MW |
16 | */ |
17 | ||
18 | #include "trap_manager.h" | |
19 | ||
20 | #include <daemon.h> | |
a229bdce | 21 | #include <threading/mutex.h> |
eba64cef | 22 | #include <threading/rwlock.h> |
12b3cdba | 23 | #include <threading/rwlock_condvar.h> |
12642a68 | 24 | #include <collections/linked_list.h> |
eb8ed130 | 25 | |
12b3cdba TB |
26 | #define INSTALL_DISABLED ((u_int)~0) |
27 | ||
eb8ed130 | 28 | typedef struct private_trap_manager_t private_trap_manager_t; |
9d737ecf MW |
29 | typedef struct trap_listener_t trap_listener_t; |
30 | ||
31 | /** | |
32 | * listener to track acquires | |
33 | */ | |
34 | struct trap_listener_t { | |
7daf5226 | 35 | |
9d737ecf MW |
36 | /** |
37 | * Implements listener interface | |
38 | */ | |
39 | listener_t listener; | |
7daf5226 | 40 | |
9d737ecf MW |
41 | /** |
42 | * points to trap_manager | |
43 | */ | |
44 | private_trap_manager_t *traps; | |
45 | }; | |
eb8ed130 MW |
46 | |
47 | /** | |
48 | * Private data of an trap_manager_t object. | |
49 | */ | |
50 | struct private_trap_manager_t { | |
7daf5226 | 51 | |
eb8ed130 MW |
52 | /** |
53 | * Public trap_manager_t interface. | |
54 | */ | |
55 | trap_manager_t public; | |
7daf5226 | 56 | |
eb8ed130 MW |
57 | /** |
58 | * Installed traps, as entry_t | |
59 | */ | |
60 | linked_list_t *traps; | |
7daf5226 | 61 | |
eb8ed130 | 62 | /** |
fcb06fdb | 63 | * read write lock for traps list |
eb8ed130 | 64 | */ |
fcb06fdb | 65 | rwlock_t *lock; |
7daf5226 | 66 | |
9d737ecf MW |
67 | /** |
68 | * listener to track acquiring IKE_SAs | |
69 | */ | |
70 | trap_listener_t listener; | |
7fa03b30 | 71 | |
a229bdce TB |
72 | /** |
73 | * list of acquires we currently handle | |
74 | */ | |
75 | linked_list_t *acquires; | |
76 | ||
77 | /** | |
78 | * mutex for list of acquires | |
79 | */ | |
80 | mutex_t *mutex; | |
81 | ||
12b3cdba TB |
82 | /** |
83 | * number of threads currently installing trap policies, or INSTALL_DISABLED | |
84 | */ | |
85 | u_int installing; | |
86 | ||
87 | /** | |
88 | * condvar to signal trap policy installation | |
89 | */ | |
90 | rwlock_condvar_t *condvar; | |
91 | ||
7fa03b30 TB |
92 | /** |
93 | * Whether to ignore traffic selectors from acquires | |
94 | */ | |
95 | bool ignore_acquire_ts; | |
eb8ed130 MW |
96 | }; |
97 | ||
98 | /** | |
99 | * A installed trap entry | |
100 | */ | |
101 | typedef struct { | |
bb492d80 TB |
102 | /** name of the trapped CHILD_SA */ |
103 | char *name; | |
eb8ed130 MW |
104 | /** ref to peer_cfg to initiate */ |
105 | peer_cfg_t *peer_cfg; | |
a229bdce | 106 | /** ref to instantiated CHILD_SA (i.e the trap policy) */ |
eb8ed130 | 107 | child_sa_t *child_sa; |
301a0bad TB |
108 | /** TRUE in case of wildcard Transport Mode SA */ |
109 | bool wildcard; | |
3c65cf64 TB |
110 | /** TRUE for CHILD_SAs that are externally managed */ |
111 | bool external; | |
a229bdce TB |
112 | } entry_t; |
113 | ||
114 | /** | |
115 | * A handled acquire | |
116 | */ | |
117 | typedef struct { | |
9d737ecf | 118 | /** pending IKE_SA connecting upon acquire */ |
fc726f13 | 119 | ike_sa_t *ike_sa; |
a229bdce | 120 | /** reqid of pending trap policy */ |
b12c53ce | 121 | uint32_t reqid; |
301a0bad TB |
122 | /** destination address (wildcard case) */ |
123 | host_t *dst; | |
0a673794 TB |
124 | /** security label, if any */ |
125 | sec_label_t *label; | |
a229bdce | 126 | } acquire_t; |
eb8ed130 MW |
127 | |
128 | /** | |
129 | * actually uninstall and destroy an installed entry | |
130 | */ | |
a229bdce TB |
131 | static void destroy_entry(entry_t *this) |
132 | { | |
3c65cf64 TB |
133 | if (!this->external) |
134 | { | |
135 | this->child_sa->destroy(this->child_sa); | |
136 | } | |
a229bdce TB |
137 | this->peer_cfg->destroy(this->peer_cfg); |
138 | free(this->name); | |
139 | free(this); | |
140 | } | |
141 | ||
142 | /** | |
143 | * destroy a cached acquire entry | |
144 | */ | |
145 | static void destroy_acquire(acquire_t *this) | |
eb8ed130 | 146 | { |
301a0bad | 147 | DESTROY_IF(this->dst); |
0a673794 | 148 | DESTROY_IF(this->label); |
a229bdce TB |
149 | free(this); |
150 | } | |
151 | ||
2e4d110d TB |
152 | CALLBACK(acquire_by_reqid, bool, |
153 | acquire_t *this, va_list args) | |
a229bdce | 154 | { |
2e4d110d | 155 | uint32_t reqid; |
0a673794 | 156 | sec_label_t *label; |
2e4d110d | 157 | |
0a673794 TB |
158 | VA_ARGS_VGET(args, reqid, label); |
159 | return this->reqid == reqid && sec_labels_equal(this->label, label); | |
eb8ed130 MW |
160 | } |
161 | ||
2e4d110d TB |
162 | CALLBACK(acquire_by_dst, bool, |
163 | acquire_t *this, va_list args) | |
301a0bad | 164 | { |
2e4d110d TB |
165 | host_t *dst; |
166 | ||
167 | VA_ARGS_VGET(args, dst); | |
301a0bad TB |
168 | return this->dst && this->dst->ip_equals(this->dst, dst); |
169 | } | |
170 | ||
1a822642 TB |
171 | /** |
172 | * Check if any remote TS are dynamic | |
173 | */ | |
174 | static bool dynamic_remote_ts(child_cfg_t *child) | |
175 | { | |
176 | enumerator_t *enumerator; | |
177 | linked_list_t *other_ts; | |
178 | traffic_selector_t *ts; | |
179 | bool found = FALSE; | |
180 | ||
84cdfbc9 | 181 | other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL, FALSE); |
1a822642 TB |
182 | enumerator = other_ts->create_enumerator(other_ts); |
183 | while (enumerator->enumerate(enumerator, &ts)) | |
184 | { | |
185 | if (ts->is_dynamic(ts)) | |
186 | { | |
187 | found = TRUE; | |
188 | break; | |
189 | } | |
190 | } | |
191 | enumerator->destroy(enumerator); | |
192 | other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); | |
193 | return found; | |
194 | } | |
195 | ||
3c65cf64 TB |
196 | /** |
197 | * Install the given trap | |
198 | */ | |
199 | static status_t install_trap(child_sa_t *child_sa, linked_list_t *local, | |
200 | linked_list_t *remote) | |
201 | { | |
202 | linked_list_t *my_ts, *other_ts, *proposals; | |
203 | proposal_t *proposal; | |
204 | child_cfg_t *child; | |
205 | protocol_id_t proto = PROTO_ESP; | |
206 | ||
207 | child = child_sa->get_config(child_sa); | |
208 | ||
209 | my_ts = child->get_traffic_selectors(child, TRUE, NULL, local, FALSE); | |
210 | other_ts = child->get_traffic_selectors(child, FALSE, NULL, remote, FALSE); | |
211 | ||
212 | /* we don't know the finally negotiated protocol (ESP|AH), we install | |
213 | * the SA with the protocol of the first proposal */ | |
214 | proposals = child->get_proposals(child, TRUE); | |
215 | if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS) | |
216 | { | |
217 | proto = proposal->get_protocol(proposal); | |
218 | } | |
219 | proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); | |
220 | ||
221 | child_sa->set_protocol(child_sa, proto); | |
222 | child_sa->set_mode(child_sa, child->get_mode(child)); | |
223 | ||
224 | child_sa->set_policies(child_sa, my_ts, other_ts); | |
225 | my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); | |
226 | other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); | |
227 | ||
228 | return child_sa->install_policies(child_sa); | |
229 | } | |
230 | ||
24fa1bb0 TB |
231 | METHOD(trap_manager_t, install, bool, |
232 | private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) | |
eb8ed130 | 233 | { |
21037942 | 234 | entry_t *entry, *found = NULL; |
eb8ed130 MW |
235 | ike_cfg_t *ike_cfg; |
236 | child_sa_t *child_sa; | |
237 | host_t *me, *other; | |
3c65cf64 | 238 | linked_list_t *local, *remote; |
eb8ed130 | 239 | enumerator_t *enumerator; |
eb8ed130 | 240 | status_t status; |
24fa1bb0 | 241 | bool result = FALSE, wildcard = FALSE; |
7daf5226 | 242 | |
eb8ed130 MW |
243 | /* try to resolve addresses */ |
244 | ike_cfg = peer->get_ike_cfg(peer); | |
7446fa28 | 245 | other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC); |
301a0bad TB |
246 | if (other && other->is_anyaddr(other) && |
247 | child->get_mode(child) == MODE_TRANSPORT) | |
248 | { | |
249 | /* allow wildcard for Transport Mode SAs */ | |
250 | me = host_create_any(other->get_family(other)); | |
251 | wildcard = TRUE; | |
252 | } | |
1a822642 | 253 | else if (other && other->is_anyaddr(other)) |
eb8ed130 | 254 | { |
1a822642 | 255 | other->destroy(other); |
eb8ed130 | 256 | DBG1(DBG_CFG, "installing trap failed, remote address unknown"); |
24fa1bb0 | 257 | return FALSE; |
eb8ed130 | 258 | } |
301a0bad | 259 | else |
1a822642 TB |
260 | { /* depending on the traffic selectors we don't really need a remote |
261 | * host yet, but we might fail later if no IP can be resolved */ | |
262 | if (!other && dynamic_remote_ts(child)) | |
263 | { /* with dynamic TS we do need a host, otherwise 0.0.0.0/0 is used, | |
264 | * which is probably not what users expect*/ | |
265 | DBG1(DBG_CFG, "installing trap failed, remote address unknown with " | |
266 | "dynamic traffic selector"); | |
24fa1bb0 | 267 | return FALSE; |
1a822642 TB |
268 | } |
269 | me = ike_cfg->resolve_me(ike_cfg, other ? other->get_family(other) | |
270 | : AF_UNSPEC); | |
271 | if (!other) | |
272 | { | |
273 | other = host_create_any(me ? me->get_family(me) : AF_INET); | |
274 | } | |
275 | other->set_port(other, ike_cfg->get_other_port(ike_cfg)); | |
276 | if ((!me || me->is_anyaddr(me)) && !other->is_anyaddr(other)) | |
eb8ed130 | 277 | { |
301a0bad | 278 | DESTROY_IF(me); |
8394ea2a | 279 | me = charon->kernel->get_source_addr(charon->kernel, other, NULL); |
eb8ed130 | 280 | } |
1a822642 TB |
281 | if (!me) |
282 | { | |
283 | me = host_create_any(other->get_family(other)); | |
284 | } | |
285 | me->set_port(me, ike_cfg->get_my_port(ike_cfg)); | |
eb8ed130 | 286 | } |
7daf5226 | 287 | |
21037942 | 288 | this->lock->write_lock(this->lock); |
12b3cdba TB |
289 | if (this->installing == INSTALL_DISABLED) |
290 | { /* flush() has been called */ | |
291 | this->lock->unlock(this->lock); | |
e8f2c13f | 292 | other->destroy(other); |
03024f4c | 293 | me->destroy(me); |
24fa1bb0 | 294 | return FALSE; |
12b3cdba | 295 | } |
21037942 TB |
296 | enumerator = this->traps->create_enumerator(this->traps); |
297 | while (enumerator->enumerate(enumerator, &entry)) | |
298 | { | |
3c65cf64 TB |
299 | if (!entry->external && |
300 | streq(entry->name, child->get_name(child)) && | |
f42dd430 TB |
301 | streq(entry->peer_cfg->get_name(entry->peer_cfg), |
302 | peer->get_name(peer))) | |
21037942 | 303 | { |
21037942 | 304 | found = entry; |
bb492d80 | 305 | if (entry->child_sa) |
3c65cf64 | 306 | { /* replace it with an updated version if already installed */ |
bb492d80 TB |
307 | this->traps->remove_at(this->traps, enumerator); |
308 | } | |
21037942 TB |
309 | break; |
310 | } | |
311 | } | |
312 | enumerator->destroy(enumerator); | |
2dcfc698 | 313 | |
21037942 | 314 | if (found) |
bb492d80 TB |
315 | { |
316 | if (!found->child_sa) | |
317 | { | |
318 | DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name); | |
319 | this->lock->unlock(this->lock); | |
e8f2c13f | 320 | other->destroy(other); |
03024f4c | 321 | me->destroy(me); |
24fa1bb0 | 322 | return FALSE; |
bb492d80 TB |
323 | } |
324 | /* config might have changed so update everything */ | |
325 | DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name); | |
21037942 TB |
326 | } |
327 | ||
bb492d80 TB |
328 | INIT(entry, |
329 | .name = strdup(child->get_name(child)), | |
330 | .peer_cfg = peer->get_ref(peer), | |
301a0bad | 331 | .wildcard = wildcard, |
bb492d80 TB |
332 | ); |
333 | this->traps->insert_first(this->traps, entry); | |
12b3cdba | 334 | this->installing++; |
bb492d80 TB |
335 | /* don't hold lock while creating CHILD_SA and installing policies */ |
336 | this->lock->unlock(this->lock); | |
337 | ||
eb8ed130 | 338 | /* create and route CHILD_SA */ |
fafa7698 TB |
339 | child_sa_create_t child_data = { |
340 | /* TODO: no reason to allocate unique interface IDs, there is currently | |
341 | * no event to use them upon trap installation and we'd also have to | |
342 | * pass them in a later initiate() call */ | |
343 | .if_id_in_def = peer->get_if_id(peer, TRUE), | |
344 | .if_id_out_def = peer->get_if_id(peer, FALSE), | |
345 | }; | |
346 | child_sa = child_sa_create(me, other, child, &child_data); | |
7ee37114 | 347 | |
3c65cf64 TB |
348 | local = linked_list_create_with_items(me, NULL); |
349 | remote = linked_list_create_with_items(other, NULL); | |
7ee37114 | 350 | |
3c65cf64 | 351 | status = install_trap(child_sa, local, remote); |
7daf5226 | 352 | |
3c65cf64 TB |
353 | local->destroy_offset(local, offsetof(host_t, destroy)); |
354 | remote->destroy_offset(remote, offsetof(host_t, destroy)); | |
eb8ed130 MW |
355 | if (status != SUCCESS) |
356 | { | |
eb8ed130 | 357 | DBG1(DBG_CFG, "installing trap failed"); |
bb492d80 TB |
358 | this->lock->write_lock(this->lock); |
359 | this->traps->remove(this->traps, entry, NULL); | |
360 | this->lock->unlock(this->lock); | |
361 | entry->child_sa = child_sa; | |
362 | destroy_entry(entry); | |
21037942 TB |
363 | } |
364 | else | |
365 | { | |
bb492d80 TB |
366 | this->lock->write_lock(this->lock); |
367 | entry->child_sa = child_sa; | |
368 | this->lock->unlock(this->lock); | |
24fa1bb0 | 369 | result = TRUE; |
4eb09d14 | 370 | } |
21037942 TB |
371 | if (found) |
372 | { | |
373 | destroy_entry(found); | |
374 | } | |
12b3cdba TB |
375 | this->lock->write_lock(this->lock); |
376 | /* do this at the end, so entries created temporarily are also destroyed */ | |
377 | this->installing--; | |
378 | this->condvar->signal(this->condvar); | |
379 | this->lock->unlock(this->lock); | |
24fa1bb0 | 380 | return result; |
eb8ed130 MW |
381 | } |
382 | ||
06356a29 | 383 | METHOD(trap_manager_t, uninstall, bool, |
ca213e19 | 384 | private_trap_manager_t *this, char *peer, char *child) |
eb8ed130 MW |
385 | { |
386 | enumerator_t *enumerator; | |
387 | entry_t *entry, *found = NULL; | |
7daf5226 | 388 | |
fcb06fdb | 389 | this->lock->write_lock(this->lock); |
69cbe2ca SM |
390 | while (this->installing) |
391 | { | |
392 | this->condvar->wait(this->condvar, this->lock); | |
393 | } | |
eb8ed130 MW |
394 | enumerator = this->traps->create_enumerator(this->traps); |
395 | while (enumerator->enumerate(enumerator, &entry)) | |
396 | { | |
3c65cf64 TB |
397 | if (!entry->external && |
398 | streq(entry->name, child) && | |
399 | (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg)))) | |
400 | { | |
401 | this->traps->remove_at(this->traps, enumerator); | |
402 | found = entry; | |
403 | break; | |
404 | } | |
405 | } | |
406 | enumerator->destroy(enumerator); | |
407 | this->lock->unlock(this->lock); | |
408 | ||
409 | if (!found) | |
410 | { | |
411 | return FALSE; | |
412 | } | |
413 | destroy_entry(found); | |
414 | return TRUE; | |
415 | } | |
416 | ||
417 | METHOD(trap_manager_t, install_external, bool, | |
418 | private_trap_manager_t *this, peer_cfg_t *peer, child_sa_t *child, | |
419 | linked_list_t *local, linked_list_t *remote) | |
420 | { | |
421 | entry_t *entry; | |
422 | ||
423 | this->lock->write_lock(this->lock); | |
424 | if (this->installing == INSTALL_DISABLED) | |
425 | { /* flush() has been called */ | |
426 | this->lock->unlock(this->lock); | |
427 | return FALSE; | |
428 | } | |
429 | ||
430 | INIT(entry, | |
431 | .name = strdup(child->get_name(child)), | |
432 | .peer_cfg = peer->get_ref(peer), | |
433 | .child_sa = child, | |
434 | .external = TRUE, | |
435 | ); | |
436 | this->traps->insert_first(this->traps, entry); | |
437 | this->lock->unlock(this->lock); | |
438 | ||
439 | if (install_trap(child, local, remote) != SUCCESS) | |
440 | { | |
441 | DBG1(DBG_CFG, "installing trap failed"); | |
442 | this->lock->write_lock(this->lock); | |
443 | this->traps->remove(this->traps, entry, NULL); | |
444 | this->lock->unlock(this->lock); | |
445 | destroy_entry(entry); | |
446 | return FALSE; | |
447 | } | |
448 | return TRUE; | |
449 | } | |
450 | ||
451 | METHOD(trap_manager_t, remove_external, bool, | |
452 | private_trap_manager_t *this, child_sa_t *child) | |
453 | { | |
454 | enumerator_t *enumerator; | |
455 | entry_t *entry, *found = NULL; | |
456 | ||
457 | this->lock->write_lock(this->lock); | |
458 | enumerator = this->traps->create_enumerator(this->traps); | |
459 | while (enumerator->enumerate(enumerator, &entry)) | |
460 | { | |
461 | if (entry->external && entry->child_sa == child) | |
eb8ed130 MW |
462 | { |
463 | this->traps->remove_at(this->traps, enumerator); | |
464 | found = entry; | |
465 | break; | |
466 | } | |
467 | } | |
468 | enumerator->destroy(enumerator); | |
fcb06fdb | 469 | this->lock->unlock(this->lock); |
7daf5226 | 470 | |
eb8ed130 MW |
471 | if (!found) |
472 | { | |
eb8ed130 MW |
473 | return FALSE; |
474 | } | |
eb8ed130 MW |
475 | destroy_entry(found); |
476 | return TRUE; | |
477 | } | |
478 | ||
525cc46c TB |
479 | CALLBACK(trap_filter, bool, |
480 | rwlock_t *lock, enumerator_t *orig, va_list args) | |
eb8ed130 | 481 | { |
525cc46c TB |
482 | entry_t *entry; |
483 | peer_cfg_t **peer_cfg; | |
484 | child_sa_t **child_sa; | |
485 | ||
486 | VA_ARGS_VGET(args, peer_cfg, child_sa); | |
487 | ||
488 | while (orig->enumerate(orig, &entry)) | |
eb8ed130 | 489 | { |
3c65cf64 TB |
490 | if (!entry->child_sa || entry->external) |
491 | { /* skip entries that are currently being installed or are managed | |
492 | * externally */ | |
525cc46c TB |
493 | continue; |
494 | } | |
495 | if (peer_cfg) | |
496 | { | |
497 | *peer_cfg = entry->peer_cfg; | |
498 | } | |
499 | if (child_sa) | |
500 | { | |
501 | *child_sa = entry->child_sa; | |
502 | } | |
503 | return TRUE; | |
eb8ed130 | 504 | } |
525cc46c | 505 | return FALSE; |
eb8ed130 MW |
506 | } |
507 | ||
06356a29 AS |
508 | METHOD(trap_manager_t, create_enumerator, enumerator_t*, |
509 | private_trap_manager_t *this) | |
eb8ed130 | 510 | { |
fcb06fdb | 511 | this->lock->read_lock(this->lock); |
eb8ed130 | 512 | return enumerator_create_filter(this->traps->create_enumerator(this->traps), |
525cc46c | 513 | trap_filter, this->lock, |
fcb06fdb | 514 | (void*)this->lock->unlock); |
eb8ed130 MW |
515 | } |
516 | ||
06356a29 | 517 | METHOD(trap_manager_t, acquire, void, |
3b699c72 | 518 | private_trap_manager_t *this, uint32_t reqid, kernel_acquire_data_t *data) |
eb8ed130 MW |
519 | { |
520 | enumerator_t *enumerator; | |
521 | entry_t *entry, *found = NULL; | |
a229bdce | 522 | acquire_t *acquire; |
eb8ed130 MW |
523 | peer_cfg_t *peer; |
524 | child_cfg_t *child; | |
525 | ike_sa_t *ike_sa; | |
301a0bad | 526 | host_t *host; |
04bfe83f | 527 | uint32_t allocated_reqid; |
301a0bad | 528 | bool wildcard, ignore = FALSE; |
7daf5226 | 529 | |
fcb06fdb | 530 | this->lock->read_lock(this->lock); |
eb8ed130 MW |
531 | enumerator = this->traps->create_enumerator(this->traps); |
532 | while (enumerator->enumerate(enumerator, &entry)) | |
533 | { | |
bb492d80 TB |
534 | if (entry->child_sa && |
535 | entry->child_sa->get_reqid(entry->child_sa) == reqid) | |
eb8ed130 MW |
536 | { |
537 | found = entry; | |
538 | break; | |
539 | } | |
540 | } | |
541 | enumerator->destroy(enumerator); | |
7daf5226 | 542 | |
eb8ed130 MW |
543 | if (!found) |
544 | { | |
301a0bad | 545 | DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid); |
fc726f13 TB |
546 | this->lock->unlock(this->lock); |
547 | return; | |
eb8ed130 | 548 | } |
301a0bad | 549 | wildcard = found->wildcard; |
a229bdce TB |
550 | |
551 | this->mutex->lock(this->mutex); | |
301a0bad TB |
552 | if (wildcard) |
553 | { /* for wildcard acquires we check that we don't have a pending acquire | |
554 | * with the same peer */ | |
b12c53ce | 555 | uint8_t mask; |
301a0bad | 556 | |
3b699c72 | 557 | data->dst->to_subnet(data->dst, &host, &mask); |
2e4d110d TB |
558 | if (this->acquires->find_first(this->acquires, acquire_by_dst, |
559 | (void**)&acquire, host)) | |
301a0bad TB |
560 | { |
561 | host->destroy(host); | |
562 | ignore = TRUE; | |
563 | } | |
564 | else | |
565 | { | |
566 | INIT(acquire, | |
567 | .dst = host, | |
568 | .reqid = reqid, | |
569 | ); | |
570 | this->acquires->insert_last(this->acquires, acquire); | |
571 | } | |
9d737ecf | 572 | } |
a229bdce TB |
573 | else |
574 | { | |
2e4d110d | 575 | if (this->acquires->find_first(this->acquires, acquire_by_reqid, |
0a673794 | 576 | (void**)&acquire, reqid, data->label)) |
301a0bad TB |
577 | { |
578 | ignore = TRUE; | |
579 | } | |
580 | else | |
581 | { | |
582 | INIT(acquire, | |
583 | .reqid = reqid, | |
0a673794 | 584 | .label = data->label ? data->label->clone(data->label) : NULL, |
301a0bad TB |
585 | ); |
586 | this->acquires->insert_last(this->acquires, acquire); | |
587 | } | |
a229bdce TB |
588 | } |
589 | this->mutex->unlock(this->mutex); | |
301a0bad TB |
590 | if (ignore) |
591 | { | |
3b699c72 TB |
592 | DBG1(DBG_CFG, "ignoring acquire for reqid %u, connection attempt " |
593 | "pending", reqid); | |
301a0bad TB |
594 | this->lock->unlock(this->lock); |
595 | return; | |
596 | } | |
fc726f13 TB |
597 | peer = found->peer_cfg->get_ref(found->peer_cfg); |
598 | child = found->child_sa->get_config(found->child_sa); | |
599 | child = child->get_ref(child); | |
04bfe83f TB |
600 | /* only pass allocated reqids explicitly, take a reference */ |
601 | allocated_reqid = found->child_sa->get_reqid_ref(found->child_sa); | |
fc726f13 TB |
602 | /* don't hold the lock while checking out the IKE_SA */ |
603 | this->lock->unlock(this->lock); | |
604 | ||
301a0bad TB |
605 | if (wildcard) |
606 | { /* the peer config would match IKE_SAs with other peers */ | |
bde5bd47 | 607 | ike_sa = charon->ike_sa_manager->create_new(charon->ike_sa_manager, |
301a0bad TB |
608 | peer->get_ike_version(peer), TRUE); |
609 | if (ike_sa) | |
610 | { | |
611 | ike_cfg_t *ike_cfg; | |
b12c53ce AS |
612 | uint16_t port; |
613 | uint8_t mask; | |
301a0bad TB |
614 | |
615 | ike_sa->set_peer_cfg(ike_sa, peer); | |
616 | ike_cfg = ike_sa->get_ike_cfg(ike_sa); | |
617 | ||
618 | port = ike_cfg->get_other_port(ike_cfg); | |
3b699c72 | 619 | data->dst->to_subnet(data->dst, &host, &mask); |
301a0bad TB |
620 | host->set_port(host, port); |
621 | ike_sa->set_other_host(ike_sa, host); | |
622 | ||
623 | port = ike_cfg->get_my_port(ike_cfg); | |
3b699c72 | 624 | data->src->to_subnet(data->src, &host, &mask); |
301a0bad TB |
625 | host->set_port(host, port); |
626 | ike_sa->set_my_host(ike_sa, host); | |
627 | ||
628 | charon->bus->set_sa(charon->bus, ike_sa); | |
629 | } | |
630 | } | |
631 | else | |
632 | { | |
633 | ike_sa = charon->ike_sa_manager->checkout_by_config( | |
fc726f13 | 634 | charon->ike_sa_manager, peer); |
301a0bad | 635 | } |
7f6386af TB |
636 | peer->destroy(peer); |
637 | ||
b1f2f05c | 638 | if (ike_sa) |
9d737ecf | 639 | { |
7f30e1ae | 640 | child_init_args_t args = { |
04bfe83f | 641 | .reqid = allocated_reqid, |
3b699c72 TB |
642 | .src = data->src, |
643 | .dst = data->dst, | |
0a673794 | 644 | .label = data->label, |
7f30e1ae TB |
645 | }; |
646 | ||
7fa03b30 | 647 | if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1) |
777bcdc0 MW |
648 | { /* in IKEv1, don't prepend the acquiring packet TS, as we only |
649 | * have a single TS that we can establish in a Quick Mode. */ | |
7f30e1ae | 650 | args.src = args.dst = NULL; |
777bcdc0 | 651 | } |
a229bdce TB |
652 | |
653 | this->mutex->lock(this->mutex); | |
654 | acquire->ike_sa = ike_sa; | |
655 | this->mutex->unlock(this->mutex); | |
656 | ||
7f30e1ae | 657 | if (ike_sa->initiate(ike_sa, child, &args) != DESTROY_ME) |
b1f2f05c | 658 | { |
b1f2f05c MW |
659 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); |
660 | } | |
661 | else | |
662 | { | |
773fcb16 TB |
663 | charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, |
664 | ike_sa); | |
9d737ecf | 665 | } |
fc726f13 | 666 | } |
a229bdce TB |
667 | else |
668 | { | |
669 | this->mutex->lock(this->mutex); | |
670 | this->acquires->remove(this->acquires, acquire, NULL); | |
671 | this->mutex->unlock(this->mutex); | |
672 | destroy_acquire(acquire); | |
673 | child->destroy(child); | |
674 | } | |
04bfe83f TB |
675 | if (allocated_reqid) |
676 | { | |
677 | charon->kernel->release_reqid(charon->kernel, allocated_reqid); | |
678 | } | |
9d737ecf MW |
679 | } |
680 | ||
681 | /** | |
991f7ccd | 682 | * Complete the acquire, if successful or failed |
9d737ecf | 683 | */ |
991f7ccd MW |
684 | static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, |
685 | child_sa_t *child_sa) | |
9d737ecf | 686 | { |
9d737ecf | 687 | enumerator_t *enumerator; |
a229bdce | 688 | acquire_t *acquire; |
7daf5226 | 689 | |
a229bdce TB |
690 | this->mutex->lock(this->mutex); |
691 | enumerator = this->acquires->create_enumerator(this->acquires); | |
692 | while (enumerator->enumerate(enumerator, &acquire)) | |
9d737ecf | 693 | { |
a229bdce | 694 | if (!acquire->ike_sa || acquire->ike_sa != ike_sa) |
991f7ccd MW |
695 | { |
696 | continue; | |
697 | } | |
301a0bad | 698 | if (child_sa) |
9d737ecf | 699 | { |
301a0bad TB |
700 | if (acquire->dst) |
701 | { | |
702 | /* since every wildcard acquire results in a separate IKE_SA | |
703 | * there is no need to compare the destination address */ | |
704 | } | |
705 | else if (child_sa->get_reqid(child_sa) != acquire->reqid) | |
706 | { | |
707 | continue; | |
708 | } | |
0a673794 TB |
709 | else if (!sec_labels_equal(acquire->label, |
710 | child_sa->get_label(child_sa))) | |
711 | { | |
712 | continue; | |
713 | } | |
9d737ecf | 714 | } |
a229bdce TB |
715 | this->acquires->remove_at(this->acquires, enumerator); |
716 | destroy_acquire(acquire); | |
9d737ecf MW |
717 | } |
718 | enumerator->destroy(enumerator); | |
a229bdce | 719 | this->mutex->unlock(this->mutex); |
991f7ccd MW |
720 | } |
721 | ||
06356a29 AS |
722 | METHOD(listener_t, ike_state_change, bool, |
723 | trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state) | |
991f7ccd MW |
724 | { |
725 | switch (state) | |
726 | { | |
727 | case IKE_DESTROYING: | |
728 | complete(listener->traps, ike_sa, NULL); | |
729 | return TRUE; | |
730 | default: | |
731 | return TRUE; | |
732 | } | |
733 | } | |
734 | ||
06356a29 AS |
735 | METHOD(listener_t, child_state_change, bool, |
736 | trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa, | |
737 | child_sa_state_t state) | |
991f7ccd MW |
738 | { |
739 | switch (state) | |
740 | { | |
741 | case CHILD_INSTALLED: | |
742 | case CHILD_DESTROYING: | |
743 | complete(listener->traps, ike_sa, child_sa); | |
744 | return TRUE; | |
745 | default: | |
746 | return TRUE; | |
747 | } | |
eb8ed130 MW |
748 | } |
749 | ||
f8437dd8 MW |
750 | METHOD(trap_manager_t, flush, void, |
751 | private_trap_manager_t *this) | |
752 | { | |
d6656f11 | 753 | this->lock->write_lock(this->lock); |
12b3cdba TB |
754 | while (this->installing) |
755 | { | |
756 | this->condvar->wait(this->condvar, this->lock); | |
757 | } | |
a229bdce | 758 | this->traps->destroy_function(this->traps, (void*)destroy_entry); |
d6656f11 | 759 | this->traps = linked_list_create(); |
12b3cdba | 760 | this->installing = INSTALL_DISABLED; |
d6656f11 | 761 | this->lock->unlock(this->lock); |
f8437dd8 MW |
762 | } |
763 | ||
06356a29 AS |
764 | METHOD(trap_manager_t, destroy, void, |
765 | private_trap_manager_t *this) | |
eb8ed130 | 766 | { |
9d737ecf | 767 | charon->bus->remove_listener(charon->bus, &this->listener.listener); |
d6656f11 | 768 | this->traps->destroy_function(this->traps, (void*)destroy_entry); |
a229bdce | 769 | this->acquires->destroy_function(this->acquires, (void*)destroy_acquire); |
12b3cdba | 770 | this->condvar->destroy(this->condvar); |
a229bdce | 771 | this->mutex->destroy(this->mutex); |
fcb06fdb | 772 | this->lock->destroy(this->lock); |
eb8ed130 MW |
773 | free(this); |
774 | } | |
775 | ||
776 | /** | |
777 | * See header | |
778 | */ | |
06356a29 | 779 | trap_manager_t *trap_manager_create(void) |
eb8ed130 | 780 | { |
06356a29 AS |
781 | private_trap_manager_t *this; |
782 | ||
783 | INIT(this, | |
784 | .public = { | |
785 | .install = _install, | |
786 | .uninstall = _uninstall, | |
3c65cf64 TB |
787 | .install_external = _install_external, |
788 | .remove_external = _remove_external, | |
06356a29 AS |
789 | .create_enumerator = _create_enumerator, |
790 | .acquire = _acquire, | |
f8437dd8 | 791 | .flush = _flush, |
06356a29 AS |
792 | .destroy = _destroy, |
793 | }, | |
6a5c8ee7 MW |
794 | .listener = { |
795 | .traps = this, | |
796 | .listener = { | |
797 | .ike_state_change = _ike_state_change, | |
798 | .child_state_change = _child_state_change, | |
799 | }, | |
800 | }, | |
06356a29 | 801 | .traps = linked_list_create(), |
a229bdce TB |
802 | .acquires = linked_list_create(), |
803 | .mutex = mutex_create(MUTEX_TYPE_DEFAULT), | |
06356a29 | 804 | .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), |
12b3cdba | 805 | .condvar = rwlock_condvar_create(), |
7fa03b30 TB |
806 | .ignore_acquire_ts = lib->settings->get_bool(lib->settings, |
807 | "%s.ignore_acquire_ts", FALSE, lib->ns), | |
06356a29 | 808 | ); |
9d737ecf | 809 | charon->bus->add_listener(charon->bus, &this->listener.listener); |
7daf5226 | 810 | |
eb8ed130 MW |
811 | return &this->public; |
812 | } |