netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2.51 0/5] MPLS actions and matches
@ 2013-11-21  3:46 Simon Horman
       [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
                   ` (3 more replies)
  0 siblings, 4 replies; 19+ messages in thread
From: Simon Horman @ 2013-11-21  3:46 UTC (permalink / raw)
  To: dev-yBygre7rU0TnMu66kgdUjQ, netdev-u79uwXL29TY76Z2rM5mHXA,
	Jesse Gross, Ben Pfaff
  Cc: Ravi K

Hi,

This series implements MPLS actions and matches based on work by
Ravi K, Leo Alterman, Yamahata-san and Joe Stringer.

This series provides three changes

* Patches 1 - 3

  Provide user-space support for the VLAN/MPLS tag insertion order
  up to and including OpenFlow 1.2, and the different ordering
  specified from OpenFlow 1.3. In a nutshell the datapath always
  uses the OpenFlow 1.3 ordering, which is to always insert tags
  immediately after the L2 header, regardless of the presence of other
  tags. And ovs-vswtichd provides compatibility for the behaviour up
  to OpenFlow 1.2, which is that MPLS tags should follow VLAN tags
  if present.

  Ben, these are for you to review.

* Patches 4 and 5

  Adding basic MPLS action and match support to the kernel datapath

  Jesse, these are for you to review after patches 1 - 4 are reviewed.


Difference between v2.51 and v2.50:

* New approach to consistency checking actions using of OpenFlow1.3+ tag order
* Use OF1.2 for all ovs-ofctl commands for new OF1.2 tests. Likewise for OF1.3.
* Add write_actions check for OF1.3.
  This further exercises consistency checking using OpenFlow1.3+ tag order.


Difference between v2.50 and v2.49:

* Correct typo in comment


Difference between v2.49 and v2.48:

* Include action consistency checking changes.


Difference between v2.48 and v2.47:
* Manual rebase for
  - "OF 1.1 set vlan vid/pcp compatibility"
  - "Native Set-Field action"
* Only use OpenFlow1.2 actions in OpenFlow1.2 tests


Difference between v2.47 and v2.46:

* Rebase of patch 4 for HAVE_RHEL_OVS_HOOK and OVS_KEY_ATTR_TCP_FLAGS


Difference between v2.46 and v2.45:

* Update changelog of "odp: Allow VLAN actions after MPLS actions"
  to reflect the current implementation


Difference between v2.45 and v2.44:

* As pointed out by Ben Pfaff and Joe Stringer
  + Update VLAN handling in the presence of MPLS push

    Previously the test for committing ODP VLAN actions after MPLS actions
    was that the VLAN TCI differed before and after the MPLS push action.
    This results in a false negative in some cases including if a VLAN tag
    is pushed after the MPLS push action in such a way that it duplicates
    the VLAN tag present before the MPLS push action.

    This is resolved by ensuring the VLAN_CFI bit of the value used to
    track the desired VLAN TCI after an MPLS push action is zero unless
    VLAN actions should be committed after MPLS actions.

  + Update tests to use ovs-ofctl monitor "-m" to allow full inspection of
    MPLS LSE and VLAN tags present in packets.

Differences between v2.44 and v2.43:

* Rebase for the following changes:
  f47ea02 ("Set datapath mask bits when setting a flow field.")
  7fdb60a ("Add support for write-actions")
  7358063 ("odp-util: Elaborate the comment for odp_flow_format() function.")
* Correct final_vlan_tci and next_vlan_tci initialisation in xlate_actions__()


Differences between v2.43 and v2.42:

* As suggested by Ben Pfaff
  Move vlan state into struct xlate_ctx
  1. Add final_vlan_tci member to struct xlate_ctx instead of vlan_tci member
     struct xlate_xin.  This seems to be a better palace for it as it does
     not need to be accessible from the caller.
  2. Move local vlan_tci variable of do_xlate_actions() to be the
     next_vlan_tci member of strict xlate_ctx.  This allows for it to work
     across resubmit actions and goto table instructions.
* Code contributed by Ben Pfaff
  + Use enum for to control order of MPLS LSE insertion
    This makes the logic somewhat clearer
* Add a helper push_mpls_from_openflow() to consolidate
  the same logic that appears in three locations.


Differences between v2.42 and v2.41:

* Rebase for:
  + 0585f7a ("datapath: Simplify mega-flow APIs.")
  + a097c0b ("datapath: Restructure datapath.c and flow.c")
* As suggested by Jesse Gross
  + Take into account that push_mpls() will have freed the skb on error
  + Remove dubious !eth_p_mpls(skb->protocol) condition from push_mpls
    The !eth_p_mpls(skb->protocol) condition on setting inner_protocol
    has no effect. Its motivation was to ensure that inner_protocol was
    only set the first time that mpls_push occured. However this is already
    ensured by the !ovs_skb_get_inner_protocol(skb) condition.
  + Return -EINVAL instead of -ENOMEM from pop_mpls() if the skb is too short
  + Do not add @inner_protocol to kernel doc for struct ovs_skb_cb.
    The patch no longer adds an inner_protocol member to struct ovs_skb_cb
  + Do not add and set otherwise unsued inner_protocol variable in
    rpl_dev_queue_xmit()
* As suggested by Pravin Shelar
  + Implement compatibility code in existing rpl_skb_gso_segment
    rather than introducing to use rpl___skb_gso_segment


Differences between v2.41 and v2.40:

* As suggested by Ben Pfaff
  + Expand struct ofpact_reg_load to include a mpls_before_vlan field
    rather than using the compat field of the ofpact field of
    struct ofpact_reg_load.
  + Pass version to  ofpacts_pull_openflow11_actions and
    ofpacts_pull_openflow11_instructions.  This removes the invalid
    assumption that that these functions are passed a full message and are
    thus able to deduce the OpenFlow version.


Differences between v2.40 and v2.39:

* Rebase for:
  + New dev_queue_xmit compat code
  + Updated put_vlan()
  + Removal of mpls_depth field from struct flow
* As suggested by Jesse Gross
  + Remove bogus mac_len update from push_mpls()
  + Slightly simplify push_mpls() by using eth_hdr()
  + Remove dubious condition !eth_p_mpls(inner_protocol) on
    an skb being considered to be MPLS in netdev_send()
  + Only use compatibility code for MPLS GSO segmentation on kernels
    older than 3.11
  + Revamp setting of inner_protocol
    1. Do not unconditionally set inner_protocol to the value of
       skb->protocol in ovs_execute_actions().
    2. Initialise inner_protocol it to zero only if compatibility code is in
       use. In the case where compatibility code is not in use it will either
       be zero due since the allocation of the skb or some other value set
       by some other user.
    3. Conditionally set the inner_protocol in push_mpls() to the value of
       skb->protocol when entering push_mpls(). The condition is that
       inner_protocol is zero and the value of skb->protocol is not an MPLS
       ethernet type.
    - This new scheme:
      + Pushes logic to set inner_protocol closer to the case where it is
	needed.
      + Avoids over-writing values set by other users.
* As suggested by Pravin Shelar
  + Only set and restore skb->protocol in rpl___skb_gso_segment() in the
    case of MPLS
  + Add inner_protocol field to struct ovs_gso_cb instead of ovs_skb_cb.
    This moves compatibility code closer to where it is used
    and creates fewer differences with mainline.
* Update comment on mac_len updates in datapath/actions.c
* Remove HAVE_INNER_PROCOTOL and instead just check
  against kernel version 3.11 directly.
  HAVE_INNER_PROCOTOL is a hang-over from work done prior
  to the merge of inner_protocol into the kernel.
* Remove dubious condition !eth_p_mpls(inner_protocol) on
  using inner_protocol as the type in rpl_skb_network_protocol()
* Do not update type of features in rpl_dev_queue_xmit.
  Though arguably correct this is not an inherent part of
  the changes made by this patch.
* Use skb_cow_head() in push_mpls()
  + Call skb_cow_head(skb, MPLS_HLEN) instead of
    make_writable(skb, skb->mac_len) to ensure that there is enough head
    room to push an MPLS LSE regardless of whether the skb is cloned or not.
  + This is consistent with the behaviour of rpl__vlan_put_tag().
  + This is a fix for crashes reported when performing mpls_push
    with headroom less than 4. This problem was introduced in v3.36.
* Skip popping in mpls_pop if the skb is too short to contain an MPLS LSE


Differences between v2.39 and v2.38:

* Rebase for removal of vlan, checksum and skb->mark compat code
  - This includes adding adding a new patch,
    "[PATCH v2.39 6/7] datapath: Break out deacceleration portion of
    vlan_push" to allow re-use of some existing code.


Differences between v2.38 and v2.37:

* Rebase for SCTP support
* Refactor validate_tp_port() to iterate over eth_types rather
  than open-coding the loop. With the addition of SCTP this logic
  is now used three times.


Differences between v2.37 and v2.36:

* Rebase


Differences between v2.36 and v2.35:

* Rebase

* Do not add set_ethertype() to datapath/actions.c.
  As this patch has evolved this function had devolved into
  to sets of functionality wrapped into a single function with
  only one line of common code. Refactor things to simply
  open-code setting the ether type in the two locations where
  set_ethertype() was previously used. The aim here is to improve
  readability.

* Update setting skb->ethertype after mpls push and pop.
  - In the case of push_mpls it should be set unconditionally
    as in v2.35 the behaviour of this function to always push
    an MPLS LSE before any VLAN tags.
  - In the case of mpls_pop eth_p_mpls(skb->protocol) is a better
    test than skb->protocol != htons(ETH_P_8021Q) as it will give the
    correct behaviour in the presence of other VLAN ethernet types,
    for example 0x88a8 which is used by 802.1ad. Moreover, it seems
    correct to update the ethernet type if it was previously set
    according to the top-most MPLS LSE.

* Deaccelerate VLANs when pushing MPLS tags the
  - Since v2.35 MPLS push will insert an MPLS LSE before any VLAN tags.
    This means that if an accelerated tag is present it should be
    deaccelerated to ensure it ends up in the correct position.

* Update skb->mac_len in push_mpls() so that it will be correct
  when used by a subsequent call to pop_mpls().

  As things stand I do not believe this is strictly necessary as
  ovs-vswitchd will not send a pop MPLS action after a push MPLS action.
  However, I have added this in order to code more defensively as I believe
  that if such a sequence did occur it would be rather unobvious why
  it didn't work.

* Do not add skb_cow_head() call in push_mpls().
  It is unnecessary as there is a make_writable() call.
  This change was also made in v2.30 but some how the
  code regressed between then and v2.35.


Differences between v2.35 and v2.34:

* Add support for the tag ordering specified up until OpenFlow 1.2 and
  the ordering specified from OpenFlow 1.3.

* Correct error in datapath patch's handling of GSO in the presence
  of MPLS and absence of VLANs.


To aid review this series is available in git at:

git://github.com/horms/openvswitch.git devel/mpls-v2.51


Patch list and overall diffstat:

Joe Stringer (1):
  odp: Allow VLAN actions after MPLS actions

Simon Horman (4):
  ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after
    mpls_push
  lib: Support pushing of MPLS LSE before or after VLAN tag
  datapath: Break out deacceleration portion of vlan_push
  datapath: Add basic MPLS support to kernel

 OPENFLOW-1.1+                                   |  12 -
 datapath/Modules.mk                             |   1 +
 datapath/actions.c                              | 157 ++++-
 datapath/datapath.c                             |   4 +-
 datapath/flow.c                                 |  29 +
 datapath/flow.h                                 |  17 +-
 datapath/flow_netlink.c                         | 286 +++++++-
 datapath/flow_netlink.h                         |   2 +-
 datapath/linux/compat/gso.c                     |  70 +-
 datapath/linux/compat/gso.h                     |  41 ++
 datapath/linux/compat/include/linux/netdevice.h |   6 +-
 datapath/linux/compat/netdevice.c               |  10 +-
 datapath/mpls.h                                 |  15 +
 include/linux/openvswitch.h                     |   7 +-
 lib/flow.c                                      |   2 +-
 lib/odp-util.c                                  |  12 +-
 lib/odp-util.h                                  |   3 +-
 lib/ofp-actions.c                               | 194 +++++-
 lib/packets.c                                   |  10 +-
 lib/packets.h                                   |   2 +-
 ofproto/ofproto-dpif-xlate.c                    | 161 ++++-
 tests/ofproto-dpif.at                           | 869 ++++++++++++++++++++++++
 22 files changed, 1765 insertions(+), 145 deletions(-)
 create mode 100644 datapath/mpls.h

-- 
1.8.4

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
       [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
@ 2013-11-21  3:46   ` Simon Horman
  2013-12-04 21:24     ` Ben Pfaff
  2013-11-21  3:46   ` [PATCH v2.51 2/5] odp: Allow VLAN actions after MPLS actions Simon Horman
  2013-11-21  3:46   ` [PATCH v2.51 3/5] lib: Support pushing of MPLS LSE before or after VLAN tag Simon Horman
  2 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-11-21  3:46 UTC (permalink / raw)
  To: dev-yBygre7rU0TnMu66kgdUjQ, netdev-u79uwXL29TY76Z2rM5mHXA,
	Jesse Gross, Ben Pfaff
  Cc: Ravi K

The aim of this patch is to support provide infrastructure for verification
of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
existing support for verifying these actions for pre-OpenFlow1.3.

In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
ordering. Open vSwitch also uses this ordering when supporting MPLS
actions via Nicira extensions to OpenFlow1.0.

When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
affect the VLANs of a packet. If VLAN tags are present immediately after
the ethernet header then they remain present there.

In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
immediately follow the ethernet header. This is OpenFlow1.3+ tag
ordering.

When using OpenFlow1.3+ tag ordering an MPLS push action affects the
VLANs of a packet as any VLAN tags previously present after the ethernet
header are moved to be immediately after the newly pushed MPLS LSE. Thus
for the purpose of action consistency checking a packet may be changed
from a VLAN packet to a non-VLAN packet.

In this way the effective value of the VLAN TCI of a packet may differ
after an MPLS push depending on the OpenFlow version in use.

This patch does not enable the logic described above.
Rather it is disabled in ofpacts_check__(). It should
be enabled when support for OpenFlow1.3+ tag order is added
and enabled.

Signed-off-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>

---

v2.51
* First non-RFC post
---
 lib/ofp-actions.c | 202 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 169 insertions(+), 33 deletions(-)

diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index e07ea1d..6754939 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1879,15 +1879,108 @@ inconsistent_match(enum ofputil_protocol *usable_protocols)
     *usable_protocols &= OFPUTIL_P_OF10_ANY;
 }
 
+/* Checks the consistency of a VLAN action by checking the state of the
+ * VLAN_CFI bit of the VLAN TCI against 'desired_cfi' and updating
+ * '*usable_protocols' accordingly.
+ *
+ * 'pre_of13_vlan_tci' is the VLAN TCI to check against for pre-OpenFlow1.3
+ * protocols.  'of13_vlan_tci' is the VLAN TCI to check against for
+ * OpenFlow1.3+ protocols.
+ *
+ * If the desired_cfi bit is in the desired state for any usable protocol
+ * then true is returned. Else false is returned.
+ *
+ * The check for consistent VLAN actions may depend on the OpenFlow version
+ * used and whether the VLAN action is preceded by an MPLS push action or
+ * not.
+ *
+ * In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
+ * immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
+ * ordering. Open vSwitch also uses this ordering when supporting MPLS
+ * actions via Nicira extensions to OpenFlow1.0.
+ *
+ * When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
+ * affect the VLANs of a packet. If VLAN tags are present immediately after
+ * the ethernet header then they remain present there.
+ *
+ * In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
+ * immediately follow the ethernet header. This is OpenFlow1.3+ tag
+ * ordering.
+ *
+ * When using OpenFlow1.3+ tag ordering an MPLS push action affects the
+ * VLANs of a packet as any VLAN tags previously present after the ethernet
+ * header are moved to be immediately after the newly pushed MPLS LSE. Thus
+ * for the purpose of action consistency checking a packet may be changed
+ * from a VLAN packet to a non-VLAN packet.
+ *
+ * In this way the effective value of the VLAN TCI of a packet may differ
+ * after an MPLS push depending on the OpenFlow version in use. This
+ * corresponds to the 'pre_of13_vlan_tci' and 'of13_vlan_tci' parameters of
+ * this function. */
+static bool
+ofpact_check_vlan(enum ofputil_protocol *usable_protocols,
+                  ovs_be16 pre_of13_vlan_tci, ovs_be16 of13_vlan_tci,
+                  bool desired_cfi)
+{
+    bool ok = false;
+    ovs_be16 tci, mask;
+
+    mask = htons(VLAN_CFI);
+    if (desired_cfi) {
+        tci = mask;
+    } else {
+        tci = htons(0);
+    }
+
+    if (*usable_protocols & OFPUTIL_P_OF13_UP) {
+        if ((of13_vlan_tci & mask) != tci) {
+            /* CFI miss-match for OpenFlow1.3+: clear those protocols */
+            *usable_protocols &= ~OFPUTIL_P_OF13_UP;
+        } else {
+            /* CFI bit is valid for a usable protocol:
+             * eventually return true */
+            ok = true;
+        }
+    }
+
+    if (*usable_protocols & ~OFPUTIL_P_OF13_UP) {
+        if ((pre_of13_vlan_tci & mask) != tci) {
+            enum ofputil_protocol of13_up;
+
+            /* CFI miss-match for pre-OpenFlow1.3: clear those protocols
+             * except OFPUTIL_P_OF10_ANY as it allows inconsistency. */
+            of13_up = *usable_protocols & OFPUTIL_P_OF13_UP;
+            inconsistent_match(usable_protocols);
+            *usable_protocols |= of13_up;
+        } else {
+            /* CFI bit is valid for a usable protocol:
+             * eventually return true */
+            ok = true;
+        }
+    }
+
+    return ok;
+}
+
+static enum ofperr ofpacts_check__(struct ofpact[], size_t ofpacts_len,
+                                   struct flow *, ofp_port_t max_ports,
+                                   uint8_t table_id, uint8_t n_tables,
+                                   enum ofputil_protocol *usable_protocols,
+                                   ovs_be16 *of13_vlan_tci);
+
 /* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci,
  * caller must restore them.
  *
+ * May also modify of13_vlan_tci which the caller should discard. On the
+ * first call to ofpact_check__() of13_vlan_tci should be set to
+ * flow->vlan_tci
+ *
  * Modifies some actions, filling in fields that could not be properly set
  * without context. */
 static enum ofperr
 ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
                struct flow *flow, ofp_port_t max_ports,
-               uint8_t table_id, uint8_t n_tables)
+               uint8_t table_id, uint8_t n_tables, ovs_be16 *of13_vlan_tci)
 {
     const struct ofpact_enqueue *enqueue;
     const struct mf_field *mf;
@@ -1920,12 +2013,13 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
          * OpenFlow 1.1+ if need be. */
         ofpact_get_SET_VLAN_VID(a)->flow_has_vlan =
             (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
-        if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
-            !ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
-            inconsistent_match(usable_protocols);
+        if (!ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+            ofpact_check_vlan(usable_protocols, flow->vlan_tci,
+                              *of13_vlan_tci, true);
         }
         /* Temporary mark that we have a vlan tag. */
         flow->vlan_tci |= htons(VLAN_CFI);
+        *of13_vlan_tci |= htons(VLAN_CFI);
         return 0;
 
     case OFPACT_SET_VLAN_PCP:
@@ -1933,29 +2027,31 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
          * OpenFlow 1.1+ if need be. */
         ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan =
             (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
-        if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
-            !ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
-            inconsistent_match(usable_protocols);
+        if (!ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+            ofpact_check_vlan(usable_protocols, flow->vlan_tci,
+                              *of13_vlan_tci, true);
         }
         /* Temporary mark that we have a vlan tag. */
         flow->vlan_tci |= htons(VLAN_CFI);
+        *of13_vlan_tci |= htons(VLAN_CFI);
         return 0;
 
     case OFPACT_STRIP_VLAN:
-        if (!(flow->vlan_tci & htons(VLAN_CFI))) {
-            inconsistent_match(usable_protocols);
-        }
+        ofpact_check_vlan(usable_protocols, flow->vlan_tci, *of13_vlan_tci,
+                          true);
         /* Temporary mark that we have no vlan tag. */
-        flow->vlan_tci = htons(0);
+        *of13_vlan_tci = flow->vlan_tci = htons(0);
         return 0;
 
     case OFPACT_PUSH_VLAN:
-        if (flow->vlan_tci & htons(VLAN_CFI)) {
+        if (!ofpact_check_vlan(usable_protocols, flow->vlan_tci,
+                               *of13_vlan_tci, false)) {
             /* Multiple VLAN headers not supported. */
             return OFPERR_OFPBAC_BAD_TAG;
         }
         /* Temporary mark that we have a vlan tag. */
         flow->vlan_tci |= htons(VLAN_CFI);
+        *of13_vlan_tci |= htons(VLAN_CFI);
         return 0;
 
     case OFPACT_SET_ETH_SRC:
@@ -2012,9 +2108,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
         mf = ofpact_get_SET_FIELD(a)->field;
         /* Require OXM_OF_VLAN_VID to have an existing VLAN header. */
         if (!mf_are_prereqs_ok(mf, flow) ||
-            (mf->id == MFF_VLAN_VID && !(flow->vlan_tci & htons(VLAN_CFI)))) {
-            VLOG_WARN_RL(&rl, "set_field %s lacks correct prerequisities",
-                         mf->name);
+            (mf->id == MFF_VLAN_VID &&
+             !ofpact_check_vlan(usable_protocols, flow->vlan_tci,
+                                *of13_vlan_tci, true))) {
             return OFPERR_OFPBAC_MATCH_INCONSISTENT;
         }
         /* Remember if we saw a vlan tag in the flow to aid translating to
@@ -2025,6 +2121,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
             /* The set field may add or remove the vlan tag,
              * Mark the status temporarily. */
             flow->vlan_tci = ofpact_get_SET_FIELD(a)->value.be16;
+            *of13_vlan_tci = ofpact_get_SET_FIELD(a)->value.be16;
         }
         return 0;
 
@@ -2071,6 +2168,13 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
          * Thus nothing can be assumed about the network protocol.
          * Temporarily mark that we have no nw_proto. */
         flow->nw_proto = 0;
+
+        /* In the case of OpenFlow1.3+ the MPLS LSE is pushed before
+         * any VLAN tags which were previously present immediately
+         * after the VLAN header.
+         * Temporarily mark that there is no VLAN tag in the case
+         * of OpenFlow1.3+ */
+        *of13_vlan_tci = htons(0);
         return 0;
 
     case OFPACT_POP_MPLS:
@@ -2091,8 +2195,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
          * consistency of an action set. */
         struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
         enum ofputil_protocol p = *usable_protocols;
-        return ofpacts_check(on->actions, ofpact_nest_get_action_len(on),
-                             flow, max_ports, table_id, n_tables, &p);
+        return ofpacts_check__(on->actions, ofpact_nest_get_action_len(on),
+                               flow, max_ports, table_id, n_tables, &p,
+                               of13_vlan_tci);
     }
 
     case OFPACT_WRITE_METADATA:
@@ -2123,23 +2228,21 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     }
 }
 
-/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are
- * appropriate for a packet with the prerequisites satisfied by 'flow' in a
- * switch with no more than 'max_ports' ports.
- *
- * If 'ofpacts' and 'flow' are inconsistent with one another, un-sets in
- * '*usable_protocols' the protocols that forbid the inconsistency.  (An
- * example of an inconsistency between match and actions is a flow that does
- * not match on an MPLS Ethertype but has an action that pops an MPLS label.)
+/* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci,
+ * caller must restore them.
  *
- * May annotate ofpacts with information gathered from the 'flow'.
+ * May also modify of13_vlan_tci which the caller should discard. On the
+ * first call to ofpacts_check__() of13_vlan_tci should be set to
+ * flow->vlan_tci
  *
- * May temporarily modify 'flow', but restores the changes before returning. */
-enum ofperr
-ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
-              struct flow *flow, ofp_port_t max_ports,
-              uint8_t table_id, uint8_t n_tables,
-              enum ofputil_protocol *usable_protocols)
+ * Modifies some actions, filling in fields that could not be properly set
+ * without context. */
+static enum ofperr
+ofpacts_check__(struct ofpact ofpacts[], size_t ofpacts_len,
+                struct flow *flow, ofp_port_t max_ports,
+                uint8_t table_id, uint8_t n_tables,
+                enum ofputil_protocol *usable_protocols,
+                ovs_be16 *of13_vlan_tci)
 {
     struct ofpact *a;
     ovs_be16 dl_type = flow->dl_type;
@@ -2149,10 +2252,19 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         error = ofpact_check__(usable_protocols, a, flow,
-                               max_ports, table_id, n_tables);
+                               max_ports, table_id, n_tables,
+                               of13_vlan_tci);
         if (error) {
             break;
         }
+        /* XXX
+         * OF1.3 tag order is not currently implemented.
+         * The following line effectively disables the
+         * use of 'of13_vlan_tci' to check OF1.3+ tag order
+         * consistency. This means that pre-OF1.3+ is always used.
+         * This line should be removed by a subequent
+         * patch which enables the use of OF1.3+ tag order */
+        *of13_vlan_tci = flow->vlan_tci;
     }
     /* Restore fields that may have been modified. */
     flow->dl_type = dl_type;
@@ -2161,6 +2273,30 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
     return error;
 }
 
+/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are
+ * appropriate for a packet with the prerequisites satisfied by 'flow' in a
+ * switch with no more than 'max_ports' ports.
+ *
+ * If 'ofpacts' and 'flow' are inconsistent with one another, un-sets in
+ * '*usable_protocols' the protocols that forbid the inconsistency.  (An
+ * example of an inconsistency between match and actions is a flow that does
+ * not match on an MPLS Ethertype but has an action that pops an MPLS label.)
+ *
+ * May annotate ofpacts with information gathered from the 'flow'.
+ *
+ * May temporarily modify 'flow', but restores the changes before returning. */
+enum ofperr
+ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
+              struct flow *flow, ofp_port_t max_ports,
+              uint8_t table_id, uint8_t n_tables,
+              enum ofputil_protocol *usable_protocols)
+{
+    ovs_be16 of13_vlan_tci = flow->vlan_tci;
+
+    return ofpacts_check__(ofpacts, ofpacts_len, flow, max_ports, table_id,
+                           n_tables, usable_protocols, &of13_vlan_tci);
+}
+
 /* Like ofpacts_check(), but reports inconsistencies as
  * OFPERR_OFPBAC_MATCH_INCONSISTENT rather than clearing bits. */
 enum ofperr
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2.51 2/5] odp: Allow VLAN actions after MPLS actions
       [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
  2013-11-21  3:46   ` [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push Simon Horman
@ 2013-11-21  3:46   ` Simon Horman
  2013-11-21  3:46   ` [PATCH v2.51 3/5] lib: Support pushing of MPLS LSE before or after VLAN tag Simon Horman
  2 siblings, 0 replies; 19+ messages in thread
From: Simon Horman @ 2013-11-21  3:46 UTC (permalink / raw)
  To: dev-yBygre7rU0TnMu66kgdUjQ, netdev-u79uwXL29TY76Z2rM5mHXA,
	Jesse Gross, Ben Pfaff
  Cc: Ravi K

From: Joe Stringer <joe-Q1GJJQv1iO6lP80pJB477g@public.gmane.org>

OpenFlow 1.1 and 1.2, and 1.3 differ in their handling of MPLS actions in the
presence of VLAN tags. To allow correct behaviour to be committed in
each situation, this patch adds a second round of VLAN tag action
handling to commit_odp_actions(), which occurs after MPLS actions. This
is implemented with two new fields in struct xlate_ctx,
'post_mpls_vlan_tci' and 'next_vlan_tci'.

Any modifications to the VLAN TCI by actions are made to 'next_vlan_tci'.
Initially 'next_vlan_tci' points to 'xin->flow->vlan_tci' so that VLAN
actions are committed before any MPLS actions.  When an MPLS action is
composed 'next_vlan_tci' is updated to point to 'post_mpls_vlan_tci'. This
causes subsequent VLAN actions to be committed after MPLS actions.

Both before and after this patch MPLS LSEs are pushed onto a packet after
any VLAN tags that may be present. This is the behaviour described in
OpenFlow 1.1 and 1.2. OpenFlow 1.3 specifies that MPLS LSEs should be
pushed onto a packet before any VLAN tags that are present. Support
for this will be added by a subsequent patch that makes use of
the infrastructure added by this patch.

Signed-off-by: Joe Stringer <joe-Q1GJJQv1iO6lP80pJB477g@public.gmane.org>
Signed-off-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>

---

v2.51 [Simon Horman]
* Use OF1.2 for all ovs-ofctl commands. This is a cosmetic change.

v2.49 - v2.50
* No change

v2.48 [Simon Horman]
* Manual rebase for
  - "OF 1.1 set vlan vid/pcp compatibility"
  - "Native Set-Field action"
* Only use OpenFlow1.2 actions in OpenFlow1.2 tests

v2.47
* No change

v2.46 [Simon Horman]
* Updated changelog as it had become out of date

v2.45 [Simon Horman]
* As pointed out by Ben Pfaff and Joe Stringer
  + Update VLAN handling in the presence of MPLS push

    Previously the test for committing ODP VLAN actions after MPLS actions
    was that the VLAN TCI differed before and after the MPLS push action.
    This results in a false negative in some cases including if a VLAN tag
    is pushed after the MPLS push action in such a way that it duplicates
    the VLAN tag present before the MPLS push action.

    This is resolved by ensuring the VLAN_CFI bit of the value used to
    track the desired VLAN TCI after an MPLS push action is zero unless
    VLAN actions should be committed after MPLS actions.

  + Update tests to use ovs-ofctl monitor "-m" to allow full inspection of
    MPLS LSE and VLAN tags present in packets.

v2.44 [Simon Horman]
* Rebase for the following changes:
  f47ea02 ("Set datapath mask bits when setting a flow field.")
  7fdb60a ("Add support for write-actions")
  7358063 ("odp-util: Elaborate the comment for odp_flow_format() function.")
* Correct final_vlan_tci and next_vlan_tci initialisation in xlate_actions__()

v2.43 [Simon Horman]
* As suggested by Ben Pfaff
  Move vlan state into struct xlate_ctx

  1. Add final_vlan_tci member to struct xlate_ctx instead of vlan_tci member
     struct xlate_xin.  This seems to be a better palace for it as it does
     not need to be accessible from the caller.

  2. Move local vlan_tci variable of do_xlate_actions() to be the
     next_vlan_tci member of strict xlate_ctx.  This allows for it to work
     across resubmit actions and goto table instructions.

v2.42
* No change

v2.41 [Joe Stringer via Simon Horman]
* Rework comments to make things a little clearer

v2.40 [Simon Horman]
* Rebase for removal of mpls_depth from struct flow

v2.38 - v2.39
* No change

v2.37
* Rebase

v2.36
* No change

v2.35 [Joe Stringer]
* First post
---
 lib/odp-util.c               |  12 +-
 lib/odp-util.h               |   3 +-
 ofproto/ofproto-dpif-xlate.c | 152 +++++++++++++----
 tests/ofproto-dpif.at        | 389 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 525 insertions(+), 31 deletions(-)

diff --git a/lib/odp-util.c b/lib/odp-util.c
index 62cc638..c5c5e08 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -3606,11 +3606,15 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base,
  * used as part of the action.
  *
  * Returns a reason to force processing the flow's packets into the userspace
- * slow path, if there is one, otherwise 0. */
+ * slow path, if there is one, otherwise 0.
+ *
+ * VLAN actions may be committed twice; If vlan_tci in 'flow' differs from the
+ * one in 'base', then it is committed before MPLS actions. If the VLAN_CFI
+ * bit of 'post_mpls_vlan_tci' is set then it is committed afterwards. */
 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)
+                   int *mpls_depth_delta, ovs_be16 post_mpls_vlan_tci)
 {
     enum slow_path_reason slow;
 
@@ -3623,6 +3627,10 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
      * 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);
