Commit 3d1e4c8a authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPSEC]: Fix SKB secpath refcounting.

When a secpath is COWed, we lose reference to the states.
parent 596aac2c
......@@ -9,7 +9,6 @@
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
#include <linux/in6.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/dst.h>
......@@ -539,7 +538,6 @@ struct sec_decap_state {
struct sec_path
{
kmem_cache_t *pool;
atomic_t refcnt;
int len;
struct sec_decap_state x[XFRM_MAX_DEPTH];
......@@ -562,6 +560,8 @@ secpath_put(struct sec_path *sp)
__secpath_destroy(sp);
}
extern struct sec_path *secpath_dup(struct sec_path *src);
static inline int
__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x)
{
......@@ -818,8 +818,7 @@ extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy
extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport);
extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard);
extern void xfrm4_input_init(void);
extern void xfrm6_input_init(void);
extern void xfrm_input_init(void);
extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq);
extern void xfrm_probe_algs(void);
......
......@@ -9,13 +9,10 @@
*
*/
#include <linux/slab.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
int xfrm4_rcv(struct sk_buff *skb)
{
return xfrm4_rcv_encap(skb, 0);
......@@ -100,19 +97,12 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
/* Allocate new secpath or COW existing one. */
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep;
struct sec_path *sp;
sp = kmem_cache_alloc(pool, SLAB_ATOMIC);
sp = secpath_dup(skb->sp);
if (!sp)
goto drop;
if (skb->sp) {
memcpy(sp, skb->sp, sizeof(struct sec_path));
if (skb->sp)
secpath_put(skb->sp);
} else {
sp->pool = pool;
sp->len = 0;
}
atomic_set(&sp->refcnt, 1);
skb->sp = sp;
}
if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
......@@ -142,16 +132,3 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
kfree_skb(skb);
return 0;
}
void __init xfrm4_input_init(void)
{
secpath_cachep = kmem_cache_create("secpath4_cache",
sizeof(struct sec_path),
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (!secpath_cachep)
panic("IP: failed to allocate secpath4_cache\n");
}
......@@ -271,7 +271,6 @@ void __init xfrm4_init(void)
{
xfrm4_state_init();
xfrm4_policy_init();
xfrm4_input_init();
}
void __exit xfrm4_fini(void)
......
......@@ -14,8 +14,6 @@
#include <net/ipv6.h>
#include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
static inline void ipip6_ecn_decapsulate(struct ipv6hdr *iph,
struct sk_buff *skb)
{
......@@ -93,19 +91,12 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
/* Allocate new secpath or COW existing one. */
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep;
struct sec_path *sp;
sp = kmem_cache_alloc(pool, SLAB_ATOMIC);
sp = secpath_dup(skb->sp);
if (!sp)
goto drop;
if (skb->sp) {
memcpy(sp, skb->sp, sizeof(struct sec_path));
if (skb->sp)
secpath_put(skb->sp);
} else {
sp->pool = pool;
sp->len = 0;
}
atomic_set(&sp->refcnt, 1);
skb->sp = sp;
}
......@@ -136,15 +127,3 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
kfree_skb(skb);
return -1;
}
void __init xfrm6_input_init(void)
{
secpath_cachep = kmem_cache_create("secpath6_cache",
sizeof(struct sec_path),
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (!secpath_cachep)
panic("IPv6: failed to allocate secpath6_cache\n");
}
......@@ -274,7 +274,6 @@ void __init xfrm6_init(void)
{
xfrm6_policy_init();
xfrm6_state_init();
xfrm6_input_init();
}
void __exit xfrm6_fini(void)
......
......@@ -7,15 +7,38 @@
*
*/
#include <linux/slab.h>
#include <net/ip.h>
#include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
void __secpath_destroy(struct sec_path *sp)
{
int i;
for (i = 0; i < sp->len; i++)
xfrm_state_put(sp->x[i].xvec);
kmem_cache_free(sp->pool, sp);
kmem_cache_free(secpath_cachep, sp);
}
struct sec_path *secpath_dup(struct sec_path *src)
{
struct sec_path *sp;
sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);
if (!sp)
return NULL;
sp->len = 0;
if (src) {
int i;
memcpy(sp, src, sizeof(*sp));
for (i = 0; i < sp->len; i++)
xfrm_state_hold(sp->x[i].xvec);
}
atomic_set(&sp->refcnt, 1);
return sp;
}
/* Fetch spi and seq from ipsec header */
......@@ -50,3 +73,13 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
*seq = *(u32*)(skb->h.raw + offset_seq);
return 0;
}
void __init xfrm_input_init(void)
{
secpath_cachep = kmem_cache_create("secpath_cache",
sizeof(struct sec_path),
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (!secpath_cachep)
panic("XFRM: failed to allocate secpath_cache\n");
}
......@@ -1218,5 +1218,6 @@ void __init xfrm_init(void)
{
xfrm_state_init();
xfrm_policy_init();
xfrm_input_init();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment