[ovs-dev] [PATCH v5 1/2] OF support and translation of generic encap and decap

Yi Yang yi.y.yang at intel.com
Wed Aug 2 08:04:12 UTC 2017


From: Jan Scheurich <jan.scheurich at ericsson.com>

This commit adds support for the OpenFlow actions generic encap
and decap (as specified in ONF EXT-382) to the OVS control plane.

CLI syntax for encap action with properties:
  encap(<header>)
  encap(<header>(<prop>=<value>,<tlv>(<class>,<type>,<value>),...))

For example:
  encap(ethernet)
  encap(nsh(md_type=1))
  encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210)))

CLI syntax for decap action:
  decap()
  decap(packet_type(ns=<pt_ns>,type=<pt_type>))

For example:
  decap()
  decap(packet_type(ns=0,type=0xfffe))
  decap(packet_type(ns=1,type=0x894f))

The first header supported for encap and decap is "ethernet" to convert
packets between packet_type (1,Ethertype) and (0,0).

This commit also implements a skeleton for the translation of generic
encap and decap actions in ofproto-dpif and adds support to encap and
decap an Ethernet header.

In general translation of encap commits pending actions and then rewrites
struct flow in accordance with the new packet type and header. In the
case of encap(ethernet) it suffices to change the packet type from
(1, Ethertype) to (0,0) and set the dl_type accordingly. A new
pending_encap flag in xlate ctx is set to mark that an corresponding
datapath encap action must be triggered at the next commit. In the
case of encap(ethernet) ofproto generetas a push_eth action.

The general case for translation of decap() is to emit a datapath action
to decap the current outermost header and then recirculate the packet
to reparse the inner headers. In the special case of an Ethernet packet,
decap() just changes the packet type from (0,0) to (1, dl_type) without
a need to recirculate. The emission of the pop_eth action for the
datapath is postponed to the next commit.

Hence encap(ethernet) and decap() on an Ethernet packet are OF octions
that only incur a cost in the dataplane when a modifed packet is
actually committed, e.g. because it is sent out. They can freely be
used for normalizing the packet type in the OF pipeline without
degrading performance.

Signed-off-by: Jan Scheurich <jan.scheurich at ericsson.com>
Signed-off-by: Yi Yang <yi.y.yang at intel.com>
Signed-off-by: Zoltan Balogh <zoltan.balogh at ericsson.com>
Co-authored-by: Zoltan Balogh <zoltan.balogh at ericsson.com>
Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 NEWS                               |   6 +
 include/openflow/openflow-common.h |   1 +
 include/openvswitch/automake.mk    |   1 +
 include/openvswitch/ofp-actions.h  |  32 ++++
 include/openvswitch/ofp-ed-props.h |  77 ++++++++
 include/openvswitch/ofp-errors.h   |   9 +
 lib/automake.mk                    |   1 +
 lib/odp-util.c                     |  84 ++++++---
 lib/odp-util.h                     |   3 +-
 lib/ofp-actions.c                  | 364 ++++++++++++++++++++++++++++++++++++-
 lib/ofp-ed-props.c                 | 157 ++++++++++++++++
 lib/packets.h                      |   3 +-
 ofproto/ofproto-dpif-xlate.c       | 116 +++++++++++-
 utilities/ovs-ofctl.8.in           |  76 +++++++-
 14 files changed, 883 insertions(+), 47 deletions(-)
 create mode 100644 include/openvswitch/ofp-ed-props.h
 create mode 100644 lib/ofp-ed-props.c

diff --git a/NEWS b/NEWS
index facea02..ca02ca7 100644
--- a/NEWS
+++ b/NEWS
@@ -62,11 +62,17 @@ Post-v2.7.0
      * The "learn" action now supports a "limit" option (see ovs-ofctl(8)).
      * The port status bit OFPPS_LIVE now reflects link aliveness.
      * OpenFlow 1.5 packet-out is now supported.
+     * Support for OpenFlow 1.5 field packet_type and packet-type-aware
+       pipeline (PTAP).
+     * Added generic encap and decap actions (EXT-382).
+       First supported use case is encap/decap for Ethernet.
    - Fedora Packaging:
      * OVN services are no longer restarted automatically after upgrade.
    - Add --cleanup option to command 'ovs-appctl exit' (see ovs-vswitchd(8)).
    - L3 tunneling:
      * Use new tunnel port option "packet_type" to configure L2 vs. L3.
+     * In conjunction with PTAP tunnel ports can handle a mix of L2 and L3
+       payload.
      * New vxlan tunnel extension "gpe" to support VXLAN-GPE tunnels.
      * New support for non-Ethernet (L3) payloads in GRE and VXLAN-GPE.
    - The BFD detection multiplier is now user-configurable.
diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h
index 5f1e225..d665519 100644
--- a/include/openflow/openflow-common.h
+++ b/include/openflow/openflow-common.h
@@ -466,6 +466,7 @@ enum ofp_header_type_namespaces {
     OFPHTN_IP_PROTO = 2,        /* ns_type is a IP protocol number. */
     OFPHTN_UDP_TCP_PORT = 3,    /* ns_type is a TCP or UDP port. */
     OFPHTN_IPV4_OPTION = 4,     /* ns_type is an IPv4 option number. */
+    OFPHTN_N_TYPES
 };
 
 #endif /* openflow/openflow-common.h */
diff --git a/include/openvswitch/automake.mk b/include/openvswitch/automake.mk
index 6bace61..0a139d0 100644
--- a/include/openvswitch/automake.mk
+++ b/include/openvswitch/automake.mk
@@ -12,6 +12,7 @@ openvswitchinclude_HEADERS = \
 	include/openvswitch/meta-flow.h \
 	include/openvswitch/ofpbuf.h \
 	include/openvswitch/ofp-actions.h \
+	include/openvswitch/ofp-ed-props.h \
 	include/openvswitch/ofp-errors.h \
 	include/openvswitch/ofp-msgs.h \
 	include/openvswitch/ofp-parse.h \
diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index 198107e..4357f4e 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -25,6 +25,7 @@
 #include "openvswitch/ofp-util.h"
 #include "openvswitch/ofp-errors.h"
 #include "openvswitch/types.h"
