diff options
Diffstat (limited to 'net/xfrm/xfrm_input.c')
| -rw-r--r-- | net/xfrm/xfrm_input.c | 69 | 
1 files changed, 68 insertions, 1 deletions
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 347ab31574d5..3f6f6f8c9fa5 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -8,15 +8,29 @@   *   */ +#include <linux/bottom_half.h> +#include <linux/interrupt.h>  #include <linux/slab.h>  #include <linux/module.h>  #include <linux/netdevice.h> +#include <linux/percpu.h>  #include <net/dst.h>  #include <net/ip.h>  #include <net/xfrm.h>  #include <net/ip_tunnels.h>  #include <net/ip6_tunnel.h> +struct xfrm_trans_tasklet { +	struct tasklet_struct tasklet; +	struct sk_buff_head queue; +}; + +struct xfrm_trans_cb { +	int (*finish)(struct net *net, struct sock *sk, struct sk_buff *skb); +}; + +#define XFRM_TRANS_SKB_CB(__skb) ((struct xfrm_trans_cb *)&((__skb)->cb[0])) +  static struct kmem_cache *secpath_cachep __read_mostly;  static DEFINE_SPINLOCK(xfrm_input_afinfo_lock); @@ -25,6 +39,8 @@ static struct xfrm_input_afinfo const __rcu *xfrm_input_afinfo[AF_INET6 + 1];  static struct gro_cells gro_cells;  static struct net_device xfrm_napi_dev; +static DEFINE_PER_CPU(struct xfrm_trans_tasklet, xfrm_trans_tasklet); +  int xfrm_input_register_afinfo(const struct xfrm_input_afinfo *afinfo)  {  	int err = 0; @@ -207,7 +223,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  	xfrm_address_t *daddr;  	struct xfrm_mode *inner_mode;  	u32 mark = skb->mark; -	unsigned int family; +	unsigned int family = AF_UNSPEC;  	int decaps = 0;  	int async = 0;  	bool xfrm_gro = false; @@ -216,6 +232,16 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)  	if (encap_type < 0) {  		x = xfrm_input_state(skb); + +		if (unlikely(x->km.state != XFRM_STATE_VALID)) { +			if (x->km.state == XFRM_STATE_ACQ) +				XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); +			else +				XFRM_INC_STATS(net, +					       LINUX_MIB_XFRMINSTATEINVALID); +			goto drop; +		} +  		family = x->outer_mode->afinfo->family;  		/* An encap_type of -1 indicates async resumption. */ @@ -467,9 +493,41 @@ int xfrm_input_resume(struct sk_buff *skb, int nexthdr)  }  EXPORT_SYMBOL(xfrm_input_resume); +static void xfrm_trans_reinject(unsigned long data) +{ +	struct xfrm_trans_tasklet *trans = (void *)data; +	struct sk_buff_head queue; +	struct sk_buff *skb; + +	__skb_queue_head_init(&queue); +	skb_queue_splice_init(&trans->queue, &queue); + +	while ((skb = __skb_dequeue(&queue))) +		XFRM_TRANS_SKB_CB(skb)->finish(dev_net(skb->dev), NULL, skb); +} + +int xfrm_trans_queue(struct sk_buff *skb, +		     int (*finish)(struct net *, struct sock *, +				   struct sk_buff *)) +{ +	struct xfrm_trans_tasklet *trans; + +	trans = this_cpu_ptr(&xfrm_trans_tasklet); + +	if (skb_queue_len(&trans->queue) >= netdev_max_backlog) +		return -ENOBUFS; + +	XFRM_TRANS_SKB_CB(skb)->finish = finish; +	skb_queue_tail(&trans->queue, skb); +	tasklet_schedule(&trans->tasklet); +	return 0; +} +EXPORT_SYMBOL(xfrm_trans_queue); +  void __init xfrm_input_init(void)  {  	int err; +	int i;  	init_dummy_netdev(&xfrm_napi_dev);  	err = gro_cells_init(&gro_cells, &xfrm_napi_dev); @@ -480,4 +538,13 @@ void __init xfrm_input_init(void)  					   sizeof(struct sec_path),  					   0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,  					   NULL); + +	for_each_possible_cpu(i) { +		struct xfrm_trans_tasklet *trans; + +		trans = &per_cpu(xfrm_trans_tasklet, i); +		__skb_queue_head_init(&trans->queue); +		tasklet_init(&trans->tasklet, xfrm_trans_reinject, +			     (unsigned long)trans); +	}  }  | 