+    if (post_mpls_vlan_tci & htons(VLAN_CFI)) {
+        base->vlan_tci = htons(0);
+        commit_vlan_action(post_mpls_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..636a3ec 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -175,7 +175,8 @@ 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);
+                                         int *mpls_depth_delta,
+                                         ovs_be16 post_mpls_vlan_tci);
 \f
 /* ofproto-dpif interface.
  *
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index a331c0b..daacee6 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+            /* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -182,6 +182,37 @@ struct xlate_ctx {
     uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
     bool exit;                  /* No further actions should be processed. */
 
+    /* The post MPLS vlan_tci.
+     *
+     * The value of the vlan TCI prior to the translation of an MPLS push
+     * actions should be stored in 'xin->flow->vlan_tci'.
+     *
+     * If an VLAN or set field action is subsequently translated then
+     * 'post_mpls_vlan_tci' is updated as according to the action.
+     *
+     * If the VLAN_CFI bit of 'post_mpls_vlan_tci' is set then VLAN ODP actions
+     * will be committed after any MPLS actions regardless of whether VLAN
+     * actions were also committed before the MPLS actions or not.
+     *
+     * This mechanism allows a VLAN tag to be popped before pushing
+     * an MPLS LSE and then the same VLAN tag pushed after pushing
+     * the MPLS LSE. In this way it is possible to push an MPLS LSE
+     * after an existing VLAN tag. Moreover this mechanism allows
+     * the order in which VLAN tags and MPLS LSEs are pushed. */
+    ovs_be16 post_mpls_vlan_tci;
+
+    /* The next vlan_tci state.
+     *
+     * This field pints to the variable update each time an
+     * action updates the VLAN tci.
+     *
+     * This variable initially points to 'xin->flow->vlan_tci' so that ODP
+     * VLAN actions are committed before any MPLS actions. When an MPLS
+     * action is composed 'next_vlan_tci' is updated to point to
+     * 'post_mpls_vlan_tci'. This causes subsequent VLAN actions to be
+     * committed after MPLS actions. */
+    ovs_be16 *next_vlan_tci;
+
     /* OpenFlow 1.1+ action set.
      *
      * 'action_set' accumulates "struct ofpact"s added by OFPACT_WRITE_ACTIONS.
@@ -1100,11 +1131,38 @@ output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan)
     }
 }
 
+static bool mpls_actions_xlated(struct xlate_ctx *ctx)
+{
+    return ctx->next_vlan_tci == &ctx->post_mpls_vlan_tci;
+}
+
+static ovs_be16
+vlan_tci_save(struct xlate_ctx *ctx)
+{
+    ovs_be16 orig_tci = ctx->xin->flow.vlan_tci;
+    /* If MPLS actions were executed after vlan actions then
+     * copy the final vlan_tci out and restore the intermediate VLAN state. */
+    if (mpls_actions_xlated(ctx)) {
+        ctx->xin->flow.vlan_tci = *ctx->next_vlan_tci;
+    }
+    return orig_tci;
+}
+
+static void
+vlan_tci_restore(struct xlate_ctx *ctx, ovs_be16 orig_tci)
+{
+    /* If MPLS actions were executed after vlan actions then
+     * copy the final vlan_tci out and restore the intermediate VLAN state. */
+    if (mpls_actions_xlated(ctx)) {
+        ctx->post_mpls_vlan_tci = ctx->xin->flow.vlan_tci;
+        ctx->xin->flow.vlan_tci = orig_tci;
+    }
+}
+
 static void
 output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
               uint16_t vlan)
 {
-    ovs_be16 *flow_tci = &ctx->xin->flow.vlan_tci;
     uint16_t vid;
     ovs_be16 tci, old_tci;
     struct xport *xport;
@@ -1129,18 +1187,18 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
         }
     }
 
-    old_tci = *flow_tci;
+    old_tci = *ctx->next_vlan_tci;
     tci = htons(vid);
     if (tci || out_xbundle->use_priority_tags) {
-        tci |= *flow_tci & htons(VLAN_PCP_MASK);
+        tci |= *ctx->next_vlan_tci & htons(VLAN_PCP_MASK);
         if (tci) {
             tci |= htons(VLAN_CFI);
         }
     }
-    *flow_tci = tci;
+    ctx->xin->flow.vlan_tci = *ctx->next_vlan_tci = tci;
 
     compose_output_action(ctx, xport->ofp_port);
-    *flow_tci = old_tci;
+    ctx->xin->flow.vlan_tci = *ctx->next_vlan_tci = old_tci;
 }
 
 /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
@@ -1373,7 +1431,7 @@ xlate_normal(struct xlate_ctx *ctx)
 
     /* Drop malformed frames. */
     if (flow->dl_type == htons(ETH_TYPE_VLAN) &&
-        !(flow->vlan_tci & htons(VLAN_CFI))) {
+        !(*ctx->next_vlan_tci & htons(VLAN_CFI))) {
         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 with partial "
@@ -1397,7 +1455,7 @@ xlate_normal(struct xlate_ctx *ctx)
     }
 
     /* Check VLAN. */
-    vid = vlan_tci_to_vid(flow->vlan_tci);
+    vid = vlan_tci_to_vid(*ctx->next_vlan_tci);
     if (!input_vid_is_valid(vid, in_xbundle, ctx->xin->packet != NULL)) {
         xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
         return;
@@ -1655,7 +1713,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
     struct flow_wildcards *wc = &ctx->xout->wc;
     struct flow *flow = &ctx->xin->flow;
-    ovs_be16 flow_vlan_tci;
+    ovs_be16 flow_vlan_tci, vlan_tci;
     uint32_t flow_pkt_mark;
     uint8_t flow_nw_tos;
     odp_port_t out_port, odp_port;
@@ -1724,6 +1782,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     }
 
     flow_vlan_tci = flow->vlan_tci;
+    vlan_tci = *ctx->next_vlan_tci;
     flow_pkt_mark = flow->pkt_mark;
     flow_nw_tos = flow->nw_tos;
 
@@ -1763,12 +1822,13 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
             wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
         }
         vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, ofp_port,
-                                              flow->vlan_tci);
+                                              *ctx->next_vlan_tci);
         if (vlandev_port == ofp_port) {
             out_port = odp_port;
         } else {
             out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port);
             flow->vlan_tci = htons(0);
+            ctx->post_mpls_vlan_tci = htons(0);
         }
     }
 
@@ -1776,7 +1836,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
                                               &ctx->xout->wc,
-                                              &ctx->mpls_depth_delta);
+                                              &ctx->mpls_depth_delta,
+                                              ctx->post_mpls_vlan_tci);
         nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
                             out_port);
 
@@ -1788,6 +1849,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
  out:
     /* Restore flow */
     flow->vlan_tci = flow_vlan_tci;
+    *ctx->next_vlan_tci = vlan_tci;
     flow->pkt_mark = flow_pkt_mark;
     flow->nw_tos = flow_nw_tos;
 }
@@ -2055,7 +2117,8 @@ 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->mpls_depth_delta,
+                                          ctx->post_mpls_vlan_tci);
 
     odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
                         ctx->xout->odp_actions.size, NULL, NULL);
@@ -2496,7 +2559,8 @@ 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->mpls_depth_delta,
+                                        ctx->post_mpls_vlan_tci);
 
   compose_flow_sample_cookie(os->probability, os->collector_set_id,
                              os->obs_domain_id, os->obs_point_id, &cookie);
@@ -2589,33 +2653,36 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         case OFPACT_SET_VLAN_VID:
             wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
-            if (flow->vlan_tci & htons(VLAN_CFI) ||
+            if (*ctx->next_vlan_tci & htons(VLAN_CFI) ||
                 ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
-                flow->vlan_tci &= ~htons(VLAN_VID_MASK);
-                flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
-                                   | htons(VLAN_CFI));
+                uint16_t vlan_vid = ofpact_get_SET_VLAN_VID(a)->vlan_vid;
+
+                *ctx->next_vlan_tci &= ~htons(VLAN_VID_MASK);
+                *ctx->next_vlan_tci |= (htons(vlan_vid) | htons(VLAN_CFI));
             }
             break;
 
         case OFPACT_SET_VLAN_PCP:
             wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
-            if (flow->vlan_tci & htons(VLAN_CFI) ||
+            if (*ctx->next_vlan_tci & htons(VLAN_CFI) ||
                 ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
-                flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
-                flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
-                                         << VLAN_PCP_SHIFT) | VLAN_CFI);
+                uint16_t vlan_pcp = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+
+                *ctx->next_vlan_tci &= ~htons(VLAN_PCP_MASK);
+                *ctx->next_vlan_tci |= htons((vlan_pcp << VLAN_PCP_SHIFT) |
+                                             VLAN_CFI);
             }
             break;
 
         case OFPACT_STRIP_VLAN:
             memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
-            flow->vlan_tci = htons(0);
+            *ctx->next_vlan_tci = htons(0);
             break;
 
         case OFPACT_PUSH_VLAN:
             /* XXX 802.1AD(QinQ) */
             memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
-            flow->vlan_tci = htons(VLAN_CFI);
+            *ctx->next_vlan_tci = htons(VLAN_CFI);
             break;
 
         case OFPACT_SET_ETH_SRC:
@@ -2697,13 +2764,19 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             flow->skb_priority = ctx->orig_skb_priority;
             break;
 
-        case OFPACT_REG_MOVE:
+        case OFPACT_REG_MOVE: {
+            ovs_be16 orig_tci = flow->vlan_tci;
             nxm_execute_reg_move(ofpact_get_REG_MOVE(a), flow, wc);
+            vlan_tci_restore(ctx, orig_tci);
             break;
+        }
 
-        case OFPACT_REG_LOAD:
+        case OFPACT_REG_LOAD: {
+            ovs_be16 orig_tci = vlan_tci_save(ctx);
             nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow, wc);
+            vlan_tci_restore(ctx, orig_tci);
             break;
+        }
 
         case OFPACT_SET_FIELD:
             set_field = ofpact_get_SET_FIELD(a);
@@ -2712,28 +2785,49 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
             /* Set field action only ever overwrites packet's outermost
              * applicable header fields.  Do nothing if no header exists. */
-            if ((mf->id != MFF_VLAN_VID || flow->vlan_tci & htons(VLAN_CFI))
+            if ((mf->id != MFF_VLAN_VID ||
+                 *ctx->next_vlan_tci & htons(VLAN_CFI))
                 && ((mf->id != MFF_MPLS_LABEL && mf->id != MFF_MPLS_TC)
                     || flow->mpls_lse)) {
+                ovs_be16 orig_tci = vlan_tci_save(ctx);
                 mf_set_flow_value(mf, &set_field->value, flow);
+                vlan_tci_restore(ctx, orig_tci);
             }
             break;
 
-        case OFPACT_STACK_PUSH:
+        case OFPACT_STACK_PUSH: {
+            ovs_be16 orig_tci = vlan_tci_save(ctx);
             nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc,
                                    &ctx->stack);
+            vlan_tci_restore(ctx, orig_tci);
             break;
+        }
 
-        case OFPACT_STACK_POP:
+        case OFPACT_STACK_POP: {
+            ovs_be16 orig_tci = vlan_tci_save(ctx);
             nxm_execute_stack_pop(ofpact_get_STACK_POP(a), flow, wc,
                                   &ctx->stack);
+            vlan_tci_restore(ctx, orig_tci);
             break;
+        }
 
         case OFPACT_PUSH_MPLS:
             if (compose_mpls_push_action(ctx,
                                          ofpact_get_PUSH_MPLS(a)->ethertype)) {
                 return;
             }
+
+            /* Save and pop any existing VLAN tags. They will be pushed
+             * back onto the packet after pushing the MPLS LSE. The overall
+             * effect is to push the MPLS LSE after any VLAN tags that may
+             * be present. This is the behaviour described for OpenFlow 1.1
+             * and 1.2. This code needs to be enhanced to make this
+             * conditional to also and support pushing the MPLS LSE before
+             * any VLAN tags that may be present, the behaviour described
+             * for OpenFlow 1.3. */
+            ctx->post_mpls_vlan_tci = *ctx->next_vlan_tci;
+            flow->vlan_tci = htons(0);
+            ctx->next_vlan_tci = &ctx->post_mpls_vlan_tci;
             break;
 
         case OFPACT_POP_MPLS:
@@ -3087,6 +3181,8 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     ctx.table_id = 0;
     ctx.exit = false;
     ctx.mpls_depth_delta = 0;