+#include "openvswitch/ofp-ed-props.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -93,6 +94,10 @@ struct vl_mff_map;
     OFPACT(PUSH_MPLS,       ofpact_push_mpls,   ofpact, "push_mpls")    \
     OFPACT(POP_MPLS,        ofpact_pop_mpls,    ofpact, "pop_mpls")     \
                                                                         \
+    /* Generic encap & decap */                                         \
+    OFPACT(ENCAP,           ofpact_encap,       props, "encap")         \
+    OFPACT(DECAP,           ofpact_decap,       ofpact, "decap")        \
+                                                                        \
     /* Metadata. */                                                     \
     OFPACT(SET_TUNNEL,      ofpact_tunnel,      ofpact, "set_tunnel")   \
     OFPACT(SET_QUEUE,       ofpact_queue,       ofpact, "set_queue")    \
@@ -974,6 +979,33 @@ struct ofpact_unroll_xlate {
     ovs_be64 rule_cookie;         /* OVS_BE64_MAX if none. */
 };
 
+/* OFPACT_ENCAP.
+ *
+ * Used for NXAST_ENCAP. */
+
+struct ofpact_encap {
+    struct ofpact ofpact;
+    ovs_be32 new_pkt_type;        /* Packet type of the header to add. */
+    uint16_t hdr_size;            /* New header size in bytes. */
+    uint16_t n_props;             /* Number of encap properties. */
+    struct ofpact_ed_prop props[]; /* Properties in internal format. */
+};
+
+/* OFPACT_DECAP.
+ *
+ * Used for NXAST_DECAP. */
+struct ofpact_decap {
+    struct ofpact ofpact;
+
+    /* New packet type.
+     *
+     * The special value (0,0xFFFE) "Use next proto" is used to request OVS to
+     * automatically set the new packet type based on the decap'ed header's
+     * next protocol.
+     */
+    ovs_be32 new_pkt_type;
+};
+
 /* Converting OpenFlow to ofpacts. */
 enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
                                           unsigned int actions_len,
diff --git a/include/openvswitch/ofp-ed-props.h b/include/openvswitch/ofp-ed-props.h
new file mode 100644
index 0000000..cf2fa62
--- /dev/null
+++ b/include/openvswitch/ofp-ed-props.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 Intel, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OPENVSWITCH_OFP_ED_PROPS_H
+#define OPENVSWITCH_OFP_ED_PROPS_H 1
+
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/types.h"
+#include "openvswitch/ofpbuf.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+enum ofp_ed_prop_class {
+    OFPPPC_BASIC = 0,            /* ONF Basic class. */
+    OFPPPC_MPLS  = 1,            /* MPLS property  class. */
+    OFPPPC_GRE   = 2,            /* GRE property  class. */
+    OFPPPC_GTP   = 3,            /* GTP property  class. */
+
+    /* Experimenter property class.
+     *
+     * First 32 bits of property data
+     * is exp id after that is the experimenter property data.
+     */
+    OFPPPC_EXPERIMENTER=0xffff
+};
+
+/*
+ * External representation of encap/decap properties.
+ * These must be padded to a multiple of 8 bytes.
+ */
+struct ofp_ed_prop_header {
+    ovs_be16 prop_class;
+    uint8_t type;
+    uint8_t len;
+};
+
+/*
+ * Internal representation of encap/decap properties
+ */
+struct ofpact_ed_prop {
+    uint16_t prop_class;
+    uint8_t type;
+    uint8_t len;
+};
+
+enum ofperr decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
+                           struct ofpbuf *out, size_t *remaining);
+enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop,
+                           struct ofpbuf *out);
+bool parse_ed_prop_class(const char *str, uint16_t *prop_class);
+bool parse_ed_prop_type(uint16_t prop_class, const char *str, uint8_t *type);
+char *parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type,
+                          const char *str, struct ofpbuf *out);
+char *format_ed_prop_class(const struct ofpact_ed_prop *prop);
+char *format_ed_prop_type(const struct ofpact_ed_prop *prop);
+void format_ed_prop(struct ds *s, const struct ofpact_ed_prop *prop);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* ofp-ed-props.h */
diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h
index b551ce6..3d09be3 100644
--- a/include/openvswitch/ofp-errors.h
+++ b/include/openvswitch/ofp-errors.h
@@ -276,6 +276,15 @@ enum ofperr {
      * 64. */
     OFPERR_NXBAC_BAD_CONJUNCTION,
 
+    /* NX1.3+(39).  Unsupported packet type in encap or decap. */
+    OFPERR_NXBAC_BAD_HEADER_TYPE,
+
+    /* NX1.3+(40).  Unrecognized encap or decap property. */
+    OFPERR_NXBAC_UNKNOWN_ED_PROP,
+
+    /* NX1.3+(41).  Error in encap or decap property. */
+    OFPERR_NXBAC_BAD_ED_PROP,
+
 /* ## --------------------- ## */
 /* ## OFPET_BAD_INSTRUCTION ## */
 /* ## --------------------- ## */
diff --git a/lib/automake.mk b/lib/automake.mk
index 54a1032..bd56f43 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -149,6 +149,7 @@ lib_libopenvswitch_la_SOURCES = \
 	lib/odp-util.c \
 	lib/odp-util.h \
 	lib/ofp-actions.c \
+	lib/ofp-ed-props.c \
 	lib/ofp-errors.c \
 	lib/ofp-msgs.c \
 	lib/ofp-parse.c \
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 9d95d53..6390734 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -5922,13 +5922,17 @@ put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow)
 }
 
 static void
-commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
-                             struct ofpbuf *odp_actions,
-                             struct flow_wildcards *wc,
-                             bool use_masked)
+commit_set_ether_action(const struct flow *flow, struct flow *base_flow,
+                        struct ofpbuf *odp_actions,
+                        struct flow_wildcards *wc,
+                        bool use_masked)
 {
     struct ovs_key_ethernet key, base, mask;
 
+    if (flow->packet_type != htonl(PT_ETH)) {
+        return;
+    }
+
     get_ethernet_key(flow, &key);
     get_ethernet_key(base_flow, &base);
     get_ethernet_key(&wc->masks, &mask);
@@ -5941,29 +5945,6 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
 }
 
 static void
