Enhance userspace support for MPLS, for up to 3 labels.
authorBen Pfaff <blp@nicira.com>
Tue, 4 Feb 2014 18:32:35 +0000 (10:32 -0800)
committerBen Pfaff <blp@nicira.com>
Tue, 4 Feb 2014 18:41:30 +0000 (10:41 -0800)
This commit makes the userspace support for MPLS more complete.  Now
up to 3 labels are supported.

Signed-off-by: Ben Pfaff <blp@nicira.com>
Co-authored-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Simon Horman <horms@verge.net.au>
18 files changed:
lib/dpif-netdev.c
lib/flow.c
lib/flow.h
lib/match.c
lib/match.h
lib/meta-flow.c
lib/nx-match.c
lib/odp-util.c
lib/odp-util.h
lib/ofp-util.c
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif-xlate.h
ofproto/ofproto-dpif.c
ofproto/ofproto-dpif.h
tests/odp.at
tests/ofproto-dpif.at
utilities/ovs-dpctl.c

index 0268793..73eb99d 100644 (file)
@@ -1365,7 +1365,8 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
         ofpbuf_use_stack(&buf, &state->maskbuf, sizeof state->maskbuf);
         minimask_expand(&netdev_flow->cr.match.mask, &wc);
         odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
-                               odp_to_u32(wc.masks.in_port.odp_port));
+                               odp_to_u32(wc.masks.in_port.odp_port),
+                               SIZE_MAX);
 
         *mask = buf.data;
         *mask_len = buf.size;
index b1b9f98..06ba036 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -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 < FLOW_MAX_MPLS_LABELS) {
+            flow->mpls_lse[idx++] = mh->mpls_lse;
         }
         if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
             break;
@@ -535,7 +534,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
 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;
@@ -1047,37 +1046,212 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
     flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
 }
 
+/* Returns the number of MPLS LSEs present in 'flow'
+ *
+ * Returns 0 if the 'dl_type' of 'flow' is not an MPLS ethernet type.
+ * Otherwise traverses 'flow''s MPLS label stack stopping at the
+ * first entry that has the BoS bit set. If no such entry exists then
+ * the maximum number of LSEs that can be stored in 'flow' is returned.
+ */
+int
+flow_count_mpls_labels(const struct flow *flow, struct flow_wildcards *wc)
+{
+    if (wc) {
+        wc->masks.dl_type = OVS_BE16_MAX;
+    }
+    if (eth_type_mpls(flow->dl_type)) {
+        int i;
+        int len = FLOW_MAX_MPLS_LABELS;
+
+        for (i = 0; i < len; i++) {
+            if (wc) {
+                wc->masks.mpls_lse[i] |= htonl(MPLS_BOS_MASK);
+            }
+            if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+                return i + 1;
+            }
+        }
+
+        return len;
+    } else {
+        return 0;
+    }
+}
+
+/* Returns the number consecutive of MPLS LSEs, starting at the
+ * innermost LSE, that are common in 'a' and 'b'.
+ *
+ * 'an' must be flow_count_mpls_labels(a).
+ * 'bn' must be flow_count_mpls_labels(b).
+ */
+int
+flow_count_common_mpls_labels(const struct flow *a, int an,
+                              const struct flow *b, int bn,
+                              struct flow_wildcards *wc)
+{
+    int min_n = MIN(an, bn);
+    if (min_n == 0) {
+        return 0;
+    } else {
+        int common_n = 0;
+        int a_last = an - 1;
+        int b_last = bn - 1;
+        int i;
+
+        for (i = 0; i < min_n; i++) {
+            if (wc) {
+                wc->masks.mpls_lse[a_last - i] = OVS_BE32_MAX;
+                wc->masks.mpls_lse[b_last - i] = OVS_BE32_MAX;
+            }
+            if (a->mpls_lse[a_last - i] != b->mpls_lse[b_last - i]) {
+                break;
+            } else {
+                common_n++;
+            }
+        }
+
+        return common_n;
+    }
+}
+
+/* Adds a new outermost MPLS label to 'flow' and changes 'flow''s Ethernet type
+ * to 'mpls_eth_type', which must be an MPLS Ethertype.
+ *
+ * If the new label is the first MPLS label in 'flow', it is generated as;
+ *
+ *     - label: 2, if 'flow' is IPv6, otherwise 0.
+ *
+ *     - TTL: IPv4 or IPv6 TTL, if present and nonzero, otherwise 64.
+ *
+ *     - TC: IPv4 or IPv6 TOS, if present, otherwise 0.
+ *
+ *     - BoS: 1.
+ *
+ * If the new label is the second or label MPLS label in 'flow', it is
+ * generated as;
+ *
+ *     - label: 0.
+ *
+ *     - TTL: Copied from outer label.
+ *
+ *     - TC: Copied from outer label.
+ *
+ *     - BoS: 0.
+ *
+ * 'n' must be flow_count_mpls_labels(flow).  'n' must be less than
+ * FLOW_MAX_MPLS_LABELS (because otherwise flow->mpls_lse[] would overflow).
+ */
+void
+flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
+               struct flow_wildcards *wc)
+{
+    ovs_assert(eth_type_mpls(mpls_eth_type));
+    ovs_assert(n < FLOW_MAX_MPLS_LABELS);
+
+    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 {
+        int label = 0;          /* IPv4 Explicit Null. */
+        int tc = 0;
+        int ttl = 64;
+
+        if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            label = 2;
+        }
+
+        if (is_ip_any(flow)) {
+            tc = (flow->nw_tos & IP_DSCP_MASK) >> 2;
+            wc->masks.nw_tos |= IP_DSCP_MASK;
+
+            if (flow->nw_ttl) {
+                ttl = flow->nw_ttl;
+            }
+            wc->masks.nw_ttl = 0xff;
+        }
+
+        flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
+
+        /* Clear all L3 and L4 fields. */
+        BUILD_ASSERT(FLOW_WC_SEQ == 24);
+        memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
+               sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
+    }
+    flow->dl_type = mpls_eth_type;
+}
+
+/* Tries to remove the outermost MPLS label from 'flow'.  Returns true if
+ * successful, false otherwise.  On success, sets 'flow''s Ethernet type to
+ * 'eth_type'.
+ *
+ * 'n' must be flow_count_mpls_labels(flow). */
+bool
+flow_pop_mpls(struct flow *flow, int n, ovs_be16 eth_type,
+              struct flow_wildcards *wc)
+{
+    int i;
+
+    if (n == 0) {
+        /* Nothing to pop. */
+        return false;
+    } else if (n == FLOW_MAX_MPLS_LABELS
+               && !(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);
 }
 
+/* Sets the entire MPLS LSE. */
+void
+flow_set_mpls_lse(struct flow *flow, int idx, ovs_be32 lse)
+{
+    flow->mpls_lse[idx] = lse;
+}
 
 static void
 flow_compose_l4(struct ofpbuf *b, const struct flow *flow)
@@ -1235,8 +1409,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 < FLOW_MAX_MPLS_LABELS; 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]);
+        }
     }
 }
 \f
index 9e8549d..3109a84 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -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);
@@ -76,6 +76,9 @@ union flow_in_port {
     odp_port_t odp_port;
 };
 
+/* Maximum number of supported MPLS labels. */
+#define FLOW_MAX_MPLS_LABELS 3
+
 /*
  * A flow in the network.
  *
@@ -106,9 +109,9 @@ struct flow {
     uint8_t dl_dst[6];          /* Ethernet destination address. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
+    ovs_be32 mpls_lse[FLOW_MAX_MPLS_LABELS]; /* MPLS label stack entry. */
 
     /* L3 */
-    ovs_be32 mpls_lse;          /* 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 +126,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 +138,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.
@@ -145,7 +149,7 @@ BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
  * within the struct flow. */
 enum {
     FLOW_SEGMENT_1_ENDS_AT = offsetof(struct flow, dl_src),
-    FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, mpls_lse),
+    FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, ipv6_src),
     FLOW_SEGMENT_3_ENDS_AT = offsetof(struct flow, tp_src),
 };
 BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT % 4 == 0);