+    ctx.post_mpls_vlan_tci = htons(0);
+    ctx.next_vlan_tci = &ctx.xin->flow.vlan_tci;
 
     if (!xin->ofpacts && !ctx.rule) {
         rule_dpif_lookup(ctx.xbridge->ofproto, flow, wc, &rule);
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index b78e156..9becf05 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1063,6 +1063,395 @@ done
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - OF1.2 VLAN+MPLS handling])
+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
+cookie=0xa dl_src=40:44:44:44:54:50 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:54:51 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:54:52 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:54:53 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:54:54 actions=push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xa dl_src=40:44:44:44:54:55 actions=push_vlan:0x8100,set_vlan_vid:99,set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xa dl_src=40:44:44:44:54:56 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xa dl_src=40:44:44:44:54:57 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xa dl_src=40:44:44:44:54:58,vlan_tci=0x1000/0x1000 actions=load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],controller
+cookie=0xa dl_src=40:44:44:44:54:59,vlan_tci=0x1000/0x1000 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],set_vlan_pcp:1,load:99->OXM_OF_VLAN_VID[[]],controller
+])
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 add-flows br0 flows.txt])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push the MPLS tag before pushing a VLAN tag, so we see
+dnl both of these in the final flow
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:50,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 50 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be stripped
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:51,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 51 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push the MPLS tag before pushing a VLAN tag, so we see
+dnl both of these in the final flow
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:52,dst=52:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:52,dl_dst=52:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  52 54 00 00 00 07 40 44-44 44 54 52 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be stripped
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:53,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 53 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push the VLAN tag before pushing a MPLS tag, but these
+dnl actions are reordered, so we see both of these in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:54,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 54 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be stripped
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:55,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 55 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push the VLAN tag before pushing a MPLS tag, but these
+dnl actions are reordered, so we see both of these in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:56,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:56,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 56 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be stripped
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 -m 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:57,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:57,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 57 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be stripped
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:58,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:58,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 58 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be modified
+dnl before we push MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:59,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.2): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:54:59,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=0,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 54 59 81 00 20 63
+00000010  88 47 00 00 a1 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore])
+AT_CHECK([ovs-ofctl --protocols=OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:50 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:51 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:52 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:53 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:54 actions=push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:55 actions=push_vlan:0x8100,set_field:4195->vlan_vid,set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:56 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:54:57 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, vlan_tci=0x1000/0x1000,dl_src=40:44:44:44:54:58 actions=load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, vlan_tci=0x1000/0x1000,dl_src=40:44:44:44:54:59 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],set_field:1->vlan_pcp,load:0x63->OXM_OF_VLAN_VID[[]],CONTROLLER:65535
+OFPST_FLOW reply (OF1.2):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - fragment handling])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2.51 3/5] lib: Support pushing of MPLS LSE before or after VLAN tag
       [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
  2013-11-21  3:46   ` [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push Simon Horman
  2013-11-21  3:46   ` [PATCH v2.51 2/5] odp: Allow VLAN actions after MPLS actions Simon Horman
@ 2013-11-21  3:46   ` Simon Horman
  2 siblings, 0 replies; 19+ messages in thread
From: Simon Horman @ 2013-11-21  3:46 UTC (permalink / raw)
  To: dev-yBygre7rU0TnMu66kgdUjQ, netdev-u79uwXL29TY76Z2rM5mHXA,
	Jesse Gross, Ben Pfaff
  Cc: Ravi K

This patch modifies the push_mpls behaviour to allow
pushing of an MPLS LSE either before any VLAN tag that may be present.

Pushing the MPLS LSE before any VLAN tag that is present is the
behaviour specified in OpenFlow 1.3.

Pushing the MPLS LSE after the any VLAN tag that is present is the
behaviour specified in OpenFlow 1.1 and 1.2. This is the only behaviour
that was supported prior to this patch.

When an push_mpls action has been inserted using OpenFlow 1.2 or earlier
the behaviour of pushing the MPLS LSE before any VLAN tag that may be
present is implemented by by inserting VLAN actions around the MPLS push
action during odp translation; Pop VLAN tags before committing MPLS
actions, and push the expected VLAN tag afterwards.

The trigger condition for the two different behaviours is the value of
the mpls_before_vlan field of struct ofpact_push_mpls. This field is set
when parsing OpenFlow actions.

Signed-off-by: Joe Stringer <joe-Q1GJJQv1iO6lP80pJB477g@public.gmane.org>
Signed-off-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>

---

v2.51
* Revert ofpact_check__() changes introduced in v2.49
* Enable consistency checking when OpenFlow1.3 is in use
  by deleting the line of ofpacts_check__() which disables it.
* Add write_actions test
* Use OF1.3 for all ovs-ofctl commands
  - Allows write_actions to show up in dump-flows
  - Otherwise cosmetic

v2.50
* No change

v2.49 [Simon Horman]
* Teach ofpact_check__() when OpenFlow1.3 is in use and thus
  MPLS LSEs are pushed before VLAN tags, that in effect there
  is no VLAN tag present after an mpls_push action.

v2.48 [Simon Horman]
* Only use OpenFlow1.3 actions in OpenFlow1.3 tests

v2.46 - v2.47
* No change

v2.45 [Simon Horman]
* Rebased for updates to VLAN handling in the presence of MPLS
* Update tests to use ovs-ofctl monitor "-m" to allow full
  inspection of MPLS LSE and VLAN tags present in packets.

v2.44
* No change

v2.43 [Simon Horman]
* Trivial rebase as the result of replacing 'mpls_before_vlan' field
  of struct ofpact_push_mpls with a 'position' field.

v2.42
* No change

v2.41 [Simon Horman]
* Use 'mpls_before_vlan' field of struct ofpact_push_mpls.
* Reword changelog to describe the difference in behaviour between
  different Open Flow versions.

v2.40 [Simon Horman]
* Trivial rebase for removal of set_ethertype()

v2.36 - v2.39
* No change

v2.35 [Joe Stringer]
* First post
---
 lib/flow.c                   |   2 +-
 lib/ofp-actions.c            |   8 -
 lib/packets.c                |  10 +-
 lib/packets.h                |   2 +-
 ofproto/ofproto-dpif-xlate.c |  29 +--
 tests/ofproto-dpif.at        | 480 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 503 insertions(+), 28 deletions(-)

diff --git a/lib/flow.c b/lib/flow.c
index c6683a5..ae4d2b2 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1136,7 +1136,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
     }
 
     if (eth_type_mpls(flow->dl_type)) {
-        b->l2_5 = b->l3;
+        b->l2_5 = (char*)b->l2 + ETH_HEADER_LEN;
         push_mpls(b, flow->dl_type, flow->mpls_lse);
     }
 }
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 6754939..36fc617 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -2257,14 +2257,6 @@ ofpacts_check__(struct ofpact ofpacts[], size_t ofpacts_len,
         if (error) {
             break;
         }
-        /* XXX
-         * OF1.3 tag order is not currently implemented.
-         * The following line effectively disables the
-         * use of 'of13_vlan_tci' to check OF1.3+ tag order
-         * consistency. This means that pre-OF1.3+ is always used.
-         * This line should be removed by a subequent
-         * patch which enables the use of OF1.3+ tag order */
-        *of13_vlan_tci = flow->vlan_tci;
     }
     /* Restore fields that may have been modified. */
     flow->dl_type = dl_type;
diff --git a/lib/packets.c b/lib/packets.c
index 4bec4d1..b55726e 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -219,11 +219,11 @@ eth_pop_vlan(struct ofpbuf *packet)
 
 /* Set ethertype of the packet. */
 void
-set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type)
+set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type, bool inner)
 {
     struct eth_header *eh = packet->data;
 
-    if (eh->eth_type == htons(ETH_TYPE_VLAN)) {
+    if (inner && eh->eth_type == htons(ETH_TYPE_VLAN)) {
         ovs_be16 *p;
         p = ALIGNED_CAST(ovs_be16 *,
                 (char *)(packet->l2_5 ? packet->l2_5 : packet->l3) - 2);
@@ -331,8 +331,8 @@ push_mpls(struct ofpbuf *packet, ovs_be16 ethtype, ovs_be32 lse)
 
     if (!is_mpls(packet)) {
         /* Set ethtype and MPLS label stack entry. */
-        set_ethertype(packet, ethtype);
-        packet->l2_5 = packet->l3;
+        set_ethertype(packet, ethtype, false);
+        packet->l2_5 = (char*)packet->l2 + ETH_HEADER_LEN;
     }
 
     /* Push new MPLS shim header onto packet. */
@@ -353,7 +353,7 @@ pop_mpls(struct ofpbuf *packet, ovs_be16 ethtype)
         size_t len;
         mh = packet->l2_5;
         len = (char*)packet->l2_5 - (char*)packet->l2;
-        set_ethertype(packet, ethtype);
+        set_ethertype(packet, ethtype, true);
         if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
             packet->l2_5 = NULL;
         } else {
diff --git a/lib/packets.h b/lib/packets.h
index ef8c00c..4d1d197 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -143,7 +143,7 @@ void compose_rarp(struct ofpbuf *, const uint8_t eth_src[ETH_ADDR_LEN]);
 void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
 void eth_pop_vlan(struct ofpbuf *);
 
-void set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type);
+void set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type, bool inner);
 
 const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
 void eth_format_masked(const uint8_t eth[ETH_ADDR_LEN],
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index daacee6..d734028 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2811,24 +2811,27 @@ 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)) {
+        case OFPACT_PUSH_MPLS: {
+            struct ofpact_push_mpls *oam = ofpact_get_PUSH_MPLS(a);
+
+            if (compose_mpls_push_action(ctx, oam->ethertype)) {
                 return;
             }
 
-            /* Save and pop any existing VLAN tags. They will be pushed
-             * back onto the packet after pushing the MPLS LSE. The overall
-             * effect is to push the MPLS LSE after any VLAN tags that may
-             * be present. This is the behaviour described for OpenFlow 1.1
-             * and 1.2. This code needs to be enhanced to make this
-             * conditional to also and support pushing the MPLS LSE before
-             * any VLAN tags that may be present, the behaviour described
-             * for OpenFlow 1.3. */
-            ctx->post_mpls_vlan_tci = *ctx->next_vlan_tci;
-            flow->vlan_tci = htons(0);
+            /* Save and pop any existing VLAN tags if the MPLS LSE should
+             * be pushed after VLAN tags.  The overall effect is to push
+             * the MPLS LSE after any VLAN tags that may be present.  This
+             * is the behaviour described for OpenFlow 1.1 and 1.2.
+             * Do not save and therefore pop the VLAN tags if the MPLS LSE
+             * should be pushed before any VLAN tags that are present.
+             * This is the behaviour described for OpenFlow 1.3. */
+            if (oam->position == OFPACT_MPLS_AFTER_VLAN) {
+                ctx->post_mpls_vlan_tci = *ctx->next_vlan_tci;
+                flow->vlan_tci = htons(0);
+            }
             ctx->next_vlan_tci = &ctx->post_mpls_vlan_tci;
             break;
+        }
 
         case OFPACT_POP_MPLS:
             if (compose_mpls_pop_action(ctx,
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 9becf05..4aa48a5 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1452,6 +1452,486 @@ OFPST_FLOW reply (OF1.2):
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - OF1.3+ VLAN+MPLS handling])
+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
+cookie=0xa dl_src=40:44:44:44:55:44 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:55:45 actions=push_vlan:0x8100,set_vlan_vid:99,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:55:46 actions=push_vlan:0x8100,set_vlan_vid:99,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:55:47 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:55:48 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:55:49 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,set_vlan_vid:99,controller
+cookie=0xa dl_src=40:44:44:44:55:50 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,set_vlan_vid:99,controller
+cookie=0xa dl_src=40:44:44:44:55:51 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:55:52 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:55:53,vlan_tci=0x1000/0x1000 actions=load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:55:54 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller
+cookie=0xa dl_src=40:44:44:44:55:55 actions=push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],write_actions(push_vlan:0x8100,load:99->OXM_OF_VLAN_VID[[]],set_vlan_pcp:1,controller)
+])
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 add-flows br0 flows.txt])
+
+dnl Modified MPLS controller action.
+dnl The input packet has a VLAN tag, but because we push an MPLS tag in
+dnl OF1.3 mode, we can no longer see it in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:44,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): 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:55:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 44 88 47 00 00
+00000010  a7 40 e0 58 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): 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:55:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 44 88 47 00 00
+00000010  a7 40 e0 58 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): 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:55:44,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 44 88 47 00 00
+00000010  a7 40 e0 58 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push a VLAN tag, then an MPLS tag in OF1.3 mode, so we
+dnl can only see the MPLS tag in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:45,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:55:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 45 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:55:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 45 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:55:45,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 45 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet is vlan-tagged; we update this tag then
+dnl push an MPLS tag in OF1.3 mode. As such, we can only see the MPLS tag in
+dnl the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:46,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): 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:55:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 46 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): 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:55:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 46 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): 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:55:46,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 46 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push a VLAN tag, then an MPLS tag in OF1.3 mode, so we
+dnl can only see the MPLS tag in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:47,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:55:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 47 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:55:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 47 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:55:47,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 47 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet is vlan-tagged; we update this tag then
+dnl push an MPLS tag in OF1.3 mode. As such, we can only see the MPLS tag in
+dnl the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:48,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): 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:55:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 48 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): 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:55:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 48 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): 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:55:48,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 48 88 47 00 00
+00000010  a7 40 00 63 08 00 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push the MPLS tag before pushing a VLAN tag, so we see
+dnl both of these in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:49,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=0,dl_src=40:44:44:44:55:49,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 49 81 00 00 63
+00000010  88 47 00 00 a7 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=0,dl_src=40:44:44:44:55:49,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 49 81 00 00 63
+00000010  88 47 00 00 a7 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=0,dl_src=40:44:44:44:55:49,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 49 81 00 00 63
+00000010  88 47 00 00 a7 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be stripped
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:50,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=0,dl_src=40:44:44:44:55:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 50 81 00 00 63
+00000010  88 47 00 00 a7 40 e0 58-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=0,dl_src=40:44:44:44:55:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 50 81 00 00 63
+00000010  88 47 00 00 a7 40 e0 58-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=0,dl_src=40:44:44:44:55:50,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 50 81 00 00 63
+00000010  88 47 00 00 a7 40 e0 58-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, we push the MPLS tag before pushing a VLAN tag, so we see
+dnl both of these in the final flow.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:51,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 51 81 00 20 63
+00000010  88 47 00 00 a7 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 51 81 00 20 63
+00000010  88 47 00 00 a7 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:51,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 51 81 00 20 63
+00000010  88 47 00 00 a7 40 45 00-00 28 00 00 00 00 40 06
+00000020  f9 7c c0 a8 00 01 c0 a8-00 02 00 00 00 00 00 00
+00000030  00 00 00 00 00 00 50 00-00 00 00 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be unaltered
+dnl before we push the MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:52,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:52,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 52 81 00 20 63
+00000010  88 47 00 00 a7 40 e0 58-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:52,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 52 81 00 20 63
+00000010  88 47 00 00 a7 40 e0 58-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:52,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 52 81 00 20 63
+00000010  88 47 00 00 a7 40 e0 58-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be modified
+dnl before we push MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:53,dst=50:54:00:00:00:07),eth_type(0x8100),vlan(vid=88,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no))'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 53 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 53 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=68 in_port=1 (via action) data_len=68 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:53,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 53 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be modified
+dnl before we push MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:54,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=72 in_port=1 (via action) data_len=72 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 54 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00 00 00 00 00-
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=72 in_port=1 (via action) data_len=72 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 54 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00 00 00 00 00-
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=72 in_port=1 (via action) data_len=72 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:54,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 54 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00 00 00 00 00-
+])
+
+dnl Modified MPLS controller action.
+dnl In this test, the input packet in vlan-tagged, which should be modified
+dnl before we push MPLS and VLAN tags.
+AT_CHECK([ovs-ofctl --protocols=OpenFlow13 monitor br0 65534 -m -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:55:55,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=72 in_port=1 (via action) data_len=72 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 55 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00 00 00 00 00-
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=72 in_port=1 (via action) data_len=72 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 55 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00 00 00 00 00-
+dnl
+OFPT_PACKET_IN (OF1.3): cookie=0xa total_len=72 in_port=1 (via action) data_len=72 (unbuffered)
+mpls,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:55:55,dl_dst=50:54:00:00:00:07,mpls_label=10,mpls_tc=3,mpls_ttl=64,mpls_bos=1
+00000000  50 54 00 00 00 07 40 44-44 44 55 55 81 00 20 63
+00000010  88 47 00 00 a7 40 20 63-08 00 45 00 00 28 00 00
+00000020  00 00 40 06 f9 7c c0 a8-00 01 c0 a8 00 02 00 00
+00000030  00 00 00 00 00 00 00 00-00 00 50 00 00 00 00 00
+00000040  00 00 00 00 00 00 00 00-
+])
+
+AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore])
+AT_CHECK([ovs-ofctl --protocol=OpenFlow13 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:44 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:45 actions=push_vlan:0x8100,set_field:4195->vlan_vid,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:46 actions=push_vlan:0x8100,set_field:4195->vlan_vid,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:47 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:48 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:49 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,set_field:4195->vlan_vid,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:50 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,set_field:4195->vlan_vid,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:51 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:52 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:54 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:55:55 actions=push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],write_actions(push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535)
+ cookie=0xa, n_packets=3, n_bytes=180, vlan_tci=0x1000/0x1000,dl_src=40:44:44:44:55:53 actions=load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],push_vlan:0x8100,load:0x63->OXM_OF_VLAN_VID[[]],set_field:1->vlan_pcp,CONTROLLER:65535
+OFPST_FLOW reply (OF1.3):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - fragment handling])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2.51 4/5] datapath: Break out deacceleration portion of vlan_push
  2013-11-21  3:46 [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
       [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
@ 2013-11-21  3:46 ` Simon Horman
  2013-11-21  3:46 ` [PATCH v2.51 5/5] datapath: Add basic MPLS support to kernel Simon Horman
  2013-11-26  8:08 ` [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
  3 siblings, 0 replies; 19+ messages in thread
From: Simon Horman @ 2013-11-21  3:46 UTC (permalink / raw)
  To: dev, netdev, Jesse Gross, Ben Pfaff; +Cc: Pravin B Shelar, Ravi K, Joe Stringer

Break out deacceleration portion of vlan_push into vlan_put
so that it may be re-used by mpls_push.

For both vlan_push and mpls_push if there is an accelerated VLAN tag
present then it should be deaccelerated, adding it to the data of
the skb, before the new tag is added.

Signed-off-by: Simon Horman <horms@verge.net.au>

---

v2.41 - v2.51
* No change

v2.40
* As suggested by Jesse Gross
  + Simplify vlan_push by returning an error code
    rather than an error code encoded as a struct xkb_buff *

v2.39
* First post
---
 datapath/actions.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 30ea1d2..ee2456b 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -105,21 +105,29 @@ static int pop_vlan(struct sk_buff *skb)
 	return 0;
 }
 
-static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan)
+/* push down current VLAN tag */
+static int put_vlan(struct sk_buff *skb)
 {
-	if (unlikely(vlan_tx_tag_present(skb))) {
-		u16 current_tag;
+	u16 current_tag = vlan_tx_tag_get(skb);
 
-		/* push down current VLAN tag */
-		current_tag = vlan_tx_tag_get(skb);
+	if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
+		return -ENOMEM;
 
-		if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
-			return -ENOMEM;
+	if (skb->ip_summed == CHECKSUM_COMPLETE)
+		skb->csum = csum_add(skb->csum, csum_partial(skb->data
+				+ (2 * ETH_ALEN), VLAN_HLEN, 0));
 
-		if (skb->ip_summed == CHECKSUM_COMPLETE)
-			skb->csum = csum_add(skb->csum, csum_partial(skb->data
-					+ (2 * ETH_ALEN), VLAN_HLEN, 0));
+	return 0;
+}
+
+static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan)
+{
+	if (unlikely(vlan_tx_tag_present(skb))) {
+		int err;
 
+		err = put_vlan(skb);
+		if (unlikely(err))
+			return err;
 	}
 	__vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
 	return 0;
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v2.51 5/5] datapath: Add basic MPLS support to kernel
  2013-11-21  3:46 [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
       [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
  2013-11-21  3:46 ` [PATCH v2.51 4/5] datapath: Break out deacceleration portion of vlan_push Simon Horman
@ 2013-11-21  3:46 ` Simon Horman
  2013-11-26  8:08 ` [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
  3 siblings, 0 replies; 19+ messages in thread
From: Simon Horman @ 2013-11-21  3:46 UTC (permalink / raw)
  To: dev, netdev, Jesse Gross, Ben Pfaff; +Cc: Pravin B Shelar, Ravi K, Joe Stringer

Allow datapath to recognize and extract MPLS labels into flow keys
and execute actions which push, pop, and set labels on packets.

Based heavily on work by Leo Alterman, Ravi K, Isaku Yamahata and Joe Stringer.

Cc: Ravi K <rkerur@gmail.com>
Cc: Leo Alterman <lalterman@nicira.com>
Cc: Isaku Yamahata <yamahata@valinux.co.jp>
Cc: Joe Stringer <joe@wand.net.nz>
Signed-off-by: Simon Horman <horms@verge.net.au>

---

v2.50 - v2.51
* No change

v2.49
* Remove MPLS items from OPENFLOW-1.1+. They should now be complete.

v2.47
* Rebase for HAVE_RHEL_OVS_HOOK and OVS_KEY_ATTR_TCP_FLAGS

v2.43 - v2.46
* No change

v2.42
* Rebase for:
  + 0585f7a ("datapath: Simplify mega-flow APIs.")
  + a097c0b ("datapath: Restructure datapath.c and flow.c")
* As suggested by Jesse Gross
  + Take into account that push_mpls() will have freed the skb on error
  + Remove dubious !eth_p_mpls(skb->protocol) condition from push_mpls
    The !eth_p_mpls(skb->protocol) condition on setting inner_protocol
    has no effect. Its motivation was to ensure that inner_protocol was
    only set the first time that mpls_push occured. However this is already
    ensured by the !ovs_skb_get_inner_protocol(skb) condition.
  + Return -EINVAL instead of -ENOMEM from pop_mpls() if the skb is too short
  + Do not add @inner_protocol to kernel doc for struct ovs_skb_cb.
    The patch no longer adds an inner_protocol member to struct ovs_skb_cb
  + Do not add and set otherwise unsued inner_protocol variable in
    rpl_dev_queue_xmit()
* As suggested by Pravin Shelar
  + Implement compatibility code in existing rpl_skb_gso_segment
    rather than introducing to use rpl___skb_gso_segment

v2.41
* No change

v2.40
* Rebase for:
  + New dev_queue_xmit compat code
  + Updated put_vlan()
* As suggested by Jesse Gross
  + Remove bogus mac_len update from push_mpls()
  + Slightly simplify push_mpls() by using eth_hdr()
  + Remove dubious condition !eth_p_mpls(inner_protocol) on
    an skb being considered to be MPLS in netdev_send()
  + Only use compatibility code for MPLS GSO segmentation on kernels
    older than 3.11
  + Revamp setting of inner_protocol
    1. Do not unconditionally set inner_protocol to the value of
       skb->protocol in ovs_execute_actions().
    2. Initialise inner_protocol it to zero only if compatibility code is in
       use. In the case where compatibility code is not in use it will either
       be zero due since the allocation of the skb or some other value set
       by some other user.
    3. Conditionally set the inner_protocol in push_mpls() to the value of
       skb->protocol when entering push_mpls(). The condition is that
       inner_protocol is zero and the value of skb->protocol is not an MPLS
       ethernet type.
    - This new scheme:
      + Pushes logic to set inner_protocol closer to the case where it is
	needed.
      + Avoids over-writing values set by other users.
* As suggested by Pravin Shelar
  + Only set and restore skb->protocol in rpl___skb_gso_segment() in the
    case of MPLS
  + Add inner_protocol field to struct ovs_gso_cb instead of ovs_skb_cb.
    This moves compatibility code closer to where it is used
    and creates fewer differences with mainline.
* Update comment on mac_len updates in datapath/actions.c
* Remove HAVE_INNER_PROCOTOL and instead just check
  against kernel version 3.11 directly.
  HAVE_INNER_PROCOTOL is a hang-over from work done prior
  to the merge of inner_protocol into the kernel.
* Remove dubious condition !eth_p_mpls(inner_protocol) on
  using inner_protocol as the type in rpl_skb_network_protocol()
* Do not update type of features in rpl_dev_queue_xmit.
  Though arguably correct this is not an inherent part of
  the changes made by this patch.
* Use skb_cow_head() in push_mpls()
  + Call skb_cow_head(skb, MPLS_HLEN) instead of
    make_writable(skb, skb->mac_len) to ensure that there is enough head
    room to push an MPLS LSE regardless of whether the skb is cloned or not.
  + This is consistent with the behaviour of rpl__vlan_put_tag().
  + This is a fix for crashes reported when performing mpls_push
    with headroom less than 4. This problem was introduced in v3.36.
* Skip popping in mpls_pop if the skb is too short to contain an MPLS LSE

v2.39
* Rebase for removal of vlan, checksum and skb->mark compat code

v2.38
* Rebase for SCTP support
* Refactor validate_tp_port() to iterate over eth_types rather
  than open-coding the loop. With the addition of SCTP this logic
  is now used three times.

v2.36 - v2.37
* Rebase

* Do not add set_ethertype() to datapath/actions.c.
  As this patch has evolved this function had devolved into
  to sets of functionality wrapped into a single function with
  only one line of common code. Refactor things to simply
  open-code setting the ether type in the two locations where
  set_ethertype() was previously used. The aim here is to improve
  readability.

* Update setting skb->ethertype after mpls push and pop.
  - In the case of push_mpls it should be set unconditionally
    as in v2.35 the behaviour of this function to always push
    an MPLS LSE before any VLAN tags.
  - In the case of mpls_pop eth_p_mpls(skb->protocol) is a better
    test than skb->protocol != htons(ETH_P_8021Q) as it will give the
    correct behaviour in the presence of other VLAN ethernet types,
    for example 0x88a8 which is used by 802.1ad. Moreover, it seems
    correct to update the ethernet type if it was previously set
    according to the top-most MPLS LSE.

* Deaccelerate VLANs when pushing MPLS tags the
  - Since v2.35 MPLS push will insert an MPLS LSE before any VLAN tags.
    This means that if an accelerated tag is present it should be
    deaccelerated to ensure it ends up in the correct position.

* Update skb->mac_len in push_mpls() so that it will be correct
  when used by a subsequent call to pop_mpls().

  As things stand I do not believe this is strictly necessary as
  ovs-vswitchd will not send a pop MPLS action after a push MPLS action.
  However, I have added this in order to code more defensively as I believe
  that if such a sequence did occur it would be rather unobvious why
  it didn't work.

* Do not add skb_cow_head() call in push_mpls().
  It is unnecessary as there is a make_writable() call.
  This change was also made in v2.30 but some how the
  code regressed between then and v2.35.

v2.35
* Rebase
* Move MPLS constants to mpls.h
* Push MPLS tags after ethernet, before VLAN tags
  - This is consistent with the OpenFlow 1.3 specification
  - Compatibility with OpenFlow 1.2 and earlier versions
    may be provided by ovs-vswitchd.
* Correct GSO behaviour in the presence of MPLS but absence of VLANs

v2.34
* Rebase for megaflow changes

v2.33
* Ensure that inner_protocol is always set to to the current
  skb->protocol value in ovs_execute_actions(). This ensures
  it is set to the correct value in the absence of a push_mpls action.
  Also remove setting of inner_protocol in push_mpls() as
  it duplicates the code now in ovs_execute_actions().
* Call __skb_gso_segment() instead of skb_gso_segment() from
  rpl___skb_gso_segment() in the case that HAVE___SKB_GSO_SEGMENT is set.
  This was a typo.

v2.32
* As suggested by Jesse Gross
  - Use int instead of size_t in validate_and_copy_actions__().
  - Fix crazy edit mess in pop_mpls() action comment
  - Move eth_p_mpls() into mpls.h
  - Refactor skb_gso_segment MPLS handling into rpl_skb_gso_segment
    Address Jesse's comments regarding this code:
    "Can we push this completely into the skb_gso_segment() compatibility
     code? It's both nicer and may make the interactions with the vlan code
     less confusing."
  - Move GSO compatibility code into linux/compat/gso.*
  - Set skb->protocol on mpls_push and mpls_pop in the presence
    of an offloaded VLAN.

v2.31
* As suggested by Jesse Gross
  - There is no need to make mac_header_end inline as it is not in a header file
  - Remove dubious if (*skb_ethertype == ethertype) optimisation from
    set_ethertype
  - Only set skb->protocol in push_mpls() or pop_mpls() for non-VLAN packets
  - Use MAX_ETH_TYPES instead of SAMPLE_ACTION_DEPTH for array size
    of types in struct eth_types. This corrects a typo/thinko.
  - Correct eth type tracking logic such that start isn't advanced
    when entering a sample action, ensuring that all possibly types
    are checked when verifying nested actions.
* Define HAVE_INNER_PROTOCOL based on kernel version.
  inner_protocol has been merged into net-next and should appear in
  v3.11 so there is no longer a need for a acinclude.m4 test to check for it.
* Add MPLS GSO compatibility code.
  This is for use on kernels that do not have MPLS GSO support.
  Thanks to Joe Stringer for his work on this.

v2.30
* As suggested by Jesse Gross
  - Use skb_cow_head in push_mpls to ensure there is sufficient headroom for
    skb_push
  - Call make_writable with skb->mac_len instead of skb->mac_len + MPLS_HLEN
    in push_mpls as only the first skb->mac_len bytes of existing packet data
    are modified.
  - Rename skb_mac_header_end as mac_header_end, this seems
    to be a more appropriate name for a local function.
  - Remove OVS_CSUM_COMPLETE code from set_ethertype().
    Inside OVS the ethernet header is not covered by OVS_CSUM_COMPLETE.
  - Use __skb_pull() instead of skb_pull() in pop_mpls()
  - Decrement and decrement skb->mac_len when poping and pushing VLAN tags.
    Previously mac_len was reset, but this would result in forgetting
    the MPLS label stack.
  - Remove spurious comment from before do_execute_actions().
  - Move OVS_KEY_ATTR_MPLS attribute to its final, upstreamable, location.
  - Correct ethertype check for OVS_ACTION_ATTR_POP_MPLS case in
    validate_and_copy_actions() to check for MPLS ethertypes rather than
    ETH_P_IP.
  - Rewrite tracking of eth types used to verify actions in the presence
    of sample actions. There is a large comment above struct eth_types
    describing the new implementation.

v2.29
* Break include/ and lib/ portions of the patch out into a
  separate patch "datapath: Add basic MPLS support to kernel"
* Update for new MPLS GSO scheme
  - skb->protocol is set to the new ethertype of the packet
    on MPLS push and pop
  - When pushing the first MPLS LSE onto a previously non-MPLS
    packet set skb->inner_protocol to the original ethertype.
  - skb->inner_protocol may be used by the network stack
    for GSO of the inner-packet.
* Drop const from ethertype parameter of set_ethertype.
  This appears to be a legacy of this parameter being a pointer.
* Pass the ethertype patrameter of pop_mpls as a value rather
  than a pointer.

v2.28
* Kernel Datapath changes as suggested by Jarno Rajahalme
  + Correct the logic introduced in v2.27 to set the network_header
    to after the MPLS label stack in the case of an MPLS packet.
    - Increment stack_len offset so that label stacks of depth greater
      than two do not cause an infinite loop.
    - Correct offset passed to check_header to include skb->mac len

v2.27
* Kernel Datapath changes as suggested by Jarno Rajahalme and Jesse Gross:
  + Previously the mac_len and network_header of an skb corresponded
    to the end of the L2 header.  To support GSO, just before transmission,
    do_output, with the results as follows:

    Input: non-MPLS skb: Output: network header and mac_len correspond
                         to the beginning of the L3 headers
    Input: MPLS:         Output: network header and mac_len correspond to the
                         end of the L2 headers.

    This is somewhat confusing.

  + The new scheme is as follows:
    - The mac_len always corresponds to the end of the L2 header.
    - The network header always corresponds to the beginning of the
      L3 header.

  + Note that in the case of MPLS output the end of the L2 headers and the
  beginning of the L3 headers will differ.

* Remove unused declaration of skb_cb_mpls_stack()

v2.26
* Rebase on master
* Kernel Datapath changes as suggested by Jarno Rajahalme
  - Use skb_network_header() instead of skb_mac_header() to locate
    the ethertype to set in set_ethertype() as the latter will
    be wrong in the presence of VLAN tags. This resolves
    a regression introduced in v2.24.
  - Enhance comment in do_output()
  - do_execute_actions(): Do not alter mpls_stack_depth if
    a MPLS push or pop action fail. This is achieved by altering
    mpls_stack_depth at the end of push_mpls() and pop_mpls().

v2.25
* Rebase on master
* Pass big-endian value as the last argument of eth_types_set() in
  validate_and_copy_actions__()
* Use revised GSO support as provided by the patch series
  "[PATCH 0/2] Small Modifications to GSO to allow segmentation of MPLS"
  - Set skb->mac_len to the length of the l2 header + MPLS stack length
  - Update skb->network_header accordingly
  - Set skb->encapsulated_features

v2.24
* Use skb_mac_header() in set_ethertype()
* Set skb->encapsulation in set_ethertype() to support MPLS GSO.
  Also add a note about the other requirements for MPLS GSO.
  MPLS GSO support will be posted as a patch net-next (Linux mainline)
  "MPLS: Add limited GSO support"
* Do not add ETH_TYPE_MIN, it is no longer used

v2.23
* As suggested by Jesse Gross:
  - Verify the current ethernet type when validating sample actions
    both for the taken and not-taken path if the sample action.
  - Document that the OVS_KEY_ATTR_MPLS attribute accepts a list of
    struct ovs_key_mpls but that an implementation may restrict
    the length it accepts.
  - Restrict the array length of the OVS_KEY_ATTR_MPLS to one.
    + Don't add ovs_flow_verify_key_len as it was added to
      handle attributes whose values are arrays but there are
      no attributes with values that are arrays (of length greater than one).

v2.22
* As suggested by Jesse Gross:
  - Fix sparse warning in validate_and_copy_actions()
    I have no idea why sparse doesn't show this up this on my system.
  - Remove call to skb_cow_head() from push_mpls() as it
    is already covered by a call to make_writable()
  - Check (key_type > OVS_KEY_ATTR_MAX) in ovs_flow_verify_key_len()
  - Disallow set actions on l2.5+ data and MPLS push and pop actions
    after an MPLS pop action as there is no verification that the packet
    is actually of the new ethernet type. This may later be supported
    using recirculation or by other means.
  - Do not add spurious debuging message to ovs_flow_cmd_new_or_set()

v2.21
* As suggested by Jesse Gross:
  - Verify that l3 and l4 actions always always occur prior to
    a push_mpls action and use the network header pointer of an skb
    to track the top of the MPLS stack. This avoids adding an l2_size
    element to the skb callback.

v2.20
* As suggested by Jesse Gross:
  - Do not add ovs_dp_ioctl_hook
    + This appears to be garbage from a rebase
  - Do not add skb_cb_set_l2_size. Instead set OVS_CB(skb)->l2_size
    in ovs_flow_extract().
  - Do not free skb on error in push_mpls(), it is freed in the caller
  - Call skb_reset_mac_len() in pop_mpls() and push_mpls()
  - Update checksums in pop_mpls(), push_mpls() and set_mpls().
  - Rename skb_cb_mpls_bos() as skb_cb_mpls_stack().
    It returns the top not the bottom of the stack.
  - Track the current eth_type in validate_and_copy_actions
    which is initially the eth_type of the flow and may be modified
    by push_mpls and pop_mpls actions. Use this to correctly validate
    mpls_set actions. This is to allow mpls_set actions to be applied
    to a non-MPLS frame after an mpls_push action (although ovs-vswitchd
    doesn't currently do that).
    Also:
    + Remove the check of the eth_type in set_mpls() as the new validation
      scheme should ensure it cannot be incorrect.
    + Use the current eth_type to validate mpls_pop actions and remove
      the eth_type check from pop_mpls().
  - Move OVS_KEY_ATTR_MPLS to non-upstream group in ovs_key_lens
  - Remove unnecessary memset of mpls_key in ovs_flow_to_nlattrs()
  - Make a union of the mpls and ip elements of struct sw_flow_key.
    Currently the code stops parsing after an MPLS header so it is
    not possible for the ip and mpls elements to be used simultaneously
    and some space can be saved by using a union.
  - Allow an array of MPLS key attributes
    + Currently all but the first element is ignored
    + User-space needs to be updated to accept more than one element,
      currently it will treat their presence as an error
  - Do not update network header in ovs_flow_extract() for after parsing
    the MPLS stack as it is never used because no l3+ processing
    occurs on MPLS frames.
  - Allow multiple MPLS entries in a match by allowing the OVS_KEY_ATTR_MPLS
    to be an array of struct ovs_key_mpls with at least one entry.
    Currently only one entry is used which is byte-for-byte compatible with
    the previous scheme of having OVS_KEY_ATTR_MPLS as a struct
    ovs_key_mpls.
* Make skb writable in pop_mpls(), push_mpls() and set_mpls().

v2.18 - v2.19
* No change

v2.17
* As suggested by Ben Pfaff
  - Use consistent terminology for MPLS.
    + Consistently refer to the MPLS component of a packet as the
      MPLS label stack and entries in the stack as MPLS label stack entries
      (LSE).  An MPLS label is a component of an MPLS label stack entry.
      The other components are the traffic class (TC), time to live (TTL)
      and bottom of stack (BoS) bit.
  - Rename compose_.*mpls_ functions as execute_.*mpls_

v2.16
* No change

v2.15
* As suggested by Ben Pfaff
  - Use OVS_ACTION_SET to set OVS_KEY_ATTR_MPLS instead of
    OVS_ACTION_ATTR_SET_MPLS

v2.14
* Remove include/linux/openvswitch.h portion which added add
  new key and action attributes. This
  now present in "User-Space MPLS actions and matches"
  which is now a dependency of this patch

v2.13
* As suggested by Jarno Rajahalme
  - Rename mpls_bos element of ovs_skb_cb as l2_size as it is set and used
    regardless of if an MPLS stack is present or not. Update the name of
    helper functions and documentation accordingly.
  - Ensure that skb_cb_mpls_bos() never returns NULL
* Correct endieness in eth_p_mpls()

v2.12
* Update skb and network header on MPLS extraction in ovs_flow_extract()
* Use NULL in skb_cb_mpls_bos()
* Add eth_p_mpls helper

v2.10 - v2.11
* No change

v2.9
* datapath: Always update the mpls bos if  vlan_pop is successful

  Regardless of the details of how a successful
  vlan_pop is achieved, the mpls bos needs to be updated.

  Without this fix it has been observed that the following
  results in malformed packets

v2.8
* No change

v2.7
* Rebase

v2.6
* As suggested by Yamahata-san
  - Do not guard against label == 0 for
    OVS_ACTION_ATTR_SET_MPLS in validate_actions().
    A label of 0 is valid
  - Remove comment stupulating that if
    the top_label element of struct sw_flow_key is 0 then
    there is no MPLS label. An MPLS label of 0 is valid
    and the correct check if ethertype is
    ntohs(ETH_TYPE_MPLS) or ntohs(ETH_TYPE_MPLS_MCAST)

v2.4 - v2.5
* No change

v2.3
* s/mpls_stack/mpls_bos/
  This is in keeping with the naming used in the OpenFlow 1.3 specification

v2.2
* Call skb_reset_mac_header() in skb_cb_set_mpls_stack()
  eth_hdr(skb) is non-NULL when called in skb_cb_set_mpls_stack().
* Add a call to skb_cb_set_mpls_stack() in ovs_packet_cmd_execute().
  I apologise that I have mislaid my notes on this but
  it avoids a kernel panic. I can investigate again if necessary.
* Use struct ovs_action_push_mpls instead of
  __be16 to decode OVS_ACTION_ATTR_PUSH_MPLS in validate_actions(). This is
  consistent with the data format for the attribute.
* Indentation fix in skb_cb_mpls_stack(). [cosmetic]

v2.1
* Manual rebase
---
 OPENFLOW-1.1+                                   |  12 -
 datapath/Modules.mk                             |   1 +
 datapath/actions.c                              | 131 ++++++++++-
 datapath/datapath.c                             |   4 +-
 datapath/flow.c                                 |  29 +++
 datapath/flow.h                                 |  17 +-
 datapath/flow_netlink.c                         | 286 ++++++++++++++++++++++--
 datapath/flow_netlink.h                         |   2 +-
 datapath/linux/compat/gso.c                     |  70 +++++-
 datapath/linux/compat/gso.h                     |  41 ++++
 datapath/linux/compat/include/linux/netdevice.h |   6 +-
 datapath/linux/compat/netdevice.c               |  10 +-
 datapath/mpls.h                                 |  15 ++
 include/linux/openvswitch.h                     |   7 +-
 14 files changed, 569 insertions(+), 62 deletions(-)
 create mode 100644 datapath/mpls.h

diff --git a/OPENFLOW-1.1+ b/OPENFLOW-1.1+
index e194ba7..6d044db 100644
--- a/OPENFLOW-1.1+
+++ b/OPENFLOW-1.1+
@@ -66,10 +66,6 @@ probably incomplete.
       behavior does not change.
       [required for OF1.1 and OF1.2]
 
-    * MPLS.  Simon Horman maintains a patch series that adds this
-      feature.  This is partially merged.
-      [optional for OF1.1+]
-
     * Match and set double-tagged VLANs (QinQ).  This requires kernel
       work for reasonable performance.
       [optional for OF1.1+]
@@ -138,18 +134,10 @@ didn't compare the specs carefully yet.)
       some kind of "hardware" support, if we judged it useful enough.)
       [optional for OF1.3+]
 
-    * MPLS BoS matching.
-      Part of MPLS patchset by Simon Horman.
-      [optional for OF1.3+]
-
     * Provider Backbone Bridge tagging.  I don't plan to implement
       this (but we'd accept an implementation).
       [optional for OF1.3+]
 
-    * Rework tag order.
-      Part of MPLS patchset by Simon Horman.
-      [required for v1.3+]
-
     * On-demand flow counters.  I think this might be a real
       optimization in some cases for the software switch.
       [optional for OF1.3+]
diff --git a/datapath/Modules.mk b/datapath/Modules.mk
index b652411..6aa80e5 100644
--- a/datapath/Modules.mk
+++ b/datapath/Modules.mk
@@ -26,6 +26,7 @@ openvswitch_headers = \
 	flow.h \
 	flow_netlink.h \
 	flow_table.h \
+	mpls.h \
 	vlan.h \
 	vport.h \
 	vport-internal_dev.h \
diff --git a/datapath/actions.c b/datapath/actions.c
index ee2456b..d47776d 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -35,6 +35,8 @@
 #include <net/sctp/checksum.h>
 
 #include "datapath.h"
+#include "gso.h"
+#include "mpls.h"
 #include "vlan.h"
 #include "vport.h"
 
@@ -71,7 +73,8 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
 
 	vlan_set_encap_proto(skb, vhdr);
 	skb->mac_header += VLAN_HLEN;
-	skb_reset_mac_len(skb);
+	/* Update mac_len for subsequent MPLS actions */
+	skb->mac_len -= VLAN_HLEN;
 
 	return 0;
 }
@@ -113,6 +116,9 @@ static int put_vlan(struct sk_buff *skb)
 	if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
 		return -ENOMEM;
 
+	/* update mac_len for subsequent MPLS actions */
+	skb->mac_len += VLAN_HLEN;
+
 	if (skb->ip_summed == CHECKSUM_COMPLETE)
 		skb->csum = csum_add(skb->csum, csum_partial(skb->data
 				+ (2 * ETH_ALEN), VLAN_HLEN, 0));
@@ -133,6 +139,114 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla
 	return 0;
 }
 
+/* The end of the mac header.
+ *
+ * For non-MPLS skbs this will correspond to the network header.
+ * For MPLS skbs it will be before the network_header as the MPLS
+ * label stack lies between the end of the mac header and the network
+ * header. That is, for MPLS skbs the end of the mac header
+ * is the top of the MPLS label stack.
+ */
+static unsigned char *mac_header_end(const struct sk_buff *skb)
+{
+	return skb_mac_header(skb) + skb->mac_len;
+}
+
+/* Push MPLS after the ethernet header. */
+static int push_mpls(struct sk_buff *skb,
+		     const struct ovs_action_push_mpls *mpls)
+{
+	__be32 *new_mpls_lse;
+	struct ethhdr *hdr;
+
+	if (unlikely(vlan_tx_tag_present(skb))) {
+		int err;
+
+		err = put_vlan(skb);
+		if (unlikely(err))
+			return err;
+
+	        vlan_set_tci(skb, 0);
+	}
+
+	if (skb_cow_head(skb, MPLS_HLEN) < 0) {
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+	skb_push(skb, MPLS_HLEN);
+
+	memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb),
+		ETH_HLEN);
+	skb_reset_mac_header(skb);
+
+	new_mpls_lse = (__be32 *)(skb_mac_header(skb) + ETH_HLEN);
+	*new_mpls_lse = mpls->mpls_lse;
+
+	if (skb->ip_summed == CHECKSUM_COMPLETE)
+		skb->csum = csum_add(skb->csum, csum_partial(new_mpls_lse,
+							     MPLS_HLEN, 0));
+
+	hdr = eth_hdr(skb);
+	hdr->h_proto = mpls->mpls_ethertype;
+	if (!ovs_skb_get_inner_protocol(skb))
+		ovs_skb_set_inner_protocol(skb, skb->protocol);
+	skb->protocol = mpls->mpls_ethertype;
+	return 0;
+}
+
+static int pop_mpls(struct sk_buff *skb, const __be16 ethertype)
+{
+	struct ethhdr *hdr;
+	int err;
+
+	err = make_writable(skb, skb->mac_len + MPLS_HLEN);
+	if (unlikely(err))
+		return err;
+
+	if (unlikely(skb->len < skb->mac_len + MPLS_HLEN))
+		return -EINVAL;
+
+	if (skb->ip_summed == CHECKSUM_COMPLETE)
+		skb->csum = csum_sub(skb->csum,
+				     csum_partial(mac_header_end(skb),
+						  MPLS_HLEN, 0));
+
+	memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
+		skb->mac_len);
+
+	__skb_pull(skb, MPLS_HLEN);
+	skb_reset_mac_header(skb);
+
+	/* mac_header_end() is used to locate the ethertype
+	 * field correctly in the presence of VLAN tags.
+	 */
+	hdr = (struct ethhdr *)(mac_header_end(skb) - ETH_HLEN);
+	hdr->h_proto = ethertype;
+	if (eth_p_mpls(skb->protocol))
+		skb->protocol = ethertype;
+	return 0;
+}
+
+static int set_mpls(struct sk_buff *skb, const __be32 *mpls_lse)
+{
+	__be32 *stack = (__be32 *)mac_header_end(skb);
+	int err;
+
+	err = make_writable(skb, skb->mac_len + MPLS_HLEN);
+	if (unlikely(err))
+		return err;
+
+	if (skb->ip_summed == CHECKSUM_COMPLETE) {
+		__be32 diff[] = { ~(*stack), *mpls_lse };
+		skb->csum = ~csum_partial((char *)diff, sizeof(diff),
+					  ~skb->csum);
+	}
+
+	*stack = *mpls_lse;
+
+	return 0;
+}
+
 static int set_eth_addr(struct sk_buff *skb,
 			const struct ovs_key_ethernet *eth_key)
 {
@@ -508,6 +622,9 @@ static int execute_set_action(struct sk_buff *skb,
 
 	case OVS_KEY_ATTR_SCTP:
 		err = set_sctp(skb, nla_data(nested_attr));
+
+	case OVS_KEY_ATTR_MPLS:
+		err = set_mpls(skb, nla_data(nested_attr));
 		break;
 	}
 
@@ -544,6 +661,16 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 			output_userspace(dp, skb, a);
 			break;
 
+		case OVS_ACTION_ATTR_PUSH_MPLS:
+			err = push_mpls(skb, nla_data(a));
+			if (unlikely(err)) /* skb already freed. */
+				return err;
+			break;
+
+		case OVS_ACTION_ATTR_POP_MPLS:
+			err = pop_mpls(skb, nla_get_be16(a));
+			break;
+
 		case OVS_ACTION_ATTR_PUSH_VLAN:
 			err = push_vlan(skb, nla_data(a));
 			if (unlikely(err)) /* skb already freed. */
@@ -617,6 +744,8 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb)
 		goto out_loop;
 	}
 
+	ovs_skb_init_inner_protocol(skb);
+
 	OVS_CB(skb)->tun_key = NULL;
 	error = do_execute_actions(dp, skb, acts->actions,
 					 acts->actions_len, false);
diff --git a/datapath/datapath.c b/datapath/datapath.c
index d0a8595..d558abb 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -512,7 +512,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 		goto err_flow_free;
 
 	err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS],
-				   &flow->key, 0, &acts);
+				   &flow->key, &acts);
 	rcu_assign_pointer(flow->sf_acts, acts);
 	if (err)
 		goto err_flow_free;
@@ -778,7 +778,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 
 		ovs_flow_mask_key(&masked_key, &key, &mask);
 		error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
-					     &masked_key, 0, &acts);
+					     &masked_key, &acts);
 		if (error) {
 			OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
 			goto err_kfree;
diff --git a/datapath/flow.c b/datapath/flow.c
index 57eb6b5..9817341 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -45,6 +45,7 @@
 #include <net/ipv6.h>
 #include <net/ndisc.h>
 
+#include "mpls.h"
 #include "vlan.h"
 
 u64 ovs_flow_used_time(unsigned long flow_jiffies)
@@ -443,6 +444,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
 		return -ENOMEM;
 
 	skb_reset_network_header(skb);
+	skb_reset_mac_len(skb);
 	__skb_push(skb, skb->data - skb_mac_header(skb));
 
 	/* Network layer. */
@@ -526,6 +528,33 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
 			memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
 			memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
 		}
+	} else if (eth_p_mpls(key->eth.type)) {
+		size_t stack_len = MPLS_HLEN;
+
+		/* In the presence of an MPLS label stack the end of the L2
+		 * header and the beginning of the L3 header differ.
+		 *
+		 * Advance network_header to the beginning of the L3
+		 * header. mac_len corresponds to the end of the L2 header.
+		 */
+		while (1) {
+			__be32 lse;
+
+			error = check_header(skb, skb->mac_len + stack_len);
+			if (unlikely(error))
+				return 0;
+
+			memcpy(&lse, skb_network_header(skb), MPLS_HLEN);
+
+			if (stack_len == MPLS_HLEN)
+				memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN);
+
+			skb_set_network_header(skb, skb->mac_len + stack_len);
+			if (lse & htonl(MPLS_BOS_MASK))
+				break;
+
+			stack_len += MPLS_HLEN;
+		}
 	} else if (key->eth.type == htons(ETH_P_IPV6)) {
 		int nh_len;             /* IPv6 Header + Extensions */
 
diff --git a/datapath/flow.h b/datapath/flow.h
index 6b68cf1..6aaf55d 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -80,12 +80,17 @@ struct sw_flow_key {
 		__be16 tci;		/* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
 		__be16 type;		/* Ethernet frame type. */
 	} eth;
-	struct {
-		u8     proto;		/* IP protocol or lower 8 bits of ARP opcode. */
-		u8     tos;		/* IP ToS. */
-		u8     ttl;		/* IP TTL/hop limit. */
-		u8     frag;		/* One of OVS_FRAG_TYPE_*. */
-	} ip;
+	union {
+		struct {
+			__be32 top_lse;		/* top label stack entry */
+		} mpls;
+		struct {
+			u8     proto;		/* IP protocol or lower 8 bits of ARP opcode. */
+			u8     tos;		/* IP ToS. */
+			u8     ttl;		/* IP TTL/hop limit. */
+			u8     frag;		/* One of OVS_FRAG_TYPE_*. */
+		} ip;
+	};
 	union {
 		struct {
 			struct {
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 75c72b3..cf1f9fe 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -18,6 +18,7 @@
 
 #include "flow.h"
 #include "datapath.h"
+#include "mpls.h"
 #include <linux/uaccess.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -120,7 +121,8 @@ static bool match_validate(const struct sw_flow_match *match,
 			| (1ULL << OVS_KEY_ATTR_ICMP)
 			| (1ULL << OVS_KEY_ATTR_ICMPV6)
 			| (1ULL << OVS_KEY_ATTR_ARP)
-			| (1ULL << OVS_KEY_ATTR_ND));
+			| (1ULL << OVS_KEY_ATTR_ND)
+			| (1ULL << OVS_KEY_ATTR_MPLS));
 
 	/* Always allowed mask fields. */
 	mask_allowed |= ((1ULL << OVS_KEY_ATTR_TUNNEL)
@@ -135,6 +137,13 @@ static bool match_validate(const struct sw_flow_match *match,
 			mask_allowed |= 1ULL << OVS_KEY_ATTR_ARP;
 	}
 
+
+	if (eth_p_mpls(match->key->eth.type)) {
+		key_expected |= 1ULL << OVS_KEY_ATTR_MPLS;
+		if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+			mask_allowed |= 1ULL << OVS_KEY_ATTR_MPLS;
+	}
+
 	if (match->key->eth.type == htons(ETH_P_IP)) {
 		key_expected |= 1ULL << OVS_KEY_ATTR_IPV4;
 		if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
@@ -250,6 +259,7 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
 	[OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
 	[OVS_KEY_ATTR_TUNNEL] = -1,
+	[OVS_KEY_ATTR_MPLS] = sizeof(struct ovs_key_mpls),
 };
 
 static bool is_all_zero(const u8 *fp, size_t size)
@@ -624,6 +634,16 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
 		attrs &= ~(1ULL << OVS_KEY_ATTR_ARP);
 	}
 
+	if (attrs & (1ULL << OVS_KEY_ATTR_MPLS)) {
+		const struct ovs_key_mpls *mpls_key;
+
+		mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]);
+		SW_FLOW_KEY_PUT(match, mpls.top_lse,
+				mpls_key->mpls_lse, is_mask);
+
+		attrs &= ~(1ULL << OVS_KEY_ATTR_MPLS);
+	 }
+
 	if (attrs & (1ULL << OVS_KEY_ATTR_TCP)) {
 		const struct ovs_key_tcp *tcp_key;
 
@@ -1009,6 +1029,14 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
 		arp_key->arp_op = htons(output->ip.proto);
 		memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN);
 		memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN);
+	} else if (eth_p_mpls(swkey->eth.type)) {
+		struct ovs_key_mpls *mpls_key;
+
+		nla = nla_reserve(skb, OVS_KEY_ATTR_MPLS, sizeof(*mpls_key));
+		if (!nla)
+			goto nla_put_failure;
+		mpls_key = nla_data(nla);
+		mpls_key->mpls_lse = output->mpls.top_lse;
 	}
 
 	if ((swkey->eth.type == htons(ETH_P_IP) ||
@@ -1217,15 +1245,133 @@ static inline void add_nested_action_end(struct sw_flow_actions *sfa,
 
 	a->nla_len = sfa->actions_len - st_offset;
 }
+#define MAX_ETH_TYPES 16 /* Arbitrary Limit */
+
+/* struct eth_types - possible eth types
+ * @types: provides storage for the possible eth types.
+ * @start: is the index of the first entry of types which is possible.
+ * @end: is the index of the last entry of types which is possible.
+ * @cursor: is the index of the entry which should be updated if an action
+ * changes the eth type.
+ *
+ * Due to the sample action there may be multiple possible eth types.
+ * In order to correctly validate actions all possible types are tracked
+ * and verified. This is done using struct eth_types.
+ *
+ * Initially start, end and cursor should be 0, and the first element of
+ * types should be set to the eth type of the flow.
+ *
+ * When an action changes the eth type then the values of start and end are
+ * updated to the value of cursor. The new type is stored at types[cursor].
+ *
+ * When entering a sample action the start and cursor values are saved. The
+ * value of cursor is set to the value of end plus one.
+ *
+ * When leaving a sample action the start and cursor values are restored to
+ * their saved values.
+ *
+ * An example follows.
+ *
+ * actions: pop_mpls(A),sample(pop_mpls(B)),sample(pop_mpls(C)),pop_mpls(D)
+ *
+ * 0. Initial state:
+ *	types = { original_eth_type }
+ * 	start = end = cursor = 0;
+ *
+ * 1. pop_mpls(A)
+ *    a. Check types from start (0) to end (0) inclusive
+ *       i.e. Check against original_eth_type
+ *    b. Set start = end = cursor
+ *    c. Set types[cursor] = A
+ *    New state:
+ *	types = { A }
+ *	start = end = cursor = 0;
+ *
+ * 2. Enter first sample()
+ *    a. Save start and cursor
+ *    b. Set cursor = end + 1
+ *    New state:
+ *	types = { A }
+ *	start = end = 0;
+ *	cursor = 1;
+ *
+ * 3. pop_mpls(B)
+ *    a. Check types from start (0) to end (0)
+ *       i.e: Check against A
+ *    b. Set start = end = cursor
+ *    c. Set types[cursor] = B
+ *    New state:
+ *	types = { A, B }
+ *	start = end = cursor = 1;
+ *
+ * 4. Leave first sample()
+ *    a. Restore start and cursor to the values when entering 2.
+ *    New state:
+ *	types = { A, B }
+ *	start = cursor = 0;
+ *	end = 1;
+ *
+ * 5. Enter second sample()
+ *    a. Save start and cursor
+ *    b. Set cursor = end + 1
+ *    New state:
+ *	types = { A, B }
+ *	start = 0;
+ *	end = 1;
+ *	cursor = 2;
+ *
+ * 6. pop_mpls(C)
+ *    a. Check types from start (0) to end (1) inclusive
+ *       i.e: Check against A and B
+ *    b. Set start = end = cursor
+ *    c. Set types[cursor] = C
+ *    New state:
+ *	types = { A, B, C }
+ *	start = end = cursor = 2;
+ *
+ * 7. Leave second sample()
+ *    a. Restore start and cursor to the values when entering 5.
+ *    New state:
+ *	types = { A, B, C }
+ *	start = cursor = 0;
+ *	end = 2;
+ *
+ * 8. pop_mpls(D)
+ *    a. Check types from start (0) to end (2) inclusive
+ *       i.e: Check against A, B and C
+ *    b. Set start = end = cursor
+ *    c. Set types[cursor] = D
+ *    New state:
+ *	types = { D } // Trailing entries of type are no longer used end = 0
+ *	start = end = cursor = 0;
+ */
+struct eth_types {
+	int start, end, cursor;
+	__be16 types[MAX_ETH_TYPES];
+};
+
+static void eth_types_set(struct eth_types *types, __be16 type)
+{
+	types->start = types->end = types->cursor;
+	types->types[types->cursor] = type;
+}
+
+static int ovs_nla_copy_actions__(const struct nlattr *attr,
+				  const struct sw_flow_key *key,
+				  int depth,
+				  struct sw_flow_actions **sfa,
+				  struct eth_types *eth_types);
 
 static int validate_and_copy_sample(const struct nlattr *attr,
 				    const struct sw_flow_key *key, int depth,
-				    struct sw_flow_actions **sfa)
+				    struct sw_flow_actions **sfa,
+				    struct eth_types *eth_types)
 {
 	const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
 	const struct nlattr *probability, *actions;
 	const struct nlattr *a;
 	int rem, start, err, st_acts;
+	int saved_eth_types_start, saved_eth_types_cursor;
 
 	memset(attrs, 0, sizeof(attrs));
 	nla_for_each_nested(a, attr, rem) {
@@ -1257,22 +1403,38 @@ static int validate_and_copy_sample(const struct nlattr *attr,
 	if (st_acts < 0)
 		return st_acts;
 
-	err = ovs_nla_copy_actions(actions, key, depth + 1, sfa);
+	/* Save and update eth_types cursor and start.  Please see the
+	 * comment for struct eth_types for a discussion of this.
+	 */
+	saved_eth_types_start = eth_types->start;
+	saved_eth_types_cursor = eth_types->cursor;
+	eth_types->cursor = eth_types->end + 1;
+	if (eth_types->cursor == MAX_ETH_TYPES)
+		return -EINVAL;
+
+	err = ovs_nla_copy_actions__(actions, key, depth + 1, sfa, eth_types);
 	if (err)
 		return err;
 
+	/* Restore eth_types cursor and start.  Please see the
+	 * comment for struct eth_types for a discussion of this.
+	 */
+	eth_types->cursor = saved_eth_types_cursor;
+	eth_types->start = saved_eth_types_start;
+
 	add_nested_action_end(*sfa, st_acts);
 	add_nested_action_end(*sfa, start);
 
 	return 0;
 }
 
-static int validate_tp_port(const struct sw_flow_key *flow_key)
+static int validate_tp_port__(const struct sw_flow_key *flow_key,
+			      __be16 eth_type)
 {
-	if (flow_key->eth.type == htons(ETH_P_IP)) {
+	if (eth_type == htons(ETH_P_IP)) {
 		if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
 			return 0;
-	} else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
+	} else  if (eth_type == htons(ETH_P_IPV6)) {
 		if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
 			return 0;
 	}
@@ -1280,6 +1442,21 @@ static int validate_tp_port(const struct sw_flow_key *flow_key)
 	return -EINVAL;
 }
 
+static int validate_tp_port(const struct sw_flow_key *flow_key,
+                           const struct eth_types *eth_types)
+{
+	int i;
+
+	for (i = eth_types->start; i < eth_types->end; i++) {
+		int ret = validate_tp_port__(flow_key, eth_types->types[i]);
+
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 void ovs_match_init(struct sw_flow_match *match,
 		    struct sw_flow_key *key,
 		    struct sw_flow_mask *mask)
@@ -1322,7 +1499,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
 static int validate_set(const struct nlattr *a,
 			const struct sw_flow_key *flow_key,
 			struct sw_flow_actions **sfa,
-			bool *set_tun)
+			bool *set_tun, struct eth_types *eth_types)
 {
 	const struct nlattr *ovs_key = nla_data(a);
 	int key_type = nla_type(ovs_key);
@@ -1353,9 +1530,12 @@ static int validate_set(const struct nlattr *a,
 			return err;
 		break;
 
-	case OVS_KEY_ATTR_IPV4:
-		if (flow_key->eth.type != htons(ETH_P_IP))
-			return -EINVAL;
+	case OVS_KEY_ATTR_IPV4: {
+		int i;
+
+		for (i = eth_types->start; i <= eth_types->end; i++)
+			if (eth_types->types[i] != htons(ETH_P_IP))
+				return -EINVAL;
 
 		if (!flow_key->ip.proto)
 			return -EINVAL;
@@ -1368,10 +1548,14 @@ static int validate_set(const struct nlattr *a,
 			return -EINVAL;
 
 		break;
+	}
 
-	case OVS_KEY_ATTR_IPV6:
-		if (flow_key->eth.type != htons(ETH_P_IPV6))
-			return -EINVAL;
+	case OVS_KEY_ATTR_IPV6: {
+		int i;
+
+		for (i = eth_types->start; i <= eth_types->end; i++)
+			if (eth_types->types[i] != htons(ETH_P_IPV6))
+				return -EINVAL;
 
 		if (!flow_key->ip.proto)
 			return -EINVAL;
@@ -1387,24 +1571,35 @@ static int validate_set(const struct nlattr *a,
 			return -EINVAL;
 
 		break;
+	}
+
 
 	case OVS_KEY_ATTR_TCP:
 		if (flow_key->ip.proto != IPPROTO_TCP)
 			return -EINVAL;
 
-		return validate_tp_port(flow_key);
+		return validate_tp_port(flow_key, eth_types);
 
 	case OVS_KEY_ATTR_UDP:
 		if (flow_key->ip.proto != IPPROTO_UDP)
 			return -EINVAL;
 
-		return validate_tp_port(flow_key);
+		return validate_tp_port(flow_key, eth_types);
+
+	case OVS_KEY_ATTR_MPLS: {
+		int i;
+
+		for (i = eth_types->start; i < eth_types->end; i++)
+			if (!eth_p_mpls(eth_types->types[i]))
+				return -EINVAL;
+		break;
+	}
 
 	case OVS_KEY_ATTR_SCTP:
 		if (flow_key->ip.proto != IPPROTO_SCTP)
 			return -EINVAL;
 
-		return validate_tp_port(flow_key);
+		return validate_tp_port(flow_key, eth_types);
 
 	default:
 		return -EINVAL;
@@ -1448,10 +1643,11 @@ static int copy_action(const struct nlattr *from,
 	return 0;
 }
 
-int ovs_nla_copy_actions(const struct nlattr *attr,
-			 const struct sw_flow_key *key,
-			 int depth,
-			 struct sw_flow_actions **sfa)
+static int ovs_nla_copy_actions__(const struct nlattr *attr,
+				  const struct sw_flow_key *key,
+				  int depth,
+				  struct sw_flow_actions **sfa,
+				  struct eth_types *eth_types)
 {
 	const struct nlattr *a;
 	int rem, err;
@@ -1464,6 +1660,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
 		static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
 			[OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
 			[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
+			[OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls),
+			[OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16),
 			[OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
 			[OVS_ACTION_ATTR_POP_VLAN] = 0,
 			[OVS_ACTION_ATTR_SET] = (u32)-1,
@@ -1506,14 +1704,44 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
 				return -EINVAL;
 			break;
 
+		case OVS_ACTION_ATTR_PUSH_MPLS: {
+			const struct ovs_action_push_mpls *mpls = nla_data(a);
+			if (!eth_p_mpls(mpls->mpls_ethertype))
+				return -EINVAL;
+			eth_types_set(eth_types, mpls->mpls_ethertype);
+			break;
+		}
+
+		case OVS_ACTION_ATTR_POP_MPLS: {
+			int i;
+
+			for (i = eth_types->start; i <= eth_types->end; i++)
+				if (!eth_p_mpls(eth_types->types[i]))
+					return -EINVAL;
+
+			/* Disallow subsequent L2.5+ set and mpls_pop actions
+			 * as there is no check here to ensure that the new
+			 * eth_type is valid and thus set actions could
+			 * write off the end of the packet or otherwise
+			 * corrupt it.
+			 *
+			 * Support for these actions is planned using packet
+			 * recirculation.
+			 */
+			eth_types_set(eth_types, htons(0));
+			break;
+		}
+
 		case OVS_ACTION_ATTR_SET:
-			err = validate_set(a, key, sfa, &skip_copy);
+			err = validate_set(a, key, sfa, &skip_copy,
+					   eth_types);
 			if (err)
 				return err;
 			break;
 
 		case OVS_ACTION_ATTR_SAMPLE:
-			err = validate_and_copy_sample(a, key, depth, sfa);
+			err = validate_and_copy_sample(a, key, depth, sfa,
+						       eth_types);
 			if (err)
 				return err;
 			skip_copy = true;
@@ -1535,6 +1763,20 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
 	return 0;
 }
 
+int ovs_nla_copy_actions(const struct nlattr *attr,
+			 const struct sw_flow_key *key,
+			 struct sw_flow_actions **sfa)
+{
+	struct eth_types eth_type = {
+		.start = 0,
+		.end = 0,
+		.cursor = 0,
+		.types = { key->eth.type, },
+	};
+
+	return ovs_nla_copy_actions__(attr, key, 0, sfa, &eth_type);
+}
+
 static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
 {
 	const struct nlattr *a;
diff --git a/datapath/flow_netlink.h b/datapath/flow_netlink.h
index 4401510..b471ece 100644
--- a/datapath/flow_netlink.h
+++ b/datapath/flow_netlink.h
@@ -49,7 +49,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
 		      const struct nlattr *);
 
 int ovs_nla_copy_actions(const struct nlattr *attr,
-			 const struct sw_flow_key *key, int depth,
+			 const struct sw_flow_key *key,
 			 struct sw_flow_actions **sfa);
 int ovs_nla_put_actions(const struct nlattr *attr,
 			int len, struct sk_buff *skb);
diff --git a/datapath/linux/compat/gso.c b/datapath/linux/compat/gso.c
index 32f906c..43f0d16 100644
--- a/datapath/linux/compat/gso.c
+++ b/datapath/linux/compat/gso.c
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/if.h>
 #include <linux/if_tunnel.h>
+#include <linux/if_vlan.h>
 #include <linux/icmp.h>
 #include <linux/in.h>
 #include <linux/ip.h>
@@ -35,6 +36,8 @@
 #include <net/xfrm.h>
 
 #include "gso.h"
+#include "mpls.h"
+#include "vlan.h"
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \
 	!defined(HAVE_VLAN_BUG_WORKAROUND)
@@ -47,10 +50,12 @@ MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
 #define vlan_tso true
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
 static bool dev_supports_vlan_tx(struct net_device *dev)
 {
-#if defined(HAVE_VLAN_BUG_WORKAROUND)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+	return true;
+#elif defined(HAVE_VLAN_BUG_WORKAROUND)
 	return dev->features & NETIF_F_HW_VLAN_TX;
 #else
 	/* Assume that the driver is buggy. */
@@ -58,24 +63,64 @@ static bool dev_supports_vlan_tx(struct net_device *dev)
 #endif
 }
 
+/* Strictly this is not needed and will be optimised out
+ * as this code is guarded by if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0).
+ * It is here to make things explicit should the compatibility
+ * code be extended in some way prior extending its life-span
+ * beyond v3.11.
+ */
+static bool supports_mpls_gso(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+	return true;
+#else
+	return false;
+#endif
+}
+
 int rpl_dev_queue_xmit(struct sk_buff *skb)
 {
 #undef dev_queue_xmit
 	int err = -ENOMEM;
+	bool vlan, mpls;
+
+	vlan = mpls = false;
+
+	if (eth_p_mpls(skb->protocol) && !supports_mpls_gso())
+		mpls = true;
+
+	if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev))
+		vlan = true;
 
-	if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) {
+	if (vlan || mpls) {
 		int features;
 
 		features = netif_skb_features(skb);
 
-		if (!vlan_tso)
-			features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
-				      NETIF_F_UFO | NETIF_F_FSO);
+		if (vlan) {
+			if (!vlan_tso)
+				features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
+					      NETIF_F_UFO | NETIF_F_FSO);
 
-		skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
-		if (unlikely(!skb))
-			return err;
-		vlan_set_tci(skb, 0);
+			skb = __vlan_put_tag(skb, skb->vlan_proto,
+					     vlan_tx_tag_get(skb));
+			if (unlikely(!skb))
+				return err;
+			vlan_set_tci(skb, 0);
+		}
+
+		/* As of v3.11 the kernel provides an mpls_features field in
+		 * struct net_device which allows devices to advertise which
+		 * features its supports for MPLS. This value defaults to
+		 * NETIF_F_SG and as of v3.11.
+		 *
+		 * This compatibility code is intended for kernels older
+		 * than v3.11 that do not support MPLS GSO and thus do not
+		 * provide mpls_features. Thus this code uses NETIF_F_SG
+		 * directly in place of mpls_features.
+		 */
+		if (mpls)
+			features &= NETIF_F_SG;
 
 		if (netif_needs_gso(skb, features)) {
 			struct sk_buff *nskb;
@@ -114,13 +159,15 @@ drop:
 	kfree_skb(skb);
 	return err;
 }
-#endif /* kernel version < 2.6.37 */
 
 static __be16 __skb_network_protocol(struct sk_buff *skb)
 {
 	__be16 type = skb->protocol;
 	int vlan_depth = ETH_HLEN;
 
+	if (eth_p_mpls(skb->protocol))
+		type = ovs_skb_get_inner_protocol(skb);
+
 	while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
 		struct vlan_hdr *vh;
 
@@ -134,6 +181,7 @@ static __be16 __skb_network_protocol(struct sk_buff *skb)
 
 	return type;
 }
+#endif /* kernel version < 3.11.0 */
 
 static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb,
 					   netdev_features_t features,
diff --git a/datapath/linux/compat/gso.h b/datapath/linux/compat/gso.h
index 44fd213..d7a9cea 100644
--- a/datapath/linux/compat/gso.h
+++ b/datapath/linux/compat/gso.h
@@ -1,6 +1,7 @@
 #ifndef __LINUX_GSO_WRAPPER_H
 #define __LINUX_GSO_WRAPPER_H
 
+#include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <net/protocol.h>
 
@@ -11,6 +12,9 @@ struct ovs_gso_cb {
 	sk_buff_data_t	inner_network_header;
 	sk_buff_data_t	inner_mac_header;
 	void (*fix_segment)(struct sk_buff *);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+	__be16			inner_protocol;
+#endif
 };
 #define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb)
 
@@ -69,4 +73,41 @@ static inline void skb_reset_inner_headers(struct sk_buff *skb)
 
 #define ip_local_out rpl_ip_local_out
 int ip_local_out(struct sk_buff *skb);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
+	OVS_GSO_CB(skb)->inner_protocol = htons(0);
+}
+
+static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
+					      __be16 ethertype) {
+	OVS_GSO_CB(skb)->inner_protocol = ethertype;
+}
+
+static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
+{
+	return OVS_GSO_CB(skb)->inner_protocol;
+}
+
+#else
+
+static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
+	/* Nothing to do. The inner_protocol is either zero or
+	 * has been set to a value by another user.
+	 * Either way it may be considered initialised.
+	 */
+}
+
+static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
+					      __be16 ethertype)
+{
+	skb->inner_protocol = ethertype;
+}
+
+static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
+{
+	return skb->inner_protocol;
+}
+#endif
+
 #endif
diff --git a/datapath/linux/compat/include/linux/netdevice.h b/datapath/linux/compat/include/linux/netdevice.h
index b303f39..4987d1c 100644
--- a/datapath/linux/compat/include/linux/netdevice.h
+++ b/datapath/linux/compat/include/linux/netdevice.h
@@ -60,10 +60,12 @@ static inline struct net_device *dev_get_by_index_rcu(struct net *net, int ifind
 #define NETIF_F_FSO 0
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
 #define skb_gso_segment rpl_skb_gso_segment
 struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features);
+#endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 #define netif_skb_features rpl_netif_skb_features
 u32 rpl_netif_skb_features(struct sk_buff *skb);
 
@@ -112,7 +114,7 @@ static inline struct net_device *netdev_master_upper_dev_get(struct net_device *
 }
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
 #define dev_queue_xmit rpl_dev_queue_xmit
 int dev_queue_xmit(struct sk_buff *skb);
 #endif
diff --git a/datapath/linux/compat/netdevice.c b/datapath/linux/compat/netdevice.c
index 3d28a9b..e9efa7d 100644
--- a/datapath/linux/compat/netdevice.c
+++ b/datapath/linux/compat/netdevice.c
@@ -1,6 +1,9 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
 
+#include "mpls.h"
+#include "gso.h"
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 #ifndef HAVE_CAN_CHECKSUM_PROTOCOL
 static bool can_checksum_protocol(unsigned long features, __be16 protocol)
@@ -67,7 +70,9 @@ u32 rpl_netif_skb_features(struct sk_buff *skb)
 		return harmonize_features(skb, protocol, features);
 	}
 }
+#endif	/* kernel version < 2.6.38 */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
 struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features)
 {
 	int vlan_depth = ETH_HLEN;
@@ -75,6 +80,9 @@ struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features)
 	__be16 skb_proto;
 	struct sk_buff *skb_gso;
 
+	if (eth_p_mpls(skb->protocol))
+		type = ovs_skb_get_inner_protocol(skb);
+
 	while (type == htons(ETH_P_8021Q)) {
 		struct vlan_hdr *vh;
 
@@ -95,4 +103,4 @@ struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, u32 features)
 	skb->protocol = skb_proto;
 	return skb_gso;
 }
-#endif	/* kernel version < 2.6.38 */
+#endif	/* kernel version < 3.11.0 */
diff --git a/datapath/mpls.h b/datapath/mpls.h
new file mode 100644
index 0000000..7eab104
--- /dev/null
+++ b/datapath/mpls.h
@@ -0,0 +1,15 @@
+#ifndef MPLS_H
+#define MPLS_H 1
+
+#include <linux/if_ether.h>
+
+#define MPLS_BOS_MASK	0x00000100
+#define MPLS_HLEN 4
+
+static inline bool eth_p_mpls(__be16 eth_type)
+{
+	return eth_type == htons(ETH_P_MPLS_UC) ||
+		eth_type == htons(ETH_P_MPLS_MC);
+}
+
+#endif
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index b429201..63ab2fa 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -295,14 +295,13 @@ enum ovs_key_attr {
 	OVS_KEY_ATTR_TUNNEL,	/* Nested set of ovs_tunnel attributes */
 	OVS_KEY_ATTR_SCTP,      /* struct ovs_key_sctp */
 	OVS_KEY_ATTR_TCP_FLAGS,	/* be16 TCP flags. */
+	OVS_KEY_ATTR_MPLS,      /* array of struct ovs_key_mpls.
+				 * The implementation may restrict
+				 * the accepted length of the array. */
 
 #ifdef __KERNEL__
 	OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
 #endif
-
-	OVS_KEY_ATTR_MPLS = 62, /* array of struct ovs_key_mpls.
-				 * The implementation may restrict
-				 * the accepted length of the array. */
 	__OVS_KEY_ATTR_MAX
 };
 
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches
  2013-11-21  3:46 [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
                   ` (2 preceding siblings ...)
  2013-11-21  3:46 ` [PATCH v2.51 5/5] datapath: Add basic MPLS support to kernel Simon Horman
@ 2013-11-26  8:08 ` Simon Horman
  2013-11-26 15:33   ` Ben Pfaff
  3 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-11-26  8:08 UTC (permalink / raw)
  To: dev, netdev, Jesse Gross, Ben Pfaff; +Cc: Ravi K

On Thu, Nov 21, 2013 at 12:46:41PM +0900, Simon Horman wrote:
> Hi,
> 
> This series implements MPLS actions and matches based on work by
> Ravi K, Leo Alterman, Yamahata-san and Joe Stringer.
> 
> This series provides three changes
> 
> * Patches 1 - 3
> 
>   Provide user-space support for the VLAN/MPLS tag insertion order
>   up to and including OpenFlow 1.2, and the different ordering
>   specified from OpenFlow 1.3. In a nutshell the datapath always
>   uses the OpenFlow 1.3 ordering, which is to always insert tags
>   immediately after the L2 header, regardless of the presence of other
>   tags. And ovs-vswtichd provides compatibility for the behaviour up
>   to OpenFlow 1.2, which is that MPLS tags should follow VLAN tags
>   if present.
> 
>   Ben, these are for you to review.

Hi Ben,

I wonder if you could find some time to look over these.

I believe that the first patch has not previously been reviewed
but other than that there should be few areas of excitement.

> 
> * Patches 4 and 5
> 
>   Adding basic MPLS action and match support to the kernel datapath
> 
>   Jesse, these are for you to review after patches 1 - 4 are reviewed.
> 
> 
> Difference between v2.51 and v2.50:
> 
> * New approach to consistency checking actions using of OpenFlow1.3+ tag order
> * Use OF1.2 for all ovs-ofctl commands for new OF1.2 tests. Likewise for OF1.3.
> * Add write_actions check for OF1.3.
>   This further exercises consistency checking using OpenFlow1.3+ tag order.
> 
> 
> Difference between v2.50 and v2.49:
> 
> * Correct typo in comment
> 
> 
> Difference between v2.49 and v2.48:
> 
> * Include action consistency checking changes.
> 
> 
> Difference between v2.48 and v2.47:
> * Manual rebase for
>   - "OF 1.1 set vlan vid/pcp compatibility"
>   - "Native Set-Field action"
> * Only use OpenFlow1.2 actions in OpenFlow1.2 tests
> 
> 
> Difference between v2.47 and v2.46:
> 
> * Rebase of patch 4 for HAVE_RHEL_OVS_HOOK and OVS_KEY_ATTR_TCP_FLAGS
> 
> 
> Difference between v2.46 and v2.45:
> 
> * Update changelog of "odp: Allow VLAN actions after MPLS actions"
>   to reflect the current implementation
> 
> 
> Difference between v2.45 and v2.44:
> 
> * As pointed out by Ben Pfaff and Joe Stringer
>   + Update VLAN handling in the presence of MPLS push
> 
>     Previously the test for committing ODP VLAN actions after MPLS actions
>     was that the VLAN TCI differed before and after the MPLS push action.
>     This results in a false negative in some cases including if a VLAN tag
>     is pushed after the MPLS push action in such a way that it duplicates
>     the VLAN tag present before the MPLS push action.
> 
>     This is resolved by ensuring the VLAN_CFI bit of the value used to
>     track the desired VLAN TCI after an MPLS push action is zero unless
>     VLAN actions should be committed after MPLS actions.
> 
>   + Update tests to use ovs-ofctl monitor "-m" to allow full inspection of
>     MPLS LSE and VLAN tags present in packets.
> 
> Differences between v2.44 and v2.43:
> 
> * Rebase for the following changes:
>   f47ea02 ("Set datapath mask bits when setting a flow field.")
>   7fdb60a ("Add support for write-actions")
>   7358063 ("odp-util: Elaborate the comment for odp_flow_format() function.")
> * Correct final_vlan_tci and next_vlan_tci initialisation in xlate_actions__()
> 
> 
> Differences between v2.43 and v2.42:
> 
> * As suggested by Ben Pfaff
>   Move vlan state into struct xlate_ctx
>   1. Add final_vlan_tci member to struct xlate_ctx instead of vlan_tci member
>      struct xlate_xin.  This seems to be a better palace for it as it does
>      not need to be accessible from the caller.
>   2. Move local vlan_tci variable of do_xlate_actions() to be the
>      next_vlan_tci member of strict xlate_ctx.  This allows for it to work
>      across resubmit actions and goto table instructions.
> * Code contributed by Ben Pfaff
>   + Use enum for to control order of MPLS LSE insertion
>     This makes the logic somewhat clearer
> * Add a helper push_mpls_from_openflow() to consolidate
>   the same logic that appears in three locations.
> 
> 
> Differences between v2.42 and v2.41:
> 
> * Rebase for:
>   + 0585f7a ("datapath: Simplify mega-flow APIs.")
>   + a097c0b ("datapath: Restructure datapath.c and flow.c")
> * As suggested by Jesse Gross
>   + Take into account that push_mpls() will have freed the skb on error
>   + Remove dubious !eth_p_mpls(skb->protocol) condition from push_mpls
>     The !eth_p_mpls(skb->protocol) condition on setting inner_protocol
>     has no effect. Its motivation was to ensure that inner_protocol was
>     only set the first time that mpls_push occured. However this is already
>     ensured by the !ovs_skb_get_inner_protocol(skb) condition.
>   + Return -EINVAL instead of -ENOMEM from pop_mpls() if the skb is too short
>   + Do not add @inner_protocol to kernel doc for struct ovs_skb_cb.
>     The patch no longer adds an inner_protocol member to struct ovs_skb_cb
>   + Do not add and set otherwise unsued inner_protocol variable in
>     rpl_dev_queue_xmit()
> * As suggested by Pravin Shelar
>   + Implement compatibility code in existing rpl_skb_gso_segment
>     rather than introducing to use rpl___skb_gso_segment
> 
> 
> Differences between v2.41 and v2.40:
> 
> * As suggested by Ben Pfaff
>   + Expand struct ofpact_reg_load to include a mpls_before_vlan field
>     rather than using the compat field of the ofpact field of
>     struct ofpact_reg_load.
>   + Pass version to  ofpacts_pull_openflow11_actions and
>     ofpacts_pull_openflow11_instructions.  This removes the invalid
>     assumption that that these functions are passed a full message and are
>     thus able to deduce the OpenFlow version.
> 
> 
> Differences between v2.40 and v2.39:
> 
> * Rebase for:
>   + New dev_queue_xmit compat code
>   + Updated put_vlan()
>   + Removal of mpls_depth field from struct flow
> * As suggested by Jesse Gross
>   + Remove bogus mac_len update from push_mpls()
>   + Slightly simplify push_mpls() by using eth_hdr()
>   + Remove dubious condition !eth_p_mpls(inner_protocol) on
>     an skb being considered to be MPLS in netdev_send()
>   + Only use compatibility code for MPLS GSO segmentation on kernels
>     older than 3.11
>   + Revamp setting of inner_protocol
>     1. Do not unconditionally set inner_protocol to the value of
>        skb->protocol in ovs_execute_actions().
>     2. Initialise inner_protocol it to zero only if compatibility code is in
>        use. In the case where compatibility code is not in use it will either
>        be zero due since the allocation of the skb or some other value set
>        by some other user.
>     3. Conditionally set the inner_protocol in push_mpls() to the value of
>        skb->protocol when entering push_mpls(). The condition is that
>        inner_protocol is zero and the value of skb->protocol is not an MPLS
>        ethernet type.
>     - This new scheme:
>       + Pushes logic to set inner_protocol closer to the case where it is
> 	needed.
>       + Avoids over-writing values set by other users.
> * As suggested by Pravin Shelar
>   + Only set and restore skb->protocol in rpl___skb_gso_segment() in the
>     case of MPLS
>   + Add inner_protocol field to struct ovs_gso_cb instead of ovs_skb_cb.
>     This moves compatibility code closer to where it is used
>     and creates fewer differences with mainline.
> * Update comment on mac_len updates in datapath/actions.c
> * Remove HAVE_INNER_PROCOTOL and instead just check
>   against kernel version 3.11 directly.
>   HAVE_INNER_PROCOTOL is a hang-over from work done prior
>   to the merge of inner_protocol into the kernel.
> * Remove dubious condition !eth_p_mpls(inner_protocol) on
>   using inner_protocol as the type in rpl_skb_network_protocol()
> * Do not update type of features in rpl_dev_queue_xmit.
>   Though arguably correct this is not an inherent part of
>   the changes made by this patch.
> * Use skb_cow_head() in push_mpls()
>   + Call skb_cow_head(skb, MPLS_HLEN) instead of
>     make_writable(skb, skb->mac_len) to ensure that there is enough head
>     room to push an MPLS LSE regardless of whether the skb is cloned or not.
>   + This is consistent with the behaviour of rpl__vlan_put_tag().
>   + This is a fix for crashes reported when performing mpls_push
>     with headroom less than 4. This problem was introduced in v3.36.
> * Skip popping in mpls_pop if the skb is too short to contain an MPLS LSE
> 
> 
> Differences between v2.39 and v2.38:
> 
> * Rebase for removal of vlan, checksum and skb->mark compat code
>   - This includes adding adding a new patch,
>     "[PATCH v2.39 6/7] datapath: Break out deacceleration portion of
>     vlan_push" to allow re-use of some existing code.
> 
> 
> Differences between v2.38 and v2.37:
> 
> * Rebase for SCTP support
> * Refactor validate_tp_port() to iterate over eth_types rather
>   than open-coding the loop. With the addition of SCTP this logic
>   is now used three times.
> 
> 
> Differences between v2.37 and v2.36:
> 
> * Rebase
> 
> 
> Differences between v2.36 and v2.35:
> 
> * Rebase
> 
> * Do not add set_ethertype() to datapath/actions.c.
>   As this patch has evolved this function had devolved into
>   to sets of functionality wrapped into a single function with
>   only one line of common code. Refactor things to simply
>   open-code setting the ether type in the two locations where
>   set_ethertype() was previously used. The aim here is to improve
>   readability.
> 
> * Update setting skb->ethertype after mpls push and pop.
>   - In the case of push_mpls it should be set unconditionally
>     as in v2.35 the behaviour of this function to always push
>     an MPLS LSE before any VLAN tags.
>   - In the case of mpls_pop eth_p_mpls(skb->protocol) is a better
>     test than skb->protocol != htons(ETH_P_8021Q) as it will give the
>     correct behaviour in the presence of other VLAN ethernet types,
>     for example 0x88a8 which is used by 802.1ad. Moreover, it seems
>     correct to update the ethernet type if it was previously set
>     according to the top-most MPLS LSE.
> 
> * Deaccelerate VLANs when pushing MPLS tags the
>   - Since v2.35 MPLS push will insert an MPLS LSE before any VLAN tags.
>     This means that if an accelerated tag is present it should be
>     deaccelerated to ensure it ends up in the correct position.
> 
> * Update skb->mac_len in push_mpls() so that it will be correct
>   when used by a subsequent call to pop_mpls().
> 
>   As things stand I do not believe this is strictly necessary as
>   ovs-vswitchd will not send a pop MPLS action after a push MPLS action.
>   However, I have added this in order to code more defensively as I believe
>   that if such a sequence did occur it would be rather unobvious why
>   it didn't work.
> 
> * Do not add skb_cow_head() call in push_mpls().
>   It is unnecessary as there is a make_writable() call.
>   This change was also made in v2.30 but some how the
>   code regressed between then and v2.35.
> 
> 
> Differences between v2.35 and v2.34:
> 
> * Add support for the tag ordering specified up until OpenFlow 1.2 and
>   the ordering specified from OpenFlow 1.3.
> 
> * Correct error in datapath patch's handling of GSO in the presence
>   of MPLS and absence of VLANs.
> 
> 
> To aid review this series is available in git at:
> 
> git://github.com/horms/openvswitch.git devel/mpls-v2.51
> 
> 
> Patch list and overall diffstat:
> 
> Joe Stringer (1):
>   odp: Allow VLAN actions after MPLS actions
> 
> Simon Horman (4):
>   ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after
>     mpls_push
>   lib: Support pushing of MPLS LSE before or after VLAN tag
>   datapath: Break out deacceleration portion of vlan_push
>   datapath: Add basic MPLS support to kernel
> 
>  OPENFLOW-1.1+                                   |  12 -
>  datapath/Modules.mk                             |   1 +
>  datapath/actions.c                              | 157 ++++-
>  datapath/datapath.c                             |   4 +-
>  datapath/flow.c                                 |  29 +
>  datapath/flow.h                                 |  17 +-
>  datapath/flow_netlink.c                         | 286 +++++++-
>  datapath/flow_netlink.h                         |   2 +-
>  datapath/linux/compat/gso.c                     |  70 +-
>  datapath/linux/compat/gso.h                     |  41 ++
>  datapath/linux/compat/include/linux/netdevice.h |   6 +-
>  datapath/linux/compat/netdevice.c               |  10 +-
>  datapath/mpls.h                                 |  15 +
>  include/linux/openvswitch.h                     |   7 +-
>  lib/flow.c                                      |   2 +-
>  lib/odp-util.c                                  |  12 +-
>  lib/odp-util.h                                  |   3 +-
>  lib/ofp-actions.c                               | 194 +++++-
>  lib/packets.c                                   |  10 +-
>  lib/packets.h                                   |   2 +-
>  ofproto/ofproto-dpif-xlate.c                    | 161 ++++-
>  tests/ofproto-dpif.at                           | 869 ++++++++++++++++++++++++
>  22 files changed, 1765 insertions(+), 145 deletions(-)
>  create mode 100644 datapath/mpls.h
> 
> -- 
> 1.8.4
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches
  2013-11-26  8:08 ` [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
@ 2013-11-26 15:33   ` Ben Pfaff
  2013-11-27  0:12     ` Simon Horman
  0 siblings, 1 reply; 19+ messages in thread
From: Ben Pfaff @ 2013-11-26 15:33 UTC (permalink / raw)
  To: Simon Horman; +Cc: dev, netdev, Jesse Gross, Ravi K

On Tue, Nov 26, 2013 at 05:08:04PM +0900, Simon Horman wrote:
> On Thu, Nov 21, 2013 at 12:46:41PM +0900, Simon Horman wrote:
> > Hi,
> > 
> > This series implements MPLS actions and matches based on work by
> > Ravi K, Leo Alterman, Yamahata-san and Joe Stringer.
> > 
> > This series provides three changes
> > 
> > * Patches 1 - 3
> > 
> >   Provide user-space support for the VLAN/MPLS tag insertion order
> >   up to and including OpenFlow 1.2, and the different ordering
> >   specified from OpenFlow 1.3. In a nutshell the datapath always
> >   uses the OpenFlow 1.3 ordering, which is to always insert tags
> >   immediately after the L2 header, regardless of the presence of other
> >   tags. And ovs-vswtichd provides compatibility for the behaviour up
> >   to OpenFlow 1.2, which is that MPLS tags should follow VLAN tags
> >   if present.
> > 
> >   Ben, these are for you to review.
> 
> Hi Ben,
> 
> I wonder if you could find some time to look over these.
> 
> I believe that the first patch has not previously been reviewed
> but other than that there should be few areas of excitement.

I'm taking today and the remainder of this week off (Thursday and Friday
are a holiday here in the U.S.), so unfortunately there is not much
chance of progress before Monday.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches
  2013-11-26 15:33   ` Ben Pfaff
@ 2013-11-27  0:12     ` Simon Horman
  2013-12-04  9:16       ` Simon Horman
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-11-27  0:12 UTC (permalink / raw)
  To: Ben Pfaff; +Cc: dev, netdev, Jesse Gross, Ravi K

On Tue, Nov 26, 2013 at 07:33:47AM -0800, Ben Pfaff wrote:
> On Tue, Nov 26, 2013 at 05:08:04PM +0900, Simon Horman wrote:
> > On Thu, Nov 21, 2013 at 12:46:41PM +0900, Simon Horman wrote:
> > > Hi,
> > > 
> > > This series implements MPLS actions and matches based on work by
> > > Ravi K, Leo Alterman, Yamahata-san and Joe Stringer.
> > > 
> > > This series provides three changes
> > > 
> > > * Patches 1 - 3
> > > 
> > >   Provide user-space support for the VLAN/MPLS tag insertion order
> > >   up to and including OpenFlow 1.2, and the different ordering
> > >   specified from OpenFlow 1.3. In a nutshell the datapath always
> > >   uses the OpenFlow 1.3 ordering, which is to always insert tags
> > >   immediately after the L2 header, regardless of the presence of other
> > >   tags. And ovs-vswtichd provides compatibility for the behaviour up
> > >   to OpenFlow 1.2, which is that MPLS tags should follow VLAN tags
> > >   if present.
> > > 
> > >   Ben, these are for you to review.
> > 
> > Hi Ben,
> > 
> > I wonder if you could find some time to look over these.
> > 
> > I believe that the first patch has not previously been reviewed
> > but other than that there should be few areas of excitement.
> 
> I'm taking today and the remainder of this week off (Thursday and Friday
> are a holiday here in the U.S.), so unfortunately there is not much
> chance of progress before Monday.

Enjoy your break :)

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches
  2013-11-27  0:12     ` Simon Horman
@ 2013-12-04  9:16       ` Simon Horman
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Horman @ 2013-12-04  9:16 UTC (permalink / raw)
  To: Ben Pfaff; +Cc: dev, netdev, Jesse Gross, Ravi K

On Wed, Nov 27, 2013 at 09:12:30AM +0900, Simon Horman wrote:
> On Tue, Nov 26, 2013 at 07:33:47AM -0800, Ben Pfaff wrote:
> > On Tue, Nov 26, 2013 at 05:08:04PM +0900, Simon Horman wrote:
> > > On Thu, Nov 21, 2013 at 12:46:41PM +0900, Simon Horman wrote:
> > > > Hi,
> > > > 
> > > > This series implements MPLS actions and matches based on work by
> > > > Ravi K, Leo Alterman, Yamahata-san and Joe Stringer.
> > > > 
> > > > This series provides three changes
> > > > 
> > > > * Patches 1 - 3
> > > > 
> > > >   Provide user-space support for the VLAN/MPLS tag insertion order
> > > >   up to and including OpenFlow 1.2, and the different ordering
> > > >   specified from OpenFlow 1.3. In a nutshell the datapath always
> > > >   uses the OpenFlow 1.3 ordering, which is to always insert tags
> > > >   immediately after the L2 header, regardless of the presence of other
> > > >   tags. And ovs-vswtichd provides compatibility for the behaviour up
> > > >   to OpenFlow 1.2, which is that MPLS tags should follow VLAN tags
> > > >   if present.
> > > > 
> > > >   Ben, these are for you to review.
> > > 
> > > Hi Ben,
> > > 
> > > I wonder if you could find some time to look over these.
> > > 
> > > I believe that the first patch has not previously been reviewed
> > > but other than that there should be few areas of excitement.
> > 
> > I'm taking today and the remainder of this week off (Thursday and Friday
> > are a holiday here in the U.S.), so unfortunately there is not much
> > chance of progress before Monday.
> 
> Enjoy your break :)

Hi Ben,

I know this has been dragging on and on.
But I'd like to nicely ask you to consider reviewing this series.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-11-21  3:46   ` [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push Simon Horman
@ 2013-12-04 21:24     ` Ben Pfaff
  2013-12-04 23:58       ` Simon Horman
  0 siblings, 1 reply; 19+ messages in thread
From: Ben Pfaff @ 2013-12-04 21:24 UTC (permalink / raw)
  To: Simon Horman
  Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> The aim of this patch is to support provide infrastructure for verification
> of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> existing support for verifying these actions for pre-OpenFlow1.3.
> 
> In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> ordering. Open vSwitch also uses this ordering when supporting MPLS
> actions via Nicira extensions to OpenFlow1.0.
> 
> When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> affect the VLANs of a packet. If VLAN tags are present immediately after
> the ethernet header then they remain present there.
> 
> In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> immediately follow the ethernet header. This is OpenFlow1.3+ tag
> ordering.
> 
> When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> VLANs of a packet as any VLAN tags previously present after the ethernet
> header are moved to be immediately after the newly pushed MPLS LSE. Thus
> for the purpose of action consistency checking a packet may be changed
> from a VLAN packet to a non-VLAN packet.
> 
> In this way the effective value of the VLAN TCI of a packet may differ
> after an MPLS push depending on the OpenFlow version in use.
> 
> This patch does not enable the logic described above.
> Rather it is disabled in ofpacts_check__(). It should
> be enabled when support for OpenFlow1.3+ tag order is added
> and enabled.
> 
> Signed-off-by: Simon Horman <horms@verge.net.au>

As far as I can tell this doesn't make sense, because where the MPLS
tag goes is a property of the action that we know at the time we parse
the push_mpls action.  So why isn't this patch just the following?

diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index a02f842..f444374 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
          * Thus nothing can be assumed about the network protocol.
          * Temporarily mark that we have no nw_proto. */
         flow->nw_proto = 0;
+        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
+            flow->vlan_tci = 0;
+        }
         return 0;
 
     case OFPACT_POP_MPLS:

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-12-04 21:24     ` Ben Pfaff
@ 2013-12-04 23:58       ` Simon Horman
  2013-12-05  0:44         ` Ben Pfaff
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-12-04 23:58 UTC (permalink / raw)
  To: Ben Pfaff; +Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > The aim of this patch is to support provide infrastructure for verification
> > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > existing support for verifying these actions for pre-OpenFlow1.3.
> > 
> > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > actions via Nicira extensions to OpenFlow1.0.
> > 
> > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > affect the VLANs of a packet. If VLAN tags are present immediately after
> > the ethernet header then they remain present there.
> > 
> > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > ordering.
> > 
> > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > VLANs of a packet as any VLAN tags previously present after the ethernet
> > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > for the purpose of action consistency checking a packet may be changed
> > from a VLAN packet to a non-VLAN packet.
> > 
> > In this way the effective value of the VLAN TCI of a packet may differ
> > after an MPLS push depending on the OpenFlow version in use.
> > 
> > This patch does not enable the logic described above.
> > Rather it is disabled in ofpacts_check__(). It should
> > be enabled when support for OpenFlow1.3+ tag order is added
> > and enabled.
> > 
> > Signed-off-by: Simon Horman <horms@verge.net.au>
> 
> As far as I can tell this doesn't make sense, because where the MPLS
> tag goes is a property of the action that we know at the time we parse
> the push_mpls action.  So why isn't this patch just the following?
> 
> diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> index a02f842..f444374 100644
> --- a/lib/ofp-actions.c
> +++ b/lib/ofp-actions.c
> @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
>           * Thus nothing can be assumed about the network protocol.
>           * Temporarily mark that we have no nw_proto. */
>          flow->nw_proto = 0;
> +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> +            flow->vlan_tci = 0;
> +        }
>          return 0;

That was more or less what I originally tried.  However I believe that it
doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
set at the time that ofpact_check__ is called.  In particular this occurs
when it is called indirectly from parse_ofp_str__.

Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
is used to check actions when a one of number of protocols may be used,
that is multiple bits of *usable_protocols.  If we could rely on
ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
*usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
would need to be cleared.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-12-04 23:58       ` Simon Horman
@ 2013-12-05  0:44         ` Ben Pfaff
  2013-12-05  0:51           ` Simon Horman
  0 siblings, 1 reply; 19+ messages in thread
From: Ben Pfaff @ 2013-12-05  0:44 UTC (permalink / raw)
  To: Simon Horman
  Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > The aim of this patch is to support provide infrastructure for verification
> > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > 
> > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > actions via Nicira extensions to OpenFlow1.0.
> > > 
> > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > the ethernet header then they remain present there.
> > > 
> > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > ordering.
> > > 
> > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > for the purpose of action consistency checking a packet may be changed
> > > from a VLAN packet to a non-VLAN packet.
> > > 
> > > In this way the effective value of the VLAN TCI of a packet may differ
> > > after an MPLS push depending on the OpenFlow version in use.
> > > 
> > > This patch does not enable the logic described above.
> > > Rather it is disabled in ofpacts_check__(). It should
> > > be enabled when support for OpenFlow1.3+ tag order is added
> > > and enabled.
> > > 
> > > Signed-off-by: Simon Horman <horms@verge.net.au>
> > 
> > As far as I can tell this doesn't make sense, because where the MPLS
> > tag goes is a property of the action that we know at the time we parse
> > the push_mpls action.  So why isn't this patch just the following?
> > 
> > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > index a02f842..f444374 100644
> > --- a/lib/ofp-actions.c
> > +++ b/lib/ofp-actions.c
> > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> >           * Thus nothing can be assumed about the network protocol.
> >           * Temporarily mark that we have no nw_proto. */
> >          flow->nw_proto = 0;
> > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > +            flow->vlan_tci = 0;
> > +        }
> >          return 0;
> 
> That was more or less what I originally tried.  However I believe that it
> doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> set at the time that ofpact_check__ is called.  In particular this occurs
> when it is called indirectly from parse_ofp_str__.
> 
> Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> is used to check actions when a one of number of protocols may be used,
> that is multiple bits of *usable_protocols.  If we could rely on
> ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> would need to be cleared.

I think this might be a mistake in how we define the syntax that
parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
ovs-ofctl command line, then I want that to have some specific
meaning.  I don't want it to mean "do one thing if you happen to
negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
OpenFlow 1.3", because that's totally unusable and broken from a user
perspective.  So if that the issue then I think we should change the
syntax.  One way would be to have "push_mpls" default to the 1.3
behavior (which seems generally saner) and allow the user to specify
an option to get the 1.2 behavior.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-12-05  0:44         ` Ben Pfaff
@ 2013-12-05  0:51           ` Simon Horman
  2013-12-05  1:01             ` Ben Pfaff
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-12-05  0:51 UTC (permalink / raw)
  To: Ben Pfaff; +Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Wed, Dec 04, 2013 at 04:44:17PM -0800, Ben Pfaff wrote:
> On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> > On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > > The aim of this patch is to support provide infrastructure for verification
> > > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > > 
> > > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > > actions via Nicira extensions to OpenFlow1.0.
> > > > 
> > > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > > the ethernet header then they remain present there.
> > > > 
> > > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > > ordering.
> > > > 
> > > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > > for the purpose of action consistency checking a packet may be changed
> > > > from a VLAN packet to a non-VLAN packet.
> > > > 
> > > > In this way the effective value of the VLAN TCI of a packet may differ
> > > > after an MPLS push depending on the OpenFlow version in use.
> > > > 
> > > > This patch does not enable the logic described above.
> > > > Rather it is disabled in ofpacts_check__(). It should
> > > > be enabled when support for OpenFlow1.3+ tag order is added
> > > > and enabled.
> > > > 
> > > > Signed-off-by: Simon Horman <horms@verge.net.au>
> > > 
> > > As far as I can tell this doesn't make sense, because where the MPLS
> > > tag goes is a property of the action that we know at the time we parse
> > > the push_mpls action.  So why isn't this patch just the following?
> > > 
> > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > > index a02f842..f444374 100644
> > > --- a/lib/ofp-actions.c
> > > +++ b/lib/ofp-actions.c
> > > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> > >           * Thus nothing can be assumed about the network protocol.
> > >           * Temporarily mark that we have no nw_proto. */
> > >          flow->nw_proto = 0;
> > > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > > +            flow->vlan_tci = 0;
> > > +        }
> > >          return 0;
> > 
> > That was more or less what I originally tried.  However I believe that it
> > doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> > set at the time that ofpact_check__ is called.  In particular this occurs
> > when it is called indirectly from parse_ofp_str__.
> > 
> > Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> > is used to check actions when a one of number of protocols may be used,
> > that is multiple bits of *usable_protocols.  If we could rely on
> > ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> > set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> > *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> > would need to be cleared.
> 
> I think this might be a mistake in how we define the syntax that
> parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
> ovs-ofctl command line, then I want that to have some specific
> meaning.  I don't want it to mean "do one thing if you happen to
> negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
> OpenFlow 1.3", because that's totally unusable and broken from a user
> perspective.

To clarify, that is exactly what this series was trying to do.

I think there is some precedence in the handling of actions
that set_vlans. Some OF versions implicitly push a tag, some don't.

But I do agree that the behaviour you describe above would
be very confusing for users.

> So if that the issue then I think we should change the
> syntax.  One way would be to have "push_mpls" default to the 1.3
> behavior (which seems generally saner) and allow the user to specify
> an option to get the 1.2 behavior.

Sure, I think I am happy with that idea.

By an option do you mean a different action name, for example append_mpls,
or push_mpls_after_vlan?

Or do you have something else in mind?

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-12-05  0:51           ` Simon Horman
@ 2013-12-05  1:01             ` Ben Pfaff
       [not found]               ` <20131205010111.GE11354-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Ben Pfaff @ 2013-12-05  1:01 UTC (permalink / raw)
  To: Simon Horman
  Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Thu, Dec 05, 2013 at 09:51:39AM +0900, Simon Horman wrote:
> On Wed, Dec 04, 2013 at 04:44:17PM -0800, Ben Pfaff wrote:
> > On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> > > On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > > > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > > > The aim of this patch is to support provide infrastructure for verification
> > > > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > > > 
> > > > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > > > actions via Nicira extensions to OpenFlow1.0.
> > > > > 
> > > > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > > > the ethernet header then they remain present there.
> > > > > 
> > > > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > > > ordering.
> > > > > 
> > > > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > > > for the purpose of action consistency checking a packet may be changed
> > > > > from a VLAN packet to a non-VLAN packet.
> > > > > 
> > > > > In this way the effective value of the VLAN TCI of a packet may differ
> > > > > after an MPLS push depending on the OpenFlow version in use.
> > > > > 
> > > > > This patch does not enable the logic described above.
> > > > > Rather it is disabled in ofpacts_check__(). It should
> > > > > be enabled when support for OpenFlow1.3+ tag order is added
> > > > > and enabled.
> > > > > 
> > > > > Signed-off-by: Simon Horman <horms@verge.net.au>
> > > > 
> > > > As far as I can tell this doesn't make sense, because where the MPLS
> > > > tag goes is a property of the action that we know at the time we parse
> > > > the push_mpls action.  So why isn't this patch just the following?
> > > > 
> > > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > > > index a02f842..f444374 100644
> > > > --- a/lib/ofp-actions.c
> > > > +++ b/lib/ofp-actions.c
> > > > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> > > >           * Thus nothing can be assumed about the network protocol.
> > > >           * Temporarily mark that we have no nw_proto. */
> > > >          flow->nw_proto = 0;
> > > > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > > > +            flow->vlan_tci = 0;
> > > > +        }
> > > >          return 0;
> > > 
> > > That was more or less what I originally tried.  However I believe that it
> > > doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> > > set at the time that ofpact_check__ is called.  In particular this occurs
> > > when it is called indirectly from parse_ofp_str__.
> > > 
> > > Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> > > is used to check actions when a one of number of protocols may be used,
> > > that is multiple bits of *usable_protocols.  If we could rely on
> > > ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> > > set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> > > *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> > > would need to be cleared.
> > 
> > I think this might be a mistake in how we define the syntax that
> > parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
> > ovs-ofctl command line, then I want that to have some specific
> > meaning.  I don't want it to mean "do one thing if you happen to
> > negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
> > OpenFlow 1.3", because that's totally unusable and broken from a user
> > perspective.
> 
> To clarify, that is exactly what this series was trying to do.
> 
> I think there is some precedence in the handling of actions
> that set_vlans. Some OF versions implicitly push a tag, some don't.

