aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2005-10-23 17:18:00 +1000
committerHerbert Xu <herbert@gondor.apana.org.au>2005-10-23 17:18:00 +1000
commit49636bb12892786e4a7b207b37ca7b0c5ca1cae0 (patch)
treeb7a29d9344d0e6ee41d5e28f5a7b6fda5da05fa3 /net
parent6fb9974f49f7a6032118c5b6caa6e08e7097913e (diff)
[NEIGH] Fix timer leak in neigh_changeaddr
neigh_changeaddr attempts to delete neighbour timers without setting nud_state. This doesn't work because the timer may have already fired when we acquire the write lock in neigh_changeaddr. The result is that the timer may keep firing for quite a while until the entry reaches NEIGH_FAILED. It should be setting the nud_state straight away so that if the timer has already fired it can simply exit once we relinquish the lock. In fact, this whole function is simply duplicating the logic in neigh_ifdown which in turn is already doing the right thing when it comes to deleting timers and setting nud_state. So all we have to do is take that code out and put it into a common function and make both neigh_changeaddr and neigh_ifdown call it. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'net')
-rw-r--r--net/core/neighbour.c43
1 files changed, 13 insertions, 30 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 37d8d8c2952..1dcf7fa1f0f 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -175,39 +175,10 @@ static void pneigh_queue_purge(struct sk_buff_head *list)
}
}
-void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
-{
- int i;
-
- write_lock_bh(&tbl->lock);
-
- for (i=0; i <= tbl->hash_mask; i++) {
- struct neighbour *n, **np;
-
- np = &tbl->hash_buckets[i];
- while ((n = *np) != NULL) {
- if (dev && n->dev != dev) {
- np = &n->next;
- continue;
- }
- *np = n->next;
- write_lock_bh(&n->lock);
- n->dead = 1;
- neigh_del_timer(n);
- write_unlock_bh(&n->lock);
- neigh_release(n);
- }
- }
-
- write_unlock_bh(&tbl->lock);
-}
-
-int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
+static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
{
int i;
- write_lock_bh(&tbl->lock);
-
for (i = 0; i <= tbl->hash_mask; i++) {
struct neighbour *n, **np = &tbl->hash_buckets[i];
@@ -243,7 +214,19 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
neigh_release(n);
}
}
+}
+void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
+{
+ write_lock_bh(&tbl->lock);
+ neigh_flush_dev(tbl, dev);
+ write_unlock_bh(&tbl->lock);
+}
+
+int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
+{
+ write_lock_bh(&tbl->lock);
+ neigh_flush_dev(tbl, dev);
pneigh_ifdown(tbl, dev);
write_unlock_bh(&tbl->lock);