diff mbox series

[PATCHv4,net-next,2/2] openvswitch: add erspan version I and II support

Message ID 1516313096-3971-3-git-send-email-u9012063@gmail.com
State Changes Requested, archived
Delegated to: David Miller
Headers show
Series net: erspan: add support for openvswitch | expand

Commit Message

William Tu Jan. 18, 2018, 10:04 p.m. UTC
The patch adds support for openvswitch to configure erspan
v1 and v2.  The OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS attr is added
to uapi as a nested attribute to support all ERSPAN v1 and v2's
fields.  Note taht Previous commit "openvswitch: Add erspan tunnel
support." was reverted since it does not design properly.

Signed-off-by: William Tu <u9012063@gmail.com>
---
 include/uapi/linux/openvswitch.h |  11 +++
 net/openvswitch/flow_netlink.c   | 154 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 164 insertions(+), 1 deletion(-)

Comments

Pravin Shelar Jan. 21, 2018, 5:52 a.m. UTC | #1
On Thu, Jan 18, 2018 at 2:04 PM, William Tu <u9012063@gmail.com> wrote:
> The patch adds support for openvswitch to configure erspan
> v1 and v2.  The OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS attr is added
> to uapi as a nested attribute to support all ERSPAN v1 and v2's
> fields.  Note taht Previous commit "openvswitch: Add erspan tunnel
> support." was reverted since it does not design properly.
>
> Signed-off-by: William Tu <u9012063@gmail.com>
> ---
>  include/uapi/linux/openvswitch.h |  11 +++
>  net/openvswitch/flow_netlink.c   | 154 ++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 164 insertions(+), 1 deletion(-)
>
> diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
> index dcfab5e3b55c..f1f98fd703fe 100644
> --- a/include/uapi/linux/openvswitch.h
> +++ b/include/uapi/linux/openvswitch.h
> @@ -273,6 +273,16 @@ enum {
>
>  #define OVS_VXLAN_EXT_MAX (__OVS_VXLAN_EXT_MAX - 1)
>
> +enum {
> +       OVS_ERSPAN_OPT_UNSPEC,
> +       OVS_ERSPAN_OPT_IDX,     /* u32 index */
> +       OVS_ERSPAN_OPT_VER,     /* u8 version number */
> +       OVS_ERSPAN_OPT_DIR,     /* u8 direction */
> +       OVS_ERSPAN_OPT_HWID,    /* u8 hardware ID */
> +       __OVS_ERSPAN_OPT_MAX,
> +};
> +
> +#define OVS_ERSPAN_OPT_MAX (__OVS_ERSPAN_OPT_MAX - 1)
>
>  /* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
>   */
> @@ -363,6 +373,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,        /* Nested OVS_ERSPAN_OPT_* */
>         __OVS_TUNNEL_KEY_ATTR_MAX
>  };
>
Rather than passing individual members of erspan_metadata, can you
just pass whole structure between kernel and userspace. So that ovs
kernel module can just handle all erspan options as one binary blob
and does not need to interpret it.
William Tu Jan. 22, 2018, 6:12 p.m. UTC | #2
On Sat, Jan 20, 2018 at 9:52 PM, Pravin Shelar <pshelar@ovn.org> wrote:
> On Thu, Jan 18, 2018 at 2:04 PM, William Tu <u9012063@gmail.com> wrote:
>> The patch adds support for openvswitch to configure erspan
>> v1 and v2.  The OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS attr is added
>> to uapi as a nested attribute to support all ERSPAN v1 and v2's
>> fields.  Note taht Previous commit "openvswitch: Add erspan tunnel
>> support." was reverted since it does not design properly.
>>
>> Signed-off-by: William Tu <u9012063@gmail.com>
>> ---
>>  include/uapi/linux/openvswitch.h |  11 +++
>>  net/openvswitch/flow_netlink.c   | 154 ++++++++++++++++++++++++++++++++++++++-
>>  2 files changed, 164 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
>> index dcfab5e3b55c..f1f98fd703fe 100644
>> --- a/include/uapi/linux/openvswitch.h
>> +++ b/include/uapi/linux/openvswitch.h
>> @@ -273,6 +273,16 @@ enum {
>>
>>  #define OVS_VXLAN_EXT_MAX (__OVS_VXLAN_EXT_MAX - 1)
>>
>> +enum {
>> +       OVS_ERSPAN_OPT_UNSPEC,
>> +       OVS_ERSPAN_OPT_IDX,     /* u32 index */
>> +       OVS_ERSPAN_OPT_VER,     /* u8 version number */
>> +       OVS_ERSPAN_OPT_DIR,     /* u8 direction */
>> +       OVS_ERSPAN_OPT_HWID,    /* u8 hardware ID */
>> +       __OVS_ERSPAN_OPT_MAX,
>> +};
>> +
>> +#define OVS_ERSPAN_OPT_MAX (__OVS_ERSPAN_OPT_MAX - 1)
>>
>>  /* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
>>   */
>> @@ -363,6 +373,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,        /* Nested OVS_ERSPAN_OPT_* */
>>         __OVS_TUNNEL_KEY_ATTR_MAX
>>  };
>>
> Rather than passing individual members of erspan_metadata, can you
> just pass whole structure between kernel and userspace. So that ovs
> kernel module can just handle all erspan options as one binary blob
> and does not need to interpret it.

