]>
Commit | Line | Data |
---|---|---|
3ebebc5e MW |
1 | /** |
2 | * @file child_sa.c | |
3 | * | |
4 | * @brief Implementation of child_sa_t. | |
5 | * | |
6 | */ | |
7 | ||
8 | /* | |
1396815a | 9 | * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger |
c71d53ba MW |
10 | * Copyright (C) 2005-2006 Martin Willi |
11 | * Copyright (C) 2005 Jan Hutter | |
3ebebc5e MW |
12 | * Hochschule fuer Technik Rapperswil |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify it | |
15 | * under the terms of the GNU General Public License as published by the | |
16 | * Free Software Foundation; either version 2 of the License, or (at your | |
17 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, but | |
20 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
21 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
22 | * for more details. | |
23 | */ | |
24 | ||
b92eef28 | 25 | #include "child_sa.h" |
3ebebc5e | 26 | |
aeda79ff MW |
27 | #include <daemon.h> |
28 | ||
3ebebc5e | 29 | |
bcb95ced MW |
30 | /** |
31 | * String mappings for child_sa_state_t. | |
32 | */ | |
33 | mapping_t child_sa_state_m[] = { | |
34 | {CHILD_CREATED, "CREATED"}, | |
35 | {CHILD_INSTALLED, "INSTALLED"}, | |
45f76a7d | 36 | {CHILD_ROUTED, "ROUTED"}, |
bcb95ced MW |
37 | {CHILD_REKEYING, "REKEYING"}, |
38 | {CHILD_DELETING, "DELETING"}, | |
39 | {MAPPING_END, NULL} | |
40 | }; | |
41 | ||
16b9a73c | 42 | typedef struct sa_policy_t sa_policy_t; |
5d187bd2 MW |
43 | |
44 | /** | |
45 | * Struct used to store information for a policy. This | |
46 | * is needed since we must provide all this information | |
47 | * for deleting a policy... | |
48 | */ | |
16b9a73c | 49 | struct sa_policy_t { |
92ee45a0 MW |
50 | /** |
51 | * Traffic selector for us | |
52 | */ | |
53 | traffic_selector_t *my_ts; | |
5d187bd2 MW |
54 | |
55 | /** | |
92ee45a0 | 56 | * Traffic selector for other |
5d187bd2 | 57 | */ |
92ee45a0 | 58 | traffic_selector_t *other_ts; |
5d187bd2 MW |
59 | }; |
60 | ||
3ebebc5e MW |
61 | typedef struct private_child_sa_t private_child_sa_t; |
62 | ||
63 | /** | |
144f676c | 64 | * Private data of a child_sa_t object. |
3ebebc5e MW |
65 | */ |
66 | struct private_child_sa_t { | |
67 | /** | |
144f676c | 68 | * Public interface of child_sa_t. |
3ebebc5e MW |
69 | */ |
70 | child_sa_t public; | |
71 | ||
8d77edde MW |
72 | struct { |
73 | /** address of peer */ | |
74 | host_t *addr; | |
75 | /** actual used SPI, 0 if unused */ | |
76 | u_int32_t spi; | |
77 | } me, other; | |
aeda79ff | 78 | |
3efbf983 MW |
79 | /** |
80 | * Allocated SPI for a ESP proposal candidates | |
81 | */ | |
82 | u_int32_t alloc_esp_spi; | |
83 | ||
84 | /** | |
85 | * Allocated SPI for a AH proposal candidates | |
86 | */ | |
87 | u_int32_t alloc_ah_spi; | |
88 | ||
aeda79ff | 89 | /** |
8d77edde | 90 | * Protocol used to protect this SA, ESP|AH |
aeda79ff | 91 | */ |
8d77edde | 92 | protocol_id_t protocol; |
30b5b412 MW |
93 | |
94 | /** | |
8d77edde | 95 | * List containing sa_policy_t objects |
5d187bd2 | 96 | */ |
8d77edde | 97 | linked_list_t *policies; |
5d187bd2 | 98 | |
8dfbe71b MW |
99 | /** |
100 | * Seperate list for local traffic selectors | |
101 | */ | |
102 | linked_list_t *my_ts; | |
103 | ||
104 | /** | |
105 | * Seperate list for remote traffic selectors | |
106 | */ | |
107 | linked_list_t *other_ts; | |
108 | ||
5d187bd2 | 109 | /** |
8d77edde | 110 | * reqid used for this child_sa |
5d187bd2 | 111 | */ |
8d77edde | 112 | u_int32_t reqid; |
5d187bd2 | 113 | |
c0593835 MW |
114 | /** |
115 | * encryption algorithm used for this SA | |
116 | */ | |
117 | algorithm_t encryption; | |
118 | ||
119 | /** | |
120 | * integrity protection algorithm used for this SA | |
121 | */ | |
122 | algorithm_t integrity; | |
123 | ||
a2a3fb3e MW |
124 | /** |
125 | * time, on which SA was installed | |
126 | */ | |
127 | time_t install_time; | |
128 | ||
5d187bd2 | 129 | /** |
8d77edde | 130 | * Lifetime before rekeying |
5d187bd2 | 131 | */ |
8d77edde | 132 | u_int32_t soft_lifetime; |
30b5b412 | 133 | |
a527a426 | 134 | /** |
8d77edde | 135 | * Lifetime before delete |
a527a426 | 136 | */ |
8d77edde | 137 | u_int32_t hard_lifetime; |
a527a426 | 138 | |
bcb95ced MW |
139 | /** |
140 | * state of the CHILD_SA | |
141 | */ | |
142 | child_sa_state_t state; | |
143 | ||
695723d4 | 144 | /** |
abba7ecb | 145 | * transaction which is rekeying this CHILD_SA |
695723d4 | 146 | */ |
abba7ecb | 147 | void *rekeying_transaction; |
1396815a MW |
148 | |
149 | /** | |
150 | * Specifies if NAT traversal is used | |
151 | */ | |
152 | bool use_natt; | |
695723d4 | 153 | |
30b5b412 MW |
154 | /** |
155 | * CHILD_SAs own logger | |
156 | */ | |
157 | logger_t *logger; | |
3ebebc5e MW |
158 | }; |
159 | ||
32b6500f MW |
160 | /** |
161 | * Implements child_sa_t.get_reqid | |
162 | */ | |
163 | static u_int32_t get_reqid(private_child_sa_t *this) | |
164 | { | |
165 | return this->reqid; | |
166 | } | |
8d77edde MW |
167 | |
168 | /** | |
169 | * Implements child_sa_t.get_spi | |
170 | */ | |
171 | u_int32_t get_spi(private_child_sa_t *this, bool inbound) | |
172 | { | |
173 | if (inbound) | |
174 | { | |
175 | return this->me.spi; | |
176 | } | |
177 | return this->other.spi; | |
178 | } | |
179 | ||
180 | /** | |
181 | * Implements child_sa_t.get_protocol | |
182 | */ | |
183 | protocol_id_t get_protocol(private_child_sa_t *this) | |
184 | { | |
185 | return this->protocol; | |
186 | } | |
32b6500f | 187 | |
bcb95ced MW |
188 | /** |
189 | * Implements child_sa_t.get_state | |
190 | */ | |
191 | static child_sa_state_t get_state(private_child_sa_t *this) | |
192 | { | |
193 | return this->state; | |
194 | } | |
195 | ||
196 | /** | |
197 | * Implements child_sa_t.set_state | |
198 | */ | |
199 | static void set_state(private_child_sa_t *this, child_sa_state_t state) | |
200 | { | |
201 | this->state = state; | |
202 | } | |
203 | ||
1396815a MW |
204 | /** |
205 | * Allocate SPI for a single proposal | |
206 | */ | |
207 | static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal) | |
208 | { | |
209 | protocol_id_t protocol = proposal->get_protocol(proposal); | |
210 | ||
211 | if (protocol == PROTO_AH) | |
212 | { | |
213 | /* get a new spi for AH, if not already done */ | |
214 | if (this->alloc_ah_spi == 0) | |
215 | { | |
216 | if (charon->kernel_interface->get_spi( | |
217 | charon->kernel_interface, | |
218 | this->other.addr, this->me.addr, | |
219 | PROTO_AH, this->reqid, | |
220 | &this->alloc_ah_spi) != SUCCESS) | |
221 | { | |
222 | return FAILED; | |
223 | } | |
224 | } | |
225 | proposal->set_spi(proposal, this->alloc_ah_spi); | |
226 | } | |
227 | if (protocol == PROTO_ESP) | |
228 | { | |
229 | /* get a new spi for ESP, if not already done */ | |
230 | if (this->alloc_esp_spi == 0) | |
231 | { | |
232 | if (charon->kernel_interface->get_spi( | |
233 | charon->kernel_interface, | |
234 | this->other.addr, this->me.addr, | |
235 | PROTO_ESP, this->reqid, | |
236 | &this->alloc_esp_spi) != SUCCESS) | |
237 | { | |
238 | return FAILED; | |
239 | } | |
240 | } | |
241 | proposal->set_spi(proposal, this->alloc_esp_spi); | |
242 | } | |
243 | return SUCCESS; | |
244 | } | |
245 | ||
246 | ||
3ebebc5e | 247 | /** |
30b5b412 | 248 | * Implements child_sa_t.alloc |
3ebebc5e | 249 | */ |
30b5b412 | 250 | static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) |
3ebebc5e | 251 | { |
30b5b412 MW |
252 | iterator_t *iterator; |
253 | proposal_t *proposal; | |
30b5b412 | 254 | |
3efbf983 | 255 | /* iterator through proposals to update spis */ |
30b5b412 MW |
256 | iterator = proposals->create_iterator(proposals, TRUE); |
257 | while(iterator->has_next(iterator)) | |
258 | { | |
259 | iterator->current(iterator, (void**)&proposal); | |
1396815a | 260 | if (alloc_proposal(this, proposal) != SUCCESS) |
3efbf983 | 261 | { |
1396815a MW |
262 | iterator->destroy(iterator); |
263 | return FAILED; | |
3efbf983 | 264 | } |
30b5b412 MW |
265 | } |
266 | iterator->destroy(iterator); | |
267 | return SUCCESS; | |
3ebebc5e MW |
268 | } |
269 | ||
30b5b412 | 270 | static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus, bool mine) |
3ebebc5e | 271 | { |
30b5b412 | 272 | u_int32_t spi; |
5c131a01 MW |
273 | algorithm_t *enc_algo, *int_algo; |
274 | algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0}; | |
275 | algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; | |
30b5b412 MW |
276 | host_t *src; |
277 | host_t *dst; | |
1396815a | 278 | natt_conf_t *natt; |
30b5b412 | 279 | status_t status; |
aeda79ff | 280 | |
8d77edde MW |
281 | this->protocol = proposal->get_protocol(proposal); |
282 | ||
283 | /* now we have to decide which spi to use. Use self allocated, if "mine", | |
3efbf983 MW |
284 | * or the one in the proposal, if not "mine" (others). Additionally, |
285 | * source and dest host switch depending on the role */ | |
8d77edde | 286 | if (mine) |
aeda79ff | 287 | { |
3efbf983 MW |
288 | /* if we have allocated SPIs for AH and ESP, we must delete the unused |
289 | * one. */ | |
290 | if (this->protocol == PROTO_ESP) | |
291 | { | |
292 | this->me.spi = this->alloc_esp_spi; | |
293 | if (this->alloc_ah_spi) | |
294 | { | |
295 | charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, | |
296 | this->alloc_ah_spi, PROTO_AH); | |
297 | } | |
298 | } | |
299 | else | |
300 | { | |
301 | this->me.spi = this->alloc_ah_spi; | |
302 | if (this->alloc_esp_spi) | |
303 | { | |
304 | charon->kernel_interface->del_sa(charon->kernel_interface, this->me.addr, | |
305 | this->alloc_esp_spi, PROTO_ESP); | |
306 | } | |
307 | } | |
8d77edde | 308 | spi = this->me.spi; |
3efbf983 MW |
309 | dst = this->me.addr; |
310 | src = this->other.addr; | |
aeda79ff | 311 | } |
8d77edde MW |
312 | else |
313 | { | |
3efbf983 MW |
314 | this->other.spi = proposal->get_spi(proposal); |
315 | spi = this->other.spi; | |
316 | src = this->me.addr; | |
317 | dst = this->other.addr; | |
8d77edde MW |
318 | } |
319 | ||
1d390631 | 320 | this->logger->log(this->logger, CONTROL|LEVEL1, "adding %s %s SA", |
5c131a01 MW |
321 | mine ? "inbound" : "outbound", |
322 | mapping_find(protocol_id_m, this->protocol)); | |
323 | ||
324 | /* select encryption algo */ | |
325 | if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo)) | |
8d77edde | 326 | { |
5c131a01 MW |
327 | this->logger->log(this->logger, CONTROL|LEVEL2, " using %s for encryption", |
328 | mapping_find(encryption_algorithm_m, enc_algo->algorithm)); | |
8d77edde MW |
329 | } |
330 | else | |
331 | { | |
5c131a01 | 332 | enc_algo = &enc_algo_none; |
8d77edde MW |
333 | } |
334 | ||
5c131a01 MW |
335 | /* select integrity algo */ |
336 | if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo)) | |
8d77edde | 337 | { |
5c131a01 MW |
338 | this->logger->log(this->logger, CONTROL|LEVEL2, " using %s for integrity", |
339 | mapping_find(integrity_algorithm_m, int_algo->algorithm)); | |
8d77edde MW |
340 | } |
341 | else | |
342 | { | |
5c131a01 | 343 | int_algo = &int_algo_none; |
8d77edde MW |
344 | } |
345 | ||
1396815a MW |
346 | /* setup nat-t */ |
347 | if (this->use_natt) | |
348 | { | |
349 | natt = alloca(sizeof(natt_conf_t)); | |
350 | natt->sport = src->get_port(src); | |
351 | natt->dport = dst->get_port(dst); | |
352 | } | |
353 | else | |
354 | { | |
355 | natt = NULL; | |
356 | } | |
357 | ||
358 | ||
5c131a01 MW |
359 | /* send SA down to the kernel */ |
360 | this->logger->log(this->logger, CONTROL|LEVEL2, | |
361 | " SPI 0x%.8x, src %s dst %s", | |
92ee45a0 | 362 | ntohl(spi), src->get_string(src), dst->get_string(dst)); |
8d77edde | 363 | status = charon->kernel_interface->add_sa(charon->kernel_interface, |
1396815a MW |
364 | src, dst, |
365 | spi, this->protocol, | |
366 | this->reqid, | |
698d7749 | 367 | mine ? this->soft_lifetime : 0, |
1396815a MW |
368 | this->hard_lifetime, |
369 | enc_algo, int_algo, | |
370 | prf_plus, natt, mine); | |
a2a3fb3e | 371 | |
c0593835 MW |
372 | this->encryption = *enc_algo; |
373 | this->integrity = *int_algo; | |
a2a3fb3e | 374 | this->install_time = time(NULL); |
abba7ecb | 375 | |
8d77edde | 376 | return status; |
30b5b412 MW |
377 | } |
378 | ||
379 | static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus) | |
380 | { | |
891dfaf9 | 381 | u_int32_t outbound_spi, inbound_spi; |
30b5b412 | 382 | |
891dfaf9 MW |
383 | /* backup outbound spi, as alloc overwrites it */ |
384 | outbound_spi = proposal->get_spi(proposal); | |
30b5b412 | 385 | |
891dfaf9 | 386 | /* get SPIs inbound SAs */ |
1396815a | 387 | if (alloc_proposal(this, proposal) != SUCCESS) |
30b5b412 | 388 | { |
30b5b412 MW |
389 | return FAILED; |
390 | } | |
891dfaf9 | 391 | inbound_spi = proposal->get_spi(proposal); |
30b5b412 | 392 | |
891dfaf9 | 393 | /* install inbound SAs */ |
30b5b412 MW |
394 | if (install(this, proposal, prf_plus, TRUE) != SUCCESS) |
395 | { | |
396 | return FAILED; | |
397 | } | |
a527a426 | 398 | |
891dfaf9 MW |
399 | /* install outbound SAs, restore spi*/ |
400 | proposal->set_spi(proposal, outbound_spi); | |
401 | if (install(this, proposal, prf_plus, FALSE) != SUCCESS) | |
402 | { | |
403 | return FAILED; | |
404 | } | |
405 | proposal->set_spi(proposal, inbound_spi); | |
406 | ||
bcb95ced MW |
407 | this->state = CHILD_INSTALLED; |
408 | ||
30b5b412 MW |
409 | return SUCCESS; |
410 | } | |
411 | ||
412 | static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus) | |
413 | { | |
891dfaf9 MW |
414 | u_int32_t inbound_spi; |
415 | ||
416 | /* backup received spi, as install() overwrites it */ | |
417 | inbound_spi = proposal->get_spi(proposal); | |
418 | ||
419 | /* install outbound SAs */ | |
420 | if (install(this, proposal, prf_plus, FALSE) != SUCCESS) | |
30b5b412 MW |
421 | { |
422 | return FAILED; | |
423 | } | |
891dfaf9 MW |
424 | |
425 | /* restore spi */ | |
426 | proposal->set_spi(proposal, inbound_spi); | |
427 | /* install inbound SAs */ | |
428 | if (install(this, proposal, prf_plus, TRUE) != SUCCESS) | |
30b5b412 MW |
429 | { |
430 | return FAILED; | |
431 | } | |
a527a426 | 432 | |
bcb95ced MW |
433 | this->state = CHILD_INSTALLED; |
434 | ||
a527a426 MW |
435 | return SUCCESS; |
436 | } | |
437 | ||
5d187bd2 | 438 | static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list) |
a527a426 | 439 | { |
5d187bd2 MW |
440 | iterator_t *my_iter, *other_iter; |
441 | traffic_selector_t *my_ts, *other_ts; | |
a527a426 | 442 | |
5d187bd2 MW |
443 | /* iterate over both lists */ |
444 | my_iter = my_ts_list->create_iterator(my_ts_list, TRUE); | |
445 | other_iter = other_ts_list->create_iterator(other_ts_list, TRUE); | |
446 | while (my_iter->has_next(my_iter)) | |
a527a426 | 447 | { |
5d187bd2 MW |
448 | my_iter->current(my_iter, (void**)&my_ts); |
449 | other_iter->reset(other_iter); | |
450 | while (other_iter->has_next(other_iter)) | |
451 | { | |
452 | /* set up policies for every entry in my_ts_list to every entry in other_ts_list */ | |
5d187bd2 | 453 | status_t status; |
92ee45a0 | 454 | sa_policy_t *policy; |
5d187bd2 MW |
455 | |
456 | other_iter->current(other_iter, (void**)&other_ts); | |
457 | ||
92ee45a0 MW |
458 | if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts)) |
459 | { | |
460 | this->logger->log(this->logger, CONTROL|LEVEL1, | |
461 | "CHILD_SA policy uses two different IP families, ignored"); | |
462 | continue; | |
463 | } | |
464 | ||
5f0eb96f MW |
465 | /* only set up policies if protocol matches, or if one is zero (any) */ |
466 | if (my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts) && | |
467 | my_ts->get_protocol(my_ts) && other_ts->get_protocol(other_ts)) | |
5d187bd2 | 468 | { |
92ee45a0 | 469 | this->logger->log(this->logger, CONTROL|LEVEL1, |
5f0eb96f | 470 | "CHILD_SA policy uses two different protocols, ignored"); |
5d187bd2 MW |
471 | continue; |
472 | } | |
5d187bd2 | 473 | |
5d187bd2 MW |
474 | /* install 3 policies: out, in and forward */ |
475 | status = charon->kernel_interface->add_policy(charon->kernel_interface, | |
92ee45a0 MW |
476 | this->me.addr, this->other.addr, my_ts, other_ts, |
477 | POLICY_OUT, this->protocol, this->reqid, FALSE); | |
478 | ||
5d187bd2 | 479 | status |= charon->kernel_interface->add_policy(charon->kernel_interface, |
92ee45a0 MW |
480 | this->other.addr, this->me.addr, other_ts, my_ts, |
481 | POLICY_IN, this->protocol, this->reqid, FALSE); | |
482 | ||
5d187bd2 | 483 | status |= charon->kernel_interface->add_policy(charon->kernel_interface, |
92ee45a0 MW |
484 | this->other.addr, this->me.addr, other_ts, my_ts, |
485 | POLICY_FWD, this->protocol, this->reqid, FALSE); | |
5d187bd2 MW |
486 | |
487 | if (status != SUCCESS) | |
488 | { | |
489 | my_iter->destroy(my_iter); | |
490 | other_iter->destroy(other_iter); | |
5d187bd2 MW |
491 | return status; |
492 | } | |
493 | ||
92ee45a0 MW |
494 | /* store policy to delete/update them later */ |
495 | policy = malloc_thing(sa_policy_t); | |
496 | policy->my_ts = my_ts->clone(my_ts); | |
497 | policy->other_ts = other_ts->clone(other_ts); | |
498 | this->policies->insert_last(this->policies, (void*)policy); | |
8dfbe71b MW |
499 | /* add to separate list to query them via get_*_traffic_selectors() */ |
500 | this->my_ts->insert_last(this->my_ts, (void*)policy->my_ts); | |
501 | this->other_ts->insert_last(this->other_ts, (void*)policy->other_ts); | |
5d187bd2 MW |
502 | } |
503 | } | |
5d187bd2 MW |
504 | my_iter->destroy(my_iter); |
505 | other_iter->destroy(other_iter); | |
45f76a7d MW |
506 | |
507 | /* switch to routed state if no SAD entry set up */ | |
508 | if (this->state == CHILD_CREATED) | |
509 | { | |
510 | this->state = CHILD_ROUTED; | |
511 | } | |
512 | ||
5d187bd2 | 513 | return SUCCESS; |
30b5b412 MW |
514 | } |
515 | ||
8dfbe71b MW |
516 | /** |
517 | * Implementation of child_sa_t.get_my_traffic_selectors. | |
518 | */ | |
519 | static linked_list_t *get_my_traffic_selectors(private_child_sa_t *this) | |
520 | { | |
521 | return this->my_ts; | |
522 | } | |
523 | ||
524 | /** | |
525 | * Implementation of child_sa_t.get_my_traffic_selectors. | |
526 | */ | |
527 | static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this) | |
528 | { | |
529 | return this->other_ts; | |
530 | } | |
531 | ||
695723d4 | 532 | /** |
abba7ecb MW |
533 | * Implementation of child_sa_t.set_rekeying_transaction. |
534 | */ | |
535 | static void set_rekeying_transaction(private_child_sa_t *this, void *transaction) | |
536 | { | |
537 | this->rekeying_transaction = transaction; | |
abba7ecb MW |
538 | } |
539 | ||
540 | /** | |
541 | * Implementation of child_sa_t.get_rekeying_transaction. | |
695723d4 | 542 | */ |
abba7ecb | 543 | static void* get_rekeying_transaction(private_child_sa_t *this) |
695723d4 | 544 | { |
abba7ecb MW |
545 | return this->rekeying_transaction; |
546 | } | |
547 | ||
2f89902d MW |
548 | /** |
549 | * Implementation of child_sa_t.get_use_time | |
550 | */ | |
551 | static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time) | |
552 | { | |
92ee45a0 MW |
553 | iterator_t *iterator; |
554 | sa_policy_t *policy; | |
fe04e93a | 555 | status_t status = FAILED; |
2f89902d MW |
556 | |
557 | *use_time = UNDEFINED_TIME; | |
92ee45a0 MW |
558 | |
559 | iterator = this->policies->create_iterator(this->policies, TRUE); | |
560 | while (iterator->iterate(iterator, (void**)&policy)) | |
aeeb4f4f | 561 | { |
92ee45a0 MW |
562 | if (inbound) |
563 | { | |
564 | time_t in = UNDEFINED_TIME, fwd = UNDEFINED_TIME; | |
565 | ||
566 | status = charon->kernel_interface->query_policy( | |
567 | charon->kernel_interface, | |
568 | policy->other_ts, policy->my_ts, | |
569 | POLICY_IN, (u_int32_t*)&in); | |
570 | status |= charon->kernel_interface->query_policy( | |
571 | charon->kernel_interface, | |
572 | policy->other_ts, policy->my_ts, | |
573 | POLICY_FWD, (u_int32_t*)&fwd); | |
574 | *use_time = max(in, fwd); | |
575 | } | |
576 | else | |
577 | { | |
578 | status = charon->kernel_interface->query_policy( | |
579 | charon->kernel_interface, | |
580 | policy->my_ts, policy->other_ts, | |
581 | POLICY_OUT, (u_int32_t*)use_time); | |
582 | } | |
aeeb4f4f | 583 | } |
92ee45a0 | 584 | iterator->destroy(iterator); |
aeeb4f4f | 585 | return status; |
2f89902d MW |
586 | } |
587 | ||
5f0eb96f MW |
588 | /** |
589 | * Implementation of child_sa_t.log_status. | |
590 | */ | |
591 | static void log_status(private_child_sa_t *this, logger_t *logger, char* name) | |
592 | { | |
593 | iterator_t *iterator; | |
594 | char use_in_str[12] = "unused"; | |
595 | char use_out_str[12] = "unused"; | |
596 | char rekey_str[12] = "disabled"; | |
c0593835 MW |
597 | char enc_str[32] = ""; |
598 | char int_str[32] = ""; | |
92ee45a0 MW |
599 | u_int32_t use_in, use_out, use_fwd, now, rekeying; |
600 | status_t status; | |
5f0eb96f MW |
601 | |
602 | if (logger == NULL) | |
603 | { | |
604 | logger = this->logger; | |
605 | } | |
92ee45a0 MW |
606 | now = (u_int32_t)time(NULL); |
607 | ||
45f76a7d | 608 | if (this->state == CHILD_INSTALLED) |
c0593835 | 609 | { |
45f76a7d MW |
610 | /* query SA times */ |
611 | status = charon->kernel_interface->query_sa(charon->kernel_interface, | |
612 | this->me.addr, this->me.spi, this->protocol, &use_in); | |
613 | if (status == SUCCESS && use_in) | |
c0593835 | 614 | { |
45f76a7d MW |
615 | snprintf(use_in_str, sizeof(use_in_str), "%ds", now - use_in); |
616 | } | |
617 | status = charon->kernel_interface->query_sa(charon->kernel_interface, | |
618 | this->other.addr, this->other.spi, this->protocol, &use_out); | |
619 | if (status == SUCCESS && use_out) | |
620 | { | |
621 | snprintf(use_out_str, sizeof(use_out_str), "%ds", now - use_out); | |
622 | } | |
623 | ||
624 | /* calculate rekey times */ | |
625 | if (this->soft_lifetime) | |
626 | { | |
627 | rekeying = this->soft_lifetime - (now - this->install_time); | |
628 | snprintf(rekey_str, sizeof(rekey_str), "%ds", (int)rekeying); | |
629 | } | |
630 | ||
631 | /* algorithms used */ | |
632 | if (this->protocol == PROTO_ESP) | |
633 | { | |
634 | if (this->encryption.key_size) | |
635 | { | |
636 | snprintf(enc_str, sizeof(enc_str), "%s-%d,", | |
637 | mapping_find(encryption_algorithm_m, this->encryption.algorithm), | |
638 | this->encryption.key_size); | |
639 | } | |
640 | else | |
641 | { | |
642 | snprintf(enc_str, sizeof(enc_str), "%s,", | |
643 | mapping_find(encryption_algorithm_m, this->encryption.algorithm)); | |
644 | } | |
645 | } | |
646 | if (this->integrity.key_size) | |
647 | { | |
648 | snprintf(int_str, sizeof(int_str), "%s-%d", | |
649 | mapping_find(integrity_algorithm_m, this->integrity.algorithm), | |
650 | this->integrity.key_size); | |
c0593835 MW |
651 | } |
652 | else | |
653 | { | |
45f76a7d MW |
654 | snprintf(int_str, sizeof(int_str), "%s", |
655 | mapping_find(integrity_algorithm_m, this->integrity.algorithm)); | |
c0593835 | 656 | } |
45f76a7d MW |
657 | |
658 | logger->log(logger, CONTROL|LEVEL1, | |
659 | " \"%s\": state: %s, reqid: %d, ", | |
660 | name, mapping_find(child_sa_state_m, this->state), this->reqid); | |
661 | logger->log(logger, CONTROL|LEVEL1, | |
662 | " \"%s\": %s (%s%s), SPIs (in/out): 0x%x/0x%x", | |
663 | name, this->protocol == PROTO_ESP ? "ESP" : "AH", | |
664 | enc_str, int_str, | |
665 | htonl(this->me.spi), htonl(this->other.spi)); | |
666 | logger->log(logger, CONTROL|LEVEL1, | |
667 | " \"%s\": rekeying: %s, key age (in/out): %s/%s", | |
668 | name, rekey_str, use_in_str, use_out_str); | |
c0593835 MW |
669 | } |
670 | else | |
671 | { | |
45f76a7d MW |
672 | logger->log(logger, CONTROL|LEVEL1, " \"%s\": state: %s, reqid: %d", |
673 | name, mapping_find(child_sa_state_m, this->state), | |
674 | this->reqid); | |
c0593835 MW |
675 | } |
676 | ||
5f0eb96f MW |
677 | iterator = this->policies->create_iterator(this->policies, TRUE); |
678 | while (iterator->has_next(iterator)) | |
679 | { | |
680 | sa_policy_t *policy; | |
92ee45a0 MW |
681 | char *my_str; |
682 | char *other_str; | |
683 | char pol_in_str[12] = "unused"; | |
684 | char pol_out_str[12] = "unused"; | |
685 | char pol_fwd_str[12] = "unused"; | |
5f0eb96f | 686 | |
92ee45a0 | 687 | /* get ts strings */ |
5f0eb96f | 688 | iterator->current(iterator, (void**)&policy); |
92ee45a0 MW |
689 | my_str = policy->my_ts->get_string(policy->my_ts); |
690 | other_str = policy->other_ts->get_string(policy->other_ts); | |
5f0eb96f | 691 | |
92ee45a0 MW |
692 | /* query policy times */ |
693 | status = charon->kernel_interface->query_policy(charon->kernel_interface, | |
694 | policy->other_ts, policy->my_ts, POLICY_IN, &use_in); | |
695 | if (status == SUCCESS && use_in) | |
5f0eb96f | 696 | { |
92ee45a0 | 697 | snprintf(pol_in_str, sizeof(pol_in_str), "%ds", now - use_in); |
5f0eb96f | 698 | } |
92ee45a0 MW |
699 | status = charon->kernel_interface->query_policy(charon->kernel_interface, |
700 | policy->my_ts, policy->other_ts, POLICY_OUT, &use_out); | |
701 | if (status == SUCCESS && use_out) | |
5f0eb96f | 702 | { |
92ee45a0 | 703 | snprintf(pol_out_str, sizeof(pol_out_str), "%ds", now - use_out); |
5f0eb96f | 704 | } |
92ee45a0 MW |
705 | status = charon->kernel_interface->query_policy(charon->kernel_interface, |
706 | policy->other_ts, policy->my_ts, POLICY_FWD, &use_fwd); | |
707 | if (status == SUCCESS && use_fwd) | |
5f0eb96f | 708 | { |
92ee45a0 | 709 | snprintf(pol_fwd_str, sizeof(pol_fwd_str), "%ds", now - use_fwd); |
5f0eb96f MW |
710 | } |
711 | ||
92ee45a0 MW |
712 | logger->log(logger, CONTROL, |
713 | " \"%s\": %s====%s, last use (in/out/fwd): %s/%s/%s", | |
714 | name, my_str, other_str, pol_in_str, pol_out_str, pol_fwd_str); | |
5f0eb96f MW |
715 | } |
716 | iterator->destroy(iterator); | |
717 | } | |
718 | ||
1396815a MW |
719 | /** |
720 | * Update the host adress/port of a SA | |
721 | */ | |
722 | static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, | |
723 | int my_changes, int other_changes, bool mine) | |
724 | { | |
725 | host_t *src, *dst, *new_src, *new_dst; | |
726 | int src_changes, dst_changes; | |
727 | status_t status; | |
728 | u_int32_t spi; | |
729 | ||
730 | if (mine) | |
1396815a MW |
731 | { |
732 | src = this->other.addr; | |
733 | dst = this->me.addr; | |
734 | new_src = new_other; | |
735 | new_dst = new_me; | |
736 | src_changes = other_changes; | |
737 | dst_changes = my_changes; | |
738 | spi = this->other.spi; | |
739 | } | |
abba7ecb MW |
740 | else |
741 | { | |
742 | src = this->me.addr; | |
743 | dst = this->other.addr; | |
744 | new_src = new_me; | |
745 | new_dst = new_other; | |
746 | src_changes = my_changes; | |
747 | dst_changes = other_changes; | |
748 | spi = this->me.spi; | |
749 | } | |
1396815a MW |
750 | |
751 | this->logger->log(this->logger, CONTROL|LEVEL1, | |
752 | "updating %s SA 0x%x, from %s:%d..%s:%d to %s:%d..%s:%d", | |
753 | mapping_find(protocol_id_m, this->protocol), ntohl(spi), | |
92ee45a0 MW |
754 | src->get_string(src), src->get_port(src), |
755 | dst->get_string(dst), dst->get_port(dst), | |
756 | new_src->get_string(new_src), new_src->get_port(new_src), | |
757 | new_dst->get_string(new_dst), new_dst->get_port(new_dst)); | |
1396815a | 758 | |
92ee45a0 MW |
759 | status = charon->kernel_interface->update_sa(charon->kernel_interface, |
760 | dst, spi, this->protocol, | |
761 | new_src, new_dst, | |
762 | src_changes, dst_changes); | |
1396815a MW |
763 | |
764 | if (status != SUCCESS) | |
765 | { | |
766 | return FAILED; | |
767 | } | |
768 | return SUCCESS; | |
769 | } | |
770 | ||
771 | /** | |
772 | * Update the host adress/port of a policy | |
773 | */ | |
774 | static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other) | |
775 | { | |
776 | iterator_t *iterator; | |
777 | sa_policy_t *policy; | |
778 | status_t status; | |
779 | ||
780 | iterator = this->policies->create_iterator(this->policies, TRUE); | |
781 | while (iterator->iterate(iterator, (void**)&policy)) | |
782 | { | |
1396815a MW |
783 | status = charon->kernel_interface->add_policy( |
784 | charon->kernel_interface, | |
785 | new_me, new_other, | |
92ee45a0 MW |
786 | policy->my_ts, policy->other_ts, |
787 | POLICY_OUT, this->protocol, this->reqid, TRUE); | |
1396815a MW |
788 | |
789 | status |= charon->kernel_interface->add_policy( | |
790 | charon->kernel_interface, | |
791 | new_other, new_me, | |
92ee45a0 MW |
792 | policy->other_ts, policy->my_ts, |
793 | POLICY_IN, this->protocol, this->reqid, TRUE); | |
1396815a MW |
794 | |
795 | status |= charon->kernel_interface->add_policy( | |
796 | charon->kernel_interface, | |
797 | new_other, new_me, | |
92ee45a0 MW |
798 | policy->other_ts, policy->my_ts, |
799 | POLICY_FWD, this->protocol, this->reqid, TRUE); | |
aeeb4f4f | 800 | |
1396815a MW |
801 | if (status != SUCCESS) |
802 | { | |
803 | iterator->destroy(iterator); | |
804 | return FAILED; | |
805 | } | |
806 | } | |
807 | iterator->destroy(iterator); | |
92ee45a0 | 808 | |
698d7749 | 809 | return SUCCESS; |
1396815a MW |
810 | } |
811 | ||
812 | /** | |
813 | * Implementation of child_sa_t.update_hosts. | |
814 | */ | |
815 | static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, | |
92ee45a0 | 816 | host_diff_t my_changes, host_diff_t other_changes) |
1396815a | 817 | { |
abba7ecb | 818 | if (!my_changes && !other_changes) |
1396815a MW |
819 | { |
820 | return SUCCESS; | |
821 | } | |
822 | ||
823 | /* update our (initator) SAs */ | |
824 | if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS) | |
825 | { | |
826 | return FAILED; | |
827 | } | |
828 | ||
829 | /* update his (responder) SAs */ | |
830 | if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS) | |
831 | { | |
832 | return FAILED; | |
833 | } | |
834 | ||
835 | /* update policies */ | |
836 | if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR) | |
837 | { | |
838 | if (update_policy_hosts(this, new_me, new_other) != SUCCESS) | |
839 | { | |
840 | return FAILED; | |
841 | } | |
842 | } | |
843 | ||
844 | /* update hosts */ | |
845 | if (my_changes) | |
846 | { | |
847 | this->me.addr->destroy(this->me.addr); | |
848 | this->me.addr = new_me->clone(new_me); | |
849 | } | |
850 | ||
851 | if (other_changes) | |
852 | { | |
853 | this->other.addr->destroy(this->other.addr); | |
854 | this->other.addr = new_other->clone(new_other); | |
855 | } | |
856 | ||
857 | return SUCCESS; | |
858 | } | |
859 | ||
30b5b412 MW |
860 | /** |
861 | * Implementation of child_sa_t.destroy. | |
862 | */ | |
863 | static void destroy(private_child_sa_t *this) | |
864 | { | |
16b9a73c | 865 | sa_policy_t *policy; |
5d187bd2 MW |
866 | |
867 | /* delete SAs in the kernel, if they are set up */ | |
3efbf983 | 868 | if (this->me.spi) |
5d187bd2 MW |
869 | { |
870 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
695723d4 | 871 | this->me.addr, this->me.spi, this->protocol); |
3efbf983 MW |
872 | } |
873 | if (this->alloc_esp_spi && this->alloc_esp_spi != this->me.spi) | |
874 | { | |
875 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
876 | this->me.addr, this->alloc_esp_spi, PROTO_ESP); | |
877 | } | |
878 | if (this->alloc_ah_spi && this->alloc_ah_spi != this->me.spi) | |
879 | { | |
880 | charon->kernel_interface->del_sa(charon->kernel_interface, | |
881 | this->me.addr, this->alloc_ah_spi, PROTO_AH); | |
882 | } | |
883 | if (this->other.spi) | |
884 | { | |
5d187bd2 | 885 | charon->kernel_interface->del_sa(charon->kernel_interface, |
695723d4 MW |
886 | this->other.addr, this->other.spi, this->protocol); |
887 | } | |
888 | ||
889 | /* delete all policies in the kernel */ | |
890 | while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) | |
891 | { | |
aeeb4f4f MW |
892 | /* let rekeyed policies, as they are used by another child_sa */ |
893 | charon->kernel_interface->del_policy(charon->kernel_interface, | |
92ee45a0 MW |
894 | policy->my_ts, policy->other_ts, |
895 | POLICY_OUT); | |
aeeb4f4f MW |
896 | |
897 | charon->kernel_interface->del_policy(charon->kernel_interface, | |
92ee45a0 MW |
898 | policy->other_ts, policy->my_ts, |
899 | POLICY_IN); | |
aeeb4f4f MW |
900 | |
901 | charon->kernel_interface->del_policy(charon->kernel_interface, | |
92ee45a0 MW |
902 | policy->other_ts, policy->my_ts, |
903 | POLICY_FWD); | |
904 | policy->my_ts->destroy(policy->my_ts); | |
905 | policy->other_ts->destroy(policy->other_ts); | |
695723d4 | 906 | free(policy); |
5d187bd2 | 907 | } |
695723d4 | 908 | this->policies->destroy(this->policies); |
abba7ecb | 909 | |
8dfbe71b MW |
910 | this->my_ts->destroy(this->my_ts); |
911 | this->other_ts->destroy(this->other_ts); | |
abba7ecb MW |
912 | this->me.addr->destroy(this->me.addr); |
913 | this->other.addr->destroy(this->other.addr); | |
5113680f | 914 | free(this); |
30b5b412 MW |
915 | } |
916 | ||
917 | /* | |
918 | * Described in header. | |
919 | */ | |
50f98119 | 920 | child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, |
1396815a MW |
921 | u_int32_t soft_lifetime, u_int32_t hard_lifetime, |
922 | bool use_natt) | |
30b5b412 | 923 | { |
1396815a | 924 | static u_int32_t reqid = REQID_START; |
5113680f | 925 | private_child_sa_t *this = malloc_thing(private_child_sa_t); |
30b5b412 MW |
926 | |
927 | /* public functions */ | |
32b6500f | 928 | this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid; |
8d77edde MW |
929 | this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi; |
930 | this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol; | |
30b5b412 MW |
931 | this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; |
932 | this->public.add = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))add; | |
933 | this->public.update = (status_t(*)(child_sa_t*,proposal_t*,prf_plus_t*))update; | |
92ee45a0 | 934 | this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_diff_t,host_diff_t))update_hosts; |
5d187bd2 | 935 | this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; |
8dfbe71b MW |
936 | this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors; |
937 | this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors; | |
2f89902d | 938 | this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time; |
abba7ecb MW |
939 | this->public.set_rekeying_transaction = (void (*)(child_sa_t*,void*))set_rekeying_transaction; |
940 | this->public.get_rekeying_transaction = (void* (*)(child_sa_t*))get_rekeying_transaction; | |
bcb95ced MW |
941 | this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; |
942 | this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; | |
e168ee17 | 943 | this->public.log_status = (void (*)(child_sa_t*, logger_t*, char*))log_status; |
30b5b412 MW |
944 | this->public.destroy = (void(*)(child_sa_t*))destroy; |
945 | ||
946 | /* private data */ | |
5113680f | 947 | this->logger = logger_manager->get_logger(logger_manager, CHILD_SA); |
abba7ecb MW |
948 | this->me.addr = me->clone(me); |
949 | this->other.addr = other->clone(other); | |
8d77edde MW |
950 | this->me.spi = 0; |
951 | this->other.spi = 0; | |
3efbf983 MW |
952 | this->alloc_ah_spi = 0; |
953 | this->alloc_esp_spi = 0; | |
1396815a | 954 | this->use_natt = use_natt; |
8d77edde MW |
955 | this->soft_lifetime = soft_lifetime; |
956 | this->hard_lifetime = hard_lifetime; | |
bcb95ced | 957 | this->state = CHILD_CREATED; |
50f98119 MW |
958 | /* reuse old reqid if we are rekeying an existing CHILD_SA */ |
959 | this->reqid = rekey ? rekey : ++reqid; | |
c0593835 MW |
960 | this->encryption.algorithm = ENCR_UNDEFINED; |
961 | this->encryption.key_size = 0; | |
962 | this->integrity.algorithm = AUTH_UNDEFINED; | |
963 | this->encryption.key_size = 0; | |
5d187bd2 | 964 | this->policies = linked_list_create(); |
8dfbe71b MW |
965 | this->my_ts = linked_list_create(); |
966 | this->other_ts = linked_list_create(); | |
8d77edde | 967 | this->protocol = PROTO_NONE; |
abba7ecb | 968 | this->rekeying_transaction = NULL; |
3ebebc5e | 969 | |
1396815a | 970 | return &this->public; |
3ebebc5e | 971 | } |