[ovs-dev] [PATCH] [RFC] Alternate approach to MPLS.

Ben Pfaff blp at nicira.com
Fri Dec 13 08:28:21 UTC 2013


I've been a little frustrated with the current approach to MPLS, because it
seems quite difficult to understand.  One particularly difficult bit for
me is the variables used during translation, e.g. mpls_depth_delta and
pre_push_mpls_lse.  And what we end up with is support for a single MPLS
label, which I don't think is going to make any real-world users happy.

I think that we can do something easier to understand and more powerful
by just keeping track of all the labels in struct flow.  This patch is a
first stab at that.  It's incomplete--in particular I have not implemented
commit_mpls_action() because it seems slightly tricky and it's very late
at night here--and obviously untested, but I'd appreciate feedback on the
approach.

I think that if this works out then it would obviate the need for patches
"odp: Allow VLAN actions after MPLS actions" and "lib: Support pushing of
MPLS LSE before or after VLAN tag".

CC: Simon Horman <horms at verge.net.au>
---
 include/openflow/nicira-ext.h |   38 +++++++
 lib/flow.c                    |   96 ++++++++++++++---
 lib/flow.h                    |   21 ++--
 lib/match.c                   |   74 +++++++------
 lib/match.h                   |   15 +--
 lib/meta-flow.c               |  116 +++++++++++++++++---
 lib/meta-flow.h               |    5 +
 lib/nx-match.c                |   40 +++++--
 lib/odp-util.c                |  126 +++++++++++-----------
 lib/odp-util.h                |    3 +-
 lib/ofp-util.c                |   17 +--
 ofproto/ofproto-dpif-xlate.c  |  237 ++++++++++-------------------------------
 utilities/ovs-dpctl.c         |    6 +-
 13 files changed, 450 insertions(+), 344 deletions(-)

diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 22939f4..3746484 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1803,6 +1803,44 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 #define NXM_NX_TCP_FLAGS   NXM_HEADER  (0x0001, 34, 2)
 #define NXM_NX_TCP_FLAGS_W NXM_HEADER_W(0x0001, 34, 2)
 
+/* MPLS LSEs.
+ *
+ * NXM_NX_MPLS_LSE0 is the outermost label.
+ *
+ * Prereqs:
+ *   NXM_OF_ETH_TYPE must be either 0x8847 or 0x8848.
+ *   For NXM_NX_MPLS_LSE1 and later, NXM_NX_MPLS_LSE0 must match a value of 0
+ *   in the BOS bit.
+ *   For NXM_NX_MPLS_LSE2 and later, NXM_NX_MPLS_LSE1 must match a value of 0
+ *   in the BOS bit.
+ *   For NXM_NX_MPLS_LSE3 and later, NXM_NX_MPLS_LSE2 must match a value of 0
+ *   in the BOS bit.
+ *   and so on...
+ *
+ * Format:
+ *   32-bit integer in network byte order.  Space is reserved for up to
+ *   8 labels, but switches may implement fewer.
+ *
+ * Masking: Fully maskable.
+ */
+#define NXM_NX_MPLS_LSE0   NXM_HEADER  (0x0001, 35, 4)
+#define NXM_NX_MPLS_LSE0_W NXM_HEADER_W(0x0001, 35, 4)
+#define NXM_NX_MPLS_LSE1   NXM_HEADER  (0x0001, 36, 4)
+#define NXM_NX_MPLS_LSE1_W NXM_HEADER_W(0x0001, 36, 4)
+#define NXM_NX_MPLS_LSE2   NXM_HEADER  (0x0001, 37, 4)
+#define NXM_NX_MPLS_LSE2_W NXM_HEADER_W(0x0001, 37, 4)
+#define NXM_NX_MPLS_LSE3   NXM_HEADER  (0x0001, 38, 4)
+#define NXM_NX_MPLS_LSE3_W NXM_HEADER_W(0x0001, 38, 4)
+#define NXM_NX_MPLS_LSE4   NXM_HEADER  (0x0001, 39, 4)
+#define NXM_NX_MPLS_LSE4_W NXM_HEADER_W(0x0001, 39, 4)
+#define NXM_NX_MPLS_LSE5   NXM_HEADER  (0x0001, 40, 4)
+#define NXM_NX_MPLS_LSE5_W NXM_HEADER_W(0x0001, 40, 4)
+#define NXM_NX_MPLS_LSE6   NXM_HEADER  (0x0001, 41, 4)
+#define NXM_NX_MPLS_LSE6_W NXM_HEADER_W(0x0001, 41, 4)
+#define NXM_NX_MPLS_LSE7   NXM_HEADER  (0x0001, 42, 4)
+#define NXM_NX_MPLS_LSE8_W NXM_HEADER_W(0x0001, 42, 4)
+
+
 /* ## --------------------- ## */
 /* ## Requests and replies. ## */
 /* ## --------------------- ## */
