[ovs-dev] [PATCH v9 1/3] nsh: add new flow key 'ttl'

Yi Yang yi.y.yang at intel.com
Thu Jan 11 05:24:01 UTC 2018


IETF NSH draft added a new filed ttl in NSH header, this patch
is to add new nsh key 'ttl' for it.

Signed-off-by: Yi Yang <yi.y.yang at intel.com>
---
 NEWS                                              |   1 +
 datapath/linux/compat/include/linux/openvswitch.h |   2 +-
 include/openvswitch/flow.h                        |   6 +-
 include/openvswitch/meta-flow.h                   |  14 +++
 include/openvswitch/nsh.h                         | 102 ++++++++++++++++
 include/openvswitch/packets.h                     |  12 +-
 lib/flow.c                                        |  23 ++--
 lib/flow.h                                        |   2 +-
 lib/match.c                                       |  12 +-
 lib/meta-flow.c                                   |  56 +++++++--
 lib/meta-flow.xml                                 | 139 ++++++++++++++++++----
 lib/nx-match.c                                    |  16 ++-
 lib/odp-execute.c                                 |  40 +++----
 lib/odp-util.c                                    | 130 +++++++++-----------
 lib/odp-util.h                                    |   2 +-
 ofproto/ofproto-dpif-xlate.c                      |   7 +-
 tests/nsh.at                                      |  41 +++----
 tests/ofproto.at                                  |   3 +-
 18 files changed, 422 insertions(+), 186 deletions(-)

diff --git a/NEWS b/NEWS
index 8caf6bb..ae684e3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
 Post-v2.8.0
 --------------------
    - NSH implementation now conforms to latest draft (draft-ietf-sfc-nsh-28).
+     * Add ttl field.
    - OVSDB:
      * New high-level documentation in ovsdb(7).
      * New file format documentation for developers in ovsdb(5).
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 3ddb1c5..c7142b6 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -504,9 +504,9 @@ enum ovs_nsh_key_attr {
 
 struct ovs_nsh_key_base {
 	__u8 flags;
+	__u8 ttl;
 	__u8 mdtype;
 	__u8 np;
-	__u8 pad;
 	__be32 path_hdr;
 };
 
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index a658a58..cd61fff 100644
--- a/include/openvswitch/flow.h
+++ b/include/openvswitch/flow.h
@@ -146,7 +146,7 @@ struct flow {
     struct eth_addr arp_tha;    /* ARP/ND target hardware address. */
     ovs_be16 tcp_flags;         /* TCP flags. With L3 to avoid matching L4. */
     ovs_be16 pad2;              /* Pad to 64 bits. */
-    struct flow_nsh nsh;        /* Network Service Header keys */
+    struct ovs_key_nsh nsh;     /* Network Service Header keys */
 
     /* L4 (64-bit aligned) */
     ovs_be16 tp_src;            /* TCP/UDP/SCTP source port/ICMP type. */
@@ -159,13 +159,13 @@ struct flow {
 };
 BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0);
 BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
-BUILD_ASSERT_DECL(sizeof(struct flow_nsh) % sizeof(uint64_t) == 0);
+BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) % sizeof(uint64_t) == 0);
 
 #define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t))
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
 BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
-                  == sizeof(struct flow_tnl) + sizeof(struct flow_nsh) + 300
+                  == sizeof(struct flow_tnl) + sizeof(struct ovs_key_nsh) + 300
                   && FLOW_WC_SEQ == 40);
 
 /* Incremental points at which flow classification may be performed in
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 436501f..d0844b3 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -1833,6 +1833,20 @@ enum OVS_PACKED_ENUM mf_field_id {
     MFF_NSH_C3,
     MFF_NSH_C4,
 
+    /* "nsh_ttl".
+     *
+     * TTL field in NSH base header.
+     *
+     * Type: u8.
+     * Maskable: no.
+     * Formatting: decimal.
+     * Prerequisites: NSH.
+     * Access: read/write.
+     * NXM: none.
+     * OXM: NXOXM_NSH_TTL(10) since OF1.3 and v2.9.
+     */
+    MFF_NSH_TTL,
+
     MFF_N_IDS
 };
 
diff --git a/include/openvswitch/nsh.h b/include/openvswitch/nsh.h
index 63b480e..55f59d6 100644
--- a/include/openvswitch/nsh.h
+++ b/include/openvswitch/nsh.h
@@ -299,6 +299,108 @@ nsh_reset_ver_flags_ttl_len(struct nsh_hdr *nsh)
     nsh->ver_flags_ttl_len = 0;
 }
 
