From patchwork Wed Feb 21 22:19:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Tu X-Patchwork-Id: 876408 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=) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="p6M42HUy"; dkim-atps=neutral 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 3zmsbf0sQCz9sWF for ; Thu, 22 Feb 2018 09:29:18 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 38BCB1189; Wed, 21 Feb 2018 22:28:16 +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 E9B2C1152 for ; Wed, 21 Feb 2018 22:28:14 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-pg0-f65.google.com (mail-pg0-f65.google.com [74.125.83.65]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id BA73B477 for ; Wed, 21 Feb 2018 22:28:11 +0000 (UTC) Received: by mail-pg0-f65.google.com with SMTP id e11so1214890pgq.12 for ; Wed, 21 Feb 2018 14:28:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=Dn8mWKn803jTRmfMcGJ7OeuYHzH6q2qolwrM/uWWRiE=; b=p6M42HUyDX+P6bjHPXbjvRU4Tp0QlladCfJ3QBFQq3OLDWQUsAvAgQ4QakBsWxjka+ lg3oTw8mlqmUY4YqjRXcJ9tFNC5rrmWm4ml5KqWiUL5s6TBlhHdpT4XXATqyvXxCOgGS HNRIZHNZwK0QNgI43u0xZwsVsEZqe7uZoqIFpZM+LlFHX/Dhmu/bh6qTc79SIqQiqeFM 5QHJk/QtkYq3WvtGztqqLvq1prhkgVmI7VwdpYlotqYfSU/K8yjB0TqIGPZIPS6vR9+1 5w+k/RmX0ZFbF9gOP+M0z7djIkEpVFRo7jWvvUPcim7h2Cb9ivouaW1TBQitvxKwYONr fZHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=Dn8mWKn803jTRmfMcGJ7OeuYHzH6q2qolwrM/uWWRiE=; b=qlcBctfvO8mZ6yprAO0rUqJIMbfgzVj1UjuV+9F/ayXEtghg+LP+1/lSIo89cgghOP HSGhhDaQvi1cD9q0xyWZKAEwjXlHEMmsuTbR0qYrBKknu4fvWx5jw4k8K9wa5sFwPFY7 AYc/urPzBHmzhWmjDeFGJBlHBr0K4GT8Hg4w7nqU9iEMx6KIgqa+7pQxe/mJVNOVhZ2H ZK6UTSl9MlP4BUm9QKPqJx3PBDP+zR8DUuOfG2CDTZ5/Jx/Sp8Gha4QJAFhf4FXw/0k5 ooVgMO601Tx/F5KNd28uz5/OgG+njTNoE1C58fYn9hPQEVfB0Y0VWVPL8tm5SW2LfiHY VazA== X-Gm-Message-State: APf1xPD1wgfLhuysRU+2RbS8UqD2OPcbHM6qPPCjpI4bWOjgt5RVee2M 6b/d6z88EL6cJopEEPakzVAz6x3k X-Google-Smtp-Source: AH8x226eQAW2v5AFXtF5asvzHVwNy7qC5q41Vzm4F7nlLsxfxYHhnpLey6YyxwQ7bzehyBRUl1761w== X-Received: by 10.101.66.193 with SMTP id l1mr3840271pgp.57.1519252090325; Wed, 21 Feb 2018 14:28:10 -0800 (PST) Received: from sc9-mailhost2.vmware.com ([208.91.1.34]) by smtp.gmail.com with ESMTPSA id f80sm70159198pfa.176.2018.02.21.14.28.09 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 21 Feb 2018 14:28:09 -0800 (PST) From: William Tu To: dev@openvswitch.org Date: Wed, 21 Feb 2018 14:19:31 -0800 Message-Id: <1519251571-109146-3-git-send-email-u9012063@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1519251571-109146-1-git-send-email-u9012063@gmail.com> References: <1519251571-109146-1-git-send-email-u9012063@gmail.com> X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE autolearn=no version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH RFC 2/2] tunnel: add erspan support for kernel datapath. 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 ERSPAN is a tunneling protocol based on GRE tunnel. The patch add erspan tunnel support for ovs-vswitchd with kernel datapath. Configuring erspan tunnel is similar to gre tunnel, but with additional erspan's parameters. Matching a flow on erspan's metadata is also supported, see ovs-fields for more details. Signed-off-by: William Tu --- datapath/linux/compat/include/linux/openvswitch.h | 2 + include/openvswitch/flow.h | 4 +- include/openvswitch/match.h | 8 ++ include/openvswitch/meta-flow.h | 56 ++++++++ include/openvswitch/packets.h | 6 +- lib/dpif-netlink-rtnl.c | 5 + lib/dpif-netlink.c | 5 + lib/flow.c | 32 +++-- lib/flow.h | 2 +- lib/match.c | 66 +++++++++- lib/meta-flow.c | 77 +++++++++++ lib/meta-flow.xml | 86 ++++++++++++ lib/netdev-vport.c | 23 ++++ lib/netdev.h | 5 + lib/nx-match.c | 13 +- lib/odp-util.c | 151 ++++++++++++++++++++++ lib/odp-util.h | 2 +- lib/ofp-match.c | 2 +- lib/packets.h | 91 +++++++++++++ ofproto/ofproto-dpif-rid.h | 2 +- ofproto/ofproto-dpif-xlate.c | 3 +- ofproto/tunnel.c | 13 ++ tests/odp.at | 25 +++- tests/ofproto.at | 6 +- tests/system-common-macros.at | 5 + tests/system-traffic.at | 70 ++++++++++ tests/tunnel.at | 105 +++++++++++++++ vswitchd/vswitch.xml | 34 +++++ 28 files changed, 879 insertions(+), 20 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 84ebcaf579b6..efb37c875e87 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -238,6 +238,7 @@ enum ovs_vport_type { OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ + OVS_VPORT_TYPE_ERSPAN = 107, /* ERSPAN tunnel. */ __OVS_VPORT_TYPE_MAX }; @@ -395,6 +396,7 @@ enum ovs_tunnel_key_attr { OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ OVS_TUNNEL_KEY_ATTR_PAD, + OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, /* struct erspan_metadata. */ __OVS_TUNNEL_KEY_ATTR_MAX }; diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h index cd61ffff0289..5d2cf09680f1 100644 --- a/include/openvswitch/flow.h +++ b/include/openvswitch/flow.h @@ -27,7 +27,7 @@ extern "C" { /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ -#define FLOW_WC_SEQ 40 +#define FLOW_WC_SEQ 41 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 16 @@ -166,7 +166,7 @@ BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) % sizeof(uint64_t) == 0); /* 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 ovs_key_nsh) + 300 - && FLOW_WC_SEQ == 40); + && FLOW_WC_SEQ == 41); /* Incremental points at which flow classification may be performed in * segments. diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h index 61a67de2c413..ff5b9c80e966 100644 --- a/include/openvswitch/match.h +++ b/include/openvswitch/match.h @@ -109,6 +109,14 @@ void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16 void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id); void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask); void match_set_tun_gbp_flags(struct match *match, uint8_t flags); +void match_set_tun_erspan_ver(struct match *match, uint8_t ver); +void match_set_tun_erspan_ver_masked(struct match *match, uint8_t ver, uint8_t mask); +void match_set_tun_erspan_idx(struct match *match, uint32_t idx); +void match_set_tun_erspan_idx_masked(struct match *match, uint32_t idx, uint32_t mask); +void match_set_tun_erspan_dir(struct match *match, uint8_t dir); +void match_set_tun_erspan_dir_masked(struct match *match, uint8_t dir, uint8_t mask); +void match_set_tun_erspan_hwid(struct match *match, uint8_t hwid); +void match_set_tun_erspan_hwid_masked(struct match *match, uint8_t hwid, uint8_t mask); void match_set_in_port(struct match *, ofp_port_t ofp_port); void match_set_pkt_mark(struct match *, uint32_t pkt_mark); void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask); diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h index 98c9e1cb5a76..69eb14715772 100644 --- a/include/openvswitch/meta-flow.h +++ b/include/openvswitch/meta-flow.h @@ -450,6 +450,62 @@ enum OVS_PACKED_ENUM mf_field_id { */ MFF_TUN_GBP_FLAGS, + /* "tun_erspan_idx". + * + * ERSPAN index (direction/port number) + * + * Type: be32. + * Maskable: bitwise. + * Formatting: hexadecimal. + * Prerequisites: none. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_ET_ERSPAN_IDX(11) since OF1.5 and v2.9. + */ + MFF_TUN_ERSPAN_IDX, + + /* "tun_erspan_ver". + * + * ERSPAN vsersion (v1 / v2) + * + * Type: u8. + * Maskable: bitwise. + * Formatting: decimal. + * Prerequisites: none. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_ET_ERSPAN_VER(12) since OF1.5 and v2.9. + */ + MFF_TUN_ERSPAN_VER, + + /* "tun_erspan_dir". + * + * ERSPAN mirrored traffic's direction + * + * Type: u8. + * Maskable: bitwise. + * Formatting: decimal. + * Prerequisites: none. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_ET_ERSPAN_DIR(13) since OF1.5 and v2.9. + */ + MFF_TUN_ERSPAN_DIR, + + /* "tun_erspan_hwid". + * + * ERSPAN Hardware ID + * + * Type: u8. + * Maskable: bitwise. + * Formatting: hexadecimal. + * Prerequisites: none. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_ET_ERSPAN_HWID(14) since OF1.5 and v2.9. + */ + MFF_TUN_ERSPAN_HWID, + #if TUN_METADATA_NUM_OPTS == 64 /* "tun_metadata". * diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h index fef756b72e57..7bcb9b58410c 100644 --- a/include/openvswitch/packets.h +++ b/include/openvswitch/packets.h @@ -39,7 +39,11 @@ struct flow_tnl { ovs_be16 tp_dst; ovs_be16 gbp_id; uint8_t gbp_flags; - uint8_t pad1[5]; /* Pad to 64 bits. */ + uint32_t erspan_idx; + uint8_t erspan_ver; + uint8_t erspan_dir; + uint8_t erspan_hwid; + uint8_t pad1[6]; /* Pad to 64 bits. */ struct tun_metadata metadata; }; diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c index 40c456951827..f26e6206973b 100644 --- a/lib/dpif-netlink-rtnl.c +++ b/lib/dpif-netlink-rtnl.c @@ -99,6 +99,8 @@ vport_type_to_kind(enum ovs_vport_type type, } case OVS_VPORT_TYPE_GENEVE: return "geneve"; + case OVS_VPORT_TYPE_ERSPAN: + return "erspan"; case OVS_VPORT_TYPE_NETDEV: case OVS_VPORT_TYPE_INTERNAL: case OVS_VPORT_TYPE_LISP: @@ -253,6 +255,7 @@ dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg, err = dpif_netlink_rtnl_vxlan_verify(tnl_cfg, kind, reply); break; case OVS_VPORT_TYPE_GRE: + case OVS_VPORT_TYPE_ERSPAN: err = dpif_netlink_rtnl_gre_verify(tnl_cfg, kind, reply); break; case OVS_VPORT_TYPE_GENEVE: @@ -316,6 +319,7 @@ dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg, nl_msg_put_be16(&request, IFLA_VXLAN_PORT, tnl_cfg->dst_port); break; case OVS_VPORT_TYPE_GRE: + case OVS_VPORT_TYPE_ERSPAN: nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA); break; case OVS_VPORT_TYPE_GENEVE: @@ -433,6 +437,7 @@ dpif_netlink_rtnl_port_destroy(const char *name, const char *type) case OVS_VPORT_TYPE_VXLAN: case OVS_VPORT_TYPE_GRE: case OVS_VPORT_TYPE_GENEVE: + case OVS_VPORT_TYPE_ERSPAN: return dpif_netlink_rtnl_destroy(name); case OVS_VPORT_TYPE_NETDEV: case OVS_VPORT_TYPE_INTERNAL: diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 8543a2bbe462..fc81d9d755b5 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -786,6 +786,9 @@ get_vport_type(const struct dpif_netlink_vport *vport) case OVS_VPORT_TYPE_STT: return "stt"; + case OVS_VPORT_TYPE_ERSPAN: + return "erspan"; + case OVS_VPORT_TYPE_UNSPEC: case __OVS_VPORT_TYPE_MAX: break; @@ -813,6 +816,8 @@ netdev_to_ovs_vport_type(const char *type) return OVS_VPORT_TYPE_VXLAN; } else if (!strcmp(type, "lisp")) { return OVS_VPORT_TYPE_LISP; + } else if (!strcmp(type, "erspan")) { + return OVS_VPORT_TYPE_ERSPAN; } else { return OVS_VPORT_TYPE_UNSPEC; } diff --git a/lib/flow.c b/lib/flow.c index 38ff29c8cd14..82182285131d 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -126,7 +126,7 @@ struct mf_ctx { * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are * defined as macros. */ -#if (FLOW_WC_SEQ != 40) +#if (FLOW_WC_SEQ != 41) #define MINIFLOW_ASSERT(X) ovs_assert(X) BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime " "assertions enabled. Consider updating FLOW_WC_SEQ after " @@ -1014,7 +1014,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); match_init_catchall(flow_metadata); if (flow->tunnel.tun_id != htonll(0)) { @@ -1042,6 +1042,18 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) if (flow->tunnel.gbp_flags) { match_set_tun_gbp_flags(flow_metadata, flow->tunnel.gbp_flags); } + if (flow->tunnel.erspan_ver) { + match_set_tun_erspan_ver(flow_metadata, flow->tunnel.erspan_ver); + } + if (flow->tunnel.erspan_idx) { + match_set_tun_erspan_idx(flow_metadata, flow->tunnel.erspan_idx); + } + if (flow->tunnel.erspan_dir) { + match_set_tun_erspan_dir(flow_metadata, flow->tunnel.erspan_dir); + } + if (flow->tunnel.erspan_hwid) { + match_set_tun_erspan_hwid(flow_metadata, flow->tunnel.erspan_hwid); + } tun_metadata_get_fmd(&flow->tunnel, flow_metadata); if (flow->metadata != htonll(0)) { match_set_metadata(flow_metadata, flow->metadata); @@ -1581,7 +1593,7 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc, memset(&wc->masks, 0x0, sizeof wc->masks); /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); if (flow_tnl_dst_is_set(&flow->tunnel)) { if (flow->tunnel.flags & FLOW_TNL_F_KEY) { @@ -1598,6 +1610,10 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc, WC_MASK_FIELD(wc, tunnel.tp_dst); WC_MASK_FIELD(wc, tunnel.gbp_id); WC_MASK_FIELD(wc, tunnel.gbp_flags); + WC_MASK_FIELD(wc, tunnel.erspan_ver); + WC_MASK_FIELD(wc, tunnel.erspan_idx); + WC_MASK_FIELD(wc, tunnel.erspan_dir); + WC_MASK_FIELD(wc, tunnel.erspan_hwid); if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) { if (flow->tunnel.metadata.present.map) { @@ -1728,7 +1744,7 @@ void flow_wc_map(const struct flow *flow, struct flowmap *map) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); flowmap_init(map); @@ -1829,7 +1845,7 @@ void flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); memset(&wc->masks.regs, 0, sizeof wc->masks.regs); @@ -1973,7 +1989,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards *wc, int idx, uint32_t miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); uint32_t hash = basis; if (flow) { @@ -2020,7 +2036,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst); uint32_t flow_hash_5tuple(const struct flow *flow, uint32_t basis) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); uint32_t hash = basis; if (flow) { @@ -2609,7 +2625,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, if (clear_flow_L3) { /* Clear all L3 and L4 fields and dp_hash. */ - BUILD_ASSERT(FLOW_WC_SEQ == 40); + BUILD_ASSERT(FLOW_WC_SEQ == 41); memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); flow->dp_hash = 0; diff --git a/lib/flow.h b/lib/flow.h index 770a07a62778..2b96dc9a6b5c 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -917,7 +917,7 @@ static inline void pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow) { /* Update this function whenever struct flow changes. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); md->recirc_id = flow->recirc_id; md->dp_hash = flow->dp_hash; diff --git a/lib/match.c b/lib/match.c index 97a5282997b0..27dddd874af7 100644 --- a/lib/match.c +++ b/lib/match.c @@ -320,6 +320,58 @@ match_set_tun_gbp_flags(struct match *match, uint8_t flags) } void +match_set_tun_erspan_ver_masked(struct match *match, uint8_t ver, uint8_t mask) +{ + match->wc.masks.tunnel.erspan_ver = ver; + match->flow.tunnel.erspan_ver = ver & mask; +} + +void +match_set_tun_erspan_ver(struct match *match, uint8_t ver) +{ + match_set_tun_erspan_ver_masked(match, ver, UINT8_MAX); +} + +void +match_set_tun_erspan_idx_masked(struct match *match, uint32_t erspan_idx, uint32_t mask) +{ + match->wc.masks.tunnel.erspan_idx = mask; + match->flow.tunnel.erspan_idx = erspan_idx & mask; +} + +void +match_set_tun_erspan_idx(struct match *match, uint32_t erspan_idx) +{ + match_set_tun_erspan_idx_masked(match, erspan_idx, UINT32_MAX); +} + +void +match_set_tun_erspan_dir_masked(struct match *match, uint8_t dir, uint8_t mask) +{ + match->wc.masks.tunnel.erspan_dir = dir; + match->flow.tunnel.erspan_dir = dir & mask; +} + +void +match_set_tun_erspan_dir(struct match *match, uint8_t dir) +{ + match_set_tun_erspan_dir_masked(match, dir, UINT8_MAX); +} + +void +match_set_tun_erspan_hwid_masked(struct match *match, uint8_t hwid, uint8_t mask) +{ + match->wc.masks.tunnel.erspan_hwid = hwid; + match->flow.tunnel.erspan_hwid = hwid & mask; +} + +void +match_set_tun_erspan_hwid(struct match *match, uint8_t hwid) +{ + match_set_tun_erspan_hwid_masked(match, hwid, UINT8_MAX); +} + +void match_set_in_port(struct match *match, ofp_port_t ofp_port) { match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX); @@ -1232,6 +1284,18 @@ format_flow_tunnel(struct ds *s, const struct match *match) if (wc->masks.tunnel.ip_ttl) { ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl); } + if (wc->masks.tunnel.erspan_ver) { + ds_put_format(s, "erspan_ver=%"PRIu8",", tnl->erspan_ver); + } + if (wc->masks.tunnel.erspan_idx && tnl->erspan_ver == 1) { + ds_put_format(s, "erspan_idx=%#"PRIx32",", tnl->erspan_idx); + } + if (wc->masks.tunnel.erspan_dir && tnl->erspan_ver == 2) { + ds_put_format(s, "erspan_dir=%"PRIu8",", tnl->erspan_dir); + } + if (wc->masks.tunnel.erspan_hwid && tnl->erspan_ver == 2) { + ds_put_format(s, "erspan_hwid=%#"PRIx8",", tnl->erspan_hwid); + } if (wc->masks.tunnel.flags & FLOW_TNL_F_MASK) { format_flags_masked(s, "tun_flags", flow_tun_flag_to_string, tnl->flags & FLOW_TNL_F_MASK, @@ -1303,7 +1367,7 @@ match_format(const struct match *match, bool is_megaflow = false; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "%spriority=%s%d,", diff --git a/lib/meta-flow.c b/lib/meta-flow.c index aa2ec012d671..8b8d174c163b 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -231,6 +231,14 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) return !wc->masks.tunnel.gbp_id; case MFF_TUN_GBP_FLAGS: return !wc->masks.tunnel.gbp_flags; + case MFF_TUN_ERSPAN_VER: + return !wc->masks.tunnel.erspan_ver; + case MFF_TUN_ERSPAN_IDX: + return !wc->masks.tunnel.erspan_idx; + case MFF_TUN_ERSPAN_DIR: + return !wc->masks.tunnel.erspan_dir; + case MFF_TUN_ERSPAN_HWID: + return !wc->masks.tunnel.erspan_hwid; CASE_MFF_TUN_METADATA: return !ULLONG_GET(wc->masks.tunnel.metadata.present.map, mf->id - MFF_TUN_METADATA0); @@ -513,6 +521,10 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_TUN_TTL: case MFF_TUN_GBP_ID: case MFF_TUN_GBP_FLAGS: + case MFF_TUN_ERSPAN_IDX: + case MFF_TUN_ERSPAN_VER: + case MFF_TUN_ERSPAN_DIR: + case MFF_TUN_ERSPAN_HWID: CASE_MFF_TUN_METADATA: case MFF_METADATA: case MFF_IN_PORT: @@ -680,6 +692,18 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, case MFF_TUN_TOS: value->u8 = flow->tunnel.ip_tos; break; + case MFF_TUN_ERSPAN_VER: + value->u8 = flow->tunnel.erspan_ver; + break; + case MFF_TUN_ERSPAN_IDX: + value->be32 = htonl(flow->tunnel.erspan_idx); + break; + case MFF_TUN_ERSPAN_DIR: + value->u8 = flow->tunnel.erspan_dir; + break; + case MFF_TUN_ERSPAN_HWID: + value->u8 = flow->tunnel.erspan_hwid; + break; CASE_MFF_TUN_METADATA: tun_metadata_read(&flow->tunnel, mf, value); break; @@ -994,6 +1018,18 @@ mf_set_value(const struct mf_field *mf, case MFF_TUN_TTL: match_set_tun_ttl(match, value->u8); break; + case MFF_TUN_ERSPAN_VER: + match_set_tun_erspan_ver(match, value->u8); + break; + case MFF_TUN_ERSPAN_IDX: + match_set_tun_erspan_idx(match, ntohl(value->be32)); + break; + case MFF_TUN_ERSPAN_DIR: + match_set_tun_erspan_dir(match, value->u8); + break; + case MFF_TUN_ERSPAN_HWID: + match_set_tun_erspan_hwid(match, value->u8); + break; CASE_MFF_TUN_METADATA: tun_metadata_set_match(mf, value, NULL, match, err_str); break; @@ -1391,6 +1427,18 @@ mf_set_flow_value(const struct mf_field *mf, case MFF_TUN_TTL: flow->tunnel.ip_ttl = value->u8; break; + case MFF_TUN_ERSPAN_VER: + flow->tunnel.erspan_ver = value->u8; + break; + case MFF_TUN_ERSPAN_IDX: + flow->tunnel.erspan_idx = ntohl(value->be32); + break; + case MFF_TUN_ERSPAN_DIR: + flow->tunnel.erspan_dir = value->u8; + break; + case MFF_TUN_ERSPAN_HWID: + flow->tunnel.erspan_hwid = value->u8; + break; CASE_MFF_TUN_METADATA: tun_metadata_write(&flow->tunnel, mf, value); break; @@ -1700,6 +1748,10 @@ mf_is_pipeline_field(const struct mf_field *mf) case MFF_TUN_FLAGS: case MFF_TUN_GBP_ID: case MFF_TUN_GBP_FLAGS: + case MFF_TUN_ERSPAN_VER: + case MFF_TUN_ERSPAN_IDX: + case MFF_TUN_ERSPAN_DIR: + case MFF_TUN_ERSPAN_HWID: CASE_MFF_TUN_METADATA: case MFF_METADATA: case MFF_IN_PORT: @@ -1876,6 +1928,18 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) case MFF_TUN_TTL: match_set_tun_ttl_masked(match, 0, 0); break; + case MFF_TUN_ERSPAN_VER: + match_set_tun_erspan_ver_masked(match, 0, 0); + break; + case MFF_TUN_ERSPAN_IDX: + match_set_tun_erspan_idx_masked(match, 0, 0); + break; + case MFF_TUN_ERSPAN_DIR: + match_set_tun_erspan_dir_masked(match, 0, 0); + break; + case MFF_TUN_ERSPAN_HWID: + match_set_tun_erspan_hwid_masked(match, 0, 0); + break; CASE_MFF_TUN_METADATA: tun_metadata_set_match(mf, NULL, NULL, match, err_str); break; @@ -2256,6 +2320,19 @@ mf_set(const struct mf_field *mf, case MFF_TUN_TOS: match_set_tun_tos_masked(match, value->u8, mask->u8); break; + case MFF_TUN_ERSPAN_VER: + match_set_tun_erspan_ver_masked(match, value->u8, mask->u8); + break; + case MFF_TUN_ERSPAN_IDX: + match_set_tun_erspan_idx_masked(match, ntohl(value->be32), + ntohl(mask->be32)); + break; + case MFF_TUN_ERSPAN_DIR: + match_set_tun_erspan_dir_masked(match, value->u8, mask->u8); + break; + case MFF_TUN_ERSPAN_HWID: + match_set_tun_erspan_hwid_masked(match, value->u8, mask->u8); + break; CASE_MFF_TUN_METADATA: tun_metadata_set_match(mf, value, mask, match, err_str); break; diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml index 933d4b86b090..7b3bb61935e9 100644 --- a/lib/meta-flow.xml +++ b/lib/meta-flow.xml @@ -1456,6 +1456,7 @@ ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
  • LISP has a 24-bit instance ID.
  • GRE has an optional 32-bit key.
  • STT has a 64-bit key.
  • +
  • ERSPAN has a 10-bit key (Session ID).
  • @@ -1715,6 +1716,91 @@ ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1' +

    ERSPAN Metadata Fields FIXME

    +

    + These fields provide access to features in the ERSPAN tunneling + protocol. The ERSPAN header is defined in the draft +

    + +

    + ERSPAN version number. 1 for version 1 (type II) + or 2 for version 2 (type III). +

    +
    + +

    ERSPAN version 1 header format:

    + +
    + + + +
    +
    + + + + + +
    +
    + + + +
    + +
    + +

    ERSPAN has a fixed 8-byte GRE header, including 4-byte GRE base header + another 4-byte sequence number. The ERSPAN's 10-bit session ID holds + the tunnel ID. +

    + +

    + ERSPAN index is a 20-bit index/port number associated with the ERSPAN + traffic's source port and direction (ingress/egress). This field is + platform dependent. +

    +
    + +

    ERSPAN version 2 header format:

    + +
    + + + +
    +
    + + + + + + + + +
    +
    + + + +
    + +
    + + +

    + Specifies the ERSPAN v2 mirrored traffic's direction. + Value 1 for egress traffic, and value 0 for ingress traffic. +

    +
    + +

    + ERSPAN hardware ID is a 6-bit unique identifier of an + ERSPAN v2 engine within a system. +

    +
    +

    Geneve Fields

    diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 52aa12d79933..bad341be17a2 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -539,6 +539,14 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) } else if (!strcmp(node->key, "egress_pkt_mark")) { tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10); tnl_cfg.set_egress_pkt_mark = true; + } else if (!strcmp(node->key, "erspan_idx")) { + tnl_cfg.erspan_idx = atoi(node->value); + } else if (!strcmp(node->key, "erspan_ver")) { + tnl_cfg.erspan_ver = atoi(node->value); + } else if (!strcmp(node->key, "erspan_dir")) { + tnl_cfg.erspan_dir = atoi(node->value); + } else if (!strcmp(node->key, "erspan_hwid")) { + tnl_cfg.erspan_hwid = atoi(node->value); } else { ds_put_format(&errors, "%s: unknown %s argument '%s'\n", name, type, node->key); @@ -725,6 +733,20 @@ get_tunnel_config(const struct netdev *dev, struct smap *args) smap_add_format(args, "egress_pkt_mark", "%"PRIu32, tnl_cfg.egress_pkt_mark); } + + if (tnl_cfg.erspan_idx) { + smap_add_format(args, "erspan_idx", "0x%x", tnl_cfg.erspan_idx); + } + if (tnl_cfg.erspan_ver) { + smap_add_format(args, "erspan_ver", "%d", tnl_cfg.erspan_ver); + } + if (tnl_cfg.erspan_dir) { + smap_add_format(args, "erspan_dir", "%d", tnl_cfg.erspan_dir); + } + if (tnl_cfg.erspan_hwid) { + smap_add_format(args, "erspan_hwid", "0x%x", tnl_cfg.erspan_hwid); + } + return 0; } @@ -979,6 +1001,7 @@ netdev_vport_tunnel_register(void) NETDEV_VPORT_GET_IFINDEX), TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL), TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL), + TUNNEL_CLASS("erspan", "erspan_sys", NULL, NULL, NULL, NULL), }; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; diff --git a/lib/netdev.h b/lib/netdev.h index ff1b604b24e2..4f64d72e49d2 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -127,6 +127,11 @@ struct netdev_tunnel_config { bool csum; bool dont_fragment; enum netdev_pt_mode pt_mode; + + uint32_t erspan_idx; + uint8_t erspan_ver; + uint8_t erspan_dir; + uint8_t erspan_hwid; }; void netdev_run(void); diff --git a/lib/nx-match.c b/lib/nx-match.c index 005c4a6ac09d..9a5ccabe7c55 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1027,7 +1027,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); struct nxm_put_ctx ctx = { .output = b, .implied_ethernet = false }; @@ -1156,6 +1156,17 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags); tun_metadata_to_nx_match(b, oxm, match); + /* ERSPAN */ + nxm_put_32m(&ctx, MFF_TUN_ERSPAN_IDX, oxm, + htonl(flow->tunnel.erspan_idx), + htonl(match->wc.masks.tunnel.erspan_idx)); + nxm_put_8m(&ctx, MFF_TUN_ERSPAN_VER, oxm, + flow->tunnel.erspan_ver, match->wc.masks.tunnel.erspan_ver); + nxm_put_8m(&ctx, MFF_TUN_ERSPAN_DIR, oxm, + flow->tunnel.erspan_dir, match->wc.masks.tunnel.erspan_dir); + nxm_put_8m(&ctx, MFF_TUN_ERSPAN_HWID, oxm, + flow->tunnel.erspan_hwid, match->wc.masks.tunnel.erspan_hwid); + /* Network Service Header */ nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags, match->wc.masks.nsh.flags); diff --git a/lib/odp-util.c b/lib/odp-util.c index 5da83b4c64c4..cc1d4c751c6e 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -2375,6 +2375,7 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + .next_max = OVS_VXLAN_EXT_MAX}, [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 }, [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 }, + [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = ATTR_LEN_VARIABLE }, }; const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = { @@ -2698,6 +2699,23 @@ odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask, case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: tun_metadata_from_geneve_nlattr(a, is_mask, tun); break; + case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS: { + int attr_len = nl_attr_get_size(a); + struct erspan_metadata opts; + + memcpy(&opts, nl_attr_get(a), attr_len); + + tun->erspan_ver = opts.version; + if (tun->erspan_ver == 1) { + tun->erspan_idx = ntohl(opts.u.index); + } else if (tun->erspan_ver == 2) { + tun->erspan_dir = opts.u.md2.dir; + tun->erspan_hwid = get_hwid(&opts.u.md2); + } else { + VLOG_WARN("%s invalid erspan version\n", __func__); + } + break; + } default: /* Allow this to show up as unexpected, if there are unknown @@ -2776,6 +2794,19 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key, nl_msg_end_nested(a, vxlan_opts_ofs); } tun_metadata_to_geneve_nlattr(tun_key, tun_flow_key, key_buf, a); + if (tun_key->erspan_ver) { + struct erspan_metadata opts; + + opts.version = tun_key->erspan_ver; + if (opts.version == 1) { + opts.u.index = htonl(tun_key->erspan_idx); + } else { + opts.u.md2.dir = tun_key->erspan_dir; + set_hwid(&opts.u.md2, tun_key->erspan_hwid); + } + nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, + &opts, sizeof(opts)); + } nl_msg_end_nested(a, tun_key_ofs); } @@ -3232,6 +3263,46 @@ format_odp_tun_vxlan_opt(const struct nlattr *attr, ofpbuf_uninit(&ofp); } +static void +format_odp_tun_erspan_opt(const struct nlattr *attr, + const struct nlattr *mask_attr, struct ds *ds, + bool verbose) +{ + const struct erspan_metadata *opts, *mask; + uint8_t ver, ver_ma, dir, dir_ma, hwid, hwid_ma; + + opts = nl_attr_get(attr); + mask = mask_attr ? nl_attr_get(mask_attr) : NULL; + + ver = (uint8_t)opts->version; + if (mask) { + ver_ma = (uint8_t)mask->version; + } + + format_u8u(ds, "ver", ver, mask ? &ver_ma : NULL, verbose); + + if (opts->version == 1) { + if (mask) { + ds_put_format(ds, "idx=%#"PRIx32"/%#"PRIx32",", + ntohl(opts->u.index), + ntohl(mask->u.index)); + } else { + ds_put_format(ds, "idx=%#"PRIx32",", ntohl(opts->u.index)); + } + } else if (opts->version == 2) { + dir = opts->u.md2.dir; + hwid = opts->u.md2.hwid; + if (mask) { + dir_ma = mask->u.md2.dir; + hwid_ma = mask->u.md2.hwid; + } + + format_u8u(ds, "dir", dir, mask ? &dir_ma : NULL, verbose); + format_u8x(ds, "hwid", hwid, mask ? &hwid_ma : NULL, verbose); + } + ds_chomp(ds, ','); +} + #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL static void @@ -3479,6 +3550,11 @@ format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr, break; case OVS_TUNNEL_KEY_ATTR_PAD: break; + case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS: + ds_put_cstr(ds, "erspan("); + format_odp_tun_erspan_opt(a, ma, ds, verbose); + ds_put_cstr(ds, "),"); + break; case __OVS_TUNNEL_KEY_ATTR_MAX: default: format_unknown_key(ds, a, ma); @@ -4632,6 +4708,70 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask) } static int +scan_erspan_metadata(const char *s, + struct erspan_metadata *key, + struct erspan_metadata *mask) +{ + const char *s_base = s; + uint32_t idx = 0, idx_mask = 0; + uint8_t ver = 0, dir = 0, hwid = 0; + uint8_t ver_mask = 0, dir_mask = 0, hwid_mask = 0; + + if (!strncmp(s, "ver=", 4)) { + s += 4; + s += scan_u8(s, &ver, mask ? &ver_mask : NULL); + } + + if (s[0] == ',') { + s++; + } + + if (ver == 1) { + if (!strncmp(s, "idx=", 4)) { + s += 4; + s += scan_u32(s, &idx, mask ? &idx_mask : NULL); + } + + if (!strncmp(s, ")", 1)) { + s += 1; + key->version = ver; + key->u.index = htonl(idx); + if (mask) { + mask->u.index = htonl(idx_mask); + } + } + return s - s_base; + + } else if (ver == 2) { + if (!strncmp(s, "dir=", 4)) { + s += 4; + s += scan_u8(s, &dir, mask ? &dir_mask : NULL); + } + if (s[0] == ',') { + s++; + } + if (!strncmp(s, "hwid=", 5)) { + s += 5; + s += scan_u8(s, &hwid, mask ? &hwid_mask : NULL); + } + + if (!strncmp(s, ")", 1)) { + s += 1; + key->version = ver; + key->u.md2.hwid = hwid; + key->u.md2.dir = dir; + if (mask) { + mask->u.md2.hwid = hwid_mask; + mask->u.md2.dir = dir_mask; + } + } + return s - s_base; + } + + return 0; +} + +static int scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask) { const char *s_base = s; @@ -4766,6 +4906,15 @@ geneve_to_attr(struct ofpbuf *a, const void *data_) geneve->len); } +static void +erspan_to_attr(struct ofpbuf *a, const void *data_) +{ + const struct erspan_metadata *md = data_; + + nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, md, + sizeof *md); +} + #define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC) \ { \ unsigned long call_fn = (unsigned long)FUNC; \ @@ -5134,6 +5283,8 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, SCAN_FIELD_NESTED("ttl=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_TTL); SCAN_FIELD_NESTED("tp_src=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_SRC); SCAN_FIELD_NESTED("tp_dst=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_DST); + SCAN_FIELD_NESTED_FUNC("erspan(", struct erspan_metadata, erspan_metadata, + erspan_to_attr); SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr); SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve, geneve_to_attr); diff --git a/lib/odp-util.h b/lib/odp-util.h index 1fad159db9fb..6d5538f7f022 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -147,7 +147,7 @@ void odp_portno_name_format(const struct hmap *portno_names, * add another field and forget to adjust this value. */ #define ODPUTIL_FLOW_KEY_BYTES 640 -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow * key. An array of "struct nlattr" might not, in theory, be sufficiently diff --git a/lib/ofp-match.c b/lib/ofp-match.c index d1031f6aaa3e..58591521a4db 100644 --- a/lib/ofp-match.c +++ b/lib/ofp-match.c @@ -65,7 +65,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/lib/packets.h b/lib/packets.h index 9a71aa3abbdb..bf6d38298f85 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -404,6 +404,8 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, #define ETH_TYPE_MPLS 0x8847 #define ETH_TYPE_MPLS_MCAST 0x8848 #define ETH_TYPE_NSH 0x894f +#define ETH_TYPE_ERSPAN1 0x88be /* version 1 type II */ +#define ETH_TYPE_ERSPAN2 0x22eb /* version 2 type III */ static inline bool eth_type_mpls(ovs_be16 eth_type) { @@ -1246,6 +1248,95 @@ struct gre_base_hdr { #define GRE_FLAGS 0x00F8 #define GRE_VERSION 0x0007 +/* + * ERSPAN protocol header and metadata + * + * Version 1 (Type II) header (8 octets [42:49]) + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ver | VLAN | COS | En|T| Session ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | Index | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * ERSPAN Version 2 (Type III) header (12 octets [42:49]) + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ver | VLAN | COS |BSO|T| Session ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Timestamp | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SGT |P| FT | Hw ID |D|Gra|O| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +/* ERSPAN has fixed 8-byte GRE header */ +#define ERSPAN_GREHDR_LEN 8 +#define ERSPAN_HDR(gre_base_hdr) \ + ((struct erspan_base_hdr *)((char *)gre_base_hdr + ERSPAN_GREHDR_LEN)) + +#define ERSPAN_V1_MDSIZE 4 +#define ERSPAN_V2_MDSIZE 8 + +#define SID_MASK 0x03ff /* 10-bit Session ID. */ + +struct erspan_base_hdr { + uint8_t ver:4, + vlan_upper:4; + uint8_t vlan:8; + uint8_t cos:3, + en:2, + t:1, + session_id_upper:2; + uint8_t session_id:8; +}; + +struct erspan_md2 { + ovs_be32 timestamp; + ovs_be16 sgt; + uint8_t hwid_upper:2, + ft:5, + p:1; + uint8_t o:1, + gra:2, + dir:1, + hwid:4; +}; + +struct erspan_metadata { + int version; + union { + ovs_be32 index; /* Version 1 (type II)*/ + struct erspan_md2 md2; /* Version 2 (type III) */ + } u; +}; + +static inline uint16_t get_sid(const struct erspan_base_hdr *ershdr) +{ + return (ershdr->session_id_upper << 8) + ershdr->session_id; +} + +static inline void set_sid(struct erspan_base_hdr *ershdr, uint16_t id) +{ + ershdr->session_id = id & 0xff; + ershdr->session_id_upper = (id >> 8) &0x3; +} + +static inline uint8_t get_hwid(const struct erspan_md2 *md2) +{ + return (md2->hwid_upper << 4) + md2->hwid; +} + +static inline void set_hwid(struct erspan_md2 *md2, uint8_t hwid) +{ + md2->hwid = hwid & 0xf; + md2->hwid_upper = (hwid >> 4) & 0x3; +} + /* VXLAN protocol header */ struct vxlanhdr { union { diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h index 441584af80f3..ab5b87a0521a 100644 --- a/ofproto/ofproto-dpif-rid.h +++ b/ofproto/ofproto-dpif-rid.h @@ -99,7 +99,7 @@ struct rule; /* Metadata for restoring pipeline context after recirculation. Helpers * are inlined below to keep them together with the definition for easier * updates. */ -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); struct frozen_metadata { /* Metadata in struct flow. */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index cc450a896948..d56b4d3335bf 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -3266,6 +3266,7 @@ propagate_tunnel_data_to_flow(struct xlate_ctx *ctx, struct eth_addr dmac, switch (tnl_type) { case OVS_VPORT_TYPE_GRE: + case OVS_VPORT_TYPE_ERSPAN: nw_proto = IPPROTO_GRE; break; case OVS_VPORT_TYPE_VXLAN: @@ -3741,7 +3742,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); memset(&flow_tnl, 0, sizeof flow_tnl); if (!check_output_prerequisites(ctx, xport, flow, check_stp)) { diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c index e0214ced69e2..2df4f0ce6911 100644 --- a/ofproto/tunnel.c +++ b/ofproto/tunnel.c @@ -474,6 +474,19 @@ tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow, wc->masks.pkt_mark = UINT32_MAX; } + if (cfg->erspan_ver) { + flow->tunnel.erspan_ver = cfg->erspan_ver; + } + if (cfg->erspan_idx) { + flow->tunnel.erspan_idx = cfg->erspan_idx; + } + if (cfg->erspan_dir) { + flow->tunnel.erspan_dir = cfg->erspan_dir; + } + if (cfg->erspan_hwid) { + flow->tunnel.erspan_hwid = cfg->erspan_hwid; + } + if (pre_flow_str) { char *post_flow_str = flow_to_string(flow, NULL); char *tnl_str = tnl_port_to_string(tnl_port); diff --git a/tests/odp.at b/tests/odp.at index cdf3d6645ea4..aa61e8371b67 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -80,6 +80,16 @@ sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt | s echo echo '# Valid forms with IP later fragment.' sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt | sed -n 's/,frag=no),.*/,frag=later)/p' + + echo + echo '# Valid forms with tunnel and ERSPAN v1 headers.' + sed + 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=1,idx=0x7),flags(df|key)),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt + + echo + echo '# Valid forms with tunnel and ERSPAN v2 headers.' + sed + 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=2,dir=0x1,hwid=0x7),flags(df|key)),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt ) > odp-in.txt AT_CAPTURE_FILE([odp-in.txt]) @@ -172,7 +182,18 @@ sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt echo echo '# Valid forms with IP later fragment.' -sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt) > odp.txt +sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt + + echo + echo '# Valid forms with tunnel and ERSPAN v1 headers.' + sed + 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=1/0,idx=0x7/0xf),flags(df|key)),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt + + echo + echo '# Valid forms with tunnel and ERSPAN v2 headers.' + sed + 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,ttl=128,erspan(ver=2,dir=0x1,hwid=0x7/0xf),flags(df|key)),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt +) > odp.txt AT_CAPTURE_FILE([odp.txt]) AT_CHECK_UNQUOTED([ovstest test-odp parse-wc-keys < odp.txt], [0], [`cat odp.txt` ]) @@ -358,6 +379,8 @@ ct_clear trunc(100) clone(1) clone(clone(push_vlan(vid=12,pcp=0),2),1) +set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=1,idx=0x7),flags(df|key))) +set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=2,dir=1,hwid=0x1),flags(df|key))) ]) AT_CHECK_UNQUOTED([ovstest test-odp parse-actions < actions.txt], [0], [`cat actions.txt` diff --git a/tests/ofproto.at b/tests/ofproto.at index c1beea7aec89..ecf77b8105d0 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -2394,7 +2394,7 @@ head_table () { instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table Write-Actions and Apply-Actions features: actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue - supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl + supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_metadata0 dnl tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl matching: @@ -2410,6 +2410,10 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 tun_flags: arbitrary mask tun_gbp_id: arbitrary mask tun_gbp_flags: arbitrary mask + tun_erspan_idx: arbitrary mask + tun_erspan_ver: arbitrary mask + tun_erspan_dir: arbitrary mask + tun_erspan_hwid: arbitrary mask tun_metadata0: arbitrary mask tun_metadata1: arbitrary mask tun_metadata2: arbitrary mask diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at index f7d4adb947a0..7567381f673f 100644 --- a/tests/system-common-macros.at +++ b/tests/system-common-macros.at @@ -296,6 +296,11 @@ m4_define([OVS_CHECK_GRE], [AT_SKIP_IF([! ip link add foo type gretap help 2>&1 | grep gretap >/dev/null]) OVS_CHECK_FIREWALL()]) +# OVS_CHECK_ERSPAN() +m4_define([OVS_CHECK_ERSPAN], + [AT_SKIP_IF([! ip link add foo type erspan help 2>&1 | grep erspan >/dev/null]) + OVS_CHECK_FIREWALL()]) + # OVS_CHECK_GRE_L3() m4_define([OVS_CHECK_GRE_L3], [AT_SKIP_IF([! ip link add foo type gre help 2>&1 | grep "gre " >/dev/null]) diff --git a/tests/system-traffic.at b/tests/system-traffic.at index 2afadec15827..8f3609804d1e 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -339,6 +339,76 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PI OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([datapath - ping over erspan v1 tunnel]) +OVS_CHECK_GRE() +OVS_CHECK_ERSPAN() + +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-underlay]) + +AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) +AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) + +ADD_NAMESPACES(at_ns0) + +dnl Set up underlay link from host into the namespace using veth pair. +ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") +AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) +AT_CHECK([ip link set dev br-underlay up]) + +dnl Set up tunnel endpoints on OVS outside the namespace and with a native +dnl linux device inside the namespace. +ADD_OVS_TUNNEL([erspan], [br0], [at_erspan0], [172.31.1.1], [10.1.1.100/24], [options:key=1 options:erspan_ver=1 options:erspan_idx=7]) +ADD_NATIVE_TUNNEL([erspan], [ns_erspan0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [seq key 1 erspan_ver 1 erspan 7]) + +dnl First, check the underlay +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 172.31.1.100 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +dnl Okay, now check the overlay with different packet sizes +dnl NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl +NS_CHECK_EXEC([at_ns0], [ping -s 1200 -i 0.3 -c 3000 10.1.1.100 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([datapath - ping over erspan v2 tunnel]) +OVS_CHECK_GRE() +OVS_CHECK_ERSPAN() + +OVS_TRAFFIC_VSWITCHD_START() +ADD_BR([br-underlay]) + +AT_CHECK([ovs-ofctl add-flow br0 "actions=normal"]) +AT_CHECK([ovs-ofctl add-flow br-underlay "actions=normal"]) + +ADD_NAMESPACES(at_ns0) + +dnl Set up underlay link from host into the namespace using veth pair. +ADD_VETH(p0, at_ns0, br-underlay, "172.31.1.1/24") +AT_CHECK([ip addr add dev br-underlay "172.31.1.100/24"]) +AT_CHECK([ip link set dev br-underlay up]) + +dnl Set up tunnel endpoints on OVS outside the namespace and with a native +dnl linux device inside the namespace. +ADD_OVS_TUNNEL([erspan], [br0], [at_erspan0], [172.31.1.1], [10.1.1.100/24], [options:key=1 options:erspan_ver=2 options:erspan_dir=1 options:erspan_hwid=0x7]) +ADD_NATIVE_TUNNEL([erspan], [ns_erspan0], [at_ns0], [172.31.1.100], [10.1.1.1/24], [seq key 1 erspan_ver 2 erspan_dir egress erspan_hwid 7]) + +dnl First, check the underlay +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 172.31.1.100 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + +dnl Okay, now check the overlay with different packet sizes +dnl NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.100 | FORMAT_PING], [0], [dnl +NS_CHECK_EXEC([at_ns0], [ping -s 1200 -i 0.3 -c 3000 10.1.1.100 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([datapath - ping over geneve tunnel]) OVS_CHECK_GENEVE() diff --git a/tests/tunnel.at b/tests/tunnel.at index 3c217b344f9b..6919359a54b7 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -406,6 +406,18 @@ AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([tunnel - ERSPAN]) +OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=erspan \ + options:remote_ip=1.1.1.1 ofport_request=1]) + +AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl + br0 65534/100: (dummy-internal) + p1 1/1: (erspan: remote_ip=1.1.1.1) +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([tunnel - different VXLAN UDP port]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \ options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341]) @@ -466,6 +478,99 @@ AT_CHECK([tail -1 stdout], [0], OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([tunnel - ERSPAN v1/v2 metadata]) +OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \ + ofport_request=1 \ + -- add-port br0 p2 -- set Interface p2 type=dummy \ + ofport_request=2 \ + -- add-port br0 p3 -- set Interface p3 type=erspan \ + options:remote_ip=1.1.1.1 ofport_request=3 \ + options:key=1 options:erspan_ver=1 options:erspan_idx=7 \ + -- add-port br0 p4 -- set Interface p4 type=erspan \ + options:remote_ip=1.1.1.2 ofport_request=4 \ + options:key=2 options:erspan_ver=2 options:erspan_dir=1 options:erspan_hwid=7 \ + ]) +OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP + +AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl + br0 65534/100: (dummy-internal) + p1 1/3: (dummy) + p2 2/2: (dummy) + p3 3/1: (erspan: erspan_idx=0x7, erspan_ver=1, key=1, remote_ip=1.1.1.1) + p4 4/1: (erspan: erspan_dir=1, erspan_hwid=0x7, erspan_ver=2, key=2, remote_ip=1.1.1.2) +]) + +AT_DATA([flows.txt], [dnl +in_port=1,actions=3 +in_port=2,actions=4 +in_port=3,tun_erspan_ver=1,tun_erspan_idx=0x7,actions=1 +in_port=4,tun_erspan_ver=2,tun_erspan_dir=1,tun_erspan_hwid=0xf/0x1,actions=2 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) + +dnl test encap: in_port=1,actions=3 (erspan v1 port) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: set(tunnel(tun_id=0x1,dst=1.1.1.1,ttl=64,erspan(ver=1,idx=0x7),flags(df|key))),1 +]) + +dnl test encap: in_port=2,actions=4 (erspan v2 port) +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: set(tunnel(tun_id=0x2,dst=1.1.1.2,ttl=64,erspan(ver=2,dir=1,hwid=0x7),flags(df|key))),1 +]) + +dnl receive packet from ERSPAN port with v1 metadata +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,ttl=64,erspan(ver=1,idx=0x7),flags(df|key)),in_port(1),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([tail -2 stdout], [0], + [Megaflow: recirc_id=0,eth,ip,tun_id=0x1,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,erspan_ver=1,erspan_idx=0x7,tun_flags=+df-csum+key,in_port=3,nw_frag=no +Datapath actions: 3 +]) + +dnl receive packet from ERSPAN port with wrong v1 metadata +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,ttl=64,erspan(ver=1,idx=0xabcd),flags(df|key)),in_port(1),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([tail -2 stdout], [0], + [Megaflow: recirc_id=0,eth,ip,tun_id=0x1,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,erspan_ver=1,erspan_idx=0xabcd,tun_flags=+df-csum+key,in_port=3,nw_frag=no +Datapath actions: drop +]) + +dnl receive packet from ERSPAN port with v2 metadata +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=1,hwid=0x7),flags(df|key)),in_port(1),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([tail -2 stdout], [0], + [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,erspan_ver=2,erspan_dir=1,erspan_hwid=0x1,tun_flags=+df-csum+key,in_port=4,nw_frag=no +Datapath actions: 2 +]) + +dnl receive packet from ERSPAN port with wrong v2 metadata +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=0,hwid=0x17),flags(df|key)),in_port(1),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([tail -2 stdout], [0], + [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,erspan_ver=2,erspan_dir=0,erspan_hwid=0x1,tun_flags=+df-csum+key,in_port=4,nw_frag=no +Datapath actions: drop +]) + +dnl test wildcard mask: recevie all v2 regardless of its metadata +AT_CHECK([ovs-ofctl del-flows br0 in_port=4,tun_erspan_ver=2,tun_erspan_dir=1,tun_erspan_hwid=0xf/0x1]) +AT_CHECK([ovs-ofctl add-flow br0 in_port=4,tun_erspan_ver=2,tun_erspan_dir=0/0,tun_erspan_hwid=0x0/0x0,actions=2]) + +AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip], +[0], [dnl +NXST_FLOW reply: + in_port=1 actions=output:3 + in_port=2 actions=output:4 + erspan_ver=1,erspan_idx=0x7,in_port=3 actions=output:1 + erspan_ver=2,in_port=4 actions=output:2 +]) + +dnl this time it won't drop +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=0,hwid=0x17),flags(df|key)),in_port(1),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) +AT_CHECK([tail -2 stdout], [0], + [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,erspan_ver=2,tun_flags=+df-csum+key,in_port=4,nw_frag=no +Datapath actions: 2 +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([tunnel - Geneve metadata]) OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=geneve \ options:remote_ip=1.1.1.1 ofport_request=1 \ diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 0c6a43d602c7..88554f321216 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2595,6 +2595,40 @@ + +

    + Only erspan interfaces support these options. +

    + +

    + 20 bit index/port number associated with the ERSPAN traffic's + source port and direction (ingress/egress). This field is + platform dependent. +

    +
    + + +

    + ERSPAN version: 1 for version 1 (type II) + or 2 for version 2 (type III). +

    +
    + + +

    + Specifies the ERSPAN v2 mirrored traffic's direction. + 1 for egress traffic, and 0 for ingress traffic. +

    +
    + + +

    + ERSPAN hardware ID is a 6-bit unique identifier of an + ERSPAN v2 engine within a system. +

    +
    + +

    These options apply only to patch ports, that is, interfaces