diff --git a/lib/flow.c b/lib/flow.c
index 3633dff..17856f0 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -109,12 +109,11 @@ static void
 parse_mpls(struct ofpbuf *b, struct flow *flow)
 {
     struct mpls_hdr *mh;
-    bool top = true;
+    int idx = 0;
 
     while ((mh = ofpbuf_try_pull(b, sizeof *mh))) {
-        if (top) {
-            top = false;
-            flow->mpls_lse = mh->mpls_lse;
+        if (idx < ARRAY_SIZE(flow->mpls_lse)) {
+            flow->mpls_lse[idx++] = mh->mpls_lse;
         }
         if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
             break;
@@ -523,7 +522,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     fmd->tun_id = flow->tunnel.tun_id;
     fmd->tun_src = flow->tunnel.ip_src;
@@ -1036,35 +1035,95 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
     flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
 }
 
+void
+flow_push_mpls(struct flow *flow, ovs_be32 mpls_eth_type,
+               struct flow_wildcards *wc)
+{
+    int n = flow_count_mpls_labels(flow);
+
+    ovs_assert(n < ARRAY_SIZE(flow->mpls_lse));
+
+    memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    if (n) {
+        int i;
+
+        for (i = n; i >= 1; i--) {
+            flow->mpls_lse[i] = flow->mpls_lse[i - 1];
+        }
+        flow->mpls_lse[0] = (flow->mpls_lse[1]
+                             & htonl(MPLS_TTL_MASK | MPLS_TC_MASK));
+    } else {
+        ovs_be32 label;
+        uint8_t tc, ttl;
+
+        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            label = htonl(0x2); /* IPV6 Explicit Null. */
+        } else {
+            label = htonl(0x0); /* IPV4 Explicit Null. */
+        }
+        wc->masks.nw_tos |= IP_DSCP_MASK;
+        wc->masks.nw_ttl = 0xff;
+        tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
+        ttl = flow->nw_ttl ? flow->nw_ttl : 0x40;
+        flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, label);
+        flow->nw_proto = 0;     /* XXX clear everything else L3+ also */
+    }
+    flow->dl_type = mpls_eth_type;
+}
+
+bool
+flow_pop_mpls(struct flow *flow, ovs_be32 eth_type, struct flow_wildcards *wc)
+{
+    int n = flow_count_mpls_labels(flow);
+    int i;
+
+    if (n == 0) {
+        /* Nothing to pop. */
+        return false;
+    } else if (n == ARRAY_SIZE(flow->mpls_lse)
+        && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+        /* Can't pop because we don't know what to fill in mpls_lse[n - 1]. */
+        return false;
+    }
+
+    memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    for (i = 1; i < n; i++) {
+        flow->mpls_lse[i - 1] = flow->mpls_lse[i];
+    }
+    flow->mpls_lse[n - 1] = 0;
+    flow->dl_type = eth_type;
+    return true;
+}
+
 /* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted
  * as an OpenFlow 1.1 "mpls_label" value. */
 void
-flow_set_mpls_label(struct flow *flow, ovs_be32 label)
+flow_set_mpls_label(struct flow *flow, int idx, ovs_be32 label)
 {
-    set_mpls_lse_label(&flow->mpls_lse, label);
+    set_mpls_lse_label(&flow->mpls_lse[idx], label);
 }
 
 /* Sets the MPLS TTL that 'flow' matches to 'ttl', which should be in the
  * range 0...255. */
 void
-flow_set_mpls_ttl(struct flow *flow, uint8_t ttl)
+flow_set_mpls_ttl(struct flow *flow, int idx, uint8_t ttl)
 {
-    set_mpls_lse_ttl(&flow->mpls_lse, ttl);
+    set_mpls_lse_ttl(&flow->mpls_lse[idx], ttl);
 }
 
 /* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
  * range 0...7. */
 void
-flow_set_mpls_tc(struct flow *flow, uint8_t tc)
+flow_set_mpls_tc(struct flow *flow, int idx, uint8_t tc)
 {
-    set_mpls_lse_tc(&flow->mpls_lse, tc);
+    set_mpls_lse_tc(&flow->mpls_lse[idx], tc);
 }
 
 /* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */
 void
-flow_set_mpls_bos(struct flow *flow, uint8_t bos)
+flow_set_mpls_bos(struct flow *flow, int idx, uint8_t bos)
 {
-    set_mpls_lse_bos(&flow->mpls_lse, bos);
+    set_mpls_lse_bos(&flow->mpls_lse[idx], bos);
 }
 
 
@@ -1224,8 +1283,17 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
     }
 
     if (eth_type_mpls(flow->dl_type)) {
+        int n;
+
         b->l2_5 = b->l3;
-        push_mpls(b, flow->dl_type, flow->mpls_lse);
+        for (n = 1; n < ARRAY_SIZE(flow->mpls_lse); n++) {
+            if (flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK)) {
+                break;
+            }
+        }
+        while (n > 0) {
+            push_mpls(b, flow->dl_type, flow->mpls_lse[--n]);
+        }
     }
 }
 
diff --git a/lib/flow.h b/lib/flow.h
index 44d96ad..c670c49 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -36,7 +36,7 @@ struct ofpbuf;
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 23
+#define FLOW_WC_SEQ 24
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -108,7 +108,7 @@ struct flow {
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
 
     /* L3 */
-    ovs_be32 mpls_lse;          /* MPLS label stack entry. */
+    ovs_be32 mpls_lse[3];       /* MPLS label stack entry. */
     struct in6_addr ipv6_src;   /* IPv6 source address. */
     struct in6_addr ipv6_dst;   /* IPv6 destination address. */
     struct in6_addr nd_target;  /* IPv6 neighbor discovery (ND) target. */
@@ -123,6 +123,7 @@ struct flow {
     uint8_t arp_tha[6];         /* ARP/ND target hardware address. */
     ovs_be16 tcp_flags;         /* TCP flags. With L3 to avoid matching L4. */
     ovs_be16 pad;               /* Padding. */
+
     /* L4 */
     ovs_be16 tp_src;            /* TCP/UDP/SCTP source port. */
     ovs_be16 tp_dst;            /* TCP/UDP/SCTP destination port.
@@ -134,8 +135,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
 BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
-                  == sizeof(struct flow_tnl) + 156
-                  && FLOW_WC_SEQ == 23);
+                  == sizeof(struct flow_tnl) + 164
+                  && FLOW_WC_SEQ == 24);
 
 /* Incremental points at which flow classification may be performed in
  * segments.
@@ -193,10 +194,14 @@ void flow_set_dl_vlan(struct flow *, ovs_be16 vid);
 void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
 void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
 
-void flow_set_mpls_label(struct flow *flow, ovs_be32 label);
-void flow_set_mpls_ttl(struct flow *flow, uint8_t ttl);
-void flow_set_mpls_tc(struct flow *flow, uint8_t tc);
-void flow_set_mpls_bos(struct flow *flow, uint8_t stack);
+int flow_count_mpls_labels(const struct flow *);
+void flow_push_mpls(struct flow *, ovs_be32 mpls_eth_type,
+                    struct flow_wildcards *);
+bool flow_pop_mpls(struct flow *, ovs_be32 eth_type, struct flow_wildcards *);
+void flow_set_mpls_label(struct flow *, int idx, ovs_be32 label);
+void flow_set_mpls_ttl(struct flow *, int idx, uint8_t ttl);
+void flow_set_mpls_tc(struct flow *, int idx, uint8_t tc);
+void flow_set_mpls_bos(struct flow *, int idx, uint8_t stack);
 
 void flow_compose(struct ofpbuf *, const struct flow *);
 
diff --git a/lib/match.c b/lib/match.c
index 9e5da13..6ba614f 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -460,53 +460,53 @@ match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
 
 /* Modifies 'match' so that the MPLS label is wildcarded. */
 void
-match_set_any_mpls_label(struct match *match)
+match_set_any_mpls_label(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse &= ~htonl(MPLS_LABEL_MASK);
-    flow_set_mpls_label(&match->flow, htonl(0));
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_LABEL_MASK);
+    flow_set_mpls_label(&match->flow, idx, htonl(0));
 }
 
 /* Modifies 'match' so that it matches only packets with an MPLS header whose
  * label equals the low 20 bits of 'mpls_label'. */
 void
-match_set_mpls_label(struct match *match, ovs_be32 mpls_label)
+match_set_mpls_label(struct match *match, int idx, ovs_be32 mpls_label)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
-    flow_set_mpls_label(&match->flow, mpls_label);
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_LABEL_MASK);
+    flow_set_mpls_label(&match->flow, idx, mpls_label);
 }
 
 /* Modifies 'match' so that the MPLS TC is wildcarded. */
 void
-match_set_any_mpls_tc(struct match *match)
+match_set_any_mpls_tc(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse &= ~htonl(MPLS_TC_MASK);
-    flow_set_mpls_tc(&match->flow, 0);
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_TC_MASK);
+    flow_set_mpls_tc(&match->flow, idx, 0);
 }
 
 /* Modifies 'match' so that it matches only packets with an MPLS header whose
  * Traffic Class equals the low 3 bits of 'mpls_tc'. */
 void
-match_set_mpls_tc(struct match *match, uint8_t mpls_tc)
+match_set_mpls_tc(struct match *match, int idx, uint8_t mpls_tc)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
-    flow_set_mpls_tc(&match->flow, mpls_tc);
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_TC_MASK);
+    flow_set_mpls_tc(&match->flow, idx, mpls_tc);
 }
 
 /* Modifies 'match' so that the MPLS stack flag is wildcarded. */
 void
-match_set_any_mpls_bos(struct match *match)
+match_set_any_mpls_bos(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse &= ~htonl(MPLS_BOS_MASK);
-    flow_set_mpls_bos(&match->flow, 0);
+    match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_BOS_MASK);
+    flow_set_mpls_bos(&match->flow, idx, 0);
 }
 
 /* Modifies 'match' so that it matches only packets with an MPLS header whose
  * Stack Flag equals the lower bit of 'mpls_bos' */
 void
-match_set_mpls_bos(struct match *match, uint8_t mpls_bos)
+match_set_mpls_bos(struct match *match, int idx, uint8_t mpls_bos)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_BOS_MASK);
-    flow_set_mpls_bos(&match->flow, mpls_bos);
+    match->wc.masks.mpls_lse[idx] |= htonl(MPLS_BOS_MASK);
+    flow_set_mpls_bos(&match->flow, idx, mpls_bos);
 }
 
 void
@@ -796,6 +796,22 @@ format_be16_masked(struct ds *s, const char *name,
 }
 
 static void
+format_be32_masked(struct ds *s, const char *name,
+                   ovs_be32 value, ovs_be32 mask)
+{
+    if (mask != htonl(0)) {
+        ds_put_format(s, "%s=", name);
+        if (mask == OVS_BE32_MAX) {
+            ds_put_format(s, "%"PRIu32, ntohl(value));
+        } else {
+            ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
+                          ntohl(value), ntohl(mask));
+        }
+        ds_put_char(s, ',');
+    }
+}
+
+static void
 format_uint32_masked(struct ds *s, const char *name,
                    uint32_t value, uint32_t mask)
 {
@@ -856,7 +872,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%u,", priority);
@@ -1008,22 +1024,10 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
     if (wc->masks.nw_ttl) {
         ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
-        ds_put_format(s, "mpls_label=%"PRIu32",",
-                 mpls_lse_to_label(f->mpls_lse));
-    }
-    if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
-        ds_put_format(s, "mpls_tc=%"PRIu8",",
-                 mpls_lse_to_tc(f->mpls_lse));
-    }
-    if (wc->masks.mpls_lse & htonl(MPLS_TTL_MASK)) {
-        ds_put_format(s, "mpls_ttl=%"PRIu8",",
-                 mpls_lse_to_ttl(f->mpls_lse));
-    }
-    if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
-        ds_put_format(s, "mpls_bos=%"PRIu8",",
-                 mpls_lse_to_bos(f->mpls_lse));
-    }
+    format_be32_masked(s, "mpls_lse0", f->mpls_lse[0], wc->masks.mpls_lse[0]);
+    format_be32_masked(s, "mpls_lse1", f->mpls_lse[1], wc->masks.mpls_lse[1]);
+    format_be32_masked(s, "mpls_lse2", f->mpls_lse[2], wc->masks.mpls_lse[2]);
+
     switch (wc->masks.nw_frag) {
     case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
         ds_put_format(s, "nw_frag=%s,",
diff --git a/lib/match.h b/lib/match.h
index ee01acd..7a8ae68 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -78,13 +78,16 @@ void match_set_vlan_vid(struct match *, ovs_be16);
 void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask);
 void match_set_any_pcp(struct match *);
 void match_set_dl_vlan_pcp(struct match *, uint8_t);
-void match_set_any_mpls_label(struct match *);
-void match_set_mpls_label(struct match *, ovs_be32);
-void match_set_any_mpls_tc(struct match *);
-void match_set_mpls_tc(struct match *, uint8_t);
-void match_set_any_mpls_bos(struct match *);
-void match_set_mpls_bos(struct match *, uint8_t);
+void match_set_any_mpls_lse(struct match *, int idx);
+void match_set_mpls_lse(struct match *, int idx, ovs_be32);
+void match_set_any_mpls_label(struct match *, int idx);
+void match_set_mpls_label(struct match *, int idx, ovs_be32);
+void match_set_any_mpls_tc(struct match *, int idx);
+void match_set_mpls_tc(struct match *, int idx, uint8_t);
+void match_set_any_mpls_bos(struct match *, int idx);
+void match_set_mpls_bos(struct match *, int idx, uint8_t);
 void match_set_tp_src(struct match *, ovs_be16);
+void match_set_mpls_lse(struct match *, int idx, ovs_be32 lse);
 void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
 void match_set_tp_dst(struct match *, ovs_be16);
 void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index cd121bb..d50cc18 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -370,6 +370,42 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
         OFPUTIL_P_NXM_OXM_ANY,
         OFPUTIL_P_NONE,
         -1,
+    }, {
+        MFF_MPLS_LSE0, "mpls_lse0", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_FULLY,
+        MFS_HEXADECIMAL,
+        MFP_MPLS,
+        false,
+        NXM_NX_MPLS_LSE0, "NXM_NX_MPLS_LSE0",
+        NXM_NX_MPLS_LSE0, "NXM_NX_MPLS_LSE0",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
+        -1,
+    }, {
+        MFF_MPLS_LSE1, "mpls_lse1", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_FULLY,
+        MFS_HEXADECIMAL,
+        MFP_MPLS1,
+        false,
+        NXM_NX_MPLS_LSE1, "NXM_NX_MPLS_LSE1",
+        NXM_NX_MPLS_LSE1, "NXM_NX_MPLS_LSE1",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
+        -1,
+    }, {
+        MFF_MPLS_LSE2, "mpls_lse2", NULL,
+        MF_FIELD_SIZES(be32),
+        MFM_FULLY,
+        MFS_HEXADECIMAL,
+        MFP_MPLS2,
+        false,
+        NXM_NX_MPLS_LSE2, "NXM_NX_MPLS_LSE2",
+        NXM_NX_MPLS_LSE2, "NXM_NX_MPLS_LSE2",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NONE,
+        -1,
     },
 
     /* ## -- ## */
