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