patch-1.3.42 linux/net/ipv4/arp.c
Next file: linux/net/ipv4/devinet.c
Previous file: linux/net/ipv4/af_inet.c
Back to the patch index
Back to the overall index
- Lines: 2127
- Date:
Tue Nov 14 16:03:57 1995
- Orig file:
v1.3.41/linux/net/ipv4/arp.c
- Orig date:
Wed Nov 8 07:11:45 1995
diff -u --recursive --new-file v1.3.41/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
@@ -105,12 +105,11 @@
{
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 */
- unsigned char hlen; /* Length of hardware address */
- unsigned short htype; /* Type of hardware in use */
struct device *dev; /* Device the entry is tied to */
/*
@@ -120,6 +119,7 @@
struct timer_list timer; /* expire timer */
int retries; /* remaining retries */
struct sk_buff_head skb; /* list of queued packets */
+ struct hh_cache *hh;
};
@@ -130,17 +130,18 @@
/*
* 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.
*/
-/* RFC1122: OK. Throttles ARPing, as per 2.3.2.1. (MUST) */
-/* The recommended minimum timeout is 1 second per destination. */
-/* Is this a per-destination timeout? -- MS [YES AC]*/
-
-#define ARP_RES_TIME (250*(HZ/10))
+#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 unreachable.
+ * considered temporarily unreachable.
*/
#define ARP_MAX_TRIES 3
@@ -153,22 +154,30 @@
/*
* How often is the function 'arp_check_retries' called.
- * An entry is invalidated in the time between ARP_TIMEOUT and
+ * An unused entry is invalidated in the time between ARP_TIMEOUT and
* (ARP_TIMEOUT+ARP_CHECK_INTERVAL).
*/
-#define ARP_CHECK_INTERVAL (60 * HZ)
+#define ARP_CHECK_INTERVAL (60*HZ)
-enum proxy {
- PROXY_EXACT=0,
- PROXY_ANY,
- PROXY_NONE,
-};
+/*
+ * 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.
+ */
-/* Forward declarations. */
-static void arp_check_expire (unsigned long);
-static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type);
+#define ARP_CONFIRM_INTERVAL (300*HZ)
+#define ARP_CONFIRM_TIMEOUT ARP_RES_TIME
+
+static unsigned long arp_lock;
+static unsigned long arp_bh_mask;
+
+#define ARP_BH_BACKLOG 1
+
+static struct arp_table *arp_backlog;
+static void arp_run_bh(void);
+static void arp_check_expire (unsigned long);
static struct timer_list arp_timer =
{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -176,26 +185,17 @@
/*
* The default arp netmask is just 255.255.255.255 which means it's
* a single machine entry. Only proxy entries can have other netmasks
- *
-*/
+ */
#define DEF_ARP_NETMASK (~0)
-
/*
* 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
-
-/* The ugly +1 here is to cater for proxy entries. They are put in their
- own list for efficiency of lookup. If you don't want to find a proxy
- entry then don't look in the last entry, otherwise do
-*/
-
#define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1)
struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
@@ -203,8 +203,7 @@
NULL,
};
-unsigned long arp_cache_stamp;
-
+#define arp_proxy_list arp_tables[ARP_TABLE_SIZE]
/*
* The last bits in the IP address are used for the cache lookup.
@@ -212,82 +211,356 @@
*/
#define HASH(paddr) (htonl(paddr) & (ARP_TABLE_SIZE - 1))
-#define PROXY_HASH ARP_TABLE_SIZE
+
+/*
+ * Lock/unlock arp_table chains.
+ */
+
+static __inline__ void arp_fast_lock(void)
+{
+ ATOMIC_INCR(&arp_lock);
+}
+
+static __inline__ void arp_fast_unlock(void)
+{
+ ATOMIC_DECR(&arp_lock);
+}
+
+static __inline__ void arp_unlock(void)
+{
+ if (!ATOMIC_DECR_AND_CHECK(&arp_lock) && arp_bh_mask)
+ arp_run_bh();
+}
+
+/*
+ * Enqueue to FIFO list.
+ */
+
+static void arp_enqueue(struct arp_table **q, struct arp_table *entry)
+{
+ unsigned long flags;
+ struct arp_table * tail;
+
+ save_flags(flags);
+ cli();
+ tail = *q;
+ if (!tail)
+ entry->next = entry;
+ else
+ {
+ entry->next = tail->next;
+ tail->next = entry;
+ }
+ *q = entry;
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * Dequeue from FIFO list,
+ * caller should mask interrupts.
+ */
+
+static struct arp_table * arp_dequeue(struct arp_table **q)
+{
+ struct arp_table * entry;
+
+ if (*q)
+ {
+ entry = (*q)->next;
+ (*q)->next = entry->next;
+ if (entry->next == entry)
+ *q = NULL;
+ entry->next = NULL;
+ return entry;
+ }
+ return NULL;
+}
+
+/*
+ * Purge all linked skb's of the entry.
+ */
+
+static void arp_release_entry(struct arp_table *entry)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /* Release the list of `skb' pointers. */
+ while ((skb = skb_dequeue(&entry->skb)) != NULL)
+ {
+ skb_device_lock(skb);
+ restore_flags(flags);
+ dev_kfree_skb(skb, FREE_WRITE);
+ cli();
+ }
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * Release the entry and all resources linked to it: skb's, hh's, timer
+ * and certainly memory.
+ */
+
+static void arp_free_entry(struct arp_table *entry)
+{
+ unsigned long flags;
+ struct hh_cache *hh, *next;
+
+ del_timer(&entry->timer);
+
+ save_flags(flags);
+ cli();
+ arp_release_entry(entry);
+
+ for (hh = entry->hh; hh; hh = next)
+ {
+ next = hh->hh_next;
+ hh->hh_arp = NULL;
+ if (!--hh->hh_refcnt)
+ kfree_s(hh, sizeof(struct(struct hh_cache)));
+ }
+ restore_flags(flags);
+
+ kfree_s(entry, sizeof(struct arp_table));
+ return;
+}
+
+/*
+ * How many users has this entry?
+ */
+
+static __inline__ int arp_count_hhs(struct arp_table * entry)
+{
+ struct hh_cache *hh, **hhp;
+ 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;
+ }
+ count += hh->hh_refcnt-1;
+ hhp = &hh->hh_next;
+ }
+
+ return count;
+}
+
+/*
+ * Invalidate all hh's, so that higher level will not try to use it.
+ */
+
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
+{
+ struct hh_cache *hh;
+
+ for (hh=entry->hh; hh; hh=hh->hh_next)
+ hh->hh_uptodate = 0;
+}
+
+/*
+ * Signal to device layer, that hardware address may be changed.
+ */
+
+static __inline__ void arp_update_hhs(struct arp_table * entry)
+{
+ struct hh_cache *hh;
+
+ for (hh=entry->hh; hh; hh=hh->hh_next)
+ entry->dev->header_cache_update(hh, entry->dev, entry->ha);
+}
/*
* 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).
- * Note: Only fully resolved entries, which don't have any packets in
- * the queue, can be deleted, since ARP_TIMEOUT is much greater than
- * ARP_MAX_TRIES*ARP_RES_TIME.
+ * 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.
*/
-/* RFC1122: Looks good. Prevents stale ARP entries, as per 2.3.2.1. (MUST) */
-
static void arp_check_expire(unsigned long dummy)
{
int i;
unsigned long now = jiffies;
- unsigned long flags;
- save_flags(flags);
- cli();
- for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
+ del_timer(&arp_timer);
+
+ if (!arp_lock)
{
- struct arp_table *entry;
- struct arp_table **pentry = &arp_tables[i];
+ arp_fast_lock();
- while ((entry = *pentry) != NULL)
+ for (i = 0; i < ARP_TABLE_SIZE; i++)
{
- if ((now - entry->last_used) > ARP_TIMEOUT
- && !(entry->flags & ATF_PERM))
+ struct arp_table *entry;
+ struct arp_table **pentry;
+
+ pentry = &arp_tables[i];
+
+ while ((entry = *pentry) != NULL)
{
- *pentry = entry->next; /* remove from list */
- arp_cache_stamp++;
- del_timer(&entry->timer); /* Paranoia */
- kfree_s(entry, sizeof(struct arp_table));
- /* Don't have to remove packets in entry->skb. */
- /* See comments above. */
+ cli();
+ if (now - entry->last_used > ARP_TIMEOUT
+ && !(entry->flags & ATF_PERM)
+ && !arp_count_hhs(entry))
+ {
+ *pentry = entry->next;
+ sti();
+#if RT_CACHE_DEBUG >= 2
+ printk("arp_expire: %08x expired\n", entry->ip);
+#endif
+ arp_free_entry(entry);
+ }
+ else 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;
+ del_timer(&entry->timer);
+ entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
+ add_timer(&entry->timer);
+ arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
+ dev, dev->pa_addr, entry->ha,
+ dev->dev_addr, NULL);
+#if RT_CACHE_DEBUG >= 2
+ printk("arp_expire: %08x requires confirmation\n", entry->ip);
+#endif
+ }
+ else
+ pentry = &entry->next; /* go to next entry */
}
- else
- pentry = &entry->next; /* go to next entry */
}
+ arp_unlock();
}
- restore_flags(flags);
+
+ ip_rt_check_expire();
/*
* Set the timer again.
*/
- del_timer(&arp_timer);
arp_timer.expires = jiffies + ARP_CHECK_INTERVAL;
add_timer(&arp_timer);
}
-
/*
- * Release all linked skb's and the memory for this entry.
+ * This function is called, if an entry is not resolved in ARP_RES_TIME.
+ * When more than MAX_ARP_TRIES retries was done, release queued skb's,
+ * but not discard entry itself if it is in use.
*/
-static void arp_release_entry(struct arp_table *entry)
+static void arp_expire_request (unsigned long arg)
{
- struct sk_buff *skb;
+ struct arp_table *entry = (struct arp_table *) arg;
+ struct arp_table **pentry;
+ unsigned long hash;
unsigned long flags;
save_flags(flags);
cli();
- /* Release the list of `skb' pointers. */
- while ((skb = skb_dequeue(&entry->skb)) != NULL)
+
+ /*
+ * 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 (entry->flags & ATF_COM)
{
- skb_device_lock(skb);
restore_flags(flags);
- dev_kfree_skb(skb, FREE_WRITE);
- cli();
+ return;
}
+
+ if (arp_lock)
+ {
+#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);
+ return;
+ }
+
+ arp_fast_lock();
restore_flags(flags);
- del_timer(&entry->timer);
- kfree_s(entry, sizeof(struct arp_table));
- return;
+
+ if (entry->last_updated && --entry->retries > 0)
+ {
+ struct device *dev = entry->dev;
+
+#if RT_CACHE_DEBUG >= 2
+ 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_unlock();
+ return;
+ }
+
+ arp_release_entry(entry);
+
+ cli();
+ if (arp_count_hhs(entry))
+ {
+ 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;
+ restore_flags(flags);
+ entry->last_updated = 0;
+ del_timer(&entry->timer);
+ 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,
+ NULL, dev->dev_addr, NULL);
+ arp_unlock();
+ return;
+ }
+ restore_flags(flags);
+
+ hash = HASH(entry->ip);
+
+ pentry = &arp_tables[hash];
+
+ while (*pentry != NULL)
+ {
+ if (*pentry == entry)
+ {
+ cli();
+ *pentry = entry->next;
+ restore_flags(flags);
+#if RT_CACHE_DEBUG >= 2
+ printk("arp_expire_request: %08x is killed\n", entry->ip);
+#endif
+ arp_free_entry(entry);
+ arp_unlock();
+ return;
+ }
+ pentry = &(*pentry)->next;
+ }
+ printk("arp_expire_request: bug: ARP entry is lost!\n");
+ arp_unlock();
}
/*
@@ -298,16 +571,19 @@
{
struct device *dev=ptr;
int i;
- unsigned long flags;
- if(event!=NETDEV_DOWN)
+ if (event != NETDEV_DOWN)
return NOTIFY_DONE;
/*
* This is a bit OTT - maybe we need some arp semaphores instead.
*/
-
- save_flags(flags);
- cli();
+
+#if RT_CACHE_DEBUG >= 1
+ if (arp_lock)
+ printk("arp_device_event: bug\n");
+#endif
+ arp_fast_lock();
+
for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
{
struct arp_table *entry;
@@ -315,18 +591,15 @@
while ((entry = *pentry) != NULL)
{
- if(entry->dev==dev)
+ if (entry->dev == dev)
{
*pentry = entry->next; /* remove from list */
- del_timer(&entry->timer); /* Paranoia */
- kfree_s(entry, sizeof(struct arp_table));
+ arp_free_entry(entry);
}
else
pentry = &entry->next; /* go to next entry */
}
}
- arp_cache_stamp++;
- restore_flags(flags);
return NOTIFY_DONE;
}
@@ -338,7 +611,8 @@
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 *dest_hw, unsigned char *src_hw,
+ unsigned char *target_hw)
{
struct sk_buff *skb;
struct arphdr *arp;
@@ -348,7 +622,7 @@
* No arp on this interface.
*/
- if(dev->flags&IFF_NOARP)
+ if (dev->flags&IFF_NOARP)
return;
/*
@@ -380,7 +654,7 @@
#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);
+ arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
#endif
#else
arp->ar_pro = htons(ETH_P_IP);
@@ -395,8 +669,8 @@
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &src_ip,4);
arp_ptr+=4;
- if (dest_hw != NULL)
- memcpy(arp_ptr, dest_hw, dev->addr_len);
+ 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;
@@ -405,124 +679,35 @@
dev_queue_xmit(skb, dev, 0);
}
-
/*
- * This function is called, if an entry is not resolved in ARP_RES_TIME.
- * Either resend a request, or give it up and free the entry.
+ * This will try to retransmit everything on the queue.
*/
-static void arp_expire_request (unsigned long arg)
+static void arp_send_q(struct arp_table *entry)
{
- struct arp_table *entry = (struct arp_table *) arg;
- struct arp_table **pentry;
- unsigned long hash;
- unsigned long flags;
+ struct sk_buff *skb;
- save_flags(flags);
- cli();
+ unsigned long flags;
/*
- * 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...
+ * Empty the entire queue, building its data up ready to send
*/
- if (entry->flags & ATF_COM)
+ if(!(entry->flags&ATF_COM))
{
- restore_flags(flags);
+ 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 */
+ /* Whats happened is that someone has 'unresolved' the entry
+ as we got to use it - this 'can't happen' -- AC */
return;
}
- if (--entry->retries > 0)
- {
- u32 ip = entry->ip;
- struct device *dev = entry->dev;
-
- /* Set new timer. */
- del_timer(&entry->timer);
- entry->timer.expires = jiffies + ARP_RES_TIME;
- add_timer(&entry->timer);
- restore_flags(flags);
- arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr,
- NULL, dev->dev_addr);
- return;
- }
-
- /*
- * Arp request timed out. Delete entry and all waiting packets.
- * If we give each entry a pointer to itself, we don't have to
- * loop through everything again. Maybe hash is good enough, but
- * I will look at it later.
- */
-
- hash = HASH(entry->ip);
-
- /* proxy entries shouldn't really time out so this is really
- only here for completeness
- */
-
- /* RFC1122: They *can* be timed out, according to 2.3.2.1. */
- /* They recommend a minute. -- MS */
- /* The world doesn't work this way -- AC */
-
- if (entry->flags & ATF_PUBL)
- pentry = &arp_tables[PROXY_HASH];
- else
- pentry = &arp_tables[hash];
- while (*pentry != NULL)
- {
- if (*pentry == entry)
- {
- *pentry = entry->next; /* delete from linked list */
- del_timer(&entry->timer);
- restore_flags(flags);
- arp_release_entry(entry);
- arp_cache_stamp++;
- return;
- }
- pentry = &(*pentry)->next;
- }
- restore_flags(flags);
- printk("Possible ARP queue corruption.\n");
- /*
- * We should never arrive here.
- */
-
- /* Should we perhaps flush the ARP table (except the ones we're */
- /* publishing, if we can trust the queue that much) at this */
- /* point? -- MS */
-}
-
-
-/*
- * This will try to retransmit everything on the queue.
- */
-
-static void arp_send_q(struct arp_table *entry, unsigned char *hw_dest)
-{
- 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 */
- /* Whats happened is that someone has 'unresolved' the entry
- as we got to use it - this 'can't happen' -- AC */
- return;
- }
-
- save_flags(flags);
-
- cli();
- while((skb = skb_dequeue(&entry->skb)) != NULL)
+ save_flags(flags);
+
+ cli();
+ while((skb = skb_dequeue(&entry->skb)) != NULL)
{
IS_SKB(skb);
skb_device_lock(skb);
@@ -544,50 +729,29 @@
* Delete an ARP mapping entry in the cache.
*/
-void arp_destroy(u32 ip_addr, int force)
+static void arp_destroy(struct arp_table * entry)
{
- int checked_proxies = 0;
- struct arp_table *entry;
+ struct arp_table *entry1;
struct arp_table **pentry;
- unsigned long hash = HASH(ip_addr);
-ugly:
- cli();
- pentry = &arp_tables[hash];
- if (! *pentry) /* also check proxy entries */
- pentry = &arp_tables[PROXY_HASH];
+ if (entry->flags & ATF_PUBL)
+ pentry = &arp_proxy_list;
+ else
+ pentry = &arp_tables[HASH(entry->ip)];
- while ((entry = *pentry) != NULL)
+ while ((entry1 = *pentry) != NULL)
{
- if (entry->ip == ip_addr)
+ if (entry1 == entry)
{
- if ((entry->flags & ATF_PERM) && !force) {
- sti();
- return;
- }
- *pentry = entry->next;
+ *pentry = entry1->next;
del_timer(&entry->timer);
- sti();
- arp_release_entry(entry);
- /* this would have to be cleaned up */
- goto ugly;
- /* perhaps like this ?
- cli();
- entry = *pentry;
- */
+ arp_free_entry(entry);
+ return;
}
- pentry = &entry->next;
- if (!checked_proxies && ! *pentry)
- { /* ugly. we have to make sure we check proxy
- entries as well */
- checked_proxies = 1;
- pentry = &arp_tables[PROXY_HASH];
- }
+ pentry = &entry1->next;
}
- sti();
}
-
/*
* 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
@@ -604,7 +768,6 @@
unsigned char *arp_ptr= (unsigned char *)(arp+1);
struct arp_table *entry;
struct arp_table *proxy_entry;
- int hlen,htype;
unsigned long hash;
unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */
unsigned char *sha,*tha;
@@ -636,7 +799,7 @@
*/
/* Again, should this be an error/printk? -- MS */
- switch(dev->type)
+ switch (dev->type)
{
#ifdef CONFIG_AX25
case ARPHRD_AX25:
@@ -683,16 +846,13 @@
* Extract fields
*/
- hlen = dev->addr_len;
- htype = dev->type;
-
sha=arp_ptr;
- arp_ptr+=hlen;
- memcpy(&sip,arp_ptr,4);
- arp_ptr+=4;
+ arp_ptr += dev->addr_len;
+ memcpy(&sip, arp_ptr, 4);
+ arp_ptr += 4;
tha=arp_ptr;
- arp_ptr+=hlen;
- memcpy(&tip,arp_ptr,4);
+ arp_ptr += dev->addr_len;
+ memcpy(&tip, arp_ptr, 4);
/*
* Check for bad requests for 127.x.x.x and requests for multicast
@@ -721,47 +881,48 @@
* cache.
*/
- if(arp->ar_op == htons(ARPOP_REQUEST))
+ 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)
+ 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.
*/
- cli();
- for(proxy_entry=arp_tables[PROXY_HASH];
- proxy_entry;
- proxy_entry = proxy_entry->next)
+ 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->htype == htype &&
- !((proxy_entry->ip^tip)&proxy_entry->mask))
- break;
+ /* 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, hlen);
- sti();
- arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha);
+ 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
{
- sti();
+ arp_unlock();
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -771,7 +932,7 @@
/*
* 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);
+ arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
}
}
/*
@@ -791,21 +952,22 @@
* there.
*/
+ arp_fast_lock();
+
hash = HASH(sip);
- cli();
- for(entry=arp_tables[hash];entry;entry=entry->next)
- if(entry->ip==sip && entry->htype==htype)
+
+ for (entry=arp_tables[hash]; entry; entry=entry->next)
+ if (entry->ip == sip && entry->dev == dev)
break;
- if(entry)
+ if (entry)
{
/*
* Entry found; update it only if it is not a permanent entry.
*/
if (!(entry->flags & ATF_PERM)) {
- memcpy(entry->ha, sha, hlen);
- entry->hlen = hlen;
- entry->last_used = jiffies;
+ memcpy(entry->ha, sha, dev->addr_len);
+ entry->last_updated = jiffies;
}
if (!(entry->flags & ATF_COM))
{
@@ -815,17 +977,13 @@
*/
del_timer(&entry->timer);
entry->flags |= ATF_COM;
- sti();
+ 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, sha);
- }
- else
- {
- sti();
+ arp_send_q(entry);
}
}
else
@@ -836,94 +994,121 @@
entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
if(entry == NULL)
{
- sti();
+ arp_unlock();
printk("ARP: no memory for new arp entry\n");
-
kfree_skb(skb, FREE_READ);
return 0;
}
entry->mask = DEF_ARP_NETMASK;
entry->ip = sip;
- entry->hlen = hlen;
- entry->htype = htype;
entry->flags = ATF_COM;
+ entry->hh = NULL;
init_timer(&entry->timer);
- memcpy(entry->ha, sha, hlen);
- entry->last_used = jiffies;
+ 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;
entry->dev = skb->dev;
skb_queue_head_init(&entry->skb);
- entry->next = arp_tables[hash];
- arp_tables[hash] = entry;
- sti();
+ if (arp_lock == 1)
+ {
+ entry->next = arp_tables[hash];
+ arp_tables[hash] = entry;
+ }
+ else
+ {
+#if RT_CACHE_DEBUG >= 1
+ 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.
*/
kfree_skb(skb, FREE_READ);
+ arp_unlock();
return 0;
}
-
/*
- * Find an arp mapping in the cache. If not found, return false.
+ * 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.
*/
-int arp_query(unsigned char *haddr, u32 paddr, unsigned short type)
+static struct arp_table *arp_lookup(u32 paddr, unsigned short flags, struct device * dev)
{
struct arp_table *entry;
- unsigned long hash = HASH(paddr);
- /*
- * Find an entry
- */
- cli();
-
- for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
- if (entry->ip == paddr && entry->htype == type)
- break;
+ if (!(flags & ATF_PUBL))
+ {
+ for (entry = arp_tables[HASH(paddr)];
+ entry != NULL; entry = entry->next)
+ if (entry->ip == paddr && entry->dev == dev)
+ break;
+ return entry;
+ }
- if (entry != NULL) {
- /*
- * Update the record
- */
-
- entry->last_used = jiffies;
- memcpy(haddr, entry->ha, entry->hlen);
- sti();
- return 1;
+ if (!(flags & ATF_NETMASK))
+ {
+ for (entry = arp_proxy_list;
+ entry != NULL; entry = entry->next)
+ if (entry->ip == paddr && entry->dev == dev)
+ break;
+ return entry;
}
- sti();
- return 0;
+ for (entry=arp_proxy_list; entry != NULL; entry = entry->next)
+ if (!((entry->ip^paddr)&entry->mask) && entry->dev == dev)
+ break;
+ return entry;
}
/*
- * Find an arp mapping in the cache. If not found, post a request.
+ * Find an arp mapping in the cache. If not found, return false.
*/
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
- u32 saddr, struct sk_buff *skb)
+int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
{
struct arp_table *entry;
- unsigned long hash;
-#ifdef CONFIG_IP_MULTICAST
- u32 taddr;
-#endif
- switch (ip_chk_addr(paddr))
+ arp_fast_lock();
+
+ entry = arp_lookup(paddr, 0, dev);
+
+ if (entry != NULL)
+ {
+ entry->last_used = jiffies;
+ if (entry->flags & ATF_COM)
+ {
+ memcpy(haddr, entry->ha, dev->addr_len);
+ arp_unlock();
+ return 1;
+ }
+ }
+ arp_unlock();
+ return 0;
+}
+
+
+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");
memcpy(haddr, dev->dev_addr, dev->addr_len);
- skb->arp = 1;
- return 0;
+ return 1;
#ifdef CONFIG_IP_MULTICAST
case IS_MULTICAST:
if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
{
- /* What exactly does this do? -- MS */
+ u32 taddr;
haddr[0]=0x01;
haddr[1]=0x00;
haddr[2]=0x5e;
@@ -933,7 +1118,7 @@
haddr[4]=taddr&0xff;
taddr=taddr>>8;
haddr[3]=taddr&0x7f;
- return 0;
+ return 1;
}
/*
* If a device does not support multicast broadcast the stuff (eg AX.25 for now)
@@ -942,17 +1127,35 @@
case IS_BROADCAST:
memcpy(haddr, dev->broadcast, dev->addr_len);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Find an arp mapping in the cache. If not found, post a request.
+ */
+
+int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
+ u32 saddr, 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;
+ return 0;
}
hash = HASH(paddr);
- cli();
+ arp_fast_lock();
/*
* Find an entry
*/
- entry = arp_lookup(paddr, PROXY_NONE, dev->type);
+ entry = arp_lookup(paddr, 0, dev);
if (entry != NULL) /* It exists */
{
@@ -965,10 +1168,30 @@
if (skb != NULL)
{
- skb_queue_tail(&entry->skb, skb);
- skb_device_unlock(skb);
+ 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
+ {
+ /*
+ * FIXME: ICMP HOST UNREACHABLE should be
+ * sent in this situation. --ANK
+ */
+ if (skb->sk)
+ {
+ skb->sk->err = EHOSTDOWN;
+ skb->sk->error_report(skb->sk);
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
}
- sti();
+ arp_unlock();
return 1;
}
@@ -980,7 +1203,7 @@
memcpy(haddr, entry->ha, dev->addr_len);
if (skb)
skb->arp = 1;
- sti();
+ arp_unlock();
return 0;
}
@@ -992,42 +1215,49 @@
GFP_ATOMIC);
if (entry != NULL)
{
- entry->next = arp_tables[hash];
- entry->last_used = jiffies;
+ 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->hlen = dev->addr_len;
- entry->htype = dev->type;
entry->dev = dev;
+ entry->hh = NULL;
init_timer(&entry->timer);
entry->timer.function = arp_expire_request;
entry->timer.data = (unsigned long)entry;
entry->timer.expires = jiffies + ARP_RES_TIME;
- arp_tables[hash] = entry;
- add_timer(&entry->timer);
- entry->retries = ARP_MAX_TRIES;
skb_queue_head_init(&entry->skb);
if (skb != NULL)
{
skb_queue_tail(&entry->skb, skb);
skb_device_unlock(skb);
}
+ if (arp_lock == 1)
+ {
+ entry->next = arp_tables[hash];
+ arp_tables[hash] = entry;
+ add_timer(&entry->timer);
+ entry->retries = ARP_MAX_TRIES;
+ }
+ else
+ {
+#if RT_CACHE_DEBUG >= 1
+ printk("arp_find: %08x backlogged\n", entry->ip);
+#endif
+ arp_enqueue(&arp_backlog, entry);
+ arp_bh_mask |= ARP_BH_BACKLOG;
+ }
}
- else
- {
- if (skb != NULL && skb->free)
- kfree_skb(skb, FREE_WRITE);
- }
- sti();
+ 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);
+ dev->dev_addr, NULL);
return 1;
}
@@ -1042,7 +1272,6 @@
int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
int len=0;
- off_t begin=0;
off_t pos=0;
int size;
struct arp_table *entry;
@@ -1050,12 +1279,13 @@
int i,j,k;
const char hexbuf[] = "0123456789ABCDEF";
- size = sprintf(buffer,"IP address HW type Flags HW address Mask\n");
+ size = sprintf(buffer,"IP address HW type Flags HW address Mask Device\n");
pos+=size;
len+=size;
-
- cli();
+
+ arp_fast_lock();
+
for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
{
for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
@@ -1065,17 +1295,17 @@
*/
#ifdef CONFIG_AX25
#ifdef CONFIG_NETROM
- if (entry->htype == ARPHRD_AX25 || entry->htype == ARPHRD_NETROM)
+ if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
else {
#else
- if(entry->htype==ARPHRD_AX25)
+ 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->hlen;j++)
+ 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 ];
@@ -1089,200 +1319,330 @@
size = sprintf(buffer+len,
"%-17s0x%-10x0x%-10x%s",
in_ntoa(entry->ip),
- (unsigned int)entry->htype,
+ (unsigned int)entry->dev->type,
entry->flags,
hbuffer);
+#if RT_CACHE_DEBUG < 2
size += sprintf(buffer+len+size,
- " %-17s\n",
- entry->mask==DEF_ARP_NETMASK?
- "*":in_ntoa(entry->mask));
+ " %-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=begin+len;
+ len += size;
+ pos += size;
- if(pos<offset)
- {
+ if (pos <= offset)
len=0;
- begin=pos;
- }
- if(pos>offset+length)
+ if (pos >= offset+length)
break;
}
}
- sti();
+ arp_unlock();
- *start=buffer+(offset-begin); /* Start of wanted data */
- len-=(offset-begin); /* Start slop */
- if(len>length)
- len=length; /* Ending slop */
+ *start = buffer+len-(pos-offset); /* Start of wanted data */
+ len = pos-offset; /* Start slop */
+ if (len>length)
+ len = length; /* Ending slop */
return len;
}
-/*
- * This will find an entry in the ARP table by looking at the IP address.
- * If proxy is PROXY_EXACT then only exact IP matches will be allowed
- * for proxy entries, otherwise the netmask will be used
- */
-static struct arp_table *arp_lookup(u32 paddr, enum proxy proxy, unsigned short type)
+int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
{
struct arp_table *entry;
- unsigned long hash = HASH(paddr);
-
- for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
- if (entry->ip == paddr && entry->htype == type)
- break;
+ struct hh_cache *hh = *hhp;
+ int addr_hint;
+ unsigned long flags;
- /* it's possibly a proxy entry (with a netmask) */
- if (!entry && proxy != PROXY_NONE)
- for (entry=arp_tables[PROXY_HASH]; entry != NULL; entry = entry->next)
- if ((proxy==PROXY_EXACT) ? (entry->ip==paddr)
- : !((entry->ip^paddr)&entry->mask))
- break;
+ if (hh)
+ return 1;
- return entry;
-}
+ if ((addr_hint = ip_chk_addr(paddr)) != 0)
+ {
+ unsigned char haddr[MAX_ADDR_LEN];
+ if (hh)
+ return 1;
+ hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+ 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;
+ }
+ save_flags(flags);
-int arp_find_cache(unsigned char *dp, u32 daddr, struct device *dev)
-{
- /*
- * We need the broadcast/multicast awareness here and the find routine split up.
- */
- struct arp_table *entry;
-#ifdef CONFIG_IP_MULTICAST
- u32 taddr;
-#endif
+ arp_fast_lock();
+
+ entry = arp_lookup(paddr, 0, dev);
- switch (ip_chk_addr(daddr))
+ if (entry)
{
- case IS_MYADDR:
- printk("ARP: arp called for own IP address\n");
- memcpy(dp, dev->dev_addr, dev->addr_len);
+ 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_unlock();
return 1;
-#ifdef CONFIG_IP_MULTICAST
- case IS_MULTICAST:
- if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
+ }
+ restore_flags(flags);
+ }
+
+ hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+ 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;
+ entry->hh = hh;
+ hh->hh_refcnt++;
+ restore_flags(flags);
+ entry->last_used = jiffies;
+ arp_unlock();
+ return 0;
+ }
+
+
+ /*
+ * Create a new unresolved entry.
+ */
+
+ entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
+ GFP_ATOMIC);
+ if (entry == NULL)
+ {
+ kfree_s(hh, sizeof(struct hh_cache));
+ arp_unlock();
+ 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;
+ 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)
+ {
+ 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;
+ }
+ *hhp = hh;
+ arp_unlock();
+ return 0;
+}
+
+static void arp_run_bh()
+{
+ unsigned long flags;
+ struct arp_table *entry, *entry1;
+ struct hh_cache *hh;
+ __u32 sip;
+
+ save_flags(flags);
+ cli();
+ if (!arp_lock)
+ {
+ arp_fast_lock();
+
+ while ((entry = arp_dequeue(&arp_backlog)) != NULL)
+ {
+ unsigned long hash;
+ sti();
+ 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)
+ break;
+
+ if (!entry1)
{
- dp[0]=0x01;
- dp[1]=0x00;
- dp[2]=0x5e;
- taddr=ntohl(daddr);
- dp[5]=taddr&0xff;
- taddr=taddr>>8;
- dp[4]=taddr&0xff;
- taddr=taddr>>8;
- dp[3]=taddr&0x7f;
- return 1;
- }
- /*
- * If a device does not support multicast broadcast the stuff (eg AX.25 for now)
- */
+ 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);
+ 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);
+#if RT_CACHE_DEBUG >= 1
+ printk("arp_run_bh: %08x reinstalled\n", sip);
#endif
-
- case IS_BROADCAST:
- memcpy(dp, dev->broadcast, dev->addr_len);
- return 1;
-
- default:
- entry=arp_lookup(daddr, PROXY_NONE, dev->type);
- if(entry)
+ }
+ else
{
- memcpy(dp,entry->ha, ETH_ALEN);
- return 1;
+ struct sk_buff * skb;
+ struct hh_cache * next;
+
+ /* Discard entry, but preserve its hh's and
+ * skb's.
+ */
+ cli();
+ for (hh=entry->hh; hh; hh=next)
+ {
+ next = hh->hh_next;
+ hh->hh_next = entry1->hh;
+ entry1->hh = hh;
+ hh->hh_arp = (void*)entry1;
+ }
+ entry->hh = NULL;
+
+ /* Prune skb list from entry
+ * and graft it to entry1.
+ */
+ while ((skb = skb_dequeue(&entry->skb)) != NULL)
+ {
+ skb_device_lock(skb);
+ sti();
+ skb_queue_tail(&entry1->skb, skb);
+ skb_device_unlock(skb);
+ cli();
+ }
+ sti();
+
+#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);
+ }
}
+ cli();
+ }
+ arp_bh_mask &= ~ARP_BH_BACKLOG;
+ arp_unlock();
}
- return 0;
+ restore_flags(flags);
}
+
/*
* Set (create) an ARP cache entry.
*/
-static int arp_req_set(struct arpreq *req)
+static int arp_req_set(struct arpreq *r, struct device * dev)
{
- struct arpreq r;
struct arp_table *entry;
struct sockaddr_in *si;
- int htype, hlen;
struct rtable *rt;
+ struct device * dev1;
u32 ip;
- memcpy_fromfs(&r, req, sizeof(r));
-
- /* We only understand about IP addresses... */
- if (r.arp_pa.sa_family != AF_INET)
- return -EPFNOSUPPORT;
-
/*
* Find out about the hardware type.
* We have to be compatible with BSD UNIX, so we have to
* assume that a "not set" value (i.e. 0) means Ethernet.
+ *
+ * ANK: Hey, who wrote it? Do you really mean that BSD considers
+ * ARPHRD_NETROM as ARPHRD_ETHER, or somthing another?
*/
- switch (r.arp_ha.sa_family) {
- case ARPHRD_ETHER:
- htype = ARPHRD_ETHER;
- hlen = ETH_ALEN;
- break;
-
- case ARPHRD_ARCNET:
- htype = ARPHRD_ARCNET;
- hlen = 1; /* length of arcnet addresses */
- break;
-
-#ifdef CONFIG_AX25
- case ARPHRD_AX25:
- htype = ARPHRD_AX25;
- hlen = AX25_ADDR_LEN;
- break;
-#endif
-#ifdef CONFIG_NETROM
- case ARPHRD_NETROM:
- htype = ARPHRD_NETROM;
- hlen = AX25_ADDR_LEN;
- break;
-#endif
- case ARPHRD_IEEE802:
- htype = ARPHRD_IEEE802;
- hlen = TR_ALEN;
- break;
- default:
- return -EPFNOSUPPORT;
- }
-
- si = (struct sockaddr_in *) &r.arp_pa;
+ si = (struct sockaddr_in *) &r->arp_pa;
ip = si->sin_addr.s_addr;
- if (ip == 0)
- {
- printk("ARP: SETARP: requested PA is 0.0.0.0 !\n");
- return -EINVAL;
- }
/*
- * Is it reachable directly ?
+ * Is it reachable ?
*/
- rt = ip_rt_route(ip, NULL, NULL);
- if (rt == NULL)
+ rt = ip_rt_route(ip, 0);
+ if (!rt)
return -ENETUNREACH;
+ dev1 = rt->rt_dev;
+ ip_rt_put(rt);
+
+ if (((r->arp_flags & ATF_PUBL) && dev == dev1) ||
+ (!(r->arp_flags & ATF_PUBL) && dev != dev1))
+ return -EINVAL;
+
+#if RT_CACHE_DEBUG >= 1
+ if (arp_lock)
+ printk("arp_req_set: bug\n");
+#endif
+ arp_fast_lock();
/*
* Is there an existing entry for this address?
*/
-
- cli();
/*
* Find the entry
*/
- entry = arp_lookup(ip, PROXY_EXACT, htype);
- if (entry && (entry->flags & ATF_PUBL) != (r.arp_flags & ATF_PUBL))
+
+ entry = arp_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev);
+
+ if (entry)
{
- sti();
- arp_destroy(ip,1);
- cli();
+ arp_destroy(entry);
entry = NULL;
}
@@ -1292,77 +1652,83 @@
if (entry == NULL)
{
- unsigned long hash = HASH(ip);
- if (r.arp_flags & ATF_PUBL)
- hash = PROXY_HASH;
-
entry = (struct arp_table *) kmalloc(sizeof(struct arp_table),
GFP_ATOMIC);
if (entry == NULL)
{
- sti();
+ arp_unlock();
return -ENOMEM;
}
entry->ip = ip;
- entry->hlen = hlen;
- entry->htype = htype;
+ entry->hh = NULL;
init_timer(&entry->timer);
- entry->next = arp_tables[hash];
- arp_tables[hash] = entry;
+ entry->timer.function = arp_expire_request;
+ entry->timer.data = (unsigned long)entry;
+
+ 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);
}
/*
* We now have a pointer to an ARP entry. Update it!
*/
- memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
- entry->last_used = jiffies;
- entry->flags = r.arp_flags | ATF_COM;
+ if ((r->arp_flags & ATF_COM) && !r->arp_ha.sa_data[0])
+ memcpy(&entry->ha, dev->dev_addr, dev->addr_len);
+ else
+ memcpy(&entry->ha, &r->arp_ha.sa_data, dev->addr_len);
+ entry->last_updated = entry->last_used = jiffies;
+ 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;
- }
+ {
+ si = (struct sockaddr_in *) &r->arp_netmask;
+ entry->mask = si->sin_addr.s_addr;
+ }
else
- entry->mask = DEF_ARP_NETMASK;
- entry->dev = rt->rt_dev;
- arp_cache_stamp++;
- sti();
-
+ entry->mask = DEF_ARP_NETMASK;
+ entry->dev = dev;
+ arp_update_hhs(entry);
+ arp_unlock();
return 0;
}
+
/*
* Get an ARP cache entry.
*/
-static int arp_req_get(struct arpreq *req)
+static int arp_req_get(struct arpreq *r, struct device *dev)
{
- struct arpreq r;
struct arp_table *entry;
struct sockaddr_in *si;
- /*
- * We only understand about IP addresses...
- */
-
- memcpy_fromfs(&r, req, sizeof(r));
+ si = (struct sockaddr_in *) &r->arp_pa;
- if (r.arp_pa.sa_family != AF_INET)
- return -EPFNOSUPPORT;
+#if RT_CACHE_DEBUG >= 1
+ if (arp_lock)
+ printk("arp_req_set: bug\n");
+#endif
+ arp_fast_lock();
- /*
- * Is there an existing entry for this address?
- */
-
- si = (struct sockaddr_in *) &r.arp_pa;
- cli();
- entry = arp_lookup(si->sin_addr.s_addr, PROXY_ANY, r.arp_ha.sa_family);
+ entry = arp_lookup(si->sin_addr.s_addr, r->arp_flags|ATF_NETMASK, dev);
if (entry == NULL)
{
- sti();
+ arp_unlock();
return -ENXIO;
}
@@ -1370,19 +1736,54 @@
* We found it; copy into structure.
*/
- memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
- r.arp_ha.sa_family = entry->htype;
- r.arp_flags = entry->flags;
- sti();
-
- /*
- * Copy the information back
- */
-
- memcpy_tofs(req, &r, sizeof(r));
+ 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;
}
+static int arp_req_delete(struct arpreq *r, struct device * dev)
+{
+ struct arp_table *entry;
+ struct sockaddr_in *si;
+
+ si = (struct sockaddr_in *) &r->arp_pa;
+#if RT_CACHE_DEBUG >= 1
+ if (arp_lock)
+ printk("arp_req_delete: bug\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
+ && entry->dev == dev)
+ {
+ arp_destroy(entry);
+ arp_unlock();
+ return 0;
+ }
+ }
+ else
+ {
+ for (entry = arp_proxy_list;
+ entry != NULL; entry = entry->next)
+ if (entry->ip == si->sin_addr.s_addr
+ && entry->dev == dev)
+ {
+ arp_destroy(entry);
+ arp_unlock();
+ return 0;
+ }
+ }
+
+ arp_unlock();
+ return -ENXIO;
+}
/*
* Handle an ARP layer I/O control request.
@@ -1390,38 +1791,115 @@
int arp_ioctl(unsigned int cmd, void *arg)
{
- struct arpreq r;
- struct sockaddr_in *si;
int err;
+ struct arpreq r;
+
+ struct device * dev = NULL;
switch(cmd)
{
case SIOCDARP:
+ case SIOCSARP:
if (!suser())
return -EPERM;
- err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
- if(err)
- return err;
- memcpy_fromfs(&r, arg, sizeof(r));
- if (r.arp_pa.sa_family != AF_INET)
- return -EPFNOSUPPORT;
- si = (struct sockaddr_in *) &r.arp_pa;
- arp_destroy(si->sin_addr.s_addr, 1);
- return 0;
case SIOCGARP:
- err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
- if(err)
+ err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+ if (err)
return err;
- return arp_req_get((struct arpreq *)arg);
- case SIOCSARP:
+ memcpy_fromfs(&r, arg, sizeof(struct arpreq));
+ break;
+ case OLD_SIOCDARP:
+ case OLD_SIOCSARP:
if (!suser())
return -EPERM;
- err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
- if(err)
+ case OLD_SIOCGARP:
+ err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq_old));
+ if (err)
return err;
- return arp_req_set((struct arpreq *)arg);
+ memcpy_fromfs(&r, arg, sizeof(struct arpreq_old));
+ memset(&r.arp_dev, 0, sizeof(r.arp_dev));
+ break;
default:
return -EINVAL;
+ }
+
+ 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_dev[0])
+ {
+ if ((dev = dev_get(r.arp_dev)) == NULL)
+ return -ENODEV;
+
+ if (!r.arp_ha.sa_family)
+ r.arp_ha.sa_family = dev->type;
+ else if (r.arp_ha.sa_family != dev->type)
+ return -EINVAL;
+ }
+ else
+ {
+ /*
+ * Device was not specified. Take the first suitable one.
+ */
+ if ((dev = dev_getbytype(r.arp_ha.sa_family)) == NULL)
+ return -ENODEV;
+ }
+
+ switch(cmd)
+ {
+ case SIOCDARP:
+ return arp_req_delete(&r, dev);
+ case SIOCSARP:
+ return arp_req_set(&r, dev);
+ case OLD_SIOCDARP:
+ /* old SIOCDARP destoyes both
+ * normal and proxy mappings
+ */
+ r.arp_flags &= ~ATF_PUBL;
+ err = arp_req_delete(&r, dev);
+ r.arp_flags |= ATF_PUBL;
+ if (!err)
+ arp_req_delete(&r, dev);
+ else
+ err = arp_req_delete(&r, dev);
+ return err;
+ case OLD_SIOCSARP:
+ err = arp_req_set(&r, dev);
+ /* old SIOCSARP works so funny,
+ * that its behaviour can be emulated
+ * only approximately 8).
+ * It should work. --ANK
+ */
+ if (r.arp_flags & ATF_PUBL)
+ {
+ r.arp_flags &= ~ATF_PUBL;
+ arp_req_delete(&r, dev);
+ }
+ return err;
+ case SIOCGARP:
+ err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
+ if (err)
+ return err;
+ err = arp_req_get(&r, dev);
+ if (!err)
+ memcpy_tofs(arg, &r, sizeof(r));
+ return err;
+ case OLD_SIOCGARP:
+ err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq_old));
+ if (err)
+ return err;
+ r.arp_flags &= ~ATF_PUBL;
+ err = arp_req_get(&r, dev);
+ if (err < 0)
+ {
+ r.arp_flags |= ATF_PUBL;
+ err = arp_req_get(&r, dev);
+ }
+ if (!err)
+ memcpy_tofs(arg, &r, sizeof(struct arpreq_old));
+ return err;
}
/*NOTREACHED*/
return 0;
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