@@ -926,11 +962,15 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
         return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK));
 
     case MFF_MPLS_LABEL:
-        return !(wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK));
+        return !(wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK));
     case MFF_MPLS_TC:
-        return !(wc->masks.mpls_lse & htonl(MPLS_TC_MASK));
+        return !(wc->masks.mpls_lse[1] & htonl(MPLS_TC_MASK));
     case MFF_MPLS_BOS:
-        return !(wc->masks.mpls_lse & htonl(MPLS_BOS_MASK));
+        return !(wc->masks.mpls_lse[2] & htonl(MPLS_BOS_MASK));
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
+        return !wc->masks.mpls_lse[mf->id - MFF_MPLS_LSE0];
 
     case MFF_IPV4_SRC:
         return !wc->masks.nw_src;
@@ -1052,6 +1092,13 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
         return (flow->vlan_tci & htons(VLAN_CFI)) != 0;
     case MFP_MPLS:
         return eth_type_mpls(flow->dl_type);
+    case MFP_MPLS1:
+        return (eth_type_mpls(flow->dl_type)
+                && !(flow->mpls_lse[0] & htonl(MPLS_BOS_MASK)));
+    case MFP_MPLS2:
+        return (eth_type_mpls(flow->dl_type)
+                && !(flow->mpls_lse[0] & htonl(MPLS_BOS_MASK))
+                && !(flow->mpls_lse[1] & htonl(MPLS_BOS_MASK)));
     case MFP_IP_ANY:
         return is_ip_any(flow);
 
@@ -1115,6 +1162,15 @@ mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow *mask)
     case MFP_IP_ANY:
         mask->dl_type = OVS_BE16_MAX;
         break;
+    case MFP_MPLS1:
+        mask->dl_type = OVS_BE16_MAX;
+        mask->mpls_lse[0] = htonl(MPLS_BOS_MASK);
+        break;
+    case MFP_MPLS2:
+        mask->dl_type = OVS_BE16_MAX;
+        mask->mpls_lse[0] = htonl(MPLS_BOS_MASK);
+        mask->mpls_lse[1] = htonl(MPLS_BOS_MASK);
+        break;
     case MFP_VLAN_VID:
         mask->vlan_tci |= htons(VLAN_CFI);
         break;
@@ -1153,6 +1209,9 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ETH_DST:
     case MFF_ETH_TYPE:
     case MFF_VLAN_TCI:
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
     case MFF_IPV4_SRC:
     case MFF_IPV4_DST:
     case MFF_IPV6_SRC:
@@ -1302,15 +1361,21 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         break;
 
     case MFF_MPLS_LABEL:
-        value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse));
+        value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse[0]));
         break;
 
     case MFF_MPLS_TC:
-        value->u8 = mpls_lse_to_tc(flow->mpls_lse);
+        value->u8 = mpls_lse_to_tc(flow->mpls_lse[0]);
         break;
 
     case MFF_MPLS_BOS:
-        value->u8 = mpls_lse_to_bos(flow->mpls_lse);
+        value->u8 = mpls_lse_to_bos(flow->mpls_lse[0]);
+        break;
+
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
+        value->be32 = flow->mpls_lse[mf->id - MFF_MPLS_LSE0];
         break;
 
     case MFF_IPV4_SRC:
