NXAST_STACK_PUSH, /* struct nx_action_stack */
NXAST_STACK_POP, /* struct nx_action_stack */
NXAST_SAMPLE, /* struct nx_action_sample */
+ NXAST_SET_MPLS_LABEL, /* struct nx_action_ttl */
+ NXAST_SET_MPLS_TC, /* struct nx_action_ttl */
};
/* Header for Nicira-defined actions. */
};
OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16);
+/* Action structure for NXAST_SET_MPLS_LABEL. */
+struct nx_action_mpls_label {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SET_MPLS_LABEL. */
+ uint8_t zeros[2]; /* Must be zero. */
+ ovs_be32 label; /* LABEL */
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_label) == 16);
+
+/* Action structure for NXAST_SET_MPLS_TC. */
+struct nx_action_mpls_tc {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SET_MPLS_TC. */
+ uint8_t tc; /* TC */
+ uint8_t pad[5];
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_tc) == 16);
+
/* Action structure for NXAST_SET_MPLS_TTL. */
struct nx_action_mpls_ttl {
ovs_be16 type; /* OFPAT_VENDOR. */
struct ofp11_action_push push;
struct ofp11_action_pop_mpls ofp11_pop_mpls;
struct ofp11_action_set_queue ofp11_set_queue;
+ struct ofp11_action_mpls_label ofp11_mpls_label;
+ struct ofp11_action_mpls_tc ofp11_mpls_tc;
struct ofp11_action_mpls_ttl ofp11_mpls_ttl;
struct ofp11_action_group group;
struct ofp12_action_set_field set_field;
struct nx_action_pop_mpls pop_mpls;
struct nx_action_sample sample;
struct nx_action_learn learn;
+ struct nx_action_mpls_label mpls_label;
+ struct nx_action_mpls_tc mpls_tc;
};
static enum ofperr
OFPACT_MPLS_AFTER_VLAN, out);
break;
+ case OFPUTIL_NXAST_SET_MPLS_LABEL:
+ ofpact_put_SET_MPLS_LABEL(out)->label = a->mpls_label.label;
+ break;
+
+ case OFPUTIL_NXAST_SET_MPLS_TC:
+ ofpact_put_SET_MPLS_TC(out)->tc = a->mpls_tc.tc;
+ break;
+
case OFPUTIL_NXAST_SET_MPLS_TTL:
ofpact_put_SET_MPLS_TTL(out)->ttl = a->mpls_ttl.ttl;
break;
sf->value.mac, ETH_ADDR_LEN);
break;
+ case MFF_MPLS_LABEL:
+ ofputil_put_OFPAT11_SET_MPLS_LABEL(openflow)->mpls_label =
+ sf->value.be32;
+ break;
+
+ case MFF_MPLS_TC:
+ ofputil_put_OFPAT11_SET_MPLS_TC(openflow)->mpls_tc = sf->value.u8;
+ break;
+
case MFF_IPV4_SRC:
ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
break;
ofputil_put_OFPAT11_SET_TP_DST(openflow)->tp_port = sf->value.be16;
break;
- case MFF_MPLS_TC: /* XXX */
- case MFF_MPLS_LABEL: /* XXX */
default:
set_field_to_nxast(sf, openflow);
break;
case OFPUTIL_OFPAT12_SET_FIELD:
return set_field_from_openflow(&a->set_field, out);
+ case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+ ofpact_put_SET_MPLS_LABEL(out)->label = a->ofp11_mpls_label.mpls_label;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TC:
+ ofpact_put_SET_MPLS_TC(out)->tc = a->ofp11_mpls_tc.mpls_tc;
+ break;
+
case OFPUTIL_OFPAT11_SET_MPLS_TTL:
ofpact_put_SET_MPLS_TTL(out)->ttl = a->ofp11_mpls_ttl.mpls_ttl;
break;
case OFPACT_SET_IPV4_SRC:
case OFPACT_SET_L4_DST_PORT:
case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_SET_QUEUE:
case OFPACT_SET_TUNNEL:
case OFPACT_SET_IPV4_SRC:
case OFPACT_SET_L4_DST_PORT:
case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_SET_QUEUE:
case OFPACT_SET_TUNNEL:
case OFPACT_STACK_PUSH:
case OFPACT_STACK_POP:
case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_DEC_MPLS_TTL:
case OFPACT_PUSH_MPLS:
case OFPACT_STACK_POP:
return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow);
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_DEC_MPLS_TTL:
if (!eth_type_mpls(flow->dl_type)) {
ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
break;
+ case OFPACT_SET_MPLS_LABEL:
+ ofputil_put_NXAST_SET_MPLS_LABEL(out)->label
+ = ofpact_get_SET_MPLS_LABEL(a)->label;
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ ofputil_put_NXAST_SET_MPLS_TC(out)->tc
+ = ofpact_get_SET_MPLS_TC(a)->tc;
+ break;
+
case OFPACT_SET_MPLS_TTL:
ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl
= ofpact_get_SET_MPLS_TTL(a)->ttl;
case OFPACT_DEC_TTL:
case OFPACT_SET_IP_ECN:
case OFPACT_SET_IP_TTL:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_DEC_MPLS_TTL:
case OFPACT_SET_TUNNEL:
ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out);
break;
+ case OFPACT_SET_MPLS_LABEL:
+ ofputil_put_OFPAT11_SET_MPLS_LABEL(out)->mpls_label
+ = ofpact_get_SET_MPLS_LABEL(a)->label;
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ ofputil_put_OFPAT11_SET_MPLS_TC(out)->mpls_tc
+ = ofpact_get_SET_MPLS_TC(a)->tc;
+ break;
+
case OFPACT_SET_MPLS_TTL:
ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl
= ofpact_get_SET_MPLS_TTL(a)->ttl;
case OFPACT_SET_IP_ECN:
case OFPACT_SET_L4_SRC_PORT:
case OFPACT_SET_L4_DST_PORT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_TUNNEL: /* Convert to a set_field, too. */
switch ((int)a->type) {
value.be16 = htons(l4port->port);
break;
+ case OFPACT_SET_MPLS_LABEL:
+ field = MFF_MPLS_LABEL;
+ value.be32 = ofpact_get_SET_MPLS_LABEL(a)->label;
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ field = MFF_MPLS_TC;
+ value.u8 = ofpact_get_SET_MPLS_TC(a)->tc;
+ break;
+
case OFPACT_SET_TUNNEL:
field = MFF_TUN_ID;
value.be64 = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
case OFPACT_STACK_PUSH:
case OFPACT_STACK_POP:
case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_DEC_MPLS_TTL:
case OFPACT_SET_TUNNEL:
print_dec_ttl(ofpact_get_DEC_TTL(a), s);
break;
+ case OFPACT_SET_MPLS_LABEL:
+ ds_put_format(s, "set_mpls_label(%"PRIu32")",
+ ntohl(ofpact_get_SET_MPLS_LABEL(a)->label));
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ ds_put_format(s, "set_mpls_ttl(%"PRIu8")",
+ ofpact_get_SET_MPLS_TC(a)->tc);
+ break;
+
case OFPACT_SET_MPLS_TTL:
ds_put_format(s, "set_mpls_ttl(%"PRIu8")",
ofpact_get_SET_MPLS_TTL(a)->ttl);
DEFINE_OFPACT(STACK_PUSH, ofpact_stack, ofpact) \
DEFINE_OFPACT(STACK_POP, ofpact_stack, ofpact) \
DEFINE_OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids) \
+ DEFINE_OFPACT(SET_MPLS_LABEL, ofpact_mpls_label, ofpact) \
+ DEFINE_OFPACT(SET_MPLS_TC, ofpact_mpls_tc, ofpact) \
DEFINE_OFPACT(SET_MPLS_TTL, ofpact_mpls_ttl, ofpact) \
DEFINE_OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact) \
DEFINE_OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact) \
uint16_t cnt_ids[];
};
+/* OFPACT_SET_MPLS_LABEL.
+ *
+ * Used for OFPAT11_SET_MPLS_LABEL and NXAST_SET_MPLS_LABEL */
+struct ofpact_mpls_label {
+ struct ofpact ofpact;
+
+ ovs_be32 label;
+};
+
+/* OFPACT_SET_MPLS_TC.
+ *
+ * Used for OFPAT11_SET_MPLS_TC and NXAST_SET_MPLS_TC */
+struct ofpact_mpls_tc {
+ struct ofpact ofpact;
+
+ uint8_t tc;
+};
+
/* OFPACT_SET_MPLS_TTL.
*
- * Used for NXAST_SET_MPLS_TTL */
+ * Used for OFPAT11_SET_MPLS_TTL and NXAST_SET_MPLS_TTL */
struct ofpact_mpls_ttl {
struct ofpact ofpact;
return NULL;
}
+/* Parses 'arg' as the argument to a "set_mpls_label" action, and appends such
+ * an action to 'b'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_set_mpls_label(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(b);
+
+ if (*arg == '\0') {
+ return xstrdup("parse_set_mpls_label: expected label.");
+ }
+
+ mpls_label->label = htonl(atoi(arg));
+ return NULL;
+}
+
+/* Parses 'arg' as the argument to a "set_mpls_tc" action, and appends such an
+ * action to 'b'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_set_mpls_tc(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(b);
+
+ if (*arg == '\0') {
+ return xstrdup("parse_set_mpls_tc: expected tc.");
+ }
+
+ mpls_tc->tc = atoi(arg);
+ return NULL;
+}
+
/* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an
* action to 'ofpacts'.
*
error = parse_dec_ttl(ofpacts, arg);
break;
+ case OFPUTIL_NXAST_SET_MPLS_LABEL:
+ case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+ error = parse_set_mpls_label(ofpacts, arg);
+ break;
+
+ case OFPUTIL_NXAST_SET_MPLS_TC:
+ case OFPUTIL_OFPAT11_SET_MPLS_TC:
+ error = parse_set_mpls_tc(ofpacts, arg);
+ break;
+
case OFPUTIL_NXAST_SET_MPLS_TTL:
case OFPUTIL_OFPAT11_SET_MPLS_TTL:
error = parse_set_mpls_ttl(ofpacts, arg);
}
if (eth_type_mpls(match->flow.dl_type)) {
- enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC };
-
- if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
- /* MPLS not supported. */
- return OFPERR_OFPBMC_BAD_TAG;
+ if (!(wc & OFPFW11_MPLS_LABEL)) {
+ match_set_mpls_label(match, ofmatch->mpls_label);
+ }
+ if (!(wc & OFPFW11_MPLS_TC)) {
+ match_set_mpls_tc(match, ofmatch->mpls_tc);
}
}
ofmatch->tp_dst = match->flow.tp_dst;
}
- /* MPLS not supported. */
- wc |= OFPFW11_MPLS_LABEL;
- wc |= OFPFW11_MPLS_TC;
+ if (!(match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK))) {
+ wc |= OFPFW11_MPLS_LABEL;
+ } else {
+ ofmatch->mpls_label = htonl(mpls_lse_to_label(match->flow.mpls_lse));
+ }
+
+ if (!(match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK))) {
+ wc |= OFPFW11_MPLS_TC;
+ } else {
+ ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse);
+ }
ofmatch->metadata = match->flow.metadata;
ofmatch->metadata_mask = ~match->wc.masks.metadata;
OFPAT11_ACTION(OFPAT11_SET_NW_ECN, ofp11_action_nw_ecn, 0, "mod_nw_ecn")
OFPAT11_ACTION(OFPAT11_SET_TP_SRC, ofp_action_tp_port, 0, "mod_tp_src")
OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, 0, "mod_tp_dst")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_LABEL, ofp11_action_mpls_label, 0, "set_mpls_label")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_TC, ofp11_action_mpls_tc, 0, "set_mpls_tc")
OFPAT11_ACTION(OFPAT11_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl")
OFPAT11_ACTION(OFPAT11_DEC_MPLS_TTL, ofp_action_header, 0, "dec_mpls_ttl")
OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, 0, "push_vlan")
NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL)
NXAST_ACTION(NXAST_WRITE_METADATA, nx_action_write_metadata, 0,
"write_metadata")
+NXAST_ACTION(NXAST_SET_MPLS_LABEL, nx_action_mpls_label, 0, "set_mpls_label")
+NXAST_ACTION(NXAST_SET_MPLS_TC, nx_action_mpls_tc, 0, "set_mpls_tc")
NXAST_ACTION(NXAST_SET_MPLS_TTL, nx_action_mpls_ttl, 0, "set_mpls_ttl")
NXAST_ACTION(NXAST_DEC_MPLS_TTL, nx_action_header, 0, "dec_mpls_ttl")
NXAST_ACTION(NXAST_PUSH_MPLS, nx_action_push_mpls, 0, "push_mpls")
}
}
+static bool
+compose_set_mpls_label_action(struct xlate_ctx *ctx, ovs_be32 label)
+{
+ if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
+ return true;
+ }
+
+ /* If mpls_depth_delta is negative then an MPLS POP action has been
+ * executed and the resulting MPLS label stack is unknown. This means
+ * a SET MPLS LABEL action can't be executed as it needs to manipulate
+ * the top-most MPLS LSE. Thus, stop processing.
+ *
+ * It is planned that in the future this case will be handled
+ * by recirculation.
+ */
+ if (ctx->mpls_depth_delta < 0) {
+ return true;
+ }
+
+ ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
+ set_mpls_lse_label(&ctx->xin->flow.mpls_lse, label);
+ return false;
+}
+
+static bool
+compose_set_mpls_tc_action(struct xlate_ctx *ctx, uint8_t tc)
+{
+ if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
+ return true;
+ }
+
+ /* If mpls_depth_delta is negative then an MPLS POP action has been
+ * executed and the resulting MPLS label stack is unknown. This means
+ * a SET MPLS TC action can't be executed as it needs to manipulate
+ * the top-most MPLS LSE. Thus, stop processing.
+ *
+ * It is planned that in the future this case will be handled
+ * by recirculation.
+ */
+ if (ctx->mpls_depth_delta < 0) {
+ return true;
+ }
+
+ ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
+ set_mpls_lse_tc(&ctx->xin->flow.mpls_lse, tc);
+ return false;
+}
+
static bool
compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl)
{
}
break;
+ case OFPACT_SET_MPLS_LABEL:
+ if (compose_set_mpls_label_action(ctx,
+ ofpact_get_SET_MPLS_LABEL(a)->label)) {
+ return;
+ }
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ if (compose_set_mpls_tc_action(ctx,
+ ofpact_get_SET_MPLS_TC(a)->tc)) {
+ return;
+ }
+ break;
+
case OFPACT_SET_MPLS_TTL:
if (compose_set_mpls_ttl_action(ctx,
ofpact_get_SET_MPLS_TTL(a)->ttl)) {
0000 00 00 0800 00 16 00000000ffffffff 00000000ffffffff 0000 01bb dnl
00000000 00 000000 0000000000000000ffffffffffffffff
-dnl mpls_label not yet supported:
-# bad ofp11_match: OFPBMC_BAD_TAG
+# mpls,mpls_label=284280
+# 64: 12 -> 00
+# 65: 34 -> 04
0000 0058 00000000 000002f7 dnl
000000000000ffffffffffff 000000000000ffffffffffff dnl
0000 00 00 8847 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
12345678 00 000000 0000000000000000ffffffffffffffff
-dnl mpls_tc not yet supported:
-# bad ofp11_match: OFPBMC_BAD_TAG
+# mplsm,mpls_tc=2
+# 68: 5a -> 02
0000 0058 00000000 000001f7 dnl
000000000000ffffffffffff 000000000000ffffffffffff dnl
0000 00 00 8848 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl