All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/4] ERSPAN version 2 (type III) support
@ 2017-12-14  0:38 William Tu
  2017-12-14  0:38 ` [PATCH net-next 1/4] net: erspan: refactor existing erspan code William Tu
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: William Tu @ 2017-12-14  0:38 UTC (permalink / raw)
  To: netdev

ERSPAN has two versions, v1 (type II) and v2 (type III).  This patch
series add support for erspan v2 based on existing erspan v1
implementation.  The first patch refactors the existing erspan v1's
header structure, making it extensible to put additional v2's header.
The second and third patch introduces erspan v2's implementation to
ipv4 and ipv6 erspan, for both native mode and collect metadata mode.
Finally, test cases are added under the samples/bpf.

Note:
ERSPAN version 2 has many features and this patch does not implement
all.  One major use case of version 2 over version 1 is its timestamp
and direction.  So the traffic collector is able to distinguish the
mirrorred traffic better.  Other features such as SGT (security group
tag), FT (frame type) for carrying non-ethernet packet, and optional
subheader are not implemented yet.

Example commandline for ERSPAN version 2:
ip link add dev ip6erspan11 type ip6erspan seq key 102 \ 
	local fc00:100::2 remote fc00:100::1 \        
	erspan_ver 2 erspan_dir 1 erspan_hwid 17

The corresponding iproute2 patch:
https://marc.info/?l=linux-netdev&m=151321141525106&w=2

William Tu (4):
  net: erspan: refactor existing erspan code
  net: erspan: introduce erspan v2 for ip_gre
  ip6_gre: add erspan v2 support
  samples/bpf: add erspan v2 sample code

 include/net/erspan.h           | 152 ++++++++++++++++++++++++++++++++++++++---
 include/net/ip6_tunnel.h       |   3 +
 include/net/ip_tunnels.h       |   5 +-
 include/uapi/linux/if_ether.h  |   1 +
 include/uapi/linux/if_tunnel.h |   3 +
 net/ipv4/ip_gre.c              | 124 +++++++++++++++++++++++++++------
 net/ipv6/ip6_gre.c             | 139 +++++++++++++++++++++++++++++++------
 net/openvswitch/flow_netlink.c |   8 +--
 samples/bpf/tcbpf2_kern.c      |  77 ++++++++++++++++++---
 samples/bpf/test_tunnel_bpf.sh |  38 ++++++++---
 10 files changed, 472 insertions(+), 78 deletions(-)

--
A simple script to test it:
#!/bin/bash
# In the namespace NS0, create veth0 and ip6erspan00
# Out of the namespace, create veth1 and ip6erspan11
# Ping in and out of namespace using ERSPAN protocol 

set -ex
function cleanup() {
	set +ex
	ip netns del ns0
	ip link del ip6erspan11
	ip link del veth1
}

function main() {
	trap cleanup 0 2 3 9

	ip netns add ns0
	ip link add veth0 type veth peer name veth1
	ip link set veth0 netns ns0

	# non-namespace
	ip addr add dev veth1 fc00:100::2/96

	if [ "$1" == "v1" ]; then
		echo "create IP6 ERSPAN v1 tunnel"
		ip link add dev ip6erspan11 type ip6erspan seq key 102 \
			local fc00:100::2 remote fc00:100::1 \
			erspan 123 erspan_ver 1
	else	
		echo "create IP6 ERSPAN v2 tunnel"
		ip link add dev ip6erspan11 type ip6erspan seq key 102 \
			local fc00:100::2 remote fc00:100::1 \
			erspan_ver 2 erspan_dir 1 erspan_hwid 17
	fi
	ip addr add dev ip6erspan11 fc00:200::2/96
	ip addr add dev ip6erspan11 10.10.200.2/24

	# namespace: ns0 
	ip netns exec ns0 ip addr add fc00:100::1/96 dev veth0

	if [ "$1" == "v1" ]; then
		ip netns exec ns0 \
		ip link add dev ip6erspan00 type ip6erspan seq key 102 \
			local fc00:100::1 remote fc00:100::2 \
			erspan 123 erspan_ver 1
	else
		ip netns exec ns0 \
		ip link add dev ip6erspan00 type ip6erspan seq key 102 \
			local fc00:100::1 remote fc00:100::2 \
			erspan_ver 2 erspan_dir 1 erspan_hwid 7
	fi

	ip netns exec ns0 ip addr add dev ip6erspan00 fc00:200::1/96
	ip netns exec ns0 ip addr add dev ip6erspan00 10.10.200.1/24

	ip link set dev veth1 up
	ip link set dev ip6erspan11 up
	ip netns exec ns0 ip link set dev ip6erspan00 up
	ip netns exec ns0 ip link set dev veth0 up
}

main $1

# Ping underlying
ping6 -c 1 fc00:100::1 || true

# ping overlay
ping -c 3 10.10.200.1
exit 0

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH net-next 1/4] net: erspan: refactor existing erspan code
  2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
@ 2017-12-14  0:38 ` William Tu
  2017-12-14  0:38 ` [PATCH net-next 2/4] net: erspan: introduce erspan v2 for ip_gre William Tu
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: William Tu @ 2017-12-14  0:38 UTC (permalink / raw)
  To: netdev

The patch refactors the existing erspan implementation in order
to support erspan version 2, which has additional metadata.  So, in
stead of having one 'struct erspanhdr' holding erspan version 1,
breaks it into 'struct erspan_base_hdr' and 'struct erspan_metadata'.

Signed-off-by: William Tu <u9012063@gmail.com>
---
 include/net/erspan.h           | 34 ++++++++++++++++++++++++----------
 net/ipv4/ip_gre.c              | 27 +++++++++++++++++----------
 net/ipv6/ip6_gre.c             | 25 ++++++++++++++++---------
 net/openvswitch/flow_netlink.c |  8 ++++----
 4 files changed, 61 insertions(+), 33 deletions(-)

diff --git a/include/net/erspan.h b/include/net/erspan.h
index 6e758d08c9ee..70c40c7c75b2 100644
--- a/include/net/erspan.h
+++ b/include/net/erspan.h
@@ -15,7 +15,7 @@
  *  s, Recur, Flags, Version fields only S (bit 03) is set to 1. The
  *  other fields are set to zero, so only a sequence number follows.
  *
- *  ERSPAN Type II header (8 octets [42:49])
+ *  ERSPAN 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
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -27,7 +27,7 @@
  * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
  */
 
-#define ERSPAN_VERSION	0x1
+#define ERSPAN_VERSION	0x1	/* ERSPAN type II */
 
 #define VER_MASK	0xf000
 #define VLAN_MASK	0x0fff
@@ -44,20 +44,29 @@ enum erspan_encap_type {
 	ERSPAN_ENCAP_INFRAME = 0x3,	/* VLAN tag perserved in frame */
 };
 
+#define ERSPAN_V1_MDSIZE	4
+#define ERSPAN_V2_MDSIZE	8
 struct erspan_metadata {
-	__be32 index;   /* type II */
+	union {
+		__be32 index;	/* Version 1 (type II)*/
+	} u;
 };
 
-struct erspanhdr {
+struct erspan_base_hdr {
 	__be16 ver_vlan;
 #define VER_OFFSET  12
 	__be16 session_id;
 #define COS_OFFSET  13
 #define EN_OFFSET   11
 #define T_OFFSET    10
-	struct erspan_metadata md;
 };
 
+static inline int erspan_hdr_len(int version)
+{
+	return sizeof(struct erspan_base_hdr) +
+	       (version == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE);
+}
+
 static inline u8 tos_to_cos(u8 tos)
 {
 	u8 dscp, cos;
@@ -73,7 +82,8 @@ static inline void erspan_build_header(struct sk_buff *skb,
 {
 	struct ethhdr *eth = eth_hdr(skb);
 	enum erspan_encap_type enc_type;
-	struct erspanhdr *ershdr;
+	struct erspan_base_hdr *ershdr;
+	struct erspan_metadata *ersmd;
 	struct qtag_prefix {
 		__be16 eth_type;
 		__be16 tci;
@@ -96,17 +106,21 @@ static inline void erspan_build_header(struct sk_buff *skb,
 		enc_type = ERSPAN_ENCAP_INFRAME;
 	}
 
-	skb_push(skb, sizeof(*ershdr));
-	ershdr = (struct erspanhdr *)skb->data;
-	memset(ershdr, 0, sizeof(*ershdr));
+	skb_push(skb, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
+	ershdr = (struct erspan_base_hdr *)skb->data;
+	memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
 
+	/* Build base header */
 	ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) |
 				 (ERSPAN_VERSION << VER_OFFSET));
 	ershdr->session_id = htons((u16)(ntohl(id) & ID_MASK) |
 			   ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) |
 			   (enc_type << EN_OFFSET & EN_MASK) |
 			   ((truncate << T_OFFSET) & T_MASK));
-	ershdr->md.index = htonl(index & INDEX_MASK);
+
+	/* Build metadata */
+	ersmd = (struct erspan_metadata *)(ershdr + 1);
+	ersmd->u.index = htonl(index & INDEX_MASK);
 }
 
 #endif
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index d828821d88d7..3e37402147f3 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -256,34 +256,41 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 {
 	struct net *net = dev_net(skb->dev);
 	struct metadata_dst *tun_dst = NULL;
+	struct erspan_base_hdr *ershdr;
+	struct erspan_metadata *pkt_md;
 	struct ip_tunnel_net *itn;
 	struct ip_tunnel *tunnel;
-	struct erspanhdr *ershdr;
 	const struct iphdr *iph;
-	__be32 index;
+	int ver;
 	int len;
 
 	itn = net_generic(net, erspan_net_id);
 	len = gre_hdr_len + sizeof(*ershdr);
 
+	/* Check based hdr len */
 	if (unlikely(!pskb_may_pull(skb, len)))
 		return -ENOMEM;
 
 	iph = ip_hdr(skb);
-	ershdr = (struct erspanhdr *)(skb->data + gre_hdr_len);
+	ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
+	ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET;
 
 	/* The original GRE header does not have key field,
 	 * Use ERSPAN 10-bit session ID as key.
 	 */
 	tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
-	index = ershdr->md.index;
+	pkt_md = (struct erspan_metadata *)(ershdr + 1);
 	tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
 				  tpi->flags | TUNNEL_KEY,
 				  iph->saddr, iph->daddr, tpi->key);
 
 	if (tunnel) {
+		len = gre_hdr_len + erspan_hdr_len(ver);
+		if (unlikely(!pskb_may_pull(skb, len)))
+			return -ENOMEM;
+
 		if (__iptunnel_pull_header(skb,
-					   gre_hdr_len + sizeof(*ershdr),
+					   len,
 					   htons(ETH_P_TEB),
 					   false, false) < 0)
 			goto drop;
@@ -307,12 +314,12 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 			if (!md)
 				return PACKET_REJECT;
 
-			md->index = index;
+			memcpy(md, pkt_md, sizeof(*md));
 			info = &tun_dst->u.tun_info;
 			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
 			info->options_len = sizeof(*md);
 		} else {
-			tunnel->index = ntohl(index);
+			tunnel->index = ntohl(pkt_md->u.index);
 		}
 
 		skb_reset_mac_header(skb);
@@ -571,7 +578,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
 	key = &tun_info->key;
 
 	/* ERSPAN has fixed 8 byte GRE header */
-	tunnel_hlen = 8 + sizeof(struct erspanhdr);
+	tunnel_hlen = 8 + sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
 
 	rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen);
 	if (!rt)
@@ -590,7 +597,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
 		goto err_free_rt;
 
 	erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
-			    ntohl(md->index), truncate, true);
+			    ntohl(md->u.index), truncate, true);
 
 	gre_build_header(skb, 8, TUNNEL_SEQ,
 			 htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++));
@@ -1238,7 +1245,7 @@ static int erspan_tunnel_init(struct net_device *dev)
 	tunnel->tun_hlen = 8;
 	tunnel->parms.iph.protocol = IPPROTO_GRE;
 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-		       sizeof(struct erspanhdr);
+		       sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
 	t_hlen = tunnel->hlen + sizeof(struct iphdr);
 
 	dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 4562579797d1..1303d0c44c36 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -501,25 +501,32 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
 static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
 			 struct tnl_ptk_info *tpi)
 {
+	struct erspan_base_hdr *ershdr;
+	struct erspan_metadata *pkt_md;
 	const struct ipv6hdr *ipv6h;
-	struct erspanhdr *ershdr;
 	struct ip6_tnl *tunnel;
-	__be32 index;
+	u8 ver;
 
 	ipv6h = ipv6_hdr(skb);
-	ershdr = (struct erspanhdr *)skb->data;
+	ershdr = (struct erspan_base_hdr *)skb->data;
 
 	if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
 		return PACKET_REJECT;
 
+	ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET;
 	tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
-	index = ershdr->md.index;
+	pkt_md = (struct erspan_metadata *)(ershdr + 1);
 
 	tunnel = ip6gre_tunnel_lookup(skb->dev,
 				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
 				      tpi->proto);
 	if (tunnel) {
-		if (__iptunnel_pull_header(skb, sizeof(*ershdr),
+		int len = erspan_hdr_len(ver);
+
+		if (unlikely(!pskb_may_pull(skb, len)))
+			return -ENOMEM;
+
+		if (__iptunnel_pull_header(skb, len,
 					   htons(ETH_P_TEB),
 					   false, false) < 0)
 			return PACKET_REJECT;
@@ -545,14 +552,14 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
 			if (!md)
 				return PACKET_REJECT;
 
-			md->index = index;
+			memcpy(md, pkt_md, sizeof(*md));
 			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
 			info->options_len = sizeof(*md);
 
 			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
 
 		} else {
-			tunnel->parms.index = ntohl(index);
+			tunnel->parms.index = ntohl(pkt_md->u.index);
 			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
 		}
 
@@ -921,7 +928,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
 			goto tx_err;
 
 		erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
-				    ntohl(md->index), truncate, false);
+				    ntohl(md->u.index), truncate, false);
 
 	} else {
 		switch (skb->protocol) {
@@ -1657,7 +1664,7 @@ static int ip6erspan_tap_init(struct net_device *dev)
 
 	tunnel->tun_hlen = 8;
 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-		       sizeof(struct erspanhdr);
+		       sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
 	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
 
 	dev->hard_header_len = LL_MAX_HEADER + t_hlen;
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 624ea74353dd..bce1f78b0de5 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -644,12 +644,12 @@ static int erspan_tun_opt_from_nlattr(const struct nlattr *attr,
 	BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts));
 
 	memset(&opts, 0, sizeof(opts));
-	opts.index = nla_get_be32(attr);
+	opts.u.index = nla_get_be32(attr);
 
 	/* Index has only 20-bit */
-	if (ntohl(opts.index) & ~INDEX_MASK) {
+	if (ntohl(opts.u.index) & ~INDEX_MASK) {
 		OVS_NLERR(log, "ERSPAN index number %x too large.",
-			  ntohl(opts.index));
+			  ntohl(opts.u.index));
 		return -EINVAL;
 	}
 
@@ -907,7 +907,7 @@ static int __ip_tun_to_nlattr(struct sk_buff *skb,
 			return -EMSGSIZE;
 		else if (output->tun_flags & TUNNEL_ERSPAN_OPT &&
 			 nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,
-				      ((struct erspan_metadata *)tun_opts)->index))
+				      ((struct erspan_metadata *)tun_opts)->u.index))
 			return -EMSGSIZE;
 	}
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next 2/4] net: erspan: introduce erspan v2 for ip_gre
  2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
  2017-12-14  0:38 ` [PATCH net-next 1/4] net: erspan: refactor existing erspan code William Tu
@ 2017-12-14  0:38 ` William Tu
  2017-12-14  0:38 ` [PATCH net-next 3/4] ip6_gre: add erspan v2 support William Tu
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: William Tu @ 2017-12-14  0:38 UTC (permalink / raw)
  To: netdev

The patch adds support for erspan version 2.  Not all features are
supported in this patch.  The SGT (security group tag), GRA (timestamp
granularity), FT (frame type) are set to fixed value.  Only hardware
ID and direction are configurable.  Optional subheader is also not
supported.

Signed-off-by: William Tu <u9012063@gmail.com>
---
 include/net/erspan.h           | 120 ++++++++++++++++++++++++++++++++++++++++-
 include/net/ip_tunnels.h       |   5 +-
 include/uapi/linux/if_ether.h  |   1 +
 include/uapi/linux/if_tunnel.h |   3 ++
 net/ipv4/ip_gre.c              | 105 ++++++++++++++++++++++++++++++------
 5 files changed, 216 insertions(+), 18 deletions(-)

diff --git a/include/net/erspan.h b/include/net/erspan.h
index 70c40c7c75b2..acdf6843095d 100644
--- a/include/net/erspan.h
+++ b/include/net/erspan.h
@@ -24,11 +24,29 @@
  * |      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|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *      Platform Specific SubHeader (8 octets, optional)
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |  Platf ID |               Platform Specific Info              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                  Platform Specific Info                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
  * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
  */
 
 #define ERSPAN_VERSION	0x1	/* ERSPAN type II */
