aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-01-25 21:39:43 -0500
committerDavid S. Miller <davem@davemloft.net>2018-01-25 21:39:43 -0500
commitf8a23d849518fdc85e07e6da8cdf562e7feec5f8 (patch)
tree636d313b0c2830bcf6a5dc7217dcd331a9e58c9a
parentb89d06ce58f1ebd43d4c491da4a9a9f0f29787d6 (diff)
parentfc1372f89ffe1f58b589643b75f679e452350703 (diff)
Merge branch 'net-erspan-add-support-for-openvswitch'
William Tu says: ==================== net: erspan: add support for openvswitch The first patch refactors the erspan header definitions. Originally, the erspan fields are defined as a group into a __be16 field, and use mask and offset to access each field. This is more costly due to calling ntohs/htons and error-prone. The first patch changes it to use bitfields. The second patch creates erspan.h in UAPI and move the definition 'struct erspan_metadata' to it for later openvswitch to use. The final patch introduces the new OVS tunnel key attribute, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, to program both v1 and v2 erspan tunnel for openvswitch. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/erspan.h123
-rw-r--r--include/uapi/linux/erspan.h52
-rw-r--r--include/uapi/linux/openvswitch.h1
-rw-r--r--net/ipv4/ip_gre.c38
-rw-r--r--net/ipv6/ip6_gre.c36
-rw-r--r--net/openvswitch/flow_netlink.c52
6 files changed, 209 insertions, 93 deletions
diff --git a/include/net/erspan.h b/include/net/erspan.h
index 712ea1b1f4db..5daa4866412b 100644
--- a/include/net/erspan.h
+++ b/include/net/erspan.h
@@ -46,6 +46,8 @@
46 * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB 46 * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
47 */ 47 */
48 48
49#include <uapi/linux/erspan.h>
50
49#define ERSPAN_VERSION 0x1 /* ERSPAN type II */ 51#define ERSPAN_VERSION 0x1 /* ERSPAN type II */
50#define VER_MASK 0xf000 52#define VER_MASK 0xf000
51#define VLAN_MASK 0x0fff 53#define VLAN_MASK 0x0fff
@@ -65,17 +67,8 @@
65#define GRA_MASK 0x0006 67#define GRA_MASK 0x0006
66#define O_MASK 0x0001 68#define O_MASK 0x0001
67 69
68/* ERSPAN version 2 metadata header */ 70#define HWID_OFFSET 4
69struct erspan_md2 { 71#define DIR_OFFSET 3
70 __be32 timestamp;
71 __be16 sgt; /* security group tag */
72 __be16 flags;
73#define P_OFFSET 15
74#define FT_OFFSET 10
75#define HWID_OFFSET 4
76#define DIR_OFFSET 3
77#define GRA_OFFSET 1
78};
79 72
80enum erspan_encap_type { 73enum erspan_encap_type {
81 ERSPAN_ENCAP_NOVLAN = 0x0, /* originally without VLAN tag */ 74 ERSPAN_ENCAP_NOVLAN = 0x0, /* originally without VLAN tag */
@@ -86,24 +79,64 @@ enum erspan_encap_type {
86 79
87#define ERSPAN_V1_MDSIZE 4 80#define ERSPAN_V1_MDSIZE 4
88#define ERSPAN_V2_MDSIZE 8 81#define ERSPAN_V2_MDSIZE 8
89struct erspan_metadata {
90 union {
91 __be32 index; /* Version 1 (type II)*/
92 struct erspan_md2 md2; /* Version 2 (type III) */
93 } u;
94 int version;
95};
96 82
97struct erspan_base_hdr { 83struct erspan_base_hdr {
98 __be16 ver_vlan; 84#if defined(__LITTLE_ENDIAN_BITFIELD)
99#define VER_OFFSET 12 85 __u8 vlan_upper:4,
100 __be16 session_id; 86 ver:4;
101#define COS_OFFSET 13 87 __u8 vlan:8;
102#define EN_OFFSET 11 88 __u8 session_id_upper:2,
103#define BSO_OFFSET EN_OFFSET 89 t:1,
104#define T_OFFSET 10 90 en:2,
91 cos:3;
92 __u8 session_id:8;
93#elif defined(__BIG_ENDIAN_BITFIELD)
94 __u8 ver: 4,
95 vlan_upper:4;
96 __u8 vlan:8;
97 __u8 cos:3,
98 en:2,
99 t:1,
100 session_id_upper:2;
101 __u8 session_id:8;
102#else
103#error "Please fix <asm/byteorder.h>"
104#endif
105}; 105};
106 106
107static inline void set_session_id(struct erspan_base_hdr *ershdr, u16 id)
108{
109 ershdr->session_id = id & 0xff;
110 ershdr->session_id_upper = (id >> 8) & 0x3;
111}
112
113static inline u16 get_session_id(const struct erspan_base_hdr *ershdr)
114{
115 return (ershdr->session_id_upper << 8) + ershdr->session_id;
116}
117
118static inline void set_vlan(struct erspan_base_hdr *ershdr, u16 vlan)
119{
120 ershdr->vlan = vlan & 0xff;
121 ershdr->vlan_upper = (vlan >> 8) & 0xf;
122}
123
124static inline u16 get_vlan(const struct erspan_base_hdr *ershdr)
125{
126 return (ershdr->vlan_upper << 8) + ershdr->vlan;
127}
128
129static inline void set_hwid(struct erspan_md2 *md2, u8 hwid)
130{
131 md2->hwid = hwid & 0xf;
132 md2->hwid_upper = (hwid >> 4) & 0x3;
133}
134
135static inline u8 get_hwid(const struct erspan_md2 *md2)
136{
137 return (md2->hwid_upper << 4) + md2->hwid;
138}
139
107static inline int erspan_hdr_len(int version) 140static inline int erspan_hdr_len(int version)
108{ 141{
109 return sizeof(struct erspan_base_hdr) + 142 return sizeof(struct erspan_base_hdr) +
@@ -120,7 +153,7 @@ static inline u8 tos_to_cos(u8 tos)
120} 153}
121 154
122static inline void erspan_build_header(struct sk_buff *skb, 155static inline void erspan_build_header(struct sk_buff *skb,
123 __be32 id, u32 index, 156 u32 id, u32 index,
124 bool truncate, bool is_ipv4) 157 bool truncate, bool is_ipv4)
125{ 158{
126 struct ethhdr *eth = (struct ethhdr *)skb->data; 159 struct ethhdr *eth = (struct ethhdr *)skb->data;
@@ -154,12 +187,12 @@ static inline void erspan_build_header(struct sk_buff *skb,
154 memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE); 187 memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
155 188
156 /* Build base header */ 189 /* Build base header */
157 ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) | 190 ershdr->ver = ERSPAN_VERSION;
158 (ERSPAN_VERSION << VER_OFFSET)); 191 ershdr->cos = tos_to_cos(tos);
159 ershdr->session_id = htons((u16)(ntohl(id) & ID_MASK) | 192 ershdr->en = enc_type;
160 ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) | 193 ershdr->t = truncate;
161 (enc_type << EN_OFFSET & EN_MASK) | 194 set_vlan(ershdr, vlan_tci);
162 ((truncate << T_OFFSET) & T_MASK)); 195 set_session_id(ershdr, id);
163 196
164 /* Build metadata */ 197 /* Build metadata */
165 ersmd = (struct erspan_metadata *)(ershdr + 1); 198 ersmd = (struct erspan_metadata *)(ershdr + 1);
@@ -187,7 +220,7 @@ static inline __be32 erspan_get_timestamp(void)
187} 220}
188 221
189static inline void erspan_build_header_v2(struct sk_buff *skb, 222static inline void erspan_build_header_v2(struct sk_buff *skb,
190 __be32 id, u8 direction, u16 hwid, 223 u32 id, u8 direction, u16 hwid,
191 bool truncate, bool is_ipv4) 224 bool truncate, bool is_ipv4)
192{ 225{
193 struct ethhdr *eth = (struct ethhdr *)skb->data; 226 struct ethhdr *eth = (struct ethhdr *)skb->data;
@@ -198,7 +231,6 @@ static inline void erspan_build_header_v2(struct sk_buff *skb,
198 __be16 tci; 231 __be16 tci;
199 } *qp; 232 } *qp;
200 u16 vlan_tci = 0; 233 u16 vlan_tci = 0;
201 u16 session_id;
202 u8 gra = 0; /* 100 usec */ 234 u8 gra = 0; /* 100 usec */
203 u8 bso = 0; /* Bad/Short/Oversized */ 235 u8 bso = 0; /* Bad/Short/Oversized */
204 u8 sgt = 0; 236 u8 sgt = 0;
@@ -221,22 +253,23 @@ static inline void erspan_build_header_v2(struct sk_buff *skb,
221 memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE); 253 memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V2_MDSIZE);
222 254
223 /* Build base header */ 255 /* Build base header */
224 ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) | 256 ershdr->ver = ERSPAN_VERSION2;
225 (ERSPAN_VERSION2 << VER_OFFSET)); 257 ershdr->cos = tos_to_cos(tos);
226 session_id = (u16)(ntohl(id) & ID_MASK) | 258 ershdr->en = bso;
227 ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) | 259 ershdr->t = truncate;
228 (bso << BSO_OFFSET & BSO_MASK) | 260 set_vlan(ershdr, vlan_tci);
229 ((truncate << T_OFFSET) & T_MASK); 261 set_session_id(ershdr, id);
230 ershdr->session_id = htons(session_id);
231 262
232 /* Build metadata */ 263 /* Build metadata */
233 md = (struct erspan_metadata *)(ershdr + 1); 264 md = (struct erspan_metadata *)(ershdr + 1);
234 md->u.md2.timestamp = erspan_get_timestamp(); 265 md->u.md2.timestamp = erspan_get_timestamp();
235 md->u.md2.sgt = htons(sgt); 266 md->u.md2.sgt = htons(sgt);
236 md->u.md2.flags = htons(((1 << P_OFFSET) & P_MASK) | 267 md->u.md2.p = 1;
237 ((hwid << HWID_OFFSET) & HWID_MASK) | 268 md->u.md2.ft = 0;
238 ((direction << DIR_OFFSET) & DIR_MASK) | 269 md->u.md2.dir = direction;
239 ((gra << GRA_OFFSET) & GRA_MASK)); 270 md->u.md2.gra = gra;
271 md->u.md2.o = 0;
272 set_hwid(&md->u.md2, hwid);
240} 273}
241 274
242#endif 275#endif
diff --git a/include/uapi/linux/erspan.h b/include/uapi/linux/erspan.h
new file mode 100644
index 000000000000..841573019ae1
--- /dev/null
+++ b/include/uapi/linux/erspan.h
@@ -0,0 +1,52 @@
1/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2/*
3 * ERSPAN Tunnel Metadata
4 *
5 * Copyright (c) 2018 VMware
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
10 *
11 * Userspace API for metadata mode ERSPAN tunnel
12 */
13#ifndef _UAPI_ERSPAN_H
14#define _UAPI_ERSPAN_H
15
16#include <linux/types.h> /* For __beXX in userspace */
17#include <asm/byteorder.h>
18
19/* ERSPAN version 2 metadata header */
20struct erspan_md2 {
21 __be32 timestamp;
22 __be16 sgt; /* security group tag */
23#if defined(__LITTLE_ENDIAN_BITFIELD)
24 __u8 hwid_upper:2,
25 ft:5,
26 p:1;
27 __u8 o:1,
28 gra:2,
29 dir:1,
30 hwid:4;
31#elif defined(__BIG_ENDIAN_BITFIELD)
32 __u8 p:1,
33 ft:5,
34 hwid_upper:2;
35 __u8 hwid:4,
36 dir:1,
37 gra:2,
38 o:1;
39#else
40#error "Please fix <asm/byteorder.h>"
41#endif
42};
43
44struct erspan_metadata {
45 int version;
46 union {
47 __be32 index; /* Version 1 (type II)*/
48 struct erspan_md2 md2; /* Version 2 (type III) */
49 } u;
50};
51
52#endif /* _UAPI_ERSPAN_H */
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index dcfab5e3b55c..713e56ce681f 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -363,6 +363,7 @@ enum ovs_tunnel_key_attr {
363 OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ 363 OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */
364 OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ 364 OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */
365 OVS_TUNNEL_KEY_ATTR_PAD, 365 OVS_TUNNEL_KEY_ATTR_PAD,
366 OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, /* struct erspan_metadata */
366 __OVS_TUNNEL_KEY_ATTR_MAX 367 __OVS_TUNNEL_KEY_ATTR_MAX
367}; 368};
368 369
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index b61f2285816d..6ec670fbbbdd 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -114,7 +114,7 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
114static struct rtnl_link_ops ipgre_link_ops __read_mostly; 114static struct rtnl_link_ops ipgre_link_ops __read_mostly;
115static int ipgre_tunnel_init(struct net_device *dev); 115static int ipgre_tunnel_init(struct net_device *dev);
116static void erspan_build_header(struct sk_buff *skb, 116static void erspan_build_header(struct sk_buff *skb,
117 __be32 id, u32 index, 117 u32 id, u32 index,
118 bool truncate, bool is_ipv4); 118 bool truncate, bool is_ipv4);
119 119
120static unsigned int ipgre_net_id __read_mostly; 120static unsigned int ipgre_net_id __read_mostly;
@@ -273,12 +273,12 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
273 273
274 iph = ip_hdr(skb); 274 iph = ip_hdr(skb);
275 ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len); 275 ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
276 ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET; 276 ver = ershdr->ver;
277 277
278 /* The original GRE header does not have key field, 278 /* The original GRE header does not have key field,
279 * Use ERSPAN 10-bit session ID as key. 279 * Use ERSPAN 10-bit session ID as key.
280 */ 280 */
281 tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK); 281 tpi->key = cpu_to_be32(get_session_id(ershdr));
282 tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, 282 tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
283 tpi->flags | TUNNEL_KEY, 283 tpi->flags | TUNNEL_KEY,
284 iph->saddr, iph->daddr, tpi->key); 284 iph->saddr, iph->daddr, tpi->key);
@@ -324,14 +324,8 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
324 if (ver == 1) { 324 if (ver == 1) {
325 tunnel->index = ntohl(pkt_md->u.index); 325 tunnel->index = ntohl(pkt_md->u.index);
326 } else { 326 } else {
327 u16 md2_flags; 327 tunnel->dir = pkt_md->u.md2.dir;
328 u16 dir, hwid; 328 tunnel->hwid = get_hwid(&pkt_md->u.md2);
329
330 md2_flags = ntohs(pkt_md->u.md2.flags);
331 dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
332 hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
333 tunnel->dir = dir;
334 tunnel->hwid = hwid;
335 } 329 }
336 330
337 } 331 }
@@ -615,19 +609,14 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev,
615 } 609 }
616 610
617 if (version == 1) { 611 if (version == 1) {
618 erspan_build_header(skb, tunnel_id_to_key32(key->tun_id), 612 erspan_build_header(skb, ntohl(tunnel_id_to_key32(key->tun_id)),
619 ntohl(md->u.index), truncate, true); 613 ntohl(md->u.index), truncate, true);
620 } else if (version == 2) { 614 } else if (version == 2) {
621 u16 md2_flags; 615 erspan_build_header_v2(skb,
622 u8 direction; 616 ntohl(tunnel_id_to_key32(key->tun_id)),
623 u16 hwid; 617 md->u.md2.dir,
624 618 get_hwid(&md->u.md2),
625 md2_flags = ntohs(md->u.md2.flags); 619 truncate, true);
626 direction = (md2_flags & DIR_MASK) >> DIR_OFFSET;
627 hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
628
629 erspan_build_header_v2(skb, tunnel_id_to_key32(key->tun_id),
630 direction, hwid, truncate, true);
631 } else { 620 } else {
632 goto err_free_rt; 621 goto err_free_rt;
633 } 622 }
@@ -733,10 +722,11 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
733 722
734 /* Push ERSPAN header */ 723 /* Push ERSPAN header */
735 if (tunnel->erspan_ver == 1) 724 if (tunnel->erspan_ver == 1)
736 erspan_build_header(skb, tunnel->parms.o_key, tunnel->index, 725 erspan_build_header(skb, ntohl(tunnel->parms.o_key),
726 tunnel->index,
737 truncate, true); 727 truncate, true);
738 else 728 else
739 erspan_build_header_v2(skb, tunnel->parms.o_key, 729 erspan_build_header_v2(skb, ntohl(tunnel->parms.o_key),
740 tunnel->dir, tunnel->hwid, 730 tunnel->dir, tunnel->hwid,
741 truncate, true); 731 truncate, true);
742 732
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index a88480193d77..05f070e123e4 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -513,8 +513,8 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
513 513
514 ipv6h = ipv6_hdr(skb); 514 ipv6h = ipv6_hdr(skb);
515 ershdr = (struct erspan_base_hdr *)skb->data; 515 ershdr = (struct erspan_base_hdr *)skb->data;
516 ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET; 516 ver = ershdr->ver;
517 tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK); 517 tpi->key = cpu_to_be32(get_session_id(ershdr));
518 518
519 tunnel = ip6gre_tunnel_lookup(skb->dev, 519 tunnel = ip6gre_tunnel_lookup(skb->dev,
520 &ipv6h->saddr, &ipv6h->daddr, tpi->key, 520 &ipv6h->saddr, &ipv6h->daddr, tpi->key,
@@ -565,14 +565,8 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
565 if (ver == 1) { 565 if (ver == 1) {
566 tunnel->parms.index = ntohl(pkt_md->u.index); 566 tunnel->parms.index = ntohl(pkt_md->u.index);
567 } else { 567 } else {
568 u16 md2_flags; 568 tunnel->parms.dir = pkt_md->u.md2.dir;
569 u16 dir, hwid; 569 tunnel->parms.hwid = get_hwid(&pkt_md->u.md2);
570
571 md2_flags = ntohs(pkt_md->u.md2.flags);
572 dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
573 hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
574 tunnel->parms.dir = dir;
575 tunnel->parms.hwid = hwid;
576 } 570 }
577 571
578 ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error); 572 ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
@@ -925,6 +919,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
925 struct ip_tunnel_info *tun_info; 919 struct ip_tunnel_info *tun_info;
926 const struct ip_tunnel_key *key; 920 const struct ip_tunnel_key *key;
927 struct erspan_metadata *md; 921 struct erspan_metadata *md;
922 __be32 tun_id;
928 923
929 tun_info = skb_tunnel_info(skb); 924 tun_info = skb_tunnel_info(skb);
930 if (unlikely(!tun_info || 925 if (unlikely(!tun_info ||
@@ -944,23 +939,18 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
944 if (!md) 939 if (!md)
945 goto tx_err; 940 goto tx_err;
946 941
942 tun_id = tunnel_id_to_key32(key->tun_id);
947 if (md->version == 1) { 943 if (md->version == 1) {
948 erspan_build_header(skb, 944 erspan_build_header(skb,
949 tunnel_id_to_key32(key->tun_id), 945 ntohl(tun_id),
950 ntohl(md->u.index), truncate, 946 ntohl(md->u.index), truncate,
951 false); 947 false);
952 } else if (md->version == 2) { 948 } else if (md->version == 2) {
953 u16 md2_flags;
954 u16 dir, hwid;
955
956 md2_flags = ntohs(md->u.md2.flags);
957 dir = (md2_flags & DIR_MASK) >> DIR_OFFSET;
958 hwid = (md2_flags & HWID_MASK) >> HWID_OFFSET;
959
960 erspan_build_header_v2(skb, 949 erspan_build_header_v2(skb,
961 tunnel_id_to_key32(key->tun_id), 950 ntohl(tun_id),
962 dir, hwid, truncate, 951 md->u.md2.dir,
963 false); 952 get_hwid(&md->u.md2),
953 truncate, false);
964 } 954 }
965 } else { 955 } else {
966 switch (skb->protocol) { 956 switch (skb->protocol) {
@@ -982,11 +972,11 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
982 } 972 }
983 973
984 if (t->parms.erspan_ver == 1) 974 if (t->parms.erspan_ver == 1)
985 erspan_build_header(skb, t->parms.o_key, 975 erspan_build_header(skb, ntohl(t->parms.o_key),
986 t->parms.index, 976 t->parms.index,
987 truncate, false); 977 truncate, false);
988 else 978 else
989 erspan_build_header_v2(skb, t->parms.o_key, 979 erspan_build_header_v2(skb, ntohl(t->parms.o_key),
990 t->parms.dir, 980 t->parms.dir,
991 t->parms.hwid, 981 t->parms.hwid,
992 truncate, false); 982 truncate, false);
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index eb55f1b3d047..7322aa1e382e 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -49,6 +49,7 @@
49#include <net/mpls.h> 49#include <net/mpls.h>
50#include <net/vxlan.h> 50#include <net/vxlan.h>
51#include <net/tun_proto.h> 51#include <net/tun_proto.h>
52#include <net/erspan.h>
52 53
53#include "flow_netlink.h" 54#include "flow_netlink.h"
54 55
@@ -329,7 +330,8 @@ size_t ovs_tun_key_attr_size(void)
329 + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */ 330 + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
330 + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */ 331 + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
331 + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */ 332 + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
332 /* OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS is mutually exclusive with 333 /* OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS and
334 * OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS is mutually exclusive with
333 * OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it. 335 * OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it.
334 */ 336 */
335 + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */ 337 + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */
@@ -400,6 +402,7 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
400 .next = ovs_vxlan_ext_key_lens }, 402 .next = ovs_vxlan_ext_key_lens },
401 [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 403 [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = sizeof(struct in6_addr) },
402 [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 404 [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = sizeof(struct in6_addr) },
405 [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = OVS_ATTR_VARIABLE },
403}; 406};
404 407
405static const struct ovs_len_tbl 408static const struct ovs_len_tbl
@@ -631,6 +634,33 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr,
631 return 0; 634 return 0;
632} 635}
633 636
637static int erspan_tun_opt_from_nlattr(const struct nlattr *a,
638 struct sw_flow_match *match, bool is_mask,
639 bool log)
640{
641 unsigned long opt_key_offset;
642
643 BUILD_BUG_ON(sizeof(struct erspan_metadata) >
644 sizeof(match->key->tun_opts));
645
646 if (nla_len(a) > sizeof(match->key->tun_opts)) {
647 OVS_NLERR(log, "ERSPAN option length err (len %d, max %zu).",
648 nla_len(a), sizeof(match->key->tun_opts));
649 return -EINVAL;
650 }
651
652 if (!is_mask)
653 SW_FLOW_KEY_PUT(match, tun_opts_len,
654 sizeof(struct erspan_metadata), false);
655 else
656 SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff, true);
657
658 opt_key_offset = TUN_METADATA_OFFSET(nla_len(a));
659 SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, nla_data(a),
660 nla_len(a), is_mask);
661 return 0;
662}
663
634static int ip_tun_from_nlattr(const struct nlattr *attr, 664static int ip_tun_from_nlattr(const struct nlattr *attr,
635 struct sw_flow_match *match, bool is_mask, 665 struct sw_flow_match *match, bool is_mask,
636 bool log) 666 bool log)
@@ -738,6 +768,20 @@ static int ip_tun_from_nlattr(const struct nlattr *attr,
738 break; 768 break;
739 case OVS_TUNNEL_KEY_ATTR_PAD: 769 case OVS_TUNNEL_KEY_ATTR_PAD:
740 break; 770 break;
771 case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS:
772 if (opts_type) {
773 OVS_NLERR(log, "Multiple metadata blocks provided");
774 return -EINVAL;
775 }
776
777 err = erspan_tun_opt_from_nlattr(a, match, is_mask,
778 log);
779 if (err)
780 return err;
781
782 tun_flags |= TUNNEL_ERSPAN_OPT;
783 opts_type = type;
784 break;
741 default: 785 default:
742 OVS_NLERR(log, "Unknown IP tunnel attribute %d", 786 OVS_NLERR(log, "Unknown IP tunnel attribute %d",
743 type); 787 type);
@@ -862,6 +906,10 @@ static int __ip_tun_to_nlattr(struct sk_buff *skb,
862 else if (output->tun_flags & TUNNEL_VXLAN_OPT && 906 else if (output->tun_flags & TUNNEL_VXLAN_OPT &&
863 vxlan_opt_to_nlattr(skb, tun_opts, swkey_tun_opts_len)) 907 vxlan_opt_to_nlattr(skb, tun_opts, swkey_tun_opts_len))
864 return -EMSGSIZE; 908 return -EMSGSIZE;
909 else if (output->tun_flags & TUNNEL_ERSPAN_OPT &&
910 nla_put(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,
911 swkey_tun_opts_len, tun_opts))
912 return -EMSGSIZE;
865 } 913 }
866 914
867 return 0; 915 return 0;
@@ -2486,6 +2534,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
2486 break; 2534 break;
2487 case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: 2535 case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS:
2488 break; 2536 break;
2537 case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS:
2538 break;
2489 } 2539 }
2490 } 2540 }
2491 2541