diff --git a/configure.ac b/configure.ac index 62f8c11e8..3659e6dca 100644 --- a/configure.ac +++ b/configure.ac @@ -1312,7 +1312,7 @@ fi AM_CONDITIONAL(RUBY_GEMS_INSTALL, [test "x$ruby_gems_install" = xtrue]) if test x$python_eggs = xtrue; then - PYTHON_PACKAGE_VERSION=`echo "$PACKAGE_VERSION" | $SED 's/dr/dev/'` + PYTHON_PACKAGE_VERSION=`echo "$PACKAGE_VERSION" | $SED 's/dr/.dev/'` AC_SUBST([PYTHON_PACKAGE_VERSION]) if test x$python_eggs_install = xtrue; then AC_PATH_PROG([EASY_INSTALL], [easy_install], [], [$PATH:/bin:/usr/bin:/usr/local/bin]) @@ -2009,6 +2009,7 @@ AC_CONFIG_FILES([ src/sw-collector/Makefile src/sec-updater/Makefile src/swanctl/Makefile + src/xfrmi/Makefile scripts/Makefile testing/Makefile ]) diff --git a/src/Makefile.am b/src/Makefile.am index 6eacbe293..b28aba75e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -143,3 +143,7 @@ endif if USE_TPM SUBDIRS += tpm_extendpcr endif + +if USE_KERNEL_NETLINK + SUBDIRS += xfrmi +endif diff --git a/src/_updown/_updown.in b/src/_updown/_updown.in index 5f709637e..d8d711051 100644 --- a/src/_updown/_updown.in +++ b/src/_updown/_updown.in @@ -119,6 +119,12 @@ # PLUTO_MARK_OUT # is an optional XFRM mark set on the outbound IPsec SA # +# PLUTO_IF_ID_IN +# is an optional XFRM interface ID set on the inbound IPsec SA +# +# PLUTO_IF_ID_OUT +# is an optional XFRM interface ID set on the outbound IPsec SA +# # PLUTO_UDP_ENC # contains the remote UDP port in the case of ESP_IN_UDP # encapsulation diff --git a/src/include/linux/xfrm.h b/src/include/linux/xfrm.h index 35261c9a5..82f394a3b 100644 --- a/src/include/linux/xfrm.h +++ b/src/include/linux/xfrm.h @@ -304,6 +304,7 @@ enum xfrm_attr_type_t { XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ XFRMA_SET_MARK, /* __u32 */ XFRMA_SET_MARK_MASK, /* __u32 */ + XFRMA_IF_ID, /* __u32 */ __XFRMA_MAX #define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c index 14148ed03..73cb02bd9 100644 --- a/src/libcharon/config/child_cfg.c +++ b/src/libcharon/config/child_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2018 Tobias Brunner + * Copyright (C) 2008-2019 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -113,6 +113,16 @@ struct private_child_cfg_t { */ uint32_t reqid; + /** + * Optionl interface ID to use for inbound CHILD_SA + */ + uint32_t if_id_in; + + /** + * Optionl interface ID to use for outbound CHILD_SA + */ + uint32_t if_id_out; + /** * Optional mark to install inbound CHILD_SA with */ @@ -551,6 +561,12 @@ METHOD(child_cfg_t, get_reqid, uint32_t, return this->reqid; } +METHOD(child_cfg_t, get_if_id, uint32_t, + private_child_cfg_t *this, bool inbound) +{ + return inbound ? this->if_id_in : this->if_id_out; +} + METHOD(child_cfg_t, get_mark, mark_t, private_child_cfg_t *this, bool inbound) { @@ -632,6 +648,8 @@ METHOD(child_cfg_t, equals, bool, LIFETIME_EQUALS(this->lifetime, other->lifetime) && this->inactivity == other->inactivity && this->reqid == other->reqid && + this->if_id_in == other->if_id_in && + this->if_id_out == other->if_id_out && this->mark_in.value == other->mark_in.value && this->mark_in.mask == other->mark_in.mask && this->mark_out.value == other->mark_out.value && @@ -695,6 +713,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data) .get_dh_group = _get_dh_group, .get_inactivity = _get_inactivity, .get_reqid = _get_reqid, + .get_if_id = _get_if_id, .get_mark = _get_mark, .get_set_mark = _get_set_mark, .get_tfc = _get_tfc, @@ -713,6 +732,8 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data) .options = data->options, .updown = strdupnull(data->updown), .reqid = data->reqid, + .if_id_in = data->if_id_in, + .if_id_out = data->if_id_out, .mode = data->mode, .start_action = data->start_action, .dpd_action = data->dpd_action, diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index e3b59e656..b80268513 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2018 Tobias Brunner + * Copyright (C) 2008-2019 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -226,6 +226,14 @@ struct child_cfg_t { */ uint32_t (*get_reqid)(child_cfg_t *this); + /** + * Optional interface ID to set on policies/SAs. + * + * @param inbound TRUE for inbound, FALSE for outbound + * @return interface ID + */ + uint32_t (*get_if_id)(child_cfg_t *this, bool inbound); + /** * Optional mark to set on policies/SAs. * @@ -350,6 +358,10 @@ struct child_cfg_create_t { child_cfg_option_t options; /** Specific reqid to use for CHILD_SA, 0 for auto assignment */ uint32_t reqid; + /** Optional inbound interface ID */ + uint32_t if_id_in; + /** Optional outbound interface ID */ + uint32_t if_id_out; /** Optional inbound mark */ mark_t mark_in; /** Optional outbound mark */ diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index e7dfb5f62..58e791f34 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2018 Tobias Brunner + * Copyright (C) 2007-2019 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -155,6 +155,16 @@ struct private_peer_cfg_t { */ linked_list_t *remote_auth; + /** + * Optionl interface ID to use for inbound CHILD_SA + */ + uint32_t if_id_in; + + /** + * Optionl interface ID to use for outbound CHILD_SA + */ + uint32_t if_id_out; + /** * PPK ID */ @@ -587,6 +597,12 @@ METHOD(peer_cfg_t, create_auth_cfg_enumerator, enumerator_t*, return this->remote_auth->create_enumerator(this->remote_auth); } +METHOD(peer_cfg_t, get_if_id, uint32_t, + private_peer_cfg_t *this, bool inbound) +{ + return inbound ? this->if_id_in : this->if_id_out; +} + METHOD(peer_cfg_t, get_ppk_id, identification_t*, private_peer_cfg_t *this) { @@ -715,6 +731,8 @@ METHOD(peer_cfg_t, equals, bool, this->aggressive == other->aggressive && this->pull_mode == other->pull_mode && auth_cfg_equal(this, other) && + this->if_id_in == other->if_id_in && + this->if_id_out == other->if_id_out && this->ppk_required == other->ppk_required && id_equal(this->ppk_id, other->ppk_id) #ifdef ME @@ -805,6 +823,7 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .create_pool_enumerator = _create_pool_enumerator, .add_auth_cfg = _add_auth_cfg, .create_auth_cfg_enumerator = _create_auth_cfg_enumerator, + .get_if_id = _get_if_id, .get_ppk_id = _get_ppk_id, .ppk_required = _ppk_required, .equals = (void*)_equals, @@ -832,6 +851,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .pull_mode = !data->push_mode, .dpd = data->dpd, .dpd_timeout = data->dpd_timeout, + .if_id_in = data->if_id_in, + .if_id_out = data->if_id_out, .ppk_id = data->ppk_id, .ppk_required = data->ppk_required, .vips = linked_list_create(), diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 49c4d1492..f4431266e 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2018 Tobias Brunner + * Copyright (C) 2007-2019 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -311,6 +311,14 @@ struct peer_cfg_t { */ enumerator_t* (*create_pool_enumerator)(peer_cfg_t *this); + /** + * Optional interface ID to set on policies/SAs. + * + * @param inbound TRUE for inbound, FALSE for outbound + * @return interface ID + */ + uint32_t (*get_if_id)(peer_cfg_t *this, bool inbound); + /** * Get the PPK ID to use with this peer. * @@ -407,6 +415,10 @@ struct peer_cfg_create_t { uint32_t dpd; /** DPD timeout interval (IKEv1 only), if 0 default applies */ uint32_t dpd_timeout; + /** Optional inbound interface ID */ + uint32_t if_id_in; + /** Optional outbound interface ID */ + uint32_t if_id_out; /** Postquantum Preshared Key ID (adopted) */ identification_t *ppk_id; /** TRUE if a PPK is required, FALSE if it's optional */ diff --git a/src/libcharon/kernel/kernel_interface.c b/src/libcharon/kernel/kernel_interface.c index 91ca259ef..e216c972c 100644 --- a/src/libcharon/kernel/kernel_interface.c +++ b/src/libcharon/kernel/kernel_interface.c @@ -199,6 +199,10 @@ typedef struct { mark_t mark_in; /** outbound mark used for SA */ mark_t mark_out; + /** inbound interface ID used for SA */ + uint32_t if_id_in; + /** outbound interface ID used for SA */ + uint32_t if_id_out; /** local traffic selectors */ array_t *local; /** remote traffic selectors */ @@ -222,7 +226,9 @@ static u_int hash_reqid(reqid_entry_t *entry) { return chunk_hash_inc(chunk_from_thing(entry->reqid), chunk_hash_inc(chunk_from_thing(entry->mark_in), - chunk_hash(chunk_from_thing(entry->mark_out)))); + chunk_hash_inc(chunk_from_thing(entry->mark_out), + chunk_hash_inc(chunk_from_thing(entry->if_id_in), + chunk_hash(chunk_from_thing(entry->if_id_out)))))); } /** @@ -234,7 +240,9 @@ static bool equals_reqid(reqid_entry_t *a, reqid_entry_t *b) a->mark_in.value == b->mark_in.value && a->mark_in.mask == b->mark_in.mask && a->mark_out.value == b->mark_out.value && - a->mark_out.mask == b->mark_out.mask; + a->mark_out.mask == b->mark_out.mask && + a->if_id_in == b->if_id_in && + a->if_id_out == b->if_id_out; } /** @@ -262,7 +270,9 @@ static u_int hash_reqid_by_ts(reqid_entry_t *entry) { return hash_ts_array(entry->local, hash_ts_array(entry->remote, chunk_hash_inc(chunk_from_thing(entry->mark_in), - chunk_hash(chunk_from_thing(entry->mark_out))))); + chunk_hash_inc(chunk_from_thing(entry->mark_out), + chunk_hash_inc(chunk_from_thing(entry->if_id_in), + chunk_hash(chunk_from_thing(entry->if_id_out))))))); } /** @@ -301,7 +311,9 @@ static bool equals_reqid_by_ts(reqid_entry_t *a, reqid_entry_t *b) a->mark_in.value == b->mark_in.value && a->mark_in.mask == b->mark_in.mask && a->mark_out.value == b->mark_out.value && - a->mark_out.mask == b->mark_out.mask; + a->mark_out.mask == b->mark_out.mask && + a->if_id_in == b->if_id_in && + a->if_id_out == b->if_id_out; } /** @@ -328,7 +340,8 @@ static array_t *array_from_ts_list(linked_list_t *list) METHOD(kernel_interface_t, alloc_reqid, status_t, private_kernel_interface_t *this, linked_list_t *local_ts, linked_list_t *remote_ts, - mark_t mark_in, mark_t mark_out, uint32_t *reqid) + mark_t mark_in, mark_t mark_out, uint32_t if_id_in, uint32_t if_id_out, + uint32_t *reqid) { static uint32_t counter = 0; reqid_entry_t *entry = NULL, *tmpl; @@ -339,6 +352,8 @@ METHOD(kernel_interface_t, alloc_reqid, status_t, .remote = array_from_ts_list(remote_ts), .mark_in = mark_in, .mark_out = mark_out, + .if_id_in = if_id_in, + .if_id_out = if_id_out, .reqid = *reqid, ); @@ -381,12 +396,14 @@ METHOD(kernel_interface_t, alloc_reqid, status_t, METHOD(kernel_interface_t, release_reqid, status_t, private_kernel_interface_t *this, uint32_t reqid, - mark_t mark_in, mark_t mark_out) + mark_t mark_in, mark_t mark_out, uint32_t if_id_in, uint32_t if_id_out) { reqid_entry_t *entry, tmpl = { .reqid = reqid, .mark_in = mark_in, .mark_out = mark_out, + .if_id_in = if_id_in, + .if_id_out = if_id_out, }; this->mutex->lock(this->mutex); diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h index f4f55adfe..b7e3686d1 100644 --- a/src/libcharon/kernel/kernel_interface.h +++ b/src/libcharon/kernel/kernel_interface.h @@ -143,13 +143,15 @@ struct kernel_interface_t { * @param remote_ts traffic selectors of remote side for SA * @param mark_in inbound mark on SA * @param mark_out outbound mark on SA + * @param if_id_in inbound interface ID on SA + * @param if_id_out outbound interface ID on SA * @param reqid allocated reqid * @return SUCCESS if reqid allocated */ status_t (*alloc_reqid)(kernel_interface_t *this, linked_list_t *local_ts, linked_list_t *remote_ts, - mark_t mark_in, mark_t mark_out, - uint32_t *reqid); + mark_t mark_in, mark_t mark_out, uint32_t if_id_in, + uint32_t if_id_out, uint32_t *reqid); /** * Release a previously allocated reqid. @@ -157,10 +159,13 @@ struct kernel_interface_t { * @param reqid reqid to release * @param mark_in inbound mark on SA * @param mark_out outbound mark on SA + * @param if_id_in inbound interface ID on SA + * @param if_id_out outbound interface ID on SA * @return SUCCESS if reqid released */ status_t (*release_reqid)(kernel_interface_t *this, uint32_t reqid, - mark_t mark_in, mark_t mark_out); + mark_t mark_in, mark_t mark_out, + uint32_t if_id_in, uint32_t if_id_out); /** * Add an SA to the SAD. diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h index 4158eb45e..70ff2eb12 100644 --- a/src/libcharon/kernel/kernel_ipsec.h +++ b/src/libcharon/kernel/kernel_ipsec.h @@ -55,6 +55,8 @@ struct kernel_ipsec_sa_id_t { uint8_t proto; /** Optional mark */ mark_t mark; + /** Optional interface ID */ + uint32_t if_id; }; /** @@ -154,6 +156,8 @@ struct kernel_ipsec_policy_id_t { traffic_selector_t *dst_ts; /** Optional mark */ mark_t mark; + /** Optional interface ID */ + uint32_t if_id; /** Network interface restricting policy */ char *interface; }; diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index ab845317f..ff75cb5c1 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -743,10 +743,11 @@ static void process_child_add(private_ha_dispatcher_t *this, return; } + child_sa_create_t data = { + .encap = ike_sa->has_condition(ike_sa, COND_NAT_ANY), + }; child_sa = child_sa_create(ike_sa->get_my_host(ike_sa), - ike_sa->get_other_host(ike_sa), config, 0, - ike_sa->has_condition(ike_sa, COND_NAT_ANY), - 0, 0); + ike_sa->get_other_host(ike_sa), config, &data); child_sa->set_mode(child_sa, mode); child_sa->set_protocol(child_sa, PROTO_ESP); child_sa->set_ipcomp(child_sa, ipcomp); diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.am b/src/libcharon/plugins/kernel_netlink/Makefile.am index 0e39c0d24..71bc09d5b 100644 --- a/src/libcharon/plugins/kernel_netlink/Makefile.am +++ b/src/libcharon/plugins/kernel_netlink/Makefile.am @@ -24,7 +24,6 @@ libstrongswan_kernel_netlink_la_LIBADD = $(DLLIB) libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version - TESTS = kernel_netlink_tests check_PROGRAMS = $(TESTS) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 2819cbe8b..27bb3792d 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2018 Tobias Brunner + * Copyright (C) 2006-2019 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2008-2016 Andreas Steffen * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser @@ -430,6 +430,9 @@ struct ipsec_sa_t { /** Optional mark */ mark_t mark; + /** Optional mark */ + uint32_t if_id; + /** Description of this SA */ ipsec_sa_cfg_t cfg; @@ -445,7 +448,8 @@ static u_int ipsec_sa_hash(ipsec_sa_t *sa) return chunk_hash_inc(sa->src->get_address(sa->src), chunk_hash_inc(sa->dst->get_address(sa->dst), chunk_hash_inc(chunk_from_thing(sa->mark), - chunk_hash(chunk_from_thing(sa->cfg))))); + chunk_hash_inc(chunk_from_thing(sa->if_id), + chunk_hash(chunk_from_thing(sa->cfg)))))); } /** @@ -457,6 +461,7 @@ static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) sa->dst->ip_equals(sa->dst, other_sa->dst) && sa->mark.value == other_sa->mark.value && sa->mark.mask == other_sa->mark.mask && + sa->if_id == other_sa->if_id && ipsec_sa_cfg_equals(&sa->cfg, &other_sa->cfg); } @@ -465,13 +470,14 @@ static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa) */ static ipsec_sa_t *ipsec_sa_create(private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst, mark_t mark, - ipsec_sa_cfg_t *cfg) + uint32_t if_id, ipsec_sa_cfg_t *cfg) { ipsec_sa_t *sa, *found; INIT(sa, .src = src, .dst = dst, .mark = mark, + .if_id = if_id, .cfg = *cfg, ); found = this->sas->get(this->sas, sa); @@ -546,7 +552,7 @@ struct policy_sa_out_t { static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this, policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst, traffic_selector_t *src_ts, traffic_selector_t *dst_ts, mark_t mark, - ipsec_sa_cfg_t *cfg) + uint32_t if_id, ipsec_sa_cfg_t *cfg) { policy_sa_t *policy; @@ -564,7 +570,7 @@ static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this, INIT(policy, .priority = 0); } policy->type = type; - policy->sa = ipsec_sa_create(this, src, dst, mark, cfg); + policy->sa = ipsec_sa_create(this, src, dst, mark, if_id, cfg); return policy; } @@ -610,6 +616,9 @@ struct policy_entry_t { /** Optional mark */ uint32_t mark; + /** Optional interface ID */ + uint32_t if_id; + /** Associated route installed for this policy */ route_entry_t *route; @@ -651,7 +660,8 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, static u_int policy_hash(policy_entry_t *key) { chunk_t chunk = chunk_from_thing(key->sel); - return chunk_hash_inc(chunk, chunk_hash(chunk_from_thing(key->mark))); + return chunk_hash_inc(chunk, chunk_hash_inc(chunk_from_thing(key->mark), + chunk_hash(chunk_from_thing(key->if_id)))); } /** @@ -661,6 +671,7 @@ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key) { return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) && key->mark == other_key->mark && + key->if_id == other_key->if_id && key->direction == other_key->direction; } @@ -1577,6 +1588,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, .spi = htonl(ntohs(data->cpi)), .proto = IPPROTO_COMP, .mark = id->mark, + .if_id = id->if_id, }; kernel_ipsec_add_sa_t ipcomp_sa = { .reqid = data->reqid, @@ -1902,6 +1914,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t, goto failed; } + if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id)) + { + goto failed; + } + if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask)) { if (!add_uint32(hdr, sizeof(request), XFRMA_SET_MARK, @@ -2034,6 +2051,10 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this, { return; } + if (sa->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, sa->if_id)) + { + return; + } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) { @@ -2132,6 +2153,10 @@ METHOD(kernel_ipsec_t, query_sa, status_t, { return FAILED; } + if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id)) + { + return FAILED; + } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) { @@ -2236,6 +2261,10 @@ METHOD(kernel_ipsec_t, del_sa, status_t, { return FAILED; } + if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id)) + { + return FAILED; + } switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr)) { @@ -2282,6 +2311,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, .spi = htonl(ntohs(data->cpi)), .proto = IPPROTO_COMP, .mark = id->mark, + .if_id = id->if_id, }; kernel_ipsec_update_sa_t ipcomp = { .new_src = data->new_src, @@ -2312,6 +2342,10 @@ METHOD(kernel_ipsec_t, update_sa, status_t, { return FAILED; } + if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id)) + { + return FAILED; + } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) { @@ -2786,6 +2820,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, policy_change_done(this, policy); return FAILED; } + if (ipsec->if_id && + !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, ipsec->if_id)) + { + policy_change_done(this, policy); + return FAILED; + } this->mutex->unlock(this->mutex); status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); @@ -2806,10 +2846,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, * - this is an outbound policy (to just get one for each child) * - routing is not disabled via strongswan.conf * - the selector is not for a specific protocol/port + * - no XFRM interface ID is configured * - we are in tunnel/BEET mode or install a bypass policy */ if (policy->direction == POLICY_OUT && this->install_routes && - !policy->sel.proto && !policy->sel.dport && !policy->sel.sport) + !policy->sel.proto && !policy->sel.dport && !policy->sel.sport && + !policy->if_id) { if (mapping->type == POLICY_PASS || (mapping->type == POLICY_IPSEC && ipsec->cfg.mode != MODE_TRANSPORT)) @@ -2837,6 +2879,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, INIT(policy, .sel = ts2selector(id->src_ts, id->dst_ts, id->interface), .mark = id->mark.value & id->mark.mask, + .if_id = id->if_id, .direction = id->dir, .reqid = data->sa->reqid, ); @@ -2882,7 +2925,8 @@ METHOD(kernel_ipsec_t, add_policy, status_t, /* cache the assigned IPsec SA */ assigned_sa = policy_sa_create(this, id->dir, data->type, data->src, - data->dst, id->src_ts, id->dst_ts, id->mark, data->sa); + data->dst, id->src_ts, id->dst_ts, id->mark, + id->if_id, data->sa); assigned_sa->auto_priority = get_priority(policy, data->prio, id->interface); assigned_sa->priority = this->get_priority ? this->get_priority(id, data) : data->manual_prio; @@ -2980,6 +3024,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t, { return FAILED; } + if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id)) + { + return FAILED; + } if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS) { @@ -3048,6 +3096,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, .src = data->src, .dst = data->dst, .mark = id->mark, + .if_id = id->if_id, .cfg = *data->sa, }; char markstr[32] = ""; @@ -3063,6 +3112,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, memset(&policy, 0, sizeof(policy_entry_t)); policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface); policy.mark = id->mark.value & id->mark.mask; + policy.if_id = id->if_id; policy.direction = id->dir; /* find the policy */ @@ -3153,6 +3203,11 @@ METHOD(kernel_ipsec_t, del_policy, status_t, policy_change_done(this, current); return FAILED; } + if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id)) + { + policy_change_done(this, current); + return FAILED; + } if (current->route) { diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index 84d78eca2..f1a9f7aaa 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -1,7 +1,8 @@ /* * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG - * Copyright (C) 2008 Tobias Brunner + * + * Copyright (C) 2008-2019 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -687,8 +688,8 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names, return &this->public; } -/** - * Described in header. +/* + * Described in header */ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, size_t buflen) @@ -705,13 +706,14 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, rta->rta_type = rta_type; rta->rta_len = RTA_LENGTH(data.len); memcpy(RTA_DATA(rta), data.ptr, data.len); - hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(rta->rta_len); } /** - * Described in header. + * Add an attribute to the given Netlink message */ -void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) +static struct rtattr *add_rtattr(struct nlmsghdr *hdr, int buflen, int type, + int len) { struct rtattr *rta; @@ -724,7 +726,44 @@ void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) rta = ((void*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len); rta->rta_type = type; rta->rta_len = RTA_LENGTH(len); - hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len; + hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(rta->rta_len); + return rta; +} +/* + * Described in header + */ +void *netlink_nested_start(struct nlmsghdr *hdr, size_t buflen, int type) +{ + return add_rtattr(hdr, buflen, type, 0); +} + +/* + * Described in header + */ +void netlink_nested_end(struct nlmsghdr *hdr, void *attr) +{ + struct rtattr *rta = attr; + void *end; + + if (attr) + { + end = (char*)hdr + NLMSG_ALIGN(hdr->nlmsg_len); + rta->rta_len = end - attr; + } +} + +/* + * Described in header + */ +void *netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) +{ + struct rtattr *rta; + + rta = add_rtattr(hdr, buflen, type, len); + if (!rta) + { + return NULL; + } return RTA_DATA(rta); } diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h index 82dce4c5c..d68a013de 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2019 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -90,6 +90,28 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names, void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, size_t buflen); +/** + * Creates an rtattr under which other rtattrs are nested to the given netlink + * message. + * + * The returned pointer has to be passed to netlink_nested_end() after the + * nested attributes have been added to the message. + * + * @param hdr netlink message + * @param buflen size of full netlink buffer + * @param type RTA type + * @return attribute pointer + */ +void *netlink_nested_start(struct nlmsghdr *hdr, size_t buflen, int type); + +/** + * Updates the length of the given attribute after nested attributes were added. + * + * @param hdr netlink message + * @param attr attribute returned from netlink_nested_start() + */ +void netlink_nested_end(struct nlmsghdr *hdr, void *attr); + /** * Reserve space in a netlink message for given size and type, returning buffer. * diff --git a/src/libcharon/plugins/updown/updown_listener.c b/src/libcharon/plugins/updown/updown_listener.c index bbefd6a02..95d5cded4 100644 --- a/src/libcharon/plugins/updown/updown_listener.c +++ b/src/libcharon/plugins/updown/updown_listener.c @@ -258,6 +258,7 @@ static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, host_t *me, *other, *host; char *iface; uint8_t mask; + uint32_t if_id; mark_t mark; bool is_host, is_ipv6; int out; @@ -356,6 +357,16 @@ static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa, push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x", mark.value, mark.mask); } + if_id = child_sa->get_if_id(child_sa, TRUE); + if (if_id) + { + push_env(envp, countof(envp), "PLUTO_IF_ID_IN=%u", if_id); + } + if_id = child_sa->get_if_id(child_sa, FALSE); + if (if_id) + { + push_env(envp, countof(envp), "PLUTO_IF_ID_OUT=%u", if_id); + } if (ike_sa->has_condition(ike_sa, COND_NAT_ANY)) { push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u", diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 5bd8c1727..61427d2b1 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -772,6 +772,8 @@ command. nat-remote = nat-fake = nat-any = + if-id-in = + if-id-out = encr-alg = encr-keysize = integ-alg = @@ -813,6 +815,8 @@ command. mark-mask-in = mark-out = mark-mask-out = + if-id-in = + if-id-out = encr-alg = encr-keysize = integ-alg = diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index f1ddde1b4..f86d5c9cd 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -327,6 +327,8 @@ typedef struct { uint64_t over_time; uint64_t rand_time; uint8_t dscp; + uint32_t if_id_in; + uint32_t if_id_out; #ifdef ME bool mediation; char *mediated_by; @@ -421,6 +423,8 @@ static void log_peer_data(peer_data_t *data) DBG2(DBG_CFG, " over_time = %llu", data->over_time); DBG2(DBG_CFG, " rand_time = %llu", data->rand_time); DBG2(DBG_CFG, " proposals = %#P", data->proposals); + DBG2(DBG_CFG, " if_id_in = %u", data->if_id_in); + DBG2(DBG_CFG, " if_id_out = %u", data->if_id_out); #ifdef ME DBG2(DBG_CFG, " mediation = %u", data->mediation); if (data->mediated_by) @@ -528,6 +532,8 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " tfc = %d", cfg->tfc); DBG2(DBG_CFG, " priority = %d", cfg->priority); DBG2(DBG_CFG, " interface = %s", cfg->interface); + DBG2(DBG_CFG, " if_id_in = %u", cfg->if_id_in); + DBG2(DBG_CFG, " if_id_out = %u", cfg->if_id_out); DBG2(DBG_CFG, " mark_in = %u/%u", cfg->mark_in.value, cfg->mark_in.mask); DBG2(DBG_CFG, " mark_in_sa = %u", has_opt(OPT_MARK_IN_SA)); @@ -1220,6 +1226,21 @@ CALLBACK(parse_set_mark, bool, return mark_from_string(buf, MARK_OP_SAME, out); } +/** + * Parse interface ID + */ +CALLBACK(parse_if_id, bool, + uint32_t *out, chunk_t v) +{ + char buf[32]; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + return if_id_from_string(buf, out); +} + /** * Parse TFC padding option */ @@ -1688,6 +1709,8 @@ CALLBACK(child_kv, bool, { "copy_df", parse_opt_copy_df, &child->cfg.options }, { "copy_ecn", parse_opt_copy_ecn, &child->cfg.options }, { "copy_dscp", parse_copy_dscp, &child->cfg.copy_dscp }, + { "if_id_in", parse_if_id, &child->cfg.if_id_in }, + { "if_id_out", parse_if_id, &child->cfg.if_id_out }, }; return parse_rules(rules, countof(rules), name, value, @@ -1766,6 +1789,8 @@ CALLBACK(peer_kv, bool, { "rand_time", parse_time, &peer->rand_time }, { "ppk_id", parse_peer_id, &peer->ppk_id }, { "ppk_required", parse_bool, &peer->ppk_required }, + { "if_id_in", parse_if_id, &peer->if_id_in }, + { "if_id_out", parse_if_id, &peer->if_id_out }, #ifdef ME { "mediation", parse_bool, &peer->mediation }, { "mediated_by", parse_string, &peer->mediated_by }, @@ -2504,6 +2529,8 @@ CALLBACK(config_sn, bool, .dpd_timeout = peer.dpd_timeout, .ppk_id = peer.ppk_id ? peer.ppk_id->clone(peer.ppk_id) : NULL, .ppk_required = peer.ppk_required, + .if_id_in = peer.if_id_in, + .if_id_out = peer.if_id_out, }; #ifdef ME cfg.mediation = peer.mediation; diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index d7b61ca72..16e3c8b1f 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -155,6 +155,7 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, { time_t t; uint64_t bytes, packets; + uint32_t if_id; uint16_t alg, ks; proposal_t *proposal; enumerator_t *enumerator; @@ -185,6 +186,16 @@ static void list_child(private_vici_query_t *this, vici_builder_t *b, } add_mark(b, child->get_mark(child, TRUE), "mark-in", "mark-mask-in"); add_mark(b, child->get_mark(child, FALSE), "mark-out", "mark-mask-out"); + if_id = child->get_if_id(child, TRUE); + if (if_id) + { + b->add_kv(b, "if-id-in", "%.8x", if_id); + } + if_id = child->get_if_id(child, FALSE); + if (if_id) + { + b->add_kv(b, "if-id-out", "%.8x", if_id); + } proposal = child->get_proposal(child); if (proposal) { @@ -343,6 +354,7 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b, ike_sa_id_t *id; identification_t *eap; proposal_t *proposal; + uint32_t if_id; uint16_t alg, ks; host_t *host; @@ -389,6 +401,17 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b, add_condition(b, ike_sa, "nat-fake", COND_NAT_FAKE); add_condition(b, ike_sa, "nat-any", COND_NAT_ANY); + if_id = ike_sa->get_if_id(ike_sa, TRUE); + if (if_id) + { + b->add_kv(b, "if-id-in", "%.8x", if_id); + } + if_id = ike_sa->get_if_id(ike_sa, FALSE); + if (if_id) + { + b->add_kv(b, "if-id-out", "%.8x", if_id); + } + proposal = ike_sa->get_proposal(ike_sa); if (proposal) { diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index a341f29bd..fc60b413f 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2018 Tobias Brunner + * Copyright (C) 2006-2019 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger @@ -153,6 +153,16 @@ struct private_child_sa_t { */ bool policies_fwd_out; + /** + * Inbound interface ID + */ + uint32_t if_id_in; + + /** + * Outbound interface ID + */ + uint32_t if_id_out; + /** * inbound mark used for this child_sa */ @@ -539,6 +549,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) .spi = this->my_spi, .proto = proto_ike2ip(this->protocol), .mark = mark_in_sa(this), + .if_id = this->if_id_in, }; kernel_ipsec_query_sa_t query = {}; @@ -572,6 +583,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) .spi = this->other_spi, .proto = proto_ike2ip(this->protocol), .mark = this->mark_out, + .if_id = this->if_id_out, }; kernel_ipsec_query_sa_t query = {}; @@ -619,6 +631,7 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) .src_ts = other_ts, .dst_ts = my_ts, .mark = this->mark_in, + .if_id = this->if_id_in, }; kernel_ipsec_query_policy_t query = {}; @@ -644,6 +657,7 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) .src_ts = my_ts, .dst_ts = other_ts, .mark = this->mark_out, + .if_id = this->if_id_out, .interface = this->config->get_interface(this->config), }; kernel_ipsec_query_policy_t query = {}; @@ -707,11 +721,13 @@ METHOD(child_sa_t, get_usestats, void, METHOD(child_sa_t, get_mark, mark_t, private_child_sa_t *this, bool inbound) { - if (inbound) - { - return this->mark_in; - } - return this->mark_out; + return inbound ? this->mark_in : this->mark_out; +} + +METHOD(child_sa_t, get_if_id, uint32_t, + private_child_sa_t *this, bool inbound) +{ + return inbound ? this->if_id_in : this->if_id_out; } METHOD(child_sa_t, get_lifetime, time_t, @@ -832,7 +848,8 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, if (!this->reqid_allocated && !this->static_reqid) { status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts, - this->mark_in, this->mark_out, &this->reqid); + this->mark_in, this->mark_out, this->if_id_in, + this->if_id_out, &this->reqid); if (status != SUCCESS) { my_ts->destroy(my_ts); @@ -872,6 +889,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, .spi = spi, .proto = proto_ike2ip(this->protocol), .mark = inbound ? mark_in_sa(this) : this->mark_out, + .if_id = inbound ? this->if_id_in : this->if_id_out, }; sa = (kernel_ipsec_add_sa_t){ .reqid = this->reqid, @@ -991,6 +1009,7 @@ static status_t install_policies_inbound(private_child_sa_t *this, .src_ts = other_ts, .dst_ts = my_ts, .mark = this->mark_in, + .if_id = this->if_id_in, }; kernel_ipsec_manage_policy_t in_policy = { .type = type, @@ -1025,6 +1044,7 @@ static status_t install_policies_outbound(private_child_sa_t *this, .src_ts = my_ts, .dst_ts = other_ts, .mark = this->mark_out, + .if_id = this->if_id_out, .interface = this->config->get_interface(this->config), }; kernel_ipsec_manage_policy_t out_policy = { @@ -1098,6 +1118,7 @@ static void del_policies_inbound(private_child_sa_t *this, .src_ts = other_ts, .dst_ts = my_ts, .mark = this->mark_in, + .if_id = this->if_id_in, }; kernel_ipsec_manage_policy_t in_policy = { .type = type, @@ -1131,6 +1152,7 @@ static void del_policies_outbound(private_child_sa_t *this, .src_ts = my_ts, .dst_ts = other_ts, .mark = this->mark_out, + .if_id = this->if_id_out, .interface = this->config->get_interface(this->config), }; kernel_ipsec_manage_policy_t out_policy = { @@ -1228,7 +1250,8 @@ METHOD(child_sa_t, install_policies, status_t, array_create_enumerator(this->other_ts)); status = charon->kernel->alloc_reqid( charon->kernel, my_ts_list, other_ts_list, - this->mark_in, this->mark_out, &this->reqid); + this->mark_in, this->mark_out, this->if_id_in, + this->if_id_out, &this->reqid); my_ts_list->destroy(my_ts_list); other_ts_list->destroy(other_ts_list); if (status != SUCCESS) @@ -1403,6 +1426,7 @@ METHOD(child_sa_t, remove_outbound, void, .spi = this->other_spi, .proto = proto_ike2ip(this->protocol), .mark = this->mark_out, + .if_id = this->if_id_out, }; kernel_ipsec_del_sa_t sa = { .cpi = this->other_cpi, @@ -1453,6 +1477,7 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other, .spi = this->my_spi, .proto = proto_ike2ip(this->protocol), .mark = mark_in_sa(this), + .if_id = this->if_id_in, }; kernel_ipsec_update_sa_t sa = { .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, @@ -1477,6 +1502,7 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other, .spi = this->other_spi, .proto = proto_ike2ip(this->protocol), .mark = this->mark_out, + .if_id = this->if_id_out, }; kernel_ipsec_update_sa_t sa = { .cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, @@ -1679,6 +1705,7 @@ METHOD(child_sa_t, destroy, void, .spi = this->my_spi, .proto = proto_ike2ip(this->protocol), .mark = mark_in_sa(this), + .if_id = this->if_id_in, }; kernel_ipsec_del_sa_t sa = { .cpi = this->my_cpi, @@ -1693,6 +1720,7 @@ METHOD(child_sa_t, destroy, void, .spi = this->other_spi, .proto = proto_ike2ip(this->protocol), .mark = this->mark_out, + .if_id = this->if_id_out, }; kernel_ipsec_del_sa_t sa = { .cpi = this->other_cpi, @@ -1703,7 +1731,8 @@ METHOD(child_sa_t, destroy, void, if (this->reqid_allocated) { if (charon->kernel->release_reqid(charon->kernel, - this->reqid, this->mark_in, this->mark_out) != SUCCESS) + this->reqid, this->mark_in, this->mark_out, + this->if_id_in, this->if_id_out) != SUCCESS) { DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); } @@ -1755,16 +1784,14 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) return host; } -/** - * Described in header. +/* + * Described in header */ -child_sa_t * child_sa_create(host_t *me, host_t* other, - child_cfg_t *config, uint32_t reqid, bool encap, - u_int mark_in, u_int mark_out) +child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config, + child_sa_create_t *data) { private_child_sa_t *this; static refcount_t unique_id = 0, unique_mark = 0; - refcount_t mark = 0; INIT(this, .public = { @@ -1787,6 +1814,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .get_installtime = _get_installtime, .get_usestats = _get_usestats, .get_mark = _get_mark, + .get_if_id = _get_if_id, .has_encap = _has_encap, .get_ipcomp = _get_ipcomp, .set_ipcomp = _set_ipcomp, @@ -1809,7 +1837,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .create_policy_enumerator = _create_policy_enumerator, .destroy = _destroy, }, - .encap = encap, + .encap = data->encap, .ipcomp = IPCOMP_NONE, .state = CHILD_CREATED, .my_ts = array_create(0, 0), @@ -1822,6 +1850,8 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .unique_id = ref_get(&unique_id), .mark_in = config->get_mark(config, TRUE), .mark_out = config->get_mark(config, FALSE), + .if_id_in = config->get_if_id(config, TRUE) ?: data->if_id_in_def, + .if_id_out = config->get_if_id(config, FALSE) ?: data->if_id_out_def, .install_time = time_monotonic(NULL), .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES), ); @@ -1829,22 +1859,31 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->config = config; config->get_ref(config); - if (mark_in) + if (data->mark_in) { - this->mark_in.value = mark_in; + this->mark_in.value = data->mark_in; } - if (mark_out) + if (data->mark_out) { - this->mark_out.value = mark_out; + this->mark_out.value = data->mark_out; } + if (data->if_id_in) + { + this->if_id_in = data->if_id_in; + } + if (data->if_id_out) + { + this->if_id_out = data->if_id_out; + } + + allocate_unique_if_ids(&this->if_id_in, &this->if_id_out); if (MARK_IS_UNIQUE(this->mark_in.value) || MARK_IS_UNIQUE(this->mark_out.value)) { - bool unique_dir; - - unique_dir = this->mark_in.value == MARK_UNIQUE_DIR || - this->mark_out.value == MARK_UNIQUE_DIR; + refcount_t mark = 0; + bool unique_dir = this->mark_in.value == MARK_UNIQUE_DIR || + this->mark_out.value == MARK_UNIQUE_DIR; if (!unique_dir) { @@ -1852,19 +1891,11 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, } if (MARK_IS_UNIQUE(this->mark_in.value)) { - if (unique_dir) - { - mark = ref_get(&unique_mark); - } - this->mark_in.value = mark; + this->mark_in.value = unique_dir ? ref_get(&unique_mark) : mark; } if (MARK_IS_UNIQUE(this->mark_out.value)) { - if (unique_dir) - { - mark = ref_get(&unique_mark); - } - this->mark_out.value = mark; + this->mark_out.value = unique_dir ? ref_get(&unique_mark) : mark; } } @@ -1878,7 +1909,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, * replace the temporary SA on the kernel level. Rekeying such an SA * requires an explicit reqid, as the cache currently knows the original * selectors only for that reqid. */ - this->reqid = reqid; + this->reqid = data->reqid; } else { diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index 183033f46..c9b3f63e2 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2017 Tobias Brunner + * Copyright (C) 2006-2019 Tobias Brunner * Copyright (C) 2006-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * HSR Hochschule fuer Technik Rapperswil @@ -26,6 +26,7 @@ typedef enum child_sa_state_t child_sa_state_t; typedef enum child_sa_outbound_state_t child_sa_outbound_state_t; typedef struct child_sa_t child_sa_t; +typedef struct child_sa_create_t child_sa_create_t; #include #include @@ -360,6 +361,14 @@ struct child_sa_t { */ mark_t (*get_mark)(child_sa_t *this, bool inbound); + /** + * Get the interface ID used with this CHILD_SA. + * + * @param inbound TRUE to get inbound ID, FALSE for outbound + * @return interface ID used with this CHILD_SA + */ + uint32_t (*get_if_id)(child_sa_t *this, bool inbound); + /** * Create an enumerator over traffic selectors of one side. * @@ -504,20 +513,40 @@ struct child_sa_t { void (*destroy) (child_sa_t *this); }; +/** + * Data passed to the constructor of a child_sa_t object. + */ +struct child_sa_create_t { + /** Optional reqid of old CHILD_SA when rekeying */ + uint32_t reqid; + /** Optional inbound mark when rekeying */ + uint32_t mark_in; + /** Optional outbound mark when rekeying */ + uint32_t mark_out; + /** Optional inbound interface ID when rekeying */ + uint32_t if_id_in; + /** Optional outbound interface ID when rekeying */ + uint32_t if_id_out; + /** Optional default inbound interface ID, if neither if_id_in, nor config + * sets one */ + uint32_t if_id_in_def; + /** Optional default outbound interface ID, if neither if_id_out, nor config + * sets one */ + uint32_t if_id_out_def; + /** TRUE to enable UDP encapsulation (NAT traversal) */ + bool encap; +}; + /** * Constructor to create a child SA negotiated with IKE. * * @param me own address * @param other remote address * @param config config to use for this CHILD_SA - * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise - * @param encap TRUE to enable UDP encapsulation (NAT traversal) - * @param mark_in explicit inbound mark value to use, 0 for config - * @param mark_out explicit outbound mark value to use, 0 for config + * @param data data for this CHILD_SA * @return child_sa_t object */ -child_sa_t * child_sa_create(host_t *me, host_t *other, child_cfg_t *config, - uint32_t reqid, bool encap, - u_int mark_in, u_int mark_out); +child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config, + child_sa_create_t *data); #endif /** CHILD_SA_H_ @}*/ diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 3d576a0e8..e75aa755c 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2018 Tobias Brunner + * Copyright (C) 2006-2019 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -296,6 +296,16 @@ struct private_ike_sa_t { * Timestamps of redirect attempts to handle loops */ array_t *redirected_at; + + /** + * Inbound interface ID + */ + uint32_t if_id_in; + + /** + * Outbound interface ID + */ + uint32_t if_id_out; }; /** @@ -419,11 +429,15 @@ METHOD(ike_sa_t, set_peer_cfg, void, DESTROY_IF(this->peer_cfg); this->peer_cfg = peer_cfg; - if (this->ike_cfg == NULL) + if (!this->ike_cfg) { this->ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); this->ike_cfg->get_ref(this->ike_cfg); } + + this->if_id_in = peer_cfg->get_if_id(peer_cfg, TRUE); + this->if_id_out = peer_cfg->get_if_id(peer_cfg, FALSE); + allocate_unique_if_ids(&this->if_id_in, &this->if_id_out); } METHOD(ike_sa_t, get_auth_cfg, auth_cfg_t*, @@ -1656,6 +1670,12 @@ METHOD(ike_sa_t, set_other_id, void, this->other_id = other; } +METHOD(ike_sa_t, get_if_id, uint32_t, + private_ike_sa_t *this, bool inbound) +{ + return inbound ? this->if_id_in : this->if_id_out; +} + METHOD(ike_sa_t, add_child_sa, void, private_ike_sa_t *this, child_sa_t *child_sa) { @@ -2810,6 +2830,8 @@ METHOD(ike_sa_t, inherit_post, void, this->other_host = other->other_host->clone(other->other_host); this->my_id = other->my_id->clone(other->my_id); this->other_id = other->other_id->clone(other->other_id); + this->if_id_in = other->if_id_in; + this->if_id_out = other->if_id_out; /* apply assigned virtual IPs... */ while (array_remove(other->my_vips, ARRAY_HEAD, &vip)) @@ -3087,6 +3109,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .create_virtual_ip_enumerator = _create_virtual_ip_enumerator, .add_configuration_attribute = _add_configuration_attribute, .create_attribute_enumerator = _create_attribute_enumerator, + .get_if_id = _get_if_id, .set_kmaddress = _set_kmaddress, .create_task_enumerator = _create_task_enumerator, .remove_task = _remove_task, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index be480eac8..c7ef1fe3c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2018 Tobias Brunner + * Copyright (C) 2006-2019 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -1068,6 +1068,14 @@ struct ike_sa_t { */ void (*clear_virtual_ips) (ike_sa_t *this, bool local); + /** + * Get interface ID to use as default for children of this IKE_SA. + * + * @param inbound TRUE for inbound interface ID + * @return interface ID + */ + uint32_t (*get_if_id)(ike_sa_t *this, bool inbound); + /** * Create an enumerator over virtual IPs. * diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index ca04a6a16..b4944cfcb 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1843,6 +1843,8 @@ METHOD(task_manager_t, queue_child_rekey, void, task->use_reqid(task, child_sa->get_reqid(child_sa)); task->use_marks(task, child_sa->get_mark(child_sa, TRUE).value, child_sa->get_mark(child_sa, FALSE).value); + task->use_if_ids(task, child_sa->get_if_id(child_sa, TRUE), + child_sa->get_if_id(child_sa, FALSE)); task->rekey(task, child_sa->get_spi(child_sa, TRUE)); queue_task(this, &task->task); diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index b0a42b8bd..3309a5ddc 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Tobias Brunner + * Copyright (C) 2012-2019 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2011 Martin Willi @@ -151,19 +151,9 @@ struct private_quick_mode_t { uint64_t lifebytes; /** - * Reqid to use, 0 for auto-allocate + * Data collected to create the CHILD_SA */ - uint32_t reqid; - - /** - * Explicit inbound mark value to use, if any - */ - u_int mark_in; - - /** - * Explicit inbound mark value to use, if any - */ - u_int mark_out; + child_sa_create_t child; /** * SPI of SA we rekey @@ -185,11 +175,6 @@ struct private_quick_mode_t { */ protocol_id_t proto; - /** - * Use UDP encapsulation - */ - bool udp; - /** * Message ID of handled quick mode exchange */ @@ -627,7 +612,7 @@ static bool get_ts(private_quick_mode_t *this, message_t *message) tsr = traffic_selector_create_from_subnet(hsr->clone(hsr), hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0, 65535); } - if (this->mode == MODE_TRANSPORT && this->udp && + if (this->mode == MODE_TRANSPORT && this->child.encap && (!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr))) { /* change TS in case of a NAT in transport mode */ DBG2(DBG_IKE, "changing received traffic selectors %R=== %R due to NAT", @@ -839,15 +824,19 @@ METHOD(task_t, build_i, status_t, diffie_hellman_group_t group; encap_t encap; - this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); this->mode = this->config->get_mode(this->config); + this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, + TRUE); + this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, + FALSE); + this->child.encap = this->ike_sa->has_condition(this->ike_sa, + COND_NAT_ANY); this->child_sa = child_sa_create( this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), - this->config, this->reqid, this->udp, - this->mark_in, this->mark_out); + this->config, &this->child); - if (this->udp && this->mode == MODE_TRANSPORT) + if (this->child.encap && this->mode == MODE_TRANSPORT) { /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */ add_nat_oa_payloads(this, message); @@ -914,7 +903,7 @@ METHOD(task_t, build_i, status_t, } get_lifetimes(this); - encap = get_encap(this->ike_sa, this->udp); + encap = get_encap(this->ike_sa, this->child.encap); sa_payload = sa_payload_create_from_proposals_v1(list, this->lifetime, this->lifebytes, AUTH_NONE, this->mode, encap, this->cpi_i); @@ -1026,7 +1015,7 @@ static void check_for_rekeyed_child(private_quick_mode_t *this, bool responder) name = this->config->get_name(this->config); enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); - while (this->reqid == 0 && enumerator->enumerate(enumerator, &child_sa)) + while (!this->child.reqid && enumerator->enumerate(enumerator, &child_sa)) { if (streq(child_sa->get_name(child_sa), name)) { @@ -1041,12 +1030,16 @@ static void check_for_rekeyed_child(private_quick_mode_t *this, bool responder) remote->equals(remote, other_ts) && this->proposal->equals(this->proposal, proposal)) { - this->reqid = child_sa->get_reqid(child_sa); this->rekey = child_sa->get_spi(child_sa, TRUE); - this->mark_in = child_sa->get_mark(child_sa, - TRUE).value; - this->mark_out = child_sa->get_mark(child_sa, - FALSE).value; + this->child.reqid = child_sa->get_reqid(child_sa); + this->child.mark_in = child_sa->get_mark(child_sa, + TRUE).value; + this->child.mark_out = child_sa->get_mark(child_sa, + FALSE).value; + this->child.if_id_in = child_sa->get_if_id(child_sa, + TRUE); + this->child.if_id_out = child_sa->get_if_id(child_sa, + FALSE); child_sa->set_state(child_sa, CHILD_REKEYING); DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}", child_sa->get_name(child_sa), @@ -1089,7 +1082,8 @@ METHOD(task_t, process_r, status_t, return send_notify(this, INVALID_PAYLOAD_TYPE); } - this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp); + this->mode = sa_payload->get_encap_mode(sa_payload, + &this->child.encap); if (!get_ts(this, message)) { @@ -1180,12 +1174,14 @@ METHOD(task_t, process_r, status_t, } check_for_rekeyed_child(this, TRUE); - + this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, + TRUE); + this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, + FALSE); this->child_sa = child_sa_create( this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), - this->config, this->reqid, this->udp, - this->mark_in, this->mark_out); + this->config, &this->child); tsi = linked_list_create_with_items(this->tsi, NULL); tsr = linked_list_create_with_items(this->tsr, NULL); @@ -1277,13 +1273,13 @@ METHOD(task_t, build_r, status_t, } } - if (this->udp && this->mode == MODE_TRANSPORT) + if (this->child.encap && this->mode == MODE_TRANSPORT) { /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */ add_nat_oa_payloads(this, message); } - encap = get_encap(this->ike_sa, this->udp); + encap = get_encap(this->ike_sa, this->child.encap); sa_payload = sa_payload_create_from_proposal_v1(this->proposal, this->lifetime, this->lifebytes, AUTH_NONE, this->mode, encap, this->cpi_r); @@ -1408,14 +1404,21 @@ METHOD(quick_mode_t, get_mid, uint32_t, METHOD(quick_mode_t, use_reqid, void, private_quick_mode_t *this, uint32_t reqid) { - this->reqid = reqid; + this->child.reqid = reqid; } METHOD(quick_mode_t, use_marks, void, - private_quick_mode_t *this, u_int in, u_int out) + private_quick_mode_t *this, uint32_t in, uint32_t out) { - this->mark_in = in; - this->mark_out = out; + this->child.mark_in = in; + this->child.mark_out = out; +} + +METHOD(quick_mode_t, use_if_ids, void, + private_quick_mode_t *this, uint32_t in, uint32_t out) +{ + this->child.if_id_in = in; + this->child.if_id_out = out; } METHOD(quick_mode_t, rekey, void, @@ -1446,8 +1449,7 @@ METHOD(task_t, migrate, void, this->dh = NULL; this->spi_i = 0; this->spi_r = 0; - this->mark_in = 0; - this->mark_out = 0; + this->child = (child_sa_create_t){}; if (!this->initiator) { @@ -1488,6 +1490,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, .get_mid = _get_mid, .use_reqid = _use_reqid, .use_marks = _use_marks, + .use_if_ids = _use_if_ids, .rekey = _rekey, }, .ike_sa = ike_sa, diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h index 0d4c5b47c..50025440e 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.h +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Tobias Brunner + * Copyright (C) 2015-2019 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2011 Martin Willi @@ -61,7 +61,15 @@ struct quick_mode_t { * @param in inbound mark value * @param out outbound mark value */ - void (*use_marks)(quick_mode_t *this, u_int in, u_int out); + void (*use_marks)(quick_mode_t *this, uint32_t in, uint32_t out); + + /** + * Use specific interface IDs, overriding configuration. + * + * @param in inbound interface ID + * @param out outbound interface ID + */ + void (*use_if_ids)(quick_mode_t *this, uint32_t in, uint32_t out); /** * Set the SPI of the old SA, if rekeying. diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index e9142d79b..ee3422c06 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1874,6 +1874,8 @@ static void trigger_mbb_reauth(private_task_manager_t *this) child_create->use_marks(child_create, child_sa->get_mark(child_sa, TRUE).value, child_sa->get_mark(child_sa, FALSE).value); + /* interface IDs are not migrated as the new CHILD_SAs on old and new + * IKE_SA go though regular updown events */ new->queue_task(new, &child_create->task); children = TRUE; } diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index ce61ef988..d74013b8f 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2018 Tobias Brunner + * Copyright (C) 2008-2019 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -169,19 +169,9 @@ struct private_child_create_t { uint16_t other_cpi; /** - * reqid to use if we are rekeying + * Data collected to create the CHILD_SA */ - uint32_t reqid; - - /** - * Explicit inbound mark value - */ - u_int mark_in; - - /** - * Explicit outbound mark value - */ - u_int mark_out; + child_sa_create_t child; /** * CHILD_SA which gets established @@ -217,7 +207,10 @@ static void schedule_delayed_retry(private_child_create_t *this) task = child_create_create(this->ike_sa, this->config->get_ref(this->config), FALSE, this->packet_tsi, this->packet_tsr); - task->use_reqid(task, this->reqid); + task->use_reqid(task, this->child.reqid); + task->use_marks(task, this->child.mark_in, this->child.mark_out); + task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out); + DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds", retry); this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry); @@ -1107,16 +1100,18 @@ METHOD(task_t, build_i, status_t, this->dh_group == MODP_NONE); this->mode = this->config->get_mode(this->config); + this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE); + this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE); + this->child.encap = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY), - this->mark_in, this->mark_out); + this->ike_sa->get_other_host(this->ike_sa), + this->config, &this->child); - if (this->reqid) + if (this->child.reqid) { DBG0(DBG_IKE, "establishing CHILD_SA %s{%d} reqid %d", this->child_sa->get_name(this->child_sa), - this->child_sa->get_unique_id(this->child_sa), this->reqid); + this->child_sa->get_unique_id(this->child_sa), this->child.reqid); } else { @@ -1392,10 +1387,12 @@ METHOD(task_t, build_r, status_t, } enumerator->destroy(enumerator); + this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE); + this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE); + this->child.encap = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY), - this->mark_in, this->mark_out); + this->ike_sa->get_other_host(this->ike_sa), + this->config, &this->child); if (this->ipcomp_received != IPCOMP_NONE) { @@ -1660,14 +1657,21 @@ METHOD(task_t, process_i, status_t, METHOD(child_create_t, use_reqid, void, private_child_create_t *this, uint32_t reqid) { - this->reqid = reqid; + this->child.reqid = reqid; } METHOD(child_create_t, use_marks, void, - private_child_create_t *this, u_int in, u_int out) + private_child_create_t *this, uint32_t in, uint32_t out) { - this->mark_in = in; - this->mark_out = out; + this->child.mark_in = in; + this->child.mark_out = out; +} + +METHOD(child_create_t, use_if_ids, void, + private_child_create_t *this, uint32_t in, uint32_t out) +{ + this->child.if_id_in = in; + this->child.if_id_out = out; } METHOD(child_create_t, use_dh_group, void, @@ -1745,10 +1749,8 @@ METHOD(task_t, migrate, void, this->ipcomp = IPCOMP_NONE; this->ipcomp_received = IPCOMP_NONE; this->other_cpi = 0; - this->reqid = 0; - this->mark_in = 0; - this->mark_out = 0; this->established = FALSE; + this->child = (child_sa_create_t){}; } METHOD(task_t, destroy, void, @@ -1797,6 +1799,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, .get_lower_nonce = _get_lower_nonce, .use_reqid = _use_reqid, .use_marks = _use_marks, + .use_if_ids = _use_if_ids, .use_dh_group = _use_dh_group, .task = { .get_type = _get_type, diff --git a/src/libcharon/sa/ikev2/tasks/child_create.h b/src/libcharon/sa/ikev2/tasks/child_create.h index 59fc6d2d9..eae1f3532 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.h +++ b/src/libcharon/sa/ikev2/tasks/child_create.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Tobias Brunner + * Copyright (C) 2018-2019 Tobias Brunner * Copyright (C) 2007 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -58,7 +58,15 @@ struct child_create_t { * @param in inbound mark value * @param out outbound mark value */ - void (*use_marks)(child_create_t *this, u_int in, u_int out); + void (*use_marks)(child_create_t *this, uint32_t in, uint32_t out); + + /** + * Use specific interface IDs, overriding configuration. + * + * @param in inbound interface ID + * @param out outbound interface ID + */ + void (*use_if_ids)(child_create_t *this, uint32_t in, uint32_t out); /** * Initially propose a specific DH group to override configuration. diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index d5188c0bc..6b0f42785 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -207,6 +207,9 @@ METHOD(task_t, build_i, status_t, this->child_create->use_marks(this->child_create, this->child_sa->get_mark(this->child_sa, TRUE).value, this->child_sa->get_mark(this->child_sa, FALSE).value); + this->child_create->use_if_ids(this->child_create, + this->child_sa->get_if_id(this->child_sa, TRUE), + this->child_sa->get_if_id(this->child_sa, FALSE)); if (this->child_create->task.build(&this->child_create->task, message) != NEED_MORE) @@ -266,6 +269,9 @@ METHOD(task_t, build_r, status_t, this->child_create->use_marks(this->child_create, this->child_sa->get_mark(this->child_sa, TRUE).value, this->child_sa->get_mark(this->child_sa, FALSE).value); + this->child_create->use_if_ids(this->child_create, + this->child_sa->get_if_id(this->child_sa, TRUE), + this->child_sa->get_if_id(this->child_sa, FALSE)); config = this->child_sa->get_config(this->child_sa); this->child_create->set_config(this->child_create, config->get_ref(config)); this->child_create->task.build(&this->child_create->task, message); diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index c35b8c0de..2bc531b38 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -293,7 +293,14 @@ METHOD(trap_manager_t, install, bool, this->lock->unlock(this->lock); /* create and route CHILD_SA */ - child_sa = child_sa_create(me, other, child, 0, FALSE, 0, 0); + child_sa_create_t child_data = { + /* TODO: no reason to allocate unique interface IDs, there is currently + * no event to use them upon trap installation and we'd also have to + * pass them in a later initiate() call */ + .if_id_in_def = peer->get_if_id(peer, TRUE), + .if_id_out_def = peer->get_if_id(peer, FALSE), + }; + child_sa = child_sa_create(me, other, child, &child_data); list = linked_list_create_with_items(me, NULL); my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index 6f19cc751..2f0f31abd 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -147,3 +147,72 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark) } return TRUE; } + +/* + * Described in header + */ +bool if_id_from_string(const char *value, uint32_t *if_id) +{ + char *endptr; + + if (!value) + { + return FALSE; + } + if (strcasepfx(value, "%unique")) + { + endptr = (char*)value + strlen("%unique"); + if (strcasepfx(endptr, "-dir")) + { + *if_id = IF_ID_UNIQUE_DIR; + endptr += strlen("-dir"); + } + else if (!*endptr) + { + *if_id = IF_ID_UNIQUE; + } + else + { + DBG1(DBG_APP, "invalid interface ID: %s", value); + return FALSE; + } + } + else + { + *if_id = strtoul(value, &endptr, 0); + } + if (*endptr) + { + DBG1(DBG_APP, "invalid interface ID: %s", value); + return FALSE; + } + return TRUE; +} + +/* + * Described in header + */ +void allocate_unique_if_ids(uint32_t *in, uint32_t *out) +{ + static refcount_t unique_if_id = 0; + + if (IF_ID_IS_UNIQUE(*in) || IF_ID_IS_UNIQUE(*out)) + { + refcount_t if_id = 0; + bool unique_dir = *in == IF_ID_UNIQUE_DIR || + *out == IF_ID_UNIQUE_DIR; + + if (!unique_dir) + { + if_id = ref_get(&unique_if_id); + } + if (IF_ID_IS_UNIQUE(*in)) + { + *in = unique_dir ? ref_get(&unique_if_id) : if_id; + } + if (IF_ID_IS_UNIQUE(*out)) + { + *out = unique_dir ? ref_get(&unique_if_id) : if_id; + } + } +} diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h index 7b7bd3743..1c61fecfe 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -240,4 +240,28 @@ enum mark_op_t { */ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark); +/** + * Special interface ID values to allocate a unique ID for each CHILD_SA/dir + */ +#define IF_ID_UNIQUE (0xFFFFFFFF) +#define IF_ID_UNIQUE_DIR (0xFFFFFFFE) +#define IF_ID_IS_UNIQUE(m) ((m) == IF_ID_UNIQUE || (m) == IF_ID_UNIQUE_DIR) + +/** + * Try to parse an interface ID from the given string. + * + * @param value string to parse + * @param if_id interface ID to fill + * @return TRUE if parsing was successful + */ +bool if_id_from_string(const char *value, uint32_t *if_id); + +/** + * Allocate up to two unique interface IDs depending on the given values. + * + * @param[out] in inbound interface ID + * @param[out] out outbound interface ID + */ +void allocate_unique_if_ids(uint32_t *in, uint32_t *out); + #endif /** IPSEC_TYPES_H_ @}*/ diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index f1d46ee6b..27343349e 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -940,6 +940,84 @@ START_TEST(test_mark_from_string) } END_TEST +/******************************************************************************* + * if_id_from_string + */ + +static struct { + char *s; + bool ok; + uint32_t i; +} if_id_data[] = { + {NULL, FALSE, 0 }, + {"", TRUE, 0 }, + {"/", FALSE, 0 }, + {"42", TRUE, 42 }, + {"0x42", TRUE, 0x42 }, + {"x", FALSE, 0 }, + {"42/", FALSE, 0 }, + {"42/0", FALSE, 0 }, + {"%unique", TRUE, IF_ID_UNIQUE }, + {"%unique/", FALSE, 0}, + {"%unique0xffffffffff", FALSE, 0}, + {"0xffffffff", TRUE, IF_ID_UNIQUE}, + {"%unique-dir", TRUE, IF_ID_UNIQUE_DIR}, + {"%unique-dir/",FALSE, 0}, + {"0xfffffffe", TRUE, IF_ID_UNIQUE_DIR}, + {"%unique-", FALSE, 0}, + {"%unique-foo", FALSE, 0}, +}; + +START_TEST(test_if_id_from_string) +{ + uint32_t if_id; + + if (if_id_from_string(if_id_data[_i].s, &if_id)) + { + ck_assert_int_eq(if_id, if_id_data[_i].i); + } + else + { + ck_assert(!if_id_data[_i].ok); + } +} +END_TEST + +/******************************************************************************* + * allocate_unique_if_ids + */ + +static struct { + uint32_t in; + uint32_t out; + uint32_t exp_in; + uint32_t exp_out; +} unique_if_id_data[] = { + {0, 0, 0, 0 }, + {42, 42, 42, 42 }, + {42, 1337, 42, 1337 }, + /* each call increases the internal counter by 1 or 2*/ + {IF_ID_UNIQUE, 42, 1, 42 }, + {42, IF_ID_UNIQUE, 42, 2 }, + {IF_ID_UNIQUE_DIR, 42, 3, 42 }, + {42, IF_ID_UNIQUE_DIR, 42, 4 }, + {IF_ID_UNIQUE, IF_ID_UNIQUE, 5, 5 }, + {IF_ID_UNIQUE_DIR, IF_ID_UNIQUE, 6, 7 }, + {IF_ID_UNIQUE, IF_ID_UNIQUE_DIR, 8, 9 }, + {IF_ID_UNIQUE_DIR, IF_ID_UNIQUE_DIR, 10, 11 }, +}; + +START_TEST(test_allocate_unique_if_ids) +{ + uint32_t if_id_in = unique_if_id_data[_i].in, + if_id_out = unique_if_id_data[_i].out; + + allocate_unique_if_ids(&if_id_in, &if_id_out); + ck_assert_int_eq(if_id_in, unique_if_id_data[_i].exp_in); + ck_assert_int_eq(if_id_out, unique_if_id_data[_i].exp_out); +} +END_TEST + /******************************************************************************* * signature_schemes_for_key */ @@ -1087,6 +1165,14 @@ Suite *utils_suite_create() tcase_add_loop_test(tc, test_mark_from_string, 0, countof(mark_data)); suite_add_tcase(s, tc); + tc = tcase_create("if_id_from_string"); + tcase_add_loop_test(tc, test_if_id_from_string, 0, countof(if_id_data)); + suite_add_tcase(s, tc); + + tc = tcase_create("allocate_unique_if_ids"); + tcase_add_loop_test(tc, test_allocate_unique_if_ids, 0, countof(unique_if_id_data)); + suite_add_tcase(s, tc); + tc = tcase_create("signature_schemes_for_key"); tcase_add_loop_test(tc, test_signature_schemes_for_key, 0, countof(scheme_data)); suite_add_tcase(s, tc); diff --git a/src/swanctl/commands/list_sas.c b/src/swanctl/commands/list_sas.c index 232f03cc2..ca2436dd2 100644 --- a/src/swanctl/commands/list_sas.c +++ b/src/swanctl/commands/list_sas.c @@ -166,12 +166,24 @@ CALLBACK(child_sas, int, printf(" in %s%s%s", child->get(child, "spi-in"), child->get(child, "cpi-in") ? "/" : "", child->get(child, "cpi-in") ?: ""); - if (child->get(child, "mark-in")) + if (child->get(child, "mark-in") || child->get(child, "if-id-in")) { - printf(" (0x%s", child->get(child, "mark-in")); - if (child->get(child, "mark-mask-in")) + printf(" ("); + if (child->get(child, "mark-in")) { - printf("/0x%s", child->get(child, "mark-mask-in")); + printf("0x%s", child->get(child, "mark-in")); + if (child->get(child, "mark-mask-in")) + { + printf("/0x%s", child->get(child, "mark-mask-in")); + } + } + else + { + printf("-"); + } + if (child->get(child, "if-id-in")) + { + printf("|0x%s", child->get(child, "if-id-in")); } printf(")"); } @@ -186,12 +198,24 @@ CALLBACK(child_sas, int, printf(" out %s%s%s", child->get(child, "spi-out"), child->get(child, "cpi-out") ? "/" : "", child->get(child, "cpi-out") ?: ""); - if (child->get(child, "mark-out")) + if (child->get(child, "mark-out") || child->get(child, "if-id-out")) { - printf(" (0x%s", child->get(child, "mark-out")); - if (child->get(child, "mark-mask-out")) + printf(" ("); + if (child->get(child, "mark-out")) { - printf("/0x%s", child->get(child, "mark-mask-out")); + printf("0x%s", child->get(child, "mark-out")); + if (child->get(child, "mark-mask-out")) + { + printf("/0x%s", child->get(child, "mark-mask-out")); + } + } + else + { + printf("-"); + } + if (child->get(child, "if-id-out")) + { + printf("|0x%s", child->get(child, "if-id-out")); } printf(")"); } diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 7c71548d0..460e17b09 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -280,6 +280,18 @@ connections..pools = other configuration attributes from. Each name references a pool by name from either the **pools** section or an external pool. +connections..if_id_in = 0 + Default inbound XFRM interface ID for children. + + XFRM interface ID set on inbound policies/SA, can be overridden by child + config, see there for details. + +connections..if_id_out = 0 + Default outbound XFRM interface ID for children. + + XFRM interface ID set on outbound policies/SA, can be overridden by child + cofnig, see there for details. + connections..mediation = no Whether this connection is a mediation connection. @@ -916,6 +928,26 @@ connections..children..mark_out = 0/0x00000000 An additional mask may be appended to the mark, separated by _/_. The default mask if omitted is 0xffffffff. +connections..children..if_id_in = 0 + Inbound XFRM interface ID. + + XFRM interface ID set on inbound policies/SA. This allows installing + duplicate policies/SAs and associates them with an interface with the same + ID. The special value _%unique_ sets a unique interface ID on each CHILD_SA + instance, beyond that the value _%unique-dir_ assigns a different unique + interface ID for each CHILD_SA direction (in/out). + +connections..children..if_id_out = 0 + Outbound XFRM interface ID. + + XFRM interface ID set on outbound policies/SA. This allows installing + duplicate policies/SAs and associates them with an interface with the same + ID. The special value _%unique_ sets a unique interface ID on each CHILD_SA + instance, beyond that the value _%unique-dir_ assigns a different unique + interface ID for each CHILD_SA direction (in/out). + + The daemon will not install routes for CHILD_SAs that have this option set. + connections..children..set_mark_in = 0/0x00000000 Netfilter mark applied to packets after the inbound IPsec SA processed them. diff --git a/src/xfrmi/.gitignore b/src/xfrmi/.gitignore new file mode 100644 index 000000000..53b7fe780 --- /dev/null +++ b/src/xfrmi/.gitignore @@ -0,0 +1 @@ +xfrmi \ No newline at end of file diff --git a/src/xfrmi/Makefile.am b/src/xfrmi/Makefile.am new file mode 100644 index 000000000..c9fedcf17 --- /dev/null +++ b/src/xfrmi/Makefile.am @@ -0,0 +1,13 @@ +ipsec_PROGRAMS = xfrmi + +xfrmi_SOURCES = xfrmi.c + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libcharon \ + -I$(top_srcdir)/src/libcharon/plugins/kernel_netlink + +xfrmi_LDADD = \ + $(top_builddir)/src/libcharon/plugins/kernel_netlink/libstrongswan-kernel-netlink.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libcharon/libcharon.la diff --git a/src/xfrmi/xfrmi.c b/src/xfrmi/xfrmi.c new file mode 100644 index 000000000..4fed54cb2 --- /dev/null +++ b/src/xfrmi/xfrmi.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2019 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_netlink_shared.h" + +#ifndef IFLA_XFRM_MAX +enum { + IFLA_XFRM_UNSPEC, + IFLA_XFRM_LINK, + IFLA_XFRM_IF_ID, + __IFLA_XFRM_MAX +}; +#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) +#endif + +/** + * Create an XFRM interface with the given ID and underlying interface + */ +static int add_xfrm_interface(char *name, uint32_t xfrm_id, uint32_t ifindex) +{ + netlink_buf_t request; + struct nlmsghdr *hdr; + struct ifinfomsg *msg; + struct rtattr *linkinfo, *info_data; + netlink_socket_t *socket; + int status = 1; + + socket = netlink_socket_create(NETLINK_ROUTE, NULL, FALSE); + if (!socket) + { + return 1; + } + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; + hdr->nlmsg_type = RTM_NEWLINK; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + msg = NLMSG_DATA(hdr); + msg->ifi_family = AF_UNSPEC; + + netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name), + sizeof(request)); + + linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO); + + netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), + sizeof(request)); + + info_data = netlink_nested_start(hdr, sizeof(request), IFLA_INFO_DATA); + + netlink_add_attribute(hdr, IFLA_XFRM_IF_ID, chunk_from_thing(xfrm_id), + sizeof(request)); + netlink_add_attribute(hdr, IFLA_XFRM_LINK, chunk_from_thing(ifindex), + sizeof(request)); + + netlink_nested_end(hdr, info_data); + netlink_nested_end(hdr, linkinfo); + + switch (socket->send_ack(socket, hdr)) + { + case SUCCESS: + status = 0; + break; + case ALREADY_DONE: + fprintf(stderr, "XFRM interface already exists\n"); + break; + default: + fprintf(stderr, "failed to create XFRM interface\n"); + break; + } + + socket->destroy(socket); + return status; +} + +/** + * Parse attributes nested in IFLA_INFO_DATA + */ +static void parse_info_data(struct rtattr *rta, size_t rtasize, char *phys, + uint32_t *if_id) +{ + uint32_t ifindex; + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFLA_XFRM_IF_ID: + if (RTA_PAYLOAD(rta) == sizeof(*if_id)) + { + *if_id = *(uint32_t*)RTA_DATA(rta); + } + break; + case IFLA_XFRM_LINK: + if (RTA_PAYLOAD(rta) == sizeof(ifindex)) + { + ifindex = *(uint32_t*)RTA_DATA(rta); + if_indextoname(ifindex, phys); + } + break; + default: + break; + } + rta = RTA_NEXT(rta, rtasize); + } +} + +/** + * Parse attributes nested in IFLA_LINKINFO + */ +static void parse_linkinfo(struct rtattr *rta, size_t rtasize, char *phys, + uint32_t *if_id) +{ + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFLA_INFO_DATA: + parse_info_data(RTA_DATA(rta), RTA_PAYLOAD(rta), phys, if_id); + break; + default: + break; + } + rta = RTA_NEXT(rta, rtasize); + } +} + +/** + * List all installed XFRM interfaces + */ +static int list_xfrm_interfaces() +{ + netlink_buf_t request; + struct nlmsghdr *hdr, *out, *current; + struct ifinfomsg *msg; + struct rtattr *linkinfo; + netlink_socket_t *socket; + size_t len; + int status = 0; + + socket = netlink_socket_create(NETLINK_ROUTE, NULL, FALSE); + if (!socket) + { + return 1; + } + + memset(&request, 0, sizeof(request)); + + hdr = &request.hdr; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + hdr->nlmsg_type = RTM_GETLINK; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + + msg = NLMSG_DATA(hdr); + msg->ifi_family = AF_UNSPEC; + + linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO); + + netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), + sizeof(request)); + + netlink_nested_end(hdr, linkinfo); + + if (socket->send(socket, hdr, &out, &len) != SUCCESS) + { + return FAILED; + } + current = out; + while (NLMSG_OK(current, len)) + { + switch (current->nlmsg_type) + { + case NLMSG_DONE: + break; + case RTM_NEWLINK: + msg = NLMSG_DATA(current); + struct rtattr *rta = IFLA_RTA(msg); + size_t rtasize = IFLA_PAYLOAD(current); + char *name = NULL, phys[IF_NAMESIZE] = {}; + uint32_t if_id = 0; + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case IFLA_IFNAME: + name = RTA_DATA(rta); + break; + case IFLA_LINKINFO: + parse_linkinfo(RTA_DATA(rta), RTA_PAYLOAD(rta), + phys, &if_id); + break; + default: + break; + } + rta = RTA_NEXT(rta, rtasize); + } + if (name) + { + printf("%2u: %-16s dev %-8s if_id 0x%.8x [%u]\n", + msg->ifi_index, name, phys, if_id, if_id); + } + /* fall through */ + default: + current = NLMSG_NEXT(current, len); + continue; + } + break; + } + free(out); + + socket->destroy(socket); + return status; +} + +static void usage(FILE *out, char *name) +{ + fprintf(out, "Create XFRM interfaces\n\n"); + fprintf(out, "%s [OPTIONS]\n\n", name); + fprintf(out, "Options:\n"); + fprintf(out, " -h, --help print this help.\n"); + fprintf(out, " -v, --debug set debug level, default: 1.\n"); + fprintf(out, " -l, --list list XFRM interfaces.\n"); + fprintf(out, " -n, --name=NAME name of the XFRM interface.\n"); + fprintf(out, " -i, --id=ID optional numeric XFRM ID.\n"); + fprintf(out, " -d, --dev=DEVICE underlying physical interface.\n"); + fprintf(out, "\n"); +} + +int main(int argc, char *argv[]) +{ + char *name = NULL, *dev = NULL, *end; + uint32_t xfrm_id = 0; + u_int ifindex; + + library_init(NULL, "xfrmi"); + atexit(library_deinit); + + while (true) + { + struct option long_opts[] = { + {"help", no_argument, NULL, 'h' }, + {"debug", no_argument, NULL, 'v' }, + {"list", no_argument, NULL, 'l' }, + {"name", required_argument, NULL, 'n' }, + {"id", required_argument, NULL, 'i' }, + {"dev", required_argument, NULL, 'd' }, + {0,0,0,0 }, + }; + switch (getopt_long(argc, argv, "hvln:i:d:", long_opts, NULL)) + { + case EOF: + break; + case 'h': + usage(stdout, argv[0]); + return 0; + case 'l': + list_xfrm_interfaces(); + return 0; + case 'v': + dbg_default_set_level(atoi(optarg)); + continue; + case 'n': + name = optarg; + continue; + case 'i': + errno = 0; + xfrm_id = strtoul(optarg, &end, 0); + if (errno || *end) + { + fprintf(stderr, "invalid XFRM ID: %s\n", + errno ? strerror(errno) : end); + return 1; + } + continue; + case 'd': + dev = optarg; + continue; + default: + usage(stderr, argv[0]); + return 1; + } + break; + } + + if (!name || !dev) + { + fprintf(stderr, "please specify a name and a physical interface\n"); + return 1; + } + ifindex = if_nametoindex(dev); + if (!ifindex) + { + fprintf(stderr, "physical interface %s not found\n", dev); + return 1; + } + + return add_xfrm_interface(name, xfrm_id, ifindex); +} diff --git a/testing/config/kernel/config-4.19 b/testing/config/kernel/config-4.19 index 79cf9e71e..3b00258e3 100644 --- a/testing/config/kernel/config-4.19 +++ b/testing/config/kernel/config-4.19 @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86 4.19.0 Kernel Configuration +# Linux/x86 4.19.23 Kernel Configuration # # @@ -132,7 +132,7 @@ CONFIG_NAMESPACES=y # CONFIG_IPC_NS is not set # CONFIG_USER_NS is not set # CONFIG_PID_NS is not set -# CONFIG_NET_NS is not set +CONFIG_NET_NS=y # CONFIG_CHECKPOINT_RESTORE is not set # CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set @@ -691,7 +691,7 @@ CONFIG_TLS=y CONFIG_XFRM=y CONFIG_XFRM_ALGO=y CONFIG_XFRM_USER=y -# CONFIG_XFRM_INTERFACE is not set +CONFIG_XFRM_INTERFACE=y CONFIG_XFRM_SUB_POLICY=y CONFIG_XFRM_MIGRATE=y CONFIG_XFRM_STATISTICS=y @@ -1849,6 +1849,7 @@ CONFIG_USB_PCI=y # CONFIG_NOP_USB_XCEIV is not set # CONFIG_USB_GADGET is not set # CONFIG_TYPEC is not set +# CONFIG_USB_ROLE_SWITCH is not set # CONFIG_USB_ULPI_BUS is not set # CONFIG_UWB is not set # CONFIG_MMC is not set @@ -2365,7 +2366,6 @@ CONFIG_CRYPTO_SERPENT_SSE2_X86_64=y CONFIG_CRYPTO_SERPENT_AVX_X86_64=y CONFIG_CRYPTO_SERPENT_AVX2_X86_64=y CONFIG_CRYPTO_SM4=y -# CONFIG_CRYPTO_SPECK is not set CONFIG_CRYPTO_TEA=y CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_TWOFISH_COMMON=y diff --git a/testing/config/kernel/config-5.0 b/testing/config/kernel/config-5.0 index 26807ef28..279d9820d 100644 --- a/testing/config/kernel/config-5.0 +++ b/testing/config/kernel/config-5.0 @@ -135,7 +135,7 @@ CONFIG_NAMESPACES=y # CONFIG_IPC_NS is not set # CONFIG_USER_NS is not set # CONFIG_PID_NS is not set -# CONFIG_NET_NS is not set +CONFIG_NET_NS=y # CONFIG_CHECKPOINT_RESTORE is not set # CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set @@ -649,7 +649,7 @@ CONFIG_TLS=y CONFIG_XFRM=y CONFIG_XFRM_ALGO=y CONFIG_XFRM_USER=y -# CONFIG_XFRM_INTERFACE is not set +CONFIG_XFRM_INTERFACE=y CONFIG_XFRM_SUB_POLICY=y CONFIG_XFRM_MIGRATE=y CONFIG_XFRM_STATISTICS=y diff --git a/testing/scripts/build-strongswan b/testing/scripts/build-strongswan index 13fd1e691..24e11bba2 100755 --- a/testing/scripts/build-strongswan +++ b/testing/scripts/build-strongswan @@ -59,6 +59,10 @@ log_action "Mounting $SWANDIR as /root/strongswan" execute "bindfs -u $SRCUID -g $SRCGID $SWANDIR $LOOPDIR/root/strongswan" do_on_exit umount $LOOPDIR/root/strongswan +log_action "Copy /etc/resolv.conf" +execute "cp /etc/resolv.conf $LOOPDIR/etc/resolv.conf" +do_on_exit rm $LOOPDIR/etc/resolv.conf + log_action "Remove SWID tags of previous versions" execute_chroot "find /usr/local/share -path '*strongswan*' -name *.swidtag -delete" diff --git a/testing/scripts/recipes/013_strongswan.mk b/testing/scripts/recipes/013_strongswan.mk index 13a1b23c8..e84d42094 100644 --- a/testing/scripts/recipes/013_strongswan.mk +++ b/testing/scripts/recipes/013_strongswan.mk @@ -105,7 +105,8 @@ CONFIG_OPTS = \ --enable-newhope \ --enable-systemd \ --enable-counters \ - --enable-save-keys + --enable-save-keys \ + --enable-python-eggs-install export ADA_PROJECT_PATH=/usr/local/ada/lib/gnat @@ -126,3 +127,5 @@ build: configure install: build cd $(BUILDDIR) && make -j install + # for Python-based updown scripts + pip install python-daemon diff --git a/testing/tests/route-based/net2net-xfrmi-ike/description.txt b/testing/tests/route-based/net2net-xfrmi-ike/description.txt new file mode 100644 index 000000000..b362a0a07 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/description.txt @@ -0,0 +1,18 @@ +One connection with two CHILD_SAs between the hosts and subnet behind the +gateways moon and sun, respectively, are set up using XFRM +interfaces. +

