Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/804216/?format=api
{ "id": 804216, "url": "http://patchwork.ozlabs.org/api/patches/804216/?format=api", "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/1503360901-30356-2-git-send-email-yi.y.yang@intel.com/", "project": { "id": 47, "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api", "name": "Open vSwitch", "link_name": "openvswitch", "list_id": "ovs-dev.openvswitch.org", "list_email": "ovs-dev@openvswitch.org", "web_url": "http://openvswitch.org/", "scm_url": "git@github.com:openvswitch/ovs.git", "webscm_url": "https://github.com/openvswitch/ovs", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<1503360901-30356-2-git-send-email-yi.y.yang@intel.com>", "list_archive_url": null, "date": "2017-08-22T00:15:00", "name": "[ovs-dev,v3,1/2] nsh: rework NSH netlink keys and actions", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "7a924e53b558416b98c419d0f892b9c8093cfed3", "submitter": { "id": 68962, "url": "http://patchwork.ozlabs.org/api/people/68962/?format=api", "name": "Yang, Yi", "email": "yi.y.yang@intel.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/1503360901-30356-2-git-send-email-yi.y.yang@intel.com/mbox/", "series": [], "comments": "http://patchwork.ozlabs.org/api/patches/804216/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/804216/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<ovs-dev-bounces@openvswitch.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "dev@openvswitch.org" ], "Delivered-To": [ "patchwork-incoming@bilbo.ozlabs.org", "ovs-dev@mail.linuxfoundation.org" ], "Authentication-Results": "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=openvswitch.org\n\t(client-ip=140.211.169.12; helo=mail.linuxfoundation.org;\n\tenvelope-from=ovs-dev-bounces@openvswitch.org;\n\treceiver=<UNKNOWN>)", "Received": [ "from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xbrlp2GZZz9s8V\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 22 Aug 2017 10:19:34 +1000 (AEST)", "from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id C93D8A7A;\n\tTue, 22 Aug 2017 00:18:56 +0000 (UTC)", "from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id 40F02A91\n\tfor <dev@openvswitch.org>; Tue, 22 Aug 2017 00:18:53 +0000 (UTC)", "from mga02.intel.com (mga02.intel.com [134.134.136.20])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id 08089450\n\tfor <dev@openvswitch.org>; Tue, 22 Aug 2017 00:18:53 +0000 (UTC)", "from orsmga004.jf.intel.com ([10.7.209.38])\n\tby orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t21 Aug 2017 17:18:52 -0700", "from unknown (HELO localhost.localdomain.bj.intel.com)\n\t([10.240.224.185])\n\tby orsmga004.jf.intel.com with ESMTP; 21 Aug 2017 17:18:50 -0700" ], "X-Greylist": "domain auto-whitelisted by SQLgrey-1.7.6", "X-ExtLoop1": "1", "X-IronPort-AV": "E=Sophos;i=\"5.41,410,1498546800\"; d=\"scan'208\";a=\"121308831\"", "From": "Yi Yang <yi.y.yang@intel.com>", "To": "dev@openvswitch.org", "Date": "Tue, 22 Aug 2017 08:15:00 +0800", "Message-Id": "<1503360901-30356-2-git-send-email-yi.y.yang@intel.com>", "X-Mailer": "git-send-email 2.1.0", "In-Reply-To": "<1503360901-30356-1-git-send-email-yi.y.yang@intel.com>", "References": "<1503360901-30356-1-git-send-email-yi.y.yang@intel.com>", "Subject": "[ovs-dev] [PATCH v3 1/2] nsh: rework NSH netlink keys and actions", "X-BeenThere": "ovs-dev@openvswitch.org", "X-Mailman-Version": "2.1.12", "Precedence": "list", "List-Id": "<ovs-dev.openvswitch.org>", "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>", "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>", "List-Post": "<mailto:ovs-dev@openvswitch.org>", "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>", "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Sender": "ovs-dev-bounces@openvswitch.org", "Errors-To": "ovs-dev-bounces@openvswitch.org" }, "content": "Per kernel data path requirements, this patch changes OVS_KEY_ATTR_NSH\nto nested attribute and adds three new NSH sub attribute keys:\n\n OVS_NSH_KEY_ATTR_BASE: for length-fixed NSH base header\n OVS_NSH_KEY_ATTR_MD1: for length-fixed MD type 1 context\n OVS_NSH_KEY_ATTR_MD2: for length-variable MD type 2 metadata\n\nNSH match fields, set and PUSH_NSH action all use the below\nnested attribute format:\n\nOVS_KEY_ATTR_NSH begin\n OVS_NSH_KEY_ATTR_BASE\n OVS_NSH_KEY_ATTR_MD1\nOVS_KEY_ATTR_NSH end\n\nor\n\nOVS_KEY_ATTR_NSH begin\n OVS_NSH_KEY_ATTR_BASE\n OVS_NSH_KEY_ATTR_MD2\nOVS_KEY_ATTR_NSH end\n\nIn addition, NSH encap and decap actions are renamed as push_nsh\nand pop_nsh to meet action naming convention.\n\nSigned-off-by: Yi Yang <yi.y.yang@intel.com>\n---\n datapath/linux/compat/include/linux/openvswitch.h | 57 +-\n include/openvswitch/nsh.h | 32 +-\n include/openvswitch/packets.h | 11 +-\n lib/dpif-netdev.c | 4 +-\n lib/dpif.c | 4 +-\n lib/flow.c | 55 +-\n lib/match.c | 12 +-\n lib/meta-flow.c | 13 +-\n lib/nx-match.c | 4 +-\n lib/odp-execute.c | 76 ++-\n lib/odp-util.c | 745 ++++++++++++++++++----\n lib/odp-util.h | 4 +\n lib/packets.c | 23 +-\n lib/packets.h | 5 +-\n ofproto/ofproto-dpif-ipfix.c | 4 +-\n ofproto/ofproto-dpif-sflow.c | 4 +-\n ofproto/ofproto-dpif-xlate.c | 24 +-\n tests/nsh.at | 28 +-\n 18 files changed, 820 insertions(+), 285 deletions(-)", "diff": "diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h\nindex bc6c94b..d7f9029 100644\n--- a/datapath/linux/compat/include/linux/openvswitch.h\n+++ b/datapath/linux/compat/include/linux/openvswitch.h\n@@ -369,7 +369,7 @@ enum ovs_key_attr {\n #ifndef __KERNEL__\n \t/* Only used within userspace data path. */\n \tOVS_KEY_ATTR_PACKET_TYPE, /* be32 packet type */\n-\tOVS_KEY_ATTR_NSH,\t /* struct ovs_key_nsh */\n+\tOVS_KEY_ATTR_NSH,\t /* Nested set of ovs_nsh_key_* */\n #endif\n \n \t__OVS_KEY_ATTR_MAX\n@@ -492,13 +492,27 @@ struct ovs_key_ct_labels {\n \t};\n };\n \n-struct ovs_key_nsh {\n- __u8 flags;\n- __u8 mdtype;\n- __u8 np;\n- __u8 pad;\n- __be32 path_hdr;\n- __be32 c[4];\n+enum ovs_nsh_key_attr {\n+\tOVS_NSH_KEY_ATTR_BASE, /* struct ovs_nsh_key_base. */\n+\tOVS_NSH_KEY_ATTR_MD1, /* struct ovs_nsh_key_md1. */\n+\tOVS_NSH_KEY_ATTR_MD2, /* variable-length octets. */\n+\t__OVS_NSH_KEY_ATTR_MAX\n+};\n+\n+#define OVS_NSH_KEY_ATTR_MAX (__OVS_NSH_KEY_ATTR_MAX - 1)\n+\n+struct ovs_nsh_key_base {\n+\t__u8 flags;\n+\t__u8 mdtype;\n+\t__u8 np;\n+\t__u8 pad;\n+\t__be32 path_hdr;\n+};\n+\n+#define NSH_MD1_CONTEXT_SIZE 4\n+\n+struct ovs_nsh_key_md1 {\n+\t__be32 context[NSH_MD1_CONTEXT_SIZE];\n };\n \n /* OVS_KEY_ATTR_CT_STATE flags */\n@@ -793,24 +807,7 @@ struct ovs_action_push_eth {\n \tstruct ovs_key_ethernet addresses;\n };\n \n-#define OVS_ENCAP_NSH_MAX_MD_LEN 16\n-/*\n- * struct ovs_action_encap_nsh - %OVS_ACTION_ATTR_ENCAP_NSH\n- * @flags: NSH header flags.\n- * @mdtype: NSH metadata type.\n- * @mdlen: Length of NSH metadata in bytes.\n- * @np: NSH next_protocol: Inner packet type.\n- * @path_hdr: NSH service path id and service index.\n- * @metadata: NSH metadata for MD type 1 or 2\n- */\n-struct ovs_action_encap_nsh {\n- uint8_t flags;\n- uint8_t mdtype;\n- uint8_t mdlen;\n- uint8_t np;\n- __be32 path_hdr;\n- uint8_t metadata[OVS_ENCAP_NSH_MAX_MD_LEN];\n-};\n+#define OVS_PUSH_NSH_MAX_MD_LEN 248\n \n /**\n * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.\n@@ -887,8 +884,8 @@ enum ovs_nat_attr {\n * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the\n * packet.\n * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the packet.\n- * @OVS_ACTION_ATTR_ENCAP_NSH: encap NSH action to push NSH header.\n- * @OVS_ACTION_ATTR_DECAP_NSH: decap NSH action to remove NSH header.\n+ * @OVS_ACTION_ATTR_PUSH_NSH: push NSH header to the packet.\n+ * @OVS_ACTION_ATTR_POP_NSH: pop the outermost NSH header off the packet.\n *\n * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all\n * fields within a header are modifiable, e.g. the IPv4 protocol and fragment\n@@ -930,8 +927,8 @@ enum ovs_action_attr {\n \tOVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */\n \tOVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */\n \tOVS_ACTION_ATTR_METER, /* u32 meter number. */\n-\tOVS_ACTION_ATTR_ENCAP_NSH, /* struct ovs_action_encap_nsh. */\n-\tOVS_ACTION_ATTR_DECAP_NSH, /* No argument. */\n+\tOVS_ACTION_ATTR_PUSH_NSH, /* Nested OVS_NSH_KEY_ATTR_*. */\n+\tOVS_ACTION_ATTR_POP_NSH, /* No argument. */\n #endif\n \t__OVS_ACTION_ATTR_MAX,\t /* Nothing past this will be accepted\n \t\t\t\t * from userspace. */\ndiff --git a/include/openvswitch/nsh.h b/include/openvswitch/nsh.h\nindex f4ccadc..5f2db20 100644\n--- a/include/openvswitch/nsh.h\n+++ b/include/openvswitch/nsh.h\n@@ -51,7 +51,7 @@ extern \"C\" {\n * @nshc<1-4>: NSH Contexts.\n */\n struct nsh_md1_ctx {\n- ovs_16aligned_be32 c[4];\n+ ovs_16aligned_be32 context[4];\n };\n \n struct nsh_md2_tlv {\n@@ -110,10 +110,20 @@ struct nsh_hdr {\n /* NSH MD Type 1 header Length. */\n #define NSH_M_TYPE1_LEN 24\n \n+/* NSH header maximum Length. */\n+#define NSH_HDR_MAX_LEN 256\n+\n+/* NSH context headers maximum Length. */\n+#define NSH_CTX_HDRS_MAX_LEN 248\n+\n+#define NSH_MD1_CTX(nsh_hdr_ptr) (&(nsh_hdr_ptr)->md1)\n+\n+#define NSH_MD2_CTX(nsh_hdr_ptr) (&(nsh_hdr_ptr)->md2)\n+\n static inline uint16_t\n nsh_hdr_len(const struct nsh_hdr *nsh)\n {\n- return 4 * (ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >> NSH_LEN_SHIFT;\n+ return ((ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >> NSH_LEN_SHIFT) << 2;\n }\n \n static inline struct nsh_md1_ctx *\n@@ -128,6 +138,24 @@ nsh_md2_ctx(struct nsh_hdr *nsh)\n return &nsh->md2;\n }\n \n+static inline uint8_t\n+nsh_get_ver(const struct nsh_hdr *nsh)\n+{\n+ return (ntohs(nsh->ver_flags_len) & NSH_VER_MASK) >> NSH_VER_SHIFT;\n+}\n+\n+static inline uint8_t\n+nsh_get_len(const struct nsh_hdr *nsh)\n+{\n+ return (ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >> NSH_LEN_SHIFT;\n+}\n+\n+static inline uint8_t\n+nsh_get_flags(const struct nsh_hdr *nsh)\n+{\n+ return (ntohs(nsh->ver_flags_len) & NSH_FLAGS_MASK) >> NSH_FLAGS_SHIFT;\n+}\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h\nindex be91e02..5ee3099 100644\n--- a/include/openvswitch/packets.h\n+++ b/include/openvswitch/packets.h\n@@ -84,7 +84,16 @@ struct flow_nsh {\n uint8_t np;\n uint8_t si;\n ovs_be32 spi;\n- ovs_be32 c[4];\n+ ovs_be32 context[4];\n+};\n+\n+struct ovs_key_nsh {\n+ uint8_t flags;\n+ uint8_t mdtype;\n+ uint8_t np;\n+ uint8_t pad;\n+ ovs_be32 path_hdr;\n+ ovs_be32 context[4];\n };\n \n /* NSH flags */\ndiff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c\nindex e2cd931..527fa0e 100644\n--- a/lib/dpif-netdev.c\n+++ b/lib/dpif-netdev.c\n@@ -5407,8 +5407,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,\n case OVS_ACTION_ATTR_PUSH_ETH:\n case OVS_ACTION_ATTR_POP_ETH:\n case OVS_ACTION_ATTR_CLONE:\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- case OVS_ACTION_ATTR_DECAP_NSH:\n+ case OVS_ACTION_ATTR_PUSH_NSH:\n+ case OVS_ACTION_ATTR_POP_NSH:\n case __OVS_ACTION_ATTR_MAX:\n OVS_NOT_REACHED();\n }\ndiff --git a/lib/dpif.c b/lib/dpif.c\nindex 79b2e6c..32669ea 100644\n--- a/lib/dpif.c\n+++ b/lib/dpif.c\n@@ -1271,8 +1271,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,\n case OVS_ACTION_ATTR_PUSH_ETH:\n case OVS_ACTION_ATTR_POP_ETH:\n case OVS_ACTION_ATTR_CLONE:\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- case OVS_ACTION_ATTR_DECAP_NSH:\n+ case OVS_ACTION_ATTR_PUSH_NSH:\n+ case OVS_ACTION_ATTR_POP_NSH:\n case OVS_ACTION_ATTR_UNSPEC:\n case __OVS_ACTION_ATTR_MAX:\n OVS_NOT_REACHED();\ndiff --git a/lib/flow.c b/lib/flow.c\nindex b2b10aa..e137880 100644\n--- a/lib/flow.c\n+++ b/lib/flow.c\n@@ -533,50 +533,52 @@ bool\n parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key)\n {\n const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap;\n- uint16_t ver_flags_len;\n uint8_t version, length, flags;\n uint32_t path_hdr;\n \n- /* Check if it is long enough for NSH header, doesn't support\n- * MD type 2 yet\n- */\n- if (OVS_UNLIKELY(*sizep < NSH_M_TYPE1_LEN)) {\n+ if (OVS_UNLIKELY(*sizep < NSH_BASE_HDR_LEN)) {\n return false;\n }\n \n- memset(key, 0, sizeof(struct flow_nsh));\n+ version = nsh_get_ver(nsh);\n+ flags = nsh_get_flags(nsh);\n \n- ver_flags_len = ntohs(nsh->ver_flags_len);\n- version = (ver_flags_len & NSH_VER_MASK) >> NSH_VER_SHIFT;\n- flags = (ver_flags_len & NSH_FLAGS_MASK) >> NSH_FLAGS_SHIFT;\n-\n- /* NSH header length is in 4 byte words. */\n- length = ((ver_flags_len & NSH_LEN_MASK) >> NSH_LEN_SHIFT) << 2;\n+ length = nsh_hdr_len(nsh);\n \n if (version != 0) {\n return false;\n }\n \n- if (length != NSH_M_TYPE1_LEN) {\n- return false;\n- }\n-\n key->flags = flags;\n key->mdtype = nsh->md_type;\n key->np = nsh->next_proto;\n \n+ if (OVS_UNLIKELY(*sizep < length)) {\n+ return false;\n+ }\n+\n path_hdr = ntohl(get_16aligned_be32(&nsh->path_hdr));\n key->si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;\n key->spi = htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT);\n \n switch (key->mdtype) {\n case NSH_M_TYPE1:\n+ if (length != NSH_M_TYPE1_LEN) {\n+ return false;\n+ }\n+\n for (size_t i = 0; i < 4; i++) {\n- key->c[i] = get_16aligned_be32(&nsh->md1.c[i]);\n+ key->context[i] = get_16aligned_be32(&nsh->md1.context[i]);\n }\n break;\n case NSH_M_TYPE2:\n- /* Don't support MD type 2 yet, so return false */\n+ /* Don't support MD type 2 metedata parsing yet */\n+ if (length < NSH_BASE_HDR_LEN) {\n+ return false;\n+ }\n+\n+ memset(key->context, 0, sizeof(key->context));\n+ break;\n default:\n return false;\n }\n@@ -879,16 +881,9 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)\n struct flow_nsh nsh;\n \n if (OVS_LIKELY(parse_nsh(&data, &size, &nsh))) {\n- if (nsh.mdtype == NSH_M_TYPE1) {\n- miniflow_push_words(mf, nsh, &nsh,\n- sizeof(struct flow_nsh) /\n- sizeof(uint64_t));\n- }\n- else if (nsh.mdtype == NSH_M_TYPE2) {\n- /* parse_nsh has stopped it from arriving here for\n- * MD type 2, will add MD type 2 support code here later\n- */\n- }\n+ miniflow_push_words(mf, nsh, &nsh,\n+ sizeof(struct flow_nsh) /\n+ sizeof(uint64_t));\n }\n }\n goto out;\n@@ -1692,7 +1687,7 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc,\n WC_MASK_FIELD(wc, nsh.np);\n WC_MASK_FIELD(wc, nsh.spi);\n WC_MASK_FIELD(wc, nsh.si);\n- WC_MASK_FIELD(wc, nsh.c);\n+ WC_MASK_FIELD(wc, nsh.context);\n } else {\n return; /* Unknown ethertype. */\n }\n@@ -1826,7 +1821,7 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)\n FLOWMAP_SET(map, nsh.np);\n FLOWMAP_SET(map, nsh.spi);\n FLOWMAP_SET(map, nsh.si);\n- FLOWMAP_SET(map, nsh.c);\n+ FLOWMAP_SET(map, nsh.context);\n }\n }\n \ndiff --git a/lib/match.c b/lib/match.c\nindex 36c78eb..8952c99 100644\n--- a/lib/match.c\n+++ b/lib/match.c\n@@ -1266,10 +1266,14 @@ format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m)\n format_be32_masked_hex(s, \"nsh_spi\", f->nsh.spi, m->nsh.spi);\n format_uint8_masked(s, \"nsh_si\", f->nsh.si, m->nsh.si);\n if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) {\n- format_be32_masked_hex(s, \"nsh_c1\", f->nsh.c[0], m->nsh.c[0]);\n- format_be32_masked_hex(s, \"nsh_c2\", f->nsh.c[1], m->nsh.c[1]);\n- format_be32_masked_hex(s, \"nsh_c3\", f->nsh.c[2], m->nsh.c[2]);\n- format_be32_masked_hex(s, \"nsh_c4\", f->nsh.c[3], m->nsh.c[3]);\n+ format_be32_masked_hex(s, \"nsh_c1\", f->nsh.context[0],\n+ m->nsh.context[0]);\n+ format_be32_masked_hex(s, \"nsh_c2\", f->nsh.context[1],\n+ m->nsh.context[1]);\n+ format_be32_masked_hex(s, \"nsh_c3\", f->nsh.context[2],\n+ m->nsh.context[2]);\n+ format_be32_masked_hex(s, \"nsh_c4\", f->nsh.context[3],\n+ m->nsh.context[3]);\n }\n }\n \ndiff --git a/lib/meta-flow.c b/lib/meta-flow.c\nindex 64a8cf1..beeddf1 100644\n--- a/lib/meta-flow.c\n+++ b/lib/meta-flow.c\n@@ -373,7 +373,7 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)\n case MFF_NSH_C2:\n case MFF_NSH_C3:\n case MFF_NSH_C4:\n- return !wc->masks.nsh.c[mf->id - MFF_NSH_C1];\n+ return !wc->masks.nsh.context[mf->id - MFF_NSH_C1];\n \n case MFF_N_IDS:\n default:\n@@ -915,7 +915,7 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,\n case MFF_NSH_C2:\n case MFF_NSH_C3:\n case MFF_NSH_C4:\n- value->be32 = flow->nsh.c[mf->id - MFF_NSH_C1];\n+ value->be32 = flow->nsh.context[mf->id - MFF_NSH_C1];\n break;\n \n case MFF_N_IDS:\n@@ -1230,7 +1230,8 @@ mf_set_value(const struct mf_field *mf,\n case MFF_NSH_C2:\n case MFF_NSH_C3:\n case MFF_NSH_C4:\n- MATCH_SET_FIELD_BE32(match, nsh.c[mf->id - MFF_NSH_C1], value->be32);\n+ MATCH_SET_FIELD_BE32(match, nsh.context[mf->id - MFF_NSH_C1],\n+ value->be32);\n break;\n \n case MFF_N_IDS:\n@@ -1621,7 +1622,7 @@ mf_set_flow_value(const struct mf_field *mf,\n case MFF_NSH_C2:\n case MFF_NSH_C3:\n case MFF_NSH_C4:\n- flow->nsh.c[mf->id - MFF_NSH_C1] = value->be32;\n+ flow->nsh.context[mf->id - MFF_NSH_C1] = value->be32;\n break;\n \n case MFF_N_IDS:\n@@ -2112,7 +2113,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)\n case MFF_NSH_C2:\n case MFF_NSH_C3:\n case MFF_NSH_C4:\n- MATCH_SET_FIELD_MASKED(match, nsh.c[mf->id - MFF_NSH_C1],\n+ MATCH_SET_FIELD_MASKED(match, nsh.context[mf->id - MFF_NSH_C1],\n htonl(0), htonl(0));\n break;\n \n@@ -2372,7 +2373,7 @@ mf_set(const struct mf_field *mf,\n case MFF_NSH_C2:\n case MFF_NSH_C3:\n case MFF_NSH_C4:\n- MATCH_SET_FIELD_MASKED(match, nsh.c[mf->id - MFF_NSH_C1],\n+ MATCH_SET_FIELD_MASKED(match, nsh.context[mf->id - MFF_NSH_C1],\n value->be32, mask->be32);\n break;\n \ndiff --git a/lib/nx-match.c b/lib/nx-match.c\nindex b782e8c..8f2a442 100644\n--- a/lib/nx-match.c\n+++ b/lib/nx-match.c\n@@ -1165,8 +1165,8 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,\n match->wc.masks.nsh.spi);\n nxm_put_8m(&ctx, MFF_NSH_SI, oxm, flow->nsh.si, match->wc.masks.nsh.si);\n for (int i = 0; i < 4; i++) {\n- nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.c[i],\n- match->wc.masks.nsh.c[i]);\n+ nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.context[i],\n+ match->wc.masks.nsh.context[i]);\n }\n \n /* Registers. */\ndiff --git a/lib/odp-execute.c b/lib/odp-execute.c\nindex 5f4d23a..f6ecc86 100644\n--- a/lib/odp-execute.c\n+++ b/lib/odp-execute.c\n@@ -273,19 +273,22 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,\n /* Set the NSH header. Assumes the NSH header is present and matches the\n * MD format of the key. The slow path must take case of that. */\n static void\n-odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,\n- const struct ovs_key_nsh *mask)\n+odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key,\n+ const struct flow_nsh *mask)\n {\n struct nsh_hdr *nsh = dp_packet_l3(packet);\n+ ovs_be32 path_hdr;\n \n if (!mask) {\n nsh->ver_flags_len = htons(key->flags << NSH_FLAGS_SHIFT) |\n (nsh->ver_flags_len & ~htons(NSH_FLAGS_MASK));\n- put_16aligned_be32(&nsh->path_hdr, key->path_hdr);\n+ path_hdr = htonl((ntohl(key->spi) << NSH_SPI_SHIFT) |\n+ key->si);\n+ put_16aligned_be32(&nsh->path_hdr, path_hdr);\n switch (nsh->md_type) {\n case NSH_M_TYPE1:\n for (int i = 0; i < 4; i++) {\n- put_16aligned_be32(&nsh->md1.c[i], key->c[i]);\n+ put_16aligned_be32(&nsh->md1.context[i], key->context[i]);\n }\n break;\n case NSH_M_TYPE2:\n@@ -300,16 +303,24 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,\n nsh->ver_flags_len = htons(flags << NSH_FLAGS_SHIFT) |\n (nsh->ver_flags_len & ~htons(NSH_FLAGS_MASK));\n \n- ovs_be32 path_hdr = get_16aligned_be32(&nsh->path_hdr);\n- path_hdr = key->path_hdr | (path_hdr & ~mask->path_hdr);\n+ path_hdr = get_16aligned_be32(&nsh->path_hdr);\n+ uint32_t spi = (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT;\n+ uint8_t si = (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;\n+ uint32_t spi_mask = ntohl(mask->spi);\n+ if (spi_mask == 0x00ffffff) {\n+ spi_mask = UINT32_MAX;\n+ }\n+ spi = ntohl(key->spi) | (spi & ~spi_mask);\n+ si = key->si | (si & ~mask->si);\n+ path_hdr = htonl((spi << NSH_SPI_SHIFT) | si);\n put_16aligned_be32(&nsh->path_hdr, path_hdr);\n switch (nsh->md_type) {\n case NSH_M_TYPE1:\n for (int i = 0; i < 4; i++) {\n- ovs_be32 p = get_16aligned_be32(&nsh->md1.c[i]);\n- ovs_be32 k = key->c[i];\n- ovs_be32 m = mask->c[i];\n- put_16aligned_be32(&nsh->md1.c[i], k | (p & ~m));\n+ ovs_be32 p = get_16aligned_be32(&nsh->md1.context[i]);\n+ ovs_be32 k = key->context[i];\n+ ovs_be32 m = mask->context[i];\n+ put_16aligned_be32(&nsh->md1.context[i], k | (p & ~m));\n }\n break;\n case NSH_M_TYPE2:\n@@ -345,9 +356,12 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)\n odp_eth_set_addrs(packet, nl_attr_get(a), NULL);\n break;\n \n- case OVS_KEY_ATTR_NSH:\n- odp_set_nsh(packet, nl_attr_get(a), NULL);\n+ case OVS_KEY_ATTR_NSH: {\n+ struct flow_nsh nsh;\n+ odp_nsh_key_from_attr(a, &nsh);\n+ odp_set_nsh(packet, &nsh, NULL);\n break;\n+ }\n \n case OVS_KEY_ATTR_IPV4:\n ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));\n@@ -473,10 +487,25 @@ odp_execute_masked_set_action(struct dp_packet *packet,\n get_mask(a, struct ovs_key_ethernet));\n break;\n \n- case OVS_KEY_ATTR_NSH:\n- odp_set_nsh(packet, nl_attr_get(a),\n- get_mask(a, struct ovs_key_nsh));\n+ case OVS_KEY_ATTR_NSH: {\n+ struct flow_nsh nsh, nsh_mask;\n+ size_t size = nl_attr_get_size(a) / 2;\n+ struct {\n+ struct nlattr nla;\n+ uint8_t data[size];\n+ } attr, mask;\n+\n+ mask.nla.nla_type = attr.nla.nla_type = nl_attr_type(a);\n+ mask.nla.nla_len = attr.nla.nla_len = NLA_HDRLEN + size;\n+ memcpy(attr.data, (char *)(a + 1), size);\n+ memcpy(mask.data, (char *)(a + 1) + size, size);\n+\n+ odp_nsh_key_from_attr(&attr.nla, &nsh);\n+ odp_nsh_key_from_attr(&mask.nla, &nsh_mask);\n+ odp_set_nsh(packet, &nsh, &nsh_mask);\n+\n break;\n+ }\n \n case OVS_KEY_ATTR_IPV4:\n odp_set_ipv4(packet, nl_attr_get(a),\n@@ -652,8 +681,8 @@ requires_datapath_assistance(const struct nlattr *a)\n case OVS_ACTION_ATTR_PUSH_ETH:\n case OVS_ACTION_ATTR_POP_ETH:\n case OVS_ACTION_ATTR_CLONE:\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- case OVS_ACTION_ATTR_DECAP_NSH:\n+ case OVS_ACTION_ATTR_PUSH_NSH:\n+ case OVS_ACTION_ATTR_POP_NSH:\n return false;\n \n case OVS_ACTION_ATTR_UNSPEC:\n@@ -818,18 +847,21 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,\n }\n break;\n \n- case OVS_ACTION_ATTR_ENCAP_NSH: {\n- const struct ovs_action_encap_nsh *enc_nsh = nl_attr_get(a);\n+ case OVS_ACTION_ATTR_PUSH_NSH: {\n+ uint8_t buffer[NSH_HDR_MAX_LEN];\n+ struct nsh_hdr *nsh_hdr = ALIGNED_CAST(struct nsh_hdr *, buffer);\n+ const struct nsh_hdr *nsh_hdr_src = nsh_hdr;\n+ odp_nsh_hdr_from_attr(nl_attr_get(a), nsh_hdr, NSH_HDR_MAX_LEN);\n DP_PACKET_BATCH_FOR_EACH (packet, batch) {\n- encap_nsh(packet, enc_nsh);\n+ push_nsh(packet, nsh_hdr_src);\n }\n break;\n }\n- case OVS_ACTION_ATTR_DECAP_NSH: {\n+ case OVS_ACTION_ATTR_POP_NSH: {\n size_t i, num = batch->count;\n \n DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {\n- if (decap_nsh(packet)) {\n+ if (pop_nsh(packet)) {\n dp_packet_batch_refill(batch, packet, i);\n } else {\n dp_packet_delete(packet);\ndiff --git a/lib/odp-util.c b/lib/odp-util.c\nindex 4f1499e..800ef34 100644\n--- a/lib/odp-util.c\n+++ b/lib/odp-util.c\n@@ -129,8 +129,8 @@ odp_action_len(uint16_t type)\n case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth);\n case OVS_ACTION_ATTR_POP_ETH: return 0;\n case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;\n- case OVS_ACTION_ATTR_ENCAP_NSH: return ATTR_LEN_VARIABLE;\n- case OVS_ACTION_ATTR_DECAP_NSH: return 0;\n+ case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE;\n+ case OVS_ACTION_ATTR_POP_NSH: return 0;\n \n case OVS_ACTION_ATTR_UNSPEC:\n case __OVS_ACTION_ATTR_MAX:\n@@ -264,7 +264,7 @@ format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key)\n switch (key->mdtype) {\n case NSH_M_TYPE1:\n for (int i = 0; i < 4; i++) {\n- ds_put_format(ds, \",c%d=0x%x\", i + 1, ntohl(key->c[i]));\n+ ds_put_format(ds, \",c%d=0x%x\", i + 1, ntohl(key->context[i]));\n }\n break;\n case NSH_M_TYPE2:\n@@ -334,41 +334,50 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,\n format_uint8_masked(ds, &first, \"np\", key->np, mask->np);\n format_be32_masked(ds, &first, \"spi\", htonl(spi), htonl(spi_mask));\n format_uint8_masked(ds, &first, \"si\", si, si_mask);\n- format_be32_masked(ds, &first, \"c1\", key->c[0], mask->c[0]);\n- format_be32_masked(ds, &first, \"c2\", key->c[1], mask->c[1]);\n- format_be32_masked(ds, &first, \"c3\", key->c[2], mask->c[2]);\n- format_be32_masked(ds, &first, \"c4\", key->c[3], mask->c[3]);\n+ format_be32_masked(ds, &first, \"c1\", key->context[0],\n+ mask->context[0]);\n+ format_be32_masked(ds, &first, \"c2\", key->context[1],\n+ mask->context[1]);\n+ format_be32_masked(ds, &first, \"c3\", key->context[2],\n+ mask->context[2]);\n+ format_be32_masked(ds, &first, \"c4\", key->context[3],\n+ mask->context[3]);\n }\n }\n \n static void\n-format_odp_encap_nsh_action(struct ds *ds,\n- const struct ovs_action_encap_nsh *encap_nsh)\n+format_odp_push_nsh_action(struct ds *ds,\n+ const struct nsh_hdr *nsh_hdr)\n {\n- uint32_t path_hdr = ntohl(encap_nsh->path_hdr);\n+ size_t mdlen = (((ntohs(nsh_hdr->ver_flags_len) & NSH_LEN_MASK)\n+ >> NSH_LEN_SHIFT) << 2) - NSH_BASE_HDR_LEN;\n+ uint32_t path_hdr = ntohl(get_16aligned_be32(&nsh_hdr->path_hdr));\n uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT;\n uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;\n+ uint8_t flags = (ntohs(nsh_hdr->ver_flags_len) & NSH_FLAGS_MASK)\n+ >> NSH_FLAGS_SHIFT;\n \n- ds_put_cstr(ds, \"encap_nsh(\");\n- ds_put_format(ds, \"flags=%d\", encap_nsh->flags);\n- ds_put_format(ds, \",mdtype=%d\", encap_nsh->mdtype);\n- ds_put_format(ds, \",np=%d\", encap_nsh->np);\n+ ds_put_cstr(ds, \"push_nsh(\");\n+ ds_put_format(ds, \"flags=%d\", flags);\n+ ds_put_format(ds, \",mdtype=%d\", nsh_hdr->md_type);\n+ ds_put_format(ds, \",np=%d\", nsh_hdr->next_proto);\n ds_put_format(ds, \",spi=0x%x\", spi);\n ds_put_format(ds, \",si=%d\", si);\n- switch (encap_nsh->mdtype) {\n+ switch (nsh_hdr->md_type) {\n case NSH_M_TYPE1: {\n- struct nsh_md1_ctx *md1_ctx =\n- ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh->metadata);\n+ const struct nsh_md1_ctx *md1_ctx = NSH_MD1_CTX(nsh_hdr);\n for (int i = 0; i < 4; i++) {\n ds_put_format(ds, \",c%d=0x%x\", i + 1,\n- ntohl(get_16aligned_be32(&md1_ctx->c[i])));\n+ ntohl(get_16aligned_be32(&md1_ctx->context[i])));\n }\n break;\n }\n- case NSH_M_TYPE2:\n+ case NSH_M_TYPE2: {\n+ const struct nsh_md2_tlv *md2_ctx = NSH_MD2_CTX(nsh_hdr);\n ds_put_cstr(ds, \",md2=\");\n- ds_put_hex(ds, encap_nsh->metadata, encap_nsh->mdlen);\n+ ds_put_hex(ds, md2_ctx, mdlen);\n break;\n+ }\n default:\n OVS_NOT_REACHED();\n }\n@@ -1057,11 +1066,16 @@ format_odp_action(struct ds *ds, const struct nlattr *a,\n case OVS_ACTION_ATTR_CLONE:\n format_odp_clone_action(ds, a, portno_names);\n break;\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- format_odp_encap_nsh_action(ds, nl_attr_get(a));\n+ case OVS_ACTION_ATTR_PUSH_NSH: {\n+ uint8_t buffer[NSH_HDR_MAX_LEN];\n+ struct nsh_hdr *nsh_hdr = ALIGNED_CAST(struct nsh_hdr *, buffer);\n+ const struct nsh_hdr *nsh_hdr_src = nsh_hdr;\n+ odp_nsh_hdr_from_attr(nl_attr_get(a), nsh_hdr, NSH_HDR_MAX_LEN);\n+ format_odp_push_nsh_action(ds, nsh_hdr_src);\n break;\n- case OVS_ACTION_ATTR_DECAP_NSH:\n- ds_put_cstr(ds, \"decap_nsh()\");\n+ }\n+ case OVS_ACTION_ATTR_POP_NSH:\n+ ds_put_cstr(ds, \"pop_nsh()\");\n break;\n case OVS_ACTION_ATTR_UNSPEC:\n case __OVS_ACTION_ATTR_MAX:\n@@ -1780,27 +1794,74 @@ find_end:\n return s - s_;\n }\n \n+static void\n+nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh,\n+ uint8_t * metadata, size_t md_size,\n+ bool is_mask)\n+{\n+ size_t nsh_key_ofs;\n+ struct ovs_nsh_key_base base;\n+ struct ovs_nsh_key_md1 md1;\n+\n+ base.flags = nsh->flags;\n+ base.mdtype = nsh->mdtype;\n+ base.np = nsh->np;\n+ base.path_hdr = htonl((ntohl(nsh->spi) << NSH_SPI_SHIFT) |\n+ nsh->si);\n+\n+ nsh_key_ofs = nl_msg_start_nested(buf, OVS_KEY_ATTR_NSH);\n+ nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_BASE, &base, sizeof base);\n+\n+ if (is_mask) {\n+ for (int i = 0; i < 4; i++) {\n+ md1.context[i] = nsh->context[i];\n+ }\n+ nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_MD1, &md1, sizeof md1);\n+ } else {\n+ switch (nsh->mdtype) {\n+ case NSH_M_TYPE1:\n+ for (int i = 0; i < 4; i++) {\n+ md1.context[i] = nsh->context[i];\n+ }\n+ nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_MD1, &md1, sizeof md1);\n+ break;\n+ case NSH_M_TYPE2:\n+ if (metadata && md_size > 0) {\n+ nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_MD2, metadata,\n+ md_size);\n+ }\n+ break;\n+ default:\n+ /* No match support for other MD formats yet. */\n+ break;\n+ }\n+ }\n+ nl_msg_end_nested(buf, nsh_key_ofs);\n+}\n+\n+\n static int\n-parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions)\n+parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)\n {\n int n = 0;\n int ret = 0;\n- struct ovs_action_encap_nsh encap_nsh;\n- uint32_t spi;\n- uint8_t si;\n uint32_t cd;\n+ struct flow_nsh nsh;\n+ uint8_t *metadata = NULL;\n+ uint8_t md_size = 0;\n \n- if (!ovs_scan_len(s, &n, \"encap_nsh(\")) {\n+ if (!ovs_scan_len(s, &n, \"push_nsh(\")) {\n ret = -EINVAL;\n goto out;\n }\n \n /* The default is NSH_M_TYPE1 */\n- encap_nsh.flags = 0;\n- encap_nsh.mdtype = NSH_M_TYPE1;\n- encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;\n- encap_nsh.path_hdr = htonl(255);\n- memset(encap_nsh.metadata, 0, NSH_M_TYPE1_MDLEN);\n+ nsh.flags = 0;\n+ nsh.mdtype = NSH_M_TYPE1;\n+ nsh.np = NSH_P_ETHERNET;\n+ nsh.spi = 0;\n+ nsh.si = 255;\n+ memset(nsh.context, 0, NSH_M_TYPE1_MDLEN);\n \n for (;;) {\n n += strspn(s + n, delimiters);\n@@ -1808,17 +1869,17 @@ parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions)\n break;\n }\n \n- if (ovs_scan_len(s, &n, \"flags=%\"SCNi8, &encap_nsh.flags)) {\n+ if (ovs_scan_len(s, &n, \"flags=%\"SCNi8, &nsh.flags)) {\n continue;\n }\n- if (ovs_scan_len(s, &n, \"mdtype=%\"SCNi8, &encap_nsh.mdtype)) {\n- switch (encap_nsh.mdtype) {\n+ if (ovs_scan_len(s, &n, \"mdtype=%\"SCNi8, &nsh.mdtype)) {\n+ switch (nsh.mdtype) {\n case NSH_M_TYPE1:\n /* This is the default format. */;\n break;\n case NSH_M_TYPE2:\n /* Length will be updated later. */\n- encap_nsh.mdlen = 0;\n+ md_size = 0;\n break;\n default:\n ret = -EINVAL;\n@@ -1826,65 +1887,60 @@ parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions)\n }\n continue;\n }\n- if (ovs_scan_len(s, &n, \"np=%\"SCNi8, &encap_nsh.np)) {\n+ if (ovs_scan_len(s, &n, \"np=%\"SCNi8, &nsh.np)) {\n continue;\n }\n- if (ovs_scan_len(s, &n, \"spi=0x%\"SCNx32, &spi)) {\n- encap_nsh.path_hdr =\n- htonl(((spi << NSH_SPI_SHIFT) & NSH_SPI_MASK) |\n- (ntohl(encap_nsh.path_hdr) & ~NSH_SPI_MASK));\n+ if (ovs_scan_len(s, &n, \"spi=0x%\"SCNx32, &nsh.spi)) {\n+ nsh.spi = htonl(nsh.spi);\n continue;\n }\n- if (ovs_scan_len(s, &n, \"si=%\"SCNi8, &si)) {\n- encap_nsh.path_hdr =\n- htonl((si << NSH_SI_SHIFT) |\n- (ntohl(encap_nsh.path_hdr) & ~NSH_SI_MASK));\n+ if (ovs_scan_len(s, &n, \"si=%\"SCNi8, &nsh.si)) {\n continue;\n }\n- if (encap_nsh.mdtype == NSH_M_TYPE1) {\n- struct nsh_md1_ctx *md1 =\n- ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh.metadata);\n+ if (nsh.mdtype == NSH_M_TYPE1) {\n if (ovs_scan_len(s, &n, \"c1=0x%\"SCNx32, &cd)) {\n- put_16aligned_be32(&md1->c[0], htonl(cd));\n+ nsh.context[0] = htonl(cd);\n continue;\n }\n if (ovs_scan_len(s, &n, \"c2=0x%\"SCNx32, &cd)) {\n- put_16aligned_be32(&md1->c[1], htonl(cd));\n+ nsh.context[1] = htonl(cd);\n continue;\n }\n if (ovs_scan_len(s, &n, \"c3=0x%\"SCNx32, &cd)) {\n- put_16aligned_be32(&md1->c[2], htonl(cd));\n+ nsh.context[2] = htonl(cd);\n continue;\n }\n if (ovs_scan_len(s, &n, \"c4=0x%\"SCNx32, &cd)) {\n- put_16aligned_be32(&md1->c[3], htonl(cd));\n+ nsh.context[3] = htonl(cd);\n continue;\n }\n }\n- else if (encap_nsh.mdtype == NSH_M_TYPE2) {\n+ else if (nsh.mdtype == NSH_M_TYPE2) {\n struct ofpbuf b;\n char buf[512];\n size_t mdlen;\n if (ovs_scan_len(s, &n, \"md2=0x%511[0-9a-fA-F]\", buf)) {\n- ofpbuf_use_stub(&b, encap_nsh.metadata,\n- OVS_ENCAP_NSH_MAX_MD_LEN);\n+ metadata = xmalloc(NSH_CTX_HDRS_MAX_LEN);\n+ ofpbuf_use_stub(&b, metadata,\n+ NSH_CTX_HDRS_MAX_LEN);\n ofpbuf_put_hex(&b, buf, &mdlen);\n- encap_nsh.mdlen = mdlen;\n+ md_size = mdlen;\n ofpbuf_uninit(&b);\n }\n continue;\n }\n }\n out:\n- if (ret < 0) {\n- return ret;\n- } else {\n- size_t size = offsetof(struct ovs_action_encap_nsh, metadata)\n- + ROUND_UP(encap_nsh.mdlen, 4);\n- nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ENCAP_NSH,\n- &encap_nsh, size);\n- return n;\n+ if (ret >= 0) {\n+ size_t offset = nl_msg_start_nested(actions, OVS_ACTION_ATTR_PUSH_NSH);\n+ nsh_key_to_attr(actions, &nsh, metadata, md_size, false);\n+ nl_msg_end_nested(actions, offset);\n+ ret = n;\n+ }\n+ if (metadata != NULL) {\n+ free(metadata);\n }\n+ return ret;\n }\n \n static int\n@@ -2089,8 +2145,8 @@ parse_odp_action(const char *s, const struct simap *port_names,\n }\n \n {\n- if (!strncmp(s, \"encap_nsh(\", 10)) {\n- int retval = parse_odp_encap_nsh_action(s, actions);\n+ if (!strncmp(s, \"push_nsh(\", 9)) {\n+ int retval = parse_odp_push_nsh_action(s, actions);\n if (retval < 0) {\n return retval;\n }\n@@ -2100,8 +2156,8 @@ parse_odp_action(const char *s, const struct simap *port_names,\n \n {\n int n;\n- if (ovs_scan(s, \"decap_nsh()%n\", &n)) {\n- nl_msg_put_flag(actions, OVS_ACTION_ATTR_DECAP_NSH);\n+ if (ovs_scan(s, \"pop_nsh()%n\", &n)) {\n+ nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_NSH);\n return n;\n }\n }\n@@ -2198,6 +2254,13 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +\n [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 },\n };\n \n+static const struct attr_len_tbl\n+ovs_nsh_key_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {\n+ [OVS_NSH_KEY_ATTR_BASE] = { .len = 8 },\n+ [OVS_NSH_KEY_ATTR_MD1] = { .len = 16 },\n+ [OVS_NSH_KEY_ATTR_MD2] = { .len = ATTR_LEN_VARIABLE },\n+};\n+\n static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {\n [OVS_KEY_ATTR_ENCAP] = { .len = ATTR_LEN_NESTED },\n [OVS_KEY_ATTR_PRIORITY] = { .len = 4 },\n@@ -2229,7 +2292,9 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =\n [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = { .len = sizeof(struct ovs_key_ct_tuple_ipv4) },\n [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = { .len = sizeof(struct ovs_key_ct_tuple_ipv6) },\n [OVS_KEY_ATTR_PACKET_TYPE] = { .len = 4 },\n- [OVS_KEY_ATTR_NSH] = { .len = sizeof(struct ovs_key_nsh) },\n+ [OVS_KEY_ATTR_NSH] = { .len = ATTR_LEN_NESTED,\n+ .next = ovs_nsh_key_attr_lens,\n+ .next_max = OVS_NSH_KEY_ATTR_MAX },\n };\n \n /* Returns the correct length of the payload for a flow key attribute of the\n@@ -2280,6 +2345,142 @@ ovs_frag_type_to_string(enum ovs_frag_type type)\n }\n }\n \n+enum odp_key_fitness\n+odp_nsh_hdr_from_attr(const struct nlattr *attr,\n+ struct nsh_hdr *nsh_hdr, size_t size)\n+{\n+ unsigned int left;\n+ const struct nlattr *a;\n+ bool unknown = false;\n+ uint8_t flags = 0;\n+ size_t mdlen = 0;\n+ bool has_md1 = false;\n+ bool has_md2 = false;\n+\n+ NL_NESTED_FOR_EACH (a, left, attr) {\n+ uint16_t type = nl_attr_type(a);\n+ size_t len = nl_attr_get_size(a);\n+ int expected_len = odp_key_attr_len(ovs_nsh_key_attr_lens,\n+ OVS_NSH_KEY_ATTR_MAX, type);\n+\n+ if (len != expected_len && expected_len >= 0) {\n+ return ODP_FIT_ERROR;\n+ }\n+\n+ switch (type) {\n+ case OVS_NSH_KEY_ATTR_BASE: {\n+ const struct ovs_nsh_key_base *base = nl_attr_get(a);\n+ nsh_hdr->next_proto = base->np;\n+ nsh_hdr->md_type = base->mdtype;\n+ put_16aligned_be32(&nsh_hdr->path_hdr, base->path_hdr);\n+ flags = base->flags;\n+ break;\n+ }\n+ case OVS_NSH_KEY_ATTR_MD1: {\n+ const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);\n+ struct nsh_md1_ctx *md1_dst = nsh_md1_ctx(nsh_hdr);\n+ has_md1 = true;\n+ mdlen = nl_attr_get_size(a);\n+ if ((mdlen + NSH_BASE_HDR_LEN != NSH_M_TYPE1_LEN) ||\n+ (mdlen + NSH_BASE_HDR_LEN > size)) {\n+ return ODP_FIT_ERROR;\n+ }\n+ memcpy(md1_dst, md1, mdlen);\n+ break;\n+ }\n+ case OVS_NSH_KEY_ATTR_MD2: {\n+ struct nsh_md2_tlv *md2_dst = nsh_md2_ctx(nsh_hdr);\n+ const uint8_t *md2 = nl_attr_get(a);\n+ has_md2 = true;\n+ mdlen = nl_attr_get_size(a);\n+ if (mdlen + NSH_BASE_HDR_LEN > size) {\n+ return ODP_FIT_ERROR;\n+ }\n+ memcpy(md2_dst, md2, mdlen);\n+ break;\n+ }\n+ default:\n+ /* Allow this to show up as unexpected, if there are unknown\n+ * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */\n+ unknown = true;\n+ break;\n+ }\n+ }\n+\n+ if (unknown) {\n+ return ODP_FIT_TOO_MUCH;\n+ }\n+\n+ if ((has_md1 && nsh_hdr->md_type != NSH_M_TYPE1)\n+ || (has_md2 && nsh_hdr->md_type != NSH_M_TYPE2)) {\n+ return ODP_FIT_ERROR;\n+ }\n+\n+ /* nsh header length = NSH_BASE_HDR_LEN + mdlen */\n+ nsh_hdr->ver_flags_len = htons(flags << NSH_FLAGS_SHIFT |\n+ (NSH_BASE_HDR_LEN + mdlen) >> 2);\n+\n+ return ODP_FIT_PERFECT;\n+}\n+\n+enum odp_key_fitness\n+odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh)\n+{\n+ unsigned int left;\n+ const struct nlattr *a;\n+ bool unknown = false;\n+ bool has_md1 = false;\n+\n+ NL_NESTED_FOR_EACH (a, left, attr) {\n+ uint16_t type = nl_attr_type(a);\n+ size_t len = nl_attr_get_size(a);\n+ int expected_len = odp_key_attr_len(ovs_nsh_key_attr_lens,\n+ OVS_NSH_KEY_ATTR_MAX, type);\n+\n+ if (len != expected_len && expected_len >= 0) {\n+ return ODP_FIT_ERROR;\n+ }\n+\n+ switch (type) {\n+ case OVS_NSH_KEY_ATTR_BASE: {\n+ const struct ovs_nsh_key_base *base = nl_attr_get(a);\n+ nsh->flags = base->flags;\n+ nsh->mdtype = base->mdtype;\n+ nsh->np = base->np;\n+ nsh->spi = htonl((ntohl(base->path_hdr) & NSH_SPI_MASK) >>\n+ NSH_SPI_SHIFT);\n+ nsh->si = (ntohl(base->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;\n+ break;\n+ }\n+ case OVS_NSH_KEY_ATTR_MD1: {\n+ const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);\n+ has_md1 = true;\n+ nsh->context[0] = md1->context[0];\n+ nsh->context[1] = md1->context[1];\n+ nsh->context[2] = md1->context[2];\n+ nsh->context[3] = md1->context[3];\n+ break;\n+ }\n+ case OVS_NSH_KEY_ATTR_MD2:\n+ default:\n+ /* Allow this to show up as unexpected, if there are unknown\n+ * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */\n+ unknown = true;\n+ break;\n+ }\n+ }\n+\n+ if (unknown) {\n+ return ODP_FIT_TOO_MUCH;\n+ }\n+\n+ if (has_md1 && nsh->mdtype != NSH_M_TYPE1) {\n+ return ODP_FIT_ERROR;\n+ }\n+\n+ return ODP_FIT_PERFECT;\n+}\n+\n static enum odp_key_fitness\n odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask,\n struct flow_tnl *tun)\n@@ -2971,6 +3172,80 @@ format_odp_tun_geneve(const struct nlattr *attr,\n }\n \n static void\n+format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr,\n+ struct ds *ds)\n+{\n+ unsigned int left;\n+ const struct nlattr *a;\n+ struct ovs_key_nsh nsh;\n+ struct ovs_key_nsh nsh_mask;\n+\n+ memset(&nsh, 0, sizeof nsh);\n+ memset(&nsh_mask, 0xff, sizeof nsh_mask);\n+\n+ NL_NESTED_FOR_EACH (a, left, attr) {\n+ enum ovs_nsh_key_attr type = nl_attr_type(a);\n+ const struct nlattr *ma = NULL;\n+\n+ if (mask_attr) {\n+ ma = nl_attr_find__(nl_attr_get(mask_attr),\n+ nl_attr_get_size(mask_attr), type);\n+ }\n+\n+ if (!check_attr_len(ds, a, ma, ovs_nsh_key_attr_lens,\n+ OVS_NSH_KEY_ATTR_MAX, true)) {\n+ continue;\n+ }\n+\n+ switch (type) {\n+ case OVS_NSH_KEY_ATTR_BASE: {\n+ const struct ovs_nsh_key_base * base = nl_attr_get(a);\n+ const struct ovs_nsh_key_base * base_mask\n+ = ma ? nl_attr_get(ma) : NULL;\n+ nsh.flags = base->flags;\n+ nsh.mdtype = base->mdtype;\n+ nsh.np = base->np;\n+ nsh.path_hdr = base->path_hdr;\n+ if (base_mask) {\n+ nsh_mask.flags = base_mask->flags;\n+ nsh_mask.mdtype = base_mask->mdtype;\n+ nsh_mask.np = base_mask->np;\n+ nsh_mask.path_hdr = base_mask->path_hdr;\n+ }\n+ break;\n+ }\n+ case OVS_NSH_KEY_ATTR_MD1: {\n+ const struct ovs_nsh_key_md1 * md1 = nl_attr_get(a);\n+ const struct ovs_nsh_key_md1 * md1_mask\n+ = ma ? nl_attr_get(ma) : NULL;\n+ nsh.context[0] = md1->context[0];\n+ nsh.context[1] = md1->context[1];\n+ nsh.context[2] = md1->context[2];\n+ nsh.context[3] = md1->context[3];\n+ if (md1_mask) {\n+ nsh_mask.context[0] = md1_mask->context[0];\n+ nsh_mask.context[1] = md1_mask->context[1];\n+ nsh_mask.context[2] = md1_mask->context[2];\n+ nsh_mask.context[3] = md1_mask->context[3];\n+ }\n+ break;\n+ }\n+ case OVS_NSH_KEY_ATTR_MD2:\n+ case __OVS_NSH_KEY_ATTR_MAX:\n+ default:\n+ /* No support for matching other metadata formats yet. */\n+ break;\n+ }\n+ }\n+\n+ if (mask_attr) {\n+ format_nsh_key_mask(ds, &nsh, &nsh_mask);\n+ } else {\n+ format_nsh_key(ds, &nsh);\n+ }\n+}\n+\n+static void\n format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,\n struct ds *ds, bool verbose)\n {\n@@ -3448,9 +3723,7 @@ format_odp_key_attr__(const struct nlattr *a, const struct nlattr *ma,\n break;\n }\n case OVS_KEY_ATTR_NSH: {\n- const struct ovs_key_nsh *mask = ma ? nl_attr_get(ma) : NULL;\n- const struct ovs_key_nsh *key = nl_attr_get(a);\n- format_nsh_key_mask(ds, key, mask);\n+ format_odp_nsh_attr(a, ma, ds);\n break;\n }\n case OVS_KEY_ATTR_UNSPEC:\n@@ -4549,6 +4822,129 @@ geneve_to_attr(struct ofpbuf *a, const void *data_)\n } SCAN_END_SINGLE(ATTR)\n \n static int\n+parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,\n+ struct ofpbuf *mask)\n+{\n+ if (strncmp(s, \"nsh(\", 4) == 0) {\n+ const char *start = s;\n+ int len;\n+ struct flow_nsh skey, smask;\n+\n+ s += 4;\n+\n+ memset(&skey, 0, sizeof skey);\n+ memset(&smask, 0, sizeof smask);\n+ do {\n+ len = 0;\n+\n+ if (strncmp(s, \"flags=\", 6) == 0) {\n+ s += 6;\n+ len = scan_u8(s, &skey.flags, mask ? &smask.flags : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"mdtype=\", 7) == 0) {\n+ s += 7;\n+ len = scan_u8(s, &skey.mdtype, mask ? &smask.mdtype : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"np=\", 3) == 0) {\n+ s += 3;\n+ len = scan_u8(s, &skey.np, mask ? &smask.np : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"spi=\", 4) == 0) {\n+ s += 4;\n+ len = scan_be32(s, &skey.spi, mask ? &smask.spi : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"si=\", 3) == 0) {\n+ s += 3;\n+ len = scan_u8(s, &skey.si, mask ? &smask.si : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"c1=\", 3) == 0) {\n+ s += 3;\n+ len = scan_be32(s, &skey.context[0],\n+ mask ? &smask.context[0] : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"c2=\", 3) == 0) {\n+ s += 3;\n+ len = scan_be32(s, &skey.context[1],\n+ mask ? &smask.context[1] : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"c3=\", 3) == 0) {\n+ s += 3;\n+ len = scan_be32(s, &skey.context[2],\n+ mask ? &smask.context[2] : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+\n+ if (strncmp(s, \"c4=\", 3) == 0) {\n+ s += 3;\n+ len = scan_be32(s, &skey.context[3],\n+ mask ? &smask.context[3] : NULL);\n+ if (len == 0) {\n+ return -EINVAL;\n+ }\n+ s += len;\n+ continue;\n+ }\n+ } while (*s++ == ',' && len != 0);\n+ if (s[-1] != ')') {\n+ return -EINVAL;\n+ }\n+\n+ nsh_key_to_attr(key, &skey, NULL, 0, false);\n+ if (mask) {\n+ nsh_key_to_attr(mask, &smask, NULL, 0, true);\n+ }\n+ return s - start;\n+ }\n+ return 0;\n+}\n+\n+static int\n parse_odp_key_mask_attr(const char *s, const struct simap *port_names,\n struct ofpbuf *key, struct ofpbuf *mask)\n {\n@@ -4694,16 +5090,13 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,\n SCAN_FIELD(\"id=\", be16, id);\n } SCAN_END(OVS_KEY_ATTR_PACKET_TYPE);\n \n- SCAN_BEGIN(\"nsh(\", struct ovs_key_nsh) {\n- SCAN_FIELD(\"flags=\", u8, flags);\n- SCAN_FIELD(\"mdtype=\", u8, mdtype);\n- SCAN_FIELD(\"np=\", u8, np);\n- SCAN_FIELD(\"path_hdr=\", be32, path_hdr);\n- SCAN_FIELD(\"c1=\", be32, c[0]);\n- SCAN_FIELD(\"c2=\", be32, c[1]);\n- SCAN_FIELD(\"c3=\", be32, c[2]);\n- SCAN_FIELD(\"c4=\", be32, c[2]);\n- } SCAN_END(OVS_KEY_ATTR_NSH);\n+ /* nsh is nested, it needs special process */\n+ int ret = parse_odp_nsh_key_mask_attr(s, key, mask);\n+ if (ret < 0) {\n+ return ret;\n+ } else {\n+ s += ret;\n+ }\n \n /* Encap open-coded. */\n if (!strncmp(s, \"encap(\", 6)) {\n@@ -4994,11 +5387,7 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,\n mpls_key[i].mpls_lse = data->mpls_lse[i];\n }\n } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {\n- struct ovs_key_nsh *nsh_key;\n-\n- nsh_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_NSH,\n- sizeof *nsh_key);\n- get_nsh_key(data, nsh_key, export_mask);\n+ nsh_key_to_attr(buf, &data->nsh, NULL, 0, export_mask);\n }\n \n if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {\n@@ -5558,13 +5947,10 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],\n expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NSH;\n }\n if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NSH)) {\n- const struct ovs_key_nsh *nsh_key;\n-\n- nsh_key = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);\n- put_nsh_key(nsh_key, flow, false);\n+ odp_nsh_key_from_attr(attrs[OVS_KEY_ATTR_NSH], &flow->nsh);\n if (is_mask) {\n- check_start = nsh_key;\n- check_len = sizeof *nsh_key;\n+ check_start = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);\n+ check_len = nl_attr_get_size(attrs[OVS_KEY_ATTR_NSH]);\n expected_bit = OVS_KEY_ATTR_NSH;\n }\n }\n@@ -6620,13 +7006,13 @@ get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask)\n flow->nsh.si);\n if (is_mask) {\n for (int i = 0; i < 4; i++) {\n- nsh->c[i] = flow->nsh.c[i];\n+ nsh->context[i] = flow->nsh.context[i];\n }\n } else {\n switch (nsh->mdtype) {\n case NSH_M_TYPE1:\n for (int i = 0; i < 4; i++) {\n- nsh->c[i] = flow->nsh.c[i];\n+ nsh->context[i] = flow->nsh.context[i];\n }\n break;\n case NSH_M_TYPE2:\n@@ -6650,15 +7036,123 @@ put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,\n switch (nsh->mdtype) {\n case NSH_M_TYPE1:\n for (int i = 0; i < 4; i++) {\n- flow->nsh.c[i] = nsh->c[i];\n+ flow->nsh.context[i] = nsh->context[i];\n+ }\n+ break;\n+ case NSH_M_TYPE2:\n+ default:\n+ /* No match support for other MD formats yet. */\n+ memset(flow->nsh.context, 0, sizeof flow->nsh.context);\n+ break;\n+ }\n+}\n+\n+static bool\n+commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set,\n+ const struct ovs_key_nsh *key, struct ovs_key_nsh *base,\n+ struct ovs_key_nsh *mask, size_t size,\n+ struct ofpbuf *odp_actions)\n+{\n+ enum ovs_key_attr attr = OVS_KEY_ATTR_NSH;\n+\n+ if (memcmp(key, base, size) == 0) {\n+ /* Mask bits are set when we have either read or set the corresponding\n+ * values. Masked bits will be exact-matched, no need to set them\n+ * if the value did not actually change. */\n+ return false;\n+ }\n+\n+ bool fully_masked = odp_mask_is_exact(attr, mask, size);\n+\n+ if (use_masked_set && !fully_masked) {\n+ size_t nsh_key_ofs;\n+ struct ovs_nsh_key_base nsh_base;\n+ struct ovs_nsh_key_base nsh_base_mask;\n+ struct ovs_nsh_key_md1 md1;\n+ struct ovs_nsh_key_md1 md1_mask;\n+ size_t offset = nl_msg_start_nested(odp_actions,\n+ OVS_ACTION_ATTR_SET_MASKED);\n+\n+ nsh_base.flags = key->flags;\n+ nsh_base.mdtype = key->mdtype;\n+ nsh_base.np = key->np;\n+ nsh_base.path_hdr = key->path_hdr;\n+\n+ nsh_base_mask.flags = mask->flags;\n+ nsh_base_mask.mdtype = mask->mdtype;\n+ nsh_base_mask.np = mask->np;\n+ nsh_base_mask.path_hdr = mask->path_hdr;\n+\n+ /* OVS_KEY_ATTR_NSH keys */\n+ nsh_key_ofs = nl_msg_start_nested(odp_actions, OVS_KEY_ATTR_NSH);\n+\n+ char *data = nl_msg_put_unspec_uninit(odp_actions,\n+ OVS_NSH_KEY_ATTR_BASE,\n+ sizeof(nsh_base));\n+ const char *lkey = (char *)&nsh_base, *lmask = (char *)&nsh_base_mask;\n+ size_t lkey_size = sizeof(nsh_base);\n+\n+ while (lkey_size--) {\n+ *data++ = *lkey++ & *lmask++;\n+ }\n+\n+ switch (key->mdtype) {\n+ case NSH_M_TYPE1:\n+ for (int i = 0; i < 4; i++) {\n+ md1.context[i] = key->context[i];\n+ md1_mask.context[i] = mask->context[i];\n+ }\n+ data = nl_msg_put_unspec_uninit(odp_actions,\n+ OVS_NSH_KEY_ATTR_MD1,\n+ sizeof(md1));\n+ lkey = (char *)&md1;\n+ lmask = (char *)&md1_mask;\n+ lkey_size = sizeof(md1);\n+\n+ while (lkey_size--) {\n+ *data++ = *lkey++ & *lmask++;\n }\n break;\n case NSH_M_TYPE2:\n default:\n /* No match support for other MD formats yet. */\n- memset(flow->nsh.c, 0, sizeof flow->nsh.c);\n break;\n+ }\n+\n+ /* OVS_KEY_ATTR_NSH masks */\n+ data = nl_msg_put_unspec_uninit(odp_actions,\n+ OVS_NSH_KEY_ATTR_BASE,\n+ sizeof(nsh_base_mask));\n+ lmask = (char *)&nsh_base_mask;\n+\n+ memcpy(data, lmask, sizeof(nsh_base_mask));\n+\n+ switch (key->mdtype) {\n+ case NSH_M_TYPE1:\n+ data = nl_msg_put_unspec_uninit(odp_actions,\n+ OVS_NSH_KEY_ATTR_MD1,\n+ sizeof(md1_mask));\n+ lmask = (char *)&md1_mask;\n+ memcpy(data, lmask, sizeof(md1_mask));\n+ break;\n+ case NSH_M_TYPE2:\n+ default:\n+ /* No match support for other MD formats yet. */\n+ break;\n+ }\n+ nl_msg_end_nested(odp_actions, nsh_key_ofs);\n+\n+ nl_msg_end_nested(odp_actions, offset);\n+ } else {\n+ if (!fully_masked) {\n+ memset(mask, 0xff, size);\n+ }\n+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);\n+ nsh_key_to_attr(odp_actions, flow_nsh, NULL, 0, false);\n+ nl_msg_end_nested(odp_actions, offset);\n }\n+ memcpy(base, key, size);\n+ return true;\n }\n \n static void\n@@ -6684,8 +7178,8 @@ commit_set_nsh_action(const struct flow *flow, struct flow *base_flow,\n mask.mdtype = 0; /* Not writable. */\n mask.np = 0; /* Not writable. */\n \n- if (commit(OVS_KEY_ATTR_NSH, use_masked, &key, &base, &mask, sizeof key,\n- odp_actions)) {\n+ if (commit_nsh(&base_flow->nsh, use_masked, &key, &base, &mask,\n+ sizeof key, odp_actions)) {\n put_nsh_key(&base, base_flow, false);\n if (mask.mdtype != 0) { /* Mask was changed by commit(). */\n put_nsh_key(&mask, &wc->masks, true);\n@@ -6788,49 +7282,36 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,\n }\n \n static void\n-odp_put_decap_nsh_action(struct ofpbuf *odp_actions)\n+odp_put_pop_nsh_action(struct ofpbuf *odp_actions)\n {\n- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_DECAP_NSH);\n+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_NSH);\n }\n \n static void\n-odp_put_encap_nsh_action(struct ofpbuf *odp_actions,\n+odp_put_push_nsh_action(struct ofpbuf *odp_actions,\n const struct flow *flow,\n struct ofpbuf *encap_data)\n {\n- struct ovs_action_encap_nsh encap_nsh;\n-\n- encap_nsh.flags = flow->nsh.flags;\n- encap_nsh.mdtype = flow->nsh.mdtype;\n- encap_nsh.np = flow->nsh.np;\n- encap_nsh.path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) |\n- flow->nsh.si);\n+ uint8_t * metadata = NULL;\n+ uint8_t md_size = 0;\n \n- switch (encap_nsh.mdtype) {\n- case NSH_M_TYPE1: {\n- struct nsh_md1_ctx *md1 =\n- ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh.metadata);\n- encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;\n- for (int i = 0; i < 4; i++) {\n- put_16aligned_be32(&md1->c[i], flow->nsh.c[i]);\n- }\n- break;\n- }\n+ switch (flow->nsh.mdtype) {\n case NSH_M_TYPE2:\n if (encap_data) {\n- ovs_assert(encap_data->size < OVS_ENCAP_NSH_MAX_MD_LEN);\n- encap_nsh.mdlen = encap_data->size;\n- memcpy(encap_nsh.metadata, encap_data->data, encap_data->size);\n+ ovs_assert(encap_data->size < OVS_PUSH_NSH_MAX_MD_LEN);\n+ metadata = encap_data->data;\n+ md_size = encap_data->size;\n } else {\n- encap_nsh.mdlen = 0;\n+ md_size = 0;\n }\n break;\n default:\n- encap_nsh.mdlen = 0;\n+ md_size = 0;\n break;\n }\n- nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_ENCAP_NSH,\n- &encap_nsh, sizeof(encap_nsh));\n+ size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_PUSH_NSH);\n+ nsh_key_to_attr(odp_actions, &flow->nsh, metadata, md_size, false);\n+ nl_msg_end_nested(odp_actions, offset);\n }\n \n static void\n@@ -6857,8 +7338,8 @@ commit_packet_type_change(const struct flow *flow,\n break;\n }\n case PT_NSH:\n- /* encap_nsh */\n- odp_put_encap_nsh_action(odp_actions, flow, encap_data);\n+ /* push_nsh */\n+ odp_put_push_nsh_action(odp_actions, flow, encap_data);\n base_flow->packet_type = flow->packet_type;\n /* Update all packet headers in base_flow. */\n memcpy(&base_flow->dl_dst, &flow->dl_dst,\n@@ -6883,8 +7364,8 @@ commit_packet_type_change(const struct flow *flow,\n * No need to update the base flow here. */\n switch (ntohl(base_flow->packet_type)) {\n case PT_NSH:\n- /* decap_nsh. */\n- odp_put_decap_nsh_action(odp_actions);\n+ /* pop_nsh. */\n+ odp_put_pop_nsh_action(odp_actions);\n break;\n default:\n /* Checks are done during translation. */\ndiff --git a/lib/odp-util.h b/lib/odp-util.h\nindex 27c2ab4..10693cb 100644\n--- a/lib/odp-util.h\n+++ b/lib/odp-util.h\n@@ -158,6 +158,10 @@ struct odputil_keybuf {\n \n enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,\n struct flow_tnl *);\n+enum odp_key_fitness odp_nsh_key_from_attr(const struct nlattr *,\n+ struct flow_nsh *);\n+enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *,\n+ struct nsh_hdr *, size_t size);\n \n int odp_ufid_from_string(const char *s_, ovs_u128 *ufid);\n void odp_format_ufid(const ovs_u128 *ufid, struct ds *);\ndiff --git a/lib/packets.c b/lib/packets.c\nindex 74d87ed..db98ab3 100644\n--- a/lib/packets.c\n+++ b/lib/packets.c\n@@ -403,10 +403,10 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)\n }\n \n void\n-encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh *encap)\n+push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src)\n {\n struct nsh_hdr *nsh;\n- size_t length = NSH_BASE_HDR_LEN + encap->mdlen;\n+ size_t length = nsh_hdr_len(nsh_hdr_src);\n uint8_t next_proto;\n \n switch (ntohl(packet->packet_type)) {\n@@ -427,23 +427,8 @@ encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh *encap)\n }\n \n nsh = (struct nsh_hdr *) dp_packet_push_uninit(packet, length);\n- nsh->ver_flags_len = htons(encap->flags << NSH_FLAGS_SHIFT | length >> 2);\n+ memcpy(nsh, nsh_hdr_src, length);\n nsh->next_proto = next_proto;\n- put_16aligned_be32(&nsh->path_hdr, encap->path_hdr);\n- nsh->md_type = encap->mdtype;\n- switch (nsh->md_type) {\n- case NSH_M_TYPE1:\n- nsh->md1 = *ALIGNED_CAST(struct nsh_md1_ctx *, encap->metadata);\n- break;\n- case NSH_M_TYPE2: {\n- /* The MD2 metadata in encap is already padded to 4 bytes. */\n- size_t len = ROUND_UP(encap->mdlen, 4);\n- memcpy(&nsh->md2, encap->metadata, len);\n- break;\n- }\n- default:\n- OVS_NOT_REACHED();\n- }\n \n packet->packet_type = htonl(PT_NSH);\n dp_packet_reset_offsets(packet);\n@@ -451,7 +436,7 @@ encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh *encap)\n }\n \n bool\n-decap_nsh(struct dp_packet *packet)\n+pop_nsh(struct dp_packet *packet)\n {\n struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);\n size_t length;\ndiff --git a/lib/packets.h b/lib/packets.h\nindex 705d0b2..e7832ba 100644\n--- a/lib/packets.h\n+++ b/lib/packets.h\n@@ -434,9 +434,8 @@ void push_eth(struct dp_packet *packet, const struct eth_addr *dst,\n const struct eth_addr *src);\n void pop_eth(struct dp_packet *packet);\n \n-void encap_nsh(struct dp_packet *packet,\n- const struct ovs_action_encap_nsh *encap_nsh);\n-bool decap_nsh(struct dp_packet *packet);\n+void push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src);\n+bool pop_nsh(struct dp_packet *packet);\n \n #define LLC_DSAP_SNAP 0xaa\n #define LLC_SSAP_SNAP 0xaa\ndiff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c\nindex 472c272..16976c4 100644\n--- a/ofproto/ofproto-dpif-ipfix.c\n+++ b/ofproto/ofproto-dpif-ipfix.c\n@@ -2823,8 +2823,8 @@ dpif_ipfix_read_actions(const struct flow *flow,\n case OVS_ACTION_ATTR_POP_MPLS:\n case OVS_ACTION_ATTR_PUSH_ETH:\n case OVS_ACTION_ATTR_POP_ETH:\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- case OVS_ACTION_ATTR_DECAP_NSH:\n+ case OVS_ACTION_ATTR_PUSH_NSH:\n+ case OVS_ACTION_ATTR_POP_NSH:\n case OVS_ACTION_ATTR_UNSPEC:\n case __OVS_ACTION_ATTR_MAX:\n default:\ndiff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c\nindex 65a2003..1af1569 100644\n--- a/ofproto/ofproto-dpif-sflow.c\n+++ b/ofproto/ofproto-dpif-sflow.c\n@@ -1199,8 +1199,8 @@ dpif_sflow_read_actions(const struct flow *flow,\n \t break;\n \tcase OVS_ACTION_ATTR_SAMPLE:\n \tcase OVS_ACTION_ATTR_CLONE:\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- case OVS_ACTION_ATTR_DECAP_NSH:\n+ case OVS_ACTION_ATTR_PUSH_NSH:\n+ case OVS_ACTION_ATTR_POP_NSH:\n \tcase OVS_ACTION_ATTR_UNSPEC:\n \tcase __OVS_ACTION_ATTR_MAX:\n \tdefault:\ndiff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c\nindex 9e1f837..4e4d6a6 100644\n--- a/ofproto/ofproto-dpif-xlate.c\n+++ b/ofproto/ofproto-dpif-xlate.c\n@@ -4392,8 +4392,8 @@ xlate_fixup_actions(struct ofpbuf *b, const struct nlattr *actions,\n case OVS_ACTION_ATTR_CT:\n case OVS_ACTION_ATTR_PUSH_ETH:\n case OVS_ACTION_ATTR_POP_ETH:\n- case OVS_ACTION_ATTR_ENCAP_NSH:\n- case OVS_ACTION_ATTR_DECAP_NSH:\n+ case OVS_ACTION_ATTR_PUSH_NSH:\n+ case OVS_ACTION_ATTR_POP_NSH:\n case OVS_ACTION_ATTR_METER:\n ofpbuf_put(b, a, nl_attr_len_pad(a, left));\n break;\n@@ -5797,17 +5797,17 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,\n \n /* For an MD2 NSH header returns a pointer to an ofpbuf with the encoded\n * MD2 TLVs provided as encap properties to the encap operation. This\n- * will be stored as encap_data in the ctx and copied into the encap_nsh\n+ * will be stored as encap_data in the ctx and copied into the push_nsh\n * action at the next commit. */\n static struct ofpbuf *\n-rewrite_flow_encap_nsh(struct xlate_ctx *ctx,\n- const struct ofpact_encap *encap,\n- struct flow *flow,\n- struct flow_wildcards *wc)\n+rewrite_flow_push_nsh(struct xlate_ctx *ctx,\n+ const struct ofpact_encap *encap,\n+ struct flow *flow,\n+ struct flow_wildcards *wc)\n {\n ovs_be32 packet_type = flow->packet_type;\n const char *ptr = (char *) encap->props;\n- struct ofpbuf *buf = ofpbuf_new(OVS_ENCAP_NSH_MAX_MD_LEN);\n+ struct ofpbuf *buf = ofpbuf_new(NSH_CTX_HDRS_MAX_LEN);\n uint8_t md_type = NSH_M_TYPE1;\n uint8_t np = 0;\n int i;\n@@ -5847,7 +5847,7 @@ rewrite_flow_encap_nsh(struct xlate_ctx *ctx,\n }\n ptr += ROUND_UP(prop_ptr->len, 8);\n }\n- if (buf->size == 0 || buf->size > OVS_ENCAP_NSH_MAX_MD_LEN) {\n+ if (buf->size == 0 || buf->size > NSH_CTX_HDRS_MAX_LEN) {\n ofpbuf_delete(buf);\n buf = NULL;\n }\n@@ -5892,7 +5892,7 @@ rewrite_flow_encap_nsh(struct xlate_ctx *ctx,\n \n if (md_type == NSH_M_TYPE1) {\n flow->nsh.mdtype = NSH_M_TYPE1;\n- memset(flow->nsh.c, 0, sizeof flow->nsh.c);\n+ memset(flow->nsh.context, 0, sizeof flow->nsh.context);\n if (buf) {\n /* Drop any MD2 context TLVs. */\n ofpbuf_delete(buf);\n@@ -5923,7 +5923,7 @@ xlate_generic_encap_action(struct xlate_ctx *ctx,\n rewrite_flow_encap_ethernet(ctx, flow, wc);\n break;\n case PT_NSH:\n- encap_data = rewrite_flow_encap_nsh(ctx, encap, flow, wc);\n+ encap_data = rewrite_flow_push_nsh(ctx, encap, flow, wc);\n break;\n default:\n /* New packet type was checked during decoding. */\n@@ -5966,7 +5966,7 @@ xlate_generic_decap_action(struct xlate_ctx *ctx,\n }\n return false;\n case PT_NSH:\n- /* The decap_nsh action is generated at the commit executed as\n+ /* The pop_nsh action is generated at the commit executed as\n * part of freezing the ctx for recirculation. Here we just set\n * the new packet type based on the NSH next protocol field. */\n switch (flow->nsh.np) {\ndiff --git a/tests/nsh.at b/tests/nsh.at\nindex aa80a2a..562f3da 100644\n--- a/tests/nsh.at\n+++ b/tests/nsh.at\n@@ -105,7 +105,7 @@ bridge(\"br0\")\n \n Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0\n Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no\n-Datapath actions: encap_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,decap_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)\n+Datapath actions: push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)\n ])\n \n AT_CHECK([\n@@ -121,7 +121,7 @@ bridge(\"br0\")\n \n Final flow: unchanged\n Megaflow: recirc_id=0,eth,in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344\n-Datapath actions: pop_eth,decap_nsh(),recirc(0x2)\n+Datapath actions: pop_eth,pop_nsh(),recirc(0x2)\n ])\n \n # Now send two real ICMP echo request packets in on port p1\n@@ -139,7 +139,7 @@ ovs-appctl time/warp 1000\n AT_CHECK([\n ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort\n ], [0], [flow-dump from non-dpdk interfaces:\n-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:encap_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,decap_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)\n+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)\n recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2\n ])\n \n@@ -170,7 +170,7 @@ ovs-appctl time/warp 1000\n AT_CHECK([\n ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort\n ], [0], [flow-dump from non-dpdk interfaces:\n-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=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),decap_nsh(),recirc(0x4)\n+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4)\n 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\n ])\n \n@@ -195,7 +195,7 @@ ovs-vsctl set bridge br0 datapath_type=dummy \\\n add-port br0 v4 -- set Interface v4 type=patch options:peer=v3 ofport_request=4])\n \n AT_DATA([flows.txt], [dnl\n- table=0,in_port=1,ip,actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678))),set_field:0x1234->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3\n+ table=0,in_port=1,ip,actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))),set_field:0x1234->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->dl_dst,3\n table=0,in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234,actions=decap(),decap(),2\n ])\n \n@@ -205,7 +205,7 @@ AT_CHECK([\n ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions\n ], [0], [dnl\n in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_spi=0x1234 actions=decap(),decap(),output:2\n- ip,in_port=1 actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678))),set_field:0x1234->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,output:3\n+ ip,in_port=1 actions=encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210))),set_field:0x1234->nsh_spi,encap(ethernet),set_field:11:22:33:44:55:66->eth_dst,output:3\n ])\n \n AT_CHECK([\n@@ -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\n bridge(\"br0\")\n -------------\n 0. ip,in_port=1, priority 32768\n- encap(nsh(md_type=2,tlv(0x1000,10,0x12345678)))\n+ encap(nsh(md_type=2,tlv(0x1000,10,0x12345678),tlv(0x2000,20,0xfedcba9876543210)))\n set_field:0x1234->nsh_spi\n encap(ethernet)\n set_field:11:22:33:44:55:66->eth_dst\n@@ -230,7 +230,7 @@ bridge(\"br0\")\n \n Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0\n Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no\n-Datapath actions: encap_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a0412345678),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)\n+Datapath actions: push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)\n ])\n \n AT_CHECK([\n@@ -246,7 +246,7 @@ bridge(\"br0\")\n \n Final flow: unchanged\n Megaflow: recirc_id=0,eth,in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234\n-Datapath actions: pop_eth,decap_nsh(),recirc(0x2)\n+Datapath actions: pop_eth,pop_nsh(),recirc(0x2)\n ])\n \n # Now send two real ICMP echo request packets in on port p1\n@@ -264,7 +264,7 @@ ovs-appctl time/warp 1000\n AT_CHECK([\n ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort\n ], [0], [flow-dump from non-dpdk interfaces:\n-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:encap_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a0412345678),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)\n+recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)\n recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2\n ])\n \n@@ -577,8 +577,8 @@ ovs-appctl time/warp 1000\n AT_CHECK([\n ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort\n ], [0], [flow-dump from non-dpdk interfaces:\n-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,encap_nsh(flags=0,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789))\n-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)\n+recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789))\n+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)\n tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x1),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6\n ])\n \n@@ -631,9 +631,9 @@ ovs-appctl time/warp 1000\n AT_CHECK([\n ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort\n ], [0], [flow-dump from non-dpdk interfaces:\n-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,encap_nsh(flags=0,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789))\n+recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789))\n tunnel(tun_id=0x0,src=20.0.0.1,dst=20.0.0.2,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(spi=0x3020,si=255), packets:1, bytes:108, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),set(nsh(spi=0x3020,si=254)),pop_eth,clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(src=30.0.0.2,dst=30.0.0.3)),tnl_pop(4789))\n-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)\n+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)\n tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x2),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6\n ])\n \n", "prefixes": [ "ovs-dev", "v3", "1/2" ] }