-
 #define VER_MASK	0xf000
 #define VLAN_MASK	0x0fff
 #define COS_MASK	0xe000
@@ -37,6 +55,28 @@
 #define ID_MASK		0x03ff
 #define INDEX_MASK	0xfffff
 
+#define ERSPAN_VERSION2	0x2	/* ERSPAN type III*/
+#define BSO_MASK	EN_MASK
+#define SGT_MASK	0xffff0000
+#define P_MASK		0x8000
+#define FT_MASK		0x7c00
+#define HWID_MASK	0x03f0
+#define DIR_MASK	0x0008
+#define GRA_MASK	0x0006
+#define O_MASK		0x0001
+
+/* ERSPAN version 2 metadata header */
+struct erspan_md2 {
+	__be32 timestamp;
+	__be16 sgt;	/* security group tag */
+	__be16 flags;
+#define P_OFFSET	15
+#define FT_OFFSET	10
+#define HWID_OFFSET	4
+#define DIR_OFFSET	3
+#define GRA_OFFSET	1
+};
+
 enum erspan_encap_type {
 	ERSPAN_ENCAP_NOVLAN = 0x0,	/* originally without VLAN tag */
 	ERSPAN_ENCAP_ISL = 0x1,		/* originally ISL encapsulated */
@@ -48,8 +88,10 @@ enum erspan_encap_type {
 #define ERSPAN_V2_MDSIZE	8
 struct erspan_metadata {
 	union {
-		__be32 index;	/* Version 1 (type II)*/
+		__be32 index;		/* Version 1 (type II)*/
+		struct erspan_md2 md2;	/* Version 2 (type III) */
 	} u;
+	int version;
 };
 
 struct erspan_base_hdr {
@@ -58,6 +100,7 @@ struct erspan_base_hdr {
 	__be16 session_id;
 #define COS_OFFSET  13
 #define EN_OFFSET   11
+#define BSO_OFFSET  EN_OFFSET
 #define T_OFFSET    10
 };
 
@@ -123,4 +166,77 @@ static inline void erspan_build_header(struct sk_buff *skb,
 	ersmd->u.index = htonl(index & INDEX_MASK);
 }
 
+/* ERSPAN GRA: timestamp granularity
+ *   00b --> granularity = 100 microseconds
+ *   01b --> granularity = 100 nanoseconds
+ *   10b --> granularity = IEEE 1588
+ * Here we only support 100 microseconds.
+ */
+static inline __be32 erspan_get_timestamp(void)
+{
+	u64 h_usecs;
+	ktime_t kt;
+
+	kt = ktime_get_real();
+	h_usecs = ktime_divns(kt, 100 * NSEC_PER_USEC);
+
+	/* ERSPAN base header only has 32-bit,
+	 * so it wraps around 4 days.
+	 */
+	return htonl((u32)h_usecs);
+}
+
+static inline void erspan_build_header_v2(struct sk_buff *skb,
+					  __be32 id, u8 direction, u16 hwid,
+					  bool truncate, bool is_ipv4)
+{
+	struct ethhdr *eth = eth_hdr(skb);
+	struct erspan_base_hdr *ershdr;
+	struct erspan_metadata *md;
+	struct qtag_prefix {
+		__be16 eth_type;
+		__be16 tci;
+	} *qp;
+	u16 vlan_tci = 0;
+	u16 session_id;
+	u8 gra = 0; /* 100 usec */
+	u8 bso = 0; /* Bad/Short/Oversized */
+	u8 sgt = 0;
+	u8 tos;
+
+	tos = is_ipv4 ? ip_hdr(skb)->tos :
+			(ipv6_hdr(skb)->priority << 4) +
+			(ipv6_hdr(skb)->flow_lbl[0] >> 4);
+
+	/* Unlike v1, v2 does not have En field,
+	 * so only extract vlan tci field.
+	 */
+	if (eth->h_proto == htons(ETH_P_8021Q)) {
+		qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN);
+		vlan_tci = ntohs(qp->tci);
+	}
+
+	skb_push(skb, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
+	ershdr = (struct erspan_base_hdr *)skb->data;
+	memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
+
+	/* Build base header */
+	ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) |
+				 (ERSPAN_VERSION2 << VER_OFFSET));
+	session_id = (u16)(ntohl(id) & ID_MASK) |
+		     ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) |
+		     (bso << BSO_OFFSET & BSO_MASK) |
+		     ((truncate << T_OFFSET) & T_MASK);
+	ershdr->session_id = htons(session_id);
+
+	/* Build metadata */
+	md = (struct erspan_metadata *)(ershdr + 1);
+	md->u.md2.timestamp = erspan_get_timestamp();
+	md->u.md2.sgt = htons(sgt);
+	md->u.md2.flags = htons(((1 << P_OFFSET) & P_MASK) |
+				((hwid << HWID_OFFSET) & HWID_MASK) |
+				((direction << DIR_OFFSET) & DIR_MASK) |
+				((gra << GRA_OFFSET) & GRA_MASK));
+}
+
 #endif
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 24628f6b09bf..1f16773cfd76 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -116,8 +116,11 @@ struct ip_tunnel {
 	u32		o_seqno;	/* The last output seqno */
 	int		tun_hlen;	/* Precalculated header length */
 
-	/* This field used only by ERSPAN */
+	/* These four fields used only by ERSPAN */
 	u32		index;		/* ERSPAN type II index */
+	u8		erspan_ver;	/* ERSPAN version */
+	u8		dir;		/* ERSPAN direction */
+	u16		hwid;		/* ERSPAN hardware ID */
 
 	struct dst_cache dst_cache;
 
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 3ee3bf7c8526..87b7529fcdfe 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -47,6 +47,7 @@
 #define ETH_P_PUP	0x0200		/* Xerox PUP packet		*/
 #define ETH_P_PUPAT	0x0201		/* Xerox PUP Addr Trans packet	*/
 #define ETH_P_TSN	0x22F0		/* TSN (IEEE 1722) packet	*/
+#define ETH_P_ERSPAN2	0x22EB		/* ERSPAN version 2 (type III)	*/
 #define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
 #define ETH_P_X25	0x0805		/* CCITT X.25			*/
 #define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index e68dadbd6d45..1b3d148c4560 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -137,6 +137,9 @@ enum {
 	IFLA_GRE_IGNORE_DF,
 	IFLA_GRE_FWMARK,
 	IFLA_GRE_ERSPAN_INDEX,
+	IFLA_GRE_ERSPAN_VER,
+	IFLA_GRE_ERSPAN_DIR,
+	IFLA_GRE_ERSPAN_HWID,
 	__IFLA_GRE_MAX,
 };
 
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 3e37402147f3..004800b923c6 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -315,11 +315,26 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 				return PACKET_REJECT;
 
 			memcpy(md, pkt_md, sizeof(*md));
