162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	IPv6 virtual tunneling interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Copyright (C) 2013 secunet Security Networks AG
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *	Author:
862306a36Sopenharmony_ci *	Steffen Klassert <steffen.klassert@secunet.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *	Based on:
1162306a36Sopenharmony_ci *	net/ipv6/ip6_tunnel.c
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/capability.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci#include <linux/sockios.h>
1962306a36Sopenharmony_ci#include <linux/icmp.h>
2062306a36Sopenharmony_ci#include <linux/if.h>
2162306a36Sopenharmony_ci#include <linux/in.h>
2262306a36Sopenharmony_ci#include <linux/ip.h>
2362306a36Sopenharmony_ci#include <linux/net.h>
2462306a36Sopenharmony_ci#include <linux/in6.h>
2562306a36Sopenharmony_ci#include <linux/netdevice.h>
2662306a36Sopenharmony_ci#include <linux/if_arp.h>
2762306a36Sopenharmony_ci#include <linux/icmpv6.h>
2862306a36Sopenharmony_ci#include <linux/init.h>
2962306a36Sopenharmony_ci#include <linux/route.h>
3062306a36Sopenharmony_ci#include <linux/rtnetlink.h>
3162306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h>
3262306a36Sopenharmony_ci#include <linux/slab.h>
3362306a36Sopenharmony_ci#include <linux/hash.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/uaccess.h>
3662306a36Sopenharmony_ci#include <linux/atomic.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <net/icmp.h>
3962306a36Sopenharmony_ci#include <net/ip.h>
4062306a36Sopenharmony_ci#include <net/ip_tunnels.h>
4162306a36Sopenharmony_ci#include <net/ipv6.h>
4262306a36Sopenharmony_ci#include <net/ip6_route.h>
4362306a36Sopenharmony_ci#include <net/addrconf.h>
4462306a36Sopenharmony_ci#include <net/ip6_tunnel.h>
4562306a36Sopenharmony_ci#include <net/xfrm.h>
4662306a36Sopenharmony_ci#include <net/net_namespace.h>
4762306a36Sopenharmony_ci#include <net/netns/generic.h>
4862306a36Sopenharmony_ci#include <linux/etherdevice.h>
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define IP6_VTI_HASH_SIZE_SHIFT  5
5162306a36Sopenharmony_ci#define IP6_VTI_HASH_SIZE (1 << IP6_VTI_HASH_SIZE_SHIFT)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return hash_32(hash, IP6_VTI_HASH_SIZE_SHIFT);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int vti6_dev_init(struct net_device *dev);
6162306a36Sopenharmony_cistatic void vti6_dev_setup(struct net_device *dev);
6262306a36Sopenharmony_cistatic struct rtnl_link_ops vti6_link_ops __read_mostly;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic unsigned int vti6_net_id __read_mostly;
6562306a36Sopenharmony_cistruct vti6_net {
6662306a36Sopenharmony_ci	/* the vti6 tunnel fallback device */
6762306a36Sopenharmony_ci	struct net_device *fb_tnl_dev;
6862306a36Sopenharmony_ci	/* lists for storing tunnels in use */
6962306a36Sopenharmony_ci	struct ip6_tnl __rcu *tnls_r_l[IP6_VTI_HASH_SIZE];
7062306a36Sopenharmony_ci	struct ip6_tnl __rcu *tnls_wc[1];
7162306a36Sopenharmony_ci	struct ip6_tnl __rcu **tnls[2];
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define for_each_vti6_tunnel_rcu(start) \
7562306a36Sopenharmony_ci	for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci * vti6_tnl_lookup - fetch tunnel matching the end-point addresses
7962306a36Sopenharmony_ci *   @net: network namespace
8062306a36Sopenharmony_ci *   @remote: the address of the tunnel exit-point
8162306a36Sopenharmony_ci *   @local: the address of the tunnel entry-point
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * Return:
8462306a36Sopenharmony_ci *   tunnel matching given end-points if found,
8562306a36Sopenharmony_ci *   else fallback tunnel if its device is up,
8662306a36Sopenharmony_ci *   else %NULL
8762306a36Sopenharmony_ci **/
8862306a36Sopenharmony_cistatic struct ip6_tnl *
8962306a36Sopenharmony_civti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
9062306a36Sopenharmony_ci		const struct in6_addr *local)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	unsigned int hash = HASH(remote, local);
9362306a36Sopenharmony_ci	struct ip6_tnl *t;
9462306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
9562306a36Sopenharmony_ci	struct in6_addr any;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
9862306a36Sopenharmony_ci		if (ipv6_addr_equal(local, &t->parms.laddr) &&
9962306a36Sopenharmony_ci		    ipv6_addr_equal(remote, &t->parms.raddr) &&
10062306a36Sopenharmony_ci		    (t->dev->flags & IFF_UP))
10162306a36Sopenharmony_ci			return t;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	memset(&any, 0, sizeof(any));
10562306a36Sopenharmony_ci	hash = HASH(&any, local);
10662306a36Sopenharmony_ci	for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
10762306a36Sopenharmony_ci		if (ipv6_addr_equal(local, &t->parms.laddr) &&
10862306a36Sopenharmony_ci		    (t->dev->flags & IFF_UP))
10962306a36Sopenharmony_ci			return t;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	hash = HASH(remote, &any);
11362306a36Sopenharmony_ci	for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
11462306a36Sopenharmony_ci		if (ipv6_addr_equal(remote, &t->parms.raddr) &&
11562306a36Sopenharmony_ci		    (t->dev->flags & IFF_UP))
11662306a36Sopenharmony_ci			return t;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	t = rcu_dereference(ip6n->tnls_wc[0]);
12062306a36Sopenharmony_ci	if (t && (t->dev->flags & IFF_UP))
12162306a36Sopenharmony_ci		return t;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return NULL;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * vti6_tnl_bucket - get head of list matching given tunnel parameters
12862306a36Sopenharmony_ci *   @ip6n: the private data for ip6_vti in the netns
12962306a36Sopenharmony_ci *   @p: parameters containing tunnel end-points
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * Description:
13262306a36Sopenharmony_ci *   vti6_tnl_bucket() returns the head of the list matching the
13362306a36Sopenharmony_ci *   &struct in6_addr entries laddr and raddr in @p.
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * Return: head of IPv6 tunnel list
13662306a36Sopenharmony_ci **/
13762306a36Sopenharmony_cistatic struct ip6_tnl __rcu **
13862306a36Sopenharmony_civti6_tnl_bucket(struct vti6_net *ip6n, const struct __ip6_tnl_parm *p)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	const struct in6_addr *remote = &p->raddr;
14162306a36Sopenharmony_ci	const struct in6_addr *local = &p->laddr;
14262306a36Sopenharmony_ci	unsigned int h = 0;
14362306a36Sopenharmony_ci	int prio = 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
14662306a36Sopenharmony_ci		prio = 1;
14762306a36Sopenharmony_ci		h = HASH(remote, local);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	return &ip6n->tnls[prio][h];
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void
15362306a36Sopenharmony_civti6_tnl_link(struct vti6_net *ip6n, struct ip6_tnl *t)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct ip6_tnl __rcu **tp = vti6_tnl_bucket(ip6n, &t->parms);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	rcu_assign_pointer(t->next, rtnl_dereference(*tp));
15862306a36Sopenharmony_ci	rcu_assign_pointer(*tp, t);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void
16262306a36Sopenharmony_civti6_tnl_unlink(struct vti6_net *ip6n, struct ip6_tnl *t)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct ip6_tnl __rcu **tp;
16562306a36Sopenharmony_ci	struct ip6_tnl *iter;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (tp = vti6_tnl_bucket(ip6n, &t->parms);
16862306a36Sopenharmony_ci	     (iter = rtnl_dereference(*tp)) != NULL;
16962306a36Sopenharmony_ci	     tp = &iter->next) {
17062306a36Sopenharmony_ci		if (t == iter) {
17162306a36Sopenharmony_ci			rcu_assign_pointer(*tp, t->next);
17262306a36Sopenharmony_ci			break;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void vti6_dev_free(struct net_device *dev)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	free_percpu(dev->tstats);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int vti6_tnl_create2(struct net_device *dev)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
18562306a36Sopenharmony_ci	struct net *net = dev_net(dev);
18662306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
18762306a36Sopenharmony_ci	int err;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	dev->rtnl_link_ops = &vti6_link_ops;
19062306a36Sopenharmony_ci	err = register_netdevice(dev);
19162306a36Sopenharmony_ci	if (err < 0)
19262306a36Sopenharmony_ci		goto out;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	strcpy(t->parms.name, dev->name);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	vti6_tnl_link(ip6n, t);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciout:
20162306a36Sopenharmony_ci	return err;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct net_device *dev;
20762306a36Sopenharmony_ci	struct ip6_tnl *t;
20862306a36Sopenharmony_ci	char name[IFNAMSIZ];
20962306a36Sopenharmony_ci	int err;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (p->name[0]) {
21262306a36Sopenharmony_ci		if (!dev_valid_name(p->name))
21362306a36Sopenharmony_ci			goto failed;
21462306a36Sopenharmony_ci		strscpy(name, p->name, IFNAMSIZ);
21562306a36Sopenharmony_ci	} else {
21662306a36Sopenharmony_ci		sprintf(name, "ip6_vti%%d");
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(*t), name, NET_NAME_UNKNOWN, vti6_dev_setup);
22062306a36Sopenharmony_ci	if (!dev)
22162306a36Sopenharmony_ci		goto failed;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	dev_net_set(dev, net);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	t = netdev_priv(dev);
22662306a36Sopenharmony_ci	t->parms = *p;
22762306a36Sopenharmony_ci	t->net = dev_net(dev);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	err = vti6_tnl_create2(dev);
23062306a36Sopenharmony_ci	if (err < 0)
23162306a36Sopenharmony_ci		goto failed_free;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return t;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cifailed_free:
23662306a36Sopenharmony_ci	free_netdev(dev);
23762306a36Sopenharmony_cifailed:
23862306a36Sopenharmony_ci	return NULL;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci * vti6_locate - find or create tunnel matching given parameters
24362306a36Sopenharmony_ci *   @net: network namespace
24462306a36Sopenharmony_ci *   @p: tunnel parameters
24562306a36Sopenharmony_ci *   @create: != 0 if allowed to create new tunnel if no match found
24662306a36Sopenharmony_ci *
24762306a36Sopenharmony_ci * Description:
24862306a36Sopenharmony_ci *   vti6_locate() first tries to locate an existing tunnel
24962306a36Sopenharmony_ci *   based on @parms. If this is unsuccessful, but @create is set a new
25062306a36Sopenharmony_ci *   tunnel device is created and registered for use.
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * Return:
25362306a36Sopenharmony_ci *   matching tunnel or NULL
25462306a36Sopenharmony_ci **/
25562306a36Sopenharmony_cistatic struct ip6_tnl *vti6_locate(struct net *net, struct __ip6_tnl_parm *p,
25662306a36Sopenharmony_ci				   int create)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	const struct in6_addr *remote = &p->raddr;
25962306a36Sopenharmony_ci	const struct in6_addr *local = &p->laddr;
26062306a36Sopenharmony_ci	struct ip6_tnl __rcu **tp;
26162306a36Sopenharmony_ci	struct ip6_tnl *t;
26262306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	for (tp = vti6_tnl_bucket(ip6n, p);
26562306a36Sopenharmony_ci	     (t = rtnl_dereference(*tp)) != NULL;
26662306a36Sopenharmony_ci	     tp = &t->next) {
26762306a36Sopenharmony_ci		if (ipv6_addr_equal(local, &t->parms.laddr) &&
26862306a36Sopenharmony_ci		    ipv6_addr_equal(remote, &t->parms.raddr)) {
26962306a36Sopenharmony_ci			if (create)
27062306a36Sopenharmony_ci				return NULL;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci			return t;
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	if (!create)
27662306a36Sopenharmony_ci		return NULL;
27762306a36Sopenharmony_ci	return vti6_tnl_create(net, p);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * vti6_dev_uninit - tunnel device uninitializer
28262306a36Sopenharmony_ci *   @dev: the device to be destroyed
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * Description:
28562306a36Sopenharmony_ci *   vti6_dev_uninit() removes tunnel from its list
28662306a36Sopenharmony_ci **/
28762306a36Sopenharmony_cistatic void vti6_dev_uninit(struct net_device *dev)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
29062306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(t->net, vti6_net_id);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (dev == ip6n->fb_tnl_dev)
29362306a36Sopenharmony_ci		RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
29462306a36Sopenharmony_ci	else
29562306a36Sopenharmony_ci		vti6_tnl_unlink(ip6n, t);
29662306a36Sopenharmony_ci	netdev_put(dev, &t->dev_tracker);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int vti6_input_proto(struct sk_buff *skb, int nexthdr, __be32 spi,
30062306a36Sopenharmony_ci			    int encap_type)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct ip6_tnl *t;
30362306a36Sopenharmony_ci	const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	rcu_read_lock();
30662306a36Sopenharmony_ci	t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr);
30762306a36Sopenharmony_ci	if (t) {
30862306a36Sopenharmony_ci		if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) {
30962306a36Sopenharmony_ci			rcu_read_unlock();
31062306a36Sopenharmony_ci			goto discard;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
31462306a36Sopenharmony_ci			rcu_read_unlock();
31562306a36Sopenharmony_ci			goto discard;
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		ipv6h = ipv6_hdr(skb);
31962306a36Sopenharmony_ci		if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) {
32062306a36Sopenharmony_ci			DEV_STATS_INC(t->dev, rx_dropped);
32162306a36Sopenharmony_ci			rcu_read_unlock();
32262306a36Sopenharmony_ci			goto discard;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		rcu_read_unlock();
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = t;
32862306a36Sopenharmony_ci		XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
32962306a36Sopenharmony_ci		XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
33062306a36Sopenharmony_ci		return xfrm_input(skb, nexthdr, spi, encap_type);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	rcu_read_unlock();
33362306a36Sopenharmony_ci	return -EINVAL;
33462306a36Sopenharmony_cidiscard:
33562306a36Sopenharmony_ci	kfree_skb(skb);
33662306a36Sopenharmony_ci	return 0;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int vti6_rcv(struct sk_buff *skb)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int nexthdr = skb_network_header(skb)[IP6CB(skb)->nhoff];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return vti6_input_proto(skb, nexthdr, 0, 0);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int vti6_rcv_cb(struct sk_buff *skb, int err)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	unsigned short family;
34962306a36Sopenharmony_ci	struct net_device *dev;
35062306a36Sopenharmony_ci	struct xfrm_state *x;
35162306a36Sopenharmony_ci	const struct xfrm_mode *inner_mode;
35262306a36Sopenharmony_ci	struct ip6_tnl *t = XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6;
35362306a36Sopenharmony_ci	u32 orig_mark = skb->mark;
35462306a36Sopenharmony_ci	int ret;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (!t)
35762306a36Sopenharmony_ci		return 1;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	dev = t->dev;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (err) {
36262306a36Sopenharmony_ci		DEV_STATS_INC(dev, rx_errors);
36362306a36Sopenharmony_ci		DEV_STATS_INC(dev, rx_dropped);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		return 0;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	x = xfrm_input_state(skb);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	inner_mode = &x->inner_mode;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (x->sel.family == AF_UNSPEC) {
37362306a36Sopenharmony_ci		inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
37462306a36Sopenharmony_ci		if (inner_mode == NULL) {
37562306a36Sopenharmony_ci			XFRM_INC_STATS(dev_net(skb->dev),
37662306a36Sopenharmony_ci				       LINUX_MIB_XFRMINSTATEMODEERROR);
37762306a36Sopenharmony_ci			return -EINVAL;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	family = inner_mode->family;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	skb->mark = be32_to_cpu(t->parms.i_key);
38462306a36Sopenharmony_ci	ret = xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family);
38562306a36Sopenharmony_ci	skb->mark = orig_mark;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (!ret)
38862306a36Sopenharmony_ci		return -EPERM;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	skb_scrub_packet(skb, !net_eq(t->net, dev_net(skb->dev)));
39162306a36Sopenharmony_ci	skb->dev = dev;
39262306a36Sopenharmony_ci	dev_sw_netstats_rx_add(dev, skb->len);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return 0;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/**
39862306a36Sopenharmony_ci * vti6_addr_conflict - compare packet addresses to tunnel's own
39962306a36Sopenharmony_ci *   @t: the outgoing tunnel device
40062306a36Sopenharmony_ci *   @hdr: IPv6 header from the incoming packet
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * Description:
40362306a36Sopenharmony_ci *   Avoid trivial tunneling loop by checking that tunnel exit-point
40462306a36Sopenharmony_ci *   doesn't match source of incoming packet.
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * Return:
40762306a36Sopenharmony_ci *   1 if conflict,
40862306a36Sopenharmony_ci *   0 else
40962306a36Sopenharmony_ci **/
41062306a36Sopenharmony_cistatic inline bool
41162306a36Sopenharmony_civti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic bool vti6_state_check(const struct xfrm_state *x,
41762306a36Sopenharmony_ci			     const struct in6_addr *dst,
41862306a36Sopenharmony_ci			     const struct in6_addr *src)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	xfrm_address_t *daddr = (xfrm_address_t *)dst;
42162306a36Sopenharmony_ci	xfrm_address_t *saddr = (xfrm_address_t *)src;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* if there is no transform then this tunnel is not functional.
42462306a36Sopenharmony_ci	 * Or if the xfrm is not mode tunnel.
42562306a36Sopenharmony_ci	 */
42662306a36Sopenharmony_ci	if (!x || x->props.mode != XFRM_MODE_TUNNEL ||
42762306a36Sopenharmony_ci	    x->props.family != AF_INET6)
42862306a36Sopenharmony_ci		return false;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (ipv6_addr_any(dst))
43162306a36Sopenharmony_ci		return xfrm_addr_equal(saddr, &x->props.saddr, AF_INET6);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (!xfrm_state_addr_check(x, daddr, saddr, AF_INET6))
43462306a36Sopenharmony_ci		return false;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return true;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/**
44062306a36Sopenharmony_ci * vti6_xmit - send a packet
44162306a36Sopenharmony_ci *   @skb: the outgoing socket buffer
44262306a36Sopenharmony_ci *   @dev: the outgoing tunnel device
44362306a36Sopenharmony_ci *   @fl: the flow informations for the xfrm_lookup
44462306a36Sopenharmony_ci **/
44562306a36Sopenharmony_cistatic int
44662306a36Sopenharmony_civti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
44962306a36Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
45062306a36Sopenharmony_ci	struct net_device *tdev;
45162306a36Sopenharmony_ci	struct xfrm_state *x;
45262306a36Sopenharmony_ci	int pkt_len = skb->len;
45362306a36Sopenharmony_ci	int err = -1;
45462306a36Sopenharmony_ci	int mtu;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (!dst) {
45762306a36Sopenharmony_ci		switch (skb->protocol) {
45862306a36Sopenharmony_ci		case htons(ETH_P_IP): {
45962306a36Sopenharmony_ci			struct rtable *rt;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci			fl->u.ip4.flowi4_oif = dev->ifindex;
46262306a36Sopenharmony_ci			fl->u.ip4.flowi4_flags |= FLOWI_FLAG_ANYSRC;
46362306a36Sopenharmony_ci			rt = __ip_route_output_key(dev_net(dev), &fl->u.ip4);
46462306a36Sopenharmony_ci			if (IS_ERR(rt))
46562306a36Sopenharmony_ci				goto tx_err_link_failure;
46662306a36Sopenharmony_ci			dst = &rt->dst;
46762306a36Sopenharmony_ci			skb_dst_set(skb, dst);
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci		}
47062306a36Sopenharmony_ci		case htons(ETH_P_IPV6):
47162306a36Sopenharmony_ci			fl->u.ip6.flowi6_oif = dev->ifindex;
47262306a36Sopenharmony_ci			fl->u.ip6.flowi6_flags |= FLOWI_FLAG_ANYSRC;
47362306a36Sopenharmony_ci			dst = ip6_route_output(dev_net(dev), NULL, &fl->u.ip6);
47462306a36Sopenharmony_ci			if (dst->error) {
47562306a36Sopenharmony_ci				dst_release(dst);
47662306a36Sopenharmony_ci				dst = NULL;
47762306a36Sopenharmony_ci				goto tx_err_link_failure;
47862306a36Sopenharmony_ci			}
47962306a36Sopenharmony_ci			skb_dst_set(skb, dst);
48062306a36Sopenharmony_ci			break;
48162306a36Sopenharmony_ci		default:
48262306a36Sopenharmony_ci			goto tx_err_link_failure;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	dst_hold(dst);
48762306a36Sopenharmony_ci	dst = xfrm_lookup_route(t->net, dst, fl, NULL, 0);
48862306a36Sopenharmony_ci	if (IS_ERR(dst)) {
48962306a36Sopenharmony_ci		err = PTR_ERR(dst);
49062306a36Sopenharmony_ci		dst = NULL;
49162306a36Sopenharmony_ci		goto tx_err_link_failure;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (dst->flags & DST_XFRM_QUEUE)
49562306a36Sopenharmony_ci		goto xmit;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	x = dst->xfrm;
49862306a36Sopenharmony_ci	if (!vti6_state_check(x, &t->parms.raddr, &t->parms.laddr))
49962306a36Sopenharmony_ci		goto tx_err_link_failure;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!ip6_tnl_xmit_ctl(t, (const struct in6_addr *)&x->props.saddr,
50262306a36Sopenharmony_ci			      (const struct in6_addr *)&x->id.daddr))
50362306a36Sopenharmony_ci		goto tx_err_link_failure;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	tdev = dst->dev;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (tdev == dev) {
50862306a36Sopenharmony_ci		DEV_STATS_INC(dev, collisions);
50962306a36Sopenharmony_ci		net_warn_ratelimited("%s: Local routing loop detected!\n",
51062306a36Sopenharmony_ci				     t->parms.name);
51162306a36Sopenharmony_ci		goto tx_err_dst_release;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	mtu = dst_mtu(dst);
51562306a36Sopenharmony_ci	if (skb->len > mtu) {
51662306a36Sopenharmony_ci		skb_dst_update_pmtu_no_confirm(skb, mtu);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		if (skb->protocol == htons(ETH_P_IPV6)) {
51962306a36Sopenharmony_ci			if (mtu < IPV6_MIN_MTU)
52062306a36Sopenharmony_ci				mtu = IPV6_MIN_MTU;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci			icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
52362306a36Sopenharmony_ci		} else {
52462306a36Sopenharmony_ci			if (!(ip_hdr(skb)->frag_off & htons(IP_DF)))
52562306a36Sopenharmony_ci				goto xmit;
52662306a36Sopenharmony_ci			icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
52762306a36Sopenharmony_ci				      htonl(mtu));
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		err = -EMSGSIZE;
53162306a36Sopenharmony_ci		goto tx_err_dst_release;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cixmit:
53562306a36Sopenharmony_ci	skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev)));
53662306a36Sopenharmony_ci	skb_dst_set(skb, dst);
53762306a36Sopenharmony_ci	skb->dev = skb_dst(skb)->dev;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	err = dst_output(t->net, skb->sk, skb);
54062306a36Sopenharmony_ci	if (net_xmit_eval(err) == 0)
54162306a36Sopenharmony_ci		err = pkt_len;
54262306a36Sopenharmony_ci	iptunnel_xmit_stats(dev, err);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_citx_err_link_failure:
54662306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_carrier_errors);
54762306a36Sopenharmony_ci	dst_link_failure(skb);
54862306a36Sopenharmony_citx_err_dst_release:
54962306a36Sopenharmony_ci	dst_release(dst);
55062306a36Sopenharmony_ci	return err;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic netdev_tx_t
55462306a36Sopenharmony_civti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
55762306a36Sopenharmony_ci	struct flowi fl;
55862306a36Sopenharmony_ci	int ret;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (!pskb_inet_may_pull(skb))
56162306a36Sopenharmony_ci		goto tx_err;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	memset(&fl, 0, sizeof(fl));
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	switch (skb->protocol) {
56662306a36Sopenharmony_ci	case htons(ETH_P_IPV6):
56762306a36Sopenharmony_ci		if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
56862306a36Sopenharmony_ci		    vti6_addr_conflict(t, ipv6_hdr(skb)))
56962306a36Sopenharmony_ci			goto tx_err;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
57262306a36Sopenharmony_ci		xfrm_decode_session(skb, &fl, AF_INET6);
57362306a36Sopenharmony_ci		break;
57462306a36Sopenharmony_ci	case htons(ETH_P_IP):
57562306a36Sopenharmony_ci		memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
57662306a36Sopenharmony_ci		xfrm_decode_session(skb, &fl, AF_INET);
57762306a36Sopenharmony_ci		break;
57862306a36Sopenharmony_ci	default:
57962306a36Sopenharmony_ci		goto tx_err;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* override mark with tunnel output key */
58362306a36Sopenharmony_ci	fl.flowi_mark = be32_to_cpu(t->parms.o_key);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	ret = vti6_xmit(skb, dev, &fl);
58662306a36Sopenharmony_ci	if (ret < 0)
58762306a36Sopenharmony_ci		goto tx_err;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return NETDEV_TX_OK;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_citx_err:
59262306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_errors);
59362306a36Sopenharmony_ci	DEV_STATS_INC(dev, tx_dropped);
59462306a36Sopenharmony_ci	kfree_skb(skb);
59562306a36Sopenharmony_ci	return NETDEV_TX_OK;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
59962306a36Sopenharmony_ci		    u8 type, u8 code, int offset, __be32 info)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	__be32 spi;
60262306a36Sopenharmony_ci	__u32 mark;
60362306a36Sopenharmony_ci	struct xfrm_state *x;
60462306a36Sopenharmony_ci	struct ip6_tnl *t;
60562306a36Sopenharmony_ci	struct ip_esp_hdr *esph;
60662306a36Sopenharmony_ci	struct ip_auth_hdr *ah;
60762306a36Sopenharmony_ci	struct ip_comp_hdr *ipch;
60862306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
60962306a36Sopenharmony_ci	const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data;
61062306a36Sopenharmony_ci	int protocol = iph->nexthdr;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	t = vti6_tnl_lookup(dev_net(skb->dev), &iph->daddr, &iph->saddr);
61362306a36Sopenharmony_ci	if (!t)
61462306a36Sopenharmony_ci		return -1;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	mark = be32_to_cpu(t->parms.o_key);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	switch (protocol) {
61962306a36Sopenharmony_ci	case IPPROTO_ESP:
62062306a36Sopenharmony_ci		esph = (struct ip_esp_hdr *)(skb->data + offset);
62162306a36Sopenharmony_ci		spi = esph->spi;
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	case IPPROTO_AH:
62462306a36Sopenharmony_ci		ah = (struct ip_auth_hdr *)(skb->data + offset);
62562306a36Sopenharmony_ci		spi = ah->spi;
62662306a36Sopenharmony_ci		break;
62762306a36Sopenharmony_ci	case IPPROTO_COMP:
62862306a36Sopenharmony_ci		ipch = (struct ip_comp_hdr *)(skb->data + offset);
62962306a36Sopenharmony_ci		spi = htonl(ntohs(ipch->cpi));
63062306a36Sopenharmony_ci		break;
63162306a36Sopenharmony_ci	default:
63262306a36Sopenharmony_ci		return 0;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (type != ICMPV6_PKT_TOOBIG &&
63662306a36Sopenharmony_ci	    type != NDISC_REDIRECT)
63762306a36Sopenharmony_ci		return 0;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	x = xfrm_state_lookup(net, mark, (const xfrm_address_t *)&iph->daddr,
64062306a36Sopenharmony_ci			      spi, protocol, AF_INET6);
64162306a36Sopenharmony_ci	if (!x)
64262306a36Sopenharmony_ci		return 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (type == NDISC_REDIRECT)
64562306a36Sopenharmony_ci		ip6_redirect(skb, net, skb->dev->ifindex, 0,
64662306a36Sopenharmony_ci			     sock_net_uid(net, NULL));
64762306a36Sopenharmony_ci	else
64862306a36Sopenharmony_ci		ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
64962306a36Sopenharmony_ci	xfrm_state_put(x);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return 0;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void vti6_link_config(struct ip6_tnl *t, bool keep_mtu)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct net_device *dev = t->dev;
65762306a36Sopenharmony_ci	struct __ip6_tnl_parm *p = &t->parms;
65862306a36Sopenharmony_ci	struct net_device *tdev = NULL;
65962306a36Sopenharmony_ci	int mtu;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	__dev_addr_set(dev, &p->laddr, sizeof(struct in6_addr));
66262306a36Sopenharmony_ci	memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	p->flags &= ~(IP6_TNL_F_CAP_XMIT | IP6_TNL_F_CAP_RCV |
66562306a36Sopenharmony_ci		      IP6_TNL_F_CAP_PER_PACKET);
66662306a36Sopenharmony_ci	p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (p->flags & IP6_TNL_F_CAP_XMIT && p->flags & IP6_TNL_F_CAP_RCV)
66962306a36Sopenharmony_ci		dev->flags |= IFF_POINTOPOINT;
67062306a36Sopenharmony_ci	else
67162306a36Sopenharmony_ci		dev->flags &= ~IFF_POINTOPOINT;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (keep_mtu && dev->mtu) {
67462306a36Sopenharmony_ci		dev->mtu = clamp(dev->mtu, dev->min_mtu, dev->max_mtu);
67562306a36Sopenharmony_ci		return;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (p->flags & IP6_TNL_F_CAP_XMIT) {
67962306a36Sopenharmony_ci		int strict = (ipv6_addr_type(&p->raddr) &
68062306a36Sopenharmony_ci			      (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
68162306a36Sopenharmony_ci		struct rt6_info *rt = rt6_lookup(t->net,
68262306a36Sopenharmony_ci						 &p->raddr, &p->laddr,
68362306a36Sopenharmony_ci						 p->link, NULL, strict);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		if (rt)
68662306a36Sopenharmony_ci			tdev = rt->dst.dev;
68762306a36Sopenharmony_ci		ip6_rt_put(rt);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (!tdev && p->link)
69162306a36Sopenharmony_ci		tdev = __dev_get_by_index(t->net, p->link);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (tdev)
69462306a36Sopenharmony_ci		mtu = tdev->mtu - sizeof(struct ipv6hdr);
69562306a36Sopenharmony_ci	else
69662306a36Sopenharmony_ci		mtu = ETH_DATA_LEN - LL_MAX_HEADER - sizeof(struct ipv6hdr);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	dev->mtu = max_t(int, mtu, IPV4_MIN_MTU);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci/**
70262306a36Sopenharmony_ci * vti6_tnl_change - update the tunnel parameters
70362306a36Sopenharmony_ci *   @t: tunnel to be changed
70462306a36Sopenharmony_ci *   @p: tunnel configuration parameters
70562306a36Sopenharmony_ci *   @keep_mtu: MTU was set from userspace, don't re-compute it
70662306a36Sopenharmony_ci *
70762306a36Sopenharmony_ci * Description:
70862306a36Sopenharmony_ci *   vti6_tnl_change() updates the tunnel parameters
70962306a36Sopenharmony_ci **/
71062306a36Sopenharmony_cistatic int
71162306a36Sopenharmony_civti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p,
71262306a36Sopenharmony_ci		bool keep_mtu)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	t->parms.laddr = p->laddr;
71562306a36Sopenharmony_ci	t->parms.raddr = p->raddr;
71662306a36Sopenharmony_ci	t->parms.link = p->link;
71762306a36Sopenharmony_ci	t->parms.i_key = p->i_key;
71862306a36Sopenharmony_ci	t->parms.o_key = p->o_key;
71962306a36Sopenharmony_ci	t->parms.proto = p->proto;
72062306a36Sopenharmony_ci	t->parms.fwmark = p->fwmark;
72162306a36Sopenharmony_ci	dst_cache_reset(&t->dst_cache);
72262306a36Sopenharmony_ci	vti6_link_config(t, keep_mtu);
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p,
72762306a36Sopenharmony_ci		       bool keep_mtu)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct net *net = dev_net(t->dev);
73062306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
73162306a36Sopenharmony_ci	int err;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	vti6_tnl_unlink(ip6n, t);
73462306a36Sopenharmony_ci	synchronize_net();
73562306a36Sopenharmony_ci	err = vti6_tnl_change(t, p, keep_mtu);
73662306a36Sopenharmony_ci	vti6_tnl_link(ip6n, t);
73762306a36Sopenharmony_ci	netdev_state_change(t->dev);
73862306a36Sopenharmony_ci	return err;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic void
74262306a36Sopenharmony_civti6_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm2 *u)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	p->laddr = u->laddr;
74562306a36Sopenharmony_ci	p->raddr = u->raddr;
74662306a36Sopenharmony_ci	p->link = u->link;
74762306a36Sopenharmony_ci	p->i_key = u->i_key;
74862306a36Sopenharmony_ci	p->o_key = u->o_key;
74962306a36Sopenharmony_ci	p->proto = u->proto;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	memcpy(p->name, u->name, sizeof(u->name));
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void
75562306a36Sopenharmony_civti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	u->laddr = p->laddr;
75862306a36Sopenharmony_ci	u->raddr = p->raddr;
75962306a36Sopenharmony_ci	u->link = p->link;
76062306a36Sopenharmony_ci	u->i_key = p->i_key;
76162306a36Sopenharmony_ci	u->o_key = p->o_key;
76262306a36Sopenharmony_ci	if (u->i_key)
76362306a36Sopenharmony_ci		u->i_flags |= GRE_KEY;
76462306a36Sopenharmony_ci	if (u->o_key)
76562306a36Sopenharmony_ci		u->o_flags |= GRE_KEY;
76662306a36Sopenharmony_ci	u->proto = p->proto;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	memcpy(u->name, p->name, sizeof(u->name));
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/**
77262306a36Sopenharmony_ci * vti6_siocdevprivate - configure vti6 tunnels from userspace
77362306a36Sopenharmony_ci *   @dev: virtual device associated with tunnel
77462306a36Sopenharmony_ci *   @ifr: unused
77562306a36Sopenharmony_ci *   @data: parameters passed from userspace
77662306a36Sopenharmony_ci *   @cmd: command to be performed
77762306a36Sopenharmony_ci *
77862306a36Sopenharmony_ci * Description:
77962306a36Sopenharmony_ci *   vti6_siocdevprivate() is used for managing vti6 tunnels
78062306a36Sopenharmony_ci *   from userspace.
78162306a36Sopenharmony_ci *
78262306a36Sopenharmony_ci *   The possible commands are the following:
78362306a36Sopenharmony_ci *     %SIOCGETTUNNEL: get tunnel parameters for device
78462306a36Sopenharmony_ci *     %SIOCADDTUNNEL: add tunnel matching given tunnel parameters
78562306a36Sopenharmony_ci *     %SIOCCHGTUNNEL: change tunnel parameters to those given
78662306a36Sopenharmony_ci *     %SIOCDELTUNNEL: delete tunnel
78762306a36Sopenharmony_ci *
78862306a36Sopenharmony_ci *   The fallback device "ip6_vti0", created during module
78962306a36Sopenharmony_ci *   initialization, can be used for creating other tunnel devices.
79062306a36Sopenharmony_ci *
79162306a36Sopenharmony_ci * Return:
79262306a36Sopenharmony_ci *   0 on success,
79362306a36Sopenharmony_ci *   %-EFAULT if unable to copy data to or from userspace,
79462306a36Sopenharmony_ci *   %-EPERM if current process hasn't %CAP_NET_ADMIN set
79562306a36Sopenharmony_ci *   %-EINVAL if passed tunnel parameters are invalid,
79662306a36Sopenharmony_ci *   %-EEXIST if changing a tunnel's parameters would cause a conflict
79762306a36Sopenharmony_ci *   %-ENODEV if attempting to change or delete a nonexisting device
79862306a36Sopenharmony_ci **/
79962306a36Sopenharmony_cistatic int
80062306a36Sopenharmony_civti6_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	int err = 0;
80362306a36Sopenharmony_ci	struct ip6_tnl_parm2 p;
80462306a36Sopenharmony_ci	struct __ip6_tnl_parm p1;
80562306a36Sopenharmony_ci	struct ip6_tnl *t = NULL;
80662306a36Sopenharmony_ci	struct net *net = dev_net(dev);
80762306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	memset(&p1, 0, sizeof(p1));
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	switch (cmd) {
81262306a36Sopenharmony_ci	case SIOCGETTUNNEL:
81362306a36Sopenharmony_ci		if (dev == ip6n->fb_tnl_dev) {
81462306a36Sopenharmony_ci			if (copy_from_user(&p, data, sizeof(p))) {
81562306a36Sopenharmony_ci				err = -EFAULT;
81662306a36Sopenharmony_ci				break;
81762306a36Sopenharmony_ci			}
81862306a36Sopenharmony_ci			vti6_parm_from_user(&p1, &p);
81962306a36Sopenharmony_ci			t = vti6_locate(net, &p1, 0);
82062306a36Sopenharmony_ci		} else {
82162306a36Sopenharmony_ci			memset(&p, 0, sizeof(p));
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci		if (!t)
82462306a36Sopenharmony_ci			t = netdev_priv(dev);
82562306a36Sopenharmony_ci		vti6_parm_to_user(&p, &t->parms);
82662306a36Sopenharmony_ci		if (copy_to_user(data, &p, sizeof(p)))
82762306a36Sopenharmony_ci			err = -EFAULT;
82862306a36Sopenharmony_ci		break;
82962306a36Sopenharmony_ci	case SIOCADDTUNNEL:
83062306a36Sopenharmony_ci	case SIOCCHGTUNNEL:
83162306a36Sopenharmony_ci		err = -EPERM;
83262306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
83362306a36Sopenharmony_ci			break;
83462306a36Sopenharmony_ci		err = -EFAULT;
83562306a36Sopenharmony_ci		if (copy_from_user(&p, data, sizeof(p)))
83662306a36Sopenharmony_ci			break;
83762306a36Sopenharmony_ci		err = -EINVAL;
83862306a36Sopenharmony_ci		if (p.proto != IPPROTO_IPV6  && p.proto != 0)
83962306a36Sopenharmony_ci			break;
84062306a36Sopenharmony_ci		vti6_parm_from_user(&p1, &p);
84162306a36Sopenharmony_ci		t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL);
84262306a36Sopenharmony_ci		if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
84362306a36Sopenharmony_ci			if (t) {
84462306a36Sopenharmony_ci				if (t->dev != dev) {
84562306a36Sopenharmony_ci					err = -EEXIST;
84662306a36Sopenharmony_ci					break;
84762306a36Sopenharmony_ci				}
84862306a36Sopenharmony_ci			} else
84962306a36Sopenharmony_ci				t = netdev_priv(dev);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci			err = vti6_update(t, &p1, false);
85262306a36Sopenharmony_ci		}
85362306a36Sopenharmony_ci		if (t) {
85462306a36Sopenharmony_ci			err = 0;
85562306a36Sopenharmony_ci			vti6_parm_to_user(&p, &t->parms);
85662306a36Sopenharmony_ci			if (copy_to_user(data, &p, sizeof(p)))
85762306a36Sopenharmony_ci				err = -EFAULT;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		} else
86062306a36Sopenharmony_ci			err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
86162306a36Sopenharmony_ci		break;
86262306a36Sopenharmony_ci	case SIOCDELTUNNEL:
86362306a36Sopenharmony_ci		err = -EPERM;
86462306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
86562306a36Sopenharmony_ci			break;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		if (dev == ip6n->fb_tnl_dev) {
86862306a36Sopenharmony_ci			err = -EFAULT;
86962306a36Sopenharmony_ci			if (copy_from_user(&p, data, sizeof(p)))
87062306a36Sopenharmony_ci				break;
87162306a36Sopenharmony_ci			err = -ENOENT;
87262306a36Sopenharmony_ci			vti6_parm_from_user(&p1, &p);
87362306a36Sopenharmony_ci			t = vti6_locate(net, &p1, 0);
87462306a36Sopenharmony_ci			if (!t)
87562306a36Sopenharmony_ci				break;
87662306a36Sopenharmony_ci			err = -EPERM;
87762306a36Sopenharmony_ci			if (t->dev == ip6n->fb_tnl_dev)
87862306a36Sopenharmony_ci				break;
87962306a36Sopenharmony_ci			dev = t->dev;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci		err = 0;
88262306a36Sopenharmony_ci		unregister_netdevice(dev);
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci	default:
88562306a36Sopenharmony_ci		err = -EINVAL;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci	return err;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic const struct net_device_ops vti6_netdev_ops = {
89162306a36Sopenharmony_ci	.ndo_init	= vti6_dev_init,
89262306a36Sopenharmony_ci	.ndo_uninit	= vti6_dev_uninit,
89362306a36Sopenharmony_ci	.ndo_start_xmit = vti6_tnl_xmit,
89462306a36Sopenharmony_ci	.ndo_siocdevprivate = vti6_siocdevprivate,
89562306a36Sopenharmony_ci	.ndo_get_stats64 = dev_get_tstats64,
89662306a36Sopenharmony_ci	.ndo_get_iflink = ip6_tnl_get_iflink,
89762306a36Sopenharmony_ci};
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/**
90062306a36Sopenharmony_ci * vti6_dev_setup - setup virtual tunnel device
90162306a36Sopenharmony_ci *   @dev: virtual device associated with tunnel
90262306a36Sopenharmony_ci *
90362306a36Sopenharmony_ci * Description:
90462306a36Sopenharmony_ci *   Initialize function pointers and device parameters
90562306a36Sopenharmony_ci **/
90662306a36Sopenharmony_cistatic void vti6_dev_setup(struct net_device *dev)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	dev->netdev_ops = &vti6_netdev_ops;
90962306a36Sopenharmony_ci	dev->header_ops = &ip_tunnel_header_ops;
91062306a36Sopenharmony_ci	dev->needs_free_netdev = true;
91162306a36Sopenharmony_ci	dev->priv_destructor = vti6_dev_free;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	dev->type = ARPHRD_TUNNEL6;
91462306a36Sopenharmony_ci	dev->min_mtu = IPV4_MIN_MTU;
91562306a36Sopenharmony_ci	dev->max_mtu = IP_MAX_MTU - sizeof(struct ipv6hdr);
91662306a36Sopenharmony_ci	dev->flags |= IFF_NOARP;
91762306a36Sopenharmony_ci	dev->addr_len = sizeof(struct in6_addr);
91862306a36Sopenharmony_ci	netif_keep_dst(dev);
91962306a36Sopenharmony_ci	/* This perm addr will be used as interface identifier by IPv6 */
92062306a36Sopenharmony_ci	dev->addr_assign_type = NET_ADDR_RANDOM;
92162306a36Sopenharmony_ci	eth_random_addr(dev->perm_addr);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/**
92562306a36Sopenharmony_ci * vti6_dev_init_gen - general initializer for all tunnel devices
92662306a36Sopenharmony_ci *   @dev: virtual device associated with tunnel
92762306a36Sopenharmony_ci **/
92862306a36Sopenharmony_cistatic inline int vti6_dev_init_gen(struct net_device *dev)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	t->dev = dev;
93362306a36Sopenharmony_ci	t->net = dev_net(dev);
93462306a36Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
93562306a36Sopenharmony_ci	if (!dev->tstats)
93662306a36Sopenharmony_ci		return -ENOMEM;
93762306a36Sopenharmony_ci	netdev_hold(dev, &t->dev_tracker, GFP_KERNEL);
93862306a36Sopenharmony_ci	return 0;
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci/**
94262306a36Sopenharmony_ci * vti6_dev_init - initializer for all non fallback tunnel devices
94362306a36Sopenharmony_ci *   @dev: virtual device associated with tunnel
94462306a36Sopenharmony_ci **/
94562306a36Sopenharmony_cistatic int vti6_dev_init(struct net_device *dev)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
94862306a36Sopenharmony_ci	int err = vti6_dev_init_gen(dev);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (err)
95162306a36Sopenharmony_ci		return err;
95262306a36Sopenharmony_ci	vti6_link_config(t, true);
95362306a36Sopenharmony_ci	return 0;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci/**
95762306a36Sopenharmony_ci * vti6_fb_tnl_dev_init - initializer for fallback tunnel device
95862306a36Sopenharmony_ci *   @dev: fallback device
95962306a36Sopenharmony_ci *
96062306a36Sopenharmony_ci * Return: 0
96162306a36Sopenharmony_ci **/
96262306a36Sopenharmony_cistatic int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct ip6_tnl *t = netdev_priv(dev);
96562306a36Sopenharmony_ci	struct net *net = dev_net(dev);
96662306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	t->parms.proto = IPPROTO_IPV6;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	rcu_assign_pointer(ip6n->tnls_wc[0], t);
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic int vti6_validate(struct nlattr *tb[], struct nlattr *data[],
97562306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void vti6_netlink_parms(struct nlattr *data[],
98162306a36Sopenharmony_ci			       struct __ip6_tnl_parm *parms)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	memset(parms, 0, sizeof(*parms));
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (!data)
98662306a36Sopenharmony_ci		return;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (data[IFLA_VTI_LINK])
98962306a36Sopenharmony_ci		parms->link = nla_get_u32(data[IFLA_VTI_LINK]);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	if (data[IFLA_VTI_LOCAL])
99262306a36Sopenharmony_ci		parms->laddr = nla_get_in6_addr(data[IFLA_VTI_LOCAL]);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (data[IFLA_VTI_REMOTE])
99562306a36Sopenharmony_ci		parms->raddr = nla_get_in6_addr(data[IFLA_VTI_REMOTE]);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (data[IFLA_VTI_IKEY])
99862306a36Sopenharmony_ci		parms->i_key = nla_get_be32(data[IFLA_VTI_IKEY]);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	if (data[IFLA_VTI_OKEY])
100162306a36Sopenharmony_ci		parms->o_key = nla_get_be32(data[IFLA_VTI_OKEY]);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	if (data[IFLA_VTI_FWMARK])
100462306a36Sopenharmony_ci		parms->fwmark = nla_get_u32(data[IFLA_VTI_FWMARK]);
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic int vti6_newlink(struct net *src_net, struct net_device *dev,
100862306a36Sopenharmony_ci			struct nlattr *tb[], struct nlattr *data[],
100962306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct net *net = dev_net(dev);
101262306a36Sopenharmony_ci	struct ip6_tnl *nt;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	nt = netdev_priv(dev);
101562306a36Sopenharmony_ci	vti6_netlink_parms(data, &nt->parms);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	nt->parms.proto = IPPROTO_IPV6;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (vti6_locate(net, &nt->parms, 0))
102062306a36Sopenharmony_ci		return -EEXIST;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	return vti6_tnl_create2(dev);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic void vti6_dellink(struct net_device *dev, struct list_head *head)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct net *net = dev_net(dev);
102862306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (dev != ip6n->fb_tnl_dev)
103162306a36Sopenharmony_ci		unregister_netdevice_queue(dev, head);
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_cistatic int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
103562306a36Sopenharmony_ci			   struct nlattr *data[],
103662306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	struct ip6_tnl *t;
103962306a36Sopenharmony_ci	struct __ip6_tnl_parm p;
104062306a36Sopenharmony_ci	struct net *net = dev_net(dev);
104162306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (dev == ip6n->fb_tnl_dev)
104462306a36Sopenharmony_ci		return -EINVAL;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	vti6_netlink_parms(data, &p);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	t = vti6_locate(net, &p, 0);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (t) {
105162306a36Sopenharmony_ci		if (t->dev != dev)
105262306a36Sopenharmony_ci			return -EEXIST;
105362306a36Sopenharmony_ci	} else
105462306a36Sopenharmony_ci		t = netdev_priv(dev);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return vti6_update(t, &p, tb && tb[IFLA_MTU]);
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic size_t vti6_get_size(const struct net_device *dev)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	return
106262306a36Sopenharmony_ci		/* IFLA_VTI_LINK */
106362306a36Sopenharmony_ci		nla_total_size(4) +
106462306a36Sopenharmony_ci		/* IFLA_VTI_LOCAL */
106562306a36Sopenharmony_ci		nla_total_size(sizeof(struct in6_addr)) +
106662306a36Sopenharmony_ci		/* IFLA_VTI_REMOTE */
106762306a36Sopenharmony_ci		nla_total_size(sizeof(struct in6_addr)) +
106862306a36Sopenharmony_ci		/* IFLA_VTI_IKEY */
106962306a36Sopenharmony_ci		nla_total_size(4) +
107062306a36Sopenharmony_ci		/* IFLA_VTI_OKEY */
107162306a36Sopenharmony_ci		nla_total_size(4) +
107262306a36Sopenharmony_ci		/* IFLA_VTI_FWMARK */
107362306a36Sopenharmony_ci		nla_total_size(4) +
107462306a36Sopenharmony_ci		0;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct ip6_tnl *tunnel = netdev_priv(dev);
108062306a36Sopenharmony_ci	struct __ip6_tnl_parm *parm = &tunnel->parms;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_VTI_LINK, parm->link) ||
108362306a36Sopenharmony_ci	    nla_put_in6_addr(skb, IFLA_VTI_LOCAL, &parm->laddr) ||
108462306a36Sopenharmony_ci	    nla_put_in6_addr(skb, IFLA_VTI_REMOTE, &parm->raddr) ||
108562306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) ||
108662306a36Sopenharmony_ci	    nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key) ||
108762306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_VTI_FWMARK, parm->fwmark))
108862306a36Sopenharmony_ci		goto nla_put_failure;
108962306a36Sopenharmony_ci	return 0;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cinla_put_failure:
109262306a36Sopenharmony_ci	return -EMSGSIZE;
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = {
109662306a36Sopenharmony_ci	[IFLA_VTI_LINK]		= { .type = NLA_U32 },
109762306a36Sopenharmony_ci	[IFLA_VTI_LOCAL]	= { .len = sizeof(struct in6_addr) },
109862306a36Sopenharmony_ci	[IFLA_VTI_REMOTE]	= { .len = sizeof(struct in6_addr) },
109962306a36Sopenharmony_ci	[IFLA_VTI_IKEY]		= { .type = NLA_U32 },
110062306a36Sopenharmony_ci	[IFLA_VTI_OKEY]		= { .type = NLA_U32 },
110162306a36Sopenharmony_ci	[IFLA_VTI_FWMARK]	= { .type = NLA_U32 },
110262306a36Sopenharmony_ci};
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic struct rtnl_link_ops vti6_link_ops __read_mostly = {
110562306a36Sopenharmony_ci	.kind		= "vti6",
110662306a36Sopenharmony_ci	.maxtype	= IFLA_VTI_MAX,
110762306a36Sopenharmony_ci	.policy		= vti6_policy,
110862306a36Sopenharmony_ci	.priv_size	= sizeof(struct ip6_tnl),
110962306a36Sopenharmony_ci	.setup		= vti6_dev_setup,
111062306a36Sopenharmony_ci	.validate	= vti6_validate,
111162306a36Sopenharmony_ci	.newlink	= vti6_newlink,
111262306a36Sopenharmony_ci	.dellink	= vti6_dellink,
111362306a36Sopenharmony_ci	.changelink	= vti6_changelink,
111462306a36Sopenharmony_ci	.get_size	= vti6_get_size,
111562306a36Sopenharmony_ci	.fill_info	= vti6_fill_info,
111662306a36Sopenharmony_ci	.get_link_net	= ip6_tnl_get_link_net,
111762306a36Sopenharmony_ci};
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n,
112062306a36Sopenharmony_ci					    struct list_head *list)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	int h;
112362306a36Sopenharmony_ci	struct ip6_tnl *t;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	for (h = 0; h < IP6_VTI_HASH_SIZE; h++) {
112662306a36Sopenharmony_ci		t = rtnl_dereference(ip6n->tnls_r_l[h]);
112762306a36Sopenharmony_ci		while (t) {
112862306a36Sopenharmony_ci			unregister_netdevice_queue(t->dev, list);
112962306a36Sopenharmony_ci			t = rtnl_dereference(t->next);
113062306a36Sopenharmony_ci		}
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	t = rtnl_dereference(ip6n->tnls_wc[0]);
113462306a36Sopenharmony_ci	if (t)
113562306a36Sopenharmony_ci		unregister_netdevice_queue(t->dev, list);
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_cistatic int __net_init vti6_init_net(struct net *net)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	struct vti6_net *ip6n = net_generic(net, vti6_net_id);
114162306a36Sopenharmony_ci	struct ip6_tnl *t = NULL;
114262306a36Sopenharmony_ci	int err;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	ip6n->tnls[0] = ip6n->tnls_wc;
114562306a36Sopenharmony_ci	ip6n->tnls[1] = ip6n->tnls_r_l;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (!net_has_fallback_tunnels(net))
114862306a36Sopenharmony_ci		return 0;
114962306a36Sopenharmony_ci	err = -ENOMEM;
115062306a36Sopenharmony_ci	ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6_vti0",
115162306a36Sopenharmony_ci					NET_NAME_UNKNOWN, vti6_dev_setup);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (!ip6n->fb_tnl_dev)
115462306a36Sopenharmony_ci		goto err_alloc_dev;
115562306a36Sopenharmony_ci	dev_net_set(ip6n->fb_tnl_dev, net);
115662306a36Sopenharmony_ci	ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
115962306a36Sopenharmony_ci	if (err < 0)
116062306a36Sopenharmony_ci		goto err_register;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	err = register_netdev(ip6n->fb_tnl_dev);
116362306a36Sopenharmony_ci	if (err < 0)
116462306a36Sopenharmony_ci		goto err_register;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	t = netdev_priv(ip6n->fb_tnl_dev);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	strcpy(t->parms.name, ip6n->fb_tnl_dev->name);
116962306a36Sopenharmony_ci	return 0;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cierr_register:
117262306a36Sopenharmony_ci	free_netdev(ip6n->fb_tnl_dev);
117362306a36Sopenharmony_cierr_alloc_dev:
117462306a36Sopenharmony_ci	return err;
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic void __net_exit vti6_exit_batch_net(struct list_head *net_list)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct vti6_net *ip6n;
118062306a36Sopenharmony_ci	struct net *net;
118162306a36Sopenharmony_ci	LIST_HEAD(list);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	rtnl_lock();
118462306a36Sopenharmony_ci	list_for_each_entry(net, net_list, exit_list) {
118562306a36Sopenharmony_ci		ip6n = net_generic(net, vti6_net_id);
118662306a36Sopenharmony_ci		vti6_destroy_tunnels(ip6n, &list);
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci	unregister_netdevice_many(&list);
118962306a36Sopenharmony_ci	rtnl_unlock();
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic struct pernet_operations vti6_net_ops = {
119362306a36Sopenharmony_ci	.init = vti6_init_net,
119462306a36Sopenharmony_ci	.exit_batch = vti6_exit_batch_net,
119562306a36Sopenharmony_ci	.id   = &vti6_net_id,
119662306a36Sopenharmony_ci	.size = sizeof(struct vti6_net),
119762306a36Sopenharmony_ci};
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic struct xfrm6_protocol vti_esp6_protocol __read_mostly = {
120062306a36Sopenharmony_ci	.handler	=	vti6_rcv,
120162306a36Sopenharmony_ci	.input_handler	=	vti6_input_proto,
120262306a36Sopenharmony_ci	.cb_handler	=	vti6_rcv_cb,
120362306a36Sopenharmony_ci	.err_handler	=	vti6_err,
120462306a36Sopenharmony_ci	.priority	=	100,
120562306a36Sopenharmony_ci};
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic struct xfrm6_protocol vti_ah6_protocol __read_mostly = {
120862306a36Sopenharmony_ci	.handler	=	vti6_rcv,
120962306a36Sopenharmony_ci	.input_handler	=	vti6_input_proto,
121062306a36Sopenharmony_ci	.cb_handler	=	vti6_rcv_cb,
121162306a36Sopenharmony_ci	.err_handler	=	vti6_err,
121262306a36Sopenharmony_ci	.priority	=	100,
121362306a36Sopenharmony_ci};
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic struct xfrm6_protocol vti_ipcomp6_protocol __read_mostly = {
121662306a36Sopenharmony_ci	.handler	=	vti6_rcv,
121762306a36Sopenharmony_ci	.input_handler	=	vti6_input_proto,
121862306a36Sopenharmony_ci	.cb_handler	=	vti6_rcv_cb,
121962306a36Sopenharmony_ci	.err_handler	=	vti6_err,
122062306a36Sopenharmony_ci	.priority	=	100,
122162306a36Sopenharmony_ci};
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL)
122462306a36Sopenharmony_cistatic int vti6_rcv_tunnel(struct sk_buff *skb)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	const xfrm_address_t *saddr;
122762306a36Sopenharmony_ci	__be32 spi;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	saddr = (const xfrm_address_t *)&ipv6_hdr(skb)->saddr;
123062306a36Sopenharmony_ci	spi = xfrm6_tunnel_spi_lookup(dev_net(skb->dev), saddr);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	return vti6_input_proto(skb, IPPROTO_IPV6, spi, 0);
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic struct xfrm6_tunnel vti_ipv6_handler __read_mostly = {
123662306a36Sopenharmony_ci	.handler	=	vti6_rcv_tunnel,
123762306a36Sopenharmony_ci	.cb_handler	=	vti6_rcv_cb,
123862306a36Sopenharmony_ci	.err_handler	=	vti6_err,
123962306a36Sopenharmony_ci	.priority	=	0,
124062306a36Sopenharmony_ci};
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic struct xfrm6_tunnel vti_ip6ip_handler __read_mostly = {
124362306a36Sopenharmony_ci	.handler	=	vti6_rcv_tunnel,
124462306a36Sopenharmony_ci	.cb_handler	=	vti6_rcv_cb,
124562306a36Sopenharmony_ci	.err_handler	=	vti6_err,
124662306a36Sopenharmony_ci	.priority	=	0,
124762306a36Sopenharmony_ci};
124862306a36Sopenharmony_ci#endif
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/**
125162306a36Sopenharmony_ci * vti6_tunnel_init - register protocol and reserve needed resources
125262306a36Sopenharmony_ci *
125362306a36Sopenharmony_ci * Return: 0 on success
125462306a36Sopenharmony_ci **/
125562306a36Sopenharmony_cistatic int __init vti6_tunnel_init(void)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	const char *msg;
125862306a36Sopenharmony_ci	int err;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	msg = "tunnel device";
126162306a36Sopenharmony_ci	err = register_pernet_device(&vti6_net_ops);
126262306a36Sopenharmony_ci	if (err < 0)
126362306a36Sopenharmony_ci		goto pernet_dev_failed;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	msg = "tunnel protocols";
126662306a36Sopenharmony_ci	err = xfrm6_protocol_register(&vti_esp6_protocol, IPPROTO_ESP);
126762306a36Sopenharmony_ci	if (err < 0)
126862306a36Sopenharmony_ci		goto xfrm_proto_esp_failed;
126962306a36Sopenharmony_ci	err = xfrm6_protocol_register(&vti_ah6_protocol, IPPROTO_AH);
127062306a36Sopenharmony_ci	if (err < 0)
127162306a36Sopenharmony_ci		goto xfrm_proto_ah_failed;
127262306a36Sopenharmony_ci	err = xfrm6_protocol_register(&vti_ipcomp6_protocol, IPPROTO_COMP);
127362306a36Sopenharmony_ci	if (err < 0)
127462306a36Sopenharmony_ci		goto xfrm_proto_comp_failed;
127562306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL)
127662306a36Sopenharmony_ci	msg = "ipv6 tunnel";
127762306a36Sopenharmony_ci	err = xfrm6_tunnel_register(&vti_ipv6_handler, AF_INET6);
127862306a36Sopenharmony_ci	if (err < 0)
127962306a36Sopenharmony_ci		goto vti_tunnel_ipv6_failed;
128062306a36Sopenharmony_ci	err = xfrm6_tunnel_register(&vti_ip6ip_handler, AF_INET);
128162306a36Sopenharmony_ci	if (err < 0)
128262306a36Sopenharmony_ci		goto vti_tunnel_ip6ip_failed;
128362306a36Sopenharmony_ci#endif
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	msg = "netlink interface";
128662306a36Sopenharmony_ci	err = rtnl_link_register(&vti6_link_ops);
128762306a36Sopenharmony_ci	if (err < 0)
128862306a36Sopenharmony_ci		goto rtnl_link_failed;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	return 0;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cirtnl_link_failed:
129362306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL)
129462306a36Sopenharmony_ci	err = xfrm6_tunnel_deregister(&vti_ip6ip_handler, AF_INET);
129562306a36Sopenharmony_civti_tunnel_ip6ip_failed:
129662306a36Sopenharmony_ci	err = xfrm6_tunnel_deregister(&vti_ipv6_handler, AF_INET6);
129762306a36Sopenharmony_civti_tunnel_ipv6_failed:
129862306a36Sopenharmony_ci#endif
129962306a36Sopenharmony_ci	xfrm6_protocol_deregister(&vti_ipcomp6_protocol, IPPROTO_COMP);
130062306a36Sopenharmony_cixfrm_proto_comp_failed:
130162306a36Sopenharmony_ci	xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH);
130262306a36Sopenharmony_cixfrm_proto_ah_failed:
130362306a36Sopenharmony_ci	xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
130462306a36Sopenharmony_cixfrm_proto_esp_failed:
130562306a36Sopenharmony_ci	unregister_pernet_device(&vti6_net_ops);
130662306a36Sopenharmony_cipernet_dev_failed:
130762306a36Sopenharmony_ci	pr_err("vti6 init: failed to register %s\n", msg);
130862306a36Sopenharmony_ci	return err;
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci/**
131262306a36Sopenharmony_ci * vti6_tunnel_cleanup - free resources and unregister protocol
131362306a36Sopenharmony_ci **/
131462306a36Sopenharmony_cistatic void __exit vti6_tunnel_cleanup(void)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	rtnl_link_unregister(&vti6_link_ops);
131762306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_INET6_XFRM_TUNNEL)
131862306a36Sopenharmony_ci	xfrm6_tunnel_deregister(&vti_ip6ip_handler, AF_INET);
131962306a36Sopenharmony_ci	xfrm6_tunnel_deregister(&vti_ipv6_handler, AF_INET6);
132062306a36Sopenharmony_ci#endif
132162306a36Sopenharmony_ci	xfrm6_protocol_deregister(&vti_ipcomp6_protocol, IPPROTO_COMP);
132262306a36Sopenharmony_ci	xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH);
132362306a36Sopenharmony_ci	xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP);
132462306a36Sopenharmony_ci	unregister_pernet_device(&vti6_net_ops);
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cimodule_init(vti6_tunnel_init);
132862306a36Sopenharmony_cimodule_exit(vti6_tunnel_cleanup);
132962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
133062306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("vti6");
133162306a36Sopenharmony_ciMODULE_ALIAS_NETDEV("ip6_vti0");
133262306a36Sopenharmony_ciMODULE_AUTHOR("Steffen Klassert");
133362306a36Sopenharmony_ciMODULE_DESCRIPTION("IPv6 virtual tunnel interface");
1334