@@ -650,6 +650,21 @@ parse_tc_flower_to_match(struct tc_flower *flower,
action->mpls.proto);
}
break;
+ case TC_ACT_MPLS_PUSH: {
+ struct ovs_action_push_mpls *push;
+ ovs_be32 mpls_lse = 0;
+
+ flow_set_mpls_lse_label(&mpls_lse, action->mpls.label);
+ flow_set_mpls_lse_tc(&mpls_lse, action->mpls.tc);
+ flow_set_mpls_lse_ttl(&mpls_lse, action->mpls.ttl);
+ flow_set_mpls_lse_bos(&mpls_lse, action->mpls.bos);
+
+ push = nl_msg_put_unspec_zero(buf, OVS_ACTION_ATTR_PUSH_MPLS,
+ sizeof *push);
+ push->mpls_ethertype = action->mpls.proto;
+ push->mpls_lse = mpls_lse;
+ }
+ break;
case TC_ACT_PEDIT: {
parse_flower_rewrite_to_netlink_action(buf, flower);
}
@@ -1333,6 +1348,16 @@ 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_PUSH_MPLS) {
+ const struct ovs_action_push_mpls *mpls_push = nl_attr_get(nla);
+
+ action->mpls.proto = mpls_push->mpls_ethertype;
+ action->mpls.label = mpls_lse_to_label(mpls_push->mpls_lse);
+ action->mpls.tc = mpls_lse_to_tc(mpls_push->mpls_lse);
+ action->mpls.ttl = mpls_lse_to_ttl(mpls_push->mpls_lse);
+ action->mpls.bos = mpls_lse_to_bos(mpls_push->mpls_lse);
+ action->type = TC_ACT_MPLS_PUSH;
+ 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;
@@ -574,6 +574,30 @@ mpls_lse_to_ttl(ovs_be32 mpls_lse)
return (ntohl(mpls_lse) & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT;
}
+/* Set label in mpls lse. */
+static inline void
+flow_set_mpls_lse_label(ovs_be32 *mpls_lse, uint32_t label)
+{
+ *mpls_lse &= ~htonl(MPLS_LABEL_MASK);
+ *mpls_lse |= htonl(label << MPLS_LABEL_SHIFT);
+}
+
+/* Set TC in mpls lse. */
+static inline void
+flow_set_mpls_lse_tc(ovs_be32 *mpls_lse, uint8_t tc)
+{
+ *mpls_lse &= ~htonl(MPLS_TC_MASK);
+ *mpls_lse |= htonl((tc & 0x7) << MPLS_TC_SHIFT);
+}
+
+/* Set BOS in mpls lse. */
+static inline void
+flow_set_mpls_lse_bos(ovs_be32 *mpls_lse, uint8_t bos)
+{
+ *mpls_lse &= ~htonl(MPLS_BOS_MASK);
+ *mpls_lse |= htonl((bos & 0x1) << MPLS_BOS_SHIFT);
+}
+
/* Set TTL in mpls lse. */
static inline void
flow_set_mpls_lse_ttl(ovs_be32 *mpls_lse, uint8_t ttl)
@@ -1243,6 +1243,10 @@ static const struct nl_policy mpls_policy[] = {
.min_len = sizeof(struct tc_mpls),
.optional = false, },
[TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, },
+ [TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
+ [TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
+ [TCA_MPLS_TTL] = { .type = NL_A_U8, .optional = true, },
+ [TCA_MPLS_BOS] = { .type = NL_A_U8, .optional = true, },
};
static int
@@ -1251,8 +1255,12 @@ 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 nlattr *mpls_label;
struct tc_action *action;
const struct tc_mpls *m;
+ struct nlattr *mpls_ttl;
+ struct nlattr *mpls_bos;
+ struct nlattr *mpls_tc;
if (!nl_parse_nested(options, mpls_policy, mpls_attrs,
ARRAY_SIZE(mpls_policy))) {
@@ -1272,6 +1280,29 @@ nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
}
action->type = TC_ACT_MPLS_POP;
break;
+ case TCA_MPLS_ACT_PUSH:
+ mpls_proto = mpls_attrs[TCA_MPLS_PROTO];
+ if (mpls_proto) {
+ action->mpls.proto = nl_attr_get_be16(mpls_proto);
+ }
+ mpls_label = mpls_attrs[TCA_MPLS_LABEL];
+ if (mpls_label) {
+ action->mpls.label = nl_attr_get_u32(mpls_label);
+ }
+ mpls_tc = mpls_attrs[TCA_MPLS_TC];
+ if (mpls_tc) {
+ action->mpls.tc = nl_attr_get_u8(mpls_tc);
+ }
+ mpls_ttl = mpls_attrs[TCA_MPLS_TTL];
+ if (mpls_ttl) {
+ action->mpls.ttl = nl_attr_get_u8(mpls_ttl);
+ }
+ mpls_bos = mpls_attrs[TCA_MPLS_BOS];
+ if (mpls_bos) {
+ action->mpls.bos = nl_attr_get_u8(mpls_bos);
+ }
+ action->type = TC_ACT_MPLS_PUSH;
+ break;
default:
VLOG_ERR_RL(&error_rl, "unknown mpls action: %d, %d",
m->action, m->m_action);
@@ -1712,6 +1743,28 @@ nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto)
}
static void
+nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto,
+ uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos)
+{
+ 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_PUSH };
+
+ nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
+ nl_msg_put_be16(request, TCA_MPLS_PROTO, proto);
+ nl_msg_put_u32(request, TCA_MPLS_LABEL, label);
+ nl_msg_put_u8(request, TCA_MPLS_TC, tc);
+ nl_msg_put_u8(request, TCA_MPLS_TTL, ttl);
+ nl_msg_put_u8(request, TCA_MPLS_BOS, bos);
+ }
+ nl_msg_end_nested(request, offset);
+}
+
+static void
nl_msg_put_act_tunnel_key_release(struct ofpbuf *request)
{
size_t offset;
@@ -2094,6 +2147,14 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
nl_msg_end_nested(request, act_offset);
}
break;
+ case TC_ACT_MPLS_PUSH: {
+ act_offset = nl_msg_start_nested(request, act_index++);
+ nl_msg_put_act_push_mpls(request, action->mpls.proto,
+ action->mpls.label, action->mpls.tc,
+ action->mpls.ttl, action->mpls.bos);
+ nl_msg_end_nested(request, act_offset);
+ }
+ break;
case TC_ACT_OUTPUT: {
ingress = action->out.ingress;
ifindex = action->out.ifindex_out;
@@ -154,6 +154,7 @@ enum tc_action_type {
TC_ACT_VLAN_POP,
TC_ACT_VLAN_PUSH,
TC_ACT_MPLS_POP,
+ TC_ACT_MPLS_PUSH,
};
struct tc_action {
@@ -171,6 +172,10 @@ struct tc_action {
struct {
ovs_be16 proto;
+ uint32_t label;
+ uint8_t tc;
+ uint8_t ttl;
+ uint8_t bos;
} mpls;
struct {