@@ -194,10 +198,19 @@ 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 *, struct flow_wildcards *);
+int flow_count_common_mpls_labels(const struct flow *a, int an,
+                                  const struct flow *b, int bn,
+                                  struct flow_wildcards *wc);
+void flow_push_mpls(struct flow *, int n, ovs_be16 mpls_eth_type,
+                    struct flow_wildcards *);
+bool flow_pop_mpls(struct flow *, int n, ovs_be16 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_set_mpls_lse(struct flow *, int idx, ovs_be32 lse);
 
 void flow_compose(struct ofpbuf *, const struct flow *);
 
index cc18a6a..b2a25fd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -95,7 +95,14 @@ match_wc_init(struct match *match, const struct flow *flow)
         memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
         memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
     } else if (eth_type_mpls(flow->dl_type)) {
-        memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse);
+        int i;
+
+        for (i = 0; i < FLOW_MAX_MPLS_LABELS; i++) {
+            wc->masks.mpls_lse[i] = OVS_BE32_MAX;
+            if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+                break;
+            }
+        }
     }
 
     if (flow->dl_type == htons(ETH_TYPE_ARP) ||
@@ -458,55 +465,71 @@ match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
     match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
 }
 
+/* Modifies 'match' so that the MPLS label 'idx' matches 'lse' exactly. */
+void
+match_set_mpls_lse(struct match *match, int idx, ovs_be32 lse)
+{
+    match->wc.masks.mpls_lse[idx] = OVS_BE32_MAX;
+    match->flow.mpls_lse[idx] = lse;
+}
+
 /* 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[idx] |= htonl(MPLS_BOS_MASK);
+    flow_set_mpls_bos(&match->flow, idx, mpls_bos);
+}
+
+/* Modifies 'match' so that the MPLS LSE is wildcarded. */
+void
+match_set_any_mpls_lse(struct match *match, int idx)
 {
-    match->wc.masks.mpls_lse |= htonl(MPLS_BOS_MASK);
-    flow_set_mpls_bos(&match->flow, mpls_bos);
+    match->wc.masks.mpls_lse[idx] = htonl(0);
+    flow_set_mpls_lse(&match->flow, idx, htonl(0));
 }
 
 void
@@ -795,6 +818,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 +895,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 +1047,25 @@ 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)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) {
         ds_put_format(s, "mpls_label=%"PRIu32",",
-                 mpls_lse_to_label(f->mpls_lse));
+                 mpls_lse_to_label(f->mpls_lse[0]));
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
         ds_put_format(s, "mpls_tc=%"PRIu8",",
-                 mpls_lse_to_tc(f->mpls_lse));
+                 mpls_lse_to_tc(f->mpls_lse[0]));
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_TTL_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_TTL_MASK)) {
         ds_put_format(s, "mpls_ttl=%"PRIu8",",
-                 mpls_lse_to_ttl(f->mpls_lse));
+                 mpls_lse_to_ttl(f->mpls_lse[0]));
     }
-    if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) {
+    if (wc->masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) {
         ds_put_format(s, "mpls_bos=%"PRIu8",",
-                 mpls_lse_to_bos(f->mpls_lse));
+                 mpls_lse_to_bos(f->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,",
index ee01acd..7a8ae68 100644 (file)
@@ -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);
index 96e0efe..a168222 100644 (file)
@@ -926,11 +926,11 @@ 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_IPV4_SRC:
         return !wc->masks.nw_src;
@@ -1302,15 +1302,16 @@ 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;
         break;
 
     case MFF_IPV4_SRC:
@@ -1498,15 +1499,16 @@ 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;
         break;
 
     case MFF_IPV4_SRC:
@@ -1711,15 +1713,16 @@ 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;
         break;
 
     case MFF_IPV4_SRC:
@@ -1921,15 +1924,16 @@ 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;
         break;
 
     case MFF_IPV4_SRC:
index 983fd7d..437e85b 100644 (file)
@@ -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,17 @@ 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));
+        if (match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
+            nxm_put_8(b, OXM_OF_MPLS_TC, mpls_lse_to_tc(flow->mpls_lse[0]));
         }
 
-        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 (match->wc.masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) {
+            nxm_put_8(b, OXM_OF_MPLS_BOS, mpls_lse_to_bos(flow->mpls_lse[0]));
         }
 
-        if (match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+        if (match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) {
             nxm_put_32(b, OXM_OF_MPLS_LABEL,
-                       htonl(mpls_lse_to_label(flow->mpls_lse)));
+                       htonl(mpls_lse_to_label(flow->mpls_lse[0])));
         }
     }
 
