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