@@ -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
};
@@ -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.
@@ -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);
@@ -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<N>".
*
@@ -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;
};
@@ -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:
@@ -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;
}
@@ -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;
@@ -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;
@@ -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,",
@@ -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;
@@ -1456,6 +1456,7 @@ ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
<li>LISP has a 24-bit instance ID.</li>
<li>GRE has an optional 32-bit key.</li>
<li>STT has a 64-bit key.</li>
+ <li>ERSPAN has a 10-bit key (Session ID).</li>
</ul>
<p>
@@ -1715,6 +1716,91 @@ ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
</dl>
</field>
+ <h2>ERSPAN Metadata Fields FIXME</h2>
+ <p>
+ These fields provide access to features in the ERSPAN tunneling
+ protocol. The ERSPAN header is defined in the draft <url
+ href="https://tools.ietf.org/html/draft-foschiano-erspan-03"/>
+ </p>
+ <field id="MFF_TUN_ERSPAN_VER" title="ERSPAN Version">
+ <p>
+ ERSPAN version number. 1 for version 1 (type II)
+ or 2 for version 2 (type III).
+ </p>
+ </field>
+
+ <p> ERSPAN version 1 header format:</p>
+ <diagram>
+ <header name="GRE">
+ <bits name="..." above="16" width="0.4"/>
+ <bits name="type" above="16" below="0x88be" width="0.4"/>
+ <bits name="seq" above="32" width=".4"/>
+ </header>
+ <header name="ERSPAN v1">
+ <bits name="ver" above="4" below="1" width="0.4"/>
+ <bits name="..." above="18" width="0.4"/>
+ <bits name="session" above="10" below="tun_id" width="0.4"/>
+ <bits name="..." above="12" width="0.4"/>
+ <bits name="idx" above="20" width="0.6"/>
+ </header>
+ <header name="Ethernet">
+ <bits name="dst" above="48" width="0.4"/>
+ <bits name="src" above="48" width="0.4"/>
+ <bits name="type" above="16" width="0.4"/>
+ </header>
+ <dots/>
+ </diagram>
+
+ <p> 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.
+ </p>
+ <field id="MFF_TUN_ERSPAN_IDX" title="ERSPAN Index">
+ <p>
+ 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.
+ </p>
+ </field>
+
+ <p> ERSPAN version 2 header format:</p>
+ <diagram>
+ <header name="GRE">
+ <bits name="..." above="16" width="0.4"/>
+ <bits name="type" above="16" below="0x22eb" width="0.4"/>
+ <bits name="seq" above="32" width=".4"/>
+ </header>
+ <header name="ERSPAN v2">
+ <bits name="ver" above="4" below="2" width="0.4"/>
+ <bits name="..." above="18" width="0.4"/>
+ <bits name="session" above="10" below="tun_id" width="0.4"/>
+ <bits name="timestamp" above="32" width=".4"/>
+ <bits name="..." above="22" width="0.4"/>
+ <bits name="hwid" above="6" width="0.4"/>
+ <bits name="dir" above="1" below="0/1" width="0.4"/>
+ <bits name="..." above="3" width="0.4"/>
+ </header>
+ <header name="Ethernet">
+ <bits name="dst" above="48" width="0.4"/>
+ <bits name="src" above="48" width="0.4"/>
+ <bits name="type" above="16" width="0.4"/>
+ </header>
+ <dots/>
+ </diagram>
+
+ <field id="MFF_TUN_ERSPAN_DIR" title="ERSPAN Direction">
+ <p>
+ Specifies the ERSPAN v2 mirrored traffic's direction.
+ Value 1 for egress traffic, and value 0 for ingress traffic.
+ </p>
+ </field>
+ <field id="MFF_TUN_ERSPAN_HWID" title="ERSPAN Hardware ID">
+ <p>
+ ERSPAN hardware ID is a 6-bit unique identifier of an
+ ERSPAN v2 engine within a system.
+ </p>
+ </field>
+
<h2>Geneve Fields</h2>
<p>
@@ -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;
@@ -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);
@@ -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);
@@ -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);
@@ -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
@@ -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);
@@ -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 {
@@ -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. */
@@ -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)) {
@@ -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);
@@ -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`
@@ -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
@@ -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])
@@ -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()
@@ -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 \
@@ -2595,6 +2595,40 @@
</group>
</group>
+ <group title="Tunnel Options: erspan only">
+ <p>
+ Only <code>erspan</code> interfaces support these options.
+ </p>
+ <column name="options" key="erspan_idx">
+ <p>
+ 20 bit index/port number associated with the ERSPAN traffic's
+ source port and direction (ingress/egress). This field is
+ platform dependent.
+ </p>
+ </column>
+
+ <column name="options" key="erspan_ver">
+ <p>
+ ERSPAN version: 1 for version 1 (type II)
+ or 2 for version 2 (type III).
+ </p>
+ </column>
+
+ <column name="options" key="erspan_dir">
+ <p>
+ Specifies the ERSPAN v2 mirrored traffic's direction.
+ 1 for egress traffic, and 0 for ingress traffic.
+ </p>
+ </column>
+
+ <column name="options" key="erspan_hwid">
+ <p>
+ ERSPAN hardware ID is a 6-bit unique identifier of an
+ ERSPAN v2 engine within a system.
+ </p>
+ </column>
+ </group>
+
<group title="Patch Options">
<p>
These options apply only to <dfn>patch ports</dfn>, that is, interfaces
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 <u9012063@gmail.com> --- 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(-)