That's a good idea. I will work on it for v5.
Thanks!
William
diff mbox series

Patch

diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index dcfab5e3b55c..f1f98fd703fe 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -273,6 +273,16 @@  enum {
 
 #define OVS_VXLAN_EXT_MAX (__OVS_VXLAN_EXT_MAX - 1)
 
+enum {
+	OVS_ERSPAN_OPT_UNSPEC,
+	OVS_ERSPAN_OPT_IDX,	/* u32 index */
+	OVS_ERSPAN_OPT_VER,	/* u8 version number */
+	OVS_ERSPAN_OPT_DIR,	/* u8 direction */
+	OVS_ERSPAN_OPT_HWID,	/* u8 hardware ID */
+	__OVS_ERSPAN_OPT_MAX,
+};
+
+#define OVS_ERSPAN_OPT_MAX (__OVS_ERSPAN_OPT_MAX - 1)
 
 /* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
  */
@@ -363,6 +373,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,	/* Nested OVS_ERSPAN_OPT_* */
 	__OVS_TUNNEL_KEY_ATTR_MAX
 };
 
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index f143908b651d..c57b96b595b5 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -49,6 +49,7 @@ 
 #include <net/mpls.h>
 #include <net/vxlan.h>
 #include <net/tun_proto.h>
+#include <net/erspan.h>
 
 #include "flow_netlink.h"
 
@@ -329,7 +330,8 @@  size_t ovs_tun_key_attr_size(void)
 		+ nla_total_size(0)    /* OVS_TUNNEL_KEY_ATTR_CSUM */
 		+ nla_total_size(0)    /* OVS_TUNNEL_KEY_ATTR_OAM */
 		+ nla_total_size(256)  /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
-		/* OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS is mutually exclusive with
+		/* OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS and
+		 * OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS is mutually exclusive with
 		 * OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it.
 		 */
 		+ nla_total_size(2)    /* OVS_TUNNEL_KEY_ATTR_TP_SRC */
@@ -384,6 +386,13 @@  static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] =
 	[OVS_VXLAN_EXT_GBP]	    = { .len = sizeof(u32) },
 };
 
+static const struct ovs_len_tbl ovs_erspan_opt_lens[OVS_ERSPAN_OPT_MAX + 1] = {
+	[OVS_ERSPAN_OPT_IDX]	= { .len = sizeof(u32) },
+	[OVS_ERSPAN_OPT_VER]	= { .len = sizeof(u8) },
+	[OVS_ERSPAN_OPT_DIR]	= { .len = sizeof(u8) },
+	[OVS_ERSPAN_OPT_HWID]	= { .len = sizeof(u8) },
+};
+
 static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
 	[OVS_TUNNEL_KEY_ATTR_ID]	    = { .len = sizeof(u64) },
 	[OVS_TUNNEL_KEY_ATTR_IPV4_SRC]	    = { .len = sizeof(u32) },
@@ -400,6 +409,8 @@  static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
 						.next = ovs_vxlan_ext_key_lens },
 	[OVS_TUNNEL_KEY_ATTR_IPV6_SRC]      = { .len = sizeof(struct in6_addr) },
 	[OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = sizeof(struct in6_addr) },
+	[OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS]   = { .len = OVS_ATTR_NESTED,
+						.next = ovs_erspan_opt_lens },
 };
 
 static const struct ovs_len_tbl
@@ -631,6 +642,94 @@  static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
 	return 0;
 }
 