-commit_ether_action(const struct flow *flow, struct flow *base_flow,
-                    struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                    bool use_masked)
-{
-    if (flow->packet_type == htonl(PT_ETH)) {
-        if (base_flow->packet_type != htonl(PT_ETH)) {
-            odp_put_push_eth_action(odp_actions, &flow->dl_src, &flow->dl_dst);
-            base_flow->packet_type = flow->packet_type;
-            base_flow->dl_src = flow->dl_src;
-            base_flow->dl_dst = flow->dl_dst;
-        } else {
-            commit_set_ether_addr_action(flow, base_flow, odp_actions, wc,
-                                         use_masked);
-        }
-    } else {
-        if (base_flow->packet_type == htonl(PT_ETH)) {
-            odp_put_pop_eth_action(odp_actions);
-            base_flow->packet_type = flow->packet_type;
-        }
-    }
-}
-
-static void
 commit_vlan_action(const struct flow* flow, struct flow *base,
                    struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
@@ -6400,6 +6381,50 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
     }
 }
 
+static void
+commit_packet_type_change(const struct flow *flow,
+                          struct flow *base_flow,
+                          struct ofpbuf *odp_actions,
+                          struct flow_wildcards *wc,
+                          bool pending_encap)
+{
+    if (flow->packet_type == base_flow->packet_type) {
+        return;
+    }
+
+    if (pending_encap) {
+        switch (ntohl(flow->packet_type)) {
+        case PT_ETH: {
+            /* push_eth */
+            odp_put_push_eth_action(odp_actions, &flow->dl_src,
+                                    &flow->dl_dst);
+            base_flow->packet_type = flow->packet_type;
+            base_flow->dl_src = flow->dl_src;
+            base_flow->dl_dst = flow->dl_dst;
+            break;
+        }
+        default:
+            /* Only the above protocols are supported for encap. The check
+             * is done at action decoding. */
+            OVS_NOT_REACHED();
+        }
+    } else {
+        if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE &&
+            base_flow->packet_type == htonl(PT_ETH)) {
+            /* pop_eth */
+            odp_put_pop_eth_action(odp_actions);
+            base_flow->packet_type = flow->packet_type;
+            base_flow->dl_src = eth_addr_zero;
+            base_flow->dl_dst = eth_addr_zero;
+        } else {
+            /* All other cases are handled through recirculation. */
+            OVS_NOT_REACHED();
+        }
+    }
+
+    wc->masks.packet_type = OVS_BE32_MAX;
+}
+
 /* If any of the flow key data that ODP actions can modify are different in
  * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
  * key from 'base' into 'flow', and then changes 'base' the same way.  Does not
@@ -6412,12 +6437,13 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
 enum slow_path_reason
 commit_odp_actions(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                   bool use_masked)
+                   bool use_masked, bool pending_encap)
 {
     enum slow_path_reason slow1, slow2;
     bool mpls_done = false;
 
-    commit_ether_action(flow, base, odp_actions, wc, use_masked);
+    commit_packet_type_change(flow, base, odp_actions, wc, pending_encap);
+    commit_set_ether_action(flow, base, odp_actions, wc, use_masked);
     /* Make packet a non-MPLS packet before committing L3/4 actions,
      * which would otherwise do nothing. */
     if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 224f78e..bdb2354 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -276,7 +276,8 @@ enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct flow *base,
                                          struct ofpbuf *odp_actions,
                                          struct flow_wildcards *wc,
-                                         bool use_masked);
+                                         bool use_masked,
+                                         bool pending_encap);
 
 /* ofproto-dpif interface.
  *
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index ae27d9d..16da78c 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -342,6 +342,12 @@ enum ofp_raw_action_type {
     /* NX1.0+(43): void. */
     NXAST_RAW_CT_CLEAR,
 
+    /* NX1.3+(46): struct nx_action_encap, ... */
+    NXAST_RAW_ENCAP,
+
+    /* NX1.3+(47): struct nx_action_decap, ... */
+    NXAST_RAW_DECAP,
+
 /* ## ------------------ ## */
 /* ## Debugging actions. ## */
 /* ## ------------------ ## */
@@ -472,6 +478,8 @@ ofpact_next_flattened(const struct ofpact *ofpact)
     case OFPACT_WRITE_METADATA:
     case OFPACT_GOTO_TABLE:
     case OFPACT_NAT:
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
         return ofpact_next(ofpact);
 
     case OFPACT_CLONE:
@@ -4019,6 +4027,304 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a,
     ds_chomp(s, ',');
     ds_put_format(s, "%s)%s", colors.paren, colors.end);
 }
