Commit 410e9a9b authored by David S. Miller's avatar David S. Miller

Merge http://kernel-acme.bkbits.net:8080/net-cleanups-2.5-neighbour

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents 3da9cf28 3e978a40
...@@ -64,7 +64,7 @@ static struct neigh_table *neigh_tables; ...@@ -64,7 +64,7 @@ static struct neigh_table *neigh_tables;
cache. cache.
- If the entry requires some non-trivial actions, increase - If the entry requires some non-trivial actions, increase
its reference count and release table lock. its reference count and release table lock.
Neighbour entries are protected: Neighbour entries are protected:
- with reference count. - with reference count.
- with rwlock neigh->lock - with rwlock neigh->lock
...@@ -101,7 +101,7 @@ static int neigh_blackhole(struct sk_buff *skb) ...@@ -101,7 +101,7 @@ static int neigh_blackhole(struct sk_buff *skb)
unsigned long neigh_rand_reach_time(unsigned long base) unsigned long neigh_rand_reach_time(unsigned long base)
{ {
return (net_random() % base) + (base>>1); return (net_random() % base) + (base >> 1);
} }
...@@ -110,7 +110,7 @@ static int neigh_forced_gc(struct neigh_table *tbl) ...@@ -110,7 +110,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
int shrunk = 0; int shrunk = 0;
int i; int i;
for (i=0; i<=NEIGH_HASHMASK; i++) { for (i = 0; i <= NEIGH_HASHMASK; i++) {
struct neighbour *n, **np; struct neighbour *n, **np;
np = &tbl->hash_buckets[i]; np = &tbl->hash_buckets[i];
...@@ -128,12 +128,12 @@ static int neigh_forced_gc(struct neigh_table *tbl) ...@@ -128,12 +128,12 @@ static int neigh_forced_gc(struct neigh_table *tbl)
*/ */
write_lock(&n->lock); write_lock(&n->lock);
if (atomic_read(&n->refcnt) == 1 && if (atomic_read(&n->refcnt) == 1 &&
!(n->nud_state&NUD_PERMANENT) && !(n->nud_state & NUD_PERMANENT) &&
(n->nud_state != NUD_INCOMPLETE || (n->nud_state != NUD_INCOMPLETE ||
jiffies - n->used > n->parms->retrans_time)) { jiffies - n->used > n->parms->retrans_time)) {
*np = n->next; *np = n->next;
n->dead = 1; n->dead = 1;
shrunk = 1; shrunk = 1;
write_unlock(&n->lock); write_unlock(&n->lock);
neigh_release(n); neigh_release(n);
continue; continue;
...@@ -143,18 +143,17 @@ static int neigh_forced_gc(struct neigh_table *tbl) ...@@ -143,18 +143,17 @@ static int neigh_forced_gc(struct neigh_table *tbl)
} }
write_unlock_bh(&tbl->lock); write_unlock_bh(&tbl->lock);
} }
tbl->last_flush = jiffies; tbl->last_flush = jiffies;
return shrunk; return shrunk;
} }
static int neigh_del_timer(struct neighbour *n) static int neigh_del_timer(struct neighbour *n)
{ {
if (n->nud_state & NUD_IN_TIMER) { if ((n->nud_state & NUD_IN_TIMER) &&
if (del_timer(&n->timer)) { del_timer(&n->timer)) {
neigh_release(n); neigh_release(n);
return 1; return 1;
}
} }
return 0; return 0;
} }
...@@ -175,10 +174,9 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) ...@@ -175,10 +174,9 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
write_lock_bh(&tbl->lock); write_lock_bh(&tbl->lock);
for (i=0; i<=NEIGH_HASHMASK; i++) { for (i = 0; i <= NEIGH_HASHMASK; i++) {
struct neighbour *n, **np; struct neighbour *n, **np = &tbl->hash_buckets[i];
np = &tbl->hash_buckets[i];
while ((n = *np) != NULL) { while ((n = *np) != NULL) {
if (dev && n->dev != dev) { if (dev && n->dev != dev) {
np = &n->next; np = &n->next;
...@@ -202,7 +200,7 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) ...@@ -202,7 +200,7 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
n->parms = &tbl->parms; n->parms = &tbl->parms;
skb_queue_purge(&n->arp_queue); skb_queue_purge(&n->arp_queue);
n->output = neigh_blackhole; n->output = neigh_blackhole;
if (n->nud_state&NUD_VALID) if (n->nud_state & NUD_VALID)
n->nud_state = NUD_NOARP; n->nud_state = NUD_NOARP;
else else
n->nud_state = NUD_NONE; n->nud_state = NUD_NONE;
...@@ -223,38 +221,39 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) ...@@ -223,38 +221,39 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
static struct neighbour *neigh_alloc(struct neigh_table *tbl) static struct neighbour *neigh_alloc(struct neigh_table *tbl)
{ {
struct neighbour *n; struct neighbour *n = NULL;
unsigned long now = jiffies; unsigned long now = jiffies;
if (tbl->entries > tbl->gc_thresh3 || if (tbl->entries > tbl->gc_thresh3 ||
(tbl->entries > tbl->gc_thresh2 && (tbl->entries > tbl->gc_thresh2 &&
now - tbl->last_flush > 5*HZ)) { now - tbl->last_flush > 5 * HZ)) {
if (neigh_forced_gc(tbl) == 0 && if (!neigh_forced_gc(tbl) &&
tbl->entries > tbl->gc_thresh3) tbl->entries > tbl->gc_thresh3)
return NULL; goto out;
} }
n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC); n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC);
if (n == NULL) if (!n)
return NULL; goto out;
memset(n, 0, tbl->entry_size); memset(n, 0, tbl->entry_size);
skb_queue_head_init(&n->arp_queue); skb_queue_head_init(&n->arp_queue);
n->lock = RW_LOCK_UNLOCKED; n->lock = RW_LOCK_UNLOCKED;
n->updated = n->used = now; n->updated = n->used = now;
n->nud_state = NUD_NONE; n->nud_state = NUD_NONE;
n->output = neigh_blackhole; n->output = neigh_blackhole;
n->parms = &tbl->parms; n->parms = &tbl->parms;
init_timer(&n->timer); init_timer(&n->timer);
n->timer.function = neigh_timer_handler; n->timer.function = neigh_timer_handler;
n->timer.data = (unsigned long)n; n->timer.data = (unsigned long)n;
tbl->stats.allocs++; tbl->stats.allocs++;
neigh_glbl_allocs++; neigh_glbl_allocs++;
tbl->entries++; tbl->entries++;
n->tbl = tbl; n->tbl = tbl;
atomic_set(&n->refcnt, 1); atomic_set(&n->refcnt, 1);
n->dead = 1; n->dead = 1;
out:
return n; return n;
} }
...@@ -262,15 +261,12 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, ...@@ -262,15 +261,12 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
struct net_device *dev) struct net_device *dev)
{ {
struct neighbour *n; struct neighbour *n;
u32 hash_val;
int key_len = tbl->key_len; int key_len = tbl->key_len;
u32 hash_val = tbl->hash(pkey, dev);
hash_val = tbl->hash(pkey, dev);
read_lock_bh(&tbl->lock); read_lock_bh(&tbl->lock);
for (n = tbl->hash_buckets[hash_val]; n; n = n->next) { for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
if (dev == n->dev && if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
memcmp(n->primary_key, pkey, key_len) == 0) {
neigh_hold(n); neigh_hold(n);
break; break;
} }
...@@ -279,17 +275,18 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, ...@@ -279,17 +275,18 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
return n; return n;
} }
struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey, struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey,
struct net_device *dev) struct net_device *dev)
{ {
struct neighbour *n, *n1;
u32 hash_val; u32 hash_val;
int key_len = tbl->key_len; int key_len = tbl->key_len;
int error; int error;
struct neighbour *n1, *rc, *n = neigh_alloc(tbl);
n = neigh_alloc(tbl); if (!n) {
if (n == NULL) rc = ERR_PTR(-ENOBUFS);
return ERR_PTR(-ENOBUFS); goto out;
}
memcpy(n->primary_key, pkey, key_len); memcpy(n->primary_key, pkey, key_len);
n->dev = dev; n->dev = dev;
...@@ -297,29 +294,28 @@ struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey, ...@@ -297,29 +294,28 @@ struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey,
/* Protocol specific setup. */ /* Protocol specific setup. */
if (tbl->constructor && (error = tbl->constructor(n)) < 0) { if (tbl->constructor && (error = tbl->constructor(n)) < 0) {
neigh_release(n); rc = ERR_PTR(error);
return ERR_PTR(error); goto out_neigh_release;
} }
/* Device specific setup. */ /* Device specific setup. */
if (n->parms->neigh_setup && if (n->parms->neigh_setup &&
(error = n->parms->neigh_setup(n)) < 0) { (error = n->parms->neigh_setup(n)) < 0) {
neigh_release(n); rc = ERR_PTR(error);
return ERR_PTR(error); goto out_neigh_release;
} }
n->confirmed = jiffies - (n->parms->base_reachable_time<<1); n->confirmed = jiffies - (n->parms->base_reachable_time << 1);
hash_val = tbl->hash(pkey, dev); hash_val = tbl->hash(pkey, dev);
write_lock_bh(&tbl->lock); write_lock_bh(&tbl->lock);
for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) { for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
if (dev == n1->dev && if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
memcmp(n1->primary_key, pkey, key_len) == 0) {
neigh_hold(n1); neigh_hold(n1);
write_unlock_bh(&tbl->lock); write_unlock_bh(&tbl->lock);
neigh_release(n); rc = n1;
return n1; goto out_neigh_release;
} }
} }
...@@ -329,69 +325,77 @@ struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey, ...@@ -329,69 +325,77 @@ struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey,
neigh_hold(n); neigh_hold(n);
write_unlock_bh(&tbl->lock); write_unlock_bh(&tbl->lock);
NEIGH_PRINTK2("neigh %p is created.\n", n); NEIGH_PRINTK2("neigh %p is created.\n", n);
return n; rc = n;
out:
return rc;
out_neigh_release:
neigh_release(n);
goto out;
} }
struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey, struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
struct net_device *dev, int creat) struct net_device *dev, int creat)
{ {
struct pneigh_entry *n; struct pneigh_entry *n;
u32 hash_val;
int key_len = tbl->key_len; int key_len = tbl->key_len;
u32 hash_val = *(u32 *)(pkey + key_len - 4);
hash_val = *(u32*)(pkey + key_len - 4); hash_val ^= (hash_val >> 16);
hash_val ^= (hash_val>>16); hash_val ^= hash_val >> 8;
hash_val ^= hash_val>>8; hash_val ^= hash_val >> 4;
hash_val ^= hash_val>>4;
hash_val &= PNEIGH_HASHMASK; hash_val &= PNEIGH_HASHMASK;
read_lock_bh(&tbl->lock); read_lock_bh(&tbl->lock);
for (n = tbl->phash_buckets[hash_val]; n; n = n->next) { for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
if (memcmp(n->key, pkey, key_len) == 0 && if (!memcmp(n->key, pkey, key_len) &&
(n->dev == dev || !n->dev)) { (n->dev == dev || !n->dev)) {
read_unlock_bh(&tbl->lock); read_unlock_bh(&tbl->lock);
return n; goto out;
} }
} }
read_unlock_bh(&tbl->lock); read_unlock_bh(&tbl->lock);
n = NULL;
if (!creat) if (!creat)
return NULL; goto out;
n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL); n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);
if (n == NULL) if (!n)
return NULL; goto out;
memcpy(n->key, pkey, key_len); memcpy(n->key, pkey, key_len);
n->dev = dev; n->dev = dev;
if (tbl->pconstructor && tbl->pconstructor(n)) { if (tbl->pconstructor && tbl->pconstructor(n)) {
kfree(n); kfree(n);
return NULL; n = NULL;
goto out;
} }
write_lock_bh(&tbl->lock); write_lock_bh(&tbl->lock);
n->next = tbl->phash_buckets[hash_val]; n->next = tbl->phash_buckets[hash_val];
tbl->phash_buckets[hash_val] = n; tbl->phash_buckets[hash_val] = n;
write_unlock_bh(&tbl->lock); write_unlock_bh(&tbl->lock);
out:
return n; return n;
} }
int pneigh_delete(struct neigh_table *tbl, const void *pkey, struct net_device *dev) int pneigh_delete(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
{ {
struct pneigh_entry *n, **np; struct pneigh_entry *n, **np;
u32 hash_val;
int key_len = tbl->key_len; int key_len = tbl->key_len;
u32 hash_val = *(u32 *)(pkey + key_len - 4);
hash_val = *(u32*)(pkey + key_len - 4); hash_val ^= (hash_val >> 16);
hash_val ^= (hash_val>>16); hash_val ^= hash_val >> 8;
hash_val ^= hash_val>>8; hash_val ^= hash_val >> 4;
hash_val ^= hash_val>>4;
hash_val &= PNEIGH_HASHMASK; hash_val &= PNEIGH_HASHMASK;
for (np = &tbl->phash_buckets[hash_val]; (n=*np) != NULL; np = &n->next) { for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL;
if (memcmp(n->key, pkey, key_len) == 0 && n->dev == dev) { np = &n->next) {
if (!memcmp(n->key, pkey, key_len) && n->dev == dev) {
write_lock_bh(&tbl->lock); write_lock_bh(&tbl->lock);
*np = n->next; *np = n->next;
write_unlock_bh(&tbl->lock); write_unlock_bh(&tbl->lock);
...@@ -409,10 +413,10 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev) ...@@ -409,10 +413,10 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
struct pneigh_entry *n, **np; struct pneigh_entry *n, **np;
u32 h; u32 h;
for (h=0; h<=PNEIGH_HASHMASK; h++) { for (h = 0; h <= PNEIGH_HASHMASK; h++) {
np = &tbl->phash_buckets[h]; np = &tbl->phash_buckets[h];
while ((n=*np) != NULL) { while ((n = *np) != NULL) {
if (n->dev == dev || dev == NULL) { if (!dev || n->dev == dev) {
*np = n->next; *np = n->next;
if (tbl->pdestructor) if (tbl->pdestructor)
tbl->pdestructor(n); tbl->pdestructor(n);
...@@ -431,17 +435,18 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev) ...@@ -431,17 +435,18 @@ static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
* *
*/ */
void neigh_destroy(struct neighbour *neigh) void neigh_destroy(struct neighbour *neigh)
{ {
struct hh_cache *hh; struct hh_cache *hh;
if (!neigh->dead) { if (!neigh->dead) {
printk("Destroying alive neighbour %p from %08lx\n", neigh, printk(KERN_WARNING
*(((unsigned long*)&neigh)-1)); "Destroying alive neighbour %p from %08lx\n", neigh,
*(((unsigned long *)&neigh) - 1));
return; return;
} }
if (neigh_del_timer(neigh)) if (neigh_del_timer(neigh))
printk("Impossible event.\n"); printk(KERN_WARNING "Impossible event.\n");
while ((hh = neigh->hh) != NULL) { while ((hh = neigh->hh) != NULL) {
neigh->hh = hh->hh_next; neigh->hh = hh->hh_next;
...@@ -519,14 +524,14 @@ static void neigh_sync(struct neighbour *n) ...@@ -519,14 +524,14 @@ static void neigh_sync(struct neighbour *n)
unsigned long now = jiffies; unsigned long now = jiffies;
u8 state = n->nud_state; u8 state = n->nud_state;
if (state&(NUD_NOARP|NUD_PERMANENT)) if (state & (NUD_NOARP | NUD_PERMANENT))
return; return;
if (state&NUD_REACHABLE) { if (state & NUD_REACHABLE) {
if (now - n->confirmed > n->parms->reachable_time) { if (now - n->confirmed > n->parms->reachable_time) {
n->nud_state = NUD_STALE; n->nud_state = NUD_STALE;
neigh_suspect(n); neigh_suspect(n);
} }
} else if (state&NUD_VALID) { } else if (state & NUD_VALID) {
if (now - n->confirmed < n->parms->reachable_time) { if (now - n->confirmed < n->parms->reachable_time) {
neigh_del_timer(n); neigh_del_timer(n);
n->nud_state = NUD_REACHABLE; n->nud_state = NUD_REACHABLE;
...@@ -537,7 +542,7 @@ static void neigh_sync(struct neighbour *n) ...@@ -537,7 +542,7 @@ static void neigh_sync(struct neighbour *n)
static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
{ {
struct neigh_table *tbl = (struct neigh_table*)arg; struct neigh_table *tbl = (struct neigh_table *)arg;
unsigned long now = jiffies; unsigned long now = jiffies;
int i; int i;
...@@ -547,15 +552,16 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) ...@@ -547,15 +552,16 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
/* /*
* periodicly recompute ReachableTime from random function * periodicly recompute ReachableTime from random function
*/ */
if (now - tbl->last_rand > 300*HZ) { if (now - tbl->last_rand > 300 * HZ) {
struct neigh_parms *p; struct neigh_parms *p;
tbl->last_rand = now; tbl->last_rand = now;
for (p=&tbl->parms; p; p = p->next) for (p = &tbl->parms; p; p = p->next)
p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); p->reachable_time =
neigh_rand_reach_time(p->base_reachable_time);
} }
for (i=0; i <= NEIGH_HASHMASK; i++) { for (i = 0; i <= NEIGH_HASHMASK; i++) {
struct neighbour *n, **np; struct neighbour *n, **np;
np = &tbl->hash_buckets[i]; np = &tbl->hash_buckets[i];
...@@ -565,7 +571,7 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) ...@@ -565,7 +571,7 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
write_lock(&n->lock); write_lock(&n->lock);
state = n->nud_state; state = n->nud_state;
if (state&(NUD_PERMANENT|NUD_IN_TIMER)) { if (state & (NUD_PERMANENT | NUD_IN_TIMER)) {
write_unlock(&n->lock); write_unlock(&n->lock);
goto next_elt; goto next_elt;
} }
...@@ -574,7 +580,8 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) ...@@ -574,7 +580,8 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
n->used = n->confirmed; n->used = n->confirmed;
if (atomic_read(&n->refcnt) == 1 && if (atomic_read(&n->refcnt) == 1 &&
(state == NUD_FAILED || now - n->used > n->parms->gc_staletime)) { (state == NUD_FAILED ||
now - n->used > n->parms->gc_staletime)) {
*np = n->next; *np = n->next;
n->dead = 1; n->dead = 1;
write_unlock(&n->lock); write_unlock(&n->lock);
...@@ -582,7 +589,7 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) ...@@ -582,7 +589,7 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
continue; continue;
} }
if (n->nud_state&NUD_REACHABLE && if (n->nud_state & NUD_REACHABLE &&
now - n->confirmed > n->parms->reachable_time) { now - n->confirmed > n->parms->reachable_time) {
n->nud_state = NUD_STALE; n->nud_state = NUD_STALE;
neigh_suspect(n); neigh_suspect(n);
...@@ -601,8 +608,8 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg) ...@@ -601,8 +608,8 @@ static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static void neigh_periodic_timer(unsigned long arg) static void neigh_periodic_timer(unsigned long arg)
{ {
struct neigh_table *tbl = (struct neigh_table*)arg; struct neigh_table *tbl = (struct neigh_table *)arg;
tasklet_schedule(&tbl->gc_task); tasklet_schedule(&tbl->gc_task);
} }
#endif #endif
...@@ -616,10 +623,10 @@ static __inline__ int neigh_max_probes(struct neighbour *n) ...@@ -616,10 +623,10 @@ static __inline__ int neigh_max_probes(struct neighbour *n)
/* Called when a timer expires for a neighbour entry. */ /* Called when a timer expires for a neighbour entry. */
static void neigh_timer_handler(unsigned long arg) static void neigh_timer_handler(unsigned long arg)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
struct neighbour *neigh = (struct neighbour*)arg; struct neighbour *neigh = (struct neighbour *)arg;
unsigned state; unsigned state;
int notify = 0; int notify = 0;
...@@ -627,14 +634,14 @@ static void neigh_timer_handler(unsigned long arg) ...@@ -627,14 +634,14 @@ static void neigh_timer_handler(unsigned long arg)
state = neigh->nud_state; state = neigh->nud_state;
if (!(state&NUD_IN_TIMER)) { if (!(state & NUD_IN_TIMER)) {
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
printk("neigh: timer & !nud_in_timer\n"); printk(KERN_WARNING "neigh: timer & !nud_in_timer\n");
#endif #endif
goto out; goto out;
} }
if ((state&NUD_VALID) && if ((state & NUD_VALID) &&
now - neigh->confirmed < neigh->parms->reachable_time) { now - neigh->confirmed < neigh->parms->reachable_time) {
neigh->nud_state = NUD_REACHABLE; neigh->nud_state = NUD_REACHABLE;
NEIGH_PRINTK2("neigh %p is still alive.\n", neigh); NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
...@@ -657,10 +664,11 @@ static void neigh_timer_handler(unsigned long arg) ...@@ -657,10 +664,11 @@ static void neigh_timer_handler(unsigned long arg)
/* It is very thin place. report_unreachable is very complicated /* It is very thin place. report_unreachable is very complicated
routine. Particularly, it can hit the same neighbour entry! routine. Particularly, it can hit the same neighbour entry!
So that, we try to be accurate and avoid dead loop. --ANK So that, we try to be accurate and avoid dead loop. --ANK
*/ */
while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) { while (neigh->nud_state == NUD_FAILED &&
(skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
write_unlock(&neigh->lock); write_unlock(&neigh->lock);
neigh->ops->error_report(neigh, skb); neigh->ops->error_report(neigh, skb);
write_lock(&neigh->lock); write_lock(&neigh->lock);
...@@ -688,61 +696,69 @@ static void neigh_timer_handler(unsigned long arg) ...@@ -688,61 +696,69 @@ static void neigh_timer_handler(unsigned long arg)
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{ {
int rc;
write_lock_bh(&neigh->lock); write_lock_bh(&neigh->lock);
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {
if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) { rc = 0;
if (neigh->parms->mcast_probes + neigh->parms->app_probes) { if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
atomic_set(&neigh->probes, neigh->parms->ucast_probes); goto out_unlock_bh;
neigh->nud_state = NUD_INCOMPLETE;
neigh_hold(neigh); if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
neigh->timer.expires = jiffies + neigh->parms->retrans_time; if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
add_timer(&neigh->timer); atomic_set(&neigh->probes, neigh->parms->ucast_probes);
write_unlock_bh(&neigh->lock); neigh->nud_state = NUD_INCOMPLETE;
neigh->ops->solicit(neigh, skb); neigh_hold(neigh);
atomic_inc(&neigh->probes); neigh->timer.expires = jiffies +
write_lock_bh(&neigh->lock); neigh->parms->retrans_time;
} else { add_timer(&neigh->timer);
neigh->nud_state = NUD_FAILED; write_unlock_bh(&neigh->lock);
write_unlock_bh(&neigh->lock); neigh->ops->solicit(neigh, skb);
atomic_inc(&neigh->probes);
if (skb) write_lock_bh(&neigh->lock);
kfree_skb(skb); } else {
return 1; neigh->nud_state = NUD_FAILED;
}
}
if (neigh->nud_state == NUD_INCOMPLETE) {
if (skb) {
if (skb_queue_len(&neigh->arp_queue) >= neigh->parms->queue_len) {
struct sk_buff *buff;
buff = neigh->arp_queue.next;
__skb_unlink(buff, &neigh->arp_queue);
kfree_skb(buff);
}
__skb_queue_tail(&neigh->arp_queue, skb);
}
write_unlock_bh(&neigh->lock); write_unlock_bh(&neigh->lock);
if (skb)
kfree_skb(skb);
return 1; return 1;
} }
if (neigh->nud_state == NUD_STALE) { }
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh_hold(neigh); if (neigh->nud_state == NUD_INCOMPLETE) {
neigh->nud_state = NUD_DELAY; if (skb) {
neigh->timer.expires = jiffies + neigh->parms->delay_probe_time; if (skb_queue_len(&neigh->arp_queue) >=
add_timer(&neigh->timer); neigh->parms->queue_len) {
struct sk_buff *buff;
buff = neigh->arp_queue.next;
__skb_unlink(buff, &neigh->arp_queue);
kfree_skb(buff);
}
__skb_queue_tail(&neigh->arp_queue, skb);
} }
rc = 1;
} else if (neigh->nud_state == NUD_STALE) {
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh_hold(neigh);
neigh->nud_state = NUD_DELAY;
neigh->timer.expires = jiffies + neigh->parms->delay_probe_time;
add_timer(&neigh->timer);
rc = 0;
} }
out_unlock_bh:
write_unlock_bh(&neigh->lock); write_unlock_bh(&neigh->lock);
return 0; return rc;
} }
static __inline__ void neigh_update_hhs(struct neighbour *neigh) static __inline__ void neigh_update_hhs(struct neighbour *neigh)
{ {
struct hh_cache *hh; struct hh_cache *hh;
void (*update)(struct hh_cache*, struct net_device*, unsigned char*) = void (*update)(struct hh_cache*, struct net_device*, unsigned char *) =
neigh->dev->header_cache_update; neigh->dev->header_cache_update;
if (update) { if (update) {
for (hh=neigh->hh; hh; hh=hh->hh_next) { for (hh = neigh->hh; hh; hh = hh->hh_next) {
write_lock_bh(&hh->hh_lock); write_lock_bh(&hh->hh_lock);
update(hh, neigh->dev, neigh->ha); update(hh, neigh->dev, neigh->ha);
write_unlock_bh(&hh->hh_lock); write_unlock_bh(&hh->hh_lock);
...@@ -755,38 +771,45 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh) ...@@ -755,38 +771,45 @@ static __inline__ void neigh_update_hhs(struct neighbour *neigh)
/* Generic update routine. /* Generic update routine.
-- lladdr is new lladdr or NULL, if it is not supplied. -- lladdr is new lladdr or NULL, if it is not supplied.
-- new is new state. -- new is new state.
-- override==1 allows to override existing lladdr, if it is different. -- override == 1 allows to override existing lladdr, if it is different.
-- arp==0 means that the change is administrative. -- arp == 0 means that the change is administrative.
Caller MUST hold reference count on the entry. Caller MUST hold reference count on the entry.
*/ */
int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override, int arp) int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
int override, int arp)
{ {
u8 old; u8 old;
int err; int err;
#ifdef CONFIG_ARPD
int notify = 0; int notify = 0;
struct net_device *dev = neigh->dev; #endif
struct net_device *dev;
write_lock_bh(&neigh->lock); write_lock_bh(&neigh->lock);
old = neigh->nud_state;
err = -EPERM; dev = neigh->dev;
if (arp && (old&(NUD_NOARP|NUD_PERMANENT))) old = neigh->nud_state;
err = -EPERM;
if (arp && (old & (NUD_NOARP | NUD_PERMANENT)))
goto out; goto out;
if (!(new&NUD_VALID)) { if (!(new & NUD_VALID)) {
neigh_del_timer(neigh); neigh_del_timer(neigh);
if (old&NUD_CONNECTED) if (old & NUD_CONNECTED)
neigh_suspect(neigh); neigh_suspect(neigh);
neigh->nud_state = new; neigh->nud_state = new;
err = 0; err = 0;
notify = old&NUD_VALID; #ifdef CONFIG_ARPD
notify = old & NUD_VALID;
#endif
goto out; goto out;
} }
/* Compare new lladdr with cached one */ /* Compare new lladdr with cached one */
if (dev->addr_len == 0) { if (!dev->addr_len) {
/* First case: device needs no address. */ /* First case: device needs no address. */
lladdr = neigh->ha; lladdr = neigh->ha;
} else if (lladdr) { } else if (lladdr) {
...@@ -795,8 +818,8 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override ...@@ -795,8 +818,8 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override
- compare new & old - compare new & old
- if they are different, check override flag - if they are different, check override flag
*/ */
if (old&NUD_VALID) { if (old & NUD_VALID) {
if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0) if (!memcmp(lladdr, neigh->ha, dev->addr_len))
lladdr = neigh->ha; lladdr = neigh->ha;
else if (!override) else if (!override)
goto out; goto out;
...@@ -806,14 +829,14 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override ...@@ -806,14 +829,14 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override
use it, otherwise discard the request. use it, otherwise discard the request.
*/ */
err = -EINVAL; err = -EINVAL;
if (!(old&NUD_VALID)) if (!(old & NUD_VALID))
goto out; goto out;
lladdr = neigh->ha; lladdr = neigh->ha;
} }
neigh_sync(neigh); neigh_sync(neigh);
old = neigh->nud_state; old = neigh->nud_state;
if (new&NUD_CONNECTED) if (new & NUD_CONNECTED)
neigh->confirmed = jiffies; neigh->confirmed = jiffies;
neigh->updated = jiffies; neigh->updated = jiffies;
...@@ -821,35 +844,35 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override ...@@ -821,35 +844,35 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override
do not change entry state, if new one is STALE. do not change entry state, if new one is STALE.
*/ */
err = 0; err = 0;
if (old&NUD_VALID) { if ((old & NUD_VALID) && lladdr == neigh->ha &&
if (lladdr == neigh->ha) (new == old || (new == NUD_STALE && (old & NUD_CONNECTED))))
if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED))) goto out;
goto out;
}
neigh_del_timer(neigh); neigh_del_timer(neigh);
neigh->nud_state = new; neigh->nud_state = new;
if (lladdr != neigh->ha) { if (lladdr != neigh->ha) {
memcpy(&neigh->ha, lladdr, dev->addr_len); memcpy(&neigh->ha, lladdr, dev->addr_len);
neigh_update_hhs(neigh); neigh_update_hhs(neigh);
if (!(new&NUD_CONNECTED)) if (!(new & NUD_CONNECTED))
neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1); neigh->confirmed = jiffies -
(neigh->parms->base_reachable_time << 1);
#ifdef CONFIG_ARPD #ifdef CONFIG_ARPD
notify = 1; notify = 1;
#endif #endif
} }
if (new == old) if (new == old)
goto out; goto out;
if (new&NUD_CONNECTED) if (new & NUD_CONNECTED)
neigh_connect(neigh); neigh_connect(neigh);
else else
neigh_suspect(neigh); neigh_suspect(neigh);
if (!(old&NUD_VALID)) { if (!(old & NUD_VALID)) {
struct sk_buff *skb; struct sk_buff *skb;
/* Again: avoid dead loop if something went wrong */ /* Again: avoid dead loop if something went wrong */
while (neigh->nud_state&NUD_VALID && while (neigh->nud_state & NUD_VALID &&
(skb=__skb_dequeue(&neigh->arp_queue)) != NULL) { (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
struct neighbour *n1 = neigh; struct neighbour *n1 = neigh;
write_unlock_bh(&neigh->lock); write_unlock_bh(&neigh->lock);
/* On shaper/eql skb->dst->neighbour != neigh :( */ /* On shaper/eql skb->dst->neighbour != neigh :( */
...@@ -869,24 +892,24 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override ...@@ -869,24 +892,24 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override
return err; return err;
} }
struct neighbour * neigh_event_ns(struct neigh_table *tbl, struct neighbour *neigh_event_ns(struct neigh_table *tbl,
u8 *lladdr, void *saddr, u8 *lladdr, void *saddr,
struct net_device *dev) struct net_device *dev)
{ {
struct neighbour *neigh; struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
lladdr || !dev->addr_len);
neigh = __neigh_lookup(tbl, saddr, dev, lladdr || !dev->addr_len);
if (neigh) if (neigh)
neigh_update(neigh, lladdr, NUD_STALE, 1, 1); neigh_update(neigh, lladdr, NUD_STALE, 1, 1);
return neigh; return neigh;
} }
static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, u16 protocol) static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst,
u16 protocol)
{ {
struct hh_cache *hh = NULL; struct hh_cache *hh;
struct net_device *dev = dst->dev; struct net_device *dev = dst->dev;
for (hh=n->hh; hh; hh = hh->hh_next) for (hh = n->hh; hh; hh = hh->hh_next)
if (hh->hh_type == protocol) if (hh->hh_type == protocol)
break; break;
...@@ -902,8 +925,8 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, u16 protoc ...@@ -902,8 +925,8 @@ static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, u16 protoc
} else { } else {
atomic_inc(&hh->hh_refcnt); atomic_inc(&hh->hh_refcnt);
hh->hh_next = n->hh; hh->hh_next = n->hh;
n->hh = hh; n->hh = hh;
if (n->nud_state&NUD_CONNECTED) if (n->nud_state & NUD_CONNECTED)
hh->hh_output = n->ops->hh_output; hh->hh_output = n->ops->hh_output;
else else
hh->hh_output = n->ops->output; hh->hh_output = n->ops->output;
...@@ -927,7 +950,8 @@ int neigh_compat_output(struct sk_buff *skb) ...@@ -927,7 +950,8 @@ int neigh_compat_output(struct sk_buff *skb)
__skb_pull(skb, skb->nh.raw - skb->data); __skb_pull(skb, skb->nh.raw - skb->data);
if (dev->hard_header && if (dev->hard_header &&
dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, skb->len) < 0 && dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,
skb->len) < 0 &&
dev->rebuild_header(skb)) dev->rebuild_header(skb))
return 0; return 0;
...@@ -940,37 +964,43 @@ int neigh_resolve_output(struct sk_buff *skb) ...@@ -940,37 +964,43 @@ int neigh_resolve_output(struct sk_buff *skb)
{ {
struct dst_entry *dst = skb->dst; struct dst_entry *dst = skb->dst;
struct neighbour *neigh; struct neighbour *neigh;
int rc = 0;
if (!dst || !(neigh = dst->neighbour)) if (!dst || !(neigh = dst->neighbour))
goto discard; goto discard;
__skb_pull(skb, skb->nh.raw - skb->data); __skb_pull(skb, skb->nh.raw - skb->data);
if (neigh_event_send(neigh, skb) == 0) { if (!neigh_event_send(neigh, skb)) {
int err; int err;
struct net_device *dev = neigh->dev; struct net_device *dev = neigh->dev;
if (dev->hard_header_cache && dst->hh == NULL) { if (dev->hard_header_cache && !dst->hh) {
write_lock_bh(&neigh->lock); write_lock_bh(&neigh->lock);
if (dst->hh == NULL) if (!dst->hh)
neigh_hh_init(neigh, dst, dst->ops->protocol); neigh_hh_init(neigh, dst, dst->ops->protocol);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); err = dev->hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
write_unlock_bh(&neigh->lock); write_unlock_bh(&neigh->lock);
} else { } else {
read_lock_bh(&neigh->lock); read_lock_bh(&neigh->lock);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); err = dev->hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock); read_unlock_bh(&neigh->lock);
} }
if (err >= 0) if (err >= 0)
return neigh->ops->queue_xmit(skb); rc = neigh->ops->queue_xmit(skb);
kfree_skb(skb); else
return -EINVAL; goto out_kfree_skb;
} }
return 0; out:
return rc;
discard: discard:
NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", dst, dst ? dst->neighbour : NULL); NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n",
dst, dst ? dst->neighbour : NULL);
out_kfree_skb:
rc = -EINVAL;
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; goto out;
} }
/* As fast as possible without hh cache */ /* As fast as possible without hh cache */
...@@ -985,12 +1015,16 @@ int neigh_connected_output(struct sk_buff *skb) ...@@ -985,12 +1015,16 @@ int neigh_connected_output(struct sk_buff *skb)
__skb_pull(skb, skb->nh.raw - skb->data); __skb_pull(skb, skb->nh.raw - skb->data);
read_lock_bh(&neigh->lock); read_lock_bh(&neigh->lock);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); err = dev->hard_header(skb, dev, ntohs(skb->protocol),
neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock); read_unlock_bh(&neigh->lock);
if (err >= 0) if (err >= 0)
return neigh->ops->queue_xmit(skb); err = neigh->ops->queue_xmit(skb);
kfree_skb(skb); else {
return -EINVAL; err = -EINVAL;
kfree_skb(skb);
}
return err;
} }
static void neigh_proxy_process(unsigned long arg) static void neigh_proxy_process(unsigned long arg)
...@@ -1004,7 +1038,7 @@ static void neigh_proxy_process(unsigned long arg) ...@@ -1004,7 +1038,7 @@ static void neigh_proxy_process(unsigned long arg)
skb = tbl->proxy_queue.next; skb = tbl->proxy_queue.next;
while (skb != (struct sk_buff*)&tbl->proxy_queue) { while (skb != (struct sk_buff *)&tbl->proxy_queue) {
struct sk_buff *back = skb; struct sk_buff *back = skb;
long tdif = back->stamp.tv_usec - now; long tdif = back->stamp.tv_usec - now;
...@@ -1031,13 +1065,13 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, ...@@ -1031,13 +1065,13 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
struct sk_buff *skb) struct sk_buff *skb)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
long sched_next = net_random()%p->proxy_delay; long sched_next = net_random() % p->proxy_delay;
if (tbl->proxy_queue.qlen > p->proxy_qlen) { if (tbl->proxy_queue.qlen > p->proxy_qlen) {
kfree_skb(skb); kfree_skb(skb);
return; return;
} }
skb->stamp.tv_sec = 0; skb->stamp.tv_sec = 0;
skb->stamp.tv_usec = now + sched_next; skb->stamp.tv_usec = now + sched_next;
spin_lock(&tbl->proxy_queue.lock); spin_lock(&tbl->proxy_queue.lock);
...@@ -1055,22 +1089,22 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, ...@@ -1055,22 +1089,22 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
} }
struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct neigh_table *tbl) struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
struct neigh_table *tbl)
{ {
struct neigh_parms *p; struct neigh_parms *p = kmalloc(sizeof(*p), GFP_KERNEL);
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p) { if (p) {
memcpy(p, &tbl->parms, sizeof(*p)); memcpy(p, &tbl->parms, sizeof(*p));
p->tbl = tbl; p->tbl = tbl;
p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); p->reachable_time =
if (dev && dev->neigh_setup) { neigh_rand_reach_time(p->base_reachable_time);
if (dev->neigh_setup(dev, p)) { if (dev && dev->neigh_setup && dev->neigh_setup(dev, p)) {
kfree(p); kfree(p);
return NULL; return NULL;
}
} }
write_lock_bh(&tbl->lock); write_lock_bh(&tbl->lock);
p->next = tbl->parms.next; p->next = tbl->parms.next;
tbl->parms.next = p; tbl->parms.next = p;
write_unlock_bh(&tbl->lock); write_unlock_bh(&tbl->lock);
} }
...@@ -1080,8 +1114,8 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct neigh_table ...@@ -1080,8 +1114,8 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct neigh_table
void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
{ {
struct neigh_parms **p; struct neigh_parms **p;
if (parms == NULL || parms == &tbl->parms) if (!parms || parms == &tbl->parms)
return; return;
write_lock_bh(&tbl->lock); write_lock_bh(&tbl->lock);
for (p = &tbl->parms.next; *p; p = &(*p)->next) { for (p = &tbl->parms.next; *p; p = &(*p)->next) {
...@@ -1104,34 +1138,37 @@ void neigh_table_init(struct neigh_table *tbl) ...@@ -1104,34 +1138,37 @@ void neigh_table_init(struct neigh_table *tbl)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time); tbl->parms.reachable_time =
neigh_rand_reach_time(tbl->parms.base_reachable_time);
if (tbl->kmem_cachep == NULL) if (!tbl->kmem_cachep)
tbl->kmem_cachep = kmem_cache_create(tbl->id, tbl->kmem_cachep = kmem_cache_create(tbl->id,
(tbl->entry_size+15)&~15, (tbl->entry_size +
15) & ~15,
0, SLAB_HWCACHE_ALIGN, 0, SLAB_HWCACHE_ALIGN,
NULL, NULL); NULL, NULL);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
tasklet_init(&tbl->gc_task, SMP_TIMER_NAME(neigh_periodic_timer), (unsigned long)tbl); tasklet_init(&tbl->gc_task, SMP_TIMER_NAME(neigh_periodic_timer),
(unsigned long)tbl);
#endif #endif
init_timer(&tbl->gc_timer); init_timer(&tbl->gc_timer);
tbl->lock = RW_LOCK_UNLOCKED; tbl->lock = RW_LOCK_UNLOCKED;
tbl->gc_timer.data = (unsigned long)tbl; tbl->gc_timer.data = (unsigned long)tbl;
tbl->gc_timer.function = neigh_periodic_timer; tbl->gc_timer.function = neigh_periodic_timer;
tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time; tbl->gc_timer.expires = now + tbl->gc_interval +
tbl->parms.reachable_time;
add_timer(&tbl->gc_timer); add_timer(&tbl->gc_timer);
init_timer(&tbl->proxy_timer); init_timer(&tbl->proxy_timer);
tbl->proxy_timer.data = (unsigned long)tbl; tbl->proxy_timer.data = (unsigned long)tbl;
tbl->proxy_timer.function = neigh_proxy_process; tbl->proxy_timer.function = neigh_proxy_process;
skb_queue_head_init(&tbl->proxy_queue); skb_queue_head_init(&tbl->proxy_queue);
tbl->last_flush = now; tbl->last_flush = now;
tbl->last_rand = now + tbl->parms.reachable_time*20; tbl->last_rand = now + tbl->parms.reachable_time * 20;
write_lock(&neigh_tbl_lock); write_lock(&neigh_tbl_lock);
tbl->next = neigh_tables; tbl->next = neigh_tables;
neigh_tables = tbl; neigh_tables = tbl;
write_unlock(&neigh_tbl_lock); write_unlock(&neigh_tbl_lock);
} }
...@@ -1167,15 +1204,14 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -1167,15 +1204,14 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
struct rtattr **nda = arg; struct rtattr **nda = arg;
struct neigh_table *tbl; struct neigh_table *tbl;
struct net_device *dev = NULL; struct net_device *dev = NULL;
int err = 0; int err = -ENODEV;
if (ndm->ndm_ifindex) { if (ndm->ndm_ifindex &&
if ((dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL) (dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
return -ENODEV; goto out;
}
read_lock(&neigh_tbl_lock); read_lock(&neigh_tbl_lock);
for (tbl=neigh_tables; tbl; tbl = tbl->next) { for (tbl = neigh_tables; tbl; tbl = tbl->next) {
struct neighbour *n; struct neighbour *n;
if (tbl->family != ndm->ndm_family) if (tbl->family != ndm->ndm_family)
...@@ -1183,34 +1219,33 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -1183,34 +1219,33 @@ int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
read_unlock(&neigh_tbl_lock); read_unlock(&neigh_tbl_lock);
err = -EINVAL; err = -EINVAL;
if (nda[NDA_DST-1] == NULL || if (!nda[NDA_DST - 1] ||
nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len)) nda[NDA_DST - 1]->rta_len != RTA_LENGTH(tbl->key_len))
goto out; goto out_dev_put;
if (ndm->ndm_flags&NTF_PROXY) { if (ndm->ndm_flags & NTF_PROXY) {
err = pneigh_delete(tbl, RTA_DATA(nda[NDA_DST-1]), dev); err = pneigh_delete(tbl,
goto out; RTA_DATA(nda[NDA_DST - 1]), dev);
goto out_dev_put;
} }
if (dev == NULL) if (!dev)
return -EINVAL; goto out;
n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev); n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST - 1]), dev);
if (n) { if (n) {
err = neigh_update(n, NULL, NUD_FAILED, 1, 0); err = neigh_update(n, NULL, NUD_FAILED, 1, 0);
neigh_release(n); neigh_release(n);
} }
out: goto out_dev_put;
if (dev)
dev_put(dev);
return err;
} }
read_unlock(&neigh_tbl_lock); read_unlock(&neigh_tbl_lock);
err = -EADDRNOTAVAIL;
out_dev_put:
if (dev) if (dev)
dev_put(dev); dev_put(dev);
out:
return -EADDRNOTAVAIL; return err;
} }
int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
...@@ -1219,15 +1254,14 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -1219,15 +1254,14 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
struct rtattr **nda = arg; struct rtattr **nda = arg;
struct neigh_table *tbl; struct neigh_table *tbl;
struct net_device *dev = NULL; struct net_device *dev = NULL;
int err = -ENODEV;
if (ndm->ndm_ifindex) { if (ndm->ndm_ifindex &&
if ((dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL) (dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
return -ENODEV; goto out;
}
read_lock(&neigh_tbl_lock); read_lock(&neigh_tbl_lock);
for (tbl=neigh_tables; tbl; tbl = tbl->next) { for (tbl = neigh_tables; tbl; tbl = tbl->next) {
int err = 0;
int override = 1; int override = 1;
struct neighbour *n; struct neighbour *n;
...@@ -1236,53 +1270,57 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -1236,53 +1270,57 @@ int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
read_unlock(&neigh_tbl_lock); read_unlock(&neigh_tbl_lock);
err = -EINVAL; err = -EINVAL;
if (nda[NDA_DST-1] == NULL || if (!nda[NDA_DST - 1] ||
nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len)) nda[NDA_DST - 1]->rta_len != RTA_LENGTH(tbl->key_len))
goto out; goto out_dev_put;
if (ndm->ndm_flags&NTF_PROXY) { if (ndm->ndm_flags & NTF_PROXY) {
err = -ENOBUFS; err = -ENOBUFS;
if (pneigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 1)) if (pneigh_lookup(tbl,
RTA_DATA(nda[NDA_DST - 1]), dev, 1))
err = 0; err = 0;
goto out; goto out_dev_put;
} }
if (dev == NULL)
return -EINVAL;
err = -EINVAL; err = -EINVAL;
if (nda[NDA_LLADDR-1] != NULL && if (!dev)
nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len))
goto out; goto out;
if (nda[NDA_LLADDR - 1] &&
nda[NDA_LLADDR - 1]->rta_len != RTA_LENGTH(dev->addr_len))
goto out_dev_put;
err = 0; err = 0;
n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev); n = neigh_lookup(tbl, RTA_DATA(nda[NDA_DST - 1]), dev);
if (n) { if (n) {
if (nlh->nlmsg_flags&NLM_F_EXCL) if (nlh->nlmsg_flags & NLM_F_EXCL)
err = -EEXIST; err = -EEXIST;
override = nlh->nlmsg_flags&NLM_F_REPLACE; override = nlh->nlmsg_flags & NLM_F_REPLACE;
} else if (!(nlh->nlmsg_flags&NLM_F_CREATE)) } else if (!(nlh->nlmsg_flags & NLM_F_CREATE))
err = -ENOENT; err = -ENOENT;
else { else {
n = __neigh_lookup_errno(tbl, RTA_DATA(nda[NDA_DST-1]), dev); n = __neigh_lookup_errno(tbl, RTA_DATA(nda[NDA_DST - 1]),
dev);
if (IS_ERR(n)) { if (IS_ERR(n)) {
err = PTR_ERR(n); err = PTR_ERR(n);
n = NULL; n = NULL;
} }
} }
if (err == 0) { if (!err) {
err = neigh_update(n, nda[NDA_LLADDR-1] ? RTA_DATA(nda[NDA_LLADDR-1]) : NULL, err = neigh_update(n, nda[NDA_LLADDR - 1] ?
RTA_DATA(nda[NDA_LLADDR - 1]) :
NULL,
ndm->ndm_state, ndm->ndm_state,
override, 0); override, 0);
} }
if (n) if (n)
neigh_release(n); neigh_release(n);
out: goto out_dev_put;
if (dev)
dev_put(dev);
return err;
} }
read_unlock(&neigh_tbl_lock);
read_unlock(&neigh_tbl_lock);
err = -EADDRNOTAVAIL;
out_dev_put:
if (dev) if (dev)
dev_put(dev); dev_put(dev);
return -EADDRNOTAVAIL; out:
return err;
} }
...@@ -1290,32 +1328,31 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n, ...@@ -1290,32 +1328,31 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
u32 pid, u32 seq, int event) u32 pid, u32 seq, int event)
{ {
unsigned long now = jiffies; unsigned long now = jiffies;
struct ndmsg *ndm; unsigned char *b = skb->tail;
struct nlmsghdr *nlh;
unsigned char *b = skb->tail;
struct nda_cacheinfo ci; struct nda_cacheinfo ci;
int locked = 0; int locked = 0;
struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, event,
sizeof(struct ndmsg));
struct ndmsg *ndm = NLMSG_DATA(nlh);
nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ndm)); ndm->ndm_family = n->ops->family;
ndm = NLMSG_DATA(nlh); ndm->ndm_flags = n->flags;
ndm->ndm_family = n->ops->family; ndm->ndm_type = n->type;
ndm->ndm_flags = n->flags;
ndm->ndm_type = n->type;
ndm->ndm_ifindex = n->dev->ifindex; ndm->ndm_ifindex = n->dev->ifindex;
RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key); RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key);
read_lock_bh(&n->lock); read_lock_bh(&n->lock);
locked=1; locked = 1;
ndm->ndm_state = n->nud_state; ndm->ndm_state = n->nud_state;
if (n->nud_state&NUD_VALID) if (n->nud_state & NUD_VALID)
RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha); RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha);
ci.ndm_used = now - n->used; ci.ndm_used = now - n->used;
ci.ndm_confirmed = now - n->confirmed; ci.ndm_confirmed = now - n->confirmed;
ci.ndm_updated = now - n->updated; ci.ndm_updated = now - n->updated;
ci.ndm_refcnt = atomic_read(&n->refcnt) - 1; ci.ndm_refcnt = atomic_read(&n->refcnt) - 1;
read_unlock_bh(&n->lock); read_unlock_bh(&n->lock);
locked=0; locked = 0;
RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci); RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
nlh->nlmsg_len = skb->tail - b; nlh->nlmsg_len = skb->tail - b;
return skb->len; return skb->len;
nlmsg_failure: nlmsg_failure:
...@@ -1327,73 +1364,70 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n, ...@@ -1327,73 +1364,70 @@ static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
} }
static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct netlink_callback *cb) static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
struct netlink_callback *cb)
{ {
struct neighbour *n; struct neighbour *n;
int h, s_h; int rc, h, s_h = cb->args[1];
int idx, s_idx; int idx, s_idx = idx = cb->args[2];
s_h = cb->args[1]; for (h = 0; h <= NEIGH_HASHMASK; h++) {
s_idx = idx = cb->args[2]; if (h < s_h)
for (h=0; h <= NEIGH_HASHMASK; h++) { continue;
if (h < s_h) continue;
if (h > s_h) if (h > s_h)
s_idx = 0; s_idx = 0;
read_lock_bh(&tbl->lock); read_lock_bh(&tbl->lock);
for (n = tbl->hash_buckets[h], idx = 0; n; for (n = tbl->hash_buckets[h], idx = 0; n; n = n->next, idx++) {
n = n->next, idx++) {
if (idx < s_idx) if (idx < s_idx)
continue; continue;
if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid, if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) { cb->nlh->nlmsg_seq,
RTM_NEWNEIGH) <= 0) {
read_unlock_bh(&tbl->lock); read_unlock_bh(&tbl->lock);
cb->args[1] = h; rc = -1;
cb->args[2] = idx; goto out;
return -1;
} }
} }
read_unlock_bh(&tbl->lock); read_unlock_bh(&tbl->lock);
} }
rc = skb->len;
out:
cb->args[1] = h; cb->args[1] = h;
cb->args[2] = idx; cb->args[2] = idx;
return skb->len; return rc;
} }
int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
{ {
int t;
int s_t;
struct neigh_table *tbl; struct neigh_table *tbl;
int family = ((struct rtgenmsg*)NLMSG_DATA(cb->nlh))->rtgen_family; int t, family, s_t;
read_lock(&neigh_tbl_lock);
family = ((struct rtgenmsg *)NLMSG_DATA(cb->nlh))->rtgen_family;
s_t = cb->args[0]; s_t = cb->args[0];
read_lock(&neigh_tbl_lock); for (tbl = neigh_tables, t = 0; tbl; tbl = tbl->next, t++) {
for (tbl=neigh_tables, t=0; tbl; tbl = tbl->next, t++) { if (t < s_t || (family && tbl->family != family))
if (t < s_t) continue;
if (family && tbl->family != family)
continue; continue;
if (t > s_t) if (t > s_t)
memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); memset(&cb->args[1], 0, sizeof(cb->args) -
if (neigh_dump_table(tbl, skb, cb) < 0) sizeof(cb->args[0]));
if (neigh_dump_table(tbl, skb, cb) < 0)
break; break;
} }
read_unlock(&neigh_tbl_lock); read_unlock(&neigh_tbl_lock);
cb->args[0] = t; cb->args[0] = t;
return skb->len; return skb->len;
} }
#ifdef CONFIG_ARPD #ifdef CONFIG_ARPD
void neigh_app_ns(struct neighbour *n) void neigh_app_ns(struct neighbour *n)
{ {
struct sk_buff *skb;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
int size = NLMSG_SPACE(sizeof(struct ndmsg)+256); int size = NLMSG_SPACE(sizeof(struct ndmsg) + 256);
struct sk_buff *skb = alloc_skb(size, GFP_ATOMIC);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) if (!skb)
return; return;
...@@ -1401,19 +1435,18 @@ void neigh_app_ns(struct neighbour *n) ...@@ -1401,19 +1435,18 @@ void neigh_app_ns(struct neighbour *n)
kfree_skb(skb); kfree_skb(skb);
return; return;
} }
nlh = (struct nlmsghdr*)skb->data; nlh = (struct nlmsghdr *)skb->data;
nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_flags = NLM_F_REQUEST;
NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH; NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC); netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
} }
static void neigh_app_notify(struct neighbour *n) static void neigh_app_notify(struct neighbour *n)
{ {
struct sk_buff *skb; struct nlmsghdr *nlh;
struct nlmsghdr *nlh; int size = NLMSG_SPACE(sizeof(struct ndmsg) + 256);
int size = NLMSG_SPACE(sizeof(struct ndmsg)+256); struct sk_buff *skb = alloc_skb(size, GFP_ATOMIC);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) if (!skb)
return; return;
...@@ -1421,7 +1454,7 @@ static void neigh_app_notify(struct neighbour *n) ...@@ -1421,7 +1454,7 @@ static void neigh_app_notify(struct neighbour *n)
kfree_skb(skb); kfree_skb(skb);
return; return;
} }
nlh = (struct nlmsghdr*)skb->data; nlh = (struct nlmsghdr *)skb->data;
NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH; NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC); netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
} }
...@@ -1430,91 +1463,173 @@ static void neigh_app_notify(struct neighbour *n) ...@@ -1430,91 +1463,173 @@ static void neigh_app_notify(struct neighbour *n)
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
struct neigh_sysctl_table struct neigh_sysctl_table {
{
struct ctl_table_header *sysctl_header; struct ctl_table_header *sysctl_header;
ctl_table neigh_vars[17]; ctl_table neigh_vars[17];
ctl_table neigh_dev[2]; ctl_table neigh_dev[2];
ctl_table neigh_neigh_dir[2]; ctl_table neigh_neigh_dir[2];
ctl_table neigh_proto_dir[2]; ctl_table neigh_proto_dir[2];
ctl_table neigh_root_dir[2]; ctl_table neigh_root_dir[2];
} neigh_sysctl_template = { } neigh_sysctl_template = {
NULL, neigh_vars: {
{{NET_NEIGH_MCAST_SOLICIT, "mcast_solicit", {
NULL, sizeof(int), 0644, NULL, ctl_name: NET_NEIGH_MCAST_SOLICIT,
&proc_dointvec}, procname: "mcast_solicit",
{NET_NEIGH_UCAST_SOLICIT, "ucast_solicit", maxlen: sizeof(int),
NULL, sizeof(int), 0644, NULL, mode: 0644,
&proc_dointvec}, proc_handler: &proc_dointvec,
{NET_NEIGH_APP_SOLICIT, "app_solicit", },
NULL, sizeof(int), 0644, NULL, {
&proc_dointvec}, ctl_name: NET_NEIGH_UCAST_SOLICIT,
{NET_NEIGH_RETRANS_TIME, "retrans_time", procname: "ucast_solicit",
NULL, sizeof(int), 0644, NULL, maxlen: sizeof(int),
&proc_dointvec}, mode: 0644,
{NET_NEIGH_REACHABLE_TIME, "base_reachable_time", proc_handler: &proc_dointvec,
NULL, sizeof(int), 0644, NULL, },
&proc_dointvec_jiffies}, {
{NET_NEIGH_DELAY_PROBE_TIME, "delay_first_probe_time", ctl_name: NET_NEIGH_APP_SOLICIT,
NULL, sizeof(int), 0644, NULL, procname: "app_solicit",
&proc_dointvec_jiffies}, maxlen: sizeof(int),
{NET_NEIGH_GC_STALE_TIME, "gc_stale_time", mode: 0644,
NULL, sizeof(int), 0644, NULL, proc_handler: &proc_dointvec,
&proc_dointvec_jiffies}, },
{NET_NEIGH_UNRES_QLEN, "unres_qlen", {
NULL, sizeof(int), 0644, NULL, ctl_name: NET_NEIGH_RETRANS_TIME,
&proc_dointvec}, procname: "retrans_time",
{NET_NEIGH_PROXY_QLEN, "proxy_qlen", maxlen: sizeof(int),
NULL, sizeof(int), 0644, NULL, mode: 0644,
&proc_dointvec}, proc_handler: &proc_dointvec,
{NET_NEIGH_ANYCAST_DELAY, "anycast_delay", },
NULL, sizeof(int), 0644, NULL, {
&proc_dointvec}, ctl_name: NET_NEIGH_REACHABLE_TIME,
{NET_NEIGH_PROXY_DELAY, "proxy_delay", procname: "base_reachable_time",
NULL, sizeof(int), 0644, NULL, maxlen: sizeof(int),
&proc_dointvec}, mode: 0644,
{NET_NEIGH_LOCKTIME, "locktime", proc_handler: &proc_dointvec_jiffies,
NULL, sizeof(int), 0644, NULL, },
&proc_dointvec}, {
{NET_NEIGH_GC_INTERVAL, "gc_interval", ctl_name: NET_NEIGH_DELAY_PROBE_TIME,
NULL, sizeof(int), 0644, NULL, procname: "delay_first_probe_time",
&proc_dointvec_jiffies}, maxlen: sizeof(int),
{NET_NEIGH_GC_THRESH1, "gc_thresh1", mode: 0644,
NULL, sizeof(int), 0644, NULL, proc_handler: &proc_dointvec_jiffies,
&proc_dointvec}, },
{NET_NEIGH_GC_THRESH2, "gc_thresh2", {
NULL, sizeof(int), 0644, NULL, ctl_name: NET_NEIGH_GC_STALE_TIME,
&proc_dointvec}, procname: "gc_stale_time",
{NET_NEIGH_GC_THRESH3, "gc_thresh3", maxlen: sizeof(int),
NULL, sizeof(int), 0644, NULL, mode: 0644,
&proc_dointvec}, proc_handler: &proc_dointvec_jiffies,
{0}}, },
{
{{NET_PROTO_CONF_DEFAULT, "default", NULL, 0, 0555, NULL},{0}}, ctl_name: NET_NEIGH_UNRES_QLEN,
{{0, "neigh", NULL, 0, 0555, NULL},{0}}, procname: "unres_qlen",
{{0, NULL, NULL, 0, 0555, NULL},{0}}, maxlen: sizeof(int),
{{CTL_NET, "net", NULL, 0, 0555, NULL},{0}} mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_PROXY_QLEN,
procname: "proxy_qlen",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_ANYCAST_DELAY,
procname: "anycast_delay",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_PROXY_DELAY,
procname: "proxy_delay",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_LOCKTIME,
procname: "locktime",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_GC_INTERVAL,
procname: "gc_interval",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec_jiffies,
},
{
ctl_name: NET_NEIGH_GC_THRESH1,
procname: "gc_thresh1",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_GC_THRESH2,
procname: "gc_thresh2",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
{
ctl_name: NET_NEIGH_GC_THRESH3,
procname: "gc_thresh3",
maxlen: sizeof(int),
mode: 0644,
proc_handler: &proc_dointvec,
},
},
neigh_dev: {
{
ctl_name: NET_PROTO_CONF_DEFAULT,
procname: "default",
mode: 0555,
},
},
neigh_neigh_dir: {
{
procname: "neigh",
mode: 0555,
},
},
neigh_proto_dir: {
{
mode: 0555,
},
},
neigh_root_dir: {
{
ctl_name: CTL_NET,
procname: "net",
mode: 0555,
},
},
}; };
int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
int p_id, int pdev_id, char *p_name) int p_id, int pdev_id, char *p_name)
{ {
struct neigh_sysctl_table *t; struct neigh_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
t = kmalloc(sizeof(*t), GFP_KERNEL); if (!t)
if (t == NULL)
return -ENOBUFS; return -ENOBUFS;
memcpy(t, &neigh_sysctl_template, sizeof(*t)); memcpy(t, &neigh_sysctl_template, sizeof(*t));
t->neigh_vars[0].data = &p->mcast_probes; t->neigh_vars[0].data = &p->mcast_probes;
t->neigh_vars[1].data = &p->ucast_probes; t->neigh_vars[1].data = &p->ucast_probes;
t->neigh_vars[2].data = &p->app_probes; t->neigh_vars[2].data = &p->app_probes;
t->neigh_vars[3].data = &p->retrans_time; t->neigh_vars[3].data = &p->retrans_time;
t->neigh_vars[4].data = &p->base_reachable_time; t->neigh_vars[4].data = &p->base_reachable_time;
t->neigh_vars[5].data = &p->delay_probe_time; t->neigh_vars[5].data = &p->delay_probe_time;
t->neigh_vars[6].data = &p->gc_staletime; t->neigh_vars[6].data = &p->gc_staletime;
t->neigh_vars[7].data = &p->queue_len; t->neigh_vars[7].data = &p->queue_len;
t->neigh_vars[8].data = &p->proxy_qlen; t->neigh_vars[8].data = &p->proxy_qlen;
t->neigh_vars[9].data = &p->anycast_delay; t->neigh_vars[9].data = &p->anycast_delay;
t->neigh_vars[10].data = &p->proxy_delay; t->neigh_vars[10].data = &p->proxy_delay;
t->neigh_vars[11].data = &p->locktime; t->neigh_vars[11].data = &p->locktime;
if (dev) { if (dev) {
...@@ -1522,23 +1637,23 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, ...@@ -1522,23 +1637,23 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
t->neigh_dev[0].ctl_name = dev->ifindex; t->neigh_dev[0].ctl_name = dev->ifindex;
memset(&t->neigh_vars[12], 0, sizeof(ctl_table)); memset(&t->neigh_vars[12], 0, sizeof(ctl_table));
} else { } else {
t->neigh_vars[12].data = (int*)(p+1); t->neigh_vars[12].data = (int *)(p + 1);
t->neigh_vars[13].data = (int*)(p+1) + 1; t->neigh_vars[13].data = (int *)(p + 1) + 1;
t->neigh_vars[14].data = (int*)(p+1) + 2; t->neigh_vars[14].data = (int *)(p + 1) + 2;
t->neigh_vars[15].data = (int*)(p+1) + 3; t->neigh_vars[15].data = (int *)(p + 1) + 3;
} }
t->neigh_neigh_dir[0].ctl_name = pdev_id; t->neigh_neigh_dir[0].ctl_name = pdev_id;
t->neigh_proto_dir[0].procname = p_name; t->neigh_proto_dir[0].procname = p_name;
t->neigh_proto_dir[0].ctl_name = p_id; t->neigh_proto_dir[0].ctl_name = p_id;
t->neigh_dev[0].child = t->neigh_vars; t->neigh_dev[0].child = t->neigh_vars;
t->neigh_neigh_dir[0].child = t->neigh_dev; t->neigh_neigh_dir[0].child = t->neigh_dev;
t->neigh_proto_dir[0].child = t->neigh_neigh_dir; t->neigh_proto_dir[0].child = t->neigh_neigh_dir;
t->neigh_root_dir[0].child = t->neigh_proto_dir; t->neigh_root_dir[0].child = t->neigh_proto_dir;
t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0); t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0);
if (t->sysctl_header == NULL) { if (!t->sysctl_header) {
kfree(t); kfree(t);
return -ENOBUFS; return -ENOBUFS;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment