162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	GRE over IPv6 protocol decoder.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Authors: Dmitry Kozlov (xeb@mail.ru)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/capability.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/uaccess.h>
1662306a36Sopenharmony_ci#include <linux/skbuff.h>
1762306a36Sopenharmony_ci#include <linux/netdevice.h>
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/tcp.h>
2062306a36Sopenharmony_ci#include <linux/udp.h>
2162306a36Sopenharmony_ci#include <linux/if_arp.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/in6.h>
2462306a36Sopenharmony_ci#include <linux/inetdevice.h>
2562306a36Sopenharmony_ci#include <linux/igmp.h>
2662306a36Sopenharmony_ci#include <linux/netfilter_ipv4.h>
2762306a36Sopenharmony_ci#include <linux/etherdevice.h>
2862306a36Sopenharmony_ci#include <linux/if_ether.h>
2962306a36Sopenharmony_ci#include <linux/hash.h>
3062306a36Sopenharmony_ci#include <linux/if_tunnel.h>
3162306a36Sopenharmony_ci#include <linux/ip6_tunnel.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <net/sock.h>
3462306a36Sopenharmony_ci#include <net/ip.h>
3562306a36Sopenharmony_ci#include <net/ip_tunnels.h>
3662306a36Sopenharmony_ci#include <net/icmp.h>
3762306a36Sopenharmony_ci#include <net/protocol.h>
3862306a36Sopenharmony_ci#include <net/addrconf.h>
3962306a36Sopenharmony_ci#include <net/arp.h>
4062306a36Sopenharmony_ci#include <net/checksum.h>
4162306a36Sopenharmony_ci#include <net/dsfield.h>
4262306a36Sopenharmony_ci#include <net/inet_ecn.h>
4362306a36Sopenharmony_ci#include <net/xfrm.h>
4462306a36Sopenharmony_ci#include <net/net_namespace.h>
4562306a36Sopenharmony_ci#include <net/netns/generic.h>
4662306a36Sopenharmony_ci#include <net/rtnetlink.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <net/ipv6.h>
4962306a36Sopenharmony_ci#include <net/ip6_fib.h>
5062306a36Sopenharmony_ci#include <net/ip6_route.h>
5162306a36Sopenharmony_ci#include <net/ip6_tunnel.h>
5262306a36Sopenharmony_ci#include <net/gre.h>
5362306a36Sopenharmony_ci#include <net/erspan.h>
5462306a36Sopenharmony_ci#include <net/dst_metadata.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic bool log_ecn_error = true;
5862306a36Sopenharmony_cimodule_param(log_ecn_error, bool, 0644);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define IP6_GRE_HASH_SIZE_SHIFT  5
6262306a36Sopenharmony_ci#define IP6_GRE_HASH_SIZE (1 << IP6_GRE_HASH_SIZE_SHIFT)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic unsigned int ip6gre_net_id __read_mostly;
6562306a36Sopenharmony_cistruct ip6gre_net {
6662306a36Sopenharmony_ci	struct ip6_tnl __rcu *tunnels[4][IP6_GRE_HASH_SIZE];
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	struct ip6_tnl __rcu *collect_md_tun;
6962306a36Sopenharmony_ci	struct ip6_tnl __rcu *collect_md_tun_erspan;
7062306a36Sopenharmony_ci	struct net_device *fb_tunnel_dev;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic struct rtnl_link_ops ip6gre_link_ops __read_mostly;
7462306a36Sopenharmony_cistatic struct rtnl_link_ops ip6gre_tap_ops __read_mostly;
7562306a36Sopenharmony_cistatic struct rtnl_link_ops ip6erspan_tap_ops __read_mostly;
7662306a36Sopenharmony_cistatic int ip6gre_tunnel_init(struct net_device *dev);
7762306a36Sopenharmony_cistatic void ip6gre_tunnel_setup(struct net_device *dev);
7862306a36Sopenharmony_cistatic void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
7962306a36Sopenharmony_cistatic void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu);
8062306a36Sopenharmony_cistatic void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Tunnel hash table */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci   4 hash tables:
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci   3: (remote,local)
8862306a36Sopenharmony_ci   2: (remote,*)
8962306a36Sopenharmony_ci   1: (*,local)
9062306a36Sopenharmony_ci   0: (*,*)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci   We require exact key match i.e. if a key is present in packet
9362306a36Sopenharmony_ci   it will match only tunnel with the same key; if it is not present,
9462306a36Sopenharmony_ci   it will match only keyless tunnel.
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci   All keysless packets, if not matched configured keyless tunnels
9762306a36Sopenharmony_ci   will match fallback tunnel.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(IP6_GRE_HASH_SIZE - 1))
10162306a36Sopenharmony_cistatic u32 HASH_ADDR(const struct in6_addr *addr)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	u32 hash = ipv6_addr_hash(addr);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return hash_32(hash, IP6_GRE_HASH_SIZE_SHIFT);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define tunnels_r_l	tunnels[3]
10962306a36Sopenharmony_ci#define tunnels_r	tunnels[2]
11062306a36Sopenharmony_ci#define tunnels_l	tunnels[1]
11162306a36Sopenharmony_ci#define tunnels_wc	tunnels[0]
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* Given src, dst and key, find appropriate for input tunnel. */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
11662306a36Sopenharmony_ci		const struct in6_addr *remote, const struct in6_addr *local,
11762306a36Sopenharmony_ci		__be32 key, __be16 gre_proto)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct net *net = dev_net(dev);
12062306a36Sopenharmony_ci	int link = dev->ifindex;
12162306a36Sopenharmony_ci	unsigned int h0 = HASH_ADDR(remote);
12262306a36Sopenharmony_ci	unsigned int h1 = HASH_KEY(key);
12362306a36Sopenharmony_ci	struct ip6_tnl *t, *cand = NULL;
12462306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
12562306a36Sopenharmony_ci	int dev_type = (gre_proto == htons(ETH_P_TEB) ||
12662306a36Sopenharmony_ci			gre_proto == htons(ETH_P_ERSPAN) ||
12762306a36Sopenharmony_ci			gre_proto == htons(ETH_P_ERSPAN2)) ?
12862306a36Sopenharmony_ci		       ARPHRD_ETHER : ARPHRD_IP6GRE;
12962306a36Sopenharmony_ci	int score, cand_score = 4;
13062306a36Sopenharmony_ci	struct net_device *ndev;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) {
13362306a36Sopenharmony_ci		if (!ipv6_addr_equal(local, &t->parms.laddr) ||
13462306a36Sopenharmony_ci		    !ipv6_addr_equal(remote, &t->parms.raddr) ||
13562306a36Sopenharmony_ci		    key != t->parms.i_key ||
13662306a36Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
13762306a36Sopenharmony_ci			continue;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
14062306a36Sopenharmony_ci		    t->dev->type != dev_type)
14162306a36Sopenharmony_ci			continue;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		score = 0;
14462306a36Sopenharmony_ci		if (t->parms.link != link)
14562306a36Sopenharmony_ci			score |= 1;
14662306a36Sopenharmony_ci		if (t->dev->type != dev_type)
14762306a36Sopenharmony_ci			score |= 2;
14862306a36Sopenharmony_ci		if (score == 0)
14962306a36Sopenharmony_ci			return t;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		if (score < cand_score) {
15262306a36Sopenharmony_ci			cand = t;
15362306a36Sopenharmony_ci			cand_score = score;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) {
15862306a36Sopenharmony_ci		if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
15962306a36Sopenharmony_ci		    key != t->parms.i_key ||
16062306a36Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
16162306a36Sopenharmony_ci			continue;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
16462306a36Sopenharmony_ci		    t->dev->type != dev_type)
16562306a36Sopenharmony_ci			continue;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		score = 0;
16862306a36Sopenharmony_ci		if (t->parms.link != link)
16962306a36Sopenharmony_ci			score |= 1;
17062306a36Sopenharmony_ci		if (t->dev->type != dev_type)
17162306a36Sopenharmony_ci			score |= 2;
17262306a36Sopenharmony_ci		if (score == 0)
17362306a36Sopenharmony_ci			return t;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		if (score < cand_score) {
17662306a36Sopenharmony_ci			cand = t;
17762306a36Sopenharmony_ci			cand_score = score;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) {
18262306a36Sopenharmony_ci		if ((!ipv6_addr_equal(local, &t->parms.laddr) &&
18362306a36Sopenharmony_ci			  (!ipv6_addr_equal(local, &t->parms.raddr) ||
18462306a36Sopenharmony_ci				 !ipv6_addr_is_multicast(local))) ||
18562306a36Sopenharmony_ci		    key != t->parms.i_key ||
18662306a36Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
18762306a36Sopenharmony_ci			continue;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
19062306a36Sopenharmony_ci		    t->dev->type != dev_type)
19162306a36Sopenharmony_ci			continue;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		score = 0;
19462306a36Sopenharmony_ci		if (t->parms.link != link)
19562306a36Sopenharmony_ci			score |= 1;
19662306a36Sopenharmony_ci		if (t->dev->type != dev_type)
19762306a36Sopenharmony_ci			score |= 2;
19862306a36Sopenharmony_ci		if (score == 0)
19962306a36Sopenharmony_ci			return t;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		if (score < cand_score) {
20262306a36Sopenharmony_ci			cand = t;
20362306a36Sopenharmony_ci			cand_score = score;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) {
20862306a36Sopenharmony_ci		if (t->parms.i_key != key ||
20962306a36Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
21062306a36Sopenharmony_ci			continue;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
21362306a36Sopenharmony_ci		    t->dev->type != dev_type)
21462306a36Sopenharmony_ci			continue;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		score = 0;
21762306a36Sopenharmony_ci		if (t->parms.link != link)
21862306a36Sopenharmony_ci			score |= 1;
21962306a36Sopenharmony_ci		if (t->dev->type != dev_type)
22062306a36Sopenharmony_ci			score |= 2;
22162306a36Sopenharmony_ci		if (score == 0)
22262306a36Sopenharmony_ci			return t;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		if (score < cand_score) {
22562306a36Sopenharmony_ci			cand = t;
22662306a36Sopenharmony_ci			cand_score = score;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (cand)
23162306a36Sopenharmony_ci		return cand;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (gre_proto == htons(ETH_P_ERSPAN) ||
23462306a36Sopenharmony_ci	    gre_proto == htons(ETH_P_ERSPAN2))
23562306a36Sopenharmony_ci		t = rcu_dereference(ign->collect_md_tun_erspan);
23662306a36Sopenharmony_ci	else
23762306a36Sopenharmony_ci		t = rcu_dereference(ign->collect_md_tun);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (t && t->dev->flags & IFF_UP)
24062306a36Sopenharmony_ci		return t;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	ndev = READ_ONCE(ign->fb_tunnel_dev);
24362306a36Sopenharmony_ci	if (ndev && ndev->flags & IFF_UP)
24462306a36Sopenharmony_ci		return netdev_priv(ndev);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return NULL;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign,
25062306a36Sopenharmony_ci		const struct __ip6_tnl_parm *p)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	const struct in6_addr *remote = &p->raddr;
25362306a36Sopenharmony_ci	const struct in6_addr *local = &p->laddr;
25462306a36Sopenharmony_ci	unsigned int h = HASH_KEY(p->i_key);
25562306a36Sopenharmony_ci	int prio = 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (!ipv6_addr_any(local))
25862306a36Sopenharmony_ci		prio |= 1;
25962306a36Sopenharmony_ci	if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) {
26062306a36Sopenharmony_ci		prio |= 2;
26162306a36Sopenharmony_ci		h ^= HASH_ADDR(remote);
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return &ign->tunnels[prio][h];
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void ip6gre_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	if (t->parms.collect_md)
27062306a36Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun, t);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void ip6erspan_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	if (t->parms.collect_md)
27662306a36Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun_erspan, t);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void ip6gre_tunnel_unlink_md(struct ip6gre_net *ign, struct ip6_tnl *t)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	if (t->parms.collect_md)
28262306a36Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun, NULL);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void ip6erspan_tunnel_unlink_md(struct ip6gre_net *ign,
28662306a36Sopenharmony_ci				       struct ip6_tnl *t)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	if (t->parms.collect_md)
28962306a36Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun_erspan, NULL);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign,
29362306a36Sopenharmony_ci		const struct ip6_tnl *t)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	return __ip6gre_bucket(ign, &t->parms);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	rcu_assign_pointer(t->next, rtnl_dereference(*tp));
30362306a36Sopenharmony_ci	rcu_assign_pointer(*tp, t);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct ip6_tnl __rcu **tp;
30962306a36Sopenharmony_ci	struct ip6_tnl *iter;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	for (tp = ip6gre_bucket(ign, t);
31262306a36Sopenharmony_ci	     (iter = rtnl_dereference(*tp)) != NULL;
31362306a36Sopenharmony_ci	     tp = &iter->next) {
31462306a36Sopenharmony_ci		if (t == iter) {
31562306a36Sopenharmony_ci			rcu_assign_pointer(*tp, t->next);
31662306a36Sopenharmony_ci			break;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_find(struct net *net,
32262306a36Sopenharmony_ci					   const struct __ip6_tnl_parm *parms,
32362306a36Sopenharmony_ci					   int type)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	const struct in6_addr *remote = &parms->raddr;
32662306a36Sopenharmony_ci	const struct in6_addr *local = &parms->laddr;
32762306a36Sopenharmony_ci	__be32 key = parms->i_key;
32862306a36Sopenharmony_ci	int link = parms->link;
32962306a36Sopenharmony_ci	struct ip6_tnl *t;
33062306a36Sopenharmony_ci	struct ip6_tnl __rcu **tp;
33162306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	for (tp = __ip6gre_bucket(ign, parms);
33462306a36Sopenharmony_ci	     (t = rtnl_dereference(*tp)) != NULL;
33562306a36Sopenharmony_ci	     tp = &t->next)
33662306a36Sopenharmony_ci		if (ipv6_addr_equal(local, &t->parms.laddr) &&
33762306a36Sopenharmony_ci		    ipv6_addr_equal(remote, &t->parms.raddr) &&
33862306a36Sopenharmony_ci		    key == t->parms.i_key &&
33962306a36Sopenharmony_ci		    link == t->parms.link &&
34062306a36Sopenharmony_ci		    type == t->dev->type)
34162306a36Sopenharmony_ci			break;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return t;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_locate(struct net *net,
34762306a36Sopenharmony_ci		const struct __ip6_tnl_parm *parms, int create)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct ip6_tnl *t, *nt;
35062306a36Sopenharmony_ci	struct net_device *dev;
35162306a36Sopenharmony_ci	char name[IFNAMSIZ];
35262306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE);
35562306a36Sopenharmony_ci	if (t && create)
35662306a36Sopenharmony_ci		return NULL;
35762306a36Sopenharmony_ci	if (t || !create)
35862306a36Sopenharmony_ci		return t;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (parms->name[0]) {
36162306a36Sopenharmony_ci		if (!dev_valid_name(parms->name))
36262306a36Sopenharmony_ci			return NULL;
36362306a36Sopenharmony_ci		strscpy(name, parms->name, IFNAMSIZ);
36462306a36Sopenharmony_ci	} else {
36562306a36Sopenharmony_ci		strcpy(name, "ip6gre%d");
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,
36862306a36Sopenharmony_ci			   ip6gre_tunnel_setup);
36962306a36Sopenharmony_ci	if (!dev)
37062306a36Sopenharmony_ci		return NULL;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dev_net_set(dev, net);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	nt = netdev_priv(dev);
37562306a36Sopenharmony_ci	nt->parms = *parms;
37662306a36Sopenharmony_ci	dev->rtnl_link_ops = &ip6gre_link_ops;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	nt->dev = dev;
37962306a36Sopenharmony_ci	nt->net = dev_net(dev);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (register_netdevice(dev) < 0)
38262306a36Sopenharmony_ci		goto failed_free;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ip6gre_tnl_link_config(nt, 1);
38562306a36Sopenharmony_ci	ip6gre_tunnel_link(ign, nt);
38662306a36Sopenharmony_ci	return nt;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cifailed_free:
38962306a36Sopenharmony_ci	free_netdev(dev);
39062306a36Sopenharmony_ci	return NULL;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic void ip6erspan_tunnel_uninit(struct net_device *dev)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
39662306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ip6erspan_tunnel_unlink_md(ign, t);
39962306a36Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
40062306a36Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
40162306a36Sopenharmony_ci	netdev_put(dev, &t->dev_tracker);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void ip6gre_tunnel_uninit(struct net_device *dev)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
40762306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	ip6gre_tunnel_unlink_md(ign, t);
41062306a36Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
41162306a36Sopenharmony_ci	if (ign->fb_tunnel_dev == dev)
41262306a36Sopenharmony_ci		WRITE_ONCE(ign->fb_tunnel_dev, NULL);
41362306a36Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
41462306a36Sopenharmony_ci	netdev_put(dev, &t->dev_tracker);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
41962306a36Sopenharmony_ci		       u8 type, u8 code, int offset, __be32 info)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
42262306a36Sopenharmony_ci	const struct ipv6hdr *ipv6h;
42362306a36Sopenharmony_ci	struct tnl_ptk_info tpi;
42462306a36Sopenharmony_ci	struct ip6_tnl *t;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IPV6),
42762306a36Sopenharmony_ci			     offset) < 0)
42862306a36Sopenharmony_ci		return -EINVAL;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	ipv6h = (const struct ipv6hdr *)skb->data;
43162306a36Sopenharmony_ci	t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
43262306a36Sopenharmony_ci				 tpi.key, tpi.proto);
43362306a36Sopenharmony_ci	if (!t)
43462306a36Sopenharmony_ci		return -ENOENT;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	switch (type) {
43762306a36Sopenharmony_ci	case ICMPV6_DEST_UNREACH:
43862306a36Sopenharmony_ci		net_dbg_ratelimited("%s: Path to destination invalid or inactive!\n",
43962306a36Sopenharmony_ci				    t->parms.name);
44062306a36Sopenharmony_ci		if (code != ICMPV6_PORT_UNREACH)
44162306a36Sopenharmony_ci			break;
44262306a36Sopenharmony_ci		return 0;
44362306a36Sopenharmony_ci	case ICMPV6_TIME_EXCEED:
44462306a36Sopenharmony_ci		if (code == ICMPV6_EXC_HOPLIMIT) {
44562306a36Sopenharmony_ci			net_dbg_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n",
44662306a36Sopenharmony_ci					    t->parms.name);
44762306a36Sopenharmony_ci			break;
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci		return 0;
45062306a36Sopenharmony_ci	case ICMPV6_PARAMPROB: {
45162306a36Sopenharmony_ci		struct ipv6_tlv_tnl_enc_lim *tel;
45262306a36Sopenharmony_ci		__u32 teli;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		teli = 0;
45562306a36Sopenharmony_ci		if (code == ICMPV6_HDR_FIELD)
45662306a36Sopenharmony_ci			teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (teli && teli == be32_to_cpu(info) - 2) {
45962306a36Sopenharmony_ci			tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
46062306a36Sopenharmony_ci			if (tel->encap_limit == 0) {
46162306a36Sopenharmony_ci				net_dbg_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n",
46262306a36Sopenharmony_ci						    t->parms.name);
46362306a36Sopenharmony_ci			}
46462306a36Sopenharmony_ci		} else {
46562306a36Sopenharmony_ci			net_dbg_ratelimited("%s: Recipient unable to parse tunneled packet!\n",
46662306a36Sopenharmony_ci					    t->parms.name);
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci		return 0;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci	case ICMPV6_PKT_TOOBIG:
47162306a36Sopenharmony_ci		ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
47262306a36Sopenharmony_ci		return 0;
47362306a36Sopenharmony_ci	case NDISC_REDIRECT:
47462306a36Sopenharmony_ci		ip6_redirect(skb, net, skb->dev->ifindex, 0,
47562306a36Sopenharmony_ci			     sock_net_uid(net, NULL));
47662306a36Sopenharmony_ci		return 0;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO))
48062306a36Sopenharmony_ci		t->err_count++;
48162306a36Sopenharmony_ci	else
48262306a36Sopenharmony_ci		t->err_count = 1;
48362306a36Sopenharmony_ci	t->err_time = jiffies;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return 0;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	const struct ipv6hdr *ipv6h;
49162306a36Sopenharmony_ci	struct ip6_tnl *tunnel;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
49462306a36Sopenharmony_ci	tunnel = ip6gre_tunnel_lookup(skb->dev,
49562306a36Sopenharmony_ci				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
49662306a36Sopenharmony_ci				      tpi->proto);
49762306a36Sopenharmony_ci	if (tunnel) {
49862306a36Sopenharmony_ci		if (tunnel->parms.collect_md) {
49962306a36Sopenharmony_ci			struct metadata_dst *tun_dst;
50062306a36Sopenharmony_ci			__be64 tun_id;
50162306a36Sopenharmony_ci			__be16 flags;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			flags = tpi->flags;
50462306a36Sopenharmony_ci			tun_id = key32_to_tunnel_id(tpi->key);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci			tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0);
50762306a36Sopenharmony_ci			if (!tun_dst)
50862306a36Sopenharmony_ci				return PACKET_REJECT;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
51162306a36Sopenharmony_ci		} else {
51262306a36Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		return PACKET_RCVD;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return PACKET_REJECT;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int ip6erspan_rcv(struct sk_buff *skb,
52262306a36Sopenharmony_ci			 struct tnl_ptk_info *tpi,
52362306a36Sopenharmony_ci			 int gre_hdr_len)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct erspan_base_hdr *ershdr;
52662306a36Sopenharmony_ci	const struct ipv6hdr *ipv6h;
52762306a36Sopenharmony_ci	struct erspan_md2 *md2;
52862306a36Sopenharmony_ci	struct ip6_tnl *tunnel;
52962306a36Sopenharmony_ci	u8 ver;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
53262306a36Sopenharmony_ci	ershdr = (struct erspan_base_hdr *)skb->data;
53362306a36Sopenharmony_ci	ver = ershdr->ver;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	tunnel = ip6gre_tunnel_lookup(skb->dev,
53662306a36Sopenharmony_ci				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
53762306a36Sopenharmony_ci				      tpi->proto);
53862306a36Sopenharmony_ci	if (tunnel) {
53962306a36Sopenharmony_ci		int len = erspan_hdr_len(ver);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		if (unlikely(!pskb_may_pull(skb, len)))
54262306a36Sopenharmony_ci			return PACKET_REJECT;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		if (__iptunnel_pull_header(skb, len,
54562306a36Sopenharmony_ci					   htons(ETH_P_TEB),
54662306a36Sopenharmony_ci					   false, false) < 0)
54762306a36Sopenharmony_ci			return PACKET_REJECT;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		if (tunnel->parms.collect_md) {
55062306a36Sopenharmony_ci			struct erspan_metadata *pkt_md, *md;
55162306a36Sopenharmony_ci			struct metadata_dst *tun_dst;
55262306a36Sopenharmony_ci			struct ip_tunnel_info *info;
55362306a36Sopenharmony_ci			unsigned char *gh;
55462306a36Sopenharmony_ci			__be64 tun_id;
55562306a36Sopenharmony_ci			__be16 flags;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci			tpi->flags |= TUNNEL_KEY;
55862306a36Sopenharmony_ci			flags = tpi->flags;
55962306a36Sopenharmony_ci			tun_id = key32_to_tunnel_id(tpi->key);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci			tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id,
56262306a36Sopenharmony_ci						  sizeof(*md));
56362306a36Sopenharmony_ci			if (!tun_dst)
56462306a36Sopenharmony_ci				return PACKET_REJECT;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci			/* skb can be uncloned in __iptunnel_pull_header, so
56762306a36Sopenharmony_ci			 * old pkt_md is no longer valid and we need to reset
56862306a36Sopenharmony_ci			 * it
56962306a36Sopenharmony_ci			 */
57062306a36Sopenharmony_ci			gh = skb_network_header(skb) +
57162306a36Sopenharmony_ci			     skb_network_header_len(skb);
57262306a36Sopenharmony_ci			pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
57362306a36Sopenharmony_ci							    sizeof(*ershdr));
57462306a36Sopenharmony_ci			info = &tun_dst->u.tun_info;
57562306a36Sopenharmony_ci			md = ip_tunnel_info_opts(info);
57662306a36Sopenharmony_ci			md->version = ver;
57762306a36Sopenharmony_ci			md2 = &md->u.md2;
57862306a36Sopenharmony_ci			memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
57962306a36Sopenharmony_ci						       ERSPAN_V2_MDSIZE);
58062306a36Sopenharmony_ci			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
58162306a36Sopenharmony_ci			info->options_len = sizeof(*md);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		} else {
58662306a36Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		return PACKET_RCVD;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return PACKET_REJECT;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic int gre_rcv(struct sk_buff *skb)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct tnl_ptk_info tpi;
59862306a36Sopenharmony_ci	bool csum_err = false;
59962306a36Sopenharmony_ci	int hdr_len;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0);
60262306a36Sopenharmony_ci	if (hdr_len < 0)
60362306a36Sopenharmony_ci		goto drop;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))
60662306a36Sopenharmony_ci		goto drop;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
60962306a36Sopenharmony_ci		     tpi.proto == htons(ETH_P_ERSPAN2))) {
61062306a36Sopenharmony_ci		if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
61162306a36Sopenharmony_ci			return 0;
61262306a36Sopenharmony_ci		goto out;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (ip6gre_rcv(skb, &tpi) == PACKET_RCVD)
61662306a36Sopenharmony_ci		return 0;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ciout:
61962306a36Sopenharmony_ci	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
62062306a36Sopenharmony_cidrop:
62162306a36Sopenharmony_ci	kfree_skb(skb);
62262306a36Sopenharmony_ci	return 0;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int gre_handle_offloads(struct sk_buff *skb, bool csum)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	return iptunnel_handle_offloads(skb,
62862306a36Sopenharmony_ci					csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic void prepare_ip6gre_xmit_ipv4(struct sk_buff *skb,
63262306a36Sopenharmony_ci				     struct net_device *dev,
63362306a36Sopenharmony_ci				     struct flowi6 *fl6, __u8 *dsfield,
63462306a36Sopenharmony_ci				     int *encap_limit)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
63762306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
64062306a36Sopenharmony_ci		*encap_limit = t->parms.encap_limit;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
64562306a36Sopenharmony_ci		*dsfield = ipv4_get_dsfield(iph);
64662306a36Sopenharmony_ci	else
64762306a36Sopenharmony_ci		*dsfield = ip6_tclass(t->parms.flowinfo);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
65062306a36Sopenharmony_ci		fl6->flowi6_mark = skb->mark;
65162306a36Sopenharmony_ci	else
65262306a36Sopenharmony_ci		fl6->flowi6_mark = t->parms.fwmark;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb,
65862306a36Sopenharmony_ci				    struct net_device *dev,
65962306a36Sopenharmony_ci				    struct flowi6 *fl6, __u8 *dsfield,
66062306a36Sopenharmony_ci				    int *encap_limit)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct ipv6hdr *ipv6h;
66362306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
66462306a36Sopenharmony_ci	__u16 offset;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
66762306a36Sopenharmony_ci	/* ip6_tnl_parse_tlv_enc_lim() might have reallocated skb->head */
66862306a36Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (offset > 0) {
67162306a36Sopenharmony_ci		struct ipv6_tlv_tnl_enc_lim *tel;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
67462306a36Sopenharmony_ci		if (tel->encap_limit == 0) {
67562306a36Sopenharmony_ci			icmpv6_ndo_send(skb, ICMPV6_PARAMPROB,
67662306a36Sopenharmony_ci					ICMPV6_HDR_FIELD, offset + 2);
67762306a36Sopenharmony_ci			return -1;
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci		*encap_limit = tel->encap_limit - 1;
68062306a36Sopenharmony_ci	} else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) {
68162306a36Sopenharmony_ci		*encap_limit = t->parms.encap_limit;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
68762306a36Sopenharmony_ci		*dsfield = ipv6_get_dsfield(ipv6h);
68862306a36Sopenharmony_ci	else
68962306a36Sopenharmony_ci		*dsfield = ip6_tclass(t->parms.flowinfo);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
69262306a36Sopenharmony_ci		fl6->flowlabel |= ip6_flowlabel(ipv6h);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
69562306a36Sopenharmony_ci		fl6->flowi6_mark = skb->mark;
69662306a36Sopenharmony_ci	else
69762306a36Sopenharmony_ci		fl6->flowi6_mark = t->parms.fwmark;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int prepare_ip6gre_xmit_other(struct sk_buff *skb,
70562306a36Sopenharmony_ci				     struct net_device *dev,
70662306a36Sopenharmony_ci				     struct flowi6 *fl6, __u8 *dsfield,
70762306a36Sopenharmony_ci				     int *encap_limit)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
71262306a36Sopenharmony_ci		*encap_limit = t->parms.encap_limit;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
71762306a36Sopenharmony_ci		*dsfield = 0;
71862306a36Sopenharmony_ci	else
71962306a36Sopenharmony_ci		*dsfield = ip6_tclass(t->parms.flowinfo);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
72262306a36Sopenharmony_ci		fl6->flowi6_mark = skb->mark;
72362306a36Sopenharmony_ci	else
72462306a36Sopenharmony_ci		fl6->flowi6_mark = t->parms.fwmark;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return 0;
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct ip_tunnel_info *tun_info;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	tun_info = skb_tunnel_info(skb);
73662306a36Sopenharmony_ci	if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX)))
73762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	return tun_info;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic netdev_tx_t __gre6_xmit(struct sk_buff *skb,
74362306a36Sopenharmony_ci			       struct net_device *dev, __u8 dsfield,
74462306a36Sopenharmony_ci			       struct flowi6 *fl6, int encap_limit,
74562306a36Sopenharmony_ci			       __u32 *pmtu, __be16 proto)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct ip6_tnl *tunnel = netdev_priv(dev);
74862306a36Sopenharmony_ci	__be16 protocol;
74962306a36Sopenharmony_ci	__be16 flags;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (dev->type == ARPHRD_ETHER)
75262306a36Sopenharmony_ci		IPCB(skb)->flags = 0;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (dev->header_ops && dev->type == ARPHRD_IP6GRE)
75562306a36Sopenharmony_ci		fl6->daddr = ((struct ipv6hdr *)skb->data)->daddr;
75662306a36Sopenharmony_ci	else
75762306a36Sopenharmony_ci		fl6->daddr = tunnel->parms.raddr;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* Push GRE header. */
76062306a36Sopenharmony_ci	protocol = (dev->type == ARPHRD_ETHER) ? htons(ETH_P_TEB) : proto;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (tunnel->parms.collect_md) {
76362306a36Sopenharmony_ci		struct ip_tunnel_info *tun_info;
76462306a36Sopenharmony_ci		const struct ip_tunnel_key *key;
76562306a36Sopenharmony_ci		int tun_hlen;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		tun_info = skb_tunnel_info_txcheck(skb);
76862306a36Sopenharmony_ci		if (IS_ERR(tun_info) ||
76962306a36Sopenharmony_ci		    unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
77062306a36Sopenharmony_ci			return -EINVAL;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		key = &tun_info->key;
77362306a36Sopenharmony_ci		memset(fl6, 0, sizeof(*fl6));
77462306a36Sopenharmony_ci		fl6->flowi6_proto = IPPROTO_GRE;
77562306a36Sopenharmony_ci		fl6->daddr = key->u.ipv6.dst;
77662306a36Sopenharmony_ci		fl6->flowlabel = key->label;
77762306a36Sopenharmony_ci		fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
77862306a36Sopenharmony_ci		fl6->fl6_gre_key = tunnel_id_to_key32(key->tun_id);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		dsfield = key->tos;
78162306a36Sopenharmony_ci		flags = key->tun_flags &
78262306a36Sopenharmony_ci			(TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ);
78362306a36Sopenharmony_ci		tun_hlen = gre_calc_hlen(flags);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		if (skb_cow_head(skb, dev->needed_headroom ?: tun_hlen + tunnel->encap_hlen))
78662306a36Sopenharmony_ci			return -ENOMEM;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		gre_build_header(skb, tun_hlen,
78962306a36Sopenharmony_ci				 flags, protocol,
79062306a36Sopenharmony_ci				 tunnel_id_to_key32(tun_info->key.tun_id),
79162306a36Sopenharmony_ci				 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
79262306a36Sopenharmony_ci						      : 0);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	} else {
79562306a36Sopenharmony_ci		if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen))
79662306a36Sopenharmony_ci			return -ENOMEM;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		flags = tunnel->parms.o_flags;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		gre_build_header(skb, tunnel->tun_hlen, flags,
80162306a36Sopenharmony_ci				 protocol, tunnel->parms.o_key,
80262306a36Sopenharmony_ci				 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
80362306a36Sopenharmony_ci						      : 0);
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,
80762306a36Sopenharmony_ci			    NEXTHDR_GRE);
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
81362306a36Sopenharmony_ci	int encap_limit = -1;
81462306a36Sopenharmony_ci	struct flowi6 fl6;
81562306a36Sopenharmony_ci	__u8 dsfield = 0;
81662306a36Sopenharmony_ci	__u32 mtu;
81762306a36Sopenharmony_ci	int err;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (!t->parms.collect_md)
82262306a36Sopenharmony_ci		prepare_ip6gre_xmit_ipv4(skb, dev, &fl6,
82362306a36Sopenharmony_ci					 &dsfield, &encap_limit);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
82662306a36Sopenharmony_ci	if (err)
82762306a36Sopenharmony_ci		return -1;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
83062306a36Sopenharmony_ci			  skb->protocol);
83162306a36Sopenharmony_ci	if (err != 0) {
83262306a36Sopenharmony_ci		/* XXX: send ICMP error even if DF is not set. */
83362306a36Sopenharmony_ci		if (err == -EMSGSIZE)
83462306a36Sopenharmony_ci			icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
83562306a36Sopenharmony_ci				      htonl(mtu));
83662306a36Sopenharmony_ci		return -1;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return 0;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
84562306a36Sopenharmony_ci	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
84662306a36Sopenharmony_ci	int encap_limit = -1;
84762306a36Sopenharmony_ci	struct flowi6 fl6;
84862306a36Sopenharmony_ci	__u8 dsfield = 0;
84962306a36Sopenharmony_ci	__u32 mtu;
85062306a36Sopenharmony_ci	int err;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
85362306a36Sopenharmony_ci		return -1;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (!t->parms.collect_md &&
85662306a36Sopenharmony_ci	    prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit))
85762306a36Sopenharmony_ci		return -1;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)))
86062306a36Sopenharmony_ci		return -1;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit,
86362306a36Sopenharmony_ci			  &mtu, skb->protocol);
86462306a36Sopenharmony_ci	if (err != 0) {
86562306a36Sopenharmony_ci		if (err == -EMSGSIZE)
86662306a36Sopenharmony_ci			icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
86762306a36Sopenharmony_ci		return -1;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return 0;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
87662306a36Sopenharmony_ci	int encap_limit = -1;
87762306a36Sopenharmony_ci	struct flowi6 fl6;
87862306a36Sopenharmony_ci	__u8 dsfield = 0;
87962306a36Sopenharmony_ci	__u32 mtu;
88062306a36Sopenharmony_ci	int err;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (!t->parms.collect_md &&
88362306a36Sopenharmony_ci	    prepare_ip6gre_xmit_other(skb, dev, &fl6, &dsfield, &encap_limit))
88462306a36Sopenharmony_ci		return -1;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
88762306a36Sopenharmony_ci	if (err)
88862306a36Sopenharmony_ci		return err;
88962306a36Sopenharmony_ci	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu, skb->protocol);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	return err;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_cistatic netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
89562306a36Sopenharmony_ci	struct net_device *dev)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
89862306a36Sopenharmony_ci	__be16 payload_protocol;
89962306a36Sopenharmony_ci	int ret;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
90262306a36Sopenharmony_ci		goto tx_err;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
90562306a36Sopenharmony_ci		goto tx_err;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	payload_protocol = skb_protocol(skb, true);
90862306a36Sopenharmony_ci	switch (payload_protocol) {
90962306a36Sopenharmony_ci	case htons(ETH_P_IP):
91062306a36Sopenharmony_ci		ret = ip6gre_xmit_ipv4(skb, dev);
91162306a36Sopenharmony_ci		break;
91262306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
91362306a36Sopenharmony_ci		ret = ip6gre_xmit_ipv6(skb, dev);
91462306a36Sopenharmony_ci		break;
91562306a36Sopenharmony_ci	default:
91662306a36Sopenharmony_ci		ret = ip6gre_xmit_other(skb, dev);
91762306a36Sopenharmony_ci		break;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (ret < 0)
92162306a36Sopenharmony_ci		goto tx_err;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return NETDEV_TX_OK;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_citx_err:
92662306a36Sopenharmony_ci	if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
92762306a36Sopenharmony_ci		DEV_STATS_INC(dev, tx_errors);
92862306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
92962306a36Sopenharmony_ci	kfree_skb(skb);
93062306a36Sopenharmony_ci	return NETDEV_TX_OK;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
93462306a36Sopenharmony_ci					 struct net_device *dev)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	struct ip_tunnel_info *tun_info = NULL;
93762306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
93862306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
93962306a36Sopenharmony_ci	bool truncate = false;
94062306a36Sopenharmony_ci	int encap_limit = -1;
94162306a36Sopenharmony_ci	__u8 dsfield = false;
94262306a36Sopenharmony_ci	struct flowi6 fl6;
94362306a36Sopenharmony_ci	int err = -EINVAL;
94462306a36Sopenharmony_ci	__be16 proto;
94562306a36Sopenharmony_ci	__u32 mtu;
94662306a36Sopenharmony_ci	int nhoff;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
94962306a36Sopenharmony_ci		goto tx_err;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
95262306a36Sopenharmony_ci		goto tx_err;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (gre_handle_offloads(skb, false))
95562306a36Sopenharmony_ci		goto tx_err;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if (skb->len > dev->mtu + dev->hard_header_len) {
95862306a36Sopenharmony_ci		if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
95962306a36Sopenharmony_ci			goto tx_err;
96062306a36Sopenharmony_ci		truncate = true;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	nhoff = skb_network_offset(skb);
96462306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP) &&
96562306a36Sopenharmony_ci	    (ntohs(ip_hdr(skb)->tot_len) > skb->len - nhoff))
96662306a36Sopenharmony_ci		truncate = true;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IPV6)) {
96962306a36Sopenharmony_ci		int thoff;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		if (skb_transport_header_was_set(skb))
97262306a36Sopenharmony_ci			thoff = skb_transport_offset(skb);
97362306a36Sopenharmony_ci		else
97462306a36Sopenharmony_ci			thoff = nhoff + sizeof(struct ipv6hdr);
97562306a36Sopenharmony_ci		if (ntohs(ipv6_hdr(skb)->payload_len) > skb->len - thoff)
97662306a36Sopenharmony_ci			truncate = true;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
98062306a36Sopenharmony_ci		goto tx_err;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	t->parms.o_flags &= ~TUNNEL_KEY;
98362306a36Sopenharmony_ci	IPCB(skb)->flags = 0;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* For collect_md mode, derive fl6 from the tunnel key,
98662306a36Sopenharmony_ci	 * for native mode, call prepare_ip6gre_xmit_{ipv4,ipv6}.
98762306a36Sopenharmony_ci	 */
98862306a36Sopenharmony_ci	if (t->parms.collect_md) {
98962306a36Sopenharmony_ci		const struct ip_tunnel_key *key;
99062306a36Sopenharmony_ci		struct erspan_metadata *md;
99162306a36Sopenharmony_ci		__be32 tun_id;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		tun_info = skb_tunnel_info_txcheck(skb);
99462306a36Sopenharmony_ci		if (IS_ERR(tun_info) ||
99562306a36Sopenharmony_ci		    unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
99662306a36Sopenharmony_ci			goto tx_err;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		key = &tun_info->key;
99962306a36Sopenharmony_ci		memset(&fl6, 0, sizeof(fl6));
100062306a36Sopenharmony_ci		fl6.flowi6_proto = IPPROTO_GRE;
100162306a36Sopenharmony_ci		fl6.daddr = key->u.ipv6.dst;
100262306a36Sopenharmony_ci		fl6.flowlabel = key->label;
100362306a36Sopenharmony_ci		fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
100462306a36Sopenharmony_ci		fl6.fl6_gre_key = tunnel_id_to_key32(key->tun_id);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci		dsfield = key->tos;
100762306a36Sopenharmony_ci		if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
100862306a36Sopenharmony_ci			goto tx_err;
100962306a36Sopenharmony_ci		if (tun_info->options_len < sizeof(*md))
101062306a36Sopenharmony_ci			goto tx_err;
101162306a36Sopenharmony_ci		md = ip_tunnel_info_opts(tun_info);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		tun_id = tunnel_id_to_key32(key->tun_id);
101462306a36Sopenharmony_ci		if (md->version == 1) {
101562306a36Sopenharmony_ci			erspan_build_header(skb,
101662306a36Sopenharmony_ci					    ntohl(tun_id),
101762306a36Sopenharmony_ci					    ntohl(md->u.index), truncate,
101862306a36Sopenharmony_ci					    false);
101962306a36Sopenharmony_ci			proto = htons(ETH_P_ERSPAN);
102062306a36Sopenharmony_ci		} else if (md->version == 2) {
102162306a36Sopenharmony_ci			erspan_build_header_v2(skb,
102262306a36Sopenharmony_ci					       ntohl(tun_id),
102362306a36Sopenharmony_ci					       md->u.md2.dir,
102462306a36Sopenharmony_ci					       get_hwid(&md->u.md2),
102562306a36Sopenharmony_ci					       truncate, false);
102662306a36Sopenharmony_ci			proto = htons(ETH_P_ERSPAN2);
102762306a36Sopenharmony_ci		} else {
102862306a36Sopenharmony_ci			goto tx_err;
102962306a36Sopenharmony_ci		}
103062306a36Sopenharmony_ci	} else {
103162306a36Sopenharmony_ci		switch (skb->protocol) {
103262306a36Sopenharmony_ci		case htons(ETH_P_IP):
103362306a36Sopenharmony_ci			memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
103462306a36Sopenharmony_ci			prepare_ip6gre_xmit_ipv4(skb, dev, &fl6,
103562306a36Sopenharmony_ci						 &dsfield, &encap_limit);
103662306a36Sopenharmony_ci			break;
103762306a36Sopenharmony_ci		case htons(ETH_P_IPV6):
103862306a36Sopenharmony_ci			if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr))
103962306a36Sopenharmony_ci				goto tx_err;
104062306a36Sopenharmony_ci			if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6,
104162306a36Sopenharmony_ci						     &dsfield, &encap_limit))
104262306a36Sopenharmony_ci				goto tx_err;
104362306a36Sopenharmony_ci			break;
104462306a36Sopenharmony_ci		default:
104562306a36Sopenharmony_ci			memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
104662306a36Sopenharmony_ci			break;
104762306a36Sopenharmony_ci		}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		if (t->parms.erspan_ver == 1) {
105062306a36Sopenharmony_ci			erspan_build_header(skb, ntohl(t->parms.o_key),
105162306a36Sopenharmony_ci					    t->parms.index,
105262306a36Sopenharmony_ci					    truncate, false);
105362306a36Sopenharmony_ci			proto = htons(ETH_P_ERSPAN);
105462306a36Sopenharmony_ci		} else if (t->parms.erspan_ver == 2) {
105562306a36Sopenharmony_ci			erspan_build_header_v2(skb, ntohl(t->parms.o_key),
105662306a36Sopenharmony_ci					       t->parms.dir,
105762306a36Sopenharmony_ci					       t->parms.hwid,
105862306a36Sopenharmony_ci					       truncate, false);
105962306a36Sopenharmony_ci			proto = htons(ETH_P_ERSPAN2);
106062306a36Sopenharmony_ci		} else {
106162306a36Sopenharmony_ci			goto tx_err;
106262306a36Sopenharmony_ci		}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		fl6.daddr = t->parms.raddr;
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	/* Push GRE header. */
106862306a36Sopenharmony_ci	gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(atomic_fetch_inc(&t->o_seqno)));
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* TooBig packet may have updated dst->dev's mtu */
107162306a36Sopenharmony_ci	if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu)
107262306a36Sopenharmony_ci		dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, false);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
107562306a36Sopenharmony_ci			   NEXTHDR_GRE);
107662306a36Sopenharmony_ci	if (err != 0) {
107762306a36Sopenharmony_ci		/* XXX: send ICMP error even if DF is not set. */
107862306a36Sopenharmony_ci		if (err == -EMSGSIZE) {
107962306a36Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP))
108062306a36Sopenharmony_ci				icmp_ndo_send(skb, ICMP_DEST_UNREACH,
108162306a36Sopenharmony_ci					      ICMP_FRAG_NEEDED, htonl(mtu));
108262306a36Sopenharmony_ci			else
108362306a36Sopenharmony_ci				icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		goto tx_err;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci	return NETDEV_TX_OK;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_citx_err:
109162306a36Sopenharmony_ci	if (!IS_ERR(tun_info))
109262306a36Sopenharmony_ci		DEV_STATS_INC(dev, tx_errors);
109362306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
109462306a36Sopenharmony_ci	kfree_skb(skb);
109562306a36Sopenharmony_ci	return NETDEV_TX_OK;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic void ip6gre_tnl_link_config_common(struct ip6_tnl *t)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct net_device *dev = t->dev;
110162306a36Sopenharmony_ci	struct __ip6_tnl_parm *p = &t->parms;
110262306a36Sopenharmony_ci	struct flowi6 *fl6 = &t->fl.u.ip6;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (dev->type != ARPHRD_ETHER) {
110562306a36Sopenharmony_ci		__dev_addr_set(dev, &p->laddr, sizeof(struct in6_addr));
110662306a36Sopenharmony_ci		memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* Set up flowi template */
111062306a36Sopenharmony_ci	fl6->saddr = p->laddr;
111162306a36Sopenharmony_ci	fl6->daddr = p->raddr;
111262306a36Sopenharmony_ci	fl6->flowi6_oif = p->link;
111362306a36Sopenharmony_ci	fl6->flowlabel = 0;
111462306a36Sopenharmony_ci	fl6->flowi6_proto = IPPROTO_GRE;
111562306a36Sopenharmony_ci	fl6->fl6_gre_key = t->parms.o_key;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
111862306a36Sopenharmony_ci		fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
111962306a36Sopenharmony_ci	if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
112062306a36Sopenharmony_ci		fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
112362306a36Sopenharmony_ci	p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (p->flags&IP6_TNL_F_CAP_XMIT &&
112662306a36Sopenharmony_ci			p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER)
112762306a36Sopenharmony_ci		dev->flags |= IFF_POINTOPOINT;
112862306a36Sopenharmony_ci	else
112962306a36Sopenharmony_ci		dev->flags &= ~IFF_POINTOPOINT;
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu,
113362306a36Sopenharmony_ci					 int t_hlen)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	const struct __ip6_tnl_parm *p = &t->parms;
113662306a36Sopenharmony_ci	struct net_device *dev = t->dev;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (p->flags & IP6_TNL_F_CAP_XMIT) {
113962306a36Sopenharmony_ci		int strict = (ipv6_addr_type(&p->raddr) &
114062306a36Sopenharmony_ci			      (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci		struct rt6_info *rt = rt6_lookup(t->net,
114362306a36Sopenharmony_ci						 &p->raddr, &p->laddr,
114462306a36Sopenharmony_ci						 p->link, NULL, strict);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		if (!rt)
114762306a36Sopenharmony_ci			return;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		if (rt->dst.dev) {
115062306a36Sopenharmony_ci			unsigned short dst_len = rt->dst.dev->hard_header_len +
115162306a36Sopenharmony_ci						 t_hlen;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci			if (t->dev->header_ops)
115462306a36Sopenharmony_ci				dev->hard_header_len = dst_len;
115562306a36Sopenharmony_ci			else
115662306a36Sopenharmony_ci				dev->needed_headroom = dst_len;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci			if (set_mtu) {
115962306a36Sopenharmony_ci				int mtu = rt->dst.dev->mtu - t_hlen;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci				if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
116262306a36Sopenharmony_ci					mtu -= 8;
116362306a36Sopenharmony_ci				if (dev->type == ARPHRD_ETHER)
116462306a36Sopenharmony_ci					mtu -= ETH_HLEN;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci				if (mtu < IPV6_MIN_MTU)
116762306a36Sopenharmony_ci					mtu = IPV6_MIN_MTU;
116862306a36Sopenharmony_ci				WRITE_ONCE(dev->mtu, mtu);
116962306a36Sopenharmony_ci			}
117062306a36Sopenharmony_ci		}
117162306a36Sopenharmony_ci		ip6_rt_put(rt);
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_cistatic int ip6gre_calc_hlen(struct ip6_tnl *tunnel)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	int t_hlen;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
118062306a36Sopenharmony_ci	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (tunnel->dev->header_ops)
118562306a36Sopenharmony_ci		tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
118662306a36Sopenharmony_ci	else
118762306a36Sopenharmony_ci		tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	return t_hlen;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	ip6gre_tnl_link_config_common(t);
119562306a36Sopenharmony_ci	ip6gre_tnl_link_config_route(t, set_mtu, ip6gre_calc_hlen(t));
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic void ip6gre_tnl_copy_tnl_parm(struct ip6_tnl *t,
119962306a36Sopenharmony_ci				     const struct __ip6_tnl_parm *p)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	t->parms.laddr = p->laddr;
120262306a36Sopenharmony_ci	t->parms.raddr = p->raddr;
120362306a36Sopenharmony_ci	t->parms.flags = p->flags;
120462306a36Sopenharmony_ci	t->parms.hop_limit = p->hop_limit;
120562306a36Sopenharmony_ci	t->parms.encap_limit = p->encap_limit;
120662306a36Sopenharmony_ci	t->parms.flowinfo = p->flowinfo;
120762306a36Sopenharmony_ci	t->parms.link = p->link;
120862306a36Sopenharmony_ci	t->parms.proto = p->proto;
120962306a36Sopenharmony_ci	t->parms.i_key = p->i_key;
121062306a36Sopenharmony_ci	t->parms.o_key = p->o_key;
121162306a36Sopenharmony_ci	t->parms.i_flags = p->i_flags;
121262306a36Sopenharmony_ci	t->parms.o_flags = p->o_flags;
121362306a36Sopenharmony_ci	t->parms.fwmark = p->fwmark;
121462306a36Sopenharmony_ci	t->parms.erspan_ver = p->erspan_ver;
121562306a36Sopenharmony_ci	t->parms.index = p->index;
121662306a36Sopenharmony_ci	t->parms.dir = p->dir;
121762306a36Sopenharmony_ci	t->parms.hwid = p->hwid;
121862306a36Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
121962306a36Sopenharmony_ci}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_cistatic int ip6gre_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
122262306a36Sopenharmony_ci			     int set_mtu)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	ip6gre_tnl_copy_tnl_parm(t, p);
122562306a36Sopenharmony_ci	ip6gre_tnl_link_config(t, set_mtu);
122662306a36Sopenharmony_ci	return 0;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p,
123062306a36Sopenharmony_ci	const struct ip6_tnl_parm2 *u)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	p->laddr = u->laddr;
123362306a36Sopenharmony_ci	p->raddr = u->raddr;
123462306a36Sopenharmony_ci	p->flags = u->flags;
123562306a36Sopenharmony_ci	p->hop_limit = u->hop_limit;
123662306a36Sopenharmony_ci	p->encap_limit = u->encap_limit;
123762306a36Sopenharmony_ci	p->flowinfo = u->flowinfo;
123862306a36Sopenharmony_ci	p->link = u->link;
123962306a36Sopenharmony_ci	p->i_key = u->i_key;
124062306a36Sopenharmony_ci	p->o_key = u->o_key;
124162306a36Sopenharmony_ci	p->i_flags = gre_flags_to_tnl_flags(u->i_flags);
124262306a36Sopenharmony_ci	p->o_flags = gre_flags_to_tnl_flags(u->o_flags);
124362306a36Sopenharmony_ci	memcpy(p->name, u->name, sizeof(u->name));
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u,
124762306a36Sopenharmony_ci	const struct __ip6_tnl_parm *p)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	u->proto = IPPROTO_GRE;
125062306a36Sopenharmony_ci	u->laddr = p->laddr;
125162306a36Sopenharmony_ci	u->raddr = p->raddr;
125262306a36Sopenharmony_ci	u->flags = p->flags;
125362306a36Sopenharmony_ci	u->hop_limit = p->hop_limit;
125462306a36Sopenharmony_ci	u->encap_limit = p->encap_limit;
125562306a36Sopenharmony_ci	u->flowinfo = p->flowinfo;
125662306a36Sopenharmony_ci	u->link = p->link;
125762306a36Sopenharmony_ci	u->i_key = p->i_key;
125862306a36Sopenharmony_ci	u->o_key = p->o_key;
125962306a36Sopenharmony_ci	u->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags);
126062306a36Sopenharmony_ci	u->o_flags = gre_tnl_flags_to_gre_flags(p->o_flags);
126162306a36Sopenharmony_ci	memcpy(u->name, p->name, sizeof(u->name));
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic int ip6gre_tunnel_siocdevprivate(struct net_device *dev,
126562306a36Sopenharmony_ci					struct ifreq *ifr, void __user *data,
126662306a36Sopenharmony_ci					int cmd)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	int err = 0;
126962306a36Sopenharmony_ci	struct ip6_tnl_parm2 p;
127062306a36Sopenharmony_ci	struct __ip6_tnl_parm p1;
127162306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
127262306a36Sopenharmony_ci	struct net *net = t->net;
127362306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	memset(&p1, 0, sizeof(p1));
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	switch (cmd) {
127862306a36Sopenharmony_ci	case SIOCGETTUNNEL:
127962306a36Sopenharmony_ci		if (dev == ign->fb_tunnel_dev) {
128062306a36Sopenharmony_ci			if (copy_from_user(&p, data, sizeof(p))) {
128162306a36Sopenharmony_ci				err = -EFAULT;
128262306a36Sopenharmony_ci				break;
128362306a36Sopenharmony_ci			}
128462306a36Sopenharmony_ci			ip6gre_tnl_parm_from_user(&p1, &p);
128562306a36Sopenharmony_ci			t = ip6gre_tunnel_locate(net, &p1, 0);
128662306a36Sopenharmony_ci			if (!t)
128762306a36Sopenharmony_ci				t = netdev_priv(dev);
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci		memset(&p, 0, sizeof(p));
129062306a36Sopenharmony_ci		ip6gre_tnl_parm_to_user(&p, &t->parms);
129162306a36Sopenharmony_ci		if (copy_to_user(data, &p, sizeof(p)))
129262306a36Sopenharmony_ci			err = -EFAULT;
129362306a36Sopenharmony_ci		break;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	case SIOCADDTUNNEL:
129662306a36Sopenharmony_ci	case SIOCCHGTUNNEL:
129762306a36Sopenharmony_ci		err = -EPERM;
129862306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
129962306a36Sopenharmony_ci			goto done;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci		err = -EFAULT;
130262306a36Sopenharmony_ci		if (copy_from_user(&p, data, sizeof(p)))
130362306a36Sopenharmony_ci			goto done;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		err = -EINVAL;
130662306a36Sopenharmony_ci		if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))
130762306a36Sopenharmony_ci			goto done;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci		if (!(p.i_flags&GRE_KEY))
131062306a36Sopenharmony_ci			p.i_key = 0;
131162306a36Sopenharmony_ci		if (!(p.o_flags&GRE_KEY))
131262306a36Sopenharmony_ci			p.o_key = 0;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		ip6gre_tnl_parm_from_user(&p1, &p);
131562306a36Sopenharmony_ci		t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
131862306a36Sopenharmony_ci			if (t) {
131962306a36Sopenharmony_ci				if (t->dev != dev) {
132062306a36Sopenharmony_ci					err = -EEXIST;
132162306a36Sopenharmony_ci					break;
132262306a36Sopenharmony_ci				}
132362306a36Sopenharmony_ci			} else {
132462306a36Sopenharmony_ci				t = netdev_priv(dev);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci				ip6gre_tunnel_unlink(ign, t);
132762306a36Sopenharmony_ci				synchronize_net();
132862306a36Sopenharmony_ci				ip6gre_tnl_change(t, &p1, 1);
132962306a36Sopenharmony_ci				ip6gre_tunnel_link(ign, t);
133062306a36Sopenharmony_ci				netdev_state_change(dev);
133162306a36Sopenharmony_ci			}
133262306a36Sopenharmony_ci		}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci		if (t) {
133562306a36Sopenharmony_ci			err = 0;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci			memset(&p, 0, sizeof(p));
133862306a36Sopenharmony_ci			ip6gre_tnl_parm_to_user(&p, &t->parms);
133962306a36Sopenharmony_ci			if (copy_to_user(data, &p, sizeof(p)))
134062306a36Sopenharmony_ci				err = -EFAULT;
134162306a36Sopenharmony_ci		} else
134262306a36Sopenharmony_ci			err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
134362306a36Sopenharmony_ci		break;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	case SIOCDELTUNNEL:
134662306a36Sopenharmony_ci		err = -EPERM;
134762306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
134862306a36Sopenharmony_ci			goto done;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci		if (dev == ign->fb_tunnel_dev) {
135162306a36Sopenharmony_ci			err = -EFAULT;
135262306a36Sopenharmony_ci			if (copy_from_user(&p, data, sizeof(p)))
135362306a36Sopenharmony_ci				goto done;
135462306a36Sopenharmony_ci			err = -ENOENT;
135562306a36Sopenharmony_ci			ip6gre_tnl_parm_from_user(&p1, &p);
135662306a36Sopenharmony_ci			t = ip6gre_tunnel_locate(net, &p1, 0);
135762306a36Sopenharmony_ci			if (!t)
135862306a36Sopenharmony_ci				goto done;
135962306a36Sopenharmony_ci			err = -EPERM;
136062306a36Sopenharmony_ci			if (t == netdev_priv(ign->fb_tunnel_dev))
136162306a36Sopenharmony_ci				goto done;
136262306a36Sopenharmony_ci			dev = t->dev;
136362306a36Sopenharmony_ci		}
136462306a36Sopenharmony_ci		unregister_netdevice(dev);
136562306a36Sopenharmony_ci		err = 0;
136662306a36Sopenharmony_ci		break;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	default:
136962306a36Sopenharmony_ci		err = -EINVAL;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cidone:
137362306a36Sopenharmony_ci	return err;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
137762306a36Sopenharmony_ci			 unsigned short type, const void *daddr,
137862306a36Sopenharmony_ci			 const void *saddr, unsigned int len)
137962306a36Sopenharmony_ci{
138062306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
138162306a36Sopenharmony_ci	struct ipv6hdr *ipv6h;
138262306a36Sopenharmony_ci	__be16 *p;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	ipv6h = skb_push(skb, t->hlen + sizeof(*ipv6h));
138562306a36Sopenharmony_ci	ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb,
138662306a36Sopenharmony_ci						  t->fl.u.ip6.flowlabel,
138762306a36Sopenharmony_ci						  true, &t->fl.u.ip6));
138862306a36Sopenharmony_ci	ipv6h->hop_limit = t->parms.hop_limit;
138962306a36Sopenharmony_ci	ipv6h->nexthdr = NEXTHDR_GRE;
139062306a36Sopenharmony_ci	ipv6h->saddr = t->parms.laddr;
139162306a36Sopenharmony_ci	ipv6h->daddr = t->parms.raddr;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	p = (__be16 *)(ipv6h + 1);
139462306a36Sopenharmony_ci	p[0] = t->parms.o_flags;
139562306a36Sopenharmony_ci	p[1] = htons(type);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/*
139862306a36Sopenharmony_ci	 *	Set the source hardware address.
139962306a36Sopenharmony_ci	 */
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	if (saddr)
140262306a36Sopenharmony_ci		memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr));
140362306a36Sopenharmony_ci	if (daddr)
140462306a36Sopenharmony_ci		memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr));
140562306a36Sopenharmony_ci	if (!ipv6_addr_any(&ipv6h->daddr))
140662306a36Sopenharmony_ci		return t->hlen;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	return -t->hlen;
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_cistatic const struct header_ops ip6gre_header_ops = {
141262306a36Sopenharmony_ci	.create	= ip6gre_header,
141362306a36Sopenharmony_ci};
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic const struct net_device_ops ip6gre_netdev_ops = {
141662306a36Sopenharmony_ci	.ndo_init		= ip6gre_tunnel_init,
141762306a36Sopenharmony_ci	.ndo_uninit		= ip6gre_tunnel_uninit,
141862306a36Sopenharmony_ci	.ndo_start_xmit		= ip6gre_tunnel_xmit,
141962306a36Sopenharmony_ci	.ndo_siocdevprivate	= ip6gre_tunnel_siocdevprivate,
142062306a36Sopenharmony_ci	.ndo_change_mtu		= ip6_tnl_change_mtu,
142162306a36Sopenharmony_ci	.ndo_get_stats64	= dev_get_tstats64,
142262306a36Sopenharmony_ci	.ndo_get_iflink		= ip6_tnl_get_iflink,
142362306a36Sopenharmony_ci};
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic void ip6gre_dev_free(struct net_device *dev)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	gro_cells_destroy(&t->gro_cells);
143062306a36Sopenharmony_ci	dst_cache_destroy(&t->dst_cache);
143162306a36Sopenharmony_ci	free_percpu(dev->tstats);
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic void ip6gre_tunnel_setup(struct net_device *dev)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	dev->netdev_ops = &ip6gre_netdev_ops;
143762306a36Sopenharmony_ci	dev->needs_free_netdev = true;
143862306a36Sopenharmony_ci	dev->priv_destructor = ip6gre_dev_free;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	dev->type = ARPHRD_IP6GRE;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	dev->flags |= IFF_NOARP;
144362306a36Sopenharmony_ci	dev->addr_len = sizeof(struct in6_addr);
144462306a36Sopenharmony_ci	netif_keep_dst(dev);
144562306a36Sopenharmony_ci	/* This perm addr will be used as interface identifier by IPv6 */
144662306a36Sopenharmony_ci	dev->addr_assign_type = NET_ADDR_RANDOM;
144762306a36Sopenharmony_ci	eth_random_addr(dev->perm_addr);
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci#define GRE6_FEATURES (NETIF_F_SG |		\
145162306a36Sopenharmony_ci		       NETIF_F_FRAGLIST |	\
145262306a36Sopenharmony_ci		       NETIF_F_HIGHDMA |	\
145362306a36Sopenharmony_ci		       NETIF_F_HW_CSUM)
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cistatic void ip6gre_tnl_init_features(struct net_device *dev)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	struct ip6_tnl *nt = netdev_priv(dev);
145862306a36Sopenharmony_ci	__be16 flags;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	dev->features		|= GRE6_FEATURES | NETIF_F_LLTX;
146162306a36Sopenharmony_ci	dev->hw_features	|= GRE6_FEATURES;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	flags = nt->parms.o_flags;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	/* TCP offload with GRE SEQ is not supported, nor can we support 2
146662306a36Sopenharmony_ci	 * levels of outer headers requiring an update.
146762306a36Sopenharmony_ci	 */
146862306a36Sopenharmony_ci	if (flags & TUNNEL_SEQ)
146962306a36Sopenharmony_ci		return;
147062306a36Sopenharmony_ci	if (flags & TUNNEL_CSUM && nt->encap.type != TUNNEL_ENCAP_NONE)
147162306a36Sopenharmony_ci		return;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	dev->features |= NETIF_F_GSO_SOFTWARE;
147462306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic int ip6gre_tunnel_init_common(struct net_device *dev)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct ip6_tnl *tunnel;
148062306a36Sopenharmony_ci	int ret;
148162306a36Sopenharmony_ci	int t_hlen;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	tunnel = netdev_priv(dev);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	tunnel->dev = dev;
148662306a36Sopenharmony_ci	tunnel->net = dev_net(dev);
148762306a36Sopenharmony_ci	strcpy(tunnel->parms.name, dev->name);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
149062306a36Sopenharmony_ci	if (!dev->tstats)
149162306a36Sopenharmony_ci		return -ENOMEM;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
149462306a36Sopenharmony_ci	if (ret)
149562306a36Sopenharmony_ci		goto cleanup_alloc_pcpu_stats;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	ret = gro_cells_init(&tunnel->gro_cells, dev);
149862306a36Sopenharmony_ci	if (ret)
149962306a36Sopenharmony_ci		goto cleanup_dst_cache_init;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	t_hlen = ip6gre_calc_hlen(tunnel);
150262306a36Sopenharmony_ci	dev->mtu = ETH_DATA_LEN - t_hlen;
150362306a36Sopenharmony_ci	if (dev->type == ARPHRD_ETHER)
150462306a36Sopenharmony_ci		dev->mtu -= ETH_HLEN;
150562306a36Sopenharmony_ci	if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
150662306a36Sopenharmony_ci		dev->mtu -= 8;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	if (tunnel->parms.collect_md) {
150962306a36Sopenharmony_ci		netif_keep_dst(dev);
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci	ip6gre_tnl_init_features(dev);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	netdev_hold(dev, &tunnel->dev_tracker, GFP_KERNEL);
151462306a36Sopenharmony_ci	return 0;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_cicleanup_dst_cache_init:
151762306a36Sopenharmony_ci	dst_cache_destroy(&tunnel->dst_cache);
151862306a36Sopenharmony_cicleanup_alloc_pcpu_stats:
151962306a36Sopenharmony_ci	free_percpu(dev->tstats);
152062306a36Sopenharmony_ci	dev->tstats = NULL;
152162306a36Sopenharmony_ci	return ret;
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic int ip6gre_tunnel_init(struct net_device *dev)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct ip6_tnl *tunnel;
152762306a36Sopenharmony_ci	int ret;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	ret = ip6gre_tunnel_init_common(dev);
153062306a36Sopenharmony_ci	if (ret)
153162306a36Sopenharmony_ci		return ret;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	tunnel = netdev_priv(dev);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	if (tunnel->parms.collect_md)
153662306a36Sopenharmony_ci		return 0;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	__dev_addr_set(dev, &tunnel->parms.laddr, sizeof(struct in6_addr));
153962306a36Sopenharmony_ci	memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr));
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (ipv6_addr_any(&tunnel->parms.raddr))
154262306a36Sopenharmony_ci		dev->header_ops = &ip6gre_header_ops;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	return 0;
154562306a36Sopenharmony_ci}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_cistatic void ip6gre_fb_tunnel_init(struct net_device *dev)
154862306a36Sopenharmony_ci{
154962306a36Sopenharmony_ci	struct ip6_tnl *tunnel = netdev_priv(dev);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	tunnel->dev = dev;
155262306a36Sopenharmony_ci	tunnel->net = dev_net(dev);
155362306a36Sopenharmony_ci	strcpy(tunnel->parms.name, dev->name);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	tunnel->hlen		= sizeof(struct ipv6hdr) + 4;
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_cistatic struct inet6_protocol ip6gre_protocol __read_mostly = {
155962306a36Sopenharmony_ci	.handler     = gre_rcv,
156062306a36Sopenharmony_ci	.err_handler = ip6gre_err,
156162306a36Sopenharmony_ci	.flags       = INET6_PROTO_FINAL,
156262306a36Sopenharmony_ci};
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_cistatic void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
156562306a36Sopenharmony_ci{
156662306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
156762306a36Sopenharmony_ci	struct net_device *dev, *aux;
156862306a36Sopenharmony_ci	int prio;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	for_each_netdev_safe(net, dev, aux)
157162306a36Sopenharmony_ci		if (dev->rtnl_link_ops == &ip6gre_link_ops ||
157262306a36Sopenharmony_ci		    dev->rtnl_link_ops == &ip6gre_tap_ops ||
157362306a36Sopenharmony_ci		    dev->rtnl_link_ops == &ip6erspan_tap_ops)
157462306a36Sopenharmony_ci			unregister_netdevice_queue(dev, head);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	for (prio = 0; prio < 4; prio++) {
157762306a36Sopenharmony_ci		int h;
157862306a36Sopenharmony_ci		for (h = 0; h < IP6_GRE_HASH_SIZE; h++) {
157962306a36Sopenharmony_ci			struct ip6_tnl *t;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci			t = rtnl_dereference(ign->tunnels[prio][h]);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci			while (t) {
158462306a36Sopenharmony_ci				/* If dev is in the same netns, it has already
158562306a36Sopenharmony_ci				 * been added to the list by the previous loop.
158662306a36Sopenharmony_ci				 */
158762306a36Sopenharmony_ci				if (!net_eq(dev_net(t->dev), net))
158862306a36Sopenharmony_ci					unregister_netdevice_queue(t->dev,
158962306a36Sopenharmony_ci								   head);
159062306a36Sopenharmony_ci				t = rtnl_dereference(t->next);
159162306a36Sopenharmony_ci			}
159262306a36Sopenharmony_ci		}
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_cistatic int __net_init ip6gre_init_net(struct net *net)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
159962306a36Sopenharmony_ci	struct net_device *ndev;
160062306a36Sopenharmony_ci	int err;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (!net_has_fallback_tunnels(net))
160362306a36Sopenharmony_ci		return 0;
160462306a36Sopenharmony_ci	ndev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0",
160562306a36Sopenharmony_ci			    NET_NAME_UNKNOWN, ip6gre_tunnel_setup);
160662306a36Sopenharmony_ci	if (!ndev) {
160762306a36Sopenharmony_ci		err = -ENOMEM;
160862306a36Sopenharmony_ci		goto err_alloc_dev;
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci	ign->fb_tunnel_dev = ndev;
161162306a36Sopenharmony_ci	dev_net_set(ign->fb_tunnel_dev, net);
161262306a36Sopenharmony_ci	/* FB netdevice is special: we have one, and only one per netns.
161362306a36Sopenharmony_ci	 * Allowing to move it to another netns is clearly unsafe.
161462306a36Sopenharmony_ci	 */
161562306a36Sopenharmony_ci	ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
161962306a36Sopenharmony_ci	ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	err = register_netdev(ign->fb_tunnel_dev);
162262306a36Sopenharmony_ci	if (err)
162362306a36Sopenharmony_ci		goto err_reg_dev;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	rcu_assign_pointer(ign->tunnels_wc[0],
162662306a36Sopenharmony_ci			   netdev_priv(ign->fb_tunnel_dev));
162762306a36Sopenharmony_ci	return 0;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_cierr_reg_dev:
163062306a36Sopenharmony_ci	free_netdev(ndev);
163162306a36Sopenharmony_cierr_alloc_dev:
163262306a36Sopenharmony_ci	return err;
163362306a36Sopenharmony_ci}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_cistatic void __net_exit ip6gre_exit_batch_net(struct list_head *net_list)
163662306a36Sopenharmony_ci{
163762306a36Sopenharmony_ci	struct net *net;
163862306a36Sopenharmony_ci	LIST_HEAD(list);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	rtnl_lock();
164162306a36Sopenharmony_ci	list_for_each_entry(net, net_list, exit_list)
164262306a36Sopenharmony_ci		ip6gre_destroy_tunnels(net, &list);
164362306a36Sopenharmony_ci	unregister_netdevice_many(&list);
164462306a36Sopenharmony_ci	rtnl_unlock();
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_cistatic struct pernet_operations ip6gre_net_ops = {
164862306a36Sopenharmony_ci	.init = ip6gre_init_net,
164962306a36Sopenharmony_ci	.exit_batch = ip6gre_exit_batch_net,
165062306a36Sopenharmony_ci	.id   = &ip6gre_net_id,
165162306a36Sopenharmony_ci	.size = sizeof(struct ip6gre_net),
165262306a36Sopenharmony_ci};
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_cistatic int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[],
165562306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
165662306a36Sopenharmony_ci{
165762306a36Sopenharmony_ci	__be16 flags;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	if (!data)
166062306a36Sopenharmony_ci		return 0;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	flags = 0;
166362306a36Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
166462306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
166562306a36Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
166662306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
166762306a36Sopenharmony_ci	if (flags & (GRE_VERSION|GRE_ROUTING))
166862306a36Sopenharmony_ci		return -EINVAL;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	return 0;
167162306a36Sopenharmony_ci}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_cistatic int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[],
167462306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
167562306a36Sopenharmony_ci{
167662306a36Sopenharmony_ci	struct in6_addr daddr;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
167962306a36Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
168062306a36Sopenharmony_ci			return -EINVAL;
168162306a36Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
168262306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
168362306a36Sopenharmony_ci	}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if (!data)
168662306a36Sopenharmony_ci		goto out;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	if (data[IFLA_GRE_REMOTE]) {
168962306a36Sopenharmony_ci		daddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]);
169062306a36Sopenharmony_ci		if (ipv6_addr_any(&daddr))
169162306a36Sopenharmony_ci			return -EINVAL;
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ciout:
169562306a36Sopenharmony_ci	return ip6gre_tunnel_validate(tb, data, extack);
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_cistatic int ip6erspan_tap_validate(struct nlattr *tb[], struct nlattr *data[],
169962306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
170062306a36Sopenharmony_ci{
170162306a36Sopenharmony_ci	__be16 flags = 0;
170262306a36Sopenharmony_ci	int ret, ver = 0;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	if (!data)
170562306a36Sopenharmony_ci		return 0;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	ret = ip6gre_tap_validate(tb, data, extack);
170862306a36Sopenharmony_ci	if (ret)
170962306a36Sopenharmony_ci		return ret;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	/* ERSPAN should only have GRE sequence and key flag */
171262306a36Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
171362306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
171462306a36Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
171562306a36Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
171662306a36Sopenharmony_ci	if (!data[IFLA_GRE_COLLECT_METADATA] &&
171762306a36Sopenharmony_ci	    flags != (GRE_SEQ | GRE_KEY))
171862306a36Sopenharmony_ci		return -EINVAL;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	/* ERSPAN Session ID only has 10-bit. Since we reuse
172162306a36Sopenharmony_ci	 * 32-bit key field as ID, check it's range.
172262306a36Sopenharmony_ci	 */
172362306a36Sopenharmony_ci	if (data[IFLA_GRE_IKEY] &&
172462306a36Sopenharmony_ci	    (ntohl(nla_get_be32(data[IFLA_GRE_IKEY])) & ~ID_MASK))
172562306a36Sopenharmony_ci		return -EINVAL;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	if (data[IFLA_GRE_OKEY] &&
172862306a36Sopenharmony_ci	    (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK))
172962306a36Sopenharmony_ci		return -EINVAL;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (data[IFLA_GRE_ERSPAN_VER]) {
173262306a36Sopenharmony_ci		ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
173362306a36Sopenharmony_ci		if (ver != 1 && ver != 2)
173462306a36Sopenharmony_ci			return -EINVAL;
173562306a36Sopenharmony_ci	}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	if (ver == 1) {
173862306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_INDEX]) {
173962306a36Sopenharmony_ci			u32 index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci			if (index & ~INDEX_MASK)
174262306a36Sopenharmony_ci				return -EINVAL;
174362306a36Sopenharmony_ci		}
174462306a36Sopenharmony_ci	} else if (ver == 2) {
174562306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_DIR]) {
174662306a36Sopenharmony_ci			u16 dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci			if (dir & ~(DIR_MASK >> DIR_OFFSET))
174962306a36Sopenharmony_ci				return -EINVAL;
175062306a36Sopenharmony_ci		}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_HWID]) {
175362306a36Sopenharmony_ci			u16 hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci			if (hwid & ~(HWID_MASK >> HWID_OFFSET))
175662306a36Sopenharmony_ci				return -EINVAL;
175762306a36Sopenharmony_ci		}
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	return 0;
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_cistatic void ip6erspan_set_version(struct nlattr *data[],
176462306a36Sopenharmony_ci				  struct __ip6_tnl_parm *parms)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	if (!data)
176762306a36Sopenharmony_ci		return;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	parms->erspan_ver = 1;
177062306a36Sopenharmony_ci	if (data[IFLA_GRE_ERSPAN_VER])
177162306a36Sopenharmony_ci		parms->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	if (parms->erspan_ver == 1) {
177462306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_INDEX])
177562306a36Sopenharmony_ci			parms->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
177662306a36Sopenharmony_ci	} else if (parms->erspan_ver == 2) {
177762306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_DIR])
177862306a36Sopenharmony_ci			parms->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
177962306a36Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_HWID])
178062306a36Sopenharmony_ci			parms->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
178162306a36Sopenharmony_ci	}
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic void ip6gre_netlink_parms(struct nlattr *data[],
178562306a36Sopenharmony_ci				struct __ip6_tnl_parm *parms)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	memset(parms, 0, sizeof(*parms));
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	if (!data)
179062306a36Sopenharmony_ci		return;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (data[IFLA_GRE_LINK])
179362306a36Sopenharmony_ci		parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
179662306a36Sopenharmony_ci		parms->i_flags = gre_flags_to_tnl_flags(
179762306a36Sopenharmony_ci				nla_get_be16(data[IFLA_GRE_IFLAGS]));
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
180062306a36Sopenharmony_ci		parms->o_flags = gre_flags_to_tnl_flags(
180162306a36Sopenharmony_ci				nla_get_be16(data[IFLA_GRE_OFLAGS]));
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (data[IFLA_GRE_IKEY])
180462306a36Sopenharmony_ci		parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	if (data[IFLA_GRE_OKEY])
180762306a36Sopenharmony_ci		parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (data[IFLA_GRE_LOCAL])
181062306a36Sopenharmony_ci		parms->laddr = nla_get_in6_addr(data[IFLA_GRE_LOCAL]);
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	if (data[IFLA_GRE_REMOTE])
181362306a36Sopenharmony_ci		parms->raddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	if (data[IFLA_GRE_TTL])
181662306a36Sopenharmony_ci		parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_LIMIT])
181962306a36Sopenharmony_ci		parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	if (data[IFLA_GRE_FLOWINFO])
182262306a36Sopenharmony_ci		parms->flowinfo = nla_get_be32(data[IFLA_GRE_FLOWINFO]);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	if (data[IFLA_GRE_FLAGS])
182562306a36Sopenharmony_ci		parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (data[IFLA_GRE_FWMARK])
182862306a36Sopenharmony_ci		parms->fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (data[IFLA_GRE_COLLECT_METADATA])
183162306a36Sopenharmony_ci		parms->collect_md = true;
183262306a36Sopenharmony_ci}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_cistatic int ip6gre_tap_init(struct net_device *dev)
183562306a36Sopenharmony_ci{
183662306a36Sopenharmony_ci	int ret;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	ret = ip6gre_tunnel_init_common(dev);
183962306a36Sopenharmony_ci	if (ret)
184062306a36Sopenharmony_ci		return ret;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	return 0;
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_cistatic const struct net_device_ops ip6gre_tap_netdev_ops = {
184862306a36Sopenharmony_ci	.ndo_init = ip6gre_tap_init,
184962306a36Sopenharmony_ci	.ndo_uninit = ip6gre_tunnel_uninit,
185062306a36Sopenharmony_ci	.ndo_start_xmit = ip6gre_tunnel_xmit,
185162306a36Sopenharmony_ci	.ndo_set_mac_address = eth_mac_addr,
185262306a36Sopenharmony_ci	.ndo_validate_addr = eth_validate_addr,
185362306a36Sopenharmony_ci	.ndo_change_mtu = ip6_tnl_change_mtu,
185462306a36Sopenharmony_ci	.ndo_get_stats64 = dev_get_tstats64,
185562306a36Sopenharmony_ci	.ndo_get_iflink = ip6_tnl_get_iflink,
185662306a36Sopenharmony_ci};
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_cistatic int ip6erspan_calc_hlen(struct ip6_tnl *tunnel)
185962306a36Sopenharmony_ci{
186062306a36Sopenharmony_ci	int t_hlen;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	tunnel->tun_hlen = 8;
186362306a36Sopenharmony_ci	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
186462306a36Sopenharmony_ci		       erspan_hdr_len(tunnel->parms.erspan_ver);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
186762306a36Sopenharmony_ci	tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
186862306a36Sopenharmony_ci	return t_hlen;
186962306a36Sopenharmony_ci}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_cistatic int ip6erspan_tap_init(struct net_device *dev)
187262306a36Sopenharmony_ci{
187362306a36Sopenharmony_ci	struct ip6_tnl *tunnel;
187462306a36Sopenharmony_ci	int t_hlen;
187562306a36Sopenharmony_ci	int ret;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	tunnel = netdev_priv(dev);
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	tunnel->dev = dev;
188062306a36Sopenharmony_ci	tunnel->net = dev_net(dev);
188162306a36Sopenharmony_ci	strcpy(tunnel->parms.name, dev->name);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
188462306a36Sopenharmony_ci	if (!dev->tstats)
188562306a36Sopenharmony_ci		return -ENOMEM;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
188862306a36Sopenharmony_ci	if (ret)
188962306a36Sopenharmony_ci		goto cleanup_alloc_pcpu_stats;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	ret = gro_cells_init(&tunnel->gro_cells, dev);
189262306a36Sopenharmony_ci	if (ret)
189362306a36Sopenharmony_ci		goto cleanup_dst_cache_init;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	t_hlen = ip6erspan_calc_hlen(tunnel);
189662306a36Sopenharmony_ci	dev->mtu = ETH_DATA_LEN - t_hlen;
189762306a36Sopenharmony_ci	if (dev->type == ARPHRD_ETHER)
189862306a36Sopenharmony_ci		dev->mtu -= ETH_HLEN;
189962306a36Sopenharmony_ci	if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
190062306a36Sopenharmony_ci		dev->mtu -= 8;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
190362306a36Sopenharmony_ci	ip6erspan_tnl_link_config(tunnel, 1);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	netdev_hold(dev, &tunnel->dev_tracker, GFP_KERNEL);
190662306a36Sopenharmony_ci	return 0;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_cicleanup_dst_cache_init:
190962306a36Sopenharmony_ci	dst_cache_destroy(&tunnel->dst_cache);
191062306a36Sopenharmony_cicleanup_alloc_pcpu_stats:
191162306a36Sopenharmony_ci	free_percpu(dev->tstats);
191262306a36Sopenharmony_ci	dev->tstats = NULL;
191362306a36Sopenharmony_ci	return ret;
191462306a36Sopenharmony_ci}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cistatic const struct net_device_ops ip6erspan_netdev_ops = {
191762306a36Sopenharmony_ci	.ndo_init =		ip6erspan_tap_init,
191862306a36Sopenharmony_ci	.ndo_uninit =		ip6erspan_tunnel_uninit,
191962306a36Sopenharmony_ci	.ndo_start_xmit =	ip6erspan_tunnel_xmit,
192062306a36Sopenharmony_ci	.ndo_set_mac_address =	eth_mac_addr,
192162306a36Sopenharmony_ci	.ndo_validate_addr =	eth_validate_addr,
192262306a36Sopenharmony_ci	.ndo_change_mtu =	ip6_tnl_change_mtu,
192362306a36Sopenharmony_ci	.ndo_get_stats64 =	dev_get_tstats64,
192462306a36Sopenharmony_ci	.ndo_get_iflink =	ip6_tnl_get_iflink,
192562306a36Sopenharmony_ci};
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_cistatic void ip6gre_tap_setup(struct net_device *dev)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	ether_setup(dev);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	dev->max_mtu = 0;
193362306a36Sopenharmony_ci	dev->netdev_ops = &ip6gre_tap_netdev_ops;
193462306a36Sopenharmony_ci	dev->needs_free_netdev = true;
193562306a36Sopenharmony_ci	dev->priv_destructor = ip6gre_dev_free;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
193862306a36Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
193962306a36Sopenharmony_ci	netif_keep_dst(dev);
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic bool ip6gre_netlink_encap_parms(struct nlattr *data[],
194362306a36Sopenharmony_ci				       struct ip_tunnel_encap *ipencap)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	bool ret = false;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	memset(ipencap, 0, sizeof(*ipencap));
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	if (!data)
195062306a36Sopenharmony_ci		return ret;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_TYPE]) {
195362306a36Sopenharmony_ci		ret = true;
195462306a36Sopenharmony_ci		ipencap->type = nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]);
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_FLAGS]) {
195862306a36Sopenharmony_ci		ret = true;
195962306a36Sopenharmony_ci		ipencap->flags = nla_get_u16(data[IFLA_GRE_ENCAP_FLAGS]);
196062306a36Sopenharmony_ci	}
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_SPORT]) {
196362306a36Sopenharmony_ci		ret = true;
196462306a36Sopenharmony_ci		ipencap->sport = nla_get_be16(data[IFLA_GRE_ENCAP_SPORT]);
196562306a36Sopenharmony_ci	}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_DPORT]) {
196862306a36Sopenharmony_ci		ret = true;
196962306a36Sopenharmony_ci		ipencap->dport = nla_get_be16(data[IFLA_GRE_ENCAP_DPORT]);
197062306a36Sopenharmony_ci	}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	return ret;
197362306a36Sopenharmony_ci}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_cistatic int ip6gre_newlink_common(struct net *src_net, struct net_device *dev,
197662306a36Sopenharmony_ci				 struct nlattr *tb[], struct nlattr *data[],
197762306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
197862306a36Sopenharmony_ci{
197962306a36Sopenharmony_ci	struct ip6_tnl *nt;
198062306a36Sopenharmony_ci	struct ip_tunnel_encap ipencap;
198162306a36Sopenharmony_ci	int err;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	nt = netdev_priv(dev);
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	if (ip6gre_netlink_encap_parms(data, &ipencap)) {
198662306a36Sopenharmony_ci		int err = ip6_tnl_encap_setup(nt, &ipencap);
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci		if (err < 0)
198962306a36Sopenharmony_ci			return err;
199062306a36Sopenharmony_ci	}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
199362306a36Sopenharmony_ci		eth_hw_addr_random(dev);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	nt->dev = dev;
199662306a36Sopenharmony_ci	nt->net = dev_net(dev);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	err = register_netdevice(dev);
199962306a36Sopenharmony_ci	if (err)
200062306a36Sopenharmony_ci		goto out;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (tb[IFLA_MTU])
200362306a36Sopenharmony_ci		ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ciout:
200662306a36Sopenharmony_ci	return err;
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_cistatic int ip6gre_newlink(struct net *src_net, struct net_device *dev,
201062306a36Sopenharmony_ci			  struct nlattr *tb[], struct nlattr *data[],
201162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
201262306a36Sopenharmony_ci{
201362306a36Sopenharmony_ci	struct ip6_tnl *nt = netdev_priv(dev);
201462306a36Sopenharmony_ci	struct net *net = dev_net(dev);
201562306a36Sopenharmony_ci	struct ip6gre_net *ign;
201662306a36Sopenharmony_ci	int err;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	ip6gre_netlink_parms(data, &nt->parms);
201962306a36Sopenharmony_ci	ign = net_generic(net, ip6gre_net_id);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	if (nt->parms.collect_md) {
202262306a36Sopenharmony_ci		if (rtnl_dereference(ign->collect_md_tun))
202362306a36Sopenharmony_ci			return -EEXIST;
202462306a36Sopenharmony_ci	} else {
202562306a36Sopenharmony_ci		if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
202662306a36Sopenharmony_ci			return -EEXIST;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
203062306a36Sopenharmony_ci	if (!err) {
203162306a36Sopenharmony_ci		ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
203262306a36Sopenharmony_ci		ip6gre_tunnel_link_md(ign, nt);
203362306a36Sopenharmony_ci		ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
203462306a36Sopenharmony_ci	}
203562306a36Sopenharmony_ci	return err;
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic struct ip6_tnl *
203962306a36Sopenharmony_ciip6gre_changelink_common(struct net_device *dev, struct nlattr *tb[],
204062306a36Sopenharmony_ci			 struct nlattr *data[], struct __ip6_tnl_parm *p_p,
204162306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
204262306a36Sopenharmony_ci{
204362306a36Sopenharmony_ci	struct ip6_tnl *t, *nt = netdev_priv(dev);
204462306a36Sopenharmony_ci	struct net *net = nt->net;
204562306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
204662306a36Sopenharmony_ci	struct ip_tunnel_encap ipencap;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	if (dev == ign->fb_tunnel_dev)
204962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (ip6gre_netlink_encap_parms(data, &ipencap)) {
205262306a36Sopenharmony_ci		int err = ip6_tnl_encap_setup(nt, &ipencap);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci		if (err < 0)
205562306a36Sopenharmony_ci			return ERR_PTR(err);
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	ip6gre_netlink_parms(data, p_p);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	t = ip6gre_tunnel_locate(net, p_p, 0);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	if (t) {
206362306a36Sopenharmony_ci		if (t->dev != dev)
206462306a36Sopenharmony_ci			return ERR_PTR(-EEXIST);
206562306a36Sopenharmony_ci	} else {
206662306a36Sopenharmony_ci		t = nt;
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	return t;
207062306a36Sopenharmony_ci}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_cistatic int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
207362306a36Sopenharmony_ci			     struct nlattr *data[],
207462306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
207562306a36Sopenharmony_ci{
207662306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
207762306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
207862306a36Sopenharmony_ci	struct __ip6_tnl_parm p;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
208162306a36Sopenharmony_ci	if (IS_ERR(t))
208262306a36Sopenharmony_ci		return PTR_ERR(t);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	ip6gre_tunnel_unlink_md(ign, t);
208562306a36Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
208662306a36Sopenharmony_ci	ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
208762306a36Sopenharmony_ci	ip6gre_tunnel_link_md(ign, t);
208862306a36Sopenharmony_ci	ip6gre_tunnel_link(ign, t);
208962306a36Sopenharmony_ci	return 0;
209062306a36Sopenharmony_ci}
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_cistatic void ip6gre_dellink(struct net_device *dev, struct list_head *head)
209362306a36Sopenharmony_ci{
209462306a36Sopenharmony_ci	struct net *net = dev_net(dev);
209562306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	if (dev != ign->fb_tunnel_dev)
209862306a36Sopenharmony_ci		unregister_netdevice_queue(dev, head);
209962306a36Sopenharmony_ci}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_cistatic size_t ip6gre_get_size(const struct net_device *dev)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	return
210462306a36Sopenharmony_ci		/* IFLA_GRE_LINK */
210562306a36Sopenharmony_ci		nla_total_size(4) +
210662306a36Sopenharmony_ci		/* IFLA_GRE_IFLAGS */
210762306a36Sopenharmony_ci		nla_total_size(2) +
210862306a36Sopenharmony_ci		/* IFLA_GRE_OFLAGS */
210962306a36Sopenharmony_ci		nla_total_size(2) +
211062306a36Sopenharmony_ci		/* IFLA_GRE_IKEY */
211162306a36Sopenharmony_ci		nla_total_size(4) +
211262306a36Sopenharmony_ci		/* IFLA_GRE_OKEY */
211362306a36Sopenharmony_ci		nla_total_size(4) +
211462306a36Sopenharmony_ci		/* IFLA_GRE_LOCAL */
211562306a36Sopenharmony_ci		nla_total_size(sizeof(struct in6_addr)) +
211662306a36Sopenharmony_ci		/* IFLA_GRE_REMOTE */
211762306a36Sopenharmony_ci		nla_total_size(sizeof(struct in6_addr)) +
211862306a36Sopenharmony_ci		/* IFLA_GRE_TTL */
211962306a36Sopenharmony_ci		nla_total_size(1) +
212062306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_LIMIT */
212162306a36Sopenharmony_ci		nla_total_size(1) +
212262306a36Sopenharmony_ci		/* IFLA_GRE_FLOWINFO */
212362306a36Sopenharmony_ci		nla_total_size(4) +
212462306a36Sopenharmony_ci		/* IFLA_GRE_FLAGS */
212562306a36Sopenharmony_ci		nla_total_size(4) +
212662306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_TYPE */
212762306a36Sopenharmony_ci		nla_total_size(2) +
212862306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_FLAGS */
212962306a36Sopenharmony_ci		nla_total_size(2) +
213062306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_SPORT */
213162306a36Sopenharmony_ci		nla_total_size(2) +
213262306a36Sopenharmony_ci		/* IFLA_GRE_ENCAP_DPORT */
213362306a36Sopenharmony_ci		nla_total_size(2) +
213462306a36Sopenharmony_ci		/* IFLA_GRE_COLLECT_METADATA */
213562306a36Sopenharmony_ci		nla_total_size(0) +
213662306a36Sopenharmony_ci		/* IFLA_GRE_FWMARK */
213762306a36Sopenharmony_ci		nla_total_size(4) +
213862306a36Sopenharmony_ci		/* IFLA_GRE_ERSPAN_INDEX */
213962306a36Sopenharmony_ci		nla_total_size(4) +
214062306a36Sopenharmony_ci		0;
214162306a36Sopenharmony_ci}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_cistatic int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
214462306a36Sopenharmony_ci{
214562306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
214662306a36Sopenharmony_ci	struct __ip6_tnl_parm *p = &t->parms;
214762306a36Sopenharmony_ci	__be16 o_flags = p->o_flags;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	if (p->erspan_ver == 1 || p->erspan_ver == 2) {
215062306a36Sopenharmony_ci		if (!p->collect_md)
215162306a36Sopenharmony_ci			o_flags |= TUNNEL_KEY;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci		if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver))
215462306a36Sopenharmony_ci			goto nla_put_failure;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci		if (p->erspan_ver == 1) {
215762306a36Sopenharmony_ci			if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
215862306a36Sopenharmony_ci				goto nla_put_failure;
215962306a36Sopenharmony_ci		} else {
216062306a36Sopenharmony_ci			if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir))
216162306a36Sopenharmony_ci				goto nla_put_failure;
216262306a36Sopenharmony_ci			if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid))
216362306a36Sopenharmony_ci				goto nla_put_failure;
216462306a36Sopenharmony_ci		}
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
216862306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_IFLAGS,
216962306a36Sopenharmony_ci			 gre_tnl_flags_to_gre_flags(p->i_flags)) ||
217062306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_OFLAGS,
217162306a36Sopenharmony_ci			 gre_tnl_flags_to_gre_flags(o_flags)) ||
217262306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
217362306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
217462306a36Sopenharmony_ci	    nla_put_in6_addr(skb, IFLA_GRE_LOCAL, &p->laddr) ||
217562306a36Sopenharmony_ci	    nla_put_in6_addr(skb, IFLA_GRE_REMOTE, &p->raddr) ||
217662306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) ||
217762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
217862306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) ||
217962306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags) ||
218062306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark))
218162306a36Sopenharmony_ci		goto nla_put_failure;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE,
218462306a36Sopenharmony_ci			t->encap.type) ||
218562306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_ENCAP_SPORT,
218662306a36Sopenharmony_ci			 t->encap.sport) ||
218762306a36Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_ENCAP_DPORT,
218862306a36Sopenharmony_ci			 t->encap.dport) ||
218962306a36Sopenharmony_ci	    nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS,
219062306a36Sopenharmony_ci			t->encap.flags))
219162306a36Sopenharmony_ci		goto nla_put_failure;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	if (p->collect_md) {
219462306a36Sopenharmony_ci		if (nla_put_flag(skb, IFLA_GRE_COLLECT_METADATA))
219562306a36Sopenharmony_ci			goto nla_put_failure;
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	return 0;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_cinla_put_failure:
220162306a36Sopenharmony_ci	return -EMSGSIZE;
220262306a36Sopenharmony_ci}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_cistatic const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = {
220562306a36Sopenharmony_ci	[IFLA_GRE_LINK]        = { .type = NLA_U32 },
220662306a36Sopenharmony_ci	[IFLA_GRE_IFLAGS]      = { .type = NLA_U16 },
220762306a36Sopenharmony_ci	[IFLA_GRE_OFLAGS]      = { .type = NLA_U16 },
220862306a36Sopenharmony_ci	[IFLA_GRE_IKEY]        = { .type = NLA_U32 },
220962306a36Sopenharmony_ci	[IFLA_GRE_OKEY]        = { .type = NLA_U32 },
221062306a36Sopenharmony_ci	[IFLA_GRE_LOCAL]       = { .len = sizeof_field(struct ipv6hdr, saddr) },
221162306a36Sopenharmony_ci	[IFLA_GRE_REMOTE]      = { .len = sizeof_field(struct ipv6hdr, daddr) },
221262306a36Sopenharmony_ci	[IFLA_GRE_TTL]         = { .type = NLA_U8 },
221362306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
221462306a36Sopenharmony_ci	[IFLA_GRE_FLOWINFO]    = { .type = NLA_U32 },
221562306a36Sopenharmony_ci	[IFLA_GRE_FLAGS]       = { .type = NLA_U32 },
221662306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_TYPE]   = { .type = NLA_U16 },
221762306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_FLAGS]  = { .type = NLA_U16 },
221862306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_SPORT]  = { .type = NLA_U16 },
221962306a36Sopenharmony_ci	[IFLA_GRE_ENCAP_DPORT]  = { .type = NLA_U16 },
222062306a36Sopenharmony_ci	[IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG },
222162306a36Sopenharmony_ci	[IFLA_GRE_FWMARK]       = { .type = NLA_U32 },
222262306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 },
222362306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_VER]	= { .type = NLA_U8 },
222462306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_DIR]	= { .type = NLA_U8 },
222562306a36Sopenharmony_ci	[IFLA_GRE_ERSPAN_HWID]	= { .type = NLA_U16 },
222662306a36Sopenharmony_ci};
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_cistatic void ip6erspan_tap_setup(struct net_device *dev)
222962306a36Sopenharmony_ci{
223062306a36Sopenharmony_ci	ether_setup(dev);
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	dev->max_mtu = 0;
223362306a36Sopenharmony_ci	dev->netdev_ops = &ip6erspan_netdev_ops;
223462306a36Sopenharmony_ci	dev->needs_free_netdev = true;
223562306a36Sopenharmony_ci	dev->priv_destructor = ip6gre_dev_free;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
223862306a36Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
223962306a36Sopenharmony_ci	netif_keep_dst(dev);
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_cistatic int ip6erspan_newlink(struct net *src_net, struct net_device *dev,
224362306a36Sopenharmony_ci			     struct nlattr *tb[], struct nlattr *data[],
224462306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	struct ip6_tnl *nt = netdev_priv(dev);
224762306a36Sopenharmony_ci	struct net *net = dev_net(dev);
224862306a36Sopenharmony_ci	struct ip6gre_net *ign;
224962306a36Sopenharmony_ci	int err;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	ip6gre_netlink_parms(data, &nt->parms);
225262306a36Sopenharmony_ci	ip6erspan_set_version(data, &nt->parms);
225362306a36Sopenharmony_ci	ign = net_generic(net, ip6gre_net_id);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	if (nt->parms.collect_md) {
225662306a36Sopenharmony_ci		if (rtnl_dereference(ign->collect_md_tun_erspan))
225762306a36Sopenharmony_ci			return -EEXIST;
225862306a36Sopenharmony_ci	} else {
225962306a36Sopenharmony_ci		if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
226062306a36Sopenharmony_ci			return -EEXIST;
226162306a36Sopenharmony_ci	}
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
226462306a36Sopenharmony_ci	if (!err) {
226562306a36Sopenharmony_ci		ip6erspan_tnl_link_config(nt, !tb[IFLA_MTU]);
226662306a36Sopenharmony_ci		ip6erspan_tunnel_link_md(ign, nt);
226762306a36Sopenharmony_ci		ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
226862306a36Sopenharmony_ci	}
226962306a36Sopenharmony_ci	return err;
227062306a36Sopenharmony_ci}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_cistatic void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	ip6gre_tnl_link_config_common(t);
227562306a36Sopenharmony_ci	ip6gre_tnl_link_config_route(t, set_mtu, ip6erspan_calc_hlen(t));
227662306a36Sopenharmony_ci}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_cistatic int ip6erspan_tnl_change(struct ip6_tnl *t,
227962306a36Sopenharmony_ci				const struct __ip6_tnl_parm *p, int set_mtu)
228062306a36Sopenharmony_ci{
228162306a36Sopenharmony_ci	ip6gre_tnl_copy_tnl_parm(t, p);
228262306a36Sopenharmony_ci	ip6erspan_tnl_link_config(t, set_mtu);
228362306a36Sopenharmony_ci	return 0;
228462306a36Sopenharmony_ci}
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_cistatic int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
228762306a36Sopenharmony_ci				struct nlattr *data[],
228862306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
228962306a36Sopenharmony_ci{
229062306a36Sopenharmony_ci	struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id);
229162306a36Sopenharmony_ci	struct __ip6_tnl_parm p;
229262306a36Sopenharmony_ci	struct ip6_tnl *t;
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
229562306a36Sopenharmony_ci	if (IS_ERR(t))
229662306a36Sopenharmony_ci		return PTR_ERR(t);
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	ip6erspan_set_version(data, &p);
229962306a36Sopenharmony_ci	ip6gre_tunnel_unlink_md(ign, t);
230062306a36Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
230162306a36Sopenharmony_ci	ip6erspan_tnl_change(t, &p, !tb[IFLA_MTU]);
230262306a36Sopenharmony_ci	ip6erspan_tunnel_link_md(ign, t);
230362306a36Sopenharmony_ci	ip6gre_tunnel_link(ign, t);
230462306a36Sopenharmony_ci	return 0;
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_cistatic struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
230862306a36Sopenharmony_ci	.kind		= "ip6gre",
230962306a36Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
231062306a36Sopenharmony_ci	.policy		= ip6gre_policy,
231162306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
231262306a36Sopenharmony_ci	.setup		= ip6gre_tunnel_setup,
231362306a36Sopenharmony_ci	.validate	= ip6gre_tunnel_validate,
231462306a36Sopenharmony_ci	.newlink	= ip6gre_newlink,
231562306a36Sopenharmony_ci	.changelink	= ip6gre_changelink,
231662306a36Sopenharmony_ci	.dellink	= ip6gre_dellink,
231762306a36Sopenharmony_ci	.get_size	= ip6gre_get_size,
231862306a36Sopenharmony_ci	.fill_info	= ip6gre_fill_info,
231962306a36Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
232062306a36Sopenharmony_ci};
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_cistatic struct rtnl_link_ops ip6gre_tap_ops __read_mostly = {
232362306a36Sopenharmony_ci	.kind		= "ip6gretap",
232462306a36Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
232562306a36Sopenharmony_ci	.policy		= ip6gre_policy,
232662306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
232762306a36Sopenharmony_ci	.setup		= ip6gre_tap_setup,
232862306a36Sopenharmony_ci	.validate	= ip6gre_tap_validate,
232962306a36Sopenharmony_ci	.newlink	= ip6gre_newlink,
233062306a36Sopenharmony_ci	.changelink	= ip6gre_changelink,
233162306a36Sopenharmony_ci	.get_size	= ip6gre_get_size,
233262306a36Sopenharmony_ci	.fill_info	= ip6gre_fill_info,
233362306a36Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
233462306a36Sopenharmony_ci};
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_cistatic struct rtnl_link_ops ip6erspan_tap_ops __read_mostly = {
233762306a36Sopenharmony_ci	.kind		= "ip6erspan",
233862306a36Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
233962306a36Sopenharmony_ci	.policy		= ip6gre_policy,
234062306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
234162306a36Sopenharmony_ci	.setup		= ip6erspan_tap_setup,
234262306a36Sopenharmony_ci	.validate	= ip6erspan_tap_validate,
234362306a36Sopenharmony_ci	.newlink	= ip6erspan_newlink,
234462306a36Sopenharmony_ci	.changelink	= ip6erspan_changelink,
234562306a36Sopenharmony_ci	.get_size	= ip6gre_get_size,
234662306a36Sopenharmony_ci	.fill_info	= ip6gre_fill_info,
234762306a36Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
234862306a36Sopenharmony_ci};
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci/*
235162306a36Sopenharmony_ci *	And now the modules code and kernel interface.
235262306a36Sopenharmony_ci */
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_cistatic int __init ip6gre_init(void)
235562306a36Sopenharmony_ci{
235662306a36Sopenharmony_ci	int err;
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	pr_info("GRE over IPv6 tunneling driver\n");
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	err = register_pernet_device(&ip6gre_net_ops);
236162306a36Sopenharmony_ci	if (err < 0)
236262306a36Sopenharmony_ci		return err;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE);
236562306a36Sopenharmony_ci	if (err < 0) {
236662306a36Sopenharmony_ci		pr_info("%s: can't add protocol\n", __func__);
236762306a36Sopenharmony_ci		goto add_proto_failed;
236862306a36Sopenharmony_ci	}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	err = rtnl_link_register(&ip6gre_link_ops);
237162306a36Sopenharmony_ci	if (err < 0)
237262306a36Sopenharmony_ci		goto rtnl_link_failed;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	err = rtnl_link_register(&ip6gre_tap_ops);
237562306a36Sopenharmony_ci	if (err < 0)
237662306a36Sopenharmony_ci		goto tap_ops_failed;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	err = rtnl_link_register(&ip6erspan_tap_ops);
237962306a36Sopenharmony_ci	if (err < 0)
238062306a36Sopenharmony_ci		goto erspan_link_failed;
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ciout:
238362306a36Sopenharmony_ci	return err;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_cierspan_link_failed:
238662306a36Sopenharmony_ci	rtnl_link_unregister(&ip6gre_tap_ops);
238762306a36Sopenharmony_citap_ops_failed:
238862306a36Sopenharmony_ci	rtnl_link_unregister(&ip6gre_link_ops);
238962306a36Sopenharmony_cirtnl_link_failed:
239062306a36Sopenharmony_ci	inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
239162306a36Sopenharmony_ciadd_proto_failed:
239262306a36Sopenharmony_ci	unregister_pernet_device(&ip6gre_net_ops);
239362306a36Sopenharmony_ci	goto out;
239462306a36Sopenharmony_ci}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_cistatic void __exit ip6gre_fini(void)
239762306a36Sopenharmony_ci{
239862306a36Sopenharmony_ci	rtnl_link_unregister(&ip6gre_tap_ops);
239962306a36Sopenharmony_ci	rtnl_link_unregister(&ip6gre_link_ops);
240062306a36Sopenharmony_ci	rtnl_link_unregister(&ip6erspan_tap_ops);
240162306a36Sopenharmony_ci	inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
240262306a36Sopenharmony_ci	unregister_pernet_device(&ip6gre_net_ops);
240362306a36Sopenharmony_ci}
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_cimodule_init(ip6gre_init);
240662306a36Sopenharmony_cimodule_exit(ip6gre_fini);
240762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
240862306a36Sopenharmony_ciMODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
240962306a36Sopenharmony_ciMODULE_DESCRIPTION("GRE over IPv6 tunneling device");
241062306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6gre");
241162306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6gretap");
241262306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6erspan");
241362306a36Sopenharmony_ciMODULE_ALIAS_NETDEV("ip6gre0");
2414