From 735f9e6eb6194557d0a7043c9a08b1cbaf26600e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 1 Feb 2019 09:19:03 +0100 Subject: [PATCH 01/30] kernel-netlink: Calculate length of Netlink messages correctly This is relevant when adding e.g. strings whose length are not aligned. --- src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index 84d78eca2..4014d3cbe 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -705,7 +705,7 @@ 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); } /** @@ -724,7 +724,7 @@ 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_DATA(rta); } From 888593bb5b6aed37ed05235f0918651a67c194a3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 1 Feb 2019 09:28:10 +0100 Subject: [PATCH 02/30] kernel-netlink: Add utility to create XFRM interfaces This is mainly to see what's necessary to create them (in case we integrate this into the daemon) and to experiment in our testing environment without having to add a patched version of iproute2 (the 4.20.0 version in stretch-backports doesn't support XFRM interfaces yet). The regular version of iproute2 can be used for other operations with these interfaces (delete, up, addrs etc.). --- .../plugins/kernel_netlink/.gitignore | 1 + .../plugins/kernel_netlink/Makefile.am | 6 + src/libcharon/plugins/kernel_netlink/xfrmi.c | 184 ++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 src/libcharon/plugins/kernel_netlink/xfrmi.c diff --git a/src/libcharon/plugins/kernel_netlink/.gitignore b/src/libcharon/plugins/kernel_netlink/.gitignore index e05064df6..5d4f4c897 100644 --- a/src/libcharon/plugins/kernel_netlink/.gitignore +++ b/src/libcharon/plugins/kernel_netlink/.gitignore @@ -1 +1,2 @@ kernel_netlink_tests +xfrmi diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.am b/src/libcharon/plugins/kernel_netlink/Makefile.am index 0e39c0d24..1600f8ece 100644 --- a/src/libcharon/plugins/kernel_netlink/Makefile.am +++ b/src/libcharon/plugins/kernel_netlink/Makefile.am @@ -24,6 +24,12 @@ libstrongswan_kernel_netlink_la_LIBADD = $(DLLIB) libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version +ipsec_PROGRAMS = xfrmi +xfrmi_SOURCES = xfrmi.c +xfrmi_LDADD = \ + libstrongswan-kernel-netlink.la \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(top_builddir)/src/libcharon/libcharon.la TESTS = kernel_netlink_tests diff --git a/src/libcharon/plugins/kernel_netlink/xfrmi.c b/src/libcharon/plugins/kernel_netlink/xfrmi.c new file mode 100644 index 000000000..0555ac4ce --- /dev/null +++ b/src/libcharon/plugins/kernel_netlink/xfrmi.c @@ -0,0 +1,184 @@ +/* + * 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 + +#define NLMSG_TAIL(nlh) ((void*)(((char*)nlh) + NLMSG_ALIGN(nlh->nlmsg_len))) + +/** + * 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)); + + /* the following attributes are nested under this one */ + linkinfo = netlink_reserve(hdr, sizeof(request), IFLA_LINKINFO, 0); + linkinfo = (void*)linkinfo - RTA_LENGTH(0); + + netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), + sizeof(request)); + + /* the following attributes are nested under this one */ + info_data = netlink_reserve(hdr, sizeof(request), IFLA_INFO_DATA, 0); + info_data = (void*)info_data - RTA_LENGTH(0); + + 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)); + + info_data->rta_len = NLMSG_TAIL(hdr) - (void*)info_data; + + linkinfo->rta_len = NLMSG_TAIL(hdr) - (void*)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; +} + +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, " -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; + + while (true) + { + struct option long_opts[] = { + {"help", no_argument, NULL, 'h' }, + {"debug", no_argument, NULL, 'v' }, + {"name", required_argument, NULL, 'n' }, + {"id", required_argument, NULL, 'i' }, + {"dev", required_argument, NULL, 'd' }, + {0,0,0,0 }, + }; + switch (getopt_long(argc, argv, "hvn:i:d:", long_opts, NULL)) + { + case EOF: + break; + case 'h': + usage(stdout, argv[0]); + 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; + } + + library_init(NULL, "xfrmi"); + atexit(library_deinit); + + return add_xfrm_interface(name, xfrm_id, ifindex); +} From d1cd2a054135fe7ec99972f730bdd48b44fc693f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 1 Feb 2019 13:53:29 +0100 Subject: [PATCH 03/30] include: Add XFRM attribute identifier for interface ID --- src/include/linux/xfrm.h | 1 + 1 file changed, 1 insertion(+) 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 */ From b32c3ce8feac91a9ea5c447e03516f46c76c54b9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 1 Feb 2019 17:29:20 +0100 Subject: [PATCH 04/30] kernel-netlink: Make interface ID configurable on SAs and policies --- src/libcharon/kernel/kernel_ipsec.h | 4 ++ .../kernel_netlink/kernel_netlink_ipsec.c | 67 +++++++++++++++++-- 2 files changed, 64 insertions(+), 7 deletions(-) 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/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 2819cbe8b..205e772ae 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); @@ -2837,6 +2877,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 +2923,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 +3022,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 +3094,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 +3110,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 +3201,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) { From 72c96dbf9f1e93e841e799af70bbe903c10d66e1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 10:19:36 +0100 Subject: [PATCH 05/30] child-cfg: Add property for interface ID --- src/libcharon/config/child_cfg.c | 23 ++++++++++++++++++++++- src/libcharon/config/child_cfg.h | 14 +++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) 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 */ From f99bd2a5a6e48b3bbcac85d91ccbe097b1f73a65 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 11:10:04 +0100 Subject: [PATCH 06/30] kernel-interface: Consider interface ID when allocating reqids --- src/libcharon/kernel/kernel_interface.c | 29 ++++++++++++++++++++----- src/libcharon/kernel/kernel_interface.h | 11 +++++++--- src/libcharon/sa/child_sa.c | 8 ++++--- 3 files changed, 36 insertions(+), 12 deletions(-) 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/sa/child_sa.c b/src/libcharon/sa/child_sa.c index a341f29bd..f74d0c2ce 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -832,7 +832,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, 0, 0, + &this->reqid); if (status != SUCCESS) { my_ts->destroy(my_ts); @@ -1228,7 +1229,7 @@ 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, 0, 0, &this->reqid); my_ts_list->destroy(my_ts_list); other_ts_list->destroy(other_ts_list); if (status != SUCCESS) @@ -1703,7 +1704,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, + 0, 0) != SUCCESS) { DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); } From 07166ce25ef8cc642b63fd27b0da1b40d1c47e5b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 12:25:53 +0100 Subject: [PATCH 07/30] ipsec-types: Add helper to parse interface ID --- src/libstrongswan/ipsec/ipsec_types.c | 41 ++++++++++++++++++ src/libstrongswan/ipsec/ipsec_types.h | 16 +++++++ src/libstrongswan/tests/suites/test_utils.c | 47 +++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index 6f19cc751..aa0728b6e 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -147,3 +147,44 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark) } return TRUE; } + +/* + * See 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; +} diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h index 7b7bd3743..6750e2294 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -240,4 +240,20 @@ 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); + #endif /** IPSEC_TYPES_H_ @}*/ diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index f1d46ee6b..976d7f4cf 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -940,6 +940,49 @@ 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 + /******************************************************************************* * signature_schemes_for_key */ @@ -1087,6 +1130,10 @@ 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("signature_schemes_for_key"); tcase_add_loop_test(tc, test_signature_schemes_for_key, 0, countof(scheme_data)); suite_add_tcase(s, tc); From 18ed5a07db73624f5eadd93abfd6f02297899c23 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 12:10:06 +0100 Subject: [PATCH 08/30] vici: Make interface ID configurable --- src/libcharon/plugins/vici/vici_config.c | 19 +++++++++++++++++++ src/swanctl/swanctl.opt | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index f1ddde1b4..5750d8741 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -528,6 +528,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 +1222,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 +1705,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, diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 7c71548d0..3f27fee55 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -916,6 +916,24 @@ 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). + connections..children..set_mark_in = 0/0x00000000 Netfilter mark applied to packets after the inbound IPsec SA processed them. From a6014d99b76e757cf7c3df72dd7bb44ca4a20d09 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 11:39:11 +0100 Subject: [PATCH 09/30] child-sa: Configure interface ID on SAs and policies --- src/libcharon/plugins/ha/ha_dispatcher.c | 2 +- src/libcharon/sa/child_sa.c | 104 +++++++++++++++----- src/libcharon/sa/child_sa.h | 15 ++- src/libcharon/sa/ikev1/tasks/quick_mode.c | 6 +- src/libcharon/sa/ikev2/tasks/child_create.c | 4 +- src/libcharon/sa/trap_manager.c | 2 +- 6 files changed, 98 insertions(+), 35 deletions(-) diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index ab845317f..11f3bd914 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -746,7 +746,7 @@ static void process_child_add(private_ha_dispatcher_t *this, 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); + 0, 0, 0, 0); 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/sa/child_sa.c b/src/libcharon/sa/child_sa.c index f74d0c2ce..2ea678067 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,8 +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, 0, 0, - &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); @@ -873,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, @@ -992,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, @@ -1026,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 = { @@ -1099,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, @@ -1132,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 = { @@ -1229,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, 0, 0, &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) @@ -1404,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, @@ -1454,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, @@ -1478,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, @@ -1680,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, @@ -1694,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, @@ -1705,7 +1732,7 @@ METHOD(child_sa_t, destroy, void, { if (charon->kernel->release_reqid(charon->kernel, this->reqid, this->mark_in, this->mark_out, - 0, 0) != SUCCESS) + this->if_id_in, this->if_id_out) != SUCCESS) { DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid); } @@ -1762,11 +1789,11 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) */ 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) + uint32_t mark_in, uint32_t mark_out, + uint32_t if_id_in, uint32_t if_id_out) { private_child_sa_t *this; - static refcount_t unique_id = 0, unique_mark = 0; - refcount_t mark = 0; + static refcount_t unique_id = 0, unique_mark = 0, unique_if_id = 0; INIT(this, .public = { @@ -1789,6 +1816,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, @@ -1824,6 +1852,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), + .if_id_out = config->get_if_id(config, FALSE), .install_time = time_monotonic(NULL), .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES), ); @@ -1839,14 +1869,21 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, { this->mark_out.value = mark_out; } + if (if_id_in) + { + this->if_id_in = if_id_in; + } + if (if_id_out) + { + this->if_id_out = 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) { @@ -1854,19 +1891,32 @@ 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; + } + } + + if (IF_ID_IS_UNIQUE(this->if_id_in) || + IF_ID_IS_UNIQUE(this->if_id_out)) + { + refcount_t if_id = 0; + bool unique_dir = this->if_id_in == IF_ID_UNIQUE_DIR || + this->if_id_out == IF_ID_UNIQUE_DIR; + + if (!unique_dir) + { + if_id = ref_get(&unique_if_id); + } + if (IF_ID_IS_UNIQUE(this->if_id_in)) + { + this->if_id_in = unique_dir ? ref_get(&unique_if_id) : if_id; + } + if (IF_ID_IS_UNIQUE(this->if_id_out)) + { + this->if_id_out = unique_dir ? ref_get(&unique_if_id) : if_id; } } diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index 183033f46..483dd1512 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 @@ -360,6 +360,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. * @@ -514,10 +522,13 @@ struct child_sa_t { * @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 if_id_in explicit inbound interface ID to use, 0 for config + * @param if_id_out explicit outbound interface ID to use, 0 for config * @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); + uint32_t mark_in, uint32_t mark_out, + uint32_t if_id_in, uint32_t if_id_out); #endif /** CHILD_SA_H_ @}*/ diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index b0a42b8bd..876b99e5f 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -845,7 +845,8 @@ METHOD(task_t, build_i, status_t, 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->mark_in, this->mark_out, + 0, 0); if (this->udp && this->mode == MODE_TRANSPORT) { @@ -1185,7 +1186,8 @@ METHOD(task_t, process_r, status_t, 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->mark_in, this->mark_out, + 0, 0); tsi = linked_list_create_with_items(this->tsi, NULL); tsr = linked_list_create_with_items(this->tsr, NULL); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index ce61ef988..1f93be484 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -1110,7 +1110,7 @@ METHOD(task_t, build_i, status_t, 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->mark_in, this->mark_out, 0, 0); if (this->reqid) { @@ -1395,7 +1395,7 @@ METHOD(task_t, build_r, status_t, 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->mark_in, this->mark_out, 0, 0); if (this->ipcomp_received != IPCOMP_NONE) { diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index c35b8c0de..7acbb28c2 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -293,7 +293,7 @@ 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 = child_sa_create(me, other, child, 0, FALSE, 0, 0, 0, 0); list = linked_list_create_with_items(me, NULL); my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); From 8ab336285a920d014c7dc0db29e74679f9df9a68 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 11:59:38 +0100 Subject: [PATCH 10/30] ike: Reuse interface ID during CHILD_SA rekeyings --- src/libcharon/sa/ikev1/task_manager_v1.c | 2 ++ src/libcharon/sa/ikev1/tasks/quick_mode.c | 36 +++++++++++++++++---- src/libcharon/sa/ikev1/tasks/quick_mode.h | 12 +++++-- src/libcharon/sa/ikev2/task_manager_v2.c | 2 ++ src/libcharon/sa/ikev2/tasks/child_create.c | 32 ++++++++++++++---- src/libcharon/sa/ikev2/tasks/child_create.h | 12 +++++-- src/libcharon/sa/ikev2/tasks/child_rekey.c | 6 ++++ 7 files changed, 85 insertions(+), 17 deletions(-) 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 876b99e5f..59f049dbd 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 @@ -158,12 +158,22 @@ struct private_quick_mode_t { /** * Explicit inbound mark value to use, if any */ - u_int mark_in; + uint32_t mark_in; /** - * Explicit inbound mark value to use, if any + * Explicit outbound mark value to use, if any */ - u_int mark_out; + uint32_t mark_out; + + /** + * Explicit inbound interface ID to use, if any + */ + uint32_t if_id_in; + + /** + * Explicit outbound interface ID to use, if any + */ + uint32_t if_id_out; /** * SPI of SA we rekey @@ -846,7 +856,7 @@ METHOD(task_t, build_i, status_t, this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, this->udp, this->mark_in, this->mark_out, - 0, 0); + this->if_id_in, this->if_id_out); if (this->udp && this->mode == MODE_TRANSPORT) { @@ -1048,6 +1058,8 @@ static void check_for_rekeyed_child(private_quick_mode_t *this, bool responder) TRUE).value; this->mark_out = child_sa->get_mark(child_sa, FALSE).value; + this->if_id_in = child_sa->get_if_id(child_sa, TRUE); + this->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), @@ -1187,7 +1199,7 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, this->udp, this->mark_in, this->mark_out, - 0, 0); + this->if_id_in, this->if_id_out); tsi = linked_list_create_with_items(this->tsi, NULL); tsr = linked_list_create_with_items(this->tsr, NULL); @@ -1414,12 +1426,19 @@ METHOD(quick_mode_t, use_reqid, void, } 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; } +METHOD(quick_mode_t, use_if_ids, void, + private_quick_mode_t *this, uint32_t in, uint32_t out) +{ + this->if_id_in = in; + this->if_id_out = out; +} + METHOD(quick_mode_t, rekey, void, private_quick_mode_t *this, uint32_t spi) { @@ -1450,6 +1469,8 @@ METHOD(task_t, migrate, void, this->spi_r = 0; this->mark_in = 0; this->mark_out = 0; + this->if_id_in = 0; + this->if_id_out = 0; if (!this->initiator) { @@ -1490,6 +1511,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 1f93be484..340542b19 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 @@ -176,12 +176,22 @@ struct private_child_create_t { /** * Explicit inbound mark value */ - u_int mark_in; + uint32_t mark_in; /** * Explicit outbound mark value */ - u_int mark_out; + uint32_t mark_out; + + /** + * Explicit inbound interface ID to use, if any + */ + uint32_t if_id_in; + + /** + * Explicit outbound interface ID to use, if any + */ + uint32_t if_id_out; /** * CHILD_SA which gets established @@ -1110,7 +1120,7 @@ METHOD(task_t, build_i, status_t, 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, 0, 0); + this->mark_in, this->mark_out, this->if_id_in, this->if_id_out); if (this->reqid) { @@ -1395,7 +1405,7 @@ METHOD(task_t, build_r, status_t, 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, 0, 0); + this->mark_in, this->mark_out, this->if_id_in, this->if_id_out); if (this->ipcomp_received != IPCOMP_NONE) { @@ -1664,12 +1674,19 @@ METHOD(child_create_t, use_reqid, void, } 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; } +METHOD(child_create_t, use_if_ids, void, + private_child_create_t *this, uint32_t in, uint32_t out) +{ + this->if_id_in = in; + this->if_id_out = out; +} + METHOD(child_create_t, use_dh_group, void, private_child_create_t *this, diffie_hellman_group_t dh_group) { @@ -1748,6 +1765,8 @@ METHOD(task_t, migrate, void, this->reqid = 0; this->mark_in = 0; this->mark_out = 0; + this->if_id_in = 0; + this->if_id_out = 0; this->established = FALSE; } @@ -1797,6 +1816,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); From 3fa8c3e529e8736be5f94d4641ec448e143f4523 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 12:11:10 +0100 Subject: [PATCH 11/30] updown: Pass interface ID to updown script --- src/_updown/_updown.in | 6 ++++++ src/libcharon/plugins/updown/updown_listener.c | 11 +++++++++++ 2 files changed, 17 insertions(+) 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/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", From aa841dbbfcbeacb1c88bb5768a5060b5b209c596 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 12:13:41 +0100 Subject: [PATCH 12/30] vici: Report interface IDs --- src/libcharon/plugins/vici/README.md | 2 ++ src/libcharon/plugins/vici/vici_query.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 5bd8c1727..2b0b7c2d5 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -813,6 +813,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_query.c b/src/libcharon/plugins/vici/vici_query.c index d7b61ca72..e00c1d87d 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) { From 19b6d9a62268017ab806f3aa0d3ff41e7ecd6514 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 16:20:12 +0100 Subject: [PATCH 13/30] swanctl: Report interface IDs in --list-sas --- src/swanctl/commands/list_sas.c | 40 ++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) 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(")"); } From eef923660205a36ca736c4e229b4352d5d00dde8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 Feb 2019 18:26:43 +0100 Subject: [PATCH 14/30] kernel-netlink: Add --list option to XFRM interfaces utility --- src/libcharon/plugins/kernel_netlink/xfrmi.c | 155 ++++++++++++++++++- 1 file changed, 151 insertions(+), 4 deletions(-) diff --git a/src/libcharon/plugins/kernel_netlink/xfrmi.c b/src/libcharon/plugins/kernel_netlink/xfrmi.c index 0555ac4ce..d2d0e8093 100644 --- a/src/libcharon/plugins/kernel_netlink/xfrmi.c +++ b/src/libcharon/plugins/kernel_netlink/xfrmi.c @@ -103,6 +103,148 @@ static int add_xfrm_interface(char *name, uint32_t xfrm_id, uint32_t ifindex) 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; + + /* the following attributes are nested under this one */ + linkinfo = netlink_reserve(hdr, sizeof(request), IFLA_LINKINFO, 0); + linkinfo = (void*)linkinfo - RTA_LENGTH(0); + + netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), + sizeof(request)); + + linkinfo->rta_len = NLMSG_TAIL(hdr) - (void*)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"); @@ -110,6 +252,7 @@ static void usage(FILE *out, char *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"); @@ -122,23 +265,30 @@ int main(int argc, char *argv[]) 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, "hvn:i:d:", long_opts, NULL)) + 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; @@ -177,8 +327,5 @@ int main(int argc, char *argv[]) return 1; } - library_init(NULL, "xfrmi"); - atexit(library_deinit); - return add_xfrm_interface(name, xfrm_id, ifindex); } From d74ddd78937506436ba1ec8ebfbbd8a2cdaf0cc0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 13 Feb 2019 10:33:59 +0100 Subject: [PATCH 15/30] xfrmi: Move to a separate directory to fix monolithic build --- configure.ac | 1 + src/Makefile.am | 4 ++++ src/libcharon/plugins/kernel_netlink/.gitignore | 1 - src/libcharon/plugins/kernel_netlink/Makefile.am | 7 ------- src/xfrmi/.gitignore | 1 + src/xfrmi/Makefile.am | 13 +++++++++++++ .../plugins/kernel_netlink => xfrmi}/xfrmi.c | 0 7 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 src/xfrmi/.gitignore create mode 100644 src/xfrmi/Makefile.am rename src/{libcharon/plugins/kernel_netlink => xfrmi}/xfrmi.c (100%) diff --git a/configure.ac b/configure.ac index 62f8c11e8..f01b07862 100644 --- a/configure.ac +++ b/configure.ac @@ -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/libcharon/plugins/kernel_netlink/.gitignore b/src/libcharon/plugins/kernel_netlink/.gitignore index 5d4f4c897..e05064df6 100644 --- a/src/libcharon/plugins/kernel_netlink/.gitignore +++ b/src/libcharon/plugins/kernel_netlink/.gitignore @@ -1,2 +1 @@ kernel_netlink_tests -xfrmi diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.am b/src/libcharon/plugins/kernel_netlink/Makefile.am index 1600f8ece..71bc09d5b 100644 --- a/src/libcharon/plugins/kernel_netlink/Makefile.am +++ b/src/libcharon/plugins/kernel_netlink/Makefile.am @@ -24,13 +24,6 @@ libstrongswan_kernel_netlink_la_LIBADD = $(DLLIB) libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version -ipsec_PROGRAMS = xfrmi -xfrmi_SOURCES = xfrmi.c -xfrmi_LDADD = \ - libstrongswan-kernel-netlink.la \ - $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(top_builddir)/src/libcharon/libcharon.la - TESTS = kernel_netlink_tests check_PROGRAMS = $(TESTS) 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/libcharon/plugins/kernel_netlink/xfrmi.c b/src/xfrmi/xfrmi.c similarity index 100% rename from src/libcharon/plugins/kernel_netlink/xfrmi.c rename to src/xfrmi/xfrmi.c From 5b2078ad09fffe4f0f1e0099a82ee5ab18176135 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 19 Feb 2019 10:16:31 +0100 Subject: [PATCH 16/30] testing: Enable XFRM interfaces and network namespaces in 4.19 and 5.0 kernel --- testing/config/kernel/config-4.19 | 8 ++++---- testing/config/kernel/config-5.0 | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) 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 From 801a5d31331a5be47a3225a3a35276fffb12712f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 1 Mar 2019 10:19:32 +0100 Subject: [PATCH 17/30] kernel-netlink: Don't install routes for CHILD_SAs with interface ID --- src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c | 4 +++- src/swanctl/swanctl.opt | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 205e772ae..27bb3792d 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2846,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)) diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 3f27fee55..c02c5743a 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -934,6 +934,8 @@ connections..children..if_id_out = 0 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. From 760d7c9b4fb10610423bd987785a87ac7b1df82a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 8 Feb 2019 18:57:38 +0100 Subject: [PATCH 18/30] testing: Add scenarios that use XFRM interfaces The network namespace scenario requires a kernel patch in 4.19 and 4.20 kernels (the fix is included in 5.0 kernels). --- .../net2net-xfrmi-netns/description.txt | 22 ++++++++++++ .../net2net-xfrmi-netns/evaltest.dat | 18 ++++++++++ .../hosts/moon/etc/strongswan.conf | 9 +++++ .../hosts/moon/etc/swanctl/swanctl.conf | 30 ++++++++++++++++ .../hosts/sun/etc/strongswan.conf | 9 +++++ .../hosts/sun/etc/swanctl/swanctl.conf | 32 +++++++++++++++++ .../net2net-xfrmi-netns/hosts/sun/etc/updown | 18 ++++++++++ .../net2net-xfrmi-netns/posttest.dat | 8 +++++ .../net2net-xfrmi-netns/pretest.dat | 12 +++++++ .../route-based/net2net-xfrmi-netns/test.conf | 25 ++++++++++++++ .../route-based/net2net-xfrmi/description.txt | 17 ++++++++++ .../route-based/net2net-xfrmi/evaltest.dat | 5 +++ .../hosts/moon/etc/strongswan.conf | 9 +++++ .../hosts/moon/etc/swanctl/swanctl.conf | 30 ++++++++++++++++ .../hosts/sun/etc/strongswan.conf | 9 +++++ .../hosts/sun/etc/swanctl/swanctl.conf | 32 +++++++++++++++++ .../net2net-xfrmi/hosts/sun/etc/updown | 23 +++++++++++++ .../route-based/net2net-xfrmi/posttest.dat | 10 ++++++ .../route-based/net2net-xfrmi/pretest.dat | 14 ++++++++ .../tests/route-based/net2net-xfrmi/test.conf | 25 ++++++++++++++ .../rw-shared-xfrmi/description.txt | 12 +++++++ .../route-based/rw-shared-xfrmi/evaltest.dat | 10 ++++++ .../hosts/carol/etc/strongswan.conf | 9 +++++ .../hosts/carol/etc/swanctl/swanctl.conf | 28 +++++++++++++++ .../hosts/dave/etc/strongswan.conf | 9 +++++ .../hosts/dave/etc/swanctl/swanctl.conf | 28 +++++++++++++++ .../hosts/moon/etc/strongswan.conf | 9 +++++ .../hosts/moon/etc/swanctl/swanctl.conf | 34 +++++++++++++++++++ .../route-based/rw-shared-xfrmi/posttest.dat | 10 ++++++ .../route-based/rw-shared-xfrmi/pretest.dat | 16 +++++++++ .../route-based/rw-shared-xfrmi/test.conf | 25 ++++++++++++++ 31 files changed, 547 insertions(+) create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/description.txt create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/evaltest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/route-based/net2net-xfrmi-netns/hosts/moon/etc/swanctl/swanctl.conf create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/strongswan.conf create mode 100755 testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/swanctl/swanctl.conf create mode 100755 testing/tests/route-based/net2net-xfrmi-netns/hosts/sun/etc/updown create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/posttest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/pretest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi-netns/test.conf create mode 100644 testing/tests/route-based/net2net-xfrmi/description.txt create mode 100644 testing/tests/route-based/net2net-xfrmi/evaltest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/route-based/net2net-xfrmi/hosts/moon/etc/swanctl/swanctl.conf create mode 100644 testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/strongswan.conf create mode 100755 testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/swanctl/swanctl.conf create mode 100755 testing/tests/route-based/net2net-xfrmi/hosts/sun/etc/updown create mode 100644 testing/tests/route-based/net2net-xfrmi/posttest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi/pretest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi/test.conf create mode 100644 testing/tests/route-based/rw-shared-xfrmi/description.txt create mode 100644 testing/tests/route-based/rw-shared-xfrmi/evaltest.dat create mode 100755 testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/strongswan.conf create mode 100755 testing/tests/route-based/rw-shared-xfrmi/hosts/carol/etc/swanctl/swanctl.conf create mode 100755 testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/strongswan.conf create mode 100755 testing/tests/route-based/rw-shared-xfrmi/hosts/dave/etc/swanctl/swanctl.conf create mode 100644 testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/route-based/rw-shared-xfrmi/hosts/moon/etc/swanctl/swanctl.conf create mode 100644 testing/tests/route-based/rw-shared-xfrmi/posttest.dat create mode 100644 testing/tests/route-based/rw-shared-xfrmi/pretest.dat create mode 100644 testing/tests/route-based/rw-shared-xfrmi/test.conf 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 From 2feba5aa226480fc7b3d22f97661a848cf982620 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 8 Mar 2019 15:20:40 +0100 Subject: [PATCH 19/30] kernel-netlink: Add helper functions for nested attributes --- .../kernel_netlink/kernel_netlink_shared.c | 49 +++++++++++++++++-- .../kernel_netlink/kernel_netlink_shared.h | 24 ++++++++- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index 4014d3cbe..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) @@ -709,9 +710,10 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, } /** - * 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; @@ -725,6 +727,43 @@ void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len) rta->rta_type = type; rta->rta_len = RTA_LENGTH(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. * From 28b702ad8bc5cefb01332b047cc96a3787e03d64 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 8 Mar 2019 15:22:58 +0100 Subject: [PATCH 20/30] xfrmi: Use helpers for nested Netlink attributes --- src/xfrmi/xfrmi.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/xfrmi/xfrmi.c b/src/xfrmi/xfrmi.c index d2d0e8093..4fed54cb2 100644 --- a/src/xfrmi/xfrmi.c +++ b/src/xfrmi/xfrmi.c @@ -33,8 +33,6 @@ enum { #define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1) #endif -#define NLMSG_TAIL(nlh) ((void*)(((char*)nlh) + NLMSG_ALIGN(nlh->nlmsg_len))) - /** * Create an XFRM interface with the given ID and underlying interface */ @@ -66,25 +64,20 @@ static int add_xfrm_interface(char *name, uint32_t xfrm_id, uint32_t ifindex) netlink_add_attribute(hdr, IFLA_IFNAME, chunk_from_str(name), sizeof(request)); - /* the following attributes are nested under this one */ - linkinfo = netlink_reserve(hdr, sizeof(request), IFLA_LINKINFO, 0); - linkinfo = (void*)linkinfo - RTA_LENGTH(0); + linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO); netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), sizeof(request)); - /* the following attributes are nested under this one */ - info_data = netlink_reserve(hdr, sizeof(request), IFLA_INFO_DATA, 0); - info_data = (void*)info_data - RTA_LENGTH(0); + 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)); - info_data->rta_len = NLMSG_TAIL(hdr) - (void*)info_data; - - linkinfo->rta_len = NLMSG_TAIL(hdr) - (void*)linkinfo; + netlink_nested_end(hdr, info_data); + netlink_nested_end(hdr, linkinfo); switch (socket->send_ack(socket, hdr)) { @@ -184,14 +177,12 @@ static int list_xfrm_interfaces() msg = NLMSG_DATA(hdr); msg->ifi_family = AF_UNSPEC; - /* the following attributes are nested under this one */ - linkinfo = netlink_reserve(hdr, sizeof(request), IFLA_LINKINFO, 0); - linkinfo = (void*)linkinfo - RTA_LENGTH(0); + linkinfo = netlink_nested_start(hdr, sizeof(request), IFLA_LINKINFO); netlink_add_attribute(hdr, IFLA_INFO_KIND, chunk_from_str("xfrm"), sizeof(request)); - linkinfo->rta_len = NLMSG_TAIL(hdr) - (void*)linkinfo; + netlink_nested_end(hdr, linkinfo); if (socket->send(socket, hdr, &out, &len) != SUCCESS) { From c56b8c1a26bf7c376255eacf3652de453cc32d97 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Mar 2019 16:23:59 +0100 Subject: [PATCH 21/30] peer-cfg: Add property for interface ID --- src/libcharon/config/peer_cfg.c | 23 ++++++++++++++++++++++- src/libcharon/config/peer_cfg.h | 14 +++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) 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 */ From 9347f72494e608c40a90c300478ceb43280a0b93 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Mar 2019 17:18:00 +0100 Subject: [PATCH 22/30] ipsec-types: Move allocation of unique interface IDs to helper function --- src/libcharon/sa/child_sa.c | 25 ++----------- src/libstrongswan/ipsec/ipsec_types.c | 30 +++++++++++++++- src/libstrongswan/ipsec/ipsec_types.h | 8 +++++ src/libstrongswan/tests/suites/test_utils.c | 39 +++++++++++++++++++++ 4 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 2ea678067..40137d3c2 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1793,7 +1793,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, uint32_t if_id_in, uint32_t if_id_out) { private_child_sa_t *this; - static refcount_t unique_id = 0, unique_mark = 0, unique_if_id = 0; + static refcount_t unique_id = 0, unique_mark = 0; INIT(this, .public = { @@ -1878,6 +1878,8 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->if_id_out = 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)) { @@ -1899,27 +1901,6 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, } } - if (IF_ID_IS_UNIQUE(this->if_id_in) || - IF_ID_IS_UNIQUE(this->if_id_out)) - { - refcount_t if_id = 0; - bool unique_dir = this->if_id_in == IF_ID_UNIQUE_DIR || - this->if_id_out == IF_ID_UNIQUE_DIR; - - if (!unique_dir) - { - if_id = ref_get(&unique_if_id); - } - if (IF_ID_IS_UNIQUE(this->if_id_in)) - { - this->if_id_in = unique_dir ? ref_get(&unique_if_id) : if_id; - } - if (IF_ID_IS_UNIQUE(this->if_id_out)) - { - this->if_id_out = unique_dir ? ref_get(&unique_if_id) : if_id; - } - } - if (!this->reqid) { /* reuse old reqid if we are rekeying an existing CHILD_SA and when diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index aa0728b6e..2f0f31abd 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -149,7 +149,7 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark) } /* - * See header + * Described in header */ bool if_id_from_string(const char *value, uint32_t *if_id) { @@ -188,3 +188,31 @@ bool if_id_from_string(const char *value, uint32_t *if_id) } 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 6750e2294..1c61fecfe 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -256,4 +256,12 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark); */ 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 976d7f4cf..27343349e 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -983,6 +983,41 @@ START_TEST(test_if_id_from_string) } 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 */ @@ -1134,6 +1169,10 @@ Suite *utils_suite_create() 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); From dec3c184a6450ad62c184e1c5ccefb18264dce64 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Mar 2019 17:19:51 +0100 Subject: [PATCH 23/30] ike-sa: Add property for interface ID --- src/libcharon/sa/ike_sa.c | 27 +++++++++++++++++++++++++-- src/libcharon/sa/ike_sa.h | 10 +++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) 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. * From fafa76984d62b6126ba61b2be2383bcb0225ea10 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Mar 2019 17:39:47 +0100 Subject: [PATCH 24/30] child-sa: Pass default interface ID inherited from IKE_SA Also pass optional arguments as struct. --- src/libcharon/plugins/ha/ha_dispatcher.c | 7 +- src/libcharon/sa/child_sa.c | 34 ++++---- src/libcharon/sa/child_sa.h | 38 ++++++--- src/libcharon/sa/ikev1/tasks/quick_mode.c | 95 ++++++++------------- src/libcharon/sa/ikev2/tasks/child_create.c | 65 ++++++-------- src/libcharon/sa/trap_manager.c | 9 +- 6 files changed, 117 insertions(+), 131 deletions(-) diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 11f3bd914..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, 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/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 40137d3c2..fc60b413f 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1784,13 +1784,11 @@ 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, - uint32_t mark_in, uint32_t mark_out, - uint32_t if_id_in, uint32_t if_id_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; @@ -1839,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), @@ -1852,8 +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), - .if_id_out = config->get_if_id(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), ); @@ -1861,21 +1859,21 @@ 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 (if_id_in) + if (data->if_id_in) { - this->if_id_in = if_id_in; + this->if_id_in = data->if_id_in; } - if (if_id_out) + if (data->if_id_out) { - this->if_id_out = if_id_out; + this->if_id_out = data->if_id_out; } allocate_unique_if_ids(&this->if_id_in, &this->if_id_out); @@ -1911,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 483dd1512..c9b3f63e2 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -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 @@ -512,23 +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 if_id_in explicit inbound interface ID to use, 0 for config - * @param if_id_out explicit outbound interface ID 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, - uint32_t mark_in, uint32_t mark_out, - uint32_t if_id_in, uint32_t if_id_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/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 59f049dbd..3309a5ddc 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -151,29 +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 - */ - uint32_t mark_in; - - /** - * Explicit outbound mark value to use, if any - */ - uint32_t mark_out; - - /** - * Explicit inbound interface ID to use, if any - */ - uint32_t if_id_in; - - /** - * Explicit outbound interface ID to use, if any - */ - uint32_t if_id_out; + child_sa_create_t child; /** * SPI of SA we rekey @@ -195,11 +175,6 @@ struct private_quick_mode_t { */ protocol_id_t proto; - /** - * Use UDP encapsulation - */ - bool udp; - /** * Message ID of handled quick mode exchange */ @@ -637,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", @@ -849,16 +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->if_id_in, this->if_id_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); @@ -925,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); @@ -1037,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)) { @@ -1052,14 +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->if_id_in = child_sa->get_if_id(child_sa, TRUE); - this->if_id_out = child_sa->get_if_id(child_sa, FALSE); + 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), @@ -1102,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)) { @@ -1193,13 +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->if_id_in, this->if_id_out); + this->config, &this->child); tsi = linked_list_create_with_items(this->tsi, NULL); tsr = linked_list_create_with_items(this->tsr, NULL); @@ -1291,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); @@ -1422,21 +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, 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->if_id_in = in; - this->if_id_out = out; + this->child.if_id_in = in; + this->child.if_id_out = out; } METHOD(quick_mode_t, rekey, void, @@ -1467,10 +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->if_id_in = 0; - this->if_id_out = 0; + this->child = (child_sa_create_t){}; if (!this->initiator) { diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 340542b19..d74013b8f 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -169,29 +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 - */ - uint32_t mark_in; - - /** - * Explicit outbound mark value - */ - uint32_t mark_out; - - /** - * Explicit inbound interface ID to use, if any - */ - uint32_t if_id_in; - - /** - * Explicit outbound interface ID to use, if any - */ - uint32_t if_id_out; + child_sa_create_t child; /** * CHILD_SA which gets established @@ -227,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); @@ -1117,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->if_id_in, this->if_id_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 { @@ -1402,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->if_id_in, this->if_id_out); + this->ike_sa->get_other_host(this->ike_sa), + this->config, &this->child); if (this->ipcomp_received != IPCOMP_NONE) { @@ -1670,21 +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, 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->if_id_in = in; - this->if_id_out = out; + this->child.if_id_in = in; + this->child.if_id_out = out; } METHOD(child_create_t, use_dh_group, void, @@ -1762,12 +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->if_id_in = 0; - this->if_id_out = 0; this->established = FALSE; + this->child = (child_sa_create_t){}; } METHOD(task_t, destroy, void, diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 7acbb28c2..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, 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); From 0396969a36bed41694f177e6ea5b01bdb9f1631d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 22 Mar 2019 18:33:46 +0100 Subject: [PATCH 25/30] vici: Add support for interface ID configurable on IKE_SA --- src/libcharon/plugins/vici/README.md | 2 ++ src/libcharon/plugins/vici/vici_config.c | 8 ++++++++ src/libcharon/plugins/vici/vici_query.c | 12 ++++++++++++ src/swanctl/swanctl.opt | 12 ++++++++++++ 4 files changed, 34 insertions(+) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 2b0b7c2d5..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 = diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 5750d8741..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) @@ -1785,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 }, @@ -2523,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 e00c1d87d..16e3c8b1f 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -354,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; @@ -400,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/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index c02c5743a..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. From ebe2bedebd3a54bd06348a6d7ee4efe6fcc0e5a3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 25 Mar 2019 14:26:11 +0100 Subject: [PATCH 26/30] configure: Fix package version for python packages for developer releases According to PEP 440 the suffix for development releases is .devN and not just devN. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f01b07862..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]) From ceca26c88e1e940868ff36cf7305032b38d96577 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 25 Mar 2019 14:28:21 +0100 Subject: [PATCH 27/30] testing: Enable Python eggs in testing environment (i.e. vici's Python bindings) --- testing/scripts/recipes/013_strongswan.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/scripts/recipes/013_strongswan.mk b/testing/scripts/recipes/013_strongswan.mk index 13a1b23c8..a6a29c1e9 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 From 181801317b0c8462de5342e117c568f839c70d78 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 27 Mar 2019 11:04:57 +0100 Subject: [PATCH 28/30] testing: Add /etc/resolv.conf when building strongSwan --- testing/scripts/build-strongswan | 4 ++++ 1 file changed, 4 insertions(+) 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" From 14e999c8d5b53a7888560417336fe649bada5189 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 27 Mar 2019 11:13:41 +0100 Subject: [PATCH 29/30] testing: Install python-daemon with strongSwan for use in updown scripts --- testing/scripts/recipes/013_strongswan.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/scripts/recipes/013_strongswan.mk b/testing/scripts/recipes/013_strongswan.mk index a6a29c1e9..e84d42094 100644 --- a/testing/scripts/recipes/013_strongswan.mk +++ b/testing/scripts/recipes/013_strongswan.mk @@ -127,3 +127,5 @@ build: configure install: build cd $(BUILDDIR) && make -j install + # for Python-based updown scripts + pip install python-daemon From 072de7c150f0afc1de94623162e17e751d8fdee8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 25 Mar 2019 18:31:28 +0100 Subject: [PATCH 30/30] testing: Add scenario that uses IKE-specific interface IDs --- .../net2net-xfrmi-ike/description.txt | 18 ++++ .../net2net-xfrmi-ike/evaltest.dat | 6 ++ .../hosts/moon/etc/strongswan.conf | 9 ++ .../hosts/moon/etc/swanctl/swanctl.conf | 33 +++++++ .../hosts/sun/etc/strongswan.conf | 12 +++ .../hosts/sun/etc/swanctl/swanctl.conf | 30 +++++++ .../net2net-xfrmi-ike/hosts/sun/etc/updown.py | 86 +++++++++++++++++++ .../net2net-xfrmi-ike/posttest.dat | 10 +++ .../route-based/net2net-xfrmi-ike/pretest.dat | 15 ++++ .../route-based/net2net-xfrmi-ike/test.conf | 25 ++++++ 10 files changed, 244 insertions(+) create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/description.txt create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/evaltest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/strongswan.conf create mode 100755 testing/tests/route-based/net2net-xfrmi-ike/hosts/moon/etc/swanctl/swanctl.conf create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/strongswan.conf create mode 100755 testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/swanctl/swanctl.conf create mode 100755 testing/tests/route-based/net2net-xfrmi-ike/hosts/sun/etc/updown.py create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/posttest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/pretest.dat create mode 100644 testing/tests/route-based/net2net-xfrmi-ike/test.conf 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