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.

Signed-off-by: Simon Kelley <[EMAIL PROTECTED]>
 

-- 

diff -Naur linux-2.6.16.11.orig/include/net/neighbour.h 
linux-2.6.16.11/include/net/neighbour.h
--- linux-2.6.16.11.orig/include/net/neighbour.h        2006-04-24 
21:20:24.000000000 +0100
+++ linux-2.6.16.11/include/net/neighbour.h     2006-05-04 20:09:17.000000000 
+0100
@@ -211,6 +211,7 @@
 #define NEIGH_UPDATE_F_ADMIN                   0x80000000
 
 extern void                    neigh_table_init(struct neigh_table *tbl);
+extern void                    neigh_table_init_no_netlink(struct neigh_table 
*tbl);
 extern int                     neigh_table_clear(struct neigh_table *tbl);
 extern struct neighbour *      neigh_lookup(struct neigh_table *tbl,
                                             const void *pkey,
diff -Naur linux-2.6.16.11.orig/net/atm/clip.c linux-2.6.16.11/net/atm/clip.c
--- linux-2.6.16.11.orig/net/atm/clip.c 2006-04-24 21:20:24.000000000 +0100
+++ linux-2.6.16.11/net/atm/clip.c      2006-05-04 20:10:00.000000000 +0100
@@ -995,7 +995,7 @@
 
 static int __init atm_clip_init(void)
 {
-       neigh_table_init(&clip_tbl);
+       neigh_table_init_no_netlink(&clip_tbl);
 
        clip_tbl_hook = &clip_tbl;
        register_atm_ioctl(&clip_ioctl_ops);
diff -Naur linux-2.6.16.11.orig/net/core/neighbour.c 
linux-2.6.16.11/net/core/neighbour.c
--- linux-2.6.16.11.orig/net/core/neighbour.c   2006-04-24 21:20:24.000000000 
+0100
+++ linux-2.6.16.11/net/core/neighbour.c        2006-05-04 20:07:55.000000000 
+0100
@@ -1322,8 +1322,7 @@
        kfree(parms);
 }
 
-
-void neigh_table_init(struct neigh_table *tbl)
+void neigh_table_init_no_netlink(struct neigh_table *tbl)
 {
        unsigned long now = jiffies;
        unsigned long phsize;
@@ -1381,10 +1380,19 @@
 
        tbl->last_flush = now;
        tbl->last_rand  = now + tbl->parms.reachable_time * 20;
-       write_lock(&neigh_tbl_lock);
-       tbl->next       = neigh_tables;
-       neigh_tables    = tbl;
-       write_unlock(&neigh_tbl_lock);
+}
+
+void neigh_table_init(struct neigh_table *tbl)
+{
+  struct neigh_table *tmp;
+
+  neigh_table_init_no_netlink(tbl);
+  write_lock(&neigh_tbl_lock);
+  for (tmp = neigh_tables; tmp; tmp = tmp->next)
+        BUG_ON(tmp->family == tbl->family);
+  tbl->next    = neigh_tables;
+  neigh_tables = tbl;
+  write_unlock(&neigh_tbl_lock);
 }
 
 int neigh_table_clear(struct neigh_table *tbl)
@@ -2655,6 +2663,7 @@
 EXPORT_SYMBOL(neigh_resolve_output);
 EXPORT_SYMBOL(neigh_table_clear);
 EXPORT_SYMBOL(neigh_table_init);
+EXPORT_SYMBOL(neigh_table_init_no_netlink);
 EXPORT_SYMBOL(neigh_update);
 EXPORT_SYMBOL(neigh_update_hhs);
 EXPORT_SYMBOL(pneigh_enqueue);

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to