diff options
| author | Pavel Emelyanov <xemul@openvz.org> | 2008-03-24 17:48:59 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-03-24 17:48:59 -0400 |
| commit | fa86d322d89995fef1bfb5cc768b89d8c22ea0d9 (patch) | |
| tree | e657b8adc9ccd2e13b2e2276fab4733a273ded09 /net/core | |
| parent | 8f3ea33a5078a09eba12bfe57424507809367756 (diff) | |
[NEIGH]: Fix race between pneigh deletion and ipv6's ndisc_recv_ns (v3).
Proxy neighbors do not have any reference counting, so any caller
of pneigh_lookup (unless it's a netlink triggered add/del routine)
should _not_ perform any actions on the found proxy entry.
There's one exception from this rule - the ipv6's ndisc_recv_ns()
uses found entry to check the flags for NTF_ROUTER.
This creates a race between the ndisc and pneigh_delete - after
the pneigh is returned to the caller, the nd_tbl.lock is dropped
and the deleting procedure may proceed.
One of the fixes would be to add a reference counting, but this
problem exists for ndisc only. Besides such a patch would be too
big for -rc4.
So I propose to introduce a __pneigh_lookup() which is supposed
to be called with the lock held and use it in ndisc code to check
the flags on alive pneigh entry.
Changes from v2:
As David noticed, Exported the __pneigh_lookup() to ipv6 module.
The checkpatch generates a warning on it, since the EXPORT_SYMBOL
does not follow the symbol itself, but in this file all the
exports come at the end, so I decided no to break this harmony.
Changes from v1:
Fixed comments from YOSHIFUJI - indentation of prototype in header
and the pndisc_check_router() name - and a compilation fix, pointed
by Daniel - the is_routed was (falsely) considered as uninitialized
by gcc.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
| -rw-r--r-- | net/core/neighbour.c | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index d9a02b2cc289..19b8e003f150 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c | |||
| @@ -466,6 +466,28 @@ out_neigh_release: | |||
| 466 | goto out; | 466 | goto out; |
| 467 | } | 467 | } |
| 468 | 468 | ||
| 469 | struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, | ||
| 470 | struct net *net, const void *pkey, struct net_device *dev) | ||
| 471 | { | ||
| 472 | struct pneigh_entry *n; | ||
| 473 | int key_len = tbl->key_len; | ||
| 474 | u32 hash_val = *(u32 *)(pkey + key_len - 4); | ||
| 475 | |||
| 476 | hash_val ^= (hash_val >> 16); | ||
| 477 | hash_val ^= hash_val >> 8; | ||
| 478 | hash_val ^= hash_val >> 4; | ||
| 479 | hash_val &= PNEIGH_HASHMASK; | ||
| 480 | |||
| 481 | for (n = tbl->phash_buckets[hash_val]; n; n = n->next) { | ||
| 482 | if (!memcmp(n->key, pkey, key_len) && | ||
| 483 | (n->net == net) && | ||
| 484 | (n->dev == dev || !n->dev)) | ||
| 485 | break; | ||
| 486 | } | ||
| 487 | |||
| 488 | return n; | ||
| 489 | } | ||
| 490 | |||
| 469 | struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, | 491 | struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, |
| 470 | struct net *net, const void *pkey, | 492 | struct net *net, const void *pkey, |
| 471 | struct net_device *dev, int creat) | 493 | struct net_device *dev, int creat) |
| @@ -2803,6 +2825,7 @@ EXPORT_SYMBOL(neigh_table_init_no_netlink); | |||
| 2803 | EXPORT_SYMBOL(neigh_update); | 2825 | EXPORT_SYMBOL(neigh_update); |
| 2804 | EXPORT_SYMBOL(pneigh_enqueue); | 2826 | EXPORT_SYMBOL(pneigh_enqueue); |
| 2805 | EXPORT_SYMBOL(pneigh_lookup); | 2827 | EXPORT_SYMBOL(pneigh_lookup); |
| 2828 | EXPORT_SYMBOL_GPL(__pneigh_lookup); | ||
| 2806 | 2829 | ||
| 2807 | #ifdef CONFIG_ARPD | 2830 | #ifdef CONFIG_ARPD |
| 2808 | EXPORT_SYMBOL(neigh_app_ns); | 2831 | EXPORT_SYMBOL(neigh_app_ns); |