index 873e05a..b81d0d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -353,20 +353,34 @@ 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, ntohl(mpls_key[i].mpls_lse));
+            if (mpls_mask) {
+                ds_put_format(ds, "/%#"PRIx32, ntohl(mpls_mask[i].mpls_lse));
+            }
+            ds_put_char(ds, ',');
+        }
+        ds_chomp(ds, ',');
     }
 }
 
@@ -722,7 +736,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);
@@ -1116,10 +1130,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;
     }
 
@@ -2392,7 +2419,8 @@ ovs_to_odp_frag_mask(uint8_t nw_frag_mask)
 
 static void
 odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
-                         const struct flow *flow, odp_port_t odp_in_port)
+                         const struct flow *flow, odp_port_t odp_in_port,
+                         size_t max_mpls_depth)
 {
     bool is_mask;
     struct ovs_key_ethernet *eth_key;
@@ -2494,10 +2522,15 @@ 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, NULL);
+        n = MIN(n, max_mpls_depth);
         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)) {
@@ -2579,7 +2612,7 @@ void
 odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
                        odp_port_t odp_in_port)
 {
-    odp_flow_key_from_flow__(buf, flow, flow, odp_in_port);
+    odp_flow_key_from_flow__(buf, flow, flow, odp_in_port, SIZE_MAX);
 }
 
 /* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
@@ -2592,9 +2625,11 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
  * capable of being expanded to allow for that much space. */
 void
 odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
-                       const struct flow *flow, uint32_t odp_in_port_mask)
+                       const struct flow *flow, uint32_t odp_in_port_mask,
+                       size_t max_mpls_depth)
 {
-    odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask));
+    odp_flow_key_from_flow__(buf, mask, flow, u32_to_odp(odp_in_port_mask),
+                             max_mpls_depth);
 }
 
 /* Generate ODP flow key from the given packet metadata */
@@ -2850,21 +2885,50 @@ 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)) {
+            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 < FLOW_MAX_MPLS_LABELS; i++) {
+            flow->mpls_lse[i] = mpls_lse[i];
+        }
+        if (n > FLOW_MAX_MPLS_LABELS) {
+            return ODP_FIT_TOO_MUCH;
+        }
+
+        if (!is_mask) {
+            /* BOS may be set only in the innermost label. */
+            for (i = 0; i < n - 1; i++) {
+                if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+                    return ODP_FIT_ERROR;
+                }
+            }
+
+            /* BOS must be set in the innermost label. */
+            if (n < FLOW_MAX_MPLS_LABELS
+                && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+                return ODP_FIT_TOO_LITTLE;
+            }
+        }
+
         goto done;
     } else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
         if (!is_mask) {
@@ -3398,19 +3462,26 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
 }
 
 static void
-commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+pop_vlan(struct flow *base,
+         struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (base->vlan_tci == vlan_tci) {
-        return;
-    }
-
     memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
 
     if (base->vlan_tci & htons(VLAN_CFI)) {
         nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+        base->vlan_tci = 0;
+    }
+}
+
+static void
+commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+    if (base->vlan_tci == vlan_tci) {
+        return;
     }
 
+    pop_vlan(base, odp_actions, wc);
     if (vlan_tci & htons(VLAN_CFI)) {
         struct ovs_action_push_vlan vlan;
 
@@ -3424,43 +3495,72 @@ 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;
+    int base_n = flow_count_mpls_labels(base, wc);
+    int flow_n = flow_count_mpls_labels(flow, wc);
+    int common_n = flow_count_common_mpls_labels(flow, flow_n, base, base_n,
+                                                 wc);
+
+    while (base_n > common_n) {
+        if (base_n - 1 == common_n && flow_n > common_n) {
+            /* If there is only one more LSE in base than there are common
+             * between base and flow; and flow has at least one more LSE than
+             * is common then the topmost LSE of base may be updated using
+             * set */
+            struct ovs_key_mpls mpls_key;
+
+            mpls_key.mpls_lse = flow->mpls_lse[flow_n - base_n];
+            commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
+                              &mpls_key, sizeof mpls_key);
+            flow_set_mpls_lse(base, 0, mpls_key.mpls_lse);
+            common_n++;
+        } else {
+            /* Otherwise, if there more LSEs in base than are common between
+             * base and flow then pop the topmost one. */
+            ovs_be16 dl_type;
+            bool popped;
+
+            /* If all the LSEs are to be popped and this is not the outermost
+             * LSE then use ETH_TYPE_MPLS as the ethertype parameter of the
+             * POP_MPLS action instead of flow->dl_type.
+             *
+             * This is because the POP_MPLS action requires its ethertype
+             * argument to be an MPLS ethernet type but in this case
+             * flow->dl_type will be a non-MPLS ethernet type.
+             *
+             * When the final POP_MPLS action occurs it use flow->dl_type and
+             * the and the resulting packet will have the desired dl_type. */
+            if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) {
+                dl_type = htons(ETH_TYPE_MPLS);
+            } else {
+                dl_type = flow->dl_type;
+            }
+            nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type);
+            popped = flow_pop_mpls(base, base_n, flow->dl_type, wc);
+            ovs_assert(popped);
+            base_n--;
+        }
     }
 