+
+/* Action structure for NXAST_ENCAP */
+struct nx_action_encap {
+    ovs_be16 type;         /* OFPAT_VENDOR. */
+    ovs_be16 len;          /* Total size including any property TLVs. */
+    ovs_be32 vendor;       /* NX_VENDOR_ID. */
+    ovs_be16 subtype;      /* NXAST_ENCAP. */
+    ovs_be16 hdr_size;     /* Header size in bytes, 0 = 'not specified'.*/
+    ovs_be32 new_pkt_type; /* Header type to add and PACKET_TYPE of result. */
+    struct ofp_ed_prop_header props[];  /* Encap TLV properties. */
+};
+OFP_ASSERT(sizeof(struct nx_action_encap) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
+                       enum ofp_version ofp_version OVS_UNUSED,
+                       struct ofpbuf *out)
+{
+    struct ofpact_encap *encap;
+    const struct ofp_ed_prop_header *ofp_prop;
+    size_t props_len;
+    uint16_t n_props = 0;
+    int err;
+
+    encap = ofpact_put_ENCAP(out);
+    encap->ofpact.raw = NXAST_RAW_ENCAP;
+    switch (ntohl(nae->new_pkt_type)) {
+    case PT_ETH:
+        /* Add supported encap header types here. */
+        break;
+    default:
+        return OFPERR_NXBAC_BAD_HEADER_TYPE;
+    }
+    encap->new_pkt_type = nae->new_pkt_type;
+    encap->hdr_size = ntohs(nae->hdr_size);
+
+    ofp_prop = nae->props;
+    props_len = ntohs(nae->len) - offsetof(struct nx_action_encap, props);
+    n_props = 0;
+    while (props_len > 0) {
+        err = decode_ed_prop(&ofp_prop, out, &props_len);
+        if (err) {
+            return err;
+        }
+        n_props++;
+    }
+    encap->n_props = n_props;
+    out->header = &encap->ofpact;
+    ofpact_finish_ENCAP(out, &encap);
+
+    return 0;
+}
+
+static void
+encode_ENCAP(const struct ofpact_encap *encap,
+             enum ofp_version ofp_version OVS_UNUSED,
+             struct ofpbuf *out)
+{
+    size_t start_ofs = out->size;
+    struct nx_action_encap *nae = put_NXAST_ENCAP(out);
+    int i;
+
+    nae->new_pkt_type = encap->new_pkt_type;
+    nae->hdr_size = htons(encap->hdr_size);
+    const struct ofpact_ed_prop *prop = encap->props;
+    for (i = 0; i < encap->n_props; i++) {
+        encode_ed_prop(&prop, out);
+    }
+    pad_ofpat(out, start_ofs);
+}
+
+static bool
+parse_encap_header(const char *hdr, ovs_be32 *packet_type)
+{
+    if (strcmp(hdr, "ethernet") == 0) {
+        *packet_type = htonl(PT_ETH);
+    } else {
+        return false;
+    }
+    return true;
+}
+
+static char *
+parse_ed_props(const uint16_t prop_class, char **arg, int *n_props, struct ofpbuf *out)
+{
+    char *key, *value, *err;
+    uint8_t prop_type;
+
+    while (ofputil_parse_key_value(arg, &key, &value)) {
+        if (!parse_ed_prop_type(prop_class, key, &prop_type)) {
+            return xasprintf("Invalid property: %s", key);
+        }
+        if (value == NULL) {
+            return xasprintf("Missing the value for property: %s", key);
+        }
+        err = parse_ed_prop_value(prop_class, prop_type, value, out);
+        if (err != NULL) {
+            return err;
+        }
+        (*n_props)++;
+    }
+    return NULL;
+}
+
+/* The string representation of the encap action is
+ * encap(header_type(prop=<value>,tlv(<class>,<type>,<value>),...))
+ */
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_ENCAP(char *arg,
+            const struct ofputil_port_map *port_map OVS_UNUSED,
+            struct ofpbuf *out,
+            enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    struct ofpact_encap *encap;
+    char *key, *value, *str;
+    char *error = NULL;
+    uint16_t prop_class;
+    int n_props = 0;
+
+    encap = ofpact_put_ENCAP(out);
+    encap->hdr_size = 0;
+    /* Parse encap header type. */
+    str = arg;
+    if (!ofputil_parse_key_value(&arg, &key, &value)) {
+        return xasprintf("Missing encap hdr: %s", str);
+    }
+    if (!parse_encap_header(key, &encap->new_pkt_type)) {
+        return xasprintf("Encap hdr not supported: %s", value);
+    }
+    if (!parse_ed_prop_class(key, &prop_class)) {
+        return xasprintf("Invalid encap prop class: %s", key);
+    }
+    /* Parse encap properties. */
+    error = parse_ed_props(prop_class, &value, &n_props, out);
+    if (error != NULL) {
+        return error;
+    }
+    /* ofbuf out may have been re-allocated. */
+    encap = out->header;
+    encap->n_props = n_props;
+    ofpact_finish_ENCAP(out, &encap);
+    return NULL;
+}
+
+static char *
+format_encap_pkt_type(const ovs_be32 pkt_type)
+{
+    switch (ntohl(pkt_type)) {
+    case PT_ETH:
+        return "ethernet";
+    default:
+        return "UNKNOWN";
+    }
+}
+
+static void
+format_ed_props(struct ds *s, uint16_t n_props,
+                const struct ofpact_ed_prop *prop)
+{
+    const uint8_t *p = (uint8_t *) prop;
+    int i;
+
+    if (n_props == 0) {
+        return;
+    }
+    for (i = 0; i < n_props; i++) {
+        format_ed_prop(s, prop);
+        ds_put_char(s, ',');
+        p += ROUND_UP(prop->len, 8);
+        prop = ALIGNED_CAST(const struct ofpact_ed_prop *, p);
+    }
+    if (n_props > 0) {
+        ds_chomp(s, ',');
+    }
+}
+
+static void
+format_ENCAP(const struct ofpact_encap *a,
+             const struct ofputil_port_map *port_map OVS_UNUSED,
+             struct ds *s)
+{
+    ds_put_format(s, "%sencap(%s", colors.paren, colors.end);
+    ds_put_format(s, "%s", format_encap_pkt_type(a->new_pkt_type));
+    if (a->n_props > 0) {
+        ds_put_format(s, "%s(%s", colors.paren, colors.end);
+        format_ed_props(s, a->n_props, a->props);
+        ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    }
+    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
+/* Action structure for NXAST_DECAP */
+struct nx_action_decap {
+    ovs_be16 type;         /* OFPAT_VENDOR. */
+    ovs_be16 len;          /* Total size including any property TLVs. */
+    ovs_be32 vendor;       /* NX_VENDOR_ID. */
+    ovs_be16 subtype;      /* NXAST_DECAP. */
+    uint8_t pad[2];        /* 2 bytes padding */
+
+    /* Packet type or result.
+     *
+     * The special value (0,0xFFFE) "Use next proto"
+     * is used to request OVS to automatically set the new packet type based
+     * on the decap'ed header's next protocol.
+     */
+    ovs_be32 new_pkt_type;
+};
+OFP_ASSERT(sizeof(struct nx_action_decap) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_DECAP(const struct nx_action_decap *nad,
+                       enum ofp_version ofp_version OVS_UNUSED,
+                       struct ofpbuf *ofpacts)
+{
+    struct ofpact_decap * decap;
+
+    if (ntohs(nad->len) > sizeof *nad) {
+        /* No properties supported yet. */
+        return OFPERR_NXBAC_UNKNOWN_ED_PROP;
+    }
+
+    decap = ofpact_put_DECAP(ofpacts);
+    decap->ofpact.raw = NXAST_RAW_DECAP;
+    decap->new_pkt_type = nad->new_pkt_type;
+    return 0;
+}
+
+static void
+encode_DECAP(const struct ofpact_decap *decap,
+                enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+{
+    struct nx_action_decap *nad = put_NXAST_DECAP(out);
+
+    nad->len = htons(sizeof(struct nx_action_decap));
+    nad->new_pkt_type = decap->new_pkt_type;
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_DECAP(char *arg,
+            const struct ofputil_port_map *port_map OVS_UNUSED,
+            struct ofpbuf *ofpacts,
+            enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    struct ofpact_decap *decap;
+    char *key, *value, *pos;
+    char *error = NULL;
+    uint16_t ns, type;
+
+    decap = ofpact_put_DECAP(ofpacts);
+    /* Default next packet_type is PT_USE_NEXT_PROTO. */
+    decap->new_pkt_type = htonl(PT_USE_NEXT_PROTO);
+
+    /* Parse decap packet_type if given. */
+    if (ofputil_parse_key_value(&arg, &key, &value)) {
+        if (strcmp(key, "packet_type") == 0) {
+            pos = value;
+            if (!ofputil_parse_key_value(&pos, &key, &value)
+                || strcmp(key, "ns") != 0) {
+                return xstrdup("Missing packet_type attribute ns");
+            }
+            error = str_to_u16(value, "ns", &ns);
+            if (error) {
+                return error;
+            }
+            if (ns >= OFPHTN_N_TYPES) {
+                return xasprintf("Unsupported ns value: %"PRIu16, ns);
+            }
+            if (!ofputil_parse_key_value(&pos, &key, &value)
+                || strcmp(key, "type") != 0) {
+                return xstrdup("Missing packet_type attribute type");
+            }
+            error = str_to_u16(value, "type", &type);
+            if (error) {
+                return error;
+            }
+            decap->new_pkt_type = htonl(PACKET_TYPE(ns, type));
+        } else {
+            return xasprintf("Invalid decap argument: %s", key);
+        }
+    }
+    return NULL;
+}
+
+static void
+format_DECAP(const struct ofpact_decap *a,
+             const struct ofputil_port_map *port_map OVS_UNUSED,
+             struct ds *s)
+{
+    ds_put_format(s, "%sdecap(%s", colors.paren, colors.end);
+    if (a->new_pkt_type != htonl(PT_USE_NEXT_PROTO)) {
+        ds_put_format(s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")",
+                      pt_ns(a->new_pkt_type),
+                      pt_ns_type(a->new_pkt_type));
+    }
+    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
 
 /* Action structures for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE, and
  * NXAST_RESUBMIT_TABLE_CT.
@@ -6802,6 +7108,8 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
     case OFPACT_SET_TUNNEL:
     case OFPACT_SET_VLAN_PCP:
     case OFPACT_SET_VLAN_VID:
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
         return true;
     case OFPACT_BUNDLE:
     case OFPACT_CLEAR_ACTIONS:
@@ -6877,6 +7185,8 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
     case OFPACT_SET_VLAN_PCP:
     case OFPACT_SET_VLAN_VID:
     case OFPACT_STRIP_VLAN:
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
         return true;
 
     /* In general these actions are excluded because they are not part of
@@ -6984,6 +7294,8 @@ ofpacts_execute_action_set(struct ofpbuf *action_list,
     /* The OpenFlow spec "Action Set" section specifies this order. */
     ofpacts_copy_last(action_list, action_set, OFPACT_STRIP_VLAN);
     ofpacts_copy_last(action_list, action_set, OFPACT_POP_MPLS);
+    ofpacts_copy_last(action_list, action_set, OFPACT_DECAP);
+    ofpacts_copy_last(action_list, action_set, OFPACT_ENCAP);
     ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_MPLS);
     ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_VLAN);
     ofpacts_copy_last(action_list, action_set, OFPACT_DEC_TTL);
@@ -7127,6 +7439,8 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
     case OFPACT_CT:
     case OFPACT_CT_CLEAR:
     case OFPACT_NAT:
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
     default:
         return OVSINST_OFPIT11_APPLY_ACTIONS;
     }
@@ -7488,8 +7802,8 @@ inconsistent_match(enum ofputil_protocol *usable_protocols)
     *usable_protocols &= OFPUTIL_P_OF10_ANY;
 }
 
-/* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci,
- * caller must restore them.
+/* May modify flow->packet_type, flow->dl_type, flow->nw_proto and
+ * flow->vlan_tci, caller must restore them.
  *
  * Modifies some actions, filling in fields that could not be properly set
  * without context. */
@@ -7501,6 +7815,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     struct flow *flow = &match->flow;
     const struct ofpact_enqueue *enqueue;
     const struct mf_field *mf;
+    ovs_be16 dl_type = get_dl_type(flow);
 
     switch (a->type) {
     case OFPACT_OUTPUT:
@@ -7578,7 +7893,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
 
     case OFPACT_SET_IPV4_SRC:
     case OFPACT_SET_IPV4_DST:
-        if (flow->dl_type != htons(ETH_TYPE_IP)) {
+        if (dl_type != htons(ETH_TYPE_IP)) {
             inconsistent_match(usable_protocols);
         }
         return 0;
@@ -7643,7 +7958,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
-        if (!eth_type_mpls(flow->dl_type)) {
+        if (!eth_type_mpls(dl_type)) {
             inconsistent_match(usable_protocols);
         }
         return 0;
@@ -7681,6 +7996,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
         return 0;
 
     case OFPACT_PUSH_MPLS:
+        if (flow->packet_type != htonl(PT_ETH)) {
+            inconsistent_match(usable_protocols);
+        }
         flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype;
         /* The packet is now MPLS and the MPLS payload is opaque.
          * Thus nothing can be assumed about the network protocol.
@@ -7689,7 +8007,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
         return 0;
 
     case OFPACT_POP_MPLS:
-        if (!eth_type_mpls(flow->dl_type)) {
+        if (flow->packet_type != htonl(PT_ETH)
+            || !eth_type_mpls(dl_type)) {
             inconsistent_match(usable_protocols);
         }
         flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype;
@@ -7708,7 +8027,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_CT: {
         struct ofpact_conntrack *oc = ofpact_get_CT(a);
 
-        if (!dl_type_is_ip_any(flow->dl_type)
+        if (!dl_type_is_ip_any(dl_type)
             || (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT)
             || (oc->alg == IPPORT_FTP && flow->nw_proto != IPPROTO_TCP)
             || (oc->alg == IPPORT_TFTP && flow->nw_proto != IPPROTO_UDP)) {
@@ -7733,10 +8052,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_NAT: {
         struct ofpact_nat *on = ofpact_get_NAT(a);
 
-        if (!dl_type_is_ip_any(flow->dl_type) ||
-            (on->range_af == AF_INET && flow->dl_type != htons(ETH_TYPE_IP)) ||
+        if (!dl_type_is_ip_any(dl_type) ||
+            (on->range_af == AF_INET && dl_type != htons(ETH_TYPE_IP)) ||
             (on->range_af == AF_INET6
-             && flow->dl_type != htons(ETH_TYPE_IPV6))) {
+             && dl_type != htons(ETH_TYPE_IPV6))) {
             return OFPERR_OFPBAC_MATCH_INCONSISTENT;
         }
         return 0;
@@ -7785,6 +8104,29 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_DEBUG_RECIRC:
         return 0;
 
+    case OFPACT_ENCAP:
+        flow->packet_type = ofpact_get_ENCAP(a)->new_pkt_type;
+        if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+            flow->dl_type = htons(pt_ns_type(flow->packet_type));
+        }
+        if (!is_ip_any(flow)) {
+            flow->nw_proto = 0;
+        }
+        return 0;
+
+    case OFPACT_DECAP:
+        if (flow->packet_type == htonl(PT_ETH)) {
+            /* Adjust the packet_type to allow subsequent actions. */
+            flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
+                                               ntohs(flow->dl_type));
+        } else {
+            /* The actual packet_type is only known after decapsulation.
+             * Do not allow subsequent actions that depend on packet headers. */
+            flow->packet_type = htonl(PT_UNKNOWN);
+            flow->dl_type = OVS_BE16_MAX;
+        }
+        return 0;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -7810,6 +8152,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
               enum ofputil_protocol *usable_protocols)
 {
     struct ofpact *a;
+    ovs_be32 packet_type = match->flow.packet_type;
     ovs_be16 dl_type = match->flow.dl_type;
     uint8_t nw_proto = match->flow.nw_proto;
     enum ofperr error = 0;
@@ -7825,6 +8168,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
         }
     }
     /* Restore fields that may have been modified. */