@@ -1498,15 +1563,21 @@ mf_set_value(const struct mf_field *mf,
         break;
 
     case MFF_MPLS_LABEL:
-        match_set_mpls_label(match, value->be32);
+        match_set_mpls_label(match, 0, value->be32);
         break;
 
     case MFF_MPLS_TC:
-        match_set_mpls_tc(match, value->u8);
+        match_set_mpls_tc(match, 0, value->u8);
         break;
 
     case MFF_MPLS_BOS:
-        match_set_mpls_bos(match, value->u8);
+        match_set_mpls_bos(match, 0, value->u8);
+        break;
+
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
+        match_set_mpls_lse(match, mf->id - MFF_MPLS_LSE0, value->be32);
         break;
 
     case MFF_IPV4_SRC:
@@ -1711,15 +1782,21 @@ mf_set_flow_value(const struct mf_field *mf,
         break;
 
     case MFF_MPLS_LABEL:
-        flow_set_mpls_label(flow, value->be32);
+        flow_set_mpls_label(flow, 0, value->be32);
         break;
 
     case MFF_MPLS_TC:
-        flow_set_mpls_tc(flow, value->u8);
+        flow_set_mpls_tc(flow, 0, value->u8);
         break;
 
     case MFF_MPLS_BOS:
-        flow_set_mpls_bos(flow, value->u8);
+        flow_set_mpls_bos(flow, 0, value->u8);
+        break;
+
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
+        flow->mpls_lse[mf->id - MFF_MPLS_LSE0] = value->be32;
         break;
 
     case MFF_IPV4_SRC:
@@ -1921,15 +1998,21 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
         break;
 
     case MFF_MPLS_LABEL:
-        match_set_any_mpls_label(match);
+        match_set_any_mpls_label(match, 0);
         break;
 
     case MFF_MPLS_TC:
-        match_set_any_mpls_tc(match);
+        match_set_any_mpls_tc(match, 0);
         break;
 
     case MFF_MPLS_BOS:
-        match_set_any_mpls_bos(match);
+        match_set_any_mpls_bos(match, 0);
+        break;
+
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
+        match_set_any_mpls_lse(match, mf->id - MFF_MPLS_LSE0);
         break;
 
     case MFF_IPV4_SRC:
@@ -2070,6 +2153,9 @@ mf_set(const struct mf_field *mf,
     case MFF_MPLS_LABEL:
     case MFF_MPLS_TC:
     case MFF_MPLS_BOS:
+    case MFF_MPLS_LSE0:
+    case MFF_MPLS_LSE1:
+    case MFF_MPLS_LSE2:
     case MFF_IP_PROTO:
     case MFF_IP_TTL:
     case MFF_IP_DSCP:
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index cf92556..ea81a34 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -85,6 +85,9 @@ enum OVS_PACKED_ENUM mf_field_id {
     MFF_MPLS_LABEL,             /* be32 */
     MFF_MPLS_TC,                /* u8 */
     MFF_MPLS_BOS,               /* u8 */
+    MFF_MPLS_LSE0,              /* be32 */
+    MFF_MPLS_LSE1,              /* be32 */
+    MFF_MPLS_LSE2,              /* be32 */
 
     /* L3. */
     MFF_IPV4_SRC,               /* be32 */
@@ -193,6 +196,8 @@ enum OVS_PACKED_ENUM mf_prereqs {
 
     /* L2.5 requirements. */
     MFP_MPLS,
+    MFP_MPLS1,
+    MFP_MPLS2,
 
     /* L2+L3 requirements. */
     MFP_TCP,                    /* On IPv4 or IPv6. */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 983fd7d..b8b2753 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -572,7 +572,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     /* Metadata. */
     if (match->wc.masks.in_port.ofp_port) {
@@ -616,17 +616,39 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
 
     /* MPLS. */
     if (eth_type_mpls(flow->dl_type)) {
-        if (match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK)) {
-            nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(flow->mpls_lse));
-        }
+        uint32_t mask0 = match->wc.masks.mpls_lse[0];
+        if (mask0) {
+            ovs_be32 lse0 = flow->mpls_lse[0];
+            uint32_t tc_mask = mask0 & htonl(MPLS_TC_MASK);
+            uint32_t bos_mask = mask0 & htonl(MPLS_BOS_MASK);
+            uint32_t label_mask = mask0 & htonl(MPLS_LABEL_MASK);
+            uint32_t ttl_mask = mask0 & htonl(MPLS_TTL_MASK);
+
+            if ((!tc_mask || tc_mask == htonl(MPLS_TC_MASK)) &&
+                (!bos_mask || bos_mask == htonl(MPLS_BOS_MASK)) &&
+                (!label_mask || label_mask == htonl(MPLS_LABEL_MASK)) &&
+                !ttl_mask)
+            {
+                if (tc_mask) {
+                    nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(lse0));
+                }
+
+                if (bos_mask) {
+                    nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(lse0));
+                }
 
-        if (match->wc.masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
-            nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(flow->mpls_lse));
+                if (label_mask) {
+                    nxm_put_32(b, OXM_OF_MPLS_LABEL,
+                               htonl(mpls_lse_to_label(lse0)));
+                }
+            } else {
+                nxm_put_32m(b, NXM_NX_MPLS_LSE0, lse0, mask0);
+            }
         }
 
-        if (match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
-            nxm_put_32(b, OXM_OF_MPLS_LABEL,
-                       htonl(mpls_lse_to_label(flow->mpls_lse)));
+        for (i = 1; i < ARRAY_SIZE(flow->mpls_lse); i++) {
+            nxm_put_32m(b, NXM_NX_MPLS_LSE0 + i, flow->mpls_lse[i],
+                        match->wc.masks.mpls_lse[i]);
         }
     }
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 6dbc213..0cb1250 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -352,20 +352,33 @@ format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
 
 static void
 format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key,
-            const struct ovs_key_mpls *mpls_mask)
+            const struct ovs_key_mpls *mpls_mask, int n)
 {
-    ovs_be32 key = mpls_key->mpls_lse;
+    if (n == 1) {
+        ovs_be32 key = mpls_key->mpls_lse;
 
-    if (mpls_mask == NULL) {
-        format_mpls_lse(ds, key);
+        if (mpls_mask == NULL) {
+            format_mpls_lse(ds, key);
+        } else {
+            ovs_be32 mask = mpls_mask->mpls_lse;
+
+            ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
+                          mpls_lse_to_label(key), mpls_lse_to_label(mask),
+                          mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
+                          mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
+                          mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
+        }
     } else {
-        ovs_be32 mask = mpls_mask->mpls_lse;
+        int i;
 
-        ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
-                  mpls_lse_to_label(key), mpls_lse_to_label(mask),
-                  mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
-                  mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
-                  mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
+        for (i = 0; i < n; i++) {
+            ds_put_format(ds, "lse%d=%#"PRIx32, i, mpls_key[i].mpls_lse);
+            if (mpls_mask) {
+                ds_put_format(ds, "%#"PRIx32, mpls_mask[i].mpls_lse);
+            }
+            ds_put_char(ds, ',');
+        }
+        ds_chomp(ds, ',');
     }
 }
 
@@ -721,7 +734,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
     case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
     case OVS_KEY_ATTR_ETHERTYPE: return 2;
-    case OVS_KEY_ATTR_MPLS: return sizeof(struct ovs_key_mpls);
+    case OVS_KEY_ATTR_MPLS: return -2;
     case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
     case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
     case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
@@ -1115,10 +1128,23 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
     case OVS_KEY_ATTR_MPLS: {
         const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
         const struct ovs_key_mpls *mpls_mask = NULL;
+        size_t size = nl_attr_get_size(a);
+
+        if (!size || size % sizeof *mpls_key) {
+            ds_put_format(ds, "(bad key length %"PRIuSIZE")",
+                          nl_attr_get_size(a));
+            return;
+        }
         if (!is_exact) {
             mpls_mask = nl_attr_get(ma);
+            if (nl_attr_get_size(a) != nl_attr_get_size(ma)) {
+                ds_put_format(ds, "(key length %"PRIuSIZE" != "
+                              "mask length %"PRIuSIZE")",
+                              nl_attr_get_size(a), nl_attr_get_size(ma));
+                return;
+            }
         }
-        format_mpls(ds, mpls_key, mpls_mask);
+        format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key);
         break;
     }
 
@@ -2493,10 +2519,14 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
         memcpy(arp_key->arp_tha, data->arp_tha, ETH_ADDR_LEN);
     } else if (eth_type_mpls(flow->dl_type)) {
         struct ovs_key_mpls *mpls_key;
+        int i, n;
 
+        n = flow_count_mpls_labels(flow);
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
-                                            sizeof *mpls_key);
-        mpls_key->mpls_lse = data->mpls_lse;
+                                            n * sizeof *mpls_key);
+        for (i = 0; i < n; i++) {
+            mpls_key[i].mpls_lse = data->mpls_lse[i];
+        }
     }
 
     if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -2781,21 +2811,34 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     enum ovs_key_attr expected_bit = 0xff;
 
     if (eth_type_mpls(src_flow->dl_type)) {
+        size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
+        const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
+        int n = size / sizeof(ovs_be32);
+        int i;
+
+        if (!size || size % sizeof(ovs_be32)) {
+            return ODP_FIT_ERROR;
+        }
+
         if (!is_mask) {
             expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
 
             if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
                 return ODP_FIT_TOO_LITTLE;
             }
-            flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
         } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
