18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	GRE over IPv6 protocol decoder.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Authors: Dmitry Kozlov (xeb@mail.ru)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/capability.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
168c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
178c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
188c2ecf20Sopenharmony_ci#include <linux/in.h>
198c2ecf20Sopenharmony_ci#include <linux/tcp.h>
208c2ecf20Sopenharmony_ci#include <linux/udp.h>
218c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
228c2ecf20Sopenharmony_ci#include <linux/init.h>
238c2ecf20Sopenharmony_ci#include <linux/in6.h>
248c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
258c2ecf20Sopenharmony_ci#include <linux/igmp.h>
268c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv4.h>
278c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
288c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
298c2ecf20Sopenharmony_ci#include <linux/hash.h>
308c2ecf20Sopenharmony_ci#include <linux/if_tunnel.h>
318c2ecf20Sopenharmony_ci#include <linux/ip6_tunnel.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <net/sock.h>
348c2ecf20Sopenharmony_ci#include <net/ip.h>
358c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
368c2ecf20Sopenharmony_ci#include <net/icmp.h>
378c2ecf20Sopenharmony_ci#include <net/protocol.h>
388c2ecf20Sopenharmony_ci#include <net/addrconf.h>
398c2ecf20Sopenharmony_ci#include <net/arp.h>
408c2ecf20Sopenharmony_ci#include <net/checksum.h>
418c2ecf20Sopenharmony_ci#include <net/dsfield.h>
428c2ecf20Sopenharmony_ci#include <net/inet_ecn.h>
438c2ecf20Sopenharmony_ci#include <net/xfrm.h>
448c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
458c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
468c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#include <net/ipv6.h>
498c2ecf20Sopenharmony_ci#include <net/ip6_fib.h>
508c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
518c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h>
528c2ecf20Sopenharmony_ci#include <net/gre.h>
538c2ecf20Sopenharmony_ci#include <net/erspan.h>
548c2ecf20Sopenharmony_ci#include <net/dst_metadata.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic bool log_ecn_error = true;
588c2ecf20Sopenharmony_cimodule_param(log_ecn_error, bool, 0644);
598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define IP6_GRE_HASH_SIZE_SHIFT  5
628c2ecf20Sopenharmony_ci#define IP6_GRE_HASH_SIZE (1 << IP6_GRE_HASH_SIZE_SHIFT)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic unsigned int ip6gre_net_id __read_mostly;
658c2ecf20Sopenharmony_cistruct ip6gre_net {
668c2ecf20Sopenharmony_ci	struct ip6_tnl __rcu *tunnels[4][IP6_GRE_HASH_SIZE];
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	struct ip6_tnl __rcu *collect_md_tun;
698c2ecf20Sopenharmony_ci	struct ip6_tnl __rcu *collect_md_tun_erspan;
708c2ecf20Sopenharmony_ci	struct net_device *fb_tunnel_dev;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_link_ops __read_mostly;
748c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_tap_ops __read_mostly;
758c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6erspan_tap_ops __read_mostly;
768c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_init(struct net_device *dev);
778c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_setup(struct net_device *dev);
788c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
798c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu);
808c2ecf20Sopenharmony_cistatic void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* Tunnel hash table */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci   4 hash tables:
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci   3: (remote,local)
888c2ecf20Sopenharmony_ci   2: (remote,*)
898c2ecf20Sopenharmony_ci   1: (*,local)
908c2ecf20Sopenharmony_ci   0: (*,*)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci   We require exact key match i.e. if a key is present in packet
938c2ecf20Sopenharmony_ci   it will match only tunnel with the same key; if it is not present,
948c2ecf20Sopenharmony_ci   it will match only keyless tunnel.
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci   All keysless packets, if not matched configured keyless tunnels
978c2ecf20Sopenharmony_ci   will match fallback tunnel.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(IP6_GRE_HASH_SIZE - 1))
1018c2ecf20Sopenharmony_cistatic u32 HASH_ADDR(const struct in6_addr *addr)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	u32 hash = ipv6_addr_hash(addr);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return hash_32(hash, IP6_GRE_HASH_SIZE_SHIFT);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#define tunnels_r_l	tunnels[3]
1098c2ecf20Sopenharmony_ci#define tunnels_r	tunnels[2]
1108c2ecf20Sopenharmony_ci#define tunnels_l	tunnels[1]
1118c2ecf20Sopenharmony_ci#define tunnels_wc	tunnels[0]
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Given src, dst and key, find appropriate for input tunnel. */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
1168c2ecf20Sopenharmony_ci		const struct in6_addr *remote, const struct in6_addr *local,
1178c2ecf20Sopenharmony_ci		__be32 key, __be16 gre_proto)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
1208c2ecf20Sopenharmony_ci	int link = dev->ifindex;
1218c2ecf20Sopenharmony_ci	unsigned int h0 = HASH_ADDR(remote);
1228c2ecf20Sopenharmony_ci	unsigned int h1 = HASH_KEY(key);
1238c2ecf20Sopenharmony_ci	struct ip6_tnl *t, *cand = NULL;
1248c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
1258c2ecf20Sopenharmony_ci	int dev_type = (gre_proto == htons(ETH_P_TEB) ||
1268c2ecf20Sopenharmony_ci			gre_proto == htons(ETH_P_ERSPAN) ||
1278c2ecf20Sopenharmony_ci			gre_proto == htons(ETH_P_ERSPAN2)) ?
1288c2ecf20Sopenharmony_ci		       ARPHRD_ETHER : ARPHRD_IP6GRE;
1298c2ecf20Sopenharmony_ci	int score, cand_score = 4;
1308c2ecf20Sopenharmony_ci	struct net_device *ndev;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) {
1338c2ecf20Sopenharmony_ci		if (!ipv6_addr_equal(local, &t->parms.laddr) ||
1348c2ecf20Sopenharmony_ci		    !ipv6_addr_equal(remote, &t->parms.raddr) ||
1358c2ecf20Sopenharmony_ci		    key != t->parms.i_key ||
1368c2ecf20Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
1378c2ecf20Sopenharmony_ci			continue;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
1408c2ecf20Sopenharmony_ci		    t->dev->type != dev_type)
1418c2ecf20Sopenharmony_ci			continue;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		score = 0;
1448c2ecf20Sopenharmony_ci		if (t->parms.link != link)
1458c2ecf20Sopenharmony_ci			score |= 1;
1468c2ecf20Sopenharmony_ci		if (t->dev->type != dev_type)
1478c2ecf20Sopenharmony_ci			score |= 2;
1488c2ecf20Sopenharmony_ci		if (score == 0)
1498c2ecf20Sopenharmony_ci			return t;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		if (score < cand_score) {
1528c2ecf20Sopenharmony_ci			cand = t;
1538c2ecf20Sopenharmony_ci			cand_score = score;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) {
1588c2ecf20Sopenharmony_ci		if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
1598c2ecf20Sopenharmony_ci		    key != t->parms.i_key ||
1608c2ecf20Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
1618c2ecf20Sopenharmony_ci			continue;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
1648c2ecf20Sopenharmony_ci		    t->dev->type != dev_type)
1658c2ecf20Sopenharmony_ci			continue;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		score = 0;
1688c2ecf20Sopenharmony_ci		if (t->parms.link != link)
1698c2ecf20Sopenharmony_ci			score |= 1;
1708c2ecf20Sopenharmony_ci		if (t->dev->type != dev_type)
1718c2ecf20Sopenharmony_ci			score |= 2;
1728c2ecf20Sopenharmony_ci		if (score == 0)
1738c2ecf20Sopenharmony_ci			return t;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if (score < cand_score) {
1768c2ecf20Sopenharmony_ci			cand = t;
1778c2ecf20Sopenharmony_ci			cand_score = score;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) {
1828c2ecf20Sopenharmony_ci		if ((!ipv6_addr_equal(local, &t->parms.laddr) &&
1838c2ecf20Sopenharmony_ci			  (!ipv6_addr_equal(local, &t->parms.raddr) ||
1848c2ecf20Sopenharmony_ci				 !ipv6_addr_is_multicast(local))) ||
1858c2ecf20Sopenharmony_ci		    key != t->parms.i_key ||
1868c2ecf20Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
1878c2ecf20Sopenharmony_ci			continue;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
1908c2ecf20Sopenharmony_ci		    t->dev->type != dev_type)
1918c2ecf20Sopenharmony_ci			continue;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		score = 0;
1948c2ecf20Sopenharmony_ci		if (t->parms.link != link)
1958c2ecf20Sopenharmony_ci			score |= 1;
1968c2ecf20Sopenharmony_ci		if (t->dev->type != dev_type)
1978c2ecf20Sopenharmony_ci			score |= 2;
1988c2ecf20Sopenharmony_ci		if (score == 0)
1998c2ecf20Sopenharmony_ci			return t;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		if (score < cand_score) {
2028c2ecf20Sopenharmony_ci			cand = t;
2038c2ecf20Sopenharmony_ci			cand_score = score;
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) {
2088c2ecf20Sopenharmony_ci		if (t->parms.i_key != key ||
2098c2ecf20Sopenharmony_ci		    !(t->dev->flags & IFF_UP))
2108c2ecf20Sopenharmony_ci			continue;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (t->dev->type != ARPHRD_IP6GRE &&
2138c2ecf20Sopenharmony_ci		    t->dev->type != dev_type)
2148c2ecf20Sopenharmony_ci			continue;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		score = 0;
2178c2ecf20Sopenharmony_ci		if (t->parms.link != link)
2188c2ecf20Sopenharmony_ci			score |= 1;
2198c2ecf20Sopenharmony_ci		if (t->dev->type != dev_type)
2208c2ecf20Sopenharmony_ci			score |= 2;
2218c2ecf20Sopenharmony_ci		if (score == 0)
2228c2ecf20Sopenharmony_ci			return t;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		if (score < cand_score) {
2258c2ecf20Sopenharmony_ci			cand = t;
2268c2ecf20Sopenharmony_ci			cand_score = score;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (cand)
2318c2ecf20Sopenharmony_ci		return cand;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (gre_proto == htons(ETH_P_ERSPAN) ||
2348c2ecf20Sopenharmony_ci	    gre_proto == htons(ETH_P_ERSPAN2))
2358c2ecf20Sopenharmony_ci		t = rcu_dereference(ign->collect_md_tun_erspan);
2368c2ecf20Sopenharmony_ci	else
2378c2ecf20Sopenharmony_ci		t = rcu_dereference(ign->collect_md_tun);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (t && t->dev->flags & IFF_UP)
2408c2ecf20Sopenharmony_ci		return t;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ndev = READ_ONCE(ign->fb_tunnel_dev);
2438c2ecf20Sopenharmony_ci	if (ndev && ndev->flags & IFF_UP)
2448c2ecf20Sopenharmony_ci		return netdev_priv(ndev);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return NULL;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign,
2508c2ecf20Sopenharmony_ci		const struct __ip6_tnl_parm *p)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	const struct in6_addr *remote = &p->raddr;
2538c2ecf20Sopenharmony_ci	const struct in6_addr *local = &p->laddr;
2548c2ecf20Sopenharmony_ci	unsigned int h = HASH_KEY(p->i_key);
2558c2ecf20Sopenharmony_ci	int prio = 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(local))
2588c2ecf20Sopenharmony_ci		prio |= 1;
2598c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) {
2608c2ecf20Sopenharmony_ci		prio |= 2;
2618c2ecf20Sopenharmony_ci		h ^= HASH_ADDR(remote);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return &ign->tunnels[prio][h];
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	if (t->parms.collect_md)
2708c2ecf20Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun, t);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void ip6erspan_tunnel_link_md(struct ip6gre_net *ign, struct ip6_tnl *t)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	if (t->parms.collect_md)
2768c2ecf20Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun_erspan, t);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_unlink_md(struct ip6gre_net *ign, struct ip6_tnl *t)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	if (t->parms.collect_md)
2828c2ecf20Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun, NULL);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void ip6erspan_tunnel_unlink_md(struct ip6gre_net *ign,
2868c2ecf20Sopenharmony_ci				       struct ip6_tnl *t)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	if (t->parms.collect_md)
2898c2ecf20Sopenharmony_ci		rcu_assign_pointer(ign->collect_md_tun_erspan, NULL);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign,
2938c2ecf20Sopenharmony_ci		const struct ip6_tnl *t)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	return __ip6gre_bucket(ign, &t->parms);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	rcu_assign_pointer(t->next, rtnl_dereference(*tp));
3038c2ecf20Sopenharmony_ci	rcu_assign_pointer(*tp, t);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct ip6_tnl __rcu **tp;
3098c2ecf20Sopenharmony_ci	struct ip6_tnl *iter;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	for (tp = ip6gre_bucket(ign, t);
3128c2ecf20Sopenharmony_ci	     (iter = rtnl_dereference(*tp)) != NULL;
3138c2ecf20Sopenharmony_ci	     tp = &iter->next) {
3148c2ecf20Sopenharmony_ci		if (t == iter) {
3158c2ecf20Sopenharmony_ci			rcu_assign_pointer(*tp, t->next);
3168c2ecf20Sopenharmony_ci			break;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_find(struct net *net,
3228c2ecf20Sopenharmony_ci					   const struct __ip6_tnl_parm *parms,
3238c2ecf20Sopenharmony_ci					   int type)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	const struct in6_addr *remote = &parms->raddr;
3268c2ecf20Sopenharmony_ci	const struct in6_addr *local = &parms->laddr;
3278c2ecf20Sopenharmony_ci	__be32 key = parms->i_key;
3288c2ecf20Sopenharmony_ci	int link = parms->link;
3298c2ecf20Sopenharmony_ci	struct ip6_tnl *t;
3308c2ecf20Sopenharmony_ci	struct ip6_tnl __rcu **tp;
3318c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	for (tp = __ip6gre_bucket(ign, parms);
3348c2ecf20Sopenharmony_ci	     (t = rtnl_dereference(*tp)) != NULL;
3358c2ecf20Sopenharmony_ci	     tp = &t->next)
3368c2ecf20Sopenharmony_ci		if (ipv6_addr_equal(local, &t->parms.laddr) &&
3378c2ecf20Sopenharmony_ci		    ipv6_addr_equal(remote, &t->parms.raddr) &&
3388c2ecf20Sopenharmony_ci		    key == t->parms.i_key &&
3398c2ecf20Sopenharmony_ci		    link == t->parms.link &&
3408c2ecf20Sopenharmony_ci		    type == t->dev->type)
3418c2ecf20Sopenharmony_ci			break;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return t;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic struct ip6_tnl *ip6gre_tunnel_locate(struct net *net,
3478c2ecf20Sopenharmony_ci		const struct __ip6_tnl_parm *parms, int create)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct ip6_tnl *t, *nt;
3508c2ecf20Sopenharmony_ci	struct net_device *dev;
3518c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
3528c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE);
3558c2ecf20Sopenharmony_ci	if (t && create)
3568c2ecf20Sopenharmony_ci		return NULL;
3578c2ecf20Sopenharmony_ci	if (t || !create)
3588c2ecf20Sopenharmony_ci		return t;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (parms->name[0]) {
3618c2ecf20Sopenharmony_ci		if (!dev_valid_name(parms->name))
3628c2ecf20Sopenharmony_ci			return NULL;
3638c2ecf20Sopenharmony_ci		strlcpy(name, parms->name, IFNAMSIZ);
3648c2ecf20Sopenharmony_ci	} else {
3658c2ecf20Sopenharmony_ci		strcpy(name, "ip6gre%d");
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci	dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN,
3688c2ecf20Sopenharmony_ci			   ip6gre_tunnel_setup);
3698c2ecf20Sopenharmony_ci	if (!dev)
3708c2ecf20Sopenharmony_ci		return NULL;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	dev_net_set(dev, net);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	nt = netdev_priv(dev);
3758c2ecf20Sopenharmony_ci	nt->parms = *parms;
3768c2ecf20Sopenharmony_ci	dev->rtnl_link_ops = &ip6gre_link_ops;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	nt->dev = dev;
3798c2ecf20Sopenharmony_ci	nt->net = dev_net(dev);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (register_netdevice(dev) < 0)
3828c2ecf20Sopenharmony_ci		goto failed_free;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	ip6gre_tnl_link_config(nt, 1);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Can use a lockless transmit, unless we generate output sequences */
3878c2ecf20Sopenharmony_ci	if (!(nt->parms.o_flags & TUNNEL_SEQ))
3888c2ecf20Sopenharmony_ci		dev->features |= NETIF_F_LLTX;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	ip6gre_tunnel_link(ign, nt);
3918c2ecf20Sopenharmony_ci	return nt;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cifailed_free:
3948c2ecf20Sopenharmony_ci	free_netdev(dev);
3958c2ecf20Sopenharmony_ci	return NULL;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void ip6erspan_tunnel_uninit(struct net_device *dev)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
4018c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ip6erspan_tunnel_unlink_md(ign, t);
4048c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
4058c2ecf20Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
4068c2ecf20Sopenharmony_ci	dev_put(dev);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_uninit(struct net_device *dev)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
4128c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink_md(ign, t);
4158c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
4168c2ecf20Sopenharmony_ci	if (ign->fb_tunnel_dev == dev)
4178c2ecf20Sopenharmony_ci		WRITE_ONCE(ign->fb_tunnel_dev, NULL);
4188c2ecf20Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
4198c2ecf20Sopenharmony_ci	dev_put(dev);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
4248c2ecf20Sopenharmony_ci		       u8 type, u8 code, int offset, __be32 info)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb->dev);
4278c2ecf20Sopenharmony_ci	const struct ipv6hdr *ipv6h;
4288c2ecf20Sopenharmony_ci	struct tnl_ptk_info tpi;
4298c2ecf20Sopenharmony_ci	struct ip6_tnl *t;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (gre_parse_header(skb, &tpi, NULL, htons(ETH_P_IPV6),
4328c2ecf20Sopenharmony_ci			     offset) < 0)
4338c2ecf20Sopenharmony_ci		return -EINVAL;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	ipv6h = (const struct ipv6hdr *)skb->data;
4368c2ecf20Sopenharmony_ci	t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
4378c2ecf20Sopenharmony_ci				 tpi.key, tpi.proto);
4388c2ecf20Sopenharmony_ci	if (!t)
4398c2ecf20Sopenharmony_ci		return -ENOENT;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	switch (type) {
4428c2ecf20Sopenharmony_ci	case ICMPV6_DEST_UNREACH:
4438c2ecf20Sopenharmony_ci		net_dbg_ratelimited("%s: Path to destination invalid or inactive!\n",
4448c2ecf20Sopenharmony_ci				    t->parms.name);
4458c2ecf20Sopenharmony_ci		if (code != ICMPV6_PORT_UNREACH)
4468c2ecf20Sopenharmony_ci			break;
4478c2ecf20Sopenharmony_ci		return 0;
4488c2ecf20Sopenharmony_ci	case ICMPV6_TIME_EXCEED:
4498c2ecf20Sopenharmony_ci		if (code == ICMPV6_EXC_HOPLIMIT) {
4508c2ecf20Sopenharmony_ci			net_dbg_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n",
4518c2ecf20Sopenharmony_ci					    t->parms.name);
4528c2ecf20Sopenharmony_ci			break;
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci		return 0;
4558c2ecf20Sopenharmony_ci	case ICMPV6_PARAMPROB: {
4568c2ecf20Sopenharmony_ci		struct ipv6_tlv_tnl_enc_lim *tel;
4578c2ecf20Sopenharmony_ci		__u32 teli;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		teli = 0;
4608c2ecf20Sopenharmony_ci		if (code == ICMPV6_HDR_FIELD)
4618c2ecf20Sopenharmony_ci			teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		if (teli && teli == be32_to_cpu(info) - 2) {
4648c2ecf20Sopenharmony_ci			tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
4658c2ecf20Sopenharmony_ci			if (tel->encap_limit == 0) {
4668c2ecf20Sopenharmony_ci				net_dbg_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n",
4678c2ecf20Sopenharmony_ci						    t->parms.name);
4688c2ecf20Sopenharmony_ci			}
4698c2ecf20Sopenharmony_ci		} else {
4708c2ecf20Sopenharmony_ci			net_dbg_ratelimited("%s: Recipient unable to parse tunneled packet!\n",
4718c2ecf20Sopenharmony_ci					    t->parms.name);
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci		return 0;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci	case ICMPV6_PKT_TOOBIG:
4768c2ecf20Sopenharmony_ci		ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
4778c2ecf20Sopenharmony_ci		return 0;
4788c2ecf20Sopenharmony_ci	case NDISC_REDIRECT:
4798c2ecf20Sopenharmony_ci		ip6_redirect(skb, net, skb->dev->ifindex, 0,
4808c2ecf20Sopenharmony_ci			     sock_net_uid(net, NULL));
4818c2ecf20Sopenharmony_ci		return 0;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO))
4858c2ecf20Sopenharmony_ci		t->err_count++;
4868c2ecf20Sopenharmony_ci	else
4878c2ecf20Sopenharmony_ci		t->err_count = 1;
4888c2ecf20Sopenharmony_ci	t->err_time = jiffies;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	return 0;
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	const struct ipv6hdr *ipv6h;
4968c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
4998c2ecf20Sopenharmony_ci	tunnel = ip6gre_tunnel_lookup(skb->dev,
5008c2ecf20Sopenharmony_ci				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
5018c2ecf20Sopenharmony_ci				      tpi->proto);
5028c2ecf20Sopenharmony_ci	if (tunnel) {
5038c2ecf20Sopenharmony_ci		if (tunnel->parms.collect_md) {
5048c2ecf20Sopenharmony_ci			struct metadata_dst *tun_dst;
5058c2ecf20Sopenharmony_ci			__be64 tun_id;
5068c2ecf20Sopenharmony_ci			__be16 flags;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci			flags = tpi->flags;
5098c2ecf20Sopenharmony_ci			tun_id = key32_to_tunnel_id(tpi->key);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci			tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id, 0);
5128c2ecf20Sopenharmony_ci			if (!tun_dst)
5138c2ecf20Sopenharmony_ci				return PACKET_REJECT;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
5168c2ecf20Sopenharmony_ci		} else {
5178c2ecf20Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		return PACKET_RCVD;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return PACKET_REJECT;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic int ip6erspan_rcv(struct sk_buff *skb,
5278c2ecf20Sopenharmony_ci			 struct tnl_ptk_info *tpi,
5288c2ecf20Sopenharmony_ci			 int gre_hdr_len)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	struct erspan_base_hdr *ershdr;
5318c2ecf20Sopenharmony_ci	const struct ipv6hdr *ipv6h;
5328c2ecf20Sopenharmony_ci	struct erspan_md2 *md2;
5338c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel;
5348c2ecf20Sopenharmony_ci	u8 ver;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
5378c2ecf20Sopenharmony_ci	ershdr = (struct erspan_base_hdr *)skb->data;
5388c2ecf20Sopenharmony_ci	ver = ershdr->ver;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	tunnel = ip6gre_tunnel_lookup(skb->dev,
5418c2ecf20Sopenharmony_ci				      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
5428c2ecf20Sopenharmony_ci				      tpi->proto);
5438c2ecf20Sopenharmony_ci	if (tunnel) {
5448c2ecf20Sopenharmony_ci		int len = erspan_hdr_len(ver);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		if (unlikely(!pskb_may_pull(skb, len)))
5478c2ecf20Sopenharmony_ci			return PACKET_REJECT;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		if (__iptunnel_pull_header(skb, len,
5508c2ecf20Sopenharmony_ci					   htons(ETH_P_TEB),
5518c2ecf20Sopenharmony_ci					   false, false) < 0)
5528c2ecf20Sopenharmony_ci			return PACKET_REJECT;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		if (tunnel->parms.collect_md) {
5558c2ecf20Sopenharmony_ci			struct erspan_metadata *pkt_md, *md;
5568c2ecf20Sopenharmony_ci			struct metadata_dst *tun_dst;
5578c2ecf20Sopenharmony_ci			struct ip_tunnel_info *info;
5588c2ecf20Sopenharmony_ci			unsigned char *gh;
5598c2ecf20Sopenharmony_ci			__be64 tun_id;
5608c2ecf20Sopenharmony_ci			__be16 flags;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci			tpi->flags |= TUNNEL_KEY;
5638c2ecf20Sopenharmony_ci			flags = tpi->flags;
5648c2ecf20Sopenharmony_ci			tun_id = key32_to_tunnel_id(tpi->key);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci			tun_dst = ipv6_tun_rx_dst(skb, flags, tun_id,
5678c2ecf20Sopenharmony_ci						  sizeof(*md));
5688c2ecf20Sopenharmony_ci			if (!tun_dst)
5698c2ecf20Sopenharmony_ci				return PACKET_REJECT;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci			/* skb can be uncloned in __iptunnel_pull_header, so
5728c2ecf20Sopenharmony_ci			 * old pkt_md is no longer valid and we need to reset
5738c2ecf20Sopenharmony_ci			 * it
5748c2ecf20Sopenharmony_ci			 */
5758c2ecf20Sopenharmony_ci			gh = skb_network_header(skb) +
5768c2ecf20Sopenharmony_ci			     skb_network_header_len(skb);
5778c2ecf20Sopenharmony_ci			pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
5788c2ecf20Sopenharmony_ci							    sizeof(*ershdr));
5798c2ecf20Sopenharmony_ci			info = &tun_dst->u.tun_info;
5808c2ecf20Sopenharmony_ci			md = ip_tunnel_info_opts(info);
5818c2ecf20Sopenharmony_ci			md->version = ver;
5828c2ecf20Sopenharmony_ci			md2 = &md->u.md2;
5838c2ecf20Sopenharmony_ci			memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
5848c2ecf20Sopenharmony_ci						       ERSPAN_V2_MDSIZE);
5858c2ecf20Sopenharmony_ci			info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
5868c2ecf20Sopenharmony_ci			info->options_len = sizeof(*md);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		} else {
5918c2ecf20Sopenharmony_ci			ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
5928c2ecf20Sopenharmony_ci		}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		return PACKET_RCVD;
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return PACKET_REJECT;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int gre_rcv(struct sk_buff *skb)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct tnl_ptk_info tpi;
6038c2ecf20Sopenharmony_ci	bool csum_err = false;
6048c2ecf20Sopenharmony_ci	int hdr_len;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0);
6078c2ecf20Sopenharmony_ci	if (hdr_len < 0)
6088c2ecf20Sopenharmony_ci		goto drop;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (iptunnel_pull_header(skb, hdr_len, tpi.proto, false))
6118c2ecf20Sopenharmony_ci		goto drop;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) ||
6148c2ecf20Sopenharmony_ci		     tpi.proto == htons(ETH_P_ERSPAN2))) {
6158c2ecf20Sopenharmony_ci		if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD)
6168c2ecf20Sopenharmony_ci			return 0;
6178c2ecf20Sopenharmony_ci		goto out;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if (ip6gre_rcv(skb, &tpi) == PACKET_RCVD)
6218c2ecf20Sopenharmony_ci		return 0;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ciout:
6248c2ecf20Sopenharmony_ci	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
6258c2ecf20Sopenharmony_cidrop:
6268c2ecf20Sopenharmony_ci	kfree_skb(skb);
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic int gre_handle_offloads(struct sk_buff *skb, bool csum)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	return iptunnel_handle_offloads(skb,
6338c2ecf20Sopenharmony_ci					csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic void prepare_ip6gre_xmit_ipv4(struct sk_buff *skb,
6378c2ecf20Sopenharmony_ci				     struct net_device *dev,
6388c2ecf20Sopenharmony_ci				     struct flowi6 *fl6, __u8 *dsfield,
6398c2ecf20Sopenharmony_ci				     int *encap_limit)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
6428c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
6458c2ecf20Sopenharmony_ci		*encap_limit = t->parms.encap_limit;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
6508c2ecf20Sopenharmony_ci		*dsfield = ipv4_get_dsfield(iph);
6518c2ecf20Sopenharmony_ci	else
6528c2ecf20Sopenharmony_ci		*dsfield = ip6_tclass(t->parms.flowinfo);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
6558c2ecf20Sopenharmony_ci		fl6->flowi6_mark = skb->mark;
6568c2ecf20Sopenharmony_ci	else
6578c2ecf20Sopenharmony_ci		fl6->flowi6_mark = t->parms.fwmark;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic int prepare_ip6gre_xmit_ipv6(struct sk_buff *skb,
6638c2ecf20Sopenharmony_ci				    struct net_device *dev,
6648c2ecf20Sopenharmony_ci				    struct flowi6 *fl6, __u8 *dsfield,
6658c2ecf20Sopenharmony_ci				    int *encap_limit)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct ipv6hdr *ipv6h;
6688c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
6698c2ecf20Sopenharmony_ci	__u16 offset;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
6728c2ecf20Sopenharmony_ci	/* ip6_tnl_parse_tlv_enc_lim() might have reallocated skb->head */
6738c2ecf20Sopenharmony_ci	ipv6h = ipv6_hdr(skb);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (offset > 0) {
6768c2ecf20Sopenharmony_ci		struct ipv6_tlv_tnl_enc_lim *tel;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci		tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
6798c2ecf20Sopenharmony_ci		if (tel->encap_limit == 0) {
6808c2ecf20Sopenharmony_ci			icmpv6_ndo_send(skb, ICMPV6_PARAMPROB,
6818c2ecf20Sopenharmony_ci					ICMPV6_HDR_FIELD, offset + 2);
6828c2ecf20Sopenharmony_ci			return -1;
6838c2ecf20Sopenharmony_ci		}
6848c2ecf20Sopenharmony_ci		*encap_limit = tel->encap_limit - 1;
6858c2ecf20Sopenharmony_ci	} else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) {
6868c2ecf20Sopenharmony_ci		*encap_limit = t->parms.encap_limit;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	memcpy(fl6, &t->fl.u.ip6, sizeof(*fl6));
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
6928c2ecf20Sopenharmony_ci		*dsfield = ipv6_get_dsfield(ipv6h);
6938c2ecf20Sopenharmony_ci	else
6948c2ecf20Sopenharmony_ci		*dsfield = ip6_tclass(t->parms.flowinfo);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
6978c2ecf20Sopenharmony_ci		fl6->flowlabel |= ip6_flowlabel(ipv6h);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
7008c2ecf20Sopenharmony_ci		fl6->flowi6_mark = skb->mark;
7018c2ecf20Sopenharmony_ci	else
7028c2ecf20Sopenharmony_ci		fl6->flowi6_mark = t->parms.fwmark;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return 0;
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic struct ip_tunnel_info *skb_tunnel_info_txcheck(struct sk_buff *skb)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct ip_tunnel_info *tun_info;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	tun_info = skb_tunnel_info(skb);
7148c2ecf20Sopenharmony_ci	if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX)))
7158c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	return tun_info;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic netdev_tx_t __gre6_xmit(struct sk_buff *skb,
7218c2ecf20Sopenharmony_ci			       struct net_device *dev, __u8 dsfield,
7228c2ecf20Sopenharmony_ci			       struct flowi6 *fl6, int encap_limit,
7238c2ecf20Sopenharmony_ci			       __u32 *pmtu, __be16 proto)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel = netdev_priv(dev);
7268c2ecf20Sopenharmony_ci	__be16 protocol;
7278c2ecf20Sopenharmony_ci	__be16 flags;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_ETHER)
7308c2ecf20Sopenharmony_ci		IPCB(skb)->flags = 0;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	if (dev->header_ops && dev->type == ARPHRD_IP6GRE)
7338c2ecf20Sopenharmony_ci		fl6->daddr = ((struct ipv6hdr *)skb->data)->daddr;
7348c2ecf20Sopenharmony_ci	else
7358c2ecf20Sopenharmony_ci		fl6->daddr = tunnel->parms.raddr;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Push GRE header. */
7388c2ecf20Sopenharmony_ci	protocol = (dev->type == ARPHRD_ETHER) ? htons(ETH_P_TEB) : proto;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (tunnel->parms.collect_md) {
7418c2ecf20Sopenharmony_ci		struct ip_tunnel_info *tun_info;
7428c2ecf20Sopenharmony_ci		const struct ip_tunnel_key *key;
7438c2ecf20Sopenharmony_ci		int tun_hlen;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		tun_info = skb_tunnel_info_txcheck(skb);
7468c2ecf20Sopenharmony_ci		if (IS_ERR(tun_info) ||
7478c2ecf20Sopenharmony_ci		    unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
7488c2ecf20Sopenharmony_ci			return -EINVAL;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		key = &tun_info->key;
7518c2ecf20Sopenharmony_ci		memset(fl6, 0, sizeof(*fl6));
7528c2ecf20Sopenharmony_ci		fl6->flowi6_proto = IPPROTO_GRE;
7538c2ecf20Sopenharmony_ci		fl6->daddr = key->u.ipv6.dst;
7548c2ecf20Sopenharmony_ci		fl6->flowlabel = key->label;
7558c2ecf20Sopenharmony_ci		fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
7568c2ecf20Sopenharmony_ci		fl6->fl6_gre_key = tunnel_id_to_key32(key->tun_id);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		dsfield = key->tos;
7598c2ecf20Sopenharmony_ci		flags = key->tun_flags &
7608c2ecf20Sopenharmony_ci			(TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ);
7618c2ecf20Sopenharmony_ci		tun_hlen = gre_calc_hlen(flags);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci		if (skb_cow_head(skb, dev->needed_headroom ?: tun_hlen + tunnel->encap_hlen))
7648c2ecf20Sopenharmony_ci			return -ENOMEM;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		gre_build_header(skb, tun_hlen,
7678c2ecf20Sopenharmony_ci				 flags, protocol,
7688c2ecf20Sopenharmony_ci				 tunnel_id_to_key32(tun_info->key.tun_id),
7698c2ecf20Sopenharmony_ci				 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
7708c2ecf20Sopenharmony_ci						      : 0);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	} else {
7738c2ecf20Sopenharmony_ci		if (skb_cow_head(skb, dev->needed_headroom ?: tunnel->hlen))
7748c2ecf20Sopenharmony_ci			return -ENOMEM;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		flags = tunnel->parms.o_flags;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		gre_build_header(skb, tunnel->tun_hlen, flags,
7798c2ecf20Sopenharmony_ci				 protocol, tunnel->parms.o_key,
7808c2ecf20Sopenharmony_ci				 (flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
7818c2ecf20Sopenharmony_ci						      : 0);
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,
7858c2ecf20Sopenharmony_ci			    NEXTHDR_GRE);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
7918c2ecf20Sopenharmony_ci	int encap_limit = -1;
7928c2ecf20Sopenharmony_ci	struct flowi6 fl6;
7938c2ecf20Sopenharmony_ci	__u8 dsfield = 0;
7948c2ecf20Sopenharmony_ci	__u32 mtu;
7958c2ecf20Sopenharmony_ci	int err;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (!t->parms.collect_md)
8008c2ecf20Sopenharmony_ci		prepare_ip6gre_xmit_ipv4(skb, dev, &fl6,
8018c2ecf20Sopenharmony_ci					 &dsfield, &encap_limit);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
8048c2ecf20Sopenharmony_ci	if (err)
8058c2ecf20Sopenharmony_ci		return -1;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
8088c2ecf20Sopenharmony_ci			  skb->protocol);
8098c2ecf20Sopenharmony_ci	if (err != 0) {
8108c2ecf20Sopenharmony_ci		/* XXX: send ICMP error even if DF is not set. */
8118c2ecf20Sopenharmony_ci		if (err == -EMSGSIZE)
8128c2ecf20Sopenharmony_ci			icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
8138c2ecf20Sopenharmony_ci				      htonl(mtu));
8148c2ecf20Sopenharmony_ci		return -1;
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	return 0;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
8238c2ecf20Sopenharmony_ci	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
8248c2ecf20Sopenharmony_ci	int encap_limit = -1;
8258c2ecf20Sopenharmony_ci	struct flowi6 fl6;
8268c2ecf20Sopenharmony_ci	__u8 dsfield = 0;
8278c2ecf20Sopenharmony_ci	__u32 mtu;
8288c2ecf20Sopenharmony_ci	int err;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
8318c2ecf20Sopenharmony_ci		return -1;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (!t->parms.collect_md &&
8348c2ecf20Sopenharmony_ci	    prepare_ip6gre_xmit_ipv6(skb, dev, &fl6, &dsfield, &encap_limit))
8358c2ecf20Sopenharmony_ci		return -1;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	if (gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM)))
8388c2ecf20Sopenharmony_ci		return -1;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	err = __gre6_xmit(skb, dev, dsfield, &fl6, encap_limit,
8418c2ecf20Sopenharmony_ci			  &mtu, skb->protocol);
8428c2ecf20Sopenharmony_ci	if (err != 0) {
8438c2ecf20Sopenharmony_ci		if (err == -EMSGSIZE)
8448c2ecf20Sopenharmony_ci			icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
8458c2ecf20Sopenharmony_ci		return -1;
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	return 0;
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci/**
8528c2ecf20Sopenharmony_ci * ip6gre_tnl_addr_conflict - compare packet addresses to tunnel's own
8538c2ecf20Sopenharmony_ci *   @t: the outgoing tunnel device
8548c2ecf20Sopenharmony_ci *   @hdr: IPv6 header from the incoming packet
8558c2ecf20Sopenharmony_ci *
8568c2ecf20Sopenharmony_ci * Description:
8578c2ecf20Sopenharmony_ci *   Avoid trivial tunneling loop by checking that tunnel exit-point
8588c2ecf20Sopenharmony_ci *   doesn't match source of incoming packet.
8598c2ecf20Sopenharmony_ci *
8608c2ecf20Sopenharmony_ci * Return:
8618c2ecf20Sopenharmony_ci *   1 if conflict,
8628c2ecf20Sopenharmony_ci *   0 else
8638c2ecf20Sopenharmony_ci **/
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic inline bool ip6gre_tnl_addr_conflict(const struct ip6_tnl *t,
8668c2ecf20Sopenharmony_ci	const struct ipv6hdr *hdr)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
8748c2ecf20Sopenharmony_ci	int encap_limit = -1;
8758c2ecf20Sopenharmony_ci	struct flowi6 fl6;
8768c2ecf20Sopenharmony_ci	__u32 mtu;
8778c2ecf20Sopenharmony_ci	int err;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
8808c2ecf20Sopenharmony_ci		encap_limit = t->parms.encap_limit;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (!t->parms.collect_md)
8838c2ecf20Sopenharmony_ci		memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	err = gre_handle_offloads(skb, !!(t->parms.o_flags & TUNNEL_CSUM));
8868c2ecf20Sopenharmony_ci	if (err)
8878c2ecf20Sopenharmony_ci		return err;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	err = __gre6_xmit(skb, dev, 0, &fl6, encap_limit, &mtu, skb->protocol);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	return err;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
8958c2ecf20Sopenharmony_ci	struct net_device *dev)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
8988c2ecf20Sopenharmony_ci	struct net_device_stats *stats = &t->dev->stats;
8998c2ecf20Sopenharmony_ci	int ret;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
9028c2ecf20Sopenharmony_ci		goto tx_err;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
9058c2ecf20Sopenharmony_ci		goto tx_err;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	switch (skb->protocol) {
9088c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
9098c2ecf20Sopenharmony_ci		ret = ip6gre_xmit_ipv4(skb, dev);
9108c2ecf20Sopenharmony_ci		break;
9118c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
9128c2ecf20Sopenharmony_ci		ret = ip6gre_xmit_ipv6(skb, dev);
9138c2ecf20Sopenharmony_ci		break;
9148c2ecf20Sopenharmony_ci	default:
9158c2ecf20Sopenharmony_ci		ret = ip6gre_xmit_other(skb, dev);
9168c2ecf20Sopenharmony_ci		break;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (ret < 0)
9208c2ecf20Sopenharmony_ci		goto tx_err;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_citx_err:
9258c2ecf20Sopenharmony_ci	if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
9268c2ecf20Sopenharmony_ci		stats->tx_errors++;
9278c2ecf20Sopenharmony_ci	stats->tx_dropped++;
9288c2ecf20Sopenharmony_ci	kfree_skb(skb);
9298c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
9338c2ecf20Sopenharmony_ci					 struct net_device *dev)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct ip_tunnel_info *tun_info = NULL;
9368c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
9378c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
9388c2ecf20Sopenharmony_ci	struct net_device_stats *stats;
9398c2ecf20Sopenharmony_ci	bool truncate = false;
9408c2ecf20Sopenharmony_ci	int encap_limit = -1;
9418c2ecf20Sopenharmony_ci	__u8 dsfield = false;
9428c2ecf20Sopenharmony_ci	struct flowi6 fl6;
9438c2ecf20Sopenharmony_ci	int err = -EINVAL;
9448c2ecf20Sopenharmony_ci	__be16 proto;
9458c2ecf20Sopenharmony_ci	__u32 mtu;
9468c2ecf20Sopenharmony_ci	int nhoff;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
9498c2ecf20Sopenharmony_ci		goto tx_err;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
9528c2ecf20Sopenharmony_ci		goto tx_err;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (gre_handle_offloads(skb, false))
9558c2ecf20Sopenharmony_ci		goto tx_err;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (skb->len > dev->mtu + dev->hard_header_len) {
9588c2ecf20Sopenharmony_ci		if (pskb_trim(skb, dev->mtu + dev->hard_header_len))
9598c2ecf20Sopenharmony_ci			goto tx_err;
9608c2ecf20Sopenharmony_ci		truncate = true;
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	nhoff = skb_network_offset(skb);
9648c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP) &&
9658c2ecf20Sopenharmony_ci	    (ntohs(ip_hdr(skb)->tot_len) > skb->len - nhoff))
9668c2ecf20Sopenharmony_ci		truncate = true;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IPV6)) {
9698c2ecf20Sopenharmony_ci		int thoff;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		if (skb_transport_header_was_set(skb))
9728c2ecf20Sopenharmony_ci			thoff = skb_transport_offset(skb);
9738c2ecf20Sopenharmony_ci		else
9748c2ecf20Sopenharmony_ci			thoff = nhoff + sizeof(struct ipv6hdr);
9758c2ecf20Sopenharmony_ci		if (ntohs(ipv6_hdr(skb)->payload_len) > skb->len - thoff)
9768c2ecf20Sopenharmony_ci			truncate = true;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
9808c2ecf20Sopenharmony_ci		goto tx_err;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	t->parms.o_flags &= ~TUNNEL_KEY;
9838c2ecf20Sopenharmony_ci	IPCB(skb)->flags = 0;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* For collect_md mode, derive fl6 from the tunnel key,
9868c2ecf20Sopenharmony_ci	 * for native mode, call prepare_ip6gre_xmit_{ipv4,ipv6}.
9878c2ecf20Sopenharmony_ci	 */
9888c2ecf20Sopenharmony_ci	if (t->parms.collect_md) {
9898c2ecf20Sopenharmony_ci		const struct ip_tunnel_key *key;
9908c2ecf20Sopenharmony_ci		struct erspan_metadata *md;
9918c2ecf20Sopenharmony_ci		__be32 tun_id;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci		tun_info = skb_tunnel_info_txcheck(skb);
9948c2ecf20Sopenharmony_ci		if (IS_ERR(tun_info) ||
9958c2ecf20Sopenharmony_ci		    unlikely(ip_tunnel_info_af(tun_info) != AF_INET6))
9968c2ecf20Sopenharmony_ci			goto tx_err;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci		key = &tun_info->key;
9998c2ecf20Sopenharmony_ci		memset(&fl6, 0, sizeof(fl6));
10008c2ecf20Sopenharmony_ci		fl6.flowi6_proto = IPPROTO_GRE;
10018c2ecf20Sopenharmony_ci		fl6.daddr = key->u.ipv6.dst;
10028c2ecf20Sopenharmony_ci		fl6.flowlabel = key->label;
10038c2ecf20Sopenharmony_ci		fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
10048c2ecf20Sopenharmony_ci		fl6.fl6_gre_key = tunnel_id_to_key32(key->tun_id);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci		dsfield = key->tos;
10078c2ecf20Sopenharmony_ci		if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
10088c2ecf20Sopenharmony_ci			goto tx_err;
10098c2ecf20Sopenharmony_ci		if (tun_info->options_len < sizeof(*md))
10108c2ecf20Sopenharmony_ci			goto tx_err;
10118c2ecf20Sopenharmony_ci		md = ip_tunnel_info_opts(tun_info);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci		tun_id = tunnel_id_to_key32(key->tun_id);
10148c2ecf20Sopenharmony_ci		if (md->version == 1) {
10158c2ecf20Sopenharmony_ci			erspan_build_header(skb,
10168c2ecf20Sopenharmony_ci					    ntohl(tun_id),
10178c2ecf20Sopenharmony_ci					    ntohl(md->u.index), truncate,
10188c2ecf20Sopenharmony_ci					    false);
10198c2ecf20Sopenharmony_ci			proto = htons(ETH_P_ERSPAN);
10208c2ecf20Sopenharmony_ci		} else if (md->version == 2) {
10218c2ecf20Sopenharmony_ci			erspan_build_header_v2(skb,
10228c2ecf20Sopenharmony_ci					       ntohl(tun_id),
10238c2ecf20Sopenharmony_ci					       md->u.md2.dir,
10248c2ecf20Sopenharmony_ci					       get_hwid(&md->u.md2),
10258c2ecf20Sopenharmony_ci					       truncate, false);
10268c2ecf20Sopenharmony_ci			proto = htons(ETH_P_ERSPAN2);
10278c2ecf20Sopenharmony_ci		} else {
10288c2ecf20Sopenharmony_ci			goto tx_err;
10298c2ecf20Sopenharmony_ci		}
10308c2ecf20Sopenharmony_ci	} else {
10318c2ecf20Sopenharmony_ci		switch (skb->protocol) {
10328c2ecf20Sopenharmony_ci		case htons(ETH_P_IP):
10338c2ecf20Sopenharmony_ci			memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
10348c2ecf20Sopenharmony_ci			prepare_ip6gre_xmit_ipv4(skb, dev, &fl6,
10358c2ecf20Sopenharmony_ci						 &dsfield, &encap_limit);
10368c2ecf20Sopenharmony_ci			break;
10378c2ecf20Sopenharmony_ci		case htons(ETH_P_IPV6):
10388c2ecf20Sopenharmony_ci			if (ipv6_addr_equal(&t->parms.raddr, &ipv6_hdr(skb)->saddr))
10398c2ecf20Sopenharmony_ci				goto tx_err;
10408c2ecf20Sopenharmony_ci			if (prepare_ip6gre_xmit_ipv6(skb, dev, &fl6,
10418c2ecf20Sopenharmony_ci						     &dsfield, &encap_limit))
10428c2ecf20Sopenharmony_ci				goto tx_err;
10438c2ecf20Sopenharmony_ci			break;
10448c2ecf20Sopenharmony_ci		default:
10458c2ecf20Sopenharmony_ci			memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
10468c2ecf20Sopenharmony_ci			break;
10478c2ecf20Sopenharmony_ci		}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci		if (t->parms.erspan_ver == 1) {
10508c2ecf20Sopenharmony_ci			erspan_build_header(skb, ntohl(t->parms.o_key),
10518c2ecf20Sopenharmony_ci					    t->parms.index,
10528c2ecf20Sopenharmony_ci					    truncate, false);
10538c2ecf20Sopenharmony_ci			proto = htons(ETH_P_ERSPAN);
10548c2ecf20Sopenharmony_ci		} else if (t->parms.erspan_ver == 2) {
10558c2ecf20Sopenharmony_ci			erspan_build_header_v2(skb, ntohl(t->parms.o_key),
10568c2ecf20Sopenharmony_ci					       t->parms.dir,
10578c2ecf20Sopenharmony_ci					       t->parms.hwid,
10588c2ecf20Sopenharmony_ci					       truncate, false);
10598c2ecf20Sopenharmony_ci			proto = htons(ETH_P_ERSPAN2);
10608c2ecf20Sopenharmony_ci		} else {
10618c2ecf20Sopenharmony_ci			goto tx_err;
10628c2ecf20Sopenharmony_ci		}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci		fl6.daddr = t->parms.raddr;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	/* Push GRE header. */
10688c2ecf20Sopenharmony_ci	gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(atomic_fetch_inc(&t->o_seqno)));
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	/* TooBig packet may have updated dst->dev's mtu */
10718c2ecf20Sopenharmony_ci	if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu)
10728c2ecf20Sopenharmony_ci		dst->ops->update_pmtu(dst, NULL, skb, dst->dev->mtu, false);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	err = ip6_tnl_xmit(skb, dev, dsfield, &fl6, encap_limit, &mtu,
10758c2ecf20Sopenharmony_ci			   NEXTHDR_GRE);
10768c2ecf20Sopenharmony_ci	if (err != 0) {
10778c2ecf20Sopenharmony_ci		/* XXX: send ICMP error even if DF is not set. */
10788c2ecf20Sopenharmony_ci		if (err == -EMSGSIZE) {
10798c2ecf20Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP))
10808c2ecf20Sopenharmony_ci				icmp_ndo_send(skb, ICMP_DEST_UNREACH,
10818c2ecf20Sopenharmony_ci					      ICMP_FRAG_NEEDED, htonl(mtu));
10828c2ecf20Sopenharmony_ci			else
10838c2ecf20Sopenharmony_ci				icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
10848c2ecf20Sopenharmony_ci		}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci		goto tx_err;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_citx_err:
10918c2ecf20Sopenharmony_ci	stats = &t->dev->stats;
10928c2ecf20Sopenharmony_ci	if (!IS_ERR(tun_info))
10938c2ecf20Sopenharmony_ci		stats->tx_errors++;
10948c2ecf20Sopenharmony_ci	stats->tx_dropped++;
10958c2ecf20Sopenharmony_ci	kfree_skb(skb);
10968c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config_common(struct ip6_tnl *t)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	struct net_device *dev = t->dev;
11028c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm *p = &t->parms;
11038c2ecf20Sopenharmony_ci	struct flowi6 *fl6 = &t->fl.u.ip6;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (dev->type != ARPHRD_ETHER) {
11068c2ecf20Sopenharmony_ci		memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
11078c2ecf20Sopenharmony_ci		memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
11088c2ecf20Sopenharmony_ci	}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	/* Set up flowi template */
11118c2ecf20Sopenharmony_ci	fl6->saddr = p->laddr;
11128c2ecf20Sopenharmony_ci	fl6->daddr = p->raddr;
11138c2ecf20Sopenharmony_ci	fl6->flowi6_oif = p->link;
11148c2ecf20Sopenharmony_ci	fl6->flowlabel = 0;
11158c2ecf20Sopenharmony_ci	fl6->flowi6_proto = IPPROTO_GRE;
11168c2ecf20Sopenharmony_ci	fl6->fl6_gre_key = t->parms.o_key;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
11198c2ecf20Sopenharmony_ci		fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
11208c2ecf20Sopenharmony_ci	if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
11218c2ecf20Sopenharmony_ci		fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
11248c2ecf20Sopenharmony_ci	p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	if (p->flags&IP6_TNL_F_CAP_XMIT &&
11278c2ecf20Sopenharmony_ci			p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER)
11288c2ecf20Sopenharmony_ci		dev->flags |= IFF_POINTOPOINT;
11298c2ecf20Sopenharmony_ci	else
11308c2ecf20Sopenharmony_ci		dev->flags &= ~IFF_POINTOPOINT;
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu,
11348c2ecf20Sopenharmony_ci					 int t_hlen)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	const struct __ip6_tnl_parm *p = &t->parms;
11378c2ecf20Sopenharmony_ci	struct net_device *dev = t->dev;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	if (p->flags & IP6_TNL_F_CAP_XMIT) {
11408c2ecf20Sopenharmony_ci		int strict = (ipv6_addr_type(&p->raddr) &
11418c2ecf20Sopenharmony_ci			      (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci		struct rt6_info *rt = rt6_lookup(t->net,
11448c2ecf20Sopenharmony_ci						 &p->raddr, &p->laddr,
11458c2ecf20Sopenharmony_ci						 p->link, NULL, strict);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		if (!rt)
11488c2ecf20Sopenharmony_ci			return;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		if (rt->dst.dev) {
11518c2ecf20Sopenharmony_ci			unsigned short dst_len = rt->dst.dev->hard_header_len +
11528c2ecf20Sopenharmony_ci						 t_hlen;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci			if (t->dev->header_ops)
11558c2ecf20Sopenharmony_ci				dev->hard_header_len = dst_len;
11568c2ecf20Sopenharmony_ci			else
11578c2ecf20Sopenharmony_ci				dev->needed_headroom = dst_len;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci			if (set_mtu) {
11608c2ecf20Sopenharmony_ci				int mtu = rt->dst.dev->mtu - t_hlen;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci				if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
11638c2ecf20Sopenharmony_ci					mtu -= 8;
11648c2ecf20Sopenharmony_ci				if (dev->type == ARPHRD_ETHER)
11658c2ecf20Sopenharmony_ci					mtu -= ETH_HLEN;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci				if (mtu < IPV6_MIN_MTU)
11688c2ecf20Sopenharmony_ci					mtu = IPV6_MIN_MTU;
11698c2ecf20Sopenharmony_ci				WRITE_ONCE(dev->mtu, mtu);
11708c2ecf20Sopenharmony_ci			}
11718c2ecf20Sopenharmony_ci		}
11728c2ecf20Sopenharmony_ci		ip6_rt_put(rt);
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_cistatic int ip6gre_calc_hlen(struct ip6_tnl *tunnel)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	int t_hlen;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	tunnel->tun_hlen = gre_calc_hlen(tunnel->parms.o_flags);
11818c2ecf20Sopenharmony_ci	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	if (tunnel->dev->header_ops)
11868c2ecf20Sopenharmony_ci		tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
11878c2ecf20Sopenharmony_ci	else
11888c2ecf20Sopenharmony_ci		tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	return t_hlen;
11918c2ecf20Sopenharmony_ci}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	ip6gre_tnl_link_config_common(t);
11968c2ecf20Sopenharmony_ci	ip6gre_tnl_link_config_route(t, set_mtu, ip6gre_calc_hlen(t));
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic void ip6gre_tnl_copy_tnl_parm(struct ip6_tnl *t,
12008c2ecf20Sopenharmony_ci				     const struct __ip6_tnl_parm *p)
12018c2ecf20Sopenharmony_ci{
12028c2ecf20Sopenharmony_ci	t->parms.laddr = p->laddr;
12038c2ecf20Sopenharmony_ci	t->parms.raddr = p->raddr;
12048c2ecf20Sopenharmony_ci	t->parms.flags = p->flags;
12058c2ecf20Sopenharmony_ci	t->parms.hop_limit = p->hop_limit;
12068c2ecf20Sopenharmony_ci	t->parms.encap_limit = p->encap_limit;
12078c2ecf20Sopenharmony_ci	t->parms.flowinfo = p->flowinfo;
12088c2ecf20Sopenharmony_ci	t->parms.link = p->link;
12098c2ecf20Sopenharmony_ci	t->parms.proto = p->proto;
12108c2ecf20Sopenharmony_ci	t->parms.i_key = p->i_key;
12118c2ecf20Sopenharmony_ci	t->parms.o_key = p->o_key;
12128c2ecf20Sopenharmony_ci	t->parms.i_flags = p->i_flags;
12138c2ecf20Sopenharmony_ci	t->parms.o_flags = p->o_flags;
12148c2ecf20Sopenharmony_ci	t->parms.fwmark = p->fwmark;
12158c2ecf20Sopenharmony_ci	t->parms.erspan_ver = p->erspan_ver;
12168c2ecf20Sopenharmony_ci	t->parms.index = p->index;
12178c2ecf20Sopenharmony_ci	t->parms.dir = p->dir;
12188c2ecf20Sopenharmony_ci	t->parms.hwid = p->hwid;
12198c2ecf20Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_cistatic int ip6gre_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
12238c2ecf20Sopenharmony_ci			     int set_mtu)
12248c2ecf20Sopenharmony_ci{
12258c2ecf20Sopenharmony_ci	ip6gre_tnl_copy_tnl_parm(t, p);
12268c2ecf20Sopenharmony_ci	ip6gre_tnl_link_config(t, set_mtu);
12278c2ecf20Sopenharmony_ci	return 0;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cistatic void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p,
12318c2ecf20Sopenharmony_ci	const struct ip6_tnl_parm2 *u)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	p->laddr = u->laddr;
12348c2ecf20Sopenharmony_ci	p->raddr = u->raddr;
12358c2ecf20Sopenharmony_ci	p->flags = u->flags;
12368c2ecf20Sopenharmony_ci	p->hop_limit = u->hop_limit;
12378c2ecf20Sopenharmony_ci	p->encap_limit = u->encap_limit;
12388c2ecf20Sopenharmony_ci	p->flowinfo = u->flowinfo;
12398c2ecf20Sopenharmony_ci	p->link = u->link;
12408c2ecf20Sopenharmony_ci	p->i_key = u->i_key;
12418c2ecf20Sopenharmony_ci	p->o_key = u->o_key;
12428c2ecf20Sopenharmony_ci	p->i_flags = gre_flags_to_tnl_flags(u->i_flags);
12438c2ecf20Sopenharmony_ci	p->o_flags = gre_flags_to_tnl_flags(u->o_flags);
12448c2ecf20Sopenharmony_ci	memcpy(p->name, u->name, sizeof(u->name));
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u,
12488c2ecf20Sopenharmony_ci	const struct __ip6_tnl_parm *p)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	u->proto = IPPROTO_GRE;
12518c2ecf20Sopenharmony_ci	u->laddr = p->laddr;
12528c2ecf20Sopenharmony_ci	u->raddr = p->raddr;
12538c2ecf20Sopenharmony_ci	u->flags = p->flags;
12548c2ecf20Sopenharmony_ci	u->hop_limit = p->hop_limit;
12558c2ecf20Sopenharmony_ci	u->encap_limit = p->encap_limit;
12568c2ecf20Sopenharmony_ci	u->flowinfo = p->flowinfo;
12578c2ecf20Sopenharmony_ci	u->link = p->link;
12588c2ecf20Sopenharmony_ci	u->i_key = p->i_key;
12598c2ecf20Sopenharmony_ci	u->o_key = p->o_key;
12608c2ecf20Sopenharmony_ci	u->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags);
12618c2ecf20Sopenharmony_ci	u->o_flags = gre_tnl_flags_to_gre_flags(p->o_flags);
12628c2ecf20Sopenharmony_ci	memcpy(u->name, p->name, sizeof(u->name));
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_ioctl(struct net_device *dev,
12668c2ecf20Sopenharmony_ci	struct ifreq *ifr, int cmd)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	int err = 0;
12698c2ecf20Sopenharmony_ci	struct ip6_tnl_parm2 p;
12708c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm p1;
12718c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
12728c2ecf20Sopenharmony_ci	struct net *net = t->net;
12738c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	memset(&p1, 0, sizeof(p1));
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	switch (cmd) {
12788c2ecf20Sopenharmony_ci	case SIOCGETTUNNEL:
12798c2ecf20Sopenharmony_ci		if (dev == ign->fb_tunnel_dev) {
12808c2ecf20Sopenharmony_ci			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
12818c2ecf20Sopenharmony_ci				err = -EFAULT;
12828c2ecf20Sopenharmony_ci				break;
12838c2ecf20Sopenharmony_ci			}
12848c2ecf20Sopenharmony_ci			ip6gre_tnl_parm_from_user(&p1, &p);
12858c2ecf20Sopenharmony_ci			t = ip6gre_tunnel_locate(net, &p1, 0);
12868c2ecf20Sopenharmony_ci			if (!t)
12878c2ecf20Sopenharmony_ci				t = netdev_priv(dev);
12888c2ecf20Sopenharmony_ci		}
12898c2ecf20Sopenharmony_ci		memset(&p, 0, sizeof(p));
12908c2ecf20Sopenharmony_ci		ip6gre_tnl_parm_to_user(&p, &t->parms);
12918c2ecf20Sopenharmony_ci		if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
12928c2ecf20Sopenharmony_ci			err = -EFAULT;
12938c2ecf20Sopenharmony_ci		break;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	case SIOCADDTUNNEL:
12968c2ecf20Sopenharmony_ci	case SIOCCHGTUNNEL:
12978c2ecf20Sopenharmony_ci		err = -EPERM;
12988c2ecf20Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
12998c2ecf20Sopenharmony_ci			goto done;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci		err = -EFAULT;
13028c2ecf20Sopenharmony_ci		if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
13038c2ecf20Sopenharmony_ci			goto done;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci		err = -EINVAL;
13068c2ecf20Sopenharmony_ci		if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))
13078c2ecf20Sopenharmony_ci			goto done;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci		if (!(p.i_flags&GRE_KEY))
13108c2ecf20Sopenharmony_ci			p.i_key = 0;
13118c2ecf20Sopenharmony_ci		if (!(p.o_flags&GRE_KEY))
13128c2ecf20Sopenharmony_ci			p.o_key = 0;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci		ip6gre_tnl_parm_from_user(&p1, &p);
13158c2ecf20Sopenharmony_ci		t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci		if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
13188c2ecf20Sopenharmony_ci			if (t) {
13198c2ecf20Sopenharmony_ci				if (t->dev != dev) {
13208c2ecf20Sopenharmony_ci					err = -EEXIST;
13218c2ecf20Sopenharmony_ci					break;
13228c2ecf20Sopenharmony_ci				}
13238c2ecf20Sopenharmony_ci			} else {
13248c2ecf20Sopenharmony_ci				t = netdev_priv(dev);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci				ip6gre_tunnel_unlink(ign, t);
13278c2ecf20Sopenharmony_ci				synchronize_net();
13288c2ecf20Sopenharmony_ci				ip6gre_tnl_change(t, &p1, 1);
13298c2ecf20Sopenharmony_ci				ip6gre_tunnel_link(ign, t);
13308c2ecf20Sopenharmony_ci				netdev_state_change(dev);
13318c2ecf20Sopenharmony_ci			}
13328c2ecf20Sopenharmony_ci		}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci		if (t) {
13358c2ecf20Sopenharmony_ci			err = 0;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci			memset(&p, 0, sizeof(p));
13388c2ecf20Sopenharmony_ci			ip6gre_tnl_parm_to_user(&p, &t->parms);
13398c2ecf20Sopenharmony_ci			if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
13408c2ecf20Sopenharmony_ci				err = -EFAULT;
13418c2ecf20Sopenharmony_ci		} else
13428c2ecf20Sopenharmony_ci			err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
13438c2ecf20Sopenharmony_ci		break;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	case SIOCDELTUNNEL:
13468c2ecf20Sopenharmony_ci		err = -EPERM;
13478c2ecf20Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
13488c2ecf20Sopenharmony_ci			goto done;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci		if (dev == ign->fb_tunnel_dev) {
13518c2ecf20Sopenharmony_ci			err = -EFAULT;
13528c2ecf20Sopenharmony_ci			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
13538c2ecf20Sopenharmony_ci				goto done;
13548c2ecf20Sopenharmony_ci			err = -ENOENT;
13558c2ecf20Sopenharmony_ci			ip6gre_tnl_parm_from_user(&p1, &p);
13568c2ecf20Sopenharmony_ci			t = ip6gre_tunnel_locate(net, &p1, 0);
13578c2ecf20Sopenharmony_ci			if (!t)
13588c2ecf20Sopenharmony_ci				goto done;
13598c2ecf20Sopenharmony_ci			err = -EPERM;
13608c2ecf20Sopenharmony_ci			if (t == netdev_priv(ign->fb_tunnel_dev))
13618c2ecf20Sopenharmony_ci				goto done;
13628c2ecf20Sopenharmony_ci			dev = t->dev;
13638c2ecf20Sopenharmony_ci		}
13648c2ecf20Sopenharmony_ci		unregister_netdevice(dev);
13658c2ecf20Sopenharmony_ci		err = 0;
13668c2ecf20Sopenharmony_ci		break;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	default:
13698c2ecf20Sopenharmony_ci		err = -EINVAL;
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cidone:
13738c2ecf20Sopenharmony_ci	return err;
13748c2ecf20Sopenharmony_ci}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cistatic int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
13778c2ecf20Sopenharmony_ci			 unsigned short type, const void *daddr,
13788c2ecf20Sopenharmony_ci			 const void *saddr, unsigned int len)
13798c2ecf20Sopenharmony_ci{
13808c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
13818c2ecf20Sopenharmony_ci	struct ipv6hdr *ipv6h;
13828c2ecf20Sopenharmony_ci	__be16 *p;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	ipv6h = skb_push(skb, t->hlen + sizeof(*ipv6h));
13858c2ecf20Sopenharmony_ci	ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb,
13868c2ecf20Sopenharmony_ci						  t->fl.u.ip6.flowlabel,
13878c2ecf20Sopenharmony_ci						  true, &t->fl.u.ip6));
13888c2ecf20Sopenharmony_ci	ipv6h->hop_limit = t->parms.hop_limit;
13898c2ecf20Sopenharmony_ci	ipv6h->nexthdr = NEXTHDR_GRE;
13908c2ecf20Sopenharmony_ci	ipv6h->saddr = t->parms.laddr;
13918c2ecf20Sopenharmony_ci	ipv6h->daddr = t->parms.raddr;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	p = (__be16 *)(ipv6h + 1);
13948c2ecf20Sopenharmony_ci	p[0] = t->parms.o_flags;
13958c2ecf20Sopenharmony_ci	p[1] = htons(type);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	/*
13988c2ecf20Sopenharmony_ci	 *	Set the source hardware address.
13998c2ecf20Sopenharmony_ci	 */
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	if (saddr)
14028c2ecf20Sopenharmony_ci		memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr));
14038c2ecf20Sopenharmony_ci	if (daddr)
14048c2ecf20Sopenharmony_ci		memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr));
14058c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(&ipv6h->daddr))
14068c2ecf20Sopenharmony_ci		return t->hlen;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	return -t->hlen;
14098c2ecf20Sopenharmony_ci}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_cistatic const struct header_ops ip6gre_header_ops = {
14128c2ecf20Sopenharmony_ci	.create	= ip6gre_header,
14138c2ecf20Sopenharmony_ci};
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_cistatic const struct net_device_ops ip6gre_netdev_ops = {
14168c2ecf20Sopenharmony_ci	.ndo_init		= ip6gre_tunnel_init,
14178c2ecf20Sopenharmony_ci	.ndo_uninit		= ip6gre_tunnel_uninit,
14188c2ecf20Sopenharmony_ci	.ndo_start_xmit		= ip6gre_tunnel_xmit,
14198c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= ip6gre_tunnel_ioctl,
14208c2ecf20Sopenharmony_ci	.ndo_change_mtu		= ip6_tnl_change_mtu,
14218c2ecf20Sopenharmony_ci	.ndo_get_stats64	= ip_tunnel_get_stats64,
14228c2ecf20Sopenharmony_ci	.ndo_get_iflink		= ip6_tnl_get_iflink,
14238c2ecf20Sopenharmony_ci};
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic void ip6gre_dev_free(struct net_device *dev)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	gro_cells_destroy(&t->gro_cells);
14308c2ecf20Sopenharmony_ci	dst_cache_destroy(&t->dst_cache);
14318c2ecf20Sopenharmony_ci	free_percpu(dev->tstats);
14328c2ecf20Sopenharmony_ci}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_cistatic void ip6gre_tunnel_setup(struct net_device *dev)
14358c2ecf20Sopenharmony_ci{
14368c2ecf20Sopenharmony_ci	dev->netdev_ops = &ip6gre_netdev_ops;
14378c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
14388c2ecf20Sopenharmony_ci	dev->priv_destructor = ip6gre_dev_free;
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	dev->type = ARPHRD_IP6GRE;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	dev->flags |= IFF_NOARP;
14438c2ecf20Sopenharmony_ci	dev->addr_len = sizeof(struct in6_addr);
14448c2ecf20Sopenharmony_ci	netif_keep_dst(dev);
14458c2ecf20Sopenharmony_ci	/* This perm addr will be used as interface identifier by IPv6 */
14468c2ecf20Sopenharmony_ci	dev->addr_assign_type = NET_ADDR_RANDOM;
14478c2ecf20Sopenharmony_ci	eth_random_addr(dev->perm_addr);
14488c2ecf20Sopenharmony_ci}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci#define GRE6_FEATURES (NETIF_F_SG |		\
14518c2ecf20Sopenharmony_ci		       NETIF_F_FRAGLIST |	\
14528c2ecf20Sopenharmony_ci		       NETIF_F_HIGHDMA |	\
14538c2ecf20Sopenharmony_ci		       NETIF_F_HW_CSUM)
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_cistatic void ip6gre_tnl_init_features(struct net_device *dev)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	struct ip6_tnl *nt = netdev_priv(dev);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	dev->features		|= GRE6_FEATURES;
14608c2ecf20Sopenharmony_ci	dev->hw_features	|= GRE6_FEATURES;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	if (!(nt->parms.o_flags & TUNNEL_SEQ)) {
14638c2ecf20Sopenharmony_ci		/* TCP offload with GRE SEQ is not supported, nor
14648c2ecf20Sopenharmony_ci		 * can we support 2 levels of outer headers requiring
14658c2ecf20Sopenharmony_ci		 * an update.
14668c2ecf20Sopenharmony_ci		 */
14678c2ecf20Sopenharmony_ci		if (!(nt->parms.o_flags & TUNNEL_CSUM) ||
14688c2ecf20Sopenharmony_ci		    nt->encap.type == TUNNEL_ENCAP_NONE) {
14698c2ecf20Sopenharmony_ci			dev->features    |= NETIF_F_GSO_SOFTWARE;
14708c2ecf20Sopenharmony_ci			dev->hw_features |= NETIF_F_GSO_SOFTWARE;
14718c2ecf20Sopenharmony_ci		}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci		/* Can use a lockless transmit, unless we generate
14748c2ecf20Sopenharmony_ci		 * output sequences
14758c2ecf20Sopenharmony_ci		 */
14768c2ecf20Sopenharmony_ci		dev->features |= NETIF_F_LLTX;
14778c2ecf20Sopenharmony_ci	}
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_init_common(struct net_device *dev)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel;
14838c2ecf20Sopenharmony_ci	int ret;
14848c2ecf20Sopenharmony_ci	int t_hlen;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	tunnel = netdev_priv(dev);
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	tunnel->dev = dev;
14898c2ecf20Sopenharmony_ci	tunnel->net = dev_net(dev);
14908c2ecf20Sopenharmony_ci	strcpy(tunnel->parms.name, dev->name);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
14938c2ecf20Sopenharmony_ci	if (!dev->tstats)
14948c2ecf20Sopenharmony_ci		return -ENOMEM;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
14978c2ecf20Sopenharmony_ci	if (ret)
14988c2ecf20Sopenharmony_ci		goto cleanup_alloc_pcpu_stats;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	ret = gro_cells_init(&tunnel->gro_cells, dev);
15018c2ecf20Sopenharmony_ci	if (ret)
15028c2ecf20Sopenharmony_ci		goto cleanup_dst_cache_init;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	t_hlen = ip6gre_calc_hlen(tunnel);
15058c2ecf20Sopenharmony_ci	dev->mtu = ETH_DATA_LEN - t_hlen;
15068c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_ETHER)
15078c2ecf20Sopenharmony_ci		dev->mtu -= ETH_HLEN;
15088c2ecf20Sopenharmony_ci	if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
15098c2ecf20Sopenharmony_ci		dev->mtu -= 8;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	if (tunnel->parms.collect_md) {
15128c2ecf20Sopenharmony_ci		netif_keep_dst(dev);
15138c2ecf20Sopenharmony_ci	}
15148c2ecf20Sopenharmony_ci	ip6gre_tnl_init_features(dev);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	dev_hold(dev);
15178c2ecf20Sopenharmony_ci	return 0;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_cicleanup_dst_cache_init:
15208c2ecf20Sopenharmony_ci	dst_cache_destroy(&tunnel->dst_cache);
15218c2ecf20Sopenharmony_cicleanup_alloc_pcpu_stats:
15228c2ecf20Sopenharmony_ci	free_percpu(dev->tstats);
15238c2ecf20Sopenharmony_ci	dev->tstats = NULL;
15248c2ecf20Sopenharmony_ci	return ret;
15258c2ecf20Sopenharmony_ci}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_init(struct net_device *dev)
15288c2ecf20Sopenharmony_ci{
15298c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel;
15308c2ecf20Sopenharmony_ci	int ret;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	ret = ip6gre_tunnel_init_common(dev);
15338c2ecf20Sopenharmony_ci	if (ret)
15348c2ecf20Sopenharmony_ci		return ret;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	tunnel = netdev_priv(dev);
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	if (tunnel->parms.collect_md)
15398c2ecf20Sopenharmony_ci		return 0;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	memcpy(dev->dev_addr, &tunnel->parms.laddr, sizeof(struct in6_addr));
15428c2ecf20Sopenharmony_ci	memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr));
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	if (ipv6_addr_any(&tunnel->parms.raddr))
15458c2ecf20Sopenharmony_ci		dev->header_ops = &ip6gre_header_ops;
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci	return 0;
15488c2ecf20Sopenharmony_ci}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic void ip6gre_fb_tunnel_init(struct net_device *dev)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel = netdev_priv(dev);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	tunnel->dev = dev;
15558c2ecf20Sopenharmony_ci	tunnel->net = dev_net(dev);
15568c2ecf20Sopenharmony_ci	strcpy(tunnel->parms.name, dev->name);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	tunnel->hlen		= sizeof(struct ipv6hdr) + 4;
15598c2ecf20Sopenharmony_ci}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_cistatic struct inet6_protocol ip6gre_protocol __read_mostly = {
15628c2ecf20Sopenharmony_ci	.handler     = gre_rcv,
15638c2ecf20Sopenharmony_ci	.err_handler = ip6gre_err,
15648c2ecf20Sopenharmony_ci	.flags       = INET6_PROTO_FINAL,
15658c2ecf20Sopenharmony_ci};
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_cistatic void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
15688c2ecf20Sopenharmony_ci{
15698c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
15708c2ecf20Sopenharmony_ci	struct net_device *dev, *aux;
15718c2ecf20Sopenharmony_ci	int prio;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	for_each_netdev_safe(net, dev, aux)
15748c2ecf20Sopenharmony_ci		if (dev->rtnl_link_ops == &ip6gre_link_ops ||
15758c2ecf20Sopenharmony_ci		    dev->rtnl_link_ops == &ip6gre_tap_ops ||
15768c2ecf20Sopenharmony_ci		    dev->rtnl_link_ops == &ip6erspan_tap_ops)
15778c2ecf20Sopenharmony_ci			unregister_netdevice_queue(dev, head);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	for (prio = 0; prio < 4; prio++) {
15808c2ecf20Sopenharmony_ci		int h;
15818c2ecf20Sopenharmony_ci		for (h = 0; h < IP6_GRE_HASH_SIZE; h++) {
15828c2ecf20Sopenharmony_ci			struct ip6_tnl *t;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci			t = rtnl_dereference(ign->tunnels[prio][h]);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci			while (t) {
15878c2ecf20Sopenharmony_ci				/* If dev is in the same netns, it has already
15888c2ecf20Sopenharmony_ci				 * been added to the list by the previous loop.
15898c2ecf20Sopenharmony_ci				 */
15908c2ecf20Sopenharmony_ci				if (!net_eq(dev_net(t->dev), net))
15918c2ecf20Sopenharmony_ci					unregister_netdevice_queue(t->dev,
15928c2ecf20Sopenharmony_ci								   head);
15938c2ecf20Sopenharmony_ci				t = rtnl_dereference(t->next);
15948c2ecf20Sopenharmony_ci			}
15958c2ecf20Sopenharmony_ci		}
15968c2ecf20Sopenharmony_ci	}
15978c2ecf20Sopenharmony_ci}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_cistatic int __net_init ip6gre_init_net(struct net *net)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
16028c2ecf20Sopenharmony_ci	struct net_device *ndev;
16038c2ecf20Sopenharmony_ci	int err;
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	if (!net_has_fallback_tunnels(net))
16068c2ecf20Sopenharmony_ci		return 0;
16078c2ecf20Sopenharmony_ci	ndev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0",
16088c2ecf20Sopenharmony_ci			    NET_NAME_UNKNOWN, ip6gre_tunnel_setup);
16098c2ecf20Sopenharmony_ci	if (!ndev) {
16108c2ecf20Sopenharmony_ci		err = -ENOMEM;
16118c2ecf20Sopenharmony_ci		goto err_alloc_dev;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci	ign->fb_tunnel_dev = ndev;
16148c2ecf20Sopenharmony_ci	dev_net_set(ign->fb_tunnel_dev, net);
16158c2ecf20Sopenharmony_ci	/* FB netdevice is special: we have one, and only one per netns.
16168c2ecf20Sopenharmony_ci	 * Allowing to move it to another netns is clearly unsafe.
16178c2ecf20Sopenharmony_ci	 */
16188c2ecf20Sopenharmony_ci	ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
16228c2ecf20Sopenharmony_ci	ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	err = register_netdev(ign->fb_tunnel_dev);
16258c2ecf20Sopenharmony_ci	if (err)
16268c2ecf20Sopenharmony_ci		goto err_reg_dev;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	rcu_assign_pointer(ign->tunnels_wc[0],
16298c2ecf20Sopenharmony_ci			   netdev_priv(ign->fb_tunnel_dev));
16308c2ecf20Sopenharmony_ci	return 0;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_cierr_reg_dev:
16338c2ecf20Sopenharmony_ci	free_netdev(ndev);
16348c2ecf20Sopenharmony_cierr_alloc_dev:
16358c2ecf20Sopenharmony_ci	return err;
16368c2ecf20Sopenharmony_ci}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_cistatic void __net_exit ip6gre_exit_batch_net(struct list_head *net_list)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	struct net *net;
16418c2ecf20Sopenharmony_ci	LIST_HEAD(list);
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	rtnl_lock();
16448c2ecf20Sopenharmony_ci	list_for_each_entry(net, net_list, exit_list)
16458c2ecf20Sopenharmony_ci		ip6gre_destroy_tunnels(net, &list);
16468c2ecf20Sopenharmony_ci	unregister_netdevice_many(&list);
16478c2ecf20Sopenharmony_ci	rtnl_unlock();
16488c2ecf20Sopenharmony_ci}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_cistatic struct pernet_operations ip6gre_net_ops = {
16518c2ecf20Sopenharmony_ci	.init = ip6gre_init_net,
16528c2ecf20Sopenharmony_ci	.exit_batch = ip6gre_exit_batch_net,
16538c2ecf20Sopenharmony_ci	.id   = &ip6gre_net_id,
16548c2ecf20Sopenharmony_ci	.size = sizeof(struct ip6gre_net),
16558c2ecf20Sopenharmony_ci};
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_cistatic int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[],
16588c2ecf20Sopenharmony_ci				  struct netlink_ext_ack *extack)
16598c2ecf20Sopenharmony_ci{
16608c2ecf20Sopenharmony_ci	__be16 flags;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	if (!data)
16638c2ecf20Sopenharmony_ci		return 0;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	flags = 0;
16668c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
16678c2ecf20Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
16688c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
16698c2ecf20Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
16708c2ecf20Sopenharmony_ci	if (flags & (GRE_VERSION|GRE_ROUTING))
16718c2ecf20Sopenharmony_ci		return -EINVAL;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	return 0;
16748c2ecf20Sopenharmony_ci}
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_cistatic int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[],
16778c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
16788c2ecf20Sopenharmony_ci{
16798c2ecf20Sopenharmony_ci	struct in6_addr daddr;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
16828c2ecf20Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
16838c2ecf20Sopenharmony_ci			return -EINVAL;
16848c2ecf20Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
16858c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
16868c2ecf20Sopenharmony_ci	}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	if (!data)
16898c2ecf20Sopenharmony_ci		goto out;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_REMOTE]) {
16928c2ecf20Sopenharmony_ci		daddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]);
16938c2ecf20Sopenharmony_ci		if (ipv6_addr_any(&daddr))
16948c2ecf20Sopenharmony_ci			return -EINVAL;
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ciout:
16988c2ecf20Sopenharmony_ci	return ip6gre_tunnel_validate(tb, data, extack);
16998c2ecf20Sopenharmony_ci}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_cistatic int ip6erspan_tap_validate(struct nlattr *tb[], struct nlattr *data[],
17028c2ecf20Sopenharmony_ci				  struct netlink_ext_ack *extack)
17038c2ecf20Sopenharmony_ci{
17048c2ecf20Sopenharmony_ci	__be16 flags = 0;
17058c2ecf20Sopenharmony_ci	int ret, ver = 0;
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	if (!data)
17088c2ecf20Sopenharmony_ci		return 0;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	ret = ip6gre_tap_validate(tb, data, extack);
17118c2ecf20Sopenharmony_ci	if (ret)
17128c2ecf20Sopenharmony_ci		return ret;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	/* ERSPAN should only have GRE sequence and key flag */
17158c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
17168c2ecf20Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
17178c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
17188c2ecf20Sopenharmony_ci		flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
17198c2ecf20Sopenharmony_ci	if (!data[IFLA_GRE_COLLECT_METADATA] &&
17208c2ecf20Sopenharmony_ci	    flags != (GRE_SEQ | GRE_KEY))
17218c2ecf20Sopenharmony_ci		return -EINVAL;
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	/* ERSPAN Session ID only has 10-bit. Since we reuse
17248c2ecf20Sopenharmony_ci	 * 32-bit key field as ID, check it's range.
17258c2ecf20Sopenharmony_ci	 */
17268c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_IKEY] &&
17278c2ecf20Sopenharmony_ci	    (ntohl(nla_get_be32(data[IFLA_GRE_IKEY])) & ~ID_MASK))
17288c2ecf20Sopenharmony_ci		return -EINVAL;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_OKEY] &&
17318c2ecf20Sopenharmony_ci	    (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK))
17328c2ecf20Sopenharmony_ci		return -EINVAL;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ERSPAN_VER]) {
17358c2ecf20Sopenharmony_ci		ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
17368c2ecf20Sopenharmony_ci		if (ver != 1 && ver != 2)
17378c2ecf20Sopenharmony_ci			return -EINVAL;
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	if (ver == 1) {
17418c2ecf20Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_INDEX]) {
17428c2ecf20Sopenharmony_ci			u32 index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci			if (index & ~INDEX_MASK)
17458c2ecf20Sopenharmony_ci				return -EINVAL;
17468c2ecf20Sopenharmony_ci		}
17478c2ecf20Sopenharmony_ci	} else if (ver == 2) {
17488c2ecf20Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_DIR]) {
17498c2ecf20Sopenharmony_ci			u16 dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci			if (dir & ~(DIR_MASK >> DIR_OFFSET))
17528c2ecf20Sopenharmony_ci				return -EINVAL;
17538c2ecf20Sopenharmony_ci		}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_HWID]) {
17568c2ecf20Sopenharmony_ci			u16 hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci			if (hwid & ~(HWID_MASK >> HWID_OFFSET))
17598c2ecf20Sopenharmony_ci				return -EINVAL;
17608c2ecf20Sopenharmony_ci		}
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	return 0;
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic void ip6erspan_set_version(struct nlattr *data[],
17678c2ecf20Sopenharmony_ci				  struct __ip6_tnl_parm *parms)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	if (!data)
17708c2ecf20Sopenharmony_ci		return;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	parms->erspan_ver = 1;
17738c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ERSPAN_VER])
17748c2ecf20Sopenharmony_ci		parms->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	if (parms->erspan_ver == 1) {
17778c2ecf20Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_INDEX])
17788c2ecf20Sopenharmony_ci			parms->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]);
17798c2ecf20Sopenharmony_ci	} else if (parms->erspan_ver == 2) {
17808c2ecf20Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_DIR])
17818c2ecf20Sopenharmony_ci			parms->dir = nla_get_u8(data[IFLA_GRE_ERSPAN_DIR]);
17828c2ecf20Sopenharmony_ci		if (data[IFLA_GRE_ERSPAN_HWID])
17838c2ecf20Sopenharmony_ci			parms->hwid = nla_get_u16(data[IFLA_GRE_ERSPAN_HWID]);
17848c2ecf20Sopenharmony_ci	}
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistatic void ip6gre_netlink_parms(struct nlattr *data[],
17888c2ecf20Sopenharmony_ci				struct __ip6_tnl_parm *parms)
17898c2ecf20Sopenharmony_ci{
17908c2ecf20Sopenharmony_ci	memset(parms, 0, sizeof(*parms));
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	if (!data)
17938c2ecf20Sopenharmony_ci		return;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_LINK])
17968c2ecf20Sopenharmony_ci		parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_IFLAGS])
17998c2ecf20Sopenharmony_ci		parms->i_flags = gre_flags_to_tnl_flags(
18008c2ecf20Sopenharmony_ci				nla_get_be16(data[IFLA_GRE_IFLAGS]));
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_OFLAGS])
18038c2ecf20Sopenharmony_ci		parms->o_flags = gre_flags_to_tnl_flags(
18048c2ecf20Sopenharmony_ci				nla_get_be16(data[IFLA_GRE_OFLAGS]));
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_IKEY])
18078c2ecf20Sopenharmony_ci		parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_OKEY])
18108c2ecf20Sopenharmony_ci		parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_LOCAL])
18138c2ecf20Sopenharmony_ci		parms->laddr = nla_get_in6_addr(data[IFLA_GRE_LOCAL]);
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_REMOTE])
18168c2ecf20Sopenharmony_ci		parms->raddr = nla_get_in6_addr(data[IFLA_GRE_REMOTE]);
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_TTL])
18198c2ecf20Sopenharmony_ci		parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]);
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_LIMIT])
18228c2ecf20Sopenharmony_ci		parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]);
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_FLOWINFO])
18258c2ecf20Sopenharmony_ci		parms->flowinfo = nla_get_be32(data[IFLA_GRE_FLOWINFO]);
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_FLAGS])
18288c2ecf20Sopenharmony_ci		parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]);
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_FWMARK])
18318c2ecf20Sopenharmony_ci		parms->fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_COLLECT_METADATA])
18348c2ecf20Sopenharmony_ci		parms->collect_md = true;
18358c2ecf20Sopenharmony_ci}
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_cistatic int ip6gre_tap_init(struct net_device *dev)
18388c2ecf20Sopenharmony_ci{
18398c2ecf20Sopenharmony_ci	int ret;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	ret = ip6gre_tunnel_init_common(dev);
18428c2ecf20Sopenharmony_ci	if (ret)
18438c2ecf20Sopenharmony_ci		return ret;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	return 0;
18488c2ecf20Sopenharmony_ci}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_cistatic const struct net_device_ops ip6gre_tap_netdev_ops = {
18518c2ecf20Sopenharmony_ci	.ndo_init = ip6gre_tap_init,
18528c2ecf20Sopenharmony_ci	.ndo_uninit = ip6gre_tunnel_uninit,
18538c2ecf20Sopenharmony_ci	.ndo_start_xmit = ip6gre_tunnel_xmit,
18548c2ecf20Sopenharmony_ci	.ndo_set_mac_address = eth_mac_addr,
18558c2ecf20Sopenharmony_ci	.ndo_validate_addr = eth_validate_addr,
18568c2ecf20Sopenharmony_ci	.ndo_change_mtu = ip6_tnl_change_mtu,
18578c2ecf20Sopenharmony_ci	.ndo_get_stats64 = ip_tunnel_get_stats64,
18588c2ecf20Sopenharmony_ci	.ndo_get_iflink = ip6_tnl_get_iflink,
18598c2ecf20Sopenharmony_ci};
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_cistatic int ip6erspan_calc_hlen(struct ip6_tnl *tunnel)
18628c2ecf20Sopenharmony_ci{
18638c2ecf20Sopenharmony_ci	int t_hlen;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	tunnel->tun_hlen = 8;
18668c2ecf20Sopenharmony_ci	tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
18678c2ecf20Sopenharmony_ci		       erspan_hdr_len(tunnel->parms.erspan_ver);
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
18708c2ecf20Sopenharmony_ci	tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;
18718c2ecf20Sopenharmony_ci	return t_hlen;
18728c2ecf20Sopenharmony_ci}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_cistatic int ip6erspan_tap_init(struct net_device *dev)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	struct ip6_tnl *tunnel;
18778c2ecf20Sopenharmony_ci	int t_hlen;
18788c2ecf20Sopenharmony_ci	int ret;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	tunnel = netdev_priv(dev);
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	tunnel->dev = dev;
18838c2ecf20Sopenharmony_ci	tunnel->net = dev_net(dev);
18848c2ecf20Sopenharmony_ci	strcpy(tunnel->parms.name, dev->name);
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
18878c2ecf20Sopenharmony_ci	if (!dev->tstats)
18888c2ecf20Sopenharmony_ci		return -ENOMEM;
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	ret = dst_cache_init(&tunnel->dst_cache, GFP_KERNEL);
18918c2ecf20Sopenharmony_ci	if (ret)
18928c2ecf20Sopenharmony_ci		goto cleanup_alloc_pcpu_stats;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	ret = gro_cells_init(&tunnel->gro_cells, dev);
18958c2ecf20Sopenharmony_ci	if (ret)
18968c2ecf20Sopenharmony_ci		goto cleanup_dst_cache_init;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	t_hlen = ip6erspan_calc_hlen(tunnel);
18998c2ecf20Sopenharmony_ci	dev->mtu = ETH_DATA_LEN - t_hlen;
19008c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_ETHER)
19018c2ecf20Sopenharmony_ci		dev->mtu -= ETH_HLEN;
19028c2ecf20Sopenharmony_ci	if (!(tunnel->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
19038c2ecf20Sopenharmony_ci		dev->mtu -= 8;
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
19068c2ecf20Sopenharmony_ci	ip6erspan_tnl_link_config(tunnel, 1);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	dev_hold(dev);
19098c2ecf20Sopenharmony_ci	return 0;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_cicleanup_dst_cache_init:
19128c2ecf20Sopenharmony_ci	dst_cache_destroy(&tunnel->dst_cache);
19138c2ecf20Sopenharmony_cicleanup_alloc_pcpu_stats:
19148c2ecf20Sopenharmony_ci	free_percpu(dev->tstats);
19158c2ecf20Sopenharmony_ci	dev->tstats = NULL;
19168c2ecf20Sopenharmony_ci	return ret;
19178c2ecf20Sopenharmony_ci}
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_cistatic const struct net_device_ops ip6erspan_netdev_ops = {
19208c2ecf20Sopenharmony_ci	.ndo_init =		ip6erspan_tap_init,
19218c2ecf20Sopenharmony_ci	.ndo_uninit =		ip6erspan_tunnel_uninit,
19228c2ecf20Sopenharmony_ci	.ndo_start_xmit =	ip6erspan_tunnel_xmit,
19238c2ecf20Sopenharmony_ci	.ndo_set_mac_address =	eth_mac_addr,
19248c2ecf20Sopenharmony_ci	.ndo_validate_addr =	eth_validate_addr,
19258c2ecf20Sopenharmony_ci	.ndo_change_mtu =	ip6_tnl_change_mtu,
19268c2ecf20Sopenharmony_ci	.ndo_get_stats64 =	ip_tunnel_get_stats64,
19278c2ecf20Sopenharmony_ci	.ndo_get_iflink =	ip6_tnl_get_iflink,
19288c2ecf20Sopenharmony_ci};
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_cistatic void ip6gre_tap_setup(struct net_device *dev)
19318c2ecf20Sopenharmony_ci{
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	ether_setup(dev);
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	dev->max_mtu = 0;
19368c2ecf20Sopenharmony_ci	dev->netdev_ops = &ip6gre_tap_netdev_ops;
19378c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
19388c2ecf20Sopenharmony_ci	dev->priv_destructor = ip6gre_dev_free;
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
19418c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
19428c2ecf20Sopenharmony_ci	netif_keep_dst(dev);
19438c2ecf20Sopenharmony_ci}
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_cistatic bool ip6gre_netlink_encap_parms(struct nlattr *data[],
19468c2ecf20Sopenharmony_ci				       struct ip_tunnel_encap *ipencap)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci	bool ret = false;
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	memset(ipencap, 0, sizeof(*ipencap));
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	if (!data)
19538c2ecf20Sopenharmony_ci		return ret;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_TYPE]) {
19568c2ecf20Sopenharmony_ci		ret = true;
19578c2ecf20Sopenharmony_ci		ipencap->type = nla_get_u16(data[IFLA_GRE_ENCAP_TYPE]);
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_FLAGS]) {
19618c2ecf20Sopenharmony_ci		ret = true;
19628c2ecf20Sopenharmony_ci		ipencap->flags = nla_get_u16(data[IFLA_GRE_ENCAP_FLAGS]);
19638c2ecf20Sopenharmony_ci	}
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_SPORT]) {
19668c2ecf20Sopenharmony_ci		ret = true;
19678c2ecf20Sopenharmony_ci		ipencap->sport = nla_get_be16(data[IFLA_GRE_ENCAP_SPORT]);
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	if (data[IFLA_GRE_ENCAP_DPORT]) {
19718c2ecf20Sopenharmony_ci		ret = true;
19728c2ecf20Sopenharmony_ci		ipencap->dport = nla_get_be16(data[IFLA_GRE_ENCAP_DPORT]);
19738c2ecf20Sopenharmony_ci	}
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	return ret;
19768c2ecf20Sopenharmony_ci}
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_cistatic int ip6gre_newlink_common(struct net *src_net, struct net_device *dev,
19798c2ecf20Sopenharmony_ci				 struct nlattr *tb[], struct nlattr *data[],
19808c2ecf20Sopenharmony_ci				 struct netlink_ext_ack *extack)
19818c2ecf20Sopenharmony_ci{
19828c2ecf20Sopenharmony_ci	struct ip6_tnl *nt;
19838c2ecf20Sopenharmony_ci	struct ip_tunnel_encap ipencap;
19848c2ecf20Sopenharmony_ci	int err;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	nt = netdev_priv(dev);
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	if (ip6gre_netlink_encap_parms(data, &ipencap)) {
19898c2ecf20Sopenharmony_ci		int err = ip6_tnl_encap_setup(nt, &ipencap);
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci		if (err < 0)
19928c2ecf20Sopenharmony_ci			return err;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
19968c2ecf20Sopenharmony_ci		eth_hw_addr_random(dev);
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	nt->dev = dev;
19998c2ecf20Sopenharmony_ci	nt->net = dev_net(dev);
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
20028c2ecf20Sopenharmony_ci	if (err)
20038c2ecf20Sopenharmony_ci		goto out;
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	if (tb[IFLA_MTU])
20068c2ecf20Sopenharmony_ci		ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ciout:
20098c2ecf20Sopenharmony_ci	return err;
20108c2ecf20Sopenharmony_ci}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_cistatic int ip6gre_newlink(struct net *src_net, struct net_device *dev,
20138c2ecf20Sopenharmony_ci			  struct nlattr *tb[], struct nlattr *data[],
20148c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
20158c2ecf20Sopenharmony_ci{
20168c2ecf20Sopenharmony_ci	struct ip6_tnl *nt = netdev_priv(dev);
20178c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
20188c2ecf20Sopenharmony_ci	struct ip6gre_net *ign;
20198c2ecf20Sopenharmony_ci	int err;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	ip6gre_netlink_parms(data, &nt->parms);
20228c2ecf20Sopenharmony_ci	ign = net_generic(net, ip6gre_net_id);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	if (nt->parms.collect_md) {
20258c2ecf20Sopenharmony_ci		if (rtnl_dereference(ign->collect_md_tun))
20268c2ecf20Sopenharmony_ci			return -EEXIST;
20278c2ecf20Sopenharmony_ci	} else {
20288c2ecf20Sopenharmony_ci		if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
20298c2ecf20Sopenharmony_ci			return -EEXIST;
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
20338c2ecf20Sopenharmony_ci	if (!err) {
20348c2ecf20Sopenharmony_ci		ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
20358c2ecf20Sopenharmony_ci		ip6gre_tunnel_link_md(ign, nt);
20368c2ecf20Sopenharmony_ci		ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
20378c2ecf20Sopenharmony_ci	}
20388c2ecf20Sopenharmony_ci	return err;
20398c2ecf20Sopenharmony_ci}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_cistatic struct ip6_tnl *
20428c2ecf20Sopenharmony_ciip6gre_changelink_common(struct net_device *dev, struct nlattr *tb[],
20438c2ecf20Sopenharmony_ci			 struct nlattr *data[], struct __ip6_tnl_parm *p_p,
20448c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
20458c2ecf20Sopenharmony_ci{
20468c2ecf20Sopenharmony_ci	struct ip6_tnl *t, *nt = netdev_priv(dev);
20478c2ecf20Sopenharmony_ci	struct net *net = nt->net;
20488c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
20498c2ecf20Sopenharmony_ci	struct ip_tunnel_encap ipencap;
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	if (dev == ign->fb_tunnel_dev)
20528c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	if (ip6gre_netlink_encap_parms(data, &ipencap)) {
20558c2ecf20Sopenharmony_ci		int err = ip6_tnl_encap_setup(nt, &ipencap);
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci		if (err < 0)
20588c2ecf20Sopenharmony_ci			return ERR_PTR(err);
20598c2ecf20Sopenharmony_ci	}
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	ip6gre_netlink_parms(data, p_p);
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	t = ip6gre_tunnel_locate(net, p_p, 0);
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	if (t) {
20668c2ecf20Sopenharmony_ci		if (t->dev != dev)
20678c2ecf20Sopenharmony_ci			return ERR_PTR(-EEXIST);
20688c2ecf20Sopenharmony_ci	} else {
20698c2ecf20Sopenharmony_ci		t = nt;
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	return t;
20738c2ecf20Sopenharmony_ci}
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_cistatic int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
20768c2ecf20Sopenharmony_ci			     struct nlattr *data[],
20778c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
20788c2ecf20Sopenharmony_ci{
20798c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
20808c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
20818c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm p;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
20848c2ecf20Sopenharmony_ci	if (IS_ERR(t))
20858c2ecf20Sopenharmony_ci		return PTR_ERR(t);
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink_md(ign, t);
20888c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
20898c2ecf20Sopenharmony_ci	ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
20908c2ecf20Sopenharmony_ci	ip6gre_tunnel_link_md(ign, t);
20918c2ecf20Sopenharmony_ci	ip6gre_tunnel_link(ign, t);
20928c2ecf20Sopenharmony_ci	return 0;
20938c2ecf20Sopenharmony_ci}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_cistatic void ip6gre_dellink(struct net_device *dev, struct list_head *head)
20968c2ecf20Sopenharmony_ci{
20978c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
20988c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci	if (dev != ign->fb_tunnel_dev)
21018c2ecf20Sopenharmony_ci		unregister_netdevice_queue(dev, head);
21028c2ecf20Sopenharmony_ci}
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_cistatic size_t ip6gre_get_size(const struct net_device *dev)
21058c2ecf20Sopenharmony_ci{
21068c2ecf20Sopenharmony_ci	return
21078c2ecf20Sopenharmony_ci		/* IFLA_GRE_LINK */
21088c2ecf20Sopenharmony_ci		nla_total_size(4) +
21098c2ecf20Sopenharmony_ci		/* IFLA_GRE_IFLAGS */
21108c2ecf20Sopenharmony_ci		nla_total_size(2) +
21118c2ecf20Sopenharmony_ci		/* IFLA_GRE_OFLAGS */
21128c2ecf20Sopenharmony_ci		nla_total_size(2) +
21138c2ecf20Sopenharmony_ci		/* IFLA_GRE_IKEY */
21148c2ecf20Sopenharmony_ci		nla_total_size(4) +
21158c2ecf20Sopenharmony_ci		/* IFLA_GRE_OKEY */
21168c2ecf20Sopenharmony_ci		nla_total_size(4) +
21178c2ecf20Sopenharmony_ci		/* IFLA_GRE_LOCAL */
21188c2ecf20Sopenharmony_ci		nla_total_size(sizeof(struct in6_addr)) +
21198c2ecf20Sopenharmony_ci		/* IFLA_GRE_REMOTE */
21208c2ecf20Sopenharmony_ci		nla_total_size(sizeof(struct in6_addr)) +
21218c2ecf20Sopenharmony_ci		/* IFLA_GRE_TTL */
21228c2ecf20Sopenharmony_ci		nla_total_size(1) +
21238c2ecf20Sopenharmony_ci		/* IFLA_GRE_ENCAP_LIMIT */
21248c2ecf20Sopenharmony_ci		nla_total_size(1) +
21258c2ecf20Sopenharmony_ci		/* IFLA_GRE_FLOWINFO */
21268c2ecf20Sopenharmony_ci		nla_total_size(4) +
21278c2ecf20Sopenharmony_ci		/* IFLA_GRE_FLAGS */
21288c2ecf20Sopenharmony_ci		nla_total_size(4) +
21298c2ecf20Sopenharmony_ci		/* IFLA_GRE_ENCAP_TYPE */
21308c2ecf20Sopenharmony_ci		nla_total_size(2) +
21318c2ecf20Sopenharmony_ci		/* IFLA_GRE_ENCAP_FLAGS */
21328c2ecf20Sopenharmony_ci		nla_total_size(2) +
21338c2ecf20Sopenharmony_ci		/* IFLA_GRE_ENCAP_SPORT */
21348c2ecf20Sopenharmony_ci		nla_total_size(2) +
21358c2ecf20Sopenharmony_ci		/* IFLA_GRE_ENCAP_DPORT */
21368c2ecf20Sopenharmony_ci		nla_total_size(2) +
21378c2ecf20Sopenharmony_ci		/* IFLA_GRE_COLLECT_METADATA */
21388c2ecf20Sopenharmony_ci		nla_total_size(0) +
21398c2ecf20Sopenharmony_ci		/* IFLA_GRE_FWMARK */
21408c2ecf20Sopenharmony_ci		nla_total_size(4) +
21418c2ecf20Sopenharmony_ci		/* IFLA_GRE_ERSPAN_INDEX */
21428c2ecf20Sopenharmony_ci		nla_total_size(4) +
21438c2ecf20Sopenharmony_ci		0;
21448c2ecf20Sopenharmony_ci}
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_cistatic int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
21478c2ecf20Sopenharmony_ci{
21488c2ecf20Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
21498c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm *p = &t->parms;
21508c2ecf20Sopenharmony_ci	__be16 o_flags = p->o_flags;
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	if (p->erspan_ver == 1 || p->erspan_ver == 2) {
21538c2ecf20Sopenharmony_ci		if (!p->collect_md)
21548c2ecf20Sopenharmony_ci			o_flags |= TUNNEL_KEY;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci		if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, p->erspan_ver))
21578c2ecf20Sopenharmony_ci			goto nla_put_failure;
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci		if (p->erspan_ver == 1) {
21608c2ecf20Sopenharmony_ci			if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, p->index))
21618c2ecf20Sopenharmony_ci				goto nla_put_failure;
21628c2ecf20Sopenharmony_ci		} else {
21638c2ecf20Sopenharmony_ci			if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, p->dir))
21648c2ecf20Sopenharmony_ci				goto nla_put_failure;
21658c2ecf20Sopenharmony_ci			if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, p->hwid))
21668c2ecf20Sopenharmony_ci				goto nla_put_failure;
21678c2ecf20Sopenharmony_ci		}
21688c2ecf20Sopenharmony_ci	}
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
21718c2ecf20Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_IFLAGS,
21728c2ecf20Sopenharmony_ci			 gre_tnl_flags_to_gre_flags(p->i_flags)) ||
21738c2ecf20Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_OFLAGS,
21748c2ecf20Sopenharmony_ci			 gre_tnl_flags_to_gre_flags(o_flags)) ||
21758c2ecf20Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
21768c2ecf20Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
21778c2ecf20Sopenharmony_ci	    nla_put_in6_addr(skb, IFLA_GRE_LOCAL, &p->laddr) ||
21788c2ecf20Sopenharmony_ci	    nla_put_in6_addr(skb, IFLA_GRE_REMOTE, &p->raddr) ||
21798c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) ||
21808c2ecf20Sopenharmony_ci	    nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
21818c2ecf20Sopenharmony_ci	    nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) ||
21828c2ecf20Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags) ||
21838c2ecf20Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark))
21848c2ecf20Sopenharmony_ci		goto nla_put_failure;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE,
21878c2ecf20Sopenharmony_ci			t->encap.type) ||
21888c2ecf20Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_ENCAP_SPORT,
21898c2ecf20Sopenharmony_ci			 t->encap.sport) ||
21908c2ecf20Sopenharmony_ci	    nla_put_be16(skb, IFLA_GRE_ENCAP_DPORT,
21918c2ecf20Sopenharmony_ci			 t->encap.dport) ||
21928c2ecf20Sopenharmony_ci	    nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS,
21938c2ecf20Sopenharmony_ci			t->encap.flags))
21948c2ecf20Sopenharmony_ci		goto nla_put_failure;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	if (p->collect_md) {
21978c2ecf20Sopenharmony_ci		if (nla_put_flag(skb, IFLA_GRE_COLLECT_METADATA))
21988c2ecf20Sopenharmony_ci			goto nla_put_failure;
21998c2ecf20Sopenharmony_ci	}
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	return 0;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_cinla_put_failure:
22048c2ecf20Sopenharmony_ci	return -EMSGSIZE;
22058c2ecf20Sopenharmony_ci}
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_cistatic const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = {
22088c2ecf20Sopenharmony_ci	[IFLA_GRE_LINK]        = { .type = NLA_U32 },
22098c2ecf20Sopenharmony_ci	[IFLA_GRE_IFLAGS]      = { .type = NLA_U16 },
22108c2ecf20Sopenharmony_ci	[IFLA_GRE_OFLAGS]      = { .type = NLA_U16 },
22118c2ecf20Sopenharmony_ci	[IFLA_GRE_IKEY]        = { .type = NLA_U32 },
22128c2ecf20Sopenharmony_ci	[IFLA_GRE_OKEY]        = { .type = NLA_U32 },
22138c2ecf20Sopenharmony_ci	[IFLA_GRE_LOCAL]       = { .len = sizeof_field(struct ipv6hdr, saddr) },
22148c2ecf20Sopenharmony_ci	[IFLA_GRE_REMOTE]      = { .len = sizeof_field(struct ipv6hdr, daddr) },
22158c2ecf20Sopenharmony_ci	[IFLA_GRE_TTL]         = { .type = NLA_U8 },
22168c2ecf20Sopenharmony_ci	[IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
22178c2ecf20Sopenharmony_ci	[IFLA_GRE_FLOWINFO]    = { .type = NLA_U32 },
22188c2ecf20Sopenharmony_ci	[IFLA_GRE_FLAGS]       = { .type = NLA_U32 },
22198c2ecf20Sopenharmony_ci	[IFLA_GRE_ENCAP_TYPE]   = { .type = NLA_U16 },
22208c2ecf20Sopenharmony_ci	[IFLA_GRE_ENCAP_FLAGS]  = { .type = NLA_U16 },
22218c2ecf20Sopenharmony_ci	[IFLA_GRE_ENCAP_SPORT]  = { .type = NLA_U16 },
22228c2ecf20Sopenharmony_ci	[IFLA_GRE_ENCAP_DPORT]  = { .type = NLA_U16 },
22238c2ecf20Sopenharmony_ci	[IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG },
22248c2ecf20Sopenharmony_ci	[IFLA_GRE_FWMARK]       = { .type = NLA_U32 },
22258c2ecf20Sopenharmony_ci	[IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 },
22268c2ecf20Sopenharmony_ci	[IFLA_GRE_ERSPAN_VER]	= { .type = NLA_U8 },
22278c2ecf20Sopenharmony_ci	[IFLA_GRE_ERSPAN_DIR]	= { .type = NLA_U8 },
22288c2ecf20Sopenharmony_ci	[IFLA_GRE_ERSPAN_HWID]	= { .type = NLA_U16 },
22298c2ecf20Sopenharmony_ci};
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_cistatic void ip6erspan_tap_setup(struct net_device *dev)
22328c2ecf20Sopenharmony_ci{
22338c2ecf20Sopenharmony_ci	ether_setup(dev);
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	dev->max_mtu = 0;
22368c2ecf20Sopenharmony_ci	dev->netdev_ops = &ip6erspan_netdev_ops;
22378c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
22388c2ecf20Sopenharmony_ci	dev->priv_destructor = ip6gre_dev_free;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
22418c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
22428c2ecf20Sopenharmony_ci	netif_keep_dst(dev);
22438c2ecf20Sopenharmony_ci}
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_cistatic int ip6erspan_newlink(struct net *src_net, struct net_device *dev,
22468c2ecf20Sopenharmony_ci			     struct nlattr *tb[], struct nlattr *data[],
22478c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
22488c2ecf20Sopenharmony_ci{
22498c2ecf20Sopenharmony_ci	struct ip6_tnl *nt = netdev_priv(dev);
22508c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
22518c2ecf20Sopenharmony_ci	struct ip6gre_net *ign;
22528c2ecf20Sopenharmony_ci	int err;
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	ip6gre_netlink_parms(data, &nt->parms);
22558c2ecf20Sopenharmony_ci	ip6erspan_set_version(data, &nt->parms);
22568c2ecf20Sopenharmony_ci	ign = net_generic(net, ip6gre_net_id);
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	if (nt->parms.collect_md) {
22598c2ecf20Sopenharmony_ci		if (rtnl_dereference(ign->collect_md_tun_erspan))
22608c2ecf20Sopenharmony_ci			return -EEXIST;
22618c2ecf20Sopenharmony_ci	} else {
22628c2ecf20Sopenharmony_ci		if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
22638c2ecf20Sopenharmony_ci			return -EEXIST;
22648c2ecf20Sopenharmony_ci	}
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	err = ip6gre_newlink_common(src_net, dev, tb, data, extack);
22678c2ecf20Sopenharmony_ci	if (!err) {
22688c2ecf20Sopenharmony_ci		ip6erspan_tnl_link_config(nt, !tb[IFLA_MTU]);
22698c2ecf20Sopenharmony_ci		ip6erspan_tunnel_link_md(ign, nt);
22708c2ecf20Sopenharmony_ci		ip6gre_tunnel_link(net_generic(net, ip6gre_net_id), nt);
22718c2ecf20Sopenharmony_ci	}
22728c2ecf20Sopenharmony_ci	return err;
22738c2ecf20Sopenharmony_ci}
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_cistatic void ip6erspan_tnl_link_config(struct ip6_tnl *t, int set_mtu)
22768c2ecf20Sopenharmony_ci{
22778c2ecf20Sopenharmony_ci	ip6gre_tnl_link_config_common(t);
22788c2ecf20Sopenharmony_ci	ip6gre_tnl_link_config_route(t, set_mtu, ip6erspan_calc_hlen(t));
22798c2ecf20Sopenharmony_ci}
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_cistatic int ip6erspan_tnl_change(struct ip6_tnl *t,
22828c2ecf20Sopenharmony_ci				const struct __ip6_tnl_parm *p, int set_mtu)
22838c2ecf20Sopenharmony_ci{
22848c2ecf20Sopenharmony_ci	ip6gre_tnl_copy_tnl_parm(t, p);
22858c2ecf20Sopenharmony_ci	ip6erspan_tnl_link_config(t, set_mtu);
22868c2ecf20Sopenharmony_ci	return 0;
22878c2ecf20Sopenharmony_ci}
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_cistatic int ip6erspan_changelink(struct net_device *dev, struct nlattr *tb[],
22908c2ecf20Sopenharmony_ci				struct nlattr *data[],
22918c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
22928c2ecf20Sopenharmony_ci{
22938c2ecf20Sopenharmony_ci	struct ip6gre_net *ign = net_generic(dev_net(dev), ip6gre_net_id);
22948c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm p;
22958c2ecf20Sopenharmony_ci	struct ip6_tnl *t;
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci	t = ip6gre_changelink_common(dev, tb, data, &p, extack);
22988c2ecf20Sopenharmony_ci	if (IS_ERR(t))
22998c2ecf20Sopenharmony_ci		return PTR_ERR(t);
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	ip6erspan_set_version(data, &p);
23028c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink_md(ign, t);
23038c2ecf20Sopenharmony_ci	ip6gre_tunnel_unlink(ign, t);
23048c2ecf20Sopenharmony_ci	ip6erspan_tnl_change(t, &p, !tb[IFLA_MTU]);
23058c2ecf20Sopenharmony_ci	ip6erspan_tunnel_link_md(ign, t);
23068c2ecf20Sopenharmony_ci	ip6gre_tunnel_link(ign, t);
23078c2ecf20Sopenharmony_ci	return 0;
23088c2ecf20Sopenharmony_ci}
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
23118c2ecf20Sopenharmony_ci	.kind		= "ip6gre",
23128c2ecf20Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
23138c2ecf20Sopenharmony_ci	.policy		= ip6gre_policy,
23148c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
23158c2ecf20Sopenharmony_ci	.setup		= ip6gre_tunnel_setup,
23168c2ecf20Sopenharmony_ci	.validate	= ip6gre_tunnel_validate,
23178c2ecf20Sopenharmony_ci	.newlink	= ip6gre_newlink,
23188c2ecf20Sopenharmony_ci	.changelink	= ip6gre_changelink,
23198c2ecf20Sopenharmony_ci	.dellink	= ip6gre_dellink,
23208c2ecf20Sopenharmony_ci	.get_size	= ip6gre_get_size,
23218c2ecf20Sopenharmony_ci	.fill_info	= ip6gre_fill_info,
23228c2ecf20Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
23238c2ecf20Sopenharmony_ci};
23248c2ecf20Sopenharmony_ci
23258c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6gre_tap_ops __read_mostly = {
23268c2ecf20Sopenharmony_ci	.kind		= "ip6gretap",
23278c2ecf20Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
23288c2ecf20Sopenharmony_ci	.policy		= ip6gre_policy,
23298c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
23308c2ecf20Sopenharmony_ci	.setup		= ip6gre_tap_setup,
23318c2ecf20Sopenharmony_ci	.validate	= ip6gre_tap_validate,
23328c2ecf20Sopenharmony_ci	.newlink	= ip6gre_newlink,
23338c2ecf20Sopenharmony_ci	.changelink	= ip6gre_changelink,
23348c2ecf20Sopenharmony_ci	.get_size	= ip6gre_get_size,
23358c2ecf20Sopenharmony_ci	.fill_info	= ip6gre_fill_info,
23368c2ecf20Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
23378c2ecf20Sopenharmony_ci};
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_cistatic struct rtnl_link_ops ip6erspan_tap_ops __read_mostly = {
23408c2ecf20Sopenharmony_ci	.kind		= "ip6erspan",
23418c2ecf20Sopenharmony_ci	.maxtype	= IFLA_GRE_MAX,
23428c2ecf20Sopenharmony_ci	.policy		= ip6gre_policy,
23438c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
23448c2ecf20Sopenharmony_ci	.setup		= ip6erspan_tap_setup,
23458c2ecf20Sopenharmony_ci	.validate	= ip6erspan_tap_validate,
23468c2ecf20Sopenharmony_ci	.newlink	= ip6erspan_newlink,
23478c2ecf20Sopenharmony_ci	.changelink	= ip6erspan_changelink,
23488c2ecf20Sopenharmony_ci	.get_size	= ip6gre_get_size,
23498c2ecf20Sopenharmony_ci	.fill_info	= ip6gre_fill_info,
23508c2ecf20Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
23518c2ecf20Sopenharmony_ci};
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci/*
23548c2ecf20Sopenharmony_ci *	And now the modules code and kernel interface.
23558c2ecf20Sopenharmony_ci */
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_cistatic int __init ip6gre_init(void)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	int err;
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	pr_info("GRE over IPv6 tunneling driver\n");
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	err = register_pernet_device(&ip6gre_net_ops);
23648c2ecf20Sopenharmony_ci	if (err < 0)
23658c2ecf20Sopenharmony_ci		return err;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE);
23688c2ecf20Sopenharmony_ci	if (err < 0) {
23698c2ecf20Sopenharmony_ci		pr_info("%s: can't add protocol\n", __func__);
23708c2ecf20Sopenharmony_ci		goto add_proto_failed;
23718c2ecf20Sopenharmony_ci	}
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	err = rtnl_link_register(&ip6gre_link_ops);
23748c2ecf20Sopenharmony_ci	if (err < 0)
23758c2ecf20Sopenharmony_ci		goto rtnl_link_failed;
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	err = rtnl_link_register(&ip6gre_tap_ops);
23788c2ecf20Sopenharmony_ci	if (err < 0)
23798c2ecf20Sopenharmony_ci		goto tap_ops_failed;
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	err = rtnl_link_register(&ip6erspan_tap_ops);
23828c2ecf20Sopenharmony_ci	if (err < 0)
23838c2ecf20Sopenharmony_ci		goto erspan_link_failed;
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_ciout:
23868c2ecf20Sopenharmony_ci	return err;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_cierspan_link_failed:
23898c2ecf20Sopenharmony_ci	rtnl_link_unregister(&ip6gre_tap_ops);
23908c2ecf20Sopenharmony_citap_ops_failed:
23918c2ecf20Sopenharmony_ci	rtnl_link_unregister(&ip6gre_link_ops);
23928c2ecf20Sopenharmony_cirtnl_link_failed:
23938c2ecf20Sopenharmony_ci	inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
23948c2ecf20Sopenharmony_ciadd_proto_failed:
23958c2ecf20Sopenharmony_ci	unregister_pernet_device(&ip6gre_net_ops);
23968c2ecf20Sopenharmony_ci	goto out;
23978c2ecf20Sopenharmony_ci}
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_cistatic void __exit ip6gre_fini(void)
24008c2ecf20Sopenharmony_ci{
24018c2ecf20Sopenharmony_ci	rtnl_link_unregister(&ip6gre_tap_ops);
24028c2ecf20Sopenharmony_ci	rtnl_link_unregister(&ip6gre_link_ops);
24038c2ecf20Sopenharmony_ci	rtnl_link_unregister(&ip6erspan_tap_ops);
24048c2ecf20Sopenharmony_ci	inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
24058c2ecf20Sopenharmony_ci	unregister_pernet_device(&ip6gre_net_ops);
24068c2ecf20Sopenharmony_ci}
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_cimodule_init(ip6gre_init);
24098c2ecf20Sopenharmony_cimodule_exit(ip6gre_fini);
24108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
24118c2ecf20Sopenharmony_ciMODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
24128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GRE over IPv6 tunneling device");
24138c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6gre");
24148c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6gretap");
24158c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("ip6erspan");
24168c2ecf20Sopenharmony_ciMODULE_ALIAS_NETDEV("ip6gre0");
2417