From 55412eac6937174252827b630226199d95b196f4 Mon Sep 17 00:00:00 2001 From: John Hurley Date: Tue, 30 Jul 2019 12:05:15 +0100 Subject: [PATCH] ovs-tc: offload MPLS pop actions to TC datapath TC now supports an action to pop the outer MPLS header from a packet. The next protocol after the header is required alongside this. Currently, OvS datapath rules also supply this information. Offload OvS MPLS pop actions to TC along with the next protocol. Signed-off-by: John Hurley Reviewed-by: Simon Horman Signed-off-by: Simon Horman --- lib/netdev-offload-tc.c | 9 ++++++ lib/tc.c | 69 +++++++++++++++++++++++++++++++++++++++++ lib/tc.h | 5 +++ 3 files changed, 83 insertions(+) diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 4cc044b4b6e..2e282c98d46 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -645,6 +645,11 @@ parse_tc_flower_to_match(struct tc_flower *flower, | VLAN_CFI); } break; + case TC_ACT_MPLS_POP: { + nl_msg_put_be16(buf, OVS_ACTION_ATTR_POP_MPLS, + action->mpls.proto); + } + break; case TC_ACT_PEDIT: { parse_flower_rewrite_to_netlink_action(buf, flower); } @@ -1328,6 +1333,10 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match, } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_POP_VLAN) { action->type = TC_ACT_VLAN_POP; flower.action_count++; + } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_POP_MPLS) { + action->mpls.proto = nl_attr_get_be16(nla); + action->type = TC_ACT_MPLS_POP; + flower.action_count++; } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET) { const struct nlattr *set = nl_attr_get(nla); const size_t set_len = nl_attr_get_size(nla); diff --git a/lib/tc.c b/lib/tc.c index 1eca3562008..85aa9a19228 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1237,6 +1238,49 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower) return 0; } +static const struct nl_policy mpls_policy[] = { + [TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC, + .min_len = sizeof(struct tc_mpls), + .optional = false, }, + [TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, }, +}; + +static int +nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower) +{ + struct nlattr *mpls_attrs[ARRAY_SIZE(mpls_policy)]; + const struct nlattr *mpls_parms; + struct nlattr *mpls_proto; + struct tc_action *action; + const struct tc_mpls *m; + + if (!nl_parse_nested(options, mpls_policy, mpls_attrs, + ARRAY_SIZE(mpls_policy))) { + VLOG_ERR_RL(&error_rl, "failed to parse mpls action options"); + return EPROTO; + } + + action = &flower->actions[flower->action_count++]; + mpls_parms = mpls_attrs[TCA_MPLS_PARMS]; + m = nl_attr_get_unspec(mpls_parms, sizeof *m); + + switch (m->m_action) { + case TCA_MPLS_ACT_POP: + mpls_proto = mpls_attrs[TCA_MPLS_PROTO]; + if (mpls_proto) { + action->mpls.proto = nl_attr_get_be16(mpls_proto); + } + action->type = TC_ACT_MPLS_POP; + break; + default: + VLOG_ERR_RL(&error_rl, "unknown mpls action: %d, %d", + m->action, m->m_action); + return EINVAL; + } + + return 0; +} + static const struct nl_policy csum_policy[] = { [TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct tc_csum), @@ -1319,6 +1363,8 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower) err = nl_parse_act_mirred(act_options, flower); } else if (!strcmp(act_kind, "vlan")) { err = nl_parse_act_vlan(act_options, flower); + } else if (!strcmp(act_kind, "mpls")) { + err = nl_parse_act_mpls(act_options, flower); } else if (!strcmp(act_kind, "tunnel_key")) { err = nl_parse_act_tunnel_key(act_options, flower); } else if (!strcmp(act_kind, "pedit")) { @@ -1648,6 +1694,23 @@ nl_msg_put_act_pop_vlan(struct ofpbuf *request) nl_msg_end_nested(request, offset); } +static void +nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto) +{ + size_t offset; + + nl_msg_put_string(request, TCA_ACT_KIND, "mpls"); + offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); + { + struct tc_mpls parm = { .action = TC_ACT_PIPE, + .m_action = TCA_MPLS_ACT_POP }; + + nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm); + nl_msg_put_be16(request, TCA_MPLS_PROTO, proto); + } + nl_msg_end_nested(request, offset); +} + static void nl_msg_put_act_tunnel_key_release(struct ofpbuf *request) { @@ -2025,6 +2088,12 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower) nl_msg_end_nested(request, act_offset); } break; + case TC_ACT_MPLS_POP: { + act_offset = nl_msg_start_nested(request, act_index++); + nl_msg_put_act_pop_mpls(request, action->mpls.proto); + nl_msg_end_nested(request, act_offset); + } + break; case TC_ACT_OUTPUT: { ingress = action->out.ingress; ifindex = action->out.ifindex_out; diff --git a/lib/tc.h b/lib/tc.h index 2e0f5e37ad4..7805f69f83a 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -153,6 +153,7 @@ enum tc_action_type { TC_ACT_PEDIT, TC_ACT_VLAN_POP, TC_ACT_VLAN_PUSH, + TC_ACT_MPLS_POP, }; struct tc_action { @@ -168,6 +169,10 @@ struct tc_action { uint8_t vlan_push_prio; } vlan; + struct { + ovs_be16 proto; + } mpls; + struct { bool id_present; ovs_be64 id;