-            flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
-
             if (flow->mpls_lse != 0 && flow->dl_type != htons(0xffff)) {
                 return ODP_FIT_ERROR;
             }
             expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
         }
+
+        for (i = 0; i < n && i < ARRAY_SIZE(flow->mpls_lse); i++) {
+            flow->mpls_lse[i] = mpls_lse[i];
+        }
+        if (n > ARRAY_SIZE(flow->mpls_lse)) {
+            return ODP_FIT_TOO_MUCH;
+        }
         goto done;
     } else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
         if (!is_mask) {
@@ -3352,43 +3395,9 @@ commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
 
 static void
 commit_mpls_action(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                   int *mpls_depth_delta)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (flow->mpls_lse == base->mpls_lse && !*mpls_depth_delta) {
-        return;
-    }
-
-    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
-
-    switch (*mpls_depth_delta) {
-    case -1:
-        nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, flow->dl_type);
-        break;
-    case 1: {
-        struct ovs_action_push_mpls *mpls;
-
-        mpls = nl_msg_put_unspec_zero(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
-                                      sizeof *mpls);
-        mpls->mpls_ethertype = flow->dl_type;
-        mpls->mpls_lse = flow->mpls_lse;
-        break;
-    }
-    case 0: {
-        struct ovs_key_mpls mpls_key;
-
-        mpls_key.mpls_lse = flow->mpls_lse;
-        commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
-                          &mpls_key, sizeof(mpls_key));
-        break;
-    }
-    default:
-        NOT_REACHED();
-    }
-
-    base->dl_type = flow->dl_type;
-    base->mpls_lse = flow->mpls_lse;
-    *mpls_depth_delta = 0;
+    /* XXX need to fill this in */
 }
 
 static void
@@ -3609,20 +3618,15 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base,
  * slow path, if there is one, otherwise 0. */
 enum slow_path_reason
 commit_odp_actions(const struct flow *flow, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                   int *mpls_depth_delta)
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
     enum slow_path_reason slow;
 
     commit_set_ether_addr_action(flow, base, odp_actions, wc);
-    commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
     slow = commit_set_nw_action(flow, base, odp_actions, wc);
     commit_set_port_action(flow, base, odp_actions, wc);