-    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: {
+    /* If, after the above popping and setting, there are more LSEs in flow
+     * than base then some LSEs need to be pushed. */
+    while (base_n < flow_n) {
         struct ovs_action_push_mpls *mpls;
 
-        mpls = nl_msg_put_unspec_zero(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+        /* If there's a VLAN tag, pop it off so that our new MPLS label doesn't
+         * end up outside it. */
+        pop_vlan(base, odp_actions, wc);
+
+        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;
+        mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1];
+        flow_push_mpls(base, base_n, mpls->mpls_ethertype, wc);
+        flow_set_mpls_lse(base, 0, mpls->mpls_lse);
+        base_n++;
     }
-    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:
-        OVS_NOT_REACHED();
-    }
-
-    base->dl_type = flow->dl_type;
-    base->mpls_lse = flow->mpls_lse;
-    *mpls_depth_delta = 0;
 }
 
 static void
@@ -3681,20 +3781,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);
 
index 2b62614..7bc64c7 100644 (file)
@@ -146,7 +146,8 @@ int odp_flow_from_string(const char *s,
 void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *,
                             odp_port_t odp_in_port);
 void odp_flow_key_from_mask(struct ofpbuf *, const struct flow *mask,
-                            const struct flow *flow, uint32_t odp_in_port);
+                            const struct flow *flow, uint32_t odp_in_port,
+                            size_t max_mpls_depth);
 
 uint32_t odp_flow_key_hash(const struct nlattr *, size_t);
 
@@ -181,8 +182,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);
 \f
 /* ofproto-dpif interface.
  *
index c7cc701..7de82c6 100644 (file)
@@ -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;
@@ -5344,7 +5345,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. */
index 0da402d..489012a 100644 (file)
@@ -1138,8 +1138,11 @@ handle_upcalls(struct handler *handler, struct list *upcalls)
             atomic_read(&enable_megaflows, &megaflow);
             ofpbuf_use_stack(&mask, &miss->mask_buf, sizeof miss->mask_buf);
             if (megaflow) {
+                size_t max_mpls;
+
+                max_mpls = ofproto_dpif_get_max_mpls_depth(miss->ofproto);
                 odp_flow_key_from_mask(&mask, &miss->xout.wc.masks,
-                                       &miss->flow, UINT32_MAX);
+                                       &miss->flow, UINT32_MAX, max_mpls);
             }
 
             op = &ops[n_ops++];
index b171e49..ad44582 100644 (file)
@@ -51,6 +51,7 @@
 
 COVERAGE_DEFINE(xlate_actions);
 COVERAGE_DEFINE(xlate_actions_oversize);
+COVERAGE_DEFINE(xlate_actions_mpls_overflow);
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
 
@@ -92,6 +93,10 @@ struct xbridge {
      * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
      * False if the datapath supports only 8-byte (or shorter) userdata. */
     bool variable_length_userdata;
+
+    /* Number of MPLS label stack entries that the datapath supports
+     * in matches. */
+    size_t max_mpls_depth;
 };
 
 struct xbundle {
@@ -170,13 +175,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. */
@@ -255,7 +253,8 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   const struct dpif_ipfix *ipfix,
                   const struct netflow *netflow, enum ofp_config_flags frag,
                   bool forward_bpdu, bool has_in_band,
-                  bool variable_length_userdata)
+                  bool variable_length_userdata,
+                  size_t max_mpls_depth)
 {
     struct xbridge *xbridge = xbridge_lookup(ofproto);
 
@@ -308,6 +307,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
     xbridge->miss_rule = miss_rule;
     xbridge->no_packet_in_rule = no_packet_in_rule;
     xbridge->variable_length_userdata = variable_length_userdata;
+    xbridge->max_mpls_depth = max_mpls_depth;
 }
 
 void