Maybe that is worth fixing too.

> But I do agree that the behaviour you describe above would
> be very confusing for users.
> 
> > So if that the issue then I think we should change the
> > syntax.  One way would be to have "push_mpls" default to the 1.3
> > behavior (which seems generally saner) and allow the user to specify
> > an option to get the 1.2 behavior.
> 
> Sure, I think I am happy with that idea.
> 
> By an option do you mean a different action name, for example append_mpls,
> or push_mpls_after_vlan?

I was thinking of something like a push_mpls version of the
keyword-based fin_timeout syntax.  One option would be eth_type,
defaulting to ETH_TYPE_MPLS.  Another option would be position, with
after_vlan or before_vlan as allowed values, and probably after_vlan
as the default.

With this approach, any flow with a push_mpls could be used only with
pre-OF1.3 or OF1.3+, depending on the "position" value.  One wrinkle
that might be nice, if it isn't too nasty to implement, would be to
have a third value "no_vlan" as the default.  With no_vlan, we reject
the flow at check time if a VLAN is present; if no VLAN is present,
then push_mpls has the same behavior regardless of OpenFlow version.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
       [not found]               ` <20131205010111.GE11354-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>
@ 2013-12-05  2:26                 ` Simon Horman
  2013-12-05 17:56                   ` Ben Pfaff
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-12-05  2:26 UTC (permalink / raw)
  To: Ben Pfaff
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, Ravi K, netdev-u79uwXL29TY76Z2rM5mHXA

On Wed, Dec 04, 2013 at 05:01:11PM -0800, Ben Pfaff wrote:
> On Thu, Dec 05, 2013 at 09:51:39AM +0900, Simon Horman wrote:
> > On Wed, Dec 04, 2013 at 04:44:17PM -0800, Ben Pfaff wrote:
> > > On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> > > > On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > > > > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > > > > The aim of this patch is to support provide infrastructure for verification
> > > > > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > > > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > > > > 
> > > > > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > > > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > > > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > > > > actions via Nicira extensions to OpenFlow1.0.
> > > > > > 
> > > > > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > > > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > > > > the ethernet header then they remain present there.
> > > > > > 
> > > > > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > > > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > > > > ordering.
> > > > > > 
> > > > > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > > > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > > > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > > > > for the purpose of action consistency checking a packet may be changed
> > > > > > from a VLAN packet to a non-VLAN packet.
> > > > > > 
> > > > > > In this way the effective value of the VLAN TCI of a packet may differ
> > > > > > after an MPLS push depending on the OpenFlow version in use.
> > > > > > 
> > > > > > This patch does not enable the logic described above.
> > > > > > Rather it is disabled in ofpacts_check__(). It should
> > > > > > be enabled when support for OpenFlow1.3+ tag order is added
> > > > > > and enabled.
> > > > > > 
> > > > > > Signed-off-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
> > > > > 
> > > > > As far as I can tell this doesn't make sense, because where the MPLS
> > > > > tag goes is a property of the action that we know at the time we parse
> > > > > the push_mpls action.  So why isn't this patch just the following?
> > > > > 
> > > > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > > > > index a02f842..f444374 100644
> > > > > --- a/lib/ofp-actions.c
> > > > > +++ b/lib/ofp-actions.c
> > > > > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> > > > >           * Thus nothing can be assumed about the network protocol.
> > > > >           * Temporarily mark that we have no nw_proto. */
> > > > >          flow->nw_proto = 0;
> > > > > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > > > > +            flow->vlan_tci = 0;
> > > > > +        }
> > > > >          return 0;
> > > > 
> > > > That was more or less what I originally tried.  However I believe that it
> > > > doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> > > > set at the time that ofpact_check__ is called.  In particular this occurs
> > > > when it is called indirectly from parse_ofp_str__.
> > > > 
> > > > Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> > > > is used to check actions when a one of number of protocols may be used,
> > > > that is multiple bits of *usable_protocols.  If we could rely on
> > > > ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> > > > set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> > > > *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> > > > would need to be cleared.
> > > 
> > > I think this might be a mistake in how we define the syntax that
> > > parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
> > > ovs-ofctl command line, then I want that to have some specific
> > > meaning.  I don't want it to mean "do one thing if you happen to
> > > negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
> > > OpenFlow 1.3", because that's totally unusable and broken from a user
> > > perspective.
> > 
> > To clarify, that is exactly what this series was trying to do.
> > 
> > I think there is some precedence in the handling of actions
> > that set_vlans. Some OF versions implicitly push a tag, some don't.
> 
> Maybe that is worth fixing too.
> 
> > But I do agree that the behaviour you describe above would
> > be very confusing for users.
> > 
> > > So if that the issue then I think we should change the
> > > syntax.  One way would be to have "push_mpls" default to the 1.3
> > > behavior (which seems generally saner) and allow the user to specify
> > > an option to get the 1.2 behavior.
> > 
> > Sure, I think I am happy with that idea.
> > 
> > By an option do you mean a different action name, for example append_mpls,
> > or push_mpls_after_vlan?
> 
> I was thinking of something like a push_mpls version of the
> keyword-based fin_timeout syntax.  One option would be eth_type,
> defaulting to ETH_TYPE_MPLS.  Another option would be position, with
> after_vlan or before_vlan as allowed values, and probably after_vlan
> as the default.
> 
> With this approach, any flow with a push_mpls could be used only with
> pre-OF1.3 or OF1.3+, depending on the "position" value.  One wrinkle
> that might be nice, if it isn't too nasty to implement, would be to
> have a third value "no_vlan" as the default.  With no_vlan, we reject
> the flow at check time if a VLAN is present; if no VLAN is present,
> then push_mpls has the same behavior regardless of OpenFlow version.

>From an ovs-ofctl point of view I think that makes a lot of sense and I
think it should be clean enough to implement. My initial reaction is that
using a position argument would be good and I don't see any particular
problem with a no_vlan option. But I'll give some thought to an eth_type
argument.

I would like to clarify how you would like push_mpls to work in the case
where flows are received from a controller. It seems to me that the
behaviour should depend on the OpenFlow version used by the connection with
the controller as we can't change the action to accommodate an extra
argument. I think this is also easy enough to implement: actually I am
pretty sure think the series currently does that and that the difficulty
that this patch tried to address is only on the ovs-ofctl side. Regardless,
I wanted to check that is the behaviour that you would like.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-12-05  2:26                 ` Simon Horman
@ 2013-12-05 17:56                   ` Ben Pfaff
  2013-12-06  3:29                     ` Simon Horman
  0 siblings, 1 reply; 19+ messages in thread
