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 @@ ...@@ -9,7 +9,6 @@
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/pfkeyv2.h> #include <linux/pfkeyv2.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/slab.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/dst.h> #include <net/dst.h>
...@@ -539,7 +538,6 @@ struct sec_decap_state { ...@@ -539,7 +538,6 @@ struct sec_decap_state {
struct sec_path struct sec_path
{ {
kmem_cache_t *pool;
atomic_t refcnt; atomic_t refcnt;
int len; int len;
struct sec_decap_state x[XFRM_MAX_DEPTH]; struct sec_decap_state x[XFRM_MAX_DEPTH];
...@@ -562,6 +560,8 @@ secpath_put(struct sec_path *sp) ...@@ -562,6 +560,8 @@ secpath_put(struct sec_path *sp)
__secpath_destroy(sp); __secpath_destroy(sp);
} }
extern struct sec_path *secpath_dup(struct sec_path *src);
static inline int static inline int
__xfrm4_state_addr_cmp(struct xfrm_tmpl *tmpl, struct xfrm_state *x) __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 ...@@ -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 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 km_policy_expired(struct xfrm_policy *pol, int dir, int hard);
extern void xfrm4_input_init(void); extern void xfrm_input_init(void);
extern void xfrm6_input_init(void);
extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq); extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq);
extern void xfrm_probe_algs(void); extern void xfrm_probe_algs(void);
......
...@@ -9,13 +9,10 @@ ...@@ -9,13 +9,10 @@
* *
*/ */
#include <linux/slab.h>
#include <net/inet_ecn.h> #include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
int xfrm4_rcv(struct sk_buff *skb) int xfrm4_rcv(struct sk_buff *skb)
{ {
return xfrm4_rcv_encap(skb, 0); return xfrm4_rcv_encap(skb, 0);
...@@ -100,19 +97,12 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) ...@@ -100,19 +97,12 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
/* Allocate new secpath or COW existing one. */ /* Allocate new secpath or COW existing one. */
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep;
struct sec_path *sp; struct sec_path *sp;
sp = kmem_cache_alloc(pool, SLAB_ATOMIC); sp = secpath_dup(skb->sp);
if (!sp) if (!sp)
goto drop; goto drop;
if (skb->sp) { if (skb->sp)
memcpy(sp, skb->sp, sizeof(struct sec_path));
secpath_put(skb->sp); secpath_put(skb->sp);
} else {
sp->pool = pool;
sp->len = 0;
}
atomic_set(&sp->refcnt, 1);
skb->sp = sp; skb->sp = sp;
} }
if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH) if (xfrm_nr + skb->sp->len > XFRM_MAX_DEPTH)
...@@ -142,16 +132,3 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type) ...@@ -142,16 +132,3 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
kfree_skb(skb); kfree_skb(skb);
return 0; 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) ...@@ -271,7 +271,6 @@ void __init xfrm4_init(void)
{ {
xfrm4_state_init(); xfrm4_state_init();
xfrm4_policy_init(); xfrm4_policy_init();
xfrm4_input_init();
} }
void __exit xfrm4_fini(void) void __exit xfrm4_fini(void)
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/xfrm.h> #include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
static inline void ipip6_ecn_decapsulate(struct ipv6hdr *iph, static inline void ipip6_ecn_decapsulate(struct ipv6hdr *iph,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -93,19 +91,12 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -93,19 +91,12 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
/* Allocate new secpath or COW existing one. */ /* Allocate new secpath or COW existing one. */
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
kmem_cache_t *pool = skb->sp ? skb->sp->pool : secpath_cachep;
struct sec_path *sp; struct sec_path *sp;
sp = kmem_cache_alloc(pool, SLAB_ATOMIC); sp = secpath_dup(skb->sp);
if (!sp) if (!sp)
goto drop; goto drop;
if (skb->sp) { if (skb->sp)
memcpy(sp, skb->sp, sizeof(struct sec_path));
secpath_put(skb->sp); secpath_put(skb->sp);
} else {
sp->pool = pool;
sp->len = 0;
}
atomic_set(&sp->refcnt, 1);
skb->sp = sp; skb->sp = sp;
} }
...@@ -136,15 +127,3 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) ...@@ -136,15 +127,3 @@ int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
kfree_skb(skb); kfree_skb(skb);
return -1; 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) ...@@ -274,7 +274,6 @@ void __init xfrm6_init(void)
{ {
xfrm6_policy_init(); xfrm6_policy_init();
xfrm6_state_init(); xfrm6_state_init();
xfrm6_input_init();
} }
void __exit xfrm6_fini(void) void __exit xfrm6_fini(void)
......
...@@ -7,15 +7,38 @@ ...@@ -7,15 +7,38 @@
* *
*/ */
#include <linux/slab.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/xfrm.h> #include <net/xfrm.h>
static kmem_cache_t *secpath_cachep;
void __secpath_destroy(struct sec_path *sp) void __secpath_destroy(struct sec_path *sp)
{ {
int i; int i;
for (i = 0; i < sp->len; i++) for (i = 0; i < sp->len; i++)
xfrm_state_put(sp->x[i].xvec); 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 */ /* 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) ...@@ -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); *seq = *(u32*)(skb->h.raw + offset_seq);
return 0; 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) ...@@ -1218,5 +1218,6 @@ void __init xfrm_init(void)
{ {
xfrm_state_init(); xfrm_state_init();
xfrm_policy_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