Merge branch 'ip-header-fields'

Adds new options that allow configuring how/whether certain fields in
the IP headers are copied during IPsec processing. Currently only allows
configuration on Linux.

Closes strongswan/strongswan#104.
This commit is contained in:
Tobias Brunner 2018-08-29 11:46:13 +02:00
commit d98df236a9
9 changed files with 205 additions and 14 deletions

View File

@ -147,6 +147,11 @@ struct private_child_cfg_t {
* HW offload mode
*/
hw_offload_t hw_offload;
/**
* DS header field copy mode
*/
dscp_copy_t copy_dscp;
};
METHOD(child_cfg_t, get_name, char*,
@ -487,6 +492,12 @@ METHOD(child_cfg_t, get_hw_offload, hw_offload_t,
return this->hw_offload;
}
METHOD(child_cfg_t, get_copy_dscp, dscp_copy_t,
private_child_cfg_t *this)
{
return this->copy_dscp;
}
METHOD(child_cfg_t, get_dpd_action, action_t,
private_child_cfg_t *this)
{
@ -612,6 +623,8 @@ METHOD(child_cfg_t, equals, bool,
this->tfc == other->tfc &&
this->manual_prio == other->manual_prio &&
this->replay_window == other->replay_window &&
this->hw_offload == other->hw_offload &&
this->copy_dscp == other->copy_dscp &&
streq(this->updown, other->updown) &&
streq(this->interface, other->interface);
}
@ -673,6 +686,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.get_ref = _get_ref,
.destroy = _destroy,
.get_hw_offload = _get_hw_offload,
.get_copy_dscp = _get_copy_dscp,
},
.name = strdup(name),
.options = data->options,
@ -696,6 +710,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.replay_window = lib->settings->get_int(lib->settings,
"%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
.hw_offload = data->hw_offload,
.copy_dscp = data->copy_dscp,
);
return &this->public;

View File

@ -191,6 +191,13 @@ struct child_cfg_t {
*/
hw_offload_t (*get_hw_offload) (child_cfg_t *this);
/**
* Get the copy mode for the DS header field to use for the CHILD_SA.
*
* @return IP header copy mode
*/
dscp_copy_t (*get_copy_dscp) (child_cfg_t *this);
/**
* Action to take if CHILD_SA gets closed.
*
@ -319,6 +326,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),
};
/**
@ -355,6 +368,8 @@ struct child_cfg_create_t {
char *updown;
/** HW offload mode */
hw_offload_t hw_offload;
/** How to handle the DS header field in tunnel mode */
dscp_copy_t copy_dscp;
};
/**

View File

@ -95,6 +95,12 @@ 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;
/** Whether to copy the DSCP header field to/from the outer header */
dscp_copy_t copy_dscp;
/** TRUE if initiator of the exchange creating the SA */
bool initiator;
/** TRUE if this is an inbound SA */

View File

@ -1586,6 +1586,53 @@ 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;
}
if (data->inbound)
{
switch (data->copy_dscp)
{
case DSCP_COPY_YES:
case DSCP_COPY_IN_ONLY:
sa->flags |= XFRM_STATE_DECAP_DSCP;
break;
default:
break;
}
}
else
{
switch (data->copy_dscp)
{
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)
{
goto failed;
}
/* currently the only extra flag */
*xflags |= XFRM_SA_XFLAG_DONT_ENCAP_DSCP;
break;
}
default:
break;
}
}
switch (mode)
{
case MODE_TUNNEL:

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,9 @@ 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));
DBG2(DBG_CFG, " copy_dscp = %N", dscp_copy_names, cfg->copy_dscp);
}
/**
@ -847,16 +849,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 +874,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 +892,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 +901,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 +919,47 @@ 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);
}
/**
* Parse a dscp_copy_t
*/
CALLBACK(parse_copy_dscp, bool,
dscp_copy_t *out, chunk_t v)
{
enum_map_t map[] = {
{ "no", DSCP_COPY_NO },
{ "in", DSCP_COPY_IN_ONLY },
{ "out", DSCP_COPY_OUT_ONLY },
{ "yes", DSCP_COPY_YES },
};
int d;
if (parse_map(map, countof(map), &d, v))
{
*out = d;
return TRUE;
}
return FALSE;
}
/**
@ -1567,7 +1618,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 +1644,9 @@ 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 },
{ "copy_dscp", parse_copy_dscp, &child->cfg.copy_dscp },
};
return parse_rules(rules, countof(rules), name, value,
@ -1802,7 +1856,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 +1911,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);

View File

@ -891,6 +891,9 @@ 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),
.copy_dscp = this->config->get_copy_dscp(this->config),
.initiator = initiator,
.inbound = inbound,
.update = update,

View File

@ -43,6 +43,13 @@ ENUM(hw_offload_names, HW_OFFLOAD_NO, HW_OFFLOAD_AUTO,
"auto",
);
ENUM(dscp_copy_names, DSCP_COPY_OUT_ONLY, DSCP_COPY_NO,
"out",
"in",
"yes",
"no",
);
/*
* See header
*/

View File

@ -27,6 +27,7 @@ typedef enum policy_type_t policy_type_t;
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 struct ipsec_sa_cfg_t ipsec_sa_cfg_t;
typedef struct lifetime_cfg_t lifetime_cfg_t;
typedef struct mark_t mark_t;
@ -131,6 +132,22 @@ enum hw_offload_t {
*/
extern enum_name_t *hw_offload_names;
/**
* DSCP header field copy behavior (the default is not to copy from outer
* to inner header)
*/
enum dscp_copy_t {
DSCP_COPY_OUT_ONLY,
DSCP_COPY_IN_ONLY,
DSCP_COPY_YES,
DSCP_COPY_NO,
};
/**
* enum strings for dscp_copy_t.
*/
extern enum_name_t *dscp_copy_names;
/**
* This struct contains details about IPsec SA(s) tied to a policy.
*/

View File

@ -937,6 +937,35 @@ 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). Controlling this behavior
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. Controlling this behavior is not
supported by all kernel interfaces.
connections.<conn>.children.<child>.copy_dscp = out
Whether to copy the DSCP header field to/from the outer IP header in tunnel
mode.
Whether to copy the DSCP (Differentiated Services Field Codepoint) header
field to/from the outer IP header in tunnel mode. The value _out_ only
copies the field from the inner to the outer header, the value _in_ does the
opposite and only copies the field from the outer to the inner header when
decapsulating, the value _yes_ copies the field in both directions, and the
value _no_ disables copying the field altogether. Setting this to _yes_ or
_in_ could allow an attacker to adversely affect other traffic at the
receiver, which is why the default is _out_. Controlling this behavior is
not supported by all kernel interfaces.
connections.<conn>.children.<child>.start_action = none
Action to perform after loading the configuration (_none_, _trap_, _start_).