[ovs-dev] [PATCH] nsh: rework NSH netlink keys and actions

Eric Garver e at erig.me
Thu Aug 17 15:16:26 UTC 2017


On Thu, Aug 17, 2017 at 12:04:25PM +0000, Jan Scheurich wrote:
> Hi Yi,
> 
> As discussed please adjust the netlink uAPI for NSH as follows:
> 
> OVS_KEY_ATTR_NSH
> -- OVS_NSH_KEY_ATTR_BASE		mandatory
> -- OVS_NSH_KEY_ATTR_MD1 		conditional: if mdtype=MD1
> 
> OVS_ACTION_ATTR_PUSH_NSH
> -- OVS_NSH_KEY_ATTR_BASE		mandatory
> -- OVS_NSH_PUSH_ATTR_CONTEXT	conditional: currently if MD1 or 
> 					MD2 with TLV encap properties
> 
> OVS_ACTION_ATTR_POP_NSH		no data
> 
> Remove OVS_NSH_KEY_ATTR_MD2 because the MD2 context headers will not 
> be modelled as dedicated key fields but mapped to some generic TLV fields, 
> similar to but independent from the current tunnel metadata TLV registers.
> 
> Using the name OVS_NSH_KEY_ATTR_MD2 for the opaque context data in 
> PUSH_NSH would be misleading as the variable length byte array can carry
> any type of metadata format: MD1, MD2, or any future MD type. For that
> reason I prefer the generic name OVS_NSH_PUSH_ATTR_CONTEXT. 

Then why even have ATTR_MD1? Sounds a single attribute is enough to
support all cases.