-    /* Committing MPLS actions should occur after committing nw and port
-     * actions. This is because committing MPLS actions may alter a packet so
-     * that it is no longer IP and thus nw and port actions are no longer valid.
-     */
-    commit_mpls_action(flow, base, odp_actions, wc, mpls_depth_delta);
+    commit_mpls_action(flow, base, odp_actions, wc);
+    commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
     commit_set_priority_action(flow, base, odp_actions, wc);
     commit_set_pkt_mark_action(flow, base, odp_actions, wc);
 
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 821b2c4..a222a35 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -174,8 +174,7 @@ void commit_odp_tunnel_action(const struct flow *, struct flow *base,
 enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct flow *base,
                                          struct ofpbuf *odp_actions,
-                                         struct flow_wildcards *wc,
-                                         int *mpls_depth_delta);
+                                         struct flow_wildcards *wc);
 
 /* ofproto-dpif interface.
  *
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 7fc4c7c..3140410 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -437,10 +437,10 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
 
     if (eth_type_mpls(match->flow.dl_type)) {
         if (!(wc & OFPFW11_MPLS_LABEL)) {
-            match_set_mpls_label(match, ofmatch->mpls_label);
+            match_set_mpls_label(match, 0, ofmatch->mpls_label);
         }
         if (!(wc & OFPFW11_MPLS_TC)) {
-            match_set_mpls_tc(match, ofmatch->mpls_tc);
+            match_set_mpls_tc(match, 0, ofmatch->mpls_tc);
         }
     }
 
@@ -533,16 +533,17 @@ ofputil_match_to_ofp11_match(const struct match *match,
         ofmatch->tp_dst = match->flow.tp_dst;
     }
 
-    if (!(match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK))) {
+    if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK))) {
         wc |= OFPFW11_MPLS_LABEL;
     } else {
-        ofmatch->mpls_label = htonl(mpls_lse_to_label(match->flow.mpls_lse));
+        ofmatch->mpls_label = htonl(mpls_lse_to_label(
+                                        match->flow.mpls_lse[0]));
     }
 
-    if (!(match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK))) {
+    if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK))) {
         wc |= OFPFW11_MPLS_TC;
     } else {
-        ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse);
+        ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse[0]);
     }
 
     ofmatch->metadata = match->flow.metadata;
@@ -5346,7 +5347,7 @@ ofputil_normalize_match__(struct match *match, bool may_log)
         wc.masks.nd_target = in6addr_any;
     }
     if (!(may_match & MAY_MPLS)) {
-        wc.masks.mpls_lse = htonl(0);
+        memset(wc.masks.mpls_lse, 0, sizeof wc.masks.mpls_lse);
     }
 
     /* Log any changes. */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index ac273e9..1d6bd69 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -164,13 +164,6 @@ struct xlate_ctx {
     /* The rule that we are currently translating, or NULL. */
     struct rule_dpif *rule;
 
-    int mpls_depth_delta;       /* Delta of the mpls stack depth since
-                                 * actions were last committed.
-                                 * Must be between -1 and 1 inclusive. */
-    ovs_be32 pre_push_mpls_lse; /* Used to record the top-most MPLS LSE
-                                 * prior to an mpls_push so that it may be
-                                 * used for a subsequent mpls_pop. */
-
     /* Resubmit statistics, via xlate_table_action(). */
     int recurse;                /* Current resubmit nesting depth. */
     int resubmits;              /* Total number of resubmits. */
@@ -1668,7 +1661,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
     /* If 'struct flow' gets additional metadata, we'll need to zero it out
      * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
 
     if (!xport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -1783,8 +1776,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     if (out_port != ODPP_NONE) {
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
-                                              &ctx->xout->wc,
-                                              &ctx->mpls_depth_delta);
+                                              &ctx->xout->wc);
         nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
                             out_port);
 
@@ -2062,8 +2054,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
 
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           &ctx->xout->odp_actions,
-                                          &ctx->xout->wc,
-                                          &ctx->mpls_depth_delta);
+                                          &ctx->xout->wc);
 
     odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
                         ctx->xout->odp_actions.size, NULL, NULL);
@@ -2087,99 +2078,41 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
     ofpbuf_delete(packet);
 }
 
-static bool
-compose_mpls_push_action(struct xlate_ctx *ctx, ovs_be16 eth_type)
+static void
+compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls)
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
     struct flow *flow = &ctx->xin->flow;
+    ovs_be16 vlan_tci = flow->vlan_tci;
+    int n;
 
-    ovs_assert(eth_type_mpls(eth_type));
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * composed and the resulting MPLS label stack is unknown.  This means
-     * an MPLS PUSH action can't be composed as it needs to know either the
-     * top-most MPLS LSE to use as a template for the new MPLS LSE, or that
-     * there is no MPLS label stack present.  Thus, stop processing.
-     *
-     * If mpls_depth_delta is positive then an MPLS PUSH action has been
-     * composed and no further MPLS PUSH action may be performed without
-     * losing MPLS LSE and ether type information held in xtx->xin->flow.
-     * Thus, stop processing.
-     *
-     * If the MPLS LSE of the flow and base_flow differ then the MPLS LSE
-     * has been updated.  Performing a MPLS PUSH action may be would result in
-     * losing MPLS LSE and ether type information held in xtx->xin->flow.
-     * Thus, stop processing.
-     *
-     * It is planned that in the future this case will be handled
-     * by recirculation */
-    if (ctx->mpls_depth_delta ||
-        ctx->xin->flow.mpls_lse != ctx->base_flow.mpls_lse) {
-        return true;
-    }
-
-    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
-
-    ctx->pre_push_mpls_lse = ctx->xin->flow.mpls_lse;
+    ovs_assert(eth_type_mpls(mpls->ethertype));
 
-    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
-        flow->mpls_lse &= ~htonl(MPLS_BOS_MASK);
-    } else {
-        ovs_be32 label;
-        uint8_t tc, ttl;
-
-        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
-            label = htonl(0x2); /* IPV6 Explicit Null. */
+    n = flow_count_mpls_labels(flow);
+    if (!n) {
+        if (mpls->position == OFPACT_MPLS_BEFORE_VLAN) {
+            vlan_tci = 0;
         } else {
-            label = htonl(0x0); /* IPV4 Explicit Null. */
+            flow->vlan_tci = 0;
         }
-        wc->masks.nw_tos |= IP_DSCP_MASK;
-        wc->masks.nw_ttl = 0xff;
-        tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
-        ttl = flow->nw_ttl ? flow->nw_ttl : 0x40;
-        flow->mpls_lse = set_mpls_lse_values(ttl, tc, 1, label);
+        ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
+                                              &ctx->xout->odp_actions,
+                                              &ctx->xout->wc);
+    } else if (n >= ARRAY_SIZE(flow->mpls_lse)) {
+        return;
     }
-    flow->dl_type = eth_type;
-    ctx->mpls_depth_delta++;
 
-    return false;
+    flow_push_mpls(flow, mpls->ethertype, wc);
+    flow->vlan_tci = vlan_tci;
 }
 