From: Ben Pfaff @ 2013-12-05 17:56 UTC (permalink / raw)
  To: Simon Horman
  Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Thu, Dec 05, 2013 at 11:26:06AM +0900, Simon Horman wrote:
> On Wed, Dec 04, 2013 at 05:01:11PM -0800, Ben Pfaff wrote:
> > On Thu, Dec 05, 2013 at 09:51:39AM +0900, Simon Horman wrote:
> > > On Wed, Dec 04, 2013 at 04:44:17PM -0800, Ben Pfaff wrote:
> > > > On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> > > > > On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > > > > > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > > > > > The aim of this patch is to support provide infrastructure for verification
> > > > > > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > > > > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > > > > > 
> > > > > > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > > > > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > > > > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > > > > > actions via Nicira extensions to OpenFlow1.0.
> > > > > > > 
> > > > > > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > > > > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > > > > > the ethernet header then they remain present there.
> > > > > > > 
> > > > > > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > > > > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > > > > > ordering.
> > > > > > > 
> > > > > > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > > > > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > > > > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > > > > > for the purpose of action consistency checking a packet may be changed
> > > > > > > from a VLAN packet to a non-VLAN packet.
> > > > > > > 
> > > > > > > In this way the effective value of the VLAN TCI of a packet may differ
> > > > > > > after an MPLS push depending on the OpenFlow version in use.
> > > > > > > 
> > > > > > > This patch does not enable the logic described above.
> > > > > > > Rather it is disabled in ofpacts_check__(). It should
> > > > > > > be enabled when support for OpenFlow1.3+ tag order is added
> > > > > > > and enabled.
> > > > > > > 
> > > > > > > Signed-off-by: Simon Horman <horms@verge.net.au>
> > > > > > 
> > > > > > As far as I can tell this doesn't make sense, because where the MPLS
> > > > > > tag goes is a property of the action that we know at the time we parse
> > > > > > the push_mpls action.  So why isn't this patch just the following?
> > > > > > 
> > > > > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > > > > > index a02f842..f444374 100644
> > > > > > --- a/lib/ofp-actions.c
> > > > > > +++ b/lib/ofp-actions.c
> > > > > > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> > > > > >           * Thus nothing can be assumed about the network protocol.
> > > > > >           * Temporarily mark that we have no nw_proto. */
> > > > > >          flow->nw_proto = 0;
> > > > > > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > > > > > +            flow->vlan_tci = 0;
> > > > > > +        }
> > > > > >          return 0;
> > > > > 
> > > > > That was more or less what I originally tried.  However I believe that it
> > > > > doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> > > > > set at the time that ofpact_check__ is called.  In particular this occurs
> > > > > when it is called indirectly from parse_ofp_str__.
> > > > > 
> > > > > Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> > > > > is used to check actions when a one of number of protocols may be used,
> > > > > that is multiple bits of *usable_protocols.  If we could rely on
> > > > > ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> > > > > set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> > > > > *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> > > > > would need to be cleared.
> > > > 
> > > > I think this might be a mistake in how we define the syntax that
> > > > parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
> > > > ovs-ofctl command line, then I want that to have some specific
> > > > meaning.  I don't want it to mean "do one thing if you happen to
> > > > negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
> > > > OpenFlow 1.3", because that's totally unusable and broken from a user
> > > > perspective.
> > > 
> > > To clarify, that is exactly what this series was trying to do.
> > > 
> > > I think there is some precedence in the handling of actions
> > > that set_vlans. Some OF versions implicitly push a tag, some don't.
> > 
> > Maybe that is worth fixing too.
> > 
> > > But I do agree that the behaviour you describe above would
> > > be very confusing for users.
> > > 
> > > > So if that the issue then I think we should change the
> > > > syntax.  One way would be to have "push_mpls" default to the 1.3
> > > > behavior (which seems generally saner) and allow the user to specify
> > > > an option to get the 1.2 behavior.
> > > 
> > > Sure, I think I am happy with that idea.
> > > 
> > > By an option do you mean a different action name, for example append_mpls,
> > > or push_mpls_after_vlan?
> > 
> > I was thinking of something like a push_mpls version of the
> > keyword-based fin_timeout syntax.  One option would be eth_type,
> > defaulting to ETH_TYPE_MPLS.  Another option would be position, with
> > after_vlan or before_vlan as allowed values, and probably after_vlan
> > as the default.
> > 
> > With this approach, any flow with a push_mpls could be used only with
> > pre-OF1.3 or OF1.3+, depending on the "position" value.  One wrinkle
> > that might be nice, if it isn't too nasty to implement, would be to
> > have a third value "no_vlan" as the default.  With no_vlan, we reject
> > the flow at check time if a VLAN is present; if no VLAN is present,
> > then push_mpls has the same behavior regardless of OpenFlow version.
> 
> From an ovs-ofctl point of view I think that makes a lot of sense and I
> think it should be clean enough to implement. My initial reaction is that
> using a position argument would be good and I don't see any particular
> problem with a no_vlan option. But I'll give some thought to an eth_type
> argument.