+			md->version = ver;
+
 			info = &tun_dst->u.tun_info;
 			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
 			info->options_len = sizeof(*md);
 		} else {
-			tunnel->index = ntohl(pkt_md->u.index);
+			tunnel->erspan_ver = ver;
+			if (ver == 1) {
+				tunnel->index = ntohl(pkt_md->u.index);
+			} else {
+				u16 md2_flags;
+				u16 dir, hwid;
+
+				md2_flags = ntohs(pkt_md->u.md2.flags);
+				dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
+				hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
+				tunnel->dir = dir;
+				tunnel->hwid = hwid;
+			}
+
 		}
 
 		skb_reset_mac_header(skb);
@@ -413,7 +428,8 @@ static int gre_rcv(struct sk_buff *skb)
 	if (hdr_len < 0)
 		goto drop;
 
-	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN))) {
+	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
+		     tpi.proto == htons(ETH_P_ERSPAN2))) {
 		if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
 			return 0;
 	}
@@ -568,6 +584,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
 	bool truncate = false;
 	struct flowi4 fl;
 	int tunnel_hlen;
+	int version;
 	__be16 df;
 
 	tun_info = skb_tunnel_info(skb);
@@ -576,9 +593,13 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
 		goto err_free_skb;
 
 	key = &tun_info->key;
+	md = ip_tunnel_info_opts(tun_info);
+	if (!md)
+		goto err_free_rt;
 
 	/* ERSPAN has fixed 8 byte GRE header */
-	tunnel_hlen = 8 + sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
+	version = md->version;
+	tunnel_hlen = 8 + erspan_hdr_len(version);
 
 	rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen);
 	if (!rt)
@@ -592,12 +613,23 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
 		truncate = true;
 	}
 
-	md = ip_tunnel_info_opts(tun_info);
-	if (!md)
-		goto err_free_rt;
+	if (version == 1) {
+		erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
+				    ntohl(md->u.index), truncate, true);
+	} else if (version == 2) {
+		u16 md2_flags;
+		u8 direction;
+		u16 hwid;
 
-	erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
-			    ntohl(md->u.index), truncate, true);
+		md2_flags = ntohs(md->u.md2.flags);
+		direction = (md2_flags & DIR_MASK) >> DIR_OFFSET;
+		hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
+
+		erspan_build_header_v2(skb, tunnel_id_to_key32(key->tun_id),
+				       direction, hwid,	truncate, true);
+	} else {
+		goto err_free_rt;
+	}
 
 	gre_build_header(skb, 8, TUNNEL_SEQ,
 			 htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++));
@@ -699,8 +731,14 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
 	}
 
 	/* Push ERSPAN header */
