patch-1.3.78 linux/net/ipv4/arp.c

Next file: linux/net/ipv4/ip_alias.c
Previous file: linux/net/ipv4/af_inet.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.77/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
@@ -18,7 +18,6 @@
  * as published by the Free Software Foundation; either version
  * 2 of the License, or (at your option) any later version.
  *
- *
  * Fixes:
  *		Alan Cox	:	Removed the ethernet assumptions in Florian's code
  *		Alan Cox	:	Fixed some small errors in the ARP logic
@@ -53,6 +52,8 @@
  *		Eckes		:	ARP ioctl control errors.
  *		Alexey Kuznetsov:	Arp free fix.
  *		Manuel Rodriguez:	Gratutious ARP.
+ *              Jonathan Layes  :       Added arpd support through kerneld 
+ *                                      message queue (960314)
  */
 
 /* RFC1122 Status:
@@ -74,12 +75,12 @@
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/errno.h>
-#include <linux/if_arp.h>
 #include <linux/in.h>
 #include <linux/mm.h>
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/if_arp.h>
 #include <linux/trdevice.h>
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
@@ -101,6 +102,9 @@
 #ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
 #endif
+#ifdef CONFIG_ARPD
+#include <linux/kerneld.h>
+#endif /* CONFIG_ARPD */
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -189,6 +193,14 @@
 
 static struct arp_table *arp_backlog;
 
+/* If we have arpd configured, assume that we will be running arpd and keep
+   the internal cache small */
+#ifdef CONFIG_ARPD
+#define ARP_MAXSIZE	256
+#endif /* CONFIG_ARPD */
+
+static unsigned int arp_size = 0;
+
 static void arp_run_bh(void);
 static void arp_check_expire (unsigned long);  
 
@@ -340,6 +352,7 @@
 	restore_flags(flags);
 
 	kfree_s(entry, sizeof(struct arp_table));
+	--arp_size;
 	return;
 }
 
@@ -368,6 +381,179 @@
 	return count;
 }
 
