diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/ipv4/arp.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/ipv4/arp.c')
-rw-r--r-- | net/ipv4/arp.c | 1647 |
1 files changed, 612 insertions, 1035 deletions
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 090808b68..8ef0be2af 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -7,13 +7,6 @@ * high-level addresses) into a low-level hardware address (like an Ethernet * address). * - * FIXME: - * Experiment with better retransmit timers - * Clean up the timer deletions - * If you create a proxy entry, set your interface address to the address - * and then delete it, proxies may get out of sync with reality - - * check this. - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -60,6 +53,11 @@ * Mike Shaver : /proc/sys/net/ipv4/arp_* support * Stuart Cheshire : Metricom and grat arp fixes * *** FOR 2.1 clean this up *** + * Lawrence V. Stefani: (08/12/96) Added FDDI support. + * Alan Cox : Took the AP1000 nasty FDDI hack and + * folded into the mainstream FDDI code. + * Ack spit, Linus how did you allow that + * one in... */ /* RFC1122 Status: @@ -86,6 +84,7 @@ #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/fddidevice.h> #include <linux/if_arp.h> #include <linux/trdevice.h> #include <linux/skbuff.h> @@ -105,9 +104,7 @@ #include <net/netrom.h> #endif #endif -#ifdef CONFIG_NET_ALIAS #include <linux/net_alias.h> -#endif #ifdef CONFIG_ARPD #include <net/netlink.h> #endif @@ -115,8 +112,6 @@ #include <asm/system.h> #include <asm/uaccess.h> -#include <stdarg.h> - /* * Configurable Parameters */ @@ -142,9 +137,6 @@ int sysctl_arp_check_interval = ARP_CHECK_INTERVAL; /* * Soft limit on ARP cache size. - * Note that this number should be greater than - * number of simultaneously opened sockets, or else - * hardware header cache will not be efficient. */ #if RT_CACHE_DEBUG >= 2 @@ -158,6 +150,16 @@ int sysctl_arp_check_interval = ARP_CHECK_INTERVAL; #endif /* + * Limit on unresolved ARP cache entries. + */ +#define ARP_MAX_UNRES (ARP_MAXSIZE/2) + +/* + * Maximal number of skb's queued for resolution. + */ +#define ARP_MAX_UNRES_PACKETS 3 + +/* * 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) @@ -228,58 +230,31 @@ int sysctl_arp_max_pings = ARP_MAX_PINGS; int sysctl_arp_dead_res_time = ARP_DEAD_RES_TIME; +static void arp_neigh_destroy(struct neighbour *neigh); + /* - * This structure defines the ARP mapping cache. + * Interface to generic neighbour 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 */ +struct neigh_ops arp_neigh_ops = { + AF_INET, + NULL, + arp_find, + arp_neigh_destroy }; -static atomic_t arp_size = 0; +static atomic_t arp_size = ATOMIC_INIT(0); +static atomic_t arp_unres_size = ATOMIC_INIT(0); #ifdef CONFIG_ARPD static int arpd_not_running; static int arpd_stamp; #endif -static unsigned int arp_bh_mask; - -#define ARP_BH_BACKLOG 1 - -/* - * Backlog for ARP updates. - */ -static struct arp_table *arp_backlog; - -/* - * Backlog for incomplete entries. - */ -static struct arp_table *arp_req_backlog; - - -static void arp_run_bh(void); static void arp_check_expire (unsigned long); static int arp_update (u32 sip, char *sha, struct device * dev, - unsigned long updated, struct arp_table *ientry, int grat); + unsigned long updated, int grat); static struct timer_list arp_timer = { NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire }; @@ -313,93 +288,41 @@ struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] = #define HASH(paddr) (htonl(paddr) & (ARP_TABLE_SIZE - 1)) /* - * 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. + * Hardware header cache. * - * 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 atomic_t arp_lock; - -#define ARP_LOCKED() (arp_lock != 1) - -static __inline__ void arp_fast_lock(void) -{ - atomic_inc(&arp_lock); -} - -static __inline__ void arp_unlock(void) -{ - if (atomic_dec_and_test(&arp_lock) && arp_bh_mask) - arp_run_bh(); -} - /* - * Enqueue to FIFO list. + * Signal to device layer, that hardware address may be changed. */ -static void arp_enqueue(struct arp_table **q, struct arp_table *entry) +static __inline__ void arp_update_hhs(struct arp_table * entry) { - unsigned long flags; - struct arp_table * tail; - - save_flags(flags); - cli(); - tail = *q; - if (!tail) - entry->next = entry; - else + struct hh_cache *hh; + void (*update)(struct hh_cache*, struct device*, unsigned char*) = + entry->u.neigh.dev->header_cache_update; + +#if RT_CACHE_DEBUG >= 1 + if (!update && entry->u.neigh.hh) { - entry->next = tail->next; - tail->next = entry; + printk(KERN_DEBUG "arp_update_hhs: no update callback for %s\n", entry->u.neigh.dev->name); + return; } - *q = entry; - restore_flags(flags); - return; +#endif + for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next) + update(hh, entry->u.neigh.dev, entry->u.neigh.ha); } /* - * Dequeue from FIFO list, - * caller should mask interrupts. + * Invalidate all hh's, so that higher level will not try to use it. */ -static struct arp_table * arp_dequeue(struct arp_table **q) +static __inline__ void arp_invalidate_hhs(struct arp_table * entry) { - struct arp_table * entry; + struct hh_cache *hh; - if (*q) - { - entry = (*q)->next; - (*q)->next = entry->next; - if (entry->next == entry) - *q = NULL; - entry->next = NULL; - return entry; - } - return NULL; + for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next) + hh->hh_uptodate = 0; } /* @@ -409,185 +332,55 @@ static struct arp_table * arp_dequeue(struct arp_table **q) static void arp_purge_send_q(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(); + while ((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL) + kfree_skb(skb, FREE_WRITE); +} + +static void arp_free(struct arp_table **entryp) +{ + struct arp_table *entry = *entryp; + *entryp = entry->u.next; + + if (!(entry->flags&ATF_PUBL)) { + atomic_dec(&arp_size); + if (!(entry->flags&ATF_COM)) + atomic_dec(&arp_unres_size); } - restore_flags(flags); - return; + del_timer(&entry->timer); + arp_purge_send_q(entry); + arp_invalidate_hhs(entry); + + neigh_destroy(&entry->u.neigh); } -/* - * 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) +static void arp_neigh_destroy(struct neighbour *neigh) { - unsigned long flags; + struct arp_table *entry = (struct arp_table*)neigh; struct hh_cache *hh, *next; del_timer(&entry->timer); arp_purge_send_q(entry); - save_flags(flags); - cli(); - hh = entry->hh; - entry->hh = NULL; - restore_flags(flags); + hh = entry->u.neigh.hh; + entry->u.neigh.hh = NULL; for ( ; hh; hh = next) { next = hh->hh_next; hh->hh_uptodate = 0; hh->hh_next = NULL; - hh->hh_arp = NULL; if (atomic_dec_and_test(&hh->hh_refcnt)) - kfree_s(hh, sizeof(struct(struct hh_cache))); - } - - kfree_s(entry, sizeof(struct arp_table)); - atomic_dec(&arp_size); - return; -} - -/* - * 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; - int count = 0; - - for (hh = entry->hh; hh; hh = hh->hh_next) - count += hh->hh_refcnt-1; - - return count; -} - -/* - * 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); -} - -/* - * 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; -} - -/* - * Atomic attaching new hh entry. - * Return 1, if entry has been freed, rather than attached. - */ - -static int arp_set_hh(struct hh_cache **hhp, struct hh_cache *hh) -{ - unsigned long flags; - struct hh_cache *hh1; - struct arp_table *entry; - - atomic_inc(&hh->hh_refcnt); - - save_flags(flags); - cli(); - if ((hh1 = *hhp) == NULL) - { - *hhp = hh; - restore_flags(flags); - return 0; - } - - entry = (struct arp_table*)hh->hh_arp; - - /* - * 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); + { +#if RT_CACHE_DEBUG >= 2 + extern atomic_t hh_count; + atomic_dec(&hh_count); #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; -} - -/* - * Test if a hardware address is all zero - */ - -static __inline__ int empty(unsigned char * addr, int len) -{ - while (len > 0) - { - if (*addr) - return 0; - len--; - addr++; + kfree_s(hh, sizeof(struct(struct hh_cache))); + } } - return 1; } @@ -610,7 +403,6 @@ static void arpd_send(int req, u32 addr, struct device * dev, char *ha, if (skb == NULL) return; - skb->free=1; arpreq=(struct arpd_request *)skb_put(skb, sizeof(struct arpd_request)); arpreq->req = req; arpreq->ip = addr; @@ -633,14 +425,14 @@ static void arpd_send(int req, u32 addr, struct device * dev, char *ha, * Send ARPD update message. */ -static __inline__ void arpd_update(struct arp_table * entry) +static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha) { if (arpd_not_running) return; - arpd_send(ARPD_UPDATE, entry->ip, entry->dev, entry->ha, - entry->last_updated); + arpd_send(ARPD_UPDATE, ip, dev, ha, jiffies); } + /* * Send ARPD lookup request. */ @@ -664,7 +456,7 @@ static __inline__ void arpd_flush(struct device * dev) } -static int arpd_callback(struct sk_buff *skb) +static int arpd_callback(int minor, struct sk_buff *skb) { struct device * dev; struct arpd_request *retreq; @@ -686,7 +478,7 @@ static int arpd_callback(struct sk_buff *skb) return -EINVAL; } - if (!retreq->updated || empty(retreq->ha, sizeof(retreq->ha))) + if (!retreq->updated) { /* * Invalid mapping: drop it and send ARP broadcast. @@ -696,9 +488,9 @@ static int arpd_callback(struct sk_buff *skb) } else { - arp_fast_lock(); - arp_update(retreq->ip, retreq->ha, dev, retreq->updated, NULL, 0); - arp_unlock(); + start_bh_atomic(); + arp_update(retreq->ip, retreq->ha, dev, retreq->updated, 0); + end_bh_atomic(); } kfree_skb(skb, FREE_READ); @@ -707,7 +499,7 @@ static int arpd_callback(struct sk_buff *skb) #else -static __inline__ void arpd_update(struct arp_table * entry) +static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha) { return; } @@ -732,16 +524,10 @@ static int arp_force_expire(void) 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); + static int last_index; if (last_index >= ARP_TABLE_SIZE) last_index = 0; @@ -754,31 +540,26 @@ static int arp_force_expire(void) { if (!(entry->flags & ATF_PERM)) { - int users; - cli(); - users = arp_count_hhs(entry); - - if (!users && now - entry->last_used > sysctl_arp_timeout) + if (!atomic_read(&entry->u.neigh.refcnt) && + now - entry->u.neigh.lastused > sysctl_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); + arp_free(pentry); result++; - if (arp_size < ARP_MAXSIZE) + if (atomic_read(&arp_size) < ARP_MAXSIZE) goto done; continue; } - restore_flags(flags); - if (!users && entry->last_used < oldest_used) + if (!atomic_read(&entry->u.neigh.refcnt) && + entry->u.neigh.lastused < oldest_used) { oldest_entry = pentry; - oldest_used = entry->last_used; + oldest_used = entry->u.neigh.lastused; } } - pentry = &entry->next; + pentry = &entry->u.next; } } @@ -786,15 +567,42 @@ 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); + printk("arp_force_expire: expiring %08x\n", (*oldest_entry)->ip); #endif - arp_free_entry(entry); + arp_free(oldest_entry); return 1; } +static void arp_unres_expire(void) +{ + int i; + struct arp_table *entry, **pentry; + unsigned long now = jiffies; + + for (i = 0; i < ARP_TABLE_SIZE; i++) { + pentry = &arp_tables[i & (ARP_TABLE_SIZE-1)]; + + while ((entry = *pentry) != NULL) { + if (!(entry->flags & (ATF_PERM|ATF_COM)) && + (entry->retries < sysctl_arp_max_tries || + entry->timer.expires - now < + sysctl_arp_res_time - sysctl_arp_res_time/32)) { + if (!atomic_read(&entry->u.neigh.refcnt)) { +#if RT_CACHE_DEBUG >= 2 + printk("arp_unres_expire: %08x discarded\n", entry->ip); +#endif + arp_free(pentry); + continue; + } + arp_purge_send_q(entry); + } + pentry = &entry->u.next; + } + } +} + + /* * Check if there are entries that are too old and remove them. If the * ATF_PERM flag is set, they are always left in the arp cache (permanent @@ -817,61 +625,48 @@ static void arp_check_expire(unsigned long dummy) ip_rt_check_expire(); - arp_fast_lock(); - - if (!ARP_LOCKED()) + for (i = 0; i < ARP_TABLE_SIZE; i++) { - - for (i = 0; i < ARP_TABLE_SIZE; i++) - { - struct arp_table *entry, **pentry; + struct arp_table *entry, **pentry; - pentry = &arp_tables[i]; + pentry = &arp_tables[i]; - while ((entry = *pentry) != NULL) + while ((entry = *pentry) != NULL) + { + if (entry->flags & ATF_PERM) { - if (entry->flags & ATF_PERM) - { - pentry = &entry->next; - continue; - } + pentry = &entry->u.next; + continue; + } - cli(); - if (now - entry->last_used > sysctl_arp_timeout - && !arp_count_hhs(entry)) - { - *pentry = entry->next; - sti(); + if (!atomic_read(&entry->u.neigh.refcnt) && + now - entry->u.neigh.lastused > sysctl_arp_timeout) + { #if RT_CACHE_DEBUG >= 2 - printk("arp_expire: %08x expired\n", entry->ip); + printk("arp_expire: %08x expired\n", entry->ip); #endif - arp_free_entry(entry); - continue; - } - sti(); - if (entry->last_updated - && now - entry->last_updated > sysctl_arp_confirm_interval - && !(entry->flags & ATF_PERM)) - { - struct device * dev = entry->dev; - entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings; - 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); + arp_free(pentry); + continue; + } + if (entry->last_updated && + now - entry->last_updated > sysctl_arp_confirm_interval) + { + struct device * dev = entry->u.neigh.dev; + entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings; + 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->u.neigh.ha, + dev->dev_addr, NULL); #if RT_CACHE_DEBUG >= 2 - printk("arp_expire: %08x requires confirmation\n", entry->ip); + printk("arp_expire: %08x requires confirmation\n", entry->ip); #endif - } - pentry = &entry->next; /* go to next entry */ } + pentry = &entry->u.next; /* go to next entry */ } } - arp_unlock(); - /* * Set the timer again. */ @@ -891,35 +686,10 @@ static void arp_expire_request (unsigned long arg) struct arp_table *entry = (struct arp_table *) arg; struct arp_table **pentry; unsigned long hash; - unsigned long flags; - arp_fast_lock(); - - save_flags(flags); - cli(); del_timer(&entry->timer); - /* - * If arp table is locked, defer expire processing. - */ - 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; - } - - /* - * 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, + /* 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. @@ -927,17 +697,11 @@ static void arp_expire_request (unsigned long arg) if ((entry->flags & ATF_COM) && entry->last_updated && jiffies - entry->last_updated <= sysctl_arp_confirm_interval) - { - restore_flags(flags); - arp_unlock(); return; - } - - restore_flags(flags); if (entry->last_updated && --entry->retries > 0) { - struct device *dev = entry->dev; + struct device *dev = entry->u.neigh.dev; #if RT_CACHE_DEBUG >= 2 printk("arp_expire_request: %08x timed out\n", entry->ip); @@ -946,9 +710,8 @@ static void arp_expire_request (unsigned long arg) entry->timer.expires = jiffies + sysctl_arp_res_time; add_timer(&entry->timer); arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, - entry->retries > sysctl_arp_max_tries ? entry->ha : NULL, + entry->retries > sysctl_arp_max_tries ? entry->u.neigh.ha : NULL, dev->dev_addr, NULL); - arp_unlock(); return; } @@ -958,8 +721,7 @@ static void arp_expire_request (unsigned long arg) arp_purge_send_q(entry); - cli(); - if (arp_count_hhs(entry)) + if (atomic_read(&entry->u.neigh.refcnt)) { /* * The host is dead, but someone refers to it. @@ -969,32 +731,29 @@ static void arp_expire_request (unsigned long arg) * to ARP_DEAD_RES_TIME. */ - struct device *dev = entry->dev; + struct device *dev = entry->u.neigh.dev; #if RT_CACHE_DEBUG >= 2 printk("arp_expire_request: %08x is dead\n", entry->ip); #endif entry->retries = sysctl_arp_max_tries; + if (entry->flags&ATF_COM) + atomic_inc(&arp_unres_size); entry->flags &= ~ATF_COM; arp_invalidate_hhs(entry); - restore_flags(flags); /* * Declare the entry dead. */ entry->last_updated = 0; - arpd_update(entry); entry->timer.expires = jiffies + sysctl_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); entry->last_updated = 0; - arpd_update(entry); hash = HASH(entry->ip); @@ -1004,48 +763,51 @@ static void arp_expire_request (unsigned long arg) { if (*pentry != entry) { - pentry = &(*pentry)->next; + pentry = &(*pentry)->u.next; continue; } - *pentry = entry->next; #if RT_CACHE_DEBUG >= 2 printk("arp_expire_request: %08x is killed\n", entry->ip); #endif - arp_free_entry(entry); + arp_free(pentry); } - 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! + * of the internal ARP cache, arp_force_expire() an entry. */ -static struct arp_table * arp_alloc_entry(void) +static struct arp_table * arp_alloc(int how) { struct arp_table * entry; - - if (arp_size >= ARP_MAXSIZE) + if (how && atomic_read(&arp_size) >= ARP_MAXSIZE) arp_force_expire(); + if (how > 1 && atomic_read(&arp_unres_size) >= ARP_MAX_UNRES) { + arp_unres_expire(); + if (atomic_read(&arp_unres_size) >= ARP_MAX_UNRES) { + printk(KERN_DEBUG "arp_unres_size=%d\n", + atomic_read(&arp_unres_size)); + return NULL; + } + } - entry = (struct arp_table *) - kmalloc(sizeof(struct arp_table),GFP_ATOMIC); + entry = (struct arp_table *)neigh_alloc(sizeof(struct arp_table), + &arp_neigh_ops); + atomic_set(&entry->u.neigh.refcnt, 1); if (entry != NULL) { - atomic_inc(&arp_size); - memset(entry, 0, sizeof(struct arp_table)); + if (how) + atomic_inc(&arp_size); 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); + entry->last_updated = jiffies; } return entry; } @@ -1069,29 +831,25 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr 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++) { struct arp_table *entry; struct arp_table **pentry = &arp_tables[i]; + start_bh_atomic(); + while ((entry = *pentry) != NULL) { - if (entry->dev == dev) + if (entry->u.neigh.dev != dev) { - *pentry = entry->next; /* remove from list */ - arp_free_entry(entry); + pentry = &entry->u.next; + continue; } - else - pentry = &entry->next; /* go to next entry */ + arp_free(pentry); } + + end_bh_atomic(); } - arp_unlock(); return NOTIFY_DONE; } @@ -1105,63 +863,28 @@ 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(KERN_ERR "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; - } - - save_flags(flags); - - cli(); - while((skb = skb_dequeue(&entry->skb)) != NULL) - { - IS_SKB(skb); - skb_device_lock(skb); - restore_flags(flags); - if(!skb->dev->rebuild_header(skb->data,skb->dev,skb->raddr,skb)) - { - skb->arp = 1; - if(skb->sk==NULL) - dev_queue_xmit(skb, skb->dev, 0); - else - dev_queue_xmit(skb,skb->dev,skb->sk->priority); - } - cli(); - } - restore_flags(flags); + while((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL) + dev_queue_xmit(skb); } static int arp_update (u32 sip, char *sha, struct device * dev, - unsigned long updated, struct arp_table *ientry, int grat) + unsigned long updated, int grat) { struct arp_table * entry; unsigned long hash; - int do_arpd = 0; if (updated == 0) { updated = jiffies; - do_arpd = 1; + arpd_update(sip, dev, sha); } hash = HASH(sip); - for (entry=arp_tables[hash]; entry; entry = entry->next) - if (entry->ip == sip && entry->dev == dev) + for (entry=arp_tables[hash]; entry; entry = entry->u.next) + if (entry->ip == sip && entry->u.neigh.dev == dev) break; if (entry) @@ -1173,28 +896,24 @@ arp_update (u32 sip, char *sha, struct device * dev, { del_timer(&entry->timer); entry->last_updated = updated; - if (memcmp(entry->ha, sha, dev->addr_len)!=0) + if (memcmp(entry->u.neigh.ha, sha, dev->addr_len) != 0) { - memcpy(entry->ha, sha, dev->addr_len); + memcpy(entry->u.neigh.ha, sha, dev->addr_len); if (entry->flags & ATF_COM) arp_update_hhs(entry); } - if (do_arpd) - arpd_update(entry); } if (!(entry->flags & ATF_COM)) { /* - * This entry was incomplete. Delete the retransmit timer - * and switch to complete status. + * Switch to complete status. */ entry->flags |= ATF_COM; + atomic_dec(&arp_unres_size); 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. + * Send out waiting packets. */ arp_send_q(entry); } @@ -1204,39 +923,23 @@ arp_update (u32 sip, char *sha, struct device * dev, /* * No entry found. Need to add a new entry to the arp table. */ - entry = ientry; - - if (grat && !entry) + if (grat) return 0; + entry = arp_alloc(1); 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; - } + return 0; + entry->ip = sip; + entry->flags = ATF_COM; + memcpy(entry->u.neigh.ha, sha, dev->addr_len); + entry->u.neigh.dev = dev; + entry->hatype = dev->type; entry->last_updated = updated; - entry->last_used = jiffies; - if (do_arpd) - arpd_update(entry); - if (!ARP_LOCKED()) - { - 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; + entry->u.next = arp_tables[hash]; + arp_tables[hash] = entry; + neigh_release(&entry->u.neigh); return 0; } @@ -1246,39 +949,12 @@ 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)) + for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->u.next) + if (entry->ip == paddr && entry->u.neigh.dev == dev) return entry; return NULL; } -/* - * 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, 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) @@ -1287,9 +963,9 @@ static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, s 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 case IS_MULTICAST: - if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802) + if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802 + || dev->type==ARPHRD_FDDI) { u32 taddr; haddr[0]=0x01; @@ -1306,7 +982,6 @@ static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, s /* * If a device does not support multicast broadcast the stuff (eg AX.25 for now) */ -#endif case IS_BROADCAST: memcpy(haddr, dev->broadcast, dev->addr_len); @@ -1315,57 +990,49 @@ static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, s return 0; } + +static void arp_start_resolution(struct arp_table *entry) +{ + struct device * dev = entry->u.neigh.dev; + + del_timer(&entry->timer); + entry->timer.expires = jiffies + sysctl_arp_res_time; + entry->retries = sysctl_arp_max_tries; + add_timer(&entry->timer); +#ifdef CONFIG_ARPD + if (!arpd_not_running) + arpd_lookup(entry->ip, dev); + else +#endif + arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, + dev->pa_addr, NULL, dev->dev_addr, NULL); +} + /* * Create a new unresolved entry. */ -struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache *hh, struct sk_buff *skb) +struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct sk_buff *skb) { struct arp_table *entry; + unsigned long hash = HASH(paddr); - entry = arp_alloc_entry(); + entry = arp_alloc(2); if (entry != NULL) { entry->ip = paddr; - entry->dev = dev; - if (hh) - { - entry->hh = hh; - atomic_inc(&hh->hh_refcnt); - hh->hh_arp = (void*)entry; - } - entry->timer.expires = jiffies + sysctl_arp_res_time; + entry->u.neigh.dev = dev; + entry->hatype = dev->type; if (skb != NULL) - { - skb_queue_tail(&entry->skb, skb); - skb_device_unlock(skb); - } + skb_queue_tail(&entry->u.neigh.arp_queue, skb); - if (!ARP_LOCKED()) - { - unsigned long hash = HASH(paddr); - entry->next = arp_tables[hash]; - arp_tables[hash] = entry; - add_timer(&entry->timer); - entry->retries = sysctl_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_new_entry: %08x backlogged\n", entry->ip); -#endif - arp_enqueue(&arp_req_backlog, entry); - arp_bh_mask |= ARP_BH_BACKLOG; - } + atomic_inc(&arp_unres_size); + entry->u.next = arp_tables[hash]; + arp_tables[hash] = entry; + arp_start_resolution(entry); + neigh_release(&entry->u.neigh); } return entry; } @@ -1375,21 +1042,29 @@ struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache * 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) +int arp_find(unsigned char *haddr, struct sk_buff *skb) { + struct device *dev = skb->dev; + u32 paddr; struct arp_table *entry; unsigned long hash; - if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev)) - { + if (!skb->dst) { + printk(KERN_DEBUG "arp_find called with dst==NULL\n"); + return 1; + } + + paddr = ((struct rtable*)skb->dst)->rt_gateway; + + if (arp_set_predefined(__ip_chk_addr(paddr), haddr, paddr, dev)) { if (skb) skb->arp = 1; return 0; } hash = HASH(paddr); - arp_fast_lock(); + + start_bh_atomic(); /* * Find an entry @@ -1400,11 +1075,11 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, { if (entry->flags & ATF_COM) { - entry->last_used = jiffies; - memcpy(haddr, entry->ha, dev->addr_len); + entry->u.neigh.lastused = jiffies; + memcpy(haddr, entry->u.neigh.ha, dev->addr_len); if (skb) skb->arp = 1; - arp_unlock(); + end_bh_atomic(); return 0; } @@ -1417,8 +1092,10 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, { if (entry->last_updated) { - skb_queue_tail(&entry->skb, skb); - skb_device_unlock(skb); + if (entry->u.neigh.arp_queue.qlen < ARP_MAX_UNRES_PACKETS) + skb_queue_tail(&entry->u.neigh.arp_queue, skb); + else + kfree_skb(skb, FREE_WRITE); } /* * If last_updated==0 host is dead, so @@ -1426,236 +1103,127 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev, */ else { - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev); - dev_kfree_skb(skb, FREE_WRITE); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); + kfree_skb(skb, FREE_WRITE); } } - arp_unlock(); + end_bh_atomic(); return 1; } - entry = arp_new_entry(paddr, dev, NULL, skb); + entry = arp_new_entry(paddr, dev, skb); if (skb != NULL && !entry) - dev_kfree_skb(skb, FREE_WRITE); + kfree_skb(skb, FREE_WRITE); - arp_unlock(); + end_bh_atomic(); 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 disappear 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) +int arp_find_1(unsigned char *haddr, struct dst_entry *dst, + struct neighbour *neigh) { + struct rtable *rt = (struct rtable*)dst; + struct device *dev = dst->dev; + u32 paddr = rt->rt_gateway; struct arp_table *entry; - struct hh_cache *hh; - int addr_hint; - unsigned long flags; - - save_flags(flags); + unsigned long hash; - if ((addr_hint = ip_chk_addr(paddr)) != 0) + if (!neigh) { - unsigned char haddr[MAX_ADDR_LEN]; - if (*hhp) + if ((rt->rt_flags & RTF_MULTICAST) && + (dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)) + { + u32 taddr; + haddr[0]=0x01; + haddr[1]=0x00; + haddr[2]=0x5e; + taddr=ntohl(paddr); + haddr[5]=taddr&0xff; + taddr=taddr>>8; + haddr[4]=taddr&0xff; + taddr=taddr>>8; + haddr[3]=taddr&0x7f; return 1; - hh = arp_alloc_hh(htype); - if (!hh) + } + if (rt->rt_flags & (RTF_BROADCAST|RTF_MULTICAST)) + { + memcpy(haddr, dev->broadcast, dev->addr_len); return 1; - arp_set_predefined(addr_hint, haddr, paddr, dev); - dev->header_cache_update(hh, dev, haddr); - return arp_set_hh(hhp, hh); - } - - arp_fast_lock(); - - entry = arp_lookup(paddr, dev); - - if (entry) - { - for (hh = entry->hh; hh; hh=hh->hh_next) - if (hh->hh_type == htype) - break; - - if (hh) + } + if (rt->rt_flags & RTF_LOCAL) { - arp_set_hh(hhp, hh); - arp_unlock(); + printk(KERN_DEBUG "ARP: arp called for own IP address\n"); + memcpy(haddr, dev->dev_addr, dev->addr_len); return 1; } + return 0; } - hh = arp_alloc_hh(htype); - if (!hh) - { - arp_unlock(); - return 1; - } - - if (entry) - { - - cli(); - hh->hh_arp = (void*)entry; - hh->hh_next = entry->hh; - entry->hh = hh; - atomic_inc(&hh->hh_refcnt); - restore_flags(flags); - - if (entry->flags & ATF_COM) - dev->header_cache_update(hh, dev, entry->ha); + hash = HASH(paddr); - if (arp_set_hh(hhp, hh)) - { - arp_unlock(); - return 0; - } + start_bh_atomic(); - entry->last_used = jiffies; - arp_unlock(); - return 0; - } + entry = (struct arp_table*)neigh; - entry = arp_new_entry(paddr, dev, hh, NULL); - if (entry == NULL) + if (entry->flags & ATF_COM) { - kfree_s(hh, sizeof(struct hh_cache)); - arp_unlock(); + entry->u.neigh.lastused = jiffies; + memcpy(haddr, entry->u.neigh.ha, dev->addr_len); + end_bh_atomic(); return 1; } - if (!arp_set_hh(hhp, hh)) - { - arp_unlock(); - return 0; - } - arp_unlock(); - return 1; + end_bh_atomic(); + return 0; } -static void arp_run_bh() + +struct neighbour* arp_find_neighbour(struct dst_entry *dst, int resolve) { - unsigned long flags; - struct arp_table *entry, *entry1; - struct device * dev; + struct rtable *rt = (struct rtable*)dst; + struct device *dev = rt->u.dst.dev; + u32 paddr = rt->rt_gateway; + struct arp_table *entry; unsigned long hash; - struct hh_cache *hh; - u32 sip; - save_flags(flags); - cli(); - arp_fast_lock(); + if (dst->ops->family != AF_INET) + return NULL; - while (arp_bh_mask) - { - arp_bh_mask &= ~ARP_BH_BACKLOG; + if ((dev->flags & (IFF_LOOPBACK|IFF_NOARP)) || + (rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST))) + return NULL; - while ((entry = arp_dequeue(&arp_backlog)) != NULL) - { - restore_flags(flags); - if (arp_update(entry->ip, entry->ha, entry->dev, 0, entry, 0)) - arp_free_entry(entry); - cli(); - } + hash = HASH(paddr); - cli(); - while ((entry = arp_dequeue(&arp_req_backlog)) != NULL) - { - restore_flags(flags); + start_bh_atomic(); + + /* + * Find an entry + */ + entry = arp_lookup(paddr, dev); + + if (entry != NULL) /* It exists */ + { + atomic_inc(&entry->u.neigh.refcnt); + end_bh_atomic(); + entry->u.neigh.lastused = jiffies; + return (struct neighbour*)entry; + } - dev = entry->dev; - sip = entry->ip; - hash = HASH(sip); + if (!resolve) + return NULL; - for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next) - if (entry1->ip == sip && entry1->dev == dev) - break; + entry = arp_new_entry(paddr, dev, NULL); - if (!entry1) - { - cli(); - entry->next = arp_tables[hash]; - arp_tables[hash] = entry; - restore_flags(flags); - entry->timer.expires = jiffies + sysctl_arp_res_time; - entry->retries = sysctl_arp_max_tries; - 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(KERN_DEBUG "arp_run_bh: %08x reinstalled\n", sip); -#endif - } - else - { - 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; + if (entry) + atomic_inc(&entry->u.neigh.refcnt); - /* Prune skb list from entry - * and graft it to entry1. - */ - while ((skb = skb_dequeue(&entry->skb)) != NULL) - { - skb_device_lock(skb); - restore_flags(flags); - skb_queue_tail(&entry1->skb, skb); - skb_device_unlock(skb); - cli(); - } - restore_flags(flags); - - arp_free_entry(entry); + end_bh_atomic(); - if (entry1->flags & ATF_COM) - { - arp_update_hhs(entry1); - arp_send_q(entry1); - } - } - cli(); - } - cli(); - } - arp_unlock(); - restore_flags(flags); + return (struct neighbour*)entry; } - /* * Interface to link layer: send routine and receive handler. */ @@ -1696,17 +1264,28 @@ void arp_send(int type, int ptype, u32 dest_ip, 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. */ + /* + * Fill out the arp protocol part. + * + * The arp hardware type should match the device type, except for FDDI, + * which (according to RFC 1390) should always equal 1 (Ethernet). + */ +#ifdef CONFIG_FDDI + arp->ar_hrd = (dev->type == ARPHRD_FDDI) ? htons(ARPHRD_ETHER) : htons(dev->type); +#else arp->ar_hrd = htons(dev->type); +#endif + /* + * Exceptions everywhere. AX.25 uses the AX.25 PID value not the + * DIX code for the protocol. Make these device structure fields. + */ #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP); @@ -1732,8 +1311,10 @@ void arp_send(int type, int ptype, u32 dest_ip, memset(arp_ptr, 0, dev->addr_len); arp_ptr+=dev->addr_len; memcpy(arp_ptr, &dest_ip, 4); + skb->dev = dev; + skb->priority = 0; - dev_queue_xmit(skb, dev, 0); + dev_queue_xmit(skb); } @@ -1743,15 +1324,12 @@ void arp_send(int type, int ptype, u32 dest_ip, 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; + struct arphdr *arp = skb->nh.arph; unsigned char *arp_ptr= (unsigned char *)(arp+1); - unsigned char *sha,*tha; - u32 sip,tip; - + struct rtable *rt; + 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 @@ -1759,16 +1337,46 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * is not from an IP number. We can't currently handle this, so toss * it. */ +#if defined(CONFIG_FDDI) || defined(CONFIG_AP1000) + if (dev->type == ARPHRD_FDDI) + { + /* + * According to RFC 1390, FDDI devices should accept ARP hardware types + * of 1 (Ethernet). However, to be more robust, we'll accept hardware + * types of either 1 (Ethernet) or 6 (IEEE 802.2). + */ + if (arp->ar_hln != dev->addr_len || + ((ntohs(arp->ar_hrd) != ARPHRD_ETHER) && (ntohs(arp->ar_hrd) != ARPHRD_IEEE802)) || + dev->flags & IFF_NOARP || + skb->pkt_type == PACKET_OTHERHOST || + arp->ar_pln != 4) + { + kfree_skb(skb, FREE_READ); + return 0; + } + } + else + { + if (arp->ar_hln != dev->addr_len || + dev->type != ntohs(arp->ar_hrd) || + dev->flags & IFF_NOARP || + skb->pkt_type == PACKET_OTHERHOST || + arp->ar_pln != 4) + { + kfree_skb(skb, FREE_READ); + return 0; + } + } +#else if (arp->ar_hln != dev->addr_len || - dev->type != ntohs(arp->ar_hrd) || + dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP || - arp->ar_pln != 4) - { + skb->pkt_type == PACKET_OTHERHOST || + 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 */ } +#endif /* * Another test. @@ -1776,7 +1384,6 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * 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) { @@ -1801,14 +1408,8 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) 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: + case ARPHRD_FDDI: if(arp->ar_pro != htons(ETH_P_IP)) { kfree_skb(skb, FREE_READ); @@ -1833,13 +1434,22 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) 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 (LOOPBACK(tip) || MULTICAST(tip)) { + kfree_skb(skb, FREE_READ); + return 0; + } + if (ip_route_input(skb, tip, sip, 0, dev)) { + kfree_skb(skb, FREE_READ); + return 0; + } + dev = skb->dev; + rt = (struct rtable*)skb->dst; + if (dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP || + rt->rt_flags&RTF_BROADCAST) { kfree_skb(skb, FREE_READ); return 0; } @@ -1861,84 +1471,30 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * 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 (arp->ar_op == htons(ARPOP_REQUEST)) { + struct arp_table *entry; - if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP) - { - kfree_skb(skb, FREE_READ); - return 0; + for (entry = arp_proxy_list; entry; entry = entry->u.next) { + if (!((entry->ip^tip)&entry->mask) && + ((!entry->u.neigh.dev && + (!(entry->flags & ATF_COM) || entry->hatype == dev->type)) + || entry->u.neigh.dev == dev) ) + break; } - } -#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; + if (entry && !(entry->flags & ATF_DONTPUB)) { + char *ha = (entry->flags & ATF_COM) ? entry->u.neigh.ha : dev->dev_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. - * - * 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); - - } - else - arp_unlock(); + if (rt->rt_flags&(RTF_LOCAL|RTF_NAT) || + (!(rt->rt_flags&RTCF_DOREDIRECT) && + rt->u.dst.dev != dev)) + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha); } - else - arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha); - } - arp_fast_lock(); - arp_update(sip, sha, dev, 0, NULL, ip_chk_addr(tip) != IS_MYADDR && dev->type != ARPHRD_METRICOM); - arp_unlock(); + start_bh_atomic(); + arp_update(sip, sha, dev, 0, !RT_LOCALADDR(rt->rt_flags) && dev->type != ARPHRD_METRICOM); + end_bh_atomic(); kfree_skb(skb, FREE_READ); return 0; } @@ -1953,14 +1509,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) * Set (create) an ARP cache entry. */ -static int arp_req_set(struct arpreq *r, struct device * dev) +int arp_req_set(struct arpreq *r, struct device * dev) { struct arp_table *entry, **entryp; struct sockaddr_in *si; - unsigned char *ha; + unsigned char *ha = NULL; u32 ip; u32 mask = DEF_ARP_NETMASK; - unsigned long flags; /* * Extract netmask (if supplied). @@ -1979,44 +1534,50 @@ static int arp_req_set(struct arpreq *r, struct device * dev) si = (struct sockaddr_in *) &r->arp_pa; ip = si->sin_addr.s_addr; - if (r->arp_flags&ATF_PUBL) { - if (!mask && ip) + if (ip & ~mask) return -EINVAL; - if (!dev) { - dev = dev_getbytype(r->arp_ha.sa_family); + if (!dev && (r->arp_flags & ATF_COM)) + { + dev = dev_getbyhwaddr(r->arp_ha.sa_family, r->arp_ha.sa_data); if (!dev) return -ENODEV; } } else { + struct rtable * rt; + int err; + + if ((r->arp_flags & ATF_PERM) && !(r->arp_flags & ATF_COM)) + return -EINVAL; + err = ip_route_output(&rt, ip, 0, 1, dev); + if (err) + return err; if (!dev) - { - struct rtable * rt; - rt = ip_rt_route(ip, 0); - if (!rt) - return -ENETUNREACH; - dev = rt->rt_dev; + dev = rt->u.dst.dev; + if (rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTCF_NAT)) { + if (rt->rt_flags&RTF_BROADCAST && + dev->type == ARPHRD_METRICOM && + r->arp_ha.sa_family == ARPHRD_METRICOM) { + memcpy(dev->broadcast, r->arp_ha.sa_data, dev->addr_len); + ip_rt_put(rt); + return 0; + } ip_rt_put(rt); - if (!dev) - return -ENODEV; - } - if (dev->type != ARPHRD_METRICOM && ip_chk_addr(ip)) return -EINVAL; + } + ip_rt_put(rt); } - if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) + + if (dev && (dev->flags&(IFF_LOOPBACK|IFF_NOARP))) return -ENODEV; - if (r->arp_ha.sa_family != dev->type) + if (dev && r->arp_ha.sa_family != dev->type) return -EINVAL; - arp_fast_lock(); -#if RT_CACHE_DEBUG >= 1 - if (ARP_LOCKED()) - printk("arp_req_set: bug\n"); -#endif + start_bh_atomic(); if (!(r->arp_flags & ATF_PUBL)) entryp = &arp_tables[HASH(ip)]; @@ -2025,49 +1586,100 @@ static int arp_req_set(struct arpreq *r, struct device * dev) while ((entry = *entryp) != NULL) { - /* User supplied arp entries are definitive - RHP 960603 */ + if (entry->mask == mask) + break; + if ((entry->mask & mask) != mask) + break; + entryp = &entry->u.next; + } + while ((entry = *entryp) != NULL && entry->mask == mask) + { + if (entry->ip == ip) + break; + entryp = &entry->u.next; + } + while ((entry = *entryp) != NULL && entry->mask == mask && + entry->ip == ip) + { + if (!entry->u.neigh.dev || entry->u.neigh.dev == dev) + break; + entryp = &entry->u.next; + } - if (entry->ip == ip && entry->mask == mask && entry->dev == dev) { - *entryp=entry->next; - arp_free_entry(entry); - continue; + while ((entry = *entryp) != NULL) + { + if (entry->ip != ip || entry->mask != mask || + entry->u.neigh.dev != dev) + { + entry = NULL; + break; } - if ((entry->mask & mask) != mask) + if (entry->hatype == r->arp_ha.sa_family && + (!(r->arp_flags & ATF_MAGIC) || + entry->flags == r->arp_flags)) break; - entryp = &entry->next; + entryp = &entry->u.next; } - entry = arp_alloc_entry(); - if (entry == NULL) + if (entry) + atomic_inc(&entry->u.neigh.refcnt); + else { - arp_unlock(); - return -ENOMEM; + entry = arp_alloc(r->arp_flags&ATF_PUBL ? 0 : 1); + if (entry == NULL) + { + end_bh_atomic(); + return -ENOMEM; + } + entry->ip = ip; + entry->u.neigh.dev = dev; + entry->mask = mask; + + if (dev) + entry->hatype = dev->type; + + entry->u.next = *entryp; + *entryp = entry; } - entry->ip = ip; - entry->dev = dev; - entry->mask = mask; entry->flags = r->arp_flags; + if (!(entry->flags&(ATF_PUBL|ATF_COM))) + atomic_inc(&arp_unres_size); - entry->next = *entryp; - *entryp = entry; - - ha = r->arp_ha.sa_data; - 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; - entry->flags |= ATF_COM; - restore_flags(flags); - arpd_update(entry); - arp_update_hhs(entry); - arp_unlock(); - return 0; -} + if (entry->flags & ATF_PUBL) + { + if (entry->flags & ATF_COM) + { + entry->hatype = r->arp_ha.sa_family; + ha = r->arp_ha.sa_data; + } + else if (dev) + ha = dev->dev_addr; + } + else + ha = r->arp_ha.sa_data; + if (ha) + memcpy(entry->u.neigh.ha, ha, dev ? dev->addr_len : MAX_ADDR_LEN); + else + memset(entry->u.neigh.ha, 0, MAX_ADDR_LEN); + entry->last_updated = entry->u.neigh.lastused = jiffies; + + if (!(entry->flags & ATF_PUBL)) + { + if (entry->flags & ATF_COM) + { + arpd_update(entry->ip, entry->u.neigh.dev, ha); + arp_update_hhs(entry); + } + else + arp_start_resolution(entry); + } + + neigh_release(&entry->u.neigh); + end_bh_atomic(); + return 0; +} /* * Get an ARP cache entry. @@ -2087,37 +1699,43 @@ static int arp_req_get(struct arpreq *r, struct device *dev) si = (struct sockaddr_in *) &r->arp_pa; - arp_fast_lock(); -#if RT_CACHE_DEBUG >= 1 - if (ARP_LOCKED()) - printk("arp_req_set: impossible\n"); -#endif + start_bh_atomic(); if (!(r->arp_flags & ATF_PUBL)) entry = arp_tables[HASH(si->sin_addr.s_addr)]; else entry = arp_proxy_list; - for ( ; entry ;entry = entry->next) + for ( ; entry ;entry = entry->u.next) { - if (entry->ip == si->sin_addr.s_addr - && (!dev || entry->dev == dev) - && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask)) + if (entry->ip == si->sin_addr.s_addr && + (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask) && + ( (r->arp_flags&ATF_PUBL) ? + (entry->u.neigh.dev == dev && entry->hatype == r->arp_ha.sa_family) + : (entry->u.neigh.dev == dev || !dev))) { - memcpy(r->arp_ha.sa_data, entry->ha, entry->dev->addr_len); - r->arp_ha.sa_family = entry->dev->type; + if (entry->u.neigh.dev) + { + memcpy(r->arp_ha.sa_data, entry->u.neigh.ha, entry->u.neigh.dev->addr_len); + r->arp_ha.sa_family = entry->u.neigh.dev->type; + strncpy(r->arp_dev, entry->u.neigh.dev->name, sizeof(r->arp_dev)); + } + else + { + r->arp_ha.sa_family = entry->hatype; + memset(r->arp_ha.sa_data, 0, sizeof(r->arp_ha.sa_data)); + } r->arp_flags = entry->flags; - strncpy(r->arp_dev, entry->dev->name, sizeof(r->arp_dev)); - arp_unlock(); + end_bh_atomic(); return 0; } } - arp_unlock(); + end_bh_atomic(); return -ENXIO; } -static int arp_req_delete(struct arpreq *r, struct device * dev) +int arp_req_delete(struct arpreq *r, struct device * dev) { struct sockaddr_in *si; struct arp_table *entry, **entryp; @@ -2132,11 +1750,7 @@ static int arp_req_delete(struct arpreq *r, struct device * dev) si = (struct sockaddr_in *) &r->arp_pa; - arp_fast_lock(); -#if RT_CACHE_DEBUG >= 1 - if (ARP_LOCKED()) - printk("arp_req_delete: impossible\n"); -#endif + start_bh_atomic(); if (!(r->arp_flags & ATF_PUBL)) entryp = &arp_tables[HASH(si->sin_addr.s_addr)]; @@ -2145,19 +1759,24 @@ static int arp_req_delete(struct arpreq *r, struct device * dev) while ((entry = *entryp) != NULL) { - if (entry->ip == si->sin_addr.s_addr - && (!dev || entry->dev == dev) - && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask)) + if (entry->ip == si->sin_addr.s_addr + && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask) + && (entry->u.neigh.dev == dev || (!(r->arp_flags&ATF_PUBL) && !dev)) + && (!(r->arp_flags&ATF_MAGIC) || r->arp_flags == entry->flags)) { - *entryp = entry->next; - arp_free_entry(entry); - retval = 0; - continue; + if (!atomic_read(&entry->u.neigh.refcnt)) + { + arp_free(entryp); + retval = 0; + continue; + } + if (retval) + retval = -EBUSY; } - entryp = &entry->next; + entryp = &entry->u.next; } - arp_unlock(); + end_bh_atomic(); return retval; } @@ -2169,7 +1788,6 @@ int arp_ioctl(unsigned int cmd, void *arg) { int err; struct arpreq r; - struct device * dev = NULL; switch(cmd) @@ -2181,17 +1799,7 @@ int arp_ioctl(unsigned int cmd, void *arg) case SIOCGARP: err = copy_from_user(&r, arg, sizeof(struct arpreq)); if (err) - return -EFAULT; - break; - case OLD_SIOCDARP: - case OLD_SIOCSARP: - if (!suser()) - return -EPERM; - case OLD_SIOCGARP: - err = copy_from_user(&r, arg, sizeof(struct arpreq_old)); - if (err) - return -EFAULT; - memset(&r.arp_dev, 0, sizeof(r.arp_dev)); + return -EFAULT; break; default: return -EINVAL; @@ -2200,8 +1808,9 @@ int arp_ioctl(unsigned int cmd, void *arg) if (r.arp_pa.sa_family != AF_INET) return -EPFNOSUPPORT; - if (!(r.arp_flags & ATF_PUBL)) - r.arp_flags &= ~ATF_NETMASK; + if (!(r.arp_flags & ATF_PUBL) && + (r.arp_flags & (ATF_NETMASK|ATF_DONTPUB|ATF_MAGIC))) + return -EINVAL; if (!(r.arp_flags & ATF_NETMASK)) ((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=DEF_ARP_NETMASK; @@ -2212,7 +1821,7 @@ int arp_ioctl(unsigned int cmd, void *arg) if (!r.arp_ha.sa_family) r.arp_ha.sa_family = dev->type; - else if (r.arp_ha.sa_family != dev->type) + if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type) return -EINVAL; } @@ -2222,54 +1831,10 @@ int arp_ioctl(unsigned int cmd, void *arg) return arp_req_delete(&r, dev); case SIOCSARP: return arp_req_set(&r, dev); - case OLD_SIOCDARP: - /* old SIOCDARP destroys 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 = arp_req_get(&r, dev); if (!err) - { err = copy_to_user(arg, &r, sizeof(r)); - if (err) - err = -EFAULT; - } - return err; - case OLD_SIOCGARP: - 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) - { - err = copy_to_user(arg, &r, sizeof(struct arpreq_old)); - if (err) - err = -EFAULT; - } return err; } /*NOTREACHED*/ @@ -2297,56 +1862,66 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy 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) + start_bh_atomic(); + + for(entry=arp_tables[i]; entry!=NULL; entry=entry->u.next) { /* * Convert hardware address to XX:XX:XX:XX ... form. */ #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) - if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM) - strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); + if (entry->hatype == ARPHRD_AX25 || entry->hatype == ARPHRD_NETROM) + strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha)); else { #else - if(entry->dev->type==ARPHRD_AX25) - strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); + if(entry->hatype==ARPHRD_AX25) + strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha)); else { #endif #endif - - for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++) + + if (entry->u.neigh.dev) { - hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ]; - hbuffer[k++]=hexbuf[ entry->ha[j]&15 ]; - hbuffer[k++]=':'; + for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->u.neigh.dev->addr_len;j++) + { + hbuffer[k++]=hexbuf[ (entry->u.neigh.ha[j]>>4)&15 ]; + hbuffer[k++]=hexbuf[ entry->u.neigh.ha[j]&15 ]; + hbuffer[k++]=':'; + } + hbuffer[--k]=0; } - hbuffer[--k]=0; + else + strcpy(hbuffer, "00:00:00:00:00:00"); #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) } #endif + size = sprintf(buffer+len, "%-17s0x%-10x0x%-10x%s", in_ntoa(entry->ip), - (unsigned int)entry->dev->type, + entry->hatype, 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); + "*" : in_ntoa(entry->mask), + entry->u.neigh.dev ? entry->u.neigh.dev->name : "*"); #else size += sprintf(buffer+len+size, - " %-17s %s\t%d\t%1d\n", + " %-17s %s\t%d\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); + "*" : in_ntoa(entry->mask), + entry->u.neigh.dev ? entry->u.neigh.dev->name : "*", + atomic_read(&entry->u.neigh.refcnt), + entry->u.neigh.hh ? atomic_read(&entry->u.neigh.hh->hh_refcnt) : -1, + entry->u.neigh.hh ? entry->u.neigh.hh->hh_uptodate : 0); #endif len += size; @@ -2355,11 +1930,14 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy if (pos <= offset) len=0; if (pos >= offset+length) + { + end_bh_atomic(); goto done; + } } + end_bh_atomic(); } done: - arp_unlock(); *start = buffer+len-(pos-offset); /* Start of wanted data */ len = pos-offset; /* Start slop */ @@ -2376,7 +1954,7 @@ done: static struct packet_type arp_packet_type = { - 0, /* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */ + __constant_htons(ETH_P_ARP), NULL, /* All devices */ arp_rcv, NULL, @@ -2400,8 +1978,6 @@ static struct proc_dir_entry proc_net_arp = { void arp_init (void) { - /* Register the packet type */ - arp_packet_type.type=htons(ETH_P_ARP); dev_add_pack(&arp_packet_type); /* Start with the regular checks for expired arp entries. */ add_timer(&arp_timer); @@ -2417,6 +1993,7 @@ void arp_init (void) #endif } + #ifdef CONFIG_AX25_MODULE /* |