-static bool
+static void
 compose_mpls_pop_action(struct xlate_ctx *ctx, ovs_be16 eth_type)
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
+    struct flow *flow = &ctx->xin->flow;
 
-    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
-        return true;
-    }
-
-    /* If mpls_depth_delta is negative then an MPLS POP action has been
-     * composed.  Performing another MPLS POP action
-     * would result in losing ether type that results from
-     * the already composed MPLS POP. 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;
-    }
-
-    memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
-
-    /* If mpls_depth_delta is positive then an MPLS PUSH action has been
-     * executed and the previous MPLS LSE saved in ctx->pre_push_mpls_lse. The
-     * flow's MPLS LSE should be restored to that value to allow any
-     * subsequent actions that update of the LSE to be executed correctly.
-     */
-    if (ctx->mpls_depth_delta > 0) {
-        ctx->xin->flow.mpls_lse = ctx->pre_push_mpls_lse;
-    }
-
-    ctx->xin->flow.dl_type = eth_type;
-    ctx->mpls_depth_delta--;
-
-    return false;
+    flow_pop_mpls(flow, eth_type, wc);
 }
 
 static bool
@@ -2208,99 +2141,53 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
     }
 }
 
-static bool
+static void
 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;
+    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
+        ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_LABEL_MASK);
+        set_mpls_lse_label(&ctx->xin->flow.mpls_lse[0], label);
     }
-
-    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
-    set_mpls_lse_label(&ctx->xin->flow.mpls_lse, label);
-    return false;
 }
 
-static bool
+static void
 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;
+    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
+        ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_TC_MASK);
+        set_mpls_lse_tc(&ctx->xin->flow.mpls_lse[0], tc);
     }
-
-    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
-    set_mpls_lse_tc(&ctx->xin->flow.mpls_lse, tc);
-    return false;
 }
 
-static bool
+static void
 compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl)
 {
-    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 TTL push 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;
+    if (eth_type_mpls(ctx->xin->flow.dl_type)) {
+        ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_TTL_MASK);
+        set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse[0], ttl);
     }
-
-    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TTL_MASK);
-    set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse, ttl);
-    return false;
 }
 
 static bool
 compose_dec_mpls_ttl_action(struct xlate_ctx *ctx)
 {
     struct flow *flow = &ctx->xin->flow;
-    uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse);
+    uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse[0]);
     struct flow_wildcards *wc = &ctx->xout->wc;
 
     memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+    if (eth_type_mpls(flow->dl_type)) {
+        if (ttl > 1) {
+            ttl--;
+            set_mpls_lse_ttl(&flow->mpls_lse[0], ttl);
+            return false;
+        } else {
+            execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
 
-    if (!eth_type_mpls(flow->dl_type)) {
-        return false;
-    }
-
-    if (ttl > 1) {
-        ttl--;
-        set_mpls_lse_ttl(&flow->mpls_lse, ttl);
-        return false;
+            /* Stop processing for current table. */
+            return true;
+        }
     } else {
-        execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
-
-        /* Stop processing for current table. */
         return true;
     }
 }
@@ -2503,8 +2390,7 @@ xlate_sample_action(struct xlate_ctx *ctx,
 
   ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                         &ctx->xout->odp_actions,
-                                        &ctx->xout->wc,
-                                        &ctx->mpls_depth_delta);
+                                        &ctx->xout->wc);
 
   compose_flow_sample_cookie(os->probability, os->collector_set_id,
                              os->obs_domain_id, os->obs_point_id, &cookie);
@@ -2738,38 +2624,24 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_PUSH_MPLS:
-            if (compose_mpls_push_action(ctx,
-                                         ofpact_get_PUSH_MPLS(a)->ethertype)) {
-                return;
-            }
+            compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a));
             break;
 
         case OFPACT_POP_MPLS:
-            if (compose_mpls_pop_action(ctx,
-                                        ofpact_get_POP_MPLS(a)->ethertype)) {
-                return;
-            }
+            compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
             break;
 
         case OFPACT_SET_MPLS_LABEL:
-            if (compose_set_mpls_label_action(ctx,
-                                              ofpact_get_SET_MPLS_LABEL(a)->label)) {
-                return;
-            }
-            break;
+            compose_set_mpls_label_action(
+                ctx, ofpact_get_SET_MPLS_LABEL(a)->label);
+        break;
 
         case OFPACT_SET_MPLS_TC:
-            if (compose_set_mpls_tc_action(ctx,
-                                           ofpact_get_SET_MPLS_TC(a)->tc)) {
-                return;
-            }
+            compose_set_mpls_tc_action(ctx, ofpact_get_SET_MPLS_TC(a)->tc);
             break;
 
         case OFPACT_SET_MPLS_TTL:
-            if (compose_set_mpls_ttl_action(ctx,
-                                            ofpact_get_SET_MPLS_TTL(a)->ttl)) {
-                return;
-            }
+            compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl);
             break;
 
         case OFPACT_DEC_MPLS_TTL:
@@ -3094,7 +2966,6 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     ctx.orig_skb_priority = flow->skb_priority;
     ctx.table_id = 0;
     ctx.exit = false;
-    ctx.mpls_depth_delta = 0;
 
     if (!xin->ofpacts && !ctx.rule) {
         rule_dpif_lookup(ctx.xbridge->ofproto, flow, wc, &rule);
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index bd99834..b215ef4 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -1192,9 +1192,9 @@ dpctl_normalize_actions(int argc, char *argv[])
 
         if (eth_type_mpls(af->flow.dl_type)) {
             printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ",
-                   mpls_lse_to_label(af->flow.mpls_lse),
-                   mpls_lse_to_tc(af->flow.mpls_lse),
-                   mpls_lse_to_ttl(af->flow.mpls_lse));
+                   mpls_lse_to_label(af->flow.mpls_lse[0]),
+                   mpls_lse_to_tc(af->flow.mpls_lse[0]),
+                   mpls_lse_to_ttl(af->flow.mpls_lse[0]));
         } else {
             printf("no mpls: ");
         }
-- 
1.7.10.4




More information about the dev mailing list