]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2011-2017 Tobias Brunner | |
3 | * Copyright (C) 2009 Martin Willi | |
4 | * | |
5 | * Copyright (C) secunet Security Networks AG | |
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. | |
16 | */ | |
17 | ||
18 | #include "trap_manager.h" | |
19 | ||
20 | #include <daemon.h> | |
21 | #include <threading/mutex.h> | |
22 | #include <threading/rwlock.h> | |
23 | #include <threading/rwlock_condvar.h> | |
24 | #include <collections/linked_list.h> | |
25 | ||
26 | #define INSTALL_DISABLED ((u_int)~0) | |
27 | ||
28 | typedef struct private_trap_manager_t private_trap_manager_t; | |
29 | typedef struct trap_listener_t trap_listener_t; | |
30 | ||
31 | /** | |
32 | * listener to track acquires | |
33 | */ | |
34 | struct trap_listener_t { | |
35 | ||
36 | /** | |
37 | * Implements listener interface | |
38 | */ | |
39 | listener_t listener; | |
40 | ||
41 | /** | |
42 | * points to trap_manager | |
43 | */ | |
44 | private_trap_manager_t *traps; | |
45 | }; | |
46 | ||
47 | /** | |
48 | * Private data of an trap_manager_t object. | |
49 | */ | |
50 | struct private_trap_manager_t { | |
51 | ||
52 | /** | |
53 | * Public trap_manager_t interface. | |
54 | */ | |
55 | trap_manager_t public; | |
56 | ||
57 | /** | |
58 | * Installed traps, as entry_t | |
59 | */ | |
60 | linked_list_t *traps; | |
61 | ||
62 | /** | |
63 | * read write lock for traps list | |
64 | */ | |
65 | rwlock_t *lock; | |
66 | ||
67 | /** | |
68 | * listener to track acquiring IKE_SAs | |
69 | */ | |
70 | trap_listener_t listener; | |
71 | ||
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 | ||
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 | ||
92 | /** | |
93 | * Whether to ignore traffic selectors from acquires | |
94 | */ | |
95 | bool ignore_acquire_ts; | |
96 | ||
97 | /** | |
98 | * Current acquire sequence number if not generated by the kernel | |
99 | */ | |
100 | refcount_t acquire_seq; | |
101 | }; | |
102 | ||
103 | /** | |
104 | * A installed trap entry | |
105 | */ | |
106 | typedef struct { | |
107 | /** name of the trapped CHILD_SA */ | |
108 | char *name; | |
109 | /** ref to peer_cfg to initiate */ | |
110 | peer_cfg_t *peer_cfg; | |
111 | /** ref to instantiated CHILD_SA (i.e the trap policy) */ | |
112 | child_sa_t *child_sa; | |
113 | /** TRUE in case of wildcard Transport Mode SA */ | |
114 | bool wildcard; | |
115 | /** TRUE for CHILD_SAs that are externally managed */ | |
116 | bool external; | |
117 | } entry_t; | |
118 | ||
119 | /** | |
120 | * A handled acquire | |
121 | */ | |
122 | typedef struct { | |
123 | /** pending IKE_SA connecting upon acquire */ | |
124 | ike_sa_t *ike_sa; | |
125 | /** reqid of pending trap policy */ | |
126 | uint32_t reqid; | |
127 | /** destination address (wildcard case) */ | |
128 | host_t *dst; | |
129 | /** data from the kernel */ | |
130 | kernel_acquire_data_t *data; | |
131 | /** optional sequence number from the kernel if an acquire is retriggered */ | |
132 | uint32_t new_seq; | |
133 | } acquire_t; | |
134 | ||
135 | /** | |
136 | * actually uninstall and destroy an installed entry | |
137 | */ | |
138 | static void destroy_entry(entry_t *this) | |
139 | { | |
140 | if (!this->external) | |
141 | { | |
142 | this->child_sa->destroy(this->child_sa); | |
143 | } | |
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) | |
153 | { | |
154 | DESTROY_IF(this->dst); | |
155 | kernel_acquire_data_destroy(this->data); | |
156 | free(this); | |
157 | } | |
158 | ||
159 | CALLBACK(acquire_by_reqid, bool, | |
160 | acquire_t *this, va_list args) | |
161 | { | |
162 | uint32_t reqid, cpu; | |
163 | sec_label_t *label; | |
164 | ||
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); | |
168 | } | |
169 | ||
170 | CALLBACK(acquire_by_dst, bool, | |
171 | acquire_t *this, va_list args) | |
172 | { | |
173 | host_t *dst; | |
174 | ||
175 | VA_ARGS_VGET(args, dst); | |
176 | return this->dst && this->dst->ip_equals(this->dst, dst); | |
177 | } | |
178 | ||
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 | ||
189 | other_ts = child->get_traffic_selectors(child, FALSE, NULL); | |
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 | ||
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 | ||
217 | my_ts = child->get_traffic_selectors(child, TRUE, local); | |
218 | other_ts = child->get_traffic_selectors(child, FALSE, remote); | |
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 | ||
239 | METHOD(trap_manager_t, install, bool, | |
240 | private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) | |
241 | { | |
242 | entry_t *entry, *found = NULL; | |
243 | ike_cfg_t *ike_cfg; | |
244 | child_sa_t *child_sa; | |
245 | host_t *me, *other; | |
246 | linked_list_t *local, *remote; | |
247 | enumerator_t *enumerator; | |
248 | status_t status; | |
249 | bool result = FALSE, wildcard = FALSE; | |
250 | ||
251 | /* try to resolve addresses */ | |
252 | ike_cfg = peer->get_ike_cfg(peer); | |
253 | other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC); | |
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 | } | |
261 | else if (other && other->is_anyaddr(other)) | |
262 | { | |
263 | other->destroy(other); | |
264 | DBG1(DBG_CFG, "installing trap failed, remote address unknown"); | |
265 | return FALSE; | |
266 | } | |
267 | else | |
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"); | |
275 | return FALSE; | |
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)) | |
285 | { | |
286 | DESTROY_IF(me); | |
287 | me = charon->kernel->get_source_addr(charon->kernel, other, NULL); | |
288 | } | |
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)); | |
294 | } | |
295 | ||
296 | this->lock->write_lock(this->lock); | |
297 | if (this->installing == INSTALL_DISABLED) | |
298 | { /* flush() has been called */ | |
299 | this->lock->unlock(this->lock); | |
300 | other->destroy(other); | |
301 | me->destroy(me); | |
302 | return FALSE; | |
303 | } | |
304 | enumerator = this->traps->create_enumerator(this->traps); | |
305 | while (enumerator->enumerate(enumerator, &entry)) | |
306 | { | |
307 | if (!entry->external && | |
308 | streq(entry->name, child->get_name(child)) && | |
309 | streq(entry->peer_cfg->get_name(entry->peer_cfg), | |
310 | peer->get_name(peer))) | |
311 | { | |
312 | found = entry; | |
313 | if (entry->child_sa) | |
314 | { /* replace it with an updated version if already installed */ | |
315 | this->traps->remove_at(this->traps, enumerator); | |
316 | } | |
317 | break; | |
318 | } | |
319 | } | |
320 | enumerator->destroy(enumerator); | |
321 | ||
322 | if (found) | |
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); | |
328 | other->destroy(other); | |
329 | me->destroy(me); | |
330 | return FALSE; | |
331 | } | |
332 | /* config might have changed so update everything */ | |
333 | DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name); | |
334 | } | |
335 | ||
336 | INIT(entry, | |
337 | .name = strdup(child->get_name(child)), | |
338 | .peer_cfg = peer->get_ref(peer), | |
339 | .wildcard = wildcard, | |
340 | ); | |
341 | this->traps->insert_first(this->traps, entry); | |
342 | this->installing++; | |
343 | /* don't hold lock while creating CHILD_SA and installing policies */ | |
344 | this->lock->unlock(this->lock); | |
345 | ||
346 | /* create and route CHILD_SA */ | |
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), | |
353 | .cpu = CPU_ID_MAX, | |
354 | }; | |
355 | child_sa = child_sa_create(me, other, child, &child_data); | |
356 | ||
357 | local = linked_list_create_with_items(me, NULL); | |
358 | remote = linked_list_create_with_items(other, NULL); | |
359 | ||
360 | status = install_trap(child_sa, local, remote); | |
361 | ||
362 | local->destroy_offset(local, offsetof(host_t, destroy)); | |
363 | remote->destroy_offset(remote, offsetof(host_t, destroy)); | |
364 | if (status != SUCCESS) | |
365 | { | |
366 | DBG1(DBG_CFG, "installing trap failed"); | |
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); | |
372 | } | |
373 | else | |
374 | { | |
375 | this->lock->write_lock(this->lock); | |
376 | entry->child_sa = child_sa; | |
377 | this->lock->unlock(this->lock); | |
378 | result = TRUE; | |
379 | } | |
380 | if (found) | |
381 | { | |
382 | destroy_entry(found); | |
383 | } | |
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); | |
389 | return result; | |
390 | } | |
391 | ||
392 | METHOD(trap_manager_t, uninstall, bool, | |
393 | private_trap_manager_t *this, char *peer, char *child) | |
394 | { | |
395 | enumerator_t *enumerator; | |
396 | entry_t *entry, *found = NULL; | |
397 | ||
398 | this->lock->write_lock(this->lock); | |
399 | while (this->installing) | |
400 | { | |
401 | this->condvar->wait(this->condvar, this->lock); | |
402 | } | |
403 | enumerator = this->traps->create_enumerator(this->traps); | |
404 | while (enumerator->enumerate(enumerator, &entry)) | |
405 | { | |
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) | |
471 | { | |
472 | this->traps->remove_at(this->traps, enumerator); | |
473 | found = entry; | |
474 | break; | |
475 | } | |
476 | } | |
477 | enumerator->destroy(enumerator); | |
478 | this->lock->unlock(this->lock); | |
479 | ||
480 | if (!found) | |
481 | { | |
482 | return FALSE; | |
483 | } | |
484 | destroy_entry(found); | |
485 | return TRUE; | |
486 | } | |
487 | ||
488 | CALLBACK(trap_filter, bool, | |
489 | rwlock_t *lock, enumerator_t *orig, va_list args) | |
490 | { | |
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)) | |
498 | { | |
499 | if (!entry->child_sa || entry->external) | |
500 | { /* skip entries that are currently being installed or are managed | |
501 | * externally */ | |
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; | |
513 | } | |
514 | return FALSE; | |
515 | } | |
516 | ||
517 | METHOD(trap_manager_t, create_enumerator, enumerator_t*, | |
518 | private_trap_manager_t *this) | |
519 | { | |
520 | this->lock->read_lock(this->lock); | |
521 | return enumerator_create_filter(this->traps->create_enumerator(this->traps), | |
522 | trap_filter, this->lock, | |
523 | (void*)this->lock->unlock); | |
524 | } | |
525 | ||
526 | METHOD(trap_manager_t, acquire, void, | |
527 | private_trap_manager_t *this, uint32_t reqid, kernel_acquire_data_t *data) | |
528 | { | |
529 | enumerator_t *enumerator; | |
530 | entry_t *entry, *found = NULL; | |
531 | acquire_t *acquire = NULL; | |
532 | peer_cfg_t *peer; | |
533 | child_cfg_t *child; | |
534 | ike_sa_t *ike_sa; | |
535 | host_t *host = NULL; | |
536 | uint32_t allocated_reqid, seq = 0; | |
537 | bool wildcard; | |
538 | ||
539 | this->lock->read_lock(this->lock); | |
540 | enumerator = this->traps->create_enumerator(this->traps); | |
541 | while (enumerator->enumerate(enumerator, &entry)) | |
542 | { | |
543 | if (entry->child_sa && | |
544 | entry->child_sa->get_reqid(entry->child_sa) == reqid) | |
545 | { | |
546 | found = entry; | |
547 | break; | |
548 | } | |
549 | } | |
550 | enumerator->destroy(enumerator); | |
551 | ||
552 | if (!found) | |
553 | { | |
554 | DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid); | |
555 | this->lock->unlock(this->lock); | |
556 | return; | |
557 | } | |
558 | wildcard = found->wildcard; | |
559 | ||
560 | this->mutex->lock(this->mutex); | |
561 | if (wildcard) | |
562 | { | |
563 | /* for wildcard acquires we check that we don't have a pending acquire | |
564 | * with the same peer */ | |
565 | uint8_t mask; | |
566 | ||
567 | data->dst->to_subnet(data->dst, &host, &mask); | |
568 | if (this->acquires->find_first(this->acquires, acquire_by_dst, | |
569 | (void**)&acquire, host)) | |
570 | { | |
571 | host->destroy(host); | |
572 | } | |
573 | } | |
574 | else | |
575 | { | |
576 | this->acquires->find_first(this->acquires, acquire_by_reqid, | |
577 | (void**)&acquire, reqid, data->cpu, | |
578 | data->label); | |
579 | } | |
580 | if (!acquire) | |
581 | { | |
582 | INIT(acquire, | |
583 | .dst = host, | |
584 | .reqid = reqid, | |
585 | .data = kernel_acquire_data_clone(data), | |
586 | ); | |
587 | seq = data->seq = data->seq ?: ref_get_nonzero(&this->acquire_seq); | |
588 | this->acquires->insert_last(this->acquires, acquire); | |
589 | } | |
590 | else if (data->seq && data->seq != acquire->data->seq) | |
591 | { | |
592 | /* acquire got retriggered, update to latest sequence number */ | |
593 | acquire->new_seq = data->seq; | |
594 | } | |
595 | this->mutex->unlock(this->mutex); | |
596 | if (!seq) | |
597 | { | |
598 | DBG1(DBG_CFG, "ignoring acquire for reqid %u, connection attempt " | |
599 | "pending", reqid); | |
600 | this->lock->unlock(this->lock); | |
601 | return; | |
602 | } | |
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); | |
606 | /* only pass allocated reqids explicitly, take a reference */ | |
607 | allocated_reqid = found->child_sa->get_reqid_ref(found->child_sa); | |
608 | /* don't hold the lock while checking out the IKE_SA */ | |
609 | this->lock->unlock(this->lock); | |
610 | ||
611 | if (wildcard) | |
612 | { /* the peer config would match IKE_SAs with other peers */ | |
613 | ike_sa = charon->ike_sa_manager->create_new(charon->ike_sa_manager, | |
614 | peer->get_ike_version(peer), TRUE); | |
615 | if (ike_sa) | |
616 | { | |
617 | ike_cfg_t *ike_cfg; | |
618 | uint16_t port; | |
619 | uint8_t mask; | |
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); | |
625 | data->dst->to_subnet(data->dst, &host, &mask); | |
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); | |
630 | data->src->to_subnet(data->src, &host, &mask); | |
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( | |
640 | charon->ike_sa_manager, peer); | |
641 | } | |
642 | peer->destroy(peer); | |
643 | ||
644 | if (ike_sa) | |
645 | { | |
646 | child_init_args_t args = { | |
647 | .reqid = allocated_reqid, | |
648 | .cpu = data->cpu, | |
649 | .src = data->src, | |
650 | .dst = data->dst, | |
651 | .label = data->label, | |
652 | .seq = seq, | |
653 | }; | |
654 | ||
655 | if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1) | |
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. */ | |
658 | args.src = args.dst = NULL; | |
659 | } | |
660 | ||
661 | this->mutex->lock(this->mutex); | |
662 | acquire->ike_sa = ike_sa; | |
663 | this->mutex->unlock(this->mutex); | |
664 | ||
665 | if (ike_sa->initiate(ike_sa, child, &args) != DESTROY_ME) | |
666 | { | |
667 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
668 | } | |
669 | else | |
670 | { | |
671 | charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, | |
672 | ike_sa); | |
673 | } | |
674 | } | |
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 | } | |
683 | if (allocated_reqid) | |
684 | { | |
685 | charon->kernel->release_reqid(charon->kernel, allocated_reqid); | |
686 | } | |
687 | } | |
688 | ||
689 | /** | |
690 | * Update the sequence number of the CHILD_SA before installing it in case | |
691 | * the acquire got retriggered. | |
692 | */ | |
693 | static void update_acquire_seq(private_trap_manager_t *this, ike_sa_t *ike_sa, | |
694 | child_sa_t *child_sa) | |
695 | { | |
696 | enumerator_t *enumerator; | |
697 | acquire_t *acquire; | |
698 | uint32_t seq; | |
699 | ||
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)) | |
712 | { | |
713 | return; | |
714 | } | |
715 | ||
716 | this->mutex->lock(this->mutex); | |
717 | enumerator = this->acquires->create_enumerator(this->acquires); | |
718 | while (enumerator->enumerate(enumerator, &acquire)) | |
719 | { | |
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) | |
732 | { | |
733 | continue; | |
734 | } | |
735 | if (acquire->new_seq) | |
736 | { | |
737 | child_sa->set_acquire_seq(child_sa, acquire->new_seq); | |
738 | acquire->data->seq = acquire->new_seq; | |
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 */ | |
769 | if (!acquire->ike_sa || (seq && seq != acquire->data->seq) || | |
770 | (!seq && acquire->ike_sa != ike_sa)) | |
771 | { | |
772 | continue; | |
773 | } | |
774 | this->acquires->remove_at(this->acquires, enumerator); | |
775 | destroy_acquire(acquire); | |
776 | } | |
777 | enumerator->destroy(enumerator); | |
778 | this->mutex->unlock(this->mutex); | |
779 | } | |
780 | ||
781 | METHOD(listener_t, ike_state_change, bool, | |
782 | trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state) | |
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 | ||
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) | |
797 | { | |
798 | switch (state) | |
799 | { | |
800 | case CHILD_INSTALLING: | |
801 | update_acquire_seq(listener->traps, ike_sa, child_sa); | |
802 | return TRUE; | |
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 | } | |
810 | } | |
811 | ||
812 | METHOD(trap_manager_t, flush, void, | |
813 | private_trap_manager_t *this) | |
814 | { | |
815 | this->lock->write_lock(this->lock); | |
816 | while (this->installing) | |
817 | { | |
818 | this->condvar->wait(this->condvar, this->lock); | |
819 | } | |
820 | this->traps->destroy_function(this->traps, (void*)destroy_entry); | |
821 | this->traps = linked_list_create(); | |
822 | this->installing = INSTALL_DISABLED; | |
823 | this->lock->unlock(this->lock); | |
824 | } | |
825 | ||
826 | METHOD(trap_manager_t, destroy, void, | |
827 | private_trap_manager_t *this) | |
828 | { | |
829 | charon->bus->remove_listener(charon->bus, &this->listener.listener); | |
830 | this->traps->destroy_function(this->traps, (void*)destroy_entry); | |
831 | this->acquires->destroy_function(this->acquires, (void*)destroy_acquire); | |
832 | this->condvar->destroy(this->condvar); | |
833 | this->mutex->destroy(this->mutex); | |
834 | this->lock->destroy(this->lock); | |
835 | free(this); | |
836 | } | |
837 | ||
838 | /** | |
839 | * See header | |
840 | */ | |
841 | trap_manager_t *trap_manager_create(void) | |
842 | { | |
843 | private_trap_manager_t *this; | |
844 | ||
845 | INIT(this, | |
846 | .public = { | |
847 | .install = _install, | |
848 | .uninstall = _uninstall, | |
849 | .install_external = _install_external, | |
850 | .remove_external = _remove_external, | |
851 | .create_enumerator = _create_enumerator, | |
852 | .acquire = _acquire, | |
853 | .flush = _flush, | |
854 | .destroy = _destroy, | |
855 | }, | |
856 | .listener = { | |
857 | .traps = this, | |
858 | .listener = { | |
859 | .ike_state_change = _ike_state_change, | |
860 | .child_state_change = _child_state_change, | |
861 | }, | |
862 | }, | |
863 | .traps = linked_list_create(), | |
864 | .acquires = linked_list_create(), | |
865 | .mutex = mutex_create(MUTEX_TYPE_DEFAULT), | |
866 | .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), | |
867 | .condvar = rwlock_condvar_create(), | |
868 | .ignore_acquire_ts = lib->settings->get_bool(lib->settings, | |
869 | "%s.ignore_acquire_ts", FALSE, lib->ns), | |
870 | ); | |
871 | charon->bus->add_listener(charon->bus, &this->listener.listener); | |
872 | ||
873 | return &this->public; | |
874 | } |