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