@@ -1684,7 +1684,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");
@@ -1799,8 +1799,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);
 
@@ -2075,8 +2074,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, &md, ctx->xout->odp_actions.data,
                         ctx->xout->odp_actions.size, NULL);
@@ -2100,99 +2098,63 @@ 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, wc);
+    if (!n) {
+        if (mpls->position == OFPACT_MPLS_BEFORE_VLAN) {
+            vlan_tci = 0;
         } else {
-            label = htonl(0x0); /* IPV4 Explicit Null. */
+            flow->vlan_tci = 0;
+        }
+        ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
+                                              &ctx->xout->odp_actions,
+                                              &ctx->xout->wc);
+    } else if (n >= FLOW_MAX_MPLS_LABELS) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet on which an "
+                         "MPLS push action can't be performed as it would "
+                         "have more MPLS LSEs than the %d supported.",
+                         ctx->xbridge->name, FLOW_MAX_MPLS_LABELS);
         }
-        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->exit = true;
+        return;
+    } else if (n >= ctx->xbridge->max_mpls_depth) {
+        COVERAGE_INC(xlate_actions_mpls_overflow);
+        ctx->xout->slow |= SLOW_ACTION;
     }
-    flow->dl_type = eth_type;
-    ctx->mpls_depth_delta++;
 
-    return false;
+    flow_push_mpls(flow, n, 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;
+    int n = flow_count_mpls_labels(flow, wc);
 
-    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;
+    if (!flow_pop_mpls(flow, n, eth_type, wc) && n >= FLOW_MAX_MPLS_LABELS) {
+        if (ctx->xin->packet != NULL) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+            VLOG_WARN_RL(&rl, "bridge %s: dropping packet on which an "
+                         "MPLS pop action can't be performed as it has "
+                         "more MPLS LSEs than the %d supported.",
+                         ctx->xbridge->name, FLOW_MAX_MPLS_LABELS);
+        }
+        ctx->exit = true;
+        ofpbuf_clear(&ctx->xout->odp_actions);
     }
-
-    ctx->xin->flow.dl_type = eth_type;
-    ctx->mpls_depth_delta--;
-
-    return false;
 }
 
 static bool
@@ -2221,99 +2183,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;
     }
 }
@@ -2525,8 +2441,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);
@@ -2760,38 +2675,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:
@@ -3082,7 +2983,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,
index 173b479..8b01d4e 100644 (file)
@@ -129,7 +129,8 @@ void xlate_ofproto_set(struct ofproto_dpif *, const char *name,
                        const struct mbridge *, const struct dpif_sflow *,
                        const struct dpif_ipfix *, const struct netflow *,
                        enum ofp_config_flags, bool forward_bpdu,
-                       bool has_in_band, bool variable_length_userdata)
+                       bool has_in_band, bool variable_length_userdata,
+                       size_t mpls_label_stack_length)
     OVS_REQ_WRLOCK(xlate_rwlock);
 void xlate_remove_ofproto(struct ofproto_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 
index a3cd3ec..7b3e1eb 100644 (file)
@@ -256,6 +256,10 @@ struct dpif_backer {
      * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions.
      * False if the datapath supports only 8-byte (or shorter) userdata. */
     bool variable_length_userdata;
+
+    /* Maximum number of MPLS label stack entries that the datapath supports
+     * in a match */
+    size_t max_mpls_depth;
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
@@ -319,6 +323,12 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
     return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
 }
 
+size_t
+ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *ofproto)
+{
+    return ofproto->backer->max_mpls_depth;
+}
+
 static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto,
                                         ofp_port_t ofp_port);
 static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
@@ -558,7 +568,8 @@ type_run(const char *type)
                               ofproto->netflow, ofproto->up.frag_handling,
                               ofproto->up.forward_bpdu,
                               connmgr_has_in_band(ofproto->up.connmgr),