push_mpls currently takes a required eth_type argument.  There are only
two values and my understanding is that 0x8847 is more common, hence my
suggestion that it be the default.

> I would like to clarify how you would like push_mpls to work in the case
> where flows are received from a controller. It seems to me that the
> behaviour should depend on the OpenFlow version used by the connection with
> the controller as we can't change the action to accommodate an extra
> argument. 

Right, when push_mpls is received over OpenFlow then it always behaves
as the particular OpenFlow version specifies.

> I think this is also easy enough to implement: actually I am pretty
> sure think the series currently does that and that the difficulty that
> this patch tried to address is only on the ovs-ofctl side. Regardless,
> I wanted to check that is the behaviour that you would like.

I hope I clarified, let me know if it's still unclear.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
  2013-12-05 17:56                   ` Ben Pfaff
@ 2013-12-06  3:29                     ` Simon Horman
       [not found]                       ` <20131206032924.GA20522-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Horman @ 2013-12-06  3:29 UTC (permalink / raw)
  To: Ben Pfaff; +Cc: dev, netdev, Jesse Gross, Pravin B Shelar, Ravi K, Joe Stringer

On Thu, Dec 05, 2013 at 09:56:25AM -0800, Ben Pfaff wrote:
> On Thu, Dec 05, 2013 at 11:26:06AM +0900, Simon Horman wrote:
> > On Wed, Dec 04, 2013 at 05:01:11PM -0800, Ben Pfaff wrote:
> > > On Thu, Dec 05, 2013 at 09:51:39AM +0900, Simon Horman wrote:
> > > > On Wed, Dec 04, 2013 at 04:44:17PM -0800, Ben Pfaff wrote:
> > > > > On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> > > > > > On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > > > > > > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > > > > > > The aim of this patch is to support provide infrastructure for verification
> > > > > > > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > > > > > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > > > > > > 
> > > > > > > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > > > > > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > > > > > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > > > > > > actions via Nicira extensions to OpenFlow1.0.
> > > > > > > > 
> > > > > > > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > > > > > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > > > > > > the ethernet header then they remain present there.
> > > > > > > > 
> > > > > > > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > > > > > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > > > > > > ordering.
> > > > > > > > 
> > > > > > > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > > > > > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > > > > > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > > > > > > for the purpose of action consistency checking a packet may be changed
> > > > > > > > from a VLAN packet to a non-VLAN packet.
> > > > > > > > 
> > > > > > > > In this way the effective value of the VLAN TCI of a packet may differ
> > > > > > > > after an MPLS push depending on the OpenFlow version in use.
> > > > > > > > 
> > > > > > > > This patch does not enable the logic described above.
> > > > > > > > Rather it is disabled in ofpacts_check__(). It should
> > > > > > > > be enabled when support for OpenFlow1.3+ tag order is added
> > > > > > > > and enabled.
> > > > > > > > 
> > > > > > > > Signed-off-by: Simon Horman <horms@verge.net.au>
> > > > > > > 
> > > > > > > As far as I can tell this doesn't make sense, because where the MPLS
> > > > > > > tag goes is a property of the action that we know at the time we parse
> > > > > > > the push_mpls action.  So why isn't this patch just the following?
> > > > > > > 
> > > > > > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > > > > > > index a02f842..f444374 100644
> > > > > > > --- a/lib/ofp-actions.c
> > > > > > > +++ b/lib/ofp-actions.c
> > > > > > > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> > > > > > >           * Thus nothing can be assumed about the network protocol.
> > > > > > >           * Temporarily mark that we have no nw_proto. */
> > > > > > >          flow->nw_proto = 0;
> > > > > > > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > > > > > > +            flow->vlan_tci = 0;
> > > > > > > +        }
> > > > > > >          return 0;
> > > > > > 
> > > > > > That was more or less what I originally tried.  However I believe that it
> > > > > > doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> > > > > > set at the time that ofpact_check__ is called.  In particular this occurs
> > > > > > when it is called indirectly from parse_ofp_str__.
> > > > > > 
> > > > > > Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> > > > > > is used to check actions when a one of number of protocols may be used,
> > > > > > that is multiple bits of *usable_protocols.  If we could rely on
> > > > > > ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> > > > > > set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> > > > > > *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> > > > > > would need to be cleared.
> > > > > 
> > > > > I think this might be a mistake in how we define the syntax that
> > > > > parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
> > > > > ovs-ofctl command line, then I want that to have some specific
> > > > > meaning.  I don't want it to mean "do one thing if you happen to
> > > > > negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
> > > > > OpenFlow 1.3", because that's totally unusable and broken from a user
> > > > > perspective.
> > > > 
> > > > To clarify, that is exactly what this series was trying to do.
> > > > 
> > > > I think there is some precedence in the handling of actions
> > > > that set_vlans. Some OF versions implicitly push a tag, some don't.
> > > 
> > > Maybe that is worth fixing too.
> > > 
> > > > But I do agree that the behaviour you describe above would
> > > > be very confusing for users.
> > > > 
> > > > > So if that the issue then I think we should change the
> > > > > syntax.  One way would be to have "push_mpls" default to the 1.3
> > > > > behavior (which seems generally saner) and allow the user to specify
> > > > > an option to get the 1.2 behavior.
> > > > 
> > > > Sure, I think I am happy with that idea.
> > > > 
> > > > By an option do you mean a different action name, for example append_mpls,
> > > > or push_mpls_after_vlan?
> > > 
> > > I was thinking of something like a push_mpls version of the
> > > keyword-based fin_timeout syntax.  One option would be eth_type,
> > > defaulting to ETH_TYPE_MPLS.  Another option would be position, with
> > > after_vlan or before_vlan as allowed values, and probably after_vlan
> > > as the default.
> > > 
> > > With this approach, any flow with a push_mpls could be used only with
> > > pre-OF1.3 or OF1.3+, depending on the "position" value.  One wrinkle
> > > that might be nice, if it isn't too nasty to implement, would be to
> > > have a third value "no_vlan" as the default.  With no_vlan, we reject
> > > the flow at check time if a VLAN is present; if no VLAN is present,
> > > then push_mpls has the same behavior regardless of OpenFlow version.
> > 
> > From an ovs-ofctl point of view I think that makes a lot of sense and I
> > think it should be clean enough to implement. My initial reaction is that
> > using a position argument would be good and I don't see any particular
> > problem with a no_vlan option. But I'll give some thought to an eth_type
> > argument.
> 
> push_mpls currently takes a required eth_type argument.  There are only
> two values and my understanding is that 0x8847 is more common, hence my
> suggestion that it be the default.

I'm not sure that we can use the eth_type argument of an mpls_push action
in order to differentiate between pushing the LSE before or after
any VLAN tags that may be present. There are two acceptable values for the
eth_type argument, 0x8847 and 0x8848. But I believe they are both
equally acceptable regardless of where the LSE is to be pushed
in relation to existing VLAN tags.

> > I would like to clarify how you would like push_mpls to work in the case
> > where flows are received from a controller. It seems to me that the
> > behaviour should depend on the OpenFlow version used by the connection with
> > the controller as we can't change the action to accommodate an extra
> > argument. 
> 
> Right, when push_mpls is received over OpenFlow then it always behaves
> as the particular OpenFlow version specifies.

Thanks, this is clear to me.

> > I think this is also easy enough to implement: actually I am pretty
> > sure think the series currently does that and that the difficulty that
> > this patch tried to address is only on the ovs-ofctl side. Regardless,
> > I wanted to check that is the behaviour that you would like.
> 
> I hope I clarified, let me know if it's still unclear.
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push
       [not found]                       ` <20131206032924.GA20522-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
