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

Yang, Yi Y yi.y.yang at intel.com
Thu Aug 17 22:54:59 UTC 2017


Jan, MD2 is flexible enough, so we mustn't worry there will be one MDx, it is very weird to use OVS_NSH_PUSH_ATTR_CONTEXT as sub key of OVS_KEY_ATTR_NSH. 

For PUSH_NSH, the below format is clear enough, it won't make people confused. Yes, maybe we won't use OVS_NSH_KEY_ATTR_MD2 to transfer MD2 match fields, but this isn't a good reason why we want to use a worse name.

OVS_ACTION_ATTR_PUSH_NSH
-- OVS_NSH_KEY_ATTR_BASE
-- OVS_NSH_KEY_ATTR_MD1

Or 

OVS_ACTION_ATTR_PUSH_NSH
-- OVS_NSH_KEY_ATTR_BASE
-- OVS_NSH_KEY_ATTR_MD2

I have worked out a version with ttl key and aligned to the lasted NSH draft, I have double confirmed from its author, this will be final version, NSH header format won't be changed anymore except some text changes, I'll send out this version today to catch up with OVS guys' review, kernel version will be sent out later today.

If other guys think your change is reasonable enough, I will send out next version with this, but now I don't think we have strong reason to change it.


-----Original Message-----
From: Jan Scheurich [mailto:jan.scheurich at ericsson.com] 
Sent: Thursday, August 17, 2017 8:04 PM
To: Yang, Yi Y <yi.y.yang at intel.com>; dev at openvswitch.org
Subject: RE: [ovs-dev] [PATCH] nsh: rework NSH netlink keys and actions

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. 

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


More information about the dev mailing list