-                              ofproto->backer->variable_length_userdata);
+                              ofproto->backer->variable_length_userdata,
+                              ofproto->backer->max_mpls_depth);
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                 xlate_bundle_set(ofproto, bundle, bundle->name,
@@ -781,6 +792,7 @@ struct odp_garbage {
 };
 
 static bool check_variable_length_userdata(struct dpif_backer *backer);
+static size_t check_max_mpls_depth(struct dpif_backer *backer);
 
 static int
 open_dpif_backer(const char *type, struct dpif_backer **backerp)
@@ -881,6 +893,7 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
         return error;
     }
     backer->variable_length_userdata = check_variable_length_userdata(backer);
+    backer->max_mpls_depth = check_max_mpls_depth(backer);
 
     if (backer->recv_set_enable) {
         udpif_set_threads(backer->udpif, n_handlers, n_revalidators);
@@ -965,6 +978,53 @@ check_variable_length_userdata(struct dpif_backer *backer)
     }
 }
 
+/* Tests the MPLS label stack depth supported by 'backer''s datapath.
+ *
+ * Returns the number of elements in a struct flow's mpls_lse field
+ * if the datapath supports at least that many entries in an
+ * MPLS label stack.
+ * Otherwise returns the number of MPLS push actions supported by
+ * the datapath. */
+static size_t
+check_max_mpls_depth(struct dpif_backer *backer)
+{
+    struct flow flow;
+    int n;
+
+    for (n = 0; n < FLOW_MAX_MPLS_LABELS; n++) {
+        struct odputil_keybuf keybuf;
+        struct ofpbuf key;
+        int error;
+
+        memset(&flow, 0, sizeof flow);
+        flow.dl_type = htons(ETH_TYPE_MPLS);
+        flow_set_mpls_bos(&flow, n, 1);
+
+        ofpbuf_use_stack(&key, &keybuf, sizeof keybuf);
+        odp_flow_key_from_flow(&key, &flow, 0);
+
+        error = dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY,
+                              key.data, key.size, NULL, 0, NULL, 0, NULL);
+        if (error && error != EEXIST) {
+            if (error != EINVAL) {
+                VLOG_WARN("%s: MPLS stack length feature probe failed (%s)",
+                          dpif_name(backer->dpif), ovs_strerror(error));
+            }
+            break;
+        }
+
+        error = dpif_flow_del(backer->dpif, key.data, key.size, NULL);
+        if (error) {
+            VLOG_WARN("%s: failed to delete MPLS feature probe flow",
+                      dpif_name(backer->dpif));
+        }
+    }
+
+    VLOG_INFO("%s: MPLS label stack length probed as %d",
+              dpif_name(backer->dpif), n);
+    return n;
+}
+
 static int
 construct(struct ofproto *ofproto_)
 {
index 5da0b5d..d09e285 100644 (file)
@@ -65,6 +65,8 @@ extern struct ovs_rwlock xlate_rwlock;
  *   Ofproto-dpif-xlate is responsible for translating translating OpenFlow
  *   actions into datapath actions. */
 
+size_t ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *);
+
 void rule_dpif_lookup(struct ofproto_dpif *, const struct flow *,
                       struct flow_wildcards *, struct rule_dpif **rule);
 
index b505345..a968dd3 100644 (file)
@@ -79,9 +79,19 @@ sed 's/^/skb_priority(0),skb_mark(0),/' odp-base.txt | sed -n 's/,frag=no),/,fra
  echo
  echo '# Valid forms with IP later fragment.'
 sed 's/^/skb_priority(0),skb_mark(0),/' odp-base.txt | sed -n 's/,frag=no),.*/,frag=later)/p'