+static inline uint8_t
+nsh_get_ttl(const struct nsh_hdr *nsh)
+{
+    return (ntohs(nsh->ver_flags_ttl_len) & NSH_TTL_MASK) >> NSH_TTL_SHIFT;
+}
+
+#ifndef __CHECKER__
+static inline ovs_be32
+nsh_16aligned_be32(const ovs_16aligned_be32 *x)
+{
+#ifdef WORDS_BIGENDIAN
+    return ((ovs_be32) x->hi << 16) | (ovs_be32) x->lo;
+#else
+    return ((ovs_be32) x->lo << 16) | (ovs_be32) x->hi;
+#endif
+}
+#else  /* __CHECKER__ */
+/* Making sparse happy with these functions also makes them unreadable, so
+ * don't bother to show it their implementations. */
+ovs_be32 nsh_16aligned_be32(const ovs_16aligned_be32 *x);
+#endif
+
+static inline ovs_be32
+nsh_get_path_hdr(const struct nsh_hdr *nsh)
+{
+    return nsh_16aligned_be32(&nsh->path_hdr);
+}
+
+static inline ovs_be32
+nsh_get_spi(const struct nsh_hdr *nsh)
+{
+    uint32_t path_hdr = ntohl(nsh_get_path_hdr(nsh));
+    return htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
+}
+
+static inline uint8_t
+nsh_get_si(const struct nsh_hdr *nsh)
+{
+    uint32_t path_hdr = ntohl(nsh_get_path_hdr(nsh));
+    return (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
+}
+
+static inline ovs_be32
+nsh_path_hdr_to_spi(ovs_be32 path_hdr)
+{
+    return htonl((ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
+}
+
+static inline uint32_t
+nsh_path_hdr_to_spi_uint32(ovs_be32 path_hdr)
+{
+    return (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
+}
+
+static inline uint8_t
+nsh_path_hdr_to_si(ovs_be32 path_hdr)
+{
+    return (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
+}
+
+static inline ovs_be32
+nsh_spi_si_to_path_hdr(uint32_t spi, uint8_t si)
+{
+    return htonl((spi << NSH_SPI_SHIFT) | si);
+}
+
+static inline void
+nsh_set_flags_and_ttl(struct nsh_hdr *nsh, uint8_t flags, uint8_t ttl)
+{
+    nsh->ver_flags_ttl_len
+        = htons((ntohs(nsh->ver_flags_ttl_len)
+                 & ~(NSH_FLAGS_MASK | NSH_TTL_MASK))
+                | ((flags << NSH_FLAGS_SHIFT)& NSH_FLAGS_MASK)
+                | ((ttl << NSH_TTL_SHIFT) & NSH_TTL_MASK));
+}
+
+static inline void
+nsh_set_flags_ttl_len(struct nsh_hdr *nsh, uint8_t flags, uint8_t ttl,
+                      uint16_t len)
+{
+    nsh->ver_flags_ttl_len
+        = htons((ntohs(nsh->ver_flags_ttl_len)
+                 & ~(NSH_FLAGS_MASK | NSH_TTL_MASK | NSH_LEN_MASK))
+                | ((flags << NSH_FLAGS_SHIFT)& NSH_FLAGS_MASK)
+                | ((ttl << NSH_TTL_SHIFT) & NSH_TTL_MASK)
+                | (((len >> 2) << NSH_LEN_SHIFT) & NSH_LEN_MASK));
+}
+
+static inline void
+nsh_path_hdr_set_spi(ovs_be32 *path_hdr, ovs_be32 spi)
+{
+    *path_hdr = htonl((ntohl(*path_hdr) & ~NSH_SPI_MASK) |
+                      ((ntohl(spi) << NSH_SPI_SHIFT) & NSH_SPI_MASK));
+}
+
+static inline void
+nsh_path_hdr_set_si(ovs_be32 *path_hdr, uint8_t si)
+{
+    *path_hdr = htonl((ntohl(*path_hdr) & ~NSH_SI_MASK) |
+                      ((si << NSH_SI_SHIFT) & NSH_SI_MASK));
+}
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
index ae1cf9c..fef756b 100644
--- a/include/openvswitch/packets.h
+++ b/include/openvswitch/packets.h
@@ -74,21 +74,11 @@ union flow_vlan_hdr {
     };
 };
 
-/* Network Service Header keys */
-struct flow_nsh {
-    uint8_t flags;
-    uint8_t mdtype;
-    uint8_t np;
-    uint8_t si;
-    ovs_be32 spi;
-    ovs_be32 context[4];
-};
-
 struct ovs_key_nsh {
     uint8_t flags;
+    uint8_t ttl;
     uint8_t mdtype;
     uint8_t np;
-    uint8_t pad;
     ovs_be32 path_hdr;
     ovs_be32 context[4];
 };
diff --git a/lib/flow.c b/lib/flow.c
index 964f734..f9d7c2a 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -530,11 +530,10 @@ parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
 }
 
 bool
-parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key)
+parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key)
 {
     const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap;
-    uint8_t version, length, flags;
-    uint32_t path_hdr;
+    uint8_t version, length, flags, ttl;
 
     /* Check if it is long enough for NSH header, doesn't support
      * MD type 2 yet
@@ -546,18 +545,17 @@ parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key)
     version = nsh_get_ver(nsh);
     flags = nsh_get_flags(nsh);
     length = nsh_hdr_len(nsh);
+    ttl = nsh_get_ttl(nsh);
 
     if (OVS_UNLIKELY(length > *sizep || version != 0)) {
         return false;
     }
 
     key->flags = flags;
+    key->ttl = ttl;
     key->mdtype = nsh->md_type;
     key->np = nsh->next_proto;
-
-    path_hdr = ntohl(get_16aligned_be32(&nsh->path_hdr));
-    key->si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
-    key->spi = htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
+    key->path_hdr = nsh_get_path_hdr(nsh);
 
     switch (key->mdtype) {
         case NSH_M_TYPE1:
@@ -876,11 +874,11 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
                 miniflow_pad_to_64(mf, arp_tha);
             }
         } else if (dl_type == htons(ETH_TYPE_NSH)) {
-            struct flow_nsh nsh;
+            struct ovs_key_nsh nsh;
 
             if (OVS_LIKELY(parse_nsh(&data, &size, &nsh))) {
                 miniflow_push_words(mf, nsh, &nsh,
-                                    sizeof(struct flow_nsh) /
+                                    sizeof(struct ovs_key_nsh) /
                                     sizeof(uint64_t));
             }
         }
@@ -1684,10 +1682,10 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc,
         return;
     } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
         WC_MASK_FIELD(wc, nsh.flags);
+        WC_MASK_FIELD(wc, nsh.ttl);
         WC_MASK_FIELD(wc, nsh.mdtype);
         WC_MASK_FIELD(wc, nsh.np);
-        WC_MASK_FIELD(wc, nsh.spi);
-        WC_MASK_FIELD(wc, nsh.si);
+        WC_MASK_FIELD(wc, nsh.path_hdr);
         WC_MASK_FIELD(wc, nsh.context);
     } else {
         return; /* Unknown ethertype. */
@@ -1820,8 +1818,7 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
         FLOWMAP_SET(map, nsh.flags);
         FLOWMAP_SET(map, nsh.mdtype);
         FLOWMAP_SET(map, nsh.np);
-        FLOWMAP_SET(map, nsh.spi);
-        FLOWMAP_SET(map, nsh.si);
+        FLOWMAP_SET(map, nsh.path_hdr);
         FLOWMAP_SET(map, nsh.context);
     }
 }
diff --git a/lib/flow.h b/lib/flow.h
index b3128da..eb1e2bf 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -129,7 +129,7 @@ bool flow_compose(struct dp_packet *, const struct flow *, size_t);
 bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
                          uint8_t *nw_frag);
 ovs_be16 parse_dl_type(const struct eth_header *data_, size_t size);
-bool parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key);
+bool parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key);
 
 static inline uint64_t
 flow_get_xreg(const struct flow *flow, int idx)
diff --git a/lib/match.c b/lib/match.c
index 8952c99..b880492 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -1260,11 +1260,19 @@ format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
 static void
 format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m)
 {
+    ovs_be32 spi_mask = nsh_path_hdr_to_spi(m->nsh.path_hdr);
+    if (spi_mask == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
+        spi_mask = OVS_BE32_MAX;
+    }
     format_uint8_masked(s, "nsh_flags", f->nsh.flags, m->nsh.flags);
+    format_uint8_masked(s, "nsh_ttl", f->nsh.ttl, m->nsh.ttl);
     format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, m->nsh.mdtype);
     format_uint8_masked(s, "nsh_np", f->nsh.np, m->nsh.np);
