xref: /openbmc/linux/net/ipv6/ip6_gre.c (revision 1e952e95843d437b8a904dbd5b48d72db8ac23ec)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c12b395aSxeb@mail.ru /*
3c12b395aSxeb@mail.ru  *	GRE over IPv6 protocol decoder.
4c12b395aSxeb@mail.ru  *
5c12b395aSxeb@mail.ru  *	Authors: Dmitry Kozlov (xeb@mail.ru)
6c12b395aSxeb@mail.ru  */
7c12b395aSxeb@mail.ru 
8c12b395aSxeb@mail.ru #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9c12b395aSxeb@mail.ru 
10c12b395aSxeb@mail.ru #include <linux/capability.h>
11c12b395aSxeb@mail.ru #include <linux/module.h>
12c12b395aSxeb@mail.ru #include <linux/types.h>
13c12b395aSxeb@mail.ru #include <linux/kernel.h>
14c12b395aSxeb@mail.ru #include <linux/slab.h>
15c12b395aSxeb@mail.ru #include <linux/uaccess.h>
16c12b395aSxeb@mail.ru #include <linux/skbuff.h>
17c12b395aSxeb@mail.ru #include <linux/netdevice.h>
18c12b395aSxeb@mail.ru #include <linux/in.h>
19c12b395aSxeb@mail.ru #include <linux/tcp.h>
20c12b395aSxeb@mail.ru #include <linux/udp.h>
21c12b395aSxeb@mail.ru #include <linux/if_arp.h>
22c12b395aSxeb@mail.ru #include <linux/init.h>
23c12b395aSxeb@mail.ru #include <linux/in6.h>
24c12b395aSxeb@mail.ru #include <linux/inetdevice.h>
25c12b395aSxeb@mail.ru #include <linux/igmp.h>
26c12b395aSxeb@mail.ru #include <linux/netfilter_ipv4.h>
27c12b395aSxeb@mail.ru #include <linux/etherdevice.h>
28c12b395aSxeb@mail.ru #include <linux/if_ether.h>
29c12b395aSxeb@mail.ru #include <linux/hash.h>
30c12b395aSxeb@mail.ru #include <linux/if_tunnel.h>
31c12b395aSxeb@mail.ru #include <linux/ip6_tunnel.h>
32c12b395aSxeb@mail.ru 
33c12b395aSxeb@mail.ru #include <net/sock.h>
34c12b395aSxeb@mail.ru #include <net/ip.h>
35c5441932SPravin B Shelar #include <net/ip_tunnels.h>
36c12b395aSxeb@mail.ru #include <net/icmp.h>
37c12b395aSxeb@mail.ru #include <net/protocol.h>
38c12b395aSxeb@mail.ru #include <net/addrconf.h>
39c12b395aSxeb@mail.ru #include <net/arp.h>
40c12b395aSxeb@mail.ru #include <net/checksum.h>
41c12b395aSxeb@mail.ru #include <net/dsfield.h>
42c12b395aSxeb@mail.ru #include <net/inet_ecn.h>
43c12b395aSxeb@mail.ru #include <net/xfrm.h>
44c12b395aSxeb@mail.ru #include <net/net_namespace.h>
45c12b395aSxeb@mail.ru #include <net/netns/generic.h>
46c12b395aSxeb@mail.ru #include <net/rtnetlink.h>
47c12b395aSxeb@mail.ru 
48c12b395aSxeb@mail.ru #include <net/ipv6.h>
49c12b395aSxeb@mail.ru #include <net/ip6_fib.h>
50c12b395aSxeb@mail.ru #include <net/ip6_route.h>
51c12b395aSxeb@mail.ru #include <net/ip6_tunnel.h>
52308edfdfSTom Herbert #include <net/gre.h>
535a963eb6SWilliam Tu #include <net/erspan.h>
546712abc1SWilliam Tu #include <net/dst_metadata.h>
55c12b395aSxeb@mail.ru 
56c12b395aSxeb@mail.ru 
57eccc1bb8Sstephen hemminger static bool log_ecn_error = true;
58eccc1bb8Sstephen hemminger module_param(log_ecn_error, bool, 0644);
59eccc1bb8Sstephen hemminger MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
60eccc1bb8Sstephen hemminger 
61e87a8f24SJiri Kosina #define IP6_GRE_HASH_SIZE_SHIFT  5
62e87a8f24SJiri Kosina #define IP6_GRE_HASH_SIZE (1 << IP6_GRE_HASH_SIZE_SHIFT)
63c12b395aSxeb@mail.ru 
64c7d03a00SAlexey Dobriyan static unsigned int ip6gre_net_id __read_mostly;
65c12b395aSxeb@mail.ru struct ip6gre_net {
66e87a8f24SJiri Kosina 	struct ip6_tnl __rcu *tunnels[4][IP6_GRE_HASH_SIZE];
67c12b395aSxeb@mail.ru 
686712abc1SWilliam Tu 	struct ip6_tnl __rcu *collect_md_tun;
69b80d0b93SWilliam Tu 	struct ip6_tnl __rcu *collect_md_tun_erspan;
70c12b395aSxeb@mail.ru 	struct net_device *fb_tunnel_dev;
71c12b395aSxeb@mail.ru };
72c12b395aSxeb@mail.ru 
73c12b395aSxeb@mail.ru static struct rtnl_link_ops ip6gre_link_ops __read_mostly;
7422f08069SNicolas Dichtel static struct rtnl_link_ops ip6gre_tap_ops __read_mostly;
755a963eb6SWilliam Tu static struct rtnl_link_ops ip6erspan_tap_ops __read_mostly;
76c12b395aSxeb@mail.ru static int ip6gre_tunnel_init(struct net_device *dev);
77c12b395aSxeb@mail.ru static void ip6gre_tunnel_setup(struct net_device *dev);
78c12b395aSxeb@mail.ru static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
79c12b395aSxeb@mail.ru static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu);
802d665034SPetr Machata static void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu);
81c12b395aSxeb@mail.ru 
82c12b395aSxeb@mail.ru /* Tunnel hash table */
83c12b395aSxeb@mail.ru 
84c12b395aSxeb@mail.ru /*
85c12b395aSxeb@mail.ru    4 hash tables:
86c12b395aSxeb@mail.ru 
87c12b395aSxeb@mail.ru    3: (remote,local)
88c12b395aSxeb@mail.ru    2: (remote,*)
89c12b395aSxeb@mail.ru    1: (*,local)
90c12b395aSxeb@mail.ru    0: (*,*)
91c12b395aSxeb@mail.ru 
92c12b395aSxeb@mail.ru    We require exact key match i.e. if a key is present in packet
93c12b395aSxeb@mail.ru    it will match only tunnel with the same key; if it is not present,
94c12b395aSxeb@mail.ru    it will match only keyless tunnel.
95c12b395aSxeb@mail.ru 
96c12b395aSxeb@mail.ru    All keysless packets, if not matched configured keyless tunnels
97c12b395aSxeb@mail.ru    will match fallback tunnel.
98c12b395aSxeb@mail.ru  */
99c12b395aSxeb@mail.ru 
100e87a8f24SJiri Kosina #define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(IP6_GRE_HASH_SIZE - 1))
HASH_ADDR(const struct in6_addr * addr)101c12b395aSxeb@mail.ru static u32 HASH_ADDR(const struct in6_addr *addr)
102c12b395aSxeb@mail.ru {
103c12b395aSxeb@mail.ru 	u32 hash = ipv6_addr_hash(addr);
104c12b395aSxeb@mail.ru 
105e87a8f24SJiri Kosina 	return hash_32(hash, IP6_GRE_HASH_SIZE_SHIFT);
106c12b395aSxeb@mail.ru }
107c12b395aSxeb@mail.ru 
108c12b395aSxeb@mail.ru #define tunnels_r_l	tunnels[3]
109c12b395aSxeb@mail.ru #define tunnels_r	tunnels[2]
110c12b395aSxeb@mail.ru #define tunnels_l	tunnels[1]
111c12b395aSxeb@mail.ru #define tunnels_wc	tunnels[0]
112c12b395aSxeb@mail.ru 
113c12b395aSxeb@mail.ru /* Given src, dst and key, find appropriate for input tunnel. */
114c12b395aSxeb@mail.ru 
ip6gre_tunnel_lookup(struct net_device * dev,const struct in6_addr * remote,const struct in6_addr * local,__be32 key,__be16 gre_proto)115c12b395aSxeb@mail.ru static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
116c12b395aSxeb@mail.ru 		const struct in6_addr *remote, const struct in6_addr *local,
117c12b395aSxeb@mail.ru 		__be32 key, __be16 gre_proto)
118c12b395aSxeb@mail.ru {
119c12b395aSxeb@mail.ru 	struct net *net = dev_net(dev);
120c12b395aSxeb@mail.ru 	int link = dev->ifindex;
121c12b395aSxeb@mail.ru 	unsigned int h0 = HASH_ADDR(remote);
122c12b395aSxeb@mail.ru 	unsigned int h1 = HASH_KEY(key);
123c12b395aSxeb@mail.ru 	struct ip6_tnl *t, *cand = NULL;
124c12b395aSxeb@mail.ru 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
1255a963eb6SWilliam Tu 	int dev_type = (gre_proto == htons(ETH_P_TEB) ||
1263b04caabSWilliam Tu 			gre_proto == htons(ETH_P_ERSPAN) ||
1273b04caabSWilliam Tu 			gre_proto == htons(ETH_P_ERSPAN2)) ?
128c12b395aSxeb@mail.ru 		       ARPHRD_ETHER : ARPHRD_IP6GRE;
129c12b395aSxeb@mail.ru 	int score, cand_score = 4;
130dafabb65STaehee Yoo 	struct net_device *ndev;
131c12b395aSxeb@mail.ru 
132e086cadcSAmerigo Wang 	for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) {
133c12b395aSxeb@mail.ru 		if (!ipv6_addr_equal(local, &t->parms.laddr) ||
134c12b395aSxeb@mail.ru 		    !ipv6_addr_equal(remote, &t->parms.raddr) ||
135c12b395aSxeb@mail.ru 		    key != t->parms.i_key ||
136c12b395aSxeb@mail.ru 		    !(t->dev->flags & IFF_UP))
137c12b395aSxeb@mail.ru 			continue;
138c12b395aSxeb@mail.ru 
139c12b395aSxeb@mail.ru 		if (t->dev->type != ARPHRD_IP6GRE &&
140c12b395aSxeb@mail.ru 		    t->dev->type != dev_type)
141c12b395aSxeb@mail.ru 			continue;
142c12b395aSxeb@mail.ru 
143c12b395aSxeb@mail.ru 		score = 0;
144c12b395aSxeb@mail.ru 		if (t->parms.link != link)
145c12b395aSxeb@mail.ru 			score |= 1;
146c12b395aSxeb@mail.ru 		if (t->dev->type != dev_type)
147c12b395aSxeb@mail.ru 			score |= 2;
148c12b395aSxeb@mail.ru 		if (score == 0)
149c12b395aSxeb@mail.ru 			return t;
150c12b395aSxeb@mail.ru 
151c12b395aSxeb@mail.ru 		if (score < cand_score) {
152c12b395aSxeb@mail.ru 			cand = t;
153c12b395aSxeb@mail.ru 			cand_score = score;
154c12b395aSxeb@mail.ru 		}
155c12b395aSxeb@mail.ru 	}
156c12b395aSxeb@mail.ru 
157e086cadcSAmerigo Wang 	for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) {
158c12b395aSxeb@mail.ru 		if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
159c12b395aSxeb@mail.ru 		    key != t->parms.i_key ||
160c12b395aSxeb@mail.ru 		    !(t->dev->flags & IFF_UP))
161c12b395aSxeb@mail.ru 			continue;
162c12b395aSxeb@mail.ru 
163c12b395aSxeb@mail.ru 		if (t->dev->type != ARPHRD_IP6GRE &&
164c12b395aSxeb@mail.ru 		    t->dev->type != dev_type)
165c12b395aSxeb@mail.ru 			continue;
166c12b395aSxeb@mail.ru 
167c12b395aSxeb@mail.ru 		score = 0;
168c12b395aSxeb@mail.ru 		if (t->parms.link != link)
169c12b395aSxeb@mail.ru 			score |= 1;
170c12b395aSxeb@mail.ru 		if (t->dev->type != dev_type)
171c12b395aSxeb@mail.ru 			score |= 2;
172c12b395aSxeb@mail.ru 		if (score == 0)
173c12b395aSxeb@mail.ru 			return t;
174c12b395aSxeb@mail.ru 
175c12b395aSxeb@mail.ru 		if (score < cand_score) {
176c12b395aSxeb@mail.ru 			cand = t;
177c12b395aSxeb@mail.ru 			cand_score = score;
178c12b395aSxeb@mail.ru 		}
179c12b395aSxeb@mail.ru 	}
180c12b395aSxeb@mail.ru 
181e086cadcSAmerigo Wang 	for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) {
182c12b395aSxeb@mail.ru 		if ((!ipv6_addr_equal(local, &t->parms.laddr) &&
183c12b395aSxeb@mail.ru 			  (!ipv6_addr_equal(local, &t->parms.raddr) ||
184c12b395aSxeb@mail.ru 				 !ipv6_addr_is_multicast(local))) ||
185c12b395aSxeb@mail.ru 		    key != t->parms.i_key ||
186c12b395aSxeb@mail.ru 		    !(t->dev->flags & IFF_UP))
187c12b395aSxeb@mail.ru 			continue;
188c12b395aSxeb@mail.ru 
189c12b395aSxeb@mail.ru 		if (t->dev->type != ARPHRD_IP6GRE &&
190c12b395aSxeb@mail.ru 		    t->dev->type != dev_type)
191c12b395aSxeb@mail.ru 			continue;
192c12b395aSxeb@mail.ru 
193c12b395aSxeb@mail.ru 		score = 0;
194c12b395aSxeb@mail.ru 		if (t->parms.link != link)
195c12b395aSxeb@mail.ru 			score |= 1;
196c12b395aSxeb@mail.ru 		if (t->dev->type != dev_type)
197c12b395aSxeb@mail.ru 			score |= 2;
198c12b395aSxeb@mail.ru 		if (score == 0)
199c12b395aSxeb@mail.ru 			return t;
200c12b395aSxeb@mail.ru 
201c12b395aSxeb@mail.ru 		if (score < cand_score) {
202c12b395aSxeb@mail.ru 			cand = t;
203c12b395aSxeb@mail.ru 			cand_score = score;
204c12b395aSxeb@mail.ru 		}
205c12b395aSxeb@mail.ru 	}
206c12b395aSxeb@mail.ru 
207e086cadcSAmerigo Wang 	for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) {
208c12b395aSxeb@mail.ru 		if (t->parms.i_key != key ||
209c12b395aSxeb@mail.ru 		    !(t->dev->flags & IFF_UP))
210c12b395aSxeb@mail.ru 			continue;
211c12b395aSxeb@mail.ru 
212c12b395aSxeb@mail.ru 		if (t->dev->type != ARPHRD_IP6GRE &&
213c12b395aSxeb@mail.ru 		    t->dev->type != dev_type)
214c12b395aSxeb@mail.ru 			continue;
215c12b395aSxeb@mail.ru 
216c12b395aSxeb@mail.ru 		score = 0;
217c12b395aSxeb@mail.ru 		if (t->parms.link != link)
218c12b395aSxeb@mail.ru 			score |= 1;
219c12b395aSxeb@mail.ru 		if (t->dev->type != dev_type)
220c12b395aSxeb@mail.ru 			score |= 2;
221c12b395aSxeb@mail.ru 		if (score == 0)
222c12b395aSxeb@mail.ru 			return t;
223c12b395aSxeb@mail.ru 
224c12b395aSxeb@mail.ru 		if (score < cand_score) {
225c12b395aSxeb@mail.ru 			cand = t;
226c12b395aSxeb@mail.ru 			cand_score = score;
227c12b395aSxeb@mail.ru 		}
228c12b395aSxeb@mail.ru 	}
229c12b395aSxeb@mail.ru 
23053b24b8fSIan Morris 	if (cand)
231c12b395aSxeb@mail.ru 		return cand;
232c12b395aSxeb@mail.ru 
233b80d0b93SWilliam Tu 	if (gre_proto == htons(ETH_P_ERSPAN) ||
234b80d0b93SWilliam Tu 	    gre_proto == htons(ETH_P_ERSPAN2))
235b80d0b93SWilliam Tu 		t = rcu_dereference(ign->collect_md_tun_erspan);
236b80d0b93SWilliam Tu 	else
2376712abc1SWilliam Tu 		t = rcu_dereference(ign->collect_md_tun);
238b80d0b93SWilliam Tu 
2396712abc1SWilliam Tu 	if (t && t->dev->flags & IFF_UP)
2406712abc1SWilliam Tu 		return t;
2416712abc1SWilliam Tu 
242dafabb65STaehee Yoo 	ndev = READ_ONCE(ign->fb_tunnel_dev);
243dafabb65STaehee Yoo 	if (ndev && ndev->flags & IFF_UP)
244dafabb65STaehee Yoo 		return netdev_priv(ndev);
245c12b395aSxeb@mail.ru 
246c12b395aSxeb@mail.ru 	return NULL;
247c12b395aSxeb@mail.ru }
248c12b395aSxeb@mail.ru 
__ip6gre_bucket(struct ip6gre_net * ign,const struct __ip6_tnl_parm * p)249c12b395aSxeb@mail.ru static struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign,
250c12b395aSxeb@mail.ru 		const struct __ip6_tnl_parm *p)
251c12b395aSxeb@mail.ru {
252c12b395aSxeb@mail.ru 	const struct in6_addr *remote = &p->raddr;
253c12b395aSxeb@mail.ru 	const struct in6_addr *local = &p->laddr;
254c12b395aSxeb@mail.ru 	unsigned int h = HASH_KEY(p->i_key);
255c12b395aSxeb@mail.ru 	int prio = 0;
256c12b395aSxeb@mail.ru 
257c12b395aSxeb@mail.ru 	if (!ipv6_addr_any(local))
258c12b395aSxeb@mail.ru 		prio |= 1;
259c12b395aSxeb@mail.ru 	if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) {
260c12b395aSxeb@mail.ru 		prio |= 2;
261c12b395aSxeb@mail.ru 		h ^= HASH_ADDR(remote);
262c12b395aSxeb@mail.ru 	}
263c12b395aSxeb@mail.ru 
264c12b395aSxeb@mail.ru 	return &ign->tunnels[prio][h];
265c12b395aSxeb@mail.ru }
266c12b395aSxeb@mail.ru 
ip6gre_tunnel_link_md(struct ip6gre_net * ign,struct ip6_tnl * t)267b80d0b93SWilliam Tu static void ip6gre_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
268b80d0b93SWilliam Tu {
269b80d0b93SWilliam Tu 	if (t->parms.collect_md)
270b80d0b93SWilliam Tu 		rcu_assign_pointer(ign->collect_md_tun, t);
271b80d0b93SWilliam Tu }
272b80d0b93SWilliam Tu 
ip6erspan_tunnel_link_md(struct ip6gre_net * ign,struct ip6_tnl * t)273b80d0b93SWilliam Tu static void ip6erspan_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
274b80d0b93SWilliam Tu {
275b80d0b93SWilliam Tu 	if (t->parms.collect_md)
276b80d0b93SWilliam Tu 		rcu_assign_pointer(ign->collect_md_tun_erspan, t);
277b80d0b93SWilliam Tu }
278b80d0b93SWilliam Tu 
ip6gre_tunnel_unlink_md(struct ip6gre_net * ign,struct ip6_tnl * t)279b80d0b93SWilliam Tu static void ip6gre_tunnel_unlink_md(struct ip6gre_net *ign, struct ip6_tnl *t)
280b80d0b93SWilliam Tu {
281b80d0b93SWilliam Tu 	if (t->parms.collect_md)
282b80d0b93SWilliam Tu 		rcu_assign_pointer(ign->collect_md_tun, NULL);
283b80d0b93SWilliam Tu }
284b80d0b93SWilliam Tu 
ip6erspan_tunnel_unlink_md(struct ip6gre_net * ign,struct ip6_tnl * t)285b80d0b93SWilliam Tu static void ip6erspan_tunnel_unlink_md(struct ip6gre_net *ign,
286b80d0b93SWilliam Tu 				       struct ip6_tnl *t)
287b80d0b93SWilliam Tu {
288b80d0b93SWilliam Tu 	if (t->parms.collect_md)
289b80d0b93SWilliam Tu 		rcu_assign_pointer(ign->collect_md_tun_erspan, NULL);
290b80d0b93SWilliam Tu }
291b80d0b93SWilliam Tu 
ip6gre_bucket(struct ip6gre_net * ign,const struct ip6_tnl * t)292c12b395aSxeb@mail.ru static inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign,
293c12b395aSxeb@mail.ru 		const struct ip6_tnl *t)
294c12b395aSxeb@mail.ru {
295c12b395aSxeb@mail.ru 	return __ip6gre_bucket(ign, &t->parms);
296c12b395aSxeb@mail.ru }
297c12b395aSxeb@mail.ru 
ip6gre_tunnel_link(struct ip6gre_net * ign,struct ip6_tnl * t)298c12b395aSxeb@mail.ru static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t)
299c12b395aSxeb@mail.ru {
300c12b395aSxeb@mail.ru 	struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t);
301c12b395aSxeb@mail.ru 
302c12b395aSxeb@mail.ru 	rcu_assign_pointer(t->next, rtnl_dereference(*tp));
303c12b395aSxeb@mail.ru 	rcu_assign_pointer(*tp, t);
304c12b395aSxeb@mail.ru }
305c12b395aSxeb@mail.ru 
ip6gre_tunnel_unlink(struct ip6gre_net * ign,struct ip6_tnl * t)306c12b395aSxeb@mail.ru static void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t)
307c12b395aSxeb@mail.ru {
308c12b395aSxeb@mail.ru 	struct ip6_tnl __rcu **tp;
309c12b395aSxeb@mail.ru 	struct ip6_tnl *iter;
310c12b395aSxeb@mail.ru 
311c12b395aSxeb@mail.ru 	for (tp = ip6gre_bucket(ign, t);
312c12b395aSxeb@mail.ru 	     (iter = rtnl_dereference(*tp)) != NULL;
313c12b395aSxeb@mail.ru 	     tp = &iter->next) {
314c12b395aSxeb@mail.ru 		if (t == iter) {
315c12b395aSxeb@mail.ru 			rcu_assign_pointer(*tp, t->next);
316c12b395aSxeb@mail.ru 			break;
317c12b395aSxeb@mail.ru 		}
318c12b395aSxeb@mail.ru 	}
319c12b395aSxeb@mail.ru }
320c12b395aSxeb@mail.ru 
ip6gre_tunnel_find(struct net * net,const struct __ip6_tnl_parm * parms,int type)321c12b395aSxeb@mail.ru static struct ip6_tnl *ip6gre_tunnel_find(struct net *net,
322c12b395aSxeb@mail.ru 					   const struct __ip6_tnl_parm *parms,
323c12b395aSxeb@mail.ru 					   int type)
324c12b395aSxeb@mail.ru {
325c12b395aSxeb@mail.ru 	const struct in6_addr *remote = &parms->raddr;
326c12b395aSxeb@mail.ru 	const struct in6_addr *local = &parms->laddr;
327c12b395aSxeb@mail.ru 	__be32 key = parms->i_key;
328c12b395aSxeb@mail.ru 	int link = parms->link;
329c12b395aSxeb@mail.ru 	struct ip6_tnl *t;
330c12b395aSxeb@mail.ru 	struct ip6_tnl __rcu **tp;
331c12b395aSxeb@mail.ru 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
332c12b395aSxeb@mail.ru 
333c12b395aSxeb@mail.ru 	for (tp = __ip6gre_bucket(ign, parms);
334c12b395aSxeb@mail.ru 	     (t = rtnl_dereference(*tp)) != NULL;
335c12b395aSxeb@mail.ru 	     tp = &t->next)
336c12b395aSxeb@mail.ru 		if (ipv6_addr_equal(local, &t->parms.laddr) &&
337c12b395aSxeb@mail.ru 		    ipv6_addr_equal(remote, &t->parms.raddr) &&
338c12b395aSxeb@mail.ru 		    key == t->parms.i_key &&
339c12b395aSxeb@mail.ru 		    link == t->parms.link &&
340c12b395aSxeb@mail.ru 		    type == t->dev->type)
341c12b395aSxeb@mail.ru 			break;
342c12b395aSxeb@mail.ru 
343c12b395aSxeb@mail.ru 	return t;
344c12b395aSxeb@mail.ru }
345c12b395aSxeb@mail.ru 
ip6gre_tunnel_locate(struct net * net,const struct __ip6_tnl_parm * parms,int create)346c12b395aSxeb@mail.ru static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net,
347c12b395aSxeb@mail.ru 		const struct __ip6_tnl_parm *parms, int create)
348c12b395aSxeb@mail.ru {
349c12b395aSxeb@mail.ru 	struct ip6_tnl *t, *nt;
350c12b395aSxeb@mail.ru 	struct net_device *dev;
351c12b395aSxeb@mail.ru 	char name[IFNAMSIZ];
352c12b395aSxeb@mail.ru 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
353c12b395aSxeb@mail.ru 
354c12b395aSxeb@mail.ru 	t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE);
355cd0a0bd9SSteffen Klassert 	if (t && create)
356cd0a0bd9SSteffen Klassert 		return NULL;
357c12b395aSxeb@mail.ru 	if (t || !create)
358c12b395aSxeb@mail.ru 		return t;
359c12b395aSxeb@mail.ru 
3605f42df01SEric Dumazet 	if (parms->name[0]) {
3615f42df01SEric Dumazet 		if (!dev_valid_name(parms->name))
3625f42df01SEric Dumazet 			return NULL;
3637574cc58SWolfram Sang 		strscpy(name, parms->name, IFNAMSIZ);
3645f42df01SEric Dumazet 	} else {
365c12b395aSxeb@mail.ru 		strcpy(name, "ip6gre%d");
3665f42df01SEric Dumazet 	}
367c835a677STom Gundersen 	dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,
368c835a677STom Gundersen 			   ip6gre_tunnel_setup);
369c12b395aSxeb@mail.ru 	if (!dev)
370c12b395aSxeb@mail.ru 		return NULL;
371c12b395aSxeb@mail.ru 
372c12b395aSxeb@mail.ru 	dev_net_set(dev, net);
373c12b395aSxeb@mail.ru 
374c12b395aSxeb@mail.ru 	nt = netdev_priv(dev);
375c12b395aSxeb@mail.ru 	nt->parms = *parms;
376c12b395aSxeb@mail.ru 	dev->rtnl_link_ops = &ip6gre_link_ops;
377c12b395aSxeb@mail.ru 
378c12b395aSxeb@mail.ru 	nt->dev = dev;
3790bd87628SNicolas Dichtel 	nt->net = dev_net(dev);
380c12b395aSxeb@mail.ru 
381c12b395aSxeb@mail.ru 	if (register_netdevice(dev) < 0)
382c12b395aSxeb@mail.ru 		goto failed_free;
383c12b395aSxeb@mail.ru 
384128bb975SAlexey Kodanev 	ip6gre_tnl_link_config(nt, 1);
385c12b395aSxeb@mail.ru 	ip6gre_tunnel_link(ign, nt);
386c12b395aSxeb@mail.ru 	return nt;
387c12b395aSxeb@mail.ru 
388c12b395aSxeb@mail.ru failed_free:
389c12b395aSxeb@mail.ru 	free_netdev(dev);
390c12b395aSxeb@mail.ru 	return NULL;
391c12b395aSxeb@mail.ru }
392c12b395aSxeb@mail.ru 
ip6erspan_tunnel_uninit(struct net_device * dev)393b80d0b93SWilliam Tu static void ip6erspan_tunnel_uninit(struct net_device *dev)
394b80d0b93SWilliam Tu {
395b80d0b93SWilliam Tu 	struct ip6_tnl *t = netdev_priv(dev);
396b80d0b93SWilliam Tu 	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
397b80d0b93SWilliam Tu 
398b80d0b93SWilliam Tu 	ip6erspan_tunnel_unlink_md(ign, t);
399b80d0b93SWilliam Tu 	ip6gre_tunnel_unlink(ign, t);
400b80d0b93SWilliam Tu 	dst_cache_reset(&t->dst_cache);
401d62607c3SJakub Kicinski 	netdev_put(dev, &t->dev_tracker);
402b80d0b93SWilliam Tu }
403b80d0b93SWilliam Tu 
ip6gre_tunnel_uninit(struct net_device * dev)404c12b395aSxeb@mail.ru static void ip6gre_tunnel_uninit(struct net_device *dev)
405c12b395aSxeb@mail.ru {
40622f08069SNicolas Dichtel 	struct ip6_tnl *t = netdev_priv(dev);
40722f08069SNicolas Dichtel 	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
408c12b395aSxeb@mail.ru 
409b80d0b93SWilliam Tu 	ip6gre_tunnel_unlink_md(ign, t);
41022f08069SNicolas Dichtel 	ip6gre_tunnel_unlink(ign, t);
411dafabb65STaehee Yoo 	if (ign->fb_tunnel_dev == dev)
412dafabb65STaehee Yoo 		WRITE_ONCE(ign->fb_tunnel_dev, NULL);
413607f725fSPaolo Abeni 	dst_cache_reset(&t->dst_cache);
414d62607c3SJakub Kicinski 	netdev_put(dev, &t->dev_tracker);
415c12b395aSxeb@mail.ru }
416c12b395aSxeb@mail.ru 
417c12b395aSxeb@mail.ru 
ip6gre_err(struct sk_buff * skb,struct inet6_skb_parm * opt,u8 type,u8 code,int offset,__be32 info)41832bbd879SStefano Brivio static int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
419c12b395aSxeb@mail.ru 		       u8 type, u8 code, int offset, __be32 info)
420c12b395aSxeb@mail.ru {
421929fc032SXin Long 	struct net *net = dev_net(skb->dev);
4227892032cSEric Dumazet 	const struct ipv6hdr *ipv6h;
423a82738adSHaishuang Yan 	struct tnl_ptk_info tpi;
424c12b395aSxeb@mail.ru 	struct ip6_tnl *t;
425c12b395aSxeb@mail.ru 
426a82738adSHaishuang Yan 	if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IPV6),
427a82738adSHaishuang Yan 			     offset) < 0)
42832bbd879SStefano Brivio 		return -EINVAL;
429c12b395aSxeb@mail.ru 
430b87fb39eSEric Dumazet 	ipv6h = (const struct ipv6hdr *)skb->data;
431c12b395aSxeb@mail.ru 	t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
432a82738adSHaishuang Yan 				 tpi.key, tpi.proto);
43363159f29SIan Morris 	if (!t)
43432bbd879SStefano Brivio 		return -ENOENT;
435c12b395aSxeb@mail.ru 
436c12b395aSxeb@mail.ru 	switch (type) {
437c12b395aSxeb@mail.ru 	case ICMPV6_DEST_UNREACH:
438a46496ceSMatt Bennett 		net_dbg_ratelimited("%s: Path to destination invalid or inactive!\n",
439c12b395aSxeb@mail.ru 				    t->parms.name);
440f8d20b46SXin Long 		if (code != ICMPV6_PORT_UNREACH)
441c12b395aSxeb@mail.ru 			break;
44232bbd879SStefano Brivio 		return 0;
443c12b395aSxeb@mail.ru 	case ICMPV6_TIME_EXCEED:
444c12b395aSxeb@mail.ru 		if (code == ICMPV6_EXC_HOPLIMIT) {
445a46496ceSMatt Bennett 			net_dbg_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n",
446c12b395aSxeb@mail.ru 					    t->parms.name);
447c12b395aSxeb@mail.ru 			break;
448f8d20b46SXin Long 		}
44932bbd879SStefano Brivio 		return 0;
45046d30cb1SKees Cook 	case ICMPV6_PARAMPROB: {
45146d30cb1SKees Cook 		struct ipv6_tlv_tnl_enc_lim *tel;
45246d30cb1SKees Cook 		__u32 teli;
45346d30cb1SKees Cook 
454c12b395aSxeb@mail.ru 		teli = 0;
455c12b395aSxeb@mail.ru 		if (code == ICMPV6_HDR_FIELD)
456c12b395aSxeb@mail.ru 			teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data);
457c12b395aSxeb@mail.ru 
458d1e158e2SSabrina Dubroca 		if (teli && teli == be32_to_cpu(info) - 2) {
459c12b395aSxeb@mail.ru 			tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
460c12b395aSxeb@mail.ru 			if (tel->encap_limit == 0) {
461a46496ceSMatt Bennett 				net_dbg_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n",
462c12b395aSxeb@mail.ru 						    t->parms.name);
463c12b395aSxeb@mail.ru 			}
464c12b395aSxeb@mail.ru 		} else {
465a46496ceSMatt Bennett 			net_dbg_ratelimited("%s: Recipient unable to parse tunneled packet!\n",
466c12b395aSxeb@mail.ru 					    t->parms.name);
467c12b395aSxeb@mail.ru 		}
46832bbd879SStefano Brivio 		return 0;
46946d30cb1SKees Cook 	}
470c12b395aSxeb@mail.ru 	case ICMPV6_PKT_TOOBIG:
471fe1a4ca0SXin Long 		ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
47232bbd879SStefano Brivio 		return 0;
473929fc032SXin Long 	case NDISC_REDIRECT:
474929fc032SXin Long 		ip6_redirect(skb, net, skb->dev->ifindex, 0,
475929fc032SXin Long 			     sock_net_uid(net, NULL));
47632bbd879SStefano Brivio 		return 0;
477c12b395aSxeb@mail.ru 	}
478c12b395aSxeb@mail.ru 
479c12b395aSxeb@mail.ru 	if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO))
480c12b395aSxeb@mail.ru 		t->err_count++;
481c12b395aSxeb@mail.ru 	else
482c12b395aSxeb@mail.ru 		t->err_count = 1;
483c12b395aSxeb@mail.ru 	t->err_time = jiffies;
48432bbd879SStefano Brivio 
48532bbd879SStefano Brivio 	return 0;
486c12b395aSxeb@mail.ru }
487c12b395aSxeb@mail.ru 
ip6gre_rcv(struct sk_buff * skb,const struct tnl_ptk_info * tpi)488308edfdfSTom Herbert static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
489c12b395aSxeb@mail.ru {
490c12b395aSxeb@mail.ru 	const struct ipv6hdr *ipv6h;
491c12b395aSxeb@mail.ru 	struct ip6_tnl *tunnel;
492c12b395aSxeb@mail.ru 
493c12b395aSxeb@mail.ru 	ipv6h = ipv6_hdr(skb);
494c12b395aSxeb@mail.ru 	tunnel = ip6gre_tunnel_lookup(skb->dev,
495308edfdfSTom Herbert 				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
496308edfdfSTom Herbert 				      tpi->proto);
497c12b395aSxeb@mail.ru 	if (tunnel) {
4986712abc1SWilliam Tu 		if (tunnel->parms.collect_md) {
4996712abc1SWilliam Tu 			struct metadata_dst *tun_dst;
5006712abc1SWilliam Tu 			__be64 tun_id;
5016712abc1SWilliam Tu 			__be16 flags;
5026712abc1SWilliam Tu 
5036712abc1SWilliam Tu 			flags = tpi->flags;
5046712abc1SWilliam Tu 			tun_id = key32_to_tunnel_id(tpi->key);
5056712abc1SWilliam Tu 
5066712abc1SWilliam Tu 			tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0);
5076712abc1SWilliam Tu 			if (!tun_dst)
5086712abc1SWilliam Tu 				return PACKET_REJECT;
5096712abc1SWilliam Tu 
5106712abc1SWilliam Tu 			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
5116712abc1SWilliam Tu 		} else {
512981542c5SAlexey Kodanev 			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
5136712abc1SWilliam Tu 		}
514c12b395aSxeb@mail.ru 
515308edfdfSTom Herbert 		return PACKET_RCVD;
516308edfdfSTom Herbert 	}
517308edfdfSTom Herbert 
518308edfdfSTom Herbert 	return PACKET_REJECT;
519308edfdfSTom Herbert }
520308edfdfSTom Herbert 
ip6erspan_rcv(struct sk_buff * skb,struct tnl_ptk_info * tpi,int gre_hdr_len)521a057fed3SLorenzo Bianconi static int ip6erspan_rcv(struct sk_buff *skb,
5222a3cabaeSLorenzo Bianconi 			 struct tnl_ptk_info *tpi,
5232a3cabaeSLorenzo Bianconi 			 int gre_hdr_len)
5245a963eb6SWilliam Tu {
5251d7e2ed2SWilliam Tu 	struct erspan_base_hdr *ershdr;
5265a963eb6SWilliam Tu 	const struct ipv6hdr *ipv6h;
5273df19283SWilliam Tu 	struct erspan_md2 *md2;
5285a963eb6SWilliam Tu 	struct ip6_tnl *tunnel;
5291d7e2ed2SWilliam Tu 	u8 ver;
5305a963eb6SWilliam Tu 
5314e3fdeecSEric Dumazet 	if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
5324e3fdeecSEric Dumazet 		return PACKET_REJECT;
5334e3fdeecSEric Dumazet 
534293a1991SHaishuang Yan 	ipv6h = ipv6_hdr(skb);
535293a1991SHaishuang Yan 	ershdr = (struct erspan_base_hdr *)skb->data;
536c69de58bSWilliam Tu 	ver = ershdr->ver;
5375a963eb6SWilliam Tu 
5385a963eb6SWilliam Tu 	tunnel = ip6gre_tunnel_lookup(skb->dev,
5395a963eb6SWilliam Tu 				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
5405a963eb6SWilliam Tu 				      tpi->proto);
5415a963eb6SWilliam Tu 	if (tunnel) {
5421d7e2ed2SWilliam Tu 		int len = erspan_hdr_len(ver);
5431d7e2ed2SWilliam Tu 
5441d7e2ed2SWilliam Tu 		if (unlikely(!pskb_may_pull(skb, len)))
545ae3e1337SWilliam Tu 			return PACKET_REJECT;
5461d7e2ed2SWilliam Tu 
5471d7e2ed2SWilliam Tu 		if (__iptunnel_pull_header(skb, len,
5485a963eb6SWilliam Tu 					   htons(ETH_P_TEB),
5495a963eb6SWilliam Tu 					   false, false) < 0)
5505a963eb6SWilliam Tu 			return PACKET_REJECT;
5515a963eb6SWilliam Tu 
552ef7baf5eSWilliam Tu 		if (tunnel->parms.collect_md) {
5532a3cabaeSLorenzo Bianconi 			struct erspan_metadata *pkt_md, *md;
554ef7baf5eSWilliam Tu 			struct metadata_dst *tun_dst;
555ef7baf5eSWilliam Tu 			struct ip_tunnel_info *info;
5562a3cabaeSLorenzo Bianconi 			unsigned char *gh;
557ef7baf5eSWilliam Tu 			__be64 tun_id;
558ef7baf5eSWilliam Tu 			__be16 flags;
559ef7baf5eSWilliam Tu 
560ef7baf5eSWilliam Tu 			tpi->flags |= TUNNEL_KEY;
561ef7baf5eSWilliam Tu 			flags = tpi->flags;
562ef7baf5eSWilliam Tu 			tun_id = key32_to_tunnel_id(tpi->key);
563ef7baf5eSWilliam Tu 
564ef7baf5eSWilliam Tu 			tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id,
565ef7baf5eSWilliam Tu 						  sizeof(*md));
566ef7baf5eSWilliam Tu 			if (!tun_dst)
567ef7baf5eSWilliam Tu 				return PACKET_REJECT;
568ef7baf5eSWilliam Tu 
5692a3cabaeSLorenzo Bianconi 			/* skb can be uncloned in __iptunnel_pull_header, so
5702a3cabaeSLorenzo Bianconi 			 * old pkt_md is no longer valid and we need to reset
5712a3cabaeSLorenzo Bianconi 			 * it
5722a3cabaeSLorenzo Bianconi 			 */
5732a3cabaeSLorenzo Bianconi 			gh = skb_network_header(skb) +
5742a3cabaeSLorenzo Bianconi 			     skb_network_header_len(skb);
5752a3cabaeSLorenzo Bianconi 			pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
5762a3cabaeSLorenzo Bianconi 							    sizeof(*ershdr));
577ef7baf5eSWilliam Tu 			info = &tun_dst->u.tun_info;
578ef7baf5eSWilliam Tu 			md = ip_tunnel_info_opts(info);
57994d7d8f2SWilliam Tu 			md->version = ver;
5803df19283SWilliam Tu 			md2 = &md->u.md2;
5813df19283SWilliam Tu 			memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
5823df19283SWilliam Tu 						       ERSPAN_V2_MDSIZE);
583ef7baf5eSWilliam Tu 			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
584ef7baf5eSWilliam Tu 			info->options_len = sizeof(*md);
585ef7baf5eSWilliam Tu 
586ef7baf5eSWilliam Tu 			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
587ef7baf5eSWilliam Tu 
588ef7baf5eSWilliam Tu 		} else {
5895a963eb6SWilliam Tu 			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
590ef7baf5eSWilliam Tu 		}
5915a963eb6SWilliam Tu 
5925a963eb6SWilliam Tu 		return PACKET_RCVD;
5935a963eb6SWilliam Tu 	}
5945a963eb6SWilliam Tu 
5955a963eb6SWilliam Tu 	return PACKET_REJECT;
5965a963eb6SWilliam Tu }
5975a963eb6SWilliam Tu 
gre_rcv(struct sk_buff * skb)598308edfdfSTom Herbert static int gre_rcv(struct sk_buff *skb)
599308edfdfSTom Herbert {
600308edfdfSTom Herbert 	struct tnl_ptk_info tpi;
601308edfdfSTom Herbert 	bool csum_err = false;
602308edfdfSTom Herbert 	int hdr_len;
603308edfdfSTom Herbert 
604e582615aSEric Dumazet 	hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0);
605f132ae7cSJiri Benc 	if (hdr_len < 0)
606c12b395aSxeb@mail.ru 		goto drop;
607c12b395aSxeb@mail.ru 
608308edfdfSTom Herbert 	if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))
609c12b395aSxeb@mail.ru 		goto drop;
610c12b395aSxeb@mail.ru 
61194d7d8f2SWilliam Tu 	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
61294d7d8f2SWilliam Tu 		     tpi.proto == htons(ETH_P_ERSPAN2))) {
6132a3cabaeSLorenzo Bianconi 		if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
6145a963eb6SWilliam Tu 			return 0;
615a7343211SHaishuang Yan 		goto out;
6165a963eb6SWilliam Tu 	}
6175a963eb6SWilliam Tu 
618308edfdfSTom Herbert 	if (ip6gre_rcv(skb, &tpi) == PACKET_RCVD)
619c12b395aSxeb@mail.ru 		return 0;
620c12b395aSxeb@mail.ru 
621a7343211SHaishuang Yan out:
622308edfdfSTom Herbert 	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
623c12b395aSxeb@mail.ru drop:
624c12b395aSxeb@mail.ru 	kfree_skb(skb);
625c12b395aSxeb@mail.ru 	return 0;
626c12b395aSxeb@mail.ru }
627c12b395aSxeb@mail.ru 
gre_handle_offloads(struct sk_buff * skb,bool csum)628b05229f4STom Herbert static int gre_handle_offloads(struct sk_buff *skb, bool csum)
629c12b395aSxeb@mail.ru {
630b05229f4STom Herbert 	return iptunnel_handle_offloads(skb,
631b05229f4STom Herbert 					csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
632c12b395aSxeb@mail.ru }
633c12b395aSxeb@mail.ru 
prepare_ip6gre_xmit_ipv4(struct sk_buff * skb,struct net_device * dev,struct flowi6 * fl6,__u8 * dsfield,int * encap_limit)634898b2979SWilliam Tu static void prepare_ip6gre_xmit_ipv4(struct sk_buff *skb,
635898b2979SWilliam Tu 				     struct net_device *dev,
636898b2979SWilliam Tu 				     struct flowi6 *fl6, __u8 *dsfield,
637898b2979SWilliam Tu 				     int *encap_limit)
638898b2979SWilliam Tu {
639898b2979SWilliam Tu 	const struct iphdr *iph = ip_hdr(skb);
640898b2979SWilliam Tu 	struct ip6_tnl *t = netdev_priv(dev);
641898b2979SWilliam Tu 
642898b2979SWilliam Tu 	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
643898b2979SWilliam Tu 		*encap_limit = t->parms.encap_limit;
644898b2979SWilliam Tu 
645898b2979SWilliam Tu 	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
646898b2979SWilliam Tu 
647898b2979SWilliam Tu 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
648898b2979SWilliam Tu 		*dsfield = ipv4_get_dsfield(iph);
649898b2979SWilliam Tu 	else
650898b2979SWilliam Tu 		*dsfield = ip6_tclass(t->parms.flowinfo);
651898b2979SWilliam Tu 
652898b2979SWilliam Tu 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
653898b2979SWilliam Tu 		fl6->flowi6_mark = skb->mark;
654898b2979SWilliam Tu 	else
655898b2979SWilliam Tu 		fl6->flowi6_mark = t->parms.fwmark;
656898b2979SWilliam Tu 
657898b2979SWilliam Tu 	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
658898b2979SWilliam Tu }
659898b2979SWilliam Tu 
prepare_ip6gre_xmit_ipv6(struct sk_buff * skb,struct net_device * dev,struct flowi6 * fl6,__u8 * dsfield,int * encap_limit)660898b2979SWilliam Tu static int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb,
661898b2979SWilliam Tu 				    struct net_device *dev,
662898b2979SWilliam Tu 				    struct flowi6 *fl6, __u8 *dsfield,
663898b2979SWilliam Tu 				    int *encap_limit)
664898b2979SWilliam Tu {
6653bc817d6SHaishuang Yan 	struct ipv6hdr *ipv6h;
666898b2979SWilliam Tu 	struct ip6_tnl *t = netdev_priv(dev);
667898b2979SWilliam Tu 	__u16 offset;
668898b2979SWilliam Tu 
669898b2979SWilliam Tu 	offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
670898b2979SWilliam Tu 	/* ip6_tnl_parse_tlv_enc_lim() might have reallocated skb->head */
6713bc817d6SHaishuang Yan 	ipv6h = ipv6_hdr(skb);
672898b2979SWilliam Tu 
673898b2979SWilliam Tu 	if (offset > 0) {
674898b2979SWilliam Tu 		struct ipv6_tlv_tnl_enc_lim *tel;
675898b2979SWilliam Tu 
676898b2979SWilliam Tu 		tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
677898b2979SWilliam Tu 		if (tel->encap_limit == 0) {
6784372339eSJason A. Donenfeld 			icmpv6_ndo_send(skb, ICMPV6_PARAMPROB,
679898b2979SWilliam Tu 					ICMPV6_HDR_FIELD, offset + 2);
680898b2979SWilliam Tu 			return -1;
681898b2979SWilliam Tu 		}
682898b2979SWilliam Tu 		*encap_limit = tel->encap_limit - 1;
683898b2979SWilliam Tu 	} else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) {
684898b2979SWilliam Tu 		*encap_limit = t->parms.encap_limit;
685898b2979SWilliam Tu 	}
686898b2979SWilliam Tu 
687898b2979SWilliam Tu 	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
688898b2979SWilliam Tu 
689898b2979SWilliam Tu 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
690898b2979SWilliam Tu 		*dsfield = ipv6_get_dsfield(ipv6h);
691898b2979SWilliam Tu 	else
692898b2979SWilliam Tu 		*dsfield = ip6_tclass(t->parms.flowinfo);
693898b2979SWilliam Tu 
694898b2979SWilliam Tu 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
695898b2979SWilliam Tu 		fl6->flowlabel |= ip6_flowlabel(ipv6h);
696898b2979SWilliam Tu 
697898b2979SWilliam Tu 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
698898b2979SWilliam Tu 		fl6->flowi6_mark = skb->mark;
699898b2979SWilliam Tu 	else
700898b2979SWilliam Tu 		fl6->flowi6_mark = t->parms.fwmark;
701898b2979SWilliam Tu 
702898b2979SWilliam Tu 	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
703898b2979SWilliam Tu 
704898b2979SWilliam Tu 	return 0;
705898b2979SWilliam Tu }
706898b2979SWilliam Tu 
prepare_ip6gre_xmit_other(struct sk_buff * skb,struct net_device * dev,struct flowi6 * fl6,__u8 * dsfield,int * encap_limit)70741337f52SMatthias May static int prepare_ip6gre_xmit_other(struct sk_buff *skb,
70841337f52SMatthias May 				     struct net_device *dev,
70941337f52SMatthias May 				     struct flowi6 *fl6, __u8 *dsfield,
71041337f52SMatthias May 				     int *encap_limit)
71141337f52SMatthias May {
71241337f52SMatthias May 	struct ip6_tnl *t = netdev_priv(dev);
71341337f52SMatthias May 
71441337f52SMatthias May 	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
71541337f52SMatthias May 		*encap_limit = t->parms.encap_limit;
71641337f52SMatthias May 
71741337f52SMatthias May 	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
71841337f52SMatthias May 
71941337f52SMatthias May 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
72041337f52SMatthias May 		*dsfield = 0;
72141337f52SMatthias May 	else
72241337f52SMatthias May 		*dsfield = ip6_tclass(t->parms.flowinfo);
72341337f52SMatthias May 
72441337f52SMatthias May 	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
72541337f52SMatthias May 		fl6->flowi6_mark = skb->mark;
72641337f52SMatthias May 	else
72741337f52SMatthias May 		fl6->flowi6_mark = t->parms.fwmark;
72841337f52SMatthias May 
72941337f52SMatthias May 	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
73041337f52SMatthias May 
73141337f52SMatthias May 	return 0;
73241337f52SMatthias May }
73341337f52SMatthias May 
skb_tunnel_info_txcheck(struct sk_buff * skb)734e5f7e211SDavide Caratti static struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
735e5f7e211SDavide Caratti {
736e5f7e211SDavide Caratti 	struct ip_tunnel_info *tun_info;
737e5f7e211SDavide Caratti 
738e5f7e211SDavide Caratti 	tun_info = skb_tunnel_info(skb);
739e5f7e211SDavide Caratti 	if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX)))
740e5f7e211SDavide Caratti 		return ERR_PTR(-EINVAL);
741e5f7e211SDavide Caratti 
742e5f7e211SDavide Caratti 	return tun_info;
743e5f7e211SDavide Caratti }
744e5f7e211SDavide Caratti 
__gre6_xmit(struct sk_buff * skb,struct net_device * dev,__u8 dsfield,struct flowi6 * fl6,int encap_limit,__u32 * pmtu,__be16 proto)745b05229f4STom Herbert static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
746b05229f4STom Herbert 			       struct net_device *dev, __u8 dsfield,
747b05229f4STom Herbert 			       struct flowi6 *fl6, int encap_limit,
748b05229f4STom Herbert 			       __u32 *pmtu, __be16 proto)
749c12b395aSxeb@mail.ru {
750c12b395aSxeb@mail.ru 	struct ip6_tnl *tunnel = netdev_priv(dev);
7518aec4959SXin Long 	__be16 protocol;
752fde98ae9SPeilin Ye 	__be16 flags;
753c12b395aSxeb@mail.ru 
754c12b395aSxeb@mail.ru 	if (dev->type == ARPHRD_ETHER)
755c12b395aSxeb@mail.ru 		IPCB(skb)->flags = 0;
756c12b395aSxeb@mail.ru 
757b05229f4STom Herbert 	if (dev->header_ops && dev->type == ARPHRD_IP6GRE)
758b05229f4STom Herbert 		fl6->daddr = ((struct ipv6hdr *)skb->data)->daddr;
759b05229f4STom Herbert 	else
760c12b395aSxeb@mail.ru 		fl6->daddr = tunnel->parms.raddr;
761c12b395aSxeb@mail.ru 
762b05229f4STom Herbert 	/* Push GRE header. */
7638aec4959SXin Long 	protocol = (dev->type == ARPHRD_ETHER) ? htons(ETH_P_TEB) : proto;
7646712abc1SWilliam Tu 
7656712abc1SWilliam Tu 	if (tunnel->parms.collect_md) {
7666712abc1SWilliam Tu 		struct ip_tunnel_info *tun_info;
7676712abc1SWilliam Tu 		const struct ip_tunnel_key *key;
768f40c064eSPeilin Ye 		int tun_hlen;
7696712abc1SWilliam Tu 
770e5f7e211SDavide Caratti 		tun_info = skb_tunnel_info_txcheck(skb);
771e5f7e211SDavide Caratti 		if (IS_ERR(tun_info) ||
772e5f7e211SDavide Caratti 		    unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
7736712abc1SWilliam Tu 			return -EINVAL;
7746712abc1SWilliam Tu 
7756712abc1SWilliam Tu 		key = &tun_info->key;
7766712abc1SWilliam Tu 		memset(fl6, 0, sizeof(*fl6));
7776712abc1SWilliam Tu 		fl6->flowi6_proto = IPPROTO_GRE;
7786712abc1SWilliam Tu 		fl6->daddr = key->u.ipv6.dst;
7796712abc1SWilliam Tu 		fl6->flowlabel = key->label;
7806712abc1SWilliam Tu 		fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
781bcf141b2SGhalem Boudour 		fl6->fl6_gre_key = tunnel_id_to_key32(key->tun_id);
7826712abc1SWilliam Tu 
7836712abc1SWilliam Tu 		dsfield = key->tos;
78477a5196aSWilliam Tu 		flags = key->tun_flags &
78577a5196aSWilliam Tu 			(TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ);
786f40c064eSPeilin Ye 		tun_hlen = gre_calc_hlen(flags);
7876712abc1SWilliam Tu 
788ab198e1dSPeilin Ye 		if (skb_cow_head(skb, dev->needed_headroom ?: tun_hlen + tunnel->encap_hlen))
789ab198e1dSPeilin Ye 			return -ENOMEM;
790ab198e1dSPeilin Ye 
791f40c064eSPeilin Ye 		gre_build_header(skb, tun_hlen,
7926712abc1SWilliam Tu 				 flags, protocol,
79377a5196aSWilliam Tu 				 tunnel_id_to_key32(tun_info->key.tun_id),
79431c417c9SPeilin Ye 				 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
79577a5196aSWilliam Tu 						      : 0);
7966712abc1SWilliam Tu 
7976712abc1SWilliam Tu 	} else {
798ab198e1dSPeilin Ye 		if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen))
799ab198e1dSPeilin Ye 			return -ENOMEM;
800ab198e1dSPeilin Ye 
801fde98ae9SPeilin Ye 		flags = tunnel->parms.o_flags;
802fde98ae9SPeilin Ye 
803fde98ae9SPeilin Ye 		gre_build_header(skb, tunnel->tun_hlen, flags,
8046712abc1SWilliam Tu 				 protocol, tunnel->parms.o_key,
80531c417c9SPeilin Ye 				 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
80631c417c9SPeilin Ye 						      : 0);
8076712abc1SWilliam Tu 	}
808c12b395aSxeb@mail.ru 
809b05229f4STom Herbert 	return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,
810b05229f4STom Herbert 			    NEXTHDR_GRE);
811c12b395aSxeb@mail.ru }
812c12b395aSxeb@mail.ru 
ip6gre_xmit_ipv4(struct sk_buff * skb,struct net_device * dev)813c12b395aSxeb@mail.ru static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
814c12b395aSxeb@mail.ru {
815c12b395aSxeb@mail.ru 	struct ip6_tnl *t = netdev_priv(dev);
816c12b395aSxeb@mail.ru 	int encap_limit = -1;
817c12b395aSxeb@mail.ru 	struct flowi6 fl6;
8186712abc1SWilliam Tu 	__u8 dsfield = 0;
819c12b395aSxeb@mail.ru 	__u32 mtu;
820c12b395aSxeb@mail.ru 	int err;
821c12b395aSxeb@mail.ru 
8225146d1f1SBernie Harris 	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
8235146d1f1SBernie Harris 
8246712abc1SWilliam Tu 	if (!t->parms.collect_md)
8256712abc1SWilliam Tu 		prepare_ip6gre_xmit_ipv4(skb, dev, &fl6,
8266712abc1SWilliam Tu 					 &dsfield, &encap_limit);
827e2d118a1SLorenzo Colitti 
828b05229f4STom Herbert 	err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
829b05229f4STom Herbert 	if (err)
830b05229f4STom Herbert 		return -1;
831b05229f4STom Herbert 
832b05229f4STom Herbert 	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
833b05229f4STom Herbert 			  skb->protocol);
834c12b395aSxeb@mail.ru 	if (err != 0) {
835c12b395aSxeb@mail.ru 		/* XXX: send ICMP error even if DF is not set. */
836c12b395aSxeb@mail.ru 		if (err == -EMSGSIZE)
8374372339eSJason A. Donenfeld 			icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
838c12b395aSxeb@mail.ru 				      htonl(mtu));
839c12b395aSxeb@mail.ru 		return -1;
840c12b395aSxeb@mail.ru 	}
841c12b395aSxeb@mail.ru 
842c12b395aSxeb@mail.ru 	return 0;
843c12b395aSxeb@mail.ru }
844c12b395aSxeb@mail.ru 
ip6gre_xmit_ipv6(struct sk_buff * skb,struct net_device * dev)845c12b395aSxeb@mail.ru static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
846c12b395aSxeb@mail.ru {
847c12b395aSxeb@mail.ru 	struct ip6_tnl *t = netdev_priv(dev);
848c12b395aSxeb@mail.ru 	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
849c12b395aSxeb@mail.ru 	int encap_limit = -1;
850c12b395aSxeb@mail.ru 	struct flowi6 fl6;
8516712abc1SWilliam Tu 	__u8 dsfield = 0;
852c12b395aSxeb@mail.ru 	__u32 mtu;
853c12b395aSxeb@mail.ru 	int err;
854c12b395aSxeb@mail.ru 
855c12b395aSxeb@mail.ru 	if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
856c12b395aSxeb@mail.ru 		return -1;
857c12b395aSxeb@mail.ru 
8586712abc1SWilliam Tu 	if (!t->parms.collect_md &&
8596712abc1SWilliam Tu 	    prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit))
860c12b395aSxeb@mail.ru 		return -1;
861e2d118a1SLorenzo Colitti 
862b05229f4STom Herbert 	if (gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)))
863b05229f4STom Herbert 		return -1;
864b05229f4STom Herbert 
865b05229f4STom Herbert 	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit,
866b05229f4STom Herbert 			  &mtu, skb->protocol);
867c12b395aSxeb@mail.ru 	if (err != 0) {
868c12b395aSxeb@mail.ru 		if (err == -EMSGSIZE)
8694372339eSJason A. Donenfeld 			icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
870c12b395aSxeb@mail.ru 		return -1;
871c12b395aSxeb@mail.ru 	}
872c12b395aSxeb@mail.ru 
873c12b395aSxeb@mail.ru 	return 0;
874c12b395aSxeb@mail.ru }
875c12b395aSxeb@mail.ru 
ip6gre_xmit_other(struct sk_buff * skb,struct net_device * dev)876c12b395aSxeb@mail.ru static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
877c12b395aSxeb@mail.ru {
878c12b395aSxeb@mail.ru 	struct ip6_tnl *t = netdev_priv(dev);
879c12b395aSxeb@mail.ru 	int encap_limit = -1;
880c12b395aSxeb@mail.ru 	struct flowi6 fl6;
88141337f52SMatthias May 	__u8 dsfield = 0;
882c12b395aSxeb@mail.ru 	__u32 mtu;
883c12b395aSxeb@mail.ru 	int err;
884c12b395aSxeb@mail.ru 
88541337f52SMatthias May 	if (!t->parms.collect_md &&
88641337f52SMatthias May 	    prepare_ip6gre_xmit_other(skb, dev, &fl6, &dsfield, &encap_limit))
88741337f52SMatthias May 		return -1;
888c12b395aSxeb@mail.ru 
889b05229f4STom Herbert 	err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
890b05229f4STom Herbert 	if (err)
891b05229f4STom Herbert 		return err;
89241337f52SMatthias May 	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, skb->protocol);
893c12b395aSxeb@mail.ru 
894c12b395aSxeb@mail.ru 	return err;
895c12b395aSxeb@mail.ru }
896c12b395aSxeb@mail.ru 
ip6gre_tunnel_xmit(struct sk_buff * skb,struct net_device * dev)897c12b395aSxeb@mail.ru static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
898c12b395aSxeb@mail.ru 	struct net_device *dev)
899c12b395aSxeb@mail.ru {
900c12b395aSxeb@mail.ru 	struct ip6_tnl *t = netdev_priv(dev);
9013f8a8447SMatthias May 	__be16 payload_protocol;
902c12b395aSxeb@mail.ru 	int ret;
903c12b395aSxeb@mail.ru 
904cb9f1b78SWillem de Bruijn 	if (!pskb_inet_may_pull(skb))
905cb9f1b78SWillem de Bruijn 		goto tx_err;
906cb9f1b78SWillem de Bruijn 
907d5005140SSteffen Klassert 	if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
90841ab3e31STommi Rantala 		goto tx_err;
909c12b395aSxeb@mail.ru 
9103f8a8447SMatthias May 	payload_protocol = skb_protocol(skb, true);
9113f8a8447SMatthias May 	switch (payload_protocol) {
912c12b395aSxeb@mail.ru 	case htons(ETH_P_IP):
913c12b395aSxeb@mail.ru 		ret = ip6gre_xmit_ipv4(skb, dev);
914c12b395aSxeb@mail.ru 		break;
915c12b395aSxeb@mail.ru 	case htons(ETH_P_IPV6):
916c12b395aSxeb@mail.ru 		ret = ip6gre_xmit_ipv6(skb, dev);
917c12b395aSxeb@mail.ru 		break;
918c12b395aSxeb@mail.ru 	default:
919c12b395aSxeb@mail.ru 		ret = ip6gre_xmit_other(skb, dev);
920c12b395aSxeb@mail.ru 		break;
921c12b395aSxeb@mail.ru 	}
922c12b395aSxeb@mail.ru 
923c12b395aSxeb@mail.ru 	if (ret < 0)
924c12b395aSxeb@mail.ru 		goto tx_err;
925c12b395aSxeb@mail.ru 
926c12b395aSxeb@mail.ru 	return NETDEV_TX_OK;
927c12b395aSxeb@mail.ru 
928c12b395aSxeb@mail.ru tx_err:
929e5f7e211SDavide Caratti 	if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
9302fad1ba3SEric Dumazet 		DEV_STATS_INC(dev, tx_errors);
9312fad1ba3SEric Dumazet 	DEV_STATS_INC(dev, tx_dropped);
932c12b395aSxeb@mail.ru 	kfree_skb(skb);
933c12b395aSxeb@mail.ru 	return NETDEV_TX_OK;
934c12b395aSxeb@mail.ru }
935c12b395aSxeb@mail.ru 
ip6erspan_tunnel_xmit(struct sk_buff * skb,struct net_device * dev)9365a963eb6SWilliam Tu static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
9375a963eb6SWilliam Tu 					 struct net_device *dev)
9385a963eb6SWilliam Tu {
939e5f7e211SDavide Caratti 	struct ip_tunnel_info *tun_info = NULL;
9405a963eb6SWilliam Tu 	struct ip6_tnl *t = netdev_priv(dev);
9415a963eb6SWilliam Tu 	struct dst_entry *dst = skb_dst(skb);
9425a963eb6SWilliam Tu 	bool truncate = false;
9435a963eb6SWilliam Tu 	int encap_limit = -1;
9445a963eb6SWilliam Tu 	__u8 dsfield = false;
9455a963eb6SWilliam Tu 	struct flowi6 fl6;
9465a963eb6SWilliam Tu 	int err = -EINVAL;
94720704bd1SXin Long 	__be16 proto;
9485a963eb6SWilliam Tu 	__u32 mtu;
9491baf5ebfSWilliam Tu 	int nhoff;
9505a963eb6SWilliam Tu 
951cb9f1b78SWillem de Bruijn 	if (!pskb_inet_may_pull(skb))
952cb9f1b78SWillem de Bruijn 		goto tx_err;
953cb9f1b78SWillem de Bruijn 
9545a963eb6SWilliam Tu 	if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
9555a963eb6SWilliam Tu 		goto tx_err;
9565a963eb6SWilliam Tu 
9575a963eb6SWilliam Tu 	if (gre_handle_offloads(skb, false))
9585a963eb6SWilliam Tu 		goto tx_err;
9595a963eb6SWilliam Tu 
960ef7baf5eSWilliam Tu 	if (skb->len > dev->mtu + dev->hard_header_len) {
9614258faa1SYuanjun Gong 		if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
9624258faa1SYuanjun Gong 			goto tx_err;
963ef7baf5eSWilliam Tu 		truncate = true;
964ef7baf5eSWilliam Tu 	}
965ef7baf5eSWilliam Tu 
9668e50ed77SEric Dumazet 	nhoff = skb_network_offset(skb);
9671baf5ebfSWilliam Tu 	if (skb->protocol == htons(ETH_P_IP) &&
9681baf5ebfSWilliam Tu 	    (ntohs(ip_hdr(skb)->tot_len) > skb->len - nhoff))
9691baf5ebfSWilliam Tu 		truncate = true;
9701baf5ebfSWilliam Tu 
971301bd140SEric Dumazet 	if (skb->protocol == htons(ETH_P_IPV6)) {
972301bd140SEric Dumazet 		int thoff;
973301bd140SEric Dumazet 
974301bd140SEric Dumazet 		if (skb_transport_header_was_set(skb))
9758e50ed77SEric Dumazet 			thoff = skb_transport_offset(skb);
976301bd140SEric Dumazet 		else
977301bd140SEric Dumazet 			thoff = nhoff + sizeof(struct ipv6hdr);
978301bd140SEric Dumazet 		if (ntohs(ipv6_hdr(skb)->payload_len) > skb->len - thoff)
979d5db21a3SWilliam Tu 			truncate = true;
980301bd140SEric Dumazet 	}
981d5db21a3SWilliam Tu 
9825691484dSPetr Machata 	if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
983e41c7c68SWilliam Tu 		goto tx_err;
984e41c7c68SWilliam Tu 
985ef7baf5eSWilliam Tu 	t->parms.o_flags &= ~TUNNEL_KEY;
986ef7baf5eSWilliam Tu 	IPCB(skb)->flags = 0;
987ef7baf5eSWilliam Tu 
988ef7baf5eSWilliam Tu 	/* For collect_md mode, derive fl6 from the tunnel key,
989ef7baf5eSWilliam Tu 	 * for native mode, call prepare_ip6gre_xmit_{ipv4,ipv6}.
990ef7baf5eSWilliam Tu 	 */
991ef7baf5eSWilliam Tu 	if (t->parms.collect_md) {
992ef7baf5eSWilliam Tu 		const struct ip_tunnel_key *key;
993ef7baf5eSWilliam Tu 		struct erspan_metadata *md;
994c69de58bSWilliam Tu 		__be32 tun_id;
995ef7baf5eSWilliam Tu 
996e5f7e211SDavide Caratti 		tun_info = skb_tunnel_info_txcheck(skb);
997e5f7e211SDavide Caratti 		if (IS_ERR(tun_info) ||
998e5f7e211SDavide Caratti 		    unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
99928e48603SXin Long 			goto tx_err;
1000ef7baf5eSWilliam Tu 
1001ef7baf5eSWilliam Tu 		key = &tun_info->key;
1002ef7baf5eSWilliam Tu 		memset(&fl6, 0, sizeof(fl6));
1003ef7baf5eSWilliam Tu 		fl6.flowi6_proto = IPPROTO_GRE;
1004ef7baf5eSWilliam Tu 		fl6.daddr = key->u.ipv6.dst;
1005ef7baf5eSWilliam Tu 		fl6.flowlabel = key->label;
1006ef7baf5eSWilliam Tu 		fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
1007bcf141b2SGhalem Boudour 		fl6.fl6_gre_key = tunnel_id_to_key32(key->tun_id);
1008ef7baf5eSWilliam Tu 
1009ef7baf5eSWilliam Tu 		dsfield = key->tos;
1010256c87c1SPieter Jansen van Vuuren 		if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
1011256c87c1SPieter Jansen van Vuuren 			goto tx_err;
10122eb8d6d2SXin Long 		if (tun_info->options_len < sizeof(*md))
1013ef7baf5eSWilliam Tu 			goto tx_err;
10142eb8d6d2SXin Long 		md = ip_tunnel_info_opts(tun_info);
1015ef7baf5eSWilliam Tu 
1016c69de58bSWilliam Tu 		tun_id = tunnel_id_to_key32(key->tun_id);
101794d7d8f2SWilliam Tu 		if (md->version == 1) {
101894d7d8f2SWilliam Tu 			erspan_build_header(skb,
1019c69de58bSWilliam Tu 					    ntohl(tun_id),
102094d7d8f2SWilliam Tu 					    ntohl(md->u.index), truncate,
102194d7d8f2SWilliam Tu 					    false);
1022d80fc101SXin Long 			proto = htons(ETH_P_ERSPAN);
102394d7d8f2SWilliam Tu 		} else if (md->version == 2) {
102494d7d8f2SWilliam Tu 			erspan_build_header_v2(skb,
1025c69de58bSWilliam Tu 					       ntohl(tun_id),
1026c69de58bSWilliam Tu 					       md->u.md2.dir,
1027c69de58bSWilliam Tu 					       get_hwid(&md->u.md2),
1028c69de58bSWilliam Tu 					       truncate, false);
1029d80fc101SXin Long 			proto = htons(ETH_P_ERSPAN2);
1030d6aa7119SWilliam Tu 		} else {
1031d6aa7119SWilliam Tu 			goto tx_err;
103294d7d8f2SWilliam Tu 		}
1033ef7baf5eSWilliam Tu 	} else {
10345a963eb6SWilliam Tu 		switch (skb->protocol) {
10355a963eb6SWilliam Tu 		case htons(ETH_P_IP):
10365a963eb6SWilliam Tu 			memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
10375a963eb6SWilliam Tu 			prepare_ip6gre_xmit_ipv4(skb, dev, &fl6,
10385a963eb6SWilliam Tu 						 &dsfield, &encap_limit);
10395a963eb6SWilliam Tu 			break;
10405a963eb6SWilliam Tu 		case htons(ETH_P_IPV6):
1041cb9f1b78SWillem de Bruijn 			if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr))
10425a963eb6SWilliam Tu 				goto tx_err;
10435a963eb6SWilliam Tu 			if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6,
10445a963eb6SWilliam Tu 						     &dsfield, &encap_limit))
10455a963eb6SWilliam Tu 				goto tx_err;
10465a963eb6SWilliam Tu 			break;
10475a963eb6SWilliam Tu 		default:
10485a963eb6SWilliam Tu 			memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
10495a963eb6SWilliam Tu 			break;
10505a963eb6SWilliam Tu 		}
10515a963eb6SWilliam Tu 
1052d80fc101SXin Long 		if (t->parms.erspan_ver == 1) {
1053c69de58bSWilliam Tu 			erspan_build_header(skb, ntohl(t->parms.o_key),
105494d7d8f2SWilliam Tu 					    t->parms.index,
105594d7d8f2SWilliam Tu 					    truncate, false);
1056d80fc101SXin Long 			proto = htons(ETH_P_ERSPAN);
1057d80fc101SXin Long 		} else if (t->parms.erspan_ver == 2) {
1058c69de58bSWilliam Tu 			erspan_build_header_v2(skb, ntohl(t->parms.o_key),
105994d7d8f2SWilliam Tu 					       t->parms.dir,
106094d7d8f2SWilliam Tu 					       t->parms.hwid,
10615a963eb6SWilliam Tu 					       truncate, false);
1062d80fc101SXin Long 			proto = htons(ETH_P_ERSPAN2);
1063d80fc101SXin Long 		} else {
106402f99df1SWilliam Tu 			goto tx_err;
1065d80fc101SXin Long 		}
106602f99df1SWilliam Tu 
10675a963eb6SWilliam Tu 		fl6.daddr = t->parms.raddr;
1068ef7baf5eSWilliam Tu 	}
10695a963eb6SWilliam Tu 
10705a963eb6SWilliam Tu 	/* Push GRE header. */
107131c417c9SPeilin Ye 	gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(atomic_fetch_inc(&t->o_seqno)));
10725a963eb6SWilliam Tu 
10735a963eb6SWilliam Tu 	/* TooBig packet may have updated dst->dev's mtu */
1074ef7baf5eSWilliam Tu 	if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu)
1075675d76adSHangbin Liu 		dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, false);
10765a963eb6SWilliam Tu 
10775a963eb6SWilliam Tu 	err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
10785a963eb6SWilliam Tu 			   NEXTHDR_GRE);
10795a963eb6SWilliam Tu 	if (err != 0) {
10805a963eb6SWilliam Tu 		/* XXX: send ICMP error even if DF is not set. */
10815a963eb6SWilliam Tu 		if (err == -EMSGSIZE) {
10825a963eb6SWilliam Tu 			if (skb->protocol == htons(ETH_P_IP))
10834372339eSJason A. Donenfeld 				icmp_ndo_send(skb, ICMP_DEST_UNREACH,
10845a963eb6SWilliam Tu 					      ICMP_FRAG_NEEDED, htonl(mtu));
10855a963eb6SWilliam Tu 			else
10864372339eSJason A. Donenfeld 				icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
10875a963eb6SWilliam Tu 		}
10885a963eb6SWilliam Tu 
10895a963eb6SWilliam Tu 		goto tx_err;
10905a963eb6SWilliam Tu 	}
10915a963eb6SWilliam Tu 	return NETDEV_TX_OK;
10925a963eb6SWilliam Tu 
10935a963eb6SWilliam Tu tx_err:
1094e5f7e211SDavide Caratti 	if (!IS_ERR(tun_info))
10952fad1ba3SEric Dumazet 		DEV_STATS_INC(dev, tx_errors);
10962fad1ba3SEric Dumazet 	DEV_STATS_INC(dev, tx_dropped);
10975a963eb6SWilliam Tu 	kfree_skb(skb);
10985a963eb6SWilliam Tu 	return NETDEV_TX_OK;
10995a963eb6SWilliam Tu }
11005a963eb6SWilliam Tu 
ip6gre_tnl_link_config_common(struct ip6_tnl * t)1101a483373eSPetr Machata static void ip6gre_tnl_link_config_common(struct ip6_tnl *t)
1102c12b395aSxeb@mail.ru {
1103c12b395aSxeb@mail.ru 	struct net_device *dev = t->dev;
1104c12b395aSxeb@mail.ru 	struct __ip6_tnl_parm *p = &t->parms;
1105c12b395aSxeb@mail.ru 	struct flowi6 *fl6 = &t->fl.u.ip6;
1106c12b395aSxeb@mail.ru 
1107c12b395aSxeb@mail.ru 	if (dev->type != ARPHRD_ETHER) {
11085a1b7e1aSJakub Kicinski 		__dev_addr_set(dev, &p->laddr, sizeof(struct in6_addr));
1109c12b395aSxeb@mail.ru 		memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
1110c12b395aSxeb@mail.ru 	}
1111c12b395aSxeb@mail.ru 
1112c12b395aSxeb@mail.ru 	/* Set up flowi template */
1113c12b395aSxeb@mail.ru 	fl6->saddr = p->laddr;
1114c12b395aSxeb@mail.ru 	fl6->daddr = p->raddr;
1115c12b395aSxeb@mail.ru 	fl6->flowi6_oif = p->link;
1116c12b395aSxeb@mail.ru 	fl6->flowlabel = 0;
1117252f3f5aSHaishuang Yan 	fl6->flowi6_proto = IPPROTO_GRE;
1118bcf141b2SGhalem Boudour 	fl6->fl6_gre_key = t->parms.o_key;
1119c12b395aSxeb@mail.ru 
1120c12b395aSxeb@mail.ru 	if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
1121c12b395aSxeb@mail.ru 		fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
1122c12b395aSxeb@mail.ru 	if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
1123c12b395aSxeb@mail.ru 		fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
1124c12b395aSxeb@mail.ru 
1125c12b395aSxeb@mail.ru 	p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
1126c12b395aSxeb@mail.ru 	p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
1127c12b395aSxeb@mail.ru 
1128c12b395aSxeb@mail.ru 	if (p->flags&IP6_TNL_F_CAP_XMIT &&
1129c12b395aSxeb@mail.ru 			p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER)
1130c12b395aSxeb@mail.ru 		dev->flags |= IFF_POINTOPOINT;
1131c12b395aSxeb@mail.ru 	else
1132c12b395aSxeb@mail.ru 		dev->flags &= ~IFF_POINTOPOINT;
1133a483373eSPetr Machata }
1134c12b395aSxeb@mail.ru 
ip6gre_tnl_link_config_route(struct ip6_tnl * t,int set_mtu,int t_hlen)1135a483373eSPetr Machata static void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu,
1136a483373eSPetr Machata 					 int t_hlen)
1137a483373eSPetr Machata {
1138a483373eSPetr Machata 	const struct __ip6_tnl_parm *p = &t->parms;
1139a483373eSPetr Machata 	struct net_device *dev = t->dev;
1140c12b395aSxeb@mail.ru 
1141c12b395aSxeb@mail.ru 	if (p->flags & IP6_TNL_F_CAP_XMIT) {
1142c12b395aSxeb@mail.ru 		int strict = (ipv6_addr_type(&p->raddr) &
1143c12b395aSxeb@mail.ru 			      (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
1144c12b395aSxeb@mail.ru 
114522f08069SNicolas Dichtel 		struct rt6_info *rt = rt6_lookup(t->net,
1146c12b395aSxeb@mail.ru 						 &p->raddr, &p->laddr,
1147b75cc8f9SDavid Ahern 						 p->link, NULL, strict);
1148c12b395aSxeb@mail.ru 
114963159f29SIan Morris 		if (!rt)
1150c12b395aSxeb@mail.ru 			return;
1151c12b395aSxeb@mail.ru 
1152c12b395aSxeb@mail.ru 		if (rt->dst.dev) {
1153832ba596SAntoine Tenart 			unsigned short dst_len = rt->dst.dev->hard_header_len +
1154db2ec95dSTom Herbert 						 t_hlen;
1155c12b395aSxeb@mail.ru 
1156832ba596SAntoine Tenart 			if (t->dev->header_ops)
1157832ba596SAntoine Tenart 				dev->hard_header_len = dst_len;
1158832ba596SAntoine Tenart 			else
1159832ba596SAntoine Tenart 				dev->needed_headroom = dst_len;
1160832ba596SAntoine Tenart 
1161c12b395aSxeb@mail.ru 			if (set_mtu) {
1162d89d7ff0SEric Dumazet 				int mtu = rt->dst.dev->mtu - t_hlen;
1163c12b395aSxeb@mail.ru 
1164d89d7ff0SEric Dumazet 				if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
1165d89d7ff0SEric Dumazet 					mtu -= 8;
1166d89d7ff0SEric Dumazet 				if (dev->type == ARPHRD_ETHER)
1167d89d7ff0SEric Dumazet 					mtu -= ETH_HLEN;
1168d89d7ff0SEric Dumazet 
1169d89d7ff0SEric Dumazet 				if (mtu < IPV6_MIN_MTU)
1170d89d7ff0SEric Dumazet 					mtu = IPV6_MIN_MTU;
1171d89d7ff0SEric Dumazet 				WRITE_ONCE(dev->mtu, mtu);
1172c12b395aSxeb@mail.ru 			}
1173c12b395aSxeb@mail.ru 		}
117494e187c0SAmerigo Wang 		ip6_rt_put(rt);
1175c12b395aSxeb@mail.ru 	}
1176c12b395aSxeb@mail.ru }
1177c12b395aSxeb@mail.ru 
ip6gre_calc_hlen(struct ip6_tnl * tunnel)1178a483373eSPetr Machata static int ip6gre_calc_hlen(struct ip6_tnl *tunnel)
1179a483373eSPetr Machata {
1180a483373eSPetr Machata 	int t_hlen;
1181a483373eSPetr Machata 
1182a483373eSPetr Machata 	tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
1183a483373eSPetr Machata 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
1184a483373eSPetr Machata 
1185a483373eSPetr Machata 	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
1186832ba596SAntoine Tenart 
1187832ba596SAntoine Tenart 	if (tunnel->dev->header_ops)
1188832ba596SAntoine Tenart 		tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
1189832ba596SAntoine Tenart 	else
1190eb95f52fSMaria Pasechnik 		tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
1191832ba596SAntoine Tenart 
1192a483373eSPetr Machata 	return t_hlen;
1193a483373eSPetr Machata }
1194a483373eSPetr Machata 
ip6gre_tnl_link_config(struct ip6_tnl * t,int set_mtu)1195a483373eSPetr Machata static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
1196a483373eSPetr Machata {
1197a483373eSPetr Machata 	ip6gre_tnl_link_config_common(t);
1198a483373eSPetr Machata 	ip6gre_tnl_link_config_route(t, set_mtu, ip6gre_calc_hlen(t));
1199a483373eSPetr Machata }
1200a483373eSPetr Machata 
ip6gre_tnl_copy_tnl_parm(struct ip6_tnl * t,const struct __ip6_tnl_parm * p)1201a6465350SPetr Machata static void ip6gre_tnl_copy_tnl_parm(struct ip6_tnl *t,
1202a6465350SPetr Machata 				     const struct __ip6_tnl_parm *p)
1203c12b395aSxeb@mail.ru {
1204c12b395aSxeb@mail.ru 	t->parms.laddr = p->laddr;
1205c12b395aSxeb@mail.ru 	t->parms.raddr = p->raddr;
1206c12b395aSxeb@mail.ru 	t->parms.flags = p->flags;
1207c12b395aSxeb@mail.ru 	t->parms.hop_limit = p->hop_limit;
1208c12b395aSxeb@mail.ru 	t->parms.encap_limit = p->encap_limit;
1209c12b395aSxeb@mail.ru 	t->parms.flowinfo = p->flowinfo;
1210c12b395aSxeb@mail.ru 	t->parms.link = p->link;
1211c12b395aSxeb@mail.ru 	t->parms.proto = p->proto;
1212c12b395aSxeb@mail.ru 	t->parms.i_key = p->i_key;
1213c12b395aSxeb@mail.ru 	t->parms.o_key = p->o_key;
1214c12b395aSxeb@mail.ru 	t->parms.i_flags = p->i_flags;
1215c12b395aSxeb@mail.ru 	t->parms.o_flags = p->o_flags;
12160a473b82SCraig Gallek 	t->parms.fwmark = p->fwmark;
121780b3671eSHangbin Liu 	t->parms.erspan_ver = p->erspan_ver;
121880b3671eSHangbin Liu 	t->parms.index = p->index;
121980b3671eSHangbin Liu 	t->parms.dir = p->dir;
122080b3671eSHangbin Liu 	t->parms.hwid = p->hwid;
1221607f725fSPaolo Abeni 	dst_cache_reset(&t->dst_cache);
1222a6465350SPetr Machata }
1223a6465350SPetr Machata 
ip6gre_tnl_change(struct ip6_tnl * t,const struct __ip6_tnl_parm * p,int set_mtu)1224a6465350SPetr Machata static int ip6gre_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
1225a6465350SPetr Machata 			     int set_mtu)
1226a6465350SPetr Machata {
1227a6465350SPetr Machata 	ip6gre_tnl_copy_tnl_parm(t, p);
1228c12b395aSxeb@mail.ru 	ip6gre_tnl_link_config(t, set_mtu);
1229c12b395aSxeb@mail.ru 	return 0;
1230c12b395aSxeb@mail.ru }
1231c12b395aSxeb@mail.ru 
ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm * p,const struct ip6_tnl_parm2 * u)1232c12b395aSxeb@mail.ru static void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p,
1233c12b395aSxeb@mail.ru 	const struct ip6_tnl_parm2 *u)
1234c12b395aSxeb@mail.ru {
1235c12b395aSxeb@mail.ru 	p->laddr = u->laddr;
1236c12b395aSxeb@mail.ru 	p->raddr = u->raddr;
1237c12b395aSxeb@mail.ru 	p->flags = u->flags;
1238c12b395aSxeb@mail.ru 	p->hop_limit = u->hop_limit;
1239c12b395aSxeb@mail.ru 	p->encap_limit = u->encap_limit;
1240c12b395aSxeb@mail.ru 	p->flowinfo = u->flowinfo;
1241c12b395aSxeb@mail.ru 	p->link = u->link;
1242c12b395aSxeb@mail.ru 	p->i_key = u->i_key;
1243c12b395aSxeb@mail.ru 	p->o_key = u->o_key;
1244f41fe3c2STom Herbert 	p->i_flags = gre_flags_to_tnl_flags(u->i_flags);
1245f41fe3c2STom Herbert 	p->o_flags = gre_flags_to_tnl_flags(u->o_flags);
1246c12b395aSxeb@mail.ru 	memcpy(p->name, u->name, sizeof(u->name));
1247c12b395aSxeb@mail.ru }
1248c12b395aSxeb@mail.ru 
ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 * u,const struct __ip6_tnl_parm * p)1249c12b395aSxeb@mail.ru static void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u,
1250c12b395aSxeb@mail.ru 	const struct __ip6_tnl_parm *p)
1251c12b395aSxeb@mail.ru {
1252c12b395aSxeb@mail.ru 	u->proto = IPPROTO_GRE;
1253c12b395aSxeb@mail.ru 	u->laddr = p->laddr;
1254c12b395aSxeb@mail.ru 	u->raddr = p->raddr;
1255c12b395aSxeb@mail.ru 	u->flags = p->flags;
1256c12b395aSxeb@mail.ru 	u->hop_limit = p->hop_limit;
1257c12b395aSxeb@mail.ru 	u->encap_limit = p->encap_limit;
1258c12b395aSxeb@mail.ru 	u->flowinfo = p->flowinfo;
1259c12b395aSxeb@mail.ru 	u->link = p->link;
1260c12b395aSxeb@mail.ru 	u->i_key = p->i_key;
1261c12b395aSxeb@mail.ru 	u->o_key = p->o_key;
1262f41fe3c2STom Herbert 	u->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags);
1263f41fe3c2STom Herbert 	u->o_flags = gre_tnl_flags_to_gre_flags(p->o_flags);
1264c12b395aSxeb@mail.ru 	memcpy(u->name, p->name, sizeof(u->name));
1265c12b395aSxeb@mail.ru }
1266c12b395aSxeb@mail.ru 
ip6gre_tunnel_siocdevprivate(struct net_device * dev,struct ifreq * ifr,void __user * data,int cmd)12673e7a1c7cSArnd Bergmann static int ip6gre_tunnel_siocdevprivate(struct net_device *dev,
12683e7a1c7cSArnd Bergmann 					struct ifreq *ifr, void __user *data,
12693e7a1c7cSArnd Bergmann 					int cmd)
1270c12b395aSxeb@mail.ru {
1271c12b395aSxeb@mail.ru 	int err = 0;
1272c12b395aSxeb@mail.ru 	struct ip6_tnl_parm2 p;
1273c12b395aSxeb@mail.ru 	struct __ip6_tnl_parm p1;
127422f08069SNicolas Dichtel 	struct ip6_tnl *t = netdev_priv(dev);
127522f08069SNicolas Dichtel 	struct net *net = t->net;
1276c12b395aSxeb@mail.ru 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
1277c12b395aSxeb@mail.ru 
1278308edfdfSTom Herbert 	memset(&p1, 0, sizeof(p1));
1279308edfdfSTom Herbert 
1280c12b395aSxeb@mail.ru 	switch (cmd) {
1281c12b395aSxeb@mail.ru 	case SIOCGETTUNNEL:
1282c12b395aSxeb@mail.ru 		if (dev == ign->fb_tunnel_dev) {
12833e7a1c7cSArnd Bergmann 			if (copy_from_user(&p, data, sizeof(p))) {
1284c12b395aSxeb@mail.ru 				err = -EFAULT;
1285c12b395aSxeb@mail.ru 				break;
1286c12b395aSxeb@mail.ru 			}
1287c12b395aSxeb@mail.ru 			ip6gre_tnl_parm_from_user(&p1, &p);
1288c12b395aSxeb@mail.ru 			t = ip6gre_tunnel_locate(net, &p1, 0);
128963159f29SIan Morris 			if (!t)
1290c12b395aSxeb@mail.ru 				t = netdev_priv(dev);
129122f08069SNicolas Dichtel 		}
12925dbd5068SAmerigo Wang 		memset(&p, 0, sizeof(p));
1293c12b395aSxeb@mail.ru 		ip6gre_tnl_parm_to_user(&p, &t->parms);
12943e7a1c7cSArnd Bergmann 		if (copy_to_user(data, &p, sizeof(p)))
1295c12b395aSxeb@mail.ru 			err = -EFAULT;
1296c12b395aSxeb@mail.ru 		break;
1297c12b395aSxeb@mail.ru 
1298c12b395aSxeb@mail.ru 	case SIOCADDTUNNEL:
1299c12b395aSxeb@mail.ru 	case SIOCCHGTUNNEL:
1300c12b395aSxeb@mail.ru 		err = -EPERM;
1301af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1302c12b395aSxeb@mail.ru 			goto done;
1303c12b395aSxeb@mail.ru 
1304c12b395aSxeb@mail.ru 		err = -EFAULT;
13053e7a1c7cSArnd Bergmann 		if (copy_from_user(&p, data, sizeof(p)))
1306c12b395aSxeb@mail.ru 			goto done;
1307c12b395aSxeb@mail.ru 
1308c12b395aSxeb@mail.ru 		err = -EINVAL;
1309c12b395aSxeb@mail.ru 		if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))
1310c12b395aSxeb@mail.ru 			goto done;
1311c12b395aSxeb@mail.ru 
1312c12b395aSxeb@mail.ru 		if (!(p.i_flags&GRE_KEY))
1313c12b395aSxeb@mail.ru 			p.i_key = 0;
1314c12b395aSxeb@mail.ru 		if (!(p.o_flags&GRE_KEY))
1315c12b395aSxeb@mail.ru 			p.o_key = 0;
1316c12b395aSxeb@mail.ru 
1317c12b395aSxeb@mail.ru 		ip6gre_tnl_parm_from_user(&p1, &p);
1318c12b395aSxeb@mail.ru 		t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL);
1319c12b395aSxeb@mail.ru 
1320c12b395aSxeb@mail.ru 		if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
132153b24b8fSIan Morris 			if (t) {
1322c12b395aSxeb@mail.ru 				if (t->dev != dev) {
1323c12b395aSxeb@mail.ru 					err = -EEXIST;
1324c12b395aSxeb@mail.ru 					break;
1325c12b395aSxeb@mail.ru 				}
1326c12b395aSxeb@mail.ru 			} else {
1327c12b395aSxeb@mail.ru 				t = netdev_priv(dev);
1328c12b395aSxeb@mail.ru 
1329c12b395aSxeb@mail.ru 				ip6gre_tunnel_unlink(ign, t);
1330c12b395aSxeb@mail.ru 				synchronize_net();
1331c12b395aSxeb@mail.ru 				ip6gre_tnl_change(t, &p1, 1);
1332c12b395aSxeb@mail.ru 				ip6gre_tunnel_link(ign, t);
1333c12b395aSxeb@mail.ru 				netdev_state_change(dev);
1334c12b395aSxeb@mail.ru 			}
1335c12b395aSxeb@mail.ru 		}
1336c12b395aSxeb@mail.ru 
1337c12b395aSxeb@mail.ru 		if (t) {
1338c12b395aSxeb@mail.ru 			err = 0;
1339c12b395aSxeb@mail.ru 
13405dbd5068SAmerigo Wang 			memset(&p, 0, sizeof(p));
1341c12b395aSxeb@mail.ru 			ip6gre_tnl_parm_to_user(&p, &t->parms);
13423e7a1c7cSArnd Bergmann 			if (copy_to_user(data, &p, sizeof(p)))
1343c12b395aSxeb@mail.ru 				err = -EFAULT;
1344c12b395aSxeb@mail.ru 		} else
1345c12b395aSxeb@mail.ru 			err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
1346c12b395aSxeb@mail.ru 		break;
1347c12b395aSxeb@mail.ru 
1348c12b395aSxeb@mail.ru 	case SIOCDELTUNNEL:
1349c12b395aSxeb@mail.ru 		err = -EPERM;
1350af31f412SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1351c12b395aSxeb@mail.ru 			goto done;
1352c12b395aSxeb@mail.ru 
1353c12b395aSxeb@mail.ru 		if (dev == ign->fb_tunnel_dev) {
1354c12b395aSxeb@mail.ru 			err = -EFAULT;
13553e7a1c7cSArnd Bergmann 			if (copy_from_user(&p, data, sizeof(p)))
1356c12b395aSxeb@mail.ru 				goto done;
1357c12b395aSxeb@mail.ru 			err = -ENOENT;
1358c12b395aSxeb@mail.ru 			ip6gre_tnl_parm_from_user(&p1, &p);
1359c12b395aSxeb@mail.ru 			t = ip6gre_tunnel_locate(net, &p1, 0);
136063159f29SIan Morris 			if (!t)
1361c12b395aSxeb@mail.ru 				goto done;
1362c12b395aSxeb@mail.ru 			err = -EPERM;
1363c12b395aSxeb@mail.ru 			if (t == netdev_priv(ign->fb_tunnel_dev))
1364c12b395aSxeb@mail.ru 				goto done;
1365c12b395aSxeb@mail.ru 			dev = t->dev;
1366c12b395aSxeb@mail.ru 		}
1367c12b395aSxeb@mail.ru 		unregister_netdevice(dev);
1368c12b395aSxeb@mail.ru 		err = 0;
1369c12b395aSxeb@mail.ru 		break;
1370c12b395aSxeb@mail.ru 
1371c12b395aSxeb@mail.ru 	default:
1372c12b395aSxeb@mail.ru 		err = -EINVAL;
1373c12b395aSxeb@mail.ru 	}
1374c12b395aSxeb@mail.ru 
1375c12b395aSxeb@mail.ru done:
1376c12b395aSxeb@mail.ru 	return err;
1377c12b395aSxeb@mail.ru }
1378c12b395aSxeb@mail.ru 
ip6gre_header(struct sk_buff * skb,struct net_device * dev,unsigned short type,const void * daddr,const void * saddr,unsigned int len)1379c12b395aSxeb@mail.ru static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
138076cc0d32SXin Long 			 unsigned short type, const void *daddr,
138176cc0d32SXin Long 			 const void *saddr, unsigned int len)
1382c12b395aSxeb@mail.ru {
1383c12b395aSxeb@mail.ru 	struct ip6_tnl *t = netdev_priv(dev);
138476cc0d32SXin Long 	struct ipv6hdr *ipv6h;
138576cc0d32SXin Long 	__be16 *p;
1386c12b395aSxeb@mail.ru 
138776cc0d32SXin Long 	ipv6h = skb_push(skb, t->hlen + sizeof(*ipv6h));
138876cc0d32SXin Long 	ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb,
138976cc0d32SXin Long 						  t->fl.u.ip6.flowlabel,
139076cc0d32SXin Long 						  true, &t->fl.u.ip6));
1391c12b395aSxeb@mail.ru 	ipv6h->hop_limit = t->parms.hop_limit;
1392c12b395aSxeb@mail.ru 	ipv6h->nexthdr = NEXTHDR_GRE;
1393c12b395aSxeb@mail.ru 	ipv6h->saddr = t->parms.laddr;
1394c12b395aSxeb@mail.ru 	ipv6h->daddr = t->parms.raddr;
1395c12b395aSxeb@mail.ru 
139676cc0d32SXin Long 	p = (__be16 *)(ipv6h + 1);
1397c12b395aSxeb@mail.ru 	p[0] = t->parms.o_flags;
1398c12b395aSxeb@mail.ru 	p[1] = htons(type);
1399c12b395aSxeb@mail.ru 
1400c12b395aSxeb@mail.ru 	/*
1401c12b395aSxeb@mail.ru 	 *	Set the source hardware address.
1402c12b395aSxeb@mail.ru 	 */
1403c12b395aSxeb@mail.ru 
1404c12b395aSxeb@mail.ru 	if (saddr)
1405c12b395aSxeb@mail.ru 		memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr));
1406c12b395aSxeb@mail.ru 	if (daddr)
1407c12b395aSxeb@mail.ru 		memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr));
1408c12b395aSxeb@mail.ru 	if (!ipv6_addr_any(&ipv6h->daddr))
1409c12b395aSxeb@mail.ru 		return t->hlen;
1410c12b395aSxeb@mail.ru 
1411c12b395aSxeb@mail.ru 	return -t->hlen;
1412c12b395aSxeb@mail.ru }
1413c12b395aSxeb@mail.ru 
1414c12b395aSxeb@mail.ru static const struct header_ops ip6gre_header_ops = {
1415c12b395aSxeb@mail.ru 	.create	= ip6gre_header,
1416c12b395aSxeb@mail.ru };
1417c12b395aSxeb@mail.ru 
1418c12b395aSxeb@mail.ru static const struct net_device_ops ip6gre_netdev_ops = {
1419c12b395aSxeb@mail.ru 	.ndo_init		= ip6gre_tunnel_init,
1420c12b395aSxeb@mail.ru 	.ndo_uninit		= ip6gre_tunnel_uninit,
1421c12b395aSxeb@mail.ru 	.ndo_start_xmit		= ip6gre_tunnel_xmit,
14223e7a1c7cSArnd Bergmann 	.ndo_siocdevprivate	= ip6gre_tunnel_siocdevprivate,
1423b05229f4STom Herbert 	.ndo_change_mtu		= ip6_tnl_change_mtu,
142498d7fc46SHeiner Kallweit 	.ndo_get_stats64	= dev_get_tstats64,
1425ecf2c06aSNicolas Dichtel 	.ndo_get_iflink		= ip6_tnl_get_iflink,
1426c12b395aSxeb@mail.ru };
1427c12b395aSxeb@mail.ru 
ip6gre_dev_free(struct net_device * dev)1428c12b395aSxeb@mail.ru static void ip6gre_dev_free(struct net_device *dev)
1429c12b395aSxeb@mail.ru {
1430cdf3464eSMartin KaFai Lau 	struct ip6_tnl *t = netdev_priv(dev);
1431cdf3464eSMartin KaFai Lau 
14320c1dd2a1SEran Ben Elisha 	gro_cells_destroy(&t->gro_cells);
1433607f725fSPaolo Abeni 	dst_cache_destroy(&t->dst_cache);
1434c12b395aSxeb@mail.ru 	free_percpu(dev->tstats);
1435c12b395aSxeb@mail.ru }
1436c12b395aSxeb@mail.ru 
ip6gre_tunnel_setup(struct net_device * dev)1437c12b395aSxeb@mail.ru static void ip6gre_tunnel_setup(struct net_device *dev)
1438c12b395aSxeb@mail.ru {
1439c12b395aSxeb@mail.ru 	dev->netdev_ops = &ip6gre_netdev_ops;
1440cf124db5SDavid S. Miller 	dev->needs_free_netdev = true;
1441cf124db5SDavid S. Miller 	dev->priv_destructor = ip6gre_dev_free;
1442c12b395aSxeb@mail.ru 
1443c12b395aSxeb@mail.ru 	dev->type = ARPHRD_IP6GRE;
1444b05229f4STom Herbert 
1445c12b395aSxeb@mail.ru 	dev->flags |= IFF_NOARP;
1446c12b395aSxeb@mail.ru 	dev->addr_len = sizeof(struct in6_addr);
144702875878SEric Dumazet 	netif_keep_dst(dev);
144845ce0fd1SFelix Jia 	/* This perm addr will be used as interface identifier by IPv6 */
144945ce0fd1SFelix Jia 	dev->addr_assign_type = NET_ADDR_RANDOM;
145045ce0fd1SFelix Jia 	eth_random_addr(dev->perm_addr);
1451c12b395aSxeb@mail.ru }
1452c12b395aSxeb@mail.ru 
1453e5a9336aSAlexey Kodanev #define GRE6_FEATURES (NETIF_F_SG |		\
1454e5a9336aSAlexey Kodanev 		       NETIF_F_FRAGLIST |	\
1455e5a9336aSAlexey Kodanev 		       NETIF_F_HIGHDMA |	\
1456e5a9336aSAlexey Kodanev 		       NETIF_F_HW_CSUM)
1457e5a9336aSAlexey Kodanev 
ip6gre_tnl_init_features(struct net_device * dev)1458e5a9336aSAlexey Kodanev static void ip6gre_tnl_init_features(struct net_device *dev)
1459e5a9336aSAlexey Kodanev {
1460e5a9336aSAlexey Kodanev 	struct ip6_tnl *nt = netdev_priv(dev);
1461b11ebf2cSPeilin Ye 	__be16 flags;
1462e5a9336aSAlexey Kodanev 
1463b11ebf2cSPeilin Ye 	dev->features		|= GRE6_FEATURES | NETIF_F_LLTX;
1464e5a9336aSAlexey Kodanev 	dev->hw_features	|= GRE6_FEATURES;
1465e5a9336aSAlexey Kodanev 
1466b11ebf2cSPeilin Ye 	flags = nt->parms.o_flags;
1467b11ebf2cSPeilin Ye 
1468b11ebf2cSPeilin Ye 	/* TCP offload with GRE SEQ is not supported, nor can we support 2
1469b11ebf2cSPeilin Ye 	 * levels of outer headers requiring an update.
1470e5a9336aSAlexey Kodanev 	 */
1471b11ebf2cSPeilin Ye 	if (flags & TUNNEL_SEQ)
1472b11ebf2cSPeilin Ye 		return;
1473b11ebf2cSPeilin Ye 	if (flags & TUNNEL_CSUM && nt->encap.type != TUNNEL_ENCAP_NONE)
1474b11ebf2cSPeilin Ye 		return;
1475b11ebf2cSPeilin Ye 
1476e5a9336aSAlexey Kodanev 	dev->features |= NETIF_F_GSO_SOFTWARE;
1477e5a9336aSAlexey Kodanev 	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
1478e5a9336aSAlexey Kodanev }
1479e5a9336aSAlexey Kodanev 
ip6gre_tunnel_init_common(struct net_device * dev)1480a3c119d3SMartin KaFai Lau static int ip6gre_tunnel_init_common(struct net_device *dev)
1481c12b395aSxeb@mail.ru {
1482c12b395aSxeb@mail.ru 	struct ip6_tnl *tunnel;
1483cdf3464eSMartin KaFai Lau 	int ret;
1484b05229f4STom Herbert 	int t_hlen;
1485c12b395aSxeb@mail.ru 
1486c12b395aSxeb@mail.ru 	tunnel = netdev_priv(dev);
1487c12b395aSxeb@mail.ru 
1488c12b395aSxeb@mail.ru 	tunnel->dev = dev;
14890bd87628SNicolas Dichtel 	tunnel->net = dev_net(dev);
1490c12b395aSxeb@mail.ru 	strcpy(tunnel->parms.name, dev->name);
1491c12b395aSxeb@mail.ru 
1492a3c119d3SMartin KaFai Lau 	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
1493a3c119d3SMartin KaFai Lau 	if (!dev->tstats)
1494a3c119d3SMartin KaFai Lau 		return -ENOMEM;
1495a3c119d3SMartin KaFai Lau 
1496607f725fSPaolo Abeni 	ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
14970c1dd2a1SEran Ben Elisha 	if (ret)
14980c1dd2a1SEran Ben Elisha 		goto cleanup_alloc_pcpu_stats;
14990c1dd2a1SEran Ben Elisha 
15000c1dd2a1SEran Ben Elisha 	ret = gro_cells_init(&tunnel->gro_cells, dev);
15010c1dd2a1SEran Ben Elisha 	if (ret)
15020c1dd2a1SEran Ben Elisha 		goto cleanup_dst_cache_init;
1503cdf3464eSMartin KaFai Lau 
1504a483373eSPetr Machata 	t_hlen = ip6gre_calc_hlen(tunnel);
1505db2ec95dSTom Herbert 	dev->mtu = ETH_DATA_LEN - t_hlen;
15061b227e53SHaishuang Yan 	if (dev->type == ARPHRD_ETHER)
15071b227e53SHaishuang Yan 		dev->mtu -= ETH_HLEN;
1508b05229f4STom Herbert 	if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
1509b05229f4STom Herbert 		dev->mtu -= 8;
1510b05229f4STom Herbert 
15116712abc1SWilliam Tu 	if (tunnel->parms.collect_md) {
15126712abc1SWilliam Tu 		netif_keep_dst(dev);
15136712abc1SWilliam Tu 	}
1514e5a9336aSAlexey Kodanev 	ip6gre_tnl_init_features(dev);
15156712abc1SWilliam Tu 
1516d62607c3SJakub Kicinski 	netdev_hold(dev, &tunnel->dev_tracker, GFP_KERNEL);
1517*a7b862abSEric Dumazet 	netdev_lockdep_set_classes(dev);
1518a3c119d3SMartin KaFai Lau 	return 0;
15190c1dd2a1SEran Ben Elisha 
15200c1dd2a1SEran Ben Elisha cleanup_dst_cache_init:
15210c1dd2a1SEran Ben Elisha 	dst_cache_destroy(&tunnel->dst_cache);
15220c1dd2a1SEran Ben Elisha cleanup_alloc_pcpu_stats:
15230c1dd2a1SEran Ben Elisha 	free_percpu(dev->tstats);
15240c1dd2a1SEran Ben Elisha 	dev->tstats = NULL;
15250c1dd2a1SEran Ben Elisha 	return ret;
1526a3c119d3SMartin KaFai Lau }
1527a3c119d3SMartin KaFai Lau 
ip6gre_tunnel_init(struct net_device * dev)1528a3c119d3SMartin KaFai Lau static int ip6gre_tunnel_init(struct net_device *dev)
1529a3c119d3SMartin KaFai Lau {
1530a3c119d3SMartin KaFai Lau 	struct ip6_tnl *tunnel;
1531a3c119d3SMartin KaFai Lau 	int ret;
1532a3c119d3SMartin KaFai Lau 
1533a3c119d3SMartin KaFai Lau 	ret = ip6gre_tunnel_init_common(dev);
1534a3c119d3SMartin KaFai Lau 	if (ret)
1535a3c119d3SMartin KaFai Lau 		return ret;
1536a3c119d3SMartin KaFai Lau 
1537a3c119d3SMartin KaFai Lau 	tunnel = netdev_priv(dev);
1538a3c119d3SMartin KaFai Lau 
15396712abc1SWilliam Tu 	if (tunnel->parms.collect_md)
15406712abc1SWilliam Tu 		return 0;
15416712abc1SWilliam Tu 
15425a1b7e1aSJakub Kicinski 	__dev_addr_set(dev, &tunnel->parms.laddr, sizeof(struct in6_addr));
1543c12b395aSxeb@mail.ru 	memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr));
1544c12b395aSxeb@mail.ru 
1545c12b395aSxeb@mail.ru 	if (ipv6_addr_any(&tunnel->parms.raddr))
1546c12b395aSxeb@mail.ru 		dev->header_ops = &ip6gre_header_ops;
1547c12b395aSxeb@mail.ru 
1548c12b395aSxeb@mail.ru 	return 0;
1549c12b395aSxeb@mail.ru }
1550c12b395aSxeb@mail.ru 
ip6gre_fb_tunnel_init(struct net_device * dev)1551c12b395aSxeb@mail.ru static void ip6gre_fb_tunnel_init(struct net_device *dev)
1552c12b395aSxeb@mail.ru {
1553c12b395aSxeb@mail.ru 	struct ip6_tnl *tunnel = netdev_priv(dev);
1554c12b395aSxeb@mail.ru 
1555c12b395aSxeb@mail.ru 	tunnel->dev = dev;
15560bd87628SNicolas Dichtel 	tunnel->net = dev_net(dev);
1557c12b395aSxeb@mail.ru 	strcpy(tunnel->parms.name, dev->name);
1558c12b395aSxeb@mail.ru 
1559c12b395aSxeb@mail.ru 	tunnel->hlen		= sizeof(struct ipv6hdr) + 4;
1560c12b395aSxeb@mail.ru }
1561c12b395aSxeb@mail.ru 
1562c12b395aSxeb@mail.ru static struct inet6_protocol ip6gre_protocol __read_mostly = {
1563308edfdfSTom Herbert 	.handler     = gre_rcv,
1564c12b395aSxeb@mail.ru 	.err_handler = ip6gre_err,
1565bcf141b2SGhalem Boudour 	.flags       = INET6_PROTO_FINAL,
1566c12b395aSxeb@mail.ru };
1567c12b395aSxeb@mail.ru 
ip6gre_destroy_tunnels(struct net * net,struct list_head * head)156822f08069SNicolas Dichtel static void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
1569c12b395aSxeb@mail.ru {
157022f08069SNicolas Dichtel 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
157122f08069SNicolas Dichtel 	struct net_device *dev, *aux;
1572c12b395aSxeb@mail.ru 	int prio;
1573c12b395aSxeb@mail.ru 
157422f08069SNicolas Dichtel 	for_each_netdev_safe(net, dev, aux)
157522f08069SNicolas Dichtel 		if (dev->rtnl_link_ops == &ip6gre_link_ops ||
15765a963eb6SWilliam Tu 		    dev->rtnl_link_ops == &ip6gre_tap_ops ||
15775a963eb6SWilliam Tu 		    dev->rtnl_link_ops == &ip6erspan_tap_ops)
157822f08069SNicolas Dichtel 			unregister_netdevice_queue(dev, head);
157922f08069SNicolas Dichtel 
1580c12b395aSxeb@mail.ru 	for (prio = 0; prio < 4; prio++) {
1581c12b395aSxeb@mail.ru 		int h;
1582e87a8f24SJiri Kosina 		for (h = 0; h < IP6_GRE_HASH_SIZE; h++) {
1583c12b395aSxeb@mail.ru 			struct ip6_tnl *t;
1584c12b395aSxeb@mail.ru 
1585c12b395aSxeb@mail.ru 			t = rtnl_dereference(ign->tunnels[prio][h]);
1586c12b395aSxeb@mail.ru 
158753b24b8fSIan Morris 			while (t) {
158822f08069SNicolas Dichtel 				/* If dev is in the same netns, it has already
158922f08069SNicolas Dichtel 				 * been added to the list by the previous loop.
159022f08069SNicolas Dichtel 				 */
159122f08069SNicolas Dichtel 				if (!net_eq(dev_net(t->dev), net))
159222f08069SNicolas Dichtel 					unregister_netdevice_queue(t->dev,
159322f08069SNicolas Dichtel 								   head);
1594c12b395aSxeb@mail.ru 				t = rtnl_dereference(t->next);
1595c12b395aSxeb@mail.ru 			}
1596c12b395aSxeb@mail.ru 		}
1597c12b395aSxeb@mail.ru 	}
1598c12b395aSxeb@mail.ru }
1599c12b395aSxeb@mail.ru 
ip6gre_init_net(struct net * net)1600c12b395aSxeb@mail.ru static int __net_init ip6gre_init_net(struct net *net)
1601c12b395aSxeb@mail.ru {
1602c12b395aSxeb@mail.ru 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
160346ef5b89SWei Yongjun 	struct net_device *ndev;
1604c12b395aSxeb@mail.ru 	int err;
1605c12b395aSxeb@mail.ru 
160679134e6cSEric Dumazet 	if (!net_has_fallback_tunnels(net))
160779134e6cSEric Dumazet 		return 0;
160846ef5b89SWei Yongjun 	ndev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0",
160946ef5b89SWei Yongjun 			    NET_NAME_UNKNOWN, ip6gre_tunnel_setup);
161046ef5b89SWei Yongjun 	if (!ndev) {
1611c12b395aSxeb@mail.ru 		err = -ENOMEM;
1612c12b395aSxeb@mail.ru 		goto err_alloc_dev;
1613c12b395aSxeb@mail.ru 	}
161446ef5b89SWei Yongjun 	ign->fb_tunnel_dev = ndev;
1615c12b395aSxeb@mail.ru 	dev_net_set(ign->fb_tunnel_dev, net);
161622f08069SNicolas Dichtel 	/* FB netdevice is special: we have one, and only one per netns.
161722f08069SNicolas Dichtel 	 * Allowing to move it to another netns is clearly unsafe.
161822f08069SNicolas Dichtel 	 */
161922f08069SNicolas Dichtel 	ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
162022f08069SNicolas Dichtel 
1621c12b395aSxeb@mail.ru 
1622c12b395aSxeb@mail.ru 	ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
1623c12b395aSxeb@mail.ru 	ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
1624c12b395aSxeb@mail.ru 
1625c12b395aSxeb@mail.ru 	err = register_netdev(ign->fb_tunnel_dev);
1626c12b395aSxeb@mail.ru 	if (err)
1627c12b395aSxeb@mail.ru 		goto err_reg_dev;
1628c12b395aSxeb@mail.ru 
1629c12b395aSxeb@mail.ru 	rcu_assign_pointer(ign->tunnels_wc[0],
1630c12b395aSxeb@mail.ru 			   netdev_priv(ign->fb_tunnel_dev));
1631c12b395aSxeb@mail.ru 	return 0;
1632c12b395aSxeb@mail.ru 
1633c12b395aSxeb@mail.ru err_reg_dev:
163446ef5b89SWei Yongjun 	free_netdev(ndev);
1635c12b395aSxeb@mail.ru err_alloc_dev:
1636c12b395aSxeb@mail.ru 	return err;
1637c12b395aSxeb@mail.ru }
1638c12b395aSxeb@mail.ru 
ip6gre_exit_batch_net(struct list_head * net_list)1639bb401caeSEric Dumazet static void __net_exit ip6gre_exit_batch_net(struct list_head *net_list)
1640c12b395aSxeb@mail.ru {
1641bb401caeSEric Dumazet 	struct net *net;
1642c12b395aSxeb@mail.ru 	LIST_HEAD(list);
1643c12b395aSxeb@mail.ru 
1644c12b395aSxeb@mail.ru 	rtnl_lock();
1645bb401caeSEric Dumazet 	list_for_each_entry(net, net_list, exit_list)
164622f08069SNicolas Dichtel 		ip6gre_destroy_tunnels(net, &list);
1647c12b395aSxeb@mail.ru 	unregister_netdevice_many(&list);
1648c12b395aSxeb@mail.ru 	rtnl_unlock();
1649c12b395aSxeb@mail.ru }
1650c12b395aSxeb@mail.ru 
1651c12b395aSxeb@mail.ru static struct pernet_operations ip6gre_net_ops = {
1652c12b395aSxeb@mail.ru 	.init = ip6gre_init_net,
1653bb401caeSEric Dumazet 	.exit_batch = ip6gre_exit_batch_net,
1654c12b395aSxeb@mail.ru 	.id   = &ip6gre_net_id,
1655c12b395aSxeb@mail.ru 	.size = sizeof(struct ip6gre_net),
1656c12b395aSxeb@mail.ru };
1657c12b395aSxeb@mail.ru 
ip6gre_tunnel_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)1658a8b8a889SMatthias Schiffer static int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[],
1659a8b8a889SMatthias Schiffer 				  struct netlink_ext_ack *extack)
1660c12b395aSxeb@mail.ru {
1661c12b395aSxeb@mail.ru 	__be16 flags;
1662c12b395aSxeb@mail.ru 
1663c12b395aSxeb@mail.ru 	if (!data)
1664c12b395aSxeb@mail.ru 		return 0;
1665c12b395aSxeb@mail.ru 
1666c12b395aSxeb@mail.ru 	flags = 0;
1667c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_IFLAGS])
1668c12b395aSxeb@mail.ru 		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
1669c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_OFLAGS])
1670c12b395aSxeb@mail.ru 		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
1671c12b395aSxeb@mail.ru 	if (flags & (GRE_VERSION|GRE_ROUTING))
1672c12b395aSxeb@mail.ru 		return -EINVAL;
1673c12b395aSxeb@mail.ru 
1674c12b395aSxeb@mail.ru 	return 0;
1675c12b395aSxeb@mail.ru }
1676c12b395aSxeb@mail.ru 
ip6gre_tap_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)1677a8b8a889SMatthias Schiffer static int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[],
1678a8b8a889SMatthias Schiffer 			       struct netlink_ext_ack *extack)
1679c12b395aSxeb@mail.ru {
1680c12b395aSxeb@mail.ru 	struct in6_addr daddr;
1681c12b395aSxeb@mail.ru 
1682c12b395aSxeb@mail.ru 	if (tb[IFLA_ADDRESS]) {
1683c12b395aSxeb@mail.ru 		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
1684c12b395aSxeb@mail.ru 			return -EINVAL;
1685c12b395aSxeb@mail.ru 		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
1686c12b395aSxeb@mail.ru 			return -EADDRNOTAVAIL;
1687c12b395aSxeb@mail.ru 	}
1688c12b395aSxeb@mail.ru 
1689c12b395aSxeb@mail.ru 	if (!data)
1690c12b395aSxeb@mail.ru 		goto out;
1691c12b395aSxeb@mail.ru 
1692c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_REMOTE]) {
169367b61f6cSJiri Benc 		daddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]);
1694c12b395aSxeb@mail.ru 		if (ipv6_addr_any(&daddr))
1695c12b395aSxeb@mail.ru 			return -EINVAL;
1696c12b395aSxeb@mail.ru 	}
1697c12b395aSxeb@mail.ru 
1698c12b395aSxeb@mail.ru out:
1699a8b8a889SMatthias Schiffer 	return ip6gre_tunnel_validate(tb, data, extack);
1700c12b395aSxeb@mail.ru }
1701c12b395aSxeb@mail.ru 
ip6erspan_tap_validate(struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)17025a963eb6SWilliam Tu static int ip6erspan_tap_validate(struct nlattr *tb[], struct nlattr *data[],
17035a963eb6SWilliam Tu 				  struct netlink_ext_ack *extack)
17045a963eb6SWilliam Tu {
17055a963eb6SWilliam Tu 	__be16 flags = 0;
170694d7d8f2SWilliam Tu 	int ret, ver = 0;
17075a963eb6SWilliam Tu 
17085a963eb6SWilliam Tu 	if (!data)
17095a963eb6SWilliam Tu 		return 0;
17105a963eb6SWilliam Tu 
17115a963eb6SWilliam Tu 	ret = ip6gre_tap_validate(tb, data, extack);
17125a963eb6SWilliam Tu 	if (ret)
17135a963eb6SWilliam Tu 		return ret;
17145a963eb6SWilliam Tu 
17155a963eb6SWilliam Tu 	/* ERSPAN should only have GRE sequence and key flag */
17165a963eb6SWilliam Tu 	if (data[IFLA_GRE_OFLAGS])
17175a963eb6SWilliam Tu 		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
17185a963eb6SWilliam Tu 	if (data[IFLA_GRE_IFLAGS])
17195a963eb6SWilliam Tu 		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
17205a963eb6SWilliam Tu 	if (!data[IFLA_GRE_COLLECT_METADATA] &&
17215a963eb6SWilliam Tu 	    flags != (GRE_SEQ | GRE_KEY))
17225a963eb6SWilliam Tu 		return -EINVAL;
17235a963eb6SWilliam Tu 
17245a963eb6SWilliam Tu 	/* ERSPAN Session ID only has 10-bit. Since we reuse
17255a963eb6SWilliam Tu 	 * 32-bit key field as ID, check it's range.
17265a963eb6SWilliam Tu 	 */
17275a963eb6SWilliam Tu 	if (data[IFLA_GRE_IKEY] &&
17285a963eb6SWilliam Tu 	    (ntohl(nla_get_be32(data[IFLA_GRE_IKEY])) & ~ID_MASK))
17295a963eb6SWilliam Tu 		return -EINVAL;
17305a963eb6SWilliam Tu 
17315a963eb6SWilliam Tu 	if (data[IFLA_GRE_OKEY] &&
17325a963eb6SWilliam Tu 	    (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK))
17335a963eb6SWilliam Tu 		return -EINVAL;
17345a963eb6SWilliam Tu 
173594d7d8f2SWilliam Tu 	if (data[IFLA_GRE_ERSPAN_VER]) {
173694d7d8f2SWilliam Tu 		ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
173794d7d8f2SWilliam Tu 		if (ver != 1 && ver != 2)
173894d7d8f2SWilliam Tu 			return -EINVAL;
173994d7d8f2SWilliam Tu 	}
174094d7d8f2SWilliam Tu 
174194d7d8f2SWilliam Tu 	if (ver == 1) {
17425a963eb6SWilliam Tu 		if (data[IFLA_GRE_ERSPAN_INDEX]) {
17435a963eb6SWilliam Tu 			u32 index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
17445a963eb6SWilliam Tu 
17455a963eb6SWilliam Tu 			if (index & ~INDEX_MASK)
17465a963eb6SWilliam Tu 				return -EINVAL;
17475a963eb6SWilliam Tu 		}
174894d7d8f2SWilliam Tu 	} else if (ver == 2) {
174994d7d8f2SWilliam Tu 		if (data[IFLA_GRE_ERSPAN_DIR]) {
175094d7d8f2SWilliam Tu 			u16 dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
175194d7d8f2SWilliam Tu 
175294d7d8f2SWilliam Tu 			if (dir & ~(DIR_MASK >> DIR_OFFSET))
175394d7d8f2SWilliam Tu 				return -EINVAL;
175494d7d8f2SWilliam Tu 		}
175594d7d8f2SWilliam Tu 
175694d7d8f2SWilliam Tu 		if (data[IFLA_GRE_ERSPAN_HWID]) {
175794d7d8f2SWilliam Tu 			u16 hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
175894d7d8f2SWilliam Tu 
175994d7d8f2SWilliam Tu 			if (hwid & ~(HWID_MASK >> HWID_OFFSET))
176094d7d8f2SWilliam Tu 				return -EINVAL;
176194d7d8f2SWilliam Tu 		}
176294d7d8f2SWilliam Tu 	}
176394d7d8f2SWilliam Tu 
17645a963eb6SWilliam Tu 	return 0;
17655a963eb6SWilliam Tu }
1766c12b395aSxeb@mail.ru 
ip6erspan_set_version(struct nlattr * data[],struct __ip6_tnl_parm * parms)17674974d5f6SLorenzo Bianconi static void ip6erspan_set_version(struct nlattr *data[],
17684974d5f6SLorenzo Bianconi 				  struct __ip6_tnl_parm *parms)
17694974d5f6SLorenzo Bianconi {
1770efcc9bcaSLorenzo Bianconi 	if (!data)
1771efcc9bcaSLorenzo Bianconi 		return;
1772efcc9bcaSLorenzo Bianconi 
17734974d5f6SLorenzo Bianconi 	parms->erspan_ver = 1;
17744974d5f6SLorenzo Bianconi 	if (data[IFLA_GRE_ERSPAN_VER])
17754974d5f6SLorenzo Bianconi 		parms->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
17764974d5f6SLorenzo Bianconi 
17774974d5f6SLorenzo Bianconi 	if (parms->erspan_ver == 1) {
17784974d5f6SLorenzo Bianconi 		if (data[IFLA_GRE_ERSPAN_INDEX])
17794974d5f6SLorenzo Bianconi 			parms->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
17804974d5f6SLorenzo Bianconi 	} else if (parms->erspan_ver == 2) {
17814974d5f6SLorenzo Bianconi 		if (data[IFLA_GRE_ERSPAN_DIR])
17824974d5f6SLorenzo Bianconi 			parms->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
17834974d5f6SLorenzo Bianconi 		if (data[IFLA_GRE_ERSPAN_HWID])
17844974d5f6SLorenzo Bianconi 			parms->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
17854974d5f6SLorenzo Bianconi 	}
17864974d5f6SLorenzo Bianconi }
17874974d5f6SLorenzo Bianconi 
ip6gre_netlink_parms(struct nlattr * data[],struct __ip6_tnl_parm * parms)1788c12b395aSxeb@mail.ru static void ip6gre_netlink_parms(struct nlattr *data[],
1789c12b395aSxeb@mail.ru 				struct __ip6_tnl_parm *parms)
1790c12b395aSxeb@mail.ru {
1791c12b395aSxeb@mail.ru 	memset(parms, 0, sizeof(*parms));
1792c12b395aSxeb@mail.ru 
1793c12b395aSxeb@mail.ru 	if (!data)
1794c12b395aSxeb@mail.ru 		return;
1795c12b395aSxeb@mail.ru 
1796c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_LINK])
1797c12b395aSxeb@mail.ru 		parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
1798c12b395aSxeb@mail.ru 
1799c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_IFLAGS])
1800f41fe3c2STom Herbert 		parms->i_flags = gre_flags_to_tnl_flags(
1801f41fe3c2STom Herbert 				nla_get_be16(data[IFLA_GRE_IFLAGS]));
1802c12b395aSxeb@mail.ru 
1803c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_OFLAGS])
1804f41fe3c2STom Herbert 		parms->o_flags = gre_flags_to_tnl_flags(
1805f41fe3c2STom Herbert 				nla_get_be16(data[IFLA_GRE_OFLAGS]));
1806c12b395aSxeb@mail.ru 
1807c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_IKEY])
1808c12b395aSxeb@mail.ru 		parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
1809c12b395aSxeb@mail.ru 
1810c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_OKEY])
1811c12b395aSxeb@mail.ru 		parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
1812c12b395aSxeb@mail.ru 
1813c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_LOCAL])
181467b61f6cSJiri Benc 		parms->laddr = nla_get_in6_addr(data[IFLA_GRE_LOCAL]);
1815c12b395aSxeb@mail.ru 
1816c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_REMOTE])
181767b61f6cSJiri Benc 		parms->raddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]);
1818c12b395aSxeb@mail.ru 
1819c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_TTL])
1820c12b395aSxeb@mail.ru 		parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]);
1821c12b395aSxeb@mail.ru 
1822c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_ENCAP_LIMIT])
1823c12b395aSxeb@mail.ru 		parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]);
1824c12b395aSxeb@mail.ru 
1825c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_FLOWINFO])
1826c2675de4SLance Richardson 		parms->flowinfo = nla_get_be32(data[IFLA_GRE_FLOWINFO]);
1827c12b395aSxeb@mail.ru 
1828c12b395aSxeb@mail.ru 	if (data[IFLA_GRE_FLAGS])
1829c12b395aSxeb@mail.ru 		parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]);
18300a473b82SCraig Gallek 
18310a473b82SCraig Gallek 	if (data[IFLA_GRE_FWMARK])
18320a473b82SCraig Gallek 		parms->fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
18335a963eb6SWilliam Tu 
18346712abc1SWilliam Tu 	if (data[IFLA_GRE_COLLECT_METADATA])
18356712abc1SWilliam Tu 		parms->collect_md = true;
1836c12b395aSxeb@mail.ru }
1837c12b395aSxeb@mail.ru 
ip6gre_tap_init(struct net_device * dev)1838c12b395aSxeb@mail.ru static int ip6gre_tap_init(struct net_device *dev)
1839c12b395aSxeb@mail.ru {
1840a3c119d3SMartin KaFai Lau 	int ret;
1841a3c119d3SMartin KaFai Lau 
1842a3c119d3SMartin KaFai Lau 	ret = ip6gre_tunnel_init_common(dev);
1843a3c119d3SMartin KaFai Lau 	if (ret)
1844a3c119d3SMartin KaFai Lau 		return ret;
1845c12b395aSxeb@mail.ru 
18460a46baafSShweta Choudaha 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
18470a46baafSShweta Choudaha 
1848c12b395aSxeb@mail.ru 	return 0;
1849c12b395aSxeb@mail.ru }
1850c12b395aSxeb@mail.ru 
1851c12b395aSxeb@mail.ru static const struct net_device_ops ip6gre_tap_netdev_ops = {
1852c12b395aSxeb@mail.ru 	.ndo_init = ip6gre_tap_init,
1853c12b395aSxeb@mail.ru 	.ndo_uninit = ip6gre_tunnel_uninit,
1854c12b395aSxeb@mail.ru 	.ndo_start_xmit = ip6gre_tunnel_xmit,
1855c12b395aSxeb@mail.ru 	.ndo_set_mac_address = eth_mac_addr,
1856c12b395aSxeb@mail.ru 	.ndo_validate_addr = eth_validate_addr,
1857b05229f4STom Herbert 	.ndo_change_mtu = ip6_tnl_change_mtu,
185898d7fc46SHeiner Kallweit 	.ndo_get_stats64 = dev_get_tstats64,
1859ecf2c06aSNicolas Dichtel 	.ndo_get_iflink = ip6_tnl_get_iflink,
1860c12b395aSxeb@mail.ru };
1861c12b395aSxeb@mail.ru 
ip6erspan_calc_hlen(struct ip6_tnl * tunnel)18622d665034SPetr Machata static int ip6erspan_calc_hlen(struct ip6_tnl *tunnel)
18632d665034SPetr Machata {
18642d665034SPetr Machata 	int t_hlen;
18652d665034SPetr Machata 
18662d665034SPetr Machata 	tunnel->tun_hlen = 8;
18672d665034SPetr Machata 	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
18682d665034SPetr Machata 		       erspan_hdr_len(tunnel->parms.erspan_ver);
18692d665034SPetr Machata 
18702d665034SPetr Machata 	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
1871eb95f52fSMaria Pasechnik 	tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
18722d665034SPetr Machata 	return t_hlen;
18732d665034SPetr Machata }
18742d665034SPetr Machata 
ip6erspan_tap_init(struct net_device * dev)18755a963eb6SWilliam Tu static int ip6erspan_tap_init(struct net_device *dev)
18765a963eb6SWilliam Tu {
18775a963eb6SWilliam Tu 	struct ip6_tnl *tunnel;
18785a963eb6SWilliam Tu 	int t_hlen;
18795a963eb6SWilliam Tu 	int ret;
18805a963eb6SWilliam Tu 
18815a963eb6SWilliam Tu 	tunnel = netdev_priv(dev);
18825a963eb6SWilliam Tu 
18835a963eb6SWilliam Tu 	tunnel->dev = dev;
18845a963eb6SWilliam Tu 	tunnel->net = dev_net(dev);
18855a963eb6SWilliam Tu 	strcpy(tunnel->parms.name, dev->name);
18865a963eb6SWilliam Tu 
18875a963eb6SWilliam Tu 	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
18885a963eb6SWilliam Tu 	if (!dev->tstats)
18895a963eb6SWilliam Tu 		return -ENOMEM;
18905a963eb6SWilliam Tu 
18915a963eb6SWilliam Tu 	ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
18920c1dd2a1SEran Ben Elisha 	if (ret)
18930c1dd2a1SEran Ben Elisha 		goto cleanup_alloc_pcpu_stats;
18940c1dd2a1SEran Ben Elisha 
18950c1dd2a1SEran Ben Elisha 	ret = gro_cells_init(&tunnel->gro_cells, dev);
18960c1dd2a1SEran Ben Elisha 	if (ret)
18970c1dd2a1SEran Ben Elisha 		goto cleanup_dst_cache_init;
18985a963eb6SWilliam Tu 
18992d665034SPetr Machata 	t_hlen = ip6erspan_calc_hlen(tunnel);
19005a963eb6SWilliam Tu 	dev->mtu = ETH_DATA_LEN - t_hlen;
19015a963eb6SWilliam Tu 	if (dev->type == ARPHRD_ETHER)
19025a963eb6SWilliam Tu 		dev->mtu -= ETH_HLEN;
19035a963eb6SWilliam Tu 	if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
19045a963eb6SWilliam Tu 		dev->mtu -= 8;
19055a963eb6SWilliam Tu 
19065a963eb6SWilliam Tu 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
19072d665034SPetr Machata 	ip6erspan_tnl_link_config(tunnel, 1);
19085a963eb6SWilliam Tu 
1909d62607c3SJakub Kicinski 	netdev_hold(dev, &tunnel->dev_tracker, GFP_KERNEL);
1910*a7b862abSEric Dumazet 	netdev_lockdep_set_classes(dev);
19115a963eb6SWilliam Tu 	return 0;
19120c1dd2a1SEran Ben Elisha 
19130c1dd2a1SEran Ben Elisha cleanup_dst_cache_init:
19140c1dd2a1SEran Ben Elisha 	dst_cache_destroy(&tunnel->dst_cache);
19150c1dd2a1SEran Ben Elisha cleanup_alloc_pcpu_stats:
19160c1dd2a1SEran Ben Elisha 	free_percpu(dev->tstats);
19170c1dd2a1SEran Ben Elisha 	dev->tstats = NULL;
19180c1dd2a1SEran Ben Elisha 	return ret;
19195a963eb6SWilliam Tu }
19205a963eb6SWilliam Tu 
19215a963eb6SWilliam Tu static const struct net_device_ops ip6erspan_netdev_ops = {
19225a963eb6SWilliam Tu 	.ndo_init =		ip6erspan_tap_init,
1923b80d0b93SWilliam Tu 	.ndo_uninit =		ip6erspan_tunnel_uninit,
19245a963eb6SWilliam Tu 	.ndo_start_xmit =	ip6erspan_tunnel_xmit,
19255a963eb6SWilliam Tu 	.ndo_set_mac_address =	eth_mac_addr,
19265a963eb6SWilliam Tu 	.ndo_validate_addr =	eth_validate_addr,
19275a963eb6SWilliam Tu 	.ndo_change_mtu =	ip6_tnl_change_mtu,
192898d7fc46SHeiner Kallweit 	.ndo_get_stats64 =	dev_get_tstats64,
19295a963eb6SWilliam Tu 	.ndo_get_iflink =	ip6_tnl_get_iflink,
19305a963eb6SWilliam Tu };
19315a963eb6SWilliam Tu 
ip6gre_tap_setup(struct net_device * dev)1932c12b395aSxeb@mail.ru static void ip6gre_tap_setup(struct net_device *dev)
1933c12b395aSxeb@mail.ru {
1934c12b395aSxeb@mail.ru 
1935c12b395aSxeb@mail.ru 	ether_setup(dev);
1936c12b395aSxeb@mail.ru 
19372c52129aSXin Long 	dev->max_mtu = 0;
1938c12b395aSxeb@mail.ru 	dev->netdev_ops = &ip6gre_tap_netdev_ops;
1939cf124db5SDavid S. Miller 	dev->needs_free_netdev = true;
1940cf124db5SDavid S. Miller 	dev->priv_destructor = ip6gre_dev_free;
1941c12b395aSxeb@mail.ru 
1942d13b161cSJiri Benc 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
19430a46baafSShweta Choudaha 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
19442d40557cSXin Long 	netif_keep_dst(dev);
1945c12b395aSxeb@mail.ru }
1946c12b395aSxeb@mail.ru 
ip6gre_netlink_encap_parms(struct nlattr * data[],struct ip_tunnel_encap * ipencap)19471faf3d9fSTom Herbert static bool ip6gre_netlink_encap_parms(struct nlattr *data[],
19481faf3d9fSTom Herbert 				       struct ip_tunnel_encap *ipencap)
19491faf3d9fSTom Herbert {
19501faf3d9fSTom Herbert 	bool ret = false;
19511faf3d9fSTom Herbert 
19521faf3d9fSTom Herbert 	memset(ipencap, 0, sizeof(*ipencap));
19531faf3d9fSTom Herbert 
19541faf3d9fSTom Herbert 	if (!data)
19551faf3d9fSTom Herbert 		return ret;
19561faf3d9fSTom Herbert 
19571faf3d9fSTom Herbert 	if (data[IFLA_GRE_ENCAP_TYPE]) {
19581faf3d9fSTom Herbert 		ret = true;
19591faf3d9fSTom Herbert 		ipencap->type = nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]);
19601faf3d9fSTom Herbert 	}
19611faf3d9fSTom Herbert 
19621faf3d9fSTom Herbert 	if (data[IFLA_GRE_ENCAP_FLAGS]) {
19631faf3d9fSTom Herbert 		ret = true;
19641faf3d9fSTom Herbert 		ipencap->flags = nla_get_u16(data[IFLA_GRE_ENCAP_FLAGS]);
19651faf3d9fSTom Herbert 	}
19661faf3d9fSTom Herbert 
19671faf3d9fSTom Herbert 	if (data[IFLA_GRE_ENCAP_SPORT]) {
19681faf3d9fSTom Herbert 		ret = true;
19691faf3d9fSTom Herbert 		ipencap->sport = nla_get_be16(data[IFLA_GRE_ENCAP_SPORT]);
19701faf3d9fSTom Herbert 	}
19711faf3d9fSTom Herbert 
19721faf3d9fSTom Herbert 	if (data[IFLA_GRE_ENCAP_DPORT]) {
19731faf3d9fSTom Herbert 		ret = true;
19741faf3d9fSTom Herbert 		ipencap->dport = nla_get_be16(data[IFLA_GRE_ENCAP_DPORT]);
19751faf3d9fSTom Herbert 	}
19761faf3d9fSTom Herbert 
19771faf3d9fSTom Herbert 	return ret;
19781faf3d9fSTom Herbert }
19791faf3d9fSTom Herbert 
ip6gre_newlink_common(struct net * src_net,struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)19807fa38a7cSPetr Machata static int ip6gre_newlink_common(struct net *src_net, struct net_device *dev,
19817a3f4a18SMatthias Schiffer 				 struct nlattr *tb[], struct nlattr *data[],
19827a3f4a18SMatthias Schiffer 				 struct netlink_ext_ack *extack)
1983c12b395aSxeb@mail.ru {
1984c12b395aSxeb@mail.ru 	struct ip6_tnl *nt;
19851faf3d9fSTom Herbert 	struct ip_tunnel_encap ipencap;
1986c12b395aSxeb@mail.ru 	int err;
1987c12b395aSxeb@mail.ru 
1988c12b395aSxeb@mail.ru 	nt = netdev_priv(dev);
19891faf3d9fSTom Herbert 
19901faf3d9fSTom Herbert 	if (ip6gre_netlink_encap_parms(data, &ipencap)) {
19911faf3d9fSTom Herbert 		int err = ip6_tnl_encap_setup(nt, &ipencap);
19921faf3d9fSTom Herbert 
19931faf3d9fSTom Herbert 		if (err < 0)
19941faf3d9fSTom Herbert 			return err;
19951faf3d9fSTom Herbert 	}
19961faf3d9fSTom Herbert 
1997c12b395aSxeb@mail.ru 	if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
1998c12b395aSxeb@mail.ru 		eth_hw_addr_random(dev);
1999c12b395aSxeb@mail.ru 
2000c12b395aSxeb@mail.ru 	nt->dev = dev;
20010bd87628SNicolas Dichtel 	nt->net = dev_net(dev);
2002c12b395aSxeb@mail.ru 
2003c12b395aSxeb@mail.ru 	err = register_netdevice(dev);
2004c12b395aSxeb@mail.ru 	if (err)
2005c12b395aSxeb@mail.ru 		goto out;
2006c12b395aSxeb@mail.ru 
2007128bb975SAlexey Kodanev 	if (tb[IFLA_MTU])
2008128bb975SAlexey Kodanev 		ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
2009128bb975SAlexey Kodanev 
2010c12b395aSxeb@mail.ru out:
2011c12b395aSxeb@mail.ru 	return err;
2012c12b395aSxeb@mail.ru }
2013c12b395aSxeb@mail.ru 
ip6gre_newlink(struct net * src_net,struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)20147fa38a7cSPetr Machata static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
20157fa38a7cSPetr Machata 			  struct nlattr *tb[], struct nlattr *data[],
20167fa38a7cSPetr Machata 			  struct netlink_ext_ack *extack)
20177fa38a7cSPetr Machata {
20187fa38a7cSPetr Machata 	struct ip6_tnl *nt = netdev_priv(dev);
20197fa38a7cSPetr Machata 	struct net *net = dev_net(dev);
2020b80d0b93SWilliam Tu 	struct ip6gre_net *ign;
2021b80d0b93SWilliam Tu 	int err;
20227fa38a7cSPetr Machata 
2023b80d0b93SWilliam Tu 	ip6gre_netlink_parms(data, &nt->parms);
2024b80d0b93SWilliam Tu 	ign = net_generic(net, ip6gre_net_id);
2025b80d0b93SWilliam Tu 
2026b80d0b93SWilliam Tu 	if (nt->parms.collect_md) {
2027b80d0b93SWilliam Tu 		if (rtnl_dereference(ign->collect_md_tun))
2028b80d0b93SWilliam Tu 			return -EEXIST;
2029b80d0b93SWilliam Tu 	} else {
2030b80d0b93SWilliam Tu 		if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
2031b80d0b93SWilliam Tu 			return -EEXIST;
2032b80d0b93SWilliam Tu 	}
2033b80d0b93SWilliam Tu 
2034b80d0b93SWilliam Tu 	err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
20357fa38a7cSPetr Machata 	if (!err) {
20367fa38a7cSPetr Machata 		ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
2037b80d0b93SWilliam Tu 		ip6gre_tunnel_link_md(ign, nt);
20387fa38a7cSPetr Machata 		ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
20397fa38a7cSPetr Machata 	}
20407fa38a7cSPetr Machata 	return err;
20417fa38a7cSPetr Machata }
20427fa38a7cSPetr Machata 
2043c8632fc3SPetr Machata static struct ip6_tnl *
ip6gre_changelink_common(struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct __ip6_tnl_parm * p_p,struct netlink_ext_ack * extack)2044c8632fc3SPetr Machata ip6gre_changelink_common(struct net_device *dev, struct nlattr *tb[],
2045c8632fc3SPetr Machata 			 struct nlattr *data[], struct __ip6_tnl_parm *p_p,
2046ad744b22SMatthias Schiffer 			 struct netlink_ext_ack *extack)
2047c12b395aSxeb@mail.ru {
204822f08069SNicolas Dichtel 	struct ip6_tnl *t, *nt = netdev_priv(dev);
204922f08069SNicolas Dichtel 	struct net *net = nt->net;
2050c12b395aSxeb@mail.ru 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
20511faf3d9fSTom Herbert 	struct ip_tunnel_encap ipencap;
2052c12b395aSxeb@mail.ru 
2053c12b395aSxeb@mail.ru 	if (dev == ign->fb_tunnel_dev)
2054c8632fc3SPetr Machata 		return ERR_PTR(-EINVAL);
2055c12b395aSxeb@mail.ru 
20561faf3d9fSTom Herbert 	if (ip6gre_netlink_encap_parms(data, &ipencap)) {
20571faf3d9fSTom Herbert 		int err = ip6_tnl_encap_setup(nt, &ipencap);
20581faf3d9fSTom Herbert 
20591faf3d9fSTom Herbert 		if (err < 0)
2060c8632fc3SPetr Machata 			return ERR_PTR(err);
20611faf3d9fSTom Herbert 	}
20621faf3d9fSTom Herbert 
2063c8632fc3SPetr Machata 	ip6gre_netlink_parms(data, p_p);
2064c12b395aSxeb@mail.ru 
2065c8632fc3SPetr Machata 	t = ip6gre_tunnel_locate(net, p_p, 0);
2066c12b395aSxeb@mail.ru 
2067c12b395aSxeb@mail.ru 	if (t) {
2068c12b395aSxeb@mail.ru 		if (t->dev != dev)
2069c8632fc3SPetr Machata 			return ERR_PTR(-EEXIST);
2070c12b395aSxeb@mail.ru 	} else {
2071c12b395aSxeb@mail.ru 		t = nt;
20726a61d4dbSNicolas Dichtel 	}
2073c12b395aSxeb@mail.ru 
2074c8632fc3SPetr Machata 	return t;
2075c8632fc3SPetr Machata }
2076c8632fc3SPetr Machata 
ip6gre_changelink(struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)2077c12b395aSxeb@mail.ru static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
2078c12b395aSxeb@mail.ru 			     struct nlattr *data[],
2079c12b395aSxeb@mail.ru 			     struct netlink_ext_ack *extack)
2080c12b395aSxeb@mail.ru {
2081ab5098faSOlivier Matz 	struct ip6_tnl *t = netdev_priv(dev);
2082ab5098faSOlivier Matz 	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
2083c12b395aSxeb@mail.ru 	struct __ip6_tnl_parm p;
2084c12b395aSxeb@mail.ru 
2085c8632fc3SPetr Machata 	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
2086c8632fc3SPetr Machata 	if (IS_ERR(t))
2087c8632fc3SPetr Machata 		return PTR_ERR(t);
2088c12b395aSxeb@mail.ru 
2089b80d0b93SWilliam Tu 	ip6gre_tunnel_unlink_md(ign, t);
2090c12b395aSxeb@mail.ru 	ip6gre_tunnel_unlink(ign, t);
2091c12b395aSxeb@mail.ru 	ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
2092b80d0b93SWilliam Tu 	ip6gre_tunnel_link_md(ign, t);
2093c12b395aSxeb@mail.ru 	ip6gre_tunnel_link(ign, t);
2094c12b395aSxeb@mail.ru 	return 0;
2095c12b395aSxeb@mail.ru }
2096c12b395aSxeb@mail.ru 
ip6gre_dellink(struct net_device * dev,struct list_head * head)209754d63f78SNicolas Dichtel static void ip6gre_dellink(struct net_device *dev, struct list_head *head)
209854d63f78SNicolas Dichtel {
209954d63f78SNicolas Dichtel 	struct net *net = dev_net(dev);
210054d63f78SNicolas Dichtel 	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
210154d63f78SNicolas Dichtel 
210254d63f78SNicolas Dichtel 	if (dev != ign->fb_tunnel_dev)
210354d63f78SNicolas Dichtel 		unregister_netdevice_queue(dev, head);
210454d63f78SNicolas Dichtel }
210554d63f78SNicolas Dichtel 
ip6gre_get_size(const struct net_device * dev)2106c12b395aSxeb@mail.ru static size_t ip6gre_get_size(const struct net_device *dev)
2107c12b395aSxeb@mail.ru {
2108c12b395aSxeb@mail.ru 	return
2109c12b395aSxeb@mail.ru 		/* IFLA_GRE_LINK */
2110c12b395aSxeb@mail.ru 		nla_total_size(4) +
2111c12b395aSxeb@mail.ru 		/* IFLA_GRE_IFLAGS */
2112c12b395aSxeb@mail.ru 		nla_total_size(2) +
2113c12b395aSxeb@mail.ru 		/* IFLA_GRE_OFLAGS */
2114c12b395aSxeb@mail.ru 		nla_total_size(2) +
2115c12b395aSxeb@mail.ru 		/* IFLA_GRE_IKEY */
2116c12b395aSxeb@mail.ru 		nla_total_size(4) +
2117c12b395aSxeb@mail.ru 		/* IFLA_GRE_OKEY */
2118c12b395aSxeb@mail.ru 		nla_total_size(4) +
2119c12b395aSxeb@mail.ru 		/* IFLA_GRE_LOCAL */
2120a3754133SNicolas Dichtel 		nla_total_size(sizeof(struct in6_addr)) +
2121c12b395aSxeb@mail.ru 		/* IFLA_GRE_REMOTE */
2122a3754133SNicolas Dichtel 		nla_total_size(sizeof(struct in6_addr)) +
2123c12b395aSxeb@mail.ru 		/* IFLA_GRE_TTL */
2124c12b395aSxeb@mail.ru 		nla_total_size(1) +
2125c12b395aSxeb@mail.ru 		/* IFLA_GRE_ENCAP_LIMIT */
2126c12b395aSxeb@mail.ru 		nla_total_size(1) +
2127c12b395aSxeb@mail.ru 		/* IFLA_GRE_FLOWINFO */
2128c12b395aSxeb@mail.ru 		nla_total_size(4) +
2129c12b395aSxeb@mail.ru 		/* IFLA_GRE_FLAGS */
2130c12b395aSxeb@mail.ru 		nla_total_size(4) +
21311faf3d9fSTom Herbert 		/* IFLA_GRE_ENCAP_TYPE */
21321faf3d9fSTom Herbert 		nla_total_size(2) +
21331faf3d9fSTom Herbert 		/* IFLA_GRE_ENCAP_FLAGS */
21341faf3d9fSTom Herbert 		nla_total_size(2) +
21351faf3d9fSTom Herbert 		/* IFLA_GRE_ENCAP_SPORT */
21361faf3d9fSTom Herbert 		nla_total_size(2) +
21371faf3d9fSTom Herbert 		/* IFLA_GRE_ENCAP_DPORT */
21381faf3d9fSTom Herbert 		nla_total_size(2) +
21396712abc1SWilliam Tu 		/* IFLA_GRE_COLLECT_METADATA */
21406712abc1SWilliam Tu 		nla_total_size(0) +
21410a473b82SCraig Gallek 		/* IFLA_GRE_FWMARK */
21420a473b82SCraig Gallek 		nla_total_size(4) +
21435a963eb6SWilliam Tu 		/* IFLA_GRE_ERSPAN_INDEX */
21445a963eb6SWilliam Tu 		nla_total_size(4) +
2145c12b395aSxeb@mail.ru 		0;
2146c12b395aSxeb@mail.ru }
2147c12b395aSxeb@mail.ru 
ip6gre_fill_info(struct sk_buff * skb,const struct net_device * dev)2148c12b395aSxeb@mail.ru static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
2149c12b395aSxeb@mail.ru {
2150c12b395aSxeb@mail.ru 	struct ip6_tnl *t = netdev_priv(dev);
2151c12b395aSxeb@mail.ru 	struct __ip6_tnl_parm *p = &t->parms;
2152c706863bSLorenzo Bianconi 	__be16 o_flags = p->o_flags;
2153c706863bSLorenzo Bianconi 
2154103d0244SLorenzo Bianconi 	if (p->erspan_ver == 1 || p->erspan_ver == 2) {
2155103d0244SLorenzo Bianconi 		if (!p->collect_md)
2156c706863bSLorenzo Bianconi 			o_flags |= TUNNEL_KEY;
2157c12b395aSxeb@mail.ru 
2158103d0244SLorenzo Bianconi 		if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver))
2159103d0244SLorenzo Bianconi 			goto nla_put_failure;
2160103d0244SLorenzo Bianconi 
2161103d0244SLorenzo Bianconi 		if (p->erspan_ver == 1) {
2162103d0244SLorenzo Bianconi 			if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
2163103d0244SLorenzo Bianconi 				goto nla_put_failure;
2164103d0244SLorenzo Bianconi 		} else {
2165103d0244SLorenzo Bianconi 			if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir))
2166103d0244SLorenzo Bianconi 				goto nla_put_failure;
2167103d0244SLorenzo Bianconi 			if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid))
2168103d0244SLorenzo Bianconi 				goto nla_put_failure;
2169103d0244SLorenzo Bianconi 		}
2170103d0244SLorenzo Bianconi 	}
2171103d0244SLorenzo Bianconi 
2172c12b395aSxeb@mail.ru 	if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
2173f41fe3c2STom Herbert 	    nla_put_be16(skb, IFLA_GRE_IFLAGS,
2174f41fe3c2STom Herbert 			 gre_tnl_flags_to_gre_flags(p->i_flags)) ||
2175f41fe3c2STom Herbert 	    nla_put_be16(skb, IFLA_GRE_OFLAGS,
2176c706863bSLorenzo Bianconi 			 gre_tnl_flags_to_gre_flags(o_flags)) ||
2177c12b395aSxeb@mail.ru 	    nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
2178c12b395aSxeb@mail.ru 	    nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
2179930345eaSJiri Benc 	    nla_put_in6_addr(skb, IFLA_GRE_LOCAL, &p->laddr) ||
2180930345eaSJiri Benc 	    nla_put_in6_addr(skb, IFLA_GRE_REMOTE, &p->raddr) ||
2181c12b395aSxeb@mail.ru 	    nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) ||
2182c12b395aSxeb@mail.ru 	    nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
2183c12b395aSxeb@mail.ru 	    nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) ||
21840a473b82SCraig Gallek 	    nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags) ||
2185103d0244SLorenzo Bianconi 	    nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark))
2186c12b395aSxeb@mail.ru 		goto nla_put_failure;
21871faf3d9fSTom Herbert 
21881faf3d9fSTom Herbert 	if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE,
21891faf3d9fSTom Herbert 			t->encap.type) ||
21901faf3d9fSTom Herbert 	    nla_put_be16(skb, IFLA_GRE_ENCAP_SPORT,
21911faf3d9fSTom Herbert 			 t->encap.sport) ||
21921faf3d9fSTom Herbert 	    nla_put_be16(skb, IFLA_GRE_ENCAP_DPORT,
21931faf3d9fSTom Herbert 			 t->encap.dport) ||
21941faf3d9fSTom Herbert 	    nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS,
21951faf3d9fSTom Herbert 			t->encap.flags))
21961faf3d9fSTom Herbert 		goto nla_put_failure;
21971faf3d9fSTom Herbert 
21986712abc1SWilliam Tu 	if (p->collect_md) {
21996712abc1SWilliam Tu 		if (nla_put_flag(skb, IFLA_GRE_COLLECT_METADATA))
22006712abc1SWilliam Tu 			goto nla_put_failure;
22016712abc1SWilliam Tu 	}
22026712abc1SWilliam Tu 
2203c12b395aSxeb@mail.ru 	return 0;
2204c12b395aSxeb@mail.ru 
2205c12b395aSxeb@mail.ru nla_put_failure:
2206c12b395aSxeb@mail.ru 	return -EMSGSIZE;
2207c12b395aSxeb@mail.ru }
2208c12b395aSxeb@mail.ru 
2209c12b395aSxeb@mail.ru static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = {
2210c12b395aSxeb@mail.ru 	[IFLA_GRE_LINK]        = { .type = NLA_U32 },
2211c12b395aSxeb@mail.ru 	[IFLA_GRE_IFLAGS]      = { .type = NLA_U16 },
2212c12b395aSxeb@mail.ru 	[IFLA_GRE_OFLAGS]      = { .type = NLA_U16 },
2213c12b395aSxeb@mail.ru 	[IFLA_GRE_IKEY]        = { .type = NLA_U32 },
2214c12b395aSxeb@mail.ru 	[IFLA_GRE_OKEY]        = { .type = NLA_U32 },
2215c593642cSPankaj Bharadiya 	[IFLA_GRE_LOCAL]       = { .len = sizeof_field(struct ipv6hdr, saddr) },
2216c593642cSPankaj Bharadiya 	[IFLA_GRE_REMOTE]      = { .len = sizeof_field(struct ipv6hdr, daddr) },
2217c12b395aSxeb@mail.ru 	[IFLA_GRE_TTL]         = { .type = NLA_U8 },
2218c12b395aSxeb@mail.ru 	[IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
2219c12b395aSxeb@mail.ru 	[IFLA_GRE_FLOWINFO]    = { .type = NLA_U32 },
2220c12b395aSxeb@mail.ru 	[IFLA_GRE_FLAGS]       = { .type = NLA_U32 },
22211faf3d9fSTom Herbert 	[IFLA_GRE_ENCAP_TYPE]   = { .type = NLA_U16 },
22221faf3d9fSTom Herbert 	[IFLA_GRE_ENCAP_FLAGS]  = { .type = NLA_U16 },
22231faf3d9fSTom Herbert 	[IFLA_GRE_ENCAP_SPORT]  = { .type = NLA_U16 },
22241faf3d9fSTom Herbert 	[IFLA_GRE_ENCAP_DPORT]  = { .type = NLA_U16 },
22256712abc1SWilliam Tu 	[IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG },
22260a473b82SCraig Gallek 	[IFLA_GRE_FWMARK]       = { .type = NLA_U32 },
22275a963eb6SWilliam Tu 	[IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 },
222894d7d8f2SWilliam Tu 	[IFLA_GRE_ERSPAN_VER]	= { .type = NLA_U8 },
222994d7d8f2SWilliam Tu 	[IFLA_GRE_ERSPAN_DIR]	= { .type = NLA_U8 },
223094d7d8f2SWilliam Tu 	[IFLA_GRE_ERSPAN_HWID]	= { .type = NLA_U16 },
2231c12b395aSxeb@mail.ru };
2232c12b395aSxeb@mail.ru 
ip6erspan_tap_setup(struct net_device * dev)22335a963eb6SWilliam Tu static void ip6erspan_tap_setup(struct net_device *dev)
22345a963eb6SWilliam Tu {
22355a963eb6SWilliam Tu 	ether_setup(dev);
22365a963eb6SWilliam Tu 
22374123f637SHaishuang Yan 	dev->max_mtu = 0;
22385a963eb6SWilliam Tu 	dev->netdev_ops = &ip6erspan_netdev_ops;
22395a963eb6SWilliam Tu 	dev->needs_free_netdev = true;
22405a963eb6SWilliam Tu 	dev->priv_destructor = ip6gre_dev_free;
22415a963eb6SWilliam Tu 
22425a963eb6SWilliam Tu 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
22435a963eb6SWilliam Tu 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
22445a963eb6SWilliam Tu 	netif_keep_dst(dev);
22455a963eb6SWilliam Tu }
22465a963eb6SWilliam Tu 
ip6erspan_newlink(struct net * src_net,struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)22472d665034SPetr Machata static int ip6erspan_newlink(struct net *src_net, struct net_device *dev,
22482d665034SPetr Machata 			     struct nlattr *tb[], struct nlattr *data[],
22492d665034SPetr Machata 			     struct netlink_ext_ack *extack)
22502d665034SPetr Machata {
22512d665034SPetr Machata 	struct ip6_tnl *nt = netdev_priv(dev);
22522d665034SPetr Machata 	struct net *net = dev_net(dev);
2253b80d0b93SWilliam Tu 	struct ip6gre_net *ign;
2254b80d0b93SWilliam Tu 	int err;
22552d665034SPetr Machata 
2256b80d0b93SWilliam Tu 	ip6gre_netlink_parms(data, &nt->parms);
22574974d5f6SLorenzo Bianconi 	ip6erspan_set_version(data, &nt->parms);
2258b80d0b93SWilliam Tu 	ign = net_generic(net, ip6gre_net_id);
2259b80d0b93SWilliam Tu 
2260b80d0b93SWilliam Tu 	if (nt->parms.collect_md) {
2261b80d0b93SWilliam Tu 		if (rtnl_dereference(ign->collect_md_tun_erspan))
2262b80d0b93SWilliam Tu 			return -EEXIST;
2263b80d0b93SWilliam Tu 	} else {
2264b80d0b93SWilliam Tu 		if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
2265b80d0b93SWilliam Tu 			return -EEXIST;
2266b80d0b93SWilliam Tu 	}
2267b80d0b93SWilliam Tu 
2268b80d0b93SWilliam Tu 	err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
22692d665034SPetr Machata 	if (!err) {
22702d665034SPetr Machata 		ip6erspan_tnl_link_config(nt, !tb[IFLA_MTU]);
2271b80d0b93SWilliam Tu 		ip6erspan_tunnel_link_md(ign, nt);
22722d665034SPetr Machata 		ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
22732d665034SPetr Machata 	}
22742d665034SPetr Machata 	return err;
22752d665034SPetr Machata }
22762d665034SPetr Machata 
ip6erspan_tnl_link_config(struct ip6_tnl * t,int set_mtu)22772d665034SPetr Machata static void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu)
22782d665034SPetr Machata {
22792d665034SPetr Machata 	ip6gre_tnl_link_config_common(t);
22802d665034SPetr Machata 	ip6gre_tnl_link_config_route(t, set_mtu, ip6erspan_calc_hlen(t));
22812d665034SPetr Machata }
22822d665034SPetr Machata 
ip6erspan_tnl_change(struct ip6_tnl * t,const struct __ip6_tnl_parm * p,int set_mtu)22832d665034SPetr Machata static int ip6erspan_tnl_change(struct ip6_tnl *t,
22842d665034SPetr Machata 				const struct __ip6_tnl_parm *p, int set_mtu)
22852d665034SPetr Machata {
22862d665034SPetr Machata 	ip6gre_tnl_copy_tnl_parm(t, p);
22872d665034SPetr Machata 	ip6erspan_tnl_link_config(t, set_mtu);
22882d665034SPetr Machata 	return 0;
22892d665034SPetr Machata }
22902d665034SPetr Machata 
ip6erspan_changelink(struct net_device * dev,struct nlattr * tb[],struct nlattr * data[],struct netlink_ext_ack * extack)22912d665034SPetr Machata static int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
22922d665034SPetr Machata 				struct nlattr *data[],
22932d665034SPetr Machata 				struct netlink_ext_ack *extack)
22942d665034SPetr Machata {
22952d665034SPetr Machata 	struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id);
22962d665034SPetr Machata 	struct __ip6_tnl_parm p;
22972d665034SPetr Machata 	struct ip6_tnl *t;
22982d665034SPetr Machata 
22992d665034SPetr Machata 	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
23002d665034SPetr Machata 	if (IS_ERR(t))
23012d665034SPetr Machata 		return PTR_ERR(t);
23022d665034SPetr Machata 
23034974d5f6SLorenzo Bianconi 	ip6erspan_set_version(data, &p);
2304b80d0b93SWilliam Tu 	ip6gre_tunnel_unlink_md(ign, t);
23052d665034SPetr Machata 	ip6gre_tunnel_unlink(ign, t);
23062d665034SPetr Machata 	ip6erspan_tnl_change(t, &p, !tb[IFLA_MTU]);
2307b80d0b93SWilliam Tu 	ip6erspan_tunnel_link_md(ign, t);
23082d665034SPetr Machata 	ip6gre_tunnel_link(ign, t);
23092d665034SPetr Machata 	return 0;
23102d665034SPetr Machata }
23112d665034SPetr Machata 
2312c12b395aSxeb@mail.ru static struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
2313c12b395aSxeb@mail.ru 	.kind		= "ip6gre",
2314c12b395aSxeb@mail.ru 	.maxtype	= IFLA_GRE_MAX,
2315c12b395aSxeb@mail.ru 	.policy		= ip6gre_policy,
2316c12b395aSxeb@mail.ru 	.priv_size	= sizeof(struct ip6_tnl),
2317c12b395aSxeb@mail.ru 	.setup		= ip6gre_tunnel_setup,
2318c12b395aSxeb@mail.ru 	.validate	= ip6gre_tunnel_validate,
2319c12b395aSxeb@mail.ru 	.newlink	= ip6gre_newlink,
2320c12b395aSxeb@mail.ru 	.changelink	= ip6gre_changelink,
232154d63f78SNicolas Dichtel 	.dellink	= ip6gre_dellink,
2322c12b395aSxeb@mail.ru 	.get_size	= ip6gre_get_size,
2323c12b395aSxeb@mail.ru 	.fill_info	= ip6gre_fill_info,
23241728d4faSNicolas Dichtel 	.get_link_net	= ip6_tnl_get_link_net,
2325c12b395aSxeb@mail.ru };
2326c12b395aSxeb@mail.ru 
2327c12b395aSxeb@mail.ru static struct rtnl_link_ops ip6gre_tap_ops __read_mostly = {
2328c12b395aSxeb@mail.ru 	.kind		= "ip6gretap",
2329c12b395aSxeb@mail.ru 	.maxtype	= IFLA_GRE_MAX,
2330c12b395aSxeb@mail.ru 	.policy		= ip6gre_policy,
2331c12b395aSxeb@mail.ru 	.priv_size	= sizeof(struct ip6_tnl),
2332c12b395aSxeb@mail.ru 	.setup		= ip6gre_tap_setup,
2333c12b395aSxeb@mail.ru 	.validate	= ip6gre_tap_validate,
2334c12b395aSxeb@mail.ru 	.newlink	= ip6gre_newlink,
2335c12b395aSxeb@mail.ru 	.changelink	= ip6gre_changelink,
2336c12b395aSxeb@mail.ru 	.get_size	= ip6gre_get_size,
2337c12b395aSxeb@mail.ru 	.fill_info	= ip6gre_fill_info,
23383390e397SNicolas Dichtel 	.get_link_net	= ip6_tnl_get_link_net,
2339c12b395aSxeb@mail.ru };
2340c12b395aSxeb@mail.ru 
23415a963eb6SWilliam Tu static struct rtnl_link_ops ip6erspan_tap_ops __read_mostly = {
23425a963eb6SWilliam Tu 	.kind		= "ip6erspan",
23435a963eb6SWilliam Tu 	.maxtype	= IFLA_GRE_MAX,
23445a963eb6SWilliam Tu 	.policy		= ip6gre_policy,
23455a963eb6SWilliam Tu 	.priv_size	= sizeof(struct ip6_tnl),
23465a963eb6SWilliam Tu 	.setup		= ip6erspan_tap_setup,
23475a963eb6SWilliam Tu 	.validate	= ip6erspan_tap_validate,
23482d665034SPetr Machata 	.newlink	= ip6erspan_newlink,
23492d665034SPetr Machata 	.changelink	= ip6erspan_changelink,
23505a963eb6SWilliam Tu 	.get_size	= ip6gre_get_size,
23515a963eb6SWilliam Tu 	.fill_info	= ip6gre_fill_info,
23525a963eb6SWilliam Tu 	.get_link_net	= ip6_tnl_get_link_net,
23535a963eb6SWilliam Tu };
23545a963eb6SWilliam Tu 
2355c12b395aSxeb@mail.ru /*
2356c12b395aSxeb@mail.ru  *	And now the modules code and kernel interface.
2357c12b395aSxeb@mail.ru  */
2358c12b395aSxeb@mail.ru 
ip6gre_init(void)2359c12b395aSxeb@mail.ru static int __init ip6gre_init(void)
2360c12b395aSxeb@mail.ru {
2361c12b395aSxeb@mail.ru 	int err;
2362c12b395aSxeb@mail.ru 
2363c12b395aSxeb@mail.ru 	pr_info("GRE over IPv6 tunneling driver\n");
2364c12b395aSxeb@mail.ru 
2365c12b395aSxeb@mail.ru 	err = register_pernet_device(&ip6gre_net_ops);
2366c12b395aSxeb@mail.ru 	if (err < 0)
2367c12b395aSxeb@mail.ru 		return err;
2368c12b395aSxeb@mail.ru 
2369c12b395aSxeb@mail.ru 	err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE);
2370c12b395aSxeb@mail.ru 	if (err < 0) {
2371c12b395aSxeb@mail.ru 		pr_info("%s: can't add protocol\n", __func__);
2372c12b395aSxeb@mail.ru 		goto add_proto_failed;
2373c12b395aSxeb@mail.ru 	}
2374c12b395aSxeb@mail.ru 
2375c12b395aSxeb@mail.ru 	err = rtnl_link_register(&ip6gre_link_ops);
2376c12b395aSxeb@mail.ru 	if (err < 0)
2377c12b395aSxeb@mail.ru 		goto rtnl_link_failed;
2378c12b395aSxeb@mail.ru 
2379c12b395aSxeb@mail.ru 	err = rtnl_link_register(&ip6gre_tap_ops);
2380c12b395aSxeb@mail.ru 	if (err < 0)
2381c12b395aSxeb@mail.ru 		goto tap_ops_failed;
2382c12b395aSxeb@mail.ru 
23835a963eb6SWilliam Tu 	err = rtnl_link_register(&ip6erspan_tap_ops);
23845a963eb6SWilliam Tu 	if (err < 0)
23855a963eb6SWilliam Tu 		goto erspan_link_failed;
23865a963eb6SWilliam Tu 
2387c12b395aSxeb@mail.ru out:
2388c12b395aSxeb@mail.ru 	return err;
2389c12b395aSxeb@mail.ru 
23905a963eb6SWilliam Tu erspan_link_failed:
23915a963eb6SWilliam Tu 	rtnl_link_unregister(&ip6gre_tap_ops);
2392c12b395aSxeb@mail.ru tap_ops_failed:
2393c12b395aSxeb@mail.ru 	rtnl_link_unregister(&ip6gre_link_ops);
2394c12b395aSxeb@mail.ru rtnl_link_failed:
2395c12b395aSxeb@mail.ru 	inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
2396c12b395aSxeb@mail.ru add_proto_failed:
2397c12b395aSxeb@mail.ru 	unregister_pernet_device(&ip6gre_net_ops);
2398c12b395aSxeb@mail.ru 	goto out;
2399c12b395aSxeb@mail.ru }
2400c12b395aSxeb@mail.ru 
ip6gre_fini(void)2401c12b395aSxeb@mail.ru static void __exit ip6gre_fini(void)
2402c12b395aSxeb@mail.ru {
2403c12b395aSxeb@mail.ru 	rtnl_link_unregister(&ip6gre_tap_ops);
2404c12b395aSxeb@mail.ru 	rtnl_link_unregister(&ip6gre_link_ops);
24055a963eb6SWilliam Tu 	rtnl_link_unregister(&ip6erspan_tap_ops);
2406c12b395aSxeb@mail.ru 	inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
2407c12b395aSxeb@mail.ru 	unregister_pernet_device(&ip6gre_net_ops);
2408c12b395aSxeb@mail.ru }
2409c12b395aSxeb@mail.ru 
2410c12b395aSxeb@mail.ru module_init(ip6gre_init);
2411c12b395aSxeb@mail.ru module_exit(ip6gre_fini);
2412c12b395aSxeb@mail.ru MODULE_LICENSE("GPL");
2413c12b395aSxeb@mail.ru MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
2414c12b395aSxeb@mail.ru MODULE_DESCRIPTION("GRE over IPv6 tunneling device");
2415c12b395aSxeb@mail.ru MODULE_ALIAS_RTNL_LINK("ip6gre");
24165a4ee9a9SNicolas Dichtel MODULE_ALIAS_RTNL_LINK("ip6gretap");
241794d7d8f2SWilliam Tu MODULE_ALIAS_RTNL_LINK("ip6erspan");
2418c12b395aSxeb@mail.ru MODULE_ALIAS_NETDEV("ip6gre0");
2419