Commit 33bb7dda authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[XFRM]: Check whether a dst is still valid before adding it to a bundle.

parent 1f6dc81c
...@@ -860,7 +860,7 @@ extern void xfrm_policy_flush(void); ...@@ -860,7 +860,7 @@ extern void xfrm_policy_flush(void);
extern void xfrm_policy_kill(struct xfrm_policy *); extern void xfrm_policy_kill(struct xfrm_policy *);
extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl); extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl);
extern int xfrm_flush_bundles(struct xfrm_state *x); extern int xfrm_flush_bundles(void);
extern wait_queue_head_t km_waitq; extern wait_queue_head_t km_waitq;
extern void km_state_expired(struct xfrm_state *x, int hard); extern void km_state_expired(struct xfrm_state *x, int hard);
......
...@@ -692,6 +692,8 @@ static inline int policy_to_flow_dir(int dir) ...@@ -692,6 +692,8 @@ static inline int policy_to_flow_dir(int dir)
}; };
} }
static int stale_bundle(struct dst_entry *dst);
/* Main function: finds/creates a bundle for given flow. /* Main function: finds/creates a bundle for given flow.
* *
* At the moment we eat a raw IP route. Mostly to speed up lookups * At the moment we eat a raw IP route. Mostly to speed up lookups
...@@ -816,10 +818,11 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl, ...@@ -816,10 +818,11 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
} }
write_lock_bh(&policy->lock); write_lock_bh(&policy->lock);
if (unlikely(policy->dead)) { if (unlikely(policy->dead || stale_bundle(dst))) {
/* Wow! While we worked on resolving, this /* Wow! While we worked on resolving, this
* policy has gone. Retry. It is not paranoia, * policy has gone. Retry. It is not paranoia,
* we just cannot enlist new bundle to dead object. * we just cannot enlist new bundle to dead object.
* We can't enlist stable bundles either.
*/ */
write_unlock_bh(&policy->lock); write_unlock_bh(&policy->lock);
...@@ -986,19 +989,28 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) ...@@ -986,19 +989,28 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
/* Optimize later using cookies and generation ids. */ /* Optimize later using cookies and generation ids. */
static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie) static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie)
{
if (!stale_bundle(dst))
return dst;
dst_release(dst);
return NULL;
}
static int stale_bundle(struct dst_entry *dst)
{ {
struct dst_entry *child = dst; struct dst_entry *child = dst;
while (child) { while (child) {
if (child->obsolete > 0 || if (child->obsolete > 0 ||
(child->dev && !netif_running(child->dev)) ||
(child->xfrm && child->xfrm->km.state != XFRM_STATE_VALID)) { (child->xfrm && child->xfrm->km.state != XFRM_STATE_VALID)) {
dst_release(dst); return 1;
return NULL;
} }
child = child->child; child = child->child;
} }
return dst; return 0;
} }
static void xfrm_dst_destroy(struct dst_entry *dst) static void xfrm_dst_destroy(struct dst_entry *dst)
...@@ -1024,8 +1036,7 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst) ...@@ -1024,8 +1036,7 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst)
return dst; return dst;
} }
static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *), static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
void *data)
{ {
int i; int i;
struct xfrm_policy *pol; struct xfrm_policy *pol;
...@@ -1037,7 +1048,7 @@ static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *), ...@@ -1037,7 +1048,7 @@ static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *),
write_lock(&pol->lock); write_lock(&pol->lock);
dstp = &pol->bundles; dstp = &pol->bundles;
while ((dst=*dstp) != NULL) { while ((dst=*dstp) != NULL) {
if (func(dst, data)) { if (func(dst)) {
*dstp = dst->next; *dstp = dst->next;
dst->next = gc_list; dst->next = gc_list;
gc_list = dst; gc_list = dst;
...@@ -1057,30 +1068,19 @@ static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *), ...@@ -1057,30 +1068,19 @@ static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *),
} }
} }
static int unused_dst(struct dst_entry *dst, void *data) static int unused_bundle(struct dst_entry *dst)
{ {
return !atomic_read(&dst->__refcnt); return !atomic_read(&dst->__refcnt);
} }
static void __xfrm_garbage_collect(void) static void __xfrm_garbage_collect(void)
{ {
xfrm_prune_bundles(unused_dst, NULL); xfrm_prune_bundles(unused_bundle);
} }
static int bundle_depends_on(struct dst_entry *dst, void *data) int xfrm_flush_bundles(void)
{ {
struct xfrm_state *x = data; xfrm_prune_bundles(stale_bundle);
do {
if (dst->xfrm == x)
return 1;
} while ((dst = dst->child) != NULL);
return 0;
}
int xfrm_flush_bundles(struct xfrm_state *x)
{
xfrm_prune_bundles(bundle_depends_on, x);
return 0; return 0;
} }
...@@ -1203,25 +1203,11 @@ void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) ...@@ -1203,25 +1203,11 @@ void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo)
read_unlock(&afinfo->lock); read_unlock(&afinfo->lock);
} }
static int bundle_has_dev(struct dst_entry *dst, void *data)
{
struct net_device *dev = data;
do {
if (dst->dev == dev)
return 1;
} while ((dst = dst->child) != NULL);
return 0;
}
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{ {
struct net_device *dev = ptr;
switch (event) { switch (event) {
case NETDEV_UNREGISTER:
case NETDEV_DOWN: case NETDEV_DOWN:
xfrm_prune_bundles(bundle_has_dev, dev); xfrm_flush_bundles();
} }
return NOTIFY_DONE; return NOTIFY_DONE;
} }
......
...@@ -219,7 +219,7 @@ static void __xfrm_state_delete(struct xfrm_state *x) ...@@ -219,7 +219,7 @@ static void __xfrm_state_delete(struct xfrm_state *x)
* there are DSTs attached to this xfrm_state. * there are DSTs attached to this xfrm_state.
*/ */
if (atomic_read(&x->refcnt) > 2) if (atomic_read(&x->refcnt) > 2)
xfrm_flush_bundles(x); xfrm_flush_bundles();
/* All xfrm_state objects are created by one of two possible /* All xfrm_state objects are created by one of two possible
* paths: * paths:
......
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