]>
Commit | Line | Data |
---|---|---|
3ebebc5e | 1 | /* |
35ef1b03 | 2 | * Copyright (C) 2006-2018 Tobias Brunner |
1003cf23 | 3 | * Copyright (C) 2016 Andreas Steffen |
a985db3f | 4 | * Copyright (C) 2005-2008 Martin Willi |
d4aad554 | 5 | * Copyright (C) 2006 Daniel Roethlisberger |
c71d53ba | 6 | * Copyright (C) 2005 Jan Hutter |
89da06ac | 7 | * HSR Hochschule fuer Technik Rapperswil |
3ebebc5e MW |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | * for more details. | |
18 | */ | |
19 | ||
60356f33 | 20 | #define _GNU_SOURCE |
b92eef28 | 21 | #include "child_sa.h" |
3ebebc5e | 22 | |
a095243f | 23 | #include <stdio.h> |
4c23a8c9 | 24 | #include <string.h> |
e13389a7 | 25 | #include <time.h> |
4c23a8c9 | 26 | |
aeda79ff | 27 | #include <daemon.h> |
2745ae26 | 28 | #include <collections/array.h> |
aeda79ff | 29 | |
a985db3f | 30 | ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, |
60356f33 MW |
31 | "CREATED", |
32 | "ROUTED", | |
ea625fab | 33 | "INSTALLING", |
60356f33 | 34 | "INSTALLED", |
ad3af574 | 35 | "UPDATING", |
60356f33 | 36 | "REKEYING", |
70728eb1 | 37 | "REKEYED", |
013857ad | 38 | "RETRYING", |
60356f33 | 39 | "DELETING", |
a985db3f | 40 | "DESTROYING", |
60356f33 | 41 | ); |
bcb95ced | 42 | |
2c116ef5 | 43 | ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES, |
afbea8ce | 44 | "REGISTERED", |
2c116ef5 TB |
45 | "SA", |
46 | "POLICIES", | |
afbea8ce TB |
47 | ); |
48 | ||
3ebebc5e MW |
49 | typedef struct private_child_sa_t private_child_sa_t; |
50 | ||
51 | /** | |
7b8bae99 | 52 | * Private data of a child_sa_t object. |
3ebebc5e MW |
53 | */ |
54 | struct private_child_sa_t { | |
55 | /** | |
144f676c | 56 | * Public interface of child_sa_t. |
3ebebc5e MW |
57 | */ |
58 | child_sa_t public; | |
7daf5226 | 59 | |
80853d84 MW |
60 | /** |
61 | * address of us | |
62 | */ | |
63 | host_t *my_addr; | |
7daf5226 | 64 | |
3efbf983 | 65 | /** |
80853d84 | 66 | * address of remote |
3efbf983 | 67 | */ |
80853d84 | 68 | host_t *other_addr; |
7daf5226 | 69 | |
3efbf983 | 70 | /** |
80853d84 | 71 | * our actually used SPI, 0 if unused |
3efbf983 | 72 | */ |
b12c53ce | 73 | uint32_t my_spi; |
7daf5226 | 74 | |
aeda79ff | 75 | /** |
80853d84 | 76 | * others used SPI, 0 if unused |
aeda79ff | 77 | */ |
b12c53ce | 78 | uint32_t other_spi; |
7daf5226 | 79 | |
8dfbe71b | 80 | /** |
80853d84 | 81 | * our Compression Parameter Index (CPI) used, 0 if unused |
8dfbe71b | 82 | */ |
b12c53ce | 83 | uint16_t my_cpi; |
7daf5226 | 84 | |
8dfbe71b | 85 | /** |
80853d84 | 86 | * others Compression Parameter Index (CPI) used, 0 if unused |
8dfbe71b | 87 | */ |
b12c53ce | 88 | uint16_t other_cpi; |
7daf5226 | 89 | |
5d187bd2 | 90 | /** |
2745ae26 | 91 | * Array for local traffic selectors |
5d187bd2 | 92 | */ |
2745ae26 | 93 | array_t *my_ts; |
7daf5226 | 94 | |
80853d84 | 95 | /** |
2745ae26 | 96 | * Array for remote traffic selectors |
80853d84 | 97 | */ |
2745ae26 | 98 | array_t *other_ts; |
7daf5226 | 99 | |
8a3a389e TB |
100 | /** |
101 | * Outbound encryption key cached during a rekeying | |
102 | */ | |
103 | chunk_t encr_r; | |
104 | ||
105 | /** | |
106 | * Outbound integrity key cached during a rekeying | |
107 | */ | |
108 | chunk_t integ_r; | |
109 | ||
110 | /** | |
111 | * Whether the outbound SA has only been registered yet during a rekeying | |
112 | */ | |
afbea8ce | 113 | child_sa_outbound_state_t outbound_state; |
8a3a389e TB |
114 | |
115 | /** | |
116 | * Whether the peer supports TFCv3 | |
117 | */ | |
118 | bool tfcv3; | |
119 | ||
cad13450 TB |
120 | /** |
121 | * The outbound SPI of the CHILD_SA that replaced this one during a rekeying | |
122 | */ | |
123 | uint32_t rekey_spi; | |
124 | ||
c0593835 | 125 | /** |
80853d84 | 126 | * Protocol used to protect this SA, ESP|AH |
c0593835 | 127 | */ |
80853d84 | 128 | protocol_id_t protocol; |
7daf5226 | 129 | |
3c7e72f5 | 130 | /** |
80853d84 | 131 | * reqid used for this child_sa |
3c7e72f5 | 132 | */ |
b12c53ce | 133 | uint32_t reqid; |
7daf5226 | 134 | |
0da97f50 MW |
135 | /** |
136 | * Did we allocate/confirm and must release the reqid? | |
137 | */ | |
138 | bool reqid_allocated; | |
139 | ||
3665adef TB |
140 | /** |
141 | * Is the reqid statically configured | |
142 | */ | |
143 | bool static_reqid; | |
144 | ||
ad1b5345 | 145 | /** |
4ec397b8 MW |
146 | * Unique CHILD_SA identifier |
147 | */ | |
b12c53ce | 148 | uint32_t unique_id; |
4ec397b8 | 149 | |
ad1b5345 TB |
150 | /** |
151 | * Whether FWD policieis in the outbound direction should be installed | |
152 | */ | |
153 | bool policies_fwd_out; | |
154 | ||
ee26c537 AS |
155 | /** |
156 | * inbound mark used for this child_sa | |
157 | */ | |
158 | mark_t mark_in; | |
159 | ||
160 | /** | |
161 | * outbound mark used for this child_sa | |
162 | */ | |
163 | mark_t mark_out; | |
164 | ||
a2a3fb3e | 165 | /** |
6e10aead | 166 | * absolute time when rekeying is scheduled |
a2a3fb3e | 167 | */ |
6e10aead | 168 | time_t rekey_time; |
7daf5226 | 169 | |
e70d5576 | 170 | /** |
6e10aead | 171 | * absolute time when the SA expires |
e70d5576 | 172 | */ |
6e10aead | 173 | time_t expire_time; |
7daf5226 | 174 | |
763e0353 MW |
175 | /** |
176 | * absolute time when SA has been installed | |
177 | */ | |
178 | time_t install_time; | |
179 | ||
bcb95ced MW |
180 | /** |
181 | * state of the CHILD_SA | |
182 | */ | |
183 | child_sa_state_t state; | |
fff4ee8a | 184 | |
c8f7a114 TB |
185 | /** |
186 | * TRUE if this CHILD_SA is used to install trap policies | |
187 | */ | |
188 | bool trap; | |
189 | ||
1239c6f4 | 190 | /** |
fc2d1c42 | 191 | * Specifies if UDP encapsulation is enabled (NAT traversal) |
1239c6f4 | 192 | */ |
fc2d1c42 | 193 | bool encap; |
7daf5226 | 194 | |
d4aad554 TB |
195 | /** |
196 | * Specifies the IPComp transform used (IPCOMP_NONE if disabled) | |
197 | */ | |
198 | ipcomp_transform_t ipcomp; | |
7daf5226 | 199 | |
7652be89 MW |
200 | /** |
201 | * mode this SA uses, tunnel/transport | |
202 | */ | |
a341a68f | 203 | ipsec_mode_t mode; |
7daf5226 | 204 | |
4c401ea2 MW |
205 | /** |
206 | * Action to enforce if peer closes the CHILD_SA | |
207 | */ | |
208 | action_t close_action; | |
209 | ||
210 | /** | |
211 | * Action to enforce if peer is considered dead | |
212 | */ | |
213 | action_t dpd_action; | |
214 | ||
80853d84 | 215 | /** |
7b3814f7 MW |
216 | * selected proposal |
217 | */ | |
218 | proposal_t *proposal; | |
7daf5226 | 219 | |
c60c7694 | 220 | /** |
e0fe7651 | 221 | * config used to create this child |
c60c7694 | 222 | */ |
e0fe7651 | 223 | child_cfg_t *config; |
3f720dc7 AS |
224 | |
225 | /** | |
226 | * time of last use in seconds (inbound) | |
227 | */ | |
c99458e9 | 228 | time_t my_usetime; |
3f720dc7 AS |
229 | |
230 | /** | |
231 | * time of last use in seconds (outbound) | |
232 | */ | |
c99458e9 | 233 | time_t other_usetime; |
3f720dc7 AS |
234 | |
235 | /** | |
236 | * last number of inbound bytes | |
237 | */ | |
b12c53ce | 238 | uint64_t my_usebytes; |
3f720dc7 AS |
239 | |
240 | /** | |
241 | * last number of outbound bytes | |
242 | */ | |
b12c53ce | 243 | uint64_t other_usebytes; |
7eeeb1c7 MW |
244 | |
245 | /** | |
246 | * last number of inbound packets | |
247 | */ | |
b12c53ce | 248 | uint64_t my_usepackets; |
7eeeb1c7 MW |
249 | |
250 | /** | |
251 | * last number of outbound bytes | |
252 | */ | |
b12c53ce | 253 | uint64_t other_usepackets; |
3ebebc5e MW |
254 | }; |
255 | ||
9f166d9a | 256 | /** |
ea43f8ff | 257 | * Convert an IKEv2 specific protocol identifier to the IP protocol identifier |
9f166d9a | 258 | */ |
b12c53ce | 259 | static inline uint8_t proto_ike2ip(protocol_id_t protocol) |
9f166d9a TB |
260 | { |
261 | switch (protocol) | |
262 | { | |
263 | case PROTO_ESP: | |
264 | return IPPROTO_ESP; | |
265 | case PROTO_AH: | |
266 | return IPPROTO_AH; | |
267 | default: | |
268 | return protocol; | |
269 | } | |
270 | } | |
271 | ||
ea43f8ff TB |
272 | /** |
273 | * Returns the mark to use on the inbound SA | |
274 | */ | |
275 | static inline mark_t mark_in_sa(private_child_sa_t *this) | |
276 | { | |
277 | if (this->config->has_option(this->config, OPT_MARK_IN_SA)) | |
278 | { | |
279 | return this->mark_in; | |
280 | } | |
281 | return (mark_t){}; | |
282 | } | |
283 | ||
9d941742 TB |
284 | METHOD(child_sa_t, get_name, char*, |
285 | private_child_sa_t *this) | |
9be547c0 | 286 | { |
e0fe7651 | 287 | return this->config->get_name(this->config); |
9be547c0 MW |
288 | } |
289 | ||
b12c53ce | 290 | METHOD(child_sa_t, get_reqid, uint32_t, |
9d941742 | 291 | private_child_sa_t *this) |
32b6500f MW |
292 | { |
293 | return this->reqid; | |
294 | } | |
3aaf7908 | 295 | |
b12c53ce | 296 | METHOD(child_sa_t, get_unique_id, uint32_t, |
4ec397b8 MW |
297 | private_child_sa_t *this) |
298 | { | |
299 | return this->unique_id; | |
300 | } | |
301 | ||
9d941742 TB |
302 | METHOD(child_sa_t, get_config, child_cfg_t*, |
303 | private_child_sa_t *this) | |
3aaf7908 MW |
304 | { |
305 | return this->config; | |
306 | } | |
307 | ||
9d941742 TB |
308 | METHOD(child_sa_t, set_state, void, |
309 | private_child_sa_t *this, child_sa_state_t state) | |
3aaf7908 | 310 | { |
859dae25 TB |
311 | if (this->state != state) |
312 | { | |
313 | DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", | |
314 | get_name(this), this->unique_id, | |
315 | child_sa_state_names, this->state, | |
316 | child_sa_state_names, state); | |
317 | charon->bus->child_state_change(charon->bus, &this->public, state); | |
318 | this->state = state; | |
319 | } | |
3aaf7908 MW |
320 | } |
321 | ||
9d941742 TB |
322 | METHOD(child_sa_t, get_state, child_sa_state_t, |
323 | private_child_sa_t *this) | |
3aaf7908 MW |
324 | { |
325 | return this->state; | |
326 | } | |
327 | ||
afbea8ce TB |
328 | METHOD(child_sa_t, get_outbound_state, child_sa_outbound_state_t, |
329 | private_child_sa_t *this) | |
330 | { | |
331 | return this->outbound_state; | |
332 | } | |
333 | ||
b12c53ce | 334 | METHOD(child_sa_t, get_spi, uint32_t, |
9d941742 | 335 | private_child_sa_t *this, bool inbound) |
8d77edde | 336 | { |
80853d84 | 337 | return inbound ? this->my_spi : this->other_spi; |
8d77edde MW |
338 | } |
339 | ||
b12c53ce | 340 | METHOD(child_sa_t, get_cpi, uint16_t, |
9d941742 | 341 | private_child_sa_t *this, bool inbound) |
66da78b4 | 342 | { |
80853d84 | 343 | return inbound ? this->my_cpi : this->other_cpi; |
66da78b4 AS |
344 | } |
345 | ||
9d941742 TB |
346 | METHOD(child_sa_t, get_protocol, protocol_id_t, |
347 | private_child_sa_t *this) | |
8d77edde MW |
348 | { |
349 | return this->protocol; | |
350 | } | |
32b6500f | 351 | |
9d941742 TB |
352 | METHOD(child_sa_t, set_protocol, void, |
353 | private_child_sa_t *this, protocol_id_t protocol) | |
3aaf7908 MW |
354 | { |
355 | this->protocol = protocol; | |
356 | } | |
357 | ||
9d941742 TB |
358 | METHOD(child_sa_t, get_mode, ipsec_mode_t, |
359 | private_child_sa_t *this) | |
80853d84 MW |
360 | { |
361 | return this->mode; | |
362 | } | |
363 | ||
9d941742 TB |
364 | METHOD(child_sa_t, set_mode, void, |
365 | private_child_sa_t *this, ipsec_mode_t mode) | |
3aaf7908 MW |
366 | { |
367 | this->mode = mode; | |
368 | } | |
369 | ||
9d941742 TB |
370 | METHOD(child_sa_t, has_encap, bool, |
371 | private_child_sa_t *this) | |
82d20c05 MW |
372 | { |
373 | return this->encap; | |
374 | } | |
375 | ||
9d941742 TB |
376 | METHOD(child_sa_t, get_ipcomp, ipcomp_transform_t, |
377 | private_child_sa_t *this) | |
82d20c05 MW |
378 | { |
379 | return this->ipcomp; | |
380 | } | |
381 | ||
9d941742 TB |
382 | METHOD(child_sa_t, set_ipcomp, void, |
383 | private_child_sa_t *this, ipcomp_transform_t ipcomp) | |
bcb95ced | 384 | { |
3aaf7908 | 385 | this->ipcomp = ipcomp; |
bcb95ced MW |
386 | } |
387 | ||
9d941742 TB |
388 | METHOD(child_sa_t, set_close_action, void, |
389 | private_child_sa_t *this, action_t action) | |
4c401ea2 MW |
390 | { |
391 | this->close_action = action; | |
392 | } | |
393 | ||
9d941742 TB |
394 | METHOD(child_sa_t, get_close_action, action_t, |
395 | private_child_sa_t *this) | |
4c401ea2 MW |
396 | { |
397 | return this->close_action; | |
398 | } | |
399 | ||
9d941742 TB |
400 | METHOD(child_sa_t, set_dpd_action, void, |
401 | private_child_sa_t *this, action_t action) | |
4c401ea2 MW |
402 | { |
403 | this->dpd_action = action; | |
404 | } | |
405 | ||
9d941742 TB |
406 | METHOD(child_sa_t, get_dpd_action, action_t, |
407 | private_child_sa_t *this) | |
4c401ea2 MW |
408 | { |
409 | return this->dpd_action; | |
410 | } | |
411 | ||
9d941742 TB |
412 | METHOD(child_sa_t, get_proposal, proposal_t*, |
413 | private_child_sa_t *this) | |
c60c7694 | 414 | { |
3aaf7908 MW |
415 | return this->proposal; |
416 | } | |
417 | ||
9d941742 TB |
418 | METHOD(child_sa_t, set_proposal, void, |
419 | private_child_sa_t *this, proposal_t *proposal) | |
3aaf7908 MW |
420 | { |
421 | this->proposal = proposal->clone(proposal); | |
422 | } | |
423 | ||
553bb787 MW |
424 | METHOD(child_sa_t, create_ts_enumerator, enumerator_t*, |
425 | private_child_sa_t *this, bool local) | |
3aaf7908 | 426 | { |
553bb787 MW |
427 | if (local) |
428 | { | |
2745ae26 | 429 | return array_create_enumerator(this->my_ts); |
553bb787 | 430 | } |
2745ae26 | 431 | return array_create_enumerator(this->other_ts); |
c60c7694 MW |
432 | } |
433 | ||
1df106bf MW |
434 | typedef struct policy_enumerator_t policy_enumerator_t; |
435 | ||
436 | /** | |
437 | * Private policy enumerator | |
438 | */ | |
439 | struct policy_enumerator_t { | |
440 | /** implements enumerator_t */ | |
441 | enumerator_t public; | |
442 | /** enumerator over own TS */ | |
443 | enumerator_t *mine; | |
444 | /** enumerator over others TS */ | |
445 | enumerator_t *other; | |
2745ae26 MW |
446 | /** array of others TS, to recreate enumerator */ |
447 | array_t *array; | |
6df2837a MW |
448 | /** currently enumerating TS for "me" side */ |
449 | traffic_selector_t *ts; | |
1df106bf MW |
450 | }; |
451 | ||
9d941742 | 452 | METHOD(enumerator_t, policy_enumerate, bool, |
95a63bf2 | 453 | policy_enumerator_t *this, va_list args) |
1df106bf | 454 | { |
95a63bf2 TB |
455 | traffic_selector_t *other_ts, **my_out, **other_out; |
456 | ||
457 | VA_ARGS_VGET(args, my_out, other_out); | |
7daf5226 | 458 | |
6df2837a | 459 | while (this->ts || this->mine->enumerate(this->mine, &this->ts)) |
1df106bf | 460 | { |
6df2837a MW |
461 | if (!this->other->enumerate(this->other, &other_ts)) |
462 | { /* end of others list, restart with new of mine */ | |
463 | this->other->destroy(this->other); | |
2745ae26 | 464 | this->other = array_create_enumerator(this->array); |
6df2837a MW |
465 | this->ts = NULL; |
466 | continue; | |
467 | } | |
468 | if (this->ts->get_type(this->ts) != other_ts->get_type(other_ts)) | |
469 | { /* family mismatch */ | |
470 | continue; | |
471 | } | |
472 | if (this->ts->get_protocol(this->ts) && | |
473 | other_ts->get_protocol(other_ts) && | |
474 | this->ts->get_protocol(this->ts) != other_ts->get_protocol(other_ts)) | |
475 | { /* protocol mismatch */ | |
476 | continue; | |
1df106bf | 477 | } |
a6e0f14f TB |
478 | if (my_out) |
479 | { | |
480 | *my_out = this->ts; | |
481 | } | |
482 | if (other_out) | |
483 | { | |
484 | *other_out = other_ts; | |
485 | } | |
6df2837a | 486 | return TRUE; |
1df106bf MW |
487 | } |
488 | return FALSE; | |
489 | } | |
490 | ||
9d941742 TB |
491 | METHOD(enumerator_t, policy_destroy, void, |
492 | policy_enumerator_t *this) | |
1df106bf MW |
493 | { |
494 | this->mine->destroy(this->mine); | |
495 | this->other->destroy(this->other); | |
496 | free(this); | |
497 | } | |
498 | ||
9d941742 TB |
499 | METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, |
500 | private_child_sa_t *this) | |
1df106bf | 501 | { |
9d941742 | 502 | policy_enumerator_t *e; |
7daf5226 | 503 | |
9d941742 TB |
504 | INIT(e, |
505 | .public = { | |
95a63bf2 TB |
506 | .enumerate = enumerator_enumerate_default, |
507 | .venumerate = _policy_enumerate, | |
9d941742 TB |
508 | .destroy = _policy_destroy, |
509 | }, | |
2745ae26 MW |
510 | .mine = array_create_enumerator(this->my_ts), |
511 | .other = array_create_enumerator(this->other_ts), | |
512 | .array = this->other_ts, | |
9d941742 TB |
513 | .ts = NULL, |
514 | ); | |
7daf5226 | 515 | |
1df106bf MW |
516 | return &e->public; |
517 | } | |
518 | ||
dd83c6d4 TB |
519 | /** |
520 | * update the cached usebytes | |
521 | * returns SUCCESS if the usebytes have changed, FAILED if not or no SPIs | |
522 | * are available, and NOT_SUPPORTED if the kernel interface does not support | |
99dd4291 | 523 | * querying the usebytes. |
dd83c6d4 | 524 | */ |
c3a78360 | 525 | static status_t update_usebytes(private_child_sa_t *this, bool inbound) |
dd83c6d4 TB |
526 | { |
527 | status_t status = FAILED; | |
b12c53ce | 528 | uint64_t bytes, packets; |
c99458e9 | 529 | time_t time; |
7daf5226 | 530 | |
dd83c6d4 TB |
531 | if (inbound) |
532 | { | |
533 | if (this->my_spi) | |
534 | { | |
89da06ac TB |
535 | kernel_ipsec_sa_id_t id = { |
536 | .src = this->other_addr, | |
537 | .dst = this->my_addr, | |
538 | .spi = this->my_spi, | |
539 | .proto = proto_ike2ip(this->protocol), | |
ea43f8ff | 540 | .mark = mark_in_sa(this), |
89da06ac TB |
541 | }; |
542 | kernel_ipsec_query_sa_t query = {}; | |
543 | ||
544 | status = charon->kernel->query_sa(charon->kernel, &id, &query, | |
545 | &bytes, &packets, &time); | |
dd83c6d4 TB |
546 | if (status == SUCCESS) |
547 | { | |
548 | if (bytes > this->my_usebytes) | |
549 | { | |
550 | this->my_usebytes = bytes; | |
7eeeb1c7 | 551 | this->my_usepackets = packets; |
5c12700f MW |
552 | if (time) |
553 | { | |
554 | this->my_usetime = time; | |
555 | } | |
dd83c6d4 | 556 | } |
1042b919 TE |
557 | else |
558 | { | |
559 | status = FAILED; | |
560 | } | |
dd83c6d4 TB |
561 | } |
562 | } | |
563 | } | |
564 | else | |
565 | { | |
2c116ef5 | 566 | if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) |
dd83c6d4 | 567 | { |
89da06ac TB |
568 | kernel_ipsec_sa_id_t id = { |
569 | .src = this->my_addr, | |
570 | .dst = this->other_addr, | |
571 | .spi = this->other_spi, | |
572 | .proto = proto_ike2ip(this->protocol), | |
573 | .mark = this->mark_out, | |
574 | }; | |
575 | kernel_ipsec_query_sa_t query = {}; | |
576 | ||
577 | status = charon->kernel->query_sa(charon->kernel, &id, &query, | |
578 | &bytes, &packets, &time); | |
dd83c6d4 TB |
579 | if (status == SUCCESS) |
580 | { | |
581 | if (bytes > this->other_usebytes) | |
582 | { | |
583 | this->other_usebytes = bytes; | |
7eeeb1c7 | 584 | this->other_usepackets = packets; |
5c12700f MW |
585 | if (time) |
586 | { | |
587 | this->other_usetime = time; | |
588 | } | |
dd83c6d4 | 589 | } |
1042b919 TE |
590 | else |
591 | { | |
592 | status = FAILED; | |
593 | } | |
dd83c6d4 TB |
594 | } |
595 | } | |
596 | } | |
597 | return status; | |
598 | } | |
599 | ||
a3ce4bc2 | 600 | /** |
c3a78360 | 601 | * updates the cached usetime |
a3ce4bc2 | 602 | */ |
5c12700f | 603 | static bool update_usetime(private_child_sa_t *this, bool inbound) |
a3ce4bc2 | 604 | { |
1df106bf | 605 | enumerator_t *enumerator; |
6e10aead | 606 | traffic_selector_t *my_ts, *other_ts; |
d7083b65 | 607 | time_t last_use = 0; |
7daf5226 | 608 | |
1df106bf MW |
609 | enumerator = create_policy_enumerator(this); |
610 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
a3ce4bc2 | 611 | { |
d7083b65 | 612 | time_t in, out, fwd; |
7daf5226 | 613 | |
dd83c6d4 | 614 | if (inbound) |
a3ce4bc2 | 615 | { |
89da06ac TB |
616 | kernel_ipsec_policy_id_t id = { |
617 | .dir = POLICY_IN, | |
618 | .src_ts = other_ts, | |
619 | .dst_ts = my_ts, | |
620 | .mark = this->mark_in, | |
621 | }; | |
622 | kernel_ipsec_query_policy_t query = {}; | |
623 | ||
624 | if (charon->kernel->query_policy(charon->kernel, &id, &query, | |
625 | &in) == SUCCESS) | |
6e10aead MW |
626 | { |
627 | last_use = max(last_use, in); | |
628 | } | |
ce42db09 | 629 | if (this->mode != MODE_TRANSPORT) |
6e10aead | 630 | { |
89da06ac TB |
631 | id.dir = POLICY_FWD; |
632 | if (charon->kernel->query_policy(charon->kernel, &id, &query, | |
633 | &fwd) == SUCCESS) | |
d487b4b7 AS |
634 | { |
635 | last_use = max(last_use, fwd); | |
636 | } | |
6e10aead | 637 | } |
a3ce4bc2 | 638 | } |
6e10aead | 639 | else |
a3ce4bc2 | 640 | { |
89da06ac TB |
641 | kernel_ipsec_policy_id_t id = { |
642 | .dir = POLICY_OUT, | |
643 | .src_ts = my_ts, | |
644 | .dst_ts = other_ts, | |
645 | .mark = this->mark_out, | |
c26e4330 | 646 | .interface = this->config->get_interface(this->config), |
89da06ac TB |
647 | }; |
648 | kernel_ipsec_query_policy_t query = {}; | |
649 | ||
650 | if (charon->kernel->query_policy(charon->kernel, &id, &query, | |
651 | &out) == SUCCESS) | |
6e10aead MW |
652 | { |
653 | last_use = max(last_use, out); | |
654 | } | |
a3ce4bc2 MW |
655 | } |
656 | } | |
1df106bf | 657 | enumerator->destroy(enumerator); |
99dd4291 AS |
658 | |
659 | if (last_use == 0) | |
660 | { | |
5c12700f | 661 | return FALSE; |
99dd4291 | 662 | } |
3f720dc7 AS |
663 | if (inbound) |
664 | { | |
665 | this->my_usetime = last_use; | |
666 | } | |
667 | else | |
668 | { | |
669 | this->other_usetime = last_use; | |
670 | } | |
5c12700f | 671 | return TRUE; |
6e10aead MW |
672 | } |
673 | ||
9d941742 | 674 | METHOD(child_sa_t, get_usestats, void, |
d954a208 | 675 | private_child_sa_t *this, bool inbound, |
b12c53ce | 676 | time_t *time, uint64_t *bytes, uint64_t *packets) |
2ad51539 | 677 | { |
bdaf9f97 | 678 | if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED) |
99dd4291 AS |
679 | { |
680 | /* there was traffic since last update or the kernel interface | |
681 | * does not support querying the number of usebytes. | |
682 | */ | |
bdaf9f97 MW |
683 | if (time) |
684 | { | |
5c12700f MW |
685 | if (!update_usetime(this, inbound) && !bytes && !packets) |
686 | { | |
687 | /* if policy query did not yield a usetime, query SAs instead */ | |
688 | update_usebytes(this, inbound); | |
689 | } | |
bdaf9f97 | 690 | } |
c3a78360 TB |
691 | } |
692 | if (time) | |
693 | { | |
694 | *time = inbound ? this->my_usetime : this->other_usetime; | |
695 | } | |
696 | if (bytes) | |
697 | { | |
698 | *bytes = inbound ? this->my_usebytes : this->other_usebytes; | |
699 | } | |
d954a208 MW |
700 | if (packets) |
701 | { | |
702 | *packets = inbound ? this->my_usepackets : this->other_usepackets; | |
703 | } | |
2ad51539 AS |
704 | } |
705 | ||
79f39ecf MW |
706 | METHOD(child_sa_t, get_mark, mark_t, |
707 | private_child_sa_t *this, bool inbound) | |
708 | { | |
709 | if (inbound) | |
710 | { | |
711 | return this->mark_in; | |
712 | } | |
713 | return this->mark_out; | |
714 | } | |
715 | ||
9d941742 TB |
716 | METHOD(child_sa_t, get_lifetime, time_t, |
717 | private_child_sa_t *this, bool hard) | |
6e10aead | 718 | { |
80853d84 | 719 | return hard ? this->expire_time : this->rekey_time; |
a3ce4bc2 MW |
720 | } |
721 | ||
763e0353 MW |
722 | METHOD(child_sa_t, get_installtime, time_t, |
723 | private_child_sa_t *this) | |
724 | { | |
725 | return this->install_time; | |
726 | } | |
727 | ||
b12c53ce | 728 | METHOD(child_sa_t, alloc_spi, uint32_t, |
9d941742 | 729 | private_child_sa_t *this, protocol_id_t protocol) |
1396815a | 730 | { |
8394ea2a TB |
731 | if (charon->kernel->get_spi(charon->kernel, this->other_addr, this->my_addr, |
732 | proto_ike2ip(protocol), &this->my_spi) == SUCCESS) | |
64e8ca28 | 733 | { |
21b096f3 MW |
734 | /* if we allocate a SPI, but then are unable to establish the SA, we |
735 | * need to know the protocol family to delete the partial SA */ | |
736 | this->protocol = protocol; | |
64e8ca28 | 737 | return this->my_spi; |
1396815a | 738 | } |
3aaf7908 | 739 | return 0; |
1396815a MW |
740 | } |
741 | ||
b12c53ce | 742 | METHOD(child_sa_t, alloc_cpi, uint16_t, |
9d941742 | 743 | private_child_sa_t *this) |
3ebebc5e | 744 | { |
8394ea2a TB |
745 | if (charon->kernel->get_cpi(charon->kernel, this->other_addr, this->my_addr, |
746 | &this->my_cpi) == SUCCESS) | |
30b5b412 | 747 | { |
3aaf7908 | 748 | return this->my_cpi; |
30b5b412 | 749 | } |
3aaf7908 | 750 | return 0; |
3ebebc5e MW |
751 | } |
752 | ||
8a3a389e TB |
753 | /** |
754 | * Install the given SA in the kernel | |
755 | */ | |
756 | static status_t install_internal(private_child_sa_t *this, chunk_t encr, | |
757 | chunk_t integ, uint32_t spi, uint16_t cpi, bool initiator, bool inbound, | |
758 | bool tfcv3) | |
3ebebc5e | 759 | { |
b12c53ce AS |
760 | uint16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; |
761 | uint16_t esn = NO_EXT_SEQ_NUMBERS; | |
4989aba8 | 762 | linked_list_t *my_ts, *other_ts, *src_ts, *dst_ts; |
cb123493 | 763 | time_t now; |
89da06ac TB |
764 | kernel_ipsec_sa_id_t id; |
765 | kernel_ipsec_add_sa_t sa; | |
cb123493 | 766 | lifetime_cfg_t *lifetime; |
b12c53ce | 767 | uint32_t tfc = 0; |
e517b4b1 | 768 | host_t *src, *dst; |
30b5b412 | 769 | status_t status; |
acad5e9f | 770 | bool update = FALSE; |
7daf5226 | 771 | |
4989aba8 TB |
772 | /* BEET requires the bound address from the traffic selectors */ |
773 | my_ts = linked_list_create_from_enumerator( | |
774 | array_create_enumerator(this->my_ts)); | |
775 | other_ts = linked_list_create_from_enumerator( | |
776 | array_create_enumerator(this->other_ts)); | |
777 | ||
80853d84 MW |
778 | /* now we have to decide which spi to use. Use self allocated, if "in", |
779 | * or the one in the proposal, if not "in" (others). Additionally, | |
3efbf983 | 780 | * source and dest host switch depending on the role */ |
3aaf7908 | 781 | if (inbound) |
aeda79ff | 782 | { |
80853d84 MW |
783 | dst = this->my_addr; |
784 | src = this->other_addr; | |
acad5e9f MW |
785 | if (this->my_spi == spi) |
786 | { /* alloc_spi has been called, do an SA update */ | |
787 | update = TRUE; | |
788 | } | |
3aaf7908 MW |
789 | this->my_spi = spi; |
790 | this->my_cpi = cpi; | |
4989aba8 TB |
791 | dst_ts = my_ts; |
792 | src_ts = other_ts; | |
aeda79ff | 793 | } |
8d77edde MW |
794 | else |
795 | { | |
80853d84 MW |
796 | src = this->my_addr; |
797 | dst = this->other_addr; | |
3aaf7908 MW |
798 | this->other_spi = spi; |
799 | this->other_cpi = cpi; | |
4989aba8 TB |
800 | src_ts = my_ts; |
801 | dst_ts = other_ts; | |
37788b1d | 802 | |
55df72e6 MW |
803 | if (tfcv3) |
804 | { | |
805 | tfc = this->config->get_tfc(this->config); | |
806 | } | |
2c116ef5 | 807 | this->outbound_state |= CHILD_OUTBOUND_SA; |
8d77edde | 808 | } |
7daf5226 | 809 | |
3aaf7908 | 810 | DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", |
60356f33 | 811 | protocol_id_names, this->protocol); |
7daf5226 | 812 | |
5c131a01 | 813 | /* send SA down to the kernel */ |
b83806d8 | 814 | DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); |
7daf5226 | 815 | |
3aaf7908 MW |
816 | this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM, |
817 | &enc_alg, &size); | |
818 | this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM, | |
819 | &int_alg, &size); | |
bd01b9d8 MW |
820 | this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS, |
821 | &esn, NULL); | |
7daf5226 | 822 | |
7637633b TB |
823 | if (int_alg == AUTH_HMAC_SHA2_256_128 && |
824 | this->config->has_option(this->config, OPT_SHA256_96)) | |
825 | { | |
826 | DBG2(DBG_CHD, " using %N with 96-bit truncation", | |
827 | integrity_algorithm_names, int_alg); | |
828 | int_alg = AUTH_HMAC_SHA2_256_96; | |
829 | } | |
830 | ||
3665adef | 831 | if (!this->reqid_allocated && !this->static_reqid) |
0da97f50 | 832 | { |
8394ea2a TB |
833 | status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts, |
834 | this->mark_in, this->mark_out, &this->reqid); | |
0da97f50 MW |
835 | if (status != SUCCESS) |
836 | { | |
4989aba8 TB |
837 | my_ts->destroy(my_ts); |
838 | other_ts->destroy(other_ts); | |
0da97f50 MW |
839 | return status; |
840 | } | |
841 | this->reqid_allocated = TRUE; | |
842 | } | |
843 | ||
b1df6312 | 844 | lifetime = this->config->get_lifetime(this->config, TRUE); |
7daf5226 | 845 | |
6180a558 | 846 | now = time_monotonic(NULL); |
e75f4237 | 847 | if (lifetime->time.rekey) |
37974979 | 848 | { |
106b938b MW |
849 | if (this->rekey_time) |
850 | { | |
851 | this->rekey_time = min(this->rekey_time, now + lifetime->time.rekey); | |
852 | } | |
853 | else | |
854 | { | |
855 | this->rekey_time = now + lifetime->time.rekey; | |
856 | } | |
37974979 | 857 | } |
e75f4237 | 858 | if (lifetime->time.life) |
37974979 | 859 | { |
e75f4237 | 860 | this->expire_time = now + lifetime->time.life; |
cb123493 | 861 | } |
7daf5226 | 862 | |
e75f4237 | 863 | if (!lifetime->time.jitter && !inbound) |
cb123493 | 864 | { /* avoid triggering multiple rekey events */ |
e75f4237 | 865 | lifetime->time.rekey = 0; |
37974979 | 866 | } |
7daf5226 | 867 | |
89da06ac TB |
868 | id = (kernel_ipsec_sa_id_t){ |
869 | .src = src, | |
870 | .dst = dst, | |
871 | .spi = spi, | |
872 | .proto = proto_ike2ip(this->protocol), | |
ea43f8ff | 873 | .mark = inbound ? mark_in_sa(this) : this->mark_out, |
89da06ac TB |
874 | }; |
875 | sa = (kernel_ipsec_add_sa_t){ | |
876 | .reqid = this->reqid, | |
877 | .mode = this->mode, | |
878 | .src_ts = src_ts, | |
879 | .dst_ts = dst_ts, | |
c26e4330 | 880 | .interface = inbound ? NULL : this->config->get_interface(this->config), |
89da06ac TB |
881 | .lifetime = lifetime, |
882 | .enc_alg = enc_alg, | |
883 | .enc_key = encr, | |
884 | .int_alg = int_alg, | |
885 | .int_key = integ, | |
886 | .replay_window = this->config->get_replay_window(this->config), | |
887 | .tfc = tfc, | |
888 | .ipcomp = this->ipcomp, | |
889 | .cpi = cpi, | |
890 | .encap = this->encap, | |
aeee0bcc | 891 | .hw_offload = this->config->has_option(this->config, OPT_HW_OFFLOAD), |
89da06ac TB |
892 | .esn = esn, |
893 | .initiator = initiator, | |
894 | .inbound = inbound, | |
895 | .update = update, | |
896 | }; | |
897 | ||
898 | status = charon->kernel->add_sa(charon->kernel, &id, &sa); | |
7daf5226 | 899 | |
4989aba8 TB |
900 | my_ts->destroy(my_ts); |
901 | other_ts->destroy(other_ts); | |
cb123493 | 902 | free(lifetime); |
7daf5226 | 903 | |
8d77edde | 904 | return status; |
30b5b412 MW |
905 | } |
906 | ||
8a3a389e TB |
907 | METHOD(child_sa_t, install, status_t, |
908 | private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, | |
909 | uint16_t cpi, bool initiator, bool inbound, bool tfcv3) | |
910 | { | |
911 | return install_internal(this, encr, integ, spi, cpi, initiator, inbound, | |
912 | tfcv3); | |
913 | } | |
914 | ||
7452adfa MW |
915 | /** |
916 | * Check kernel interface if policy updates are required | |
917 | */ | |
918 | static bool require_policy_update() | |
919 | { | |
920 | kernel_feature_t f; | |
921 | ||
8394ea2a | 922 | f = charon->kernel->get_features(charon->kernel); |
7452adfa MW |
923 | return !(f & KERNEL_NO_POLICY_UPDATES); |
924 | } | |
925 | ||
a6e0f14f TB |
926 | /** |
927 | * Prepare SA config to install/delete policies | |
928 | */ | |
929 | static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, | |
930 | ipsec_sa_cfg_t *other_sa) | |
931 | { | |
932 | enumerator_t *enumerator; | |
933 | ||
934 | *my_sa = (ipsec_sa_cfg_t){ | |
935 | .mode = this->mode, | |
936 | .reqid = this->reqid, | |
937 | .ipcomp = { | |
938 | .transform = this->ipcomp, | |
939 | }, | |
940 | }; | |
941 | *other_sa = *my_sa; | |
942 | ||
943 | my_sa->ipcomp.cpi = this->my_cpi; | |
944 | other_sa->ipcomp.cpi = this->other_cpi; | |
945 | ||
946 | if (this->protocol == PROTO_ESP) | |
947 | { | |
948 | my_sa->esp.use = TRUE; | |
949 | my_sa->esp.spi = this->my_spi; | |
950 | other_sa->esp.use = TRUE; | |
951 | other_sa->esp.spi = this->other_spi; | |
952 | } | |
953 | else | |
954 | { | |
955 | my_sa->ah.use = TRUE; | |
956 | my_sa->ah.spi = this->my_spi; | |
957 | other_sa->ah.use = TRUE; | |
958 | other_sa->ah.spi = this->other_spi; | |
959 | } | |
960 | ||
961 | enumerator = create_policy_enumerator(this); | |
962 | while (enumerator->enumerate(enumerator, NULL, NULL)) | |
963 | { | |
964 | my_sa->policy_count++; | |
965 | other_sa->policy_count++; | |
966 | } | |
967 | enumerator->destroy(enumerator); | |
968 | } | |
969 | ||
f1c1965d | 970 | /** |
29ef4cf5 | 971 | * Install inbound policie(s): in, fwd |
f1c1965d | 972 | */ |
29ef4cf5 | 973 | static status_t install_policies_inbound(private_child_sa_t *this, |
f1c1965d TB |
974 | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
975 | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, | |
7f57c4f9 AS |
976 | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
977 | policy_priority_t priority, uint32_t manual_prio) | |
f1c1965d | 978 | { |
29ef4cf5 | 979 | kernel_ipsec_policy_id_t in_id = { |
89da06ac TB |
980 | .dir = POLICY_IN, |
981 | .src_ts = other_ts, | |
982 | .dst_ts = my_ts, | |
983 | .mark = this->mark_in, | |
984 | }; | |
29ef4cf5 | 985 | kernel_ipsec_manage_policy_t in_policy = { |
89da06ac TB |
986 | .type = type, |
987 | .prio = priority, | |
7f57c4f9 | 988 | .manual_prio = manual_prio, |
89da06ac TB |
989 | .src = other_addr, |
990 | .dst = my_addr, | |
991 | .sa = my_sa, | |
992 | }; | |
f1c1965d | 993 | status_t status = SUCCESS; |
89da06ac | 994 | |
89da06ac | 995 | status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); |
f1c1965d TB |
996 | if (this->mode != MODE_TRANSPORT) |
997 | { | |
89da06ac TB |
998 | in_id.dir = POLICY_FWD; |
999 | status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); | |
29ef4cf5 TB |
1000 | } |
1001 | return status; | |
1002 | } | |
9c126352 | 1003 | |
29ef4cf5 TB |
1004 | /** |
1005 | * Install outbound policie(s): out, [fwd] | |
1006 | */ | |
1007 | static status_t install_policies_outbound(private_child_sa_t *this, | |
1008 | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, | |
1009 | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, | |
1010 | ipsec_sa_cfg_t *other_sa, policy_type_t type, | |
1011 | policy_priority_t priority, uint32_t manual_prio) | |
1012 | { | |
1013 | kernel_ipsec_policy_id_t out_id = { | |
1014 | .dir = POLICY_OUT, | |
1015 | .src_ts = my_ts, | |
1016 | .dst_ts = other_ts, | |
1017 | .mark = this->mark_out, | |
1018 | .interface = this->config->get_interface(this->config), | |
1019 | }; | |
1020 | kernel_ipsec_manage_policy_t out_policy = { | |
1021 | .type = type, | |
1022 | .prio = priority, | |
1023 | .manual_prio = manual_prio, | |
1024 | .src = my_addr, | |
1025 | .dst = other_addr, | |
1026 | .sa = other_sa, | |
1027 | }; | |
1028 | status_t status = SUCCESS; | |
1029 | ||
1030 | status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy); | |
1031 | ||
1032 | if (this->mode != MODE_TRANSPORT && this->policies_fwd_out) | |
1033 | { | |
9c126352 TB |
1034 | /* install an "outbound" FWD policy in case there is a drop policy |
1035 | * matching outbound forwarded traffic, to allow another tunnel to use | |
1036 | * the reversed subnets and do the same we don't set a reqid (this also | |
1037 | * allows the kernel backend to distinguish between the two types of | |
979f4651 TB |
1038 | * FWD policies). To avoid problems with symmetrically overlapping |
1039 | * policies of two SAs we install them with reduced priority. As they | |
1040 | * basically act as bypass policies for drop policies we use a higher | |
1041 | * priority than is used for them. */ | |
29ef4cf5 TB |
1042 | out_id.dir = POLICY_FWD; |
1043 | other_sa->reqid = 0; | |
1044 | if (priority == POLICY_PRIORITY_DEFAULT) | |
979f4651 | 1045 | { |
29ef4cf5 | 1046 | out_policy.prio = POLICY_PRIORITY_ROUTED; |
979f4651 | 1047 | } |
29ef4cf5 TB |
1048 | status |= charon->kernel->add_policy(charon->kernel, &out_id, |
1049 | &out_policy); | |
1050 | /* reset the reqid for any other further policies */ | |
1051 | other_sa->reqid = this->reqid; | |
f1c1965d TB |
1052 | } |
1053 | return status; | |
1054 | } | |
1055 | ||
1056 | /** | |
29ef4cf5 | 1057 | * Install all policies |
f1c1965d | 1058 | */ |
29ef4cf5 TB |
1059 | static status_t install_policies_internal(private_child_sa_t *this, |
1060 | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, | |
1061 | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, | |
1062 | ipsec_sa_cfg_t *other_sa, policy_type_t type, | |
529ffc2f | 1063 | policy_priority_t priority, uint32_t manual_prio, bool outbound) |
29ef4cf5 TB |
1064 | { |
1065 | status_t status = SUCCESS; | |
1066 | ||
1067 | status |= install_policies_inbound(this, my_addr, other_addr, my_ts, | |
529ffc2f TB |
1068 | other_ts, my_sa, other_sa, type, priority, manual_prio); |
1069 | if (outbound) | |
1070 | { | |
1071 | status |= install_policies_outbound(this, my_addr, other_addr, my_ts, | |
1072 | other_ts, my_sa, other_sa, type, priority, manual_prio); | |
1073 | } | |
29ef4cf5 TB |
1074 | return status; |
1075 | } | |
1076 | ||
1077 | /** | |
1078 | * Delete inbound policies: in, fwd | |
1079 | */ | |
1080 | static void del_policies_inbound(private_child_sa_t *this, | |
a6e0f14f TB |
1081 | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, |
1082 | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, | |
7f57c4f9 AS |
1083 | ipsec_sa_cfg_t *other_sa, policy_type_t type, |
1084 | policy_priority_t priority, uint32_t manual_prio) | |
f1c1965d | 1085 | { |
29ef4cf5 | 1086 | kernel_ipsec_policy_id_t in_id = { |
89da06ac TB |
1087 | .dir = POLICY_IN, |
1088 | .src_ts = other_ts, | |
1089 | .dst_ts = my_ts, | |
1090 | .mark = this->mark_in, | |
1091 | }; | |
29ef4cf5 | 1092 | kernel_ipsec_manage_policy_t in_policy = { |
89da06ac TB |
1093 | .type = type, |
1094 | .prio = priority, | |
7f57c4f9 | 1095 | .manual_prio = manual_prio, |
89da06ac TB |
1096 | .src = other_addr, |
1097 | .dst = my_addr, | |
1098 | .sa = my_sa, | |
1099 | }; | |
a6e0f14f | 1100 | |
89da06ac | 1101 | charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); |
29ef4cf5 | 1102 | |
f1c1965d TB |
1103 | if (this->mode != MODE_TRANSPORT) |
1104 | { | |
89da06ac TB |
1105 | in_id.dir = POLICY_FWD; |
1106 | charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); | |
29ef4cf5 TB |
1107 | } |
1108 | } | |
1109 | ||
1110 | /** | |
1111 | * Delete outbound policies: out, [fwd] | |
1112 | */ | |
1113 | static void del_policies_outbound(private_child_sa_t *this, | |
1114 | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, | |
1115 | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, | |
1116 | ipsec_sa_cfg_t *other_sa, policy_type_t type, | |
1117 | policy_priority_t priority, uint32_t manual_prio) | |
1118 | { | |
1119 | kernel_ipsec_policy_id_t out_id = { | |
1120 | .dir = POLICY_OUT, | |
1121 | .src_ts = my_ts, | |
1122 | .dst_ts = other_ts, | |
1123 | .mark = this->mark_out, | |
1124 | .interface = this->config->get_interface(this->config), | |
1125 | }; | |
1126 | kernel_ipsec_manage_policy_t out_policy = { | |
1127 | .type = type, | |
1128 | .prio = priority, | |
1129 | .manual_prio = manual_prio, | |
1130 | .src = my_addr, | |
1131 | .dst = other_addr, | |
1132 | .sa = other_sa, | |
1133 | }; | |
1134 | ||
1135 | charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); | |
9c126352 | 1136 | |
29ef4cf5 TB |
1137 | if (this->mode != MODE_TRANSPORT && this->policies_fwd_out) |
1138 | { | |
1139 | out_id.dir = POLICY_FWD; | |
1140 | other_sa->reqid = 0; | |
1141 | if (priority == POLICY_PRIORITY_DEFAULT) | |
979f4651 | 1142 | { |
29ef4cf5 | 1143 | out_policy.prio = POLICY_PRIORITY_ROUTED; |
979f4651 | 1144 | } |
29ef4cf5 TB |
1145 | charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); |
1146 | other_sa->reqid = this->reqid; | |
f1c1965d TB |
1147 | } |
1148 | } | |
1149 | ||
29ef4cf5 TB |
1150 | /** |
1151 | * Delete in- and outbound policies | |
1152 | */ | |
1153 | static void del_policies_internal(private_child_sa_t *this, | |
1154 | host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, | |
1155 | traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, | |
1156 | ipsec_sa_cfg_t *other_sa, policy_type_t type, | |
529ffc2f | 1157 | policy_priority_t priority, uint32_t manual_prio, bool outbound) |
29ef4cf5 | 1158 | { |
529ffc2f TB |
1159 | if (outbound) |
1160 | { | |
1161 | del_policies_outbound(this, my_addr, other_addr, my_ts, other_ts, my_sa, | |
1162 | other_sa, type, priority, manual_prio); | |
1163 | } | |
29ef4cf5 | 1164 | del_policies_inbound(this, my_addr, other_addr, my_ts, other_ts, my_sa, |
529ffc2f | 1165 | other_sa, type, priority, manual_prio); |
29ef4cf5 TB |
1166 | } |
1167 | ||
4989aba8 | 1168 | METHOD(child_sa_t, set_policies, void, |
9d941742 TB |
1169 | private_child_sa_t *this, linked_list_t *my_ts_list, |
1170 | linked_list_t *other_ts_list) | |
a527a426 | 1171 | { |
1df106bf | 1172 | enumerator_t *enumerator; |
5d187bd2 | 1173 | traffic_selector_t *my_ts, *other_ts; |
7daf5226 | 1174 | |
4989aba8 | 1175 | if (array_count(this->my_ts)) |
0da97f50 | 1176 | { |
4989aba8 TB |
1177 | array_destroy_offset(this->my_ts, |
1178 | offsetof(traffic_selector_t, destroy)); | |
1179 | this->my_ts = array_create(0, 0); | |
0da97f50 | 1180 | } |
1df106bf MW |
1181 | enumerator = my_ts_list->create_enumerator(my_ts_list); |
1182 | while (enumerator->enumerate(enumerator, &my_ts)) | |
a527a426 | 1183 | { |
2745ae26 | 1184 | array_insert(this->my_ts, ARRAY_TAIL, my_ts->clone(my_ts)); |
1df106bf MW |
1185 | } |
1186 | enumerator->destroy(enumerator); | |
5499473c MW |
1187 | array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL); |
1188 | ||
4989aba8 TB |
1189 | if (array_count(this->other_ts)) |
1190 | { | |
1191 | array_destroy_offset(this->other_ts, | |
1192 | offsetof(traffic_selector_t, destroy)); | |
1193 | this->other_ts = array_create(0, 0); | |
1194 | } | |
1df106bf MW |
1195 | enumerator = other_ts_list->create_enumerator(other_ts_list); |
1196 | while (enumerator->enumerate(enumerator, &other_ts)) | |
1197 | { | |
2745ae26 | 1198 | array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts)); |
1df106bf MW |
1199 | } |
1200 | enumerator->destroy(enumerator); | |
5499473c | 1201 | array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL); |
4989aba8 TB |
1202 | } |
1203 | ||
1204 | METHOD(child_sa_t, install_policies, status_t, | |
1205 | private_child_sa_t *this) | |
1206 | { | |
1207 | enumerator_t *enumerator; | |
1208 | linked_list_t *my_ts_list, *other_ts_list; | |
1209 | traffic_selector_t *my_ts, *other_ts; | |
1210 | status_t status = SUCCESS; | |
2c116ef5 | 1211 | bool install_outbound = FALSE; |
4989aba8 TB |
1212 | |
1213 | if (!this->reqid_allocated && !this->static_reqid) | |
1214 | { | |
1215 | my_ts_list = linked_list_create_from_enumerator( | |
1216 | array_create_enumerator(this->my_ts)); | |
1217 | other_ts_list = linked_list_create_from_enumerator( | |
1218 | array_create_enumerator(this->other_ts)); | |
1219 | status = charon->kernel->alloc_reqid( | |
1220 | charon->kernel, my_ts_list, other_ts_list, | |
1221 | this->mark_in, this->mark_out, &this->reqid); | |
1222 | my_ts_list->destroy(my_ts_list); | |
1223 | other_ts_list->destroy(other_ts_list); | |
1224 | if (status != SUCCESS) | |
1225 | { | |
1226 | return status; | |
1227 | } | |
1228 | this->reqid_allocated = TRUE; | |
1229 | } | |
7daf5226 | 1230 | |
2c116ef5 TB |
1231 | if (!(this->outbound_state & CHILD_OUTBOUND_REGISTERED)) |
1232 | { | |
1233 | install_outbound = TRUE; | |
1234 | this->outbound_state |= CHILD_OUTBOUND_POLICIES; | |
1235 | } | |
1236 | ||
749ac175 | 1237 | if (!this->config->has_option(this->config, OPT_NO_POLICIES)) |
1df106bf | 1238 | { |
fbedc6a4 | 1239 | policy_priority_t priority; |
a6e0f14f | 1240 | ipsec_sa_cfg_t my_sa, other_sa; |
7f57c4f9 | 1241 | uint32_t manual_prio; |
a6e0f14f TB |
1242 | |
1243 | prepare_sa_cfg(this, &my_sa, &other_sa); | |
7f57c4f9 | 1244 | manual_prio = this->config->get_manual_prio(this->config); |
71b6d2ff | 1245 | |
c8f7a114 TB |
1246 | /* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD |
1247 | * entry) we install a trap policy */ | |
1248 | this->trap = this->state == CHILD_CREATED; | |
1249 | priority = this->trap ? POLICY_PRIORITY_ROUTED | |
1250 | : POLICY_PRIORITY_DEFAULT; | |
fbedc6a4 | 1251 | |
d487b4b7 AS |
1252 | /* enumerate pairs of traffic selectors */ |
1253 | enumerator = create_policy_enumerator(this); | |
1254 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
1255 | { | |
529ffc2f | 1256 | status |= install_policies_internal(this, this->my_addr, |
f1c1965d | 1257 | this->other_addr, my_ts, other_ts, |
529ffc2f TB |
1258 | &my_sa, &other_sa, POLICY_IPSEC, priority, |
1259 | manual_prio, install_outbound); | |
d487b4b7 AS |
1260 | if (status != SUCCESS) |
1261 | { | |
1262 | break; | |
1263 | } | |
5d187bd2 | 1264 | } |
d487b4b7 | 1265 | enumerator->destroy(enumerator); |
5d187bd2 | 1266 | } |
7daf5226 | 1267 | |
c8f7a114 TB |
1268 | if (status == SUCCESS && this->trap) |
1269 | { | |
3aaf7908 | 1270 | set_state(this, CHILD_ROUTED); |
45f76a7d | 1271 | } |
1df106bf | 1272 | return status; |
30b5b412 MW |
1273 | } |
1274 | ||
a146b4c9 | 1275 | METHOD(child_sa_t, register_outbound, status_t, |
8a3a389e TB |
1276 | private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, |
1277 | uint16_t cpi, bool tfcv3) | |
1278 | { | |
a146b4c9 TB |
1279 | status_t status; |
1280 | ||
1281 | /* if the kernel supports installing SPIs with policies we install the | |
1282 | * SA immediately as it will only be used once we update the policies */ | |
1283 | if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI) | |
1284 | { | |
1285 | status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE, | |
1286 | tfcv3); | |
1287 | } | |
1288 | else | |
1289 | { | |
1290 | DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, | |
1291 | this->protocol); | |
1292 | DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, | |
1293 | this->other_addr); | |
1294 | ||
1295 | this->other_spi = spi; | |
1296 | this->other_cpi = cpi; | |
1297 | this->encr_r = chunk_clone(encr); | |
1298 | this->integ_r = chunk_clone(integ); | |
1299 | this->tfcv3 = tfcv3; | |
1300 | status = SUCCESS; | |
1301 | } | |
2c116ef5 | 1302 | this->outbound_state |= CHILD_OUTBOUND_REGISTERED; |
a146b4c9 | 1303 | return status; |
8a3a389e TB |
1304 | } |
1305 | ||
1306 | METHOD(child_sa_t, install_outbound, status_t, | |
1307 | private_child_sa_t *this) | |
1308 | { | |
1309 | enumerator_t *enumerator; | |
1310 | traffic_selector_t *my_ts, *other_ts; | |
2c116ef5 | 1311 | status_t status = SUCCESS; |
8a3a389e | 1312 | |
a146b4c9 TB |
1313 | if (!(this->outbound_state & CHILD_OUTBOUND_SA)) |
1314 | { | |
1315 | status = install_internal(this, this->encr_r, this->integ_r, | |
1316 | this->other_spi, this->other_cpi, FALSE, | |
1317 | FALSE, this->tfcv3); | |
1318 | chunk_clear(&this->encr_r); | |
1319 | chunk_clear(&this->integ_r); | |
1320 | } | |
2c116ef5 | 1321 | this->outbound_state &= ~CHILD_OUTBOUND_REGISTERED; |
8a3a389e TB |
1322 | if (status != SUCCESS) |
1323 | { | |
1324 | return status; | |
1325 | } | |
a146b4c9 TB |
1326 | if (!this->config->has_option(this->config, OPT_NO_POLICIES) && |
1327 | !(this->outbound_state & CHILD_OUTBOUND_POLICIES)) | |
8a3a389e TB |
1328 | { |
1329 | ipsec_sa_cfg_t my_sa, other_sa; | |
1330 | uint32_t manual_prio; | |
1331 | ||
1332 | prepare_sa_cfg(this, &my_sa, &other_sa); | |
1333 | manual_prio = this->config->get_manual_prio(this->config); | |
1334 | ||
1335 | enumerator = create_policy_enumerator(this); | |
1336 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
1337 | { | |
8a3a389e TB |
1338 | status |= install_policies_outbound(this, this->my_addr, |
1339 | this->other_addr, my_ts, other_ts, | |
1340 | &my_sa, &other_sa, POLICY_IPSEC, | |
1341 | POLICY_PRIORITY_DEFAULT, manual_prio); | |
1342 | if (status != SUCCESS) | |
1343 | { | |
1344 | break; | |
1345 | } | |
1346 | } | |
1347 | enumerator->destroy(enumerator); | |
1348 | } | |
a146b4c9 | 1349 | this->outbound_state |= CHILD_OUTBOUND_POLICIES; |
8a3a389e TB |
1350 | return status; |
1351 | } | |
1352 | ||
c5fed4cd TB |
1353 | METHOD(child_sa_t, remove_outbound, void, |
1354 | private_child_sa_t *this) | |
1355 | { | |
1356 | enumerator_t *enumerator; | |
1357 | traffic_selector_t *my_ts, *other_ts; | |
1358 | ||
2c116ef5 | 1359 | if (!(this->outbound_state & CHILD_OUTBOUND_SA)) |
c5fed4cd | 1360 | { |
2c116ef5 TB |
1361 | if (this->outbound_state & CHILD_OUTBOUND_REGISTERED) |
1362 | { | |
c5fed4cd TB |
1363 | chunk_clear(&this->encr_r); |
1364 | chunk_clear(&this->integ_r); | |
afbea8ce | 1365 | this->outbound_state = CHILD_OUTBOUND_NONE; |
2c116ef5 TB |
1366 | } |
1367 | return; | |
c5fed4cd TB |
1368 | } |
1369 | ||
2c116ef5 TB |
1370 | if (!this->config->has_option(this->config, OPT_NO_POLICIES) && |
1371 | (this->outbound_state & CHILD_OUTBOUND_POLICIES)) | |
c5fed4cd TB |
1372 | { |
1373 | ipsec_sa_cfg_t my_sa, other_sa; | |
1374 | uint32_t manual_prio; | |
1375 | ||
1376 | prepare_sa_cfg(this, &my_sa, &other_sa); | |
1377 | manual_prio = this->config->get_manual_prio(this->config); | |
1378 | ||
1379 | enumerator = create_policy_enumerator(this); | |
1380 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
1381 | { | |
1382 | del_policies_outbound(this, this->my_addr, this->other_addr, | |
1383 | my_ts, other_ts, &my_sa, &other_sa, | |
1384 | POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, | |
1385 | manual_prio); | |
c5fed4cd TB |
1386 | } |
1387 | enumerator->destroy(enumerator); | |
1388 | } | |
1389 | ||
1390 | kernel_ipsec_sa_id_t id = { | |
1391 | .src = this->my_addr, | |
1392 | .dst = this->other_addr, | |
1393 | .spi = this->other_spi, | |
1394 | .proto = proto_ike2ip(this->protocol), | |
1395 | .mark = this->mark_out, | |
1396 | }; | |
1397 | kernel_ipsec_del_sa_t sa = { | |
1398 | .cpi = this->other_cpi, | |
1399 | }; | |
1400 | charon->kernel->del_sa(charon->kernel, &id, &sa); | |
afbea8ce | 1401 | this->outbound_state = CHILD_OUTBOUND_NONE; |
c5fed4cd TB |
1402 | } |
1403 | ||
cad13450 TB |
1404 | METHOD(child_sa_t, set_rekey_spi, void, |
1405 | private_child_sa_t *this, uint32_t spi) | |
1406 | { | |
1407 | this->rekey_spi = spi; | |
1408 | } | |
1409 | ||
1410 | METHOD(child_sa_t, get_rekey_spi, uint32_t, | |
1411 | private_child_sa_t *this) | |
1412 | { | |
1413 | return this->rekey_spi; | |
1414 | } | |
1415 | ||
8a2e4d4a TB |
1416 | CALLBACK(reinstall_vip, void, |
1417 | host_t *vip, va_list args) | |
101d26ba | 1418 | { |
8a2e4d4a | 1419 | host_t *me; |
b185cdd1 MW |
1420 | char *iface; |
1421 | ||
8a2e4d4a | 1422 | VA_ARGS_VGET(args, me); |
8394ea2a | 1423 | if (charon->kernel->get_interface(charon->kernel, me, &iface)) |
b185cdd1 | 1424 | { |
8394ea2a TB |
1425 | charon->kernel->del_ip(charon->kernel, vip, -1, TRUE); |
1426 | charon->kernel->add_ip(charon->kernel, vip, -1, iface); | |
b185cdd1 MW |
1427 | free(iface); |
1428 | } | |
101d26ba MW |
1429 | } |
1430 | ||
35ef1b03 TB |
1431 | /** |
1432 | * Update addresses and encap state of IPsec SAs in the kernel | |
1433 | */ | |
1434 | static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other, | |
1435 | bool encap) | |
1436 | { | |
1437 | /* update our (initiator) SA */ | |
1438 | if (this->my_spi) | |
1439 | { | |
1440 | kernel_ipsec_sa_id_t id = { | |
1441 | .src = this->other_addr, | |
1442 | .dst = this->my_addr, | |
1443 | .spi = this->my_spi, | |
1444 | .proto = proto_ike2ip(this->protocol), | |
1445 | .mark = mark_in_sa(this), | |
1446 | }; | |
1447 | kernel_ipsec_update_sa_t sa = { | |
1448 | .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, | |
1449 | .new_src = other, | |
1450 | .new_dst = me, | |
1451 | .encap = this->encap, | |
1452 | .new_encap = encap, | |
1453 | }; | |
1454 | if (charon->kernel->update_sa(charon->kernel, &id, | |
1455 | &sa) == NOT_SUPPORTED) | |
1456 | { | |
1457 | return NOT_SUPPORTED; | |
1458 | } | |
1459 | } | |
1460 | ||
1461 | /* update his (responder) SA */ | |
ca79bd54 | 1462 | if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) |
35ef1b03 TB |
1463 | { |
1464 | kernel_ipsec_sa_id_t id = { | |
1465 | .src = this->my_addr, | |
1466 | .dst = this->other_addr, | |
1467 | .spi = this->other_spi, | |
1468 | .proto = proto_ike2ip(this->protocol), | |
1469 | .mark = this->mark_out, | |
1470 | }; | |
1471 | kernel_ipsec_update_sa_t sa = { | |
1472 | .cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, | |
1473 | .new_src = me, | |
1474 | .new_dst = other, | |
1475 | .encap = this->encap, | |
1476 | .new_encap = encap, | |
1477 | }; | |
1478 | if (charon->kernel->update_sa(charon->kernel, &id, | |
1479 | &sa) == NOT_SUPPORTED) | |
1480 | { | |
1481 | return NOT_SUPPORTED; | |
1482 | } | |
1483 | } | |
1484 | /* we currently ignore the actual return values above */ | |
1485 | return SUCCESS; | |
1486 | } | |
1487 | ||
9d941742 | 1488 | METHOD(child_sa_t, update, status_t, |
35ef1b03 | 1489 | private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, |
101d26ba | 1490 | bool encap) |
1396815a | 1491 | { |
ad3af574 | 1492 | child_sa_state_t old; |
08c6ed9f | 1493 | bool transport_proxy_mode; |
7daf5226 | 1494 | |
2b3100b5 | 1495 | /* anything changed at all? */ |
dd83c6d4 | 1496 | if (me->equals(me, this->my_addr) && |
80853d84 | 1497 | other->equals(other, this->other_addr) && this->encap == encap) |
1396815a MW |
1498 | { |
1499 | return SUCCESS; | |
1500 | } | |
7daf5226 | 1501 | |
ad3af574 MW |
1502 | old = this->state; |
1503 | set_state(this, CHILD_UPDATING); | |
749ac175 TB |
1504 | transport_proxy_mode = this->mode == MODE_TRANSPORT && |
1505 | this->config->has_option(this->config, | |
1506 | OPT_PROXY_MODE); | |
7daf5226 | 1507 | |
35ef1b03 TB |
1508 | if (!this->config->has_option(this->config, OPT_NO_POLICIES) && |
1509 | require_policy_update()) | |
ea625fab | 1510 | { |
35ef1b03 TB |
1511 | ipsec_sa_cfg_t my_sa, other_sa; |
1512 | enumerator_t *enumerator; | |
1513 | traffic_selector_t *my_ts, *other_ts; | |
1514 | uint32_t manual_prio; | |
1515 | status_t state; | |
529ffc2f | 1516 | bool outbound; |
35ef1b03 TB |
1517 | |
1518 | prepare_sa_cfg(this, &my_sa, &other_sa); | |
1519 | manual_prio = this->config->get_manual_prio(this->config); | |
529ffc2f | 1520 | outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES); |
7daf5226 | 1521 | |
35ef1b03 TB |
1522 | enumerator = create_policy_enumerator(this); |
1523 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
7a915d62 | 1524 | { |
35ef1b03 | 1525 | /* install drop policy to avoid traffic leaks, acquires etc. */ |
529ffc2f TB |
1526 | if (outbound) |
1527 | { | |
1528 | install_policies_outbound(this, this->my_addr, this->other_addr, | |
1529 | my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, | |
1530 | POLICY_PRIORITY_DEFAULT, manual_prio); | |
1531 | } | |
35ef1b03 TB |
1532 | /* remove old policies */ |
1533 | del_policies_internal(this, this->my_addr, this->other_addr, | |
1534 | my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, | |
529ffc2f | 1535 | POLICY_PRIORITY_DEFAULT, manual_prio, outbound); |
7a915d62 | 1536 | } |
35ef1b03 | 1537 | enumerator->destroy(enumerator); |
7daf5226 | 1538 | |
35ef1b03 TB |
1539 | /* update the IPsec SAs */ |
1540 | state = update_sas(this, me, other, encap); | |
7daf5226 | 1541 | |
35ef1b03 TB |
1542 | enumerator = create_policy_enumerator(this); |
1543 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
1544 | { | |
1545 | traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL; | |
a6e0f14f | 1546 | |
35ef1b03 TB |
1547 | /* reinstall the previous policies if we can't update the SAs */ |
1548 | if (state == NOT_SUPPORTED) | |
1549 | { | |
1550 | install_policies_internal(this, this->my_addr, this->other_addr, | |
529ffc2f TB |
1551 | my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, |
1552 | POLICY_PRIORITY_DEFAULT, manual_prio, outbound); | |
35ef1b03 TB |
1553 | } |
1554 | else | |
d487b4b7 | 1555 | { |
f1c1965d | 1556 | /* check if we have to update a "dynamic" traffic selector */ |
d487b4b7 AS |
1557 | if (!me->ip_equals(me, this->my_addr) && |
1558 | my_ts->is_host(my_ts, this->my_addr)) | |
1559 | { | |
241cf8e7 | 1560 | old_my_ts = my_ts->clone(my_ts); |
d487b4b7 AS |
1561 | my_ts->set_address(my_ts, me); |
1562 | } | |
1563 | if (!other->ip_equals(other, this->other_addr) && | |
1564 | other_ts->is_host(other_ts, this->other_addr)) | |
1565 | { | |
241cf8e7 | 1566 | old_other_ts = other_ts->clone(other_ts); |
d487b4b7 AS |
1567 | other_ts->set_address(other_ts, other); |
1568 | } | |
7daf5226 | 1569 | |
d487b4b7 AS |
1570 | /* we reinstall the virtual IP to handle interface roaming |
1571 | * correctly */ | |
8a2e4d4a | 1572 | vips->invoke_function(vips, reinstall_vip, me); |
7daf5226 | 1573 | |
d487b4b7 | 1574 | /* reinstall updated policies */ |
f1c1965d | 1575 | install_policies_internal(this, me, other, my_ts, other_ts, |
529ffc2f TB |
1576 | &my_sa, &other_sa, POLICY_IPSEC, |
1577 | POLICY_PRIORITY_DEFAULT, manual_prio, outbound); | |
d487b4b7 | 1578 | } |
35ef1b03 | 1579 | /* remove the drop policy */ |
529ffc2f TB |
1580 | if (outbound) |
1581 | { | |
1582 | del_policies_outbound(this, this->my_addr, this->other_addr, | |
1583 | old_my_ts ?: my_ts, old_other_ts ?: other_ts, | |
1584 | &my_sa, &other_sa, POLICY_DROP, | |
1585 | POLICY_PRIORITY_DEFAULT, 0); | |
1586 | } | |
35ef1b03 TB |
1587 | |
1588 | DESTROY_IF(old_my_ts); | |
1589 | DESTROY_IF(old_other_ts); | |
1590 | } | |
1591 | enumerator->destroy(enumerator); | |
1592 | ||
1593 | if (state == NOT_SUPPORTED) | |
1594 | { | |
1595 | set_state(this, old); | |
1596 | return NOT_SUPPORTED; | |
1597 | } | |
1598 | ||
1599 | } | |
1600 | else if (!transport_proxy_mode) | |
1601 | { | |
1602 | if (update_sas(this, me, other, encap) == NOT_SUPPORTED) | |
1603 | { | |
1604 | set_state(this, old); | |
1605 | return NOT_SUPPORTED; | |
1396815a MW |
1606 | } |
1607 | } | |
7a915d62 | 1608 | |
08c6ed9f AS |
1609 | if (!transport_proxy_mode) |
1610 | { | |
1611 | /* apply hosts */ | |
1612 | if (!me->equals(me, this->my_addr)) | |
1613 | { | |
1614 | this->my_addr->destroy(this->my_addr); | |
1615 | this->my_addr = me->clone(me); | |
1616 | } | |
1617 | if (!other->equals(other, this->other_addr)) | |
1618 | { | |
1619 | this->other_addr->destroy(this->other_addr); | |
1620 | this->other_addr = other->clone(other); | |
1621 | } | |
1622 | } | |
1623 | ||
ea625fab | 1624 | this->encap = encap; |
ad3af574 | 1625 | set_state(this, old); |
7a915d62 | 1626 | |
1396815a MW |
1627 | return SUCCESS; |
1628 | } | |
1629 | ||
9d941742 TB |
1630 | METHOD(child_sa_t, destroy, void, |
1631 | private_child_sa_t *this) | |
30b5b412 | 1632 | { |
1df106bf MW |
1633 | enumerator_t *enumerator; |
1634 | traffic_selector_t *my_ts, *other_ts; | |
fbedc6a4 TB |
1635 | policy_priority_t priority; |
1636 | ||
c8f7a114 | 1637 | priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT; |
7daf5226 | 1638 | |
a985db3f | 1639 | set_state(this, CHILD_DESTROYING); |
7daf5226 | 1640 | |
749ac175 | 1641 | if (!this->config->has_option(this->config, OPT_NO_POLICIES)) |
695723d4 | 1642 | { |
a6e0f14f | 1643 | ipsec_sa_cfg_t my_sa, other_sa; |
7f57c4f9 | 1644 | uint32_t manual_prio; |
c5fed4cd | 1645 | bool del_outbound; |
a6e0f14f TB |
1646 | |
1647 | prepare_sa_cfg(this, &my_sa, &other_sa); | |
7f57c4f9 | 1648 | manual_prio = this->config->get_manual_prio(this->config); |
2c116ef5 TB |
1649 | del_outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) || |
1650 | this->trap; | |
a6e0f14f | 1651 | |
d487b4b7 AS |
1652 | /* delete all policies in the kernel */ |
1653 | enumerator = create_policy_enumerator(this); | |
1654 | while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) | |
1655 | { | |
529ffc2f TB |
1656 | del_policies_internal(this, this->my_addr, |
1657 | this->other_addr, my_ts, other_ts, &my_sa, &other_sa, | |
1658 | POLICY_IPSEC, priority, manual_prio, del_outbound); | |
d487b4b7 AS |
1659 | } |
1660 | enumerator->destroy(enumerator); | |
5d187bd2 | 1661 | } |
7daf5226 | 1662 | |
46188b0e TB |
1663 | /* delete SAs in the kernel, if they are set up */ |
1664 | if (this->my_spi) | |
1665 | { | |
89da06ac TB |
1666 | kernel_ipsec_sa_id_t id = { |
1667 | .src = this->other_addr, | |
1668 | .dst = this->my_addr, | |
1669 | .spi = this->my_spi, | |
1670 | .proto = proto_ike2ip(this->protocol), | |
ea43f8ff | 1671 | .mark = mark_in_sa(this), |
89da06ac TB |
1672 | }; |
1673 | kernel_ipsec_del_sa_t sa = { | |
1674 | .cpi = this->my_cpi, | |
1675 | }; | |
1676 | charon->kernel->del_sa(charon->kernel, &id, &sa); | |
46188b0e | 1677 | } |
2c116ef5 | 1678 | if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) |
46188b0e | 1679 | { |
89da06ac TB |
1680 | kernel_ipsec_sa_id_t id = { |
1681 | .src = this->my_addr, | |
1682 | .dst = this->other_addr, | |
1683 | .spi = this->other_spi, | |
1684 | .proto = proto_ike2ip(this->protocol), | |
1685 | .mark = this->mark_out, | |
1686 | }; | |
1687 | kernel_ipsec_del_sa_t sa = { | |
1688 | .cpi = this->other_cpi, | |
1689 | }; | |
1690 | charon->kernel->del_sa(charon->kernel, &id, &sa); | |
46188b0e TB |
1691 | } |
1692 | ||
0da97f50 MW |
1693 | if (this->reqid_allocated) |
1694 | { | |
8394ea2a | 1695 | if (charon->kernel->release_reqid(charon->kernel, |
0da97f50 MW |
1696 | this->reqid, this->mark_in, this->mark_out) != SUCCESS) |
1697 | { | |
1698 | DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); | |
1699 | } | |
1700 | } | |
1701 | ||
2745ae26 MW |
1702 | array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); |
1703 | array_destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy)); | |
80853d84 MW |
1704 | this->my_addr->destroy(this->my_addr); |
1705 | this->other_addr->destroy(this->other_addr); | |
1706 | DESTROY_IF(this->proposal); | |
e0fe7651 | 1707 | this->config->destroy(this->config); |
8a3a389e TB |
1708 | chunk_clear(&this->encr_r); |
1709 | chunk_clear(&this->integ_r); | |
5113680f | 1710 | free(this); |
30b5b412 MW |
1711 | } |
1712 | ||
c6f1d0de MW |
1713 | /** |
1714 | * Get proxy address for one side, if any | |
1715 | */ | |
1716 | static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) | |
1717 | { | |
1718 | host_t *host = NULL; | |
b12c53ce | 1719 | uint8_t mask; |
c6f1d0de MW |
1720 | enumerator_t *enumerator; |
1721 | linked_list_t *ts_list, *list; | |
1722 | traffic_selector_t *ts; | |
1723 | ||
1724 | list = linked_list_create_with_items(ike, NULL); | |
1725 | ts_list = config->get_traffic_selectors(config, local, NULL, list); | |
1726 | list->destroy(list); | |
1727 | ||
1728 | enumerator = ts_list->create_enumerator(ts_list); | |
1729 | while (enumerator->enumerate(enumerator, &ts)) | |
1730 | { | |
1731 | if (ts->is_host(ts, NULL) && ts->to_subnet(ts, &host, &mask)) | |
1732 | { | |
1733 | DBG1(DBG_CHD, "%s address: %H is a transport mode proxy for %H", | |
1734 | local ? "my" : "other", ike, host); | |
1735 | break; | |
1736 | } | |
1737 | } | |
1738 | enumerator->destroy(enumerator); | |
1739 | ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); | |
1740 | ||
1741 | if (!host) | |
1742 | { | |
1743 | host = ike->clone(ike); | |
1744 | } | |
1745 | return host; | |
1746 | } | |
1747 | ||
9d941742 | 1748 | /** |
30b5b412 MW |
1749 | * Described in header. |
1750 | */ | |
c60c7694 | 1751 | child_sa_t * child_sa_create(host_t *me, host_t* other, |
c9998e5f | 1752 | child_cfg_t *config, uint32_t reqid, bool encap, |
85b23888 | 1753 | u_int mark_in, u_int mark_out) |
30b5b412 | 1754 | { |
9d941742 | 1755 | private_child_sa_t *this; |
b2abd010 | 1756 | static refcount_t unique_id = 0, unique_mark = 0; |
32e5c492 | 1757 | refcount_t mark = 0; |
9d941742 TB |
1758 | |
1759 | INIT(this, | |
1760 | .public = { | |
1761 | .get_name = _get_name, | |
1762 | .get_reqid = _get_reqid, | |
4ec397b8 | 1763 | .get_unique_id = _get_unique_id, |
9d941742 TB |
1764 | .get_config = _get_config, |
1765 | .get_state = _get_state, | |
1766 | .set_state = _set_state, | |
afbea8ce | 1767 | .get_outbound_state = _get_outbound_state, |
9d941742 TB |
1768 | .get_spi = _get_spi, |
1769 | .get_cpi = _get_cpi, | |
1770 | .get_protocol = _get_protocol, | |
1771 | .set_protocol = _set_protocol, | |
1772 | .get_mode = _get_mode, | |
1773 | .set_mode = _set_mode, | |
1774 | .get_proposal = _get_proposal, | |
1775 | .set_proposal = _set_proposal, | |
1776 | .get_lifetime = _get_lifetime, | |
763e0353 | 1777 | .get_installtime = _get_installtime, |
9d941742 | 1778 | .get_usestats = _get_usestats, |
79f39ecf | 1779 | .get_mark = _get_mark, |
9d941742 TB |
1780 | .has_encap = _has_encap, |
1781 | .get_ipcomp = _get_ipcomp, | |
1782 | .set_ipcomp = _set_ipcomp, | |
1783 | .get_close_action = _get_close_action, | |
1784 | .set_close_action = _set_close_action, | |
1785 | .get_dpd_action = _get_dpd_action, | |
1786 | .set_dpd_action = _set_dpd_action, | |
1787 | .alloc_spi = _alloc_spi, | |
1788 | .alloc_cpi = _alloc_cpi, | |
1789 | .install = _install, | |
8a3a389e TB |
1790 | .register_outbound = _register_outbound, |
1791 | .install_outbound = _install_outbound, | |
c5fed4cd | 1792 | .remove_outbound = _remove_outbound, |
cad13450 TB |
1793 | .set_rekey_spi = _set_rekey_spi, |
1794 | .get_rekey_spi = _get_rekey_spi, | |
9d941742 | 1795 | .update = _update, |
4989aba8 TB |
1796 | .set_policies = _set_policies, |
1797 | .install_policies = _install_policies, | |
553bb787 | 1798 | .create_ts_enumerator = _create_ts_enumerator, |
9d941742 TB |
1799 | .create_policy_enumerator = _create_policy_enumerator, |
1800 | .destroy = _destroy, | |
1801 | }, | |
9d941742 TB |
1802 | .encap = encap, |
1803 | .ipcomp = IPCOMP_NONE, | |
1804 | .state = CHILD_CREATED, | |
2745ae26 MW |
1805 | .my_ts = array_create(0, 0), |
1806 | .other_ts = array_create(0, 0), | |
9d941742 TB |
1807 | .protocol = PROTO_NONE, |
1808 | .mode = MODE_TUNNEL, | |
1809 | .close_action = config->get_close_action(config), | |
1810 | .dpd_action = config->get_dpd_action(config), | |
1811 | .reqid = config->get_reqid(config), | |
4ec397b8 | 1812 | .unique_id = ref_get(&unique_id), |
9d941742 TB |
1813 | .mark_in = config->get_mark(config, TRUE), |
1814 | .mark_out = config->get_mark(config, FALSE), | |
763e0353 | 1815 | .install_time = time_monotonic(NULL), |
749ac175 | 1816 | .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES), |
9d941742 TB |
1817 | ); |
1818 | ||
e0fe7651 MW |
1819 | this->config = config; |
1820 | config->get_ref(config); | |
ee26c537 | 1821 | |
85b23888 MW |
1822 | if (mark_in) |
1823 | { | |
1824 | this->mark_in.value = mark_in; | |
1825 | } | |
1826 | if (mark_out) | |
1827 | { | |
1828 | this->mark_out.value = mark_out; | |
1829 | } | |
32e5c492 EB |
1830 | |
1831 | if (MARK_IS_UNIQUE(this->mark_in.value) || | |
1832 | MARK_IS_UNIQUE(this->mark_out.value)) | |
85b23888 | 1833 | { |
32e5c492 EB |
1834 | bool unique_dir; |
1835 | ||
1836 | unique_dir = this->mark_in.value == MARK_UNIQUE_DIR || | |
1837 | this->mark_out.value == MARK_UNIQUE_DIR; | |
1838 | ||
1839 | if (!unique_dir) | |
1840 | { | |
1841 | mark = ref_get(&unique_mark); | |
1842 | } | |
1843 | if (MARK_IS_UNIQUE(this->mark_in.value)) | |
85b23888 | 1844 | { |
32e5c492 EB |
1845 | if (unique_dir) |
1846 | { | |
1847 | mark = ref_get(&unique_mark); | |
1848 | } | |
85b23888 MW |
1849 | this->mark_in.value = mark; |
1850 | } | |
32e5c492 | 1851 | if (MARK_IS_UNIQUE(this->mark_out.value)) |
85b23888 | 1852 | { |
32e5c492 EB |
1853 | if (unique_dir) |
1854 | { | |
1855 | mark = ref_get(&unique_mark); | |
1856 | } | |
85b23888 MW |
1857 | this->mark_out.value = mark; |
1858 | } | |
1859 | } | |
1860 | ||
71a66a62 RB |
1861 | if (!this->reqid) |
1862 | { | |
c9998e5f TB |
1863 | /* reuse old reqid if we are rekeying an existing CHILD_SA and when |
1864 | * initiating a trap policy. While the reqid cache would find the same | |
1865 | * reqid for our selectors, this does not work in a special case: If an | |
1866 | * SA is triggered by a trap policy, but the negotiated TS get | |
1867 | * narrowed, we still must reuse the same reqid to successfully | |
1868 | * replace the temporary SA on the kernel level. Rekeying such an SA | |
1869 | * requires an explicit reqid, as the cache currently knows the original | |
1870 | * selectors only for that reqid. */ | |
1871 | this->reqid = reqid; | |
71a66a62 | 1872 | } |
3665adef TB |
1873 | else |
1874 | { | |
1875 | this->static_reqid = TRUE; | |
1876 | } | |
7daf5226 | 1877 | |
dd83c6d4 | 1878 | /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ |
d487b4b7 | 1879 | if (config->get_mode(config) == MODE_TRANSPORT && |
749ac175 | 1880 | config->has_option(config, OPT_PROXY_MODE)) |
d487b4b7 | 1881 | { |
d487b4b7 | 1882 | this->mode = MODE_TRANSPORT; |
7daf5226 | 1883 | |
c6f1d0de MW |
1884 | this->my_addr = get_proxy_addr(config, me, TRUE); |
1885 | this->other_addr = get_proxy_addr(config, other, FALSE); | |
1886 | } | |
1887 | else | |
1888 | { | |
1889 | this->my_addr = me->clone(me); | |
1890 | this->other_addr = other->clone(other); | |
d487b4b7 | 1891 | } |
1396815a | 1892 | return &this->public; |
3ebebc5e | 1893 | } |