2 * Copyright (C) 2008-2015 Tobias Brunner
3 * Copyright (C) 2005-2007 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "child_cfg.h"
24 ENUM(action_names
, ACTION_NONE
, ACTION_RESTART
,
30 /** Default replay window size, if not set using charon.replay_window */
31 #define DEFAULT_REPLAY_WINDOW 32
33 typedef struct private_child_cfg_t private_child_cfg_t
;
36 * Private data of an child_cfg_t object
38 struct private_child_cfg_t
{
46 * Number of references hold by others to this child_cfg
51 * Name of the child_cfg, used to query it
56 * list for all proposals
58 linked_list_t
*proposals
;
61 * list for traffic selectors for my site
66 * list for traffic selectors for others site
68 linked_list_t
*other_ts
;
81 * Mode to propose for a initiated CHILD: tunnel/transport
86 * action to take to start CHILD_SA
88 action_t start_action
;
91 * action to take on DPD
96 * action to take on CHILD_SA close
98 action_t close_action
;
101 * CHILD_SA lifetime config
103 lifetime_cfg_t lifetime
;
113 u_int32_t inactivity
;
116 * Reqid to install CHILD_SA with
121 * Optional mark to install inbound CHILD_SA with
126 * Optional mark to install outbound CHILD_SA with
131 * Traffic Flow Confidentiality padding, if enabled
136 * set up IPsec transport SA in MIPv6 proxy mode
141 * enable installation and removal of kernel IPsec policies
146 * anti-replay window size
148 u_int32_t replay_window
;
151 METHOD(child_cfg_t
, get_name
, char*,
152 private_child_cfg_t
*this)
157 METHOD(child_cfg_t
, add_proposal
, void,
158 private_child_cfg_t
*this, proposal_t
*proposal
)
162 this->proposals
->insert_last(this->proposals
, proposal
);
166 static bool match_proposal(proposal_t
*item
, proposal_t
*proposal
)
168 return item
->equals(item
, proposal
);
171 METHOD(child_cfg_t
, get_proposals
, linked_list_t
*,
172 private_child_cfg_t
*this, bool strip_dh
)
174 enumerator_t
*enumerator
;
176 linked_list_t
*proposals
= linked_list_create();
178 enumerator
= this->proposals
->create_enumerator(this->proposals
);
179 while (enumerator
->enumerate(enumerator
, ¤t
))
181 current
= current
->clone(current
);
184 current
->strip_dh(current
, MODP_NONE
);
186 if (proposals
->find_first(proposals
, (linked_list_match_t
)match_proposal
,
187 NULL
, current
) == SUCCESS
)
189 current
->destroy(current
);
192 proposals
->insert_last(proposals
, current
);
194 enumerator
->destroy(enumerator
);
196 DBG2(DBG_CFG
, "configured proposals: %#P", proposals
);
201 METHOD(child_cfg_t
, select_proposal
, proposal_t
*,
202 private_child_cfg_t
*this, linked_list_t
*proposals
, bool strip_dh
,
205 enumerator_t
*stored_enum
, *supplied_enum
;
206 proposal_t
*stored
, *supplied
, *selected
= NULL
;
208 stored_enum
= this->proposals
->create_enumerator(this->proposals
);
209 supplied_enum
= proposals
->create_enumerator(proposals
);
211 /* compare all stored proposals with all supplied. Stored ones are preferred. */
212 while (stored_enum
->enumerate(stored_enum
, &stored
))
214 stored
= stored
->clone(stored
);
215 while (supplied_enum
->enumerate(supplied_enum
, &supplied
))
219 stored
->strip_dh(stored
, MODP_NONE
);
221 selected
= stored
->select(stored
, supplied
, private);
224 DBG2(DBG_CFG
, "received proposals: %#P", proposals
);
225 DBG2(DBG_CFG
, "configured proposals: %#P", this->proposals
);
226 DBG2(DBG_CFG
, "selected proposal: %P", selected
);
230 stored
->destroy(stored
);
235 supplied_enum
->destroy(supplied_enum
);
236 supplied_enum
= proposals
->create_enumerator(proposals
);
238 stored_enum
->destroy(stored_enum
);
239 supplied_enum
->destroy(supplied_enum
);
240 if (selected
== NULL
)
242 DBG1(DBG_CFG
, "received proposals: %#P", proposals
);
243 DBG1(DBG_CFG
, "configured proposals: %#P", this->proposals
);
248 METHOD(child_cfg_t
, add_traffic_selector
, void,
249 private_child_cfg_t
*this, bool local
, traffic_selector_t
*ts
)
253 this->my_ts
->insert_last(this->my_ts
, ts
);
257 this->other_ts
->insert_last(this->other_ts
, ts
);
261 METHOD(child_cfg_t
, get_traffic_selectors
, linked_list_t
*,
262 private_child_cfg_t
*this, bool local
, linked_list_t
*supplied
,
263 linked_list_t
*hosts
)
265 enumerator_t
*e1
, *e2
;
266 traffic_selector_t
*ts1
, *ts2
, *selected
;
267 linked_list_t
*result
, *derived
;
270 result
= linked_list_create();
271 derived
= linked_list_create();
274 e1
= this->my_ts
->create_enumerator(this->my_ts
);
278 e1
= this->other_ts
->create_enumerator(this->other_ts
);
280 /* In a first step, replace "dynamic" TS with the host list */
281 while (e1
->enumerate(e1
, &ts1
))
283 if (hosts
&& hosts
->get_count(hosts
) &&
284 ts1
->is_dynamic(ts1
))
286 e2
= hosts
->create_enumerator(hosts
);
287 while (e2
->enumerate(e2
, &host
))
289 ts2
= ts1
->clone(ts1
);
290 ts2
->set_address(ts2
, host
);
291 derived
->insert_last(derived
, ts2
);
297 derived
->insert_last(derived
, ts1
->clone(ts1
));
302 DBG2(DBG_CFG
, "%s traffic selectors for %s:",
303 supplied ?
"selecting" : "proposing", local ?
"us" : "other");
304 if (supplied
== NULL
)
306 while (derived
->remove_first(derived
, (void**)&ts1
) == SUCCESS
)
308 DBG2(DBG_CFG
, " %R", ts1
);
309 result
->insert_last(result
, ts1
);
311 derived
->destroy(derived
);
315 e1
= derived
->create_enumerator(derived
);
316 e2
= supplied
->create_enumerator(supplied
);
317 /* enumerate all configured/derived selectors */
318 while (e1
->enumerate(e1
, &ts1
))
320 /* enumerate all supplied traffic selectors */
321 while (e2
->enumerate(e2
, &ts2
))
323 selected
= ts1
->get_subset(ts1
, ts2
);
326 DBG2(DBG_CFG
, " config: %R, received: %R => match: %R",
328 result
->insert_last(result
, selected
);
332 DBG2(DBG_CFG
, " config: %R, received: %R => no match",
336 supplied
->reset_enumerator(supplied
, e2
);
341 /* check if we/peer did any narrowing, raise alert */
342 e1
= derived
->create_enumerator(derived
);
343 e2
= result
->create_enumerator(result
);
344 while (e1
->enumerate(e1
, &ts1
))
346 if (!e2
->enumerate(e2
, &ts2
) || !ts1
->equals(ts1
, ts2
))
348 charon
->bus
->alert(charon
->bus
, ALERT_TS_NARROWED
,
349 local
, result
, this);
356 derived
->destroy_offset(derived
, offsetof(traffic_selector_t
, destroy
));
359 /* remove any redundant traffic selectors in the list */
360 e1
= result
->create_enumerator(result
);
361 e2
= result
->create_enumerator(result
);
362 while (e1
->enumerate(e1
, &ts1
))
364 while (e2
->enumerate(e2
, &ts2
))
368 if (ts2
->is_contained_in(ts2
, ts1
))
370 result
->remove_at(result
, e2
);
372 result
->reset_enumerator(result
, e1
);
375 if (ts1
->is_contained_in(ts1
, ts2
))
377 result
->remove_at(result
, e1
);
383 result
->reset_enumerator(result
, e2
);
391 METHOD(child_cfg_t
, get_updown
, char*,
392 private_child_cfg_t
*this)
397 METHOD(child_cfg_t
, get_hostaccess
, bool,
398 private_child_cfg_t
*this)
400 return this->hostaccess
;
404 * Applies jitter to the rekey value. Returns the new rekey value.
405 * Note: The distribution of random values is not perfect, but it
406 * should get the job done.
408 static u_int64_t
apply_jitter(u_int64_t rekey
, u_int64_t jitter
)
414 jitter
= (jitter
== UINT64_MAX
) ? jitter
: jitter
+ 1;
415 return rekey
- jitter
* (random() / (RAND_MAX
+ 1.0));
417 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
419 METHOD(child_cfg_t
, get_lifetime
, lifetime_cfg_t
*,
420 private_child_cfg_t
*this)
422 lifetime_cfg_t
*lft
= malloc_thing(lifetime_cfg_t
);
423 memcpy(lft
, &this->lifetime
, sizeof(lifetime_cfg_t
));
424 APPLY_JITTER(lft
->time
);
425 APPLY_JITTER(lft
->bytes
);
426 APPLY_JITTER(lft
->packets
);
430 METHOD(child_cfg_t
, get_mode
, ipsec_mode_t
,
431 private_child_cfg_t
*this)
436 METHOD(child_cfg_t
, get_start_action
, action_t
,
437 private_child_cfg_t
*this)
439 return this->start_action
;
442 METHOD(child_cfg_t
, get_dpd_action
, action_t
,
443 private_child_cfg_t
*this)
445 return this->dpd_action
;
448 METHOD(child_cfg_t
, get_close_action
, action_t
,
449 private_child_cfg_t
*this)
451 return this->close_action
;
454 METHOD(child_cfg_t
, get_dh_group
, diffie_hellman_group_t
,
455 private_child_cfg_t
*this)
457 enumerator_t
*enumerator
;
458 proposal_t
*proposal
;
459 u_int16_t dh_group
= MODP_NONE
;
461 enumerator
= this->proposals
->create_enumerator(this->proposals
);
462 while (enumerator
->enumerate(enumerator
, &proposal
))
464 if (proposal
->get_algorithm(proposal
, DIFFIE_HELLMAN_GROUP
, &dh_group
, NULL
))
469 enumerator
->destroy(enumerator
);
473 METHOD(child_cfg_t
, use_ipcomp
, bool,
474 private_child_cfg_t
*this)
476 return this->use_ipcomp
;
479 METHOD(child_cfg_t
, get_inactivity
, u_int32_t
,
480 private_child_cfg_t
*this)
482 return this->inactivity
;
485 METHOD(child_cfg_t
, get_reqid
, u_int32_t
,
486 private_child_cfg_t
*this)
491 METHOD(child_cfg_t
, get_mark
, mark_t
,
492 private_child_cfg_t
*this, bool inbound
)
494 return inbound ?
this->mark_in
: this->mark_out
;
497 METHOD(child_cfg_t
, get_tfc
, u_int32_t
,
498 private_child_cfg_t
*this)
503 METHOD(child_cfg_t
, get_replay_window
, u_int32_t
,
504 private_child_cfg_t
*this)
506 return this->replay_window
;
509 METHOD(child_cfg_t
, set_replay_window
, void,
510 private_child_cfg_t
*this, u_int32_t replay_window
)
512 this->replay_window
= replay_window
;
515 METHOD(child_cfg_t
, set_mipv6_options
, void,
516 private_child_cfg_t
*this, bool proxy_mode
, bool install_policy
)
518 this->proxy_mode
= proxy_mode
;
519 this->install_policy
= install_policy
;
522 METHOD(child_cfg_t
, use_proxy_mode
, bool,
523 private_child_cfg_t
*this)
525 return this->proxy_mode
;
528 METHOD(child_cfg_t
, install_policy
, bool,
529 private_child_cfg_t
*this)
531 return this->install_policy
;
534 #define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; })
535 #define LIFETIME_EQUALS(a, b) ({ LT_PART_EQUALS(a.time, b.time) && LT_PART_EQUALS(a.bytes, b.bytes) && LT_PART_EQUALS(a.packets, b.packets); })
537 METHOD(child_cfg_t
, equals
, bool,
538 private_child_cfg_t
*this, child_cfg_t
*other_pub
)
540 private_child_cfg_t
*other
= (private_child_cfg_t
*)other_pub
;
546 if (this->public.equals
!= other
->public.equals
)
550 if (!this->proposals
->equals_offset(this->proposals
, other
->proposals
,
551 offsetof(proposal_t
, equals
)))
555 if (!this->my_ts
->equals_offset(this->my_ts
, other
->my_ts
,
556 offsetof(traffic_selector_t
, equals
)))
560 if (!this->other_ts
->equals_offset(this->other_ts
, other
->other_ts
,
561 offsetof(traffic_selector_t
, equals
)))
565 return this->hostaccess
== other
->hostaccess
&&
566 this->mode
== other
->mode
&&
567 this->start_action
== other
->start_action
&&
568 this->dpd_action
== other
->dpd_action
&&
569 this->close_action
== other
->close_action
&&
570 LIFETIME_EQUALS(this->lifetime
, other
->lifetime
) &&
571 this->use_ipcomp
== other
->use_ipcomp
&&
572 this->inactivity
== other
->inactivity
&&
573 this->reqid
== other
->reqid
&&
574 this->mark_in
.value
== other
->mark_in
.value
&&
575 this->mark_in
.mask
== other
->mark_in
.mask
&&
576 this->mark_out
.value
== other
->mark_out
.value
&&
577 this->mark_out
.mask
== other
->mark_out
.mask
&&
578 this->tfc
== other
->tfc
&&
579 this->replay_window
== other
->replay_window
&&
580 this->proxy_mode
== other
->proxy_mode
&&
581 this->install_policy
== other
->install_policy
&&
582 streq(this->updown
, other
->updown
);
585 METHOD(child_cfg_t
, get_ref
, child_cfg_t
*,
586 private_child_cfg_t
*this)
588 ref_get(&this->refcount
);
589 return &this->public;
592 METHOD(child_cfg_t
, destroy
, void,
593 private_child_cfg_t
*this)
595 if (ref_put(&this->refcount
))
597 this->proposals
->destroy_offset(this->proposals
, offsetof(proposal_t
, destroy
));
598 this->my_ts
->destroy_offset(this->my_ts
, offsetof(traffic_selector_t
, destroy
));
599 this->other_ts
->destroy_offset(this->other_ts
, offsetof(traffic_selector_t
, destroy
));
610 * Described in header-file
612 child_cfg_t
*child_cfg_create(char *name
, lifetime_cfg_t
*lifetime
,
613 char *updown
, bool hostaccess
,
614 ipsec_mode_t mode
, action_t start_action
,
615 action_t dpd_action
, action_t close_action
,
616 bool ipcomp
, u_int32_t inactivity
, u_int32_t reqid
,
617 mark_t
*mark_in
, mark_t
*mark_out
, u_int32_t tfc
)
619 private_child_cfg_t
*this;
623 .get_name
= _get_name
,
624 .add_traffic_selector
= _add_traffic_selector
,
625 .get_traffic_selectors
= _get_traffic_selectors
,
626 .add_proposal
= _add_proposal
,
627 .get_proposals
= _get_proposals
,
628 .select_proposal
= _select_proposal
,
629 .get_updown
= _get_updown
,
630 .get_hostaccess
= _get_hostaccess
,
631 .get_mode
= _get_mode
,
632 .get_start_action
= _get_start_action
,
633 .get_dpd_action
= _get_dpd_action
,
634 .get_close_action
= _get_close_action
,
635 .get_lifetime
= _get_lifetime
,
636 .get_dh_group
= _get_dh_group
,
637 .set_mipv6_options
= _set_mipv6_options
,
638 .use_ipcomp
= _use_ipcomp
,
639 .get_inactivity
= _get_inactivity
,
640 .get_reqid
= _get_reqid
,
641 .get_mark
= _get_mark
,
643 .get_replay_window
= _get_replay_window
,
644 .set_replay_window
= _set_replay_window
,
645 .use_proxy_mode
= _use_proxy_mode
,
646 .install_policy
= _install_policy
,
651 .name
= strdup(name
),
652 .updown
= strdupnull(updown
),
653 .hostaccess
= hostaccess
,
655 .start_action
= start_action
,
656 .dpd_action
= dpd_action
,
657 .close_action
= close_action
,
658 .use_ipcomp
= ipcomp
,
659 .inactivity
= inactivity
,
662 .install_policy
= TRUE
,
664 .proposals
= linked_list_create(),
665 .my_ts
= linked_list_create(),
666 .other_ts
= linked_list_create(),
668 .replay_window
= lib
->settings
->get_int(lib
->settings
,
669 "%s.replay_window", DEFAULT_REPLAY_WINDOW
, lib
->ns
),
674 this->mark_in
= *mark_in
;
678 this->mark_out
= *mark_out
;
680 memcpy(&this->lifetime
, lifetime
, sizeof(lifetime_cfg_t
));
682 return &this->public;