-	erspan_build_header(skb, tunnel->parms.o_key, tunnel->index,
-			    truncate, true);
+	if (tunnel->erspan_ver == 1)
+		erspan_build_header(skb, tunnel->parms.o_key, tunnel->index,
+				    truncate, true);
+	else
+		erspan_build_header_v2(skb, tunnel->parms.o_key,
+				       tunnel->dir, tunnel->hwid,
+				       truncate, true);
+
 	tunnel->parms.o_flags &= ~TUNNEL_KEY;
 	__gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_ERSPAN));
 	return NETDEV_TX_OK;
@@ -1172,13 +1210,32 @@ static int ipgre_netlink_parms(struct net_device *dev,
 	if (data[IFLA_GRE_FWMARK])
 		*fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
 
-	if (data[IFLA_GRE_ERSPAN_INDEX]) {
-		t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
+	if (data[IFLA_GRE_ERSPAN_VER]) {
+		t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
 
-		if (t->index & ~INDEX_MASK)
+		if (t->erspan_ver != 1 && t->erspan_ver != 2)
 			return -EINVAL;
 	}
 
+	if (t->erspan_ver == 1) {
+		if (data[IFLA_GRE_ERSPAN_INDEX]) {
+			t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
+			if (t->index & ~INDEX_MASK)
+				return -EINVAL;
+		}
+	} else if (t->erspan_ver == 2) {
+		if (data[IFLA_GRE_ERSPAN_DIR]) {
+			t->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
+			if (t->dir & ~(DIR_MASK >> DIR_OFFSET))
+				return -EINVAL;
+		}
+		if (data[IFLA_GRE_ERSPAN_HWID]) {
+			t->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
+			if (t->hwid & ~(HWID_MASK >> HWID_OFFSET))
+				return -EINVAL;
+		}
+	}
+
 	return 0;
 }
 
@@ -1245,7 +1302,7 @@ static int erspan_tunnel_init(struct net_device *dev)
 	tunnel->tun_hlen = 8;
 	tunnel->parms.iph.protocol = IPPROTO_GRE;
 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-		       sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
+		       erspan_hdr_len(tunnel->erspan_ver);
 	t_hlen = tunnel->hlen + sizeof(struct iphdr);
 
 	dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4;
@@ -1375,6 +1432,12 @@ static size_t ipgre_get_size(const struct net_device *dev)
 		nla_total_size(4) +
 		/* IFLA_GRE_ERSPAN_INDEX */
 		nla_total_size(4) +
+		/* IFLA_GRE_ERSPAN_VER */
+		nla_total_size(1) +
+		/* IFLA_GRE_ERSPAN_DIR */
+		nla_total_size(1) +
+		/* IFLA_GRE_ERSPAN_HWID */
+		nla_total_size(2) +
 		0;
 }
 
@@ -1417,9 +1480,18 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
 			goto nla_put_failure;
 	}
 
-	if (t->index)
+	if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
+		goto nla_put_failure;
+
+	if (t->erspan_ver == 1) {
 		if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index))
 			goto nla_put_failure;
+	} else if (t->erspan_ver == 2) {
+		if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir))
+			goto nla_put_failure;
+		if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid))
+			goto nla_put_failure;
+	}
 
 	return 0;
 
@@ -1455,6 +1527,9 @@ static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = {
 	[IFLA_GRE_IGNORE_DF]	= { .type = NLA_U8 },
 	[IFLA_GRE_FWMARK]	= { .type = NLA_U32 },
 	[IFLA_GRE_ERSPAN_INDEX]	= { .type = NLA_U32 },
+	[IFLA_GRE_ERSPAN_VER]	= { .type = NLA_U8 },
+	[IFLA_GRE_ERSPAN_DIR]	= { .type = NLA_U8 },
+	[IFLA_GRE_ERSPAN_HWID]	= { .type = NLA_U16 },
 };
 
 static struct rtnl_link_ops ipgre_link_ops __read_mostly = {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next 3/4] ip6_gre: add erspan v2 support
  2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
  2017-12-14  0:38 ` [PATCH net-next 1/4] net: erspan: refactor existing erspan code William Tu
  2017-12-14  0:38 ` [PATCH net-next 2/4] net: erspan: introduce erspan v2 for ip_gre William Tu
@ 2017-12-14  0:38 ` William Tu
  2017-12-14  0:38 ` [PATCH net-next 4/4] samples/bpf: add erspan v2 sample code William Tu
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: William Tu @ 2017-12-14  0:38 UTC (permalink / raw)
  To: netdev

Similar to support for ipv4 erspan, this patch adds
erspan v2 to ip6erspan tunnel.

Signed-off-by: William Tu <u9012063@gmail.com>
---
 include/net/ip6_tunnel.h |   3 ++
 net/ipv6/ip6_gre.c       | 120 ++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 107 insertions(+), 16 deletions(-)

diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 109a5a8877ef..236e40ba06bf 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -37,6 +37,9 @@ struct __ip6_tnl_parm {
 
 	__u32			fwmark;
 	__u32			index;	/* ERSPAN type II index */
+	__u8			erspan_ver;	/* ERSPAN version */
+	__u8			dir;	/* direction */
+	__u16			hwid;	/* hwid */
 };
 
 /* IPv6 tunnel */
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 1303d0c44c36..5c9c65f1d5c2 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -553,13 +553,28 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
 				return PACKET_REJECT;
 
 			memcpy(md, pkt_md, sizeof(*md));
+			md->version = ver;
 			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
 			info->options_len = sizeof(*md);
 
 			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
 
 		} else {
-			tunnel->parms.index = ntohl(pkt_md->u.index);
+			tunnel->parms.erspan_ver = ver;
+
+			if (ver == 1) {
+				tunnel->parms.index = ntohl(pkt_md->u.index);
+			} else {
+				u16 md2_flags;
+				u16 dir, hwid;
+
+				md2_flags = ntohs(pkt_md->u.md2.flags);
+				dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
+				hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
+				tunnel->parms.dir = dir;
+				tunnel->parms.hwid = hwid;
+			}
+
 			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
 		}
 
@@ -582,7 +597,8 @@ static int gre_rcv(struct sk_buff *skb)
 	if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))
 		goto drop;
 
-	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN))) {
+	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
+		     tpi.proto == htons(ETH_P_ERSPAN2))) {
 		if (ip6erspan_rcv(skb, hdr_len, &tpi) == PACKET_RCVD)
 			return 0;
 		goto drop;
@@ -927,9 +943,24 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
 		if (!md)
 			goto tx_err;
 
-		erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
-				    ntohl(md->u.index), truncate, false);
-
+		if (md->version == 1) {
+			erspan_build_header(skb,
+					    tunnel_id_to_key32(key->tun_id),
+					    ntohl(md->u.index), truncate,
+					    false);
+		} else if (md->version == 2) {
+			u16 md2_flags;
+			u16 dir, hwid;
+
+			md2_flags = ntohs(md->u.md2.flags);
+			dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
+			hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
+
+			erspan_build_header_v2(skb,
+					       tunnel_id_to_key32(key->tun_id),
+					       dir, hwid, truncate,
+					       false);
+		}
 	} else {
 		switch (skb->protocol) {
 		case htons(ETH_P_IP):
@@ -949,8 +980,15 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
 			break;
 		}
 
-		erspan_build_header(skb, t->parms.o_key, t->parms.index,
-				    truncate, false);
+		if (t->parms.erspan_ver == 1)
+			erspan_build_header(skb, t->parms.o_key,
+					    t->parms.index,
+					    truncate, false);
+		else
+			erspan_build_header_v2(skb, t->parms.o_key,
+					       t->parms.dir,
+					       t->parms.hwid,
+					       truncate, false);
 		fl6.daddr = t->parms.raddr;
 	}
 
