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

Next file: linux/net/ipv4/devinet.c
Previous file: linux/net/ipv4/Config.in
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.98/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
@@ -103,8 +103,8 @@
 #include <linux/net_alias.h>
 #endif
 #ifdef CONFIG_ARPD
-#include <linux/kerneld.h>
-#endif /* CONFIG_ARPD */
+#include <net/netlink.h>
+#endif
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -112,97 +112,157 @@
 #include <stdarg.h>
 
 /*
- *	This structure defines the ARP mapping cache. As long as we make changes
- *	in this structure, we keep interrupts off. But normally we can copy the
- *	hardware address and the device pointer in a local variable and then 
- *	make any "long calls" to send a packet out.
+ *	Configurable Parameters
  */
 
-struct arp_table
-{
-	struct arp_table		*next;			/* Linked entry list 		*/
-	unsigned long			last_used;		/* For expiry 			*/
-	unsigned long			last_updated;		/* For expiry 			*/
-	unsigned int			flags;			/* Control status 		*/
-	u32				ip;			/* ip address of entry 		*/
-	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
-	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
-	struct device			*dev;			/* Device the entry is tied to 	*/
+/*
+ *	After that time, an unused entry is deleted from the arp table.
+ *	RFC1122 recommends set it to 60*HZ, if your site uses proxy arp
+ *	and dynamic routing.
+ */
 
-	/*
-	 *	The following entries are only used for unresolved hw addresses.
-	 */
-	
-	struct timer_list		timer;			/* expire timer 		*/
-	int				retries;		/* remaining retries	 	*/
-	struct sk_buff_head		skb;			/* list of queued packets 	*/
-	struct hh_cache			*hh;
-};
+#ifndef CONFIG_ARPD
+#define ARP_TIMEOUT		(600*HZ)
+#else
+#define ARP_TIMEOUT		(60*HZ)
+#define ARPD_TIMEOUT		(600*HZ)
+#endif
+
+/*
+ *	How often is ARP cache checked for expire.
+ *	It is useless to set ARP_CHECK_INTERVAL > ARP_TIMEOUT
+ */
 
+#define ARP_CHECK_INTERVAL	(60*HZ)
 
 /*
- *	Configurable Parameters (don't touch unless you know what you are doing
+ *	Soft limit on ARP cache size.
+ *	Note that this number should be greater, than
+ *	number of simultaneously opened sockets, else
+ *	hardware header cache will be not efficient.
  */
 
+#if RT_CACHE_DEBUG >= 2
+#define ARP_MAXSIZE	4
+#else
+#ifdef CONFIG_ARPD
+#define ARP_MAXSIZE	64
+#else
+#define ARP_MAXSIZE	256
+#endif /* CONFIG_ARPD */
+#endif
+
 /*
  *	If an arp request is send, ARP_RES_TIME is the timeout value until the
  *	next request is send.
  * 	RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST)
  *	The recommended minimum timeout is 1 second per destination.
- *	This timeout is prolongated to ARP_DEAD_RES_TIME, if
- *	destination does not respond.
+ *
  */
 
 #define ARP_RES_TIME		(5*HZ)
-#define ARP_DEAD_RES_TIME	(60*HZ)
 
 /*
- *	The number of times an arp request is send, until the host is
- *	considered temporarily unreachable.
+ *	The number of times an broadcast arp request is send, until
+ *	the host is considered temporarily unreachable.
  */
 
 #define ARP_MAX_TRIES		3
 
 /*
- *	After that time, an unused entry is deleted from the arp table.
+ *	The entry is reconfirmed by sending point-to-point ARP
+ *	request after ARP_CONFIRM_INTERVAL.
+ *	RFC1122 recommends 60*HZ.
+ *
+ *	Warning: there exist nodes, that answer only broadcast
+ *	ARP requests (Cisco-4000 in hot standby mode?)
+ *	Now arp code should work with such nodes, but
+ *	it still will generate redundant broadcast requests, so that
+ *	this interval should be enough long.
  */
 
-#define ARP_TIMEOUT		(600*HZ)
+#define ARP_CONFIRM_INTERVAL	(300*HZ)
 
 /*
- *	How often is the function 'arp_check_retries' called.
- *	An unused entry is invalidated in the time between ARP_TIMEOUT and
- *	(ARP_TIMEOUT+ARP_CHECK_INTERVAL).
+ *	We wait for answer to unicast request for ARP_CONFIRM_TIMEOUT.
  */
 
-#define ARP_CHECK_INTERVAL	(60*HZ)
+#define ARP_CONFIRM_TIMEOUT	ARP_RES_TIME
 
 /*
- *	The entry is reconfirmed by sending point-to-point ARP
- *	request after ARP_CONFIRM_INTERVAL. If destinations does not respond
- *	for ARP_CONFIRM_TIMEOUT, normal broadcast resolution scheme is started.
+ *	The number of times an unicast arp request is retried, until
+ *	the cache entry is considered suspicious.
+ *	Value 0 means that no unicast pings will be sent.
+ *	RFC1122 recommends 2.
+ */
+
+#define ARP_MAX_PINGS		1
+
+/*
+ *	When a host is dead, but someone tries to connect it,
+ *	we do not remove corresponding cache entry (it would
+ *	be useless, it will be created again immediately)
+ *	Instead we prolongate interval between broadcasts
+ *	to ARP_DEAD_RES_TIME.
+ *	This interval should be not very long.
+ *	(When the host will be up again, we will notice it only
+ *	when ARP_DEAD_RES_TIME expires, or when the host will arp us.
  */
 
-#define ARP_CONFIRM_INTERVAL	(300*HZ)
-#define ARP_CONFIRM_TIMEOUT	ARP_RES_TIME
+#define ARP_DEAD_RES_TIME	(60*HZ)
+
+/*
+ *	This structure defines the ARP mapping cache.
+ */
+
+struct arp_table
+{
+	struct arp_table		*next;			/* Linked entry list 		*/
+	unsigned long			last_used;		/* For expiry 			*/
+	unsigned long			last_updated;		/* For expiry 			*/
+	unsigned int			flags;			/* Control status 		*/
+	u32				ip;			/* ip address of entry 		*/
+	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
+	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
+	struct device			*dev;			/* Device the entry is tied to 	*/
+	struct hh_cache			*hh;			/* Hardware headers chain	*/
+
+	/*
+	 *	The following entries are only used for unresolved hw addresses.
+	 */
+	
+	struct timer_list		timer;			/* expire timer 		*/
+	int				retries;		/* remaining retries	 	*/
+	struct sk_buff_head		skb;			/* list of queued packets 	*/
+};
+
+
+static atomic_t arp_size = 0;
+
+#ifdef CONFIG_ARPD
+static int arpd_not_running;
+static int arpd_stamp;
+#endif
 
-static unsigned int arp_lock;
 static unsigned int arp_bh_mask;
 
 #define ARP_BH_BACKLOG	1
 
+/*
+ *	Backlog for ARP updates.
+ */
 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 */
+/*
+ *	Backlog for incomplete entries.
+ */
+static struct arp_table *arp_req_backlog;
 
-static unsigned int arp_size = 0;
 
 static void arp_run_bh(void);
 static void arp_check_expire (unsigned long);  