+static int erspan_tun_opt_from_nlattr(const struct nlattr *attr,
+				      struct sw_flow_match *match, bool is_mask,
+				      bool log)
+{
+	unsigned long opt_key_offset;
+	struct erspan_metadata opts;
+	struct nlattr *a;
+	u16 hwid, dir;
+	int rem;
+
+	BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts));
+
+	memset(&opts, 0, sizeof(opts));
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+
+		if (type > OVS_ERSPAN_OPT_MAX) {
+			OVS_NLERR(log, "ERSPAN option %d out of range max %d",
+				  type, OVS_ERSPAN_OPT_MAX);
+			return -EINVAL;
+		}
+
+		if (!check_attr_len(nla_len(a),
+				    ovs_erspan_opt_lens[type].len)) {
+			OVS_NLERR(log, "ERSPAN option %d has unexpected len %d expected %d",
+				  type, nla_len(a),
+				  ovs_erspan_opt_lens[type].len);
+			return -EINVAL;
+		}
+
+		switch (type) {
+		case OVS_ERSPAN_OPT_IDX:
+			opts.u.index = htonl(nla_get_u32(a));
+			if (ntohl(opts.u.index) & ~INDEX_MASK) {
+				OVS_NLERR(log,
+					  "ERSPAN index number %x too large.",
+					  ntohl(opts.u.index));
+				return -EINVAL;
+			}
+			break;
+		case OVS_ERSPAN_OPT_VER:
+			opts.version = nla_get_u8(a);
+			if (opts.version != 1 && opts.version != 2) {
+				OVS_NLERR(log,
+					  "ERSPAN version %d not supported.",
+					  opts.version);
+				return -EINVAL;
+			}
+			break;
+		case OVS_ERSPAN_OPT_DIR:
+			dir = nla_get_u8(a);
+			if (dir != 0 && dir != 1) {
+				OVS_NLERR(log,
+					  "ERSPAN direction %d invalid.",
+					  dir);
+				return -EINVAL;
+			}
+			opts.u.md2.dir = dir;
+			break;
+		case OVS_ERSPAN_OPT_HWID:
+			hwid = nla_get_u8(a);
+			if (hwid & ~(HWID_MASK >> HWID_OFFSET)) {
+				OVS_NLERR(log,
+					  "ERSPAN hardware ID %x invalid.",
+					  hwid);
+				return -EINVAL;
+			}
+			set_hwid(&opts.u.md2, hwid);
+			break;
+		default:
+			OVS_NLERR(log, "Unknown ERSPAN opt attribute %d",
+				  type);
+			return -EINVAL;
+		}
+	}
+	if (rem) {
+		OVS_NLERR(log, "ERSPAN opt message has %d unknown bytes.",
+			  rem);
+		return -EINVAL;
+	}
+
+	SW_FLOW_KEY_PUT(match, tun_opts_len, sizeof(opts), is_mask);
+	opt_key_offset = TUN_METADATA_OFFSET(sizeof(opts));
+	SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, &opts, sizeof(opts),
+				  is_mask);
+	return 0;
+}
+
 static int ip_tun_from_nlattr(const struct nlattr *attr,
 			      struct sw_flow_match *match, bool is_mask,
 			      bool log)
@@ -738,6 +837,20 @@  static int ip_tun_from_nlattr(const struct nlattr *attr,
 			break;
 		case OVS_TUNNEL_KEY_ATTR_PAD:
 			break;
+		case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS:
+			if (opts_type) {
+				OVS_NLERR(log, "Multiple metadata blocks provided");
+				return -EINVAL;
+			}
+
+			err = erspan_tun_opt_from_nlattr(a, match, is_mask,
+							 log);
+			if (err)
+				return err;
+
+			tun_flags |= TUNNEL_ERSPAN_OPT;
+			opts_type = type;
+			break;
 		default:
 			OVS_NLERR(log, "Unknown IP tunnel attribute %d",
 				  type);
@@ -803,6 +916,39 @@  static int vxlan_opt_to_nlattr(struct sk_buff *skb,
 	return 0;
 }
 
+static int erspan_opt_to_nlattr(struct sk_buff *skb,
+				const void *tun_opts, int swkey_tun_opts_len)
+{
+	const struct erspan_metadata *opts = tun_opts;
+	struct nlattr *nla;
+
+	nla = nla_nest_start(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS);
+	if (!nla)
+		return -EMSGSIZE;
+
+	if (nla_put_u8(skb, OVS_ERSPAN_OPT_VER, opts->version) < 0)
+		return -EMSGSIZE;
+
+	if (opts->version == 1) {
+		if (nla_put_u32(skb, OVS_ERSPAN_OPT_IDX, ntohl(opts->u.index)) < 0)
+			return -EMSGSIZE;
+
+	} else if (opts->version == 2) {
+		if (nla_put_u8(skb, OVS_ERSPAN_OPT_DIR,
+			       opts->u.md2.dir) < 0)
+			return -EMSGSIZE;
+
+		if (nla_put_u8(skb, OVS_ERSPAN_OPT_HWID,
+			       get_hwid(&opts->u.md2)) < 0)
+			return -EMSGSIZE;
+	} else {
+		return -EINVAL;
+	}
+
+	nla_nest_end(skb, nla);
+	return 0;
+}
+
 static int __ip_tun_to_nlattr(struct sk_buff *skb,
 			      const struct ip_tunnel_key *output,
 			      const void *tun_opts, int swkey_tun_opts_len,
@@ -862,6 +1008,10 @@  static int __ip_tun_to_nlattr(struct sk_buff *skb,
 		else if (output->tun_flags & TUNNEL_VXLAN_OPT &&
 			 vxlan_opt_to_nlattr(skb, tun_opts, swkey_tun_opts_len))
 			return -EMSGSIZE;
+		else if (output->tun_flags & TUNNEL_ERSPAN_OPT &&
+			 erspan_opt_to_nlattr(skb, tun_opts,
+					      swkey_tun_opts_len))
+			return -EMSGSIZE;
 	}
 
 	return 0;
@@ -2486,6 +2636,8 @@  static int validate_and_copy_set_tun(const struct nlattr *attr,
 			break;
 		case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS:
 			break;
+		case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS:
+			break;
 		}
 	};