@ 2013-12-06  4:21                         ` Ben Pfaff
  0 siblings, 0 replies; 19+ messages in thread
From: Ben Pfaff @ 2013-12-06  4:21 UTC (permalink / raw)
  To: Simon Horman
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, Ravi K, netdev-u79uwXL29TY76Z2rM5mHXA

On Fri, Dec 06, 2013 at 12:29:24PM +0900, Simon Horman wrote:
> On Thu, Dec 05, 2013 at 09:56:25AM -0800, Ben Pfaff wrote:
> > On Thu, Dec 05, 2013 at 11:26:06AM +0900, Simon Horman wrote:
> > > On Wed, Dec 04, 2013 at 05:01:11PM -0800, Ben Pfaff wrote:
> > > > On Thu, Dec 05, 2013 at 09:51:39AM +0900, Simon Horman wrote:
> > > > > On Wed, Dec 04, 2013 at 04:44:17PM -0800, Ben Pfaff wrote:
> > > > > > On Thu, Dec 05, 2013 at 08:58:49AM +0900, Simon Horman wrote:
> > > > > > > On Wed, Dec 04, 2013 at 01:24:29PM -0800, Ben Pfaff wrote:
> > > > > > > > On Thu, Nov 21, 2013 at 12:46:42PM +0900, Simon Horman wrote:
> > > > > > > > > The aim of this patch is to support provide infrastructure for verification
> > > > > > > > > of VLAN actions after an mpls_push action for OpenFlow1.3. This supplements
> > > > > > > > > existing support for verifying these actions for pre-OpenFlow1.3.
> > > > > > > > > 
> > > > > > > > > In OpenFlow1.1 and 1.2 MPLS tags are pushed after any VLAN tags that
> > > > > > > > > immediately follow the ethernet header. This is pre-OpenFlow1.3 tag
> > > > > > > > > ordering. Open vSwitch also uses this ordering when supporting MPLS
> > > > > > > > > actions via Nicira extensions to OpenFlow1.0.
> > > > > > > > > 
> > > > > > > > > When using pre-OpenFlow1.3 tag ordering an MPLS push action does not
> > > > > > > > > affect the VLANs of a packet. If VLAN tags are present immediately after
> > > > > > > > > the ethernet header then they remain present there.
> > > > > > > > > 
> > > > > > > > > In of OpenFlow1.3+ MPLS LSEs are pushed before any VLAN tags that
> > > > > > > > > immediately follow the ethernet header. This is OpenFlow1.3+ tag
> > > > > > > > > ordering.
> > > > > > > > > 
> > > > > > > > > When using OpenFlow1.3+ tag ordering an MPLS push action affects the
> > > > > > > > > VLANs of a packet as any VLAN tags previously present after the ethernet
> > > > > > > > > header are moved to be immediately after the newly pushed MPLS LSE. Thus
> > > > > > > > > for the purpose of action consistency checking a packet may be changed
> > > > > > > > > from a VLAN packet to a non-VLAN packet.
> > > > > > > > > 
> > > > > > > > > In this way the effective value of the VLAN TCI of a packet may differ
> > > > > > > > > after an MPLS push depending on the OpenFlow version in use.
> > > > > > > > > 
> > > > > > > > > This patch does not enable the logic described above.
> > > > > > > > > Rather it is disabled in ofpacts_check__(). It should
> > > > > > > > > be enabled when support for OpenFlow1.3+ tag order is added
> > > > > > > > > and enabled.
> > > > > > > > > 
> > > > > > > > > Signed-off-by: Simon Horman <horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
> > > > > > > > 
> > > > > > > > As far as I can tell this doesn't make sense, because where the MPLS
> > > > > > > > tag goes is a property of the action that we know at the time we parse
> > > > > > > > the push_mpls action.  So why isn't this patch just the following?
> > > > > > > > 
> > > > > > > > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > > > > > > > index a02f842..f444374 100644
> > > > > > > > --- a/lib/ofp-actions.c
> > > > > > > > +++ b/lib/ofp-actions.c
> > > > > > > > @@ -2071,6 +2071,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
> > > > > > > >           * Thus nothing can be assumed about the network protocol.
> > > > > > > >           * Temporarily mark that we have no nw_proto. */
> > > > > > > >          flow->nw_proto = 0;
> > > > > > > > +        if (ofpact_get_PUSH_MPLS(a)->position == OFPACT_MPLS_BEFORE_VLAN) {
> > > > > > > > +            flow->vlan_tci = 0;
> > > > > > > > +        }
> > > > > > > >          return 0;
> > > > > > > 
> > > > > > > That was more or less what I originally tried.  However I believe that it
> > > > > > > doesn't work because ofpact_get_PUSH_MPLS(a)->position may not have been
> > > > > > > set at the time that ofpact_check__ is called.  In particular this occurs
> > > > > > > when it is called indirectly from parse_ofp_str__.
> > > > > > > 
> > > > > > > Moreover, when ofpact_check__ is called indirectly from parse_ofp_str__ it
> > > > > > > is used to check actions when a one of number of protocols may be used,
> > > > > > > that is multiple bits of *usable_protocols.  If we could rely on
> > > > > > > ofpact_get_PUSH_MPLS(a)->position then I believe that implies that if it is
> > > > > > > set to OFPACT_MPLS_BEFORE_VLAN all pre-OpenFlow1.3 bits of
> > > > > > > *usable_protocols need to be cleared.  Otherwise all OpenFlow1.3+ bits
> > > > > > > would need to be cleared.
> > > > > > 
> > > > > > I think this might be a mistake in how we define the syntax that
> > > > > > parse_ofp_str__() parses.  If I write "actions=push_mpls" on an
> > > > > > ovs-ofctl command line, then I want that to have some specific
> > > > > > meaning.  I don't want it to mean "do one thing if you happen to
> > > > > > negotiate OpenFlow 1.2 or some other thing if you happen to negotiate
> > > > > > OpenFlow 1.3", because that's totally unusable and broken from a user
> > > > > > perspective.
> > > > > 
> > > > > To clarify, that is exactly what this series was trying to do.
> > > > > 
> > > > > I think there is some precedence in the handling of actions
> > > > > that set_vlans. Some OF versions implicitly push a tag, some don't.
> > > > 
> > > > Maybe that is worth fixing too.
> > > > 
> > > > > But I do agree that the behaviour you describe above would
> > > > > be very confusing for users.
> > > > > 
> > > > > > So if that the issue then I think we should change the
> > > > > > syntax.  One way would be to have "push_mpls" default to the 1.3
> > > > > > behavior (which seems generally saner) and allow the user to specify
> > > > > > an option to get the 1.2 behavior.
> > > > > 
> > > > > Sure, I think I am happy with that idea.
> > > > > 
> > > > > By an option do you mean a different action name, for example append_mpls,
> > > > > or push_mpls_after_vlan?
> > > > 
> > > > I was thinking of something like a push_mpls version of the
> > > > keyword-based fin_timeout syntax.  One option would be eth_type,
> > > > defaulting to ETH_TYPE_MPLS.  Another option would be position, with
> > > > after_vlan or before_vlan as allowed values, and probably after_vlan
> > > > as the default.
> > > > 
> > > > With this approach, any flow with a push_mpls could be used only with
> > > > pre-OF1.3 or OF1.3+, depending on the "position" value.  One wrinkle
> > > > that might be nice, if it isn't too nasty to implement, would be to
> > > > have a third value "no_vlan" as the default.  With no_vlan, we reject
> > > > the flow at check time if a VLAN is present; if no VLAN is present,
> > > > then push_mpls has the same behavior regardless of OpenFlow version.
> > > 
> > > From an ovs-ofctl point of view I think that makes a lot of sense and I
> > > think it should be clean enough to implement. My initial reaction is that
> > > using a position argument would be good and I don't see any particular
> > > problem with a no_vlan option. But I'll give some thought to an eth_type
> > > argument.
> > 
> > push_mpls currently takes a required eth_type argument.  There are only
> > two values and my understanding is that 0x8847 is more common, hence my
> > suggestion that it be the default.
> 
> I'm not sure that we can use the eth_type argument of an mpls_push action
> in order to differentiate between pushing the LSE before or after
> any VLAN tags that may be present. There are two acceptable values for the
> eth_type argument, 0x8847 and 0x8848. But I believe they are both
> equally acceptable regardless of where the LSE is to be pushed
> in relation to existing VLAN tags.

The eth_type is irrelevant to the change we're making, but there has to
be some way to specify it after the change, just as there is before the
change, so I'm suggesting a way.

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2013-12-06  4:21 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-21  3:46 [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
     [not found] ` <1385005606-30130-1-git-send-email-horms-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