+The gateways use route-based forwarding with XFRM interfaces, with +firewall rules to allow traffic to pass. Both peers use connection-defined +interface IDs so all CHILD_SAs share the same XFRM interface. The IKE daemon +does not install routes for CHILD_SAs with outbound interface ID, so routes for +the target subnets are installed statically or via updown events. +

+Both gateways use separate interfaces for in- and outbound traffic (which is +completely optional and mainly for testing purposes, a single interface will +usually be enough). Gateway moon creates them before initiating the +connection, while gateway sun dynamically creates the interfaces via +ike-updown event using the passed unique generated interface IDs. +

+Clients alice and venus behind gateway moon ping client +bob located behind gateway sun. diff --git a/testing/tests/route-based/net2net-xfrmi-ike/evaltest.dat b/testing/tests/route-based/net2net-xfrmi-ike/evaltest.dat new file mode 100644 index 000000000..cd514c9a2 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/evaltest.dat @@ -0,0 +1,6 @@ +moon::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::gw.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=4500 local-id=moon.strongswan.org remote-host=PH_IP_SUN remote-port=4500 remote-id=sun.strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[10.1.0.10/32] remote-ts=\[10.2.0.0/16].*local-ts=\[10.1.0.20/32] remote-ts=\[10.2.0.0/16]::YES +sun::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::gw.*version=2 state=ESTABLISHED local-host=PH_IP_SUN local-port=4500 local-id=sun.strongswan.org remote-host=PH_IP_MOON remote-port=4500 remote-id=moon.strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.10/32].*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.20/32]::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +venus::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/strongswan.conf b/testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..15f8ad5bc --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown +} diff --git a/testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..ca8bd2904 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,33 @@ +connections { + + gw-gw { + local_addrs = PH_IP_MOON + remote_addrs = PH_IP_SUN + + if_id_out = 1337 + if_id_in = 42 + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = pubkey + id = sun.strongswan.org + } + children { + alice-net { + local_ts = 10.1.0.10/32 + remote_ts = 0.0.0.0/0 + + esp_proposals = aes128gcm128-x25519 + } + venus-net : connections.gw-gw.children.alice-net { + local_ts = 10.1.0.20/32 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/strongswan.conf b/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..283cb1d72 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/strongswan.conf @@ -0,0 +1,12 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default + start-scripts { + updown = /usr/bin/python /etc/updown.py + } +} diff --git a/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/swanctl/swanctl.conf b/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..ebe3b5873 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/swanctl/swanctl.conf @@ -0,0 +1,30 @@ +connections { + + gw-gw { + local_addrs = PH_IP_SUN + remote_addrs = PH_IP_MOON + + if_id_in = %unique-dir + if_id_out = %unique-dir + + local { + auth = pubkey + certs = sunCert.pem + id = sun.strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + net-net { + local_ts = 10.2.0.0/16 + remote_ts = 10.1.0.0/16 + + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/updown.py b/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/updown.py new file mode 100755 index 000000000..b17eb6bab --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/updown.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +import vici +import daemon +import logging +from logging.handlers import SysLogHandler +import subprocess + + +logger = logging.getLogger('updownLogger') +handler = SysLogHandler(address='/dev/log', facility=SysLogHandler.LOG_DAEMON) +handler.setFormatter(logging.Formatter('charon-updown: %(message)s')) +logger.addHandler(handler) +logger.setLevel(logging.INFO) + + +def handle_interfaces(ike_sa, up): + if_id_in = int(ike_sa['if-id-in'], 16) + if_id_out = int(ike_sa['if-id-out'], 16) + ifname_in = "xfrm-{}-in".format(if_id_in) + ifname_out = "xfrm-{}-out".format(if_id_out) + + if up: + logger.info("add XFRM interfaces %s and %s", ifname_in, ifname_out) + subprocess.call(["/usr/local/libexec/ipsec/xfrmi", "-n", ifname_out, + "-i", str(if_id_out), "-d", "eth0"]) + subprocess.call(["/usr/local/libexec/ipsec/xfrmi", "-n", ifname_in, + "-i", str(if_id_in), "-d", "eth0"]) + subprocess.call(["ip", "link", "set", ifname_out, "up"]) + subprocess.call(["ip", "link", "set", ifname_in, "up"]) + subprocess.call(["iptables", "-A", "FORWARD", "-o", ifname_out, + "-j", "ACCEPT"]) + subprocess.call(["iptables", "-A", "FORWARD", "-i", ifname_in, + "-j", "ACCEPT"]) + + else: + logger.info("delete XFRM interfaces %s and %s", ifname_in, ifname_out) + subprocess.call(["iptables", "-D", "FORWARD", "-o", ifname_out, + "-j", "ACCEPT"]) + subprocess.call(["iptables", "-D", "FORWARD", "-i", ifname_in, + "-j", "ACCEPT"]) + subprocess.call(["ip", "link", "del", ifname_out]) + subprocess.call(["ip", "link", "del", ifname_in]) + + +def install_routes(ike_sa): + if_id_out = int(ike_sa['if-id-out'], 16) + ifname_out = "xfrm-{}-out".format(if_id_out) + child_sa = next(ike_sa["child-sas"].itervalues()) + + for ts in child_sa['remote-ts']: + logger.info("add route to %s via %s", ts, ifname_out) + subprocess.call(["ip", "route", "add", ts, "dev", ifname_out]) + + +# daemonize and run parallel to the IKE daemon +with daemon.DaemonContext(): + logger.debug("starting Python updown listener") + try: + session = vici.Session() + ver = session.version() + logger.info("connected to {daemon} {version} ({sysname}, {release}, " + "{machine})".format(**ver)) + except: + logger.error("failed to get status via vici") + sys.exit(1) + + try: + for label, event in session.listen(["ike-updown", "child-updown"]): + logger.debug("received event: %s %s", label, repr(event)) + + name = next((x for x in iter(event) if x != "up")) + up = event.get("up", "") == "yes" + ike_sa = event[name] + + if label == "ike-updown": + handle_interfaces(ike_sa, up) + + elif label == "child-updown" and up: + install_routes(ike_sa) + + except IOError: + logger.error("daemon disconnected") + except: + logger.error("exception while listening for events " + + repr(sys.exc_info()[1])) diff --git a/testing/tests/route-based/net2net-xfrmi-ike/posttest.dat b/testing/tests/route-based/net2net-xfrmi-ike/posttest.dat new file mode 100644 index 000000000..310a564ad --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/posttest.dat @@ -0,0 +1,10 @@ +moon::swanctl --terminate --ike gw-gw +moon::systemctl stop strongswan-swanctl +sun::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +sun::iptables-restore < /etc/iptables.flush +moon::ip -s link show xfrm-moon-out +moon::ip -s link show xfrm-moon-in +moon::ip link del xfrm-moon-out +moon::ip link del xfrm-moon-in +sun::rm /etc/updown.py diff --git a/testing/tests/route-based/net2net-xfrmi-ike/pretest.dat b/testing/tests/route-based/net2net-xfrmi-ike/pretest.dat new file mode 100644 index 000000000..bdd904ae1 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/pretest.dat @@ -0,0 +1,15 @@ +moon::iptables-restore < /etc/iptables.rules +sun::iptables-restore < /etc/iptables.rules +moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-out -d eth0 -i 1337 +moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-in -d eth0 -i 42 +moon::ip link set xfrm-moon-out up +moon::ip link set xfrm-moon-in up +moon::ip route add 10.2.0.0/16 dev xfrm-moon-out +moon::iptables -A FORWARD -o xfrm-moon-out -j ACCEPT +moon::iptables -A FORWARD -i xfrm-moon-in -j ACCEPT +moon::systemctl start strongswan-swanctl +sun::systemctl start strongswan-swanctl +moon::expect-connection gw-gw +sun::expect-connection gw-gw +moon::swanctl --initiate --child alice-net +moon::swanctl --initiate --child venus-net diff --git a/testing/tests/route-based/net2net-xfrmi-ike/test.conf b/testing/tests/route-based/net2net-xfrmi-ike/test.conf new file mode 100644 index 000000000..7f82c02f1 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-ike/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice venus moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-v-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" + +# charon controlled by swanctl +# +SWANCTL=1 diff --git a/testing/tests/route-based/net2net-xfrmi-netns/description.txt b/testing/tests/route-based/net2net-xfrmi-netns/description.txt new file mode 100644 index 000000000..10c2d53ec --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/description.txt @@ -0,0 +1,22 @@ +This scenario demonstrates a property of XFRM interfaces that allows +moving them into network namespaces while retaining access to IPsec SAs and +policies in the original namespace. This enables an IKE daemon in one namespace +to provide IPsec tunnels for processes in other namespaces without having to +give them access to the keys and IKE credentials. +

+The gateways use route-based forwarding with XFRM interfaces, with +firewall rules to allow traffic to pass. The IPsec traffic selector used is +0.0.0.0/0, however, specific routing is achieved with routes on the XFRM +interfaces. The IKE daemon does not install routes for CHILD_SAs with outbound +interface ID, so static routes are installed for the target subnets. +

+The XFRM interface on gateway moon is moved into a new network namespace +from which a ping is sent to client bob. It is then moved back out and +alice sends another ping to bob to test if that works too. +

+Gateway sun dynamically creates the XFRM interface via updown script +using the passed unique generated interface ID. +

+Note that the dropped packet seen on the XFRM interface on moon +is an IPv6 Router Solicitation (NDP) sent from that namespace, which doesn't +match the IPsec policy. diff --git a/testing/tests/route-based/net2net-xfrmi-netns/evaltest.dat b/testing/tests/route-based/net2net-xfrmi-netns/evaltest.dat new file mode 100644 index 000000000..6770a41b4 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/evaltest.dat @@ -0,0 +1,18 @@ +moon::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::gw.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=4500 local-id=moon.strongswan.org remote-host=PH_IP_SUN remote-port=4500 remote-id=sun.strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[0.0.0.0/0] remote-ts=\[0.0.0.0/0]::YES +sun::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::gw.*version=2 state=ESTABLISHED local-host=PH_IP_SUN local-port=4500 local-id=sun.strongswan.org remote-host=PH_IP_MOON remote-port=4500 remote-id=moon.strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[0.0.0.0/0] remote-ts=\[0.0.0.0/0]::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +moon::ip netns add xfrmi-test::.*::NO +moon::ip link set xfrm-moon netns xfrmi-test::.*::NO +moon::ip netns exec xfrmi-test ip addr add 10.1.0.42/32 dev xfrm-moon::.*::NO +moon::ip netns exec xfrmi-test ip link set dev xfrm-moon up::.*::NO +moon::ip netns exec xfrmi-test ip route add 10.2.0.0/16 dev xfrm-moon src 10.1.0.42::.*::NO +moon::ip netns exec xfrmi-test ip xfrm state::.*::NO +moon::ip netns exec xfrmi-test ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +alice::ping -c 1 -W 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::NO +moon::ip netns exec xfrmi-test ip link set xfrm-moon netns 1::.*::NO +moon::ip netns del xfrmi-test::.*::NO +moon::ip link set dev xfrm-moon up::.*::NO +moon::ip route add 10.2.0.0/16 dev xfrm-moon::.*::NO +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/strongswan.conf b/testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..15f8ad5bc --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown +} diff --git a/testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..a9c8f893c --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,30 @@ +connections { + + gw-gw { + local_addrs = PH_IP_MOON + remote_addrs = PH_IP_SUN + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = pubkey + id = sun.strongswan.org + } + children { + net-net { + local_ts = 0.0.0.0/0 + remote_ts = 0.0.0.0/0 + + if_id_out = 42 + if_id_in = 42 + + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/strongswan.conf b/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..15f8ad5bc --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown +} diff --git a/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/swanctl/swanctl.conf b/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..f98fcfbd6 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/swanctl/swanctl.conf @@ -0,0 +1,32 @@ +connections { + + gw-gw { + local_addrs = PH_IP_SUN + remote_addrs = PH_IP_MOON + + local { + auth = pubkey + certs = sunCert.pem + id = sun.strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + net-net { + local_ts = 0.0.0.0/0 + remote_ts = 0.0.0.0/0 + + if_id_out = %unique + if_id_in = %unique + + updown = /etc/updown + + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/updown b/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/updown new file mode 100755 index 000000000..71e904dfc --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/updown @@ -0,0 +1,18 @@ +#!/bin/bash + +IF_NAME="xfrmi-${PLUTO_IF_ID_IN}" + +case "${PLUTO_VERB}" in + up-client) + /usr/local/libexec/ipsec/xfrmi -n "${IF_NAME}" -i "${PLUTO_IF_ID_IN}" -d eth0 + ip link set "${IF_NAME}" up + ip route add 10.1.0.0/16 dev "${IF_NAME}" + iptables -A FORWARD -i "${IF_NAME}" -j ACCEPT + iptables -A FORWARD -o "${IF_NAME}" -j ACCEPT + ;; + down-client) + iptables -D FORWARD -i "${IF_NAME}" -j ACCEPT + iptables -D FORWARD -o "${IF_NAME}" -j ACCEPT + ip link del "${IF_NAME}" + ;; +esac diff --git a/testing/tests/route-based/net2net-xfrmi-netns/posttest.dat b/testing/tests/route-based/net2net-xfrmi-netns/posttest.dat new file mode 100644 index 000000000..634cfe6d5 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/posttest.dat @@ -0,0 +1,8 @@ +moon::swanctl --terminate --ike gw-gw +moon::systemctl stop strongswan-swanctl +sun::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +sun::iptables-restore < /etc/iptables.flush +moon::ip -s link show xfrm-moon +moon::ip link del xfrm-moon +sun::rm /etc/updown diff --git a/testing/tests/route-based/net2net-xfrmi-netns/pretest.dat b/testing/tests/route-based/net2net-xfrmi-netns/pretest.dat new file mode 100644 index 000000000..2c337f51e --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/pretest.dat @@ -0,0 +1,12 @@ +moon::iptables-restore < /etc/iptables.rules +sun::iptables-restore < /etc/iptables.rules +moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon -i 42 -d eth0 +moon::ip link set xfrm-moon up +moon::ip route add 10.2.0.0/16 dev xfrm-moon +moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT +moon::iptables -A FORWARD -o xfrm-moon -j ACCEPT +moon::systemctl start strongswan-swanctl +sun::systemctl start strongswan-swanctl +moon::expect-connection gw-gw +sun::expect-connection gw-gw +moon::swanctl --initiate --child net-net diff --git a/testing/tests/route-based/net2net-xfrmi-netns/test.conf b/testing/tests/route-based/net2net-xfrmi-netns/test.conf new file mode 100644 index 000000000..87abc763b --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi-netns/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" + +# charon controlled by swanctl +# +SWANCTL=1 diff --git a/testing/tests/route-based/net2net-xfrmi/description.txt b/testing/tests/route-based/net2net-xfrmi/description.txt new file mode 100644 index 000000000..11cb00588 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/description.txt @@ -0,0 +1,17 @@ +A connection between the subnets behind the gateways moon and sun +is set up using XFRM interfaces. +

+The gateways use route-based forwarding with XFRM interfaces, with +firewall rules to allow traffic to pass. The IPsec traffic selector used is +0.0.0.0/0, however, specific routing is achieved with routes on the XFRM +interfaces. The IKE daemon does not install routes for CHILD_SAs with outbound +interface ID, so static routes are installed for the target subnets. +

+Both gateways use separate interfaces for in- and outbound traffic (which is +completely optional and mainly for testing purposes, a single interface will +usually be enough). Gateway moon creates them before initiating the +connection, while gateway sun dynamically creates the interfaces via +updown script using the passed unique generated interface IDs. +

+Client alice behind gateway moon pings client bob located +behind gateway sun. diff --git a/testing/tests/route-based/net2net-xfrmi/evaltest.dat b/testing/tests/route-based/net2net-xfrmi/evaltest.dat new file mode 100644 index 000000000..0bf5cdb5a --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/evaltest.dat @@ -0,0 +1,5 @@ +moon::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::gw.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=4500 local-id=moon.strongswan.org remote-host=PH_IP_SUN remote-port=4500 remote-id=sun.strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[0.0.0.0/0] remote-ts=\[0.0.0.0/0]::YES +sun::swanctl --list-sas --ike-id 1 --raw 2> /dev/null::gw.*version=2 state=ESTABLISHED local-host=PH_IP_SUN local-port=4500 local-id=sun.strongswan.org remote-host=PH_IP_MOON remote-port=4500 remote-id=moon.strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[0.0.0.0/0] remote-ts=\[0.0.0.0/0]::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/strongswan.conf b/testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..15f8ad5bc --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown +} diff --git a/testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..356bfb754 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,30 @@ +connections { + + gw-gw { + local_addrs = PH_IP_MOON + remote_addrs = PH_IP_SUN + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = pubkey + id = sun.strongswan.org + } + children { + net-net { + local_ts = 0.0.0.0/0 + remote_ts = 0.0.0.0/0 + + if_id_out = 1337 + if_id_in = 42 + + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/strongswan.conf b/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..15f8ad5bc --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown +} diff --git a/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/swanctl/swanctl.conf b/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..09ed4fe6a --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/swanctl/swanctl.conf @@ -0,0 +1,32 @@ +connections { + + gw-gw { + local_addrs = PH_IP_SUN + remote_addrs = PH_IP_MOON + + local { + auth = pubkey + certs = sunCert.pem + id = sun.strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + net-net { + local_ts = 0.0.0.0/0 + remote_ts = 0.0.0.0/0 + + if_id_in = %unique-dir + if_id_out = %unique-dir + + updown = /etc/updown + + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/updown b/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/updown new file mode 100755 index 000000000..bf0d0b86a --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/updown @@ -0,0 +1,23 @@ +#!/bin/bash + +IF_NAME="xfrm-" +IF_NAME_IN="${IF_NAME}${PLUTO_IF_ID_IN}-in" +IF_NAME_OUT="${IF_NAME}${PLUTO_IF_ID_OUT}-out" + +case "${PLUTO_VERB}" in + up-client) + /usr/local/libexec/ipsec/xfrmi -n "${IF_NAME_OUT}" -i "${PLUTO_IF_ID_OUT}" -d eth0 + /usr/local/libexec/ipsec/xfrmi -n "${IF_NAME_IN}" -i "${PLUTO_IF_ID_IN}" -d eth0 + ip link set "${IF_NAME_OUT}" up + ip link set "${IF_NAME_IN}" up + ip route add 10.1.0.0/16 dev "${IF_NAME_OUT}" + iptables -A FORWARD -o "${IF_NAME_OUT}" -j ACCEPT + iptables -A FORWARD -i "${IF_NAME_IN}" -j ACCEPT + ;; + down-client) + iptables -D FORWARD -o "${IF_NAME_OUT}" -j ACCEPT + iptables -D FORWARD -i "${IF_NAME_IN}" -j ACCEPT + ip link del "${IF_NAME_OUT}" + ip link del "${IF_NAME_IN}" + ;; +esac diff --git a/testing/tests/route-based/net2net-xfrmi/posttest.dat b/testing/tests/route-based/net2net-xfrmi/posttest.dat new file mode 100644 index 000000000..ba0915d60 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/posttest.dat @@ -0,0 +1,10 @@ +moon::swanctl --terminate --ike gw-gw +moon::systemctl stop strongswan-swanctl +sun::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +sun::iptables-restore < /etc/iptables.flush +moon::ip -s link show xfrm-moon-out +moon::ip -s link show xfrm-moon-in +moon::ip link del xfrm-moon-out +moon::ip link del xfrm-moon-in +sun::rm /etc/updown diff --git a/testing/tests/route-based/net2net-xfrmi/pretest.dat b/testing/tests/route-based/net2net-xfrmi/pretest.dat new file mode 100644 index 000000000..416054161 --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/pretest.dat @@ -0,0 +1,14 @@ +moon::iptables-restore < /etc/iptables.rules +sun::iptables-restore < /etc/iptables.rules +moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-out -d eth0 -i 1337 +moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon-in -d eth0 -i 42 +moon::ip link set xfrm-moon-out up +moon::ip link set xfrm-moon-in up +moon::ip route add 10.2.0.0/16 dev xfrm-moon-out +moon::iptables -A FORWARD -o xfrm-moon-out -j ACCEPT +moon::iptables -A FORWARD -i xfrm-moon-in -j ACCEPT +moon::systemctl start strongswan-swanctl +sun::systemctl start strongswan-swanctl +moon::expect-connection gw-gw +sun::expect-connection gw-gw +moon::swanctl --initiate --child net-net diff --git a/testing/tests/route-based/net2net-xfrmi/test.conf b/testing/tests/route-based/net2net-xfrmi/test.conf new file mode 100644 index 000000000..87abc763b --- /dev/null +++ b/testing/tests/route-based/net2net-xfrmi/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun" + +# charon controlled by swanctl +# +SWANCTL=1 diff --git a/testing/tests/route-based/rw-shared-xfrmi/description.txt b/testing/tests/route-based/rw-shared-xfrmi/description.txt new file mode 100644 index 000000000..776b0d01b --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/description.txt @@ -0,0 +1,12 @@ +The roadwarriors carol and dave set up a connection each to +gateway moon. Both carol and dave request a virtual +IP via IKEv2 configuration payload. +

+The gateway moon uses route-based forwarding with an XFRM +interface, with firewall rules to allow traffic to pass. The IKE daemon +does not install routes for CHILD_SAs with outbound interface ID, so a static +route is installed for the virtual IP subnet via XFRM interface. +

+Both carol and dave ping the client alice behind the +gateway moon. The source IP addresses of the two pings will be the +virtual IPs carol1 and dave1, respectively. diff --git a/testing/tests/route-based/rw-shared-xfrmi/evaltest.dat b/testing/tests/route-based/rw-shared-xfrmi/evaltest.dat new file mode 100644 index 000000000..f69310314 --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/evaltest.dat @@ -0,0 +1,10 @@ +carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=PH_IP_CAROL local-port=4500 local-id=carol@strongswan.org remote-host=PH_IP_MOON remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[10.3.0.1/32] remote-ts=\[10.1.0.0/16]::YES +dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=PH_IP_DAVE local-port=4500 local-id=dave@strongswan.org remote-host=PH_IP_MOON remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[10.3.0.2/32] remote-ts=\[10.1.0.0/16]::YES +moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=4500 local-id=moon.strongswan.org remote-host=PH_IP_CAROL remote-port=4500 remote-id=carol@strongswan.org.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[10.1.0.0/16] remote-ts=\[10.3.0.1/32]::YES +moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=PH_IP_MOON local-port=4500 local-id=moon.strongswan.org remote-host=PH_IP_DAVE remote-port=4500 remote-id=dave@strongswan.org.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*local-ts=\[10.1.0.0/16] remote-ts=\[10.3.0.2/32]::YES +carol::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES +dave::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES +moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES +moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/strongswan.conf b/testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/strongswan.conf new file mode 100755 index 000000000..ad4c18e43 --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 hmac pem pkcs1 x509 revocation curve25519 gmp curl kernel-netlink socket-default updown vici +} diff --git a/testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..15e80d2aa --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/swanctl/swanctl.conf @@ -0,0 +1,28 @@ +connections { + + home { + local_addrs = PH_IP_CAROL + remote_addrs = PH_IP_MOON + vips = 0.0.0.0 + + local { + auth = pubkey + certs = carolCert.pem + id = carol@strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/strongswan.conf b/testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/strongswan.conf new file mode 100755 index 000000000..ad4c18e43 --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 hmac pem pkcs1 x509 revocation curve25519 gmp curl kernel-netlink socket-default updown vici +} diff --git a/testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..5b14d36ef --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/swanctl/swanctl.conf @@ -0,0 +1,28 @@ +connections { + + home { + local_addrs = PH_IP_DAVE + remote_addrs = PH_IP_MOON + vips = 0.0.0.0 + + local { + auth = pubkey + certs = daveCert.pem + id = dave@strongswan.org + } + remote { + auth = pubkey + id = moon.strongswan.org + } + children { + home { + remote_ts = 10.1.0.0/16 + + updown = /usr/local/libexec/ipsec/_updown iptables + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} diff --git a/testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/strongswan.conf b/testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..15f8ad5bc --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/strongswan.conf @@ -0,0 +1,9 @@ +# /etc/strongswan.conf - strongSwan configuration file + +swanctl { + load = pem pkcs1 x509 revocation constraints pubkey openssl random +} + +charon-systemd { + load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac vici kernel-netlink socket-default updown +} diff --git a/testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/swanctl/swanctl.conf new file mode 100755 index 000000000..7565630e8 --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/swanctl/swanctl.conf @@ -0,0 +1,34 @@ +connections { + + rw { + local_addrs = PH_IP_MOON + pools = rw_pool + + local { + auth = pubkey + certs = moonCert.pem + id = moon.strongswan.org + } + remote { + auth = pubkey + } + children { + net { + local_ts = 10.1.0.0/16 + + if_id_out = 42 + if_id_in = 42 + + esp_proposals = aes128gcm128-x25519 + } + } + version = 2 + proposals = aes128-sha256-x25519 + } +} + +pools { + rw_pool { + addrs = 10.3.0.0/28 + } +} diff --git a/testing/tests/route-based/rw-shared-xfrmi/posttest.dat b/testing/tests/route-based/rw-shared-xfrmi/posttest.dat new file mode 100644 index 000000000..7c0aaded6 --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/posttest.dat @@ -0,0 +1,10 @@ +carol::swanctl --terminate --ike home +dave::swanctl --terminate --ike home +moon::systemctl stop strongswan-swanctl +carol::systemctl stop strongswan-swanctl +dave::systemctl stop strongswan-swanctl +moon::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush +moon::ip -s link show xfrm-moon +moon::ip link del xfrm-moon diff --git a/testing/tests/route-based/rw-shared-xfrmi/pretest.dat b/testing/tests/route-based/rw-shared-xfrmi/pretest.dat new file mode 100644 index 000000000..bbf368ac6 --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/pretest.dat @@ -0,0 +1,16 @@ +moon::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +moon::/usr/local/libexec/ipsec/xfrmi -n xfrm-moon -i 42 -d eth0 +moon::ip link set xfrm-moon up +moon::ip route add 10.3.0.0/28 dev xfrm-moon +moon::iptables -A FORWARD -i xfrm-moon -j ACCEPT +moon::iptables -A FORWARD -o xfrm-moon -j ACCEPT +moon::systemctl start strongswan-swanctl +carol::systemctl start strongswan-swanctl +dave::systemctl start strongswan-swanctl +moon::expect-connection rw +carol::expect-connection home +carol::swanctl --initiate --child home +dave::expect-connection home +dave::swanctl --initiate --child home diff --git a/testing/tests/route-based/rw-shared-xfrmi/test.conf b/testing/tests/route-based/rw-shared-xfrmi/test.conf new file mode 100644 index 000000000..1227b9d1c --- /dev/null +++ b/testing/tests/route-based/rw-shared-xfrmi/test.conf @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol dave" + +# charon controlled by swanctl +# +SWANCTL=1