+
+/*
+ *	Force the expiry of an entry in the internal cache so the memory
+ *	can be used for a new request or for loading a query from arpd.
+ *	I'm not really sure what the best algorithm should be, so I just
+ *	search for the oldest.  NOTE:  make sure the cache is locked before
+ *      jumping into this function!  If someone wants to do something
+ *	other than searching the whole cache, by all means do so!
+ */
+
+#ifdef CONFIG_ARPD
+static int arp_force_expire(void)
+{
+	int i;
+	struct arp_table *entry = NULL;
+	struct arp_table **pentry = NULL;
+	struct arp_table **oldest_entry = NULL, **last_resort = NULL;
+	unsigned long oldest_used = ~0;
+
+#if RT_CACHE_DEBUG >= 2
+	printk("Looking for something to force expire.\n");
+#endif
+	for (i = 0; i < ARP_TABLE_SIZE; i++)
+	{
+		pentry = &arp_tables[i];
+
+		while ((entry = *pentry) != NULL)
+		{
+			if (entry->last_used < oldest_used)
+			{
+				if (arp_count_hhs(entry) == 0)
+				{
+					oldest_entry = pentry;
+				}
+				last_resort = pentry;
+				oldest_used = entry->last_used;
+			}
+			pentry = &entry->next;	/* go to next entry */
+		}
+	}
+	if (oldest_entry == NULL)
+	{
+		if (last_resort == NULL)
+			return -1;
+		oldest_entry = last_resort;
+	}
+		
+	entry = *oldest_entry;
+	*oldest_entry = (*oldest_entry)->next;
+#if RT_CACHE_DEBUG >= 2
+	printk("Force expiring %08x\n", entry->ip);
+#endif
+	arp_free_entry(entry);
+	return 0;
+}
+#endif /* CONFIG_ARPD */
+
+
+static void arpd_update(struct arp_table * entry, int loc)
+{
+#ifdef CONFIG_ARPD
+	static struct arpd_request arpreq;
+
+	arpreq.req = ARPD_UPDATE;
+	arpreq.ip = entry->ip;
+	arpreq.mask = entry->mask;
+	memcpy (arpreq.ha, entry->ha, MAX_ADDR_LEN);
+	arpreq.loc = loc;
+	arpreq.last_used = entry->last_used;
+	arpreq.last_updated = entry->last_updated;
+	arpreq.flags = entry->flags;
+	arpreq.dev = entry->dev;
+
+	kerneld_send(KERNELD_ARP, 0, sizeof(arpreq),
+				(char *) &arpreq, NULL);
+#endif /* CONFIG_ARPD */
+}
+
+/* 
+ * Allocate memory for a new entry.  If we are at the maximum limit
+ * of the internal ARP cache, arp_force_expire() an entry.  NOTE:  
+ * arp_force_expire() needs the cache to be locked, so therefore
+ * arp_add_entry() should only be called with the cache locked too!
+ */
+
+static struct arp_table * arp_add_entry(void)
+{
+	struct arp_table * entry;
+
+#ifdef CONFIG_ARPD
+	if (arp_size >= ARP_MAXSIZE)
+	{
+		if (arp_force_expire() < 0)
+			return NULL;
+	}
+#endif /* CONFIG_ARPD */
+
+	entry = (struct arp_table *)
+		kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+
+	if (entry != NULL)
+		++arp_size;
+	return entry;
+}
+
+
+/*
+ * Lookup ARP entry by (addr, dev) pair in the arpd.
+ */
+
+static struct arp_table * arpd_lookup(u32 addr, unsigned short flags, 
+						struct device * dev,
+						int loc)
+{
+#ifdef CONFIG_ARPD
+	static struct arpd_request arpreq, retreq;
+	struct arp_table * entry;
+	int rv, i;
+
+	arpreq.req = ARPD_LOOKUP;
+	arpreq.ip = addr;
+	arpreq.loc = loc;
+
+	rv = kerneld_send(KERNELD_ARP, 
+			sizeof(retreq) | KERNELD_WAIT,
+			sizeof(arpreq),
+			(char *) &arpreq,
+			(char *) &retreq);
+	/* don't worry about rv != 0 too much, it's probably
+	   because arpd isn't running or an entry couldn't
+	   be found */
+
+	if (rv != 0)
+		return NULL;
+	if (dev != retreq.dev)
+		return NULL;
+	if (! memcmp (retreq.ha, "\0\0\0\0\0\0", 6))
+		return NULL;
+
+	arp_fast_lock();
+	entry = arp_add_entry();
+	arp_unlock();
+
+	if (entry == NULL)
+		return NULL;
+
+	entry->next = NULL;
+	entry->last_used = retreq.last_used;
+	entry->last_updated = retreq.last_updated;
+	entry->flags = retreq.flags;
+	entry->ip = retreq.ip;
+	entry->mask = retreq.mask;
+	memcpy (entry->ha, retreq.ha, MAX_ADDR_LEN);
+	arpreq.dev = entry->dev;
+
+	skb_queue_head_init(&entry->skb);
+	entry->hh = NULL;
+	entry->retries = 0;
+
+#if RT_CACHE_DEBUG >= 2
+	printk("Inserting arpd entry %08x\n in local cache.", entry->ip);
+#endif
+	i = HASH(entry->ip);
+	arp_fast_lock();
+	entry->next = arp_tables[i]->next;
+	arp_tables[i]->next = entry;
+	arp_unlock();
+	return entry;
+#endif /* CONFIG_ARPD */
+	return NULL;
+}
+
+
 /*
  * Invalidate all hh's, so that higher level will not try to use it.
  */