+static int  arp_update (u32 sip, char *sha, struct device * dev,
+	    struct arp_table *ientry, int grat);
 
 static struct timer_list arp_timer =
 	{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -216,12 +276,10 @@
 
 /*
  * 	The size of the hash table. Must be a power of two.
- * 	Maybe we should remove hashing in the future for arp and concentrate
- * 	on Patrick Schaaf's Host-Cache-Lookup...
  */
 
-#define ARP_TABLE_SIZE  16
-#define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1)
+#define ARP_TABLE_SIZE		16
+#define FULL_ARP_TABLE_SIZE	(ARP_TABLE_SIZE+1)
 
 struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
 {
@@ -238,22 +296,47 @@
 #define HASH(paddr) 		(htonl(paddr) & (ARP_TABLE_SIZE - 1))
 
 /*
- * Lock/unlock arp_table chains.
+ *	ARP cache semaphore.
+ *
+ *	Every time when someone wants to traverse arp table,
+ *	he MUST call arp_fast_lock.
+ *	It will guarantee that arp cache list will not change
+ *	by interrupts and the entry that you found will not
+ *	disappear unexpectedly.
+ *	
+ *	If you want to modify arp cache lists, you MUST
+ *	call arp_fast_lock, and check that you are the only
+ *	owner of semaphore (arp_lock == 1). If it is not the case
+ *	you can defer your operation or forgot it,
+ *	but DO NOT TOUCH lists.
+ *
+ *	However, you are allowed to change arp entry contents.
+ *
+ *	Assumptions:
+ *	     -- interrupt code MUST have lock/unlock balanced,
+ *		you cannot lock cache on interrupt and defer unlocking
+ *		to callback.
+ *		In particular, it means that lock/unlock are allowed
+ *		to be non-atomic. They are made atomic, but it was not
+ *		necessary.
+ *	     -- nobody is allowed to sleep while
+ *		it keeps arp locked. (route cache has similar locking
+ *		scheme, but allows sleeping)
+ *		
  */
 
-static __inline__ void arp_fast_lock(void)
-{
-	ATOMIC_INCR(&arp_lock);
-}
+static atomic_t arp_lock;
+
+#define ARP_LOCKED() (arp_lock != 1)
 
-static __inline__ void arp_fast_unlock(void)
+static __inline__ void arp_fast_lock(void)
 {
-	ATOMIC_DECR(&arp_lock);
+	atomic_inc(&arp_lock);
 }
 
 static __inline__ void arp_unlock(void)
 {
-	if (!ATOMIC_DECR_AND_CHECK(&arp_lock) && arp_bh_mask)
+	if (atomic_dec_and_test(&arp_lock) && arp_bh_mask)
 		arp_run_bh();
 }
 
@@ -306,7 +389,7 @@
  * Purge all linked skb's of the entry.
  */
 
-static void arp_release_entry(struct arp_table *entry)
+static void arp_purge_send_q(struct arp_table *entry)
 {
 	struct sk_buff *skb;
 	unsigned long flags;
@@ -328,6 +411,7 @@
 /*
  * 	Release the entry and all resources linked to it: skb's, hh's, timer
  * 	and certainly memory.
+ *	The entry should be already removed from lists.
  */
 
 static void arp_free_entry(struct arp_table *entry)
@@ -336,280 +420,416 @@
 	struct hh_cache *hh, *next;
 
 	del_timer(&entry->timer);
+	arp_purge_send_q(entry);
 
 	save_flags(flags);
 	cli();
-	arp_release_entry(entry);
+	hh = entry->hh;
+	entry->hh = NULL;
+	restore_flags(flags);
 
-	for (hh = entry->hh; hh; hh = next)
+	for ( ; hh; hh = next)
 	{
 		next = hh->hh_next;
-		hh->hh_arp = NULL;
 		hh->hh_uptodate = 0;
-		if (!--hh->hh_refcnt)
+		hh->hh_next = NULL;
+		hh->hh_arp = NULL;
+		if (atomic_dec_and_test(&hh->hh_refcnt))
 			kfree_s(hh, sizeof(struct(struct hh_cache)));
 	}
-	restore_flags(flags);
 
 	kfree_s(entry, sizeof(struct arp_table));
-	--arp_size;
+	atomic_dec(&arp_size);
 	return;
 }
 
 /*
- * How many users has this entry?
+ *	Hardware header cache.
+ *
+ *	BEWARE! Hardware header cache has no locking, so that
+ *	it requires especially careful handling.
+ *	It is the only part of arp+route, where a list
+ *	should be traversed with masked interrupts.
+ *	Luckily, this list contains one element 8), as rule.
+ */
+
+/*
+ *	How many users has this entry?
+ *	The answer is reliable only when interrupts are masked.
  */
 
 static __inline__ int arp_count_hhs(struct arp_table * entry)
 {
-	struct hh_cache *hh, **hhp;
+	struct hh_cache *hh;
 	int count = 0;
 
-	hhp = &entry->hh;
-	while ((hh=*hhp) != NULL)
-	{
-		if (hh->hh_refcnt == 1)
-		{
-			*hhp = hh->hh_next;
-			kfree_s(hh, sizeof(struct hh_cache));
-			continue;
-		}
+	for (hh = entry->hh; hh; hh = hh->hh_next)
 		count += hh->hh_refcnt-1;
-		hhp = &hh->hh_next;
-	}
 
 	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!
+ * Signal to device layer, that hardware address may be changed.
  */
 
-#ifdef CONFIG_ARPD
-static int arp_force_expire(void)
+static __inline__ void arp_update_hhs(struct arp_table * entry)
 {
-	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];
+	struct hh_cache *hh;
 
-		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;
+	for (hh=entry->hh; hh; hh=hh->hh_next)
+		entry->dev->header_cache_update(hh, entry->dev, entry->ha);
 }
-#endif /* CONFIG_ARPD */
 
+/*
+ *	Invalidate all hh's, so that higher level will not try to use it.
+ */
 
-static void arpd_update(struct arp_table * entry, int loc)
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
 {
-#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;
+	struct hh_cache *hh;
 
-	kerneld_send(KERNELD_ARP, 0, sizeof(arpreq),
-				(char *) &arpreq, NULL);
-#endif /* CONFIG_ARPD */
+	for (hh=entry->hh; hh; hh=hh->hh_next)
+		hh->hh_uptodate = 0;
 }
 
-/* 
- * 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!
+/*
+ *	Atomic attaching new hh entry.
+ *	Return 1, if entry has been freed, rather than attached.
  */
 
-static struct arp_table * arp_add_entry(void)
+static int arp_set_hh(struct hh_cache **hhp, struct hh_cache *hh)
 {
-	struct arp_table * entry;
+	unsigned long flags;
+	struct hh_cache *hh1;
+	struct arp_table *entry;
 
-#ifdef CONFIG_ARPD
-	if (arp_size >= ARP_MAXSIZE)
+	atomic_inc(&hh->hh_refcnt);
+
+	save_flags(flags);
+	cli();
+	if ((hh1 = *hhp) == NULL)
 	{
-		if (arp_force_expire() < 0)
-			return NULL;
+		*hhp = hh;
+		restore_flags(flags);
+		return 0;
 	}
-#endif /* CONFIG_ARPD */
 
-	entry = (struct arp_table *)
-		kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+	entry = (struct arp_table*)hh->hh_arp;
 
-	if (entry != NULL)
-		++arp_size;
-	return entry;
+	/*
+	 *	An hh1 entry is already attached to this point.
+	 *	Is it not linked to arp entry? Link it!
+	 */
+	if (!hh1->hh_arp && entry)
+	{
+		atomic_inc(&hh1->hh_refcnt);
+		hh1->hh_next = entry->hh;
+		entry->hh = hh1;
+		hh1->hh_arp = (void*)entry;
+		restore_flags(flags);
+
+		if (entry->flags & ATF_COM)
+			entry->dev->header_cache_update(hh1, entry->dev, entry->ha);
+#if RT_CACHE_DEBUG >= 1
+		printk("arp_set_hh: %08x is reattached. Good!\n", entry->ip);
+#endif
+	}
+#if RT_CACHE_DEBUG >= 1
+	else if (entry)
+		printk("arp_set_hh: %08x rr1 ok!\n", entry->ip);
+#endif
+	restore_flags(flags);
+	if (atomic_dec_and_test(&hh->hh_refcnt))
+		kfree_s(hh, sizeof(struct hh_cache));
+	return 1;
 }
 
+static __inline__ struct hh_cache * arp_alloc_hh(int htype)
+{
+	struct hh_cache *hh;
+	hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+	if (hh)
+	{
+		memset(hh, 0, sizeof(struct hh_cache));
+		hh->hh_type = htype;
+	}
+	return hh;
+}
 
 /*
- * Lookup ARP entry by (addr, dev) pair in the arpd.
+ * Test if a hardware address is all zero
  */
 
-static struct arp_table * arpd_lookup(u32 addr, unsigned short flags, 
-						struct device * dev,
-						int loc)
+static __inline__ int empty(unsigned char * addr, int len)
 {
-#ifdef CONFIG_ARPD
-	static struct arpd_request arpreq, retreq;
-	struct arp_table * entry;
-	int rv, i;
+	while (len > 0)
+	{
+		if (*addr)
+			return 0;
+		len--;
+		addr++;
+	}
+	return 1;
+}
 
-	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();
+#ifdef CONFIG_ARPD
 
-	if (entry == NULL)
-		return NULL;
+/*
+ *	Send ARPD message.
+ */
+static void arpd_send(int req, u32 addr, struct device * dev, char *ha,
+		      unsigned long updated)
+{
+	int retval;
+	struct sk_buff *skb;
+	struct arpd_request *arpreq;
 
-	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;
+	if (arpd_not_running)
+		return;
 
-	skb_queue_head_init(&entry->skb);
-	entry->hh = NULL;
-	entry->retries = 0;
+	skb = alloc_skb(sizeof(struct arpd_request), GFP_ATOMIC);
+	if (skb == NULL)
+		return;
 
-#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;
-}
+	skb->free=1;
+	arpreq=(struct arpd_request *)skb_put(skb, sizeof(struct arpd_request));
+	arpreq->req = req;
+	arpreq->ip  = addr;
+	arpreq->dev = (unsigned long)dev;
+	arpreq->stamp = arpd_stamp;
+	arpreq->updated = updated;
+	if (ha)
+		memcpy(arpreq->ha, ha, sizeof(arpreq->ha));
 
+	retval = netlink_post(NETLINK_ARPD, skb);
+	if (retval)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		if (retval == -EUNATCH)
+			arpd_not_running = 1;
+	}
+}
 
 /*
- * Invalidate all hh's, so that higher level will not try to use it.
+ *	Send ARPD update message.
  */
 
-static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
+static __inline__ void arpd_update(struct arp_table * entry)
 {
-	struct hh_cache *hh;
-
-	for (hh=entry->hh; hh; hh=hh->hh_next)
-		hh->hh_uptodate = 0;
+	if (arpd_not_running)
+		return;
+	arpd_send(ARPD_UPDATE, entry->ip, entry->dev, entry->ha,
+		  entry->last_updated);
 }
 
 /*
- * Signal to device layer, that hardware address may be changed.
+ *	Send ARPD lookup request.
  */
 
-static __inline__ void arp_update_hhs(struct arp_table * entry)
+static __inline__ void arpd_lookup(u32 addr, struct device * dev)
 {
-	struct hh_cache *hh;
-
-	for (hh=entry->hh; hh; hh=hh->hh_next)
-		entry->dev->header_cache_update(hh, entry->dev, entry->ha);
+	if (arpd_not_running)
+		return;
+	arpd_send(ARPD_LOOKUP, addr, dev, NULL, 0);
 }
 
 /*
- *	Check if there are too old entries and remove them. If the ATF_PERM
- *	flag is set, they are always left in the arp cache (permanent entry).
- *      If an entry was not be confirmed  for ARP_CONFIRM_INTERVAL,
- *	declare it invalid and send point-to-point ARP request.
- *	If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
- *	give it to shred by arp_expire_entry.
+ *	Send ARPD flush message.
  */
 
-static void arp_check_expire(unsigned long dummy)
+static __inline__ void arpd_flush(struct device * dev)
 {
-	int i;
-	unsigned long now = jiffies;
+	if (arpd_not_running)
+		return;
+	arpd_send(ARPD_FLUSH, 0, dev, NULL, 0);
+}
 
-	del_timer(&arp_timer);
 
-	if (!arp_lock)
-	{
-		arp_fast_lock();
+static int arpd_callback(struct sk_buff *skb)
+{
+	struct device * dev;
+	struct arpd_request *retreq;
 
-		for (i = 0; i < ARP_TABLE_SIZE; i++)
-		{
-			struct arp_table *entry;
-			struct arp_table **pentry;
-		
-			pentry = &arp_tables[i];
+	arpd_not_running = 0;
+
+	if (skb->len != sizeof(struct arpd_request))
+	{
+		kfree_skb(skb, FREE_READ);
+		return -EINVAL;
+	}
+
+	retreq = (struct arpd_request *)skb->data;
+	dev = (struct device*)retreq->dev;
+
+	if (retreq->stamp != arpd_stamp || !dev)
+	{
+		kfree_skb(skb, FREE_READ);
+		return -EINVAL;
+	}
+
+	if (!retreq->updated || empty(retreq->ha, sizeof(retreq->ha)))
+	{
+/*
+ *	Invalid mapping: drop it and send ARP broadcast.
+ */
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, retreq->ip, dev, dev->pa_addr, NULL, 
+			 dev->dev_addr, NULL);
+	}
+	else
+	{
+		arp_fast_lock();
+		arp_update(retreq->ip, retreq->ha, dev, NULL, 0);
+		arp_unlock();
+
+/*
+ *	Old mapping: we cannot trust it, send ARP broadcast to confirm it.
+ *	If it will answer, the entry will be updated,
+ *	if not ... we are lost. We will use it for ARP_CONFIRM_INTERVAL.
+ */
+		if (jiffies - retreq->updated < ARPD_TIMEOUT)
+			arp_send(ARPOP_REQUEST, ETH_P_ARP, retreq->ip, dev, dev->pa_addr, NULL, 
+				 dev->dev_addr, NULL);
+	}
+
+	kfree_skb(skb, FREE_READ);
+	return sizeof(struct arpd_request);
+}
+
+#else
+
+static __inline__ void arpd_update(struct arp_table * entry)
+{
+	return;
+}
+
+#endif /* CONFIG_ARPD */
+
+
+
+
+/*
+ *	ARP expiration routines.
+ */
+
+/*
+ *	Force the expiry of an entry in the internal cache so the memory
+ *	can be used for a new request.
+ */
+
+static int arp_force_expire(void)
+{
+	int i;
+	struct arp_table *entry, **pentry;
+	struct arp_table **oldest_entry = NULL;
+	unsigned long oldest_used = ~0;
+	unsigned long flags;
+	unsigned long now = jiffies;
+	int result = 0;
+
+	static last_index;
+
+	if (ARP_LOCKED())
+		return 0;
+
+	save_flags(flags);
+
+	if (last_index >= ARP_TABLE_SIZE)
+		last_index = 0;
+
+	for (i = 0; i < ARP_TABLE_SIZE; i++, last_index++)
+	{
+		pentry = &arp_tables[last_index & (ARP_TABLE_SIZE-1)];
+
+		while ((entry = *pentry) != NULL)
+		{
+			if (!(entry->flags & ATF_PERM))
+			{
+				int users;
+				cli();
+				users = arp_count_hhs(entry);
+
+				if (!users && now - entry->last_used > ARP_TIMEOUT)
+				{
+					*pentry = entry->next;
+					restore_flags(flags);
+#if RT_CACHE_DEBUG >= 2
+					printk("arp_force_expire: %08x expired\n", entry->ip);
+#endif
+					arp_free_entry(entry);
+					result++;
+					if (arp_size < ARP_MAXSIZE)
+						goto done;
+					continue;
+				}
+				restore_flags(flags);
+				if (!users && entry->last_used < oldest_used)
+				{
+					oldest_entry = pentry;
+					oldest_used = entry->last_used;
+				}
+			}
+			pentry = &entry->next;
+		}
+	}
+
+done:
+	if (result || !oldest_entry)
+		return result;
+
+	entry = *oldest_entry;
+	*oldest_entry = entry->next;
+#if RT_CACHE_DEBUG >= 2
+	printk("arp_force_expire: expiring %08x\n", entry->ip);
+#endif
+	arp_free_entry(entry);
+	return 1;
+}
+
+/*
+ *	Check if there are too old entries and remove them. If the ATF_PERM
+ *	flag is set, they are always left in the arp cache (permanent entry).
+ *      If an entry was not be confirmed  for ARP_CONFIRM_INTERVAL,
+ *	send point-to-point ARP request.
+ *	If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
+ *	give it to shred by arp_expire_entry.
+ */
+
+static void arp_check_expire(unsigned long dummy)
+{
+	int i;
+	unsigned long now = jiffies;
+
+	del_timer(&arp_timer);
+
+#ifdef CONFIG_ARPD
+	arpd_not_running = 0;
+#endif
+
+	ip_rt_check_expire();
+
+	arp_fast_lock();
+
+	if (!ARP_LOCKED())
+	{
+
+		for (i = 0; i < ARP_TABLE_SIZE; i++)
+		{
+			struct arp_table *entry, **pentry;
+		
+			pentry = &arp_tables[i];
+
+			while ((entry = *pentry) != NULL)
+			{
+				if (entry->flags & ATF_PERM)
+				{
+					pentry = &entry->next;
+					continue;
+				}
 
-			while ((entry = *pentry) != NULL)
-			{
 				cli();
 				if (now - entry->last_used > ARP_TIMEOUT
-				    && !(entry->flags & ATF_PERM)
 				    && !arp_count_hhs(entry))
 				{
 					*pentry = entry->next;
@@ -618,17 +838,15 @@
 					printk("arp_expire: %08x expired\n", entry->ip);
 #endif
 					arp_free_entry(entry);
+					continue;
 				}
-				else if (entry->last_updated
-					 && now - entry->last_updated > ARP_CONFIRM_INTERVAL
-					 && !(entry->flags & ATF_PERM))
+				sti();
+				if (entry->last_updated
+				    && now - entry->last_updated > ARP_CONFIRM_INTERVAL
+				    && !(entry->flags & ATF_PERM))
 				{
 					struct device * dev = entry->dev;
-					pentry = &entry->next;
-					entry->flags &= ~ATF_COM;
-					arp_invalidate_hhs(entry);
-					sti();
-					entry->retries = ARP_MAX_TRIES+1;
+					entry->retries = ARP_MAX_TRIES+ARP_MAX_PINGS;
 					del_timer(&entry->timer);
 					entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
 					add_timer(&entry->timer);
@@ -639,14 +857,12 @@
 					printk("arp_expire: %08x requires confirmation\n", entry->ip);
 #endif
 				}
-				else
-					pentry = &entry->next;	/* go to next entry */
+				pentry = &entry->next;	/* go to next entry */
 			}
 		}
-		arp_unlock();
 	}
 
-	ip_rt_check_expire();
+	arp_unlock();
 
 	/*
 	 *	Set the timer again.
@@ -669,34 +885,46 @@
 	unsigned long hash;
 	unsigned long flags;
 
+	arp_fast_lock();
+
 	save_flags(flags);
 	cli();
+	del_timer(&entry->timer);
 
 	/*
-	 *	Since all timeouts are handled with interrupts enabled, there is a
-	 *	small chance, that this entry has just been resolved by an incoming
-	 *	packet. This is the only race condition, but it is handled...
+	 *	If arp table is locked, defer expire processing.
 	 */
-	
-	if (entry->flags & ATF_COM)
+	if (ARP_LOCKED())
 	{
+#if RT_CACHE_DEBUG >= 1
+		printk(KERN_DEBUG "arp_expire_request: %08x deferred\n", entry->ip);
+#endif
+		entry->timer.expires = jiffies + HZ/10;
+		add_timer(&entry->timer);
 		restore_flags(flags);
+		arp_unlock();
 		return;
 	}
 
-	if (arp_lock)
+	/*
+	 *	Since all timeouts are handled with interrupts enabled, there is a
+	 *	small chance, that this entry has just been resolved by an incoming
+	 *	packet. This is the only race condition, but it is handled...
+	 *
+	 *	One exception: if entry is COMPLETE but old,
+	 *	it means that point-to-point ARP ping has been failed
+	 *	(It really occurs with Cisco 4000 routers)
+	 *	We should reconfirm it.
+	 */
+	
+	if ((entry->flags & ATF_COM) && entry->last_updated
+	    && jiffies - entry->last_updated <= ARP_CONFIRM_INTERVAL)
 	{
-#if RT_CACHE_DEBUG >= 1
-		printk("arp_expire_request: %08x postponed\n", entry->ip);
-#endif
-		del_timer(&entry->timer);
-		entry->timer.expires = jiffies + HZ/10;
-		add_timer(&entry->timer);
 		restore_flags(flags);
+		arp_unlock();
 		return;
 	}
 
-	arp_fast_lock();
 	restore_flags(flags);
 
 	if (entry->last_updated && --entry->retries > 0)
@@ -707,29 +935,47 @@
 		printk("arp_expire_request: %08x timed out\n", entry->ip);
 #endif
 		/* Set new timer. */
-		del_timer(&entry->timer);
 		entry->timer.expires = jiffies + ARP_RES_TIME;
 		add_timer(&entry->timer);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
-			 NULL, dev->dev_addr, NULL);
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr,
+			 entry->retries > ARP_MAX_TRIES ? entry->ha : NULL,
+			 dev->dev_addr, NULL);
 		arp_unlock();
 		return;
 	}
 
-	arp_release_entry(entry);
+	/*
+	 *	The host is really dead.
+	 */
+
+	arp_purge_send_q(entry);
 
 	cli();
 	if (arp_count_hhs(entry))
 	{
+		/*
+		 *	The host is dead, but someone refers to it.
+		 *	It is useless to drop this entry just now,
+		 *	it will be born again, so that
+		 *	we keep it, but slow down retransmitting
+		 *	to ARP_DEAD_RES_TIME.
+		 */
+
 		struct device *dev = entry->dev;
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x is dead\n", entry->ip);
 #endif
-		arp_release_entry(entry);
 		entry->retries = ARP_MAX_TRIES;
+		entry->flags &= ~ATF_COM;
+		arp_invalidate_hhs(entry);
 		restore_flags(flags);
+
+		/*
+		 *	Declare the entry dead.
+		 */
 		entry->last_updated = 0;
-		del_timer(&entry->timer);
+		arpd_update(entry);
+
 		entry->timer.expires = jiffies + ARP_DEAD_RES_TIME;
 		add_timer(&entry->timer);
 		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
@@ -739,30 +985,65 @@
 	}
 	restore_flags(flags);
 
+	entry->last_updated = 0;
+	arpd_update(entry);
+
 	hash = HASH(entry->ip);
 
 	pentry = &arp_tables[hash];
 
 	while (*pentry != NULL)
 	{
-		if (*pentry == entry)
+		if (*pentry != entry)
 		{
-			cli();
-			*pentry = entry->next;
-			restore_flags(flags);
+			pentry = &(*pentry)->next;
+			continue;
+		}
+		*pentry = entry->next;
 #if RT_CACHE_DEBUG >= 2
-			printk("arp_expire_request: %08x is killed\n", entry->ip);
+		printk("arp_expire_request: %08x is killed\n", entry->ip);
 #endif
-			arp_free_entry(entry);
-			arp_unlock();
-			return;
-		}
-		pentry = &(*pentry)->next;
+		arp_free_entry(entry);
 	}
-	printk("arp_expire_request: bug: ARP entry is lost!\n");
 	arp_unlock();
 }
 
+
+/* 
+ * 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_alloc_entry() should only be called with the cache locked too!
+ */
+
+static struct arp_table * arp_alloc_entry(void)
+{
+	struct arp_table * entry;
+
+
+	if (arp_size >= ARP_MAXSIZE)
+		arp_force_expire();
+
+	entry = (struct arp_table *)
+		kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+
+	if (entry != NULL)
+	{
+		atomic_inc(&arp_size);
+		memset(entry, 0, sizeof(struct arp_table));
+
+                entry->mask = DEF_ARP_NETMASK;
+		init_timer(&entry->timer);
+		entry->timer.function = arp_expire_request;
+		entry->timer.data = (unsigned long)entry;
+		entry->last_updated = entry->last_used = jiffies;
+		skb_queue_head_init(&entry->skb);
+	}
+	return entry;
+}
+
+
+
 /*
  *	Purge a device from the ARP queue
  */
@@ -774,15 +1055,17 @@
 	
 	if (event != NETDEV_DOWN)
 		return NOTIFY_DONE;
-	/*
-	 *	This is a bit OTT - maybe we need some arp semaphores instead.
-	 */
 
-#if RT_CACHE_DEBUG >= 1	 
-	if (arp_lock)
-		printk("arp_device_event: bug\n");
+#ifdef  CONFIG_ARPD
+	arpd_flush(dev);
+	arpd_stamp++;
 #endif
+
 	arp_fast_lock();
