From 9cee688f78c601387c6a7e3699c84a04683a3ebe Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 20 Apr 2018 14:01:12 +0200 Subject: [PATCH 1/7] kernel-netlink: Add support for setting mark/mask an SA should apply to processed traffic --- src/include/linux/xfrm.h | 3 ++ src/libcharon/kernel/kernel_ipsec.h | 4 +- .../kernel_netlink/kernel_netlink_ipsec.c | 45 +++++++++++++------ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/include/linux/xfrm.h b/src/include/linux/xfrm.h index dbaa4f128..35261c9a5 100644 --- a/src/include/linux/xfrm.h +++ b/src/include/linux/xfrm.h @@ -302,8 +302,11 @@ enum xfrm_attr_type_t { XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ XFRMA_PAD, XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ + XFRMA_SET_MARK, /* __u32 */ + XFRMA_SET_MARK_MASK, /* __u32 */ __XFRMA_MAX +#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */ #define XFRMA_MAX (__XFRMA_MAX - 1) }; diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h index 195c80cff..4158eb45e 100644 --- a/src/libcharon/kernel/kernel_ipsec.h +++ b/src/libcharon/kernel/kernel_ipsec.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2016 Andreas Steffen - * Copyright (C) 2006-2016 Tobias Brunner + * Copyright (C) 2006-2018 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -93,6 +93,8 @@ struct kernel_ipsec_add_sa_t { bool encap; /** no (disabled), yes (enabled), auto (enabled if supported) */ hw_offload_t hw_offload; + /** Mark the SA should apply to packets after processing */ + mark_t mark; /** TRUE to use Extended Sequence Numbers */ bool esn; /** TRUE to copy the DF bit to the outer IPv4 header in tunnel mode */ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 33e982d22..1292e0895 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1335,6 +1335,23 @@ static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark) return TRUE; } +/** + * Add a uint32 attribute to message + */ +static bool add_uint32(struct nlmsghdr *hdr, int buflen, + enum xfrm_attr_type_t type, uint32_t value) +{ + uint32_t *xvalue; + + xvalue = netlink_reserve(hdr, buflen, type, sizeof(*xvalue)); + if (!xvalue) + { + return FALSE; + } + *xvalue = value; + return TRUE; +} + /** * Check if kernel supports HW offload */ @@ -1616,16 +1633,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t, case DSCP_COPY_IN_ONLY: case DSCP_COPY_NO: { - uint32_t *xflags; - - xflags = netlink_reserve(hdr, sizeof(request), - XFRMA_SA_EXTRA_FLAGS, sizeof(*xflags)); - if (!xflags) + /* currently the only extra flag */ + if (!add_uint32(hdr, sizeof(request), XFRMA_SA_EXTRA_FLAGS, + XFRM_SA_XFLAG_DONT_ENCAP_DSCP)) { goto failed; } - /* currently the only extra flag */ - *xflags |= XFRM_SA_XFLAG_DONT_ENCAP_DSCP; break; } default: @@ -1876,17 +1889,23 @@ METHOD(kernel_ipsec_t, add_sa, status_t, goto failed; } + if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask)) + { + if (!add_uint32(hdr, sizeof(request), XFRMA_SET_MARK, + data->mark.value) || + !add_uint32(hdr, sizeof(request), XFRMA_SET_MARK_MASK, + data->mark.mask)) + { + goto failed; + } + } + if (data->tfc && id->proto == IPPROTO_ESP && mode == MODE_TUNNEL) { /* the kernel supports TFC padding only for tunnel mode ESP SAs */ - uint32_t *tfcpad; - - tfcpad = netlink_reserve(hdr, sizeof(request), XFRMA_TFCPAD, - sizeof(*tfcpad)); - if (!tfcpad) + if (!add_uint32(hdr, sizeof(request), XFRMA_TFCPAD, data->tfc)) { goto failed; } - *tfcpad = data->tfc; } if (id->proto != IPPROTO_COMP) From fa4d4012aeb78fc34b08b10c284af0862778b87b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 20 Apr 2018 14:02:57 +0200 Subject: [PATCH 2/7] child-cfg: Add properties for in-/outbound mark the SA should set --- src/libcharon/config/child_cfg.c | 25 ++++++++++++++++++++++++- src/libcharon/config/child_cfg.h | 16 ++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c index 98c15782b..14148ed03 100644 --- a/src/libcharon/config/child_cfg.c +++ b/src/libcharon/config/child_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -123,6 +123,16 @@ struct private_child_cfg_t { */ mark_t mark_out; + /** + * Optional mark to set to packets after inbound processing + */ + mark_t set_mark_in; + + /** + * Optional mark to set to packets after outbound processing + */ + mark_t set_mark_out; + /** * Traffic Flow Confidentiality padding, if enabled */ @@ -547,6 +557,12 @@ METHOD(child_cfg_t, get_mark, mark_t, return inbound ? this->mark_in : this->mark_out; } +METHOD(child_cfg_t, get_set_mark, mark_t, + private_child_cfg_t *this, bool inbound) +{ + return inbound ? this->set_mark_in : this->set_mark_out; +} + METHOD(child_cfg_t, get_tfc, uint32_t, private_child_cfg_t *this) { @@ -620,6 +636,10 @@ METHOD(child_cfg_t, equals, bool, this->mark_in.mask == other->mark_in.mask && this->mark_out.value == other->mark_out.value && this->mark_out.mask == other->mark_out.mask && + this->set_mark_in.value == other->set_mark_in.value && + this->set_mark_in.mask == other->set_mark_in.mask && + this->set_mark_out.value == other->set_mark_out.value && + this->set_mark_out.mask == other->set_mark_out.mask && this->tfc == other->tfc && this->manual_prio == other->manual_prio && this->replay_window == other->replay_window && @@ -676,6 +696,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data) .get_inactivity = _get_inactivity, .get_reqid = _get_reqid, .get_mark = _get_mark, + .get_set_mark = _get_set_mark, .get_tfc = _get_tfc, .get_manual_prio = _get_manual_prio, .get_interface = _get_interface, @@ -698,6 +719,8 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data) .close_action = data->close_action, .mark_in = data->mark_in, .mark_out = data->mark_out, + .set_mark_in = data->set_mark_in, + .set_mark_out = data->set_mark_out, .lifetime = data->lifetime, .inactivity = data->inactivity, .tfc = data->tfc, diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index f552dae71..e3b59e656 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2016 Andreas Steffen * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -227,13 +227,21 @@ struct child_cfg_t { uint32_t (*get_reqid)(child_cfg_t *this); /** - * Optional mark for CHILD_SA. + * Optional mark to set on policies/SAs. * * @param inbound TRUE for inbound, FALSE for outbound * @return mark */ mark_t (*get_mark)(child_cfg_t *this, bool inbound); + /** + * Optional mark the SAs should apply after processing packets. + * + * @param inbound TRUE for inbound, FALSE for outbound + * @return mark + */ + mark_t (*get_set_mark)(child_cfg_t *this, bool inbound); + /** * Get the TFC padding value to use for CHILD_SA. * @@ -346,6 +354,10 @@ struct child_cfg_create_t { mark_t mark_in; /** Optional outbound mark */ mark_t mark_out; + /** Optional inbound mark the SA should apply to traffic */ + mark_t set_mark_in; + /** Optional outbound mark the SA should apply to traffic */ + mark_t set_mark_out; /** Mode to propose for CHILD_SA */ ipsec_mode_t mode; /** TFC padding size, 0 to disable, -1 to pad to PMTU */ From f59450fde6622cddcd9d55074dcc8e67fb0af820 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 20 Apr 2018 14:08:35 +0200 Subject: [PATCH 3/7] child-sa: Configure in-/outbound mark the SA should set --- src/libcharon/sa/child_sa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index d06996e43..efa790fc5 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -890,6 +890,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, .cpi = cpi, .encap = this->encap, .hw_offload = this->config->get_hw_offload(this->config), + .mark = this->config->get_set_mark(this->config, inbound), .esn = esn, .copy_df = !this->config->has_option(this->config, OPT_NO_COPY_DF), .copy_ecn = !this->config->has_option(this->config, OPT_NO_COPY_ECN), From 60f78969230f5654a102cf4a8e1403efc4775f38 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 20 Apr 2018 14:12:48 +0200 Subject: [PATCH 4/7] vici: Make in-/outbound marks the SA should set configurable --- src/libcharon/plugins/vici/vici_config.c | 6 ++++++ src/swanctl/swanctl.opt | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 7a66af1f4..05fa8c5ca 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -528,6 +528,10 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " mark_in_sa = %u", has_opt(OPT_MARK_IN_SA)); DBG2(DBG_CFG, " mark_out = %u/%u", cfg->mark_out.value, cfg->mark_out.mask); + DBG2(DBG_CFG, " set_mark_in = %u/%u", + cfg->set_mark_in.value, cfg->set_mark_in.mask); + DBG2(DBG_CFG, " set_mark_out = %u/%u", + cfg->set_mark_out.value, cfg->set_mark_out.mask); DBG2(DBG_CFG, " inactivity = %llu", cfg->inactivity); DBG2(DBG_CFG, " proposals = %#P", data->proposals); DBG2(DBG_CFG, " local_ts = %#R", data->local_ts); @@ -1639,6 +1643,8 @@ CALLBACK(child_kv, bool, { "mark_in", parse_mark, &child->cfg.mark_in }, { "mark_in_sa", parse_opt_mark_in, &child->cfg.options }, { "mark_out", parse_mark, &child->cfg.mark_out }, + { "set_mark_in", parse_mark, &child->cfg.set_mark_in }, + { "set_mark_out", parse_mark, &child->cfg.set_mark_out }, { "tfc_padding", parse_tfc, &child->cfg.tfc }, { "priority", parse_uint32, &child->cfg.priority }, { "interface", parse_string, &child->cfg.interface }, diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 3f67b938a..8cdd66c3f 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -910,6 +910,28 @@ 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..set_mark_in = 0/0x00000000 + Netfilter mark applied to packets after the inbound IPsec SA processed them. + + Netfilter mark applied to packets after the inbound IPsec SA processed them. + This way it's not necessary to mark packets via Netfilter before decryption + or right afterwards to match policies or process them differently (e.g. via + policy routing). + + An additional mask may be appended to the mark, separated by _/_. The + default mask if omitted is 0xffffffff. + +connections..children..set_mark_out = 0/0x00000000 + Netfilter mark applied to packets after the outbound IPsec SA processed + them. + + Netfilter mark applied to packets after the outbound IPsec SA processed + them. This allows processing ESP packets differently than the original + traffic (e.g. via policy routing). + + An additional mask may be appended to the mark, separated by _/_. The + default mask if omitted is 0xffffffff. + connections..children..tfc_padding = 0 Traffic Flow Confidentiality padding. From b9aacf9adc96b11c6cd140e4e43a781a5c7a6304 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 14 May 2018 12:55:27 +0200 Subject: [PATCH 5/7] vici: Document kernel requirements for set_mark_in/set_mark_out options --- src/swanctl/swanctl.opt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 8cdd66c3f..79655ed35 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -921,6 +921,8 @@ connections..children..set_mark_in = 0/0x00000000 An additional mask may be appended to the mark, separated by _/_. The default mask if omitted is 0xffffffff. + Setting marks in XFRM input requires Linux 4.19 or higher. + connections..children..set_mark_out = 0/0x00000000 Netfilter mark applied to packets after the outbound IPsec SA processed them. @@ -932,6 +934,9 @@ connections..children..set_mark_out = 0/0x00000000 An additional mask may be appended to the mark, separated by _/_. The default mask if omitted is 0xffffffff. + Setting marks in XFRM output is supported since Linux 4.14. Setting a mask + requires at least Linux 4.19. + connections..children..tfc_padding = 0 Traffic Flow Confidentiality padding. From ebd2d3877e146ad128a06dcec9e8aa3ea85adffd Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 14 May 2018 13:42:53 +0200 Subject: [PATCH 6/7] ipsec-types: Restrict the use of %unique and other keywords when parsing marks %unique (and the upcoming %same key) are usable in specific contexts only. To restrict the user from using it in other places where it does not get the expected results, reject such keywords unless explicitly allowed. --- .../kernel_netlink/kernel_netlink_net.c | 2 +- .../socket_default/socket_default_socket.c | 2 +- src/libcharon/plugins/vici/vici_config.c | 2 +- src/libstrongswan/ipsec/ipsec_types.c | 7 +- src/libstrongswan/ipsec/ipsec_types.h | 14 ++- src/libstrongswan/tests/suites/test_utils.c | 88 ++++++++++++------- src/starter/confread.c | 6 +- 7 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index 3ef3dc712..c9a76ba01 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -2925,7 +2925,7 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type, msg->rtm_flags |= FIB_RULE_INVERT; fwmark++; } - if (mark_from_string(fwmark, &mark)) + if (mark_from_string(fwmark, MARK_OP_NONE, &mark)) { chunk = chunk_from_thing(mark.value); netlink_add_attribute(hdr, FRA_FWMARK, chunk, sizeof(request)); diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 57e092968..68e5a7a0e 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -745,7 +745,7 @@ static int open_socket(private_socket_default_socket_t *this, fwmark = lib->settings->get_str(lib->settings, "%s.plugins.socket-default.fwmark", NULL, lib->ns); - if (fwmark && mark_from_string(fwmark, &mark)) + if (fwmark && mark_from_string(fwmark, MARK_OP_NONE, &mark)) { if (setsockopt(skt, SOL_SOCKET, SO_MARK, &mark.value, sizeof(mark.value)) < 0) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 05fa8c5ca..98f68264c 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -1181,7 +1181,7 @@ CALLBACK(parse_mark, bool, { return FALSE; } - return mark_from_string(buf, out); + return mark_from_string(buf, MARK_OP_UNIQUE, out); } /** diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index d231bb3a4..7eed1566a 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -69,7 +69,7 @@ bool ipsec_sa_cfg_equals(ipsec_sa_cfg_t *a, ipsec_sa_cfg_t *b) /* * See header */ -bool mark_from_string(const char *value, mark_t *mark) +bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark) { char *endptr; @@ -79,6 +79,11 @@ bool mark_from_string(const char *value, mark_t *mark) } if (strcasepfx(value, "%unique")) { + if (!(ops & MARK_OP_UNIQUE)) + { + DBG1(DBG_APP, "unexpected use of %%unique mark", value); + return FALSE; + } endptr = (char*)value + strlen("%unique"); if (strcasepfx(endptr, "-dir")) { diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h index bd5545e22..5b1735178 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -28,6 +28,7 @@ typedef enum policy_priority_t policy_priority_t; typedef enum ipcomp_transform_t ipcomp_transform_t; typedef enum hw_offload_t hw_offload_t; typedef enum dscp_copy_t dscp_copy_t; +typedef enum mark_op_t mark_op_t; typedef struct ipsec_sa_cfg_t ipsec_sa_cfg_t; typedef struct lifetime_cfg_t lifetime_cfg_t; typedef struct mark_t mark_t; @@ -216,13 +217,24 @@ struct mark_t { #define MARK_UNIQUE_DIR (0xFFFFFFFE) #define MARK_IS_UNIQUE(m) ((m) == MARK_UNIQUE || (m) == MARK_UNIQUE_DIR) +/** + * Special mark operations to accept when parsing marks. + */ +enum mark_op_t { + /** none of the following */ + MARK_OP_NONE = 0, + /** %unique and %unique-dir */ + MARK_OP_UNIQUE = (1<<0), +}; + /** * Try to parse a mark_t from the given string of the form mark[/mask]. * * @param value string to parse + * @param ops operations to accept * @param mark mark to fill * @return TRUE if parsing was successful */ -bool mark_from_string(const char *value, mark_t *mark); +bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark); #endif /** IPSEC_TYPES_H_ @}*/ diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index 00f000a6a..5a854f3f6 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -860,47 +860,69 @@ END_TEST static struct { char *s; bool ok; + mark_op_t ops; mark_t m; } mark_data[] = { - {NULL, FALSE, { 0 }}, - {"", TRUE, { 0, 0xffffffff }}, - {"/", TRUE, { 0, 0 }}, - {"42", TRUE, { 42, 0xffffffff }}, - {"0x42", TRUE, { 0x42, 0xffffffff }}, - {"x", FALSE, { 0 }}, - {"42/", TRUE, { 0, 0 }}, - {"42/0", TRUE, { 0, 0 }}, - {"42/x", FALSE, { 0 }}, - {"42/42", TRUE, { 42, 42 }}, - {"42/0xff", TRUE, { 42, 0xff }}, - {"0x42/0xff", TRUE, { 0x42, 0xff }}, - {"/0xff", TRUE, { 0, 0xff }}, - {"/x", FALSE, { 0 }}, - {"x/x", FALSE, { 0 }}, - {"0xfffffff0/0x0000ffff", TRUE, { 0x0000fff0, 0x0000ffff }}, - {"%unique", TRUE, { MARK_UNIQUE, 0xffffffff }}, - {"%unique/", TRUE, { MARK_UNIQUE, 0 }}, - {"%unique/0x0000ffff", TRUE, { MARK_UNIQUE, 0x0000ffff }}, - {"%unique/0xffffffff", TRUE, { MARK_UNIQUE, 0xffffffff }}, - {"%unique0xffffffffff", FALSE, { 0, 0 }}, - {"0xffffffff/0x0000ffff", TRUE, { MARK_UNIQUE, 0x0000ffff }}, - {"0xffffffff/0xffffffff", TRUE, { MARK_UNIQUE, 0xffffffff }}, - {"%unique-dir", TRUE, { MARK_UNIQUE_DIR, 0xffffffff }}, - {"%unique-dir/", TRUE, { MARK_UNIQUE_DIR, 0 }}, - {"%unique-dir/0x0000ffff", TRUE, { MARK_UNIQUE_DIR, 0x0000ffff }}, - {"%unique-dir/0xffffffff", TRUE, { MARK_UNIQUE_DIR, 0xffffffff }}, - {"%unique-dir0xffffffff", FALSE, { 0, 0 }}, - {"0xfffffffe/0x0000ffff", TRUE, { MARK_UNIQUE_DIR, 0x0000ffff }}, - {"0xfffffffe/0xffffffff", TRUE, { MARK_UNIQUE_DIR, 0xffffffff }}, - {"%unique-/0xffffffff", FALSE, { 0, 0 }}, - {"%unique-foo/0xffffffff", FALSE, { 0, 0 }}, + {NULL, FALSE, MARK_OP_NONE, { 0 }}, + {"", TRUE, MARK_OP_NONE, { 0, 0xffffffff }}, + {"/", TRUE, MARK_OP_NONE, { 0, 0 }}, + {"42", TRUE, MARK_OP_NONE, { 42, 0xffffffff }}, + {"0x42", TRUE, MARK_OP_NONE, { 0x42, 0xffffffff }}, + {"x", FALSE, MARK_OP_NONE, { 0 }}, + {"42/", TRUE, MARK_OP_NONE, { 0, 0 }}, + {"42/0", TRUE, MARK_OP_NONE, { 0, 0 }}, + {"42/x", FALSE, MARK_OP_NONE, { 0 }}, + {"42/42", TRUE, MARK_OP_NONE, { 42, 42 }}, + {"42/0xff", TRUE, MARK_OP_NONE, { 42, 0xff }}, + {"0x42/0xff", TRUE, MARK_OP_NONE, { 0x42, 0xff }}, + {"/0xff", TRUE, MARK_OP_NONE, { 0, 0xff }}, + {"/x", FALSE, MARK_OP_NONE, { 0 }}, + {"x/x", FALSE, MARK_OP_NONE, { 0 }}, + {"0xfffffff0/0x0000ffff", TRUE, MARK_OP_UNIQUE, + { 0x0000fff0, 0x0000ffff }}, + {"%unique", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE, 0xffffffff }}, + {"%unique/", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE, 0 }}, + {"%unique", FALSE, MARK_OP_NONE, + { 0, 0 }}, + {"%unique/0x0000ffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE, 0x0000ffff }}, + {"%unique/0xffffffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE, 0xffffffff }}, + {"%unique0xffffffffff", FALSE, MARK_OP_UNIQUE, + { 0, 0 }}, + {"0xffffffff/0x0000ffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE, 0x0000ffff }}, + {"0xffffffff/0xffffffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE, 0xffffffff }}, + {"%unique-dir", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE_DIR, 0xffffffff }}, + {"%unique-dir/", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE_DIR, 0 }}, + {"%unique-dir", FALSE, MARK_OP_NONE, + { 0, 0 }}, + {"%unique-dir/0x0000ffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE_DIR, 0x0000ffff }}, + {"%unique-dir/0xffffffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE_DIR, 0xffffffff }}, + {"%unique-dir0xffffffff", FALSE, MARK_OP_UNIQUE, + { 0, 0 }}, + {"0xfffffffe/0x0000ffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE_DIR, 0x0000ffff }}, + {"0xfffffffe/0xffffffff", TRUE, MARK_OP_UNIQUE, + { MARK_UNIQUE_DIR, 0xffffffff }}, + {"%unique-/0xffffffff", FALSE, MARK_OP_UNIQUE, + { 0, 0 }}, + {"%unique-foo/0xffffffff", FALSE, MARK_OP_UNIQUE, + { 0, 0 }}, }; START_TEST(test_mark_from_string) { mark_t mark; - if (mark_from_string(mark_data[_i].s, &mark)) + if (mark_from_string(mark_data[_i].s, mark_data[_i].ops, &mark)) { ck_assert_int_eq(mark.value, mark_data[_i].m.value); ck_assert_int_eq(mark.mask, mark_data[_i].m.mask); diff --git a/src/starter/confread.c b/src/starter/confread.c index 345d0b60b..407ef5e13 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -444,7 +444,7 @@ static void handle_keyword(kw_token_t token, starter_conn_t *conn, char *key, KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_COMPRESS) break; case KW_MARK: - if (!mark_from_string(value, &conn->mark_in)) + if (!mark_from_string(value, MARK_OP_UNIQUE, &conn->mark_in)) { cfg->err++; break; @@ -452,13 +452,13 @@ static void handle_keyword(kw_token_t token, starter_conn_t *conn, char *key, conn->mark_out = conn->mark_in; break; case KW_MARK_IN: - if (!mark_from_string(value, &conn->mark_in)) + if (!mark_from_string(value, MARK_OP_UNIQUE, &conn->mark_in)) { cfg->err++; } break; case KW_MARK_OUT: - if (!mark_from_string(value, &conn->mark_out)) + if (!mark_from_string(value, MARK_OP_UNIQUE, &conn->mark_out)) { cfg->err++; } From 902dc29f7a3c5e2597b4a59718c6f8b34bf893f2 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 9 May 2018 13:40:36 +0200 Subject: [PATCH 7/7] child-sa: Use SA matching mark as SA set mark if the latter is %same For inbound processing, it can be rather useful to apply the mark to the packet in the SA, so the associated policy with that mark implicitly matches. When using %unique as match mark, we don't know the mark beforehand, so we most likely want to set the mark we match against. --- src/libcharon/plugins/vici/vici_config.c | 19 +++++++++++++++++-- src/libcharon/sa/child_sa.c | 5 +++++ src/libstrongswan/ipsec/ipsec_types.c | 18 ++++++++++++++++++ src/libstrongswan/ipsec/ipsec_types.h | 3 +++ src/libstrongswan/tests/suites/test_utils.c | 6 ++++++ src/swanctl/swanctl.opt | 8 ++++++-- 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 98f68264c..8afefaa09 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -1184,6 +1184,21 @@ CALLBACK(parse_mark, bool, return mark_from_string(buf, MARK_OP_UNIQUE, out); } +/** + * Parse a mark_t when using it as set_mark. + */ +CALLBACK(parse_set_mark, bool, + mark_t *out, chunk_t v) +{ + char buf[32]; + + if (!vici_stringify(v, buf, sizeof(buf))) + { + return FALSE; + } + return mark_from_string(buf, MARK_OP_SAME, out); +} + /** * Parse TFC padding option */ @@ -1643,8 +1658,8 @@ CALLBACK(child_kv, bool, { "mark_in", parse_mark, &child->cfg.mark_in }, { "mark_in_sa", parse_opt_mark_in, &child->cfg.options }, { "mark_out", parse_mark, &child->cfg.mark_out }, - { "set_mark_in", parse_mark, &child->cfg.set_mark_in }, - { "set_mark_out", parse_mark, &child->cfg.set_mark_out }, + { "set_mark_in", parse_set_mark, &child->cfg.set_mark_in }, + { "set_mark_out", parse_set_mark, &child->cfg.set_mark_out }, { "tfc_padding", parse_tfc, &child->cfg.tfc }, { "priority", parse_uint32, &child->cfg.priority }, { "interface", parse_string, &child->cfg.interface }, diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index efa790fc5..c33398bee 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -900,6 +900,11 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, .update = update, }; + if (sa.mark.value == MARK_SAME) + { + sa.mark.value = inbound ? this->mark_in.value : this->mark_out.value; + } + status = charon->kernel->add_sa(charon->kernel, &id, &sa); my_ts->destroy(my_ts); diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index 7eed1566a..6f19cc751 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -100,6 +100,24 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark) return FALSE; } } + else if (strcasepfx(value, "%same")) + { + if (!(ops & MARK_OP_SAME)) + { + DBG1(DBG_APP, "unexpected use of %%same mark", value); + return FALSE; + } + endptr = (char*)value + strlen("%same"); + if (!*endptr || *endptr == '/') + { + mark->value = MARK_SAME; + } + else + { + DBG1(DBG_APP, "invalid mark value: %s", value); + return FALSE; + } + } else { mark->value = strtoul(value, &endptr, 0); diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h index 5b1735178..7b7bd3743 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -215,6 +215,7 @@ struct mark_t { */ #define MARK_UNIQUE (0xFFFFFFFF) #define MARK_UNIQUE_DIR (0xFFFFFFFE) +#define MARK_SAME (0xFFFFFFFF) #define MARK_IS_UNIQUE(m) ((m) == MARK_UNIQUE || (m) == MARK_UNIQUE_DIR) /** @@ -225,6 +226,8 @@ enum mark_op_t { MARK_OP_NONE = 0, /** %unique and %unique-dir */ MARK_OP_UNIQUE = (1<<0), + /** %same */ + MARK_OP_SAME = (1<<1), }; /** diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index 5a854f3f6..f1d46ee6b 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -916,6 +916,12 @@ static struct { { 0, 0 }}, {"%unique-foo/0xffffffff", FALSE, MARK_OP_UNIQUE, { 0, 0 }}, + {"%same", TRUE, MARK_OP_SAME, + { MARK_SAME, 0xffffffff }}, + {"%same/0x0000ffff", TRUE, MARK_OP_SAME, + { MARK_SAME, 0x0000ffff }}, + {"%%same", FALSE, MARK_OP_NONE, + { 0, 0 }}, }; START_TEST(test_mark_from_string) diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 79655ed35..5b44c541b 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -919,7 +919,9 @@ connections..children..set_mark_in = 0/0x00000000 policy routing). An additional mask may be appended to the mark, separated by _/_. The - default mask if omitted is 0xffffffff. + default mask if omitted is 0xffffffff. The special value _%same_ uses + the value (but not the mask) from **mark_in** as mark value, which can be + fixed, _%unique_ or _%unique-dir_. Setting marks in XFRM input requires Linux 4.19 or higher. @@ -932,7 +934,9 @@ connections..children..set_mark_out = 0/0x00000000 traffic (e.g. via policy routing). An additional mask may be appended to the mark, separated by _/_. The - default mask if omitted is 0xffffffff. + default mask if omitted is 0xffffffff. The special value _%same_ uses + the value (but not the mask) from **mark_out** as mark value, which can be + fixed, _%unique_ or _%unique-dir_. Setting marks in XFRM output is supported since Linux 4.14. Setting a mask requires at least Linux 4.19.