diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2006-05-12 17:56:08 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-05-12 17:56:08 -0400 |
commit | bd89efc532fe41f867f848144cc8b42054ddf6f9 (patch) | |
tree | fcf90049cb5a15bf6689cdbc6038c3fe22079009 /net | |
parent | ef34814426862c41c061520d4ac833be5914b5ba (diff) |
[NEIGH]: Fix IP-over-ATM and ARP interaction.
The classical IP over ATM code maintains its own IPv4 <-> <ATM stuff>
ARP table, using the standard neighbour-table code. The
neigh_table_init function adds this neighbour table to a linked list
of all neighbor tables which is used by the functions neigh_delete()
neigh_add() and neightbl_set(), all called by the netlink code.
Once the ATM neighbour table is added to the list, there are two
tables with family == AF_INET there, and ARP entries sent via netlink
go into the first table with matching family. This is indeterminate
and often wrong.
To see the bug, on a kernel with CLIP enabled, create a standard IPv4
ARP entry by pinging an unused address on a local subnet. Then attempt
to complete that entry by doing
ip neigh replace <ip address> lladdr <some mac address> nud reachable
Looking at the ARP tables by using
ip neigh show
will reveal two ARP entries for the same address. One of these can be
found in /proc/net/arp, and the other in /proc/net/atm/arp.
This patch adds a new function, neigh_table_init_no_netlink() which
does everything the neigh_table_init() does, except add the table to
the netlink all-arp-tables chain. In addition neigh_table_init() has a
check that all tables on the chain have a distinct address family.
The init call in clip.c is changed to call
neigh_table_init_no_netlink().
Since ATM ARP tables are rather more complicated than can currently be
handled by the available rtattrs in the netlink protocol, no
functionality is lost by this patch, and non-ATM ARP manipulation via
netlink is rescued. A more complete solution would involve a rtattr
for ATM ARP entries and some way for the netlink code to give
neigh_add and friends more information than just address family with
which to find the correct ARP table.
[ I've changed the assertion checking in neigh_table_init() to not
use BUG_ON() while holding neigh_tbl_lock. Instead we remember that
we found an existing tbl with the same family, and after dropping
the lock we'll give a diagnostic kernel log message and a stack dump.
-DaveM ]
Signed-off-by: Simon Kelley <simon@thekelleys.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/atm/clip.c | 2 | ||||
-rw-r--r-- | net/core/neighbour.c | 21 |
2 files changed, 20 insertions, 3 deletions
diff --git a/net/atm/clip.c b/net/atm/clip.c index 1a786bfaa416..72d852982664 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c | |||
@@ -963,7 +963,7 @@ static struct file_operations arp_seq_fops = { | |||
963 | static int __init atm_clip_init(void) | 963 | static int __init atm_clip_init(void) |
964 | { | 964 | { |
965 | struct proc_dir_entry *p; | 965 | struct proc_dir_entry *p; |
966 | neigh_table_init(&clip_tbl); | 966 | neigh_table_init_no_netlink(&clip_tbl); |
967 | 967 | ||
968 | clip_tbl_hook = &clip_tbl; | 968 | clip_tbl_hook = &clip_tbl; |
969 | register_atm_ioctl(&clip_ioctl_ops); | 969 | register_atm_ioctl(&clip_ioctl_ops); |
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 4cf878efdb49..50a8c73caf97 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c | |||
@@ -1326,8 +1326,7 @@ void neigh_parms_destroy(struct neigh_parms *parms) | |||
1326 | kfree(parms); | 1326 | kfree(parms); |
1327 | } | 1327 | } |
1328 | 1328 | ||
1329 | 1329 | void neigh_table_init_no_netlink(struct neigh_table *tbl) | |
1330 | void neigh_table_init(struct neigh_table *tbl) | ||
1331 | { | 1330 | { |
1332 | unsigned long now = jiffies; | 1331 | unsigned long now = jiffies; |
1333 | unsigned long phsize; | 1332 | unsigned long phsize; |
@@ -1383,10 +1382,27 @@ void neigh_table_init(struct neigh_table *tbl) | |||
1383 | 1382 | ||
1384 | tbl->last_flush = now; | 1383 | tbl->last_flush = now; |
1385 | tbl->last_rand = now + tbl->parms.reachable_time * 20; | 1384 | tbl->last_rand = now + tbl->parms.reachable_time * 20; |
1385 | } | ||
1386 | |||
1387 | void neigh_table_init(struct neigh_table *tbl) | ||
1388 | { | ||
1389 | struct neigh_table *tmp; | ||
1390 | |||
1391 | neigh_table_init_no_netlink(tbl); | ||
1386 | write_lock(&neigh_tbl_lock); | 1392 | write_lock(&neigh_tbl_lock); |
1393 | for (tmp = neigh_tables; tmp; tmp = tmp->next) { | ||
1394 | if (tmp->family == tbl->family) | ||
1395 | break; | ||
1396 | } | ||
1387 | tbl->next = neigh_tables; | 1397 | tbl->next = neigh_tables; |
1388 | neigh_tables = tbl; | 1398 | neigh_tables = tbl; |
1389 | write_unlock(&neigh_tbl_lock); | 1399 | write_unlock(&neigh_tbl_lock); |
1400 | |||
1401 | if (unlikely(tmp)) { | ||
1402 | printk(KERN_ERR "NEIGH: Registering multiple tables for " | ||
1403 | "family %d\n", tbl->family); | ||
1404 | dump_stack(); | ||
1405 | } | ||
1390 | } | 1406 | } |
1391 | 1407 | ||
1392 | int neigh_table_clear(struct neigh_table *tbl) | 1408 | int neigh_table_clear(struct neigh_table *tbl) |
@@ -2657,6 +2673,7 @@ EXPORT_SYMBOL(neigh_rand_reach_time); | |||
2657 | EXPORT_SYMBOL(neigh_resolve_output); | 2673 | EXPORT_SYMBOL(neigh_resolve_output); |
2658 | EXPORT_SYMBOL(neigh_table_clear); | 2674 | EXPORT_SYMBOL(neigh_table_clear); |
2659 | EXPORT_SYMBOL(neigh_table_init); | 2675 | EXPORT_SYMBOL(neigh_table_init); |
2676 | EXPORT_SYMBOL(neigh_table_init_no_netlink); | ||
2660 | EXPORT_SYMBOL(neigh_update); | 2677 | EXPORT_SYMBOL(neigh_update); |
2661 | EXPORT_SYMBOL(neigh_update_hhs); | 2678 | EXPORT_SYMBOL(neigh_update_hhs); |
2662 | EXPORT_SYMBOL(pneigh_enqueue); | 2679 | EXPORT_SYMBOL(pneigh_enqueue); |