+#if RT_CACHE_DEBUG >= 1	 
+	if (ARP_LOCKED())
+		printk("arp_device_event: impossible\n");
+#endif
 
 	for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
 	{
@@ -805,104 +1088,29 @@
 }
 
 
+
 /*
- *	Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
- *	message.
+ *	This will try to retransmit everything on the queue.
  */
 
-void arp_send(int type, int ptype, u32 dest_ip, 
-	      struct device *dev, u32 src_ip, 
-	      unsigned char *dest_hw, unsigned char *src_hw,
-	      unsigned char *target_hw)
+static void arp_send_q(struct arp_table *entry)
 {
 	struct sk_buff *skb;
-	struct arphdr *arp;
-	unsigned char *arp_ptr;
 
-	/*
-	 *	No arp on this interface.
-	 */
-	
-	if (dev->flags&IFF_NOARP)
-		return;
+	unsigned long flags;
 
 	/*
-	 *	Allocate a buffer
+	 *	Empty the entire queue, building its data up ready to send
 	 */
 	
-	skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
-				+ dev->hard_header_len, GFP_ATOMIC);
-	if (skb == NULL)
+	if(!(entry->flags&ATF_COM))
 	{
-		printk("ARP: no memory to send an arp packet\n");
-		return;
-	}
-	skb_reserve(skb, dev->hard_header_len);
-	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
-	skb->arp = 1;
-	skb->dev = dev;
-	skb->free = 1;
-	skb->protocol = htons (ETH_P_IP);
-
-	/*
-	 *	Fill the device header for the ARP frame
-	 */
-
-	dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
-
-	/* Fill out the arp protocol part. */
-	arp->ar_hrd = htons(dev->type);
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
-	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
-#else
-	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
-#endif
-#else
-	arp->ar_pro = htons(ETH_P_IP);
-#endif
-	arp->ar_hln = dev->addr_len;
-	arp->ar_pln = 4;
-	arp->ar_op = htons(type);
-
-	arp_ptr=(unsigned char *)(arp+1);
-
-	memcpy(arp_ptr, src_hw, dev->addr_len);
-	arp_ptr+=dev->addr_len;
-	memcpy(arp_ptr, &src_ip,4);
-	arp_ptr+=4;
-	if (target_hw != NULL)
-		memcpy(arp_ptr, target_hw, dev->addr_len);
-	else
-		memset(arp_ptr, 0, dev->addr_len);
-	arp_ptr+=dev->addr_len;
-	memcpy(arp_ptr, &dest_ip, 4);
-
-	dev_queue_xmit(skb, dev, 0);
-}
-
-/*
- *	This will try to retransmit everything on the queue.
- */
-
-static void arp_send_q(struct arp_table *entry)
-{
-	struct sk_buff *skb;
-
-	unsigned long flags;
-
-	/*
-	 *	Empty the entire queue, building its data up ready to send
-	 */
-	
-	if(!(entry->flags&ATF_COM))
-	{
-		printk("arp_send_q: incomplete entry for %s\n",
-				in_ntoa(entry->ip));
-		/* Can't flush the skb, because RFC1122 says to hang on to */
-		/* at least one from any unresolved entry.  --MS */
-		/* What's happened is that someone has 'unresolved' the entry
-		   as we got to use it - this 'can't happen' -- AC */
+		printk("arp_send_q: incomplete entry for %s\n",
+				in_ntoa(entry->ip));
+		/* Can't flush the skb, because RFC1122 says to hang on to */
+		/* at least one from any unresolved entry.  --MS */
+		/* What's happened is that someone has 'unresolved' the entry
+		   as we got to use it - this 'can't happen' -- AC */
 		return;
 	}
 
@@ -927,435 +1135,137 @@
 }
 
 
-/*
- *	Delete an ARP mapping entry in the cache.
- */
-
-static void arp_destroy(struct arp_table * entry)
+static int
+arp_update (u32 sip, char *sha, struct device * dev,
+	    struct arp_table *ientry, int grat)
 {
-	struct arp_table *entry1;
-	struct arp_table **pentry;
+	struct arp_table * entry;
+	unsigned long hash;
 
-	if (entry->flags & ATF_PUBL)
-		pentry = &arp_proxy_list;
-	else
-		pentry = &arp_tables[HASH(entry->ip)];
+	hash = HASH(sip);
 
-	while ((entry1 = *pentry) != NULL)
+	for (entry=arp_tables[hash]; entry; entry = entry->next)
+		if (entry->ip == sip && entry->dev == dev)
+			break;
+
+	if (entry)
 	{
-		if (entry1 == entry)
+/*
+ *	Entry found; update it only if it is not a permanent entry.
+ */
+		if (!(entry->flags & ATF_PERM)) 
 		{
-			*pentry = entry1->next;
 			del_timer(&entry->timer);
-			arp_free_entry(entry);
-			return;
+			entry->last_updated = jiffies;
+			if (memcmp(entry->ha, sha, dev->addr_len)!=0)
+			{
+				memcpy(entry->ha, sha, dev->addr_len);
+				if (entry->flags & ATF_COM)
+					arp_update_hhs(entry);
+			}
+			arpd_update(entry);
 		}
-		pentry = &entry1->next;
-	}
-}
 
+		if (!(entry->flags & ATF_COM))
+		{
 /*
- *	Receive an arp request by the device layer. Maybe I rewrite it, to
- *	use the incoming packet for the reply. The time for the current
- *	"overhead" isn't that high...
+ *	This entry was incomplete.  Delete the retransmit timer
+ *	and switch to complete status.
  */
-
-int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-/*
- *	We shouldn't use this type conversion. Check later.
+			entry->flags |= ATF_COM;
+			arp_update_hhs(entry);
+/* 
+ *	Send out waiting packets. We might have problems, if someone is 
+ *	manually removing entries right now -- entry might become invalid 
+ *	underneath us.
  */
-	
-	struct arphdr *arp = (struct arphdr *)skb->h.raw;
-	unsigned char *arp_ptr= (unsigned char *)(arp+1);
-	struct arp_table *entry;
-	struct arp_table *proxy_entry;
-	unsigned long hash, grat=0;
-	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
-	unsigned char *sha,*tha;
-	u32 sip,tip;
-	
-/*
- *	The hardware length of the packet should match the hardware length
- *	of the device.  Similarly, the hardware types should match.  The
- *	device should be ARP-able.  Also, if pln is not 4, then the lookup
- *	is not from an IP number.  We can't currently handle this, so toss
- *	it. 
- */  
-	if (arp->ar_hln != dev->addr_len    || 
-     		dev->type != ntohs(arp->ar_hrd) || 
-		dev->flags & IFF_NOARP          ||
-		arp->ar_pln != 4)
-	{
-		kfree_skb(skb, FREE_READ);
-		return 0;
-		/* Should this be an error/printk?  Seems like something */
-		/* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */
+			arp_send_q(entry);
+		}
+		return 1;
 	}
 
 /*
- *	Another test.
- *	The logic here is that the protocol being looked up by arp should 
- *	match the protocol the device speaks.  If it doesn't, there is a
- *	problem, so toss the packet.
+ * 	No entry found.  Need to add a new entry to the arp table.
  */