> 
> Note that this attribute will not be used as part of the NSH key, so you might 
> consider generalizing the NSH attribute enum definition to 
> 
> enum ovs_nsh_attr {
> 	OVS_NSH_KEY_ATTR_BASE,  /* struct ovs_nsh_key_base. */
> 	OVS_NSH_KEY_ATTR_MD1,   /* struct ovs_nsh_key_md1. */
> 	OVS_NSH_PUSH_ATTR_CONTEXT,   /* opaque context headers */
> 	__OVS_NSH_ATTR_MAX
> };
> 
> BR, Jan
> 
> 
> > -----Original Message-----
> > From: ovs-dev-bounces at openvswitch.org [mailto:ovs-dev-
> > bounces at openvswitch.org] On Behalf Of Yi Yang
> > Sent: Wednesday, 16 August, 2017 09:56
> > To: dev at openvswitch.org
> > Subject: [ovs-dev] [PATCH] nsh: rework NSH netlink keys and actions
> > 
> > Per kernel data path requirements, this patch changes
> > OVS_KEY_ATTR_NSH
> > to nested attribute and adds three new NSH sub attribute keys:
> > 
> >     OVS_NSH_KEY_ATTR_BASE: for length-fixed NSH base header
> >     OVS_NSH_KEY_ATTR_MD1:  for length-fixed MD type 1 context
> >     OVS_NSH_KEY_ATTR_MD2:  for length-variable MD type 2 metadata
> > 
> > NSH match fields, set and PUSH_NSH action all use the below
> > nested attribute format:
> > 
> > OVS_KEY_ATTR_NSH begin
> >     OVS_NSH_KEY_ATTR_BASE
> >     OVS_NSH_KEY_ATTR_MD1
> > OVS_KEY_ATTR_NSH end
> > 
> > or
> > 
> > OVS_KEY_ATTR_NSH begin
> >     OVS_NSH_KEY_ATTR_BASE
> >     OVS_NSH_KEY_ATTR_MD2
> > OVS_KEY_ATTR_NSH end
> > 
> > In addition, NSH encap and decap actions are renamed as push_nsh
> > and pop_nsh to meet action naming convention.
> > 
> > Signed-off-by: Yi Yang <yi.y.yang at intel.com>
> > ---
> >  datapath/linux/compat/include/linux/openvswitch.h |  57 +-
> >  include/openvswitch/nsh.h                         |  32 +-
> >  include/openvswitch/packets.h                     |  11 +-
> >  lib/dpif-netdev.c                                 |   4 +-
> >  lib/dpif.c                                        |   4 +-
> >  lib/flow.c                                        |  15 +-
> >  lib/match.c                                       |  12 +-
> >  lib/meta-flow.c                                   |  13 +-
> >  lib/nx-match.c                                    |   4 +-
> >  lib/odp-execute.c                                 |  76 ++-
> >  lib/odp-util.c                                    | 713 ++++++++++++++++++----
> >  lib/odp-util.h                                    |   4 +
> >  lib/packets.c                                     |  24 +-
> >  lib/packets.h                                     |   6 +-
> >  ofproto/ofproto-dpif-ipfix.c                      |   4 +-
> >  ofproto/ofproto-dpif-sflow.c                      |   4 +-
> >  ofproto/ofproto-dpif-xlate.c                      |  24 +-
> >  tests/nsh.at                                      |  28 +-
> >  18 files changed, 773 insertions(+), 262 deletions(-)
> > 
> > diff --git a/datapath/linux/compat/include/linux/openvswitch.h
> > b/datapath/linux/compat/include/linux/openvswitch.h
> > index bc6c94b..d7f9029 100644
> > --- a/datapath/linux/compat/include/linux/openvswitch.h
> > +++ b/datapath/linux/compat/include/linux/openvswitch.h
> > @@ -369,7 +369,7 @@ enum ovs_key_attr {
> >  #ifndef __KERNEL__
> >  	/* Only used within userspace data path. */
> >  	OVS_KEY_ATTR_PACKET_TYPE,  /* be32 packet type */
> > -	OVS_KEY_ATTR_NSH,	   /* struct ovs_key_nsh */
> > +	OVS_KEY_ATTR_NSH,	   /* Nested set of ovs_nsh_key_* */
> >  #endif
> > 
> >  	__OVS_KEY_ATTR_MAX
> > @@ -492,13 +492,27 @@ struct ovs_key_ct_labels {
> >  	};
> >  };
> > 
> > -struct ovs_key_nsh {
> > -    __u8 flags;
> > -    __u8 mdtype;
> > -    __u8 np;
> > -    __u8 pad;
> > -    __be32 path_hdr;
> > -    __be32 c[4];
> > +enum ovs_nsh_key_attr {
> > +	OVS_NSH_KEY_ATTR_BASE,          /* struct ovs_nsh_key_base. */
> > +	OVS_NSH_KEY_ATTR_MD1,           /* struct ovs_nsh_key_md1. */
> > +	OVS_NSH_KEY_ATTR_MD2,           /* variable-length octets. */
> > +	__OVS_NSH_KEY_ATTR_MAX
> > +};
> > +
> > +#define OVS_NSH_KEY_ATTR_MAX (__OVS_NSH_KEY_ATTR_MAX - 1)
> > +
> > +struct ovs_nsh_key_base {
> > +	__u8 flags;
> > +	__u8 mdtype;
> > +	__u8 np;
> > +	__u8 pad;
> > +	__be32 path_hdr;
> > +};
> > +
> > +#define NSH_MD1_CONTEXT_SIZE 4
> > +
> > +struct ovs_nsh_key_md1 {
> > +	__be32 context[NSH_MD1_CONTEXT_SIZE];
> >  };
> > 
> >  /* OVS_KEY_ATTR_CT_STATE flags */
> > @@ -793,24 +807,7 @@ struct ovs_action_push_eth {
> >  	struct ovs_key_ethernet addresses;
> >  };
> > 
> > -#define OVS_ENCAP_NSH_MAX_MD_LEN 16
> > -/*
> > - * struct ovs_action_encap_nsh - %OVS_ACTION_ATTR_ENCAP_NSH
> > - * @flags: NSH header flags.
> > - * @mdtype: NSH metadata type.
> > - * @mdlen: Length of NSH metadata in bytes.
> > - * @np: NSH next_protocol: Inner packet type.
> > - * @path_hdr: NSH service path id and service index.
> > - * @metadata: NSH metadata for MD type 1 or 2
> > - */
> > -struct ovs_action_encap_nsh {
> > -    uint8_t flags;
> > -    uint8_t mdtype;
> > -    uint8_t mdlen;
> > -    uint8_t np;
> > -    __be32 path_hdr;
> > -    uint8_t metadata[OVS_ENCAP_NSH_MAX_MD_LEN];
> > -};
> > +#define OVS_PUSH_NSH_MAX_MD_LEN 248
> > 
> >  /**
> >   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
> > @@ -887,8 +884,8 @@ enum ovs_nat_attr {
> >   * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet
> > header onto the
> >   * packet.
> >   * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off
> > the packet.
> > - * @OVS_ACTION_ATTR_ENCAP_NSH: encap NSH action to push NSH
> > header.
> > - * @OVS_ACTION_ATTR_DECAP_NSH: decap NSH action to remove NSH
> > header.
> > + * @OVS_ACTION_ATTR_PUSH_NSH: push NSH header to the packet.
> > + * @OVS_ACTION_ATTR_POP_NSH: pop the outermost NSH header off
> > the packet.
> >   *
> >   * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.
> > Not all
> >   * fields within a header are modifiable, e.g. the IPv4 protocol and
> > fragment
> > @@ -930,8 +927,8 @@ enum ovs_action_attr {
> >  	OVS_ACTION_ATTR_TUNNEL_POP,    /* u32 port number. */
> >  	OVS_ACTION_ATTR_CLONE,         /* Nested OVS_CLONE_ATTR_*.
> > */
> >  	OVS_ACTION_ATTR_METER,         /* u32 meter number. */
> > -	OVS_ACTION_ATTR_ENCAP_NSH,    /* struct
> > ovs_action_encap_nsh. */
> > -	OVS_ACTION_ATTR_DECAP_NSH,    /* No argument. */
> > +	OVS_ACTION_ATTR_PUSH_NSH,      /* Nested
> > OVS_NSH_KEY_ATTR_*. */
> > +	OVS_ACTION_ATTR_POP_NSH,       /* No argument. */
> >  #endif
> >  	__OVS_ACTION_ATTR_MAX,	      /* Nothing past this will be
> > accepted
> >  				       * from userspace. */
> > diff --git a/include/openvswitch/nsh.h b/include/openvswitch/nsh.h
> > index f4ccadc..7f35513 100644
> > --- a/include/openvswitch/nsh.h
> > +++ b/include/openvswitch/nsh.h
> > @@ -51,7 +51,7 @@ extern "C" {
> >   * @nshc<1-4>: NSH Contexts.
> >   */
> >  struct nsh_md1_ctx {
> > -    ovs_16aligned_be32 c[4];
> > +    ovs_16aligned_be32 context[4];
> >  };
> > 
> >  struct nsh_md2_tlv {
> > @@ -72,6 +72,16 @@ struct nsh_hdr {
> >      };
> >  };
> > 
> > +#define NSH_M_TYPE2_MAX_LEN 256
> > +
> > +struct push_nsh_para {
> > +    ovs_be16 ver_flags_len;
> > +    uint8_t md_type;
> > +    uint8_t next_proto;
> > +    ovs_16aligned_be32 path_hdr;
> > +    uint8_t metadata[NSH_M_TYPE2_MAX_LEN-8];
> > +};
> > +
> >  /* Masking NSH header fields. */
> >  #define NSH_VER_MASK       0xc000
> >  #define NSH_VER_SHIFT      14
> > @@ -113,7 +123,7 @@ struct nsh_hdr {
> >  static inline uint16_t
> >  nsh_hdr_len(const struct nsh_hdr *nsh)
> >  {
> > -    return 4 * (ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >>
> > NSH_LEN_SHIFT;
> > +    return ((ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >>
> > NSH_LEN_SHIFT) << 2;
> >  }
> > 
> >  static inline struct nsh_md1_ctx *
> > @@ -128,6 +138,24 @@ nsh_md2_ctx(struct nsh_hdr *nsh)
> >      return &nsh->md2;
> >  }
> > 
> > +static inline uint8_t
> > +nsh_get_ver(const struct nsh_hdr *nsh)
> > +{
> > +    return (ntohs(nsh->ver_flags_len) & NSH_VER_MASK) >>
> > NSH_VER_SHIFT;
> > +}
> > +
> > +static inline uint8_t
> > +nsh_get_len(const struct nsh_hdr *nsh)
> > +{
> > +    return (ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >>
> > NSH_LEN_SHIFT;
> > +}
> > +
> > +static inline uint8_t
> > +nsh_get_flags(const struct nsh_hdr *nsh)
> > +{
> > +    return (ntohs(nsh->ver_flags_len) & NSH_FLAGS_MASK) >>
> > NSH_FLAGS_SHIFT;
> > +}
> > +
> >  #ifdef  __cplusplus
> >  }
> >  #endif
> > diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
> > index be91e02..5ee3099 100644
> > --- a/include/openvswitch/packets.h
> > +++ b/include/openvswitch/packets.h
> > @@ -84,7 +84,16 @@ struct flow_nsh {
> >      uint8_t np;
> >      uint8_t si;
> >      ovs_be32 spi;
> > -    ovs_be32 c[4];
> > +    ovs_be32 context[4];
> > +};
> > +
> > +struct ovs_key_nsh {
> > +    uint8_t flags;
> > +    uint8_t mdtype;
> > +    uint8_t np;
> > +    uint8_t pad;
> > +    ovs_be32 path_hdr;
> > +    ovs_be32 context[4];
> >  };
> > 
> >  /* NSH flags */
> > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> > index e2cd931..527fa0e 100644
> > --- a/lib/dpif-netdev.c
> > +++ b/lib/dpif-netdev.c
> > @@ -5407,8 +5407,8 @@ dp_execute_cb(void *aux_, struct
> > dp_packet_batch *packets_,
> >      case OVS_ACTION_ATTR_PUSH_ETH:
> >      case OVS_ACTION_ATTR_POP_ETH:
> >      case OVS_ACTION_ATTR_CLONE:
> > -    case OVS_ACTION_ATTR_ENCAP_NSH:
> > -    case OVS_ACTION_ATTR_DECAP_NSH:
> > +    case OVS_ACTION_ATTR_PUSH_NSH:
> > +    case OVS_ACTION_ATTR_POP_NSH:
> >      case __OVS_ACTION_ATTR_MAX:
> >          OVS_NOT_REACHED();
> >      }
> > diff --git a/lib/dpif.c b/lib/dpif.c
> > index 4c5eac6..2e6cd17 100644
> > --- a/lib/dpif.c
> > +++ b/lib/dpif.c
> > @@ -1263,8 +1263,8 @@ dpif_execute_helper_cb(void *aux_, struct
> > dp_packet_batch *packets_,
> >      case OVS_ACTION_ATTR_PUSH_ETH:
> >      case OVS_ACTION_ATTR_POP_ETH:
> >      case OVS_ACTION_ATTR_CLONE:
> > -    case OVS_ACTION_ATTR_ENCAP_NSH:
> > -    case OVS_ACTION_ATTR_DECAP_NSH:
> > +    case OVS_ACTION_ATTR_PUSH_NSH:
> > +    case OVS_ACTION_ATTR_POP_NSH:
> >      case OVS_ACTION_ATTR_UNSPEC:
> >      case __OVS_ACTION_ATTR_MAX:
> >          OVS_NOT_REACHED();
> > diff --git a/lib/flow.c b/lib/flow.c
> > index b2b10aa..4f898fc 100644
> > --- a/lib/flow.c
> > +++ b/lib/flow.c
> > @@ -533,7 +533,6 @@ bool
> >  parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key)
> >  {
> >      const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap;
> > -    uint16_t ver_flags_len;
> >      uint8_t version, length, flags;
> >      uint32_t path_hdr;
> > 
> > @@ -546,12 +545,10 @@ parse_nsh(const void **datap, size_t *sizep,
> > struct flow_nsh *key)
> > 
> >      memset(key, 0, sizeof(struct flow_nsh));
> > 
> > -    ver_flags_len = ntohs(nsh->ver_flags_len);
> > -    version = (ver_flags_len & NSH_VER_MASK) >> NSH_VER_SHIFT;
> > -    flags = (ver_flags_len & NSH_FLAGS_MASK) >> NSH_FLAGS_SHIFT;
> > +    version = nsh_get_ver(nsh);
> > +    flags = nsh_get_flags(nsh);
> > 
> > -    /* NSH header length is in 4 byte words. */
> > -    length = ((ver_flags_len & NSH_LEN_MASK) >> NSH_LEN_SHIFT) << 2;
> > +    length = nsh_hdr_len(nsh);
> > 
> >      if (version != 0) {
> >          return false;
> > @@ -572,7 +569,7 @@ parse_nsh(const void **datap, size_t *sizep, struct
> > flow_nsh *key)
> >      switch (key->mdtype) {
> >          case NSH_M_TYPE1:
> >              for (size_t i = 0; i < 4; i++) {
> > -                key->c[i] = get_16aligned_be32(&nsh->md1.c[i]);
> > +                key->context[i] = get_16aligned_be32(&nsh->md1.context[i]);
> >              }
> >              break;
> >          case NSH_M_TYPE2:
> > @@ -1692,7 +1689,7 @@ flow_wildcards_init_for_packet(struct
> > flow_wildcards *wc,
> >          WC_MASK_FIELD(wc, nsh.np);
> >          WC_MASK_FIELD(wc, nsh.spi);
> >          WC_MASK_FIELD(wc, nsh.si);
> > -        WC_MASK_FIELD(wc, nsh.c);
> > +        WC_MASK_FIELD(wc, nsh.context);
> >      } else {
> >          return; /* Unknown ethertype. */
> >      }
> > @@ -1826,7 +1823,7 @@ flow_wc_map(const struct flow *flow, struct
> > flowmap *map)
> >          FLOWMAP_SET(map, nsh.np);
> >          FLOWMAP_SET(map, nsh.spi);
> >          FLOWMAP_SET(map, nsh.si);
> > -        FLOWMAP_SET(map, nsh.c);
> > +        FLOWMAP_SET(map, nsh.context);
> >      }
> >  }
> > 
> > diff --git a/lib/match.c b/lib/match.c
> > index 36c78eb..8952c99 100644
> > --- a/lib/match.c
> > +++ b/lib/match.c
> > @@ -1266,10 +1266,14 @@ format_nsh_masked(struct ds *s, const struct
> > flow *f, const struct flow *m)
> >      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);
> >      if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1)
> > {
> > -        format_be32_masked_hex(s, "nsh_c1", f->nsh.c[0], m->nsh.c[0]);
> > -        format_be32_masked_hex(s, "nsh_c2", f->nsh.c[1], m->nsh.c[1]);
> > -        format_be32_masked_hex(s, "nsh_c3", f->nsh.c[2], m->nsh.c[2]);
> > -        format_be32_masked_hex(s, "nsh_c4", f->nsh.c[3], m->nsh.c[3]);
> > +        format_be32_masked_hex(s, "nsh_c1", f->nsh.context[0],
> > +                               m->nsh.context[0]);
> > +        format_be32_masked_hex(s, "nsh_c2", f->nsh.context[1],
> > +                               m->nsh.context[1]);
> > +        format_be32_masked_hex(s, "nsh_c3", f->nsh.context[2],
> > +                               m->nsh.context[2]);
> > +        format_be32_masked_hex(s, "nsh_c4", f->nsh.context[3],
> > +                               m->nsh.context[3]);
> >      }
> >  }
> > 
> > diff --git a/lib/meta-flow.c b/lib/meta-flow.c
> > index 64a8cf1..beeddf1 100644
> > --- a/lib/meta-flow.c
> > +++ b/lib/meta-flow.c
> > @@ -373,7 +373,7 @@ mf_is_all_wild(const struct mf_field *mf, const
> > struct flow_wildcards *wc)
> >      case MFF_NSH_C2:
> >      case MFF_NSH_C3:
> >      case MFF_NSH_C4:
> > -        return !wc->masks.nsh.c[mf->id - MFF_NSH_C1];
> > +        return !wc->masks.nsh.context[mf->id - MFF_NSH_C1];
> > 
> >      case MFF_N_IDS:
> >      default:
> > @@ -915,7 +915,7 @@ mf_get_value(const struct mf_field *mf, const
> > struct flow *flow,
> >      case MFF_NSH_C2:
> >      case MFF_NSH_C3:
> >      case MFF_NSH_C4:
> > -        value->be32 = flow->nsh.c[mf->id - MFF_NSH_C1];
> > +        value->be32 = flow->nsh.context[mf->id - MFF_NSH_C1];
> >          break;
> > 
> >      case MFF_N_IDS:
> > @@ -1230,7 +1230,8 @@ mf_set_value(const struct mf_field *mf,
> >      case MFF_NSH_C2:
> >      case MFF_NSH_C3:
> >      case MFF_NSH_C4:
> > -        MATCH_SET_FIELD_BE32(match, nsh.c[mf->id - MFF_NSH_C1],
> > value->be32);
> > +        MATCH_SET_FIELD_BE32(match, nsh.context[mf->id -
> > MFF_NSH_C1],
> > +                             value->be32);
> >          break;
> > 
> >      case MFF_N_IDS:
> > @@ -1621,7 +1622,7 @@ mf_set_flow_value(const struct mf_field *mf,
> >      case MFF_NSH_C2:
> >      case MFF_NSH_C3:
> >      case MFF_NSH_C4:
> > -        flow->nsh.c[mf->id - MFF_NSH_C1] = value->be32;
> > +        flow->nsh.context[mf->id - MFF_NSH_C1] = value->be32;
> >          break;
> > 
> >      case MFF_N_IDS:
> > @@ -2112,7 +2113,7 @@ mf_set_wild(const struct mf_field *mf, struct
> > match *match, char **err_str)
> >      case MFF_NSH_C2:
> >      case MFF_NSH_C3:
> >      case MFF_NSH_C4:
> > -        MATCH_SET_FIELD_MASKED(match, nsh.c[mf->id - MFF_NSH_C1],
> > +        MATCH_SET_FIELD_MASKED(match, nsh.context[mf->id -
> > MFF_NSH_C1],
> >                                 htonl(0), htonl(0));
> >          break;
> > 
> > @@ -2372,7 +2373,7 @@ mf_set(const struct mf_field *mf,
> >      case MFF_NSH_C2:
> >      case MFF_NSH_C3:
> >      case MFF_NSH_C4:
> > -        MATCH_SET_FIELD_MASKED(match, nsh.c[mf->id - MFF_NSH_C1],
> > +        MATCH_SET_FIELD_MASKED(match, nsh.context[mf->id -
> > MFF_NSH_C1],
> >                                 value->be32, mask->be32);
> >          break;
> > 
> > diff --git a/lib/nx-match.c b/lib/nx-match.c
> > index b782e8c..8f2a442 100644
> > --- a/lib/nx-match.c
> > +++ b/lib/nx-match.c
> > @@ -1165,8 +1165,8 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version
> > oxm, const struct match *match,
> >                  match->wc.masks.nsh.spi);
> >      nxm_put_8m(&ctx, MFF_NSH_SI, oxm, flow->nsh.si, match-
> > >wc.masks.nsh.si);
> >      for (int i = 0; i < 4; i++) {
> > -        nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.c[i],
> > -                    match->wc.masks.nsh.c[i]);
> > +        nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.context[i],
> > +                    match->wc.masks.nsh.context[i]);
> >      }
> > 
> >      /* Registers. */
> > diff --git a/lib/odp-execute.c b/lib/odp-execute.c
> > index 5f4d23a..1254c7a 100644
> > --- a/lib/odp-execute.c
> > +++ b/lib/odp-execute.c
> > @@ -273,19 +273,22 @@ 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 ovs_key_nsh *key,
> > -            const struct ovs_key_nsh *mask)
> > +odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key,
> > +            const struct flow_nsh *mask)
> >  {
> >      struct nsh_hdr *nsh = dp_packet_l3(packet);
> > +    ovs_be32 path_hdr;
> > 
> >      if (!mask) {
> >          nsh->ver_flags_len = htons(key->flags << NSH_FLAGS_SHIFT) |
> >                               (nsh->ver_flags_len & ~htons(NSH_FLAGS_MASK));
> > -        put_16aligned_be32(&nsh->path_hdr, key->path_hdr);
> > +        path_hdr = htonl((ntohl(key->spi) << NSH_SPI_SHIFT) |
> > +                         key->si);
> > +        put_16aligned_be32(&nsh->path_hdr, path_hdr);
> >          switch (nsh->md_type) {
> >              case NSH_M_TYPE1:
> >                  for (int i = 0; i < 4; i++) {
> > -                    put_16aligned_be32(&nsh->md1.c[i], key->c[i]);
> > +                    put_16aligned_be32(&nsh->md1.context[i], key->context[i]);
> >                  }
> >                  break;
> >              case NSH_M_TYPE2:
> > @@ -300,16 +303,24 @@ odp_set_nsh(struct dp_packet *packet, const
> > struct ovs_key_nsh *key,
> >          nsh->ver_flags_len = htons(flags << NSH_FLAGS_SHIFT) |
> >                               (nsh->ver_flags_len & ~htons(NSH_FLAGS_MASK));
> > 
> > -        ovs_be32 path_hdr = get_16aligned_be32(&nsh->path_hdr);
> > -        path_hdr = key->path_hdr | (path_hdr & ~mask->path_hdr);
> > +        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);
> > +        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);
> >          put_16aligned_be32(&nsh->path_hdr, path_hdr);
> >          switch (nsh->md_type) {
> >              case NSH_M_TYPE1:
> >                  for (int i = 0; i < 4; i++) {
> > -                    ovs_be32 p = get_16aligned_be32(&nsh->md1.c[i]);
> > -                    ovs_be32 k = key->c[i];
> > -                    ovs_be32 m = mask->c[i];
> > -                    put_16aligned_be32(&nsh->md1.c[i], k | (p & ~m));
> > +                    ovs_be32 p = get_16aligned_be32(&nsh->md1.context[i]);
> > +                    ovs_be32 k = key->context[i];
> > +                    ovs_be32 m = mask->context[i];
> > +                    put_16aligned_be32(&nsh->md1.context[i], k | (p & ~m));
> >                  }
> >                  break;
> >              case NSH_M_TYPE2:
> > @@ -345,9 +356,12 @@ odp_execute_set_action(struct dp_packet
> > *packet, const struct nlattr *a)
> >          odp_eth_set_addrs(packet, nl_attr_get(a), NULL);
> >          break;
> > 
> > -    case OVS_KEY_ATTR_NSH:
> > -        odp_set_nsh(packet, nl_attr_get(a), NULL);
> > +    case OVS_KEY_ATTR_NSH: {
> > +        struct flow_nsh nsh;
> > +        odp_nsh_key_from_attr(a, &nsh);
> > +        odp_set_nsh(packet, &nsh, NULL);
> >          break;
> > +    }
> > 
> >      case OVS_KEY_ATTR_IPV4:
> >          ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));
> > @@ -473,10 +487,26 @@ odp_execute_masked_set_action(struct
> > dp_packet *packet,
> >                            get_mask(a, struct ovs_key_ethernet));
> >          break;
> > 
> > -    case OVS_KEY_ATTR_NSH:
> > -        odp_set_nsh(packet, nl_attr_get(a),
> > -                    get_mask(a, struct ovs_key_nsh));
> > +    case OVS_KEY_ATTR_NSH: {
> > +        struct flow_nsh nsh, nsh_mask;
> > +        size_t size = nl_attr_get_size(a) / 2;
> > +
> > +        struct nlattr attr[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
> > +                                                sizeof(struct nlattr))];
> > +        struct nlattr mask[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
> > +                                                sizeof(struct nlattr))];
> > +
> > +        mask->nla_type = attr->nla_type = nl_attr_type(a);
> > +        mask->nla_len = attr->nla_len = NLA_HDRLEN + size;
> > +        memcpy(attr + 1, (char *)(a + 1), size);
> > +        memcpy(mask + 1, (char *)(a + 1) + size, size);
> > +
> > +        odp_nsh_key_from_attr(attr, &nsh);
> > +        odp_nsh_key_from_attr(mask, &nsh_mask);
> > +        odp_set_nsh(packet, &nsh, &nsh_mask);
> > +
> >          break;
> > +    }
> > 
> >      case OVS_KEY_ATTR_IPV4:
> >          odp_set_ipv4(packet, nl_attr_get(a),
> > @@ -652,8 +682,8 @@ requires_datapath_assistance(const struct nlattr
> > *a)
> >      case OVS_ACTION_ATTR_PUSH_ETH:
> >      case OVS_ACTION_ATTR_POP_ETH:
> >      case OVS_ACTION_ATTR_CLONE:
> > -    case OVS_ACTION_ATTR_ENCAP_NSH:
> > -    case OVS_ACTION_ATTR_DECAP_NSH:
> > +    case OVS_ACTION_ATTR_PUSH_NSH:
> > +    case OVS_ACTION_ATTR_POP_NSH:
> >          return false;
> > 
> >      case OVS_ACTION_ATTR_UNSPEC:
> > @@ -818,18 +848,20 @@ odp_execute_actions(void *dp, struct
> > dp_packet_batch *batch, bool steal,
> >              }
> >              break;
> > 
> > -        case OVS_ACTION_ATTR_ENCAP_NSH: {
> > -            const struct ovs_action_encap_nsh *enc_nsh = nl_attr_get(a);
> > +        case OVS_ACTION_ATTR_PUSH_NSH: {
> > +            struct push_nsh_para pnp;
> > +            const struct push_nsh_para * pnpp = &pnp;
> > +            odp_push_nsh_para_from_attr(nl_attr_get(a), &pnp);
> >              DP_PACKET_BATCH_FOR_EACH (packet, batch) {
> > -                encap_nsh(packet, enc_nsh);
> > +                push_nsh(packet, pnpp);
> >              }
> >              break;
> >          }
> > -        case OVS_ACTION_ATTR_DECAP_NSH: {
> > +        case OVS_ACTION_ATTR_POP_NSH: {
> >              size_t i, num = batch->count;
> > 
> >              DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
> > -                if (decap_nsh(packet)) {
> > +                if (pop_nsh(packet)) {
> >                      dp_packet_batch_refill(batch, packet, i);
> >                  } else {
> >                      dp_packet_delete(packet);
> > diff --git a/lib/odp-util.c b/lib/odp-util.c
> > index 4f1499e..a8cb1b4 100644
> > --- a/lib/odp-util.c
> > +++ b/lib/odp-util.c
> > @@ -129,8 +129,8 @@ odp_action_len(uint16_t type)
> >      case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct
> > ovs_action_push_eth);
> >      case OVS_ACTION_ATTR_POP_ETH: return 0;
> >      case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
> > -    case OVS_ACTION_ATTR_ENCAP_NSH: return ATTR_LEN_VARIABLE;
> > -    case OVS_ACTION_ATTR_DECAP_NSH: return 0;
> > +    case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE;
> > +    case OVS_ACTION_ATTR_POP_NSH: return 0;
> > 
> >      case OVS_ACTION_ATTR_UNSPEC:
> >      case __OVS_ACTION_ATTR_MAX:
> > @@ -264,7 +264,7 @@ format_nsh_key(struct ds *ds, const struct
> > ovs_key_nsh *key)
> >      switch (key->mdtype) {
> >          case NSH_M_TYPE1:
> >              for (int i = 0; i < 4; i++) {
> > -                ds_put_format(ds, ",c%d=0x%x", i + 1, ntohl(key->c[i]));
> > +                ds_put_format(ds, ",c%d=0x%x", i + 1, ntohl(key->context[i]));
> >              }
> >              break;
> >          case NSH_M_TYPE2:
> > @@ -334,40 +334,48 @@ format_nsh_key_mask(struct ds *ds, const struct
> > ovs_key_nsh *key,
> >          format_uint8_masked(ds, &first, "np", key->np, mask->np);
> >          format_be32_masked(ds, &first, "spi", htonl(spi), htonl(spi_mask));
> >          format_uint8_masked(ds, &first, "si", si, si_mask);
> > -        format_be32_masked(ds, &first, "c1", key->c[0], mask->c[0]);
> > -        format_be32_masked(ds, &first, "c2", key->c[1], mask->c[1]);
> > -        format_be32_masked(ds, &first, "c3", key->c[2], mask->c[2]);
> > -        format_be32_masked(ds, &first, "c4", key->c[3], mask->c[3]);
> > +        format_be32_masked(ds, &first, "c1", key->context[0],
> > +                           mask->context[0]);
> > +        format_be32_masked(ds, &first, "c2", key->context[1],
> > +                           mask->context[1]);
> > +        format_be32_masked(ds, &first, "c3", key->context[2],
> > +                           mask->context[2]);
> > +        format_be32_masked(ds, &first, "c4", key->context[3],
> > +                           mask->context[3]);
> >      }
> >  }
> > 
> >  static void
> > -format_odp_encap_nsh_action(struct ds *ds,
> > -                            const struct ovs_action_encap_nsh *encap_nsh)
> > +format_odp_push_nsh_action(struct ds *ds,
> > +                            const struct push_nsh_para *push_nsh)
> >   {
> > -    uint32_t path_hdr = ntohl(encap_nsh->path_hdr);
> > +    size_t mdlen = (((ntohs(push_nsh->ver_flags_len) & NSH_LEN_MASK)
> > +                         >> NSH_LEN_SHIFT) << 2) - NSH_BASE_HDR_LEN;
> > +    uint32_t path_hdr = ntohl(get_16aligned_be32(&push_nsh->path_hdr));
> >      uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
> >      uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
> > +    uint8_t flags = (ntohs(push_nsh->ver_flags_len) & NSH_FLAGS_MASK)
> > +                        >> NSH_FLAGS_SHIFT;
> > 
> > -    ds_put_cstr(ds, "encap_nsh(");
> > -    ds_put_format(ds, "flags=%d", encap_nsh->flags);
> > -    ds_put_format(ds, ",mdtype=%d", encap_nsh->mdtype);
> > -    ds_put_format(ds, ",np=%d", encap_nsh->np);
> > +    ds_put_cstr(ds, "push_nsh(");
> > +    ds_put_format(ds, "flags=%d", flags);
> > +    ds_put_format(ds, ",mdtype=%d", push_nsh->md_type);
> > +    ds_put_format(ds, ",np=%d", push_nsh->next_proto);
> >      ds_put_format(ds, ",spi=0x%x", spi);
> >      ds_put_format(ds, ",si=%d", si);
> > -    switch (encap_nsh->mdtype) {
> > +    switch (push_nsh->md_type) {
> >      case NSH_M_TYPE1: {
> >          struct nsh_md1_ctx *md1_ctx =
> > -            ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh->metadata);
> > +            ALIGNED_CAST(struct nsh_md1_ctx *, push_nsh->metadata);
> >          for (int i = 0; i < 4; i++) {
> >              ds_put_format(ds, ",c%d=0x%x", i + 1,
> > -                          ntohl(get_16aligned_be32(&md1_ctx->c[i])));
> > +                          ntohl(get_16aligned_be32(&md1_ctx->context[i])));
> >          }
> >          break;
> >      }
> >      case NSH_M_TYPE2:
> >          ds_put_cstr(ds, ",md2=");
> > -        ds_put_hex(ds, encap_nsh->metadata, encap_nsh->mdlen);
> > +        ds_put_hex(ds, push_nsh->metadata, mdlen);
> >          break;
> >      default:
> >          OVS_NOT_REACHED();
> > @@ -1057,11 +1065,15 @@ format_odp_action(struct ds *ds, const struct
> > nlattr *a,
> >      case OVS_ACTION_ATTR_CLONE:
> >          format_odp_clone_action(ds, a, portno_names);
> >          break;
> > -    case OVS_ACTION_ATTR_ENCAP_NSH:
> > -        format_odp_encap_nsh_action(ds, nl_attr_get(a));
> > +    case OVS_ACTION_ATTR_PUSH_NSH: {
> > +        struct push_nsh_para pnp;
> > +        const struct push_nsh_para *pnpp = &pnp;
> > +        odp_push_nsh_para_from_attr(nl_attr_get(a), &pnp);
> > +        format_odp_push_nsh_action(ds, pnpp);
> >          break;
> > -    case OVS_ACTION_ATTR_DECAP_NSH:
> > -        ds_put_cstr(ds, "decap_nsh()");
> > +    }
> > +    case OVS_ACTION_ATTR_POP_NSH:
> > +        ds_put_cstr(ds, "pop_nsh()");
> >          break;
> >      case OVS_ACTION_ATTR_UNSPEC:
> >      case __OVS_ACTION_ATTR_MAX:
> > @@ -1780,27 +1792,74 @@ find_end:
> >      return s - s_;
> >  }
> > 
> > +static void
> > +nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh,
> > +                uint8_t * metadata, size_t md_size,
> > +                bool is_mask)
> > +{
> > +    size_t nsh_key_ofs;
> > +    struct ovs_nsh_key_base base;
> > +    struct ovs_nsh_key_md1 md1;
> > +
> > +    base.flags = nsh->flags;
> > +    base.mdtype = nsh->mdtype;
> > +    base.np = nsh->np;
> > +    base.path_hdr = htonl((ntohl(nsh->spi) << NSH_SPI_SHIFT) |
> > +                          nsh->si);
> > +
> > +    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);
> > +
> > +    if (is_mask) {
> > +        for (int i = 0; i < 4; i++) {
> > +            md1.context[i] = nsh->context[i];
> > +        }
> > +        nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_MD1, &md1, sizeof
> > md1);
> > +    } else {
> > +        switch (nsh->mdtype) {
> > +        case NSH_M_TYPE1:
> > +            for (int i = 0; i < 4; i++) {
> > +                md1.context[i] = nsh->context[i];
> > +            }
> > +            nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_MD1, &md1, sizeof
> > md1);
> > +            break;
> > +        case NSH_M_TYPE2:
> > +            if (metadata && md_size > 0) {
> > +                nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_MD2, metadata,
> > +                                  md_size);
> > +            }
> > +            break;
> > +        default:
> > +            /* No match support for other MD formats yet. */
> > +            break;
> > +        }
> > +    }
> > +    nl_msg_end_nested(buf, nsh_key_ofs);
> > +}
> > +
> > +
> >  static int
> > -parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions)
> > +parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
> >  {
> >      int n = 0;
> >      int ret = 0;
> > -    struct ovs_action_encap_nsh encap_nsh;
> > -    uint32_t spi;
> > -    uint8_t si;
> >      uint32_t cd;
> > +    struct flow_nsh nsh;
> > +    uint8_t *metadata = NULL;
> > +    uint8_t md_size = 0;
> > 
> > -    if (!ovs_scan_len(s, &n, "encap_nsh(")) {
> > +    if (!ovs_scan_len(s, &n, "push_nsh(")) {
> >          ret = -EINVAL;
> >          goto out;
> >      }
> > 
> >      /* The default is NSH_M_TYPE1 */
> > -    encap_nsh.flags = 0;
> > -    encap_nsh.mdtype = NSH_M_TYPE1;
> > -    encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
> > -    encap_nsh.path_hdr = htonl(255);
> > -    memset(encap_nsh.metadata, 0, NSH_M_TYPE1_MDLEN);
> > +    nsh.flags = 0;
> > +    nsh.mdtype = NSH_M_TYPE1;
> > +    nsh.np = NSH_P_ETHERNET;
> > +    nsh.spi = 0;
> > +    nsh.si = 255;
> > +    memset(nsh.context, 0, NSH_M_TYPE1_MDLEN);
> > 
> >      for (;;) {
> >          n += strspn(s + n, delimiters);
> > @@ -1808,17 +1867,17 @@ parse_odp_encap_nsh_action(const char *s,
> > struct ofpbuf *actions)
> >              break;
> >          }
> > 
> > -        if (ovs_scan_len(s, &n, "flags=%"SCNi8, &encap_nsh.flags)) {
> > +        if (ovs_scan_len(s, &n, "flags=%"SCNi8, &nsh.flags)) {
> >              continue;
> >          }
> > -        if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &encap_nsh.mdtype)) {
> > -            switch (encap_nsh.mdtype) {
> > +        if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &nsh.mdtype)) {
> > +            switch (nsh.mdtype) {
> >              case NSH_M_TYPE1:
> >                  /* This is the default format. */;
> >                  break;
> >              case NSH_M_TYPE2:
> >                  /* Length will be updated later. */
> > -                encap_nsh.mdlen = 0;
> > +                md_size = 0;
> >                  break;
> >              default:
> >                  ret = -EINVAL;
> > @@ -1826,65 +1885,60 @@ parse_odp_encap_nsh_action(const char *s,
> > struct ofpbuf *actions)
> >              }
> >              continue;
> >          }
> > -        if (ovs_scan_len(s, &n, "np=%"SCNi8, &encap_nsh.np)) {
> > +        if (ovs_scan_len(s, &n, "np=%"SCNi8, &nsh.np)) {
> >              continue;
> >          }
> > -        if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &spi)) {
> > -            encap_nsh.path_hdr =
> > -                    htonl(((spi << NSH_SPI_SHIFT) & NSH_SPI_MASK) |
> > -                            (ntohl(encap_nsh.path_hdr) & ~NSH_SPI_MASK));
> > +        if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &nsh.spi)) {
> > +            nsh.spi = htonl(nsh.spi);
> >              continue;
> >          }
> > -        if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) {
> > -            encap_nsh.path_hdr =
> > -                    htonl((si << NSH_SI_SHIFT) |
> > -                            (ntohl(encap_nsh.path_hdr) & ~NSH_SI_MASK));
> > +        if (ovs_scan_len(s, &n, "si=%"SCNi8, &nsh.si)) {
> >              continue;
> >          }
> > -        if (encap_nsh.mdtype == NSH_M_TYPE1) {
> > -            struct nsh_md1_ctx *md1 =
> > -                ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh.metadata);
> > +        if (nsh.mdtype == NSH_M_TYPE1) {
> >              if (ovs_scan_len(s, &n, "c1=0x%"SCNx32, &cd)) {
> > -                put_16aligned_be32(&md1->c[0], htonl(cd));
> > +                nsh.context[0] = htonl(cd);
> >                  continue;
> >              }
> >              if (ovs_scan_len(s, &n, "c2=0x%"SCNx32, &cd)) {
> > -                put_16aligned_be32(&md1->c[1], htonl(cd));
> > +                nsh.context[1] = htonl(cd);
> >                  continue;
> >              }
> >              if (ovs_scan_len(s, &n, "c3=0x%"SCNx32, &cd)) {
> > -                put_16aligned_be32(&md1->c[2], htonl(cd));
> > +                nsh.context[2] = htonl(cd);
> >                  continue;
> >              }
> >              if (ovs_scan_len(s, &n, "c4=0x%"SCNx32, &cd)) {
> > -                put_16aligned_be32(&md1->c[3], htonl(cd));
> > +                nsh.context[3] = htonl(cd);
> >                  continue;
> >              }
> >          }
> > -        else if (encap_nsh.mdtype == NSH_M_TYPE2) {
> > +        else if (nsh.mdtype == NSH_M_TYPE2) {
> >              struct ofpbuf b;
> >              char buf[512];
> >              size_t mdlen;
> >              if (ovs_scan_len(s, &n, "md2=0x%511[0-9a-fA-F]", buf)) {
> > -                ofpbuf_use_stub(&b, encap_nsh.metadata,
> > -                                OVS_ENCAP_NSH_MAX_MD_LEN);
> > +                metadata = xmalloc(NSH_M_TYPE2_MAX_LEN - 8);
> > +                ofpbuf_use_stub(&b, metadata,
> > +                                NSH_M_TYPE2_MAX_LEN - 8);
> >                  ofpbuf_put_hex(&b, buf, &mdlen);
> > -                encap_nsh.mdlen = mdlen;
> > +                md_size = mdlen;
> >                  ofpbuf_uninit(&b);
> >              }
> >              continue;
> >          }
> >      }
> >  out:
> > -    if (ret < 0) {
> > -        return ret;
> > -    } else {
> > -        size_t size = offsetof(struct ovs_action_encap_nsh, metadata)
> > -                + ROUND_UP(encap_nsh.mdlen, 4);
> > -        nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ENCAP_NSH,
> > -                          &encap_nsh, size);
> > -        return n;
> > +    if (ret >= 0) {
> > +        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);
> > +        ret = n;
> > +    }
> > +    if (metadata != NULL) {
> > +        free(metadata);
> >      }
> > +    return ret;
> >  }
> > 
> >  static int
> > @@ -2089,8 +2143,8 @@ parse_odp_action(const char *s, const struct
> > simap *port_names,
> >      }
> > 
> >      {
> > -        if (!strncmp(s, "encap_nsh(", 10)) {
> > -            int retval = parse_odp_encap_nsh_action(s, actions);
> > +        if (!strncmp(s, "push_nsh(", 9)) {
> > +            int retval = parse_odp_push_nsh_action(s, actions);
> >              if (retval < 0) {
> >                  return retval;
> >              }
> > @@ -2100,8 +2154,8 @@ parse_odp_action(const char *s, const struct
> > simap *port_names,
> > 
> >      {
> >          int n;
> > -        if (ovs_scan(s, "decap_nsh()%n", &n)) {
> > -            nl_msg_put_flag(actions, OVS_ACTION_ATTR_DECAP_NSH);
> > +        if (ovs_scan(s, "pop_nsh()%n", &n)) {
> > +            nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_NSH);
> >              return n;
> >          }
> >      }
> > @@ -2198,6 +2252,13 @@ static const struct attr_len_tbl
> > ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +
> >      [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = 16 },
> >  };
> > 
> > +static const struct attr_len_tbl
> > +ovs_nsh_key_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {
> > +    [OVS_NSH_KEY_ATTR_BASE]     = { .len = 8 },
> > +    [OVS_NSH_KEY_ATTR_MD1]      = { .len = 16 },
> > +    [OVS_NSH_KEY_ATTR_MD2]      = { .len = ATTR_LEN_VARIABLE },
> > +};
> > +
> >  static const struct attr_len_tbl
> > ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
> >      [OVS_KEY_ATTR_ENCAP]     = { .len = ATTR_LEN_NESTED },
> >      [OVS_KEY_ATTR_PRIORITY]  = { .len = 4 },
> > @@ -2229,7 +2290,9 @@ static const struct attr_len_tbl
> > ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
> >      [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = { .len = sizeof(struct
> > ovs_key_ct_tuple_ipv4) },
> >      [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = { .len = sizeof(struct
> > ovs_key_ct_tuple_ipv6) },
> >      [OVS_KEY_ATTR_PACKET_TYPE] = { .len = 4  },
> > -    [OVS_KEY_ATTR_NSH]       = { .len = sizeof(struct ovs_key_nsh) },
> > +    [OVS_KEY_ATTR_NSH]       = { .len = ATTR_LEN_NESTED,
> > +                                 .next = ovs_nsh_key_attr_lens,
> > +                                 .next_max = OVS_NSH_KEY_ATTR_MAX },
> >  };
> > 
> >  /* Returns the correct length of the payload for a flow key attribute of the
> > @@ -2280,6 +2343,116 @@ ovs_frag_type_to_string(enum ovs_frag_type
> > type)
> >      }
> >  }
> > 
> > +enum odp_key_fitness
> > +odp_push_nsh_para_from_attr(const struct nlattr *attr,
> > +                            struct push_nsh_para * pnp)
> > +{
> > +    unsigned int left;
> > +    const struct nlattr *a;
> > +    bool unknown = false;
> > +    uint8_t flags = 0;
> > +    size_t mdlen = 0;
> > +
> > +    NL_NESTED_FOR_EACH (a, left, attr) {
> > +        uint16_t type = nl_attr_type(a);
> > +        size_t len = nl_attr_get_size(a);
> > +        int expected_len = odp_key_attr_len(ovs_nsh_key_attr_lens,
> > +                                            OVS_NSH_KEY_ATTR_MAX, type);
> > +
> > +        if (len != expected_len && expected_len >= 0) {
> > +            return ODP_FIT_ERROR;
> > +        }
> > +
> > +        switch (type) {
> > +        case OVS_NSH_KEY_ATTR_BASE: {
> > +            const struct ovs_nsh_key_base *base = nl_attr_get(a);
> > +            pnp->next_proto = base->np;
> > +            pnp->md_type = base->mdtype;
> > +            put_16aligned_be32(&pnp->path_hdr, base->path_hdr);
> > +            flags = base->flags;
> > +            break;
> > +        }
> > +        case OVS_NSH_KEY_ATTR_MD1: {
> > +            const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
> > +            mdlen = sizeof *md1;
> > +            memcpy(pnp->metadata, md1, mdlen);
> > +            break;
> > +        }
> > +        case OVS_NSH_KEY_ATTR_MD2: {
> > +            const uint8_t *md2 = nl_attr_get(a);
> > +            mdlen = nl_attr_get_size(a);
> > +            memcpy(pnp->metadata, md2, mdlen);
> > +            break;
> > +        }
> > +        default:
> > +            /* Allow this to show up as unexpected, if there are unknown
> > +             * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH.
> > */
> > +            unknown = true;
> > +            break;
> > +        }
> > +    }
> > +
> > +    /* nsh header length  = NSH_BASE_HDR_LEN + mdlen */
> > +    pnp->ver_flags_len = htons(flags << NSH_FLAGS_SHIFT |
> > +                               (NSH_BASE_HDR_LEN + mdlen) >> 2);
> > +
> > +    if (unknown) {
> > +        return ODP_FIT_TOO_MUCH;
> > +    }
> > +    return ODP_FIT_PERFECT;
> > +}
> > +
> > +enum odp_key_fitness
> > +odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh)
> > +{
> > +    unsigned int left;
> > +    const struct nlattr *a;
> > +    bool unknown = false;
> > +
> > +    NL_NESTED_FOR_EACH (a, left, attr) {
> > +        uint16_t type = nl_attr_type(a);
> > +        size_t len = nl_attr_get_size(a);
> > +        int expected_len = odp_key_attr_len(ovs_nsh_key_attr_lens,
> > +                                            OVS_NSH_KEY_ATTR_MAX, type);
> > +
> > +        if (len != expected_len && expected_len >= 0) {
> > +            return ODP_FIT_ERROR;
> > +        }
> > +
> > +        switch (type) {
> > +        case OVS_NSH_KEY_ATTR_BASE: {
> > +            const struct ovs_nsh_key_base *base = nl_attr_get(a);
> > +            nsh->flags = base->flags;
> > +            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;
> > +            break;
> > +        }
> > +        case OVS_NSH_KEY_ATTR_MD1: {
> > +            const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
> > +            nsh->context[0] = md1->context[0];
> > +            nsh->context[1] = md1->context[1];
> > +            nsh->context[2] = md1->context[2];
> > +            nsh->context[3] = md1->context[3];
> > +            break;
> > +        }
> > +        case OVS_NSH_KEY_ATTR_MD2:
> > +        default:
> > +            /* Allow this to show up as unexpected, if there are unknown
> > +             * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH.
> > */
> > +            unknown = true;
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (unknown) {
> > +        return ODP_FIT_TOO_MUCH;
> > +    }
> > +    return ODP_FIT_PERFECT;
> > +}
> > +
> >  static enum odp_key_fitness
> >  odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask,
> >                          struct flow_tnl *tun)
> > @@ -2971,6 +3144,80 @@ format_odp_tun_geneve(const struct nlattr
> > *attr,
> >  }
> > 
> >  static void
> > +format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr
> > *mask_attr,
> > +                    struct ds *ds)
> > +{
> > +    unsigned int left;
> > +    const struct nlattr *a;
> > +    struct ovs_key_nsh nsh;
> > +    struct ovs_key_nsh nsh_mask;
> > +
> > +    memset(&nsh, 0, sizeof nsh);
> > +    memset(&nsh_mask, 0xff, sizeof nsh_mask);
> > +
> > +    NL_NESTED_FOR_EACH (a, left, attr) {
> > +        enum ovs_nsh_key_attr type = nl_attr_type(a);
> > +        const struct nlattr *ma = NULL;
> > +
> > +        if (mask_attr) {
> > +            ma = nl_attr_find__(nl_attr_get(mask_attr),
> > +                                nl_attr_get_size(mask_attr), type);
> > +        }
> > +
> > +        if (!check_attr_len(ds, a, ma, ovs_nsh_key_attr_lens,
> > +                            OVS_NSH_KEY_ATTR_MAX, true)) {
> > +            continue;
> > +        }
> > +
> > +        switch (type) {
> > +        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
> > +                = ma ? nl_attr_get(ma) : NULL;
> > +            nsh.flags = base->flags;
> > +            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.mdtype = base_mask->mdtype;
> > +                nsh_mask.np = base_mask->np;
> > +                nsh_mask.path_hdr = base_mask->path_hdr;
> > +            }
> > +            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
> > +                = ma ? nl_attr_get(ma) : NULL;
> > +            nsh.context[0] = md1->context[0];
> > +            nsh.context[1] = md1->context[1];
> > +            nsh.context[2] = md1->context[2];
> > +            nsh.context[3] = md1->context[3];
> > +            if (md1_mask) {
> > +                nsh_mask.context[0] = md1_mask->context[0];
> > +                nsh_mask.context[1] = md1_mask->context[1];
> > +                nsh_mask.context[2] = md1_mask->context[2];
> > +                nsh_mask.context[3] = md1_mask->context[3];
> > +            }
> > +            break;
> > +        }
> > +        case OVS_NSH_KEY_ATTR_MD2:
> > +        case __OVS_NSH_KEY_ATTR_MAX:
> > +        default:
> > +            /* No support for matching other metadata formats yet. */
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (mask_attr) {
> > +        format_nsh_key_mask(ds, &nsh, &nsh_mask);
> > +    } else {
> > +        format_nsh_key(ds, &nsh);
> > +    }
> > +}
> > +
> > +static void
> >  format_odp_tun_attr(const struct nlattr *attr, const struct nlattr
> > *mask_attr,
> >                      struct ds *ds, bool verbose)
> >  {
> > @@ -3448,9 +3695,7 @@ format_odp_key_attr__(const struct nlattr *a,
> > const struct nlattr *ma,
> >          break;
> >      }
> >      case OVS_KEY_ATTR_NSH: {
> > -        const struct ovs_key_nsh *mask = ma ? nl_attr_get(ma) : NULL;
> > -        const struct ovs_key_nsh *key = nl_attr_get(a);
> > -        format_nsh_key_mask(ds, key, mask);
> > +        format_odp_nsh_attr(a, ma, ds);
> >          break;
> >      }
> >      case OVS_KEY_ATTR_UNSPEC:
> > @@ -4549,6 +4794,129 @@ geneve_to_attr(struct ofpbuf *a, const void
> > *data_)
> >      } SCAN_END_SINGLE(ATTR)
> > 
> >  static int
> > +parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
> > +                            struct ofpbuf *mask)
> > +{
> > +    if (strncmp(s, "nsh(", 4) == 0) {
> > +        const char *start = s;
> > +        int len;
> > +        struct flow_nsh skey, smask;
> > +
> > +        s += 4;
> > +
> > +        memset(&skey, 0, sizeof skey);
> > +        memset(&smask, 0, sizeof smask);
> > +        do {
> > +            len = 0;
> > +
> > +            if (strncmp(s, "flags=", 6) == 0) {
> > +                s += 6;
> > +                len = scan_u8(s, &skey.flags, mask ? &smask.flags : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "mdtype=", 7) == 0) {
> > +                s += 7;
> > +                len = scan_u8(s, &skey.mdtype, mask ? &smask.mdtype : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "np=", 3) == 0) {
> > +                s += 3;
> > +                len = scan_u8(s, &skey.np, mask ? &smask.np : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "spi=", 4) == 0) {
> > +                s += 4;
> > +                len = scan_be32(s, &skey.spi, mask ? &smask.spi : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "si=", 3) == 0) {
> > +                s += 3;
> > +                len = scan_u8(s, &skey.si, mask ? &smask.si : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "c1=", 3) == 0) {
> > +                s += 3;
> > +                len = scan_be32(s, &skey.context[0],
> > +                                mask ? &smask.context[0] : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "c2=", 3) == 0) {
> > +                s += 3;
> > +                len = scan_be32(s, &skey.context[1],
> > +                                mask ? &smask.context[1] : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "c3=", 3) == 0) {
> > +                s += 3;
> > +                len = scan_be32(s, &skey.context[2],
> > +                                mask ? &smask.context[2] : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +
> > +            if (strncmp(s, "c4=", 3) == 0) {
> > +                s += 3;
> > +                len = scan_be32(s, &skey.context[3],
> > +                                mask ? &smask.context[3] : NULL);
> > +                if (len == 0) {
> > +                    return -EINVAL;
> > +                }
> > +                s += len;
> > +                continue;
> > +            }
> > +        } while (*s++ == ',' && len != 0);
> > +        if (s[-1] != ')') {
> > +            return -EINVAL;
> > +        }
> > +
> > +        nsh_key_to_attr(key, &skey, NULL, 0, false);
> > +        if (mask) {
> > +            nsh_key_to_attr(mask, &smask, NULL, 0, true);
> > +        }
> > +        return s - start;
> > +    }
> > +    return 0;
> > +}
> > +
> > +static int
> >  parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
> >                          struct ofpbuf *key, struct ofpbuf *mask)
> >  {
> > @@ -4694,16 +5062,13 @@ parse_odp_key_mask_attr(const char *s,
> > const struct simap *port_names,
> >          SCAN_FIELD("id=", be16, id);
> >      } SCAN_END(OVS_KEY_ATTR_PACKET_TYPE);
> > 
> > -    SCAN_BEGIN("nsh(", struct ovs_key_nsh) {
> > -        SCAN_FIELD("flags=", u8, flags);
> > -        SCAN_FIELD("mdtype=", u8, mdtype);
> > -        SCAN_FIELD("np=", u8, np);
> > -        SCAN_FIELD("path_hdr=", be32, path_hdr);
> > -        SCAN_FIELD("c1=", be32, c[0]);
> > -        SCAN_FIELD("c2=", be32, c[1]);
> > -        SCAN_FIELD("c3=", be32, c[2]);
> > -        SCAN_FIELD("c4=", be32, c[2]);
> > -    } SCAN_END(OVS_KEY_ATTR_NSH);
> > +    /* nsh is nested, it needs special process */
> > +    int ret = parse_odp_nsh_key_mask_attr(s, key, mask);
> > +    if (ret < 0) {
> > +       return ret;
> > +    } else {
> > +       s += ret;
> > +    }
> > 
> >      /* Encap open-coded. */
> >      if (!strncmp(s, "encap(", 6)) {
> > @@ -4994,11 +5359,7 @@ odp_flow_key_from_flow__(const struct
> > odp_flow_key_parms *parms,
> >              mpls_key[i].mpls_lse = data->mpls_lse[i];
> >          }
> >      } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
> > -        struct ovs_key_nsh *nsh_key;
> > -
> > -        nsh_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_NSH,
> > -                                            sizeof *nsh_key);
> > -        get_nsh_key(data, nsh_key, export_mask);
> > +        nsh_key_to_attr(buf, &data->nsh, NULL, 0, export_mask);
> >      }
> > 
> >      if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
> > @@ -5558,13 +5919,10 @@ parse_l2_5_onward(const struct nlattr
> > *attrs[OVS_KEY_ATTR_MAX + 1],
> >              expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NSH;
> >          }
> >          if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NSH)) {
> > -            const struct ovs_key_nsh *nsh_key;
> > -
> > -            nsh_key = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);
> > -            put_nsh_key(nsh_key, flow, false);
> > +            odp_nsh_key_from_attr(attrs[OVS_KEY_ATTR_NSH], &flow->nsh);
> >              if (is_mask) {
> > -                check_start = nsh_key;
> > -                check_len = sizeof *nsh_key;
> > +                check_start = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);
> > +                check_len = nl_attr_get_size(attrs[OVS_KEY_ATTR_NSH]);
> >                  expected_bit = OVS_KEY_ATTR_NSH;
> >              }
> >          }
> > @@ -6620,13 +6978,13 @@ get_nsh_key(const struct flow *flow, struct
> > ovs_key_nsh *nsh, bool is_mask)
> >                            flow->nsh.si);
> >      if (is_mask) {
> >          for (int i = 0; i < 4; i++) {
> > -            nsh->c[i] = flow->nsh.c[i];
> > +            nsh->context[i] = flow->nsh.context[i];
> >          }
> >      } else {
> >          switch (nsh->mdtype) {
> >          case NSH_M_TYPE1:
> >              for (int i = 0; i < 4; i++) {
> > -                nsh->c[i] = flow->nsh.c[i];
> > +                nsh->context[i] = flow->nsh.context[i];
> >              }
> >              break;
> >          case NSH_M_TYPE2:
> > @@ -6650,17 +7008,125 @@ put_nsh_key(const struct ovs_key_nsh *nsh,
> > struct flow *flow,
> >      switch (nsh->mdtype) {
> >          case NSH_M_TYPE1:
> >              for (int i = 0; i < 4; i++) {
> > -                flow->nsh.c[i] = nsh->c[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.c, 0, sizeof flow->nsh.c);
> > +            memset(flow->nsh.context, 0, sizeof flow->nsh.context);
> >              break;
> >      }
> >  }
> > 
> > +static bool
> > +commit_nsh(const struct flow_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)
> > +{
> > +    enum ovs_key_attr attr = OVS_KEY_ATTR_NSH;
> > +
> > +    if (memcmp(key, base, size)  == 0) {
> > +        /* Mask bits are set when we have either read or set the
> > corresponding
> > +         * values.  Masked bits will be exact-matched, no need to set them
> > +         * if the value did not actually change. */
> > +        return false;
> > +    }
> > +
> > +    bool fully_masked = odp_mask_is_exact(attr, mask, size);
> > +
> > +    if (use_masked_set && !fully_masked) {
> > +        size_t nsh_key_ofs;
> > +        struct ovs_nsh_key_base nsh_base;
> > +        struct ovs_nsh_key_base nsh_base_mask;
> > +        struct ovs_nsh_key_md1 md1;
> > +        struct ovs_nsh_key_md1 md1_mask;
> > +        size_t offset = nl_msg_start_nested(odp_actions,
> > +                                            OVS_ACTION_ATTR_SET_MASKED);
> > +
> > +        nsh_base.flags = key->flags;
> > +        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.mdtype = mask->mdtype;
> > +        nsh_base_mask.np = mask->np;
> > +        nsh_base_mask.path_hdr = mask->path_hdr;
> > +
> > +        /* OVS_KEY_ATTR_NSH keys */
> > +        nsh_key_ofs = nl_msg_start_nested(odp_actions,
> > OVS_KEY_ATTR_NSH);
> > +
> > +        char *data = nl_msg_put_unspec_uninit(odp_actions,
> > +                                              OVS_NSH_KEY_ATTR_BASE,
> > +                                              sizeof(nsh_base));
> > +        const char *lkey = (char *)&nsh_base, *lmask = (char
> > *)&nsh_base_mask;
> > +        size_t lkey_size = sizeof(nsh_base);
> > +
> > +        while (lkey_size--) {
> > +            *data++ = *lkey++ & *lmask++;
> > +        }
> > +
> > +        switch (key->mdtype) {
> > +        case NSH_M_TYPE1:
> > +            for (int i = 0; i < 4; i++) {
> > +                md1.context[i] = key->context[i];
> > +                md1_mask.context[i] = mask->context[i];
> > +            }
> > +            data = nl_msg_put_unspec_uninit(odp_actions,
> > +                                            OVS_NSH_KEY_ATTR_MD1,
> > +                                            sizeof(md1));
> > +            lkey = (char *)&md1;
> > +            lmask = (char *)&md1_mask;
> > +            lkey_size = sizeof(md1);
> > +
> > +            while (lkey_size--) {
> > +                *data++ = *lkey++ & *lmask++;
> > +            }
> > +            break;
> > +        case NSH_M_TYPE2:
> > +        default:
> > +            /* No match support for other MD formats yet. */
> > +            break;
> > +        }
> > +
> > +        /* OVS_KEY_ATTR_NSH masks */
> > +        data = nl_msg_put_unspec_uninit(odp_actions,
> > +                                        OVS_NSH_KEY_ATTR_BASE,
> > +                                        sizeof(nsh_base_mask));
> > +        lmask = (char *)&nsh_base_mask;
> > +
> > +        memcpy(data, lmask, sizeof(nsh_base_mask));
> > +
> > +        switch (key->mdtype) {
> > +        case NSH_M_TYPE1:
> > +            data = nl_msg_put_unspec_uninit(odp_actions,
> > +                                            OVS_NSH_KEY_ATTR_MD1,
> > +                                            sizeof(md1_mask));
> > +            lmask = (char *)&md1_mask;
> > +            memcpy(data, lmask, sizeof(md1_mask));
> > +            break;
> > +        case NSH_M_TYPE2:
> > +        default:
> > +            /* No match support for other MD formats yet. */
> > +            break;
> > +        }
> > +        nl_msg_end_nested(odp_actions, nsh_key_ofs);
> > +
> > +        nl_msg_end_nested(odp_actions, offset);
> > +    } else {
> > +        if (!fully_masked) {
> > +            memset(mask, 0xff, size);
> > +        }
> > +        size_t offset = nl_msg_start_nested(odp_actions,
> > OVS_ACTION_ATTR_SET);
> > +        nsh_key_to_attr(odp_actions, flow_nsh, NULL, 0, false);
> > +        nl_msg_end_nested(odp_actions, offset);
> > +    }
> > +    memcpy(base, key, size);
> > +    return true;
> > +}
> > +
> >  static void
> >  commit_set_nsh_action(const struct flow *flow, struct flow *base_flow,
> >                        struct ofpbuf *odp_actions,
> > @@ -6684,8 +7150,8 @@ commit_set_nsh_action(const struct flow *flow,
> > struct flow *base_flow,
> >      mask.mdtype = 0;     /* Not writable. */
> >      mask.np = 0;         /* Not writable. */
> > 
> > -    if (commit(OVS_KEY_ATTR_NSH, use_masked, &key, &base, &mask,
> > sizeof key,
> > -               odp_actions)) {
> > +    if (commit_nsh(&base_flow->nsh, use_masked, &key, &base, &mask,
> > +            sizeof key, odp_actions)) {
> >          put_nsh_key(&base, base_flow, false);
> >          if (mask.mdtype != 0) { /* Mask was changed by commit(). */
> >              put_nsh_key(&mask, &wc->masks, true);
> > @@ -6788,49 +7254,36 @@ commit_set_pkt_mark_action(const struct
> > flow *flow, struct flow *base_flow,
> >  }
> > 
> >  static void
> > -odp_put_decap_nsh_action(struct ofpbuf *odp_actions)
> > +odp_put_pop_nsh_action(struct ofpbuf *odp_actions)
> >  {
> > -    nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_DECAP_NSH);
> > +    nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_NSH);
> >  }
> > 
> >  static void
> > -odp_put_encap_nsh_action(struct ofpbuf *odp_actions,
> > +odp_put_push_nsh_action(struct ofpbuf *odp_actions,
> >                           const struct flow *flow,
> >                           struct ofpbuf *encap_data)
> >  {
> > -    struct ovs_action_encap_nsh encap_nsh;
> > +    uint8_t * metadata = NULL;
> > +    uint8_t md_size = 0;
> > 
> > -    encap_nsh.flags = flow->nsh.flags;
> > -    encap_nsh.mdtype = flow->nsh.mdtype;
> > -    encap_nsh.np = flow->nsh.np;
> > -    encap_nsh.path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) |
> > -                                   flow->nsh.si);
> > -
> > -    switch (encap_nsh.mdtype) {
> > -    case NSH_M_TYPE1: {
> > -        struct nsh_md1_ctx *md1 =
> > -            ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh.metadata);
> > -        encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
> > -        for (int i = 0; i < 4; i++) {
> > -            put_16aligned_be32(&md1->c[i], flow->nsh.c[i]);
> > -        }
> > -        break;
> > -    }
> > +    switch (flow->nsh.mdtype) {
> >      case NSH_M_TYPE2:
> >          if (encap_data) {
> > -            ovs_assert(encap_data->size < OVS_ENCAP_NSH_MAX_MD_LEN);
> > -            encap_nsh.mdlen = encap_data->size;
> > -            memcpy(encap_nsh.metadata, encap_data->data, encap_data-
> > >size);
> > +            ovs_assert(encap_data->size < OVS_PUSH_NSH_MAX_MD_LEN);
> > +            metadata = encap_data->data;
> > +            md_size = encap_data->size;
> >          } else {
> > -            encap_nsh.mdlen = 0;
> > +            md_size = 0;
> >          }
> >          break;
> >      default:
> > -        encap_nsh.mdlen = 0;
> > +        md_size = 0;
> >          break;
> >      }
> > -    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_ENCAP_NSH,
> > -                      &encap_nsh, sizeof(encap_nsh));
> > +    size_t offset = nl_msg_start_nested(odp_actions,
> > OVS_ACTION_ATTR_PUSH_NSH);
> > +    nsh_key_to_attr(odp_actions, &flow->nsh, metadata, md_size, false);
> > +    nl_msg_end_nested(odp_actions, offset);
> >  }
> > 
> >  static void
> > @@ -6857,8 +7310,8 @@ commit_packet_type_change(const struct flow
> > *flow,
> >              break;
> >          }
> >          case PT_NSH:
> > -            /* encap_nsh */
> > -            odp_put_encap_nsh_action(odp_actions, flow, encap_data);
> > +            /* push_nsh */
> > +            odp_put_push_nsh_action(odp_actions, flow, encap_data);
> >              base_flow->packet_type = flow->packet_type;
> >              /* Update all packet headers in base_flow. */
> >              memcpy(&base_flow->dl_dst, &flow->dl_dst,
> > @@ -6883,8 +7336,8 @@ commit_packet_type_change(const struct flow
> > *flow,
> >               * No need to update the base flow here. */
> >              switch (ntohl(base_flow->packet_type)) {
> >              case PT_NSH:
> > -                /* decap_nsh. */
> > -                odp_put_decap_nsh_action(odp_actions);
> > +                /* pop_nsh. */
> > +                odp_put_pop_nsh_action(odp_actions);
> >                  break;
> >              default:
> >                  /* Checks are done during translation. */
> > diff --git a/lib/odp-util.h b/lib/odp-util.h
> > index 27c2ab4..e3cd4b5 100644
> > --- a/lib/odp-util.h
> > +++ b/lib/odp-util.h
> > @@ -158,6 +158,10 @@ 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 *);
> > +enum odp_key_fitness odp_push_nsh_para_from_attr(const struct nlattr
> > *,
> > +                                                 struct push_nsh_para *);
> > 
> >  int odp_ufid_from_string(const char *s_, ovs_u128 *ufid);
> >  void odp_format_ufid(const ovs_u128 *ufid, struct ds *);
> > diff --git a/lib/packets.c b/lib/packets.c
> > index 74d87ed..7270183 100644
> > --- a/lib/packets.c
> > +++ b/lib/packets.c
> > @@ -403,10 +403,11 @@ pop_mpls(struct dp_packet *packet, ovs_be16
> > ethtype)
> >  }
> > 
> >  void
> > -encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh
> > *encap)
> > +push_nsh(struct dp_packet *packet, const struct push_nsh_para *pnp)
> >  {
> >      struct nsh_hdr *nsh;
> > -    size_t length = NSH_BASE_HDR_LEN + encap->mdlen;
> > +    size_t length = ((ntohs(pnp->ver_flags_len) & NSH_LEN_MASK)
> > +                         >> NSH_LEN_SHIFT) << 2;
> >      uint8_t next_proto;
> > 
> >      switch (ntohl(packet->packet_type)) {
> > @@ -427,23 +428,8 @@ encap_nsh(struct dp_packet *packet, const struct
> > ovs_action_encap_nsh *encap)
> >      }
> > 
> >      nsh = (struct nsh_hdr *) dp_packet_push_uninit(packet, length);
> > -    nsh->ver_flags_len = htons(encap->flags << NSH_FLAGS_SHIFT | length
> > >> 2);
> > +    memcpy(nsh, pnp, length);
> >      nsh->next_proto = next_proto;
> > -    put_16aligned_be32(&nsh->path_hdr, encap->path_hdr);
> > -    nsh->md_type = encap->mdtype;
> > -    switch (nsh->md_type) {
> > -        case NSH_M_TYPE1:
> > -            nsh->md1 = *ALIGNED_CAST(struct nsh_md1_ctx *, encap-
> > >metadata);
> > -            break;
> > -        case NSH_M_TYPE2: {
> > -            /* The MD2 metadata in encap is already padded to 4 bytes. */
> > -            size_t len = ROUND_UP(encap->mdlen, 4);
> > -            memcpy(&nsh->md2, encap->metadata, len);
> > -            break;
> > -        }
> > -        default:
> > -            OVS_NOT_REACHED();
> > -    }
> > 
> >      packet->packet_type = htonl(PT_NSH);
> >      dp_packet_reset_offsets(packet);
> > @@ -451,7 +437,7 @@ encap_nsh(struct dp_packet *packet, const struct
> > ovs_action_encap_nsh *encap)
> >  }
> > 
> >  bool
> > -decap_nsh(struct dp_packet *packet)
> > +pop_nsh(struct dp_packet *packet)
> >  {
> >      struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);
> >      size_t length;
> > diff --git a/lib/packets.h b/lib/packets.h
> > index 705d0b2..6f59c2f 100644
> > --- a/lib/packets.h
> > +++ b/lib/packets.h
> > @@ -434,9 +434,9 @@ void push_eth(struct dp_packet *packet, const
> > struct eth_addr *dst,
> >                const struct eth_addr *src);
> >  void pop_eth(struct dp_packet *packet);
> > 
> > -void encap_nsh(struct dp_packet *packet,
> > -               const struct ovs_action_encap_nsh *encap_nsh);
> > -bool decap_nsh(struct dp_packet *packet);
> > +void push_nsh(struct dp_packet *packet,
> > +               const struct push_nsh_para *oapn);
> > +bool pop_nsh(struct dp_packet *packet);
> > 
> >  #define LLC_DSAP_SNAP 0xaa
> >  #define LLC_SSAP_SNAP 0xaa
> > diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
> > index 472c272..16976c4 100644
> > --- a/ofproto/ofproto-dpif-ipfix.c
> > +++ b/ofproto/ofproto-dpif-ipfix.c
> > @@ -2823,8 +2823,8 @@ dpif_ipfix_read_actions(const struct flow *flow,
> >          case OVS_ACTION_ATTR_POP_MPLS:
> >          case OVS_ACTION_ATTR_PUSH_ETH:
> >          case OVS_ACTION_ATTR_POP_ETH:
> > -        case OVS_ACTION_ATTR_ENCAP_NSH:
> > -        case OVS_ACTION_ATTR_DECAP_NSH:
> > +        case OVS_ACTION_ATTR_PUSH_NSH:
> > +        case OVS_ACTION_ATTR_POP_NSH:
> >          case OVS_ACTION_ATTR_UNSPEC:
> >          case __OVS_ACTION_ATTR_MAX:
> >          default:
> > diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
> > index 65a2003..1af1569 100644
> > --- a/ofproto/ofproto-dpif-sflow.c
> > +++ b/ofproto/ofproto-dpif-sflow.c
> > @@ -1199,8 +1199,8 @@ dpif_sflow_read_actions(const struct flow *flow,
> >  	    break;
> >  	case OVS_ACTION_ATTR_SAMPLE:
> >  	case OVS_ACTION_ATTR_CLONE:
> > -        case OVS_ACTION_ATTR_ENCAP_NSH:
> > -        case OVS_ACTION_ATTR_DECAP_NSH:
> > +        case OVS_ACTION_ATTR_PUSH_NSH:
> > +        case OVS_ACTION_ATTR_POP_NSH:
> >  	case OVS_ACTION_ATTR_UNSPEC:
> >  	case __OVS_ACTION_ATTR_MAX:
> >  	default:
> > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> > index 973e760..437c519 100644
> > --- a/ofproto/ofproto-dpif-xlate.c
> > +++ b/ofproto/ofproto-dpif-xlate.c
> > @@ -4392,8 +4392,8 @@ xlate_fixup_actions(struct ofpbuf *b, const struct
> > nlattr *actions,
> >          case OVS_ACTION_ATTR_CT:
> >          case OVS_ACTION_ATTR_PUSH_ETH:
> >          case OVS_ACTION_ATTR_POP_ETH:
> > -        case OVS_ACTION_ATTR_ENCAP_NSH:
> > -        case OVS_ACTION_ATTR_DECAP_NSH:
> > +        case OVS_ACTION_ATTR_PUSH_NSH:
> > +        case OVS_ACTION_ATTR_POP_NSH:
> >          case OVS_ACTION_ATTR_METER:
> >              ofpbuf_put(b, a, nl_attr_len_pad(a, left));
> >              break;
> > @@ -5804,17 +5804,17 @@ rewrite_flow_encap_ethernet(struct xlate_ctx
> > *ctx,
> > 
> >  /* For an MD2 NSH header returns a pointer to an ofpbuf with the
> > encoded
> >   * MD2 TLVs provided as encap properties to the encap operation. This
> > - * will be stored as encap_data in the ctx and copied into the encap_nsh
> > + * will be stored as encap_data in the ctx and copied into the push_nsh
> >   * action at the next commit. */
> >  static struct ofpbuf *
> > -rewrite_flow_encap_nsh(struct xlate_ctx *ctx,
> > -                       const struct ofpact_encap *encap,
> > -                       struct flow *flow,
> > -                       struct flow_wildcards *wc)
> > +rewrite_flow_push_nsh(struct xlate_ctx *ctx,
> > +                      const struct ofpact_encap *encap,
> > +                      struct flow *flow,
> > +                      struct flow_wildcards *wc)
> >  {
> >      ovs_be32 packet_type = flow->packet_type;
> >      const char *ptr = (char *) encap->props;
> > -    struct ofpbuf *buf = ofpbuf_new(OVS_ENCAP_NSH_MAX_MD_LEN);
> > +    struct ofpbuf *buf = ofpbuf_new(OVS_PUSH_NSH_MAX_MD_LEN);
> >      uint8_t md_type = NSH_M_TYPE1;
> >      uint8_t np = 0;
> >      int i;
> > @@ -5854,7 +5854,7 @@ rewrite_flow_encap_nsh(struct xlate_ctx *ctx,
> >          }
> >          ptr += ROUND_UP(prop_ptr->len, 8);
> >      }
> > -    if (buf->size == 0 || buf->size > OVS_ENCAP_NSH_MAX_MD_LEN) {
> > +    if (buf->size == 0 || buf->size > OVS_PUSH_NSH_MAX_MD_LEN) {
> >          ofpbuf_delete(buf);
> >          buf = NULL;
> >      }
> > @@ -5899,7 +5899,7 @@ rewrite_flow_encap_nsh(struct xlate_ctx *ctx,
> > 
> >      if (md_type == NSH_M_TYPE1) {
> >          flow->nsh.mdtype = NSH_M_TYPE1;
> > -        memset(flow->nsh.c, 0, sizeof flow->nsh.c);
> > +        memset(flow->nsh.context, 0, sizeof flow->nsh.context);
> >          if (buf) {
> >              /* Drop any MD2 context TLVs. */
> >              ofpbuf_delete(buf);
> > @@ -5930,7 +5930,7 @@ xlate_generic_encap_action(struct xlate_ctx
> > *ctx,
> >              rewrite_flow_encap_ethernet(ctx, flow, wc);
> >              break;
> >          case PT_NSH:
> > -            encap_data = rewrite_flow_encap_nsh(ctx, encap, flow, wc);
> > +            encap_data = rewrite_flow_push_nsh(ctx, encap, flow, wc);
> >              break;
> >          default:
> >              /* New packet type was checked during decoding. */
> > @@ -5973,7 +5973,7 @@ xlate_generic_decap_action(struct xlate_ctx
> > *ctx,
> >              }
> >              return false;
> >          case PT_NSH:
> > -            /* The decap_nsh action is generated at the commit executed as
> > +            /* The pop_nsh action is generated at the commit executed as
> >               * part of freezing the ctx for recirculation. Here we just set
> >               * the new packet type based on the NSH next protocol field. */
> >              switch (flow->nsh.np) {
> > diff --git a/tests/nsh.at b/tests/nsh.at
> > index aa80a2a..562f3da 100644
> > --- a/tests/nsh.at
> > +++ b/tests/nsh.at
> > @@ -105,7 +105,7 @@ bridge("br0")
> > 
> >  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,ns
> > h_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_pro
> > to=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:
> > encap_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0
> > x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),
> > pop_eth,decap_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > +Datapath actions:
> > push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x
> > 0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),p
> > op_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> >  ])
> > 
> >  AT_CHECK([
> > @@ -121,7 +121,7 @@ bridge("br0")
> > 
> >  Final flow: unchanged
> >  Megaflow:
> > recirc_id=0,eth,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi
> > =0x1234,nsh_c1=0x11223344
> > -Datapath actions: pop_eth,decap_nsh(),recirc(0x2)
> > +Datapath actions: pop_eth,pop_nsh(),recirc(0x2)
> >  ])
> > 
> >  # Now send two real ICMP echo request packets in on port p1
> > @@ -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),e
> > th_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s,
> > actions:encap_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x112233
> > 44,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44
> > :55:66),pop_eth,decap_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,mdtype=1,np=3,spi=0x1234,si=255,c1=0x1122334
> > 4,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(fra
> > g=no), packets:1, bytes:98, used:0.0s, actions:2
> >  ])
> > 
> > @@ -170,7 +170,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),encap_nsh(flags=0,mdtype=1,np=3,spi=0
> > x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),decap_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,mdtype=1,np=3,spi=0x
> > 0,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
> >  ])
> > 
> > @@ -195,7 +195,7 @@ ovs-vsctl set bridge br0 datapath_type=dummy \
> >          add-port br0 v4 -- set Interface v4 type=patch options:peer=v3
> > ofport_request=4])
> > 
> >  AT_DATA([flows.txt], [dnl
> > -
> > table=0,in_port=1,ip,actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345
> > 678))),set_field:0x1234-
> > >nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3
> > +
> > table=0,in_port=1,ip,actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345
> > 678),tlv(0x2000,20,0xfedcba9876543210))),set_field:0x1234-
> > >nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3
> > 
> > table=0,in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234,actions=
> > decap(),decap(),2
> >  ])
> > 
> > @@ -205,7 +205,7 @@ AT_CHECK([
> >      ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep
> > actions
> >  ], [0], [dnl
> >   in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234
> > actions=decap(),decap(),output:2
> > - ip,in_port=1
> > actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678))),set_field:0x123
> > 4->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,output:3
> > + ip,in_port=1
> > actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0x
> > fedcba9876543210))),set_field:0x1234-
> > >nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,output:3
> >  ])
> > 
> >  AT_CHECK([
> > @@ -216,7 +216,7 @@ Flow:
> > icmp,in_port=1,vlan_tci=0x0000,dl_src=00:11:22:33:44:55,dl_dst=66:77:88:
> > 99
> >  bridge("br0")
> >  -------------
> >   0. ip,in_port=1, priority 32768
> > -    encap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))
> > +
> > encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9
> > 876543210)))
> >      set_field:0x1234->nsh_spi
> >      encap(ethernet)
> >      set_field:11:22:33:44:55:66->eth_dst
> > @@ -230,7 +230,7 @@ bridge("br0")
> > 
> >  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,ns
> > h_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:
> > encap_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a0412
> > 345678),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,d
> > ecap_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
> > +Datapath actions:
> > push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a04123
> > 4567820001408fedcba9876543210),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([
> > @@ -246,7 +246,7 @@ bridge("br0")
> > 
> >  Final flow: unchanged
> >  Megaflow:
> > recirc_id=0,eth,in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi
> > =0x1234
> > -Datapath actions: pop_eth,decap_nsh(),recirc(0x2)
> > +Datapath actions: pop_eth,pop_nsh(),recirc(0x2)
> >  ])
> > 
> >  # Now send two real ICMP echo request packets in on port p1
> > @@ -264,7 +264,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),e
> > th_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s,
> > actions:encap_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x1000
> > 0a0412345678),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),po
> > p_eth,decap_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,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000
> > a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:0
> > 0,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(fra
> > g=no), packets:1, bytes:98, used:0.0s, actions:2
> >  ])
> > 
> > @@ -577,8 +577,8 @@ 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=1
> > 92.168.10.30,frag=no), packets:1, bytes:98, used:0.0s,
> > actions:pop_eth,encap_nsh(flags=0,mdtype=1,np=1,spi=0x3000,si=255,c1=0
> > x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,ty
> > pe=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv
> > 4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,d
> > st=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(s
> > rc=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:decap_nsh(),recirc(0x1)
> > +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=0x
> > 0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,typ
> > e=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,ds
> > t=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(sr
> > c=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(f
> > rag=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
> >  ])
> > 
> > @@ -631,9 +631,9 @@ 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=1
> > 92.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s,
> > actions:pop_eth,encap_nsh(flags=0,mdtype=1,np=1,spi=0x3020,si=255,c1=0
> > x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,ty
> > pe=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv
> > 4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,d
> > st=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(s
> > rc=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,mdtype=1,np=1,spi=0x3020,si=255,c1=0x
> > 0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,typ
> > e=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,ds
> > t=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(sr
> > c=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,typ
> > e=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,ds
> > t=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(sr
> > c=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:decap_nsh(),recirc(0x2)
> > +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(f
> > rag=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
> >  ])
> > 
> > --
> > 2.1.0
> > 
> > _______________________________________________
> > dev mailing list
> > dev at openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev


More information about the dev mailing list