@@ -1514,7 +1552,7 @@ static int ip6erspan_tap_validate(struct nlattr *tb[], struct nlattr *data[],
 				  struct netlink_ext_ack *extack)
 {
 	__be16 flags = 0;
-	int ret;
+	int ret, ver = 0;
 
 	if (!data)
 		return 0;
@@ -1543,12 +1581,35 @@ static int ip6erspan_tap_validate(struct nlattr *tb[], struct nlattr *data[],
 	    (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK))
 		return -EINVAL;
 
-	if (data[IFLA_GRE_ERSPAN_INDEX]) {
-		u32 index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
-
-		if (index & ~INDEX_MASK)
+	if (data[IFLA_GRE_ERSPAN_VER]) {
+		ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
+		if (ver != 1 && ver != 2)
 			return -EINVAL;
 	}
+
+	if (ver == 1) {
+		if (data[IFLA_GRE_ERSPAN_INDEX]) {
+			u32 index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
+
+			if (index & ~INDEX_MASK)
+				return -EINVAL;
+		}
+	} else if (ver == 2) {
+		if (data[IFLA_GRE_ERSPAN_DIR]) {
+			u16 dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
+
+			if (dir & ~(DIR_MASK >> DIR_OFFSET))
+				return -EINVAL;
+		}
+
+		if (data[IFLA_GRE_ERSPAN_HWID]) {
+			u16 hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
+
+			if (hwid & ~(HWID_MASK >> HWID_OFFSET))
+				return -EINVAL;
+		}
+	}
+
 	return 0;
 }
 
@@ -1598,11 +1659,21 @@ static void ip6gre_netlink_parms(struct nlattr *data[],
 	if (data[IFLA_GRE_FWMARK])
 		parms->fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
 
-	if (data[IFLA_GRE_ERSPAN_INDEX])
-		parms->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
-
 	if (data[IFLA_GRE_COLLECT_METADATA])
 		parms->collect_md = true;
+
+	if (data[IFLA_GRE_ERSPAN_VER])
+		parms->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
+
+	if (parms->erspan_ver == 1) {
+		if (data[IFLA_GRE_ERSPAN_INDEX])
+			parms->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
+	} else if (parms->erspan_ver == 2) {
+		if (data[IFLA_GRE_ERSPAN_DIR])
+			parms->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
+		if (data[IFLA_GRE_ERSPAN_HWID])
+			parms->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
+	}
 }
 
 static int ip6gre_tap_init(struct net_device *dev)
@@ -1664,7 +1735,7 @@ static int ip6erspan_tap_init(struct net_device *dev)
 
 	tunnel->tun_hlen = 8;
 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-		       sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
+		       erspan_hdr_len(tunnel->parms.erspan_ver);
 	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
 
 	dev->hard_header_len = LL_MAX_HEADER + t_hlen;
@@ -1932,6 +2003,19 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
 			goto nla_put_failure;
 	}
 
+	if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver))
+		goto nla_put_failure;
+
+	if (p->erspan_ver == 1) {
+		if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
+			goto nla_put_failure;
+	} else if (p->erspan_ver == 2) {
+		if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir))
+			goto nla_put_failure;
+		if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid))
+			goto nla_put_failure;
+	}
+
 	return 0;
 
 nla_put_failure:
@@ -1957,6 +2041,9 @@ static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = {
 	[IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG },
 	[IFLA_GRE_FWMARK]       = { .type = NLA_U32 },
 	[IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 },
+	[IFLA_GRE_ERSPAN_VER]	= { .type = NLA_U8 },
+	[IFLA_GRE_ERSPAN_DIR]	= { .type = NLA_U8 },
+	[IFLA_GRE_ERSPAN_HWID]	= { .type = NLA_U16 },
 };
 
 static void ip6erspan_tap_setup(struct net_device *dev)
@@ -2078,4 +2165,5 @@ MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
 MODULE_DESCRIPTION("GRE over IPv6 tunneling device");
 MODULE_ALIAS_RTNL_LINK("ip6gre");
 MODULE_ALIAS_RTNL_LINK("ip6gretap");
+MODULE_ALIAS_RTNL_LINK("ip6erspan");
 MODULE_ALIAS_NETDEV("ip6gre0");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next 4/4] samples/bpf: add erspan v2 sample code
  2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
                   ` (2 preceding siblings ...)
  2017-12-14  0:38 ` [PATCH net-next 3/4] ip6_gre: add erspan v2 support William Tu
