162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C)2003,2004 USAGI/WIDE Project
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors	Mitsuru KANDA  <mk@linux-ipv6.org>
662306a36Sopenharmony_ci *		YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on net/ipv4/xfrm4_tunnel.c
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/xfrm.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/rculist.h>
1462306a36Sopenharmony_ci#include <net/ip.h>
1562306a36Sopenharmony_ci#include <net/xfrm.h>
1662306a36Sopenharmony_ci#include <net/ipv6.h>
1762306a36Sopenharmony_ci#include <linux/ipv6.h>
1862306a36Sopenharmony_ci#include <linux/icmpv6.h>
1962306a36Sopenharmony_ci#include <linux/mutex.h>
2062306a36Sopenharmony_ci#include <net/netns/generic.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256
2362306a36Sopenharmony_ci#define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define XFRM6_TUNNEL_SPI_MIN	1
2662306a36Sopenharmony_ci#define XFRM6_TUNNEL_SPI_MAX	0xffffffff
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct xfrm6_tunnel_net {
2962306a36Sopenharmony_ci	struct hlist_head spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE];
3062306a36Sopenharmony_ci	struct hlist_head spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE];
3162306a36Sopenharmony_ci	u32 spi;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic unsigned int xfrm6_tunnel_net_id __read_mostly;
3562306a36Sopenharmony_cistatic inline struct xfrm6_tunnel_net *xfrm6_tunnel_pernet(struct net *net)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	return net_generic(net, xfrm6_tunnel_net_id);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * xfrm_tunnel_spi things are for allocating unique id ("spi")
4262306a36Sopenharmony_ci * per xfrm_address_t.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistruct xfrm6_tunnel_spi {
4562306a36Sopenharmony_ci	struct hlist_node	list_byaddr;
4662306a36Sopenharmony_ci	struct hlist_node	list_byspi;
4762306a36Sopenharmony_ci	xfrm_address_t		addr;
4862306a36Sopenharmony_ci	u32			spi;
4962306a36Sopenharmony_ci	refcount_t		refcnt;
5062306a36Sopenharmony_ci	struct rcu_head		rcu_head;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic inline unsigned int xfrm6_tunnel_spi_hash_byaddr(const xfrm_address_t *addr)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	unsigned int h;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	h = ipv6_addr_hash((const struct in6_addr *)addr);
6262306a36Sopenharmony_ci	h ^= h >> 16;
6362306a36Sopenharmony_ci	h ^= h >> 8;
6462306a36Sopenharmony_ci	h &= XFRM6_TUNNEL_SPI_BYADDR_HSIZE - 1;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return h;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic inline unsigned int xfrm6_tunnel_spi_hash_byspi(u32 spi)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
7762306a36Sopenharmony_ci	struct xfrm6_tunnel_spi *x6spi;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	hlist_for_each_entry_rcu(x6spi,
8062306a36Sopenharmony_ci			     &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)],
8162306a36Sopenharmony_ci			     list_byaddr, lockdep_is_held(&xfrm6_tunnel_spi_lock)) {
8262306a36Sopenharmony_ci		if (xfrm6_addr_equal(&x6spi->addr, saddr))
8362306a36Sopenharmony_ci			return x6spi;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return NULL;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci__be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct xfrm6_tunnel_spi *x6spi;
9262306a36Sopenharmony_ci	u32 spi;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	rcu_read_lock_bh();
9562306a36Sopenharmony_ci	x6spi = __xfrm6_tunnel_spi_lookup(net, saddr);
9662306a36Sopenharmony_ci	spi = x6spi ? x6spi->spi : 0;
9762306a36Sopenharmony_ci	rcu_read_unlock_bh();
9862306a36Sopenharmony_ci	return htonl(spi);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm6_tunnel_spi_lookup);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int __xfrm6_tunnel_spi_check(struct net *net, u32 spi)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
10562306a36Sopenharmony_ci	struct xfrm6_tunnel_spi *x6spi;
10662306a36Sopenharmony_ci	int index = xfrm6_tunnel_spi_hash_byspi(spi);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	hlist_for_each_entry(x6spi,
10962306a36Sopenharmony_ci			     &xfrm6_tn->spi_byspi[index],
11062306a36Sopenharmony_ci			     list_byspi) {
11162306a36Sopenharmony_ci		if (x6spi->spi == spi)
11262306a36Sopenharmony_ci			return -1;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	return index;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic u32 __xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
12062306a36Sopenharmony_ci	u32 spi;
12162306a36Sopenharmony_ci	struct xfrm6_tunnel_spi *x6spi;
12262306a36Sopenharmony_ci	int index;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (xfrm6_tn->spi < XFRM6_TUNNEL_SPI_MIN ||
12562306a36Sopenharmony_ci	    xfrm6_tn->spi >= XFRM6_TUNNEL_SPI_MAX)
12662306a36Sopenharmony_ci		xfrm6_tn->spi = XFRM6_TUNNEL_SPI_MIN;
12762306a36Sopenharmony_ci	else
12862306a36Sopenharmony_ci		xfrm6_tn->spi++;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for (spi = xfrm6_tn->spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) {
13162306a36Sopenharmony_ci		index = __xfrm6_tunnel_spi_check(net, spi);
13262306a36Sopenharmony_ci		if (index >= 0)
13362306a36Sopenharmony_ci			goto alloc_spi;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		if (spi == XFRM6_TUNNEL_SPI_MAX)
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tn->spi; spi++) {
13962306a36Sopenharmony_ci		index = __xfrm6_tunnel_spi_check(net, spi);
14062306a36Sopenharmony_ci		if (index >= 0)
14162306a36Sopenharmony_ci			goto alloc_spi;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	spi = 0;
14462306a36Sopenharmony_ci	goto out;
14562306a36Sopenharmony_cialloc_spi:
14662306a36Sopenharmony_ci	xfrm6_tn->spi = spi;
14762306a36Sopenharmony_ci	x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, GFP_ATOMIC);
14862306a36Sopenharmony_ci	if (!x6spi)
14962306a36Sopenharmony_ci		goto out;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	memcpy(&x6spi->addr, saddr, sizeof(x6spi->addr));
15262306a36Sopenharmony_ci	x6spi->spi = spi;
15362306a36Sopenharmony_ci	refcount_set(&x6spi->refcnt, 1);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	hlist_add_head_rcu(&x6spi->list_byspi, &xfrm6_tn->spi_byspi[index]);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	index = xfrm6_tunnel_spi_hash_byaddr(saddr);
15862306a36Sopenharmony_ci	hlist_add_head_rcu(&x6spi->list_byaddr, &xfrm6_tn->spi_byaddr[index]);
15962306a36Sopenharmony_ciout:
16062306a36Sopenharmony_ci	return spi;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci__be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct xfrm6_tunnel_spi *x6spi;
16662306a36Sopenharmony_ci	u32 spi;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	spin_lock_bh(&xfrm6_tunnel_spi_lock);
16962306a36Sopenharmony_ci	x6spi = __xfrm6_tunnel_spi_lookup(net, saddr);
17062306a36Sopenharmony_ci	if (x6spi) {
17162306a36Sopenharmony_ci		refcount_inc(&x6spi->refcnt);
17262306a36Sopenharmony_ci		spi = x6spi->spi;
17362306a36Sopenharmony_ci	} else
17462306a36Sopenharmony_ci		spi = __xfrm6_tunnel_alloc_spi(net, saddr);
17562306a36Sopenharmony_ci	spin_unlock_bh(&xfrm6_tunnel_spi_lock);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return htonl(spi);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm6_tunnel_alloc_spi);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void x6spi_destroy_rcu(struct rcu_head *head)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	kmem_cache_free(xfrm6_tunnel_spi_kmem,
18462306a36Sopenharmony_ci			container_of(head, struct xfrm6_tunnel_spi, rcu_head));
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void xfrm6_tunnel_free_spi(struct net *net, xfrm_address_t *saddr)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
19062306a36Sopenharmony_ci	struct xfrm6_tunnel_spi *x6spi;
19162306a36Sopenharmony_ci	struct hlist_node *n;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_lock_bh(&xfrm6_tunnel_spi_lock);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	hlist_for_each_entry_safe(x6spi, n,
19662306a36Sopenharmony_ci				  &xfrm6_tn->spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)],
19762306a36Sopenharmony_ci				  list_byaddr)
19862306a36Sopenharmony_ci	{
19962306a36Sopenharmony_ci		if (xfrm6_addr_equal(&x6spi->addr, saddr)) {
20062306a36Sopenharmony_ci			if (refcount_dec_and_test(&x6spi->refcnt)) {
20162306a36Sopenharmony_ci				hlist_del_rcu(&x6spi->list_byaddr);
20262306a36Sopenharmony_ci				hlist_del_rcu(&x6spi->list_byspi);
20362306a36Sopenharmony_ci				call_rcu(&x6spi->rcu_head, x6spi_destroy_rcu);
20462306a36Sopenharmony_ci				break;
20562306a36Sopenharmony_ci			}
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	spin_unlock_bh(&xfrm6_tunnel_spi_lock);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int xfrm6_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	skb_push(skb, -skb_network_offset(skb));
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return skb_network_header(skb)[IP6CB(skb)->nhoff];
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int xfrm6_tunnel_rcv(struct sk_buff *skb)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct net *net = dev_net(skb->dev);
22562306a36Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
22662306a36Sopenharmony_ci	__be32 spi;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	spi = xfrm6_tunnel_spi_lookup(net, (const xfrm_address_t *)&iph->saddr);
22962306a36Sopenharmony_ci	return xfrm6_rcv_spi(skb, IPPROTO_IPV6, spi, NULL);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
23362306a36Sopenharmony_ci			    u8 type, u8 code, int offset, __be32 info)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	/* xfrm6_tunnel native err handling */
23662306a36Sopenharmony_ci	switch (type) {
23762306a36Sopenharmony_ci	case ICMPV6_DEST_UNREACH:
23862306a36Sopenharmony_ci		switch (code) {
23962306a36Sopenharmony_ci		case ICMPV6_NOROUTE:
24062306a36Sopenharmony_ci		case ICMPV6_ADM_PROHIBITED:
24162306a36Sopenharmony_ci		case ICMPV6_NOT_NEIGHBOUR:
24262306a36Sopenharmony_ci		case ICMPV6_ADDR_UNREACH:
24362306a36Sopenharmony_ci		case ICMPV6_PORT_UNREACH:
24462306a36Sopenharmony_ci		default:
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	case ICMPV6_PKT_TOOBIG:
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	case ICMPV6_TIME_EXCEED:
25162306a36Sopenharmony_ci		switch (code) {
25262306a36Sopenharmony_ci		case ICMPV6_EXC_HOPLIMIT:
25362306a36Sopenharmony_ci			break;
25462306a36Sopenharmony_ci		case ICMPV6_EXC_FRAGTIME:
25562306a36Sopenharmony_ci		default:
25662306a36Sopenharmony_ci			break;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case ICMPV6_PARAMPROB:
26062306a36Sopenharmony_ci		switch (code) {
26162306a36Sopenharmony_ci		case ICMPV6_HDR_FIELD: break;
26262306a36Sopenharmony_ci		case ICMPV6_UNK_NEXTHDR: break;
26362306a36Sopenharmony_ci		case ICMPV6_UNK_OPTION: break;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	default:
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int xfrm6_tunnel_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	if (x->props.mode != XFRM_MODE_TUNNEL) {
27662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 tunnel can only be used with tunnel mode");
27762306a36Sopenharmony_ci		return -EINVAL;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (x->encap) {
28162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 tunnel is not compatible with encapsulation");
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	x->props.header_len = sizeof(struct ipv6hdr);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void xfrm6_tunnel_destroy(struct xfrm_state *x)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct net *net = xs_net(x);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	xfrm6_tunnel_free_spi(net, (xfrm_address_t *)&x->props.saddr);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic const struct xfrm_type xfrm6_tunnel_type = {
29862306a36Sopenharmony_ci	.owner          = THIS_MODULE,
29962306a36Sopenharmony_ci	.proto		= IPPROTO_IPV6,
30062306a36Sopenharmony_ci	.init_state	= xfrm6_tunnel_init_state,
30162306a36Sopenharmony_ci	.destructor	= xfrm6_tunnel_destroy,
30262306a36Sopenharmony_ci	.input		= xfrm6_tunnel_input,
30362306a36Sopenharmony_ci	.output		= xfrm6_tunnel_output,
30462306a36Sopenharmony_ci};
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic struct xfrm6_tunnel xfrm6_tunnel_handler __read_mostly = {
30762306a36Sopenharmony_ci	.handler	= xfrm6_tunnel_rcv,
30862306a36Sopenharmony_ci	.err_handler	= xfrm6_tunnel_err,
30962306a36Sopenharmony_ci	.priority	= 3,
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic struct xfrm6_tunnel xfrm46_tunnel_handler __read_mostly = {
31362306a36Sopenharmony_ci	.handler	= xfrm6_tunnel_rcv,
31462306a36Sopenharmony_ci	.err_handler	= xfrm6_tunnel_err,
31562306a36Sopenharmony_ci	.priority	= 3,
31662306a36Sopenharmony_ci};
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int __net_init xfrm6_tunnel_net_init(struct net *net)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
32162306a36Sopenharmony_ci	unsigned int i;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
32462306a36Sopenharmony_ci		INIT_HLIST_HEAD(&xfrm6_tn->spi_byaddr[i]);
32562306a36Sopenharmony_ci	for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++)
32662306a36Sopenharmony_ci		INIT_HLIST_HEAD(&xfrm6_tn->spi_byspi[i]);
32762306a36Sopenharmony_ci	xfrm6_tn->spi = 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void __net_exit xfrm6_tunnel_net_exit(struct net *net)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct xfrm6_tunnel_net *xfrm6_tn = xfrm6_tunnel_pernet(net);
33562306a36Sopenharmony_ci	unsigned int i;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	xfrm_flush_gc();
33862306a36Sopenharmony_ci	xfrm_state_flush(net, 0, false, true);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++)
34162306a36Sopenharmony_ci		WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i]));
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++)
34462306a36Sopenharmony_ci		WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byspi[i]));
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic struct pernet_operations xfrm6_tunnel_net_ops = {
34862306a36Sopenharmony_ci	.init	= xfrm6_tunnel_net_init,
34962306a36Sopenharmony_ci	.exit	= xfrm6_tunnel_net_exit,
35062306a36Sopenharmony_ci	.id	= &xfrm6_tunnel_net_id,
35162306a36Sopenharmony_ci	.size	= sizeof(struct xfrm6_tunnel_net),
35262306a36Sopenharmony_ci};
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int __init xfrm6_tunnel_init(void)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	int rv;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi",
35962306a36Sopenharmony_ci						  sizeof(struct xfrm6_tunnel_spi),
36062306a36Sopenharmony_ci						  0, SLAB_HWCACHE_ALIGN,
36162306a36Sopenharmony_ci						  NULL);
36262306a36Sopenharmony_ci	if (!xfrm6_tunnel_spi_kmem)
36362306a36Sopenharmony_ci		return -ENOMEM;
36462306a36Sopenharmony_ci	rv = register_pernet_subsys(&xfrm6_tunnel_net_ops);
36562306a36Sopenharmony_ci	if (rv < 0)
36662306a36Sopenharmony_ci		goto out_pernet;
36762306a36Sopenharmony_ci	rv = xfrm_register_type(&xfrm6_tunnel_type, AF_INET6);
36862306a36Sopenharmony_ci	if (rv < 0)
36962306a36Sopenharmony_ci		goto out_type;
37062306a36Sopenharmony_ci	rv = xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6);
37162306a36Sopenharmony_ci	if (rv < 0)
37262306a36Sopenharmony_ci		goto out_xfrm6;
37362306a36Sopenharmony_ci	rv = xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET);
37462306a36Sopenharmony_ci	if (rv < 0)
37562306a36Sopenharmony_ci		goto out_xfrm46;
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ciout_xfrm46:
37962306a36Sopenharmony_ci	xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
38062306a36Sopenharmony_ciout_xfrm6:
38162306a36Sopenharmony_ci	xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
38262306a36Sopenharmony_ciout_type:
38362306a36Sopenharmony_ci	unregister_pernet_subsys(&xfrm6_tunnel_net_ops);
38462306a36Sopenharmony_ciout_pernet:
38562306a36Sopenharmony_ci	kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
38662306a36Sopenharmony_ci	return rv;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void __exit xfrm6_tunnel_fini(void)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
39262306a36Sopenharmony_ci	xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
39362306a36Sopenharmony_ci	xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
39462306a36Sopenharmony_ci	unregister_pernet_subsys(&xfrm6_tunnel_net_ops);
39562306a36Sopenharmony_ci	/* Someone maybe has gotten the xfrm6_tunnel_spi.
39662306a36Sopenharmony_ci	 * So need to wait it.
39762306a36Sopenharmony_ci	 */
39862306a36Sopenharmony_ci	rcu_barrier();
39962306a36Sopenharmony_ci	kmem_cache_destroy(xfrm6_tunnel_spi_kmem);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cimodule_init(xfrm6_tunnel_init);
40362306a36Sopenharmony_cimodule_exit(xfrm6_tunnel_fini);
40462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
40562306a36Sopenharmony_ciMODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_IPV6);
406