From patchwork Sat Jan 6 05:47:52 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang, Yi" X-Patchwork-Id: 856344 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zD9kP3JfJz9sNV for ; Sat, 6 Jan 2018 16:55:41 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id BA981C84; Sat, 6 Jan 2018 05:53:48 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 08EAFBE6 for ; Sat, 6 Jan 2018 05:53:46 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id DA0B414D for ; Sat, 6 Jan 2018 05:53:42 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga006.fm.intel.com ([10.253.24.20]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 05 Jan 2018 21:53:42 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.46,320,1511856000"; d="scan'208";a="192824641" Received: from unknown (HELO localhost.localdomain.bj.intel.com) ([10.240.224.185]) by fmsmga006.fm.intel.com with ESMTP; 05 Jan 2018 21:53:40 -0800 From: Yi Yang To: dev@openvswitch.org Date: Sat, 6 Jan 2018 13:47:52 +0800 Message-Id: <1515217674-66361-3-git-send-email-yi.y.yang@intel.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1515217674-66361-1-git-send-email-yi.y.yang@intel.com> References: <1515217674-66361-1-git-send-email-yi.y.yang@intel.com> X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v7 2/4] nsh: add new flow key 'ttl' X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org IETF NSH draft added a new filed ttl in NSH header, this patch is to add new nsh key 'ttl' for it. Signed-off-by: Yi Yang --- datapath/linux/compat/include/linux/openvswitch.h | 2 +- include/openvswitch/flow.h | 6 +- include/openvswitch/meta-flow.h | 31 +++-- include/openvswitch/nsh.h | 96 ++++++++++++++ include/openvswitch/packets.h | 12 +- lib/flow.c | 23 ++-- lib/flow.h | 2 +- lib/match.c | 12 +- lib/meta-flow.c | 56 ++++++-- lib/meta-flow.xml | 6 +- lib/nx-match.c | 16 ++- lib/odp-execute.c | 40 +++--- lib/odp-util.c | 151 ++++++++-------------- lib/odp-util.h | 2 +- lib/packets.c | 1 + ofproto/ofproto-dpif-xlate.c | 7 +- tests/nsh.at | 41 +++--- 17 files changed, 308 insertions(+), 196 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 3ddb1c5..c7142b6 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -504,9 +504,9 @@ enum ovs_nsh_key_attr { struct ovs_nsh_key_base { __u8 flags; + __u8 ttl; __u8 mdtype; __u8 np; - __u8 pad; __be32 path_hdr; }; diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h index a658a58..cd61fff 100644 --- a/include/openvswitch/flow.h +++ b/include/openvswitch/flow.h @@ -146,7 +146,7 @@ struct flow { struct eth_addr arp_tha; /* ARP/ND target hardware address. */ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */ ovs_be16 pad2; /* Pad to 64 bits. */ - struct flow_nsh nsh; /* Network Service Header keys */ + struct ovs_key_nsh nsh; /* Network Service Header keys */ /* L4 (64-bit aligned) */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */ @@ -159,13 +159,13 @@ struct flow { }; BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0); -BUILD_ASSERT_DECL(sizeof(struct flow_nsh) % sizeof(uint64_t) == 0); +BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) % sizeof(uint64_t) == 0); #define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t)) /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) - == sizeof(struct flow_tnl) + sizeof(struct flow_nsh) + 300 + == sizeof(struct flow_tnl) + sizeof(struct ovs_key_nsh) + 300 && FLOW_WC_SEQ == 40); /* Incremental points at which flow classification may be performed in diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h index 436501f..14e6b59 100644 --- a/include/openvswitch/meta-flow.h +++ b/include/openvswitch/meta-flow.h @@ -1757,6 +1757,21 @@ enum OVS_PACKED_ENUM mf_field_id { */ MFF_NSH_FLAGS, + /* "nsh_ttl". + * + * TTL field in NSH base header. + * + * Type: u8. + * Maskable: no. + * Formatting: decimal. + * Prerequisites: NSH. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_NSH_TTL(2) since OF1.3 and v2.8. + */ + MFF_NSH_TTL, + + /* "nsh_mdtype". * * mdtype field in NSH base header. @@ -1767,7 +1782,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Prerequisites: NSH. * Access: read-only. * NXM: none. - * OXM: NXOXM_NSH_MDTYPE(2) since OF1.3 and v2.8. + * OXM: NXOXM_NSH_MDTYPE(3) since OF1.3 and v2.8. */ MFF_NSH_MDTYPE, @@ -1781,7 +1796,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Prerequisites: NSH. * Access: read-only. * NXM: none. - * OXM: NXOXM_NSH_NP(3) since OF1.3 and v2.8. + * OXM: NXOXM_NSH_NP(4) since OF1.3 and v2.8. */ MFF_NSH_NP, @@ -1795,7 +1810,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Prerequisites: NSH. * Access: read/write. * NXM: none. - * OXM: NXOXM_NSH_SPI(4) since OF1.3 and v2.8. + * OXM: NXOXM_NSH_SPI(5) since OF1.3 and v2.8. */ MFF_NSH_SPI, @@ -1809,7 +1824,7 @@ enum OVS_PACKED_ENUM mf_field_id { * Prerequisites: NSH. * Access: read/write. * NXM: none. - * OXM: NXOXM_NSH_SI(5) since OF1.3 and v2.8. + * OXM: NXOXM_NSH_SI(6) since OF1.3 and v2.8. */ MFF_NSH_SI, @@ -1823,10 +1838,10 @@ enum OVS_PACKED_ENUM mf_field_id { * Prerequisites: NSH. * Access: read/write. * NXM: none. - * OXM: NXOXM_NSH_C1(6) since OF1.3 and v2.8. <1> - * OXM: NXOXM_NSH_C2(7) since OF1.3 and v2.8. <2> - * OXM: NXOXM_NSH_C3(8) since OF1.3 and v2.8. <3> - * OXM: NXOXM_NSH_C4(9) since OF1.3 and v2.8. <4> + * OXM: NXOXM_NSH_C1(7) since OF1.3 and v2.8. <1> + * OXM: NXOXM_NSH_C2(8) since OF1.3 and v2.8. <2> + * OXM: NXOXM_NSH_C3(9) since OF1.3 and v2.8. <3> + * OXM: NXOXM_NSH_C4(10) since OF1.3 and v2.8. <4> */ MFF_NSH_C1, MFF_NSH_C2, diff --git a/include/openvswitch/nsh.h b/include/openvswitch/nsh.h index 63b480e..9b02146 100644 --- a/include/openvswitch/nsh.h +++ b/include/openvswitch/nsh.h @@ -299,6 +299,102 @@ nsh_reset_ver_flags_ttl_len(struct nsh_hdr *nsh) nsh->ver_flags_ttl_len = 0; } +static inline uint8_t +nsh_get_ttl(const struct nsh_hdr *nsh) +{ + return (ntohs(nsh->ver_flags_ttl_len) & NSH_TTL_MASK) >> NSH_TTL_SHIFT; +} + +static inline ovs_be32 +nsh_16aligned_be32(const ovs_16aligned_be32 *x) +{ +#ifdef WORDS_BIGENDIAN + return ((ovs_be32) x->hi << 16) | (ovs_be32) x->lo; +#else + return ((ovs_be32) x->lo << 16) | (ovs_be32) x->hi; +#endif +} + +static inline ovs_be32 +nsh_get_path_hdr(const struct nsh_hdr *nsh) +{ + return nsh_16aligned_be32(&nsh->path_hdr); +} + +static inline ovs_be32 +nsh_get_spi(const struct nsh_hdr *nsh) +{ + ovs_be32 path_hdr = ntohl(nsh_16aligned_be32(&nsh->path_hdr)); + return htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT); +} + +static inline uint8_t +nsh_get_si(const struct nsh_hdr *nsh) +{ + ovs_be32 path_hdr = ntohl(nsh_16aligned_be32(&nsh->path_hdr)); + return (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT; +} + +static inline ovs_be32 +nsh_path_hdr_to_spi(ovs_be32 path_hdr) +{ + return htonl((ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT); +} + +static inline uint32_t +nsh_path_hdr_to_spi_uint32(ovs_be32 path_hdr) +{ + return (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT; +} + +static inline uint8_t +nsh_path_hdr_to_si(ovs_be32 path_hdr) +{ + return (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT; +} + +static inline ovs_be32 +nsh_spi_si_to_path_hdr(uint32_t spi, uint8_t si) +{ + return htonl((spi << NSH_SPI_SHIFT) | si); +} + +static inline void +nsh_set_flags_and_ttl(struct nsh_hdr *nsh, uint8_t flags, uint8_t ttl) +{ + nsh->ver_flags_ttl_len + = htons((ntohs(nsh->ver_flags_ttl_len) + & ~(NSH_FLAGS_MASK | NSH_TTL_MASK)) + | ((flags << NSH_FLAGS_SHIFT)& NSH_FLAGS_MASK) + | ((ttl << NSH_TTL_SHIFT) & NSH_TTL_MASK)); +} + +static inline void +nsh_set_flags_ttl_len(struct nsh_hdr *nsh, uint8_t flags, uint8_t ttl, + uint16_t len) +{ + nsh->ver_flags_ttl_len + = htons((ntohs(nsh->ver_flags_ttl_len) + & ~(NSH_FLAGS_MASK | NSH_TTL_MASK | NSH_LEN_MASK)) + | ((flags << NSH_FLAGS_SHIFT)& NSH_FLAGS_MASK) + | ((ttl << NSH_TTL_SHIFT) & NSH_TTL_MASK) + | (((len >> 2) << NSH_LEN_SHIFT) & NSH_LEN_MASK)); +} + +static inline void +nsh_path_hdr_set_spi(ovs_be32 *path_hdr, ovs_be32 spi) +{ + *path_hdr = htonl((ntohl(*path_hdr) & ~NSH_SPI_MASK) | + ((ntohl(spi) << NSH_SPI_SHIFT) & NSH_SPI_MASK)); +} + +static inline void +nsh_path_hdr_set_si(ovs_be32 *path_hdr, uint8_t si) +{ + *path_hdr = htonl((ntohl(*path_hdr) & ~NSH_SI_MASK) | + ((si << NSH_SI_SHIFT) & NSH_SI_MASK)); +} + #ifdef __cplusplus } #endif diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h index ae1cf9c..fef756b 100644 --- a/include/openvswitch/packets.h +++ b/include/openvswitch/packets.h @@ -74,21 +74,11 @@ union flow_vlan_hdr { }; }; -/* Network Service Header keys */ -struct flow_nsh { - uint8_t flags; - uint8_t mdtype; - uint8_t np; - uint8_t si; - ovs_be32 spi; - ovs_be32 context[4]; -}; - struct ovs_key_nsh { uint8_t flags; + uint8_t ttl; uint8_t mdtype; uint8_t np; - uint8_t pad; ovs_be32 path_hdr; ovs_be32 context[4]; }; diff --git a/lib/flow.c b/lib/flow.c index 964f734..f9d7c2a 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -530,11 +530,10 @@ parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto, } bool -parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key) +parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key) { const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap; - uint8_t version, length, flags; - uint32_t path_hdr; + uint8_t version, length, flags, ttl; /* Check if it is long enough for NSH header, doesn't support * MD type 2 yet @@ -546,18 +545,17 @@ parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key) version = nsh_get_ver(nsh); flags = nsh_get_flags(nsh); length = nsh_hdr_len(nsh); + ttl = nsh_get_ttl(nsh); if (OVS_UNLIKELY(length > *sizep || version != 0)) { return false; } key->flags = flags; + key->ttl = ttl; key->mdtype = nsh->md_type; key->np = nsh->next_proto; - - path_hdr = ntohl(get_16aligned_be32(&nsh->path_hdr)); - key->si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT; - key->spi = htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT); + key->path_hdr = nsh_get_path_hdr(nsh); switch (key->mdtype) { case NSH_M_TYPE1: @@ -876,11 +874,11 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) miniflow_pad_to_64(mf, arp_tha); } } else if (dl_type == htons(ETH_TYPE_NSH)) { - struct flow_nsh nsh; + struct ovs_key_nsh nsh; if (OVS_LIKELY(parse_nsh(&data, &size, &nsh))) { miniflow_push_words(mf, nsh, &nsh, - sizeof(struct flow_nsh) / + sizeof(struct ovs_key_nsh) / sizeof(uint64_t)); } } @@ -1684,10 +1682,10 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc, return; } else if (flow->dl_type == htons(ETH_TYPE_NSH)) { WC_MASK_FIELD(wc, nsh.flags); + WC_MASK_FIELD(wc, nsh.ttl); WC_MASK_FIELD(wc, nsh.mdtype); WC_MASK_FIELD(wc, nsh.np); - WC_MASK_FIELD(wc, nsh.spi); - WC_MASK_FIELD(wc, nsh.si); + WC_MASK_FIELD(wc, nsh.path_hdr); WC_MASK_FIELD(wc, nsh.context); } else { return; /* Unknown ethertype. */ @@ -1820,8 +1818,7 @@ flow_wc_map(const struct flow *flow, struct flowmap *map) FLOWMAP_SET(map, nsh.flags); FLOWMAP_SET(map, nsh.mdtype); FLOWMAP_SET(map, nsh.np); - FLOWMAP_SET(map, nsh.spi); - FLOWMAP_SET(map, nsh.si); + FLOWMAP_SET(map, nsh.path_hdr); FLOWMAP_SET(map, nsh.context); } } diff --git a/lib/flow.h b/lib/flow.h index b3128da..eb1e2bf 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -129,7 +129,7 @@ bool flow_compose(struct dp_packet *, const struct flow *, size_t); bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto, uint8_t *nw_frag); ovs_be16 parse_dl_type(const struct eth_header *data_, size_t size); -bool parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key); +bool parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key); static inline uint64_t flow_get_xreg(const struct flow *flow, int idx) diff --git a/lib/match.c b/lib/match.c index 8952c99..b880492 100644 --- a/lib/match.c +++ b/lib/match.c @@ -1260,11 +1260,19 @@ format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask) static void format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m) { + ovs_be32 spi_mask = nsh_path_hdr_to_spi(m->nsh.path_hdr); + if (spi_mask == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) { + spi_mask = OVS_BE32_MAX; + } format_uint8_masked(s, "nsh_flags", f->nsh.flags, m->nsh.flags); + format_uint8_masked(s, "nsh_ttl", f->nsh.ttl, m->nsh.ttl); format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, m->nsh.mdtype); format_uint8_masked(s, "nsh_np", f->nsh.np, m->nsh.np); - format_be32_masked_hex(s, "nsh_spi", f->nsh.spi, m->nsh.spi); - format_uint8_masked(s, "nsh_si", f->nsh.si, m->nsh.si); + + format_be32_masked_hex(s, "nsh_spi", nsh_path_hdr_to_spi(f->nsh.path_hdr), + spi_mask); + format_uint8_masked(s, "nsh_si", nsh_path_hdr_to_si(f->nsh.path_hdr), + nsh_path_hdr_to_si(m->nsh.path_hdr)); if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) { format_be32_masked_hex(s, "nsh_c1", f->nsh.context[0], m->nsh.context[0]); diff --git a/lib/meta-flow.c b/lib/meta-flow.c index beeddf1..ed41f50 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -40,6 +40,7 @@ #include "openvswitch/ofp-errors.h" #include "openvswitch/vlog.h" #include "vl-mff-map.h" +#include "openvswitch/nsh.h" VLOG_DEFINE_THIS_MODULE(meta_flow); @@ -361,14 +362,16 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) case MFF_NSH_FLAGS: return !wc->masks.nsh.flags; + case MFF_NSH_TTL: + return !wc->masks.nsh.ttl; case MFF_NSH_MDTYPE: return !wc->masks.nsh.mdtype; case MFF_NSH_NP: return !wc->masks.nsh.np; case MFF_NSH_SPI: - return !wc->masks.nsh.spi; + return !(wc->masks.nsh.path_hdr & htonl(NSH_SPI_MASK)); case MFF_NSH_SI: - return !wc->masks.nsh.si; + return !(wc->masks.nsh.path_hdr & htonl(NSH_SI_MASK)); case MFF_NSH_C1: case MFF_NSH_C2: case MFF_NSH_C3: @@ -606,6 +609,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_NSH_FLAGS: return true; + case MFF_NSH_TTL: + return (value->u8 <= 63); case MFF_NSH_MDTYPE: return (value->u8 == 1 || value->u8 == 2); case MFF_NSH_NP: @@ -899,6 +904,9 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, case MFF_NSH_FLAGS: value->u8 = flow->nsh.flags; break; + case MFF_NSH_TTL: + value->u8 = flow->nsh.ttl; + break; case MFF_NSH_MDTYPE: value->u8 = flow->nsh.mdtype; break; @@ -906,10 +914,13 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, value->u8 = flow->nsh.np; break; case MFF_NSH_SPI: - value->be32 = flow->nsh.spi; + value->be32 = nsh_path_hdr_to_spi(flow->nsh.path_hdr); + if (value->be32 == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) { + value->be32 = OVS_BE32_MAX; + } break; case MFF_NSH_SI: - value->u8 = flow->nsh.si; + value->u8 = nsh_path_hdr_to_si(flow->nsh.path_hdr); break; case MFF_NSH_C1: case MFF_NSH_C2: @@ -1214,6 +1225,9 @@ mf_set_value(const struct mf_field *mf, case MFF_NSH_FLAGS: MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8); break; + case MFF_NSH_TTL: + MATCH_SET_FIELD_UINT8(match, nsh.ttl, value->u8); + break; case MFF_NSH_MDTYPE: MATCH_SET_FIELD_UINT8(match, nsh.mdtype, value->u8); break; @@ -1221,10 +1235,12 @@ mf_set_value(const struct mf_field *mf, MATCH_SET_FIELD_UINT8(match, nsh.np, value->u8); break; case MFF_NSH_SPI: - MATCH_SET_FIELD_BE32(match, nsh.spi, value->be32); + match->wc.masks.nsh.path_hdr |= htonl(NSH_SPI_MASK); + nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, value->be32); break; case MFF_NSH_SI: - MATCH_SET_FIELD_UINT8(match, nsh.si, value->u8); + match->wc.masks.nsh.path_hdr |= htonl(NSH_SI_MASK); + nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, value->u8); break; case MFF_NSH_C1: case MFF_NSH_C2: @@ -1606,6 +1622,9 @@ mf_set_flow_value(const struct mf_field *mf, case MFF_NSH_FLAGS: flow->nsh.flags = value->u8; break; + case MFF_NSH_TTL: + flow->nsh.ttl = value->u8; + break; case MFF_NSH_MDTYPE: flow->nsh.mdtype = value->u8; break; @@ -1613,10 +1632,10 @@ mf_set_flow_value(const struct mf_field *mf, flow->nsh.np = value->u8; break; case MFF_NSH_SPI: - flow->nsh.spi = value->be32; + nsh_path_hdr_set_spi(&flow->nsh.path_hdr, value->be32); break; case MFF_NSH_SI: - flow->nsh.si = value->u8; + nsh_path_hdr_set_si(&flow->nsh.path_hdr, value->u8); break; case MFF_NSH_C1: case MFF_NSH_C2: @@ -1752,6 +1771,7 @@ mf_is_pipeline_field(const struct mf_field *mf) case MFF_ND_SLL: case MFF_ND_TLL: case MFF_NSH_FLAGS: + case MFF_NSH_TTL: case MFF_NSH_MDTYPE: case MFF_NSH_NP: case MFF_NSH_SPI: @@ -2097,6 +2117,9 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) case MFF_NSH_FLAGS: MATCH_SET_FIELD_MASKED(match, nsh.flags, 0, 0); break; + case MFF_NSH_TTL: + MATCH_SET_FIELD_MASKED(match, nsh.ttl, 0, 0); + break; case MFF_NSH_MDTYPE: MATCH_SET_FIELD_MASKED(match, nsh.mdtype, 0, 0); break; @@ -2104,10 +2127,12 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) MATCH_SET_FIELD_MASKED(match, nsh.np, 0, 0); break; case MFF_NSH_SPI: - MATCH_SET_FIELD_MASKED(match, nsh.spi, htonl(0), htonl(0)); + match->wc.masks.nsh.path_hdr &= ~htonl(NSH_SPI_MASK); + nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, htonl(0)); break; case MFF_NSH_SI: - MATCH_SET_FIELD_MASKED(match, nsh.si, 0, 0); + match->wc.masks.nsh.path_hdr &= ~htonl(NSH_SI_MASK); + nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, 0); break; case MFF_NSH_C1: case MFF_NSH_C2: @@ -2357,6 +2382,9 @@ mf_set(const struct mf_field *mf, case MFF_NSH_FLAGS: MATCH_SET_FIELD_MASKED(match, nsh.flags, value->u8, mask->u8); break; + case MFF_NSH_TTL: + MATCH_SET_FIELD_MASKED(match, nsh.ttl, value->u8, mask->u8); + break; case MFF_NSH_MDTYPE: MATCH_SET_FIELD_MASKED(match, nsh.mdtype, value->u8, mask->u8); break; @@ -2364,10 +2392,14 @@ mf_set(const struct mf_field *mf, MATCH_SET_FIELD_MASKED(match, nsh.np, value->u8, mask->u8); break; case MFF_NSH_SPI: - MATCH_SET_FIELD_MASKED(match, nsh.spi, value->be32, mask->be32); + match->wc.masks.nsh.path_hdr |= mask->be32; + nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, + value->be32 & mask->be32); break; case MFF_NSH_SI: - MATCH_SET_FIELD_MASKED(match, nsh.si, value->u8, mask->u8); + match->wc.masks.nsh.path_hdr |= htonl(mask->u8); + nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, + value->u8 & mask->u8); break; case MFF_NSH_C1: case MFF_NSH_C2: diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml index 08ee0ec..8e00c68 100644 --- a/lib/meta-flow.xml +++ b/lib/meta-flow.xml @@ -1311,7 +1311,9 @@ tcp,tp_src=0x07c0/0xfff0 + title="flags field (2 bits)"/> + OFPR_INVALID_TTL ``packet-in'' messages via OpenFlow. - +