@ 2017-12-14  0:38 ` William Tu
  2017-12-14  1:54 ` [PATCH net-next 0/4] ERSPAN version 2 (type III) support Stephen Hemminger
  2017-12-15 17:34 ` David Miller
  5 siblings, 0 replies; 8+ messages in thread
From: William Tu @ 2017-12-14  0:38 UTC (permalink / raw)
  To: netdev

Extend the existing tests for ipv4 ipv6 erspan version 2.

Signed-off-by: William Tu <u9012063@gmail.com>
---
 samples/bpf/tcbpf2_kern.c      | 77 +++++++++++++++++++++++++++++++++++++-----
 samples/bpf/test_tunnel_bpf.sh | 38 +++++++++++++++------
 2 files changed, 96 insertions(+), 19 deletions(-)

diff --git a/samples/bpf/tcbpf2_kern.c b/samples/bpf/tcbpf2_kern.c
index 79ad061079dd..f6bbf8f50da3 100644
--- a/samples/bpf/tcbpf2_kern.c
+++ b/samples/bpf/tcbpf2_kern.c
@@ -35,12 +35,22 @@ struct geneve_opt {
 	u8	opt_data[8]; /* hard-coded to 8 byte */
 };
 
+struct erspan_md2 {
+	__be32 timestamp;
+	__be16 sgt;
+	__be16 flags;
+};
+
 struct vxlan_metadata {
 	u32     gbp;
 };
 
 struct erspan_metadata {
-	__be32 index;
+	union {
+		__be32 index;
+		struct erspan_md2 md2;
+	} u;
+	int version;
 };
 
 SEC("gre_set_tunnel")
@@ -143,7 +153,18 @@ int _erspan_set_tunnel(struct __sk_buff *skb)
 		return TC_ACT_SHOT;
 	}
 
-	md.index = htonl(123);
+	__builtin_memset(&md, 0, sizeof(md));
+#ifdef ERSPAN_V1
+	md.version = 1;
+	md.u.index = htonl(123);
+#else
+	u8 direction = 1;
+	u16 hwid = 7;
+
+	md.version = 2;
+	md.u.md2.flags = htons((direction << 3) | (hwid << 4));
+#endif
+
 	ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
 	if (ret < 0) {
 		ERROR(ret);
@@ -156,7 +177,7 @@ int _erspan_set_tunnel(struct __sk_buff *skb)
 SEC("erspan_get_tunnel")
 int _erspan_get_tunnel(struct __sk_buff *skb)
 {
-	char fmt[] = "key %d remote ip 0x%x erspan index 0x%x\n";
+	char fmt[] = "key %d remote ip 0x%x erspan version %d\n";
 	struct bpf_tunnel_key key;
 	struct erspan_metadata md;
 	u32 index;
@@ -174,9 +195,22 @@ int _erspan_get_tunnel(struct __sk_buff *skb)
 		return TC_ACT_SHOT;
 	}
 
-	index = bpf_ntohl(md.index);
 	bpf_trace_printk(fmt, sizeof(fmt),
-			key.tunnel_id, key.remote_ipv4, index);
+			key.tunnel_id, key.remote_ipv4, md.version);
+
+#ifdef ERSPAN_V1
+	char fmt2[] = "\tindex %x\n";
+
+	index = bpf_ntohl(md.u.index);
+	bpf_trace_printk(fmt2, sizeof(fmt2), index);
+#else
+	char fmt2[] = "\tdirection %d hwid %x timestamp %u\n";
+
+	bpf_trace_printk(fmt2, sizeof(fmt2),
+		(ntohs(md.u.md2.flags) >> 3) & 0x1,
+		(ntohs(md.u.md2.flags) >> 4) & 0x3f,
+		bpf_ntohl(md.u.md2.timestamp));
+#endif
 
 	return TC_ACT_OK;
 }
@@ -201,7 +235,19 @@ int _ip4ip6erspan_set_tunnel(struct __sk_buff *skb)
 		return TC_ACT_SHOT;
 	}
 
-	md.index = htonl(123);
+	__builtin_memset(&md, 0, sizeof(md));
+
+#ifdef ERSPAN_V1
+	md.u.index = htonl(123);
+	md.version = 1;
+#else
+	u8 direction = 0;
+	u16 hwid = 17;
+
+	md.version = 2;
+	md.u.md2.flags = htons((direction << 3) | (hwid << 4));
+#endif
+
 	ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
 	if (ret < 0) {
 		ERROR(ret);
@@ -214,7 +260,7 @@ int _ip4ip6erspan_set_tunnel(struct __sk_buff *skb)
 SEC("ip4ip6erspan_get_tunnel")
 int _ip4ip6erspan_get_tunnel(struct __sk_buff *skb)
 {
-	char fmt[] = "key %d remote ip6 ::%x erspan index 0x%x\n";
+	char fmt[] = "ip6erspan get key %d remote ip6 ::%x erspan version %d\n";
 	struct bpf_tunnel_key key;
 	struct erspan_metadata md;
 	u32 index;
@@ -232,9 +278,22 @@ int _ip4ip6erspan_get_tunnel(struct __sk_buff *skb)
 		return TC_ACT_SHOT;
 	}
 
-	index = bpf_ntohl(md.index);
 	bpf_trace_printk(fmt, sizeof(fmt),
-			key.tunnel_id, key.remote_ipv6[0], index);
+			key.tunnel_id, key.remote_ipv4, md.version);
+
+#ifdef ERSPAN_V1
+	char fmt2[] = "\tindex %x\n";
+
+	index = bpf_ntohl(md.u.index);
+	bpf_trace_printk(fmt2, sizeof(fmt2), index);
+#else
+	char fmt2[] = "\tdirection %d hwid %x timestamp %u\n";
+
+	bpf_trace_printk(fmt2, sizeof(fmt2),
+		(ntohs(md.u.md2.flags) >> 3) & 0x1,
+		(ntohs(md.u.md2.flags) >> 4) & 0x3f,
+		bpf_ntohl(md.u.md2.timestamp));
+#endif
 
 	return TC_ACT_OK;
 }
diff --git a/samples/bpf/test_tunnel_bpf.sh b/samples/bpf/test_tunnel_bpf.sh
index f53efb62f699..ae7f7c38309b 100755
--- a/samples/bpf/test_tunnel_bpf.sh
+++ b/samples/bpf/test_tunnel_bpf.sh
@@ -59,8 +59,17 @@ function add_ip6gretap_tunnel {
 
 function add_erspan_tunnel {
 	# in namespace
-	ip netns exec at_ns0 \
-		ip link add dev $DEV_NS type $TYPE seq key 2 local 172.16.1.100 remote 172.16.1.200 erspan 123
+	if [ "$1" == "v1" ]; then
+		ip netns exec at_ns0 \
+		ip link add dev $DEV_NS type $TYPE seq key 2 \
+		local 172.16.1.100 remote 172.16.1.200 \
+		erspan_ver 1 erspan 123
+	else
+		ip netns exec at_ns0 \
+		ip link add dev $DEV_NS type $TYPE seq key 2 \
+		local 172.16.1.100 remote 172.16.1.200 \
+		erspan_ver 2 erspan_dir 1 erspan_hwid 3
+	fi
 	ip netns exec at_ns0 ip link set dev $DEV_NS up
 	ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
 
@@ -79,10 +88,17 @@ function add_ip6erspan_tunnel {
 	ip link set dev veth1 up
 
 	# in namespace
-	ip netns exec at_ns0 \
-		ip link add dev $DEV_NS type $TYPE seq key 2 erspan 123 \
-		local ::11 remote ::22
-
+	if [ "$1" == "v1" ]; then
+		ip netns exec at_ns0 \
+		ip link add dev $DEV_NS type $TYPE seq key 2 \
+		local ::11 remote ::22 \
+		erspan_ver 1 erspan 123
+	else
+		ip netns exec at_ns0 \
+		ip link add dev $DEV_NS type $TYPE seq key 2 \
+		local ::11 remote ::22 \
+		erspan_ver 2 erspan_dir 1 erspan_hwid 7
+	fi
 	ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
 	ip netns exec at_ns0 ip link set dev $DEV_NS up
 
@@ -199,7 +215,7 @@ function test_erspan {
 	DEV_NS=erspan00
 	DEV=erspan11
 	config_device
-	add_erspan_tunnel
+	add_erspan_tunnel $1
 	attach_bpf $DEV erspan_set_tunnel erspan_get_tunnel
 	ping -c 1 10.1.1.100
 	ip netns exec at_ns0 ping -c 1 10.1.1.200
@@ -211,7 +227,7 @@ function test_ip6erspan {
 	DEV_NS=ip6erspan00
 	DEV=ip6erspan11
 	config_device
-	add_ip6erspan_tunnel
+	add_ip6erspan_tunnel $1
 	attach_bpf $DEV ip4ip6erspan_set_tunnel ip4ip6erspan_get_tunnel
 	ping6 -c 3 ::11
 	ip netns exec at_ns0 ping -c 1 10.1.1.200
@@ -288,9 +304,11 @@ test_ip6gre
 echo "Testing IP6GRETAP tunnel..."
 test_ip6gretap
 echo "Testing ERSPAN tunnel..."
-test_erspan
+test_erspan v1
+test_erspan v2
 echo "Testing IP6ERSPAN tunnel..."
-test_ip6erspan
+test_ip6erspan v1
+test_ip6erspan v2
 echo "Testing VXLAN tunnel..."
 test_vxlan
 echo "Testing GENEVE tunnel..."
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next 0/4] ERSPAN version 2 (type III) support
  2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
                   ` (3 preceding siblings ...)
  2017-12-14  0:38 ` [PATCH net-next 4/4] samples/bpf: add erspan v2 sample code William Tu
@ 2017-12-14  1:54 ` Stephen Hemminger
  2017-12-14  2:08   ` William Tu
  2017-12-15 17:34 ` David Miller
  5 siblings, 1 reply; 8+ messages in thread
