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