-    format_be32_masked_hex(s, "nsh_spi", f->nsh.spi, m->nsh.spi);
-    format_uint8_masked(s, "nsh_si", f->nsh.si, m->nsh.si);
+
+    format_be32_masked_hex(s, "nsh_spi", nsh_path_hdr_to_spi(f->nsh.path_hdr),
+                           spi_mask);
+    format_uint8_masked(s, "nsh_si", nsh_path_hdr_to_si(f->nsh.path_hdr),
+                        nsh_path_hdr_to_si(m->nsh.path_hdr));
     if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) {
         format_be32_masked_hex(s, "nsh_c1", f->nsh.context[0],
                                m->nsh.context[0]);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index beeddf1..ed41f50 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -40,6 +40,7 @@
 #include "openvswitch/ofp-errors.h"
 #include "openvswitch/vlog.h"
 #include "vl-mff-map.h"
+#include "openvswitch/nsh.h"
 
 VLOG_DEFINE_THIS_MODULE(meta_flow);
 
@@ -361,14 +362,16 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
 
     case MFF_NSH_FLAGS:
         return !wc->masks.nsh.flags;
+    case MFF_NSH_TTL:
+        return !wc->masks.nsh.ttl;
     case MFF_NSH_MDTYPE:
         return !wc->masks.nsh.mdtype;
     case MFF_NSH_NP:
         return !wc->masks.nsh.np;
     case MFF_NSH_SPI:
-        return !wc->masks.nsh.spi;
+        return !(wc->masks.nsh.path_hdr & htonl(NSH_SPI_MASK));
     case MFF_NSH_SI:
-        return !wc->masks.nsh.si;
+        return !(wc->masks.nsh.path_hdr & htonl(NSH_SI_MASK));
     case MFF_NSH_C1:
     case MFF_NSH_C2:
     case MFF_NSH_C3:
@@ -606,6 +609,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
 
     case MFF_NSH_FLAGS:
         return true;
+    case MFF_NSH_TTL:
+        return (value->u8 <= 63);
     case MFF_NSH_MDTYPE:
         return (value->u8 == 1 || value->u8 == 2);
     case MFF_NSH_NP:
@@ -899,6 +904,9 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_NSH_FLAGS:
         value->u8 = flow->nsh.flags;
         break;
+    case MFF_NSH_TTL:
+        value->u8 = flow->nsh.ttl;
+        break;
     case MFF_NSH_MDTYPE:
         value->u8 = flow->nsh.mdtype;
         break;
@@ -906,10 +914,13 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->u8 = flow->nsh.np;
         break;
     case MFF_NSH_SPI:
-        value->be32 = flow->nsh.spi;
+        value->be32 = nsh_path_hdr_to_spi(flow->nsh.path_hdr);
+        if (value->be32 == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
+            value->be32 = OVS_BE32_MAX;
+        }
         break;
     case MFF_NSH_SI:
-        value->u8 = flow->nsh.si;
+        value->u8 = nsh_path_hdr_to_si(flow->nsh.path_hdr);
         break;
     case MFF_NSH_C1:
     case MFF_NSH_C2:
@@ -1214,6 +1225,9 @@ mf_set_value(const struct mf_field *mf,
     case MFF_NSH_FLAGS:
         MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8);
         break;
+    case MFF_NSH_TTL:
+        MATCH_SET_FIELD_UINT8(match, nsh.ttl, value->u8);
+        break;
     case MFF_NSH_MDTYPE:
         MATCH_SET_FIELD_UINT8(match, nsh.mdtype, value->u8);
         break;
@@ -1221,10 +1235,12 @@ mf_set_value(const struct mf_field *mf,
         MATCH_SET_FIELD_UINT8(match, nsh.np, value->u8);
         break;
     case MFF_NSH_SPI:
-        MATCH_SET_FIELD_BE32(match, nsh.spi, value->be32);
+        match->wc.masks.nsh.path_hdr |= htonl(NSH_SPI_MASK);
+        nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, value->be32);
         break;
     case MFF_NSH_SI:
-        MATCH_SET_FIELD_UINT8(match, nsh.si, value->u8);
+        match->wc.masks.nsh.path_hdr |= htonl(NSH_SI_MASK);
+        nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, value->u8);
         break;
     case MFF_NSH_C1:
     case MFF_NSH_C2:
@@ -1606,6 +1622,9 @@ mf_set_flow_value(const struct mf_field *mf,
     case MFF_NSH_FLAGS:
         flow->nsh.flags = value->u8;
         break;
+    case MFF_NSH_TTL:
+        flow->nsh.ttl = value->u8;
+        break;
     case MFF_NSH_MDTYPE:
         flow->nsh.mdtype = value->u8;
         break;
@@ -1613,10 +1632,10 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->nsh.np = value->u8;
         break;
     case MFF_NSH_SPI:
-        flow->nsh.spi = value->be32;
+        nsh_path_hdr_set_spi(&flow->nsh.path_hdr, value->be32);
         break;
     case MFF_NSH_SI:
-        flow->nsh.si = value->u8;
+        nsh_path_hdr_set_si(&flow->nsh.path_hdr, value->u8);
         break;
     case MFF_NSH_C1:
     case MFF_NSH_C2:
@@ -1752,6 +1771,7 @@ mf_is_pipeline_field(const struct mf_field *mf)
     case MFF_ND_SLL:
     case MFF_ND_TLL:
     case MFF_NSH_FLAGS:
+    case MFF_NSH_TTL:
     case MFF_NSH_MDTYPE:
     case MFF_NSH_NP:
     case MFF_NSH_SPI:
@@ -2097,6 +2117,9 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
     case MFF_NSH_FLAGS:
         MATCH_SET_FIELD_MASKED(match, nsh.flags, 0, 0);
         break;
+    case MFF_NSH_TTL:
+        MATCH_SET_FIELD_MASKED(match, nsh.ttl, 0, 0);
+        break;
     case MFF_NSH_MDTYPE:
         MATCH_SET_FIELD_MASKED(match, nsh.mdtype, 0, 0);
         break;
@@ -2104,10 +2127,12 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         MATCH_SET_FIELD_MASKED(match, nsh.np, 0, 0);
         break;
     case MFF_NSH_SPI:
-        MATCH_SET_FIELD_MASKED(match, nsh.spi, htonl(0), htonl(0));
+        match->wc.masks.nsh.path_hdr &= ~htonl(NSH_SPI_MASK);
+        nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, htonl(0));
         break;
     case MFF_NSH_SI:
-        MATCH_SET_FIELD_MASKED(match, nsh.si, 0, 0);
+        match->wc.masks.nsh.path_hdr &= ~htonl(NSH_SI_MASK);
+        nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, 0);
         break;
     case MFF_NSH_C1:
     case MFF_NSH_C2:
@@ -2357,6 +2382,9 @@ mf_set(const struct mf_field *mf,
     case MFF_NSH_FLAGS:
         MATCH_SET_FIELD_MASKED(match, nsh.flags, value->u8, mask->u8);
         break;
+    case MFF_NSH_TTL:
+        MATCH_SET_FIELD_MASKED(match, nsh.ttl, value->u8, mask->u8);
+        break;
     case MFF_NSH_MDTYPE:
         MATCH_SET_FIELD_MASKED(match, nsh.mdtype, value->u8, mask->u8);
         break;
@@ -2364,10 +2392,14 @@ mf_set(const struct mf_field *mf,
         MATCH_SET_FIELD_MASKED(match, nsh.np, value->u8, mask->u8);
         break;
     case MFF_NSH_SPI:
-        MATCH_SET_FIELD_MASKED(match, nsh.spi, value->be32, mask->be32);
+        match->wc.masks.nsh.path_hdr |= mask->be32;
+        nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr,
+                             value->be32 & mask->be32);
         break;
     case MFF_NSH_SI:
-        MATCH_SET_FIELD_MASKED(match, nsh.si, value->u8, mask->u8);
+        match->wc.masks.nsh.path_hdr |= htonl(mask->u8);
+        nsh_path_hdr_set_si(&match->flow.nsh.path_hdr,
+                             value->u8 & mask->u8);
         break;
     case MFF_NSH_C1:
     case MFF_NSH_C2:
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 08ee0ec..933d4b8 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -1309,27 +1309,6 @@ tcp,tp_src=0x07c0/0xfff0
     </field>
   </group>
 
-  <group title="Network Service Header">
-    <field id="MFF_NSH_FLAGS"
-        title="flags field (8 bits)"/>
-    <field id="MFF_NSH_MDTYPE"
-        title="mdtype field (8 bits)"/>
-    <field id="MFF_NSH_NP"
-        title="np (next protocol) field (8 bits)"/>
-    <field id="MFF_NSH_SPI"
-        title="spi (service path identifier) field (24 bits)"/>
-    <field id="MFF_NSH_SI"
-        title="si (service index) field (8 bits)"/>
-    <field id="MFF_NSH_C1"
-        title="c1 (Network Platform Context) field (32 bits)"/>
-    <field id="MFF_NSH_C2"
-        title="c2 (Network Shared Context) field (32 bits)"/>
-    <field id="MFF_NSH_C3"
-        title="c3 (Service Platform Context) field (32 bits)"/>
-    <field id="MFF_NSH_C4"
-        title="c4 (Service Shared Context) field (32 bits)"/>
-  </group>
-
   <group title="Tunnel">
     <p>
       The fields in this group relate to tunnels, which Open vSwitch
@@ -3964,7 +3943,7 @@ r r c c c.
       listen for <code>OFPR_INVALID_TTL</code> ``packet-in'' messages via
       OpenFlow.
     </field>
-    
+
     <field id="MFF_IP_FRAG" title="IPv4/v6 Fragment Bitmask">
       <p>
         Specifies what kinds of IP fragments or non-fragments to match.  The
@@ -4180,6 +4159,115 @@ r r c c c.
     <field id="MFF_ARP_THA" title="ARP Target Ethernet Address"/>
   </group>
 
+  <group title="Layer 3: NSH">
+    <p>
+      Service functions are widely deployed and essential in many networks.
+      These service functions provide a range of features such as security,
+      WAN acceleration, and server load balancing.  Service functions may
+      be instantiated at different points in the network infrastructure
+      such as the wide area network, data center, and so forth.
+    </p>
+
+    <p>
+      Prior to development of the SFC architecture [RFC 7665] and the
+      protocol specified in this document, current service function
+      deployment models have been relatively static and bound to topology
+      for insertion and policy selection.  Furthermore, they do not adapt
+      well to elastic service environments enabled by virtualization.
+    </p>
+
+    <p>
+      New data center network and cloud architectures require more flexible
+      service function deployment models.  Additionally, the transition to
+      virtual platforms demands an agile service insertion model that
+      supports dynamic and elastic service delivery.  Specifically, the
+      following functions are necessary:
+    </p>
+
+    <p>
+      1.  The movement of service functions and application workloads in
+          the network.
+    </p>
+
+    <p>
+      2.  The ability to easily bind service policy to granular
+          information, such as per-subscriber state.
+    </p>
+
+    <p>
+      3.  The capability to steer traffic to the requisite service
+          function(s).
+    </p>
+
+    <p>
+      The Network Service Header (NSH) specification defines a new data
+      plane protocol, which is an encapsulation for service function
+      chains.  The NSH is designed to encapsulate an original packet or
+      frame, and in turn be encapsulated by an outer transport
+      encapsulation (which is used to deliver the NSH to NSH-aware network
+      elements), as shown in Figure 1:
+    </p>
+
+    <pre>
+
+                        +------------------------------+
+                        |    Transport Encapsulation   |
+                        +------------------------------+
+                        | Network Service Header (NSH) |
+                        +------------------------------+
+                        |    Original Packet / Frame   |
+                        +------------------------------+
+
+                 Figure 1: Network Service Header Encapsulation
+    </pre>
+
+    <p>
+      The NSH is composed of the following elements:
+    </p>
+
+    <p>
+      1.  Service Function Path identification.
+    </p>
+
+    <p>
+      2.  Indication of location within a Service Function Path.
+    </p>
+
+    <p>
+      3.  Optional, per packet metadata (fixed length or variable).
+    </p>
+
+    <p>
+      [RFC 7665] provides an overview of a service chaining architecture
+      that clearly defines the roles of the various elements and the scope
+      of a service function chaining encapsulation.  Figure 3 of [RFC 7665]
+      depicts the SFC architectural components after classification.  The
+      NSH is the SFC encapsulation referenced in [RFC 7665].
+    </p>
+
+    <field id="MFF_NSH_FLAGS"
+        title="flags field (2 bits)"/>
+    <field id="MFF_NSH_TTL"
+        title="TTL field (6 bits)"/>
+    <field id="MFF_NSH_MDTYPE"
+        title="mdtype field (8 bits)"/>
+    <field id="MFF_NSH_NP"
+        title="np (next protocol) field (8 bits)"/>
+    <field id="MFF_NSH_SPI"
+        title="spi (service path identifier) field (24 bits)"/>
+    <field id="MFF_NSH_SI"
+        title="si (service index) field (8 bits)"/>
+    <field id="MFF_NSH_C1"
+        title="c1 (Network Platform Context) field (32 bits)"/>
+    <field id="MFF_NSH_C2"
+        title="c2 (Network Shared Context) field (32 bits)"/>
+    <field id="MFF_NSH_C3"
+        title="c3 (Service Platform Context) field (32 bits)"/>
+    <field id="MFF_NSH_C4"
+        title="c4 (Service Shared Context) field (32 bits)"/>
+  </group>
+
+
   <group title="Layer 4: TCP, UDP, and SCTP">
     <p>
       For matching purposes, no distinction is made whether these protocols are
@@ -4567,6 +4655,13 @@ r r c c c.
       Layer 3 Networks, '' <url href="https://tools.ietf.org/html/rfc7348"/>.
     </dd>
 
+    <dt>RFC 7665</dt>
+    <dd>
+      J. Halpern, Ed. and C. Pignataro, Ed.,
+      ``Service Function Chaining (SFC) Architecture,''
+      <url href="https://tools.ietf.org/html/rfc7665"/>.
+    </dd>
+
     <dt>Srinivasan</dt>
     <dd>
       V. Srinivasan, S. Suriy, and G. Varghese, ``Packet
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 8f2a442..aa7691a 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1022,6 +1022,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     const struct flow *flow = &match->flow;
     const size_t start_len = b->size;
     ovs_be16 dl_type = get_dl_type(flow);
+    ovs_be32 spi_mask;
     int match_len;
     int i;
 
@@ -1157,13 +1158,22 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     /* Network Service Header */
     nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
             match->wc.masks.nsh.flags);
