From: Tobias Brunner Date: Mon, 11 Jun 2018 08:49:16 +0000 (+0200) Subject: kernel: Add options to control DF and ECN header bits/fields via XFRM X-Git-Tag: 5.7.0rc1~39^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dc8b015d782e70aa476ec758f9e26cd869d01f9a;p=thirdparty%2Fstrongswan.git kernel: Add options to control DF and ECN header bits/fields via XFRM The options control whether the DF and ECN header bits/fields are copied from the unencrypted packets to the encrypted packets in tunnel mode (DF only for IPv4), and for ECN whether the same is done for inbound packets. Note: This implementation only works with Linux/Netlink/XFRM. Based on a patch by Markus Sattler. --- diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index 2defd03301..2b41a0cc4e 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -319,6 +319,12 @@ enum child_cfg_option_t { /** Set mark on inbound SAs */ OPT_MARK_IN_SA = (1<<6), + + /** Disable copying the DF bit to the outer IPv4 header in tunnel mode */ + OPT_NO_COPY_DF = (1<<7), + + /** Disable copying the ECN header field in tunnel mode */ + OPT_NO_COPY_ECN = (1<<8), }; /** diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h index 94b9c284bc..3feacb27e5 100644 --- a/src/libcharon/kernel/kernel_ipsec.h +++ b/src/libcharon/kernel/kernel_ipsec.h @@ -95,6 +95,10 @@ struct kernel_ipsec_add_sa_t { hw_offload_t hw_offload; /** TRUE to use Extended Sequence Numbers */ bool esn; + /** TRUE to copy the DF bit to the outer IPv4 header in tunnel mode */ + bool copy_df; + /** TRUE to copy the ECN header field to/from the outer header */ + bool copy_ecn; /** TRUE if initiator of the exchange creating the SA */ bool initiator; /** TRUE if this is an inbound SA */ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 4926c3de87..9751933e36 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1586,6 +1586,17 @@ METHOD(kernel_ipsec_t, add_sa, status_t, sa->id.proto = id->proto; sa->family = id->src->get_family(id->src); sa->mode = mode2kernel(mode); + + if (!data->copy_df) + { + sa->flags |= XFRM_STATE_NOPMTUDISC; + } + + if (!data->copy_ecn) + { + sa->flags |= XFRM_STATE_NOECN; + } + switch (mode) { case MODE_TUNNEL: diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index dfb4c84f38..394d21997a 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -484,7 +484,6 @@ typedef struct { linked_list_t *local_ts; linked_list_t *remote_ts; uint32_t replay_window; - bool policies; child_cfg_create_t cfg; } child_data_t; @@ -511,7 +510,7 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " ipcomp = %u", has_opt(OPT_IPCOMP)); DBG2(DBG_CFG, " mode = %N%s", ipsec_mode_names, cfg->mode, has_opt(OPT_PROXY_MODE) ? "_PROXY" : ""); - DBG2(DBG_CFG, " policies = %u", data->policies); + DBG2(DBG_CFG, " policies = %u", !has_opt(OPT_NO_POLICIES)); DBG2(DBG_CFG, " policies_fwd_out = %u", has_opt(OPT_FWD_OUT_POLICIES)); if (data->replay_window != REPLAY_UNDEFINED) { @@ -535,6 +534,8 @@ static void log_child_data(child_data_t *data, char *name) DBG2(DBG_CFG, " remote_ts = %#R", data->remote_ts); DBG2(DBG_CFG, " hw_offload = %N", hw_offload_names, cfg->hw_offload); DBG2(DBG_CFG, " sha256_96 = %u", has_opt(OPT_SHA256_96)); + DBG2(DBG_CFG, " copy_df = %u", !has_opt(OPT_NO_COPY_DF)); + DBG2(DBG_CFG, " copy_ecn = %u", !has_opt(OPT_NO_COPY_ECN)); } /** @@ -847,16 +848,17 @@ CALLBACK(parse_mode, bool, } /** - * Enable a child_cfg_option_t + * Enable a child_cfg_option_t, the flag controls whether the option is enabled + * if the parsed value is TRUE or FALSE. */ static bool parse_option(child_cfg_option_t *out, child_cfg_option_t opt, - chunk_t v) + chunk_t v, bool add_if_true) { bool val; if (parse_bool(&val, v)) { - if (val) + if (val == add_if_true) { *out |= opt; } @@ -871,7 +873,16 @@ static bool parse_option(child_cfg_option_t *out, child_cfg_option_t opt, CALLBACK(parse_opt_haccess, bool, child_cfg_option_t *out, chunk_t v) { - return parse_option(out, OPT_HOSTACCESS, v); + return parse_option(out, OPT_HOSTACCESS, v, TRUE); +} + +/** + * Parse OPT_NO_POLICIES option + */ +CALLBACK(parse_opt_policies, bool, + child_cfg_option_t *out, chunk_t v) +{ + return parse_option(out, OPT_NO_POLICIES, v, FALSE); } /** @@ -880,7 +891,7 @@ CALLBACK(parse_opt_haccess, bool, CALLBACK(parse_opt_fwd_out, bool, child_cfg_option_t *out, chunk_t v) { - return parse_option(out, OPT_FWD_OUT_POLICIES, v); + return parse_option(out, OPT_FWD_OUT_POLICIES, v, TRUE); } /** @@ -889,17 +900,16 @@ CALLBACK(parse_opt_fwd_out, bool, CALLBACK(parse_opt_ipcomp, bool, child_cfg_option_t *out, chunk_t v) { - return parse_option(out, OPT_IPCOMP, v); + return parse_option(out, OPT_IPCOMP, v, TRUE); } - /** * Parse OPT_SHA256_96 option */ CALLBACK(parse_opt_sha256_96, bool, child_cfg_option_t *out, chunk_t v) { - return parse_option(out, OPT_SHA256_96, v); + return parse_option(out, OPT_SHA256_96, v, TRUE); } /** @@ -908,7 +918,25 @@ CALLBACK(parse_opt_sha256_96, bool, CALLBACK(parse_opt_mark_in, bool, child_cfg_option_t *out, chunk_t v) { - return parse_option(out, OPT_MARK_IN_SA, v); + return parse_option(out, OPT_MARK_IN_SA, v, TRUE); +} + +/** + * Parse OPT_NO_COPY_DF option + */ +CALLBACK(parse_opt_copy_df, bool, + child_cfg_option_t *out, chunk_t v) +{ + return parse_option(out, OPT_NO_COPY_DF, v, FALSE); +} + +/** + * Parse OPT_NO_COPY_ECN option + */ +CALLBACK(parse_opt_copy_ecn, bool, + child_cfg_option_t *out, chunk_t v) +{ + return parse_option(out, OPT_NO_COPY_ECN, v, FALSE); } /** @@ -1567,7 +1595,7 @@ CALLBACK(child_kv, bool, { "updown", parse_string, &child->cfg.updown }, { "hostaccess", parse_opt_haccess, &child->cfg.options }, { "mode", parse_mode, &child->cfg }, - { "policies", parse_bool, &child->policies }, + { "policies", parse_opt_policies, &child->cfg.options }, { "policies_fwd_out", parse_opt_fwd_out, &child->cfg.options }, { "replay_window", parse_uint32, &child->replay_window }, { "rekey_time", parse_time, &child->cfg.lifetime.time.rekey }, @@ -1593,6 +1621,8 @@ CALLBACK(child_kv, bool, { "interface", parse_string, &child->cfg.interface }, { "hw_offload", parse_hw_offload, &child->cfg.hw_offload }, { "sha256_96", parse_opt_sha256_96,&child->cfg.options }, + { "copy_df", parse_opt_copy_df, &child->cfg.options }, + { "copy_ecn", parse_opt_copy_ecn, &child->cfg.options }, }; return parse_rules(rules, countof(rules), name, value, @@ -1802,7 +1832,6 @@ CALLBACK(children_sn, bool, .proposals = linked_list_create(), .local_ts = linked_list_create(), .remote_ts = linked_list_create(), - .policies = TRUE, .replay_window = REPLAY_UNDEFINED, .cfg = { .mode = MODE_TUNNEL, @@ -1858,7 +1887,6 @@ CALLBACK(children_sn, bool, child.proposals->insert_last(child.proposals, proposal); } } - child.cfg.options |= child.policies ? 0 : OPT_NO_POLICIES; check_lifetimes(&child.cfg.lifetime); diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 49717703c9..b0efec54a7 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -891,6 +891,8 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, .encap = this->encap, .hw_offload = this->config->get_hw_offload(this->config), .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), .initiator = initiator, .inbound = inbound, .update = update, diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index 120e5812e3..d1e823a16b 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -937,6 +937,21 @@ connections..children..hw_offload = no enables offloading, if it's supported, but the installation does not fail otherwise. +connections..children..copy_df = yes + Whether to copy the DF bit to the outer IPv4 header in tunnel mode. + + Whether to copy the DF bit to the outer IPv4 header in tunnel mode. This + effectively disables Path MTU discovery (PMTUD). Disabling this is not + supported by all kernel interfaces. + +connections..children..copy_ecn = yes + Whether to copy the ECN header field to/from the outer IP header in tunnel + mode. + + Whether to copy the ECN (Explicit Congestion Notification) header field + to/from the outer IP header in tunnel mode. Disabling this is not supported + by all kernel interfaces. + connections..children..start_action = none Action to perform after loading the configuration (_none_, _trap_, _start_).