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.
This commit is contained in:
Tobias Brunner
2018-08-29 11:36:04 +02:00
parent de4c3d2e76
commit dc8b015d78
6 changed files with 80 additions and 14 deletions
+6
View File
@@ -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),
};
/**
+4
View File
@@ -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 */
@@ -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:
+42 -14
View File
@@ -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);
+2
View File
@@ -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,
+15
View File
@@ -937,6 +937,21 @@ connections.<conn>.children.<child>.hw_offload = no
enables offloading, if it's supported, but the installation does not fail
otherwise.
connections.<conn>.children.<child>.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.<conn>.children.<child>.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.<conn>.children.<child>.start_action = none
Action to perform after loading the configuration (_none_, _trap_, _start_).