+    nxm_put_8m(&ctx, MFF_NSH_TTL, oxm, flow->nsh.ttl,
+            match->wc.masks.nsh.ttl);
     nxm_put_8m(&ctx, MFF_NSH_MDTYPE, oxm, flow->nsh.mdtype,
             match->wc.masks.nsh.mdtype);
     nxm_put_8m(&ctx, MFF_NSH_NP, oxm, flow->nsh.np,
             match->wc.masks.nsh.np);
-    nxm_put_32m(&ctx, MFF_NSH_SPI, oxm, flow->nsh.spi,
-                match->wc.masks.nsh.spi);
-    nxm_put_8m(&ctx, MFF_NSH_SI, oxm, flow->nsh.si, match->wc.masks.nsh.si);
+    spi_mask = nsh_path_hdr_to_spi(match->wc.masks.nsh.path_hdr);
+    if (spi_mask == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
+        spi_mask = OVS_BE32_MAX;
+    }
+    nxm_put_32m(&ctx, MFF_NSH_SPI, oxm,
+                nsh_path_hdr_to_spi(flow->nsh.path_hdr),
+                spi_mask);
+    nxm_put_8m(&ctx, MFF_NSH_SI, oxm,
+                nsh_path_hdr_to_si(flow->nsh.path_hdr),
+                nsh_path_hdr_to_si(match->wc.masks.nsh.path_hdr));
     for (int i = 0; i < 4; i++) {
         nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.context[i],
                     match->wc.masks.nsh.context[i]);
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 7f9d419..c680364 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -274,19 +274,16 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
 /* Set the NSH header. Assumes the NSH header is present and matches the
  * MD format of the key. The slow path must take case of that. */
 static void
-odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key,
-            const struct flow_nsh *mask)
+odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
+            const struct ovs_key_nsh *mask)
 {
     struct nsh_hdr *nsh = dp_packet_l3(packet);
     uint8_t mdtype = nsh_md_type(nsh);
     ovs_be32 path_hdr;
 
     if (!mask) {
-        nsh->ver_flags_ttl_len = htons(key->flags << NSH_FLAGS_SHIFT) |
-                (nsh->ver_flags_ttl_len & ~htons(NSH_FLAGS_MASK));
-        path_hdr = htonl((ntohl(key->spi) << NSH_SPI_SHIFT) |
-                         key->si);
-        put_16aligned_be32(&nsh->path_hdr, path_hdr);
+        nsh_set_flags_and_ttl(nsh, key->flags, key->ttl);
+        put_16aligned_be32(&nsh->path_hdr, key->path_hdr);
         switch (mdtype) {
             case NSH_M_TYPE1:
                 for (int i = 0; i < 4; i++) {
@@ -299,22 +296,25 @@ odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key,
                 break;
         }
     } else {
-        uint8_t flags = (ntohs(nsh->ver_flags_ttl_len) & NSH_FLAGS_MASK) >>
-                            NSH_FLAGS_SHIFT;
+        uint8_t flags = nsh_get_flags(nsh);
+        uint8_t ttl = nsh_get_ttl(nsh);
+
         flags = key->flags | (flags & ~mask->flags);
-        nsh->ver_flags_ttl_len = htons(flags << NSH_FLAGS_SHIFT) |
-                (nsh->ver_flags_ttl_len & ~htons(NSH_FLAGS_MASK));
+        ttl = key->ttl | (ttl & ~mask->ttl);
+        nsh_set_flags_and_ttl(nsh, flags, ttl);
 
-        path_hdr = get_16aligned_be32(&nsh->path_hdr);
-        uint32_t spi = (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
-        uint8_t si = (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
-        uint32_t spi_mask = ntohl(mask->spi);
+        uint32_t spi = ntohl(nsh_get_spi(nsh));
+        uint8_t si = nsh_get_si(nsh);
+        uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr);
+        uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr);
         if (spi_mask == 0x00ffffff) {
             spi_mask = UINT32_MAX;
         }
-        spi = ntohl(key->spi) | (spi & ~spi_mask);
-        si = key->si | (si & ~mask->si);
-        path_hdr = htonl((spi << NSH_SPI_SHIFT) | si);
+        spi = nsh_path_hdr_to_spi_uint32(key->path_hdr) | (spi & ~spi_mask);
+        si = nsh_path_hdr_to_si(key->path_hdr) | (si & ~si_mask);
+        path_hdr = nsh_get_path_hdr(nsh);
+        nsh_path_hdr_set_spi(&path_hdr, htonl(spi));
+        nsh_path_hdr_set_si(&path_hdr, si);
         put_16aligned_be32(&nsh->path_hdr, path_hdr);
         switch (mdtype) {
             case NSH_M_TYPE1:
@@ -359,7 +359,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
         break;
 
     case OVS_KEY_ATTR_NSH: {
-        struct flow_nsh nsh;
+        struct ovs_key_nsh nsh;
         odp_nsh_key_from_attr(a, &nsh);
         odp_set_nsh(packet, &nsh, NULL);
         break;
@@ -490,7 +490,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
         break;
 
     case OVS_KEY_ATTR_NSH: {
-        struct flow_nsh nsh, nsh_mask;
+        struct ovs_key_nsh nsh, nsh_mask;
         struct {
             struct nlattr nla;
             uint8_t data[sizeof(struct ovs_nsh_key_base) + NSH_CTX_HDRS_MAX_LEN
diff --git a/lib/odp-util.c b/lib/odp-util.c
index ff08821..69c34ca 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -246,12 +246,13 @@ static void
 format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key)
 {
     ds_put_format(ds, "flags=%d", key->flags);
+    ds_put_format(ds, "ttl=%d", key->ttl);
     ds_put_format(ds, ",mdtype=%d", key->mdtype);
     ds_put_format(ds, ",np=%d", key->np);
     ds_put_format(ds, ",spi=0x%x",
-                  (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
+                  nsh_path_hdr_to_spi_uint32(key->path_hdr));
     ds_put_format(ds, ",si=%d",
-                  (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT);
+                  nsh_path_hdr_to_si(key->path_hdr));
 
     switch (key->mdtype) {
         case NSH_M_TYPE1:
@@ -311,17 +312,16 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,
         format_nsh_key(ds, key);
     } else {
         bool first = true;
-        uint32_t spi = (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
-        uint32_t spi_mask = (ntohl(mask->path_hdr) & NSH_SPI_MASK) >>
-                                NSH_SPI_SHIFT;
-        if (spi_mask == 0x00ffffff) {
+        uint32_t spi = nsh_path_hdr_to_spi_uint32(key->path_hdr);
+        uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr);
+        if (spi_mask == (NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
             spi_mask = UINT32_MAX;
         }
-        uint8_t si = (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
-        uint8_t si_mask = (ntohl(mask->path_hdr) & NSH_SI_MASK) >>
-                              NSH_SI_SHIFT;
+        uint8_t si = nsh_path_hdr_to_si(key->path_hdr);
+        uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr);
 
         format_uint8_masked(ds, &first, "flags", key->flags, mask->flags);
+        format_uint8_masked(ds, &first, "ttl", key->ttl, mask->ttl);
         format_uint8_masked(ds, &first, "mdtype", key->mdtype, mask->mdtype);
         format_uint8_masked(ds, &first, "np", key->np, mask->np);
         format_be32_masked(ds, &first, "spi", htonl(spi), htonl(spi_mask));
@@ -342,13 +342,14 @@ format_odp_push_nsh_action(struct ds *ds,
                            const struct nsh_hdr *nsh_hdr)
  {
     size_t mdlen = nsh_hdr_len(nsh_hdr) - NSH_BASE_HDR_LEN;
-    uint32_t path_hdr = ntohl(get_16aligned_be32(&nsh_hdr->path_hdr));
-    uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
-    uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
+    uint32_t spi = ntohl(nsh_get_spi(nsh_hdr));
+    uint8_t si = nsh_get_si(nsh_hdr);
     uint8_t flags = nsh_get_flags(nsh_hdr);
+    uint8_t ttl = nsh_get_ttl(nsh_hdr);
 
     ds_put_cstr(ds, "push_nsh(");
     ds_put_format(ds, "flags=%d", flags);
+    ds_put_format(ds, ",ttl=%d", ttl);
     ds_put_format(ds, ",mdtype=%d", nsh_hdr->md_type);
     ds_put_format(ds, ",np=%d", nsh_hdr->next_proto);
     ds_put_format(ds, ",spi=0x%x", spi);
@@ -1785,7 +1786,7 @@ find_end:
 }
 
 static void
-nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh,
+nsh_key_to_attr(struct ofpbuf *buf, const struct ovs_key_nsh *nsh,
                 uint8_t * metadata, size_t md_size,
                 bool is_mask)
 {
@@ -1793,10 +1794,10 @@ nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh,
     struct ovs_nsh_key_base base;
 
     base.flags = nsh->flags;
+    base.ttl = nsh->ttl;
     base.mdtype = nsh->mdtype;
     base.np = nsh->np;
-    base.path_hdr = htonl((ntohl(nsh->spi) << NSH_SPI_SHIFT) |
-                          nsh->si);
+    base.path_hdr = nsh->path_hdr;
 
     nsh_key_ofs = nl_msg_start_nested(buf, OVS_KEY_ATTR_NSH);
     nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_BASE, &base, sizeof base);
@@ -1831,8 +1832,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
     int n = 0;
     int ret = 0;
     uint32_t spi = 0;
+    uint8_t si = 255;
     uint32_t cd;
-    struct flow_nsh nsh;
+    struct ovs_key_nsh nsh;
     uint8_t metadata[NSH_CTX_HDRS_MAX_LEN];
     uint8_t md_size = 0;
 
@@ -1843,10 +1845,10 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
 
     /* The default is NSH_M_TYPE1 */
     nsh.flags = 0;
+    nsh.ttl = 63;
     nsh.mdtype = NSH_M_TYPE1;
     nsh.np = NSH_P_ETHERNET;
-    nsh.spi = 0;
-    nsh.si = 255;
+    nsh.path_hdr = nsh_spi_si_to_path_hdr(0, 255);
     memset(nsh.context, 0, NSH_M_TYPE1_MDLEN);
 
     for (;;) {
@@ -1858,6 +1860,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
         if (ovs_scan_len(s, &n, "flags=%"SCNi8, &nsh.flags)) {
             continue;
         }
+        if (ovs_scan_len(s, &n, "ttl=%"SCNi8, &nsh.ttl)) {
+            continue;
+        }
         if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &nsh.mdtype)) {
             switch (nsh.mdtype) {
             case NSH_M_TYPE1:
@@ -1877,10 +1882,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
             continue;
         }
         if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &spi)) {
-            nsh.spi = htonl(spi);
             continue;
         }
-        if (ovs_scan_len(s, &n, "si=%"SCNi8, &nsh.si)) {
+        if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) {
             continue;
         }
         if (nsh.mdtype == NSH_M_TYPE1) {
@@ -1925,6 +1929,7 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
     }
 out:
     if (ret >= 0) {
+        nsh.path_hdr = nsh_spi_si_to_path_hdr(spi, si);
         size_t offset = nl_msg_start_nested(actions, OVS_ACTION_ATTR_PUSH_NSH);
         nsh_key_to_attr(actions, &nsh, metadata, md_size, false);
         nl_msg_end_nested(actions, offset);
@@ -2352,6 +2357,7 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
     const struct nlattr *a;
     bool unknown = false;
     uint8_t flags = 0;
+    uint8_t ttl = 63;
     size_t mdlen = 0;
     bool has_md1 = false;
     bool has_md2 = false;
@@ -2373,6 +2379,7 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
             nsh_hdr->md_type = base->mdtype;
             put_16aligned_be32(&nsh_hdr->path_hdr, base->path_hdr);
             flags = base->flags;
+            ttl = base->ttl;
             break;
         }
         case OVS_NSH_KEY_ATTR_MD1: {
@@ -2416,14 +2423,13 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
     }
 
     /* nsh header length  = NSH_BASE_HDR_LEN + mdlen */
-    nsh_hdr->ver_flags_ttl_len = htons(flags << NSH_FLAGS_SHIFT |
-                               (NSH_BASE_HDR_LEN + mdlen) >> 2);
+    nsh_set_flags_ttl_len(nsh_hdr, flags, ttl, NSH_BASE_HDR_LEN + mdlen);
 
     return ODP_FIT_PERFECT;
 }
 
 enum odp_key_fitness
-odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh)
+odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh)
 {
     unsigned int left;
     const struct nlattr *a;
@@ -2444,11 +2450,10 @@ odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh)
         case OVS_NSH_KEY_ATTR_BASE: {
             const struct ovs_nsh_key_base *base = nl_attr_get(a);
             nsh->flags = base->flags;
+            nsh->ttl = base->ttl;
             nsh->mdtype = base->mdtype;
             nsh->np = base->np;
-            nsh->spi = htonl((ntohl(base->path_hdr) & NSH_SPI_MASK) >>
-                                 NSH_SPI_SHIFT);
-            nsh->si = (ntohl(base->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
+            nsh->path_hdr = base->path_hdr;
             break;
         }
         case OVS_NSH_KEY_ATTR_MD1: {
@@ -3197,15 +3202,17 @@ format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
         case OVS_NSH_KEY_ATTR_UNSPEC:
             break;
         case OVS_NSH_KEY_ATTR_BASE: {
-            const struct ovs_nsh_key_base * base = nl_attr_get(a);
-            const struct ovs_nsh_key_base * base_mask
+            const struct ovs_nsh_key_base *base = nl_attr_get(a);
+            const struct ovs_nsh_key_base *base_mask
                 = ma ? nl_attr_get(ma) : NULL;
             nsh.flags = base->flags;
+            nsh.ttl = base->ttl;
             nsh.mdtype = base->mdtype;
             nsh.np = base->np;
             nsh.path_hdr = base->path_hdr;
             if (base_mask) {
                 nsh_mask.flags = base_mask->flags;
+                nsh_mask.ttl = base_mask->ttl;
                 nsh_mask.mdtype = base_mask->mdtype;
                 nsh_mask.np = base_mask->np;
                 nsh_mask.path_hdr = base_mask->path_hdr;
@@ -3213,8 +3220,8 @@ format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
             break;
         }
         case OVS_NSH_KEY_ATTR_MD1: {
-            const struct ovs_nsh_key_md1 * md1 = nl_attr_get(a);
-            const struct ovs_nsh_key_md1 * md1_mask
+            const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
+            const struct ovs_nsh_key_md1 *md1_mask
                 = ma ? nl_attr_get(ma) : NULL;
             memcpy(nsh.context, md1->context, sizeof md1->context);
             if (md1_mask) {
@@ -4822,7 +4829,9 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
     if (strncmp(s, "nsh(", 4) == 0) {
         const char *start = s;
         int len;
-        struct flow_nsh skey, smask;
+        struct ovs_key_nsh skey, smask;
+        uint32_t spi = 0, spi_mask = 0;
+        uint8_t si = 0, si_mask = 0;
 
         s += 4;
 
@@ -4863,7 +4872,7 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
 
             if (strncmp(s, "spi=", 4) == 0) {
                 s += 4;
-                len = scan_be32(s, &skey.spi, mask ? &smask.spi : NULL);
+                len = scan_u32(s, &spi, mask ? &spi_mask : NULL);
                 if (len == 0) {
                     return -EINVAL;
                 }
@@ -4873,7 +4882,7 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
 
             if (strncmp(s, "si=", 3) == 0) {
                 s += 3;
-                len = scan_u8(s, &skey.si, mask ? &smask.si : NULL);
+                len = scan_u8(s, &si, mask ? &si_mask : NULL);
                 if (len == 0) {
                     return -EINVAL;
                 }
@@ -4929,6 +4938,9 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
             return -EINVAL;
         }
 
+        skey.path_hdr = nsh_spi_si_to_path_hdr(spi, si);
+        smask.path_hdr = nsh_spi_si_to_path_hdr(spi_mask, si_mask);
+
         nsh_key_to_attr(key, &skey, NULL, 0, false);
         if (mask) {
             nsh_key_to_attr(mask, &smask, NULL, 0, true);
@@ -6990,59 +7002,29 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
     return 0;
 }
 
-static void
+static inline void
 get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask)
 {
-    nsh->flags = flow->nsh.flags;
-    nsh->mdtype = flow->nsh.mdtype;
-    nsh->np = flow->nsh.np;
-    nsh->path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) |
-                          flow->nsh.si);
-    if (is_mask) {
-        for (int i = 0; i < 4; i++) {
-            nsh->context[i] = flow->nsh.context[i];
-        }
-    } else {
-        switch (nsh->mdtype) {
-        case NSH_M_TYPE1:
-            for (int i = 0; i < 4; i++) {
-                nsh->context[i] = flow->nsh.context[i];
-            }
-            break;
-        case NSH_M_TYPE2:
-        default:
-            /* No match support for other MD formats yet. */
-            break;
+    *nsh = flow->nsh;
+    if (!is_mask) {
+        if (nsh->mdtype != NSH_M_TYPE1) {
+            memset(nsh->context, 0, sizeof(nsh->context));
         }
     }
 }
 
-static void
+static inline void
 put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,
             bool is_mask OVS_UNUSED)
 {
-    flow->nsh.flags = nsh->flags;
-    flow->nsh.mdtype = nsh->mdtype;
-    flow->nsh.np = nsh->np;
-    flow->nsh.spi = htonl((ntohl(nsh->path_hdr) & NSH_SPI_MASK) >>
-                              NSH_SPI_SHIFT);
-    flow->nsh.si = (ntohl(nsh->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
-    switch (nsh->mdtype) {
-        case NSH_M_TYPE1:
-            for (int i = 0; i < 4; i++) {
-                flow->nsh.context[i] = nsh->context[i];
-            }
-            break;
-        case NSH_M_TYPE2:
-        default:
-            /* No match support for other MD formats yet. */
-            memset(flow->nsh.context, 0, sizeof flow->nsh.context);
-            break;
+    flow->nsh = *nsh;
+    if (flow->nsh.mdtype != NSH_M_TYPE1) {
+        memset(flow->nsh.context, 0, sizeof(flow->nsh.context));
     }
 }
 
 static bool
-commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set,
+commit_nsh(const struct ovs_key_nsh * flow_nsh, bool use_masked_set,
            const struct ovs_key_nsh *key, struct ovs_key_nsh *base,
            struct ovs_key_nsh *mask, size_t size,
            struct ofpbuf *odp_actions)
@@ -7068,11 +7050,13 @@ commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set,
                                             OVS_ACTION_ATTR_SET_MASKED);
 
         nsh_base.flags = key->flags;
+        nsh_base.ttl = key->ttl;
         nsh_base.mdtype = key->mdtype;
         nsh_base.np = key->np;
         nsh_base.path_hdr = key->path_hdr;
 
         nsh_base_mask.flags = mask->flags;
+        nsh_base_mask.ttl = mask->ttl;
         nsh_base_mask.mdtype = mask->mdtype;
         nsh_base_mask.np = mask->np;
         nsh_base_mask.path_hdr = mask->path_hdr;
diff --git a/lib/odp-util.h b/lib/odp-util.h
index f7ce206..fafea62 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -159,7 +159,7 @@ struct odputil_keybuf {
 enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
                                            struct flow_tnl *);
 enum odp_key_fitness odp_nsh_key_from_attr(const struct nlattr *,
-                                           struct flow_nsh *);
+                                           struct ovs_key_nsh *);
 enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *,
                                            struct nsh_hdr *, size_t size);
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index c77477c..2f0a19c 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -5935,10 +5935,10 @@ rewrite_flow_push_nsh(struct xlate_ctx *ctx,
     /* Populate the flow with the new NSH header. */
     flow->packet_type = htonl(PT_NSH);
     flow->dl_type = htons(ETH_TYPE_NSH);
-    flow->nsh.flags = 0;    /* */
+    flow->nsh.flags = 0;
+    flow->nsh.ttl = 63;
     flow->nsh.np = np;
-    flow->nsh.spi = 0;
-    flow->nsh.si = 255;
+    flow->nsh.path_hdr = htonl(255);
 
     if (md_type == NSH_M_TYPE1) {
         flow->nsh.mdtype = NSH_M_TYPE1;
@@ -5951,6 +5951,7 @@ rewrite_flow_push_nsh(struct xlate_ctx *ctx,
     } else if (md_type == NSH_M_TYPE2) {
         flow->nsh.mdtype = NSH_M_TYPE2;
     }
+    flow->nsh.mdtype &= NSH_MDTYPE_MASK;
 
     return buf;
 }
diff --git a/tests/nsh.at b/tests/nsh.at
index bec6e87..6177cea 100644
--- a/tests/nsh.at
+++ b/tests/nsh.at
@@ -13,7 +13,7 @@ OVS_VSWITCHD_START([dnl
     add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
 
 AT_DATA([flows.txt], [dnl
-    table=0,in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,actions=set_field:0x80->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,2
+    table=0,in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,actions=set_field:0x2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,2
 ])
 
 AT_CHECK([
@@ -21,25 +21,25 @@ AT_CHECK([
     ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
     ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions
 ], [0], [dnl
- in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:128->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2
+ in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2
 ])
 
 AT_CHECK([
-    ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00'
+    ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00'
 ], [0], [dnl
-Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
 
 bridge("br0")
 -------------
- 0. in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768
-    set_field:128->nsh_flags
+ 0. in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768
+    set_field:2->nsh_flags
     set_field:254->nsh_si
     set_field:0x44332211->nsh_c1
     output:2
 
-Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=128,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
-Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344
-Datapath actions: set(nsh(flags=128,spi=0x123456,si=254,c1=0x44332211)),2
+Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=2,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344
+Datapath actions: set(nsh(flags=2,ttl=63,spi=0x123456,si=254,c1=0x44332211)),2
 ])
 
 OVS_VSWITCHD_STOP
@@ -103,15 +103,15 @@ bridge("br0")
     decap()
     decap()
 
-Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
 Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no
-Datapath actions: push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
+Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
 ])
 
 AT_CHECK([
     ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344'
 ], [0], [dnl
-Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
 
 bridge("br0")
 -------------
@@ -139,7 +139,7 @@ ovs-appctl time/warp 1000
 AT_CHECK([
     ovs-appctl dpctl/dump-flows dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
 ], [0], [flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)
 recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2
 ])
 
@@ -172,7 +172,7 @@ ovs-appctl time/warp 1000
 AT_CHECK([
     ovs-appctl dpctl/dump-flows dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
 ], [0], [flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4)
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4)
 recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100,pcp=0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2
 ])
 
@@ -230,15 +230,15 @@ bridge("br0")
     decap()
     decap()
 
-Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
 Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no
-Datapath actions: push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
+Datapath actions: push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
 ])
 
 AT_CHECK([
     ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234'
 ], [0], [dnl
-Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
 
 bridge("br0")
 -------------
@@ -266,7 +266,7 @@ ovs-appctl time/warp 1000
 AT_CHECK([
     ovs-appctl dpctl/dump-flows dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
 ], [0], [flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)
+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)
 recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2
 ])
 
@@ -575,11 +575,12 @@ AT_CHECK([
 
 ovs-appctl time/warp 1000
 ovs-appctl time/warp 1000
+ovs-appctl time/warp 1000
 
 AT_CHECK([
     ovs-appctl dpctl/dump-flows dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
 ], [0], [flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789))
+recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789))
 tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3000,si=255), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x1)
 tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x1),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6
 ])
@@ -633,7 +634,7 @@ ovs-appctl time/warp 1000
 AT_CHECK([
     ovs-appctl dpctl/dump-flows dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
 ], [0], [flow-dump from non-dpdk interfaces:
-recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789))
+recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789))
 tunnel(tun_id=0x0,src=20.0.0.1,dst=20.0.0.2,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(spi=0x3020,si=255), packets:1, bytes:108, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),set(nsh(spi=0x3020,si=254)),pop_eth,clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(src=30.0.0.2,dst=30.0.0.3)),tnl_pop(4789))
 tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3020,si=254), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x2)
 tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x2),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6
diff --git a/tests/ofproto.at b/tests/ofproto.at
index ec1dc51..b49f546 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2396,7 +2396,7 @@ head_table () {
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
         supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl
 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
-metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4
+metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
     matching:
       dp_hash: arbitrary mask
       recirc_id: exact match or wildcard
@@ -2567,6 +2567,7 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
       nsh_c2: arbitrary mask
       nsh_c3: arbitrary mask
       nsh_c4: arbitrary mask
+      nsh_ttl: exact match or wildcard
 
 ' $1
 }
-- 
2.1.0



More information about the dev mailing list