Specifies what kinds of IP fragments or non-fragments to match. The diff --git a/lib/nx-match.c b/lib/nx-match.c index 8f2a442..aa7691a 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1022,6 +1022,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, const struct flow *flow = &match->flow; const size_t start_len = b->size; ovs_be16 dl_type = get_dl_type(flow); + ovs_be32 spi_mask; int match_len; int i; @@ -1157,13 +1158,22 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, /* Network Service Header */ nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags, match->wc.masks.nsh.flags); + nxm_put_8m(&ctx, MFF_NSH_TTL, oxm, flow->nsh.ttl, + match->wc.masks.nsh.ttl); nxm_put_8m(&ctx, MFF_NSH_MDTYPE, oxm, flow->nsh.mdtype, match->wc.masks.nsh.mdtype); nxm_put_8m(&ctx, MFF_NSH_NP, oxm, flow->nsh.np, match->wc.masks.nsh.np); - nxm_put_32m(&ctx, MFF_NSH_SPI, oxm, flow->nsh.spi, - match->wc.masks.nsh.spi); - nxm_put_8m(&ctx, MFF_NSH_SI, oxm, flow->nsh.si, match->wc.masks.nsh.si); + spi_mask = nsh_path_hdr_to_spi(match->wc.masks.nsh.path_hdr); + if (spi_mask == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) { + spi_mask = OVS_BE32_MAX; + } + nxm_put_32m(&ctx, MFF_NSH_SPI, oxm, + nsh_path_hdr_to_spi(flow->nsh.path_hdr), + spi_mask); + nxm_put_8m(&ctx, MFF_NSH_SI, oxm, + nsh_path_hdr_to_si(flow->nsh.path_hdr), + nsh_path_hdr_to_si(match->wc.masks.nsh.path_hdr)); for (int i = 0; i < 4; i++) { nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.context[i], match->wc.masks.nsh.context[i]); diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 7f9d419..c680364 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -274,19 +274,16 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key, /* Set the NSH header. Assumes the NSH header is present and matches the * MD format of the key. The slow path must take case of that. */ static void -odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key, - const struct flow_nsh *mask) +odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key, + const struct ovs_key_nsh *mask) { struct nsh_hdr *nsh = dp_packet_l3(packet); uint8_t mdtype = nsh_md_type(nsh); ovs_be32 path_hdr; if (!mask) { - nsh->ver_flags_ttl_len = htons(key->flags << NSH_FLAGS_SHIFT) | - (nsh->ver_flags_ttl_len & ~htons(NSH_FLAGS_MASK)); - path_hdr = htonl((ntohl(key->spi) << NSH_SPI_SHIFT) | - key->si); - put_16aligned_be32(&nsh->path_hdr, path_hdr); + nsh_set_flags_and_ttl(nsh, key->flags, key->ttl); + put_16aligned_be32(&nsh->path_hdr, key->path_hdr); switch (mdtype) { case NSH_M_TYPE1: for (int i = 0; i < 4; i++) { @@ -299,22 +296,25 @@ odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key, break; } } else { - uint8_t flags = (ntohs(nsh->ver_flags_ttl_len) & NSH_FLAGS_MASK) >> - NSH_FLAGS_SHIFT; + uint8_t flags = nsh_get_flags(nsh); + uint8_t ttl = nsh_get_ttl(nsh); + flags = key->flags | (flags & ~mask->flags); - nsh->ver_flags_ttl_len = htons(flags << NSH_FLAGS_SHIFT) | - (nsh->ver_flags_ttl_len & ~htons(NSH_FLAGS_MASK)); + ttl = key->ttl | (ttl & ~mask->ttl); + nsh_set_flags_and_ttl(nsh, flags, ttl); - path_hdr = get_16aligned_be32(&nsh->path_hdr); - uint32_t spi = (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT; - uint8_t si = (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT; - uint32_t spi_mask = ntohl(mask->spi); + uint32_t spi = ntohl(nsh_get_spi(nsh)); + uint8_t si = nsh_get_si(nsh); + uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr); + uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr); if (spi_mask == 0x00ffffff) { spi_mask = UINT32_MAX; } - spi = ntohl(key->spi) | (spi & ~spi_mask); - si = key->si | (si & ~mask->si); - path_hdr = htonl((spi << NSH_SPI_SHIFT) | si); + spi = nsh_path_hdr_to_spi_uint32(key->path_hdr) | (spi & ~spi_mask); + si = nsh_path_hdr_to_si(key->path_hdr) | (si & ~si_mask); + path_hdr = nsh_get_path_hdr(nsh); + nsh_path_hdr_set_spi(&path_hdr, htonl(spi)); + nsh_path_hdr_set_si(&path_hdr, si); put_16aligned_be32(&nsh->path_hdr, path_hdr); switch (mdtype) { case NSH_M_TYPE1: @@ -359,7 +359,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a) break; case OVS_KEY_ATTR_NSH: { - struct flow_nsh nsh; + struct ovs_key_nsh nsh; odp_nsh_key_from_attr(a, &nsh); odp_set_nsh(packet, &nsh, NULL); break; @@ -490,7 +490,7 @@ odp_execute_masked_set_action(struct dp_packet *packet, break; case OVS_KEY_ATTR_NSH: { - struct flow_nsh nsh, nsh_mask; + struct ovs_key_nsh nsh, nsh_mask; struct { struct nlattr nla; uint8_t data[sizeof(struct ovs_nsh_key_base) + NSH_CTX_HDRS_MAX_LEN diff --git a/lib/odp-util.c b/lib/odp-util.c index ff08821..95fef7e 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -246,12 +246,13 @@ static void format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key) { ds_put_format(ds, "flags=%d", key->flags); + ds_put_format(ds, "ttl=%d", key->ttl); ds_put_format(ds, ",mdtype=%d", key->mdtype); ds_put_format(ds, ",np=%d", key->np); ds_put_format(ds, ",spi=0x%x", - (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT); + nsh_path_hdr_to_spi_uint32(key->path_hdr)); ds_put_format(ds, ",si=%d", - (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT); + nsh_path_hdr_to_si(key->path_hdr)); switch (key->mdtype) { case NSH_M_TYPE1: @@ -311,17 +312,16 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key, format_nsh_key(ds, key); } else { bool first = true; - uint32_t spi = (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT; - uint32_t spi_mask = (ntohl(mask->path_hdr) & NSH_SPI_MASK) >> - NSH_SPI_SHIFT; - if (spi_mask == 0x00ffffff) { + uint32_t spi = nsh_path_hdr_to_spi_uint32(key->path_hdr); + uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr); + if (spi_mask == (NSH_SPI_MASK >> NSH_SPI_SHIFT)) { spi_mask = UINT32_MAX; } - uint8_t si = (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT; - uint8_t si_mask = (ntohl(mask->path_hdr) & NSH_SI_MASK) >> - NSH_SI_SHIFT; + uint8_t si = nsh_path_hdr_to_si(key->path_hdr); + uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr); format_uint8_masked(ds, &first, "flags", key->flags, mask->flags); + format_uint8_masked(ds, &first, "ttl", key->ttl, mask->ttl); format_uint8_masked(ds, &first, "mdtype", key->mdtype, mask->mdtype); format_uint8_masked(ds, &first, "np", key->np, mask->np); format_be32_masked(ds, &first, "spi", htonl(spi), htonl(spi_mask)); @@ -342,13 +342,14 @@ format_odp_push_nsh_action(struct ds *ds, const struct nsh_hdr *nsh_hdr) { size_t mdlen = nsh_hdr_len(nsh_hdr) - NSH_BASE_HDR_LEN; - uint32_t path_hdr = ntohl(get_16aligned_be32(&nsh_hdr->path_hdr)); - uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT; - uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT; + uint32_t spi = ntohl(nsh_get_spi(nsh_hdr)); + uint8_t si = nsh_get_si(nsh_hdr); uint8_t flags = nsh_get_flags(nsh_hdr); + uint8_t ttl = nsh_get_ttl(nsh_hdr); ds_put_cstr(ds, "push_nsh("); ds_put_format(ds, "flags=%d", flags); + ds_put_format(ds, ",ttl=%d", ttl); ds_put_format(ds, ",mdtype=%d", nsh_hdr->md_type); ds_put_format(ds, ",np=%d", nsh_hdr->next_proto); ds_put_format(ds, ",spi=0x%x", spi); @@ -1785,18 +1786,14 @@ find_end: } static void -nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh, +nsh_key_to_attr(struct ofpbuf *buf, const struct ovs_key_nsh *nsh, uint8_t * metadata, size_t md_size, bool is_mask) { size_t nsh_key_ofs; struct ovs_nsh_key_base base; - base.flags = nsh->flags; - base.mdtype = nsh->mdtype; - base.np = nsh->np; - base.path_hdr = htonl((ntohl(nsh->spi) << NSH_SPI_SHIFT) | - nsh->si); + memcpy(&base, nsh, sizeof(base)); nsh_key_ofs = nl_msg_start_nested(buf, OVS_KEY_ATTR_NSH); nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_BASE, &base, sizeof base); @@ -1831,8 +1828,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions) int n = 0; int ret = 0; uint32_t spi = 0; + uint8_t si = 255; uint32_t cd; - struct flow_nsh nsh; + struct ovs_key_nsh nsh; uint8_t metadata[NSH_CTX_HDRS_MAX_LEN]; uint8_t md_size = 0; @@ -1843,10 +1841,10 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions) /* The default is NSH_M_TYPE1 */ nsh.flags = 0; + nsh.ttl = 63; nsh.mdtype = NSH_M_TYPE1; nsh.np = NSH_P_ETHERNET; - nsh.spi = 0; - nsh.si = 255; + nsh.path_hdr = nsh_spi_si_to_path_hdr(0, 255); memset(nsh.context, 0, NSH_M_TYPE1_MDLEN); for (;;) { @@ -1858,6 +1856,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions) if (ovs_scan_len(s, &n, "flags=%"SCNi8, &nsh.flags)) { continue; } + if (ovs_scan_len(s, &n, "ttl=%"SCNi8, &nsh.ttl)) { + continue; + } if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &nsh.mdtype)) { switch (nsh.mdtype) { case NSH_M_TYPE1: @@ -1877,10 +1878,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions) continue; } if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &spi)) { - nsh.spi = htonl(spi); continue; } - if (ovs_scan_len(s, &n, "si=%"SCNi8, &nsh.si)) { + if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) { continue; } if (nsh.mdtype == NSH_M_TYPE1) { @@ -1925,6 +1925,7 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions) } out: if (ret >= 0) { + nsh.path_hdr = nsh_spi_si_to_path_hdr(spi, si); size_t offset = nl_msg_start_nested(actions, OVS_ACTION_ATTR_PUSH_NSH); nsh_key_to_attr(actions, &nsh, metadata, md_size, false); nl_msg_end_nested(actions, offset); @@ -2352,6 +2353,7 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr, const struct nlattr *a; bool unknown = false; uint8_t flags = 0; + uint8_t ttl = 63; size_t mdlen = 0; bool has_md1 = false; bool has_md2 = false; @@ -2373,6 +2375,7 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr, nsh_hdr->md_type = base->mdtype; put_16aligned_be32(&nsh_hdr->path_hdr, base->path_hdr); flags = base->flags; + ttl = base->ttl; break; } case OVS_NSH_KEY_ATTR_MD1: { @@ -2416,14 +2419,13 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr, } /* nsh header length = NSH_BASE_HDR_LEN + mdlen */ - nsh_hdr->ver_flags_ttl_len = htons(flags << NSH_FLAGS_SHIFT | - (NSH_BASE_HDR_LEN + mdlen) >> 2); + nsh_set_flags_ttl_len(nsh_hdr, flags, ttl, NSH_BASE_HDR_LEN + mdlen); return ODP_FIT_PERFECT; } enum odp_key_fitness -odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh) +odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh) { unsigned int left; const struct nlattr *a; @@ -2443,12 +2445,7 @@ odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh) 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; + memcpy(nsh, base, sizeof(*base)); break; } case OVS_NSH_KEY_ATTR_MD1: { @@ -3197,24 +3194,18 @@ format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr, case OVS_NSH_KEY_ATTR_UNSPEC: break; case OVS_NSH_KEY_ATTR_BASE: { - const struct ovs_nsh_key_base * base = nl_attr_get(a); - const struct ovs_nsh_key_base * base_mask + const struct ovs_nsh_key_base *base = nl_attr_get(a); + const struct ovs_nsh_key_base *base_mask = ma ? nl_attr_get(ma) : NULL; - nsh.flags = base->flags; - nsh.mdtype = base->mdtype; - nsh.np = base->np; - nsh.path_hdr = base->path_hdr; + memcpy(&nsh, base, sizeof(*base)); 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; + memcpy(&nsh_mask, base_mask, sizeof(*base_mask)); } break; } case OVS_NSH_KEY_ATTR_MD1: { - const struct ovs_nsh_key_md1 * md1 = nl_attr_get(a); - const struct ovs_nsh_key_md1 * md1_mask + const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a); + const struct ovs_nsh_key_md1 *md1_mask = ma ? nl_attr_get(ma) : NULL; memcpy(nsh.context, md1->context, sizeof md1->context); if (md1_mask) { @@ -4822,7 +4813,9 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key, if (strncmp(s, "nsh(", 4) == 0) { const char *start = s; int len; - struct flow_nsh skey, smask; + struct ovs_key_nsh skey, smask; + uint32_t spi = 0, spi_mask = 0; + uint8_t si = 0, si_mask = 0; s += 4; @@ -4863,7 +4856,7 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key, if (strncmp(s, "spi=", 4) == 0) { s += 4; - len = scan_be32(s, &skey.spi, mask ? &smask.spi : NULL); + len = scan_be32(s, &spi, mask ? &spi_mask : NULL); if (len == 0) { return -EINVAL; } @@ -4873,7 +4866,7 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key, if (strncmp(s, "si=", 3) == 0) { s += 3; - len = scan_u8(s, &skey.si, mask ? &smask.si : NULL); + len = scan_u8(s, &si, mask ? &si_mask : NULL); if (len == 0) { return -EINVAL; } @@ -4929,6 +4922,9 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key, return -EINVAL; } + skey.path_hdr = nsh_spi_si_to_path_hdr(spi, si); + smask.path_hdr = nsh_spi_si_to_path_hdr(spi_mask, si_mask); + nsh_key_to_attr(key, &skey, NULL, 0, false); if (mask) { nsh_key_to_attr(mask, &smask, NULL, 0, true); @@ -6990,59 +6986,29 @@ commit_set_nw_action(const struct flow *flow, struct flow *base, return 0; } -static void +static inline void get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask) { - nsh->flags = flow->nsh.flags; - nsh->mdtype = flow->nsh.mdtype; - nsh->np = flow->nsh.np; - nsh->path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) | - flow->nsh.si); - if (is_mask) { - for (int i = 0; i < 4; i++) { - nsh->context[i] = flow->nsh.context[i]; - } - } else { - switch (nsh->mdtype) { - case NSH_M_TYPE1: - for (int i = 0; i < 4; i++) { - nsh->context[i] = flow->nsh.context[i]; - } - break; - case NSH_M_TYPE2: - default: - /* No match support for other MD formats yet. */ - break; + memcpy(nsh, &flow->nsh, sizeof(*nsh)); + if (!is_mask) { + if (nsh->mdtype != NSH_M_TYPE1) { + memset(nsh, 0, sizeof(nsh->context)); } } } -static void +static inline void put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow, bool is_mask OVS_UNUSED) { - flow->nsh.flags = nsh->flags; - flow->nsh.mdtype = nsh->mdtype; - flow->nsh.np = nsh->np; - flow->nsh.spi = htonl((ntohl(nsh->path_hdr) & NSH_SPI_MASK) >> - NSH_SPI_SHIFT); - flow->nsh.si = (ntohl(nsh->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT; - switch (nsh->mdtype) { - case NSH_M_TYPE1: - for (int i = 0; i < 4; i++) { - flow->nsh.context[i] = nsh->context[i]; - } - break; - case NSH_M_TYPE2: - default: - /* No match support for other MD formats yet. */ - memset(flow->nsh.context, 0, sizeof flow->nsh.context); - break; + memcpy(&flow->nsh, nsh, sizeof(*nsh)); + if (flow->nsh.mdtype != NSH_M_TYPE1) { + memset(flow->nsh.context, 0, sizeof(flow->nsh.context)); } } static bool -commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set, +commit_nsh(const struct ovs_key_nsh * flow_nsh, bool use_masked_set, const struct ovs_key_nsh *key, struct ovs_key_nsh *base, struct ovs_key_nsh *mask, size_t size, struct ofpbuf *odp_actions) @@ -7067,15 +7033,8 @@ commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set, 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; + memcpy(&nsh_base, key, sizeof(nsh_base)); + memcpy(&nsh_base_mask, mask, sizeof(nsh_base_mask)); /* OVS_KEY_ATTR_NSH keys */ nsh_key_ofs = nl_msg_start_nested(odp_actions, OVS_KEY_ATTR_NSH); diff --git a/lib/odp-util.h b/lib/odp-util.h index f7ce206..fafea62 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -159,7 +159,7 @@ struct odputil_keybuf { enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, struct flow_tnl *); enum odp_key_fitness odp_nsh_key_from_attr(const struct nlattr *, - struct flow_nsh *); + struct ovs_key_nsh *); enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *, struct nsh_hdr *, size_t size); diff --git a/lib/packets.c b/lib/packets.c index 8ebae8c..4a3bee6 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -441,6 +441,7 @@ push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src) nsh = (struct nsh_hdr *) dp_packet_push_uninit(packet, length); memcpy(nsh, nsh_hdr_src, length); nsh->next_proto = next_proto; + nsh->md_type &= NSH_MDTYPE_MASK; packet->packet_type = htonl(PT_NSH); dp_packet_reset_offsets(packet); packet->l3_ofs = 0; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 4506150..bf8b060 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -5926,10 +5926,10 @@ rewrite_flow_push_nsh(struct xlate_ctx *ctx, /* Populate the flow with the new NSH header. */ flow->packet_type = htonl(PT_NSH); flow->dl_type = htons(ETH_TYPE_NSH); - flow->nsh.flags = 0; /* */ + flow->nsh.flags = 0; + flow->nsh.ttl = 63; flow->nsh.np = np; - flow->nsh.spi = 0; - flow->nsh.si = 255; + flow->nsh.path_hdr = htonl(255); if (md_type == NSH_M_TYPE1) { flow->nsh.mdtype = NSH_M_TYPE1; @@ -5942,6 +5942,7 @@ rewrite_flow_push_nsh(struct xlate_ctx *ctx, } else if (md_type == NSH_M_TYPE2) { flow->nsh.mdtype = NSH_M_TYPE2; } + flow->nsh.mdtype &= NSH_MDTYPE_MASK; return buf; } diff --git a/tests/nsh.at b/tests/nsh.at index bec6e87..6177cea 100644 --- a/tests/nsh.at +++ b/tests/nsh.at @@ -13,7 +13,7 @@ OVS_VSWITCHD_START([dnl add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) AT_DATA([flows.txt], [dnl - table=0,in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,actions=set_field:0x80->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,2 + table=0,in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,actions=set_field:0x2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,2 ]) AT_CHECK([ @@ -21,25 +21,25 @@ AT_CHECK([ ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ], [0], [dnl - in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:128->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2 + in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2 ]) AT_CHECK([ - ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00' + ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00' ], [0], [dnl -Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 +Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 bridge("br0") ------------- - 0. in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768 - set_field:128->nsh_flags + 0. in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768 + set_field:2->nsh_flags set_field:254->nsh_si set_field:0x44332211->nsh_c1 output:2 -Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=128,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 -Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 -Datapath actions: set(nsh(flags=128,spi=0x123456,si=254,c1=0x44332211)),2 +Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=2,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 +Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 +Datapath actions: set(nsh(flags=2,ttl=63,spi=0x123456,si=254,c1=0x44332211)),2 ]) OVS_VSWITCHD_STOP @@ -103,15 +103,15 @@ bridge("br0") decap() decap() -Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 +Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no -Datapath actions: push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1) +Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1) ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344' ], [0], [dnl -Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 +Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 bridge("br0") ------------- @@ -139,7 +139,7 @@ ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from non-dpdk interfaces: -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3) +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3) recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 ]) @@ -172,7 +172,7 @@ ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from non-dpdk interfaces: -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4) +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4) recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100,pcp=0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2 ]) @@ -230,15 +230,15 @@ bridge("br0") decap() decap() -Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 +Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no -Datapath actions: push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1) +Datapath actions: push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1) ]) AT_CHECK([ ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234' ], [0], [dnl -Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 +Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 bridge("br0") ------------- @@ -266,7 +266,7 @@ ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from non-dpdk interfaces: -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3) +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3) recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 ]) @@ -575,11 +575,12 @@ AT_CHECK([ ovs-appctl time/warp 1000 ovs-appctl time/warp 1000 +ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from non-dpdk interfaces: -recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789)) +recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789)) tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3000,si=255), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x1) tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x1),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6 ]) @@ -633,7 +634,7 @@ ovs-appctl time/warp 1000 AT_CHECK([ ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ], [0], [flow-dump from non-dpdk interfaces: -recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789)) +recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789)) tunnel(tun_id=0x0,src=20.0.0.1,dst=20.0.0.2,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(spi=0x3020,si=255), packets:1, bytes:108, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),set(nsh(spi=0x3020,si=254)),pop_eth,clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(src=30.0.0.2,dst=30.0.0.3)),tnl_pop(4789)) tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3020,si=254), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x2) tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x2),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6