+    match->flow.packet_type = packet_type;
     match->flow.dl_type = dl_type;
     memcpy(&match->flow.vlans, &vlans, sizeof(vlans));
     match->flow.nw_proto = nw_proto;
@@ -8276,6 +8620,8 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_CT:
     case OFPACT_CT_CLEAR:
     case OFPACT_NAT:
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
     default:
         return false;
     }
diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c
new file mode 100644
index 0000000..a346138
--- /dev/null
+++ b/lib/ofp-ed-props.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2017 Intel, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+#include "openvswitch/ofp-ed-props.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/ofp-parse.h"
+#include "util.h"
+#include "lib/packets.h"
+
+
+enum ofperr
+decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
+               struct ofpbuf *out OVS_UNUSED,
+               size_t *remaining)
+{
+    uint16_t prop_class = ntohs((*ofp_prop)->prop_class);
+    size_t len = (*ofp_prop)->len;
+    size_t pad_len = ROUND_UP(len, 8);
+
+    if (pad_len > *remaining) {
+        return OFPERR_OFPBAC_BAD_LEN;
+    }
+
+    switch (prop_class) {
+    default:
+        return OFPERR_NXBAC_UNKNOWN_ED_PROP;
+    }
+
+    *remaining -= pad_len;
+    *ofp_prop = ALIGNED_CAST(const struct ofp_ed_prop_header *,
+                             ((char *)(*ofp_prop) + pad_len));
+    return 0;
+}
+
+enum ofperr
+encode_ed_prop(const struct ofpact_ed_prop **prop,
+               struct ofpbuf *out OVS_UNUSED)
+{
+    size_t prop_len;
+
+    switch ((*prop)->prop_class) {
+    default:
+        return OFPERR_OFPBAC_BAD_ARGUMENT;
+    }
+
+    *prop = ALIGNED_CAST(const struct ofpact_ed_prop *,
+                         ((char *)(*prop) + prop_len));
+    return 0;
+}
+
+bool
+parse_ed_prop_class(const char *str OVS_UNUSED,
+                    uint16_t *prop_class)
+{
+    if (!strcmp(str,"basic")) {
+        *prop_class = OFPPPC_BASIC;
+    } else if (!strcmp(str,"ethernet")) {
+        *prop_class = OFPPPC_BASIC;
+    } else if (!strcmp(str,"mpls")) {
+        *prop_class = OFPPPC_MPLS;
+    } else if (!strcmp(str,"gre")) {
+        *prop_class = OFPPPC_GRE;
+    } else if (!strcmp(str,"gtp")) {
+        *prop_class = OFPPPC_GTP;
+    } else {
+        return false;
+    }
+    return true;
+}
+
+bool
+parse_ed_prop_type(uint16_t prop_class,
+                   const char *str OVS_UNUSED,
+                   uint8_t *type OVS_UNUSED)
+{
+    switch (prop_class) {
+    default:
+        return false;
+    }
+}
+
+/* Parse the value of an encap/decap property based on property class
+ * and type and append the parsed property in internal format to the
+ * ofpbuf out.
+ * Returns a malloced string in the event of a parse error. The caller
+ * must free the string.
+ */
+
+char *
+parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED,
+                    const char *value, struct ofpbuf *out OVS_UNUSED)
+{
+
+    if (value == NULL || *value == '\0') {
+        return xstrdup("Value missing for encap property");
+    }
+
+    switch (prop_class) {
+    default:
+        /* Unsupported property classes rejected before. */
+        OVS_NOT_REACHED();
+    }
+
+    return NULL;
+}
+
+char *
+format_ed_prop_class(const struct ofpact_ed_prop *prop)
+{
+    switch (prop->prop_class) {
+    case OFPPPC_BASIC:
+        return "basic";
+    case OFPPPC_MPLS:
+        return "mpls";
+    case OFPPPC_GRE:
+        return "gre";
+    case OFPPPC_GTP:
+        return "gtp";
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
+char *
+format_ed_prop_type(const struct ofpact_ed_prop *prop)
+{
+    switch (prop->prop_class) {
+    default:
+        OVS_NOT_REACHED();
+    }
+}
+
+void
+format_ed_prop(struct ds *s OVS_UNUSED,
+                     const struct ofpact_ed_prop *prop)
+{
+    switch (prop->prop_class) {
+    default:
+        OVS_NOT_REACHED();
+    }
+}
diff --git a/lib/packets.h b/lib/packets.h
index a9d5e84..8287ca3 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1265,7 +1265,8 @@ pt_ns_type(ovs_be32 packet_type)
 
 /* Well-known packet_type field values. */
 enum packet_type {
-    PT_ETH  = PACKET_TYPE(OFPHTN_ONF, 0x0000),  /* Default: Ethernet */
+    PT_ETH  = PACKET_TYPE(OFPHTN_ONF, 0x0000),  /* Default PT: Ethernet */
+    PT_USE_NEXT_PROTO = PACKET_TYPE(OFPHTN_ONF, 0xfffe),  /* Pseudo PT for decap. */
     PT_IPV4 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IP),
     PT_IPV6 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IPV6),
     PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS),
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index c103793..a2f5dc7 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -234,6 +234,8 @@ struct xlate_ctx {
     bool in_action_set;         /* Currently translating action_set, if true. */
     bool in_packet_out;         /* Currently translating a packet_out msg, if
                                  * true. */
+    bool pending_encap;         /* Waiting to commit a pending encap
+                                 * action, if true. */
 
     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
     ovs_be64 rule_cookie;       /* Cookie of the rule being translated. */
@@ -3465,7 +3467,8 @@ xlate_commit_actions(struct xlate_ctx *ctx)
 
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           ctx->odp_actions, ctx->wc,
-                                          use_masked);
+                                          use_masked, ctx->pending_encap);
+    ctx->pending_encap = false;
 }
 
 static void
