xref: /openbmc/linux/net/core/neighbour.c (revision cdd8512a)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	Generic address resolution entity
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
71da177e4SLinus Torvalds  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Fixes:
101da177e4SLinus Torvalds  *	Vitaly E. Lavrov	releasing NULL neighbor in neigh_add.
111da177e4SLinus Torvalds  *	Harald Welte		Add neighbour cache statistics like rtstat
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
14e005d193SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15e005d193SJoe Perches 
165a0e3ad6STejun Heo #include <linux/slab.h>
1785704cb8SKonstantin Khlebnikov #include <linux/kmemleak.h>
181da177e4SLinus Torvalds #include <linux/types.h>
191da177e4SLinus Torvalds #include <linux/kernel.h>
201da177e4SLinus Torvalds #include <linux/module.h>
211da177e4SLinus Torvalds #include <linux/socket.h>
221da177e4SLinus Torvalds #include <linux/netdevice.h>
231da177e4SLinus Torvalds #include <linux/proc_fs.h>
241da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
251da177e4SLinus Torvalds #include <linux/sysctl.h>
261da177e4SLinus Torvalds #endif
271da177e4SLinus Torvalds #include <linux/times.h>
28457c4cbcSEric W. Biederman #include <net/net_namespace.h>
291da177e4SLinus Torvalds #include <net/neighbour.h>
304b2a2bfeSDavid Ahern #include <net/arp.h>
311da177e4SLinus Torvalds #include <net/dst.h>
321da177e4SLinus Torvalds #include <net/sock.h>
338d71740cSTom Tucker #include <net/netevent.h>
34a14a49d2SThomas Graf #include <net/netlink.h>
351da177e4SLinus Torvalds #include <linux/rtnetlink.h>
361da177e4SLinus Torvalds #include <linux/random.h>
37543537bdSPaulo Marques #include <linux/string.h>
38c3609d51Svignesh babu #include <linux/log2.h>
391d4c8c29SJiri Pirko #include <linux/inetdevice.h>
40bba24896SJiri Pirko #include <net/addrconf.h>
411da177e4SLinus Torvalds 
4256dd18a4SRoopa Prabhu #include <trace/events/neigh.h>
4356dd18a4SRoopa Prabhu 
441da177e4SLinus Torvalds #define NEIGH_DEBUG 1
45d5d427cdSJoe Perches #define neigh_dbg(level, fmt, ...)		\
46d5d427cdSJoe Perches do {						\
47d5d427cdSJoe Perches 	if (level <= NEIGH_DEBUG)		\
48d5d427cdSJoe Perches 		pr_debug(fmt, ##__VA_ARGS__);	\
49d5d427cdSJoe Perches } while (0)
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #define PNEIGH_HASHMASK		0xF
521da177e4SLinus Torvalds 
53e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t);
547b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags,
557b8f7a40SRoopa Prabhu 			   u32 pid);
567b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid);
5753b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
5853b76cdfSWolfgang Bumiller 				    struct net_device *dev);
591da177e4SLinus Torvalds 
6045fc3b11SAmos Waterland #ifdef CONFIG_PROC_FS
6171a5053aSChristoph Hellwig static const struct seq_operations neigh_stat_seq_ops;
6245fc3b11SAmos Waterland #endif
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /*
651da177e4SLinus Torvalds    Neighbour hash table buckets are protected with rwlock tbl->lock.
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds    - All the scans/updates to hash buckets MUST be made under this lock.
681da177e4SLinus Torvalds    - NOTHING clever should be made under this lock: no callbacks
691da177e4SLinus Torvalds      to protocol backends, no attempts to send something to network.
701da177e4SLinus Torvalds      It will result in deadlocks, if backend/driver wants to use neighbour
711da177e4SLinus Torvalds      cache.
721da177e4SLinus Torvalds    - If the entry requires some non-trivial actions, increase
731da177e4SLinus Torvalds      its reference count and release table lock.
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds    Neighbour entries are protected:
761da177e4SLinus Torvalds    - with reference count.
771da177e4SLinus Torvalds    - with rwlock neigh->lock
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds    Reference count prevents destruction.
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds    neigh->lock mainly serializes ll address data and its validity state.
821da177e4SLinus Torvalds    However, the same lock is used to protect another entry fields:
831da177e4SLinus Torvalds     - timer
841da177e4SLinus Torvalds     - resolution queue
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds    Again, nothing clever shall be made under neigh->lock,
871da177e4SLinus Torvalds    the most complicated procedure, which we allow is dev->hard_header.
881da177e4SLinus Torvalds    It is supposed, that dev->hard_header is simplistic and does
891da177e4SLinus Torvalds    not make callbacks to neighbour tables.
901da177e4SLinus Torvalds  */
911da177e4SLinus Torvalds 
neigh_blackhole(struct neighbour * neigh,struct sk_buff * skb)928f40b161SDavid S. Miller static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	kfree_skb(skb);
951da177e4SLinus Torvalds 	return -ENETDOWN;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
neigh_cleanup_and_release(struct neighbour * neigh)984f494554SThomas Graf static void neigh_cleanup_and_release(struct neighbour *neigh)
994f494554SThomas Graf {
10056dd18a4SRoopa Prabhu 	trace_neigh_cleanup_and_release(neigh, 0);
1017b8f7a40SRoopa Prabhu 	__neigh_notify(neigh, RTM_DELNEIGH, 0, 0);
10253f800e3SIdo Schimmel 	call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
1034f494554SThomas Graf 	neigh_release(neigh);
1044f494554SThomas Graf }
1054f494554SThomas Graf 
1061da177e4SLinus Torvalds /*
1071da177e4SLinus Torvalds  * It is random distribution in the interval (1/2)*base...(3/2)*base.
1081da177e4SLinus Torvalds  * It corresponds to default IPv6 settings and is not overridable,
1091da177e4SLinus Torvalds  * because it is really reasonable choice.
1101da177e4SLinus Torvalds  */
1111da177e4SLinus Torvalds 
neigh_rand_reach_time(unsigned long base)1121da177e4SLinus Torvalds unsigned long neigh_rand_reach_time(unsigned long base)
1131da177e4SLinus Torvalds {
1148032bf12SJason A. Donenfeld 	return base ? get_random_u32_below(base) + (base >> 1) : 0;
1151da177e4SLinus Torvalds }
1160a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_rand_reach_time);
1171da177e4SLinus Torvalds 
neigh_mark_dead(struct neighbour * n)11858956317SDavid Ahern static void neigh_mark_dead(struct neighbour *n)
11958956317SDavid Ahern {
12058956317SDavid Ahern 	n->dead = 1;
12158956317SDavid Ahern 	if (!list_empty(&n->gc_list)) {
12258956317SDavid Ahern 		list_del_init(&n->gc_list);
12358956317SDavid Ahern 		atomic_dec(&n->tbl->gc_entries);
12458956317SDavid Ahern 	}
1257482e384SDaniel Borkmann 	if (!list_empty(&n->managed_list))
1267482e384SDaniel Borkmann 		list_del_init(&n->managed_list);
12758956317SDavid Ahern }
12858956317SDavid Ahern 
neigh_update_gc_list(struct neighbour * n)1299c29a2f5SDavid Ahern static void neigh_update_gc_list(struct neighbour *n)
13058956317SDavid Ahern {
131e997f8a2SDavid Ahern 	bool on_gc_list, exempt_from_gc;
13258956317SDavid Ahern 
1339c29a2f5SDavid Ahern 	write_lock_bh(&n->tbl->lock);
1349c29a2f5SDavid Ahern 	write_lock(&n->lock);
135eefb45eeSChinmay Agarwal 	if (n->dead)
136eefb45eeSChinmay Agarwal 		goto out;
137eefb45eeSChinmay Agarwal 
138e997f8a2SDavid Ahern 	/* remove from the gc list if new state is permanent or if neighbor
139e997f8a2SDavid Ahern 	 * is externally learned; otherwise entry should be on the gc list
14058956317SDavid Ahern 	 */
141e997f8a2SDavid Ahern 	exempt_from_gc = n->nud_state & NUD_PERMANENT ||
142e997f8a2SDavid Ahern 			 n->flags & NTF_EXT_LEARNED;
1439c29a2f5SDavid Ahern 	on_gc_list = !list_empty(&n->gc_list);
1448cc196d6SDavid Ahern 
145e997f8a2SDavid Ahern 	if (exempt_from_gc && on_gc_list) {
1469c29a2f5SDavid Ahern 		list_del_init(&n->gc_list);
14758956317SDavid Ahern 		atomic_dec(&n->tbl->gc_entries);
148e997f8a2SDavid Ahern 	} else if (!exempt_from_gc && !on_gc_list) {
14958956317SDavid Ahern 		/* add entries to the tail; cleaning removes from the front */
15058956317SDavid Ahern 		list_add_tail(&n->gc_list, &n->tbl->gc_list);
15158956317SDavid Ahern 		atomic_inc(&n->tbl->gc_entries);
15258956317SDavid Ahern 	}
153eefb45eeSChinmay Agarwal out:
1549c29a2f5SDavid Ahern 	write_unlock(&n->lock);
1559c29a2f5SDavid Ahern 	write_unlock_bh(&n->tbl->lock);
15658956317SDavid Ahern }
1571da177e4SLinus Torvalds 
neigh_update_managed_list(struct neighbour * n)1587482e384SDaniel Borkmann static void neigh_update_managed_list(struct neighbour *n)
159526f1b58SDavid Ahern {
1607482e384SDaniel Borkmann 	bool on_managed_list, add_to_managed;
1617482e384SDaniel Borkmann 
1627482e384SDaniel Borkmann 	write_lock_bh(&n->tbl->lock);
1637482e384SDaniel Borkmann 	write_lock(&n->lock);
1647482e384SDaniel Borkmann 	if (n->dead)
1657482e384SDaniel Borkmann 		goto out;
1667482e384SDaniel Borkmann 
1677482e384SDaniel Borkmann 	add_to_managed = n->flags & NTF_MANAGED;
1687482e384SDaniel Borkmann 	on_managed_list = !list_empty(&n->managed_list);
1697482e384SDaniel Borkmann 
1707482e384SDaniel Borkmann 	if (!add_to_managed && on_managed_list)
1717482e384SDaniel Borkmann 		list_del_init(&n->managed_list);
1727482e384SDaniel Borkmann 	else if (add_to_managed && !on_managed_list)
1737482e384SDaniel Borkmann 		list_add_tail(&n->managed_list, &n->tbl->managed_list);
1747482e384SDaniel Borkmann out:
1757482e384SDaniel Borkmann 	write_unlock(&n->lock);
1767482e384SDaniel Borkmann 	write_unlock_bh(&n->tbl->lock);
1777482e384SDaniel Borkmann }
1787482e384SDaniel Borkmann 
neigh_update_flags(struct neighbour * neigh,u32 flags,int * notify,bool * gc_update,bool * managed_update)1797482e384SDaniel Borkmann static void neigh_update_flags(struct neighbour *neigh, u32 flags, int *notify,
1807482e384SDaniel Borkmann 			       bool *gc_update, bool *managed_update)
1817482e384SDaniel Borkmann {
1827482e384SDaniel Borkmann 	u32 ndm_flags, old_flags = neigh->flags;
183526f1b58SDavid Ahern 
184526f1b58SDavid Ahern 	if (!(flags & NEIGH_UPDATE_F_ADMIN))
1857482e384SDaniel Borkmann 		return;
186526f1b58SDavid Ahern 
187526f1b58SDavid Ahern 	ndm_flags  = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
1887482e384SDaniel Borkmann 	ndm_flags |= (flags & NEIGH_UPDATE_F_MANAGED) ? NTF_MANAGED : 0;
1897482e384SDaniel Borkmann 
1907482e384SDaniel Borkmann 	if ((old_flags ^ ndm_flags) & NTF_EXT_LEARNED) {
191526f1b58SDavid Ahern 		if (ndm_flags & NTF_EXT_LEARNED)
192526f1b58SDavid Ahern 			neigh->flags |= NTF_EXT_LEARNED;
193526f1b58SDavid Ahern 		else
194526f1b58SDavid Ahern 			neigh->flags &= ~NTF_EXT_LEARNED;
195526f1b58SDavid Ahern 		*notify = 1;
1967482e384SDaniel Borkmann 		*gc_update = true;
197526f1b58SDavid Ahern 	}
1987482e384SDaniel Borkmann 	if ((old_flags ^ ndm_flags) & NTF_MANAGED) {
1997482e384SDaniel Borkmann 		if (ndm_flags & NTF_MANAGED)
2007482e384SDaniel Borkmann 			neigh->flags |= NTF_MANAGED;
2017482e384SDaniel Borkmann 		else
2027482e384SDaniel Borkmann 			neigh->flags &= ~NTF_MANAGED;
2037482e384SDaniel Borkmann 		*notify = 1;
2047482e384SDaniel Borkmann 		*managed_update = true;
2057482e384SDaniel Borkmann 	}
206526f1b58SDavid Ahern }
207526f1b58SDavid Ahern 
neigh_del(struct neighbour * n,struct neighbour __rcu ** np,struct neigh_table * tbl)2087e6f182bSDavid Ahern static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np,
2097e6f182bSDavid Ahern 		      struct neigh_table *tbl)
2105071034eSSowmini Varadhan {
2115071034eSSowmini Varadhan 	bool retval = false;
2125071034eSSowmini Varadhan 
2135071034eSSowmini Varadhan 	write_lock(&n->lock);
2147e6f182bSDavid Ahern 	if (refcount_read(&n->refcnt) == 1) {
2155071034eSSowmini Varadhan 		struct neighbour *neigh;
2165071034eSSowmini Varadhan 
2175071034eSSowmini Varadhan 		neigh = rcu_dereference_protected(n->next,
2185071034eSSowmini Varadhan 						  lockdep_is_held(&tbl->lock));
2195071034eSSowmini Varadhan 		rcu_assign_pointer(*np, neigh);
22058956317SDavid Ahern 		neigh_mark_dead(n);
2215071034eSSowmini Varadhan 		retval = true;
2225071034eSSowmini Varadhan 	}
2235071034eSSowmini Varadhan 	write_unlock(&n->lock);
2245071034eSSowmini Varadhan 	if (retval)
2255071034eSSowmini Varadhan 		neigh_cleanup_and_release(n);
2265071034eSSowmini Varadhan 	return retval;
2275071034eSSowmini Varadhan }
2285071034eSSowmini Varadhan 
neigh_remove_one(struct neighbour * ndel,struct neigh_table * tbl)2295071034eSSowmini Varadhan bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
2305071034eSSowmini Varadhan {
2315071034eSSowmini Varadhan 	struct neigh_hash_table *nht;
2325071034eSSowmini Varadhan 	void *pkey = ndel->primary_key;
2335071034eSSowmini Varadhan 	u32 hash_val;
2345071034eSSowmini Varadhan 	struct neighbour *n;
2355071034eSSowmini Varadhan 	struct neighbour __rcu **np;
2365071034eSSowmini Varadhan 
2375071034eSSowmini Varadhan 	nht = rcu_dereference_protected(tbl->nht,
2385071034eSSowmini Varadhan 					lockdep_is_held(&tbl->lock));
2395071034eSSowmini Varadhan 	hash_val = tbl->hash(pkey, ndel->dev, nht->hash_rnd);
2405071034eSSowmini Varadhan 	hash_val = hash_val >> (32 - nht->hash_shift);
2415071034eSSowmini Varadhan 
2425071034eSSowmini Varadhan 	np = &nht->hash_buckets[hash_val];
2435071034eSSowmini Varadhan 	while ((n = rcu_dereference_protected(*np,
2445071034eSSowmini Varadhan 					      lockdep_is_held(&tbl->lock)))) {
2455071034eSSowmini Varadhan 		if (n == ndel)
2467e6f182bSDavid Ahern 			return neigh_del(n, np, tbl);
2475071034eSSowmini Varadhan 		np = &n->next;
2485071034eSSowmini Varadhan 	}
2495071034eSSowmini Varadhan 	return false;
2505071034eSSowmini Varadhan }
2515071034eSSowmini Varadhan 
neigh_forced_gc(struct neigh_table * tbl)2521da177e4SLinus Torvalds static int neigh_forced_gc(struct neigh_table *tbl)
2531da177e4SLinus Torvalds {
254a9beb7e8SEric Dumazet 	int max_clean = atomic_read(&tbl->gc_entries) -
255a9beb7e8SEric Dumazet 			READ_ONCE(tbl->gc_thresh2);
256*cdd8512aSJudy Hsiao 	u64 tmax = ktime_get_ns() + NSEC_PER_MSEC;
25758956317SDavid Ahern 	unsigned long tref = jiffies - 5 * HZ;
25858956317SDavid Ahern 	struct neighbour *n, *tmp;
2591da177e4SLinus Torvalds 	int shrunk = 0;
260*cdd8512aSJudy Hsiao 	int loop = 0;
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	write_lock_bh(&tbl->lock);
2651da177e4SLinus Torvalds 
26658956317SDavid Ahern 	list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) {
26758956317SDavid Ahern 		if (refcount_read(&n->refcnt) == 1) {
26858956317SDavid Ahern 			bool remove = false;
26958956317SDavid Ahern 
27058956317SDavid Ahern 			write_lock(&n->lock);
271758a7f0bSDavid Ahern 			if ((n->nud_state == NUD_FAILED) ||
2727a6b1ab7SDavid Ahern 			    (n->nud_state == NUD_NOARP) ||
2738cf8821eSJeff Dike 			    (tbl->is_multicast &&
2748cf8821eSJeff Dike 			     tbl->is_multicast(n->primary_key)) ||
275c1d2ecdfSJulian Anastasov 			    !time_in_range(n->updated, tref, jiffies))
27658956317SDavid Ahern 				remove = true;
27758956317SDavid Ahern 			write_unlock(&n->lock);
27858956317SDavid Ahern 
27958956317SDavid Ahern 			if (remove && neigh_remove_one(n, tbl))
28058956317SDavid Ahern 				shrunk++;
28158956317SDavid Ahern 			if (shrunk >= max_clean)
28258956317SDavid Ahern 				break;
283*cdd8512aSJudy Hsiao 			if (++loop == 16) {
284*cdd8512aSJudy Hsiao 				if (ktime_get_ns() > tmax)
285*cdd8512aSJudy Hsiao 					goto unlock;
286*cdd8512aSJudy Hsiao 				loop = 0;
287*cdd8512aSJudy Hsiao 			}
2881da177e4SLinus Torvalds 		}
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 
291a9beb7e8SEric Dumazet 	WRITE_ONCE(tbl->last_flush, jiffies);
292*cdd8512aSJudy Hsiao unlock:
2931da177e4SLinus Torvalds 	write_unlock_bh(&tbl->lock);
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	return shrunk;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
neigh_add_timer(struct neighbour * n,unsigned long when)298a43d8994SPavel Emelyanov static void neigh_add_timer(struct neighbour *n, unsigned long when)
299a43d8994SPavel Emelyanov {
300c1d2ecdfSJulian Anastasov 	/* Use safe distance from the jiffies - LONG_MAX point while timer
301c1d2ecdfSJulian Anastasov 	 * is running in DELAY/PROBE state but still show to user space
302c1d2ecdfSJulian Anastasov 	 * large times in the past.
303c1d2ecdfSJulian Anastasov 	 */
304c1d2ecdfSJulian Anastasov 	unsigned long mint = jiffies - (LONG_MAX - 86400 * HZ);
305c1d2ecdfSJulian Anastasov 
306a43d8994SPavel Emelyanov 	neigh_hold(n);
307c1d2ecdfSJulian Anastasov 	if (!time_in_range(n->confirmed, mint, jiffies))
308c1d2ecdfSJulian Anastasov 		n->confirmed = mint;
309c1d2ecdfSJulian Anastasov 	if (time_before(n->used, n->confirmed))
310c1d2ecdfSJulian Anastasov 		n->used = n->confirmed;
311a43d8994SPavel Emelyanov 	if (unlikely(mod_timer(&n->timer, when))) {
312a43d8994SPavel Emelyanov 		printk("NEIGH: BUG, double timer add, state is %x\n",
313a43d8994SPavel Emelyanov 		       n->nud_state);
314a43d8994SPavel Emelyanov 		dump_stack();
315a43d8994SPavel Emelyanov 	}
316a43d8994SPavel Emelyanov }
317a43d8994SPavel Emelyanov 
neigh_del_timer(struct neighbour * n)3181da177e4SLinus Torvalds static int neigh_del_timer(struct neighbour *n)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds 	if ((n->nud_state & NUD_IN_TIMER) &&
3211da177e4SLinus Torvalds 	    del_timer(&n->timer)) {
3221da177e4SLinus Torvalds 		neigh_release(n);
3231da177e4SLinus Torvalds 		return 1;
3241da177e4SLinus Torvalds 	}
3251da177e4SLinus Torvalds 	return 0;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
neigh_get_dev_parms_rcu(struct net_device * dev,int family)3288207f253SThomas Zeitlhofer static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev,
3298207f253SThomas Zeitlhofer 						   int family)
3308207f253SThomas Zeitlhofer {
3318207f253SThomas Zeitlhofer 	switch (family) {
3328207f253SThomas Zeitlhofer 	case AF_INET:
3338207f253SThomas Zeitlhofer 		return __in_dev_arp_parms_get_rcu(dev);
3348207f253SThomas Zeitlhofer 	case AF_INET6:
3358207f253SThomas Zeitlhofer 		return __in6_dev_nd_parms_get_rcu(dev);
3368207f253SThomas Zeitlhofer 	}
3378207f253SThomas Zeitlhofer 	return NULL;
3388207f253SThomas Zeitlhofer }
3398207f253SThomas Zeitlhofer 
neigh_parms_qlen_dec(struct net_device * dev,int family)3408207f253SThomas Zeitlhofer static void neigh_parms_qlen_dec(struct net_device *dev, int family)
3418207f253SThomas Zeitlhofer {
3428207f253SThomas Zeitlhofer 	struct neigh_parms *p;
3438207f253SThomas Zeitlhofer 
3448207f253SThomas Zeitlhofer 	rcu_read_lock();
3458207f253SThomas Zeitlhofer 	p = neigh_get_dev_parms_rcu(dev, family);
3468207f253SThomas Zeitlhofer 	if (p)
3478207f253SThomas Zeitlhofer 		p->qlen--;
3488207f253SThomas Zeitlhofer 	rcu_read_unlock();
3498207f253SThomas Zeitlhofer }
3508207f253SThomas Zeitlhofer 
pneigh_queue_purge(struct sk_buff_head * list,struct net * net,int family)3518207f253SThomas Zeitlhofer static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,
3528207f253SThomas Zeitlhofer 			       int family)
3531da177e4SLinus Torvalds {
354d5485d9dSYang Yingliang 	struct sk_buff_head tmp;
35566ba215cSDenis V. Lunev 	unsigned long flags;
3561da177e4SLinus Torvalds 	struct sk_buff *skb;
3571da177e4SLinus Torvalds 
358d5485d9dSYang Yingliang 	skb_queue_head_init(&tmp);
35966ba215cSDenis V. Lunev 	spin_lock_irqsave(&list->lock, flags);
36066ba215cSDenis V. Lunev 	skb = skb_peek(list);
36166ba215cSDenis V. Lunev 	while (skb != NULL) {
36266ba215cSDenis V. Lunev 		struct sk_buff *skb_next = skb_peek_next(skb, list);
3630ff4eb3dSAlexander Mikhalitsyn 		struct net_device *dev = skb->dev;
364d5485d9dSYang Yingliang 
3650ff4eb3dSAlexander Mikhalitsyn 		if (net == NULL || net_eq(dev_net(dev), net)) {
3668207f253SThomas Zeitlhofer 			neigh_parms_qlen_dec(dev, family);
36766ba215cSDenis V. Lunev 			__skb_unlink(skb, list);
368d5485d9dSYang Yingliang 			__skb_queue_tail(&tmp, skb);
3691da177e4SLinus Torvalds 		}
37066ba215cSDenis V. Lunev 		skb = skb_next;
37166ba215cSDenis V. Lunev 	}
37266ba215cSDenis V. Lunev 	spin_unlock_irqrestore(&list->lock, flags);
373d5485d9dSYang Yingliang 
374d5485d9dSYang Yingliang 	while ((skb = __skb_dequeue(&tmp))) {
375d5485d9dSYang Yingliang 		dev_put(skb->dev);
376d5485d9dSYang Yingliang 		kfree_skb(skb);
377d5485d9dSYang Yingliang 	}
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
neigh_flush_dev(struct neigh_table * tbl,struct net_device * dev,bool skip_perm)380859bd2efSDavid Ahern static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
381859bd2efSDavid Ahern 			    bool skip_perm)
3821da177e4SLinus Torvalds {
3831da177e4SLinus Torvalds 	int i;
384d6bf7817SEric Dumazet 	struct neigh_hash_table *nht;
3851da177e4SLinus Torvalds 
386d6bf7817SEric Dumazet 	nht = rcu_dereference_protected(tbl->nht,
387d6bf7817SEric Dumazet 					lockdep_is_held(&tbl->lock));
388d6bf7817SEric Dumazet 
389cd089336SDavid S. Miller 	for (i = 0; i < (1 << nht->hash_shift); i++) {
390767e97e1SEric Dumazet 		struct neighbour *n;
391767e97e1SEric Dumazet 		struct neighbour __rcu **np = &nht->hash_buckets[i];
3921da177e4SLinus Torvalds 
393767e97e1SEric Dumazet 		while ((n = rcu_dereference_protected(*np,
394767e97e1SEric Dumazet 					lockdep_is_held(&tbl->lock))) != NULL) {
3951da177e4SLinus Torvalds 			if (dev && n->dev != dev) {
3961da177e4SLinus Torvalds 				np = &n->next;
3971da177e4SLinus Torvalds 				continue;
3981da177e4SLinus Torvalds 			}
399859bd2efSDavid Ahern 			if (skip_perm && n->nud_state & NUD_PERMANENT) {
400859bd2efSDavid Ahern 				np = &n->next;
401859bd2efSDavid Ahern 				continue;
402859bd2efSDavid Ahern 			}
403767e97e1SEric Dumazet 			rcu_assign_pointer(*np,
404767e97e1SEric Dumazet 				   rcu_dereference_protected(n->next,
405767e97e1SEric Dumazet 						lockdep_is_held(&tbl->lock)));
4061da177e4SLinus Torvalds 			write_lock(&n->lock);
4071da177e4SLinus Torvalds 			neigh_del_timer(n);
40858956317SDavid Ahern 			neigh_mark_dead(n);
4099f237430SReshetova, Elena 			if (refcount_read(&n->refcnt) != 1) {
4101da177e4SLinus Torvalds 				/* The most unpleasant situation.
4111da177e4SLinus Torvalds 				   We must destroy neighbour entry,
4121da177e4SLinus Torvalds 				   but someone still uses it.
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 				   The destroy will be delayed until
4151da177e4SLinus Torvalds 				   the last user releases us, but
4161da177e4SLinus Torvalds 				   we must kill timers etc. and move
4171da177e4SLinus Torvalds 				   it to safe state.
4181da177e4SLinus Torvalds 				 */
419c9ab4d85SEric Dumazet 				__skb_queue_purge(&n->arp_queue);
4208b5c171bSEric Dumazet 				n->arp_queue_len_bytes = 0;
4215baa0433SEric Dumazet 				WRITE_ONCE(n->output, neigh_blackhole);
4221da177e4SLinus Torvalds 				if (n->nud_state & NUD_VALID)
4231da177e4SLinus Torvalds 					n->nud_state = NUD_NOARP;
4241da177e4SLinus Torvalds 				else
4251da177e4SLinus Torvalds 					n->nud_state = NUD_NONE;
426d5d427cdSJoe Perches 				neigh_dbg(2, "neigh %p is stray\n", n);
4271da177e4SLinus Torvalds 			}
4281da177e4SLinus Torvalds 			write_unlock(&n->lock);
4294f494554SThomas Graf 			neigh_cleanup_and_release(n);
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds 	}
43249636bb1SHerbert Xu }
4331da177e4SLinus Torvalds 
neigh_changeaddr(struct neigh_table * tbl,struct net_device * dev)43449636bb1SHerbert Xu void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
43549636bb1SHerbert Xu {
43649636bb1SHerbert Xu 	write_lock_bh(&tbl->lock);
437859bd2efSDavid Ahern 	neigh_flush_dev(tbl, dev, false);
43849636bb1SHerbert Xu 	write_unlock_bh(&tbl->lock);
43949636bb1SHerbert Xu }
4400a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_changeaddr);
44149636bb1SHerbert Xu 
__neigh_ifdown(struct neigh_table * tbl,struct net_device * dev,bool skip_perm)442859bd2efSDavid Ahern static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
443859bd2efSDavid Ahern 			  bool skip_perm)
44449636bb1SHerbert Xu {
44549636bb1SHerbert Xu 	write_lock_bh(&tbl->lock);
446859bd2efSDavid Ahern 	neigh_flush_dev(tbl, dev, skip_perm);
44753b76cdfSWolfgang Bumiller 	pneigh_ifdown_and_unlock(tbl, dev);
4488207f253SThomas Zeitlhofer 	pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL,
4498207f253SThomas Zeitlhofer 			   tbl->family);
45066ba215cSDenis V. Lunev 	if (skb_queue_empty_lockless(&tbl->proxy_queue))
4511da177e4SLinus Torvalds 		del_timer_sync(&tbl->proxy_timer);
4521da177e4SLinus Torvalds 	return 0;
4531da177e4SLinus Torvalds }
454859bd2efSDavid Ahern 
neigh_carrier_down(struct neigh_table * tbl,struct net_device * dev)455859bd2efSDavid Ahern int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev)
456859bd2efSDavid Ahern {
457859bd2efSDavid Ahern 	__neigh_ifdown(tbl, dev, true);
458859bd2efSDavid Ahern 	return 0;
459859bd2efSDavid Ahern }
460859bd2efSDavid Ahern EXPORT_SYMBOL(neigh_carrier_down);
461859bd2efSDavid Ahern 
neigh_ifdown(struct neigh_table * tbl,struct net_device * dev)462859bd2efSDavid Ahern int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
463859bd2efSDavid Ahern {
464859bd2efSDavid Ahern 	__neigh_ifdown(tbl, dev, false);
465859bd2efSDavid Ahern 	return 0;
466859bd2efSDavid Ahern }
4670a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_ifdown);
4681da177e4SLinus Torvalds 
neigh_alloc(struct neigh_table * tbl,struct net_device * dev,u32 flags,bool exempt_from_gc)46958956317SDavid Ahern static struct neighbour *neigh_alloc(struct neigh_table *tbl,
47058956317SDavid Ahern 				     struct net_device *dev,
4712c611ad9SRoopa Prabhu 				     u32 flags, bool exempt_from_gc)
4721da177e4SLinus Torvalds {
4731da177e4SLinus Torvalds 	struct neighbour *n = NULL;
4741da177e4SLinus Torvalds 	unsigned long now = jiffies;
475a9beb7e8SEric Dumazet 	int entries, gc_thresh3;
4761da177e4SLinus Torvalds 
477e997f8a2SDavid Ahern 	if (exempt_from_gc)
47858956317SDavid Ahern 		goto do_alloc;
47958956317SDavid Ahern 
48058956317SDavid Ahern 	entries = atomic_inc_return(&tbl->gc_entries) - 1;
481a9beb7e8SEric Dumazet 	gc_thresh3 = READ_ONCE(tbl->gc_thresh3);
482a9beb7e8SEric Dumazet 	if (entries >= gc_thresh3 ||
483a9beb7e8SEric Dumazet 	    (entries >= READ_ONCE(tbl->gc_thresh2) &&
484a9beb7e8SEric Dumazet 	     time_after(now, READ_ONCE(tbl->last_flush) + 5 * HZ))) {
485a9beb7e8SEric Dumazet 		if (!neigh_forced_gc(tbl) && entries >= gc_thresh3) {
486fb811395SRick Jones 			net_info_ratelimited("%s: neighbor table overflow!\n",
487fb811395SRick Jones 					     tbl->id);
488fb811395SRick Jones 			NEIGH_CACHE_STAT_INC(tbl, table_fulls);
4891da177e4SLinus Torvalds 			goto out_entries;
4901da177e4SLinus Torvalds 		}
491fb811395SRick Jones 	}
4921da177e4SLinus Torvalds 
49358956317SDavid Ahern do_alloc:
49408433effSYOSHIFUJI Hideaki / 吉藤英明 	n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC);
4951da177e4SLinus Torvalds 	if (!n)
4961da177e4SLinus Torvalds 		goto out_entries;
4971da177e4SLinus Torvalds 
498c9ab4d85SEric Dumazet 	__skb_queue_head_init(&n->arp_queue);
4991da177e4SLinus Torvalds 	rwlock_init(&n->lock);
5000ed8ddf4SEric Dumazet 	seqlock_init(&n->ha_lock);
5011da177e4SLinus Torvalds 	n->updated	  = n->used = now;
5021da177e4SLinus Torvalds 	n->nud_state	  = NUD_NONE;
5031da177e4SLinus Torvalds 	n->output	  = neigh_blackhole;
504e4400bbfSDaniel Borkmann 	n->flags	  = flags;
505f6b72b62SDavid S. Miller 	seqlock_init(&n->hh.hh_lock);
5061da177e4SLinus Torvalds 	n->parms	  = neigh_parms_clone(&tbl->parms);
507e99e88a9SKees Cook 	timer_setup(&n->timer, neigh_timer_handler, 0);
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 	NEIGH_CACHE_STAT_INC(tbl, allocs);
5101da177e4SLinus Torvalds 	n->tbl		  = tbl;
5119f237430SReshetova, Elena 	refcount_set(&n->refcnt, 1);
5121da177e4SLinus Torvalds 	n->dead		  = 1;
51358956317SDavid Ahern 	INIT_LIST_HEAD(&n->gc_list);
5147482e384SDaniel Borkmann 	INIT_LIST_HEAD(&n->managed_list);
51558956317SDavid Ahern 
51658956317SDavid Ahern 	atomic_inc(&tbl->entries);
5171da177e4SLinus Torvalds out:
5181da177e4SLinus Torvalds 	return n;
5191da177e4SLinus Torvalds 
5201da177e4SLinus Torvalds out_entries:
521e997f8a2SDavid Ahern 	if (!exempt_from_gc)
52258956317SDavid Ahern 		atomic_dec(&tbl->gc_entries);
5231da177e4SLinus Torvalds 	goto out;
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds 
neigh_get_hash_rnd(u32 * x)5262c2aba6cSDavid S. Miller static void neigh_get_hash_rnd(u32 *x)
5272c2aba6cSDavid S. Miller {
528b3d0f789SJason A. Donenfeld 	*x = get_random_u32() | 1;
5292c2aba6cSDavid S. Miller }
5302c2aba6cSDavid S. Miller 
neigh_hash_alloc(unsigned int shift)531cd089336SDavid S. Miller static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift)
5321da177e4SLinus Torvalds {
533cd089336SDavid S. Miller 	size_t size = (1 << shift) * sizeof(struct neighbour *);
534d6bf7817SEric Dumazet 	struct neigh_hash_table *ret;
5356193d2beSEric Dumazet 	struct neighbour __rcu **buckets;
5362c2aba6cSDavid S. Miller 	int i;
5371da177e4SLinus Torvalds 
538d6bf7817SEric Dumazet 	ret = kmalloc(sizeof(*ret), GFP_ATOMIC);
539d6bf7817SEric Dumazet 	if (!ret)
540d6bf7817SEric Dumazet 		return NULL;
54185704cb8SKonstantin Khlebnikov 	if (size <= PAGE_SIZE) {
542d6bf7817SEric Dumazet 		buckets = kzalloc(size, GFP_ATOMIC);
54385704cb8SKonstantin Khlebnikov 	} else {
5446193d2beSEric Dumazet 		buckets = (struct neighbour __rcu **)
545d6bf7817SEric Dumazet 			  __get_free_pages(GFP_ATOMIC | __GFP_ZERO,
546d6bf7817SEric Dumazet 					   get_order(size));
54701b833abSKonstantin Khlebnikov 		kmemleak_alloc(buckets, size, 1, GFP_ATOMIC);
54885704cb8SKonstantin Khlebnikov 	}
549d6bf7817SEric Dumazet 	if (!buckets) {
550d6bf7817SEric Dumazet 		kfree(ret);
551d6bf7817SEric Dumazet 		return NULL;
5521da177e4SLinus Torvalds 	}
5536193d2beSEric Dumazet 	ret->hash_buckets = buckets;
554cd089336SDavid S. Miller 	ret->hash_shift = shift;
5552c2aba6cSDavid S. Miller 	for (i = 0; i < NEIGH_NUM_HASH_RND; i++)
5562c2aba6cSDavid S. Miller 		neigh_get_hash_rnd(&ret->hash_rnd[i]);
5571da177e4SLinus Torvalds 	return ret;
5581da177e4SLinus Torvalds }
5591da177e4SLinus Torvalds 
neigh_hash_free_rcu(struct rcu_head * head)560d6bf7817SEric Dumazet static void neigh_hash_free_rcu(struct rcu_head *head)
5611da177e4SLinus Torvalds {
562d6bf7817SEric Dumazet 	struct neigh_hash_table *nht = container_of(head,
563d6bf7817SEric Dumazet 						    struct neigh_hash_table,
564d6bf7817SEric Dumazet 						    rcu);
565cd089336SDavid S. Miller 	size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *);
5666193d2beSEric Dumazet 	struct neighbour __rcu **buckets = nht->hash_buckets;
5671da177e4SLinus Torvalds 
56885704cb8SKonstantin Khlebnikov 	if (size <= PAGE_SIZE) {
569d6bf7817SEric Dumazet 		kfree(buckets);
57085704cb8SKonstantin Khlebnikov 	} else {
57185704cb8SKonstantin Khlebnikov 		kmemleak_free(buckets);
572d6bf7817SEric Dumazet 		free_pages((unsigned long)buckets, get_order(size));
57385704cb8SKonstantin Khlebnikov 	}
574d6bf7817SEric Dumazet 	kfree(nht);
5751da177e4SLinus Torvalds }
5761da177e4SLinus Torvalds 
neigh_hash_grow(struct neigh_table * tbl,unsigned long new_shift)577d6bf7817SEric Dumazet static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl,
578cd089336SDavid S. Miller 						unsigned long new_shift)
5791da177e4SLinus Torvalds {
580d6bf7817SEric Dumazet 	unsigned int i, hash;
581d6bf7817SEric Dumazet 	struct neigh_hash_table *new_nht, *old_nht;
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 	NEIGH_CACHE_STAT_INC(tbl, hash_grows);
5841da177e4SLinus Torvalds 
585d6bf7817SEric Dumazet 	old_nht = rcu_dereference_protected(tbl->nht,
586d6bf7817SEric Dumazet 					    lockdep_is_held(&tbl->lock));
587cd089336SDavid S. Miller 	new_nht = neigh_hash_alloc(new_shift);
588d6bf7817SEric Dumazet 	if (!new_nht)
589d6bf7817SEric Dumazet 		return old_nht;
5901da177e4SLinus Torvalds 
591cd089336SDavid S. Miller 	for (i = 0; i < (1 << old_nht->hash_shift); i++) {
5921da177e4SLinus Torvalds 		struct neighbour *n, *next;
5931da177e4SLinus Torvalds 
594767e97e1SEric Dumazet 		for (n = rcu_dereference_protected(old_nht->hash_buckets[i],
595767e97e1SEric Dumazet 						   lockdep_is_held(&tbl->lock));
596d6bf7817SEric Dumazet 		     n != NULL;
597d6bf7817SEric Dumazet 		     n = next) {
598d6bf7817SEric Dumazet 			hash = tbl->hash(n->primary_key, n->dev,
599d6bf7817SEric Dumazet 					 new_nht->hash_rnd);
6001da177e4SLinus Torvalds 
601cd089336SDavid S. Miller 			hash >>= (32 - new_nht->hash_shift);
602767e97e1SEric Dumazet 			next = rcu_dereference_protected(n->next,
603767e97e1SEric Dumazet 						lockdep_is_held(&tbl->lock));
6041da177e4SLinus Torvalds 
605767e97e1SEric Dumazet 			rcu_assign_pointer(n->next,
606767e97e1SEric Dumazet 					   rcu_dereference_protected(
607767e97e1SEric Dumazet 						new_nht->hash_buckets[hash],
608767e97e1SEric Dumazet 						lockdep_is_held(&tbl->lock)));
609767e97e1SEric Dumazet 			rcu_assign_pointer(new_nht->hash_buckets[hash], n);
6101da177e4SLinus Torvalds 		}
6111da177e4SLinus Torvalds 	}
6121da177e4SLinus Torvalds 
613d6bf7817SEric Dumazet 	rcu_assign_pointer(tbl->nht, new_nht);
614d6bf7817SEric Dumazet 	call_rcu(&old_nht->rcu, neigh_hash_free_rcu);
615d6bf7817SEric Dumazet 	return new_nht;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
neigh_lookup(struct neigh_table * tbl,const void * pkey,struct net_device * dev)6181da177e4SLinus Torvalds struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
6191da177e4SLinus Torvalds 			       struct net_device *dev)
6201da177e4SLinus Torvalds {
6211da177e4SLinus Torvalds 	struct neighbour *n;
6221da177e4SLinus Torvalds 
6231da177e4SLinus Torvalds 	NEIGH_CACHE_STAT_INC(tbl, lookups);
6241da177e4SLinus Torvalds 
62509eed119SEric Dumazet 	rcu_read_lock();
62660395a20SEric W. Biederman 	n = __neigh_lookup_noref(tbl, pkey, dev);
62760395a20SEric W. Biederman 	if (n) {
6289f237430SReshetova, Elena 		if (!refcount_inc_not_zero(&n->refcnt))
629767e97e1SEric Dumazet 			n = NULL;
6301da177e4SLinus Torvalds 		NEIGH_CACHE_STAT_INC(tbl, hits);
6311da177e4SLinus Torvalds 	}
632767e97e1SEric Dumazet 
63309eed119SEric Dumazet 	rcu_read_unlock();
6341da177e4SLinus Torvalds 	return n;
6351da177e4SLinus Torvalds }
6360a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup);
6371da177e4SLinus Torvalds 
638e4400bbfSDaniel Borkmann static struct neighbour *
___neigh_create(struct neigh_table * tbl,const void * pkey,struct net_device * dev,u32 flags,bool exempt_from_gc,bool want_ref)639e4400bbfSDaniel Borkmann ___neigh_create(struct neigh_table *tbl, const void *pkey,
6402c611ad9SRoopa Prabhu 		struct net_device *dev, u32 flags,
641e997f8a2SDavid Ahern 		bool exempt_from_gc, bool want_ref)
6421da177e4SLinus Torvalds {
643e4400bbfSDaniel Borkmann 	u32 hash_val, key_len = tbl->key_len;
644e4400bbfSDaniel Borkmann 	struct neighbour *n1, *rc, *n;
645d6bf7817SEric Dumazet 	struct neigh_hash_table *nht;
646e4400bbfSDaniel Borkmann 	int error;
6471da177e4SLinus Torvalds 
648e4400bbfSDaniel Borkmann 	n = neigh_alloc(tbl, dev, flags, exempt_from_gc);
649fc651001SDavid Ahern 	trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc);
6501da177e4SLinus Torvalds 	if (!n) {
6511da177e4SLinus Torvalds 		rc = ERR_PTR(-ENOBUFS);
6521da177e4SLinus Torvalds 		goto out;
6531da177e4SLinus Torvalds 	}
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	memcpy(n->primary_key, pkey, key_len);
6561da177e4SLinus Torvalds 	n->dev = dev;
657d62607c3SJakub Kicinski 	netdev_hold(dev, &n->dev_tracker, GFP_ATOMIC);
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	/* Protocol specific setup. */
6601da177e4SLinus Torvalds 	if (tbl->constructor &&	(error = tbl->constructor(n)) < 0) {
6611da177e4SLinus Torvalds 		rc = ERR_PTR(error);
6621da177e4SLinus Torvalds 		goto out_neigh_release;
6631da177e4SLinus Torvalds 	}
6641da177e4SLinus Torvalds 
665da6a8fa0SDavid Miller 	if (dev->netdev_ops->ndo_neigh_construct) {
666503eebc2SJiri Pirko 		error = dev->netdev_ops->ndo_neigh_construct(dev, n);
667da6a8fa0SDavid Miller 		if (error < 0) {
668da6a8fa0SDavid Miller 			rc = ERR_PTR(error);
669da6a8fa0SDavid Miller 			goto out_neigh_release;
670da6a8fa0SDavid Miller 		}
671da6a8fa0SDavid Miller 	}
672da6a8fa0SDavid Miller 
673447f2191SDavid S. Miller 	/* Device specific setup. */
674447f2191SDavid S. Miller 	if (n->parms->neigh_setup &&
675447f2191SDavid S. Miller 	    (error = n->parms->neigh_setup(n)) < 0) {
676447f2191SDavid S. Miller 		rc = ERR_PTR(error);
677447f2191SDavid S. Miller 		goto out_neigh_release;
678447f2191SDavid S. Miller 	}
679447f2191SDavid S. Miller 
6801f9248e5SJiri Pirko 	n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1);
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 	write_lock_bh(&tbl->lock);
683d6bf7817SEric Dumazet 	nht = rcu_dereference_protected(tbl->nht,
684d6bf7817SEric Dumazet 					lockdep_is_held(&tbl->lock));
6851da177e4SLinus Torvalds 
686cd089336SDavid S. Miller 	if (atomic_read(&tbl->entries) > (1 << nht->hash_shift))
687cd089336SDavid S. Miller 		nht = neigh_hash_grow(tbl, nht->hash_shift + 1);
6881da177e4SLinus Torvalds 
689096b9854SJim Westfall 	hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	if (n->parms->dead) {
6921da177e4SLinus Torvalds 		rc = ERR_PTR(-EINVAL);
6931da177e4SLinus Torvalds 		goto out_tbl_unlock;
6941da177e4SLinus Torvalds 	}
6951da177e4SLinus Torvalds 
696767e97e1SEric Dumazet 	for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val],
697767e97e1SEric Dumazet 					    lockdep_is_held(&tbl->lock));
698767e97e1SEric Dumazet 	     n1 != NULL;
699767e97e1SEric Dumazet 	     n1 = rcu_dereference_protected(n1->next,
700767e97e1SEric Dumazet 			lockdep_is_held(&tbl->lock))) {
701096b9854SJim Westfall 		if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) {
702a263b309SDavid S. Miller 			if (want_ref)
7031da177e4SLinus Torvalds 				neigh_hold(n1);
7041da177e4SLinus Torvalds 			rc = n1;
7051da177e4SLinus Torvalds 			goto out_tbl_unlock;
7061da177e4SLinus Torvalds 		}
7071da177e4SLinus Torvalds 	}
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	n->dead = 0;
710e997f8a2SDavid Ahern 	if (!exempt_from_gc)
7118cc196d6SDavid Ahern 		list_add_tail(&n->gc_list, &n->tbl->gc_list);
7127482e384SDaniel Borkmann 	if (n->flags & NTF_MANAGED)
7137482e384SDaniel Borkmann 		list_add_tail(&n->managed_list, &n->tbl->managed_list);
714a263b309SDavid S. Miller 	if (want_ref)
7151da177e4SLinus Torvalds 		neigh_hold(n);
716767e97e1SEric Dumazet 	rcu_assign_pointer(n->next,
717767e97e1SEric Dumazet 			   rcu_dereference_protected(nht->hash_buckets[hash_val],
718767e97e1SEric Dumazet 						     lockdep_is_held(&tbl->lock)));
719767e97e1SEric Dumazet 	rcu_assign_pointer(nht->hash_buckets[hash_val], n);
7201da177e4SLinus Torvalds 	write_unlock_bh(&tbl->lock);
721d5d427cdSJoe Perches 	neigh_dbg(2, "neigh %p is created\n", n);
7221da177e4SLinus Torvalds 	rc = n;
7231da177e4SLinus Torvalds out:
7241da177e4SLinus Torvalds 	return rc;
7251da177e4SLinus Torvalds out_tbl_unlock:
7261da177e4SLinus Torvalds 	write_unlock_bh(&tbl->lock);
7271da177e4SLinus Torvalds out_neigh_release:
72864c6f4bbSDavid Ahern 	if (!exempt_from_gc)
72964c6f4bbSDavid Ahern 		atomic_dec(&tbl->gc_entries);
7301da177e4SLinus Torvalds 	neigh_release(n);
7311da177e4SLinus Torvalds 	goto out;
7321da177e4SLinus Torvalds }
73358956317SDavid Ahern 
__neigh_create(struct neigh_table * tbl,const void * pkey,struct net_device * dev,bool want_ref)73458956317SDavid Ahern struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
73558956317SDavid Ahern 				 struct net_device *dev, bool want_ref)
73658956317SDavid Ahern {
737e4400bbfSDaniel Borkmann 	return ___neigh_create(tbl, pkey, dev, 0, false, want_ref);
73858956317SDavid Ahern }
739a263b309SDavid S. Miller EXPORT_SYMBOL(__neigh_create);
7401da177e4SLinus Torvalds 
pneigh_hash(const void * pkey,unsigned int key_len)74101ccdf12SAlexey Dobriyan static u32 pneigh_hash(const void *pkey, unsigned int key_len)
742fa86d322SPavel Emelyanov {
743fa86d322SPavel Emelyanov 	u32 hash_val = *(u32 *)(pkey + key_len - 4);
744fa86d322SPavel Emelyanov 	hash_val ^= (hash_val >> 16);
745fa86d322SPavel Emelyanov 	hash_val ^= hash_val >> 8;
746fa86d322SPavel Emelyanov 	hash_val ^= hash_val >> 4;
747fa86d322SPavel Emelyanov 	hash_val &= PNEIGH_HASHMASK;
748be01d655SYOSHIFUJI Hideaki 	return hash_val;
749fa86d322SPavel Emelyanov }
750fa86d322SPavel Emelyanov 
__pneigh_lookup_1(struct pneigh_entry * n,struct net * net,const void * pkey,unsigned int key_len,struct net_device * dev)751be01d655SYOSHIFUJI Hideaki static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n,
752be01d655SYOSHIFUJI Hideaki 					      struct net *net,
753be01d655SYOSHIFUJI Hideaki 					      const void *pkey,
75401ccdf12SAlexey Dobriyan 					      unsigned int key_len,
755be01d655SYOSHIFUJI Hideaki 					      struct net_device *dev)
756be01d655SYOSHIFUJI Hideaki {
757be01d655SYOSHIFUJI Hideaki 	while (n) {
758be01d655SYOSHIFUJI Hideaki 		if (!memcmp(n->key, pkey, key_len) &&
759be01d655SYOSHIFUJI Hideaki 		    net_eq(pneigh_net(n), net) &&
760be01d655SYOSHIFUJI Hideaki 		    (n->dev == dev || !n->dev))
761fa86d322SPavel Emelyanov 			return n;
762be01d655SYOSHIFUJI Hideaki 		n = n->next;
763be01d655SYOSHIFUJI Hideaki 	}
764be01d655SYOSHIFUJI Hideaki 	return NULL;
765be01d655SYOSHIFUJI Hideaki }
766be01d655SYOSHIFUJI Hideaki 
__pneigh_lookup(struct neigh_table * tbl,struct net * net,const void * pkey,struct net_device * dev)767be01d655SYOSHIFUJI Hideaki struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl,
768be01d655SYOSHIFUJI Hideaki 		struct net *net, const void *pkey, struct net_device *dev)
769be01d655SYOSHIFUJI Hideaki {
77001ccdf12SAlexey Dobriyan 	unsigned int key_len = tbl->key_len;
771be01d655SYOSHIFUJI Hideaki 	u32 hash_val = pneigh_hash(pkey, key_len);
772be01d655SYOSHIFUJI Hideaki 
773be01d655SYOSHIFUJI Hideaki 	return __pneigh_lookup_1(tbl->phash_buckets[hash_val],
774be01d655SYOSHIFUJI Hideaki 				 net, pkey, key_len, dev);
775fa86d322SPavel Emelyanov }
7760a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL_GPL(__pneigh_lookup);
777fa86d322SPavel Emelyanov 
pneigh_lookup(struct neigh_table * tbl,struct net * net,const void * pkey,struct net_device * dev,int creat)778426b5303SEric W. Biederman struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
779426b5303SEric W. Biederman 				    struct net *net, const void *pkey,
7801da177e4SLinus Torvalds 				    struct net_device *dev, int creat)
7811da177e4SLinus Torvalds {
7821da177e4SLinus Torvalds 	struct pneigh_entry *n;
78301ccdf12SAlexey Dobriyan 	unsigned int key_len = tbl->key_len;
784be01d655SYOSHIFUJI Hideaki 	u32 hash_val = pneigh_hash(pkey, key_len);
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds 	read_lock_bh(&tbl->lock);
787be01d655SYOSHIFUJI Hideaki 	n = __pneigh_lookup_1(tbl->phash_buckets[hash_val],
788be01d655SYOSHIFUJI Hideaki 			      net, pkey, key_len, dev);
789be01d655SYOSHIFUJI Hideaki 	read_unlock_bh(&tbl->lock);
7901da177e4SLinus Torvalds 
791be01d655SYOSHIFUJI Hideaki 	if (n || !creat)
7921da177e4SLinus Torvalds 		goto out;
7931da177e4SLinus Torvalds 
7944ae28944SPavel Emelyanov 	ASSERT_RTNL();
7954ae28944SPavel Emelyanov 
796e195e9b5SEric Dumazet 	n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL);
7971da177e4SLinus Torvalds 	if (!n)
7981da177e4SLinus Torvalds 		goto out;
7991da177e4SLinus Torvalds 
800efd7ef1cSEric W. Biederman 	write_pnet(&n->net, net);
8011da177e4SLinus Torvalds 	memcpy(n->key, pkey, key_len);
8021da177e4SLinus Torvalds 	n->dev = dev;
803d62607c3SJakub Kicinski 	netdev_hold(dev, &n->dev_tracker, GFP_KERNEL);
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	if (tbl->pconstructor && tbl->pconstructor(n)) {
806d62607c3SJakub Kicinski 		netdev_put(dev, &n->dev_tracker);
8071da177e4SLinus Torvalds 		kfree(n);
8081da177e4SLinus Torvalds 		n = NULL;
8091da177e4SLinus Torvalds 		goto out;
8101da177e4SLinus Torvalds 	}
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds 	write_lock_bh(&tbl->lock);
8131da177e4SLinus Torvalds 	n->next = tbl->phash_buckets[hash_val];
8141da177e4SLinus Torvalds 	tbl->phash_buckets[hash_val] = n;
8151da177e4SLinus Torvalds 	write_unlock_bh(&tbl->lock);
8161da177e4SLinus Torvalds out:
8171da177e4SLinus Torvalds 	return n;
8181da177e4SLinus Torvalds }
8190a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_lookup);
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds 
pneigh_delete(struct neigh_table * tbl,struct net * net,const void * pkey,struct net_device * dev)822426b5303SEric W. Biederman int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
8231da177e4SLinus Torvalds 		  struct net_device *dev)
8241da177e4SLinus Torvalds {
8251da177e4SLinus Torvalds 	struct pneigh_entry *n, **np;
82601ccdf12SAlexey Dobriyan 	unsigned int key_len = tbl->key_len;
827be01d655SYOSHIFUJI Hideaki 	u32 hash_val = pneigh_hash(pkey, key_len);
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 	write_lock_bh(&tbl->lock);
8301da177e4SLinus Torvalds 	for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL;
8311da177e4SLinus Torvalds 	     np = &n->next) {
832426b5303SEric W. Biederman 		if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
833878628fbSYOSHIFUJI Hideaki 		    net_eq(pneigh_net(n), net)) {
8341da177e4SLinus Torvalds 			*np = n->next;
8351da177e4SLinus Torvalds 			write_unlock_bh(&tbl->lock);
8361da177e4SLinus Torvalds 			if (tbl->pdestructor)
8371da177e4SLinus Torvalds 				tbl->pdestructor(n);
838d62607c3SJakub Kicinski 			netdev_put(n->dev, &n->dev_tracker);
8391da177e4SLinus Torvalds 			kfree(n);
8401da177e4SLinus Torvalds 			return 0;
8411da177e4SLinus Torvalds 		}
8421da177e4SLinus Torvalds 	}
8431da177e4SLinus Torvalds 	write_unlock_bh(&tbl->lock);
8441da177e4SLinus Torvalds 	return -ENOENT;
8451da177e4SLinus Torvalds }
8461da177e4SLinus Torvalds 
pneigh_ifdown_and_unlock(struct neigh_table * tbl,struct net_device * dev)84753b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
84853b76cdfSWolfgang Bumiller 				    struct net_device *dev)
8491da177e4SLinus Torvalds {
85053b76cdfSWolfgang Bumiller 	struct pneigh_entry *n, **np, *freelist = NULL;
8511da177e4SLinus Torvalds 	u32 h;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	for (h = 0; h <= PNEIGH_HASHMASK; h++) {
8541da177e4SLinus Torvalds 		np = &tbl->phash_buckets[h];
8551da177e4SLinus Torvalds 		while ((n = *np) != NULL) {
8561da177e4SLinus Torvalds 			if (!dev || n->dev == dev) {
8571da177e4SLinus Torvalds 				*np = n->next;
85853b76cdfSWolfgang Bumiller 				n->next = freelist;
85953b76cdfSWolfgang Bumiller 				freelist = n;
86053b76cdfSWolfgang Bumiller 				continue;
86153b76cdfSWolfgang Bumiller 			}
86253b76cdfSWolfgang Bumiller 			np = &n->next;
86353b76cdfSWolfgang Bumiller 		}
86453b76cdfSWolfgang Bumiller 	}
86553b76cdfSWolfgang Bumiller 	write_unlock_bh(&tbl->lock);
86653b76cdfSWolfgang Bumiller 	while ((n = freelist)) {
86753b76cdfSWolfgang Bumiller 		freelist = n->next;
86853b76cdfSWolfgang Bumiller 		n->next = NULL;
8691da177e4SLinus Torvalds 		if (tbl->pdestructor)
8701da177e4SLinus Torvalds 			tbl->pdestructor(n);
871d62607c3SJakub Kicinski 		netdev_put(n->dev, &n->dev_tracker);
8721da177e4SLinus Torvalds 		kfree(n);
8731da177e4SLinus Torvalds 	}
8741da177e4SLinus Torvalds 	return -ENOENT;
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds 
87706f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms);
87806f0511dSDenis V. Lunev 
neigh_parms_put(struct neigh_parms * parms)87906f0511dSDenis V. Lunev static inline void neigh_parms_put(struct neigh_parms *parms)
88006f0511dSDenis V. Lunev {
8816343944bSReshetova, Elena 	if (refcount_dec_and_test(&parms->refcnt))
88206f0511dSDenis V. Lunev 		neigh_parms_destroy(parms);
88306f0511dSDenis V. Lunev }
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds /*
8861da177e4SLinus Torvalds  *	neighbour must already be out of the table;
8871da177e4SLinus Torvalds  *
8881da177e4SLinus Torvalds  */
neigh_destroy(struct neighbour * neigh)8891da177e4SLinus Torvalds void neigh_destroy(struct neighbour *neigh)
8901da177e4SLinus Torvalds {
891da6a8fa0SDavid Miller 	struct net_device *dev = neigh->dev;
892da6a8fa0SDavid Miller 
8931da177e4SLinus Torvalds 	NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	if (!neigh->dead) {
896e005d193SJoe Perches 		pr_warn("Destroying alive neighbour %p\n", neigh);
8971da177e4SLinus Torvalds 		dump_stack();
8981da177e4SLinus Torvalds 		return;
8991da177e4SLinus Torvalds 	}
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	if (neigh_del_timer(neigh))
902e005d193SJoe Perches 		pr_warn("Impossible event\n");
9031da177e4SLinus Torvalds 
904c9ab4d85SEric Dumazet 	write_lock_bh(&neigh->lock);
905c9ab4d85SEric Dumazet 	__skb_queue_purge(&neigh->arp_queue);
906c9ab4d85SEric Dumazet 	write_unlock_bh(&neigh->lock);
9078b5c171bSEric Dumazet 	neigh->arp_queue_len_bytes = 0;
9081da177e4SLinus Torvalds 
909447f2191SDavid S. Miller 	if (dev->netdev_ops->ndo_neigh_destroy)
910503eebc2SJiri Pirko 		dev->netdev_ops->ndo_neigh_destroy(dev, neigh);
911447f2191SDavid S. Miller 
912d62607c3SJakub Kicinski 	netdev_put(dev, &neigh->dev_tracker);
9131da177e4SLinus Torvalds 	neigh_parms_put(neigh->parms);
9141da177e4SLinus Torvalds 
915d5d427cdSJoe Perches 	neigh_dbg(2, "neigh %p is destroyed\n", neigh);
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	atomic_dec(&neigh->tbl->entries);
9185b8b0060SDavid Miller 	kfree_rcu(neigh, rcu);
9191da177e4SLinus Torvalds }
9200a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_destroy);
9211da177e4SLinus Torvalds 
9221da177e4SLinus Torvalds /* Neighbour state is suspicious;
9231da177e4SLinus Torvalds    disable fast path.
9241da177e4SLinus Torvalds 
9251da177e4SLinus Torvalds    Called with write_locked neigh.
9261da177e4SLinus Torvalds  */
neigh_suspect(struct neighbour * neigh)9271da177e4SLinus Torvalds static void neigh_suspect(struct neighbour *neigh)
9281da177e4SLinus Torvalds {
929d5d427cdSJoe Perches 	neigh_dbg(2, "neigh %p is suspected\n", neigh);
9301da177e4SLinus Torvalds 
9315baa0433SEric Dumazet 	WRITE_ONCE(neigh->output, neigh->ops->output);
9321da177e4SLinus Torvalds }
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds /* Neighbour state is OK;
9351da177e4SLinus Torvalds    enable fast path.
9361da177e4SLinus Torvalds 
9371da177e4SLinus Torvalds    Called with write_locked neigh.
9381da177e4SLinus Torvalds  */
neigh_connect(struct neighbour * neigh)9391da177e4SLinus Torvalds static void neigh_connect(struct neighbour *neigh)
9401da177e4SLinus Torvalds {
941d5d427cdSJoe Perches 	neigh_dbg(2, "neigh %p is connected\n", neigh);
9421da177e4SLinus Torvalds 
9435baa0433SEric Dumazet 	WRITE_ONCE(neigh->output, neigh->ops->connected_output);
9441da177e4SLinus Torvalds }
9451da177e4SLinus Torvalds 
neigh_periodic_work(struct work_struct * work)946e4c4e448SEric Dumazet static void neigh_periodic_work(struct work_struct *work)
9471da177e4SLinus Torvalds {
948e4c4e448SEric Dumazet 	struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
949767e97e1SEric Dumazet 	struct neighbour *n;
950767e97e1SEric Dumazet 	struct neighbour __rcu **np;
951e4c4e448SEric Dumazet 	unsigned int i;
952d6bf7817SEric Dumazet 	struct neigh_hash_table *nht;
9531da177e4SLinus Torvalds 
9541da177e4SLinus Torvalds 	NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
9551da177e4SLinus Torvalds 
956e4c4e448SEric Dumazet 	write_lock_bh(&tbl->lock);
957d6bf7817SEric Dumazet 	nht = rcu_dereference_protected(tbl->nht,
958d6bf7817SEric Dumazet 					lockdep_is_held(&tbl->lock));
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 	/*
9611da177e4SLinus Torvalds 	 *	periodically recompute ReachableTime from random function
9621da177e4SLinus Torvalds 	 */
9631da177e4SLinus Torvalds 
964e4c4e448SEric Dumazet 	if (time_after(jiffies, tbl->last_rand + 300 * HZ)) {
9651da177e4SLinus Torvalds 		struct neigh_parms *p;
966a9beb7e8SEric Dumazet 
967a9beb7e8SEric Dumazet 		WRITE_ONCE(tbl->last_rand, jiffies);
96875fbfd33SNicolas Dichtel 		list_for_each_entry(p, &tbl->parms_list, list)
9691da177e4SLinus Torvalds 			p->reachable_time =
9701f9248e5SJiri Pirko 				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
9711da177e4SLinus Torvalds 	}
9721da177e4SLinus Torvalds 
973a9beb7e8SEric Dumazet 	if (atomic_read(&tbl->entries) < READ_ONCE(tbl->gc_thresh1))
974feff9ab2SDuan Jiong 		goto out;
975feff9ab2SDuan Jiong 
976cd089336SDavid S. Miller 	for (i = 0 ; i < (1 << nht->hash_shift); i++) {
977d6bf7817SEric Dumazet 		np = &nht->hash_buckets[i];
9781da177e4SLinus Torvalds 
979767e97e1SEric Dumazet 		while ((n = rcu_dereference_protected(*np,
980767e97e1SEric Dumazet 				lockdep_is_held(&tbl->lock))) != NULL) {
9811da177e4SLinus Torvalds 			unsigned int state;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 			write_lock(&n->lock);
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 			state = n->nud_state;
9869ce33e46SRoopa Prabhu 			if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) ||
9879ce33e46SRoopa Prabhu 			    (n->flags & NTF_EXT_LEARNED)) {
9881da177e4SLinus Torvalds 				write_unlock(&n->lock);
9891da177e4SLinus Torvalds 				goto next_elt;
9901da177e4SLinus Torvalds 			}
9911da177e4SLinus Torvalds 
992c1d2ecdfSJulian Anastasov 			if (time_before(n->used, n->confirmed) &&
993c1d2ecdfSJulian Anastasov 			    time_is_before_eq_jiffies(n->confirmed))
9941da177e4SLinus Torvalds 				n->used = n->confirmed;
9951da177e4SLinus Torvalds 
9969f237430SReshetova, Elena 			if (refcount_read(&n->refcnt) == 1 &&
9971da177e4SLinus Torvalds 			    (state == NUD_FAILED ||
998c1d2ecdfSJulian Anastasov 			     !time_in_range_open(jiffies, n->used,
999c1d2ecdfSJulian Anastasov 						 n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
100025563b58SEric Dumazet 				rcu_assign_pointer(*np,
100125563b58SEric Dumazet 					rcu_dereference_protected(n->next,
100225563b58SEric Dumazet 						lockdep_is_held(&tbl->lock)));
100358956317SDavid Ahern 				neigh_mark_dead(n);
10041da177e4SLinus Torvalds 				write_unlock(&n->lock);
10054f494554SThomas Graf 				neigh_cleanup_and_release(n);
10061da177e4SLinus Torvalds 				continue;
10071da177e4SLinus Torvalds 			}
10081da177e4SLinus Torvalds 			write_unlock(&n->lock);
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds next_elt:
10111da177e4SLinus Torvalds 			np = &n->next;
10121da177e4SLinus Torvalds 		}
1013e4c4e448SEric Dumazet 		/*
1014e4c4e448SEric Dumazet 		 * It's fine to release lock here, even if hash table
1015e4c4e448SEric Dumazet 		 * grows while we are preempted.
1016e4c4e448SEric Dumazet 		 */
1017e4c4e448SEric Dumazet 		write_unlock_bh(&tbl->lock);
1018e4c4e448SEric Dumazet 		cond_resched();
1019e4c4e448SEric Dumazet 		write_lock_bh(&tbl->lock);
102084338a6cSMichel Machado 		nht = rcu_dereference_protected(tbl->nht,
102184338a6cSMichel Machado 						lockdep_is_held(&tbl->lock));
1022e4c4e448SEric Dumazet 	}
10232724680bSYOSHIFUJI Hideaki / 吉藤英明 out:
10241f9248e5SJiri Pirko 	/* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks.
10251f9248e5SJiri Pirko 	 * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2
10261f9248e5SJiri Pirko 	 * BASE_REACHABLE_TIME.
10271da177e4SLinus Torvalds 	 */
1028f618002bSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
10291f9248e5SJiri Pirko 			      NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1);
1030e4c4e448SEric Dumazet 	write_unlock_bh(&tbl->lock);
10311da177e4SLinus Torvalds }
10321da177e4SLinus Torvalds 
neigh_max_probes(struct neighbour * n)10331da177e4SLinus Torvalds static __inline__ int neigh_max_probes(struct neighbour *n)
10341da177e4SLinus Torvalds {
10351da177e4SLinus Torvalds 	struct neigh_parms *p = n->parms;
10368da86466SYOSHIFUJI Hideaki/吉藤英明 	return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) +
10378da86466SYOSHIFUJI Hideaki/吉藤英明 	       (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) :
10388da86466SYOSHIFUJI Hideaki/吉藤英明 	        NEIGH_VAR(p, MCAST_PROBES));
10391da177e4SLinus Torvalds }
10401da177e4SLinus Torvalds 
neigh_invalidate(struct neighbour * neigh)10415ef12d98STimo Teras static void neigh_invalidate(struct neighbour *neigh)
10420a141509SEric Dumazet 	__releases(neigh->lock)
10430a141509SEric Dumazet 	__acquires(neigh->lock)
10445ef12d98STimo Teras {
10455ef12d98STimo Teras 	struct sk_buff *skb;
10465ef12d98STimo Teras 
10475ef12d98STimo Teras 	NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
1048d5d427cdSJoe Perches 	neigh_dbg(2, "neigh %p is failed\n", neigh);
10495ef12d98STimo Teras 	neigh->updated = jiffies;
10505ef12d98STimo Teras 
10515ef12d98STimo Teras 	/* It is very thin place. report_unreachable is very complicated
10525ef12d98STimo Teras 	   routine. Particularly, it can hit the same neighbour entry!
10535ef12d98STimo Teras 
10545ef12d98STimo Teras 	   So that, we try to be accurate and avoid dead loop. --ANK
10555ef12d98STimo Teras 	 */
10565ef12d98STimo Teras 	while (neigh->nud_state == NUD_FAILED &&
10575ef12d98STimo Teras 	       (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
10585ef12d98STimo Teras 		write_unlock(&neigh->lock);
10595ef12d98STimo Teras 		neigh->ops->error_report(neigh, skb);
10605ef12d98STimo Teras 		write_lock(&neigh->lock);
10615ef12d98STimo Teras 	}
1062c9ab4d85SEric Dumazet 	__skb_queue_purge(&neigh->arp_queue);
10638b5c171bSEric Dumazet 	neigh->arp_queue_len_bytes = 0;
10645ef12d98STimo Teras }
10655ef12d98STimo Teras 
neigh_probe(struct neighbour * neigh)1066cd28ca0aSEric Dumazet static void neigh_probe(struct neighbour *neigh)
1067cd28ca0aSEric Dumazet 	__releases(neigh->lock)
1068cd28ca0aSEric Dumazet {
10694ed377e3SHannes Frederic Sowa 	struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue);
1070cd28ca0aSEric Dumazet 	/* keep skb alive even if arp_queue overflows */
1071cd28ca0aSEric Dumazet 	if (skb)
107219125c1aSMartin Zhang 		skb = skb_clone(skb, GFP_ATOMIC);
1073cd28ca0aSEric Dumazet 	write_unlock(&neigh->lock);
107448481c8fSEric Dumazet 	if (neigh->ops->solicit)
1075cd28ca0aSEric Dumazet 		neigh->ops->solicit(neigh, skb);
1076cd28ca0aSEric Dumazet 	atomic_inc(&neigh->probes);
107787fff3caSYang Wei 	consume_skb(skb);
1078cd28ca0aSEric Dumazet }
1079cd28ca0aSEric Dumazet 
10801da177e4SLinus Torvalds /* Called when a timer expires for a neighbour entry. */
10811da177e4SLinus Torvalds 
neigh_timer_handler(struct timer_list * t)1082e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t)
10831da177e4SLinus Torvalds {
10841da177e4SLinus Torvalds 	unsigned long now, next;
1085e99e88a9SKees Cook 	struct neighbour *neigh = from_timer(neigh, t, timer);
108695c96174SEric Dumazet 	unsigned int state;
10871da177e4SLinus Torvalds 	int notify = 0;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	write_lock(&neigh->lock);
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	state = neigh->nud_state;
10921da177e4SLinus Torvalds 	now = jiffies;
10931da177e4SLinus Torvalds 	next = now + HZ;
10941da177e4SLinus Torvalds 
1095045f7b3bSDavid S. Miller 	if (!(state & NUD_IN_TIMER))
10961da177e4SLinus Torvalds 		goto out;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	if (state & NUD_REACHABLE) {
10991da177e4SLinus Torvalds 		if (time_before_eq(now,
11001da177e4SLinus Torvalds 				   neigh->confirmed + neigh->parms->reachable_time)) {
1101d5d427cdSJoe Perches 			neigh_dbg(2, "neigh %p is still alive\n", neigh);
11021da177e4SLinus Torvalds 			next = neigh->confirmed + neigh->parms->reachable_time;
11031da177e4SLinus Torvalds 		} else if (time_before_eq(now,
11041f9248e5SJiri Pirko 					  neigh->used +
11051f9248e5SJiri Pirko 					  NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
1106d5d427cdSJoe Perches 			neigh_dbg(2, "neigh %p is delayed\n", neigh);
1107b071af52SEric Dumazet 			WRITE_ONCE(neigh->nud_state, NUD_DELAY);
1108955aaa2fSYOSHIFUJI Hideaki 			neigh->updated = jiffies;
11091da177e4SLinus Torvalds 			neigh_suspect(neigh);
11101f9248e5SJiri Pirko 			next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME);
11111da177e4SLinus Torvalds 		} else {
1112d5d427cdSJoe Perches 			neigh_dbg(2, "neigh %p is suspected\n", neigh);
1113b071af52SEric Dumazet 			WRITE_ONCE(neigh->nud_state, NUD_STALE);
1114955aaa2fSYOSHIFUJI Hideaki 			neigh->updated = jiffies;
11151da177e4SLinus Torvalds 			neigh_suspect(neigh);
11168d71740cSTom Tucker 			notify = 1;
11171da177e4SLinus Torvalds 		}
11181da177e4SLinus Torvalds 	} else if (state & NUD_DELAY) {
11191da177e4SLinus Torvalds 		if (time_before_eq(now,
11201f9248e5SJiri Pirko 				   neigh->confirmed +
11211f9248e5SJiri Pirko 				   NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
1122d5d427cdSJoe Perches 			neigh_dbg(2, "neigh %p is now reachable\n", neigh);
1123b071af52SEric Dumazet 			WRITE_ONCE(neigh->nud_state, NUD_REACHABLE);
1124955aaa2fSYOSHIFUJI Hideaki 			neigh->updated = jiffies;
11251da177e4SLinus Torvalds 			neigh_connect(neigh);
11268d71740cSTom Tucker 			notify = 1;
11271da177e4SLinus Torvalds 			next = neigh->confirmed + neigh->parms->reachable_time;
11281da177e4SLinus Torvalds 		} else {
1129d5d427cdSJoe Perches 			neigh_dbg(2, "neigh %p is probed\n", neigh);
1130b071af52SEric Dumazet 			WRITE_ONCE(neigh->nud_state, NUD_PROBE);
1131955aaa2fSYOSHIFUJI Hideaki 			neigh->updated = jiffies;
11321da177e4SLinus Torvalds 			atomic_set(&neigh->probes, 0);
1133765c9c63SErik Kline 			notify = 1;
113419e16d22SHangbin Liu 			next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
113519e16d22SHangbin Liu 					 HZ/100);
11361da177e4SLinus Torvalds 		}
11371da177e4SLinus Torvalds 	} else {
11381da177e4SLinus Torvalds 		/* NUD_PROBE|NUD_INCOMPLETE */
113919e16d22SHangbin Liu 		next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100);
11401da177e4SLinus Torvalds 	}
11411da177e4SLinus Torvalds 
11421da177e4SLinus Torvalds 	if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) &&
11431da177e4SLinus Torvalds 	    atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
1144b071af52SEric Dumazet 		WRITE_ONCE(neigh->nud_state, NUD_FAILED);
11451da177e4SLinus Torvalds 		notify = 1;
11465ef12d98STimo Teras 		neigh_invalidate(neigh);
11475e2c21dcSDuan Jiong 		goto out;
11481da177e4SLinus Torvalds 	}
11491da177e4SLinus Torvalds 
11501da177e4SLinus Torvalds 	if (neigh->nud_state & NUD_IN_TIMER) {
115196d10d5bSHangbin Liu 		if (time_before(next, jiffies + HZ/100))
115296d10d5bSHangbin Liu 			next = jiffies + HZ/100;
11536fb9974fSHerbert Xu 		if (!mod_timer(&neigh->timer, next))
11546fb9974fSHerbert Xu 			neigh_hold(neigh);
11551da177e4SLinus Torvalds 	}
11561da177e4SLinus Torvalds 	if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) {
1157cd28ca0aSEric Dumazet 		neigh_probe(neigh);
11589ff56607SDavid S. Miller 	} else {
11591da177e4SLinus Torvalds out:
11601da177e4SLinus Torvalds 		write_unlock(&neigh->lock);
11619ff56607SDavid S. Miller 	}
11621da177e4SLinus Torvalds 
1163d961db35SThomas Graf 	if (notify)
11647b8f7a40SRoopa Prabhu 		neigh_update_notify(neigh, 0);
1165d961db35SThomas Graf 
116656dd18a4SRoopa Prabhu 	trace_neigh_timer_handler(neigh, 0);
116756dd18a4SRoopa Prabhu 
11681da177e4SLinus Torvalds 	neigh_release(neigh);
11691da177e4SLinus Torvalds }
11701da177e4SLinus Torvalds 
__neigh_event_send(struct neighbour * neigh,struct sk_buff * skb,const bool immediate_ok)11714a81f6daSDaniel Borkmann int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb,
11724a81f6daSDaniel Borkmann 		       const bool immediate_ok)
11731da177e4SLinus Torvalds {
11741da177e4SLinus Torvalds 	int rc;
1175cd28ca0aSEric Dumazet 	bool immediate_probe = false;
11761da177e4SLinus Torvalds 
11771da177e4SLinus Torvalds 	write_lock_bh(&neigh->lock);
11781da177e4SLinus Torvalds 
11791da177e4SLinus Torvalds 	rc = 0;
11801da177e4SLinus Torvalds 	if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
11811da177e4SLinus Torvalds 		goto out_unlock_bh;
11822c51a97fSJulian Anastasov 	if (neigh->dead)
11832c51a97fSJulian Anastasov 		goto out_dead;
11841da177e4SLinus Torvalds 
11851da177e4SLinus Torvalds 	if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
11861f9248e5SJiri Pirko 		if (NEIGH_VAR(neigh->parms, MCAST_PROBES) +
11871f9248e5SJiri Pirko 		    NEIGH_VAR(neigh->parms, APP_PROBES)) {
1188cd28ca0aSEric Dumazet 			unsigned long next, now = jiffies;
1189cd28ca0aSEric Dumazet 
11901f9248e5SJiri Pirko 			atomic_set(&neigh->probes,
11911f9248e5SJiri Pirko 				   NEIGH_VAR(neigh->parms, UCAST_PROBES));
1192071c3798SLorenzo Bianconi 			neigh_del_timer(neigh);
1193b071af52SEric Dumazet 			WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE);
1194cd28ca0aSEric Dumazet 			neigh->updated = now;
11954a81f6daSDaniel Borkmann 			if (!immediate_ok) {
11964a81f6daSDaniel Borkmann 				next = now + 1;
11974a81f6daSDaniel Borkmann 			} else {
1198cd28ca0aSEric Dumazet 				immediate_probe = true;
11994a81f6daSDaniel Borkmann 				next = now + max(NEIGH_VAR(neigh->parms,
12004a81f6daSDaniel Borkmann 							   RETRANS_TIME),
12014a81f6daSDaniel Borkmann 						 HZ / 100);
12024a81f6daSDaniel Borkmann 			}
12034a81f6daSDaniel Borkmann 			neigh_add_timer(neigh, next);
12041da177e4SLinus Torvalds 		} else {
1205b071af52SEric Dumazet 			WRITE_ONCE(neigh->nud_state, NUD_FAILED);
1206955aaa2fSYOSHIFUJI Hideaki 			neigh->updated = jiffies;
12071da177e4SLinus Torvalds 			write_unlock_bh(&neigh->lock);
12081da177e4SLinus Torvalds 
1209a5736eddSMenglong Dong 			kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED);
12101da177e4SLinus Torvalds 			return 1;
12111da177e4SLinus Torvalds 		}
12121da177e4SLinus Torvalds 	} else if (neigh->nud_state & NUD_STALE) {
1213d5d427cdSJoe Perches 		neigh_dbg(2, "neigh %p is delayed\n", neigh);
1214071c3798SLorenzo Bianconi 		neigh_del_timer(neigh);
1215b071af52SEric Dumazet 		WRITE_ONCE(neigh->nud_state, NUD_DELAY);
1216955aaa2fSYOSHIFUJI Hideaki 		neigh->updated = jiffies;
12171f9248e5SJiri Pirko 		neigh_add_timer(neigh, jiffies +
12181f9248e5SJiri Pirko 				NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME));
12191da177e4SLinus Torvalds 	}
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	if (neigh->nud_state == NUD_INCOMPLETE) {
12221da177e4SLinus Torvalds 		if (skb) {
12238b5c171bSEric Dumazet 			while (neigh->arp_queue_len_bytes + skb->truesize >
12241f9248e5SJiri Pirko 			       NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) {
12251da177e4SLinus Torvalds 				struct sk_buff *buff;
12268b5c171bSEric Dumazet 
1227f72051b0SDavid S. Miller 				buff = __skb_dequeue(&neigh->arp_queue);
12288b5c171bSEric Dumazet 				if (!buff)
12298b5c171bSEric Dumazet 					break;
12308b5c171bSEric Dumazet 				neigh->arp_queue_len_bytes -= buff->truesize;
1231a5736eddSMenglong Dong 				kfree_skb_reason(buff, SKB_DROP_REASON_NEIGH_QUEUEFULL);
12329a6d276eSNeil Horman 				NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards);
12331da177e4SLinus Torvalds 			}
1234a4731138SEric Dumazet 			skb_dst_force(skb);
12351da177e4SLinus Torvalds 			__skb_queue_tail(&neigh->arp_queue, skb);
12368b5c171bSEric Dumazet 			neigh->arp_queue_len_bytes += skb->truesize;
12371da177e4SLinus Torvalds 		}
12381da177e4SLinus Torvalds 		rc = 1;
12391da177e4SLinus Torvalds 	}
12401da177e4SLinus Torvalds out_unlock_bh:
1241cd28ca0aSEric Dumazet 	if (immediate_probe)
1242cd28ca0aSEric Dumazet 		neigh_probe(neigh);
1243cd28ca0aSEric Dumazet 	else
1244cd28ca0aSEric Dumazet 		write_unlock(&neigh->lock);
1245cd28ca0aSEric Dumazet 	local_bh_enable();
124656dd18a4SRoopa Prabhu 	trace_neigh_event_send_done(neigh, rc);
12471da177e4SLinus Torvalds 	return rc;
12482c51a97fSJulian Anastasov 
12492c51a97fSJulian Anastasov out_dead:
12502c51a97fSJulian Anastasov 	if (neigh->nud_state & NUD_STALE)
12512c51a97fSJulian Anastasov 		goto out_unlock_bh;
12522c51a97fSJulian Anastasov 	write_unlock_bh(&neigh->lock);
1253a5736eddSMenglong Dong 	kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_DEAD);
125456dd18a4SRoopa Prabhu 	trace_neigh_event_send_dead(neigh, 1);
12552c51a97fSJulian Anastasov 	return 1;
12561da177e4SLinus Torvalds }
12570a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(__neigh_event_send);
12581da177e4SLinus Torvalds 
neigh_update_hhs(struct neighbour * neigh)1259f6b72b62SDavid S. Miller static void neigh_update_hhs(struct neighbour *neigh)
12601da177e4SLinus Torvalds {
12611da177e4SLinus Torvalds 	struct hh_cache *hh;
12623b04dddeSStephen Hemminger 	void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *)
126391a72a70SDoug Kehn 		= NULL;
126491a72a70SDoug Kehn 
126591a72a70SDoug Kehn 	if (neigh->dev->header_ops)
126691a72a70SDoug Kehn 		update = neigh->dev->header_ops->cache_update;
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 	if (update) {
1269f6b72b62SDavid S. Miller 		hh = &neigh->hh;
1270c305c6aeSEric Dumazet 		if (READ_ONCE(hh->hh_len)) {
12713644f0ceSStephen Hemminger 			write_seqlock_bh(&hh->hh_lock);
12721da177e4SLinus Torvalds 			update(hh, neigh->dev, neigh->ha);
12733644f0ceSStephen Hemminger 			write_sequnlock_bh(&hh->hh_lock);
12741da177e4SLinus Torvalds 		}
12751da177e4SLinus Torvalds 	}
12761da177e4SLinus Torvalds }
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds /* Generic update routine.
12791da177e4SLinus Torvalds    -- lladdr is new lladdr or NULL, if it is not supplied.
12801da177e4SLinus Torvalds    -- new    is new state.
12811da177e4SLinus Torvalds    -- flags
12821da177e4SLinus Torvalds 	NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr,
12831da177e4SLinus Torvalds 				if it is different.
12841da177e4SLinus Torvalds 	NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected"
12851da177e4SLinus Torvalds 				lladdr instead of overriding it
12861da177e4SLinus Torvalds 				if it is different.
12871da177e4SLinus Torvalds 	NEIGH_UPDATE_F_ADMIN	means that the change is administrative.
12883dc20f47SDaniel Borkmann 	NEIGH_UPDATE_F_USE	means that the entry is user triggered.
12897482e384SDaniel Borkmann 	NEIGH_UPDATE_F_MANAGED	means that the entry will be auto-refreshed.
12901da177e4SLinus Torvalds 	NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing
12911da177e4SLinus Torvalds 				NTF_ROUTER flag.
12921da177e4SLinus Torvalds 	NEIGH_UPDATE_F_ISROUTER	indicates if the neighbour is known as
12931da177e4SLinus Torvalds 				a router.
12941da177e4SLinus Torvalds 
12951da177e4SLinus Torvalds    Caller MUST hold reference count on the entry.
12961da177e4SLinus Torvalds  */
__neigh_update(struct neighbour * neigh,const u8 * lladdr,u8 new,u32 flags,u32 nlmsg_pid,struct netlink_ext_ack * extack)12977a35a50dSDavid Ahern static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
12987a35a50dSDavid Ahern 			  u8 new, u32 flags, u32 nlmsg_pid,
12997a35a50dSDavid Ahern 			  struct netlink_ext_ack *extack)
13001da177e4SLinus Torvalds {
13017482e384SDaniel Borkmann 	bool gc_update = false, managed_update = false;
13021da177e4SLinus Torvalds 	int update_isrouter = 0;
13037482e384SDaniel Borkmann 	struct net_device *dev;
13047482e384SDaniel Borkmann 	int err, notify = 0;
13057482e384SDaniel Borkmann 	u8 old;
13061da177e4SLinus Torvalds 
130756dd18a4SRoopa Prabhu 	trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid);
130856dd18a4SRoopa Prabhu 
13091da177e4SLinus Torvalds 	write_lock_bh(&neigh->lock);
13101da177e4SLinus Torvalds 
13111da177e4SLinus Torvalds 	dev    = neigh->dev;
13121da177e4SLinus Torvalds 	old    = neigh->nud_state;
13131da177e4SLinus Torvalds 	err    = -EPERM;
13141da177e4SLinus Torvalds 
1315eb4e8facSChinmay Agarwal 	if (neigh->dead) {
1316eb4e8facSChinmay Agarwal 		NL_SET_ERR_MSG(extack, "Neighbor entry is now dead");
1317eb4e8facSChinmay Agarwal 		new = old;
1318eb4e8facSChinmay Agarwal 		goto out;
1319eb4e8facSChinmay Agarwal 	}
13201da177e4SLinus Torvalds 	if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
13211da177e4SLinus Torvalds 	    (old & (NUD_NOARP | NUD_PERMANENT)))
13221da177e4SLinus Torvalds 		goto out;
13231da177e4SLinus Torvalds 
13247482e384SDaniel Borkmann 	neigh_update_flags(neigh, flags, &notify, &gc_update, &managed_update);
13257482e384SDaniel Borkmann 	if (flags & (NEIGH_UPDATE_F_USE | NEIGH_UPDATE_F_MANAGED)) {
13263dc20f47SDaniel Borkmann 		new = old & ~NUD_PERMANENT;
1327b071af52SEric Dumazet 		WRITE_ONCE(neigh->nud_state, new);
13283dc20f47SDaniel Borkmann 		err = 0;
13293dc20f47SDaniel Borkmann 		goto out;
13303dc20f47SDaniel Borkmann 	}
13319ce33e46SRoopa Prabhu 
13321da177e4SLinus Torvalds 	if (!(new & NUD_VALID)) {
13331da177e4SLinus Torvalds 		neigh_del_timer(neigh);
13341da177e4SLinus Torvalds 		if (old & NUD_CONNECTED)
13351da177e4SLinus Torvalds 			neigh_suspect(neigh);
1336b071af52SEric Dumazet 		WRITE_ONCE(neigh->nud_state, new);
13371da177e4SLinus Torvalds 		err = 0;
13381da177e4SLinus Torvalds 		notify = old & NUD_VALID;
1339d2fb4fb8SRoopa Prabhu 		if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
13405ef12d98STimo Teras 		    (new & NUD_FAILED)) {
13415ef12d98STimo Teras 			neigh_invalidate(neigh);
13425ef12d98STimo Teras 			notify = 1;
13435ef12d98STimo Teras 		}
13441da177e4SLinus Torvalds 		goto out;
13451da177e4SLinus Torvalds 	}
13461da177e4SLinus Torvalds 
13471da177e4SLinus Torvalds 	/* Compare new lladdr with cached one */
13481da177e4SLinus Torvalds 	if (!dev->addr_len) {
13491da177e4SLinus Torvalds 		/* First case: device needs no address. */
13501da177e4SLinus Torvalds 		lladdr = neigh->ha;
13511da177e4SLinus Torvalds 	} else if (lladdr) {
13521da177e4SLinus Torvalds 		/* The second case: if something is already cached
13531da177e4SLinus Torvalds 		   and a new address is proposed:
13541da177e4SLinus Torvalds 		   - compare new & old
13551da177e4SLinus Torvalds 		   - if they are different, check override flag
13561da177e4SLinus Torvalds 		 */
13571da177e4SLinus Torvalds 		if ((old & NUD_VALID) &&
13581da177e4SLinus Torvalds 		    !memcmp(lladdr, neigh->ha, dev->addr_len))
13591da177e4SLinus Torvalds 			lladdr = neigh->ha;
13601da177e4SLinus Torvalds 	} else {
13611da177e4SLinus Torvalds 		/* No address is supplied; if we know something,
13621da177e4SLinus Torvalds 		   use it, otherwise discard the request.
13631da177e4SLinus Torvalds 		 */
13641da177e4SLinus Torvalds 		err = -EINVAL;
13657a35a50dSDavid Ahern 		if (!(old & NUD_VALID)) {
13667a35a50dSDavid Ahern 			NL_SET_ERR_MSG(extack, "No link layer address given");
13671da177e4SLinus Torvalds 			goto out;
13687a35a50dSDavid Ahern 		}
13691da177e4SLinus Torvalds 		lladdr = neigh->ha;
13701da177e4SLinus Torvalds 	}
13711da177e4SLinus Torvalds 
1372f0e0d044SVasily Khoruzhick 	/* Update confirmed timestamp for neighbour entry after we
1373f0e0d044SVasily Khoruzhick 	 * received ARP packet even if it doesn't change IP to MAC binding.
1374f0e0d044SVasily Khoruzhick 	 */
1375f0e0d044SVasily Khoruzhick 	if (new & NUD_CONNECTED)
1376f0e0d044SVasily Khoruzhick 		neigh->confirmed = jiffies;
1377f0e0d044SVasily Khoruzhick 
13781da177e4SLinus Torvalds 	/* If entry was valid and address is not changed,
13791da177e4SLinus Torvalds 	   do not change entry state, if new one is STALE.
13801da177e4SLinus Torvalds 	 */
13811da177e4SLinus Torvalds 	err = 0;
13821da177e4SLinus Torvalds 	update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
13831da177e4SLinus Torvalds 	if (old & NUD_VALID) {
13841da177e4SLinus Torvalds 		if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) {
13851da177e4SLinus Torvalds 			update_isrouter = 0;
13861da177e4SLinus Torvalds 			if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) &&
13871da177e4SLinus Torvalds 			    (old & NUD_CONNECTED)) {
13881da177e4SLinus Torvalds 				lladdr = neigh->ha;
13891da177e4SLinus Torvalds 				new = NUD_STALE;
13901da177e4SLinus Torvalds 			} else
13911da177e4SLinus Torvalds 				goto out;
13921da177e4SLinus Torvalds 		} else {
13930e7bbcc1SJulian Anastasov 			if (lladdr == neigh->ha && new == NUD_STALE &&
13940e7bbcc1SJulian Anastasov 			    !(flags & NEIGH_UPDATE_F_ADMIN))
13951da177e4SLinus Torvalds 				new = old;
13961da177e4SLinus Torvalds 		}
13971da177e4SLinus Torvalds 	}
13981da177e4SLinus Torvalds 
1399f0e0d044SVasily Khoruzhick 	/* Update timestamp only once we know we will make a change to the
140077d71233SIhar Hrachyshka 	 * neighbour entry. Otherwise we risk to move the locktime window with
140177d71233SIhar Hrachyshka 	 * noop updates and ignore relevant ARP updates.
140277d71233SIhar Hrachyshka 	 */
1403f0e0d044SVasily Khoruzhick 	if (new != old || lladdr != neigh->ha)
140477d71233SIhar Hrachyshka 		neigh->updated = jiffies;
140577d71233SIhar Hrachyshka 
14061da177e4SLinus Torvalds 	if (new != old) {
14071da177e4SLinus Torvalds 		neigh_del_timer(neigh);
1408765c9c63SErik Kline 		if (new & NUD_PROBE)
1409765c9c63SErik Kline 			atomic_set(&neigh->probes, 0);
1410a43d8994SPavel Emelyanov 		if (new & NUD_IN_TIMER)
1411667347f1SDavid S. Miller 			neigh_add_timer(neigh, (jiffies +
14121da177e4SLinus Torvalds 						((new & NUD_REACHABLE) ?
1413667347f1SDavid S. Miller 						 neigh->parms->reachable_time :
1414667347f1SDavid S. Miller 						 0)));
1415b071af52SEric Dumazet 		WRITE_ONCE(neigh->nud_state, new);
141653385d2dSBob Gilligan 		notify = 1;
14171da177e4SLinus Torvalds 	}
14181da177e4SLinus Torvalds 
14191da177e4SLinus Torvalds 	if (lladdr != neigh->ha) {
14200ed8ddf4SEric Dumazet 		write_seqlock(&neigh->ha_lock);
14211da177e4SLinus Torvalds 		memcpy(&neigh->ha, lladdr, dev->addr_len);
14220ed8ddf4SEric Dumazet 		write_sequnlock(&neigh->ha_lock);
14231da177e4SLinus Torvalds 		neigh_update_hhs(neigh);
14241da177e4SLinus Torvalds 		if (!(new & NUD_CONNECTED))
14251da177e4SLinus Torvalds 			neigh->confirmed = jiffies -
14261f9248e5SJiri Pirko 				      (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1);
14271da177e4SLinus Torvalds 		notify = 1;
14281da177e4SLinus Torvalds 	}
14291da177e4SLinus Torvalds 	if (new == old)
14301da177e4SLinus Torvalds 		goto out;
14311da177e4SLinus Torvalds 	if (new & NUD_CONNECTED)
14321da177e4SLinus Torvalds 		neigh_connect(neigh);
14331da177e4SLinus Torvalds 	else
14341da177e4SLinus Torvalds 		neigh_suspect(neigh);
14351da177e4SLinus Torvalds 	if (!(old & NUD_VALID)) {
14361da177e4SLinus Torvalds 		struct sk_buff *skb;
14371da177e4SLinus Torvalds 
14381da177e4SLinus Torvalds 		/* Again: avoid dead loop if something went wrong */
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds 		while (neigh->nud_state & NUD_VALID &&
14411da177e4SLinus Torvalds 		       (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
144269cce1d1SDavid S. Miller 			struct dst_entry *dst = skb_dst(skb);
144369cce1d1SDavid S. Miller 			struct neighbour *n2, *n1 = neigh;
14441da177e4SLinus Torvalds 			write_unlock_bh(&neigh->lock);
1445e049f288Sroy.qing.li@gmail.com 
1446e049f288Sroy.qing.li@gmail.com 			rcu_read_lock();
144713a43d94SDavid S. Miller 
144813a43d94SDavid S. Miller 			/* Why not just use 'neigh' as-is?  The problem is that
144913a43d94SDavid S. Miller 			 * things such as shaper, eql, and sch_teql can end up
145013a43d94SDavid S. Miller 			 * using alternative, different, neigh objects to output
145113a43d94SDavid S. Miller 			 * the packet in the output path.  So what we need to do
145213a43d94SDavid S. Miller 			 * here is re-lookup the top-level neigh in the path so
145313a43d94SDavid S. Miller 			 * we can reinject the packet there.
145413a43d94SDavid S. Miller 			 */
145513a43d94SDavid S. Miller 			n2 = NULL;
1456d47ec7a0STong Zhu 			if (dst && dst->obsolete != DST_OBSOLETE_DEAD) {
145713a43d94SDavid S. Miller 				n2 = dst_neigh_lookup_skb(dst, skb);
145813a43d94SDavid S. Miller 				if (n2)
145969cce1d1SDavid S. Miller 					n1 = n2;
146013a43d94SDavid S. Miller 			}
14615baa0433SEric Dumazet 			READ_ONCE(n1->output)(n1, skb);
146213a43d94SDavid S. Miller 			if (n2)
146313a43d94SDavid S. Miller 				neigh_release(n2);
1464e049f288Sroy.qing.li@gmail.com 			rcu_read_unlock();
1465e049f288Sroy.qing.li@gmail.com 
14661da177e4SLinus Torvalds 			write_lock_bh(&neigh->lock);
14671da177e4SLinus Torvalds 		}
1468c9ab4d85SEric Dumazet 		__skb_queue_purge(&neigh->arp_queue);
14698b5c171bSEric Dumazet 		neigh->arp_queue_len_bytes = 0;
14701da177e4SLinus Torvalds 	}
14711da177e4SLinus Torvalds out:
1472fc6e8073SRoopa Prabhu 	if (update_isrouter)
1473fc6e8073SRoopa Prabhu 		neigh_update_is_router(neigh, flags, &notify);
14741da177e4SLinus Torvalds 	write_unlock_bh(&neigh->lock);
14757482e384SDaniel Borkmann 	if (((new ^ old) & NUD_PERMANENT) || gc_update)
14769c29a2f5SDavid Ahern 		neigh_update_gc_list(neigh);
14777482e384SDaniel Borkmann 	if (managed_update)
14787482e384SDaniel Borkmann 		neigh_update_managed_list(neigh);
14798d71740cSTom Tucker 	if (notify)
14807b8f7a40SRoopa Prabhu 		neigh_update_notify(neigh, nlmsg_pid);
148156dd18a4SRoopa Prabhu 	trace_neigh_update_done(neigh, err);
14821da177e4SLinus Torvalds 	return err;
14831da177e4SLinus Torvalds }
14847a35a50dSDavid Ahern 
neigh_update(struct neighbour * neigh,const u8 * lladdr,u8 new,u32 flags,u32 nlmsg_pid)14857a35a50dSDavid Ahern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
14867a35a50dSDavid Ahern 		 u32 flags, u32 nlmsg_pid)
14877a35a50dSDavid Ahern {
14887a35a50dSDavid Ahern 	return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL);
14897a35a50dSDavid Ahern }
14900a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_update);
14911da177e4SLinus Torvalds 
14927e980569SJiri Benc /* Update the neigh to listen temporarily for probe responses, even if it is
14937e980569SJiri Benc  * in a NUD_FAILED state. The caller has to hold neigh->lock for writing.
14947e980569SJiri Benc  */
__neigh_set_probe_once(struct neighbour * neigh)14957e980569SJiri Benc void __neigh_set_probe_once(struct neighbour *neigh)
14967e980569SJiri Benc {
14972c51a97fSJulian Anastasov 	if (neigh->dead)
14982c51a97fSJulian Anastasov 		return;
14997e980569SJiri Benc 	neigh->updated = jiffies;
15007e980569SJiri Benc 	if (!(neigh->nud_state & NUD_FAILED))
15017e980569SJiri Benc 		return;
1502b071af52SEric Dumazet 	WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE);
15032176d5d4SDuan Jiong 	atomic_set(&neigh->probes, neigh_max_probes(neigh));
15047e980569SJiri Benc 	neigh_add_timer(neigh,
150519e16d22SHangbin Liu 			jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
150619e16d22SHangbin Liu 				      HZ/100));
15077e980569SJiri Benc }
15087e980569SJiri Benc EXPORT_SYMBOL(__neigh_set_probe_once);
15097e980569SJiri Benc 
neigh_event_ns(struct neigh_table * tbl,u8 * lladdr,void * saddr,struct net_device * dev)15101da177e4SLinus Torvalds struct neighbour *neigh_event_ns(struct neigh_table *tbl,
15111da177e4SLinus Torvalds 				 u8 *lladdr, void *saddr,
15121da177e4SLinus Torvalds 				 struct net_device *dev)
15131da177e4SLinus Torvalds {
15141da177e4SLinus Torvalds 	struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
15151da177e4SLinus Torvalds 						 lladdr || !dev->addr_len);
15161da177e4SLinus Torvalds 	if (neigh)
15171da177e4SLinus Torvalds 		neigh_update(neigh, lladdr, NUD_STALE,
15187b8f7a40SRoopa Prabhu 			     NEIGH_UPDATE_F_OVERRIDE, 0);
15191da177e4SLinus Torvalds 	return neigh;
15201da177e4SLinus Torvalds }
15210a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_event_ns);
15221da177e4SLinus Torvalds 
152334d101ddSEric Dumazet /* called with read_lock_bh(&n->lock); */
neigh_hh_init(struct neighbour * n)1524bdf53c58SEric W. Biederman static void neigh_hh_init(struct neighbour *n)
15251da177e4SLinus Torvalds {
1526bdf53c58SEric W. Biederman 	struct net_device *dev = n->dev;
1527bdf53c58SEric W. Biederman 	__be16 prot = n->tbl->protocol;
1528f6b72b62SDavid S. Miller 	struct hh_cache	*hh = &n->hh;
15290ed8ddf4SEric Dumazet 
15300ed8ddf4SEric Dumazet 	write_lock_bh(&n->lock);
153134d101ddSEric Dumazet 
1532f6b72b62SDavid S. Miller 	/* Only one thread can come in here and initialize the
1533f6b72b62SDavid S. Miller 	 * hh_cache entry.
1534f6b72b62SDavid S. Miller 	 */
1535b23b5455SDavid S. Miller 	if (!hh->hh_len)
1536b23b5455SDavid S. Miller 		dev->header_ops->cache(n, hh, prot);
1537f6b72b62SDavid S. Miller 
15380ed8ddf4SEric Dumazet 	write_unlock_bh(&n->lock);
15391da177e4SLinus Torvalds }
15401da177e4SLinus Torvalds 
15411da177e4SLinus Torvalds /* Slow and careful. */
15421da177e4SLinus Torvalds 
neigh_resolve_output(struct neighbour * neigh,struct sk_buff * skb)15438f40b161SDavid S. Miller int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
15441da177e4SLinus Torvalds {
15451da177e4SLinus Torvalds 	int rc = 0;
15461da177e4SLinus Torvalds 
15471da177e4SLinus Torvalds 	if (!neigh_event_send(neigh, skb)) {
15481da177e4SLinus Torvalds 		int err;
15491da177e4SLinus Torvalds 		struct net_device *dev = neigh->dev;
15500ed8ddf4SEric Dumazet 		unsigned int seq;
155134d101ddSEric Dumazet 
1552c305c6aeSEric Dumazet 		if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len))
1553bdf53c58SEric W. Biederman 			neigh_hh_init(neigh);
155434d101ddSEric Dumazet 
15550ed8ddf4SEric Dumazet 		do {
1556e1f16503Sramesh.nagappa@gmail.com 			__skb_pull(skb, skb_network_offset(skb));
15570ed8ddf4SEric Dumazet 			seq = read_seqbegin(&neigh->ha_lock);
15580c4e8581SStephen Hemminger 			err = dev_hard_header(skb, dev, ntohs(skb->protocol),
15591da177e4SLinus Torvalds 					      neigh->ha, NULL, skb->len);
15600ed8ddf4SEric Dumazet 		} while (read_seqretry(&neigh->ha_lock, seq));
156134d101ddSEric Dumazet 
15621da177e4SLinus Torvalds 		if (err >= 0)
1563542d4d68SDavid S. Miller 			rc = dev_queue_xmit(skb);
15641da177e4SLinus Torvalds 		else
15651da177e4SLinus Torvalds 			goto out_kfree_skb;
15661da177e4SLinus Torvalds 	}
15671da177e4SLinus Torvalds out:
15681da177e4SLinus Torvalds 	return rc;
15691da177e4SLinus Torvalds out_kfree_skb:
15701da177e4SLinus Torvalds 	rc = -EINVAL;
15711da177e4SLinus Torvalds 	kfree_skb(skb);
15721da177e4SLinus Torvalds 	goto out;
15731da177e4SLinus Torvalds }
15740a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_resolve_output);
15751da177e4SLinus Torvalds 
15761da177e4SLinus Torvalds /* As fast as possible without hh cache */
15771da177e4SLinus Torvalds 
neigh_connected_output(struct neighbour * neigh,struct sk_buff * skb)15788f40b161SDavid S. Miller int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb)
15791da177e4SLinus Torvalds {
15801da177e4SLinus Torvalds 	struct net_device *dev = neigh->dev;
15810ed8ddf4SEric Dumazet 	unsigned int seq;
15828f40b161SDavid S. Miller 	int err;
15831da177e4SLinus Torvalds 
15840ed8ddf4SEric Dumazet 	do {
1585e1f16503Sramesh.nagappa@gmail.com 		__skb_pull(skb, skb_network_offset(skb));
15860ed8ddf4SEric Dumazet 		seq = read_seqbegin(&neigh->ha_lock);
15870c4e8581SStephen Hemminger 		err = dev_hard_header(skb, dev, ntohs(skb->protocol),
15881da177e4SLinus Torvalds 				      neigh->ha, NULL, skb->len);
15890ed8ddf4SEric Dumazet 	} while (read_seqretry(&neigh->ha_lock, seq));
15900ed8ddf4SEric Dumazet 
15911da177e4SLinus Torvalds 	if (err >= 0)
1592542d4d68SDavid S. Miller 		err = dev_queue_xmit(skb);
15931da177e4SLinus Torvalds 	else {
15941da177e4SLinus Torvalds 		err = -EINVAL;
15951da177e4SLinus Torvalds 		kfree_skb(skb);
15961da177e4SLinus Torvalds 	}
15971da177e4SLinus Torvalds 	return err;
15981da177e4SLinus Torvalds }
15990a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_connected_output);
16001da177e4SLinus Torvalds 
neigh_direct_output(struct neighbour * neigh,struct sk_buff * skb)16018f40b161SDavid S. Miller int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb)
16028f40b161SDavid S. Miller {
16038f40b161SDavid S. Miller 	return dev_queue_xmit(skb);
16048f40b161SDavid S. Miller }
16058f40b161SDavid S. Miller EXPORT_SYMBOL(neigh_direct_output);
16068f40b161SDavid S. Miller 
neigh_managed_work(struct work_struct * work)16077482e384SDaniel Borkmann static void neigh_managed_work(struct work_struct *work)
16087482e384SDaniel Borkmann {
16097482e384SDaniel Borkmann 	struct neigh_table *tbl = container_of(work, struct neigh_table,
16107482e384SDaniel Borkmann 					       managed_work.work);
16117482e384SDaniel Borkmann 	struct neighbour *neigh;
16127482e384SDaniel Borkmann 
16137482e384SDaniel Borkmann 	write_lock_bh(&tbl->lock);
16147482e384SDaniel Borkmann 	list_for_each_entry(neigh, &tbl->managed_list, managed_list)
16154a81f6daSDaniel Borkmann 		neigh_event_send_probe(neigh, NULL, false);
16167482e384SDaniel Borkmann 	queue_delayed_work(system_power_efficient_wq, &tbl->managed_work,
1617211da42eSYuwei Wang 			   NEIGH_VAR(&tbl->parms, INTERVAL_PROBE_TIME_MS));
16187482e384SDaniel Borkmann 	write_unlock_bh(&tbl->lock);
16197482e384SDaniel Borkmann }
16207482e384SDaniel Borkmann 
neigh_proxy_process(struct timer_list * t)1621e99e88a9SKees Cook static void neigh_proxy_process(struct timer_list *t)
16221da177e4SLinus Torvalds {
1623e99e88a9SKees Cook 	struct neigh_table *tbl = from_timer(tbl, t, proxy_timer);
16241da177e4SLinus Torvalds 	long sched_next = 0;
16251da177e4SLinus Torvalds 	unsigned long now = jiffies;
1626f72051b0SDavid S. Miller 	struct sk_buff *skb, *n;
16271da177e4SLinus Torvalds 
16281da177e4SLinus Torvalds 	spin_lock(&tbl->proxy_queue.lock);
16291da177e4SLinus Torvalds 
1630f72051b0SDavid S. Miller 	skb_queue_walk_safe(&tbl->proxy_queue, skb, n) {
1631f72051b0SDavid S. Miller 		long tdif = NEIGH_CB(skb)->sched_next - now;
16321da177e4SLinus Torvalds 
16331da177e4SLinus Torvalds 		if (tdif <= 0) {
1634f72051b0SDavid S. Miller 			struct net_device *dev = skb->dev;
163520e6074eSEric Dumazet 
16368207f253SThomas Zeitlhofer 			neigh_parms_qlen_dec(dev, tbl->family);
1637f72051b0SDavid S. Miller 			__skb_unlink(skb, &tbl->proxy_queue);
16380ff4eb3dSAlexander Mikhalitsyn 
163920e6074eSEric Dumazet 			if (tbl->proxy_redo && netif_running(dev)) {
164020e6074eSEric Dumazet 				rcu_read_lock();
1641f72051b0SDavid S. Miller 				tbl->proxy_redo(skb);
164220e6074eSEric Dumazet 				rcu_read_unlock();
164320e6074eSEric Dumazet 			} else {
1644f72051b0SDavid S. Miller 				kfree_skb(skb);
164520e6074eSEric Dumazet 			}
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds 			dev_put(dev);
16481da177e4SLinus Torvalds 		} else if (!sched_next || tdif < sched_next)
16491da177e4SLinus Torvalds 			sched_next = tdif;
16501da177e4SLinus Torvalds 	}
16511da177e4SLinus Torvalds 	del_timer(&tbl->proxy_timer);
16521da177e4SLinus Torvalds 	if (sched_next)
16531da177e4SLinus Torvalds 		mod_timer(&tbl->proxy_timer, jiffies + sched_next);
16541da177e4SLinus Torvalds 	spin_unlock(&tbl->proxy_queue.lock);
16551da177e4SLinus Torvalds }
16561da177e4SLinus Torvalds 
neigh_proxy_delay(struct neigh_parms * p)165762e395f8SBrian Haley static unsigned long neigh_proxy_delay(struct neigh_parms *p)
165862e395f8SBrian Haley {
165962e395f8SBrian Haley 	/* If proxy_delay is zero, do not call get_random_u32_below()
166062e395f8SBrian Haley 	 * as it is undefined behavior.
166162e395f8SBrian Haley 	 */
166262e395f8SBrian Haley 	unsigned long proxy_delay = NEIGH_VAR(p, PROXY_DELAY);
166362e395f8SBrian Haley 
166462e395f8SBrian Haley 	return proxy_delay ?
166562e395f8SBrian Haley 	       jiffies + get_random_u32_below(proxy_delay) : jiffies;
166662e395f8SBrian Haley }
166762e395f8SBrian Haley 
pneigh_enqueue(struct neigh_table * tbl,struct neigh_parms * p,struct sk_buff * skb)16681da177e4SLinus Torvalds void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
16691da177e4SLinus Torvalds 		    struct sk_buff *skb)
16701da177e4SLinus Torvalds {
167162e395f8SBrian Haley 	unsigned long sched_next = neigh_proxy_delay(p);
16721da177e4SLinus Torvalds 
16730ff4eb3dSAlexander Mikhalitsyn 	if (p->qlen > NEIGH_VAR(p, PROXY_QLEN)) {
16741da177e4SLinus Torvalds 		kfree_skb(skb);
16751da177e4SLinus Torvalds 		return;
16761da177e4SLinus Torvalds 	}
1677a61bbcf2SPatrick McHardy 
1678a61bbcf2SPatrick McHardy 	NEIGH_CB(skb)->sched_next = sched_next;
1679a61bbcf2SPatrick McHardy 	NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED;
16801da177e4SLinus Torvalds 
16811da177e4SLinus Torvalds 	spin_lock(&tbl->proxy_queue.lock);
16821da177e4SLinus Torvalds 	if (del_timer(&tbl->proxy_timer)) {
16831da177e4SLinus Torvalds 		if (time_before(tbl->proxy_timer.expires, sched_next))
16841da177e4SLinus Torvalds 			sched_next = tbl->proxy_timer.expires;
16851da177e4SLinus Torvalds 	}
1686adf30907SEric Dumazet 	skb_dst_drop(skb);
16871da177e4SLinus Torvalds 	dev_hold(skb->dev);
16881da177e4SLinus Torvalds 	__skb_queue_tail(&tbl->proxy_queue, skb);
16890ff4eb3dSAlexander Mikhalitsyn 	p->qlen++;
16901da177e4SLinus Torvalds 	mod_timer(&tbl->proxy_timer, sched_next);
16911da177e4SLinus Torvalds 	spin_unlock(&tbl->proxy_queue.lock);
16921da177e4SLinus Torvalds }
16930a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_enqueue);
16941da177e4SLinus Torvalds 
lookup_neigh_parms(struct neigh_table * tbl,struct net * net,int ifindex)169597fd5bc7STobias Klauser static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
1696426b5303SEric W. Biederman 						      struct net *net, int ifindex)
1697426b5303SEric W. Biederman {
1698426b5303SEric W. Biederman 	struct neigh_parms *p;
1699426b5303SEric W. Biederman 
170075fbfd33SNicolas Dichtel 	list_for_each_entry(p, &tbl->parms_list, list) {
1701878628fbSYOSHIFUJI Hideaki 		if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
1702170d6f99SGao feng 		    (!p->dev && !ifindex && net_eq(net, &init_net)))
1703426b5303SEric W. Biederman 			return p;
1704426b5303SEric W. Biederman 	}
1705426b5303SEric W. Biederman 
1706426b5303SEric W. Biederman 	return NULL;
1707426b5303SEric W. Biederman }
17081da177e4SLinus Torvalds 
neigh_parms_alloc(struct net_device * dev,struct neigh_table * tbl)17091da177e4SLinus Torvalds struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
17101da177e4SLinus Torvalds 				      struct neigh_table *tbl)
17111da177e4SLinus Torvalds {
1712cf89d6b2SGao feng 	struct neigh_parms *p;
171300829823SStephen Hemminger 	struct net *net = dev_net(dev);
171400829823SStephen Hemminger 	const struct net_device_ops *ops = dev->netdev_ops;
17151da177e4SLinus Torvalds 
1716cf89d6b2SGao feng 	p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL);
17171da177e4SLinus Torvalds 	if (p) {
17181da177e4SLinus Torvalds 		p->tbl		  = tbl;
17196343944bSReshetova, Elena 		refcount_set(&p->refcnt, 1);
17201da177e4SLinus Torvalds 		p->reachable_time =
17211f9248e5SJiri Pirko 				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
17220ff4eb3dSAlexander Mikhalitsyn 		p->qlen = 0;
1723d62607c3SJakub Kicinski 		netdev_hold(dev, &p->dev_tracker, GFP_KERNEL);
1724c7fb64dbSThomas Graf 		p->dev = dev;
1725efd7ef1cSEric W. Biederman 		write_pnet(&p->net, net);
17261da177e4SLinus Torvalds 		p->sysctl_table = NULL;
172763134803SVeaceslav Falico 
172863134803SVeaceslav Falico 		if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) {
1729d62607c3SJakub Kicinski 			netdev_put(dev, &p->dev_tracker);
173063134803SVeaceslav Falico 			kfree(p);
173163134803SVeaceslav Falico 			return NULL;
173263134803SVeaceslav Falico 		}
173363134803SVeaceslav Falico 
17341da177e4SLinus Torvalds 		write_lock_bh(&tbl->lock);
173575fbfd33SNicolas Dichtel 		list_add(&p->list, &tbl->parms.list);
17361da177e4SLinus Torvalds 		write_unlock_bh(&tbl->lock);
17371d4c8c29SJiri Pirko 
17381d4c8c29SJiri Pirko 		neigh_parms_data_state_cleanall(p);
17391da177e4SLinus Torvalds 	}
17401da177e4SLinus Torvalds 	return p;
17411da177e4SLinus Torvalds }
17420a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_alloc);
17431da177e4SLinus Torvalds 
neigh_rcu_free_parms(struct rcu_head * head)17441da177e4SLinus Torvalds static void neigh_rcu_free_parms(struct rcu_head *head)
17451da177e4SLinus Torvalds {
17461da177e4SLinus Torvalds 	struct neigh_parms *parms =
17471da177e4SLinus Torvalds 		container_of(head, struct neigh_parms, rcu_head);
17481da177e4SLinus Torvalds 
17491da177e4SLinus Torvalds 	neigh_parms_put(parms);
17501da177e4SLinus Torvalds }
17511da177e4SLinus Torvalds 
neigh_parms_release(struct neigh_table * tbl,struct neigh_parms * parms)17521da177e4SLinus Torvalds void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
17531da177e4SLinus Torvalds {
17541da177e4SLinus Torvalds 	if (!parms || parms == &tbl->parms)
17551da177e4SLinus Torvalds 		return;
17561da177e4SLinus Torvalds 	write_lock_bh(&tbl->lock);
175775fbfd33SNicolas Dichtel 	list_del(&parms->list);
17581da177e4SLinus Torvalds 	parms->dead = 1;
17591da177e4SLinus Torvalds 	write_unlock_bh(&tbl->lock);
1760d62607c3SJakub Kicinski 	netdev_put(parms->dev, &parms->dev_tracker);
17611da177e4SLinus Torvalds 	call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
17621da177e4SLinus Torvalds }
17630a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_release);
17641da177e4SLinus Torvalds 
neigh_parms_destroy(struct neigh_parms * parms)176506f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms)
17661da177e4SLinus Torvalds {
17671da177e4SLinus Torvalds 	kfree(parms);
17681da177e4SLinus Torvalds }
17691da177e4SLinus Torvalds 
1770c2ecba71SPavel Emelianov static struct lock_class_key neigh_table_proxy_queue_class;
1771c2ecba71SPavel Emelianov 
1772d7480fd3SWANG Cong static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly;
1773d7480fd3SWANG Cong 
neigh_table_init(int index,struct neigh_table * tbl)1774d7480fd3SWANG Cong void neigh_table_init(int index, struct neigh_table *tbl)
17751da177e4SLinus Torvalds {
17761da177e4SLinus Torvalds 	unsigned long now = jiffies;
17771da177e4SLinus Torvalds 	unsigned long phsize;
17781da177e4SLinus Torvalds 
177975fbfd33SNicolas Dichtel 	INIT_LIST_HEAD(&tbl->parms_list);
178058956317SDavid Ahern 	INIT_LIST_HEAD(&tbl->gc_list);
17817482e384SDaniel Borkmann 	INIT_LIST_HEAD(&tbl->managed_list);
17827482e384SDaniel Borkmann 
178375fbfd33SNicolas Dichtel 	list_add(&tbl->parms.list, &tbl->parms_list);
1784e42ea986SEric Dumazet 	write_pnet(&tbl->parms.net, &init_net);
17856343944bSReshetova, Elena 	refcount_set(&tbl->parms.refcnt, 1);
17861da177e4SLinus Torvalds 	tbl->parms.reachable_time =
17871f9248e5SJiri Pirko 			  neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME));
17880ff4eb3dSAlexander Mikhalitsyn 	tbl->parms.qlen = 0;
17891da177e4SLinus Torvalds 
17901da177e4SLinus Torvalds 	tbl->stats = alloc_percpu(struct neigh_statistics);
17911da177e4SLinus Torvalds 	if (!tbl->stats)
17921da177e4SLinus Torvalds 		panic("cannot create neighbour cache statistics");
17931da177e4SLinus Torvalds 
17941da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
179571a5053aSChristoph Hellwig 	if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat,
179671a5053aSChristoph Hellwig 			      &neigh_stat_seq_ops, tbl))
17971da177e4SLinus Torvalds 		panic("cannot create neighbour proc dir entry");
17981da177e4SLinus Torvalds #endif
17991da177e4SLinus Torvalds 
1800cd089336SDavid S. Miller 	RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
18011da177e4SLinus Torvalds 
18021da177e4SLinus Torvalds 	phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
180377d04bd9SAndrew Morton 	tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
18041da177e4SLinus Torvalds 
1805d6bf7817SEric Dumazet 	if (!tbl->nht || !tbl->phash_buckets)
18061da177e4SLinus Torvalds 		panic("cannot allocate neighbour cache hashes");
18071da177e4SLinus Torvalds 
180808433effSYOSHIFUJI Hideaki / 吉藤英明 	if (!tbl->entry_size)
180908433effSYOSHIFUJI Hideaki / 吉藤英明 		tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
181008433effSYOSHIFUJI Hideaki / 吉藤英明 					tbl->key_len, NEIGH_PRIV_ALIGN);
181108433effSYOSHIFUJI Hideaki / 吉藤英明 	else
181208433effSYOSHIFUJI Hideaki / 吉藤英明 		WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN);
181308433effSYOSHIFUJI Hideaki / 吉藤英明 
18141da177e4SLinus Torvalds 	rwlock_init(&tbl->lock);
18157482e384SDaniel Borkmann 
1816203b42f7STejun Heo 	INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
1817f618002bSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
1818f618002bSviresh kumar 			tbl->parms.reachable_time);
18197482e384SDaniel Borkmann 	INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work);
18207482e384SDaniel Borkmann 	queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0);
18217482e384SDaniel Borkmann 
1822e99e88a9SKees Cook 	timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0);
1823c2ecba71SPavel Emelianov 	skb_queue_head_init_class(&tbl->proxy_queue,
1824c2ecba71SPavel Emelianov 			&neigh_table_proxy_queue_class);
18251da177e4SLinus Torvalds 
18261da177e4SLinus Torvalds 	tbl->last_flush = now;
18271da177e4SLinus Torvalds 	tbl->last_rand	= now + tbl->parms.reachable_time * 20;
1828bd89efc5SSimon Kelley 
1829d7480fd3SWANG Cong 	neigh_tables[index] = tbl;
18301da177e4SLinus Torvalds }
18310a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_init);
18321da177e4SLinus Torvalds 
neigh_table_clear(int index,struct neigh_table * tbl)1833d7480fd3SWANG Cong int neigh_table_clear(int index, struct neigh_table *tbl)
18341da177e4SLinus Torvalds {
1835d7480fd3SWANG Cong 	neigh_tables[index] = NULL;
18361da177e4SLinus Torvalds 	/* It is not clean... Fix it to unload IPv6 module safely */
18374177d5b0SDaniel Borkmann 	cancel_delayed_work_sync(&tbl->managed_work);
1838a5c30b34STejun Heo 	cancel_delayed_work_sync(&tbl->gc_work);
18391da177e4SLinus Torvalds 	del_timer_sync(&tbl->proxy_timer);
18408207f253SThomas Zeitlhofer 	pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family);
18411da177e4SLinus Torvalds 	neigh_ifdown(tbl, NULL);
18421da177e4SLinus Torvalds 	if (atomic_read(&tbl->entries))
1843e005d193SJoe Perches 		pr_crit("neighbour leakage\n");
18441da177e4SLinus Torvalds 
18456193d2beSEric Dumazet 	call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
18466193d2beSEric Dumazet 		 neigh_hash_free_rcu);
1847d6bf7817SEric Dumazet 	tbl->nht = NULL;
18481da177e4SLinus Torvalds 
18491da177e4SLinus Torvalds 	kfree(tbl->phash_buckets);
18501da177e4SLinus Torvalds 	tbl->phash_buckets = NULL;
18511da177e4SLinus Torvalds 
18523f192b5cSAlexey Dobriyan 	remove_proc_entry(tbl->id, init_net.proc_net_stat);
18533f192b5cSAlexey Dobriyan 
18543fcde74bSKirill Korotaev 	free_percpu(tbl->stats);
18553fcde74bSKirill Korotaev 	tbl->stats = NULL;
18563fcde74bSKirill Korotaev 
18571da177e4SLinus Torvalds 	return 0;
18581da177e4SLinus Torvalds }
18590a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_clear);
18601da177e4SLinus Torvalds 
neigh_find_table(int family)1861d7480fd3SWANG Cong static struct neigh_table *neigh_find_table(int family)
1862d7480fd3SWANG Cong {
1863d7480fd3SWANG Cong 	struct neigh_table *tbl = NULL;
1864d7480fd3SWANG Cong 
1865d7480fd3SWANG Cong 	switch (family) {
1866d7480fd3SWANG Cong 	case AF_INET:
1867d7480fd3SWANG Cong 		tbl = neigh_tables[NEIGH_ARP_TABLE];
1868d7480fd3SWANG Cong 		break;
1869d7480fd3SWANG Cong 	case AF_INET6:
1870d7480fd3SWANG Cong 		tbl = neigh_tables[NEIGH_ND_TABLE];
1871d7480fd3SWANG Cong 		break;
1872d7480fd3SWANG Cong 	}
1873d7480fd3SWANG Cong 
1874d7480fd3SWANG Cong 	return tbl;
1875d7480fd3SWANG Cong }
1876d7480fd3SWANG Cong 
187782cbb5c6SRoopa Prabhu const struct nla_policy nda_policy[NDA_MAX+1] = {
18781274e1ccSRoopa Prabhu 	[NDA_UNSPEC]		= { .strict_start_type = NDA_NH_ID },
187982cbb5c6SRoopa Prabhu 	[NDA_DST]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
188082cbb5c6SRoopa Prabhu 	[NDA_LLADDR]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
188182cbb5c6SRoopa Prabhu 	[NDA_CACHEINFO]		= { .len = sizeof(struct nda_cacheinfo) },
188282cbb5c6SRoopa Prabhu 	[NDA_PROBES]		= { .type = NLA_U32 },
188382cbb5c6SRoopa Prabhu 	[NDA_VLAN]		= { .type = NLA_U16 },
188482cbb5c6SRoopa Prabhu 	[NDA_PORT]		= { .type = NLA_U16 },
188582cbb5c6SRoopa Prabhu 	[NDA_VNI]		= { .type = NLA_U32 },
188682cbb5c6SRoopa Prabhu 	[NDA_IFINDEX]		= { .type = NLA_U32 },
188782cbb5c6SRoopa Prabhu 	[NDA_MASTER]		= { .type = NLA_U32 },
1888a9cd3439SDavid Ahern 	[NDA_PROTOCOL]		= { .type = NLA_U8 },
18891274e1ccSRoopa Prabhu 	[NDA_NH_ID]		= { .type = NLA_U32 },
1890c8e80c11SDaniel Borkmann 	[NDA_FLAGS_EXT]		= NLA_POLICY_MASK(NLA_U32, NTF_EXT_MASK),
1891899426b3SNikolay Aleksandrov 	[NDA_FDB_EXT_ATTRS]	= { .type = NLA_NESTED },
189282cbb5c6SRoopa Prabhu };
189382cbb5c6SRoopa Prabhu 
neigh_delete(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)1894c21ef3e3SDavid Ahern static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
1895c21ef3e3SDavid Ahern 			struct netlink_ext_ack *extack)
18961da177e4SLinus Torvalds {
18973b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1898a14a49d2SThomas Graf 	struct ndmsg *ndm;
1899a14a49d2SThomas Graf 	struct nlattr *dst_attr;
19001da177e4SLinus Torvalds 	struct neigh_table *tbl;
1901d7480fd3SWANG Cong 	struct neighbour *neigh;
19021da177e4SLinus Torvalds 	struct net_device *dev = NULL;
1903a14a49d2SThomas Graf 	int err = -EINVAL;
19041da177e4SLinus Torvalds 
1905110b2499SEric Dumazet 	ASSERT_RTNL();
1906a14a49d2SThomas Graf 	if (nlmsg_len(nlh) < sizeof(*ndm))
19071da177e4SLinus Torvalds 		goto out;
19081da177e4SLinus Torvalds 
1909a14a49d2SThomas Graf 	dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST);
19107a35a50dSDavid Ahern 	if (!dst_attr) {
19117a35a50dSDavid Ahern 		NL_SET_ERR_MSG(extack, "Network address not specified");
1912a14a49d2SThomas Graf 		goto out;
19137a35a50dSDavid Ahern 	}
1914a14a49d2SThomas Graf 
1915a14a49d2SThomas Graf 	ndm = nlmsg_data(nlh);
1916a14a49d2SThomas Graf 	if (ndm->ndm_ifindex) {
1917110b2499SEric Dumazet 		dev = __dev_get_by_index(net, ndm->ndm_ifindex);
1918a14a49d2SThomas Graf 		if (dev == NULL) {
1919a14a49d2SThomas Graf 			err = -ENODEV;
1920a14a49d2SThomas Graf 			goto out;
1921a14a49d2SThomas Graf 		}
1922a14a49d2SThomas Graf 	}
1923a14a49d2SThomas Graf 
1924d7480fd3SWANG Cong 	tbl = neigh_find_table(ndm->ndm_family);
1925d7480fd3SWANG Cong 	if (tbl == NULL)
1926d7480fd3SWANG Cong 		return -EAFNOSUPPORT;
19271da177e4SLinus Torvalds 
19287a35a50dSDavid Ahern 	if (nla_len(dst_attr) < (int)tbl->key_len) {
19297a35a50dSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid network address");
1930110b2499SEric Dumazet 		goto out;
19317a35a50dSDavid Ahern 	}
19321da177e4SLinus Torvalds 
19331da177e4SLinus Torvalds 	if (ndm->ndm_flags & NTF_PROXY) {
1934426b5303SEric W. Biederman 		err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
1935110b2499SEric Dumazet 		goto out;
19361da177e4SLinus Torvalds 	}
19371da177e4SLinus Torvalds 
1938a14a49d2SThomas Graf 	if (dev == NULL)
1939110b2499SEric Dumazet 		goto out;
19401da177e4SLinus Torvalds 
1941a14a49d2SThomas Graf 	neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
1942a14a49d2SThomas Graf 	if (neigh == NULL) {
1943a14a49d2SThomas Graf 		err = -ENOENT;
1944110b2499SEric Dumazet 		goto out;
1945a14a49d2SThomas Graf 	}
1946a14a49d2SThomas Graf 
19477a35a50dSDavid Ahern 	err = __neigh_update(neigh, NULL, NUD_FAILED,
19487a35a50dSDavid Ahern 			     NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN,
19497a35a50dSDavid Ahern 			     NETLINK_CB(skb).portid, extack);
19505071034eSSowmini Varadhan 	write_lock_bh(&tbl->lock);
1951a14a49d2SThomas Graf 	neigh_release(neigh);
19525071034eSSowmini Varadhan 	neigh_remove_one(neigh, tbl);
19535071034eSSowmini Varadhan 	write_unlock_bh(&tbl->lock);
1954a14a49d2SThomas Graf 
19551da177e4SLinus Torvalds out:
19561da177e4SLinus Torvalds 	return err;
19571da177e4SLinus Torvalds }
19581da177e4SLinus Torvalds 
neigh_add(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)1959c21ef3e3SDavid Ahern static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
1960c21ef3e3SDavid Ahern 		     struct netlink_ext_ack *extack)
19611da177e4SLinus Torvalds {
1962f7aa74e4SRoopa Prabhu 	int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE |
1963f7aa74e4SRoopa Prabhu 		    NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
19643b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
19655208debdSThomas Graf 	struct ndmsg *ndm;
19665208debdSThomas Graf 	struct nlattr *tb[NDA_MAX+1];
19671da177e4SLinus Torvalds 	struct neigh_table *tbl;
19681da177e4SLinus Torvalds 	struct net_device *dev = NULL;
1969d7480fd3SWANG Cong 	struct neighbour *neigh;
1970d7480fd3SWANG Cong 	void *dst, *lladdr;
1971df9b0e30SDavid Ahern 	u8 protocol = 0;
19722c611ad9SRoopa Prabhu 	u32 ndm_flags;
19735208debdSThomas Graf 	int err;
19741da177e4SLinus Torvalds 
1975110b2499SEric Dumazet 	ASSERT_RTNL();
19768cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX,
19778cb08174SJohannes Berg 				     nda_policy, extack);
19785208debdSThomas Graf 	if (err < 0)
19791da177e4SLinus Torvalds 		goto out;
19801da177e4SLinus Torvalds 
19815208debdSThomas Graf 	err = -EINVAL;
19827a35a50dSDavid Ahern 	if (!tb[NDA_DST]) {
19837a35a50dSDavid Ahern 		NL_SET_ERR_MSG(extack, "Network address not specified");
19845208debdSThomas Graf 		goto out;
19857a35a50dSDavid Ahern 	}
19865208debdSThomas Graf 
19875208debdSThomas Graf 	ndm = nlmsg_data(nlh);
19882c611ad9SRoopa Prabhu 	ndm_flags = ndm->ndm_flags;
19892c611ad9SRoopa Prabhu 	if (tb[NDA_FLAGS_EXT]) {
19902c611ad9SRoopa Prabhu 		u32 ext = nla_get_u32(tb[NDA_FLAGS_EXT]);
19912c611ad9SRoopa Prabhu 
1992507c2f1dSDaniel Borkmann 		BUILD_BUG_ON(sizeof(neigh->flags) * BITS_PER_BYTE <
1993507c2f1dSDaniel Borkmann 			     (sizeof(ndm->ndm_flags) * BITS_PER_BYTE +
1994507c2f1dSDaniel Borkmann 			      hweight32(NTF_EXT_MASK)));
19952c611ad9SRoopa Prabhu 		ndm_flags |= (ext << NTF_EXT_SHIFT);
19962c611ad9SRoopa Prabhu 	}
19975208debdSThomas Graf 	if (ndm->ndm_ifindex) {
1998110b2499SEric Dumazet 		dev = __dev_get_by_index(net, ndm->ndm_ifindex);
19995208debdSThomas Graf 		if (dev == NULL) {
20005208debdSThomas Graf 			err = -ENODEV;
20015208debdSThomas Graf 			goto out;
20025208debdSThomas Graf 		}
20035208debdSThomas Graf 
20047a35a50dSDavid Ahern 		if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) {
20057a35a50dSDavid Ahern 			NL_SET_ERR_MSG(extack, "Invalid link address");
2006110b2499SEric Dumazet 			goto out;
20075208debdSThomas Graf 		}
20087a35a50dSDavid Ahern 	}
20095208debdSThomas Graf 
2010d7480fd3SWANG Cong 	tbl = neigh_find_table(ndm->ndm_family);
2011d7480fd3SWANG Cong 	if (tbl == NULL)
2012d7480fd3SWANG Cong 		return -EAFNOSUPPORT;
20131da177e4SLinus Torvalds 
20147a35a50dSDavid Ahern 	if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) {
20157a35a50dSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid network address");
2016110b2499SEric Dumazet 		goto out;
20177a35a50dSDavid Ahern 	}
20187a35a50dSDavid Ahern 
20195208debdSThomas Graf 	dst = nla_data(tb[NDA_DST]);
20205208debdSThomas Graf 	lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
20211da177e4SLinus Torvalds 
2022a9cd3439SDavid Ahern 	if (tb[NDA_PROTOCOL])
2023df9b0e30SDavid Ahern 		protocol = nla_get_u8(tb[NDA_PROTOCOL]);
20242c611ad9SRoopa Prabhu 	if (ndm_flags & NTF_PROXY) {
202562dd9318SVille Nuorvala 		struct pneigh_entry *pn;
202662dd9318SVille Nuorvala 
20277482e384SDaniel Borkmann 		if (ndm_flags & NTF_MANAGED) {
20287482e384SDaniel Borkmann 			NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination");
20297482e384SDaniel Borkmann 			goto out;
20307482e384SDaniel Borkmann 		}
20317482e384SDaniel Borkmann 
20325208debdSThomas Graf 		err = -ENOBUFS;
2033426b5303SEric W. Biederman 		pn = pneigh_lookup(tbl, net, dst, dev, 1);
203462dd9318SVille Nuorvala 		if (pn) {
20352c611ad9SRoopa Prabhu 			pn->flags = ndm_flags;
2036df9b0e30SDavid Ahern 			if (protocol)
2037df9b0e30SDavid Ahern 				pn->protocol = protocol;
203862dd9318SVille Nuorvala 			err = 0;
203962dd9318SVille Nuorvala 		}
2040110b2499SEric Dumazet 		goto out;
20411da177e4SLinus Torvalds 	}
20421da177e4SLinus Torvalds 
20437a35a50dSDavid Ahern 	if (!dev) {
20447a35a50dSDavid Ahern 		NL_SET_ERR_MSG(extack, "Device not specified");
2045110b2499SEric Dumazet 		goto out;
20467a35a50dSDavid Ahern 	}
20471da177e4SLinus Torvalds 
2048b8fb1ab4SDavid Ahern 	if (tbl->allow_add && !tbl->allow_add(dev, extack)) {
2049b8fb1ab4SDavid Ahern 		err = -EINVAL;
2050b8fb1ab4SDavid Ahern 		goto out;
2051b8fb1ab4SDavid Ahern 	}
2052b8fb1ab4SDavid Ahern 
20535208debdSThomas Graf 	neigh = neigh_lookup(tbl, dst, dev);
20545208debdSThomas Graf 	if (neigh == NULL) {
205530fc7efaSDaniel Borkmann 		bool ndm_permanent  = ndm->ndm_state & NUD_PERMANENT;
205630fc7efaSDaniel Borkmann 		bool exempt_from_gc = ndm_permanent ||
205730fc7efaSDaniel Borkmann 				      ndm_flags & NTF_EXT_LEARNED;
2058e997f8a2SDavid Ahern 
20595208debdSThomas Graf 		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
20601da177e4SLinus Torvalds 			err = -ENOENT;
2061110b2499SEric Dumazet 			goto out;
20625208debdSThomas Graf 		}
206330fc7efaSDaniel Borkmann 		if (ndm_permanent && (ndm_flags & NTF_MANAGED)) {
206430fc7efaSDaniel Borkmann 			NL_SET_ERR_MSG(extack, "Invalid NTF_* flag for permanent entry");
206530fc7efaSDaniel Borkmann 			err = -EINVAL;
206630fc7efaSDaniel Borkmann 			goto out;
206730fc7efaSDaniel Borkmann 		}
20685208debdSThomas Graf 
2069e4400bbfSDaniel Borkmann 		neigh = ___neigh_create(tbl, dst, dev,
20707482e384SDaniel Borkmann 					ndm_flags &
20717482e384SDaniel Borkmann 					(NTF_EXT_LEARNED | NTF_MANAGED),
2072e4400bbfSDaniel Borkmann 					exempt_from_gc, true);
20735208debdSThomas Graf 		if (IS_ERR(neigh)) {
20745208debdSThomas Graf 			err = PTR_ERR(neigh);
2075110b2499SEric Dumazet 			goto out;
20761da177e4SLinus Torvalds 		}
20775208debdSThomas Graf 	} else {
20785208debdSThomas Graf 		if (nlh->nlmsg_flags & NLM_F_EXCL) {
20795208debdSThomas Graf 			err = -EEXIST;
20805208debdSThomas Graf 			neigh_release(neigh);
2081110b2499SEric Dumazet 			goto out;
20821da177e4SLinus Torvalds 		}
20831da177e4SLinus Torvalds 
20845208debdSThomas Graf 		if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
2085f7aa74e4SRoopa Prabhu 			flags &= ~(NEIGH_UPDATE_F_OVERRIDE |
2086f7aa74e4SRoopa Prabhu 				   NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
20875208debdSThomas Graf 	}
20881da177e4SLinus Torvalds 
208938212bb3SRoman Mashak 	if (protocol)
209038212bb3SRoman Mashak 		neigh->protocol = protocol;
20912c611ad9SRoopa Prabhu 	if (ndm_flags & NTF_EXT_LEARNED)
20929ce33e46SRoopa Prabhu 		flags |= NEIGH_UPDATE_F_EXT_LEARNED;
20932c611ad9SRoopa Prabhu 	if (ndm_flags & NTF_ROUTER)
2094f7aa74e4SRoopa Prabhu 		flags |= NEIGH_UPDATE_F_ISROUTER;
20957482e384SDaniel Borkmann 	if (ndm_flags & NTF_MANAGED)
20967482e384SDaniel Borkmann 		flags |= NEIGH_UPDATE_F_MANAGED;
20972c611ad9SRoopa Prabhu 	if (ndm_flags & NTF_USE)
20983dc20f47SDaniel Borkmann 		flags |= NEIGH_UPDATE_F_USE;
2099f7aa74e4SRoopa Prabhu 
21007a35a50dSDavid Ahern 	err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
21017a35a50dSDavid Ahern 			     NETLINK_CB(skb).portid, extack);
21027482e384SDaniel Borkmann 	if (!err && ndm_flags & (NTF_USE | NTF_MANAGED)) {
21033dc20f47SDaniel Borkmann 		neigh_event_send(neigh, NULL);
21043dc20f47SDaniel Borkmann 		err = 0;
21053dc20f47SDaniel Borkmann 	}
21065208debdSThomas Graf 	neigh_release(neigh);
21071da177e4SLinus Torvalds out:
21081da177e4SLinus Torvalds 	return err;
21091da177e4SLinus Torvalds }
21101da177e4SLinus Torvalds 
neightbl_fill_parms(struct sk_buff * skb,struct neigh_parms * parms)2111c7fb64dbSThomas Graf static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
2112c7fb64dbSThomas Graf {
2113ca860fb3SThomas Graf 	struct nlattr *nest;
2114e386c6ebSThomas Graf 
2115ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(skb, NDTA_PARMS);
2116ca860fb3SThomas Graf 	if (nest == NULL)
2117ca860fb3SThomas Graf 		return -ENOBUFS;
2118c7fb64dbSThomas Graf 
21199a6308d7SDavid S. Miller 	if ((parms->dev &&
21209a6308d7SDavid S. Miller 	     nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) ||
21216343944bSReshetova, Elena 	    nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) ||
21221f9248e5SJiri Pirko 	    nla_put_u32(skb, NDTPA_QUEUE_LENBYTES,
21231f9248e5SJiri Pirko 			NEIGH_VAR(parms, QUEUE_LEN_BYTES)) ||
21248b5c171bSEric Dumazet 	    /* approximative value for deprecated QUEUE_LEN (in packets) */
21259a6308d7SDavid S. Miller 	    nla_put_u32(skb, NDTPA_QUEUE_LEN,
21261f9248e5SJiri Pirko 			NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) ||
21271f9248e5SJiri Pirko 	    nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) ||
21281f9248e5SJiri Pirko 	    nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) ||
21291f9248e5SJiri Pirko 	    nla_put_u32(skb, NDTPA_UCAST_PROBES,
21301f9248e5SJiri Pirko 			NEIGH_VAR(parms, UCAST_PROBES)) ||
21311f9248e5SJiri Pirko 	    nla_put_u32(skb, NDTPA_MCAST_PROBES,
21321f9248e5SJiri Pirko 			NEIGH_VAR(parms, MCAST_PROBES)) ||
21338da86466SYOSHIFUJI Hideaki/吉藤英明 	    nla_put_u32(skb, NDTPA_MCAST_REPROBES,
21348da86466SYOSHIFUJI Hideaki/吉藤英明 			NEIGH_VAR(parms, MCAST_REPROBES)) ||
21352175d87cSNicolas Dichtel 	    nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time,
21362175d87cSNicolas Dichtel 			  NDTPA_PAD) ||
21379a6308d7SDavid S. Miller 	    nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME,
21382175d87cSNicolas Dichtel 			  NEIGH_VAR(parms, BASE_REACHABLE_TIME), NDTPA_PAD) ||
21391f9248e5SJiri Pirko 	    nla_put_msecs(skb, NDTPA_GC_STALETIME,
21402175d87cSNicolas Dichtel 			  NEIGH_VAR(parms, GC_STALETIME), NDTPA_PAD) ||
21419a6308d7SDavid S. Miller 	    nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME,
21422175d87cSNicolas Dichtel 			  NEIGH_VAR(parms, DELAY_PROBE_TIME), NDTPA_PAD) ||
21431f9248e5SJiri Pirko 	    nla_put_msecs(skb, NDTPA_RETRANS_TIME,
21442175d87cSNicolas Dichtel 			  NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) ||
21451f9248e5SJiri Pirko 	    nla_put_msecs(skb, NDTPA_ANYCAST_DELAY,
21462175d87cSNicolas Dichtel 			  NEIGH_VAR(parms, ANYCAST_DELAY), NDTPA_PAD) ||
21471f9248e5SJiri Pirko 	    nla_put_msecs(skb, NDTPA_PROXY_DELAY,
21482175d87cSNicolas Dichtel 			  NEIGH_VAR(parms, PROXY_DELAY), NDTPA_PAD) ||
21491f9248e5SJiri Pirko 	    nla_put_msecs(skb, NDTPA_LOCKTIME,
2150211da42eSYuwei Wang 			  NEIGH_VAR(parms, LOCKTIME), NDTPA_PAD) ||
2151211da42eSYuwei Wang 	    nla_put_msecs(skb, NDTPA_INTERVAL_PROBE_TIME_MS,
2152211da42eSYuwei Wang 			  NEIGH_VAR(parms, INTERVAL_PROBE_TIME_MS), NDTPA_PAD))
21539a6308d7SDavid S. Miller 		goto nla_put_failure;
2154ca860fb3SThomas Graf 	return nla_nest_end(skb, nest);
2155c7fb64dbSThomas Graf 
2156ca860fb3SThomas Graf nla_put_failure:
2157bc3ed28cSThomas Graf 	nla_nest_cancel(skb, nest);
2158bc3ed28cSThomas Graf 	return -EMSGSIZE;
2159c7fb64dbSThomas Graf }
2160c7fb64dbSThomas Graf 
neightbl_fill_info(struct sk_buff * skb,struct neigh_table * tbl,u32 pid,u32 seq,int type,int flags)2161ca860fb3SThomas Graf static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
2162ca860fb3SThomas Graf 			      u32 pid, u32 seq, int type, int flags)
2163c7fb64dbSThomas Graf {
2164c7fb64dbSThomas Graf 	struct nlmsghdr *nlh;
2165c7fb64dbSThomas Graf 	struct ndtmsg *ndtmsg;
2166c7fb64dbSThomas Graf 
2167ca860fb3SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags);
2168ca860fb3SThomas Graf 	if (nlh == NULL)
216926932566SPatrick McHardy 		return -EMSGSIZE;
2170c7fb64dbSThomas Graf 
2171ca860fb3SThomas Graf 	ndtmsg = nlmsg_data(nlh);
2172c7fb64dbSThomas Graf 
2173c7fb64dbSThomas Graf 	read_lock_bh(&tbl->lock);
2174c7fb64dbSThomas Graf 	ndtmsg->ndtm_family = tbl->family;
21759ef1d4c7SPatrick McHardy 	ndtmsg->ndtm_pad1   = 0;
21769ef1d4c7SPatrick McHardy 	ndtmsg->ndtm_pad2   = 0;
2177c7fb64dbSThomas Graf 
21789a6308d7SDavid S. Miller 	if (nla_put_string(skb, NDTA_NAME, tbl->id) ||
2179a9beb7e8SEric Dumazet 	    nla_put_msecs(skb, NDTA_GC_INTERVAL, READ_ONCE(tbl->gc_interval),
2180a9beb7e8SEric Dumazet 			  NDTA_PAD) ||
2181a9beb7e8SEric Dumazet 	    nla_put_u32(skb, NDTA_THRESH1, READ_ONCE(tbl->gc_thresh1)) ||
2182a9beb7e8SEric Dumazet 	    nla_put_u32(skb, NDTA_THRESH2, READ_ONCE(tbl->gc_thresh2)) ||
2183a9beb7e8SEric Dumazet 	    nla_put_u32(skb, NDTA_THRESH3, READ_ONCE(tbl->gc_thresh3)))
21849a6308d7SDavid S. Miller 		goto nla_put_failure;
2185c7fb64dbSThomas Graf 	{
2186c7fb64dbSThomas Graf 		unsigned long now = jiffies;
2187a9beb7e8SEric Dumazet 		long flush_delta = now - READ_ONCE(tbl->last_flush);
2188a9beb7e8SEric Dumazet 		long rand_delta = now - READ_ONCE(tbl->last_rand);
2189d6bf7817SEric Dumazet 		struct neigh_hash_table *nht;
2190c7fb64dbSThomas Graf 		struct ndt_config ndc = {
2191c7fb64dbSThomas Graf 			.ndtc_key_len		= tbl->key_len,
2192c7fb64dbSThomas Graf 			.ndtc_entry_size	= tbl->entry_size,
2193c7fb64dbSThomas Graf 			.ndtc_entries		= atomic_read(&tbl->entries),
2194c7fb64dbSThomas Graf 			.ndtc_last_flush	= jiffies_to_msecs(flush_delta),
2195c7fb64dbSThomas Graf 			.ndtc_last_rand		= jiffies_to_msecs(rand_delta),
2196a9beb7e8SEric Dumazet 			.ndtc_proxy_qlen	= READ_ONCE(tbl->proxy_queue.qlen),
2197c7fb64dbSThomas Graf 		};
2198c7fb64dbSThomas Graf 
219909eed119SEric Dumazet 		rcu_read_lock();
220009eed119SEric Dumazet 		nht = rcu_dereference(tbl->nht);
22012c2aba6cSDavid S. Miller 		ndc.ndtc_hash_rnd = nht->hash_rnd[0];
2202cd089336SDavid S. Miller 		ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1);
220309eed119SEric Dumazet 		rcu_read_unlock();
2204d6bf7817SEric Dumazet 
22059a6308d7SDavid S. Miller 		if (nla_put(skb, NDTA_CONFIG, sizeof(ndc), &ndc))
22069a6308d7SDavid S. Miller 			goto nla_put_failure;
2207c7fb64dbSThomas Graf 	}
2208c7fb64dbSThomas Graf 
2209c7fb64dbSThomas Graf 	{
2210c7fb64dbSThomas Graf 		int cpu;
2211c7fb64dbSThomas Graf 		struct ndt_stats ndst;
2212c7fb64dbSThomas Graf 
2213c7fb64dbSThomas Graf 		memset(&ndst, 0, sizeof(ndst));
2214c7fb64dbSThomas Graf 
22156f912042SKAMEZAWA Hiroyuki 		for_each_possible_cpu(cpu) {
2216c7fb64dbSThomas Graf 			struct neigh_statistics	*st;
2217c7fb64dbSThomas Graf 
2218c7fb64dbSThomas Graf 			st = per_cpu_ptr(tbl->stats, cpu);
2219a9beb7e8SEric Dumazet 			ndst.ndts_allocs		+= READ_ONCE(st->allocs);
2220a9beb7e8SEric Dumazet 			ndst.ndts_destroys		+= READ_ONCE(st->destroys);
2221a9beb7e8SEric Dumazet 			ndst.ndts_hash_grows		+= READ_ONCE(st->hash_grows);
2222a9beb7e8SEric Dumazet 			ndst.ndts_res_failed		+= READ_ONCE(st->res_failed);
2223a9beb7e8SEric Dumazet 			ndst.ndts_lookups		+= READ_ONCE(st->lookups);
2224a9beb7e8SEric Dumazet 			ndst.ndts_hits			+= READ_ONCE(st->hits);
2225a9beb7e8SEric Dumazet 			ndst.ndts_rcv_probes_mcast	+= READ_ONCE(st->rcv_probes_mcast);
2226a9beb7e8SEric Dumazet 			ndst.ndts_rcv_probes_ucast	+= READ_ONCE(st->rcv_probes_ucast);
2227a9beb7e8SEric Dumazet 			ndst.ndts_periodic_gc_runs	+= READ_ONCE(st->periodic_gc_runs);
2228a9beb7e8SEric Dumazet 			ndst.ndts_forced_gc_runs	+= READ_ONCE(st->forced_gc_runs);
2229a9beb7e8SEric Dumazet 			ndst.ndts_table_fulls		+= READ_ONCE(st->table_fulls);
2230c7fb64dbSThomas Graf 		}
2231c7fb64dbSThomas Graf 
2232b676338fSNicolas Dichtel 		if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst,
2233b676338fSNicolas Dichtel 				  NDTA_PAD))
22349a6308d7SDavid S. Miller 			goto nla_put_failure;
2235c7fb64dbSThomas Graf 	}
2236c7fb64dbSThomas Graf 
2237c7fb64dbSThomas Graf 	BUG_ON(tbl->parms.dev);
2238c7fb64dbSThomas Graf 	if (neightbl_fill_parms(skb, &tbl->parms) < 0)
2239ca860fb3SThomas Graf 		goto nla_put_failure;
2240c7fb64dbSThomas Graf 
2241c7fb64dbSThomas Graf 	read_unlock_bh(&tbl->lock);
2242053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2243053c095aSJohannes Berg 	return 0;
2244c7fb64dbSThomas Graf 
2245ca860fb3SThomas Graf nla_put_failure:
2246c7fb64dbSThomas Graf 	read_unlock_bh(&tbl->lock);
224726932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
224826932566SPatrick McHardy 	return -EMSGSIZE;
2249c7fb64dbSThomas Graf }
2250c7fb64dbSThomas Graf 
neightbl_fill_param_info(struct sk_buff * skb,struct neigh_table * tbl,struct neigh_parms * parms,u32 pid,u32 seq,int type,unsigned int flags)2251ca860fb3SThomas Graf static int neightbl_fill_param_info(struct sk_buff *skb,
2252ca860fb3SThomas Graf 				    struct neigh_table *tbl,
2253c7fb64dbSThomas Graf 				    struct neigh_parms *parms,
2254ca860fb3SThomas Graf 				    u32 pid, u32 seq, int type,
2255ca860fb3SThomas Graf 				    unsigned int flags)
2256c7fb64dbSThomas Graf {
2257c7fb64dbSThomas Graf 	struct ndtmsg *ndtmsg;
2258c7fb64dbSThomas Graf 	struct nlmsghdr *nlh;
2259c7fb64dbSThomas Graf 
2260ca860fb3SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags);
2261ca860fb3SThomas Graf 	if (nlh == NULL)
226226932566SPatrick McHardy 		return -EMSGSIZE;
2263c7fb64dbSThomas Graf 
2264ca860fb3SThomas Graf 	ndtmsg = nlmsg_data(nlh);
2265c7fb64dbSThomas Graf 
2266c7fb64dbSThomas Graf 	read_lock_bh(&tbl->lock);
2267c7fb64dbSThomas Graf 	ndtmsg->ndtm_family = tbl->family;
22689ef1d4c7SPatrick McHardy 	ndtmsg->ndtm_pad1   = 0;
22699ef1d4c7SPatrick McHardy 	ndtmsg->ndtm_pad2   = 0;
2270c7fb64dbSThomas Graf 
2271ca860fb3SThomas Graf 	if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 ||
2272ca860fb3SThomas Graf 	    neightbl_fill_parms(skb, parms) < 0)
2273ca860fb3SThomas Graf 		goto errout;
2274c7fb64dbSThomas Graf 
2275c7fb64dbSThomas Graf 	read_unlock_bh(&tbl->lock);
2276053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2277053c095aSJohannes Berg 	return 0;
2278ca860fb3SThomas Graf errout:
2279c7fb64dbSThomas Graf 	read_unlock_bh(&tbl->lock);
228026932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
228126932566SPatrick McHardy 	return -EMSGSIZE;
2282c7fb64dbSThomas Graf }
2283c7fb64dbSThomas Graf 
2284ef7c79edSPatrick McHardy static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = {
22856b3f8674SThomas Graf 	[NDTA_NAME]		= { .type = NLA_STRING },
22866b3f8674SThomas Graf 	[NDTA_THRESH1]		= { .type = NLA_U32 },
22876b3f8674SThomas Graf 	[NDTA_THRESH2]		= { .type = NLA_U32 },
22886b3f8674SThomas Graf 	[NDTA_THRESH3]		= { .type = NLA_U32 },
22896b3f8674SThomas Graf 	[NDTA_GC_INTERVAL]	= { .type = NLA_U64 },
22906b3f8674SThomas Graf 	[NDTA_PARMS]		= { .type = NLA_NESTED },
22916b3f8674SThomas Graf };
22926b3f8674SThomas Graf 
2293ef7c79edSPatrick McHardy static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {
22946b3f8674SThomas Graf 	[NDTPA_IFINDEX]			= { .type = NLA_U32 },
22956b3f8674SThomas Graf 	[NDTPA_QUEUE_LEN]		= { .type = NLA_U32 },
22966b3f8674SThomas Graf 	[NDTPA_PROXY_QLEN]		= { .type = NLA_U32 },
22976b3f8674SThomas Graf 	[NDTPA_APP_PROBES]		= { .type = NLA_U32 },
22986b3f8674SThomas Graf 	[NDTPA_UCAST_PROBES]		= { .type = NLA_U32 },
22996b3f8674SThomas Graf 	[NDTPA_MCAST_PROBES]		= { .type = NLA_U32 },
23008da86466SYOSHIFUJI Hideaki/吉藤英明 	[NDTPA_MCAST_REPROBES]		= { .type = NLA_U32 },
23016b3f8674SThomas Graf 	[NDTPA_BASE_REACHABLE_TIME]	= { .type = NLA_U64 },
23026b3f8674SThomas Graf 	[NDTPA_GC_STALETIME]		= { .type = NLA_U64 },
23036b3f8674SThomas Graf 	[NDTPA_DELAY_PROBE_TIME]	= { .type = NLA_U64 },
23046b3f8674SThomas Graf 	[NDTPA_RETRANS_TIME]		= { .type = NLA_U64 },
23056b3f8674SThomas Graf 	[NDTPA_ANYCAST_DELAY]		= { .type = NLA_U64 },
23066b3f8674SThomas Graf 	[NDTPA_PROXY_DELAY]		= { .type = NLA_U64 },
23076b3f8674SThomas Graf 	[NDTPA_LOCKTIME]		= { .type = NLA_U64 },
2308211da42eSYuwei Wang 	[NDTPA_INTERVAL_PROBE_TIME_MS]	= { .type = NLA_U64, .min = 1 },
23096b3f8674SThomas Graf };
23106b3f8674SThomas Graf 
neightbl_set(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)2311c21ef3e3SDavid Ahern static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
2312c21ef3e3SDavid Ahern 			struct netlink_ext_ack *extack)
2313c7fb64dbSThomas Graf {
23143b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
2315c7fb64dbSThomas Graf 	struct neigh_table *tbl;
23166b3f8674SThomas Graf 	struct ndtmsg *ndtmsg;
23176b3f8674SThomas Graf 	struct nlattr *tb[NDTA_MAX+1];
2318d7480fd3SWANG Cong 	bool found = false;
2319d7480fd3SWANG Cong 	int err, tidx;
2320c7fb64dbSThomas Graf 
23218cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
2322c21ef3e3SDavid Ahern 				     nl_neightbl_policy, extack);
23236b3f8674SThomas Graf 	if (err < 0)
23246b3f8674SThomas Graf 		goto errout;
2325c7fb64dbSThomas Graf 
23266b3f8674SThomas Graf 	if (tb[NDTA_NAME] == NULL) {
23276b3f8674SThomas Graf 		err = -EINVAL;
23286b3f8674SThomas Graf 		goto errout;
23296b3f8674SThomas Graf 	}
23306b3f8674SThomas Graf 
23316b3f8674SThomas Graf 	ndtmsg = nlmsg_data(nlh);
2332d7480fd3SWANG Cong 
2333d7480fd3SWANG Cong 	for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
2334d7480fd3SWANG Cong 		tbl = neigh_tables[tidx];
2335d7480fd3SWANG Cong 		if (!tbl)
2336d7480fd3SWANG Cong 			continue;
2337c7fb64dbSThomas Graf 		if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family)
2338c7fb64dbSThomas Graf 			continue;
2339d7480fd3SWANG Cong 		if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) {
2340d7480fd3SWANG Cong 			found = true;
2341c7fb64dbSThomas Graf 			break;
2342c7fb64dbSThomas Graf 		}
2343c7fb64dbSThomas Graf 	}
2344c7fb64dbSThomas Graf 
2345d7480fd3SWANG Cong 	if (!found)
2346d7480fd3SWANG Cong 		return -ENOENT;
2347d7480fd3SWANG Cong 
2348c7fb64dbSThomas Graf 	/*
2349c7fb64dbSThomas Graf 	 * We acquire tbl->lock to be nice to the periodic timers and
2350c7fb64dbSThomas Graf 	 * make sure they always see a consistent set of values.
2351c7fb64dbSThomas Graf 	 */
2352c7fb64dbSThomas Graf 	write_lock_bh(&tbl->lock);
2353c7fb64dbSThomas Graf 
23546b3f8674SThomas Graf 	if (tb[NDTA_PARMS]) {
23556b3f8674SThomas Graf 		struct nlattr *tbp[NDTPA_MAX+1];
2356c7fb64dbSThomas Graf 		struct neigh_parms *p;
23576b3f8674SThomas Graf 		int i, ifindex = 0;
2358c7fb64dbSThomas Graf 
23598cb08174SJohannes Berg 		err = nla_parse_nested_deprecated(tbp, NDTPA_MAX,
23608cb08174SJohannes Berg 						  tb[NDTA_PARMS],
2361c21ef3e3SDavid Ahern 						  nl_ntbl_parm_policy, extack);
23626b3f8674SThomas Graf 		if (err < 0)
23636b3f8674SThomas Graf 			goto errout_tbl_lock;
2364c7fb64dbSThomas Graf 
23656b3f8674SThomas Graf 		if (tbp[NDTPA_IFINDEX])
23666b3f8674SThomas Graf 			ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
2367c7fb64dbSThomas Graf 
236897fd5bc7STobias Klauser 		p = lookup_neigh_parms(tbl, net, ifindex);
2369c7fb64dbSThomas Graf 		if (p == NULL) {
2370c7fb64dbSThomas Graf 			err = -ENOENT;
23716b3f8674SThomas Graf 			goto errout_tbl_lock;
2372c7fb64dbSThomas Graf 		}
2373c7fb64dbSThomas Graf 
23746b3f8674SThomas Graf 		for (i = 1; i <= NDTPA_MAX; i++) {
23756b3f8674SThomas Graf 			if (tbp[i] == NULL)
23766b3f8674SThomas Graf 				continue;
2377c7fb64dbSThomas Graf 
23786b3f8674SThomas Graf 			switch (i) {
23796b3f8674SThomas Graf 			case NDTPA_QUEUE_LEN:
23801f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,
23811f9248e5SJiri Pirko 					      nla_get_u32(tbp[i]) *
23821f9248e5SJiri Pirko 					      SKB_TRUESIZE(ETH_FRAME_LEN));
23838b5c171bSEric Dumazet 				break;
23848b5c171bSEric Dumazet 			case NDTPA_QUEUE_LENBYTES:
23851f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,
23861f9248e5SJiri Pirko 					      nla_get_u32(tbp[i]));
23876b3f8674SThomas Graf 				break;
23886b3f8674SThomas Graf 			case NDTPA_PROXY_QLEN:
23891f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, PROXY_QLEN,
23901f9248e5SJiri Pirko 					      nla_get_u32(tbp[i]));
23916b3f8674SThomas Graf 				break;
23926b3f8674SThomas Graf 			case NDTPA_APP_PROBES:
23931f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, APP_PROBES,
23941f9248e5SJiri Pirko 					      nla_get_u32(tbp[i]));
23956b3f8674SThomas Graf 				break;
23966b3f8674SThomas Graf 			case NDTPA_UCAST_PROBES:
23971f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, UCAST_PROBES,
23981f9248e5SJiri Pirko 					      nla_get_u32(tbp[i]));
23996b3f8674SThomas Graf 				break;
24006b3f8674SThomas Graf 			case NDTPA_MCAST_PROBES:
24011f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, MCAST_PROBES,
24021f9248e5SJiri Pirko 					      nla_get_u32(tbp[i]));
24036b3f8674SThomas Graf 				break;
24048da86466SYOSHIFUJI Hideaki/吉藤英明 			case NDTPA_MCAST_REPROBES:
24058da86466SYOSHIFUJI Hideaki/吉藤英明 				NEIGH_VAR_SET(p, MCAST_REPROBES,
24068da86466SYOSHIFUJI Hideaki/吉藤英明 					      nla_get_u32(tbp[i]));
24078da86466SYOSHIFUJI Hideaki/吉藤英明 				break;
24086b3f8674SThomas Graf 			case NDTPA_BASE_REACHABLE_TIME:
24091f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, BASE_REACHABLE_TIME,
24101f9248e5SJiri Pirko 					      nla_get_msecs(tbp[i]));
24114bf6980dSJean-Francois Remy 				/* update reachable_time as well, otherwise, the change will
24124bf6980dSJean-Francois Remy 				 * only be effective after the next time neigh_periodic_work
24134bf6980dSJean-Francois Remy 				 * decides to recompute it (can be multiple minutes)
24144bf6980dSJean-Francois Remy 				 */
24154bf6980dSJean-Francois Remy 				p->reachable_time =
24164bf6980dSJean-Francois Remy 					neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
24176b3f8674SThomas Graf 				break;
24186b3f8674SThomas Graf 			case NDTPA_GC_STALETIME:
24191f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, GC_STALETIME,
24201f9248e5SJiri Pirko 					      nla_get_msecs(tbp[i]));
24216b3f8674SThomas Graf 				break;
24226b3f8674SThomas Graf 			case NDTPA_DELAY_PROBE_TIME:
24231f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, DELAY_PROBE_TIME,
24241f9248e5SJiri Pirko 					      nla_get_msecs(tbp[i]));
24252a4501aeSIdo Schimmel 				call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p);
24266b3f8674SThomas Graf 				break;
2427211da42eSYuwei Wang 			case NDTPA_INTERVAL_PROBE_TIME_MS:
2428211da42eSYuwei Wang 				NEIGH_VAR_SET(p, INTERVAL_PROBE_TIME_MS,
2429211da42eSYuwei Wang 					      nla_get_msecs(tbp[i]));
2430211da42eSYuwei Wang 				break;
24316b3f8674SThomas Graf 			case NDTPA_RETRANS_TIME:
24321f9248e5SJiri Pirko 				NEIGH_VAR_SET(p, RETRANS_TIME,
24331f9248e5SJiri Pirko 					      nla_get_msecs(tbp[i]));
24346b3f8674SThomas Graf 				break;
24356b3f8674SThomas Graf 			case NDTPA_ANYCAST_DELAY:
24363977458cSJiri Pirko 				NEIGH_VAR_SET(p, ANYCAST_DELAY,
24373977458cSJiri Pirko 					      nla_get_msecs(tbp[i]));
24386b3f8674SThomas Graf 				break;
24396b3f8674SThomas Graf 			case NDTPA_PROXY_DELAY:
24403977458cSJiri Pirko 				NEIGH_VAR_SET(p, PROXY_DELAY,
24413977458cSJiri Pirko 					      nla_get_msecs(tbp[i]));
24426b3f8674SThomas Graf 				break;
24436b3f8674SThomas Graf 			case NDTPA_LOCKTIME:
24443977458cSJiri Pirko 				NEIGH_VAR_SET(p, LOCKTIME,
24453977458cSJiri Pirko 					      nla_get_msecs(tbp[i]));
24466b3f8674SThomas Graf 				break;
2447c7fb64dbSThomas Graf 			}
24486b3f8674SThomas Graf 		}
24496b3f8674SThomas Graf 	}
24506b3f8674SThomas Graf 
2451dc25c676SGao feng 	err = -ENOENT;
2452dc25c676SGao feng 	if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] ||
2453dc25c676SGao feng 	     tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) &&
2454dc25c676SGao feng 	    !net_eq(net, &init_net))
2455dc25c676SGao feng 		goto errout_tbl_lock;
2456dc25c676SGao feng 
24576b3f8674SThomas Graf 	if (tb[NDTA_THRESH1])
2458a9beb7e8SEric Dumazet 		WRITE_ONCE(tbl->gc_thresh1, nla_get_u32(tb[NDTA_THRESH1]));
24596b3f8674SThomas Graf 
24606b3f8674SThomas Graf 	if (tb[NDTA_THRESH2])
2461a9beb7e8SEric Dumazet 		WRITE_ONCE(tbl->gc_thresh2, nla_get_u32(tb[NDTA_THRESH2]));
24626b3f8674SThomas Graf 
24636b3f8674SThomas Graf 	if (tb[NDTA_THRESH3])
2464a9beb7e8SEric Dumazet 		WRITE_ONCE(tbl->gc_thresh3, nla_get_u32(tb[NDTA_THRESH3]));
24656b3f8674SThomas Graf 
24666b3f8674SThomas Graf 	if (tb[NDTA_GC_INTERVAL])
2467a9beb7e8SEric Dumazet 		WRITE_ONCE(tbl->gc_interval, nla_get_msecs(tb[NDTA_GC_INTERVAL]));
2468c7fb64dbSThomas Graf 
2469c7fb64dbSThomas Graf 	err = 0;
2470c7fb64dbSThomas Graf 
24716b3f8674SThomas Graf errout_tbl_lock:
2472c7fb64dbSThomas Graf 	write_unlock_bh(&tbl->lock);
24736b3f8674SThomas Graf errout:
2474c7fb64dbSThomas Graf 	return err;
2475c7fb64dbSThomas Graf }
2476c7fb64dbSThomas Graf 
neightbl_valid_dump_info(const struct nlmsghdr * nlh,struct netlink_ext_ack * extack)24779632d47fSDavid Ahern static int neightbl_valid_dump_info(const struct nlmsghdr *nlh,
24789632d47fSDavid Ahern 				    struct netlink_ext_ack *extack)
24799632d47fSDavid Ahern {
24809632d47fSDavid Ahern 	struct ndtmsg *ndtm;
24819632d47fSDavid Ahern 
24829632d47fSDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) {
24839632d47fSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request");
24849632d47fSDavid Ahern 		return -EINVAL;
24859632d47fSDavid Ahern 	}
24869632d47fSDavid Ahern 
24879632d47fSDavid Ahern 	ndtm = nlmsg_data(nlh);
24889632d47fSDavid Ahern 	if (ndtm->ndtm_pad1  || ndtm->ndtm_pad2) {
24899632d47fSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request");
24909632d47fSDavid Ahern 		return -EINVAL;
24919632d47fSDavid Ahern 	}
24929632d47fSDavid Ahern 
24939632d47fSDavid Ahern 	if (nlmsg_attrlen(nlh, sizeof(*ndtm))) {
24949632d47fSDavid Ahern 		NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request");
24959632d47fSDavid Ahern 		return -EINVAL;
24969632d47fSDavid Ahern 	}
24979632d47fSDavid Ahern 
24989632d47fSDavid Ahern 	return 0;
24999632d47fSDavid Ahern }
25009632d47fSDavid Ahern 
neightbl_dump_info(struct sk_buff * skb,struct netlink_callback * cb)2501c8822a4eSThomas Graf static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
2502c7fb64dbSThomas Graf {
25039632d47fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
25043b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
2505ca860fb3SThomas Graf 	int family, tidx, nidx = 0;
2506ca860fb3SThomas Graf 	int tbl_skip = cb->args[0];
2507ca860fb3SThomas Graf 	int neigh_skip = cb->args[1];
2508c7fb64dbSThomas Graf 	struct neigh_table *tbl;
2509c7fb64dbSThomas Graf 
25109632d47fSDavid Ahern 	if (cb->strict_check) {
25119632d47fSDavid Ahern 		int err = neightbl_valid_dump_info(nlh, cb->extack);
25129632d47fSDavid Ahern 
25139632d47fSDavid Ahern 		if (err < 0)
25149632d47fSDavid Ahern 			return err;
25159632d47fSDavid Ahern 	}
25169632d47fSDavid Ahern 
25179632d47fSDavid Ahern 	family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
2518c7fb64dbSThomas Graf 
2519d7480fd3SWANG Cong 	for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
2520c7fb64dbSThomas Graf 		struct neigh_parms *p;
2521c7fb64dbSThomas Graf 
2522d7480fd3SWANG Cong 		tbl = neigh_tables[tidx];
2523d7480fd3SWANG Cong 		if (!tbl)
2524d7480fd3SWANG Cong 			continue;
2525d7480fd3SWANG Cong 
2526ca860fb3SThomas Graf 		if (tidx < tbl_skip || (family && tbl->family != family))
2527c7fb64dbSThomas Graf 			continue;
2528c7fb64dbSThomas Graf 
252915e47304SEric W. Biederman 		if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid,
25309632d47fSDavid Ahern 				       nlh->nlmsg_seq, RTM_NEWNEIGHTBL,
25317b46a644SDavid S. Miller 				       NLM_F_MULTI) < 0)
2532c7fb64dbSThomas Graf 			break;
2533c7fb64dbSThomas Graf 
253475fbfd33SNicolas Dichtel 		nidx = 0;
253575fbfd33SNicolas Dichtel 		p = list_next_entry(&tbl->parms, list);
253675fbfd33SNicolas Dichtel 		list_for_each_entry_from(p, &tbl->parms_list, list) {
2537878628fbSYOSHIFUJI Hideaki 			if (!net_eq(neigh_parms_net(p), net))
2538426b5303SEric W. Biederman 				continue;
2539426b5303SEric W. Biederman 
2540efc683fcSGautam Kachroo 			if (nidx < neigh_skip)
2541efc683fcSGautam Kachroo 				goto next;
2542c7fb64dbSThomas Graf 
2543ca860fb3SThomas Graf 			if (neightbl_fill_param_info(skb, tbl, p,
254415e47304SEric W. Biederman 						     NETLINK_CB(cb->skb).portid,
25459632d47fSDavid Ahern 						     nlh->nlmsg_seq,
2546ca860fb3SThomas Graf 						     RTM_NEWNEIGHTBL,
25477b46a644SDavid S. Miller 						     NLM_F_MULTI) < 0)
2548c7fb64dbSThomas Graf 				goto out;
2549efc683fcSGautam Kachroo 		next:
2550efc683fcSGautam Kachroo 			nidx++;
2551c7fb64dbSThomas Graf 		}
2552c7fb64dbSThomas Graf 
2553ca860fb3SThomas Graf 		neigh_skip = 0;
2554c7fb64dbSThomas Graf 	}
2555c7fb64dbSThomas Graf out:
2556ca860fb3SThomas Graf 	cb->args[0] = tidx;
2557ca860fb3SThomas Graf 	cb->args[1] = nidx;
2558c7fb64dbSThomas Graf 
2559c7fb64dbSThomas Graf 	return skb->len;
2560c7fb64dbSThomas Graf }
25611da177e4SLinus Torvalds 
neigh_fill_info(struct sk_buff * skb,struct neighbour * neigh,u32 pid,u32 seq,int type,unsigned int flags)25628b8aec50SThomas Graf static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
25638b8aec50SThomas Graf 			   u32 pid, u32 seq, int type, unsigned int flags)
25641da177e4SLinus Torvalds {
25652c611ad9SRoopa Prabhu 	u32 neigh_flags, neigh_flags_ext;
25661da177e4SLinus Torvalds 	unsigned long now = jiffies;
25671da177e4SLinus Torvalds 	struct nda_cacheinfo ci;
25688b8aec50SThomas Graf 	struct nlmsghdr *nlh;
25698b8aec50SThomas Graf 	struct ndmsg *ndm;
25701da177e4SLinus Torvalds 
25718b8aec50SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
25728b8aec50SThomas Graf 	if (nlh == NULL)
257326932566SPatrick McHardy 		return -EMSGSIZE;
25748b8aec50SThomas Graf 
25752c611ad9SRoopa Prabhu 	neigh_flags_ext = neigh->flags >> NTF_EXT_SHIFT;
25762c611ad9SRoopa Prabhu 	neigh_flags     = neigh->flags & NTF_OLD_MASK;
25772c611ad9SRoopa Prabhu 
25788b8aec50SThomas Graf 	ndm = nlmsg_data(nlh);
25798b8aec50SThomas Graf 	ndm->ndm_family	 = neigh->ops->family;
25809ef1d4c7SPatrick McHardy 	ndm->ndm_pad1    = 0;
25819ef1d4c7SPatrick McHardy 	ndm->ndm_pad2    = 0;
25822c611ad9SRoopa Prabhu 	ndm->ndm_flags	 = neigh_flags;
25838b8aec50SThomas Graf 	ndm->ndm_type	 = neigh->type;
25848b8aec50SThomas Graf 	ndm->ndm_ifindex = neigh->dev->ifindex;
25851da177e4SLinus Torvalds 
25869a6308d7SDavid S. Miller 	if (nla_put(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key))
25879a6308d7SDavid S. Miller 		goto nla_put_failure;
25888b8aec50SThomas Graf 
25898b8aec50SThomas Graf 	read_lock_bh(&neigh->lock);
25908b8aec50SThomas Graf 	ndm->ndm_state	 = neigh->nud_state;
25910ed8ddf4SEric Dumazet 	if (neigh->nud_state & NUD_VALID) {
25920ed8ddf4SEric Dumazet 		char haddr[MAX_ADDR_LEN];
25930ed8ddf4SEric Dumazet 
25940ed8ddf4SEric Dumazet 		neigh_ha_snapshot(haddr, neigh, neigh->dev);
25950ed8ddf4SEric Dumazet 		if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) {
25968b8aec50SThomas Graf 			read_unlock_bh(&neigh->lock);
25978b8aec50SThomas Graf 			goto nla_put_failure;
25988b8aec50SThomas Graf 		}
25990ed8ddf4SEric Dumazet 	}
26008b8aec50SThomas Graf 
2601b9f5f52cSStephen Hemminger 	ci.ndm_used	 = jiffies_to_clock_t(now - neigh->used);
2602b9f5f52cSStephen Hemminger 	ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed);
2603b9f5f52cSStephen Hemminger 	ci.ndm_updated	 = jiffies_to_clock_t(now - neigh->updated);
26049f237430SReshetova, Elena 	ci.ndm_refcnt	 = refcount_read(&neigh->refcnt) - 1;
26058b8aec50SThomas Graf 	read_unlock_bh(&neigh->lock);
26068b8aec50SThomas Graf 
26079a6308d7SDavid S. Miller 	if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) ||
26089a6308d7SDavid S. Miller 	    nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
26099a6308d7SDavid S. Miller 		goto nla_put_failure;
26108b8aec50SThomas Graf 
2611df9b0e30SDavid Ahern 	if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol))
2612df9b0e30SDavid Ahern 		goto nla_put_failure;
26132c611ad9SRoopa Prabhu 	if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
26142c611ad9SRoopa Prabhu 		goto nla_put_failure;
2615df9b0e30SDavid Ahern 
2616053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2617053c095aSJohannes Berg 	return 0;
26188b8aec50SThomas Graf 
26198b8aec50SThomas Graf nla_put_failure:
262026932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
262126932566SPatrick McHardy 	return -EMSGSIZE;
26221da177e4SLinus Torvalds }
26231da177e4SLinus Torvalds 
pneigh_fill_info(struct sk_buff * skb,struct pneigh_entry * pn,u32 pid,u32 seq,int type,unsigned int flags,struct neigh_table * tbl)262484920c14STony Zelenoff static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
262584920c14STony Zelenoff 			    u32 pid, u32 seq, int type, unsigned int flags,
262684920c14STony Zelenoff 			    struct neigh_table *tbl)
262784920c14STony Zelenoff {
26282c611ad9SRoopa Prabhu 	u32 neigh_flags, neigh_flags_ext;
262984920c14STony Zelenoff 	struct nlmsghdr *nlh;
263084920c14STony Zelenoff 	struct ndmsg *ndm;
263184920c14STony Zelenoff 
263284920c14STony Zelenoff 	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
263384920c14STony Zelenoff 	if (nlh == NULL)
263484920c14STony Zelenoff 		return -EMSGSIZE;
263584920c14STony Zelenoff 
26362c611ad9SRoopa Prabhu 	neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT;
26372c611ad9SRoopa Prabhu 	neigh_flags     = pn->flags & NTF_OLD_MASK;
26382c611ad9SRoopa Prabhu 
263984920c14STony Zelenoff 	ndm = nlmsg_data(nlh);
264084920c14STony Zelenoff 	ndm->ndm_family	 = tbl->family;
264184920c14STony Zelenoff 	ndm->ndm_pad1    = 0;
264284920c14STony Zelenoff 	ndm->ndm_pad2    = 0;
26432c611ad9SRoopa Prabhu 	ndm->ndm_flags	 = neigh_flags | NTF_PROXY;
2644545469f7SJun Zhao 	ndm->ndm_type	 = RTN_UNICAST;
26456adc5fd6SKonstantin Khlebnikov 	ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0;
264684920c14STony Zelenoff 	ndm->ndm_state	 = NUD_NONE;
264784920c14STony Zelenoff 
26489a6308d7SDavid S. Miller 	if (nla_put(skb, NDA_DST, tbl->key_len, pn->key))
26499a6308d7SDavid S. Miller 		goto nla_put_failure;
265084920c14STony Zelenoff 
2651df9b0e30SDavid Ahern 	if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol))
2652df9b0e30SDavid Ahern 		goto nla_put_failure;
26532c611ad9SRoopa Prabhu 	if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
26542c611ad9SRoopa Prabhu 		goto nla_put_failure;
2655df9b0e30SDavid Ahern 
2656053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2657053c095aSJohannes Berg 	return 0;
265884920c14STony Zelenoff 
265984920c14STony Zelenoff nla_put_failure:
266084920c14STony Zelenoff 	nlmsg_cancel(skb, nlh);
266184920c14STony Zelenoff 	return -EMSGSIZE;
266284920c14STony Zelenoff }
266384920c14STony Zelenoff 
neigh_update_notify(struct neighbour * neigh,u32 nlmsg_pid)26647b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid)
2665d961db35SThomas Graf {
2666d961db35SThomas Graf 	call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
26677b8f7a40SRoopa Prabhu 	__neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid);
2668d961db35SThomas Graf }
26691da177e4SLinus Torvalds 
neigh_master_filtered(struct net_device * dev,int master_idx)267021fdd092SDavid Ahern static bool neigh_master_filtered(struct net_device *dev, int master_idx)
267121fdd092SDavid Ahern {
267221fdd092SDavid Ahern 	struct net_device *master;
267321fdd092SDavid Ahern 
267421fdd092SDavid Ahern 	if (!master_idx)
267521fdd092SDavid Ahern 		return false;
267621fdd092SDavid Ahern 
2677aab456dfSEric Dumazet 	master = dev ? netdev_master_upper_dev_get(dev) : NULL;
2678d3432bf1SLahav Schlesinger 
2679d3432bf1SLahav Schlesinger 	/* 0 is already used to denote NDA_MASTER wasn't passed, therefore need another
2680d3432bf1SLahav Schlesinger 	 * invalid value for ifindex to denote "no master".
2681d3432bf1SLahav Schlesinger 	 */
2682d3432bf1SLahav Schlesinger 	if (master_idx == -1)
2683d3432bf1SLahav Schlesinger 		return !!master;
2684d3432bf1SLahav Schlesinger 
268521fdd092SDavid Ahern 	if (!master || master->ifindex != master_idx)
268621fdd092SDavid Ahern 		return true;
268721fdd092SDavid Ahern 
268821fdd092SDavid Ahern 	return false;
268921fdd092SDavid Ahern }
269021fdd092SDavid Ahern 
neigh_ifindex_filtered(struct net_device * dev,int filter_idx)269116660f0bSDavid Ahern static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx)
269216660f0bSDavid Ahern {
2693aab456dfSEric Dumazet 	if (filter_idx && (!dev || dev->ifindex != filter_idx))
269416660f0bSDavid Ahern 		return true;
269516660f0bSDavid Ahern 
269616660f0bSDavid Ahern 	return false;
269716660f0bSDavid Ahern }
269816660f0bSDavid Ahern 
26996f52f80eSDavid Ahern struct neigh_dump_filter {
27006f52f80eSDavid Ahern 	int master_idx;
27016f52f80eSDavid Ahern 	int dev_idx;
27026f52f80eSDavid Ahern };
27036f52f80eSDavid Ahern 
neigh_dump_table(struct neigh_table * tbl,struct sk_buff * skb,struct netlink_callback * cb,struct neigh_dump_filter * filter)27041da177e4SLinus Torvalds static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
27056f52f80eSDavid Ahern 			    struct netlink_callback *cb,
27066f52f80eSDavid Ahern 			    struct neigh_dump_filter *filter)
27071da177e4SLinus Torvalds {
27083b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
27091da177e4SLinus Torvalds 	struct neighbour *n;
27101da177e4SLinus Torvalds 	int rc, h, s_h = cb->args[1];
27111da177e4SLinus Torvalds 	int idx, s_idx = idx = cb->args[2];
2712d6bf7817SEric Dumazet 	struct neigh_hash_table *nht;
271321fdd092SDavid Ahern 	unsigned int flags = NLM_F_MULTI;
271421fdd092SDavid Ahern 
27156f52f80eSDavid Ahern 	if (filter->dev_idx || filter->master_idx)
271621fdd092SDavid Ahern 		flags |= NLM_F_DUMP_FILTERED;
27171da177e4SLinus Torvalds 
271809eed119SEric Dumazet 	rcu_read_lock();
271909eed119SEric Dumazet 	nht = rcu_dereference(tbl->nht);
2720d6bf7817SEric Dumazet 
27214bd6683bSEric Dumazet 	for (h = s_h; h < (1 << nht->hash_shift); h++) {
27221da177e4SLinus Torvalds 		if (h > s_h)
27231da177e4SLinus Torvalds 			s_idx = 0;
272409eed119SEric Dumazet 		for (n = rcu_dereference(nht->hash_buckets[h]), idx = 0;
2725767e97e1SEric Dumazet 		     n != NULL;
272609eed119SEric Dumazet 		     n = rcu_dereference(n->next)) {
272718502acdSZhang Shengju 			if (idx < s_idx || !net_eq(dev_net(n->dev), net))
272818502acdSZhang Shengju 				goto next;
27296f52f80eSDavid Ahern 			if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
27306f52f80eSDavid Ahern 			    neigh_master_filtered(n->dev, filter->master_idx))
2731efc683fcSGautam Kachroo 				goto next;
273215e47304SEric W. Biederman 			if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid,
27331da177e4SLinus Torvalds 					    cb->nlh->nlmsg_seq,
2734b6544c0bSJamal Hadi Salim 					    RTM_NEWNEIGH,
273521fdd092SDavid Ahern 					    flags) < 0) {
27361da177e4SLinus Torvalds 				rc = -1;
27371da177e4SLinus Torvalds 				goto out;
27381da177e4SLinus Torvalds 			}
2739efc683fcSGautam Kachroo next:
2740efc683fcSGautam Kachroo 			idx++;
27411da177e4SLinus Torvalds 		}
27421da177e4SLinus Torvalds 	}
27431da177e4SLinus Torvalds 	rc = skb->len;
27441da177e4SLinus Torvalds out:
274509eed119SEric Dumazet 	rcu_read_unlock();
27461da177e4SLinus Torvalds 	cb->args[1] = h;
27471da177e4SLinus Torvalds 	cb->args[2] = idx;
27481da177e4SLinus Torvalds 	return rc;
27491da177e4SLinus Torvalds }
27501da177e4SLinus Torvalds 
pneigh_dump_table(struct neigh_table * tbl,struct sk_buff * skb,struct netlink_callback * cb,struct neigh_dump_filter * filter)275184920c14STony Zelenoff static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
27526f52f80eSDavid Ahern 			     struct netlink_callback *cb,
27536f52f80eSDavid Ahern 			     struct neigh_dump_filter *filter)
275484920c14STony Zelenoff {
275584920c14STony Zelenoff 	struct pneigh_entry *n;
275684920c14STony Zelenoff 	struct net *net = sock_net(skb->sk);
275784920c14STony Zelenoff 	int rc, h, s_h = cb->args[3];
275884920c14STony Zelenoff 	int idx, s_idx = idx = cb->args[4];
27596f52f80eSDavid Ahern 	unsigned int flags = NLM_F_MULTI;
27606f52f80eSDavid Ahern 
27616f52f80eSDavid Ahern 	if (filter->dev_idx || filter->master_idx)
27626f52f80eSDavid Ahern 		flags |= NLM_F_DUMP_FILTERED;
276384920c14STony Zelenoff 
276484920c14STony Zelenoff 	read_lock_bh(&tbl->lock);
276584920c14STony Zelenoff 
27664bd6683bSEric Dumazet 	for (h = s_h; h <= PNEIGH_HASHMASK; h++) {
276784920c14STony Zelenoff 		if (h > s_h)
276884920c14STony Zelenoff 			s_idx = 0;
276984920c14STony Zelenoff 		for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) {
277018502acdSZhang Shengju 			if (idx < s_idx || pneigh_net(n) != net)
277184920c14STony Zelenoff 				goto next;
27726f52f80eSDavid Ahern 			if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
27736f52f80eSDavid Ahern 			    neigh_master_filtered(n->dev, filter->master_idx))
27746f52f80eSDavid Ahern 				goto next;
277515e47304SEric W. Biederman 			if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid,
277684920c14STony Zelenoff 					    cb->nlh->nlmsg_seq,
27776f52f80eSDavid Ahern 					    RTM_NEWNEIGH, flags, tbl) < 0) {
277884920c14STony Zelenoff 				read_unlock_bh(&tbl->lock);
277984920c14STony Zelenoff 				rc = -1;
278084920c14STony Zelenoff 				goto out;
278184920c14STony Zelenoff 			}
278284920c14STony Zelenoff 		next:
278384920c14STony Zelenoff 			idx++;
278484920c14STony Zelenoff 		}
278584920c14STony Zelenoff 	}
278684920c14STony Zelenoff 
278784920c14STony Zelenoff 	read_unlock_bh(&tbl->lock);
278884920c14STony Zelenoff 	rc = skb->len;
278984920c14STony Zelenoff out:
279084920c14STony Zelenoff 	cb->args[3] = h;
279184920c14STony Zelenoff 	cb->args[4] = idx;
279284920c14STony Zelenoff 	return rc;
279384920c14STony Zelenoff 
279484920c14STony Zelenoff }
279584920c14STony Zelenoff 
neigh_valid_dump_req(const struct nlmsghdr * nlh,bool strict_check,struct neigh_dump_filter * filter,struct netlink_ext_ack * extack)279651183d23SDavid Ahern static int neigh_valid_dump_req(const struct nlmsghdr *nlh,
279751183d23SDavid Ahern 				bool strict_check,
279851183d23SDavid Ahern 				struct neigh_dump_filter *filter,
279951183d23SDavid Ahern 				struct netlink_ext_ack *extack)
280051183d23SDavid Ahern {
280151183d23SDavid Ahern 	struct nlattr *tb[NDA_MAX + 1];
280251183d23SDavid Ahern 	int err, i;
280351183d23SDavid Ahern 
280451183d23SDavid Ahern 	if (strict_check) {
280551183d23SDavid Ahern 		struct ndmsg *ndm;
280651183d23SDavid Ahern 
280751183d23SDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
280851183d23SDavid Ahern 			NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request");
280951183d23SDavid Ahern 			return -EINVAL;
281051183d23SDavid Ahern 		}
281151183d23SDavid Ahern 
281251183d23SDavid Ahern 		ndm = nlmsg_data(nlh);
281351183d23SDavid Ahern 		if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_ifindex ||
2814c0fde870SDavid Ahern 		    ndm->ndm_state || ndm->ndm_type) {
281551183d23SDavid Ahern 			NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request");
281651183d23SDavid Ahern 			return -EINVAL;
281751183d23SDavid Ahern 		}
281851183d23SDavid Ahern 
2819c0fde870SDavid Ahern 		if (ndm->ndm_flags & ~NTF_PROXY) {
2820c0fde870SDavid Ahern 			NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request");
2821c0fde870SDavid Ahern 			return -EINVAL;
2822c0fde870SDavid Ahern 		}
2823c0fde870SDavid Ahern 
28248cb08174SJohannes Berg 		err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg),
28258cb08174SJohannes Berg 						    tb, NDA_MAX, nda_policy,
28268cb08174SJohannes Berg 						    extack);
282751183d23SDavid Ahern 	} else {
28288cb08174SJohannes Berg 		err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb,
28298cb08174SJohannes Berg 					     NDA_MAX, nda_policy, extack);
283051183d23SDavid Ahern 	}
283151183d23SDavid Ahern 	if (err < 0)
283251183d23SDavid Ahern 		return err;
283351183d23SDavid Ahern 
283451183d23SDavid Ahern 	for (i = 0; i <= NDA_MAX; ++i) {
283551183d23SDavid Ahern 		if (!tb[i])
283651183d23SDavid Ahern 			continue;
283751183d23SDavid Ahern 
283851183d23SDavid Ahern 		/* all new attributes should require strict_check */
283951183d23SDavid Ahern 		switch (i) {
284051183d23SDavid Ahern 		case NDA_IFINDEX:
284151183d23SDavid Ahern 			filter->dev_idx = nla_get_u32(tb[i]);
284251183d23SDavid Ahern 			break;
284351183d23SDavid Ahern 		case NDA_MASTER:
284451183d23SDavid Ahern 			filter->master_idx = nla_get_u32(tb[i]);
284551183d23SDavid Ahern 			break;
284651183d23SDavid Ahern 		default:
284751183d23SDavid Ahern 			if (strict_check) {
284851183d23SDavid Ahern 				NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request");
284951183d23SDavid Ahern 				return -EINVAL;
285051183d23SDavid Ahern 			}
285151183d23SDavid Ahern 		}
285251183d23SDavid Ahern 	}
285351183d23SDavid Ahern 
285451183d23SDavid Ahern 	return 0;
285551183d23SDavid Ahern }
285651183d23SDavid Ahern 
neigh_dump_info(struct sk_buff * skb,struct netlink_callback * cb)2857c8822a4eSThomas Graf static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
28581da177e4SLinus Torvalds {
28596f52f80eSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
28606f52f80eSDavid Ahern 	struct neigh_dump_filter filter = {};
28611da177e4SLinus Torvalds 	struct neigh_table *tbl;
28621da177e4SLinus Torvalds 	int t, family, s_t;
286384920c14STony Zelenoff 	int proxy = 0;
28644bd6683bSEric Dumazet 	int err;
28651da177e4SLinus Torvalds 
28666f52f80eSDavid Ahern 	family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
286784920c14STony Zelenoff 
286884920c14STony Zelenoff 	/* check for full ndmsg structure presence, family member is
286984920c14STony Zelenoff 	 * the same for both structures
287084920c14STony Zelenoff 	 */
28716f52f80eSDavid Ahern 	if (nlmsg_len(nlh) >= sizeof(struct ndmsg) &&
28726f52f80eSDavid Ahern 	    ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY)
287384920c14STony Zelenoff 		proxy = 1;
287484920c14STony Zelenoff 
287551183d23SDavid Ahern 	err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack);
287651183d23SDavid Ahern 	if (err < 0 && cb->strict_check)
287751183d23SDavid Ahern 		return err;
287851183d23SDavid Ahern 
28791da177e4SLinus Torvalds 	s_t = cb->args[0];
28801da177e4SLinus Torvalds 
2881d7480fd3SWANG Cong 	for (t = 0; t < NEIGH_NR_TABLES; t++) {
2882d7480fd3SWANG Cong 		tbl = neigh_tables[t];
2883d7480fd3SWANG Cong 
2884d7480fd3SWANG Cong 		if (!tbl)
2885d7480fd3SWANG Cong 			continue;
28861da177e4SLinus Torvalds 		if (t < s_t || (family && tbl->family != family))
28871da177e4SLinus Torvalds 			continue;
28881da177e4SLinus Torvalds 		if (t > s_t)
28891da177e4SLinus Torvalds 			memset(&cb->args[1], 0, sizeof(cb->args) -
28901da177e4SLinus Torvalds 						sizeof(cb->args[0]));
289184920c14STony Zelenoff 		if (proxy)
28926f52f80eSDavid Ahern 			err = pneigh_dump_table(tbl, skb, cb, &filter);
289384920c14STony Zelenoff 		else
28946f52f80eSDavid Ahern 			err = neigh_dump_table(tbl, skb, cb, &filter);
28954bd6683bSEric Dumazet 		if (err < 0)
28964bd6683bSEric Dumazet 			break;
28971da177e4SLinus Torvalds 	}
28981da177e4SLinus Torvalds 
28991da177e4SLinus Torvalds 	cb->args[0] = t;
29001da177e4SLinus Torvalds 	return skb->len;
29011da177e4SLinus Torvalds }
29021da177e4SLinus Torvalds 
neigh_valid_get_req(const struct nlmsghdr * nlh,struct neigh_table ** tbl,void ** dst,int * dev_idx,u8 * ndm_flags,struct netlink_ext_ack * extack)290382cbb5c6SRoopa Prabhu static int neigh_valid_get_req(const struct nlmsghdr *nlh,
290482cbb5c6SRoopa Prabhu 			       struct neigh_table **tbl,
290582cbb5c6SRoopa Prabhu 			       void **dst, int *dev_idx, u8 *ndm_flags,
290682cbb5c6SRoopa Prabhu 			       struct netlink_ext_ack *extack)
290782cbb5c6SRoopa Prabhu {
290882cbb5c6SRoopa Prabhu 	struct nlattr *tb[NDA_MAX + 1];
290982cbb5c6SRoopa Prabhu 	struct ndmsg *ndm;
291082cbb5c6SRoopa Prabhu 	int err, i;
291182cbb5c6SRoopa Prabhu 
291282cbb5c6SRoopa Prabhu 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
291382cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request");
291482cbb5c6SRoopa Prabhu 		return -EINVAL;
291582cbb5c6SRoopa Prabhu 	}
291682cbb5c6SRoopa Prabhu 
291782cbb5c6SRoopa Prabhu 	ndm = nlmsg_data(nlh);
291882cbb5c6SRoopa Prabhu 	if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_state ||
291982cbb5c6SRoopa Prabhu 	    ndm->ndm_type) {
292082cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request");
292182cbb5c6SRoopa Prabhu 		return -EINVAL;
292282cbb5c6SRoopa Prabhu 	}
292382cbb5c6SRoopa Prabhu 
292482cbb5c6SRoopa Prabhu 	if (ndm->ndm_flags & ~NTF_PROXY) {
292582cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request");
292682cbb5c6SRoopa Prabhu 		return -EINVAL;
292782cbb5c6SRoopa Prabhu 	}
292882cbb5c6SRoopa Prabhu 
29298cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb,
29308cb08174SJohannes Berg 					    NDA_MAX, nda_policy, extack);
293182cbb5c6SRoopa Prabhu 	if (err < 0)
293282cbb5c6SRoopa Prabhu 		return err;
293382cbb5c6SRoopa Prabhu 
293482cbb5c6SRoopa Prabhu 	*ndm_flags = ndm->ndm_flags;
293582cbb5c6SRoopa Prabhu 	*dev_idx = ndm->ndm_ifindex;
293682cbb5c6SRoopa Prabhu 	*tbl = neigh_find_table(ndm->ndm_family);
293782cbb5c6SRoopa Prabhu 	if (*tbl == NULL) {
293882cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
293982cbb5c6SRoopa Prabhu 		return -EAFNOSUPPORT;
294082cbb5c6SRoopa Prabhu 	}
294182cbb5c6SRoopa Prabhu 
294282cbb5c6SRoopa Prabhu 	for (i = 0; i <= NDA_MAX; ++i) {
294382cbb5c6SRoopa Prabhu 		if (!tb[i])
294482cbb5c6SRoopa Prabhu 			continue;
294582cbb5c6SRoopa Prabhu 
294682cbb5c6SRoopa Prabhu 		switch (i) {
294782cbb5c6SRoopa Prabhu 		case NDA_DST:
294882cbb5c6SRoopa Prabhu 			if (nla_len(tb[i]) != (int)(*tbl)->key_len) {
294982cbb5c6SRoopa Prabhu 				NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
295082cbb5c6SRoopa Prabhu 				return -EINVAL;
295182cbb5c6SRoopa Prabhu 			}
295282cbb5c6SRoopa Prabhu 			*dst = nla_data(tb[i]);
295382cbb5c6SRoopa Prabhu 			break;
295482cbb5c6SRoopa Prabhu 		default:
295582cbb5c6SRoopa Prabhu 			NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request");
295682cbb5c6SRoopa Prabhu 			return -EINVAL;
295782cbb5c6SRoopa Prabhu 		}
295882cbb5c6SRoopa Prabhu 	}
295982cbb5c6SRoopa Prabhu 
296082cbb5c6SRoopa Prabhu 	return 0;
296182cbb5c6SRoopa Prabhu }
296282cbb5c6SRoopa Prabhu 
neigh_nlmsg_size(void)296382cbb5c6SRoopa Prabhu static inline size_t neigh_nlmsg_size(void)
296482cbb5c6SRoopa Prabhu {
296582cbb5c6SRoopa Prabhu 	return NLMSG_ALIGN(sizeof(struct ndmsg))
296682cbb5c6SRoopa Prabhu 	       + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
296782cbb5c6SRoopa Prabhu 	       + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
296882cbb5c6SRoopa Prabhu 	       + nla_total_size(sizeof(struct nda_cacheinfo))
296982cbb5c6SRoopa Prabhu 	       + nla_total_size(4)  /* NDA_PROBES */
29702c611ad9SRoopa Prabhu 	       + nla_total_size(4)  /* NDA_FLAGS_EXT */
297182cbb5c6SRoopa Prabhu 	       + nla_total_size(1); /* NDA_PROTOCOL */
297282cbb5c6SRoopa Prabhu }
297382cbb5c6SRoopa Prabhu 
neigh_get_reply(struct net * net,struct neighbour * neigh,u32 pid,u32 seq)297482cbb5c6SRoopa Prabhu static int neigh_get_reply(struct net *net, struct neighbour *neigh,
297582cbb5c6SRoopa Prabhu 			   u32 pid, u32 seq)
297682cbb5c6SRoopa Prabhu {
297782cbb5c6SRoopa Prabhu 	struct sk_buff *skb;
297882cbb5c6SRoopa Prabhu 	int err = 0;
297982cbb5c6SRoopa Prabhu 
298082cbb5c6SRoopa Prabhu 	skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL);
298182cbb5c6SRoopa Prabhu 	if (!skb)
298282cbb5c6SRoopa Prabhu 		return -ENOBUFS;
298382cbb5c6SRoopa Prabhu 
298482cbb5c6SRoopa Prabhu 	err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0);
298582cbb5c6SRoopa Prabhu 	if (err) {
298682cbb5c6SRoopa Prabhu 		kfree_skb(skb);
298782cbb5c6SRoopa Prabhu 		goto errout;
298882cbb5c6SRoopa Prabhu 	}
298982cbb5c6SRoopa Prabhu 
299082cbb5c6SRoopa Prabhu 	err = rtnl_unicast(skb, net, pid);
299182cbb5c6SRoopa Prabhu errout:
299282cbb5c6SRoopa Prabhu 	return err;
299382cbb5c6SRoopa Prabhu }
299482cbb5c6SRoopa Prabhu 
pneigh_nlmsg_size(void)299582cbb5c6SRoopa Prabhu static inline size_t pneigh_nlmsg_size(void)
299682cbb5c6SRoopa Prabhu {
299782cbb5c6SRoopa Prabhu 	return NLMSG_ALIGN(sizeof(struct ndmsg))
2998463561e6SColin Ian King 	       + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
29992c611ad9SRoopa Prabhu 	       + nla_total_size(4)  /* NDA_FLAGS_EXT */
300082cbb5c6SRoopa Prabhu 	       + nla_total_size(1); /* NDA_PROTOCOL */
300182cbb5c6SRoopa Prabhu }
300282cbb5c6SRoopa Prabhu 
pneigh_get_reply(struct net * net,struct pneigh_entry * neigh,u32 pid,u32 seq,struct neigh_table * tbl)300382cbb5c6SRoopa Prabhu static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh,
300482cbb5c6SRoopa Prabhu 			    u32 pid, u32 seq, struct neigh_table *tbl)
300582cbb5c6SRoopa Prabhu {
300682cbb5c6SRoopa Prabhu 	struct sk_buff *skb;
300782cbb5c6SRoopa Prabhu 	int err = 0;
300882cbb5c6SRoopa Prabhu 
300982cbb5c6SRoopa Prabhu 	skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL);
301082cbb5c6SRoopa Prabhu 	if (!skb)
301182cbb5c6SRoopa Prabhu 		return -ENOBUFS;
301282cbb5c6SRoopa Prabhu 
301382cbb5c6SRoopa Prabhu 	err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl);
301482cbb5c6SRoopa Prabhu 	if (err) {
301582cbb5c6SRoopa Prabhu 		kfree_skb(skb);
301682cbb5c6SRoopa Prabhu 		goto errout;
301782cbb5c6SRoopa Prabhu 	}
301882cbb5c6SRoopa Prabhu 
301982cbb5c6SRoopa Prabhu 	err = rtnl_unicast(skb, net, pid);
302082cbb5c6SRoopa Prabhu errout:
302182cbb5c6SRoopa Prabhu 	return err;
302282cbb5c6SRoopa Prabhu }
302382cbb5c6SRoopa Prabhu 
neigh_get(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)302482cbb5c6SRoopa Prabhu static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
302582cbb5c6SRoopa Prabhu 		     struct netlink_ext_ack *extack)
302682cbb5c6SRoopa Prabhu {
302782cbb5c6SRoopa Prabhu 	struct net *net = sock_net(in_skb->sk);
302882cbb5c6SRoopa Prabhu 	struct net_device *dev = NULL;
302982cbb5c6SRoopa Prabhu 	struct neigh_table *tbl = NULL;
303082cbb5c6SRoopa Prabhu 	struct neighbour *neigh;
303182cbb5c6SRoopa Prabhu 	void *dst = NULL;
303282cbb5c6SRoopa Prabhu 	u8 ndm_flags = 0;
303382cbb5c6SRoopa Prabhu 	int dev_idx = 0;
303482cbb5c6SRoopa Prabhu 	int err;
303582cbb5c6SRoopa Prabhu 
303682cbb5c6SRoopa Prabhu 	err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags,
303782cbb5c6SRoopa Prabhu 				  extack);
303882cbb5c6SRoopa Prabhu 	if (err < 0)
303982cbb5c6SRoopa Prabhu 		return err;
304082cbb5c6SRoopa Prabhu 
304182cbb5c6SRoopa Prabhu 	if (dev_idx) {
304282cbb5c6SRoopa Prabhu 		dev = __dev_get_by_index(net, dev_idx);
304382cbb5c6SRoopa Prabhu 		if (!dev) {
304482cbb5c6SRoopa Prabhu 			NL_SET_ERR_MSG(extack, "Unknown device ifindex");
304582cbb5c6SRoopa Prabhu 			return -ENODEV;
304682cbb5c6SRoopa Prabhu 		}
304782cbb5c6SRoopa Prabhu 	}
304882cbb5c6SRoopa Prabhu 
304982cbb5c6SRoopa Prabhu 	if (!dst) {
305082cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "Network address not specified");
305182cbb5c6SRoopa Prabhu 		return -EINVAL;
305282cbb5c6SRoopa Prabhu 	}
305382cbb5c6SRoopa Prabhu 
305482cbb5c6SRoopa Prabhu 	if (ndm_flags & NTF_PROXY) {
305582cbb5c6SRoopa Prabhu 		struct pneigh_entry *pn;
305682cbb5c6SRoopa Prabhu 
305782cbb5c6SRoopa Prabhu 		pn = pneigh_lookup(tbl, net, dst, dev, 0);
305882cbb5c6SRoopa Prabhu 		if (!pn) {
305982cbb5c6SRoopa Prabhu 			NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found");
306082cbb5c6SRoopa Prabhu 			return -ENOENT;
306182cbb5c6SRoopa Prabhu 		}
306282cbb5c6SRoopa Prabhu 		return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid,
306382cbb5c6SRoopa Prabhu 					nlh->nlmsg_seq, tbl);
306482cbb5c6SRoopa Prabhu 	}
306582cbb5c6SRoopa Prabhu 
306682cbb5c6SRoopa Prabhu 	if (!dev) {
306782cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "No device specified");
306882cbb5c6SRoopa Prabhu 		return -EINVAL;
306982cbb5c6SRoopa Prabhu 	}
307082cbb5c6SRoopa Prabhu 
307182cbb5c6SRoopa Prabhu 	neigh = neigh_lookup(tbl, dst, dev);
307282cbb5c6SRoopa Prabhu 	if (!neigh) {
307382cbb5c6SRoopa Prabhu 		NL_SET_ERR_MSG(extack, "Neighbour entry not found");
307482cbb5c6SRoopa Prabhu 		return -ENOENT;
307582cbb5c6SRoopa Prabhu 	}
307682cbb5c6SRoopa Prabhu 
307782cbb5c6SRoopa Prabhu 	err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid,
307882cbb5c6SRoopa Prabhu 			      nlh->nlmsg_seq);
307982cbb5c6SRoopa Prabhu 
308082cbb5c6SRoopa Prabhu 	neigh_release(neigh);
308182cbb5c6SRoopa Prabhu 
308282cbb5c6SRoopa Prabhu 	return err;
308382cbb5c6SRoopa Prabhu }
308482cbb5c6SRoopa Prabhu 
neigh_for_each(struct neigh_table * tbl,void (* cb)(struct neighbour *,void *),void * cookie)30851da177e4SLinus Torvalds void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie)
30861da177e4SLinus Torvalds {
30871da177e4SLinus Torvalds 	int chain;
3088d6bf7817SEric Dumazet 	struct neigh_hash_table *nht;
30891da177e4SLinus Torvalds 
309009eed119SEric Dumazet 	rcu_read_lock();
309109eed119SEric Dumazet 	nht = rcu_dereference(tbl->nht);
3092d6bf7817SEric Dumazet 
309309eed119SEric Dumazet 	read_lock_bh(&tbl->lock); /* avoid resizes */
3094cd089336SDavid S. Miller 	for (chain = 0; chain < (1 << nht->hash_shift); chain++) {
30951da177e4SLinus Torvalds 		struct neighbour *n;
30961da177e4SLinus Torvalds 
309709eed119SEric Dumazet 		for (n = rcu_dereference(nht->hash_buckets[chain]);
3098767e97e1SEric Dumazet 		     n != NULL;
309909eed119SEric Dumazet 		     n = rcu_dereference(n->next))
31001da177e4SLinus Torvalds 			cb(n, cookie);
31011da177e4SLinus Torvalds 	}
310209eed119SEric Dumazet 	read_unlock_bh(&tbl->lock);
310309eed119SEric Dumazet 	rcu_read_unlock();
31041da177e4SLinus Torvalds }
31051da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_for_each);
31061da177e4SLinus Torvalds 
31071da177e4SLinus Torvalds /* The tbl->lock must be held as a writer and BH disabled. */
__neigh_for_each_release(struct neigh_table * tbl,int (* cb)(struct neighbour *))31081da177e4SLinus Torvalds void __neigh_for_each_release(struct neigh_table *tbl,
31091da177e4SLinus Torvalds 			      int (*cb)(struct neighbour *))
31101da177e4SLinus Torvalds {
31111da177e4SLinus Torvalds 	int chain;
3112d6bf7817SEric Dumazet 	struct neigh_hash_table *nht;
31131da177e4SLinus Torvalds 
3114d6bf7817SEric Dumazet 	nht = rcu_dereference_protected(tbl->nht,
3115d6bf7817SEric Dumazet 					lockdep_is_held(&tbl->lock));
3116cd089336SDavid S. Miller 	for (chain = 0; chain < (1 << nht->hash_shift); chain++) {
3117767e97e1SEric Dumazet 		struct neighbour *n;
3118767e97e1SEric Dumazet 		struct neighbour __rcu **np;
31191da177e4SLinus Torvalds 
3120d6bf7817SEric Dumazet 		np = &nht->hash_buckets[chain];
3121767e97e1SEric Dumazet 		while ((n = rcu_dereference_protected(*np,
3122767e97e1SEric Dumazet 					lockdep_is_held(&tbl->lock))) != NULL) {
31231da177e4SLinus Torvalds 			int release;
31241da177e4SLinus Torvalds 
31251da177e4SLinus Torvalds 			write_lock(&n->lock);
31261da177e4SLinus Torvalds 			release = cb(n);
31271da177e4SLinus Torvalds 			if (release) {
3128767e97e1SEric Dumazet 				rcu_assign_pointer(*np,
3129767e97e1SEric Dumazet 					rcu_dereference_protected(n->next,
3130767e97e1SEric Dumazet 						lockdep_is_held(&tbl->lock)));
313158956317SDavid Ahern 				neigh_mark_dead(n);
31321da177e4SLinus Torvalds 			} else
31331da177e4SLinus Torvalds 				np = &n->next;
31341da177e4SLinus Torvalds 			write_unlock(&n->lock);
31354f494554SThomas Graf 			if (release)
31364f494554SThomas Graf 				neigh_cleanup_and_release(n);
31371da177e4SLinus Torvalds 		}
31381da177e4SLinus Torvalds 	}
3139ecbb4169SAlexey Kuznetsov }
31401da177e4SLinus Torvalds EXPORT_SYMBOL(__neigh_for_each_release);
31411da177e4SLinus Torvalds 
neigh_xmit(int index,struct net_device * dev,const void * addr,struct sk_buff * skb)3142b79bda3dSEric W. Biederman int neigh_xmit(int index, struct net_device *dev,
31434fd3d7d9SEric W. Biederman 	       const void *addr, struct sk_buff *skb)
31444fd3d7d9SEric W. Biederman {
3145b79bda3dSEric W. Biederman 	int err = -EAFNOSUPPORT;
3146b79bda3dSEric W. Biederman 	if (likely(index < NEIGH_NR_TABLES)) {
31474fd3d7d9SEric W. Biederman 		struct neigh_table *tbl;
31484fd3d7d9SEric W. Biederman 		struct neighbour *neigh;
31494fd3d7d9SEric W. Biederman 
3150b79bda3dSEric W. Biederman 		tbl = neigh_tables[index];
31514fd3d7d9SEric W. Biederman 		if (!tbl)
31524fd3d7d9SEric W. Biederman 			goto out;
315309eed119SEric Dumazet 		rcu_read_lock();
31544b2a2bfeSDavid Ahern 		if (index == NEIGH_ARP_TABLE) {
31554b2a2bfeSDavid Ahern 			u32 key = *((u32 *)addr);
31564b2a2bfeSDavid Ahern 
31574b2a2bfeSDavid Ahern 			neigh = __ipv4_neigh_lookup_noref(dev, key);
31584b2a2bfeSDavid Ahern 		} else {
31594fd3d7d9SEric W. Biederman 			neigh = __neigh_lookup_noref(tbl, addr, dev);
31604b2a2bfeSDavid Ahern 		}
31614fd3d7d9SEric W. Biederman 		if (!neigh)
31624fd3d7d9SEric W. Biederman 			neigh = __neigh_create(tbl, addr, dev, false);
31634fd3d7d9SEric W. Biederman 		err = PTR_ERR(neigh);
3164b560f03dSDavid Barroso 		if (IS_ERR(neigh)) {
316509eed119SEric Dumazet 			rcu_read_unlock();
31664fd3d7d9SEric W. Biederman 			goto out_kfree_skb;
3167b560f03dSDavid Barroso 		}
31685baa0433SEric Dumazet 		err = READ_ONCE(neigh->output)(neigh, skb);
316909eed119SEric Dumazet 		rcu_read_unlock();
31704fd3d7d9SEric W. Biederman 	}
3171b79bda3dSEric W. Biederman 	else if (index == NEIGH_LINK_TABLE) {
3172b79bda3dSEric W. Biederman 		err = dev_hard_header(skb, dev, ntohs(skb->protocol),
3173b79bda3dSEric W. Biederman 				      addr, NULL, skb->len);
3174b79bda3dSEric W. Biederman 		if (err < 0)
3175b79bda3dSEric W. Biederman 			goto out_kfree_skb;
3176b79bda3dSEric W. Biederman 		err = dev_queue_xmit(skb);
3177b79bda3dSEric W. Biederman 	}
31784fd3d7d9SEric W. Biederman out:
31794fd3d7d9SEric W. Biederman 	return err;
31804fd3d7d9SEric W. Biederman out_kfree_skb:
31814fd3d7d9SEric W. Biederman 	kfree_skb(skb);
31824fd3d7d9SEric W. Biederman 	goto out;
31834fd3d7d9SEric W. Biederman }
31844fd3d7d9SEric W. Biederman EXPORT_SYMBOL(neigh_xmit);
31854fd3d7d9SEric W. Biederman 
31861da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
31871da177e4SLinus Torvalds 
neigh_get_first(struct seq_file * seq)31881da177e4SLinus Torvalds static struct neighbour *neigh_get_first(struct seq_file *seq)
31891da177e4SLinus Torvalds {
31901da177e4SLinus Torvalds 	struct neigh_seq_state *state = seq->private;
31911218854aSYOSHIFUJI Hideaki 	struct net *net = seq_file_net(seq);
3192d6bf7817SEric Dumazet 	struct neigh_hash_table *nht = state->nht;
31931da177e4SLinus Torvalds 	struct neighbour *n = NULL;
3194f530eed6SColin Ian King 	int bucket;
31951da177e4SLinus Torvalds 
31961da177e4SLinus Torvalds 	state->flags &= ~NEIGH_SEQ_IS_PNEIGH;
3197cd089336SDavid S. Miller 	for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) {
319809eed119SEric Dumazet 		n = rcu_dereference(nht->hash_buckets[bucket]);
31991da177e4SLinus Torvalds 
32001da177e4SLinus Torvalds 		while (n) {
3201878628fbSYOSHIFUJI Hideaki 			if (!net_eq(dev_net(n->dev), net))
3202426b5303SEric W. Biederman 				goto next;
32031da177e4SLinus Torvalds 			if (state->neigh_sub_iter) {
32041da177e4SLinus Torvalds 				loff_t fakep = 0;
32051da177e4SLinus Torvalds 				void *v;
32061da177e4SLinus Torvalds 
32071da177e4SLinus Torvalds 				v = state->neigh_sub_iter(state, n, &fakep);
32081da177e4SLinus Torvalds 				if (!v)
32091da177e4SLinus Torvalds 					goto next;
32101da177e4SLinus Torvalds 			}
32111da177e4SLinus Torvalds 			if (!(state->flags & NEIGH_SEQ_SKIP_NOARP))
32121da177e4SLinus Torvalds 				break;
3213b071af52SEric Dumazet 			if (READ_ONCE(n->nud_state) & ~NUD_NOARP)
32141da177e4SLinus Torvalds 				break;
32151da177e4SLinus Torvalds next:
321609eed119SEric Dumazet 			n = rcu_dereference(n->next);
32171da177e4SLinus Torvalds 		}
32181da177e4SLinus Torvalds 
32191da177e4SLinus Torvalds 		if (n)
32201da177e4SLinus Torvalds 			break;
32211da177e4SLinus Torvalds 	}
32221da177e4SLinus Torvalds 	state->bucket = bucket;
32231da177e4SLinus Torvalds 
32241da177e4SLinus Torvalds 	return n;
32251da177e4SLinus Torvalds }
32261da177e4SLinus Torvalds 
neigh_get_next(struct seq_file * seq,struct neighbour * n,loff_t * pos)32271da177e4SLinus Torvalds static struct neighbour *neigh_get_next(struct seq_file *seq,
32281da177e4SLinus Torvalds 					struct neighbour *n,
32291da177e4SLinus Torvalds 					loff_t *pos)
32301da177e4SLinus Torvalds {
32311da177e4SLinus Torvalds 	struct neigh_seq_state *state = seq->private;
32321218854aSYOSHIFUJI Hideaki 	struct net *net = seq_file_net(seq);
3233d6bf7817SEric Dumazet 	struct neigh_hash_table *nht = state->nht;
32341da177e4SLinus Torvalds 
32351da177e4SLinus Torvalds 	if (state->neigh_sub_iter) {
32361da177e4SLinus Torvalds 		void *v = state->neigh_sub_iter(state, n, pos);
32371da177e4SLinus Torvalds 		if (v)
32381da177e4SLinus Torvalds 			return n;
32391da177e4SLinus Torvalds 	}
324009eed119SEric Dumazet 	n = rcu_dereference(n->next);
32411da177e4SLinus Torvalds 
32421da177e4SLinus Torvalds 	while (1) {
32431da177e4SLinus Torvalds 		while (n) {
3244878628fbSYOSHIFUJI Hideaki 			if (!net_eq(dev_net(n->dev), net))
3245426b5303SEric W. Biederman 				goto next;
32461da177e4SLinus Torvalds 			if (state->neigh_sub_iter) {
32471da177e4SLinus Torvalds 				void *v = state->neigh_sub_iter(state, n, pos);
32481da177e4SLinus Torvalds 				if (v)
32491da177e4SLinus Torvalds 					return n;
32501da177e4SLinus Torvalds 				goto next;
32511da177e4SLinus Torvalds 			}
32521da177e4SLinus Torvalds 			if (!(state->flags & NEIGH_SEQ_SKIP_NOARP))
32531da177e4SLinus Torvalds 				break;
32541da177e4SLinus Torvalds 
3255b071af52SEric Dumazet 			if (READ_ONCE(n->nud_state) & ~NUD_NOARP)
32561da177e4SLinus Torvalds 				break;
32571da177e4SLinus Torvalds next:
325809eed119SEric Dumazet 			n = rcu_dereference(n->next);
32591da177e4SLinus Torvalds 		}
32601da177e4SLinus Torvalds 
32611da177e4SLinus Torvalds 		if (n)
32621da177e4SLinus Torvalds 			break;
32631da177e4SLinus Torvalds 
3264cd089336SDavid S. Miller 		if (++state->bucket >= (1 << nht->hash_shift))
32651da177e4SLinus Torvalds 			break;
32661da177e4SLinus Torvalds 
326709eed119SEric Dumazet 		n = rcu_dereference(nht->hash_buckets[state->bucket]);
32681da177e4SLinus Torvalds 	}
32691da177e4SLinus Torvalds 
32701da177e4SLinus Torvalds 	if (n && pos)
32711da177e4SLinus Torvalds 		--(*pos);
32721da177e4SLinus Torvalds 	return n;
32731da177e4SLinus Torvalds }
32741da177e4SLinus Torvalds 
neigh_get_idx(struct seq_file * seq,loff_t * pos)32751da177e4SLinus Torvalds static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
32761da177e4SLinus Torvalds {
32771da177e4SLinus Torvalds 	struct neighbour *n = neigh_get_first(seq);
32781da177e4SLinus Torvalds 
32791da177e4SLinus Torvalds 	if (n) {
3280745e2031SChris Larson 		--(*pos);
32811da177e4SLinus Torvalds 		while (*pos) {
32821da177e4SLinus Torvalds 			n = neigh_get_next(seq, n, pos);
32831da177e4SLinus Torvalds 			if (!n)
32841da177e4SLinus Torvalds 				break;
32851da177e4SLinus Torvalds 		}
32861da177e4SLinus Torvalds 	}
32871da177e4SLinus Torvalds 	return *pos ? NULL : n;
32881da177e4SLinus Torvalds }
32891da177e4SLinus Torvalds 
pneigh_get_first(struct seq_file * seq)32901da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
32911da177e4SLinus Torvalds {
32921da177e4SLinus Torvalds 	struct neigh_seq_state *state = seq->private;
32931218854aSYOSHIFUJI Hideaki 	struct net *net = seq_file_net(seq);
32941da177e4SLinus Torvalds 	struct neigh_table *tbl = state->tbl;
32951da177e4SLinus Torvalds 	struct pneigh_entry *pn = NULL;
329648de7c0cSYang Li 	int bucket;
32971da177e4SLinus Torvalds 
32981da177e4SLinus Torvalds 	state->flags |= NEIGH_SEQ_IS_PNEIGH;
32991da177e4SLinus Torvalds 	for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) {
33001da177e4SLinus Torvalds 		pn = tbl->phash_buckets[bucket];
3301878628fbSYOSHIFUJI Hideaki 		while (pn && !net_eq(pneigh_net(pn), net))
3302426b5303SEric W. Biederman 			pn = pn->next;
33031da177e4SLinus Torvalds 		if (pn)
33041da177e4SLinus Torvalds 			break;
33051da177e4SLinus Torvalds 	}
33061da177e4SLinus Torvalds 	state->bucket = bucket;
33071da177e4SLinus Torvalds 
33081da177e4SLinus Torvalds 	return pn;
33091da177e4SLinus Torvalds }
33101da177e4SLinus Torvalds 
pneigh_get_next(struct seq_file * seq,struct pneigh_entry * pn,loff_t * pos)33111da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
33121da177e4SLinus Torvalds 					    struct pneigh_entry *pn,
33131da177e4SLinus Torvalds 					    loff_t *pos)
33141da177e4SLinus Torvalds {
33151da177e4SLinus Torvalds 	struct neigh_seq_state *state = seq->private;
33161218854aSYOSHIFUJI Hideaki 	struct net *net = seq_file_net(seq);
33171da177e4SLinus Torvalds 	struct neigh_table *tbl = state->tbl;
33181da177e4SLinus Torvalds 
3319df07a94cSJorge Boncompte [DTI2] 	do {
33201da177e4SLinus Torvalds 		pn = pn->next;
3321df07a94cSJorge Boncompte [DTI2] 	} while (pn && !net_eq(pneigh_net(pn), net));
3322df07a94cSJorge Boncompte [DTI2] 
33231da177e4SLinus Torvalds 	while (!pn) {
33241da177e4SLinus Torvalds 		if (++state->bucket > PNEIGH_HASHMASK)
33251da177e4SLinus Torvalds 			break;
33261da177e4SLinus Torvalds 		pn = tbl->phash_buckets[state->bucket];
3327878628fbSYOSHIFUJI Hideaki 		while (pn && !net_eq(pneigh_net(pn), net))
3328426b5303SEric W. Biederman 			pn = pn->next;
33291da177e4SLinus Torvalds 		if (pn)
33301da177e4SLinus Torvalds 			break;
33311da177e4SLinus Torvalds 	}
33321da177e4SLinus Torvalds 
33331da177e4SLinus Torvalds 	if (pn && pos)
33341da177e4SLinus Torvalds 		--(*pos);
33351da177e4SLinus Torvalds 
33361da177e4SLinus Torvalds 	return pn;
33371da177e4SLinus Torvalds }
33381da177e4SLinus Torvalds 
pneigh_get_idx(struct seq_file * seq,loff_t * pos)33391da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos)
33401da177e4SLinus Torvalds {
33411da177e4SLinus Torvalds 	struct pneigh_entry *pn = pneigh_get_first(seq);
33421da177e4SLinus Torvalds 
33431da177e4SLinus Torvalds 	if (pn) {
3344745e2031SChris Larson 		--(*pos);
33451da177e4SLinus Torvalds 		while (*pos) {
33461da177e4SLinus Torvalds 			pn = pneigh_get_next(seq, pn, pos);
33471da177e4SLinus Torvalds 			if (!pn)
33481da177e4SLinus Torvalds 				break;
33491da177e4SLinus Torvalds 		}
33501da177e4SLinus Torvalds 	}
33511da177e4SLinus Torvalds 	return *pos ? NULL : pn;
33521da177e4SLinus Torvalds }
33531da177e4SLinus Torvalds 
neigh_get_idx_any(struct seq_file * seq,loff_t * pos)33541da177e4SLinus Torvalds static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)
33551da177e4SLinus Torvalds {
33561da177e4SLinus Torvalds 	struct neigh_seq_state *state = seq->private;
33571da177e4SLinus Torvalds 	void *rc;
3358745e2031SChris Larson 	loff_t idxpos = *pos;
33591da177e4SLinus Torvalds 
3360745e2031SChris Larson 	rc = neigh_get_idx(seq, &idxpos);
33611da177e4SLinus Torvalds 	if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY))
3362745e2031SChris Larson 		rc = pneigh_get_idx(seq, &idxpos);
33631da177e4SLinus Torvalds 
33641da177e4SLinus Torvalds 	return rc;
33651da177e4SLinus Torvalds }
33661da177e4SLinus Torvalds 
neigh_seq_start(struct seq_file * seq,loff_t * pos,struct neigh_table * tbl,unsigned int neigh_seq_flags)33671da177e4SLinus Torvalds void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)
3368f3e92cb8SEric Dumazet 	__acquires(tbl->lock)
336909eed119SEric Dumazet 	__acquires(rcu)
33701da177e4SLinus Torvalds {
33711da177e4SLinus Torvalds 	struct neigh_seq_state *state = seq->private;
33721da177e4SLinus Torvalds 
33731da177e4SLinus Torvalds 	state->tbl = tbl;
33741da177e4SLinus Torvalds 	state->bucket = 0;
33751da177e4SLinus Torvalds 	state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH);
33761da177e4SLinus Torvalds 
337709eed119SEric Dumazet 	rcu_read_lock();
337809eed119SEric Dumazet 	state->nht = rcu_dereference(tbl->nht);
337909eed119SEric Dumazet 	read_lock_bh(&tbl->lock);
3380767e97e1SEric Dumazet 
3381745e2031SChris Larson 	return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN;
33821da177e4SLinus Torvalds }
33831da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_start);
33841da177e4SLinus Torvalds 
neigh_seq_next(struct seq_file * seq,void * v,loff_t * pos)33851da177e4SLinus Torvalds void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
33861da177e4SLinus Torvalds {
33871da177e4SLinus Torvalds 	struct neigh_seq_state *state;
33881da177e4SLinus Torvalds 	void *rc;
33891da177e4SLinus Torvalds 
33901da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN) {
3391bff69732SChris Larson 		rc = neigh_get_first(seq);
33921da177e4SLinus Torvalds 		goto out;
33931da177e4SLinus Torvalds 	}
33941da177e4SLinus Torvalds 
33951da177e4SLinus Torvalds 	state = seq->private;
33961da177e4SLinus Torvalds 	if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) {
33971da177e4SLinus Torvalds 		rc = neigh_get_next(seq, v, NULL);
33981da177e4SLinus Torvalds 		if (rc)
33991da177e4SLinus Torvalds 			goto out;
34001da177e4SLinus Torvalds 		if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY))
34011da177e4SLinus Torvalds 			rc = pneigh_get_first(seq);
34021da177e4SLinus Torvalds 	} else {
34031da177e4SLinus Torvalds 		BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY);
34041da177e4SLinus Torvalds 		rc = pneigh_get_next(seq, v, NULL);
34051da177e4SLinus Torvalds 	}
34061da177e4SLinus Torvalds out:
34071da177e4SLinus Torvalds 	++(*pos);
34081da177e4SLinus Torvalds 	return rc;
34091da177e4SLinus Torvalds }
34101da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_next);
34111da177e4SLinus Torvalds 
neigh_seq_stop(struct seq_file * seq,void * v)34121da177e4SLinus Torvalds void neigh_seq_stop(struct seq_file *seq, void *v)
3413f3e92cb8SEric Dumazet 	__releases(tbl->lock)
341409eed119SEric Dumazet 	__releases(rcu)
34151da177e4SLinus Torvalds {
3416f3e92cb8SEric Dumazet 	struct neigh_seq_state *state = seq->private;
3417f3e92cb8SEric Dumazet 	struct neigh_table *tbl = state->tbl;
3418f3e92cb8SEric Dumazet 
341909eed119SEric Dumazet 	read_unlock_bh(&tbl->lock);
342009eed119SEric Dumazet 	rcu_read_unlock();
34211da177e4SLinus Torvalds }
34221da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_stop);
34231da177e4SLinus Torvalds 
34241da177e4SLinus Torvalds /* statistics via seq_file */
34251da177e4SLinus Torvalds 
neigh_stat_seq_start(struct seq_file * seq,loff_t * pos)34261da177e4SLinus Torvalds static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
34271da177e4SLinus Torvalds {
3428359745d7SMuchun Song 	struct neigh_table *tbl = pde_data(file_inode(seq->file));
34291da177e4SLinus Torvalds 	int cpu;
34301da177e4SLinus Torvalds 
34311da177e4SLinus Torvalds 	if (*pos == 0)
34321da177e4SLinus Torvalds 		return SEQ_START_TOKEN;
34331da177e4SLinus Torvalds 
34340f23174aSRusty Russell 	for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
34351da177e4SLinus Torvalds 		if (!cpu_possible(cpu))
34361da177e4SLinus Torvalds 			continue;
34371da177e4SLinus Torvalds 		*pos = cpu+1;
34381da177e4SLinus Torvalds 		return per_cpu_ptr(tbl->stats, cpu);
34391da177e4SLinus Torvalds 	}
34401da177e4SLinus Torvalds 	return NULL;
34411da177e4SLinus Torvalds }
34421da177e4SLinus Torvalds 
neigh_stat_seq_next(struct seq_file * seq,void * v,loff_t * pos)34431da177e4SLinus Torvalds static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
34441da177e4SLinus Torvalds {
3445359745d7SMuchun Song 	struct neigh_table *tbl = pde_data(file_inode(seq->file));
34461da177e4SLinus Torvalds 	int cpu;
34471da177e4SLinus Torvalds 
34480f23174aSRusty Russell 	for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
34491da177e4SLinus Torvalds 		if (!cpu_possible(cpu))
34501da177e4SLinus Torvalds 			continue;
34511da177e4SLinus Torvalds 		*pos = cpu+1;
34521da177e4SLinus Torvalds 		return per_cpu_ptr(tbl->stats, cpu);
34531da177e4SLinus Torvalds 	}
34541e3f9f07SVasily Averin 	(*pos)++;
34551da177e4SLinus Torvalds 	return NULL;
34561da177e4SLinus Torvalds }
34571da177e4SLinus Torvalds 
neigh_stat_seq_stop(struct seq_file * seq,void * v)34581da177e4SLinus Torvalds static void neigh_stat_seq_stop(struct seq_file *seq, void *v)
34591da177e4SLinus Torvalds {
34601da177e4SLinus Torvalds 
34611da177e4SLinus Torvalds }
34621da177e4SLinus Torvalds 
neigh_stat_seq_show(struct seq_file * seq,void * v)34631da177e4SLinus Torvalds static int neigh_stat_seq_show(struct seq_file *seq, void *v)
34641da177e4SLinus Torvalds {
3465359745d7SMuchun Song 	struct neigh_table *tbl = pde_data(file_inode(seq->file));
34661da177e4SLinus Torvalds 	struct neigh_statistics *st = v;
34671da177e4SLinus Torvalds 
34681da177e4SLinus Torvalds 	if (v == SEQ_START_TOKEN) {
34690547ffe6SYajun Deng 		seq_puts(seq, "entries  allocs   destroys hash_grows lookups  hits     res_failed rcv_probes_mcast rcv_probes_ucast periodic_gc_runs forced_gc_runs unresolved_discards table_fulls\n");
34701da177e4SLinus Torvalds 		return 0;
34711da177e4SLinus Torvalds 	}
34721da177e4SLinus Torvalds 
34731da177e4SLinus Torvalds 	seq_printf(seq, "%08x %08lx %08lx %08lx   %08lx %08lx %08lx   "
34740547ffe6SYajun Deng 			"%08lx         %08lx         %08lx         "
34750547ffe6SYajun Deng 			"%08lx       %08lx            %08lx\n",
34761da177e4SLinus Torvalds 		   atomic_read(&tbl->entries),
34771da177e4SLinus Torvalds 
34781da177e4SLinus Torvalds 		   st->allocs,
34791da177e4SLinus Torvalds 		   st->destroys,
34801da177e4SLinus Torvalds 		   st->hash_grows,
34811da177e4SLinus Torvalds 
34821da177e4SLinus Torvalds 		   st->lookups,
34831da177e4SLinus Torvalds 		   st->hits,
34841da177e4SLinus Torvalds 
34851da177e4SLinus Torvalds 		   st->res_failed,
34861da177e4SLinus Torvalds 
34871da177e4SLinus Torvalds 		   st->rcv_probes_mcast,
34881da177e4SLinus Torvalds 		   st->rcv_probes_ucast,
34891da177e4SLinus Torvalds 
34901da177e4SLinus Torvalds 		   st->periodic_gc_runs,
34919a6d276eSNeil Horman 		   st->forced_gc_runs,
3492fb811395SRick Jones 		   st->unres_discards,
3493fb811395SRick Jones 		   st->table_fulls
34941da177e4SLinus Torvalds 		   );
34951da177e4SLinus Torvalds 
34961da177e4SLinus Torvalds 	return 0;
34971da177e4SLinus Torvalds }
34981da177e4SLinus Torvalds 
3499f690808eSStephen Hemminger static const struct seq_operations neigh_stat_seq_ops = {
35001da177e4SLinus Torvalds 	.start	= neigh_stat_seq_start,
35011da177e4SLinus Torvalds 	.next	= neigh_stat_seq_next,
35021da177e4SLinus Torvalds 	.stop	= neigh_stat_seq_stop,
35031da177e4SLinus Torvalds 	.show	= neigh_stat_seq_show,
35041da177e4SLinus Torvalds };
35051da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */
35061da177e4SLinus Torvalds 
__neigh_notify(struct neighbour * n,int type,int flags,u32 pid)35077b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags,
35087b8f7a40SRoopa Prabhu 			   u32 pid)
35091da177e4SLinus Torvalds {
3510c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(n->dev);
35118b8aec50SThomas Graf 	struct sk_buff *skb;
3512b8673311SThomas Graf 	int err = -ENOBUFS;
35131da177e4SLinus Torvalds 
3514339bf98fSThomas Graf 	skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC);
35158b8aec50SThomas Graf 	if (skb == NULL)
3516b8673311SThomas Graf 		goto errout;
35171da177e4SLinus Torvalds 
35187b8f7a40SRoopa Prabhu 	err = neigh_fill_info(skb, n, pid, 0, type, flags);
351926932566SPatrick McHardy 	if (err < 0) {
352026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in neigh_nlmsg_size() */
352126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
352226932566SPatrick McHardy 		kfree_skb(skb);
352326932566SPatrick McHardy 		goto errout;
352426932566SPatrick McHardy 	}
35251ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
35261ce85fe4SPablo Neira Ayuso 	return;
3527b8673311SThomas Graf errout:
3528b8673311SThomas Graf 	if (err < 0)
3529426b5303SEric W. Biederman 		rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
3530b8673311SThomas Graf }
3531b8673311SThomas Graf 
neigh_app_ns(struct neighbour * n)3532b8673311SThomas Graf void neigh_app_ns(struct neighbour *n)
3533b8673311SThomas Graf {
35347b8f7a40SRoopa Prabhu 	__neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0);
35358b8aec50SThomas Graf }
35360a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_app_ns);
35371da177e4SLinus Torvalds 
35381da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
3539b93196dcSCong Wang static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
35401da177e4SLinus Torvalds 
proc_unres_qlen(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)3541fe2c6338SJoe Perches static int proc_unres_qlen(struct ctl_table *ctl, int write,
354232927393SChristoph Hellwig 			   void *buffer, size_t *lenp, loff_t *ppos)
35438b5c171bSEric Dumazet {
35448b5c171bSEric Dumazet 	int size, ret;
3545fe2c6338SJoe Perches 	struct ctl_table tmp = *ctl;
35468b5c171bSEric Dumazet 
3547eec4844fSMatteo Croce 	tmp.extra1 = SYSCTL_ZERO;
3548ce46cc64SShan Wei 	tmp.extra2 = &unres_qlen_max;
35498b5c171bSEric Dumazet 	tmp.data = &size;
3550ce46cc64SShan Wei 
3551ce46cc64SShan Wei 	size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN);
3552ce46cc64SShan Wei 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
3553ce46cc64SShan Wei 
35548b5c171bSEric Dumazet 	if (write && !ret)
35558b5c171bSEric Dumazet 		*(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN);
35568b5c171bSEric Dumazet 	return ret;
35578b5c171bSEric Dumazet }
35588b5c171bSEric Dumazet 
neigh_copy_dflt_parms(struct net * net,struct neigh_parms * p,int index)35591d4c8c29SJiri Pirko static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p,
35601d4c8c29SJiri Pirko 				  int index)
35611d4c8c29SJiri Pirko {
35621d4c8c29SJiri Pirko 	struct net_device *dev;
35631d4c8c29SJiri Pirko 	int family = neigh_parms_family(p);
35641d4c8c29SJiri Pirko 
35651d4c8c29SJiri Pirko 	rcu_read_lock();
35661d4c8c29SJiri Pirko 	for_each_netdev_rcu(net, dev) {
35671d4c8c29SJiri Pirko 		struct neigh_parms *dst_p =
35681d4c8c29SJiri Pirko 				neigh_get_dev_parms_rcu(dev, family);
35691d4c8c29SJiri Pirko 
35701d4c8c29SJiri Pirko 		if (dst_p && !test_bit(index, dst_p->data_state))
35711d4c8c29SJiri Pirko 			dst_p->data[index] = p->data[index];
35721d4c8c29SJiri Pirko 	}
35731d4c8c29SJiri Pirko 	rcu_read_unlock();
35741d4c8c29SJiri Pirko }
35751d4c8c29SJiri Pirko 
neigh_proc_update(struct ctl_table * ctl,int write)35761d4c8c29SJiri Pirko static void neigh_proc_update(struct ctl_table *ctl, int write)
35771d4c8c29SJiri Pirko {
35781d4c8c29SJiri Pirko 	struct net_device *dev = ctl->extra1;
35791d4c8c29SJiri Pirko 	struct neigh_parms *p = ctl->extra2;
358077d47afbSJiri Pirko 	struct net *net = neigh_parms_net(p);
35811d4c8c29SJiri Pirko 	int index = (int *) ctl->data - p->data;
35821d4c8c29SJiri Pirko 
35831d4c8c29SJiri Pirko 	if (!write)
35841d4c8c29SJiri Pirko 		return;
35851d4c8c29SJiri Pirko 
35861d4c8c29SJiri Pirko 	set_bit(index, p->data_state);
35877627ae60SMarcus Huewe 	if (index == NEIGH_VAR_DELAY_PROBE_TIME)
35882a4501aeSIdo Schimmel 		call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p);
35891d4c8c29SJiri Pirko 	if (!dev) /* NULL dev means this is default value */
35901d4c8c29SJiri Pirko 		neigh_copy_dflt_parms(net, p, index);
35911d4c8c29SJiri Pirko }
35921d4c8c29SJiri Pirko 
neigh_proc_dointvec_zero_intmax(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)35931f9248e5SJiri Pirko static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write,
359432927393SChristoph Hellwig 					   void *buffer, size_t *lenp,
359532927393SChristoph Hellwig 					   loff_t *ppos)
35961f9248e5SJiri Pirko {
35971f9248e5SJiri Pirko 	struct ctl_table tmp = *ctl;
35981d4c8c29SJiri Pirko 	int ret;
35991f9248e5SJiri Pirko 
3600eec4844fSMatteo Croce 	tmp.extra1 = SYSCTL_ZERO;
3601eec4844fSMatteo Croce 	tmp.extra2 = SYSCTL_INT_MAX;
36021f9248e5SJiri Pirko 
36031d4c8c29SJiri Pirko 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
36041d4c8c29SJiri Pirko 	neigh_proc_update(ctl, write);
36051d4c8c29SJiri Pirko 	return ret;
36061f9248e5SJiri Pirko }
36071f9248e5SJiri Pirko 
neigh_proc_dointvec_ms_jiffies_positive(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)3608211da42eSYuwei Wang static int neigh_proc_dointvec_ms_jiffies_positive(struct ctl_table *ctl, int write,
3609211da42eSYuwei Wang 						   void *buffer, size_t *lenp, loff_t *ppos)
3610211da42eSYuwei Wang {
3611211da42eSYuwei Wang 	struct ctl_table tmp = *ctl;
3612211da42eSYuwei Wang 	int ret;
3613211da42eSYuwei Wang 
3614211da42eSYuwei Wang 	int min = msecs_to_jiffies(1);
3615211da42eSYuwei Wang 
3616211da42eSYuwei Wang 	tmp.extra1 = &min;
3617211da42eSYuwei Wang 	tmp.extra2 = NULL;
3618211da42eSYuwei Wang 
3619211da42eSYuwei Wang 	ret = proc_dointvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos);
3620211da42eSYuwei Wang 	neigh_proc_update(ctl, write);
3621211da42eSYuwei Wang 	return ret;
3622211da42eSYuwei Wang }
3623211da42eSYuwei Wang 
neigh_proc_dointvec(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)362432927393SChristoph Hellwig int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer,
362532927393SChristoph Hellwig 			size_t *lenp, loff_t *ppos)
3626cb5b09c1SJiri Pirko {
36271d4c8c29SJiri Pirko 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
36281d4c8c29SJiri Pirko 
36291d4c8c29SJiri Pirko 	neigh_proc_update(ctl, write);
36301d4c8c29SJiri Pirko 	return ret;
3631cb5b09c1SJiri Pirko }
3632cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec);
3633cb5b09c1SJiri Pirko 
neigh_proc_dointvec_jiffies(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)363432927393SChristoph Hellwig int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer,
3635cb5b09c1SJiri Pirko 				size_t *lenp, loff_t *ppos)
3636cb5b09c1SJiri Pirko {
36371d4c8c29SJiri Pirko 	int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
36381d4c8c29SJiri Pirko 
36391d4c8c29SJiri Pirko 	neigh_proc_update(ctl, write);
36401d4c8c29SJiri Pirko 	return ret;
3641cb5b09c1SJiri Pirko }
3642cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_jiffies);
3643cb5b09c1SJiri Pirko 
neigh_proc_dointvec_userhz_jiffies(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)3644cb5b09c1SJiri Pirko static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write,
364532927393SChristoph Hellwig 					      void *buffer, size_t *lenp,
364632927393SChristoph Hellwig 					      loff_t *ppos)
3647cb5b09c1SJiri Pirko {
36481d4c8c29SJiri Pirko 	int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos);
36491d4c8c29SJiri Pirko 
36501d4c8c29SJiri Pirko 	neigh_proc_update(ctl, write);
36511d4c8c29SJiri Pirko 	return ret;
3652cb5b09c1SJiri Pirko }
3653cb5b09c1SJiri Pirko 
neigh_proc_dointvec_ms_jiffies(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)3654cb5b09c1SJiri Pirko int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write,
365532927393SChristoph Hellwig 				   void *buffer, size_t *lenp, loff_t *ppos)
3656cb5b09c1SJiri Pirko {
36571d4c8c29SJiri Pirko 	int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos);
36581d4c8c29SJiri Pirko 
36591d4c8c29SJiri Pirko 	neigh_proc_update(ctl, write);
36601d4c8c29SJiri Pirko 	return ret;
3661cb5b09c1SJiri Pirko }
3662cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies);
3663cb5b09c1SJiri Pirko 
neigh_proc_dointvec_unres_qlen(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)3664cb5b09c1SJiri Pirko static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write,
366532927393SChristoph Hellwig 					  void *buffer, size_t *lenp,
366632927393SChristoph Hellwig 					  loff_t *ppos)
3667cb5b09c1SJiri Pirko {
36681d4c8c29SJiri Pirko 	int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos);
36691d4c8c29SJiri Pirko 
36701d4c8c29SJiri Pirko 	neigh_proc_update(ctl, write);
36711d4c8c29SJiri Pirko 	return ret;
3672cb5b09c1SJiri Pirko }
3673cb5b09c1SJiri Pirko 
neigh_proc_base_reachable_time(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)36744bf6980dSJean-Francois Remy static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write,
367532927393SChristoph Hellwig 					  void *buffer, size_t *lenp,
367632927393SChristoph Hellwig 					  loff_t *ppos)
36774bf6980dSJean-Francois Remy {
36784bf6980dSJean-Francois Remy 	struct neigh_parms *p = ctl->extra2;
36794bf6980dSJean-Francois Remy 	int ret;
36804bf6980dSJean-Francois Remy 
36814bf6980dSJean-Francois Remy 	if (strcmp(ctl->procname, "base_reachable_time") == 0)
36824bf6980dSJean-Francois Remy 		ret = neigh_proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
36834bf6980dSJean-Francois Remy 	else if (strcmp(ctl->procname, "base_reachable_time_ms") == 0)
36844bf6980dSJean-Francois Remy 		ret = neigh_proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos);
36854bf6980dSJean-Francois Remy 	else
36864bf6980dSJean-Francois Remy 		ret = -1;
36874bf6980dSJean-Francois Remy 
36884bf6980dSJean-Francois Remy 	if (write && ret == 0) {
36894bf6980dSJean-Francois Remy 		/* update reachable_time as well, otherwise, the change will
36904bf6980dSJean-Francois Remy 		 * only be effective after the next time neigh_periodic_work
36914bf6980dSJean-Francois Remy 		 * decides to recompute it
36924bf6980dSJean-Francois Remy 		 */
36934bf6980dSJean-Francois Remy 		p->reachable_time =
36944bf6980dSJean-Francois Remy 			neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
36954bf6980dSJean-Francois Remy 	}
36964bf6980dSJean-Francois Remy 	return ret;
36974bf6980dSJean-Francois Remy }
36984bf6980dSJean-Francois Remy 
36991f9248e5SJiri Pirko #define NEIGH_PARMS_DATA_OFFSET(index)	\
37001f9248e5SJiri Pirko 	(&((struct neigh_parms *) 0)->data[index])
37011f9248e5SJiri Pirko 
37021f9248e5SJiri Pirko #define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \
37031f9248e5SJiri Pirko 	[NEIGH_VAR_ ## attr] = { \
37041f9248e5SJiri Pirko 		.procname	= name, \
37051f9248e5SJiri Pirko 		.data		= NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \
37061f9248e5SJiri Pirko 		.maxlen		= sizeof(int), \
37071f9248e5SJiri Pirko 		.mode		= mval, \
37081f9248e5SJiri Pirko 		.proc_handler	= proc, \
37091f9248e5SJiri Pirko 	}
37101f9248e5SJiri Pirko 
37111f9248e5SJiri Pirko #define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \
37121f9248e5SJiri Pirko 	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax)
37131f9248e5SJiri Pirko 
37141f9248e5SJiri Pirko #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \
3715cb5b09c1SJiri Pirko 	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies)
37161f9248e5SJiri Pirko 
37171f9248e5SJiri Pirko #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \
3718cb5b09c1SJiri Pirko 	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies)
37191f9248e5SJiri Pirko 
3720211da42eSYuwei Wang #define NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(attr, name) \
3721211da42eSYuwei Wang 	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies_positive)
3722211da42eSYuwei Wang 
37231f9248e5SJiri Pirko #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \
3724cb5b09c1SJiri Pirko 	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies)
37251f9248e5SJiri Pirko 
37261f9248e5SJiri Pirko #define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \
3727cb5b09c1SJiri Pirko 	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen)
372854716e3bSEric W. Biederman 
37291da177e4SLinus Torvalds static struct neigh_sysctl_table {
37301da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
37318b5c171bSEric Dumazet 	struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1];
3732ab32ea5dSBrian Haley } neigh_sysctl_template __read_mostly = {
37331da177e4SLinus Torvalds 	.neigh_vars = {
37341f9248e5SJiri Pirko 		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"),
37351f9248e5SJiri Pirko 		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"),
37361f9248e5SJiri Pirko 		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"),
37378da86466SYOSHIFUJI Hideaki/吉藤英明 		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"),
37381f9248e5SJiri Pirko 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"),
37391f9248e5SJiri Pirko 		NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"),
37401f9248e5SJiri Pirko 		NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"),
3741211da42eSYuwei Wang 		NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(INTERVAL_PROBE_TIME_MS,
3742211da42eSYuwei Wang 						       "interval_probe_time_ms"),
37431f9248e5SJiri Pirko 		NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"),
37441f9248e5SJiri Pirko 		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"),
37451f9248e5SJiri Pirko 		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"),
37461f9248e5SJiri Pirko 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"),
37471f9248e5SJiri Pirko 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"),
37481f9248e5SJiri Pirko 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"),
37491f9248e5SJiri Pirko 		NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"),
37501f9248e5SJiri Pirko 		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"),
37511f9248e5SJiri Pirko 		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"),
37528b5c171bSEric Dumazet 		[NEIGH_VAR_GC_INTERVAL] = {
37531da177e4SLinus Torvalds 			.procname	= "gc_interval",
37541da177e4SLinus Torvalds 			.maxlen		= sizeof(int),
37551da177e4SLinus Torvalds 			.mode		= 0644,
37566d9f239aSAlexey Dobriyan 			.proc_handler	= proc_dointvec_jiffies,
37571da177e4SLinus Torvalds 		},
37588b5c171bSEric Dumazet 		[NEIGH_VAR_GC_THRESH1] = {
37591da177e4SLinus Torvalds 			.procname	= "gc_thresh1",
37601da177e4SLinus Torvalds 			.maxlen		= sizeof(int),
37611da177e4SLinus Torvalds 			.mode		= 0644,
3762eec4844fSMatteo Croce 			.extra1		= SYSCTL_ZERO,
3763eec4844fSMatteo Croce 			.extra2		= SYSCTL_INT_MAX,
3764555445cdSFrancesco Fusco 			.proc_handler	= proc_dointvec_minmax,
37651da177e4SLinus Torvalds 		},
37668b5c171bSEric Dumazet 		[NEIGH_VAR_GC_THRESH2] = {
37671da177e4SLinus Torvalds 			.procname	= "gc_thresh2",
37681da177e4SLinus Torvalds 			.maxlen		= sizeof(int),
37691da177e4SLinus Torvalds 			.mode		= 0644,
3770eec4844fSMatteo Croce 			.extra1		= SYSCTL_ZERO,
3771eec4844fSMatteo Croce 			.extra2		= SYSCTL_INT_MAX,
3772555445cdSFrancesco Fusco 			.proc_handler	= proc_dointvec_minmax,
37731da177e4SLinus Torvalds 		},
37748b5c171bSEric Dumazet 		[NEIGH_VAR_GC_THRESH3] = {
37751da177e4SLinus Torvalds 			.procname	= "gc_thresh3",
37761da177e4SLinus Torvalds 			.maxlen		= sizeof(int),
37771da177e4SLinus Torvalds 			.mode		= 0644,
3778eec4844fSMatteo Croce 			.extra1		= SYSCTL_ZERO,
3779eec4844fSMatteo Croce 			.extra2		= SYSCTL_INT_MAX,
3780555445cdSFrancesco Fusco 			.proc_handler	= proc_dointvec_minmax,
37811da177e4SLinus Torvalds 		},
3782c3bac5a7SPavel Emelyanov 		{},
37831da177e4SLinus Torvalds 	},
37841da177e4SLinus Torvalds };
37851da177e4SLinus Torvalds 
neigh_sysctl_register(struct net_device * dev,struct neigh_parms * p,proc_handler * handler)37861da177e4SLinus Torvalds int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
378773af614aSJiri Pirko 			  proc_handler *handler)
37881da177e4SLinus Torvalds {
37891f9248e5SJiri Pirko 	int i;
37903c607bbbSPavel Emelyanov 	struct neigh_sysctl_table *t;
37911f9248e5SJiri Pirko 	const char *dev_name_source;
37928f40a1f9SEric W. Biederman 	char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ];
379373af614aSJiri Pirko 	char *p_name;
3794c899710fSJoel Granados 	size_t neigh_vars_size;
37951da177e4SLinus Torvalds 
3796425b9c7fSVasily Averin 	t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL_ACCOUNT);
37971da177e4SLinus Torvalds 	if (!t)
37983c607bbbSPavel Emelyanov 		goto err;
37993c607bbbSPavel Emelyanov 
3800b194c1f1SJiri Pirko 	for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) {
38011f9248e5SJiri Pirko 		t->neigh_vars[i].data += (long) p;
3802cb5b09c1SJiri Pirko 		t->neigh_vars[i].extra1 = dev;
38031d4c8c29SJiri Pirko 		t->neigh_vars[i].extra2 = p;
3804cb5b09c1SJiri Pirko 	}
38051da177e4SLinus Torvalds 
3806c899710fSJoel Granados 	neigh_vars_size = ARRAY_SIZE(t->neigh_vars);
38071da177e4SLinus Torvalds 	if (dev) {
38081da177e4SLinus Torvalds 		dev_name_source = dev->name;
3809d12af679SEric W. Biederman 		/* Terminate the table early */
38108b5c171bSEric Dumazet 		memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0,
38118b5c171bSEric Dumazet 		       sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL]));
3812c899710fSJoel Granados 		neigh_vars_size = NEIGH_VAR_BASE_REACHABLE_TIME_MS + 1;
38131da177e4SLinus Torvalds 	} else {
38149ecf07a1SMathias Krause 		struct neigh_table *tbl = p->tbl;
38158f40a1f9SEric W. Biederman 		dev_name_source = "default";
38169ecf07a1SMathias Krause 		t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval;
38179ecf07a1SMathias Krause 		t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1;
38189ecf07a1SMathias Krause 		t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2;
38199ecf07a1SMathias Krause 		t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3;
38201da177e4SLinus Torvalds 	}
38211da177e4SLinus Torvalds 
3822f8572d8fSEric W. Biederman 	if (handler) {
38231da177e4SLinus Torvalds 		/* RetransTime */
38248b5c171bSEric Dumazet 		t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler;
38251da177e4SLinus Torvalds 		/* ReachableTime */
38268b5c171bSEric Dumazet 		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler;
38271da177e4SLinus Torvalds 		/* RetransTime (in milliseconds)*/
38288b5c171bSEric Dumazet 		t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler;
38291da177e4SLinus Torvalds 		/* ReachableTime (in milliseconds) */
38308b5c171bSEric Dumazet 		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler;
38314bf6980dSJean-Francois Remy 	} else {
38324bf6980dSJean-Francois Remy 		/* Those handlers will update p->reachable_time after
38334bf6980dSJean-Francois Remy 		 * base_reachable_time(_ms) is set to ensure the new timer starts being
38344bf6980dSJean-Francois Remy 		 * applied after the next neighbour update instead of waiting for
38354bf6980dSJean-Francois Remy 		 * neigh_periodic_work to update its value (can be multiple minutes)
38364bf6980dSJean-Francois Remy 		 * So any handler that replaces them should do this as well
38374bf6980dSJean-Francois Remy 		 */
38384bf6980dSJean-Francois Remy 		/* ReachableTime */
38394bf6980dSJean-Francois Remy 		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler =
38404bf6980dSJean-Francois Remy 			neigh_proc_base_reachable_time;
38414bf6980dSJean-Francois Remy 		/* ReachableTime (in milliseconds) */
38424bf6980dSJean-Francois Remy 		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler =
38434bf6980dSJean-Francois Remy 			neigh_proc_base_reachable_time;
38441da177e4SLinus Torvalds 	}
38451da177e4SLinus Torvalds 
384673af614aSJiri Pirko 	switch (neigh_parms_family(p)) {
384773af614aSJiri Pirko 	case AF_INET:
384873af614aSJiri Pirko 	      p_name = "ipv4";
384973af614aSJiri Pirko 	      break;
385073af614aSJiri Pirko 	case AF_INET6:
385173af614aSJiri Pirko 	      p_name = "ipv6";
385273af614aSJiri Pirko 	      break;
385373af614aSJiri Pirko 	default:
385473af614aSJiri Pirko 	      BUG();
385573af614aSJiri Pirko 	}
385673af614aSJiri Pirko 
38578f40a1f9SEric W. Biederman 	snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s",
38588f40a1f9SEric W. Biederman 		p_name, dev_name_source);
3859c899710fSJoel Granados 	t->sysctl_header = register_net_sysctl_sz(neigh_parms_net(p),
3860c899710fSJoel Granados 						  neigh_path, t->neigh_vars,
3861c899710fSJoel Granados 						  neigh_vars_size);
38623c607bbbSPavel Emelyanov 	if (!t->sysctl_header)
38638f40a1f9SEric W. Biederman 		goto free;
38643c607bbbSPavel Emelyanov 
38651da177e4SLinus Torvalds 	p->sysctl_table = t;
38661da177e4SLinus Torvalds 	return 0;
38671da177e4SLinus Torvalds 
38681da177e4SLinus Torvalds free:
38691da177e4SLinus Torvalds 	kfree(t);
38703c607bbbSPavel Emelyanov err:
38713c607bbbSPavel Emelyanov 	return -ENOBUFS;
38721da177e4SLinus Torvalds }
38730a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_register);
38741da177e4SLinus Torvalds 
neigh_sysctl_unregister(struct neigh_parms * p)38751da177e4SLinus Torvalds void neigh_sysctl_unregister(struct neigh_parms *p)
38761da177e4SLinus Torvalds {
38771da177e4SLinus Torvalds 	if (p->sysctl_table) {
38781da177e4SLinus Torvalds 		struct neigh_sysctl_table *t = p->sysctl_table;
38791da177e4SLinus Torvalds 		p->sysctl_table = NULL;
38805dd3df10SEric W. Biederman 		unregister_net_sysctl_table(t->sysctl_header);
38811da177e4SLinus Torvalds 		kfree(t);
38821da177e4SLinus Torvalds 	}
38831da177e4SLinus Torvalds }
38840a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_unregister);
38851da177e4SLinus Torvalds 
38861da177e4SLinus Torvalds #endif	/* CONFIG_SYSCTL */
38871da177e4SLinus Torvalds 
neigh_init(void)3888c8822a4eSThomas Graf static int __init neigh_init(void)
3889c8822a4eSThomas Graf {
3890b97bac64SFlorian Westphal 	rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0);
3891b97bac64SFlorian Westphal 	rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0);
389282cbb5c6SRoopa Prabhu 	rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0);
3893c8822a4eSThomas Graf 
3894c7ac8679SGreg Rose 	rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info,
3895b97bac64SFlorian Westphal 		      0);
3896b97bac64SFlorian Westphal 	rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0);
3897c8822a4eSThomas Graf 
3898c8822a4eSThomas Graf 	return 0;
3899c8822a4eSThomas Graf }
3900c8822a4eSThomas Graf 
3901c8822a4eSThomas Graf subsys_initcall(neigh_init);
3902