diff options
Diffstat (limited to 'net/core/neighbour.c')
| -rw-r--r-- | net/core/neighbour.c | 502 | 
1 files changed, 293 insertions, 209 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 6072610a867..ef31fef25e5 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -38,6 +38,8 @@  #include <linux/random.h>  #include <linux/string.h>  #include <linux/log2.h> +#include <linux/inetdevice.h> +#include <net/addrconf.h>  #define DEBUG  #define NEIGH_DEBUG 1 @@ -115,7 +117,7 @@ static void neigh_cleanup_and_release(struct neighbour *neigh)  unsigned long neigh_rand_reach_time(unsigned long base)  { -	return base ? (net_random() % base) + (base >> 1) : 0; +	return base ? (prandom_u32() % base) + (base >> 1) : 0;  }  EXPORT_SYMBOL(neigh_rand_reach_time); @@ -497,7 +499,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,  		goto out_neigh_release;  	} -	n->confirmed = jiffies - (n->parms->base_reachable_time << 1); +	n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1);  	write_lock_bh(&tbl->lock);  	nht = rcu_dereference_protected(tbl->nht, @@ -764,9 +766,6 @@ static void neigh_periodic_work(struct work_struct *work)  	nht = rcu_dereference_protected(tbl->nht,  					lockdep_is_held(&tbl->lock)); -	if (atomic_read(&tbl->entries) < tbl->gc_thresh1) -		goto out; -  	/*  	 *	periodically recompute ReachableTime from random function  	 */ @@ -776,9 +775,12 @@ static void neigh_periodic_work(struct work_struct *work)  		tbl->last_rand = jiffies;  		for (p = &tbl->parms; p; p = p->next)  			p->reachable_time = -				neigh_rand_reach_time(p->base_reachable_time); +				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));  	} +	if (atomic_read(&tbl->entries) < tbl->gc_thresh1) +		goto out; +  	for (i = 0 ; i < (1 << nht->hash_shift); i++) {  		np = &nht->hash_buckets[i]; @@ -799,7 +801,7 @@ static void neigh_periodic_work(struct work_struct *work)  			if (atomic_read(&n->refcnt) == 1 &&  			    (state == NUD_FAILED || -			     time_after(jiffies, n->used + n->parms->gc_staletime))) { +			     time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {  				*np = n->next;  				n->dead = 1;  				write_unlock(&n->lock); @@ -822,21 +824,22 @@ next_elt:  						lockdep_is_held(&tbl->lock));  	}  out: -	/* Cycle through all hash buckets every base_reachable_time/2 ticks. -	 * ARP entry timeouts range from 1/2 base_reachable_time to 3/2 -	 * base_reachable_time. +	/* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks. +	 * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2 +	 * BASE_REACHABLE_TIME.  	 */ -	schedule_delayed_work(&tbl->gc_work, -			      tbl->parms.base_reachable_time >> 1); +	queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, +			      NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1);  	write_unlock_bh(&tbl->lock);  }  static __inline__ int neigh_max_probes(struct neighbour *n)  {  	struct neigh_parms *p = n->parms; -	return (n->nud_state & NUD_PROBE) ? -		p->ucast_probes : -		p->ucast_probes + p->app_probes + p->mcast_probes; +	int max_probes = NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES); +	if (!(n->nud_state & NUD_PROBE)) +		max_probes += NEIGH_VAR(p, MCAST_PROBES); +	return max_probes;  }  static void neigh_invalidate(struct neighbour *neigh) @@ -867,7 +870,7 @@ static void neigh_invalidate(struct neighbour *neigh)  static void neigh_probe(struct neighbour *neigh)  	__releases(neigh->lock)  { -	struct sk_buff *skb = skb_peek(&neigh->arp_queue); +	struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue);  	/* keep skb alive even if arp_queue overflows */  	if (skb)  		skb = skb_copy(skb, GFP_ATOMIC); @@ -901,12 +904,13 @@ static void neigh_timer_handler(unsigned long arg)  			neigh_dbg(2, "neigh %p is still alive\n", neigh);  			next = neigh->confirmed + neigh->parms->reachable_time;  		} else if (time_before_eq(now, -					  neigh->used + neigh->parms->delay_probe_time)) { +					  neigh->used + +					  NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {  			neigh_dbg(2, "neigh %p is delayed\n", neigh);  			neigh->nud_state = NUD_DELAY;  			neigh->updated = jiffies;  			neigh_suspect(neigh); -			next = now + neigh->parms->delay_probe_time; +			next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME);  		} else {  			neigh_dbg(2, "neigh %p is suspected\n", neigh);  			neigh->nud_state = NUD_STALE; @@ -916,7 +920,8 @@ static void neigh_timer_handler(unsigned long arg)  		}  	} else if (state & NUD_DELAY) {  		if (time_before_eq(now, -				   neigh->confirmed + neigh->parms->delay_probe_time)) { +				   neigh->confirmed + +				   NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {  			neigh_dbg(2, "neigh %p is now reachable\n", neigh);  			neigh->nud_state = NUD_REACHABLE;  			neigh->updated = jiffies; @@ -928,11 +933,11 @@ static void neigh_timer_handler(unsigned long arg)  			neigh->nud_state = NUD_PROBE;  			neigh->updated = jiffies;  			atomic_set(&neigh->probes, 0); -			next = now + neigh->parms->retrans_time; +			next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);  		}  	} else {  		/* NUD_PROBE|NUD_INCOMPLETE */ -		next = now + neigh->parms->retrans_time; +		next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);  	}  	if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && @@ -940,6 +945,7 @@ static void neigh_timer_handler(unsigned long arg)  		neigh->nud_state = NUD_FAILED;  		notify = 1;  		neigh_invalidate(neigh); +		goto out;  	}  	if (neigh->nud_state & NUD_IN_TIMER) { @@ -973,13 +979,16 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)  		goto out_unlock_bh;  	if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { -		if (neigh->parms->mcast_probes + neigh->parms->app_probes) { +		if (NEIGH_VAR(neigh->parms, MCAST_PROBES) + +		    NEIGH_VAR(neigh->parms, APP_PROBES)) {  			unsigned long next, now = jiffies; -			atomic_set(&neigh->probes, neigh->parms->ucast_probes); +			atomic_set(&neigh->probes, +				   NEIGH_VAR(neigh->parms, UCAST_PROBES));  			neigh->nud_state     = NUD_INCOMPLETE;  			neigh->updated = now; -			next = now + max(neigh->parms->retrans_time, HZ/2); +			next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), +					 HZ/2);  			neigh_add_timer(neigh, next);  			immediate_probe = true;  		} else { @@ -994,14 +1003,14 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)  		neigh_dbg(2, "neigh %p is delayed\n", neigh);  		neigh->nud_state = NUD_DELAY;  		neigh->updated = jiffies; -		neigh_add_timer(neigh, -				jiffies + neigh->parms->delay_probe_time); +		neigh_add_timer(neigh, jiffies + +				NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME));  	}  	if (neigh->nud_state == NUD_INCOMPLETE) {  		if (skb) {  			while (neigh->arp_queue_len_bytes + skb->truesize > -			       neigh->parms->queue_len_bytes) { +			       NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) {  				struct sk_buff *buff;  				buff = __skb_dequeue(&neigh->arp_queue); @@ -1161,6 +1170,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,  						 neigh->parms->reachable_time :  						 0)));  		neigh->nud_state = new; +		notify = 1;  	}  	if (lladdr != neigh->ha) { @@ -1170,7 +1180,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,  		neigh_update_hhs(neigh);  		if (!(new & NUD_CONNECTED))  			neigh->confirmed = jiffies - -				      (neigh->parms->base_reachable_time << 1); +				      (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1);  		notify = 1;  	}  	if (new == old) @@ -1230,6 +1240,21 @@ out:  }  EXPORT_SYMBOL(neigh_update); +/* Update the neigh to listen temporarily for probe responses, even if it is + * in a NUD_FAILED state. The caller has to hold neigh->lock for writing. + */ +void __neigh_set_probe_once(struct neighbour *neigh) +{ +	neigh->updated = jiffies; +	if (!(neigh->nud_state & NUD_FAILED)) +		return; +	neigh->nud_state = NUD_INCOMPLETE; +	atomic_set(&neigh->probes, neigh_max_probes(neigh)); +	neigh_add_timer(neigh, +			jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME)); +} +EXPORT_SYMBOL(__neigh_set_probe_once); +  struct neighbour *neigh_event_ns(struct neigh_table *tbl,  				 u8 *lladdr, void *saddr,  				 struct net_device *dev) @@ -1274,7 +1299,7 @@ int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb)  	if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,  			    skb->len) < 0 && -	    dev->header_ops->rebuild(skb)) +	    dev_rebuild_header(skb))  		return 0;  	return dev_queue_xmit(skb); @@ -1391,9 +1416,11 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,  		    struct sk_buff *skb)  {  	unsigned long now = jiffies; -	unsigned long sched_next = now + (net_random() % p->proxy_delay); -	if (tbl->proxy_queue.qlen > p->proxy_qlen) { +	unsigned long sched_next = now + (prandom_u32() % +					  NEIGH_VAR(p, PROXY_DELAY)); + +	if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) {  		kfree_skb(skb);  		return;  	} @@ -1440,7 +1467,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,  		p->tbl		  = tbl;  		atomic_set(&p->refcnt, 1);  		p->reachable_time = -				neigh_rand_reach_time(p->base_reachable_time); +				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));  		dev_hold(dev);  		p->dev = dev;  		write_pnet(&p->net, hold_net(net)); @@ -1457,6 +1484,8 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,  		p->next		= tbl->parms.next;  		tbl->parms.next = p;  		write_unlock_bh(&tbl->lock); + +		neigh_parms_data_state_cleanall(p);  	}  	return p;  } @@ -1509,7 +1538,7 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl)  	write_pnet(&tbl->parms.net, &init_net);  	atomic_set(&tbl->parms.refcnt, 1);  	tbl->parms.reachable_time = -			  neigh_rand_reach_time(tbl->parms.base_reachable_time); +			  neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME));  	tbl->stats = alloc_percpu(struct neigh_statistics);  	if (!tbl->stats) @@ -1537,7 +1566,8 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl)  	rwlock_init(&tbl->lock);  	INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); -	schedule_delayed_work(&tbl->gc_work, tbl->parms.reachable_time); +	queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, +			tbl->parms.reachable_time);  	setup_timer(&tbl->proxy_timer, neigh_proxy_process, (unsigned long)tbl);  	skb_queue_head_init_class(&tbl->proxy_queue,  			&neigh_table_proxy_queue_class); @@ -1777,24 +1807,32 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)  	if ((parms->dev &&  	     nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) ||  	    nla_put_u32(skb, NDTPA_REFCNT, atomic_read(&parms->refcnt)) || -	    nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, parms->queue_len_bytes) || +	    nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, +			NEIGH_VAR(parms, QUEUE_LEN_BYTES)) ||  	    /* approximative value for deprecated QUEUE_LEN (in packets) */  	    nla_put_u32(skb, NDTPA_QUEUE_LEN, -			parms->queue_len_bytes / SKB_TRUESIZE(ETH_FRAME_LEN)) || -	    nla_put_u32(skb, NDTPA_PROXY_QLEN, parms->proxy_qlen) || -	    nla_put_u32(skb, NDTPA_APP_PROBES, parms->app_probes) || -	    nla_put_u32(skb, NDTPA_UCAST_PROBES, parms->ucast_probes) || -	    nla_put_u32(skb, NDTPA_MCAST_PROBES, parms->mcast_probes) || +			NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) || +	    nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) || +	    nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) || +	    nla_put_u32(skb, NDTPA_UCAST_PROBES, +			NEIGH_VAR(parms, UCAST_PROBES)) || +	    nla_put_u32(skb, NDTPA_MCAST_PROBES, +			NEIGH_VAR(parms, MCAST_PROBES)) ||  	    nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time) ||  	    nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, -			  parms->base_reachable_time) || -	    nla_put_msecs(skb, NDTPA_GC_STALETIME, parms->gc_staletime) || +			  NEIGH_VAR(parms, BASE_REACHABLE_TIME)) || +	    nla_put_msecs(skb, NDTPA_GC_STALETIME, +			  NEIGH_VAR(parms, GC_STALETIME)) ||  	    nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME, -			  parms->delay_probe_time) || -	    nla_put_msecs(skb, NDTPA_RETRANS_TIME, parms->retrans_time) || -	    nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, parms->anycast_delay) || -	    nla_put_msecs(skb, NDTPA_PROXY_DELAY, parms->proxy_delay) || -	    nla_put_msecs(skb, NDTPA_LOCKTIME, parms->locktime)) +			  NEIGH_VAR(parms, DELAY_PROBE_TIME)) || +	    nla_put_msecs(skb, NDTPA_RETRANS_TIME, +			  NEIGH_VAR(parms, RETRANS_TIME)) || +	    nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, +			  NEIGH_VAR(parms, ANYCAST_DELAY)) || +	    nla_put_msecs(skb, NDTPA_PROXY_DELAY, +			  NEIGH_VAR(parms, PROXY_DELAY)) || +	    nla_put_msecs(skb, NDTPA_LOCKTIME, +			  NEIGH_VAR(parms, LOCKTIME)))  		goto nla_put_failure;  	return nla_nest_end(skb, nest); @@ -2010,44 +2048,57 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)  			switch (i) {  			case NDTPA_QUEUE_LEN: -				p->queue_len_bytes = nla_get_u32(tbp[i]) * -						     SKB_TRUESIZE(ETH_FRAME_LEN); +				NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, +					      nla_get_u32(tbp[i]) * +					      SKB_TRUESIZE(ETH_FRAME_LEN));  				break;  			case NDTPA_QUEUE_LENBYTES: -				p->queue_len_bytes = nla_get_u32(tbp[i]); +				NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, +					      nla_get_u32(tbp[i]));  				break;  			case NDTPA_PROXY_QLEN: -				p->proxy_qlen = nla_get_u32(tbp[i]); +				NEIGH_VAR_SET(p, PROXY_QLEN, +					      nla_get_u32(tbp[i]));  				break;  			case NDTPA_APP_PROBES: -				p->app_probes = nla_get_u32(tbp[i]); +				NEIGH_VAR_SET(p, APP_PROBES, +					      nla_get_u32(tbp[i]));  				break;  			case NDTPA_UCAST_PROBES: -				p->ucast_probes = nla_get_u32(tbp[i]); +				NEIGH_VAR_SET(p, UCAST_PROBES, +					      nla_get_u32(tbp[i]));  				break;  			case NDTPA_MCAST_PROBES: -				p->mcast_probes = nla_get_u32(tbp[i]); +				NEIGH_VAR_SET(p, MCAST_PROBES, +					      nla_get_u32(tbp[i]));  				break;  			case NDTPA_BASE_REACHABLE_TIME: -				p->base_reachable_time = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, +					      nla_get_msecs(tbp[i]));  				break;  			case NDTPA_GC_STALETIME: -				p->gc_staletime = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, GC_STALETIME, +					      nla_get_msecs(tbp[i]));  				break;  			case NDTPA_DELAY_PROBE_TIME: -				p->delay_probe_time = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, DELAY_PROBE_TIME, +					      nla_get_msecs(tbp[i]));  				break;  			case NDTPA_RETRANS_TIME: -				p->retrans_time = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, RETRANS_TIME, +					      nla_get_msecs(tbp[i]));  				break;  			case NDTPA_ANYCAST_DELAY: -				p->anycast_delay = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, ANYCAST_DELAY, +					      nla_get_msecs(tbp[i]));  				break;  			case NDTPA_PROXY_DELAY: -				p->proxy_delay = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, PROXY_DELAY, +					      nla_get_msecs(tbp[i]));  				break;  			case NDTPA_LOCKTIME: -				p->locktime = nla_get_msecs(tbp[i]); +				NEIGH_VAR_SET(p, LOCKTIME, +					      nla_get_msecs(tbp[i]));  				break;  			}  		} @@ -2198,7 +2249,7 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,  	ndm->ndm_pad1    = 0;  	ndm->ndm_pad2    = 0;  	ndm->ndm_flags	 = pn->flags | NTF_PROXY; -	ndm->ndm_type	 = NDA_DST; +	ndm->ndm_type	 = RTN_UNICAST;  	ndm->ndm_ifindex = pn->dev->ifindex;  	ndm->ndm_state	 = NUD_NONE; @@ -2788,133 +2839,167 @@ static int proc_unres_qlen(struct ctl_table *ctl, int write,  	return ret;  } -enum { -	NEIGH_VAR_MCAST_PROBE, -	NEIGH_VAR_UCAST_PROBE, -	NEIGH_VAR_APP_PROBE, -	NEIGH_VAR_RETRANS_TIME, -	NEIGH_VAR_BASE_REACHABLE_TIME, -	NEIGH_VAR_DELAY_PROBE_TIME, -	NEIGH_VAR_GC_STALETIME, -	NEIGH_VAR_QUEUE_LEN, -	NEIGH_VAR_QUEUE_LEN_BYTES, -	NEIGH_VAR_PROXY_QLEN, -	NEIGH_VAR_ANYCAST_DELAY, -	NEIGH_VAR_PROXY_DELAY, -	NEIGH_VAR_LOCKTIME, -	NEIGH_VAR_RETRANS_TIME_MS, -	NEIGH_VAR_BASE_REACHABLE_TIME_MS, -	NEIGH_VAR_GC_INTERVAL, -	NEIGH_VAR_GC_THRESH1, -	NEIGH_VAR_GC_THRESH2, -	NEIGH_VAR_GC_THRESH3, -	NEIGH_VAR_MAX -}; +static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, +						   int family) +{ +	switch (family) { +	case AF_INET: +		return __in_dev_arp_parms_get_rcu(dev); +	case AF_INET6: +		return __in6_dev_nd_parms_get_rcu(dev); +	} +	return NULL; +} + +static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, +				  int index) +{ +	struct net_device *dev; +	int family = neigh_parms_family(p); + +	rcu_read_lock(); +	for_each_netdev_rcu(net, dev) { +		struct neigh_parms *dst_p = +				neigh_get_dev_parms_rcu(dev, family); + +		if (dst_p && !test_bit(index, dst_p->data_state)) +			dst_p->data[index] = p->data[index]; +	} +	rcu_read_unlock(); +} + +static void neigh_proc_update(struct ctl_table *ctl, int write) +{ +	struct net_device *dev = ctl->extra1; +	struct neigh_parms *p = ctl->extra2; +	struct net *net = neigh_parms_net(p); +	int index = (int *) ctl->data - p->data; + +	if (!write) +		return; + +	set_bit(index, p->data_state); +	if (!dev) /* NULL dev means this is default value */ +		neigh_copy_dflt_parms(net, p, index); +} + +static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, +					   void __user *buffer, +					   size_t *lenp, loff_t *ppos) +{ +	struct ctl_table tmp = *ctl; +	int ret; + +	tmp.extra1 = &zero; +	tmp.extra2 = &int_max; + +	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); +	neigh_proc_update(ctl, write); +	return ret; +} + +int neigh_proc_dointvec(struct ctl_table *ctl, int write, +			void __user *buffer, size_t *lenp, loff_t *ppos) +{ +	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + +	neigh_proc_update(ctl, write); +	return ret; +} +EXPORT_SYMBOL(neigh_proc_dointvec); + +int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, +				void __user *buffer, +				size_t *lenp, loff_t *ppos) +{ +	int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); + +	neigh_proc_update(ctl, write); +	return ret; +} +EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); + +static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, +					      void __user *buffer, +					      size_t *lenp, loff_t *ppos) +{ +	int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); + +	neigh_proc_update(ctl, write); +	return ret; +} + +int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, +				   void __user *buffer, +				   size_t *lenp, loff_t *ppos) +{ +	int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); + +	neigh_proc_update(ctl, write); +	return ret; +} +EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); + +static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, +					  void __user *buffer, +					  size_t *lenp, loff_t *ppos) +{ +	int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); + +	neigh_proc_update(ctl, write); +	return ret; +} + +#define NEIGH_PARMS_DATA_OFFSET(index)	\ +	(&((struct neigh_parms *) 0)->data[index]) + +#define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \ +	[NEIGH_VAR_ ## attr] = { \ +		.procname	= name, \ +		.data		= NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \ +		.maxlen		= sizeof(int), \ +		.mode		= mval, \ +		.proc_handler	= proc, \ +	} + +#define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \ +	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) + +#define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ +	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies) + +#define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ +	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) + +#define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \ +	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies) + +#define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ +	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) + +#define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ +	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen)  static struct neigh_sysctl_table {  	struct ctl_table_header *sysctl_header;  	struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1];  } neigh_sysctl_template __read_mostly = {  	.neigh_vars = { -		[NEIGH_VAR_MCAST_PROBE] = { -			.procname	= "mcast_solicit", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.extra1 	= &zero, -			.extra2		= &int_max, -			.proc_handler	= proc_dointvec_minmax, -		}, -		[NEIGH_VAR_UCAST_PROBE] = { -			.procname	= "ucast_solicit", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.extra1 	= &zero, -			.extra2		= &int_max, -			.proc_handler	= proc_dointvec_minmax, -		}, -		[NEIGH_VAR_APP_PROBE] = { -			.procname	= "app_solicit", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.extra1 	= &zero, -			.extra2		= &int_max, -			.proc_handler	= proc_dointvec_minmax, -		}, -		[NEIGH_VAR_RETRANS_TIME] = { -			.procname	= "retrans_time", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_userhz_jiffies, -		}, -		[NEIGH_VAR_BASE_REACHABLE_TIME] = { -			.procname	= "base_reachable_time", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_jiffies, -		}, -		[NEIGH_VAR_DELAY_PROBE_TIME] = { -			.procname	= "delay_first_probe_time", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_jiffies, -		}, -		[NEIGH_VAR_GC_STALETIME] = { -			.procname	= "gc_stale_time", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_jiffies, -		}, -		[NEIGH_VAR_QUEUE_LEN] = { -			.procname	= "unres_qlen", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_unres_qlen, -		}, -		[NEIGH_VAR_QUEUE_LEN_BYTES] = { -			.procname	= "unres_qlen_bytes", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.extra1		= &zero, -			.proc_handler   = proc_dointvec_minmax, -		}, -		[NEIGH_VAR_PROXY_QLEN] = { -			.procname	= "proxy_qlen", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.extra1 	= &zero, -			.extra2		= &int_max, -			.proc_handler	= proc_dointvec_minmax, -		}, -		[NEIGH_VAR_ANYCAST_DELAY] = { -			.procname	= "anycast_delay", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_userhz_jiffies, -		}, -		[NEIGH_VAR_PROXY_DELAY] = { -			.procname	= "proxy_delay", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_userhz_jiffies, -		}, -		[NEIGH_VAR_LOCKTIME] = { -			.procname	= "locktime", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_userhz_jiffies, -		}, -		[NEIGH_VAR_RETRANS_TIME_MS] = { -			.procname	= "retrans_time_ms", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_ms_jiffies, -		}, -		[NEIGH_VAR_BASE_REACHABLE_TIME_MS] = { -			.procname	= "base_reachable_time_ms", -			.maxlen		= sizeof(int), -			.mode		= 0644, -			.proc_handler	= proc_dointvec_ms_jiffies, -		}, +		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), +		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), +		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), +		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), +		NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), +		NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), +		NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"), +		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"), +		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"), +		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"), +		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"), +		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"), +		NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"), +		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"), +		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"),  		[NEIGH_VAR_GC_INTERVAL] = {  			.procname	= "gc_interval",  			.maxlen		= sizeof(int), @@ -2950,31 +3035,23 @@ static struct neigh_sysctl_table {  };  int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, -			  char *p_name, proc_handler *handler) +			  proc_handler *handler)  { +	int i;  	struct neigh_sysctl_table *t; -	const char *dev_name_source = NULL; +	const char *dev_name_source;  	char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; +	char *p_name;  	t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL);  	if (!t)  		goto err; -	t->neigh_vars[NEIGH_VAR_MCAST_PROBE].data  = &p->mcast_probes; -	t->neigh_vars[NEIGH_VAR_UCAST_PROBE].data  = &p->ucast_probes; -	t->neigh_vars[NEIGH_VAR_APP_PROBE].data  = &p->app_probes; -	t->neigh_vars[NEIGH_VAR_RETRANS_TIME].data  = &p->retrans_time; -	t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].data  = &p->base_reachable_time; -	t->neigh_vars[NEIGH_VAR_DELAY_PROBE_TIME].data  = &p->delay_probe_time; -	t->neigh_vars[NEIGH_VAR_GC_STALETIME].data  = &p->gc_staletime; -	t->neigh_vars[NEIGH_VAR_QUEUE_LEN].data  = &p->queue_len_bytes; -	t->neigh_vars[NEIGH_VAR_QUEUE_LEN_BYTES].data  = &p->queue_len_bytes; -	t->neigh_vars[NEIGH_VAR_PROXY_QLEN].data  = &p->proxy_qlen; -	t->neigh_vars[NEIGH_VAR_ANYCAST_DELAY].data  = &p->anycast_delay; -	t->neigh_vars[NEIGH_VAR_PROXY_DELAY].data = &p->proxy_delay; -	t->neigh_vars[NEIGH_VAR_LOCKTIME].data = &p->locktime; -	t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].data  = &p->retrans_time; -	t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].data  = &p->base_reachable_time; +	for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) { +		t->neigh_vars[i].data += (long) p; +		t->neigh_vars[i].extra1 = dev; +		t->neigh_vars[i].extra2 = p; +	}  	if (dev) {  		dev_name_source = dev->name; @@ -2982,33 +3059,40 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,  		memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0,  		       sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL]));  	} else { +		struct neigh_table *tbl = p->tbl;  		dev_name_source = "default"; -		t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = (int *)(p + 1); -		t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = (int *)(p + 1) + 1; -		t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = (int *)(p + 1) + 2; -		t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = (int *)(p + 1) + 3; +		t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval; +		t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1; +		t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2; +		t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3;  	} -  	if (handler) {  		/* RetransTime */  		t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; -		t->neigh_vars[NEIGH_VAR_RETRANS_TIME].extra1 = dev;  		/* ReachableTime */  		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler; -		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].extra1 = dev;  		/* RetransTime (in milliseconds)*/  		t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler; -		t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].extra1 = dev;  		/* ReachableTime (in milliseconds) */  		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler; -		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].extra1 = dev;  	}  	/* Don't export sysctls to unprivileged users */  	if (neigh_parms_net(p)->user_ns != &init_user_ns)  		t->neigh_vars[0].procname = NULL; +	switch (neigh_parms_family(p)) { +	case AF_INET: +	      p_name = "ipv4"; +	      break; +	case AF_INET6: +	      p_name = "ipv6"; +	      break; +	default: +	      BUG(); +	} +  	snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s",  		p_name, dev_name_source);  	t->sysctl_header =  | 