From: Stephen Hemminger @ 2017-12-14  1:54 UTC (permalink / raw)
  To: William Tu; +Cc: netdev

On Wed, 13 Dec 2017 16:38:54 -0800
William Tu <u9012063@gmail.com> wrote:

> ERSPAN has two versions, v1 (type II) and v2 (type III).  This patch
> series add support for erspan v2 based on existing erspan v1
> implementation.  The first patch refactors the existing erspan v1's
> header structure, making it extensible to put additional v2's header.
> The second and third patch introduces erspan v2's implementation to
> ipv4 and ipv6 erspan, for both native mode and collect metadata mode.
> Finally, test cases are added under the samples/bpf.
> 
> Note:
> ERSPAN version 2 has many features and this patch does not implement
> all.  One major use case of version 2 over version 1 is its timestamp
> and direction.  So the traffic collector is able to distinguish the
> mirrorred traffic better.  Other features such as SGT (security group
> tag), FT (frame type) for carrying non-ethernet packet, and optional
> subheader are not implemented yet.
> 
> Example commandline for ERSPAN version 2:
> ip link add dev ip6erspan11 type ip6erspan seq key 102 \ 
> 	local fc00:100::2 remote fc00:100::1 \        
> 	erspan_ver 2 erspan_dir 1 erspan_hwid 17
> 
> The corresponding iproute2 patch:
> https://marc.info/?l=linux-netdev&m=151321141525106&w=2


If this is accepted to net-next you will need to
resubmit the iproute2 patch.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next 0/4] ERSPAN version 2 (type III) support
  2017-12-14  1:54 ` [PATCH net-next 0/4] ERSPAN version 2 (type III) support Stephen Hemminger
@ 2017-12-14  2:08   ` William Tu
  0 siblings, 0 replies; 8+ messages in thread
From: William Tu @ 2017-12-14  2:08 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Linux Kernel Network Developers

On Wed, Dec 13, 2017 at 5:54 PM, Stephen Hemminger
<stephen@networkplumber.org> wrote:
> On Wed, 13 Dec 2017 16:38:54 -0800
> William Tu <u9012063@gmail.com> wrote:
>
>> ERSPAN has two versions, v1 (type II) and v2 (type III).  This patch
>> series add support for erspan v2 based on existing erspan v1
>> implementation.  The first patch refactors the existing erspan v1's
>> header structure, making it extensible to put additional v2's header.
>> The second and third patch introduces erspan v2's implementation to
>> ipv4 and ipv6 erspan, for both native mode and collect metadata mode.
>> Finally, test cases are added under the samples/bpf.
>>
>> Note:
>> ERSPAN version 2 has many features and this patch does not implement
>> all.  One major use case of version 2 over version 1 is its timestamp
>> and direction.  So the traffic collector is able to distinguish the
>> mirrorred traffic better.  Other features such as SGT (security group
>> tag), FT (frame type) for carrying non-ethernet packet, and optional
>> subheader are not implemented yet.
>>
>> Example commandline for ERSPAN version 2:
>> ip link add dev ip6erspan11 type ip6erspan seq key 102 \
>>       local fc00:100::2 remote fc00:100::1 \
>>       erspan_ver 2 erspan_dir 1 erspan_hwid 17
>>
>> The corresponding iproute2 patch:
>> https://marc.info/?l=linux-netdev&m=151321141525106&w=2
>
>
> If this is accepted to net-next you will need to
> resubmit the iproute2 patch.

Hi Stephen,
Yes, I noticed that I forgot to update the iproute2 man page. Thanks
William

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next 0/4] ERSPAN version 2 (type III) support
  2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
                   ` (4 preceding siblings ...)
  2017-12-14  1:54 ` [PATCH net-next 0/4] ERSPAN version 2 (type III) support Stephen Hemminger
@ 2017-12-15 17:34 ` David Miller
  5 siblings, 0 replies; 8+ messages in thread
From: David Miller @ 2017-12-15 17:34 UTC (permalink / raw)
  To: u9012063; +Cc: netdev

From: William Tu <u9012063@gmail.com>
Date: Wed, 13 Dec 2017 16:38:54 -0800

> ERSPAN has two versions, v1 (type II) and v2 (type III).  This patch
> series add support for erspan v2 based on existing erspan v1
> implementation.
 ...

Series applied, thanks William.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2017-12-15 17:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-14  0:38 [PATCH net-next 0/4] ERSPAN version 2 (type III) support William Tu
2017-12-14  0:38 ` [PATCH net-next 1/4] net: erspan: refactor existing erspan code William Tu
2017-12-14  0:38 ` [PATCH net-next 2/4] net: erspan: introduce erspan v2 for ip_gre William Tu
2017-12-14  0:38 ` [PATCH net-next 3/4] ip6_gre: add erspan v2 support William Tu
2017-12-14  0:38 ` [PATCH net-next 4/4] samples/bpf: add erspan v2 sample code William Tu
2017-12-14  1:54 ` [PATCH net-next 0/4] ERSPAN version 2 (type III) support Stephen Hemminger
2017-12-14  2:08   ` William Tu
2017-12-15 17:34 ` David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.