-) > odp.txt
-AT_CAPTURE_FILE([odp.txt])
-AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt`
+) > odp-in.txt
+AT_CAPTURE_FILE([odp-in.txt])
+
+dnl If the BoS bit of the last LSE is 0 then the stack is unterminated
+dnl Internally a stack of 3 LSEs will be used with the trailing LSEs
+dnl set to zero. This is reflected when the key is formated
+sed '/bos=0/{
+s/^/ODP_FIT_TOO_LITTLE: /
+s/mpls(label=100,tc=7,ttl=100,bos=0)/mpls(lse0=0x64e64,lse1=0,lse2=0)/
+s/mpls(label=1000,tc=4,ttl=200,bos=0)/mpls(lse0=0x3e88c8,lse1=0,lse2=0)/
+}' < odp-in.txt > odp-out.txt
+
+AT_CHECK_UNQUOTED([test-odp parse-keys < odp-in.txt], [0], [`cat odp-out.txt`
 ])
 AT_CLEANUP
 
index 1348790..6fe0269 100644 (file)
@@ -595,13 +595,13 @@ done
 OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:43,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=0,mpls_lse1=46912
 ])
 
 dnl Modified MPLS controller action.
@@ -2426,6 +2426,76 @@ skb_priority(0),skb_mark(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows - MPLS actions that result in a userspace action])
+OVS_VSWITCHD_START([dnl
+   add-port br0 p1 -- set Interface p1 type=dummy
+])
+ON_EXIT([kill `cat ovs-ofctl.pid`])
+
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_DATA([flows.txt], [dnl
+dl_src=60:66:66:66:66:00 actions=push_mpls:0x8847,controller
+dl_src=60:66:66:66:66:01 actions=pop_mpls:0x8849,controller
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl Packet is sent to userspace because a MPLS push or pop action is applied to
+dnl a packet with 4 MPLS LSEs but userspace and the datapath can only handle up
+dnl to 3 labels.
+dnl
+dnl The input is a frame with four MPLS labels which tcpdump -vve shows as:
+dnl 60:66:66:66:66:00 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 74: MPLS (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, [S], ttl 32)
+dnl         (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44, bad cksum 3b78 (->f978)!)
+dnl     192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4
+
+for dl_src in 00 01; do
+    AT_CHECK([ovs-appctl netdev-dummy/receive p1 "505400000007 6066666666$dl_src 8847 00014020 00014020 00014020 00014120 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45"])
+
+    AT_CHECK_UNQUOTED([ovs-appctl dpif/dump-flows br0 | grep ":$dl_src/" | STRIP_USED], [0], [dnl
+skb_priority(0),in_port(1),eth(src=60:66:66:66:66:$dl_src/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x8847),mpls(lse0=0x14020/0x100,lse1=0x14020/0x100,lse2=0x14020/0x100), packets:0, bytes:0, used:never, actions:drop
+])
+done
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/dump-flows - MPLS actions that result in a drop])
+OVS_VSWITCHD_START([dnl
+   add-port br0 p1 -- set Interface p1 type=dummy
+])
+ON_EXIT([kill `cat ovs-ofctl.pid`])
+
+AT_CAPTURE_FILE([ofctl_monitor.log])
+AT_DATA([flows.txt], [dnl
+dl_src=60:66:66:66:66:00 actions=push_mpls:0x8847,controller
+dl_src=60:66:66:66:66:01 actions=pop_mpls:0x8849,controller
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl Packet is dropped because an MPLS PUSH action is applied to a packet with
+dnl 4 MPLS LSEs but ovs-vswtichd can only handle up to 3 MPLS LSEs and thus
+dnl can't determine the resulting MPLS label after an MPLS PUSH action.
+dnl
+dnl The input is a frame with two MPLS headers which tcpdump -vve shows as:
+dnl 60:66:66:66:66:01 > 50:54:00:00:00:07, ethertype MPLS unicast (0x8847), length 74: MPLS (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, ttl 32)
+dnl         (label 20, exp 0, [S], ttl 32)
+dnl         (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44, bad cksum 3b78 (->f978)!)
+dnl     192.168.0.1.80 > 192.168.0.2.0: Flags [none], cksum 0x7744 (correct), seq 42:46, win 10000, length 4
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 66 01 88 47 00 01 40 20 00 01 40 20 00 01 40 20 00 01 41 20 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45'])
+
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
+skb_priority(0),in_port(1),eth(src=60:66:66:66:66:01/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x8847),mpls(lse0=0x14020/0x100,lse1=0x14020/0x100,lse2=0x14020/0x100), packets:0, bytes:0, used:never, actions:drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - patch ports])
 OVS_VSWITCHD_START([add-br br1 \
 -- set bridge br1 datapath-type=dummy fail-mode=secure \
index 09db084..3b1dff1 100644 (file)
@@ -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: ");
         }