@@ -1015,6 +1201,7 @@
 		if (!(entry->flags & ATF_PERM)) {
 			memcpy(entry->ha, sha, dev->addr_len);
 			entry->last_updated = jiffies;
+			arpd_update(entry, __LINE__);
 		}
 		if (!(entry->flags & ATF_COM))
 		{
@@ -1042,11 +1229,13 @@
 		if (grat) 
 			goto end;
 
-		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+                entry = arp_add_entry();
 		if(entry == NULL)
 		{
 			arp_unlock();
+#if RT_CACHE_DEBUG >= 2
 			printk("ARP: no memory for new arp entry\n");
+#endif
 			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
@@ -1060,6 +1249,7 @@
 		entry->timer.data = (unsigned long)entry;
 		memcpy(entry->ha, sha, dev->addr_len);
 		entry->last_updated = entry->last_used = jiffies;
+		arpd_update(entry, __LINE__);
 /*
  *	make entry point to	'correct' device
  */
@@ -1077,7 +1267,7 @@
 		}
 		else
 		{
-#if RT_CACHE_DEBUG >= 1
+#if RT_CACHE_DEBUG >= 2
 			printk("arp_rcv: %08x backlogged\n", entry->ip);
 #endif
 			arp_enqueue(&arp_backlog, entry);
@@ -1142,6 +1332,8 @@
 	arp_fast_lock();
 
 	entry = arp_lookup(paddr, 0, dev);
+	if (entry == NULL)
+		entry = arpd_lookup(paddr, 0, dev, __LINE__);
 
 	if (entry != NULL)
 	{
@@ -1149,10 +1341,12 @@
 		if (entry->flags & ATF_COM)
 		{
 			memcpy(haddr, entry->ha, dev->addr_len);
+			arpd_update(entry, __LINE__);
 			arp_unlock();
 			return 1;
 		}
 	}
+	arpd_update(entry, __LINE__);
 	arp_unlock();
 	return 0;
 }
@@ -1218,6 +1412,8 @@
 	 *	Find an entry
 	 */
 	entry = arp_lookup(paddr, 0, dev);
+	if (entry == NULL)
+		entry = arpd_lookup(paddr, 0, dev, __LINE__);
 
 	if (entry != NULL) 	/* It exists */
 	{
@@ -1267,6 +1463,7 @@
 		
 		entry->last_used = jiffies;
 		memcpy(haddr, entry->ha, dev->addr_len);
+		arpd_update(entry, __LINE__);
 		if (skb)
 			skb->arp = 1;
 		arp_unlock();
@@ -1277,8 +1474,7 @@
 	 *	Create a new unresolved entry.
 	 */
 	
-	entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
-					GFP_ATOMIC);
+	entry = arp_add_entry();
 	if (entry != NULL)
 	{
 		entry->last_updated = entry->last_used = jiffies;
@@ -1288,6 +1484,7 @@
 		memset(entry->ha, 0, dev->addr_len);
 		entry->dev = dev;
 		entry->hh    = NULL;
+		arpd_update(entry, __LINE__);
 		init_timer(&entry->timer);
 		entry->timer.function = arp_expire_request;
 		entry->timer.data = (unsigned long)entry;
@@ -1307,7 +1504,7 @@
 		}
 		else
 		{
-#if RT_CACHE_DEBUG >= 1
+#if RT_CACHE_DEBUG >= 2
 			printk("arp_find: %08x backlogged\n", entry->ip);
 #endif
 			arp_enqueue(&arp_backlog, entry);
@@ -1457,6 +1654,8 @@
 	arp_fast_lock();
 
 	entry = arp_lookup(paddr, 0, dev);
+	if (entry == NULL)
+		entry = arpd_lookup(paddr, 0, dev, __LINE__);
 
 	if (entry)
 	{
@@ -1498,6 +1697,7 @@
 		hh->hh_refcnt++;
 		restore_flags(flags);
 		entry->last_used = jiffies;
+		arpd_update(entry, __LINE__);
 		arp_unlock();
 		return 0;
 	}
@@ -1507,8 +1707,7 @@
 	 *	Create a new unresolved entry.
 	 */
 	
-	entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
-					GFP_ATOMIC);
+	entry = arp_add_entry();
 	if (entry == NULL)
 	{
 		kfree_s(hh, sizeof(struct hh_cache));
@@ -1523,6 +1722,7 @@
 	memset(entry->ha, 0, dev->addr_len);
 	entry->dev = dev;
 	entry->hh = hh;
+	arpd_update(entry, __LINE__);
 	ATOMIC_INCR(&hh->hh_refcnt);
 	init_timer(&entry->timer);
 	entry->timer.function = arp_expire_request;
@@ -1731,6 +1931,8 @@
 	 */
 	
 	entry = arp_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev);
+	if (entry == NULL)
+		entry = arpd_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev, __LINE__);
 
 	if (entry)
 	{
@@ -1744,8 +1946,7 @@
 	
 	if (entry == NULL)
 	{
-		entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
-					GFP_ATOMIC);
+		entry = arp_add_entry();
 		if (entry == NULL)
 		{
 			arp_unlock();
@@ -1782,6 +1983,7 @@
 		ha = dev->dev_addr;
 	memcpy(entry->ha, ha, dev->addr_len);
 	entry->last_updated = entry->last_used = jiffies;
+	arpd_update(entry, __LINE__);
 	entry->flags = r->arp_flags | ATF_COM;
 	if ((entry->flags & ATF_PUBL) && (entry->flags & ATF_NETMASK))
 	{
@@ -1816,6 +2018,9 @@
 	arp_fast_lock();
 
 	entry = arp_lookup(si->sin_addr.s_addr, r->arp_flags|ATF_NETMASK, dev);
+	if (entry == NULL)
+		entry = arpd_lookup(si->sin_addr.s_addr, 
+					r->arp_flags|ATF_NETMASK, dev, __LINE__);
 
 	if (entry == NULL)
 	{

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this