@@ -5503,6 +5506,8 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
         case OFPACT_METER:
         case OFPACT_SAMPLE:
         case OFPACT_CLONE:
+        case OFPACT_ENCAP:
+        case OFPACT_DECAP:
         case OFPACT_DEBUG_RECIRC:
         case OFPACT_CT:
         case OFPACT_CT_CLEAR:
@@ -5692,6 +5697,93 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
 }
 
 static void
+rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
+                            struct flow *flow,
+                            struct flow_wildcards *wc)
+{
+    wc->masks.packet_type = OVS_BE32_MAX;
+    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+        /* Only adjust the packet_type and zero the dummy Ethernet addresses. */
+        ovs_be16 ethertype = pt_ns_type_be(flow->packet_type);
+        flow->packet_type = htonl(PT_ETH);
+        flow->dl_src = eth_addr_zero;
+        flow->dl_dst = eth_addr_zero;
+        flow->dl_type = ethertype;
+    } else {
+        xlate_report_debug(ctx, OFT_ACTION,
+                           "encap(ethernet) unsupported for packet type "
+                           "ethernet");
+        /* TODO: Error handling: drop packet. */
+        ctx->error = 1;
+    }
+}
+
+static void
+xlate_generic_encap_action(struct xlate_ctx *ctx,
+                           const struct ofpact_encap *encap)
+{
+    struct flow *flow = &ctx->xin->flow;
+    struct flow_wildcards *wc = ctx->wc;
+
+    /* Ensure that any pending actions on the inner packet are applied before
+     * rewriting the flow */
+    xlate_commit_actions(ctx);
+
+    /* Rewrite the flow to reflect the effect of pushing the new encap header. */
+    switch (ntohl(encap->new_pkt_type)) {
+        case PT_ETH:
+            rewrite_flow_encap_ethernet(ctx, flow, wc);
+            break;
+        default:
+            /* TODO: Error handling: Should not happen if the PT is checked
+             * at decoding */
+            break;
+    }
+
+    if (!ctx->error) {
+        /* The actual encap datapath action will be generated at next commit. */
+        ctx->pending_encap = true;
+    }
+}
+
+/* Returns true if packet must be recirculated after decapsulation. */
+static bool
+xlate_generic_decap_action(struct xlate_ctx *ctx,
+                           const struct ofpact_decap *decap OVS_UNUSED)
+{
+    struct flow *flow = &ctx->xin->flow;
+
+    /* Ensure that any pending actions on the current packet are applied
+     * before generating the decap action. */
+    xlate_commit_actions(ctx);
+
+    /* We assume for now that the new_pkt_type is PT_USE_NEXT_PROTO. */
+    switch (ntohl(flow->packet_type)) {
+        case PT_ETH:
+            if (flow->vlans[0].tci & htons(VLAN_CFI)) {
+                /* Error handling: drop packet. */
+                xlate_report_debug(ctx, OFT_ACTION, "Dropping packet, cannot "
+                                   "decap Ethernet if VLAN is present.");
+                ctx->error = 1;
+            } else {
+                /* Just change the packet_type.
+                 * Delay generating pop_eth to the next commit. */
+                flow->packet_type = htonl(PACKET_TYPE(OFPHTN_ETHERTYPE,
+                                                      ntohs(flow->dl_type)));
+                ctx->wc->masks.dl_type = OVS_BE16_MAX;
+            }
+            return false;
+        default:
+            xlate_report_debug(ctx, OFT_ACTION,
+                               "decap() for unsupported packet type %x",
+                               ntohl(flow->packet_type));
+            /* TODO: Error handling: drop packet. */
+            ctx->error = 1;
+            return false;
+    }
+}
+
+static void
 recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
 {
     /* No need to recirculate if already exiting. */
@@ -5765,6 +5857,8 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
     case OFPACT_EXIT:
     case OFPACT_SAMPLE:
     case OFPACT_CLONE:
+    case OFPACT_ENCAP:
+    case OFPACT_DECAP:
     case OFPACT_UNROLL_XLATE:
     case OFPACT_CT:
     case OFPACT_CT_CLEAR:
@@ -6181,6 +6275,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             xlate_clone(ctx, ofpact_get_CLONE(a));
             break;
 
+        case OFPACT_ENCAP:
+            xlate_generic_encap_action(ctx, ofpact_get_ENCAP(a));
+            break;
+
+        case OFPACT_DECAP: {
+            bool recirc_needed =
+                    xlate_generic_decap_action(ctx, ofpact_get_DECAP(a));
+            if (!ctx->error && recirc_needed) {
+                /* Recirculate for parsing of inner packet. */
+                ctx_trigger_freeze(ctx);
+                /* Then continue with next action. */
+                a = ofpact_next(a);
+            }
+            break;
+        }
+
         case OFPACT_CT:
             compose_conntrack_action(ctx, ofpact_get_CT(a));
             break;
@@ -6423,7 +6533,7 @@ xlate_wc_finish(struct xlate_ctx *ctx)
      * use non-header fields as part of the cache. */
     flow_wildcards_clear_non_packet_fields(ctx->wc);
 
-    /* Wildcard ethernet addresses if the original packet type was not
+    /* Wildcard ethernet fields if the original packet type was not
      * Ethernet. */
     if (ctx->xin->upcall_flow->packet_type != htonl(PT_ETH)) {
         ctx->wc->masks.dl_dst = eth_addr_zero;
@@ -6512,6 +6622,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         .in_group = false,
         .in_action_set = false,
         .in_packet_out = xin->in_packet_out,
+        .pending_encap = false,
 
         .table_id = 0,
         .rule_cookie = OVS_BE64_MAX,
@@ -6672,6 +6783,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         flow->packet_type = htonl(PT_ETH);
         flow->dl_src = eth_addr_zero;
         flow->dl_dst = eth_addr_zero;
+        ctx.pending_encap = true;
     }
 
     if (!xin->ofpacts && !ctx.rule) {
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 6552590..f6bd903 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1561,17 +1561,23 @@ the action set, the one written later replaces the earlier action:
 \fBpop_mpls\fR
 .
 .IP 2.
-\fBpush_mpls\fR
+\fBdecap\fR
 .
 .IP 3.
-\fBpush_vlan\fR
+\fBencap\fR
 .
 .IP 4.
+\fBpush_mpls\fR
+.
+.IP 5.
+\fBpush_vlan\fR
+.
+.IP 6.
 \fBdec_ttl\fR
 .IQ
 \fBdec_mpls_ttl\fR
 .
-.IP 5.
+.IP 7.
 \fBload\fR
 .IQ
 \fBmove\fR
@@ -1611,10 +1617,10 @@ the later modification takes effect, and when they modify
 different parts of a field (or different fields), then both
 modifications are applied.
 .
-.IP 6.
+.IP 8.
 \fBset_queue\fR
 .
-.IP 7.
+.IP 9.
 \fBgroup\fR
 .IQ
 \fBoutput\fR
@@ -1630,6 +1636,7 @@ not visible.)
 .RE
 .IP
 Only the actions listed above may be written to the action set.
+\fBencap\fR and \fBdecap\fR actions are nonstandard.
 .
 .IP \fBwrite_metadata\fB:\fIvalue\fR[/\fImask\fR]
 Updates the metadata field for the flow. If \fImask\fR is omitted, the
@@ -1730,6 +1737,65 @@ that is saved and restored includes all flow data and metadata
 \fBpush\fR and \fBpop\fR actions, and the OpenFlow action set.
 .IP
 This action was added in Open vSwitch 2.6.90.
+.
+.IP "\fBencap(\fR\fIheader\fR[\fB(\fR\fIprop\fR\fB=\fR\fIvalue\fR,\fItlv\fR\fB(\fR\fIclass\fR,\fItype\fR,\fIvalue\fB)\fR,...\fB)\fR]\fB)\fR"
+Encapsulates the packet with a new packet header, e.g., ethernet
+or nsh.
+.
+.RS
+.IP "\fIheader\fR"
+Used to specify encapsulation header type.
+.
+.IP "\fIprop\fR\fB=\fR\fIvalue\fR"
+Used to specify the initial value for the property in the encapsulation header.
+.
+.IP "\fItlv\fR\fB(\fR\fIclass\fR,\fItype\fR,\fIvalue\fB)\fR"
+Used to specify the initial value for the TLV (Type Length Value)
+in the encapsulation header.
+.RE
+.IP
+For example, \fBencap(ethernet)\fR will encapsulate the L3 packet with
+Ethernet header.
+.IP
+\fBencap(nsh(md_type=1))\fR will encapsulate the packet with nsh header
+and nsh metadata type 1.
+.IP
+\fBencap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))\fR will encapsulate
+the packet with nsh header and nsh metadata type 2, and the nsh TLV with
+class 0x1000 and type 10 is set to 0x12345678.
+.IP
+\fIprop\fR\fB=\fR\fIvalue\fR is just used to set some
+necessary fields for encapsulation header initialization. Other fields
+in the encapsulation header must be set by \fBset_field\fR action. New
+encapsulation header implementation must add new match fields and
+corresponding \fBset\fR action in order that \fBset_field\fR action can
+change the fields in the encapsulation header on demand.
+.IP
+\fBencap(nsh(md_type=1)),\fR
+\fBset_field:0x1234->nsh_spi,set_field:0x11223344->nsh_c1\fR
+is an example to encapsulate nsh header and set nsh spi and c1.
+.IP
+This action was added in Open vSwitch 2.8.
+.
+.IP "\fBdecap(\fR[\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR]\fB)\fR"
+Decapsulates the outer packet header.
+.
+.RS
+.IP "\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR"
+It is optional and used to specify the outer header type of the
+decapsulated packet. \fInamespace\fR is 0 for Ethernet packet,
+1 for L3 packet, \fItype\fR\ is L3 protocol type, e.g.,
+0x894f for nsh, 0x0 for Ethernet.
+.RE
+.IP
+By default, \fBdecap()\fR will decapsulate the outer packet header
+according to the packet header type, if
+\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR
+is given, it will decapsulate the given packet header, it will fail
+if the actual outer packet header type is not of
+\fBpacket_type(ns=\fR\fInamespace\fR\fB,type=\fR\fItype\fR\fB)\fR.
+.IP
+This action was added in Open vSwitch 2.8.
 .RE
 .
 .PP
-- 
2.1.0



More information about the dev mailing list