From: Tobias Brunner Date: Mon, 11 Jun 2018 10:07:48 +0000 (+0200) Subject: kernel: Add option to control DS field behavior X-Git-Tag: 5.7.0rc1~39^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c993eaf9d152bbec83dfafb946cd18dc35aaf9b8;p=thirdparty%2Fstrongswan.git kernel: Add option to control DS field behavior --- diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c index d8083d4332..98c15782b4 100644 --- a/src/libcharon/config/child_cfg.c +++ b/src/libcharon/config/child_cfg.c @@ -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; diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index 2b41a0cc4e..f552dae71f 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -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. * @@ -361,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; }; /** diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h index 3feacb27e5..195c80cff4 100644 --- a/src/libcharon/kernel/kernel_ipsec.h +++ b/src/libcharon/kernel/kernel_ipsec.h @@ -99,6 +99,8 @@ struct kernel_ipsec_add_sa_t { 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 */ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 9751933e36..70edef6ad1 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1597,6 +1597,42 @@ METHOD(kernel_ipsec_t, add_sa, status_t, 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: diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 394d21997a..7a66af1f4e 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -536,6 +536,7 @@ static void log_child_data(child_data_t *data, char *name) 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); } /** @@ -939,6 +940,28 @@ CALLBACK(parse_opt_copy_ecn, bool, 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; +} + /** * Parse an action_t */ @@ -1623,6 +1646,7 @@ CALLBACK(child_kv, bool, { "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, diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index b0efec54a7..d06996e433 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -893,6 +893,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, .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, diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index 16dbf8d413..d231bb3a45 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -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 */ diff --git a/src/libstrongswan/ipsec/ipsec_types.h b/src/libstrongswan/ipsec/ipsec_types.h index 4e6e2d9dc0..bd5545e221 100644 --- a/src/libstrongswan/ipsec/ipsec_types.h +++ b/src/libstrongswan/ipsec/ipsec_types.h @@ -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. */ diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index d1e823a16b..3f67b938a1 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -941,16 +941,30 @@ 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. + effectively disables Path MTU discovery (PMTUD). Controlling this behavior + 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. + to/from the outer IP header in tunnel mode. Controlling this behavior is not + supported by all kernel interfaces. + +connections..children..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..children..start_action = none Action to perform after loading the configuration (_none_, _trap_, _start_).