/src/strongswan/src/libcharon/sa/child_sa.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2006-2025 Tobias Brunner |
3 | | * Copyright (C) 2016 Andreas Steffen |
4 | | * Copyright (C) 2005-2008 Martin Willi |
5 | | * Copyright (C) 2006 Daniel Roethlisberger |
6 | | * Copyright (C) 2005 Jan Hutter |
7 | | * |
8 | | * Copyright (C) secunet Security Networks AG |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU General Public License as published by the |
12 | | * Free Software Foundation; either version 2 of the License, or (at your |
13 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, but |
16 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
17 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
18 | | * for more details. |
19 | | */ |
20 | | |
21 | | #define _GNU_SOURCE |
22 | | #include "child_sa.h" |
23 | | |
24 | | #include <stdio.h> |
25 | | #include <string.h> |
26 | | #include <time.h> |
27 | | |
28 | | #include <daemon.h> |
29 | | #include <collections/array.h> |
30 | | |
31 | | ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, |
32 | | "CREATED", |
33 | | "ROUTED", |
34 | | "INSTALLING", |
35 | | "INSTALLED", |
36 | | "UPDATING", |
37 | | "REKEYING", |
38 | | "REKEYED", |
39 | | "RETRYING", |
40 | | "DELETING", |
41 | | "DELETED", |
42 | | "DESTROYING", |
43 | | ); |
44 | | |
45 | | ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES, |
46 | | "NONE", |
47 | | "REGISTERED", |
48 | | "SA", |
49 | | "POLICIES", |
50 | | ); |
51 | | |
52 | | typedef struct private_child_sa_t private_child_sa_t; |
53 | | |
54 | | /** |
55 | | * Private data of a child_sa_t object. |
56 | | */ |
57 | | struct private_child_sa_t { |
58 | | /** |
59 | | * Public interface of child_sa_t. |
60 | | */ |
61 | | child_sa_t public; |
62 | | |
63 | | /** |
64 | | * address of us |
65 | | */ |
66 | | host_t *my_addr; |
67 | | |
68 | | /** |
69 | | * address of remote |
70 | | */ |
71 | | host_t *other_addr; |
72 | | |
73 | | /** |
74 | | * our actually used SPI, 0 if unused |
75 | | */ |
76 | | uint32_t my_spi; |
77 | | |
78 | | /** |
79 | | * others used SPI, 0 if unused |
80 | | */ |
81 | | uint32_t other_spi; |
82 | | |
83 | | /** |
84 | | * our Compression Parameter Index (CPI) used, 0 if unused |
85 | | */ |
86 | | uint16_t my_cpi; |
87 | | |
88 | | /** |
89 | | * others Compression Parameter Index (CPI) used, 0 if unused |
90 | | */ |
91 | | uint16_t other_cpi; |
92 | | |
93 | | /** |
94 | | * Array for local traffic selectors |
95 | | */ |
96 | | array_t *my_ts; |
97 | | |
98 | | /** |
99 | | * Array for remote traffic selectors |
100 | | */ |
101 | | array_t *other_ts; |
102 | | |
103 | | /** |
104 | | * Outbound encryption key cached during a rekeying |
105 | | */ |
106 | | chunk_t encr_r; |
107 | | |
108 | | /** |
109 | | * Outbound integrity key cached during a rekeying |
110 | | */ |
111 | | chunk_t integ_r; |
112 | | |
113 | | /** |
114 | | * Whether the registered outbound SA was created as initiator |
115 | | */ |
116 | | bool initiator; |
117 | | |
118 | | /** |
119 | | * Whether the outbound SA has only been registered yet during a rekeying |
120 | | */ |
121 | | child_sa_outbound_state_t outbound_state; |
122 | | |
123 | | /** |
124 | | * Whether the inbound SA has been installed |
125 | | */ |
126 | | bool inbound_installed; |
127 | | |
128 | | /** |
129 | | * Whether the peer supports TFCv3 |
130 | | */ |
131 | | bool tfcv3; |
132 | | |
133 | | /** |
134 | | * The "other" CHILD_SA involved in a passive rekeying (either replacing |
135 | | * this one, or being replaced by it) |
136 | | */ |
137 | | child_sa_t *rekey_sa; |
138 | | |
139 | | /** |
140 | | * Protocol used to protect this SA, ESP|AH |
141 | | */ |
142 | | protocol_id_t protocol; |
143 | | |
144 | | /** |
145 | | * reqid used for this child_sa |
146 | | */ |
147 | | uint32_t reqid; |
148 | | |
149 | | /** |
150 | | * Did we allocate/confirm and must release the reqid? |
151 | | */ |
152 | | bool reqid_allocated; |
153 | | |
154 | | /** |
155 | | * Is the reqid statically configured |
156 | | */ |
157 | | bool static_reqid; |
158 | | |
159 | | /** |
160 | | * Unique CHILD_SA identifier |
161 | | */ |
162 | | uint32_t unique_id; |
163 | | |
164 | | /** |
165 | | * Optional sequence number associated with triggering acquire |
166 | | */ |
167 | | uint32_t seq; |
168 | | |
169 | | /** |
170 | | * Whether FWD policies in the outbound direction should be installed |
171 | | */ |
172 | | bool policies_fwd_out; |
173 | | |
174 | | /** |
175 | | * Inbound interface ID |
176 | | */ |
177 | | uint32_t if_id_in; |
178 | | |
179 | | /** |
180 | | * Outbound interface ID |
181 | | */ |
182 | | uint32_t if_id_out; |
183 | | |
184 | | /** |
185 | | * CPU ID to use for the outbound SA |
186 | | */ |
187 | | uint32_t cpu; |
188 | | |
189 | | /** |
190 | | * Whether to enable per-CPU feature for this SA |
191 | | */ |
192 | | bool per_cpu; |
193 | | |
194 | | /** |
195 | | * inbound mark used for this child_sa |
196 | | */ |
197 | | mark_t mark_in; |
198 | | |
199 | | /** |
200 | | * outbound mark used for this child_sa |
201 | | */ |
202 | | mark_t mark_out; |
203 | | |
204 | | /** |
205 | | * Security label |
206 | | */ |
207 | | sec_label_t *label; |
208 | | |
209 | | /** |
210 | | * absolute time when rekeying is scheduled |
211 | | */ |
212 | | time_t rekey_time; |
213 | | |
214 | | /** |
215 | | * absolute time when the SA expires |
216 | | */ |
217 | | time_t expire_time; |
218 | | |
219 | | /** |
220 | | * absolute time when SA has been installed |
221 | | */ |
222 | | time_t install_time; |
223 | | |
224 | | /** |
225 | | * state of the CHILD_SA |
226 | | */ |
227 | | child_sa_state_t state; |
228 | | |
229 | | /** |
230 | | * TRUE if this CHILD_SA is used to install trap policies |
231 | | */ |
232 | | bool trap; |
233 | | |
234 | | /** |
235 | | * Specifies if UDP encapsulation is enabled (NAT traversal) |
236 | | */ |
237 | | bool encap; |
238 | | |
239 | | /** |
240 | | * Specifies the IPComp transform used (IPCOMP_NONE if disabled) |
241 | | */ |
242 | | ipcomp_transform_t ipcomp; |
243 | | |
244 | | /** |
245 | | * mode this SA uses, tunnel/transport |
246 | | */ |
247 | | ipsec_mode_t mode; |
248 | | |
249 | | /** |
250 | | * Disable fragmenting packets across IP-TFS packets |
251 | | */ |
252 | | bool iptfs_dont_frag; |
253 | | |
254 | | /** |
255 | | * Action to enforce if peer closes the CHILD_SA |
256 | | */ |
257 | | action_t close_action; |
258 | | |
259 | | /** |
260 | | * Action to enforce if peer is considered dead |
261 | | */ |
262 | | action_t dpd_action; |
263 | | |
264 | | /** |
265 | | * selected proposal |
266 | | */ |
267 | | proposal_t *proposal; |
268 | | |
269 | | /** |
270 | | * config used to create this child |
271 | | */ |
272 | | child_cfg_t *config; |
273 | | |
274 | | /** |
275 | | * time of last use in seconds (inbound) |
276 | | */ |
277 | | time_t my_usetime; |
278 | | |
279 | | /** |
280 | | * time of last use in seconds (outbound) |
281 | | */ |
282 | | time_t other_usetime; |
283 | | |
284 | | /** |
285 | | * last number of inbound bytes |
286 | | */ |
287 | | uint64_t my_usebytes; |
288 | | |
289 | | /** |
290 | | * last number of outbound bytes |
291 | | */ |
292 | | uint64_t other_usebytes; |
293 | | |
294 | | /** |
295 | | * last number of inbound packets |
296 | | */ |
297 | | uint64_t my_usepackets; |
298 | | |
299 | | /** |
300 | | * last number of outbound packets |
301 | | */ |
302 | | uint64_t other_usepackets; |
303 | | }; |
304 | | |
305 | | /** |
306 | | * Convert an IKEv2 specific protocol identifier to the IP protocol identifier |
307 | | */ |
308 | | static inline uint8_t proto_ike2ip(protocol_id_t protocol) |
309 | 0 | { |
310 | 0 | switch (protocol) |
311 | 0 | { |
312 | 0 | case PROTO_ESP: |
313 | 0 | return IPPROTO_ESP; |
314 | 0 | case PROTO_AH: |
315 | 0 | return IPPROTO_AH; |
316 | 0 | default: |
317 | 0 | return protocol; |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | | /** |
322 | | * Returns the mark to use on the inbound SA |
323 | | */ |
324 | | static inline mark_t mark_in_sa(private_child_sa_t *this) |
325 | 0 | { |
326 | 0 | if (this->config->has_option(this->config, OPT_MARK_IN_SA)) |
327 | 0 | { |
328 | 0 | return this->mark_in; |
329 | 0 | } |
330 | 0 | return (mark_t){}; |
331 | 0 | } |
332 | | |
333 | | /** |
334 | | * Possible uses for security labels |
335 | | */ |
336 | | typedef enum { |
337 | | LABEL_USE_REQID, |
338 | | LABEL_USE_POLICY, |
339 | | LABEL_USE_SA, |
340 | | } label_use_t; |
341 | | |
342 | | /** |
343 | | * Returns the security label for either policies, SAs or reqids. |
344 | | */ |
345 | | static inline sec_label_t *label_for(private_child_sa_t *this, label_use_t use) |
346 | 0 | { |
347 | | /* For SELinux we use the configured label for policies and reqid but the |
348 | | * negotiated one for the SAs. That's because the label on the policies is |
349 | | * usually a generic one that matches specific labels on flows, which will |
350 | | * trigger an acquire if no matching SA with that label exists yet. |
351 | | * When that SA is later installed, we want to avoid having to install |
352 | | * policies in the kernel that will never get used, so we use the configured |
353 | | * label again. |
354 | | * Note that while the labels don't have to be equal, they are both either |
355 | | * NULL or defined. |
356 | | */ |
357 | 0 | if (this->config->get_label_mode(this->config) == SEC_LABEL_MODE_SELINUX) |
358 | 0 | { |
359 | 0 | switch (use) |
360 | 0 | { |
361 | 0 | case LABEL_USE_REQID: |
362 | 0 | case LABEL_USE_POLICY: |
363 | 0 | return this->config->get_label(this->config); |
364 | 0 | case LABEL_USE_SA: |
365 | 0 | return this->label; |
366 | 0 | } |
367 | 0 | } |
368 | | /* for the simple label mode we don't pass labels to the kernel, so we don't |
369 | | * use it to acquire unique reqids either */ |
370 | 0 | return NULL; |
371 | 0 | } |
372 | | |
373 | | METHOD(child_sa_t, get_name, char*, |
374 | | private_child_sa_t *this) |
375 | 0 | { |
376 | 0 | return this->config->get_name(this->config); |
377 | 0 | } |
378 | | |
379 | | METHOD(child_sa_t, get_reqid, uint32_t, |
380 | | private_child_sa_t *this) |
381 | 0 | { |
382 | 0 | return this->reqid; |
383 | 0 | } |
384 | | |
385 | | METHOD(child_sa_t, get_reqid_ref, uint32_t, |
386 | | private_child_sa_t *this) |
387 | 0 | { |
388 | 0 | if ((this->reqid_allocated || (!this->static_reqid && this->reqid)) && |
389 | 0 | charon->kernel->ref_reqid(charon->kernel, this->reqid) == SUCCESS) |
390 | 0 | { |
391 | 0 | return this->reqid; |
392 | 0 | } |
393 | 0 | return 0; |
394 | 0 | } |
395 | | |
396 | | METHOD(child_sa_t, get_unique_id, uint32_t, |
397 | | private_child_sa_t *this) |
398 | 0 | { |
399 | 0 | return this->unique_id; |
400 | 0 | } |
401 | | |
402 | | METHOD(child_sa_t, get_config, child_cfg_t*, |
403 | | private_child_sa_t *this) |
404 | 0 | { |
405 | 0 | return this->config; |
406 | 0 | } |
407 | | |
408 | | METHOD(child_sa_t, set_state, void, |
409 | | private_child_sa_t *this, child_sa_state_t state) |
410 | 0 | { |
411 | 0 | if (this->state != state) |
412 | 0 | { |
413 | 0 | DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", |
414 | 0 | get_name(this), this->unique_id, |
415 | 0 | child_sa_state_names, this->state, |
416 | 0 | child_sa_state_names, state); |
417 | 0 | charon->bus->child_state_change(charon->bus, &this->public, state); |
418 | 0 | this->state = state; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | METHOD(child_sa_t, get_state, child_sa_state_t, |
423 | | private_child_sa_t *this) |
424 | 0 | { |
425 | 0 | return this->state; |
426 | 0 | } |
427 | | |
428 | | METHOD(child_sa_t, get_outbound_state, child_sa_outbound_state_t, |
429 | | private_child_sa_t *this) |
430 | 0 | { |
431 | 0 | return this->outbound_state; |
432 | 0 | } |
433 | | |
434 | | METHOD(child_sa_t, get_spi, uint32_t, |
435 | | private_child_sa_t *this, bool inbound) |
436 | 0 | { |
437 | 0 | return inbound ? this->my_spi : this->other_spi; |
438 | 0 | } |
439 | | |
440 | | METHOD(child_sa_t, get_cpi, uint16_t, |
441 | | private_child_sa_t *this, bool inbound) |
442 | 0 | { |
443 | 0 | return inbound ? this->my_cpi : this->other_cpi; |
444 | 0 | } |
445 | | |
446 | | METHOD(child_sa_t, get_protocol, protocol_id_t, |
447 | | private_child_sa_t *this) |
448 | 0 | { |
449 | 0 | return this->protocol; |
450 | 0 | } |
451 | | |
452 | | METHOD(child_sa_t, set_protocol, void, |
453 | | private_child_sa_t *this, protocol_id_t protocol) |
454 | 0 | { |
455 | 0 | this->protocol = protocol; |
456 | 0 | } |
457 | | |
458 | | METHOD(child_sa_t, get_mode, ipsec_mode_t, |
459 | | private_child_sa_t *this) |
460 | 0 | { |
461 | 0 | return this->mode; |
462 | 0 | } |
463 | | |
464 | | METHOD(child_sa_t, set_mode, void, |
465 | | private_child_sa_t *this, ipsec_mode_t mode) |
466 | 0 | { |
467 | 0 | this->mode = mode; |
468 | 0 | } |
469 | | |
470 | | METHOD(child_sa_t, set_iptfs_dont_fragment, void, |
471 | | private_child_sa_t *this) |
472 | 0 | { |
473 | 0 | this->iptfs_dont_frag = TRUE; |
474 | 0 | } |
475 | | |
476 | | METHOD(child_sa_t, has_encap, bool, |
477 | | private_child_sa_t *this) |
478 | 0 | { |
479 | 0 | return this->encap; |
480 | 0 | } |
481 | | |
482 | | METHOD(child_sa_t, get_ipcomp, ipcomp_transform_t, |
483 | | private_child_sa_t *this) |
484 | 0 | { |
485 | 0 | return this->ipcomp; |
486 | 0 | } |
487 | | |
488 | | METHOD(child_sa_t, set_ipcomp, void, |
489 | | private_child_sa_t *this, ipcomp_transform_t ipcomp) |
490 | 0 | { |
491 | 0 | this->ipcomp = ipcomp; |
492 | 0 | } |
493 | | |
494 | | METHOD(child_sa_t, set_close_action, void, |
495 | | private_child_sa_t *this, action_t action) |
496 | 0 | { |
497 | 0 | this->close_action = action; |
498 | 0 | } |
499 | | |
500 | | METHOD(child_sa_t, get_close_action, action_t, |
501 | | private_child_sa_t *this) |
502 | 0 | { |
503 | 0 | return this->close_action; |
504 | 0 | } |
505 | | |
506 | | METHOD(child_sa_t, set_dpd_action, void, |
507 | | private_child_sa_t *this, action_t action) |
508 | 0 | { |
509 | 0 | this->dpd_action = action; |
510 | 0 | } |
511 | | |
512 | | METHOD(child_sa_t, get_dpd_action, action_t, |
513 | | private_child_sa_t *this) |
514 | 0 | { |
515 | 0 | return this->dpd_action; |
516 | 0 | } |
517 | | |
518 | | METHOD(child_sa_t, get_proposal, proposal_t*, |
519 | | private_child_sa_t *this) |
520 | 0 | { |
521 | 0 | return this->proposal; |
522 | 0 | } |
523 | | |
524 | | METHOD(child_sa_t, set_proposal, void, |
525 | | private_child_sa_t *this, proposal_t *proposal) |
526 | 0 | { |
527 | 0 | this->proposal = proposal->clone(proposal, 0); |
528 | 0 | } |
529 | | |
530 | | METHOD(child_sa_t, create_ts_enumerator, enumerator_t*, |
531 | | private_child_sa_t *this, bool local) |
532 | 0 | { |
533 | 0 | if (local) |
534 | 0 | { |
535 | 0 | return array_create_enumerator(this->my_ts); |
536 | 0 | } |
537 | 0 | return array_create_enumerator(this->other_ts); |
538 | 0 | } |
539 | | |
540 | | typedef struct policy_enumerator_t policy_enumerator_t; |
541 | | |
542 | | /** |
543 | | * Private policy enumerator |
544 | | */ |
545 | | struct policy_enumerator_t { |
546 | | /** implements enumerator_t */ |
547 | | enumerator_t public; |
548 | | /** enumerator over own TS */ |
549 | | enumerator_t *mine; |
550 | | /** enumerator over others TS */ |
551 | | enumerator_t *other; |
552 | | /** array of others TS, to recreate enumerator */ |
553 | | array_t *array; |
554 | | /** currently enumerating TS for "me" side */ |
555 | | traffic_selector_t *ts; |
556 | | }; |
557 | | |
558 | | METHOD(enumerator_t, policy_enumerate, bool, |
559 | | policy_enumerator_t *this, va_list args) |
560 | 0 | { |
561 | 0 | traffic_selector_t *other_ts, **my_out, **other_out; |
562 | |
|
563 | 0 | VA_ARGS_VGET(args, my_out, other_out); |
564 | |
|
565 | 0 | while (this->ts || this->mine->enumerate(this->mine, &this->ts)) |
566 | 0 | { |
567 | 0 | if (!this->other->enumerate(this->other, &other_ts)) |
568 | 0 | { /* end of others list, restart with new of mine */ |
569 | 0 | this->other->destroy(this->other); |
570 | 0 | this->other = array_create_enumerator(this->array); |
571 | 0 | this->ts = NULL; |
572 | 0 | continue; |
573 | 0 | } |
574 | 0 | if (this->ts->get_type(this->ts) != other_ts->get_type(other_ts)) |
575 | 0 | { /* family mismatch */ |
576 | 0 | continue; |
577 | 0 | } |
578 | 0 | if (this->ts->get_protocol(this->ts) && |
579 | 0 | other_ts->get_protocol(other_ts) && |
580 | 0 | this->ts->get_protocol(this->ts) != other_ts->get_protocol(other_ts)) |
581 | 0 | { /* protocol mismatch */ |
582 | 0 | continue; |
583 | 0 | } |
584 | 0 | if (my_out) |
585 | 0 | { |
586 | 0 | *my_out = this->ts; |
587 | 0 | } |
588 | 0 | if (other_out) |
589 | 0 | { |
590 | 0 | *other_out = other_ts; |
591 | 0 | } |
592 | 0 | return TRUE; |
593 | 0 | } |
594 | 0 | return FALSE; |
595 | 0 | } |
596 | | |
597 | | METHOD(enumerator_t, policy_destroy, void, |
598 | | policy_enumerator_t *this) |
599 | 0 | { |
600 | 0 | this->mine->destroy(this->mine); |
601 | 0 | this->other->destroy(this->other); |
602 | 0 | free(this); |
603 | 0 | } |
604 | | |
605 | | /** |
606 | | * Create an enumerator over two lists of traffic selectors, returning all the |
607 | | * pairs of traffic selectors from the first and second list. |
608 | | */ |
609 | | static enumerator_t *create_policy_enumerator_internal(array_t *my_ts, |
610 | | array_t *other_ts) |
611 | 0 | { |
612 | 0 | policy_enumerator_t *e; |
613 | |
|
614 | 0 | INIT(e, |
615 | 0 | .public = { |
616 | 0 | .enumerate = enumerator_enumerate_default, |
617 | 0 | .venumerate = _policy_enumerate, |
618 | 0 | .destroy = _policy_destroy, |
619 | 0 | }, |
620 | 0 | .mine = array_create_enumerator(my_ts), |
621 | 0 | .other = array_create_enumerator(other_ts), |
622 | 0 | .array = other_ts, |
623 | 0 | .ts = NULL, |
624 | 0 | ); |
625 | |
|
626 | 0 | return &e->public; |
627 | 0 | } |
628 | | |
629 | | METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, |
630 | | private_child_sa_t *this) |
631 | 0 | { |
632 | 0 | return create_policy_enumerator_internal(this->my_ts, this->other_ts); |
633 | 0 | } |
634 | | |
635 | | /** |
636 | | * update the cached usebytes |
637 | | * returns SUCCESS if the usebytes have changed, FAILED if not or no SPIs |
638 | | * are available, and NOT_SUPPORTED if the kernel interface does not support |
639 | | * querying the usebytes. |
640 | | */ |
641 | | static status_t update_usebytes(private_child_sa_t *this, bool inbound) |
642 | 0 | { |
643 | 0 | status_t status = FAILED; |
644 | 0 | uint64_t bytes, packets; |
645 | 0 | time_t time; |
646 | |
|
647 | 0 | if (inbound) |
648 | 0 | { |
649 | 0 | if (this->inbound_installed) |
650 | 0 | { |
651 | 0 | kernel_ipsec_sa_id_t id = { |
652 | 0 | .src = this->other_addr, |
653 | 0 | .dst = this->my_addr, |
654 | 0 | .spi = this->my_spi, |
655 | 0 | .proto = proto_ike2ip(this->protocol), |
656 | 0 | .mark = mark_in_sa(this), |
657 | 0 | .if_id = this->if_id_in, |
658 | 0 | }; |
659 | 0 | kernel_ipsec_query_sa_t query = {}; |
660 | |
|
661 | 0 | status = charon->kernel->query_sa(charon->kernel, &id, &query, |
662 | 0 | &bytes, &packets, &time); |
663 | 0 | if (status == SUCCESS) |
664 | 0 | { |
665 | 0 | if (bytes > this->my_usebytes) |
666 | 0 | { |
667 | 0 | this->my_usebytes = bytes; |
668 | 0 | this->my_usepackets = packets; |
669 | 0 | if (time) |
670 | 0 | { |
671 | 0 | this->my_usetime = time; |
672 | 0 | } |
673 | 0 | } |
674 | 0 | else |
675 | 0 | { |
676 | 0 | status = FAILED; |
677 | 0 | } |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } |
681 | 0 | else |
682 | 0 | { |
683 | 0 | if (this->outbound_state & CHILD_OUTBOUND_SA) |
684 | 0 | { |
685 | 0 | kernel_ipsec_sa_id_t id = { |
686 | 0 | .src = this->my_addr, |
687 | 0 | .dst = this->other_addr, |
688 | 0 | .spi = this->other_spi, |
689 | 0 | .proto = proto_ike2ip(this->protocol), |
690 | 0 | .mark = this->mark_out, |
691 | 0 | .if_id = this->if_id_out, |
692 | 0 | }; |
693 | 0 | kernel_ipsec_query_sa_t query = {}; |
694 | |
|
695 | 0 | status = charon->kernel->query_sa(charon->kernel, &id, &query, |
696 | 0 | &bytes, &packets, &time); |
697 | 0 | if (status == SUCCESS) |
698 | 0 | { |
699 | 0 | if (bytes > this->other_usebytes) |
700 | 0 | { |
701 | 0 | this->other_usebytes = bytes; |
702 | 0 | this->other_usepackets = packets; |
703 | 0 | if (time) |
704 | 0 | { |
705 | 0 | this->other_usetime = time; |
706 | 0 | } |
707 | 0 | } |
708 | 0 | else |
709 | 0 | { |
710 | 0 | status = FAILED; |
711 | 0 | } |
712 | 0 | } |
713 | 0 | } |
714 | 0 | } |
715 | 0 | return status; |
716 | 0 | } |
717 | | |
718 | | /** |
719 | | * updates the cached usetime |
720 | | */ |
721 | | static bool update_usetime(private_child_sa_t *this, bool inbound) |
722 | 0 | { |
723 | 0 | enumerator_t *enumerator; |
724 | 0 | traffic_selector_t *my_ts, *other_ts; |
725 | 0 | time_t last_use = 0; |
726 | |
|
727 | 0 | enumerator = create_policy_enumerator(this); |
728 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
729 | 0 | { |
730 | 0 | time_t in, out, fwd; |
731 | |
|
732 | 0 | if (inbound) |
733 | 0 | { |
734 | 0 | kernel_ipsec_policy_id_t id = { |
735 | 0 | .dir = POLICY_IN, |
736 | 0 | .src_ts = other_ts, |
737 | 0 | .dst_ts = my_ts, |
738 | 0 | .mark = this->mark_in, |
739 | 0 | .if_id = this->if_id_in, |
740 | 0 | .label = label_for(this, LABEL_USE_POLICY), |
741 | 0 | }; |
742 | 0 | kernel_ipsec_query_policy_t query = {}; |
743 | |
|
744 | 0 | if (charon->kernel->query_policy(charon->kernel, &id, &query, |
745 | 0 | &in) == SUCCESS) |
746 | 0 | { |
747 | 0 | last_use = max(last_use, in); |
748 | 0 | } |
749 | 0 | if (this->mode != MODE_TRANSPORT) |
750 | 0 | { |
751 | 0 | id.dir = POLICY_FWD; |
752 | 0 | if (charon->kernel->query_policy(charon->kernel, &id, &query, |
753 | 0 | &fwd) == SUCCESS) |
754 | 0 | { |
755 | 0 | last_use = max(last_use, fwd); |
756 | 0 | } |
757 | 0 | } |
758 | 0 | } |
759 | 0 | else |
760 | 0 | { |
761 | 0 | kernel_ipsec_policy_id_t id = { |
762 | 0 | .dir = POLICY_OUT, |
763 | 0 | .src_ts = my_ts, |
764 | 0 | .dst_ts = other_ts, |
765 | 0 | .mark = this->mark_out, |
766 | 0 | .if_id = this->if_id_out, |
767 | 0 | .interface = this->config->get_interface(this->config), |
768 | 0 | .label = label_for(this, LABEL_USE_POLICY), |
769 | 0 | }; |
770 | 0 | kernel_ipsec_query_policy_t query = {}; |
771 | |
|
772 | 0 | if (charon->kernel->query_policy(charon->kernel, &id, &query, |
773 | 0 | &out) == SUCCESS) |
774 | 0 | { |
775 | 0 | last_use = max(last_use, out); |
776 | 0 | } |
777 | 0 | } |
778 | 0 | } |
779 | 0 | enumerator->destroy(enumerator); |
780 | |
|
781 | 0 | if (last_use == 0) |
782 | 0 | { |
783 | 0 | return FALSE; |
784 | 0 | } |
785 | 0 | if (inbound) |
786 | 0 | { |
787 | 0 | this->my_usetime = last_use; |
788 | 0 | } |
789 | 0 | else |
790 | 0 | { |
791 | 0 | this->other_usetime = last_use; |
792 | 0 | } |
793 | 0 | return TRUE; |
794 | 0 | } |
795 | | |
796 | | METHOD(child_sa_t, get_usestats, void, |
797 | | private_child_sa_t *this, bool inbound, |
798 | | time_t *time, uint64_t *bytes, uint64_t *packets) |
799 | 0 | { |
800 | 0 | status_t status = NOT_SUPPORTED; |
801 | 0 | bool sa_use_time; |
802 | |
|
803 | 0 | sa_use_time = charon->kernel->get_features(charon->kernel) & KERNEL_SA_USE_TIME; |
804 | |
|
805 | 0 | if (bytes || packets || sa_use_time) |
806 | 0 | { |
807 | 0 | status = update_usebytes(this, inbound); |
808 | 0 | } |
809 | 0 | if (time && !sa_use_time && status != FAILED) |
810 | 0 | { /* query policies only if last use time is not available from SAs and |
811 | | * there was either traffic or querying the SA wasn't supported */ |
812 | 0 | update_usetime(this, inbound); |
813 | 0 | } |
814 | 0 | if (time) |
815 | 0 | { |
816 | 0 | *time = inbound ? this->my_usetime : this->other_usetime; |
817 | 0 | } |
818 | 0 | if (bytes) |
819 | 0 | { |
820 | 0 | *bytes = inbound ? this->my_usebytes : this->other_usebytes; |
821 | 0 | } |
822 | 0 | if (packets) |
823 | 0 | { |
824 | 0 | *packets = inbound ? this->my_usepackets : this->other_usepackets; |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | | METHOD(child_sa_t, get_mark, mark_t, |
829 | | private_child_sa_t *this, bool inbound) |
830 | 0 | { |
831 | 0 | return inbound ? this->mark_in : this->mark_out; |
832 | 0 | } |
833 | | |
834 | | METHOD(child_sa_t, get_if_id, uint32_t, |
835 | | private_child_sa_t *this, bool inbound) |
836 | 0 | { |
837 | 0 | return inbound ? this->if_id_in : this->if_id_out; |
838 | 0 | } |
839 | | |
840 | | METHOD(child_sa_t, get_label, sec_label_t*, |
841 | | private_child_sa_t *this) |
842 | 0 | { |
843 | 0 | return this->label ?: this->config->get_label(this->config); |
844 | 0 | } |
845 | | |
846 | | METHOD(child_sa_t, get_acquire_seq, uint32_t, |
847 | | private_child_sa_t *this) |
848 | 0 | { |
849 | 0 | return this->seq; |
850 | 0 | } |
851 | | |
852 | | METHOD(child_sa_t, set_acquire_seq, void, |
853 | | private_child_sa_t *this, uint32_t seq) |
854 | 0 | { |
855 | 0 | this->seq = seq; |
856 | 0 | } |
857 | | |
858 | | METHOD(child_sa_t, get_cpu, uint32_t, |
859 | | private_child_sa_t *this) |
860 | 0 | { |
861 | 0 | return this->cpu; |
862 | 0 | } |
863 | | |
864 | | METHOD(child_sa_t, use_per_cpu, bool, |
865 | | private_child_sa_t *this) |
866 | 0 | { |
867 | 0 | return this->per_cpu; |
868 | 0 | } |
869 | | |
870 | | METHOD(child_sa_t, set_per_cpu, void, |
871 | | private_child_sa_t *this, bool per_cpu) |
872 | 0 | { |
873 | 0 | if (!per_cpu) |
874 | 0 | { |
875 | 0 | this->cpu = CPU_ID_MAX; |
876 | 0 | } |
877 | 0 | this->per_cpu = per_cpu; |
878 | 0 | } |
879 | | |
880 | | METHOD(child_sa_t, get_lifetime, time_t, |
881 | | private_child_sa_t *this, bool hard) |
882 | 0 | { |
883 | 0 | return hard ? this->expire_time : this->rekey_time; |
884 | 0 | } |
885 | | |
886 | | METHOD(child_sa_t, get_installtime, time_t, |
887 | | private_child_sa_t *this) |
888 | 0 | { |
889 | 0 | return this->install_time; |
890 | 0 | } |
891 | | |
892 | | METHOD(child_sa_t, alloc_spi, uint32_t, |
893 | | private_child_sa_t *this, protocol_id_t protocol) |
894 | 0 | { |
895 | 0 | if (charon->kernel->get_spi(charon->kernel, this->other_addr, this->my_addr, |
896 | 0 | proto_ike2ip(protocol), &this->my_spi) == SUCCESS) |
897 | 0 | { |
898 | | /* if we allocate a SPI, but then are unable to establish the SA, we |
899 | | * need to know the protocol family to delete the partial SA */ |
900 | 0 | this->protocol = protocol; |
901 | 0 | return this->my_spi; |
902 | 0 | } |
903 | 0 | return 0; |
904 | 0 | } |
905 | | |
906 | | METHOD(child_sa_t, alloc_cpi, uint16_t, |
907 | | private_child_sa_t *this) |
908 | 0 | { |
909 | 0 | if (charon->kernel->get_cpi(charon->kernel, this->other_addr, this->my_addr, |
910 | 0 | &this->my_cpi) == SUCCESS) |
911 | 0 | { |
912 | 0 | return this->my_cpi; |
913 | 0 | } |
914 | 0 | return 0; |
915 | 0 | } |
916 | | |
917 | | /** |
918 | | * Allocate a reqid for the given local and remote traffic selector lists. |
919 | | * On success, release the previously allocated reqid. |
920 | | */ |
921 | | static status_t alloc_reqid_lists(private_child_sa_t *this, |
922 | | linked_list_t *my_ts, linked_list_t *other_ts, |
923 | | uint32_t *reqid) |
924 | 0 | { |
925 | 0 | uint32_t existing_reqid = *reqid; |
926 | 0 | status_t status; |
927 | |
|
928 | 0 | status = charon->kernel->alloc_reqid( |
929 | 0 | charon->kernel, my_ts, other_ts, |
930 | 0 | this->mark_in, this->mark_out, this->if_id_in, |
931 | 0 | this->if_id_out, label_for(this, LABEL_USE_REQID), |
932 | 0 | reqid); |
933 | |
|
934 | 0 | if (status == SUCCESS && existing_reqid) |
935 | 0 | { |
936 | 0 | if (charon->kernel->release_reqid(charon->kernel, |
937 | 0 | existing_reqid) != SUCCESS) |
938 | 0 | { |
939 | 0 | DBG1(DBG_CHD, "releasing previous reqid %u failed", existing_reqid); |
940 | 0 | } |
941 | 0 | } |
942 | 0 | return status; |
943 | 0 | } |
944 | | |
945 | | /** |
946 | | * Allocate a reqid for the given local and remote traffic selectors. |
947 | | */ |
948 | | static status_t alloc_reqid(private_child_sa_t *this, array_t *my_ts, |
949 | | array_t *other_ts, uint32_t *reqid) |
950 | 0 | { |
951 | 0 | linked_list_t *my_ts_list, *other_ts_list; |
952 | 0 | status_t status; |
953 | |
|
954 | 0 | my_ts_list = linked_list_create_from_enumerator(array_create_enumerator(my_ts)); |
955 | 0 | other_ts_list = linked_list_create_from_enumerator(array_create_enumerator(other_ts)); |
956 | 0 | status = alloc_reqid_lists(this, my_ts_list, other_ts_list, reqid); |
957 | 0 | my_ts_list->destroy(my_ts_list); |
958 | 0 | other_ts_list->destroy(other_ts_list); |
959 | 0 | return status; |
960 | 0 | } |
961 | | |
962 | | /** |
963 | | * Install the given SA in the kernel |
964 | | */ |
965 | | static status_t install_internal(private_child_sa_t *this, chunk_t encr, |
966 | | chunk_t integ, uint32_t spi, uint16_t cpi, bool initiator, bool inbound, |
967 | | bool tfcv3) |
968 | 0 | { |
969 | 0 | uint16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; |
970 | 0 | uint16_t esn = NO_EXT_SEQ_NUMBERS; |
971 | 0 | linked_list_t *my_ts, *other_ts, *src_ts, *dst_ts; |
972 | 0 | time_t now; |
973 | 0 | kernel_ipsec_sa_id_t id; |
974 | 0 | kernel_ipsec_add_sa_t sa; |
975 | 0 | lifetime_cfg_t *lifetime; |
976 | 0 | uint32_t tfc = 0; |
977 | 0 | host_t *src, *dst; |
978 | 0 | status_t status; |
979 | 0 | bool update = FALSE; |
980 | | |
981 | | /* BEET requires the bound address from the traffic selectors */ |
982 | 0 | my_ts = linked_list_create_from_enumerator( |
983 | 0 | array_create_enumerator(this->my_ts)); |
984 | 0 | other_ts = linked_list_create_from_enumerator( |
985 | 0 | array_create_enumerator(this->other_ts)); |
986 | | |
987 | | /* now we have to decide which spi to use. Use self allocated, if "in", |
988 | | * or the one in the proposal, if not "in" (others). Additionally, |
989 | | * source and dest host switch depending on the role */ |
990 | 0 | if (inbound) |
991 | 0 | { |
992 | 0 | dst = this->my_addr; |
993 | 0 | src = this->other_addr; |
994 | 0 | if (this->my_spi == spi) |
995 | 0 | { /* alloc_spi has been called, do an SA update */ |
996 | 0 | update = TRUE; |
997 | 0 | } |
998 | 0 | this->my_spi = spi; |
999 | 0 | this->my_cpi = cpi; |
1000 | 0 | dst_ts = my_ts; |
1001 | 0 | src_ts = other_ts; |
1002 | |
|
1003 | 0 | if (this->per_cpu && this->encap) |
1004 | 0 | { |
1005 | 0 | src = src->clone(src); |
1006 | | /* accept inbound traffic from any port as we don't know if the |
1007 | | * peer uses random ports or not */ |
1008 | 0 | src->set_port(src, 0); |
1009 | 0 | } |
1010 | 0 | } |
1011 | 0 | else |
1012 | 0 | { |
1013 | 0 | src = this->my_addr; |
1014 | 0 | dst = this->other_addr; |
1015 | 0 | this->other_spi = spi; |
1016 | 0 | this->other_cpi = cpi; |
1017 | 0 | src_ts = my_ts; |
1018 | 0 | dst_ts = other_ts; |
1019 | |
|
1020 | 0 | if (tfcv3) |
1021 | 0 | { |
1022 | 0 | tfc = this->config->get_tfc(this->config); |
1023 | 0 | } |
1024 | 0 | if (this->per_cpu && this->encap && |
1025 | 0 | this->config->has_option(this->config, OPT_PER_CPU_SAS_ENCAP)) |
1026 | 0 | { |
1027 | 0 | src = src->clone(src); |
1028 | | /* use a random source port between 49152 and 65535. doesn't matter |
1029 | | * if it's free or not as we don't receive traffic on it */ |
1030 | 0 | src->set_port(src, 0xc000 | (random() & 0xffff)); |
1031 | 0 | } |
1032 | 0 | } |
1033 | |
|
1034 | 0 | DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", |
1035 | 0 | protocol_id_names, this->protocol); |
1036 | | |
1037 | | /* send SA down to the kernel */ |
1038 | 0 | DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); |
1039 | |
|
1040 | 0 | this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM, |
1041 | 0 | &enc_alg, &size); |
1042 | 0 | this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM, |
1043 | 0 | &int_alg, &size); |
1044 | 0 | this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS, |
1045 | 0 | &esn, NULL); |
1046 | |
|
1047 | 0 | if (int_alg == AUTH_HMAC_SHA2_256_128 && |
1048 | 0 | this->config->has_option(this->config, OPT_SHA256_96)) |
1049 | 0 | { |
1050 | 0 | DBG2(DBG_CHD, " using %N with 96-bit truncation", |
1051 | 0 | integrity_algorithm_names, int_alg); |
1052 | 0 | int_alg = AUTH_HMAC_SHA2_256_96; |
1053 | 0 | } |
1054 | |
|
1055 | 0 | if (!this->reqid_allocated && !this->static_reqid) |
1056 | 0 | { |
1057 | 0 | status = alloc_reqid_lists(this, my_ts, other_ts, &this->reqid); |
1058 | 0 | if (status != SUCCESS) |
1059 | 0 | { |
1060 | 0 | my_ts->destroy(my_ts); |
1061 | 0 | other_ts->destroy(other_ts); |
1062 | 0 | return status; |
1063 | 0 | } |
1064 | 0 | this->reqid_allocated = TRUE; |
1065 | 0 | } |
1066 | | |
1067 | 0 | lifetime = this->config->get_lifetime(this->config, TRUE); |
1068 | |
|
1069 | 0 | now = time_monotonic(NULL); |
1070 | 0 | if (lifetime->time.rekey) |
1071 | 0 | { |
1072 | 0 | if (this->rekey_time) |
1073 | 0 | { |
1074 | 0 | this->rekey_time = min(this->rekey_time, now + lifetime->time.rekey); |
1075 | 0 | } |
1076 | 0 | else |
1077 | 0 | { |
1078 | 0 | this->rekey_time = now + lifetime->time.rekey; |
1079 | 0 | } |
1080 | 0 | } |
1081 | 0 | if (lifetime->time.life) |
1082 | 0 | { |
1083 | 0 | this->expire_time = now + lifetime->time.life; |
1084 | 0 | } |
1085 | |
|
1086 | 0 | if (!lifetime->time.jitter && !inbound) |
1087 | 0 | { /* avoid triggering multiple rekey events */ |
1088 | 0 | lifetime->time.rekey = 0; |
1089 | 0 | } |
1090 | |
|
1091 | 0 | id = (kernel_ipsec_sa_id_t){ |
1092 | 0 | .src = src, |
1093 | 0 | .dst = dst, |
1094 | 0 | .spi = spi, |
1095 | 0 | .proto = proto_ike2ip(this->protocol), |
1096 | 0 | .mark = inbound ? mark_in_sa(this) : this->mark_out, |
1097 | 0 | .if_id = inbound ? this->if_id_in : this->if_id_out, |
1098 | 0 | }; |
1099 | 0 | sa = (kernel_ipsec_add_sa_t){ |
1100 | 0 | .reqid = this->reqid, |
1101 | 0 | .seq = this->seq, |
1102 | 0 | .mode = this->mode, |
1103 | 0 | .src_ts = src_ts, |
1104 | 0 | .dst_ts = dst_ts, |
1105 | 0 | .interface = inbound ? NULL : this->config->get_interface(this->config), |
1106 | 0 | .lifetime = lifetime, |
1107 | 0 | .enc_alg = enc_alg, |
1108 | 0 | .enc_key = encr, |
1109 | 0 | .int_alg = int_alg, |
1110 | 0 | .int_key = integ, |
1111 | 0 | .replay_window = this->config->get_replay_window(this->config), |
1112 | 0 | .tfc = tfc, |
1113 | 0 | .ipcomp = this->ipcomp, |
1114 | 0 | .cpi = cpi, |
1115 | 0 | .encap = this->encap, |
1116 | 0 | .cpu = this->per_cpu ? this->cpu : CPU_ID_MAX, |
1117 | 0 | .hw_offload = this->config->get_hw_offload(this->config), |
1118 | 0 | .mark = this->config->get_set_mark(this->config, inbound), |
1119 | 0 | .esn = esn, |
1120 | 0 | .copy_df = !this->config->has_option(this->config, OPT_NO_COPY_DF), |
1121 | 0 | .copy_ecn = !this->config->has_option(this->config, OPT_NO_COPY_ECN), |
1122 | 0 | .copy_dscp = this->config->get_copy_dscp(this->config), |
1123 | 0 | .iptfs_dont_frag = this->iptfs_dont_frag, |
1124 | 0 | .forward_icmp = this->config->has_option(this->config, OPT_FORWARD_ICMP), |
1125 | 0 | .label = label_for(this, LABEL_USE_SA), |
1126 | 0 | .initiator = initiator, |
1127 | 0 | .inbound = inbound, |
1128 | 0 | .update = update, |
1129 | 0 | }; |
1130 | |
|
1131 | 0 | if (sa.mark.value == MARK_SAME) |
1132 | 0 | { |
1133 | 0 | sa.mark.value = inbound ? this->mark_in.value : this->mark_out.value; |
1134 | 0 | } |
1135 | |
|
1136 | 0 | status = charon->kernel->add_sa(charon->kernel, &id, &sa); |
1137 | |
|
1138 | 0 | if (src != this->my_addr && src != this->other_addr) |
1139 | 0 | { |
1140 | 0 | src->destroy(src); |
1141 | 0 | } |
1142 | 0 | my_ts->destroy(my_ts); |
1143 | 0 | other_ts->destroy(other_ts); |
1144 | 0 | free(lifetime); |
1145 | |
|
1146 | 0 | if (status == SUCCESS) |
1147 | 0 | { |
1148 | 0 | if (inbound) |
1149 | 0 | { |
1150 | 0 | this->inbound_installed = TRUE; |
1151 | 0 | } |
1152 | 0 | else |
1153 | 0 | { |
1154 | 0 | this->outbound_state |= CHILD_OUTBOUND_SA; |
1155 | 0 | } |
1156 | 0 | } |
1157 | 0 | return status; |
1158 | 0 | } |
1159 | | |
1160 | | METHOD(child_sa_t, install, status_t, |
1161 | | private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, |
1162 | | uint16_t cpi, bool initiator, bool inbound, bool tfcv3) |
1163 | 0 | { |
1164 | 0 | return install_internal(this, encr, integ, spi, cpi, initiator, inbound, |
1165 | 0 | tfcv3); |
1166 | 0 | } |
1167 | | |
1168 | | /** |
1169 | | * Check whether to install policies for this CHILD_SA |
1170 | | */ |
1171 | | static bool require_policies(private_child_sa_t *this) |
1172 | 0 | { |
1173 | 0 | return !this->config->has_option(this->config, OPT_NO_POLICIES); |
1174 | 0 | } |
1175 | | |
1176 | | /** |
1177 | | * Check if policy updates are required |
1178 | | */ |
1179 | | static bool require_policy_update(private_child_sa_t *this) |
1180 | 0 | { |
1181 | 0 | kernel_feature_t f; |
1182 | |
|
1183 | 0 | if (!require_policies(this)) |
1184 | 0 | { |
1185 | 0 | return FALSE; |
1186 | 0 | } |
1187 | | |
1188 | 0 | f = charon->kernel->get_features(charon->kernel); |
1189 | 0 | return !(f & KERNEL_NO_POLICY_UPDATES); |
1190 | 0 | } |
1191 | | |
1192 | | /** |
1193 | | * Prepare SA config to install/delete policies |
1194 | | */ |
1195 | | static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, |
1196 | | ipsec_sa_cfg_t *other_sa) |
1197 | 0 | { |
1198 | 0 | enumerator_t *enumerator; |
1199 | |
|
1200 | 0 | *my_sa = (ipsec_sa_cfg_t){ |
1201 | 0 | .mode = this->mode, |
1202 | 0 | .reqid = this->reqid, |
1203 | 0 | .ipcomp = { |
1204 | 0 | .transform = this->ipcomp, |
1205 | 0 | }, |
1206 | 0 | }; |
1207 | 0 | *other_sa = *my_sa; |
1208 | |
|
1209 | 0 | my_sa->ipcomp.cpi = this->my_cpi; |
1210 | 0 | other_sa->ipcomp.cpi = this->other_cpi; |
1211 | |
|
1212 | 0 | if (this->protocol == PROTO_ESP) |
1213 | 0 | { |
1214 | 0 | my_sa->esp.use = TRUE; |
1215 | 0 | my_sa->esp.spi = this->my_spi; |
1216 | 0 | other_sa->esp.use = TRUE; |
1217 | 0 | other_sa->esp.spi = this->other_spi; |
1218 | 0 | } |
1219 | 0 | else |
1220 | 0 | { |
1221 | 0 | my_sa->ah.use = TRUE; |
1222 | 0 | my_sa->ah.spi = this->my_spi; |
1223 | 0 | other_sa->ah.use = TRUE; |
1224 | 0 | other_sa->ah.spi = this->other_spi; |
1225 | 0 | } |
1226 | |
|
1227 | 0 | enumerator = create_policy_enumerator(this); |
1228 | 0 | while (enumerator->enumerate(enumerator, NULL, NULL)) |
1229 | 0 | { |
1230 | 0 | my_sa->policy_count++; |
1231 | 0 | other_sa->policy_count++; |
1232 | 0 | } |
1233 | 0 | enumerator->destroy(enumerator); |
1234 | 0 | } |
1235 | | |
1236 | | /** |
1237 | | * Install inbound policies: in, fwd |
1238 | | */ |
1239 | | static status_t install_policies_inbound(private_child_sa_t *this, |
1240 | | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1241 | | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, |
1242 | | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1243 | | policy_priority_t priority, uint32_t manual_prio) |
1244 | 0 | { |
1245 | 0 | kernel_ipsec_policy_id_t in_id = { |
1246 | 0 | .dir = POLICY_IN, |
1247 | 0 | .src_ts = other_ts, |
1248 | 0 | .dst_ts = my_ts, |
1249 | 0 | .mark = this->mark_in, |
1250 | 0 | .if_id = this->if_id_in, |
1251 | 0 | .label = label_for(this, LABEL_USE_POLICY), |
1252 | 0 | }; |
1253 | 0 | kernel_ipsec_manage_policy_t in_policy = { |
1254 | 0 | .type = type, |
1255 | 0 | .prio = priority, |
1256 | 0 | .manual_prio = manual_prio, |
1257 | 0 | .hw_offload = this->config->get_hw_offload(this->config), |
1258 | 0 | .src = other_addr, |
1259 | 0 | .dst = my_addr, |
1260 | 0 | .sa = my_sa, |
1261 | 0 | }; |
1262 | 0 | status_t status = SUCCESS; |
1263 | |
|
1264 | 0 | status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); |
1265 | 0 | if (this->mode != MODE_TRANSPORT) |
1266 | 0 | { |
1267 | 0 | in_id.dir = POLICY_FWD; |
1268 | 0 | status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); |
1269 | 0 | } |
1270 | 0 | return status; |
1271 | 0 | } |
1272 | | |
1273 | | /** |
1274 | | * Install outbound policies: out, [fwd] |
1275 | | */ |
1276 | | static status_t install_policies_outbound(private_child_sa_t *this, |
1277 | | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1278 | | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, |
1279 | | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1280 | | policy_priority_t priority, uint32_t manual_prio) |
1281 | 0 | { |
1282 | 0 | kernel_ipsec_policy_id_t out_id = { |
1283 | 0 | .dir = POLICY_OUT, |
1284 | 0 | .src_ts = my_ts, |
1285 | 0 | .dst_ts = other_ts, |
1286 | 0 | .mark = this->mark_out, |
1287 | 0 | .if_id = this->if_id_out, |
1288 | 0 | .interface = this->config->get_interface(this->config), |
1289 | 0 | .label = label_for(this, LABEL_USE_POLICY), |
1290 | 0 | }; |
1291 | 0 | kernel_ipsec_manage_policy_t out_policy = { |
1292 | 0 | .type = type, |
1293 | 0 | .prio = priority, |
1294 | 0 | .manual_prio = manual_prio, |
1295 | 0 | .hw_offload = this->config->get_hw_offload(this->config), |
1296 | 0 | .src = my_addr, |
1297 | 0 | .dst = other_addr, |
1298 | 0 | .pcpu_acquires = this->per_cpu, |
1299 | 0 | .forward_icmp = this->config->has_option(this->config, OPT_FORWARD_ICMP), |
1300 | 0 | .sa = other_sa, |
1301 | 0 | }; |
1302 | 0 | uint32_t reqid = other_sa->reqid; |
1303 | 0 | status_t status = SUCCESS; |
1304 | |
|
1305 | 0 | status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy); |
1306 | |
|
1307 | 0 | if (this->mode != MODE_TRANSPORT && this->policies_fwd_out) |
1308 | 0 | { |
1309 | | /* install an "outbound" FWD policy in case there is a drop policy |
1310 | | * matching outbound forwarded traffic, to allow another tunnel to use |
1311 | | * the reversed subnets and do the same we don't set a reqid (this also |
1312 | | * allows the kernel backend to distinguish between the two types of |
1313 | | * FWD policies). To avoid problems with symmetrically overlapping |
1314 | | * policies of two SAs we install them with reduced priority. As they |
1315 | | * basically act as bypass policies for drop policies we use a higher |
1316 | | * priority than is used for them. */ |
1317 | 0 | out_id.dir = POLICY_FWD; |
1318 | 0 | other_sa->reqid = 0; |
1319 | 0 | if (priority == POLICY_PRIORITY_DEFAULT) |
1320 | 0 | { |
1321 | 0 | out_policy.prio = POLICY_PRIORITY_ROUTED; |
1322 | 0 | } |
1323 | 0 | status |= charon->kernel->add_policy(charon->kernel, &out_id, |
1324 | 0 | &out_policy); |
1325 | | /* reset the reqid for any other further policies */ |
1326 | 0 | other_sa->reqid = reqid; |
1327 | 0 | } |
1328 | 0 | return status; |
1329 | 0 | } |
1330 | | |
1331 | | /** |
1332 | | * Install all policies |
1333 | | */ |
1334 | | static status_t install_policies_internal(private_child_sa_t *this, |
1335 | | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1336 | | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, |
1337 | | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1338 | | policy_priority_t priority, uint32_t manual_prio, bool outbound) |
1339 | 0 | { |
1340 | 0 | status_t status = SUCCESS; |
1341 | |
|
1342 | 0 | status |= install_policies_inbound(this, my_addr, other_addr, my_ts, |
1343 | 0 | other_ts, my_sa, other_sa, type, priority, manual_prio); |
1344 | 0 | if (outbound) |
1345 | 0 | { |
1346 | 0 | status |= install_policies_outbound(this, my_addr, other_addr, my_ts, |
1347 | 0 | other_ts, my_sa, other_sa, type, priority, manual_prio); |
1348 | 0 | } |
1349 | 0 | return status; |
1350 | 0 | } |
1351 | | |
1352 | | /** |
1353 | | * Delete inbound policies: in, fwd |
1354 | | */ |
1355 | | static void del_policies_inbound(private_child_sa_t *this, |
1356 | | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1357 | | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, |
1358 | | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1359 | | policy_priority_t priority, uint32_t manual_prio) |
1360 | 0 | { |
1361 | 0 | kernel_ipsec_policy_id_t in_id = { |
1362 | 0 | .dir = POLICY_IN, |
1363 | 0 | .src_ts = other_ts, |
1364 | 0 | .dst_ts = my_ts, |
1365 | 0 | .mark = this->mark_in, |
1366 | 0 | .if_id = this->if_id_in, |
1367 | 0 | .label = label_for(this, LABEL_USE_POLICY), |
1368 | 0 | }; |
1369 | 0 | kernel_ipsec_manage_policy_t in_policy = { |
1370 | 0 | .type = type, |
1371 | 0 | .prio = priority, |
1372 | 0 | .manual_prio = manual_prio, |
1373 | 0 | .hw_offload = this->config->get_hw_offload(this->config), |
1374 | 0 | .src = other_addr, |
1375 | 0 | .dst = my_addr, |
1376 | 0 | .sa = my_sa, |
1377 | 0 | }; |
1378 | |
|
1379 | 0 | charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); |
1380 | |
|
1381 | 0 | if (this->mode != MODE_TRANSPORT) |
1382 | 0 | { |
1383 | 0 | in_id.dir = POLICY_FWD; |
1384 | 0 | charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); |
1385 | 0 | } |
1386 | 0 | } |
1387 | | |
1388 | | /** |
1389 | | * Delete outbound policies: out, [fwd] |
1390 | | */ |
1391 | | static void del_policies_outbound(private_child_sa_t *this, |
1392 | | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1393 | | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, |
1394 | | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1395 | | policy_priority_t priority, uint32_t manual_prio) |
1396 | 0 | { |
1397 | 0 | kernel_ipsec_policy_id_t out_id = { |
1398 | 0 | .dir = POLICY_OUT, |
1399 | 0 | .src_ts = my_ts, |
1400 | 0 | .dst_ts = other_ts, |
1401 | 0 | .mark = this->mark_out, |
1402 | 0 | .if_id = this->if_id_out, |
1403 | 0 | .interface = this->config->get_interface(this->config), |
1404 | 0 | .label = label_for(this, LABEL_USE_POLICY), |
1405 | 0 | }; |
1406 | 0 | kernel_ipsec_manage_policy_t out_policy = { |
1407 | 0 | .type = type, |
1408 | 0 | .prio = priority, |
1409 | 0 | .manual_prio = manual_prio, |
1410 | 0 | .hw_offload = this->config->get_hw_offload(this->config), |
1411 | 0 | .src = my_addr, |
1412 | 0 | .dst = other_addr, |
1413 | 0 | .pcpu_acquires = this->per_cpu, |
1414 | 0 | .forward_icmp = this->config->has_option(this->config, OPT_FORWARD_ICMP), |
1415 | 0 | .sa = other_sa, |
1416 | 0 | }; |
1417 | 0 | uint32_t reqid = other_sa->reqid; |
1418 | |
|
1419 | 0 | charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); |
1420 | |
|
1421 | 0 | if (this->mode != MODE_TRANSPORT && this->policies_fwd_out) |
1422 | 0 | { |
1423 | 0 | out_id.dir = POLICY_FWD; |
1424 | 0 | other_sa->reqid = 0; |
1425 | 0 | if (priority == POLICY_PRIORITY_DEFAULT) |
1426 | 0 | { |
1427 | 0 | out_policy.prio = POLICY_PRIORITY_ROUTED; |
1428 | 0 | } |
1429 | 0 | charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); |
1430 | 0 | other_sa->reqid = reqid; |
1431 | 0 | } |
1432 | 0 | } |
1433 | | |
1434 | | /** |
1435 | | * Delete in- and outbound policies |
1436 | | */ |
1437 | | static void del_policies_internal(private_child_sa_t *this, |
1438 | | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1439 | | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, |
1440 | | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1441 | | policy_priority_t priority, uint32_t manual_prio, bool outbound) |
1442 | 0 | { |
1443 | 0 | if (outbound) |
1444 | 0 | { |
1445 | 0 | del_policies_outbound(this, my_addr, other_addr, my_ts, other_ts, my_sa, |
1446 | 0 | other_sa, type, priority, manual_prio); |
1447 | 0 | } |
1448 | 0 | del_policies_inbound(this, my_addr, other_addr, my_ts, other_ts, my_sa, |
1449 | 0 | other_sa, type, priority, manual_prio); |
1450 | 0 | } |
1451 | | |
1452 | | METHOD(child_sa_t, set_policies, void, |
1453 | | private_child_sa_t *this, linked_list_t *my_ts_list, |
1454 | | linked_list_t *other_ts_list) |
1455 | 0 | { |
1456 | 0 | enumerator_t *enumerator; |
1457 | 0 | traffic_selector_t *my_ts, *other_ts; |
1458 | |
|
1459 | 0 | if (array_count(this->my_ts)) |
1460 | 0 | { |
1461 | 0 | array_destroy_offset(this->my_ts, |
1462 | 0 | offsetof(traffic_selector_t, destroy)); |
1463 | 0 | this->my_ts = array_create(0, 0); |
1464 | 0 | } |
1465 | 0 | enumerator = my_ts_list->create_enumerator(my_ts_list); |
1466 | 0 | while (enumerator->enumerate(enumerator, &my_ts)) |
1467 | 0 | { |
1468 | 0 | array_insert(this->my_ts, ARRAY_TAIL, my_ts->clone(my_ts)); |
1469 | 0 | } |
1470 | 0 | enumerator->destroy(enumerator); |
1471 | 0 | array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL); |
1472 | |
|
1473 | 0 | if (array_count(this->other_ts)) |
1474 | 0 | { |
1475 | 0 | array_destroy_offset(this->other_ts, |
1476 | 0 | offsetof(traffic_selector_t, destroy)); |
1477 | 0 | this->other_ts = array_create(0, 0); |
1478 | 0 | } |
1479 | 0 | enumerator = other_ts_list->create_enumerator(other_ts_list); |
1480 | 0 | while (enumerator->enumerate(enumerator, &other_ts)) |
1481 | 0 | { |
1482 | 0 | array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts)); |
1483 | 0 | } |
1484 | 0 | enumerator->destroy(enumerator); |
1485 | 0 | array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL); |
1486 | 0 | } |
1487 | | |
1488 | | METHOD(child_sa_t, install_policies, status_t, |
1489 | | private_child_sa_t *this) |
1490 | 0 | { |
1491 | 0 | enumerator_t *enumerator; |
1492 | 0 | traffic_selector_t *my_ts, *other_ts; |
1493 | 0 | status_t status = SUCCESS; |
1494 | 0 | bool install_outbound = FALSE; |
1495 | |
|
1496 | 0 | if (!this->reqid_allocated && !this->static_reqid) |
1497 | 0 | { |
1498 | 0 | status = alloc_reqid(this, this->my_ts, this->other_ts, &this->reqid); |
1499 | 0 | if (status != SUCCESS) |
1500 | 0 | { |
1501 | 0 | return status; |
1502 | 0 | } |
1503 | 0 | this->reqid_allocated = TRUE; |
1504 | 0 | } |
1505 | | |
1506 | 0 | if (!(this->outbound_state & CHILD_OUTBOUND_REGISTERED)) |
1507 | 0 | { |
1508 | 0 | install_outbound = TRUE; |
1509 | 0 | this->outbound_state |= CHILD_OUTBOUND_POLICIES; |
1510 | 0 | } |
1511 | |
|
1512 | 0 | if (require_policies(this)) |
1513 | 0 | { |
1514 | 0 | policy_priority_t priority; |
1515 | 0 | ipsec_sa_cfg_t my_sa, other_sa; |
1516 | 0 | uint32_t manual_prio; |
1517 | |
|
1518 | 0 | prepare_sa_cfg(this, &my_sa, &other_sa); |
1519 | 0 | manual_prio = this->config->get_manual_prio(this->config); |
1520 | | |
1521 | | /* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD |
1522 | | * entry) we install a trap policy */ |
1523 | 0 | this->trap = this->state == CHILD_CREATED; |
1524 | 0 | priority = this->trap ? POLICY_PRIORITY_ROUTED |
1525 | 0 | : POLICY_PRIORITY_DEFAULT; |
1526 | | |
1527 | | /* enumerate pairs of traffic selectors */ |
1528 | 0 | enumerator = create_policy_enumerator(this); |
1529 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
1530 | 0 | { |
1531 | 0 | status |= install_policies_internal(this, this->my_addr, |
1532 | 0 | this->other_addr, my_ts, other_ts, |
1533 | 0 | &my_sa, &other_sa, POLICY_IPSEC, priority, |
1534 | 0 | manual_prio, install_outbound); |
1535 | 0 | if (status != SUCCESS) |
1536 | 0 | { |
1537 | 0 | break; |
1538 | 0 | } |
1539 | 0 | } |
1540 | 0 | enumerator->destroy(enumerator); |
1541 | 0 | } |
1542 | |
|
1543 | 0 | if (status == SUCCESS && this->trap) |
1544 | 0 | { |
1545 | 0 | set_state(this, CHILD_ROUTED); |
1546 | 0 | } |
1547 | 0 | return status; |
1548 | 0 | } |
1549 | | |
1550 | | /** |
1551 | | * Check if we can install the outbound SA immediately. |
1552 | | * |
1553 | | * If the kernel supports installing SPIs with policies, we can do so as it |
1554 | | * will only be used once we update the policies. |
1555 | | * |
1556 | | * However, if we use labels with SELinux, we can't as we don't set SPIs |
1557 | | * on the policy in order to match SAs with other labels that match the generic |
1558 | | * label that's used on the policies. The same is the case for per-CPU SAs. |
1559 | | */ |
1560 | | static bool install_outbound_immediately(private_child_sa_t *this) |
1561 | 0 | { |
1562 | 0 | if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI && |
1563 | 0 | !this->per_cpu) |
1564 | 0 | { |
1565 | 0 | if (this->config->get_label_mode(this->config) == SEC_LABEL_MODE_SELINUX) |
1566 | 0 | { |
1567 | 0 | return !this->label; |
1568 | 0 | } |
1569 | 0 | return TRUE; |
1570 | 0 | } |
1571 | 0 | return FALSE; |
1572 | 0 | } |
1573 | | |
1574 | | METHOD(child_sa_t, register_outbound, status_t, |
1575 | | private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, |
1576 | | uint16_t cpi, bool initiator, bool tfcv3) |
1577 | 0 | { |
1578 | 0 | status_t status; |
1579 | |
|
1580 | 0 | if (install_outbound_immediately(this)) |
1581 | 0 | { |
1582 | 0 | status = install_internal(this, encr, integ, spi, cpi, initiator, FALSE, |
1583 | 0 | tfcv3); |
1584 | 0 | } |
1585 | 0 | else |
1586 | 0 | { |
1587 | 0 | DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, |
1588 | 0 | this->protocol); |
1589 | 0 | DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, |
1590 | 0 | this->other_addr); |
1591 | |
|
1592 | 0 | this->other_spi = spi; |
1593 | 0 | this->other_cpi = cpi; |
1594 | 0 | this->encr_r = chunk_clone(encr); |
1595 | 0 | this->integ_r = chunk_clone(integ); |
1596 | 0 | this->initiator = initiator; |
1597 | 0 | this->tfcv3 = tfcv3; |
1598 | 0 | status = SUCCESS; |
1599 | 0 | } |
1600 | 0 | this->outbound_state |= CHILD_OUTBOUND_REGISTERED; |
1601 | 0 | return status; |
1602 | 0 | } |
1603 | | |
1604 | | METHOD(child_sa_t, install_outbound, status_t, |
1605 | | private_child_sa_t *this) |
1606 | 0 | { |
1607 | 0 | enumerator_t *enumerator; |
1608 | 0 | traffic_selector_t *my_ts, *other_ts; |
1609 | 0 | status_t status = SUCCESS; |
1610 | |
|
1611 | 0 | if (!(this->outbound_state & CHILD_OUTBOUND_SA)) |
1612 | 0 | { |
1613 | 0 | status = install_internal(this, this->encr_r, this->integ_r, |
1614 | 0 | this->other_spi, this->other_cpi, |
1615 | 0 | this->initiator, FALSE, this->tfcv3); |
1616 | 0 | chunk_clear(&this->encr_r); |
1617 | 0 | chunk_clear(&this->integ_r); |
1618 | 0 | } |
1619 | 0 | this->outbound_state &= ~CHILD_OUTBOUND_REGISTERED; |
1620 | 0 | if (status != SUCCESS) |
1621 | 0 | { |
1622 | 0 | return status; |
1623 | 0 | } |
1624 | 0 | if (require_policies(this) && |
1625 | 0 | !(this->outbound_state & CHILD_OUTBOUND_POLICIES)) |
1626 | 0 | { |
1627 | 0 | ipsec_sa_cfg_t my_sa, other_sa; |
1628 | 0 | uint32_t manual_prio; |
1629 | |
|
1630 | 0 | prepare_sa_cfg(this, &my_sa, &other_sa); |
1631 | 0 | manual_prio = this->config->get_manual_prio(this->config); |
1632 | |
|
1633 | 0 | enumerator = create_policy_enumerator(this); |
1634 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
1635 | 0 | { |
1636 | 0 | status |= install_policies_outbound(this, this->my_addr, |
1637 | 0 | this->other_addr, my_ts, other_ts, |
1638 | 0 | &my_sa, &other_sa, POLICY_IPSEC, |
1639 | 0 | POLICY_PRIORITY_DEFAULT, manual_prio); |
1640 | 0 | if (status != SUCCESS) |
1641 | 0 | { |
1642 | 0 | break; |
1643 | 0 | } |
1644 | 0 | } |
1645 | 0 | enumerator->destroy(enumerator); |
1646 | 0 | } |
1647 | 0 | this->outbound_state |= CHILD_OUTBOUND_POLICIES; |
1648 | 0 | return status; |
1649 | 0 | } |
1650 | | |
1651 | | METHOD(child_sa_t, remove_outbound, void, |
1652 | | private_child_sa_t *this) |
1653 | 0 | { |
1654 | 0 | enumerator_t *enumerator; |
1655 | 0 | traffic_selector_t *my_ts, *other_ts; |
1656 | |
|
1657 | 0 | if (!(this->outbound_state & CHILD_OUTBOUND_SA)) |
1658 | 0 | { |
1659 | 0 | if (this->outbound_state & CHILD_OUTBOUND_REGISTERED) |
1660 | 0 | { |
1661 | 0 | chunk_clear(&this->encr_r); |
1662 | 0 | chunk_clear(&this->integ_r); |
1663 | 0 | this->outbound_state = CHILD_OUTBOUND_NONE; |
1664 | 0 | } |
1665 | 0 | return; |
1666 | 0 | } |
1667 | | |
1668 | 0 | if (require_policies(this) && |
1669 | 0 | (this->outbound_state & CHILD_OUTBOUND_POLICIES)) |
1670 | 0 | { |
1671 | 0 | ipsec_sa_cfg_t my_sa, other_sa; |
1672 | 0 | uint32_t manual_prio; |
1673 | |
|
1674 | 0 | prepare_sa_cfg(this, &my_sa, &other_sa); |
1675 | 0 | manual_prio = this->config->get_manual_prio(this->config); |
1676 | |
|
1677 | 0 | enumerator = create_policy_enumerator(this); |
1678 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
1679 | 0 | { |
1680 | 0 | del_policies_outbound(this, this->my_addr, this->other_addr, |
1681 | 0 | my_ts, other_ts, &my_sa, &other_sa, |
1682 | 0 | POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, |
1683 | 0 | manual_prio); |
1684 | 0 | } |
1685 | 0 | enumerator->destroy(enumerator); |
1686 | 0 | } |
1687 | |
|
1688 | 0 | kernel_ipsec_sa_id_t id = { |
1689 | 0 | .src = this->my_addr, |
1690 | 0 | .dst = this->other_addr, |
1691 | 0 | .spi = this->other_spi, |
1692 | 0 | .proto = proto_ike2ip(this->protocol), |
1693 | 0 | .mark = this->mark_out, |
1694 | 0 | .if_id = this->if_id_out, |
1695 | 0 | }; |
1696 | 0 | kernel_ipsec_del_sa_t sa = { |
1697 | 0 | .cpi = this->other_cpi, |
1698 | 0 | }; |
1699 | 0 | charon->kernel->del_sa(charon->kernel, &id, &sa); |
1700 | 0 | this->outbound_state = CHILD_OUTBOUND_NONE; |
1701 | 0 | } |
1702 | | |
1703 | | METHOD(child_sa_t, set_rekey_sa, void, |
1704 | | private_child_sa_t *this, child_sa_t *sa) |
1705 | 0 | { |
1706 | 0 | this->rekey_sa = sa; |
1707 | 0 | } |
1708 | | |
1709 | | METHOD(child_sa_t, get_rekey_sa, child_sa_t*, |
1710 | | private_child_sa_t *this) |
1711 | 0 | { |
1712 | 0 | return this->rekey_sa; |
1713 | 0 | } |
1714 | | |
1715 | | CALLBACK(reinstall_vip, void, |
1716 | | host_t *vip, va_list args) |
1717 | 0 | { |
1718 | 0 | host_t *me; |
1719 | 0 | char *iface; |
1720 | |
|
1721 | 0 | VA_ARGS_VGET(args, me); |
1722 | 0 | if (charon->kernel->get_interface(charon->kernel, me, &iface)) |
1723 | 0 | { |
1724 | 0 | charon->kernel->del_ip(charon->kernel, vip, -1, TRUE); |
1725 | 0 | charon->kernel->add_ip(charon->kernel, vip, -1, iface); |
1726 | 0 | free(iface); |
1727 | 0 | } |
1728 | 0 | } |
1729 | | |
1730 | | /** |
1731 | | * Update addresses and encap state of IPsec SAs in the kernel |
1732 | | */ |
1733 | | static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other, |
1734 | | bool encap, uint32_t reqid) |
1735 | 0 | { |
1736 | | /* update our (initiator) SA */ |
1737 | 0 | if (this->inbound_installed) |
1738 | 0 | { |
1739 | 0 | kernel_ipsec_sa_id_t id = { |
1740 | 0 | .src = this->other_addr, |
1741 | 0 | .dst = this->my_addr, |
1742 | 0 | .spi = this->my_spi, |
1743 | 0 | .proto = proto_ike2ip(this->protocol), |
1744 | 0 | .mark = mark_in_sa(this), |
1745 | 0 | .if_id = this->if_id_in, |
1746 | 0 | }; |
1747 | 0 | kernel_ipsec_update_sa_t sa = { |
1748 | 0 | .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, |
1749 | 0 | .new_src = other, |
1750 | 0 | .new_dst = me, |
1751 | 0 | .encap = this->encap, |
1752 | 0 | .new_encap = encap, |
1753 | 0 | .new_reqid = reqid, |
1754 | 0 | }; |
1755 | 0 | if (charon->kernel->update_sa(charon->kernel, &id, |
1756 | 0 | &sa) == NOT_SUPPORTED) |
1757 | 0 | { |
1758 | 0 | return NOT_SUPPORTED; |
1759 | 0 | } |
1760 | 0 | } |
1761 | | |
1762 | | /* update his (responder) SA */ |
1763 | 0 | if (this->outbound_state & CHILD_OUTBOUND_SA) |
1764 | 0 | { |
1765 | 0 | kernel_ipsec_sa_id_t id = { |
1766 | 0 | .src = this->my_addr, |
1767 | 0 | .dst = this->other_addr, |
1768 | 0 | .spi = this->other_spi, |
1769 | 0 | .proto = proto_ike2ip(this->protocol), |
1770 | 0 | .mark = this->mark_out, |
1771 | 0 | .if_id = this->if_id_out, |
1772 | 0 | }; |
1773 | 0 | kernel_ipsec_update_sa_t sa = { |
1774 | 0 | .cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, |
1775 | 0 | .new_src = me, |
1776 | 0 | .new_dst = other, |
1777 | 0 | .encap = this->encap, |
1778 | 0 | .new_encap = encap, |
1779 | 0 | .new_reqid = reqid, |
1780 | 0 | }; |
1781 | 0 | if (charon->kernel->update_sa(charon->kernel, &id, |
1782 | 0 | &sa) == NOT_SUPPORTED) |
1783 | 0 | { |
1784 | 0 | return NOT_SUPPORTED; |
1785 | 0 | } |
1786 | 0 | } |
1787 | | /* we currently ignore the actual return values above */ |
1788 | 0 | return SUCCESS; |
1789 | 0 | } |
1790 | | |
1791 | | /** |
1792 | | * Fill the second list with copies of the given traffic selectors updating |
1793 | | * dynamic traffic selectors based on the given addresses. |
1794 | | */ |
1795 | | static void update_ts(host_t *old_host, host_t *new_host, array_t *old_list, |
1796 | | array_t *new_list) |
1797 | 0 | { |
1798 | 0 | enumerator_t *enumerator; |
1799 | 0 | traffic_selector_t *old_ts, *new_ts; |
1800 | |
|
1801 | 0 | enumerator = array_create_enumerator(old_list); |
1802 | 0 | while (enumerator->enumerate(enumerator, &old_ts)) |
1803 | 0 | { |
1804 | 0 | new_ts = old_ts->clone(old_ts); |
1805 | 0 | if (new_ts->is_host(new_ts, old_host)) |
1806 | 0 | { |
1807 | 0 | new_ts->set_address(new_ts, new_host); |
1808 | 0 | } |
1809 | 0 | array_insert(new_list, ARRAY_TAIL, new_ts); |
1810 | 0 | } |
1811 | 0 | enumerator->destroy(enumerator); |
1812 | 0 | array_sort(new_list, (void*)traffic_selector_cmp, NULL); |
1813 | 0 | } |
1814 | | |
1815 | | METHOD(child_sa_t, update, status_t, |
1816 | | private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, |
1817 | | bool encap) |
1818 | 0 | { |
1819 | 0 | child_sa_state_t old; |
1820 | 0 | bool transport_proxy_mode; |
1821 | | |
1822 | | /* anything changed at all? */ |
1823 | 0 | if (me->equals(me, this->my_addr) && |
1824 | 0 | other->equals(other, this->other_addr) && this->encap == encap) |
1825 | 0 | { |
1826 | 0 | return SUCCESS; |
1827 | 0 | } |
1828 | | |
1829 | 0 | old = this->state; |
1830 | 0 | set_state(this, CHILD_UPDATING); |
1831 | 0 | transport_proxy_mode = this->mode == MODE_TRANSPORT && |
1832 | 0 | this->config->has_option(this->config, |
1833 | 0 | OPT_PROXY_MODE); |
1834 | |
|
1835 | 0 | if (require_policy_update(this) && array_count(this->my_ts) && |
1836 | 0 | array_count(this->other_ts)) |
1837 | 0 | { |
1838 | 0 | ipsec_sa_cfg_t my_sa, other_sa; |
1839 | 0 | enumerator_t *enumerator; |
1840 | 0 | traffic_selector_t *my_ts, *other_ts; |
1841 | 0 | array_t *new_my_ts = NULL, *new_other_ts = NULL; |
1842 | 0 | policy_priority_t priority; |
1843 | 0 | uint32_t manual_prio, new_reqid = 0; |
1844 | 0 | status_t state; |
1845 | 0 | bool outbound; |
1846 | |
|
1847 | 0 | prepare_sa_cfg(this, &my_sa, &other_sa); |
1848 | 0 | manual_prio = this->config->get_manual_prio(this->config); |
1849 | 0 | priority = this->trap ? POLICY_PRIORITY_ROUTED |
1850 | 0 | : POLICY_PRIORITY_DEFAULT; |
1851 | 0 | outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) || this->trap; |
1852 | |
|
1853 | 0 | enumerator = create_policy_enumerator(this); |
1854 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
1855 | 0 | { |
1856 | 0 | if (!new_my_ts && !me->ip_equals(me, this->my_addr) && |
1857 | 0 | my_ts->is_host(my_ts, this->my_addr)) |
1858 | 0 | { |
1859 | 0 | new_my_ts = array_create(0, 0); |
1860 | 0 | } |
1861 | 0 | if (!new_other_ts && !other->ip_equals(other, this->other_addr) && |
1862 | 0 | other_ts->is_host(other_ts, this->other_addr)) |
1863 | 0 | { |
1864 | 0 | new_other_ts = array_create(0, 0); |
1865 | 0 | } |
1866 | | /* install drop policy to avoid traffic leaks, acquires etc. */ |
1867 | 0 | if (outbound) |
1868 | 0 | { |
1869 | 0 | install_policies_outbound(this, this->my_addr, this->other_addr, |
1870 | 0 | my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, |
1871 | 0 | POLICY_PRIORITY_DEFAULT, manual_prio); |
1872 | 0 | } |
1873 | | /* remove old policies */ |
1874 | 0 | del_policies_internal(this, this->my_addr, this->other_addr, |
1875 | 0 | my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, |
1876 | 0 | priority, manual_prio, outbound); |
1877 | 0 | } |
1878 | 0 | enumerator->destroy(enumerator); |
1879 | |
|
1880 | 0 | if (new_my_ts) |
1881 | 0 | { |
1882 | 0 | update_ts(this->my_addr, me, this->my_ts, new_my_ts); |
1883 | 0 | } |
1884 | 0 | if (new_other_ts) |
1885 | 0 | { |
1886 | 0 | update_ts(this->other_addr, other, this->other_ts, new_other_ts); |
1887 | 0 | } |
1888 | 0 | if (this->reqid_allocated && (new_my_ts || new_other_ts)) |
1889 | 0 | { |
1890 | | /* if we allocated a reqid with the previous TS, we have to get a |
1891 | | * new one that matches the updated TS */ |
1892 | 0 | if (alloc_reqid(this, new_my_ts ?: this->my_ts, |
1893 | 0 | new_other_ts ?: this->other_ts, &new_reqid) != SUCCESS) |
1894 | 0 | { |
1895 | 0 | DBG1(DBG_CHD, "allocating new reqid for updated SA failed"); |
1896 | 0 | } |
1897 | 0 | } |
1898 | | |
1899 | | /* update the IPsec SAs */ |
1900 | 0 | state = update_sas(this, me, other, encap, new_reqid); |
1901 | | |
1902 | | /* install new/updated policies only if we were able to update the |
1903 | | * SAs, otherwise we reinstall the old policies further below */ |
1904 | 0 | if (state != NOT_SUPPORTED) |
1905 | 0 | { |
1906 | | /* we reinstall the virtual IP to handle interface roaming |
1907 | | * correctly */ |
1908 | 0 | if (vips) |
1909 | 0 | { |
1910 | 0 | vips->invoke_function(vips, reinstall_vip, me); |
1911 | 0 | } |
1912 | 0 | if (new_reqid) |
1913 | 0 | { |
1914 | 0 | my_sa.reqid = other_sa.reqid = new_reqid; |
1915 | 0 | } |
1916 | 0 | enumerator = create_policy_enumerator_internal( |
1917 | 0 | new_my_ts ?: this->my_ts, |
1918 | 0 | new_other_ts ?: this->other_ts); |
1919 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
1920 | 0 | { |
1921 | 0 | install_policies_internal(this, me, other, my_ts, other_ts, |
1922 | 0 | &my_sa, &other_sa, POLICY_IPSEC, |
1923 | 0 | priority, manual_prio, outbound); |
1924 | 0 | } |
1925 | 0 | enumerator->destroy(enumerator); |
1926 | 0 | if (new_reqid) |
1927 | 0 | { |
1928 | 0 | my_sa.reqid = other_sa.reqid = this->reqid; |
1929 | 0 | } |
1930 | 0 | } |
1931 | |
|
1932 | 0 | enumerator = create_policy_enumerator(this); |
1933 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
1934 | 0 | { |
1935 | | /* reinstall the previous policies if we can't update the SAs */ |
1936 | 0 | if (state == NOT_SUPPORTED) |
1937 | 0 | { |
1938 | 0 | install_policies_internal(this, this->my_addr, this->other_addr, |
1939 | 0 | my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, |
1940 | 0 | priority, manual_prio, outbound); |
1941 | 0 | } |
1942 | | /* remove the drop policy */ |
1943 | 0 | if (outbound) |
1944 | 0 | { |
1945 | 0 | del_policies_outbound(this, this->my_addr, this->other_addr, |
1946 | 0 | my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, |
1947 | 0 | POLICY_PRIORITY_DEFAULT, manual_prio); |
1948 | 0 | } |
1949 | 0 | } |
1950 | 0 | enumerator->destroy(enumerator); |
1951 | |
|
1952 | 0 | if (state == NOT_SUPPORTED) |
1953 | 0 | { |
1954 | 0 | if (new_reqid && |
1955 | 0 | charon->kernel->release_reqid(charon->kernel, |
1956 | 0 | new_reqid) != SUCCESS) |
1957 | 0 | { |
1958 | 0 | DBG1(DBG_CHD, "releasing reqid %u failed", new_reqid); |
1959 | 0 | } |
1960 | 0 | array_destroy_offset(new_my_ts, |
1961 | 0 | offsetof(traffic_selector_t, destroy)); |
1962 | 0 | array_destroy_offset(new_other_ts, |
1963 | 0 | offsetof(traffic_selector_t, destroy)); |
1964 | 0 | set_state(this, old); |
1965 | 0 | return NOT_SUPPORTED; |
1966 | 0 | } |
1967 | | |
1968 | 0 | if (new_reqid) |
1969 | 0 | { |
1970 | 0 | if (charon->kernel->release_reqid(charon->kernel, |
1971 | 0 | this->reqid) != SUCCESS) |
1972 | 0 | { |
1973 | 0 | DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); |
1974 | 0 | } |
1975 | 0 | DBG1(DBG_CHD, "replaced reqid %u with reqid %u for updated " |
1976 | 0 | "CHILD_SA %s{%d}", this->reqid, new_reqid, get_name(this), |
1977 | 0 | this->unique_id); |
1978 | 0 | this->reqid = new_reqid; |
1979 | 0 | } |
1980 | 0 | if (new_my_ts) |
1981 | 0 | { |
1982 | 0 | array_destroy_offset(this->my_ts, |
1983 | 0 | offsetof(traffic_selector_t, destroy)); |
1984 | 0 | this->my_ts = new_my_ts; |
1985 | 0 | } |
1986 | 0 | if (new_other_ts) |
1987 | 0 | { |
1988 | 0 | array_destroy_offset(this->other_ts, |
1989 | 0 | offsetof(traffic_selector_t, destroy)); |
1990 | 0 | this->other_ts = new_other_ts; |
1991 | 0 | } |
1992 | 0 | } |
1993 | 0 | else if (!transport_proxy_mode) |
1994 | 0 | { |
1995 | 0 | if (update_sas(this, me, other, encap, 0) == NOT_SUPPORTED) |
1996 | 0 | { |
1997 | 0 | set_state(this, old); |
1998 | 0 | return NOT_SUPPORTED; |
1999 | 0 | } |
2000 | 0 | } |
2001 | | |
2002 | 0 | if (!transport_proxy_mode) |
2003 | 0 | { |
2004 | | /* apply hosts */ |
2005 | 0 | if (!me->equals(me, this->my_addr)) |
2006 | 0 | { |
2007 | 0 | this->my_addr->destroy(this->my_addr); |
2008 | 0 | this->my_addr = me->clone(me); |
2009 | 0 | } |
2010 | 0 | if (!other->equals(other, this->other_addr)) |
2011 | 0 | { |
2012 | 0 | this->other_addr->destroy(this->other_addr); |
2013 | 0 | this->other_addr = other->clone(other); |
2014 | 0 | } |
2015 | 0 | } |
2016 | |
|
2017 | 0 | this->encap = encap; |
2018 | 0 | set_state(this, old); |
2019 | |
|
2020 | 0 | return SUCCESS; |
2021 | 0 | } |
2022 | | |
2023 | | METHOD(child_sa_t, destroy, void, |
2024 | | private_child_sa_t *this) |
2025 | 0 | { |
2026 | 0 | enumerator_t *enumerator; |
2027 | 0 | traffic_selector_t *my_ts, *other_ts; |
2028 | 0 | policy_priority_t priority; |
2029 | |
|
2030 | 0 | priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT; |
2031 | |
|
2032 | 0 | set_state(this, CHILD_DESTROYING); |
2033 | |
|
2034 | 0 | if (require_policies(this)) |
2035 | 0 | { |
2036 | 0 | ipsec_sa_cfg_t my_sa, other_sa; |
2037 | 0 | uint32_t manual_prio; |
2038 | 0 | bool del_outbound; |
2039 | |
|
2040 | 0 | prepare_sa_cfg(this, &my_sa, &other_sa); |
2041 | 0 | manual_prio = this->config->get_manual_prio(this->config); |
2042 | 0 | del_outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) || |
2043 | 0 | this->trap; |
2044 | | |
2045 | | /* delete all policies in the kernel */ |
2046 | 0 | enumerator = create_policy_enumerator(this); |
2047 | 0 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) |
2048 | 0 | { |
2049 | 0 | del_policies_internal(this, this->my_addr, |
2050 | 0 | this->other_addr, my_ts, other_ts, &my_sa, &other_sa, |
2051 | 0 | POLICY_IPSEC, priority, manual_prio, del_outbound); |
2052 | 0 | } |
2053 | 0 | enumerator->destroy(enumerator); |
2054 | 0 | } |
2055 | | |
2056 | | /* delete SAs in the kernel, if they are set up, inbound is always deleted |
2057 | | * to remove allocated SPIs */ |
2058 | 0 | if (this->my_spi) |
2059 | 0 | { |
2060 | 0 | kernel_ipsec_sa_id_t id = { |
2061 | 0 | .src = this->other_addr, |
2062 | 0 | .dst = this->my_addr, |
2063 | 0 | .spi = this->my_spi, |
2064 | 0 | .proto = proto_ike2ip(this->protocol), |
2065 | 0 | .mark = mark_in_sa(this), |
2066 | 0 | .if_id = this->if_id_in, |
2067 | 0 | }; |
2068 | 0 | kernel_ipsec_del_sa_t sa = { |
2069 | 0 | .cpi = this->my_cpi, |
2070 | 0 | }; |
2071 | 0 | charon->kernel->del_sa(charon->kernel, &id, &sa); |
2072 | 0 | } |
2073 | 0 | if (this->outbound_state & CHILD_OUTBOUND_SA) |
2074 | 0 | { |
2075 | 0 | kernel_ipsec_sa_id_t id = { |
2076 | 0 | .src = this->my_addr, |
2077 | 0 | .dst = this->other_addr, |
2078 | 0 | .spi = this->other_spi, |
2079 | 0 | .proto = proto_ike2ip(this->protocol), |
2080 | 0 | .mark = this->mark_out, |
2081 | 0 | .if_id = this->if_id_out, |
2082 | 0 | }; |
2083 | 0 | kernel_ipsec_del_sa_t sa = { |
2084 | 0 | .cpi = this->other_cpi, |
2085 | 0 | }; |
2086 | 0 | charon->kernel->del_sa(charon->kernel, &id, &sa); |
2087 | 0 | } |
2088 | |
|
2089 | 0 | if (this->reqid_allocated || (!this->static_reqid && this->reqid)) |
2090 | 0 | { |
2091 | 0 | if (charon->kernel->release_reqid(charon->kernel, |
2092 | 0 | this->reqid) != SUCCESS) |
2093 | 0 | { |
2094 | 0 | DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); |
2095 | 0 | } |
2096 | 0 | } |
2097 | |
|
2098 | 0 | array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); |
2099 | 0 | array_destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); |
2100 | 0 | this->my_addr->destroy(this->my_addr); |
2101 | 0 | this->other_addr->destroy(this->other_addr); |
2102 | 0 | DESTROY_IF(this->proposal); |
2103 | 0 | DESTROY_IF(this->label); |
2104 | 0 | this->config->destroy(this->config); |
2105 | 0 | chunk_clear(&this->encr_r); |
2106 | 0 | chunk_clear(&this->integ_r); |
2107 | 0 | free(this); |
2108 | 0 | } |
2109 | | |
2110 | | /** |
2111 | | * Get proxy address for one side, if any |
2112 | | */ |
2113 | | static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) |
2114 | 0 | { |
2115 | 0 | host_t *host = NULL; |
2116 | 0 | uint8_t mask; |
2117 | 0 | enumerator_t *enumerator; |
2118 | 0 | linked_list_t *ts_list, *list; |
2119 | 0 | traffic_selector_t *ts; |
2120 | |
|
2121 | 0 | list = linked_list_create_with_items(ike, NULL); |
2122 | 0 | ts_list = config->get_traffic_selectors(config, local, list); |
2123 | 0 | list->destroy(list); |
2124 | |
|
2125 | 0 | enumerator = ts_list->create_enumerator(ts_list); |
2126 | 0 | while (enumerator->enumerate(enumerator, &ts)) |
2127 | 0 | { |
2128 | 0 | if (ts->is_host(ts, NULL) && ts->to_subnet(ts, &host, &mask)) |
2129 | 0 | { |
2130 | 0 | DBG1(DBG_CHD, "%s address: %H is a transport mode proxy for %H", |
2131 | 0 | local ? "my" : "other", ike, host); |
2132 | 0 | break; |
2133 | 0 | } |
2134 | 0 | } |
2135 | 0 | enumerator->destroy(enumerator); |
2136 | 0 | ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); |
2137 | |
|
2138 | 0 | if (!host) |
2139 | 0 | { |
2140 | 0 | host = ike->clone(ike); |
2141 | 0 | } |
2142 | 0 | return host; |
2143 | 0 | } |
2144 | | |
2145 | | /* |
2146 | | * Described in header |
2147 | | */ |
2148 | | child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config, |
2149 | | child_sa_create_t *data) |
2150 | 0 | { |
2151 | 0 | private_child_sa_t *this; |
2152 | 0 | static refcount_t unique_id = 0; |
2153 | |
|
2154 | 0 | INIT(this, |
2155 | 0 | .public = { |
2156 | 0 | .get_name = _get_name, |
2157 | 0 | .get_reqid = _get_reqid, |
2158 | 0 | .get_reqid_ref = _get_reqid_ref, |
2159 | 0 | .get_unique_id = _get_unique_id, |
2160 | 0 | .get_config = _get_config, |
2161 | 0 | .get_state = _get_state, |
2162 | 0 | .set_state = _set_state, |
2163 | 0 | .get_outbound_state = _get_outbound_state, |
2164 | 0 | .get_spi = _get_spi, |
2165 | 0 | .get_cpi = _get_cpi, |
2166 | 0 | .get_protocol = _get_protocol, |
2167 | 0 | .set_protocol = _set_protocol, |
2168 | 0 | .get_mode = _get_mode, |
2169 | 0 | .set_mode = _set_mode, |
2170 | 0 | .get_proposal = _get_proposal, |
2171 | 0 | .set_proposal = _set_proposal, |
2172 | 0 | .get_lifetime = _get_lifetime, |
2173 | 0 | .get_installtime = _get_installtime, |
2174 | 0 | .get_usestats = _get_usestats, |
2175 | 0 | .get_mark = _get_mark, |
2176 | 0 | .get_if_id = _get_if_id, |
2177 | 0 | .get_label = _get_label, |
2178 | 0 | .get_cpu = _get_cpu, |
2179 | 0 | .set_per_cpu = _set_per_cpu, |
2180 | 0 | .use_per_cpu = _use_per_cpu, |
2181 | 0 | .get_acquire_seq = _get_acquire_seq, |
2182 | 0 | .set_acquire_seq = _set_acquire_seq, |
2183 | 0 | .has_encap = _has_encap, |
2184 | 0 | .get_ipcomp = _get_ipcomp, |
2185 | 0 | .set_ipcomp = _set_ipcomp, |
2186 | 0 | .set_iptfs_dont_fragment = _set_iptfs_dont_fragment, |
2187 | 0 | .get_close_action = _get_close_action, |
2188 | 0 | .set_close_action = _set_close_action, |
2189 | 0 | .get_dpd_action = _get_dpd_action, |
2190 | 0 | .set_dpd_action = _set_dpd_action, |
2191 | 0 | .alloc_spi = _alloc_spi, |
2192 | 0 | .alloc_cpi = _alloc_cpi, |
2193 | 0 | .install = _install, |
2194 | 0 | .register_outbound = _register_outbound, |
2195 | 0 | .install_outbound = _install_outbound, |
2196 | 0 | .remove_outbound = _remove_outbound, |
2197 | 0 | .set_rekey_sa = _set_rekey_sa, |
2198 | 0 | .get_rekey_sa = _get_rekey_sa, |
2199 | 0 | .update = _update, |
2200 | 0 | .set_policies = _set_policies, |
2201 | 0 | .install_policies = _install_policies, |
2202 | 0 | .create_ts_enumerator = _create_ts_enumerator, |
2203 | 0 | .create_policy_enumerator = _create_policy_enumerator, |
2204 | 0 | .destroy = _destroy, |
2205 | 0 | }, |
2206 | 0 | .encap = data->encap, |
2207 | 0 | .ipcomp = IPCOMP_NONE, |
2208 | 0 | .state = CHILD_CREATED, |
2209 | 0 | .my_ts = array_create(0, 0), |
2210 | 0 | .other_ts = array_create(0, 0), |
2211 | 0 | .protocol = PROTO_NONE, |
2212 | 0 | .mode = MODE_TUNNEL, |
2213 | 0 | .close_action = config->get_close_action(config), |
2214 | 0 | .dpd_action = config->get_dpd_action(config), |
2215 | 0 | .reqid = config->get_reqid(config), |
2216 | 0 | .unique_id = ref_get_nonzero(&unique_id), |
2217 | 0 | .mark_in = config->get_mark(config, TRUE), |
2218 | 0 | .mark_out = config->get_mark(config, FALSE), |
2219 | 0 | .if_id_in = config->get_if_id(config, TRUE) ?: data->if_id_in_def, |
2220 | 0 | .if_id_out = config->get_if_id(config, FALSE) ?: data->if_id_out_def, |
2221 | 0 | .label = data->label ? data->label->clone(data->label) : NULL, |
2222 | 0 | .cpu = data->cpu, |
2223 | 0 | .per_cpu = data->per_cpu, |
2224 | 0 | .seq = data->seq, |
2225 | 0 | .install_time = time_monotonic(NULL), |
2226 | 0 | .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES), |
2227 | 0 | ); |
2228 | |
|
2229 | 0 | this->config = config; |
2230 | 0 | config->get_ref(config); |
2231 | |
|
2232 | 0 | if (data->mark_in) |
2233 | 0 | { |
2234 | 0 | this->mark_in.value = data->mark_in; |
2235 | 0 | } |
2236 | 0 | if (data->mark_out) |
2237 | 0 | { |
2238 | 0 | this->mark_out.value = data->mark_out; |
2239 | 0 | } |
2240 | 0 | if (data->if_id_in) |
2241 | 0 | { |
2242 | 0 | this->if_id_in = data->if_id_in; |
2243 | 0 | } |
2244 | 0 | if (data->if_id_out) |
2245 | 0 | { |
2246 | 0 | this->if_id_out = data->if_id_out; |
2247 | 0 | } |
2248 | |
|
2249 | 0 | allocate_unique_if_ids(&this->if_id_in, &this->if_id_out); |
2250 | 0 | allocate_unique_marks(&this->mark_in.value, &this->mark_out.value); |
2251 | |
|
2252 | 0 | if (!this->reqid) |
2253 | 0 | { |
2254 | | /* reuse old reqid if we are rekeying an existing CHILD_SA and when |
2255 | | * initiating a trap policy. the reqid cache will generally find the |
2256 | | * same reqid for our selectors. but this does not work in a special |
2257 | | * case: if the IPsec stack does not use sequence numbers for acquires, |
2258 | | * an SA is triggered by a trap policy and the negotiated TS get |
2259 | | * narrowed, we still must reuse the same reqid to successfully replace |
2260 | | * the temporary SA on the kernel level. however, if sequence numbers |
2261 | | * are supported, the reqid will later get updated in case of narrowing |
2262 | | * when alloc_reqid() is called */ |
2263 | 0 | if (data->reqid && |
2264 | 0 | charon->kernel->ref_reqid(charon->kernel, data->reqid) == SUCCESS) |
2265 | 0 | { |
2266 | 0 | this->reqid = data->reqid; |
2267 | 0 | } |
2268 | 0 | } |
2269 | 0 | else |
2270 | 0 | { |
2271 | 0 | this->static_reqid = TRUE; |
2272 | 0 | } |
2273 | | |
2274 | | /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ |
2275 | 0 | if (config->get_mode(config) == MODE_TRANSPORT && |
2276 | 0 | config->has_option(config, OPT_PROXY_MODE)) |
2277 | 0 | { |
2278 | 0 | this->mode = MODE_TRANSPORT; |
2279 | |
|
2280 | 0 | this->my_addr = get_proxy_addr(config, me, TRUE); |
2281 | 0 | this->other_addr = get_proxy_addr(config, other, FALSE); |
2282 | 0 | } |
2283 | 0 | else |
2284 | 0 | { |
2285 | 0 | this->my_addr = me->clone(me); |
2286 | 0 | this->other_addr = other->clone(other); |
2287 | 0 | } |
2288 | 0 | return &this->public; |
2289 | 0 | } |
2290 | | |
2291 | | /** |
2292 | | * Check if the given traffic selector is contained in any of the traffic |
2293 | | * selectors in the given list. |
2294 | | */ |
2295 | | static bool is_ts_match(traffic_selector_t *to_check, array_t *list) |
2296 | 0 | { |
2297 | 0 | traffic_selector_t *ts; |
2298 | 0 | int i; |
2299 | |
|
2300 | 0 | for (i = 0; i < array_count(list); i++) |
2301 | 0 | { |
2302 | 0 | array_get(list, i, &ts); |
2303 | 0 | if (to_check->is_contained_in(to_check, ts)) |
2304 | 0 | { |
2305 | 0 | return TRUE; |
2306 | 0 | } |
2307 | 0 | } |
2308 | 0 | return FALSE; |
2309 | 0 | } |
2310 | | |
2311 | | /** |
2312 | | * Check if all given traffic selectors are contained in any of the traffic |
2313 | | * selectors in the given list. |
2314 | | */ |
2315 | | static bool is_ts_list_match(traffic_selector_list_t *to_check, array_t *list) |
2316 | 0 | { |
2317 | 0 | enumerator_t *enumerator; |
2318 | 0 | traffic_selector_t *ts; |
2319 | 0 | bool matched = TRUE; |
2320 | |
|
2321 | 0 | enumerator = to_check->create_enumerator(to_check); |
2322 | 0 | while (enumerator->enumerate(enumerator, &ts)) |
2323 | 0 | { |
2324 | 0 | if (!is_ts_match(ts, list)) |
2325 | 0 | { |
2326 | 0 | matched = FALSE; |
2327 | 0 | break; |
2328 | 0 | } |
2329 | 0 | } |
2330 | 0 | enumerator->destroy(enumerator); |
2331 | 0 | return matched; |
2332 | 0 | } |
2333 | | |
2334 | | /* |
2335 | | * Described in header |
2336 | | */ |
2337 | | bool child_sa_ts_match(child_sa_t *child, traffic_selector_t *src, |
2338 | | traffic_selector_t *dst) |
2339 | 0 | { |
2340 | 0 | private_child_sa_t *this = (private_child_sa_t*)child; |
2341 | |
|
2342 | 0 | return src && dst && |
2343 | 0 | is_ts_match(src, this->my_ts) && |
2344 | 0 | is_ts_match(dst, this->other_ts); |
2345 | 0 | } |
2346 | | |
2347 | | /* |
2348 | | * Described in header |
2349 | | */ |
2350 | | bool child_sa_ts_lists_match(child_sa_t *child, traffic_selector_list_t *src, |
2351 | | traffic_selector_list_t *dst) |
2352 | 0 | { |
2353 | 0 | private_child_sa_t *this = (private_child_sa_t*)child; |
2354 | |
|
2355 | 0 | return src && dst && |
2356 | 0 | is_ts_list_match(src, this->my_ts) && |
2357 | 0 | is_ts_list_match(dst, this->other_ts); |
2358 | 0 | } |