-/* Again, should this be an error/printk? -- MS */
-
-  	switch (dev->type)
-  	{
-#ifdef CONFIG_AX25
-		case ARPHRD_AX25:
-			if(arp->ar_pro != htons(AX25_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
-#endif
-#ifdef CONFIG_NETROM
-		case ARPHRD_NETROM:
-			if(arp->ar_pro != htons(AX25_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
-#endif
-		case ARPHRD_ETHER:
-		case ARPHRD_ARCNET:
-		case ARPHRD_METRICOM:
-			if(arp->ar_pro != htons(ETH_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
+	entry = ientry;
 
-		case ARPHRD_IEEE802:
-			if(arp->ar_pro != htons(ETH_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
+	if (grat && !entry)
+		return 0;
 
-		default:
-			printk("ARP: dev->type mangled!\n");
-			kfree_skb(skb, FREE_READ);
+	if (!entry)
+	{
+		entry = arp_alloc_entry();
+		if (!entry)
 			return 0;
+
+		entry->ip = sip;
+		entry->flags = ATF_COM;
+		memcpy(entry->ha, sha, dev->addr_len);
+		entry->dev = dev;
 	}
 
-/*
- *	Extract fields
- */
+	entry->last_updated = entry->last_used = jiffies;
+	arpd_update(entry);
 
-	sha=arp_ptr;
-	arp_ptr += dev->addr_len;
-	memcpy(&sip, arp_ptr, 4);
-	arp_ptr += 4;
-	tha=arp_ptr;
-	arp_ptr += dev->addr_len;
-	memcpy(&tip, arp_ptr, 4);
-  
-/* 
- *	Check for bad requests for 127.x.x.x and requests for multicast
- *	addresses.  If this is one such, delete it.
- */
-	if (LOOPBACK(tip) || MULTICAST(tip))
+	if (!ARP_LOCKED())
 	{
-		kfree_skb(skb, FREE_READ);
+		entry->next = arp_tables[hash];
+		arp_tables[hash] = entry;
 		return 0;
 	}
+#if RT_CACHE_DEBUG >= 2
+	printk("arp_update: %08x backlogged\n", entry->ip);
+#endif
+	arp_enqueue(&arp_backlog, entry);
+	arp_bh_mask |= ARP_BH_BACKLOG;
+	return 0;
+}
 
-/*
- *  Process entry.  The idea here is we want to send a reply if it is a
- *  request for us or if it is a request for someone else that we hold
- *  a proxy for.  We want to add an entry to our cache if it is a reply
- *  to us or if it is a request for our address.  
- *  (The assumption for this last is that if someone is requesting our 
- *  address, they are probably intending to talk to us, so it saves time 
- *  if we cache their address.  Their address is also probably not in 
- *  our cache, since ours is not in their cache.)
- * 
- *  Putting this another way, we only care about replies if they are to
- *  us, in which case we add them to the cache.  For requests, we care
- *  about those for us and those for our proxies.  We reply to both,
- *  and in the case of requests for us we add the requester to the arp 
- *  cache.
- */
+
+
+static __inline__ struct arp_table *arp_lookup(u32 paddr, struct device * dev)
+{
+	struct arp_table *entry;
+
+	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->next)
+		if (entry->ip == paddr && (!dev || entry->dev == dev))
+			return entry;
+	return NULL;
+}
 
 /*
- *	try to switch to alias device whose addr is tip or closest to sip.
+ *	Find an arp mapping in the cache. If not found, return false.
  */
 
-#ifdef CONFIG_NET_ALIAS
-	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
-	{
-		/*
-		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
-		 */
-		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
+{
+	struct arp_table *entry;
 
-		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+	arp_fast_lock();
+
+	entry = arp_lookup(paddr, dev);
+
+	if (entry != NULL)
+	{
+		entry->last_used = jiffies;
+		if (entry->flags & ATF_COM)
 		{
-			kfree_skb(skb, FREE_READ);
-			return 0;
+			memcpy(haddr, entry->ha, dev->addr_len);
+			arp_unlock();
+			return 1;
 		}
 	}
-#endif
+	arp_unlock();
+	return 0;
+}
 
-	if (arp->ar_op == htons(ARPOP_REQUEST))
-	{ 
-/*
- * Only reply for the real device address or when it's in our proxy tables
- */
-		if (tip != dev->pa_addr)
-		{
-/*
- * 	To get in here, it is a request for someone else.  We need to
- * 	check if that someone else is one of our proxies.  If it isn't,
- * 	we can toss it.
- */
-			arp_fast_lock();
-
-			for (proxy_entry=arp_proxy_list;
-			     proxy_entry;
-			     proxy_entry = proxy_entry->next)
-			{
-				/* we will respond to a proxy arp request
-				   if the masked arp table ip matches the masked
-				   tip. This allows a single proxy arp table
-				   entry to be used on a gateway machine to handle
-				   all requests for a whole network, rather than
-				   having to use a huge number of proxy arp entries
-				   and having to keep them uptodate.
-				   */
-				if (proxy_entry->dev == dev &&
-				    !((proxy_entry->ip^tip)&proxy_entry->mask))
-					break;
-
-			}
-			if (proxy_entry)
-			{
-				memcpy(ha, proxy_entry->ha, dev->addr_len);
-				arp_unlock();
-				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha, sha);
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			else
-			{
-				arp_unlock();
-			}
-		}
-		else
-		{
-/*
- * 	To get here, it must be an arp request for us.  We need to reply.
- */
-			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
-		}
-		grat = 1;
-		goto gratuitous;
-	}
-/*
- *	It is now an arp reply.
- */
-	if(ip_chk_addr(tip)!=IS_MYADDR)
-	{
-/*
- *	Replies to other machines get tossed.
- */
- 		kfree_skb(skb, FREE_READ);
- 		return 0;
- 	}
-/*
- * Now all replies are handled.  Next, anything that falls through to here
- * needs to be added to the arp cache, or have its entry updated if it is 
- * there.
- */
- 
-gratuitous:
-
-	arp_fast_lock();
-
-
-	hash = HASH(sip);
-	for (entry=arp_tables[hash]; entry; entry=entry->next)
-		if (entry->ip == sip && entry->dev == dev)
-			break;
-
-	if (entry)
-	{
-/*
- *	Entry found; update it only if it is not a permanent entry.
- */
-		if (!(entry->flags & ATF_PERM)) 
-		{
-			if(memcmp(entry->ha, sha,dev->addr_len)!=0)
-			{
-				memcpy(entry->ha, sha, dev->addr_len);
-				if(entry->flags & ATF_COM)
-					arp_update_hhs(entry);
-			}
-			entry->last_updated = jiffies;
-			arpd_update(entry, __LINE__);
-		}
-		if (!(entry->flags & ATF_COM))
-		{
-/*
- *	This entry was incomplete.  Delete the retransmit timer
- *	and switch to complete status.
- */
-			del_timer(&entry->timer);
-			entry->flags |= ATF_COM;
-			arp_update_hhs(entry);
-/* 
- *	Send out waiting packets. We might have problems, if someone is 
- *	manually removing entries right now -- entry might become invalid 
- *	underneath us.
- */
-			arp_send_q(entry);
-		}
-	}
-	else
-	{
-/*
- * 	No entry found.  Need to add a new entry to the arp table.
- */
-
-		if (grat) 
-			goto end;
-
-                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;
-		}
-
-                entry->mask = DEF_ARP_NETMASK;
-		entry->ip = sip;
-		entry->flags = ATF_COM;
-		entry->hh    = NULL;
-		init_timer(&entry->timer);
-		entry->timer.function = arp_expire_request;
-		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
- */
-
-#ifdef CONFIG_NET_ALIAS
-		entry->dev = dev;
-#else
-		entry->dev = skb->dev;
-#endif
-		skb_queue_head_init(&entry->skb);
-		if (arp_lock == 1)
-		{
-			entry->next = arp_tables[hash];
-			arp_tables[hash] = entry;
-		}
-		else
-		{
-#if RT_CACHE_DEBUG >= 2
-			printk("arp_rcv: %08x backlogged\n", entry->ip);
-#endif
-			arp_enqueue(&arp_backlog, entry);
-			arp_bh_mask |= ARP_BH_BACKLOG;
-		}
-	}
-
-/*
- *	Replies have been sent, and entries have been added.  All done.
- */
-
-end:
-	kfree_skb(skb, FREE_READ);
-	arp_unlock();
-	return 0;
-}
-
-/*
- * Lookup ARP entry by (addr, dev) pair.
- * Flags: ATF_PUBL - search for proxy entries
- *	  ATF_NETMASK - search for proxy network entry.
- * NOTE:  should be called with locked ARP tables.
- */
 
-static struct arp_table *arp_lookup(u32 paddr, unsigned short flags, struct device * dev)
-{
-	struct arp_table *entry;
-
-	if (!(flags & ATF_PUBL))
-	{
-		for (entry = arp_tables[HASH(paddr)];
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == paddr && (!dev || entry->dev == dev))
-				break;
-		return entry;
-	}
-
-	if (!(flags & ATF_NETMASK))
-	{
-		for (entry = arp_proxy_list;
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == paddr && (!dev || entry->dev == dev))
-				break;
-		return entry;
-	}
-
-	for (entry=arp_proxy_list; entry != NULL; entry = entry->next)
-		if (!((entry->ip^paddr)&entry->mask) && 
-		                                  (!dev || entry->dev == dev))
-			break;
-	return entry;
-}
-
-/*
- *	Find an arp mapping in the cache. If not found, return false.
- */
-
-int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
-{
-	struct arp_table *entry;
-
-	arp_fast_lock();
-
-	entry = arp_lookup(paddr, 0, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(paddr, 0, dev, __LINE__);
-
-	if (entry != NULL)
-	{
-		entry->last_used = jiffies;
-		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;
-}
-
-
-static int arp_set_predefined(int addr_hint, unsigned char * haddr, __u32 paddr, struct device * dev)
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev)
 {
 	switch (addr_hint)
 	{
 		case IS_MYADDR:
-			printk("ARP: arp called for own IP address\n");
+			printk(KERN_DEBUG "ARP: arp called for own IP address\n");
 			memcpy(haddr, dev->dev_addr, dev->addr_len);
 			return 1;
 #ifdef CONFIG_IP_MULTICAST
@@ -1387,325 +1297,221 @@
 }
 
 /*
- *	Find an arp mapping in the cache. If not found, post a request.
+ *	Create a new unresolved entry.
  */
 
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
-	     u32 saddr, struct sk_buff *skb)
+struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache *hh, struct sk_buff *skb)
 {
 	struct arp_table *entry;
-	unsigned long hash;
-
-	if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
-	{
-		if (skb)
-			skb->arp = 1;
-		return 0;
-	}
-
-	hash = HASH(paddr);
-	arp_fast_lock();
-
-	/*
-	 *	Find an entry
-	 */
-	entry = arp_lookup(paddr, 0, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(paddr, 0, dev, __LINE__);
-
-	if (entry != NULL) 	/* It exists */
-	{
-		if (!(entry->flags & ATF_COM))
-		{
-			/*
-			 *	A request was already send, but no reply yet. Thus
-			 *	queue the packet with the previous attempt
-			 */
-			
-			if (skb != NULL)
-			{
-				if (entry->last_updated)
-				{
-					skb_queue_tail(&entry->skb, skb);
-					skb_device_unlock(skb);
-				}
-				/*
-				 * If last_updated==0 host is dead, so
-				 * drop skb's and set socket error.
-				 */
-				else
-				{
-#if 0				
-					/*
-					 * FIXME: ICMP HOST UNREACHABLE should be
-					 *	  sent in this situation. --ANK
-					 */
-					if (skb->sk)
-					{
-						skb->sk->err = EHOSTDOWN;
-						skb->sk->error_report(skb->sk);
-					}
-#else
-					icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
-#endif										
-					dev_kfree_skb(skb, FREE_WRITE);
-				}
-			}
-			arp_unlock();
-			return 1;
-		}
 
-		/*
-		 *	Update the record
-		 */
-		
-		entry->last_used = jiffies;
-		memcpy(haddr, entry->ha, dev->addr_len);
-		arpd_update(entry, __LINE__);
-		if (skb)
-			skb->arp = 1;
-		arp_unlock();
-		return 0;
-	}
+	entry = arp_alloc_entry();
 
-	/*
-	 *	Create a new unresolved entry.
-	 */
-	
-	entry = arp_add_entry();
 	if (entry != NULL)
 	{
-		entry->last_updated = entry->last_used = jiffies;
-		entry->flags = 0;
 		entry->ip = paddr;
-		entry->mask = DEF_ARP_NETMASK;
-		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;
+		if (hh)
+		{
+			entry->hh = hh;
+			atomic_inc(&hh->hh_refcnt);
+			hh->hh_arp = (void*)entry;
+		}
 		entry->timer.expires = jiffies + ARP_RES_TIME;
-		skb_queue_head_init(&entry->skb);
+
 		if (skb != NULL)
 		{
 			skb_queue_tail(&entry->skb, skb);
 			skb_device_unlock(skb);
 		}
-		if (arp_lock == 1)
+
+		if (!ARP_LOCKED())
 		{
+			unsigned long hash = HASH(paddr);
 			entry->next = arp_tables[hash];
 			arp_tables[hash] = entry;
 			add_timer(&entry->timer);
 			entry->retries = ARP_MAX_TRIES;
+#ifdef CONFIG_ARPD
+			if (!arpd_not_running)
+				arpd_lookup(paddr, dev);
+			else
+#endif
+				arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, 
+					 dev->dev_addr, NULL);
 		}
 		else
 		{
 #if RT_CACHE_DEBUG >= 2
-			printk("arp_find: %08x backlogged\n", entry->ip);
+			printk("arp_new_entry: %08x backlogged\n", entry->ip);
 #endif
-			arp_enqueue(&arp_backlog, entry);
+			arp_enqueue(&arp_req_backlog, entry);
 			arp_bh_mask |= ARP_BH_BACKLOG;
 		}
 	}
-	else if (skb != NULL)
-		dev_kfree_skb(skb, FREE_WRITE);
-	arp_unlock();
-
-	/*
-	 *	If we didn't find an entry, we will try to send an ARP packet.
-	 */
-	
-	arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, 
-		 dev->dev_addr, NULL);
-
-	return 1;
+	return entry;
 }
 
 
 /*
- *	Write the contents of the ARP cache to a PROCfs file.
+ *	Find an arp mapping in the cache. If not found, post a request.
  */
 
-#define HBUFFERLEN 30
-
-int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
+	     u32 saddr, struct sk_buff *skb)
 {
-	int len=0;
-	off_t pos=0;
-	int size;
 	struct arp_table *entry;
-	char hbuffer[HBUFFERLEN];
-	int i,j,k;
-	const char hexbuf[] =  "0123456789ABCDEF";
-
-	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
+	unsigned long hash;
 
-	pos+=size;
-	len+=size;
+	if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
+	{
+		if (skb)
+			skb->arp = 1;
+		return 0;
+	}
 
+	hash = HASH(paddr);
 	arp_fast_lock();
 
-	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
+	/*
+	 *	Find an entry
+	 */
+	entry = arp_lookup(paddr, dev);
+
+	if (entry != NULL) 	/* It exists */
 	{
-		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
+		if (entry->flags & ATF_COM)
 		{
-/*
- *	Convert hardware address to XX:XX:XX:XX ... form.
- */
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
-			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
-			else {
-#else
-			if(entry->dev->type==ARPHRD_AX25)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
-			else {
-#endif
-#endif
+			entry->last_used = jiffies;
+			memcpy(haddr, entry->ha, dev->addr_len);
+			if (skb)
+				skb->arp = 1;
+			arp_unlock();
+			return 0;
+		}
 
-			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
+		/*
+		 *	A request was already send, but no reply yet. Thus
+		 *	queue the packet with the previous attempt
+		 */
+			
+		if (skb != NULL)
+		{
+			if (entry->last_updated)
 			{
-				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
-				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
-				hbuffer[k++]=':';
+				skb_queue_tail(&entry->skb, skb);
+				skb_device_unlock(skb);
 			}
-			hbuffer[--k]=0;
-	
-#ifdef CONFIG_AX25
+			/*
+			 * If last_updated==0 host is dead, so
+			 * drop skb's and set socket error.
+			 */
+			else
+			{
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+				dev_kfree_skb(skb, FREE_WRITE);
 			}
-#endif
-			size = sprintf(buffer+len,
-				"%-17s0x%-10x0x%-10x%s",
-				in_ntoa(entry->ip),
-				(unsigned int)entry->dev->type,
-				entry->flags,
-				hbuffer);
-#if RT_CACHE_DEBUG < 2
-			size += sprintf(buffer+len+size,
-				 "     %-17s %s\n",
-				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask), entry->dev->name);
-#else
-			size += sprintf(buffer+len+size,
-				 "     %-17s %s\t%ld\t%1d\n",
-				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask), entry->dev->name, 
-				 entry->hh ? entry->hh->hh_refcnt : -1,
-				 entry->hh ? entry->hh->hh_uptodate : 0);
-#endif
-	
-			len += size;
-			pos += size;
-		  
-			if (pos <= offset)
-				len=0;
-			if (pos >= offset+length)
-				goto done;
 		}
+		arp_unlock();
+		return 1;
 	}
-done:
+
+	entry = arp_new_entry(paddr, dev, NULL, skb);
+
+	if (skb != NULL && !entry)
+		dev_kfree_skb(skb, FREE_WRITE);
+
 	arp_unlock();
-  
-	*start = buffer+len-(pos-offset);	/* Start of wanted data */
-	len = pos-offset;			/* Start slop */
-	if (len>length)
-		len = length;			/* Ending slop */
-	return len;
+	return 1;
 }
 
-
+/*
+ *	Binding hardware header cache entry.
+ *	It is the only really complicated part of arp code.
+ *	We have no locking for hh records, so that
+ *	all possible race conditions should be resolved by
+ *	cli()/sti() pairs.
+ *
+ *	Important note: hhs never disapear from lists, if ARP_LOCKED,
+ *	this fact allows to scan hh lists with enabled interrupts,
+ *	but results in generating duplicate hh entries.
+ *	It is harmless. (and I've never seen such event)
+ *
+ *	Returns 0, if hh has been just created, so that
+ *	caller should fill it.
+ */
 
 int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
 {
 	struct arp_table *entry;
-	struct hh_cache *hh = *hhp;
+	struct hh_cache *hh;
 	int addr_hint;
 	unsigned long flags;
 
-	if (hh)
-		return 1;
+	save_flags(flags);
 
 	if ((addr_hint = ip_chk_addr(paddr)) != 0)
 	{
 		unsigned char haddr[MAX_ADDR_LEN];
-		if (hh)
+		if (*hhp)
 			return 1;
-		hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+		hh = arp_alloc_hh(htype);
 		if (!hh)
 			return 1;
 		arp_set_predefined(addr_hint, haddr, paddr, dev);
-		hh->hh_uptodate = 0;
-		hh->hh_refcnt = 1;
-		hh->hh_arp = NULL;
-		hh->hh_next = NULL;
-		hh->hh_type = htype;
-		*hhp = hh;
 		dev->header_cache_update(hh, dev, haddr);
-		return 0;
+		return arp_set_hh(hhp, hh);
 	}
 
-	save_flags(flags);
-
 	arp_fast_lock();
 
-	entry = arp_lookup(paddr, 0, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(paddr, 0, dev, __LINE__);
+	entry = arp_lookup(paddr, dev);
 
 	if (entry)
 	{
-		cli();
 		for (hh = entry->hh; hh; hh=hh->hh_next)
 			if (hh->hh_type == htype)
 				break;
+
 		if (hh)
 		{
-			hh->hh_refcnt++;
-			*hhp = hh;
-			restore_flags(flags);
+			arp_set_hh(hhp, hh);
 			arp_unlock();
 			return 1;
 		}
-		restore_flags(flags);
 	}
 
-	hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+	hh = arp_alloc_hh(htype);
 	if (!hh)
 	{
 		arp_unlock();
 		return 1;
 	}
 
-	hh->hh_uptodate = 0;
-	hh->hh_refcnt = 1;
-	hh->hh_arp = NULL;
-	hh->hh_next = NULL;
-	hh->hh_type = htype;
-
 	if (entry)
 	{
-		dev->header_cache_update(hh, dev, entry->ha);
-		*hhp = hh;
+
 		cli();
 		hh->hh_arp = (void*)entry;
+		hh->hh_next = entry->hh;
 		entry->hh = hh;
-		hh->hh_refcnt++;
+		atomic_inc(&hh->hh_refcnt);
 		restore_flags(flags);
+
+		if (entry->flags & ATF_COM)
+			dev->header_cache_update(hh, dev, entry->ha);
+
+		if (arp_set_hh(hhp, hh))
+		{
+			arp_unlock();
+			return 0;
+		}
+
 		entry->last_used = jiffies;
-		arpd_update(entry, __LINE__);
 		arp_unlock();
 		return 0;
 	}
 
-
-	/*
-	 *	Create a new unresolved entry.
-	 */
-	
-	entry = arp_add_entry();
+	entry = arp_new_entry(paddr, dev, hh, NULL);
 	if (entry == NULL)
 	{
 		kfree_s(hh, sizeof(struct hh_cache));
@@ -1713,91 +1519,74 @@
 		return 1;
 	}
 
-	entry->last_updated = entry->last_used = jiffies;
-	entry->flags = 0;
-	entry->ip = paddr;
-	entry->mask = DEF_ARP_NETMASK;
-	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;
-	entry->timer.data = (unsigned long)entry;
-	entry->timer.expires = jiffies + ARP_RES_TIME;
-	skb_queue_head_init(&entry->skb);
-
-	if (arp_lock == 1)
+	if (!arp_set_hh(hhp, hh))
 	{
-		unsigned long hash = HASH(paddr);
-		cli();
-		entry->next = arp_tables[hash];
-		arp_tables[hash] = entry;
-		hh->hh_arp = (void*)entry;
-		entry->retries = ARP_MAX_TRIES;
-		restore_flags(flags);
-
-		add_timer(&entry->timer);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
-	}
-	else
-	{
-#if RT_CACHE_DEBUG >= 1
-		printk("arp_cache_bind: %08x backlogged\n", entry->ip);
-#endif
-		arp_enqueue(&arp_backlog, entry);
-		arp_bh_mask |= ARP_BH_BACKLOG;
+		arp_unlock();
+		return 0;
 	}
-	*hhp = hh;
 	arp_unlock();
-	return 0;
+	return 1;
 }
 
 static void arp_run_bh()
 {
 	unsigned long flags;
 	struct arp_table *entry, *entry1;
+	struct device  * dev;
+	unsigned long hash;
 	struct hh_cache *hh;
-	__u32 sip;
+	u32 sip;
 
 	save_flags(flags);
 	cli();
-	if (!arp_lock)
+	arp_fast_lock();
+
+	while (arp_bh_mask)
 	{
-		arp_fast_lock();
+		arp_bh_mask  &= ~ARP_BH_BACKLOG;
 
 		while ((entry = arp_dequeue(&arp_backlog)) != NULL)
 		{
-			unsigned long hash;
-			sti();
+			restore_flags(flags);
+			if (arp_update(entry->ip, entry->ha, entry->dev, entry, 0))
+				arp_free_entry(entry);
+			cli();
+		}
+
+		cli();
+		while ((entry = arp_dequeue(&arp_req_backlog)) != NULL)
+		{
+			restore_flags(flags);
+
+			dev = entry->dev;
 			sip = entry->ip;
 			hash = HASH(sip);
 
-			/* It's possible, that an entry with the same pair 
-			 * (addr,type) was already created. Our entry is older,
-			 * so it should be discarded.
-			 */
-			for (entry1=arp_tables[hash]; entry1; entry1=entry1->next)
-				if (entry1->ip==sip && entry1->dev == entry->dev)
+			for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next)
+				if (entry1->ip == sip && entry1->dev == dev)
 					break;
 
 			if (!entry1)
 			{
-				struct device  * dev = entry->dev;
 				cli();
 				entry->next = arp_tables[hash];
 				arp_tables[hash] = entry;
-				for (hh=entry->hh; hh; hh=hh->hh_next)
-					hh->hh_arp = (void*)entry;
-				sti();
-				del_timer(&entry->timer);
+				restore_flags(flags);
 				entry->timer.expires = jiffies + ARP_RES_TIME;
-				add_timer(&entry->timer);
 				entry->retries = ARP_MAX_TRIES;
-				arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+				entry->last_used = jiffies;
+				if (!(entry->flags & ATF_COM))
+				{
+					add_timer(&entry->timer);
+#ifdef CONFIG_ARPD
+					if (!arpd_not_running)
+						arpd_lookup(sip, dev);
+					else
+#endif
+						arp_send(ARPOP_REQUEST, ETH_P_ARP, sip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+				}
 #if RT_CACHE_DEBUG >= 1
-				printk("arp_run_bh: %08x reinstalled\n", sip);
+				printk(KERN_DEBUG "arp_run_bh: %08x reinstalled\n", sip);
 #endif
 			}
 			else
@@ -1824,59 +1613,353 @@
 				while ((skb = skb_dequeue(&entry->skb)) != NULL)
 				{
 					skb_device_lock(skb);
-					sti();
+					restore_flags(flags);
 					skb_queue_tail(&entry1->skb, skb);
 					skb_device_unlock(skb);
 					cli();
 				}
-				sti();
+				restore_flags(flags);
 				
-#if RT_CACHE_DEBUG >= 1
-				printk("arp_run_bh: entry %08x was born dead\n", entry->ip);
-#endif
 				arp_free_entry(entry);
 
-				if (entry1->flags & ATF_COM)
-				{
-					arp_update_hhs(entry1);
-					arp_send_q(entry1);
-				}
+				if (entry1->flags & ATF_COM)
+				{
+					arp_update_hhs(entry1);
+					arp_send_q(entry1);
+				}
+			}
+			cli();
+		}
+		cli();
+	}
+	arp_unlock();
+	restore_flags(flags);
+}
+
+
+/*
+ *	Interface to link layer: send routine and receive handler.
+ */
+
+/*
+ *	Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
+ *	message.
+ */
+
+void arp_send(int type, int ptype, u32 dest_ip, 
+	      struct device *dev, u32 src_ip, 
+	      unsigned char *dest_hw, unsigned char *src_hw,
+	      unsigned char *target_hw)
+{
+	struct sk_buff *skb;
+	struct arphdr *arp;
+	unsigned char *arp_ptr;
+
+	/*
+	 *	No arp on this interface.
+	 */
+	
+	if (dev->flags&IFF_NOARP)
+		return;
+
+	/*
+	 *	Allocate a buffer
+	 */
+	
+	skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+				+ dev->hard_header_len, GFP_ATOMIC);
+	if (skb == NULL)
+	{
+		printk("ARP: no memory to send an arp packet\n");
+		return;
+	}
+	skb_reserve(skb, dev->hard_header_len);
+	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
+	skb->arp = 1;
+	skb->dev = dev;
+	skb->free = 1;
+	skb->protocol = htons (ETH_P_IP);
+
+	/*
+	 *	Fill the device header for the ARP frame
+	 */
+
+	dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
+
+	/* Fill out the arp protocol part. */
+	arp->ar_hrd = htons(dev->type);
+#ifdef CONFIG_AX25
+#ifdef CONFIG_NETROM
+	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
+#else
+	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
+#endif
+#else
+	arp->ar_pro = htons(ETH_P_IP);
+#endif
+	arp->ar_hln = dev->addr_len;
+	arp->ar_pln = 4;
+	arp->ar_op = htons(type);
+
+	arp_ptr=(unsigned char *)(arp+1);
+
+	memcpy(arp_ptr, src_hw, dev->addr_len);
+	arp_ptr+=dev->addr_len;
+	memcpy(arp_ptr, &src_ip,4);
+	arp_ptr+=4;
+	if (target_hw != NULL)
+		memcpy(arp_ptr, target_hw, dev->addr_len);
+	else
+		memset(arp_ptr, 0, dev->addr_len);
+	arp_ptr+=dev->addr_len;
+	memcpy(arp_ptr, &dest_ip, 4);
+
+	dev_queue_xmit(skb, dev, 0);
+}
+
+
+/*
+ *	Receive an arp request by the device layer.
+ */
+
+int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+/*
+ *	We shouldn't use this type conversion. Check later.
+ */
+	
+	struct arphdr *arp = (struct arphdr *)skb->h.raw;
+	unsigned char *arp_ptr= (unsigned char *)(arp+1);
+	unsigned char *sha,*tha;
+	u32 sip,tip;
+	
+/*
+ *	The hardware length of the packet should match the hardware length
+ *	of the device.  Similarly, the hardware types should match.  The
+ *	device should be ARP-able.  Also, if pln is not 4, then the lookup
+ *	is not from an IP number.  We can't currently handle this, so toss
+ *	it. 
+ */  
+	if (arp->ar_hln != dev->addr_len    || 
+     		dev->type != ntohs(arp->ar_hrd) || 
+		dev->flags & IFF_NOARP          ||
+		arp->ar_pln != 4)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+		/* Should this be an error/printk?  Seems like something */
+		/* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */
+	}
+
+/*
+ *	Another test.
+ *	The logic here is that the protocol being looked up by arp should 
+ *	match the protocol the device speaks.  If it doesn't, there is a
+ *	problem, so toss the packet.
+ */
+/* Again, should this be an error/printk? -- MS */
+
+  	switch (dev->type)
+  	{
+#ifdef CONFIG_AX25
+		case ARPHRD_AX25:
+			if(arp->ar_pro != htons(AX25_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+#endif
+#ifdef CONFIG_NETROM
+		case ARPHRD_NETROM:
+			if(arp->ar_pro != htons(AX25_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+#endif
+		case ARPHRD_ETHER:
+		case ARPHRD_ARCNET:
+		case ARPHRD_METRICOM:
+			if(arp->ar_pro != htons(ETH_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+
+		case ARPHRD_IEEE802:
+			if(arp->ar_pro != htons(ETH_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+
+		default:
+			printk("ARP: dev->type mangled!\n");
+			kfree_skb(skb, FREE_READ);
+			return 0;
+	}
+
+/*
+ *	Extract fields
+ */
+
+	sha=arp_ptr;
+	arp_ptr += dev->addr_len;
+	memcpy(&sip, arp_ptr, 4);
+	arp_ptr += 4;
+	tha=arp_ptr;
+	arp_ptr += dev->addr_len;
+	memcpy(&tip, arp_ptr, 4);
+  
+/* 
+ *	Check for bad requests for 127.x.x.x and requests for multicast
+ *	addresses.  If this is one such, delete it.
+ */
+	if (LOOPBACK(tip) || MULTICAST(tip))
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+/*
+ *  Process entry.  The idea here is we want to send a reply if it is a
+ *  request for us or if it is a request for someone else that we hold
+ *  a proxy for.  We want to add an entry to our cache if it is a reply
+ *  to us or if it is a request for our address.  
+ *  (The assumption for this last is that if someone is requesting our 
+ *  address, they are probably intending to talk to us, so it saves time 
+ *  if we cache their address.  Their address is also probably not in 
+ *  our cache, since ours is not in their cache.)
+ * 
+ *  Putting this another way, we only care about replies if they are to
+ *  us, in which case we add them to the cache.  For requests, we care
+ *  about those for us and those for our proxies.  We reply to both,
+ *  and in the case of requests for us we add the requester to the arp 
+ *  cache.
+ */
+
+/*
+ *	try to switch to alias device whose addr is tip or closest to sip.
+ */
+
+#ifdef CONFIG_NET_ALIAS
+	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
+	{
+		/*
+		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
+		 */
+		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+
+		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+	}
+#endif
+
+	if (arp->ar_op == htons(ARPOP_REQUEST))
+	{ 
+
+/*
+ * Only reply for the real device address or when it's in our proxy tables
+ */
+		if (tip != dev->pa_addr)
+		{
+			struct arp_table *proxy_entry;
+
+/*
+ * 	To get in here, it is a request for someone else.  We need to
+ * 	check if that someone else is one of our proxies.  If it isn't,
+ * 	we can toss it.
+ *
+ *	Make "longest match" lookup, a la routing.
+ */
+
+			arp_fast_lock();
+
+			for (proxy_entry = arp_proxy_list; proxy_entry;
+			     proxy_entry = proxy_entry->next)
+			{
+				if (proxy_entry->dev == dev &&
+				    !((proxy_entry->ip^tip)&proxy_entry->mask))
+					break;
+			}
+
+			if (proxy_entry && (proxy_entry->mask || ((dev->pa_addr^tip)&dev->pa_mask)))
+			{
+				char ha[MAX_ADDR_LEN];
+				struct rtable * rt;
+
+				/* Unlock arp tables to make life for
+				 * ip_rt_route easy. Note, that we are obliged
+				 * to make local copy of hardware address.
+				 */
+
+				memcpy(ha, proxy_entry->ha, dev->addr_len);
+				arp_unlock();
+
+				rt = ip_rt_route(tip, 0);
+				if (rt  && rt->rt_dev != dev)
+					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
+				ip_rt_put(rt);
+
 			}
-			cli();
+			else
+				arp_unlock();
 		}
-		arp_bh_mask  &= ~ARP_BH_BACKLOG;
+		else
+			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+
+/*
+ *	Handle gratuitous arp.
+ */
+		arp_fast_lock();
+		arp_update(sip, sha, dev, NULL, 1);
 		arp_unlock();
+		kfree_skb(skb, FREE_READ);
+		return 0;
 	}
-	restore_flags(flags);
+
+	arp_fast_lock();
+	arp_update(sip, sha, dev, NULL, ip_chk_addr(tip) != IS_MYADDR);
+	arp_unlock();
+	kfree_skb(skb, FREE_READ);
+	return 0;
 }
 
+
+
 /*
- * Test if a hardware address is all zero
+ *	User level interface (ioctl, /proc)
  */
 
-static inline int empty(unsigned char * addr, int len)
-{
-	while (len > 0) {
-		if (*addr)
-			return 0;
-		len--;
-		addr++;
-	}
-	return 1;
-}
-
 /*
  *	Set (create) an ARP cache entry.
  */
 
 static int arp_req_set(struct arpreq *r, struct device * dev)
 {
-	struct arp_table *entry;
+	struct arp_table *entry, **entryp;
 	struct sockaddr_in *si;
-	struct rtable *rt;
-	struct device *dev1;
 	unsigned char *ha;
 	u32 ip;
+	u32 mask = DEF_ARP_NETMASK;
+	unsigned long flags;
+
+	/*
+	 *	Extract netmask (if supplied).
+	 */
+
+	if (r->arp_flags&ATF_NETMASK)
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		mask = si->sin_addr.s_addr;
+	}
 
 	/*
 	 *	Extract destination.
@@ -1885,112 +1968,89 @@
 	si = (struct sockaddr_in *) &r->arp_pa;
 	ip = si->sin_addr.s_addr;
 
-	/*
-	 *	Is it reachable ?
-	 */
 
-	if (ip_chk_addr(ip) == IS_MYADDR)
-		dev1 = dev_get("lo");
-	else {
-		rt = ip_rt_route(ip, 0);
-		if (!rt)
-			return -ENETUNREACH;
-		dev1 = rt->rt_dev;
-		ip_rt_put(rt);
+	if (r->arp_flags&ATF_PUBL)
+	{
+		if (!mask && ip)
+			return -EINVAL;
+		if (!dev)
+			dev = dev_getbytype(r->arp_ha.sa_family);
 	}
-
-	/* good guess about the device if it isn't a ATF_PUBL entry */
-	if (!dev) {
-		if (dev1->flags&(IFF_LOOPBACK|IFF_NOARP))
-			return -ENODEV;
-		dev = dev1;
+	else
+	{
+		if (ip_chk_addr(ip))
+			return -EINVAL;
+		if (!dev)
+		{
+			struct rtable * rt;
+			rt = ip_rt_route(ip, 0);
+			if (!rt)
+				return -ENETUNREACH;
+			dev = rt->rt_dev;
+			ip_rt_put(rt);
+		}
 	}
+	if (!dev || (dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
+		return -ENODEV;
 
-	/* this needs to be checked only for dev=dev1 but it doesn't hurt */	
 	if (r->arp_ha.sa_family != dev->type)	
 		return -EINVAL;
 		
-	if (((r->arp_flags & ATF_PUBL) && dev == dev1) ||
-	    (!(r->arp_flags & ATF_PUBL) && dev != dev1))
-		return -EINVAL;
-
+	arp_fast_lock();
 #if RT_CACHE_DEBUG >= 1
-	if (arp_lock)
+	if (ARP_LOCKED())
 		printk("arp_req_set: bug\n");
 #endif
-	arp_fast_lock();
-
-	/*
-	 *	Is there an existing entry for this address?
-	 */
 
-	/*
-	 *	Find the entry
-	 */
-	
-	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 (!(r->arp_flags & ATF_PUBL))
+		entryp = &arp_tables[HASH(ip)];
+	else
+		entryp = &arp_proxy_list;
 
-	if (entry)
+	while ((entry = *entryp) != NULL)
 	{
-		arp_destroy(entry);
-		entry = NULL;
+		if (entry->ip == ip && entry->mask == mask && entry->dev == dev)
+			break;
+		if ((entry->mask & mask) != mask)
+		{
+			entry = NULL;
+			break;
+		}
+		entryp = &entry->next;
 	}
 
 	/*
-	 *	Do we need to create a new entry
+	 *	Do we need to create a new entry?
 	 */
 	
 	if (entry == NULL)
 	{
-		entry = arp_add_entry();
+		entry = arp_alloc_entry();
 		if (entry == NULL)
 		{
 			arp_unlock();
 			return -ENOMEM;
 		}
 		entry->ip = ip;
-		entry->hh = NULL;
-		init_timer(&entry->timer);
-		entry->timer.function = arp_expire_request;
-		entry->timer.data = (unsigned long)entry;
+		entry->dev = dev;
+		entry->mask = mask;
+		entry->flags = r->arp_flags;
 
-		if (r->arp_flags & ATF_PUBL)
-		{
-			cli();
-			entry->next = arp_proxy_list;
-			arp_proxy_list = entry;
-			sti();
-		}
-		else
-		{
-			unsigned long hash = HASH(ip);
-			cli();
-			entry->next = arp_tables[hash];
-			arp_tables[hash] = entry;
-			sti();
-		}
-		skb_queue_head_init(&entry->skb);
+		entry->next = (*entryp)->next;
+		*entryp = entry;
 	}
-	/*
-	 *	We now have a pointer to an ARP entry.  Update it!
-	 */
+
 	ha = r->arp_ha.sa_data;
-	if ((r->arp_flags & ATF_COM) && empty(ha, dev->addr_len))
+	if (empty(ha, dev->addr_len))
 		ha = dev->dev_addr;
+
+	save_flags(flags);
+	cli();
 	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))
-	{
-		si = (struct sockaddr_in *) &r->arp_netmask;
-		entry->mask = si->sin_addr.s_addr;
-	}
-	else
-		entry->mask = DEF_ARP_NETMASK;
-	entry->dev = dev;
+	entry->flags |= ATF_COM;
+	restore_flags(flags);
+	arpd_update(entry);
 	arp_update_hhs(entry);
 	arp_unlock();
 	return 0;
@@ -2006,77 +2066,88 @@
 {
 	struct arp_table *entry;
 	struct sockaddr_in *si;
+	u32 mask = DEF_ARP_NETMASK;
+
+	if (r->arp_flags&ATF_NETMASK)
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		mask = si->sin_addr.s_addr;
+	}
 
 	si = (struct sockaddr_in *) &r->arp_pa;
 
+	arp_fast_lock();
 #if RT_CACHE_DEBUG >= 1
-	if (arp_lock)
-		printk("arp_req_set: bug\n");
+	if (ARP_LOCKED())
+		printk("arp_req_set: impossible\n");
 #endif
-	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 (!(r->arp_flags & ATF_PUBL))
+		entry = arp_tables[HASH(si->sin_addr.s_addr)];
+	else
+		entry = arp_proxy_list;
 
-	if (entry == NULL)
+	for ( ; entry ;entry = entry->next)
 	{
-		arp_unlock();
-		return -ENXIO;
+		if (entry->ip == si->sin_addr.s_addr 
+		    && (!dev || entry->dev == dev)
+		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+		{
+			memcpy(r->arp_ha.sa_data, entry->ha, entry->dev->addr_len);
+			r->arp_ha.sa_family = entry->dev->type;
+			r->arp_flags = entry->flags;
+			strncpy(r->arp_dev, entry->dev->name, sizeof(r->arp_dev));
+			arp_unlock();
+			return 0;
+		}
 	}
 
-	/*
-	 *	We found it; copy into structure.
-	 */
-	
-	memcpy(r->arp_ha.sa_data, &entry->ha, entry->dev->addr_len);
-	r->arp_ha.sa_family = entry->dev->type;
-	r->arp_flags = entry->flags;
-	strncpy(r->arp_dev, entry->dev->name, 16);
 	arp_unlock();
-	return 0;
+	return -ENXIO;
 }
 
 static int arp_req_delete(struct arpreq *r, struct device * dev)
 {
-	struct arp_table *entry;
-	struct sockaddr_in *si;
+	struct sockaddr_in	*si;
+	struct arp_table	*entry, **entryp;
+	int	retval = -ENXIO;
+	u32	mask = DEF_ARP_NETMASK;
+
+	if (r->arp_flags&ATF_NETMASK)
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		mask = si->sin_addr.s_addr;
+	}
 
 	si = (struct sockaddr_in *) &r->arp_pa;
+
+	arp_fast_lock();
 #if RT_CACHE_DEBUG >= 1
-	if (arp_lock)
-		printk("arp_req_delete: bug\n");
+	if (ARP_LOCKED())
+		printk("arp_req_delete: impossible\n");
 #endif
-	arp_fast_lock();
 
 	if (!(r->arp_flags & ATF_PUBL))
-	{
-		for (entry = arp_tables[HASH(si->sin_addr.s_addr)];
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == si->sin_addr.s_addr 
-			    && (!dev || entry->dev == dev))
-			{
-				arp_destroy(entry);
-				arp_unlock();
-				return 0;
-			}
-	}
+		entryp = &arp_tables[HASH(si->sin_addr.s_addr)];
 	else
+		entryp = &arp_proxy_list;
+
+	while ((entry = *entryp) != NULL)
 	{
-		for (entry = arp_proxy_list;
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == si->sin_addr.s_addr 
-			    && (!dev || entry->dev == dev)) 
-			{
-				arp_destroy(entry);
-				arp_unlock();
-				return 0;
-			}
+		if (entry->ip == si->sin_addr.s_addr 
+		    && (!dev || entry->dev == dev)
+		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+		{
+			*entryp = entry->next;
+			arp_free_entry(entry);
+			retval = 0;
+			continue;
+		}
+		entryp = &entry->next;
 	}
 
 	arp_unlock();
-	return -ENXIO;
+	return retval;
 }
 
 /*
@@ -2119,8 +2190,11 @@
 
 	if (r.arp_pa.sa_family != AF_INET)
 		return -EPFNOSUPPORT;
-	if (((struct sockaddr_in *)&r.arp_pa)->sin_addr.s_addr == 0)
-		return -EINVAL;
+
+	if (!(r.arp_flags & ATF_PUBL))
+		r.arp_flags &= ~ATF_NETMASK;
+	if (!(r.arp_flags & ATF_NETMASK))
+		((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=DEF_ARP_NETMASK;
 
 	if (r.arp_dev[0])
 	{
@@ -2132,14 +2206,6 @@
 		else if (r.arp_ha.sa_family != dev->type)
 			return -EINVAL;
 	}
-	else
-	{
-		if ((r.arp_flags & ATF_PUBL) &&
-		    ((cmd == SIOCSARP) || (cmd == OLD_SIOCSARP))) {
-			if ((dev = dev_getbytype(r.arp_ha.sa_family)) == NULL)
-				return -ENODEV;
-		}
-	}		 
 
 	switch(cmd)
 	{
@@ -2199,6 +2265,99 @@
 	return 0;
 }
 
+/*
+ *	Write the contents of the ARP cache to a PROCfs file.
+ */
+
+#define HBUFFERLEN 30
+
+int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len=0;
+	off_t pos=0;
+	int size;
+	struct arp_table *entry;
+	char hbuffer[HBUFFERLEN];
+	int i,j,k;
+	const char hexbuf[] =  "0123456789ABCDEF";
+
+	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
+
+	pos+=size;
+	len+=size;
+
+	arp_fast_lock();
+
+	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
+	{
+		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
+		{
+/*
+ *	Convert hardware address to XX:XX:XX:XX ... form.
+ */
+#ifdef CONFIG_AX25
+#ifdef CONFIG_NETROM
+			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
+			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+			else {
+#else
+			if(entry->dev->type==ARPHRD_AX25)
+			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+			else {
+#endif
+#endif
+
+			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
+			{
+				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
+				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
+				hbuffer[k++]=':';
+			}
+			hbuffer[--k]=0;
+	
+#ifdef CONFIG_AX25
+			}
+#endif
+			size = sprintf(buffer+len,
+				"%-17s0x%-10x0x%-10x%s",
+				in_ntoa(entry->ip),
+				(unsigned int)entry->dev->type,
+				entry->flags,
+				hbuffer);
+#if RT_CACHE_DEBUG < 2
+			size += sprintf(buffer+len+size,
+				 "     %-17s %s\n",
+				 entry->mask==DEF_ARP_NETMASK ?
+				 "*" : in_ntoa(entry->mask), entry->dev->name);
+#else
+			size += sprintf(buffer+len+size,
+				 "     %-17s %s\t%d\t%1d\n",
+				 entry->mask==DEF_ARP_NETMASK ?
+				 "*" : in_ntoa(entry->mask), entry->dev->name, 
+				 entry->hh ? entry->hh->hh_refcnt : -1,
+				 entry->hh ? entry->hh->hh_uptodate : 0);
+#endif
+	
+			len += size;
+			pos += size;
+		  
+			if (pos <= offset)
+				len=0;
+			if (pos >= offset+length)
+				goto done;
+		}
+	}
+done:
+	arp_unlock();
+  
+	*start = buffer+len-(pos-offset);	/* Start of wanted data */
+	len = pos-offset;			/* Start slop */
+	if (len>length)
+		len = length;			/* Ending slop */
+	return len;
+}
+
+
 
 /*
  *	Called once on startup.
@@ -2237,5 +2396,8 @@
 		arp_get_info
 	});
 #endif
-}
 
+#ifdef CONFIG_ARPD
+	netlink_attach(NETLINK_ARPD, arpd_callback);
+#endif
+}

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