2 * Copyright (C) 2008-2019 Tobias Brunner
3 * Copyright (C) 2016 Andreas Steffen
4 * Copyright (C) 2005-2007 Martin Willi
5 * Copyright (C) 2005 Jan Hutter
6 * HSR Hochschule fuer Technik Rapperswil
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 #include "child_cfg.h"
25 ENUM(action_names
, ACTION_NONE
, ACTION_RESTART
,
31 /** Default replay window size, if not set using charon.replay_window */
32 #define DEFAULT_REPLAY_WINDOW 32
34 typedef struct private_child_cfg_t private_child_cfg_t
;
37 * Private data of an child_cfg_t object
39 struct private_child_cfg_t
{
47 * Number of references hold by others to this child_cfg
52 * Name of the child_cfg, used to query it
59 child_cfg_option_t options
;
62 * list for all proposals
64 linked_list_t
*proposals
;
67 * list for traffic selectors for my site
72 * list for traffic selectors for others site
74 linked_list_t
*other_ts
;
82 * Mode to propose for a initiated CHILD: tunnel/transport
87 * action to take to start CHILD_SA
89 action_t start_action
;
92 * action to take on DPD
97 * action to take on CHILD_SA close
99 action_t close_action
;
102 * CHILD_SA lifetime config
104 lifetime_cfg_t lifetime
;
112 * Reqid to install CHILD_SA with
117 * Optionl interface ID to use for inbound CHILD_SA
122 * Optionl interface ID to use for outbound CHILD_SA
127 * Optional mark to install inbound CHILD_SA with
132 * Optional mark to install outbound CHILD_SA with
137 * Optional mark to set to packets after inbound processing
142 * Optional mark to set to packets after outbound processing
147 * Traffic Flow Confidentiality padding, if enabled
152 * Optional manually-set IPsec policy priorities
154 uint32_t manual_prio
;
157 * Optional restriction of IPsec policy to a given network interface
162 * anti-replay window size
164 uint32_t replay_window
;
169 hw_offload_t hw_offload
;
172 * DS header field copy mode
174 dscp_copy_t copy_dscp
;
177 METHOD(child_cfg_t
, get_name
, char*,
178 private_child_cfg_t
*this)
183 METHOD(child_cfg_t
, has_option
, bool,
184 private_child_cfg_t
*this, child_cfg_option_t option
)
186 return this->options
& option
;
189 METHOD(child_cfg_t
, add_proposal
, void,
190 private_child_cfg_t
*this, proposal_t
*proposal
)
194 this->proposals
->insert_last(this->proposals
, proposal
);
198 CALLBACK(match_proposal
, bool,
199 proposal_t
*item
, va_list args
)
201 proposal_t
*proposal
;
203 VA_ARGS_VGET(args
, proposal
);
204 return item
->equals(item
, proposal
);
207 METHOD(child_cfg_t
, get_proposals
, linked_list_t
*,
208 private_child_cfg_t
*this, bool strip_dh
)
210 enumerator_t
*enumerator
;
212 linked_list_t
*proposals
= linked_list_create();
214 enumerator
= this->proposals
->create_enumerator(this->proposals
);
215 while (enumerator
->enumerate(enumerator
, ¤t
))
217 current
= current
->clone(current
);
220 current
->strip_dh(current
, MODP_NONE
);
222 if (proposals
->find_first(proposals
, match_proposal
, NULL
, current
))
224 current
->destroy(current
);
227 proposals
->insert_last(proposals
, current
);
229 enumerator
->destroy(enumerator
);
231 DBG2(DBG_CFG
, "configured proposals: %#P", proposals
);
236 METHOD(child_cfg_t
, select_proposal
, proposal_t
*,
237 private_child_cfg_t
*this, linked_list_t
*proposals
, bool strip_dh
,
238 bool private, bool prefer_self
)
240 enumerator_t
*prefer_enum
, *match_enum
;
241 proposal_t
*proposal
, *match
, *selected
= NULL
;
245 prefer_enum
= this->proposals
->create_enumerator(this->proposals
);
246 match_enum
= proposals
->create_enumerator(proposals
);
250 prefer_enum
= proposals
->create_enumerator(proposals
);
251 match_enum
= this->proposals
->create_enumerator(this->proposals
);
254 while (prefer_enum
->enumerate(prefer_enum
, &proposal
))
256 proposal
= proposal
->clone(proposal
);
259 proposal
->strip_dh(proposal
, MODP_NONE
);
263 proposals
->reset_enumerator(proposals
, match_enum
);
267 this->proposals
->reset_enumerator(this->proposals
, match_enum
);
269 while (match_enum
->enumerate(match_enum
, &match
))
271 match
= match
->clone(match
);
274 match
->strip_dh(match
, MODP_NONE
);
276 selected
= proposal
->select(proposal
, match
, prefer_self
, private);
277 match
->destroy(match
);
280 DBG2(DBG_CFG
, "received proposals: %#P", proposals
);
281 DBG2(DBG_CFG
, "configured proposals: %#P", this->proposals
);
282 DBG1(DBG_CFG
, "selected proposal: %P", selected
);
286 proposal
->destroy(proposal
);
292 prefer_enum
->destroy(prefer_enum
);
293 match_enum
->destroy(match_enum
);
296 DBG1(DBG_CFG
, "received proposals: %#P", proposals
);
297 DBG1(DBG_CFG
, "configured proposals: %#P", this->proposals
);
302 METHOD(child_cfg_t
, add_traffic_selector
, void,
303 private_child_cfg_t
*this, bool local
, traffic_selector_t
*ts
)
307 this->my_ts
->insert_last(this->my_ts
, ts
);
311 this->other_ts
->insert_last(this->other_ts
, ts
);
315 METHOD(child_cfg_t
, get_traffic_selectors
, linked_list_t
*,
316 private_child_cfg_t
*this, bool local
, linked_list_t
*supplied
,
317 linked_list_t
*hosts
, bool log
)
319 enumerator_t
*e1
, *e2
;
320 traffic_selector_t
*ts1
, *ts2
, *selected
;
321 linked_list_t
*result
, *derived
;
324 result
= linked_list_create();
325 derived
= linked_list_create();
328 e1
= this->my_ts
->create_enumerator(this->my_ts
);
332 e1
= this->other_ts
->create_enumerator(this->other_ts
);
334 /* in a first step, replace "dynamic" TS with the host list */
335 while (e1
->enumerate(e1
, &ts1
))
337 if (hosts
&& hosts
->get_count(hosts
))
338 { /* set hosts if TS is dynamic or as initiator in transport mode */
339 bool dynamic
= ts1
->is_dynamic(ts1
),
340 proxy_mode
= has_option(this, OPT_PROXY_MODE
);
341 if (dynamic
|| (this->mode
== MODE_TRANSPORT
&& !proxy_mode
&&
344 e2
= hosts
->create_enumerator(hosts
);
345 while (e2
->enumerate(e2
, &host
))
347 ts2
= ts1
->clone(ts1
);
348 if (dynamic
|| !host
->is_anyaddr(host
))
349 { /* don't make regular TS larger than they were */
350 ts2
->set_address(ts2
, host
);
352 derived
->insert_last(derived
, ts2
);
358 derived
->insert_last(derived
, ts1
->clone(ts1
));
364 DBG2(DBG_CFG
, "%s traffic selectors for %s:",
365 supplied
? "selecting" : "proposing", local
? "us" : "other");
369 while (derived
->remove_first(derived
, (void**)&ts1
) == SUCCESS
)
373 DBG2(DBG_CFG
, " %R", ts1
);
375 result
->insert_last(result
, ts1
);
377 derived
->destroy(derived
);
381 e1
= derived
->create_enumerator(derived
);
382 e2
= supplied
->create_enumerator(supplied
);
383 /* enumerate all configured/derived selectors */
384 while (e1
->enumerate(e1
, &ts1
))
386 /* enumerate all supplied traffic selectors */
387 while (e2
->enumerate(e2
, &ts2
))
389 selected
= ts1
->get_subset(ts1
, ts2
);
394 DBG2(DBG_CFG
, " config: %R, received: %R => match: %R",
397 result
->insert_last(result
, selected
);
401 DBG2(DBG_CFG
, " config: %R, received: %R => no match",
405 supplied
->reset_enumerator(supplied
, e2
);
410 /* check if we/peer did any narrowing, raise alert */
411 e1
= derived
->create_enumerator(derived
);
412 e2
= result
->create_enumerator(result
);
413 while (e1
->enumerate(e1
, &ts1
))
415 if (!e2
->enumerate(e2
, &ts2
) || !ts1
->equals(ts1
, ts2
))
417 charon
->bus
->alert(charon
->bus
, ALERT_TS_NARROWED
,
418 local
, result
, this);
425 derived
->destroy_offset(derived
, offsetof(traffic_selector_t
, destroy
));
428 /* remove any redundant traffic selectors in the list */
429 e1
= result
->create_enumerator(result
);
430 e2
= result
->create_enumerator(result
);
431 while (e1
->enumerate(e1
, &ts1
))
433 while (e2
->enumerate(e2
, &ts2
))
437 if (ts2
->is_contained_in(ts2
, ts1
))
439 result
->remove_at(result
, e2
);
441 result
->reset_enumerator(result
, e1
);
444 if (ts1
->is_contained_in(ts1
, ts2
))
446 result
->remove_at(result
, e1
);
452 result
->reset_enumerator(result
, e2
);
460 METHOD(child_cfg_t
, get_updown
, char*,
461 private_child_cfg_t
*this)
467 * Applies jitter to the rekey value. Returns the new rekey value.
468 * Note: The distribution of random values is not perfect, but it
469 * should get the job done.
471 static uint64_t apply_jitter(uint64_t rekey
, uint64_t jitter
)
477 jitter
= (jitter
== UINT64_MAX
) ? jitter
: jitter
+ 1;
478 return rekey
- jitter
* (random() / (RAND_MAX
+ 1.0));
480 #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
482 METHOD(child_cfg_t
, get_lifetime
, lifetime_cfg_t
*,
483 private_child_cfg_t
*this, bool jitter
)
485 lifetime_cfg_t
*lft
= malloc_thing(lifetime_cfg_t
);
486 memcpy(lft
, &this->lifetime
, sizeof(lifetime_cfg_t
));
489 lft
->time
.jitter
= lft
->bytes
.jitter
= lft
->packets
.jitter
= 0;
491 APPLY_JITTER(lft
->time
);
492 APPLY_JITTER(lft
->bytes
);
493 APPLY_JITTER(lft
->packets
);
497 METHOD(child_cfg_t
, get_mode
, ipsec_mode_t
,
498 private_child_cfg_t
*this)
503 METHOD(child_cfg_t
, get_start_action
, action_t
,
504 private_child_cfg_t
*this)
506 return this->start_action
;
509 METHOD(child_cfg_t
, get_hw_offload
, hw_offload_t
,
510 private_child_cfg_t
*this)
512 return this->hw_offload
;
515 METHOD(child_cfg_t
, get_copy_dscp
, dscp_copy_t
,
516 private_child_cfg_t
*this)
518 return this->copy_dscp
;
521 METHOD(child_cfg_t
, get_dpd_action
, action_t
,
522 private_child_cfg_t
*this)
524 return this->dpd_action
;
527 METHOD(child_cfg_t
, get_close_action
, action_t
,
528 private_child_cfg_t
*this)
530 return this->close_action
;
533 METHOD(child_cfg_t
, get_dh_group
, diffie_hellman_group_t
,
534 private_child_cfg_t
*this)
536 enumerator_t
*enumerator
;
537 proposal_t
*proposal
;
538 uint16_t dh_group
= MODP_NONE
;
540 enumerator
= this->proposals
->create_enumerator(this->proposals
);
541 while (enumerator
->enumerate(enumerator
, &proposal
))
543 if (proposal
->get_algorithm(proposal
, DIFFIE_HELLMAN_GROUP
, &dh_group
, NULL
))
548 enumerator
->destroy(enumerator
);
552 METHOD(child_cfg_t
, get_inactivity
, uint32_t,
553 private_child_cfg_t
*this)
555 return this->inactivity
;
558 METHOD(child_cfg_t
, get_reqid
, uint32_t,
559 private_child_cfg_t
*this)
564 METHOD(child_cfg_t
, get_if_id
, uint32_t,
565 private_child_cfg_t
*this, bool inbound
)
567 return inbound
? this->if_id_in
: this->if_id_out
;
570 METHOD(child_cfg_t
, get_mark
, mark_t
,
571 private_child_cfg_t
*this, bool inbound
)
573 return inbound
? this->mark_in
: this->mark_out
;
576 METHOD(child_cfg_t
, get_set_mark
, mark_t
,
577 private_child_cfg_t
*this, bool inbound
)
579 return inbound
? this->set_mark_in
: this->set_mark_out
;
582 METHOD(child_cfg_t
, get_tfc
, uint32_t,
583 private_child_cfg_t
*this)
588 METHOD(child_cfg_t
, get_manual_prio
, uint32_t,
589 private_child_cfg_t
*this)
591 return this->manual_prio
;
594 METHOD(child_cfg_t
, get_interface
, char*,
595 private_child_cfg_t
*this)
597 return this->interface
;
600 METHOD(child_cfg_t
, get_replay_window
, uint32_t,
601 private_child_cfg_t
*this)
603 return this->replay_window
;
606 METHOD(child_cfg_t
, set_replay_window
, void,
607 private_child_cfg_t
*this, uint32_t replay_window
)
609 this->replay_window
= replay_window
;
612 #define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; })
613 #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); })
615 METHOD(child_cfg_t
, equals
, bool,
616 private_child_cfg_t
*this, child_cfg_t
*other_pub
)
618 private_child_cfg_t
*other
= (private_child_cfg_t
*)other_pub
;
624 if (this->public.equals
!= other
->public.equals
)
628 if (!this->proposals
->equals_offset(this->proposals
, other
->proposals
,
629 offsetof(proposal_t
, equals
)))
633 if (!this->my_ts
->equals_offset(this->my_ts
, other
->my_ts
,
634 offsetof(traffic_selector_t
, equals
)))
638 if (!this->other_ts
->equals_offset(this->other_ts
, other
->other_ts
,
639 offsetof(traffic_selector_t
, equals
)))
643 return this->options
== other
->options
&&
644 this->mode
== other
->mode
&&
645 this->start_action
== other
->start_action
&&
646 this->dpd_action
== other
->dpd_action
&&
647 this->close_action
== other
->close_action
&&
648 LIFETIME_EQUALS(this->lifetime
, other
->lifetime
) &&
649 this->inactivity
== other
->inactivity
&&
650 this->reqid
== other
->reqid
&&
651 this->if_id_in
== other
->if_id_in
&&
652 this->if_id_out
== other
->if_id_out
&&
653 this->mark_in
.value
== other
->mark_in
.value
&&
654 this->mark_in
.mask
== other
->mark_in
.mask
&&
655 this->mark_out
.value
== other
->mark_out
.value
&&
656 this->mark_out
.mask
== other
->mark_out
.mask
&&
657 this->set_mark_in
.value
== other
->set_mark_in
.value
&&
658 this->set_mark_in
.mask
== other
->set_mark_in
.mask
&&
659 this->set_mark_out
.value
== other
->set_mark_out
.value
&&
660 this->set_mark_out
.mask
== other
->set_mark_out
.mask
&&
661 this->tfc
== other
->tfc
&&
662 this->manual_prio
== other
->manual_prio
&&
663 this->replay_window
== other
->replay_window
&&
664 this->hw_offload
== other
->hw_offload
&&
665 this->copy_dscp
== other
->copy_dscp
&&
666 streq(this->updown
, other
->updown
) &&
667 streq(this->interface
, other
->interface
);
670 METHOD(child_cfg_t
, get_ref
, child_cfg_t
*,
671 private_child_cfg_t
*this)
673 ref_get(&this->refcount
);
674 return &this->public;
677 METHOD(child_cfg_t
, destroy
, void,
678 private_child_cfg_t
*this)
680 if (ref_put(&this->refcount
))
682 this->proposals
->destroy_offset(this->proposals
, offsetof(proposal_t
, destroy
));
683 this->my_ts
->destroy_offset(this->my_ts
, offsetof(traffic_selector_t
, destroy
));
684 this->other_ts
->destroy_offset(this->other_ts
, offsetof(traffic_selector_t
, destroy
));
686 free(this->interface
);
693 * Described in header-file
695 child_cfg_t
*child_cfg_create(char *name
, child_cfg_create_t
*data
)
697 private_child_cfg_t
*this;
701 .get_name
= _get_name
,
702 .add_traffic_selector
= _add_traffic_selector
,
703 .get_traffic_selectors
= _get_traffic_selectors
,
704 .add_proposal
= _add_proposal
,
705 .get_proposals
= _get_proposals
,
706 .select_proposal
= _select_proposal
,
707 .get_updown
= _get_updown
,
708 .get_mode
= _get_mode
,
709 .get_start_action
= _get_start_action
,
710 .get_dpd_action
= _get_dpd_action
,
711 .get_close_action
= _get_close_action
,
712 .get_lifetime
= _get_lifetime
,
713 .get_dh_group
= _get_dh_group
,
714 .get_inactivity
= _get_inactivity
,
715 .get_reqid
= _get_reqid
,
716 .get_if_id
= _get_if_id
,
717 .get_mark
= _get_mark
,
718 .get_set_mark
= _get_set_mark
,
720 .get_manual_prio
= _get_manual_prio
,
721 .get_interface
= _get_interface
,
722 .get_replay_window
= _get_replay_window
,
723 .set_replay_window
= _set_replay_window
,
724 .has_option
= _has_option
,
728 .get_hw_offload
= _get_hw_offload
,
729 .get_copy_dscp
= _get_copy_dscp
,
731 .name
= strdup(name
),
732 .options
= data
->options
,
733 .updown
= strdupnull(data
->updown
),
734 .reqid
= data
->reqid
,
735 .if_id_in
= data
->if_id_in
,
736 .if_id_out
= data
->if_id_out
,
738 .start_action
= data
->start_action
,
739 .dpd_action
= data
->dpd_action
,
740 .close_action
= data
->close_action
,
741 .mark_in
= data
->mark_in
,
742 .mark_out
= data
->mark_out
,
743 .set_mark_in
= data
->set_mark_in
,
744 .set_mark_out
= data
->set_mark_out
,
745 .lifetime
= data
->lifetime
,
746 .inactivity
= data
->inactivity
,
748 .manual_prio
= data
->priority
,
749 .interface
= strdupnull(data
->interface
),
751 .proposals
= linked_list_create(),
752 .my_ts
= linked_list_create(),
753 .other_ts
= linked_list_create(),
754 .replay_window
= lib
->settings
->get_int(lib
->settings
,
755 "%s.replay_window", DEFAULT_REPLAY_WINDOW
, lib
->ns
),
756 .hw_offload
= data
->hw_offload
,
757 .copy_dscp
= data
->copy_dscp
,
760 return &this->public;