2013-11-21  3:46   ` [PATCH v2.51 1/5] ofp-actions: Allow Consistency checking of OF1.3+ VLAN actions after mpls_push Simon Horman
2013-12-04 21:24     ` Ben Pfaff
2013-12-04 23:58       ` Simon Horman
2013-12-05  0:44         ` Ben Pfaff
2013-12-05  0:51           ` Simon Horman
2013-12-05  1:01             ` Ben Pfaff
     [not found]               ` <20131205010111.GE11354-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>
2013-12-05  2:26                 ` Simon Horman
2013-12-05 17:56                   ` Ben Pfaff
2013-12-06  3:29                     ` Simon Horman
     [not found]                       ` <20131206032924.GA20522-/R6kz+dDXgpPR4JQBCEnsQ@public.gmane.org>
2013-12-06  4:21                         ` Ben Pfaff
2013-11-21  3:46   ` [PATCH v2.51 2/5] odp: Allow VLAN actions after MPLS actions Simon Horman
2013-11-21  3:46   ` [PATCH v2.51 3/5] lib: Support pushing of MPLS LSE before or after VLAN tag Simon Horman
2013-11-21  3:46 ` [PATCH v2.51 4/5] datapath: Break out deacceleration portion of vlan_push Simon Horman
2013-11-21  3:46 ` [PATCH v2.51 5/5] datapath: Add basic MPLS support to kernel Simon Horman
2013-11-26  8:08 ` [ovs-dev] [PATCH v2.51 0/5] MPLS actions and matches Simon Horman
2013-11-26 15:33   ` Ben Pfaff
2013-11-27  0:12     ` Simon Horman
2013-12-04  9:16       ` Simon Horman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).